三大系统

This commit is contained in:
Tourswen
2025-12-16 02:13:18 +08:00
parent 15404222fd
commit b17407b53f
55 changed files with 4892 additions and 644 deletions

Binary file not shown.

View File

@@ -328,8 +328,8 @@
<!-- 蜜罐种 -->
<HediffDef>
<defName>ARA_Myrmecocystus_Production_InsectJelly</defName>
<label>产出虫蜜</label>
<description>这只阿拉克涅蜜罐种正在产出虫蜜,以滋养虫群。一只蜜罐种每天产出15份阿拉克涅虫蜜。</description>
<label>虫蜜腔室</label>
<description>这只阿拉克涅蜜罐种正在产出虫蜜,以滋养虫群。</description>
<descriptionHyperlinks>
<ThingDef>ARA_InsectJelly</ThingDef>
<HediffDef>ARA_Myrmecocystus_Production_Medicine</HediffDef>
@@ -368,61 +368,14 @@
</drawData>
</li>
</renderNodeProperties>
<!-- <stages>
<stages>
<li>
<becomeVisible>ture</becomeVisible>
<enablesNeeds>
<li>ARA_HoneyProduction</li>
</enablesNeeds>
</li>
<li>
<minSeverity>1.01</minSeverity>
<becomeVisible>false</becomeVisible>
</li>
</stages> -->
</stages>
<comps>
<li Class="ArachnaeSwarm.MoharHediffs.HediffCompProperties_Spawner">
<!--
==================================================
基础设置 (Basic Settings)
==================================================
-->
<!-- [DEBUG] 如果为true则为此组件启用详细的调试日志记录。 -->
<debug>true</debug>
<!-- 要生成的物品的ThingDef。 -->
<thingToSpawn>ARA_InsectJelly</thingToSpawn>
<!-- 每次生成的基础物品数量。 -->
<spawnCount>15</spawnCount>
<!--
==================================================
生成周期 (Spawning Interval)
==================================================
-->
<!-- 下一次生成事件发生前的最少天数。 -->
<minDaysB4Next>1</minDaysB4Next>
<!-- 下一次生成事件发生前的最大天数。 -->
<maxDaysB4Next>1</maxDaysB4Next>
<randomGrace>0</randomGrace>
<!--
==================================================
与年龄相关的调整 (Age-Related Adjustments)
==================================================
-->
<!-- 如果为true生成数量将根据宿主的年龄进行调整。 -->
<ageWeightedQuantity>false</ageWeightedQuantity>
<!-- 如果为true且ageWeightedQuantity为true则随着宿主年龄增长生成数量变多。 -->
<olderBiggerQuantity>true</olderBiggerQuantity>
<!-- 如果为true且ageWeightedQuantity为true则随年龄增长的数量缩放将是指数性的而非线性的。 -->
<exponentialQuantity>true</exponentialQuantity>
<!-- 指数级数量缩放的最大乘数,以防止出现荒谬的数字。 -->
<exponentialRatioLimit>20</exponentialRatioLimit>
<!--
==================================================
生成条件 (Spawning Conditions)
==================================================
-->
<!-- 如果为true当宿主Pawn饥饿时生成将暂停。 -->
<hungerRelative>true</hungerRelative>
<!-- 如果为true当宿主Pawn受伤时生成将暂停。 -->
<healthRelative>false</healthRelative>
</li>
<li Class="HediffCompProperties_GiveAbility">
<abilityDefs>
<li>ARA_Myrmecocystus_Production_Medicine</li>
@@ -1203,51 +1156,6 @@
</li>
</stages>
<comps>
<li Class="ArachnaeSwarm.MoharHediffs.HediffCompProperties_Spawner">
<!--
==================================================
基础设置 (Basic Settings)
==================================================
-->
<!-- [DEBUG] 如果为true则为此组件启用详细的调试日志记录。 -->
<debug>true</debug>
<!-- 要生成的物品的ThingDef。 -->
<thingToSpawn>ARA_Carapace</thingToSpawn>
<!-- 每次生成的基础物品数量。 -->
<spawnCount>15</spawnCount>
<!--
==================================================
生成周期 (Spawning Interval)
==================================================
-->
<!-- 下一次生成事件发生前的最少天数。 -->
<minDaysB4Next>1</minDaysB4Next>
<!-- 下一次生成事件发生前的最大天数。 -->
<maxDaysB4Next>1</maxDaysB4Next>
<randomGrace>0</randomGrace>
<!--
==================================================
与年龄相关的调整 (Age-Related Adjustments)
==================================================
-->
<!-- 如果为true生成数量将根据宿主的年龄进行调整。 -->
<ageWeightedQuantity>false</ageWeightedQuantity>
<!-- 如果为true且ageWeightedQuantity为true则随着宿主年龄增长生成数量变多。 -->
<olderBiggerQuantity>true</olderBiggerQuantity>
<!-- 如果为true且ageWeightedQuantity为true则随年龄增长的数量缩放将是指数性的而非线性的。 -->
<exponentialQuantity>true</exponentialQuantity>
<!-- 指数级数量缩放的最大乘数,以防止出现荒谬的数字。 -->
<exponentialRatioLimit>20</exponentialRatioLimit>
<!--
==================================================
生成条件 (Spawning Conditions)
==================================================
-->
<!-- 如果为true当宿主Pawn饥饿时生成将暂停。 -->
<hungerRelative>true</hungerRelative>
<!-- 如果为true当宿主Pawn受伤时生成将暂停。 -->
<healthRelative>false</healthRelative>
</li>
<li Class="HediffCompProperties_GiveAbility">
<abilityDefs>
<li>ARA_ShieldHead_Protector</li>

View File

@@ -22,6 +22,7 @@
<li>Outdoors</li>
</disablesNeeds>
<enablesNeeds>
<li>ARA_ChitinArmor</li>
<li>Indoors</li>
</enablesNeeds>
</li>
@@ -54,6 +55,9 @@
<li>DrugDesire</li>
<li>RoomSize</li>
</disablesNeeds>
<enablesNeeds>
<li>ARA_ChitinArmor</li>
</enablesNeeds>
</li>
</stages>
<comps>
@@ -95,6 +99,9 @@
<li>DrugDesire</li>
<li>RoomSize</li>
</disablesNeeds>
<enablesNeeds>
<li>ARA_ChitinArmor</li>
</enablesNeeds>
</li>
</stages>
</HediffDef>
@@ -301,4 +308,76 @@
</li>
</comps>
</HediffDef>
<HediffDef>
<defName>ARA_ChitinArmor</defName>
<label>甲壳护甲</label>
<description>阿拉克涅虫族身上的甲壳,可以根据厚度为阿拉克涅虫族提供保护。</description>
<hediffClass>HediffWithComps</hediffClass>
<stages>
<li>
<minSeverity>0</minSeverity>
<label>极薄</label>
<statFactors>
<IncomingDamageFactor>0.95</IncomingDamageFactor>
</statFactors>
</li>
<li>
<minSeverity>0.99</minSeverity>
<label></label>
<statFactors>
<IncomingDamageFactor>0.9</IncomingDamageFactor>
<StaggerDurationFactor>0.9</StaggerDurationFactor>
</statFactors>
</li>
<li>
<minSeverity>1.99</minSeverity>
<label>适中</label>
<statFactors>
<IncomingDamageFactor>0.85</IncomingDamageFactor>
<StaggerDurationFactor>0.75</StaggerDurationFactor>
</statFactors>
</li>
<li>
<minSeverity>2.99</minSeverity>
<label>较厚</label>
<statFactors>
<IncomingDamageFactor>0.7</IncomingDamageFactor>
<StaggerDurationFactor>0.5</StaggerDurationFactor>
</statFactors>
</li>
<li>
<minSeverity>3.99</minSeverity>
<label></label>
<statFactors>
<IncomingDamageFactor>0.5</IncomingDamageFactor>
<StaggerDurationFactor>0.25</StaggerDurationFactor>
</statFactors>
</li>
<li>
<minSeverity>4.99</minSeverity>
<label>极厚</label>
<statFactors>
<IncomingDamageFactor>0.35</IncomingDamageFactor>
<StaggerDurationFactor>0</StaggerDurationFactor>
</statFactors>
</li>
<li>
<minSeverity>5.99</minSeverity>
<label>固若磐石</label>
<statFactors>
<IncomingDamageFactor>0.25</IncomingDamageFactor>
<StaggerDurationFactor>0</StaggerDurationFactor>
</statFactors>
</li>
<li>
<minSeverity>6.99</minSeverity>
<label>坚不可摧</label>
<statFactors>
<IncomingDamageFactor>0.1</IncomingDamageFactor>
<StaggerDurationFactor>0</StaggerDurationFactor>
</statFactors>
</li>
</stages>
</HediffDef>
</Defs>

View File

@@ -38,4 +38,61 @@
<makeTargetPrisoner>false</makeTargetPrisoner>
<casualInterruptible>true</casualInterruptible>
</JobDef>
<JobDef>
<defName>ARA_OperateEquipmentIncubator</defName>
<driverClass>ArachnaeSwarm.JobDriver_OperateEquipmentIncubator</driverClass>
<reportString>激活阿拉克涅茧。</reportString>
<playerInterruptible>true</playerInterruptible>
<alwaysShowWeapon>false</alwaysShowWeapon>
<suspendable>true</suspendable>
<makeTargetPrisoner>false</makeTargetPrisoner>
<casualInterruptible>true</casualInterruptible>
</JobDef>
<!-- 喂养工作 -->
<JobDef>
<defName>ARA_FeedWithHoney</defName>
<driverClass>ArachnaeSwarm.JobDriver_FeedWithHoney</driverClass>
<reportString>正在喂养TargetA。</reportString>
<alwaysShowWeapon>false</alwaysShowWeapon>
<suspendable>true</suspendable>
<playerInterruptible>true</playerInterruptible>
<!-- <checkOverrideOnDamage></checkOverrideOnDamage> -->
<casualInterruptible>true</casualInterruptible>
<!-- <canBeForced>false</canBeForced> -->
<!-- <joySkill>Social</joySkill>
<joyXpPerTick>0.0005</joyXpPerTick> -->
</JobDef>
<JobDef>
<defName>ARA_ExtractHoney</defName>
<driverClass>ArachnaeSwarm.JobDriver_ExtractHoney</driverClass>
<reportString>正在挤出虫蜜。</reportString>
<alwaysShowWeapon>false</alwaysShowWeapon>
<suspendable>true</suspendable>
<playerInterruptible>true</playerInterruptible>
<!-- <checkOverrideOnDamage>Minor</checkOverrideOnDamage> -->
<casualInterruptible>true</casualInterruptible>
<!-- <canBeForced>false</canBeForced> -->
</JobDef>
<!-- 甲壳剥离工作 -->
<JobDef>
<defName>ARA_StripChitin</defName>
<driverClass>ArachnaeSwarm.JobDriver_StripChitin</driverClass>
<reportString>正在剥离甲壳</reportString>
<playerInterruptible>true</playerInterruptible>
<alwaysShowWeapon>false</alwaysShowWeapon>
<suspendable>true</suspendable>
<!-- <checkOverrideOnDamage>CheckJobOverrideOnDamageMode.Always</checkOverrideOnDamage> -->
</JobDef>
<JobDef>
<defName>ARA_SwarmMaintain</defName>
<driverClass>ArachnaeSwarm.JobDriver_SwarmMaintain</driverClass>
<reportString>正在维护TargetA。</reportString>
<playerInterruptible>true</playerInterruptible>
<!-- <checkOverrideOnDamage>CheckJobOverrideOnDamageMode.Always</checkOverrideOnDamage> -->
<alwaysShowWeapon>false</alwaysShowWeapon>
<suspendable>true</suspendable>
<casualInterruptible>true</casualInterruptible>
<makeTargetPrisoner>false</makeTargetPrisoner>
</JobDef>
</Defs>

View File

@@ -4,16 +4,57 @@
<defName>ARA_HoneyProduction</defName>
<needClass>ArachnaeSwarm.Need_HoneyProduction</needClass>
<label>蜜罐</label>
<description>代表这个生物储存阿拉克涅虫蜜原浆的多少。当其他虫族饥饿时,会尝试直接从有蜜罐腔的生物身上获取虫蜜,如果蜜罐满溢,则生物会尝试将其提取出来。</description>
<description>代表这个生物储存阿拉克涅虫蜜原浆的多少。当其他虫族饥饿时,有蜜罐腔的生物会尝试喂养它们;如果蜜罐满溢,则生物会尝试将其提取出来。</description>
<listPriority>800</listPriority>
<major>true</major>
<onlyIfCausedByHediff>true</onlyIfCausedByHediff>
<hediffRequiredAny>
<li>Wula_Synth</li>
<li>ARA_Myrmecocystus_Production_InsectJelly</li>
</hediffRequiredAny>
<showForCaravanMembers>true</showForCaravanMembers>
<developmentalStageFilter>Baby, Child, Adult</developmentalStageFilter>
<showUnitTicks>true</showUnitTicks>
<freezeWhileSleeping>false</freezeWhileSleeping>
<modExtensions>
<li Class="ArachnaeSwarm.HoneyProductionExtension">
<!-- 基础转化率食物流失的60%转化为蜜罐 -->
<baseConversionRate>0.8</baseConversionRate>
<!-- 生产速率乘数(更快生产) -->
<productionSpeedFactor>1</productionSpeedFactor>
</li>
</modExtensions>
</NeedDef>
<NeedDef>
<defName>ARA_ChitinArmor</defName>
<needClass>ArachnaeSwarm.Need_ChitinArmor</needClass>
<label>甲壳</label>
<description>代表这个生物身上的阿拉克涅甲壳的厚度,越厚的甲壳越能为虫族带来强大的防御力。虫族也可以将其从身上剥离下来,以生产甲壳素。</description>
<listPriority>800</listPriority>
<major>true</major>
<onlyIfCausedByHediff>true</onlyIfCausedByHediff>
<hediffRequiredAny>
<li>ARA_HiveMindMaster</li>
<li>ARA_HiveMindDrone</li>
<li>ARA_NonPlayer_HiveMindDroneHediff</li>
</hediffRequiredAny>
<showForCaravanMembers>true</showForCaravanMembers>
<developmentalStageFilter>Baby, Child, Adult</developmentalStageFilter>
<showUnitTicks>true</showUnitTicks>
<freezeWhileSleeping>false</freezeWhileSleeping>
<modExtensions>
<li Class="ArachnaeSwarm.NeedDefExtension_ChitinLevels">
<hediff>ARA_ChitinArmor</hediff>
<severityRange>
<min>0.0</min>
<max>10.0</max>
</severityRange>
<removeOnDeath>true</removeOnDeath>
<baseGrowthRate>0.1</baseGrowthRate>
<squareCoefficient>0.1</squareCoefficient>
</li>
</modExtensions>
</NeedDef>
</Defs>

View File

@@ -210,8 +210,6 @@
</backstoryFiltersOverride>
<abilities>
<li>ARA_AcidSprayBurst</li>
<li>ARA_RaceBaseSwarmProduceOff</li>
<li>ARA_RaceBaseSwarmProduceOn</li>
</abilities>
<apparelTags>
</apparelTags>
@@ -256,10 +254,6 @@
<apparelTags>
</apparelTags>
<apparelMoney>0</apparelMoney>
<abilities>
<li>ARA_RaceBaseSwarmProduceOff</li>
<li>ARA_RaceBaseSwarmProduceOn</li>
</abilities>
</PawnKindDef>
<PawnKindDef ParentName="ArachnaeNodeABasePawnKind">
<defName>ArachnaeNode_Race_Facehugger</defName>
@@ -498,50 +492,6 @@
</li>
</lifeStages>
</PawnKindDef>
<PawnKindDef ParentName="ARA_InsectKindBase">
<defName>ArachnaeBase_Race_HardJaw</defName>
<label>阿拉克涅坚颚种</label>
<race>ArachnaeBase_Race_HardJaw</race>
<lifeStages>
<li>
<bodyGraphicData>
<texPath>ArachnaeSwarm/Things/ARA_Scavenger/HardJaw/Naked_Thin</texPath>
<drawSize>1</drawSize>
<color>(156,148,125)</color>
<shadowData>
<volume>(0.4, 0.5, 0.37)</volume>
<offset>(0,0,-0.15)</offset>
</shadowData>
</bodyGraphicData>
<dessicatedBodyGraphicData>
<texPath>Things/Pawn/Animal/Spelopede/Dessicated_Spelopede</texPath>
<drawSize>1</drawSize>
</dessicatedBodyGraphicData>
</li>
</lifeStages>
</PawnKindDef>
<PawnKindDef ParentName="ARA_InsectKindBase">
<defName>ArachnaeBase_Race_Maid</defName>
<label>阿拉克涅家政种</label>
<race>ArachnaeBase_Race_Maid</race>
<lifeStages>
<li>
<bodyGraphicData>
<texPath>ArachnaeSwarm/Things/ARA_Scavenger/Maid/Naked_Thin</texPath>
<drawSize>1</drawSize>
<color>(156,148,125)</color>
<shadowData>
<volume>(0.4, 0.5, 0.37)</volume>
<offset>(0,0,-0.15)</offset>
</shadowData>
</bodyGraphicData>
<dessicatedBodyGraphicData>
<texPath>Things/Pawn/Animal/Spelopede/Dessicated_Spelopede</texPath>
<drawSize>1</drawSize>
</dessicatedBodyGraphicData>
</li>
</lifeStages>
</PawnKindDef>
<PawnKindDef Name="ArachnaeBase_Race_Acidcut" ParentName="ARA_InsectKindBase">
<defName>ArachnaeBase_Race_Acidcut</defName>
<label>阿拉克涅酸噬种</label>

View File

@@ -6,7 +6,7 @@
<workerClass>ArachnaeSwarm.RoomRoleWorker_Incubator</workerClass>
<relatedStats>
<li>Space</li>
<li>ARA_IncubatorRateFactor</li>
<li>ARA_IncubatorQualityFactor</li>
</relatedStats>
</RoomRoleDef>
</Defs>

View File

@@ -775,11 +775,6 @@
</race>
<comps>
<!-- <li Class="ArachnaeSwarm.CompProperties_MilkableArachnae">
<milkDef>ARA_InsectJelly</milkDef>
<milkIntervalDays>1</milkIntervalDays>
<milkAmount>10</milkAmount>
</li> -->
<li Class="ArachnaeSwarm.CompProperties_NodeSwarmLifetime">
<immuteHediff>ARA_Cycle_Suppression_Hediff</immuteHediff>
<lifespanHediff>ARA_LifespanHediff</lifespanHediff>
@@ -937,7 +932,7 @@
<allowDuplicates>false</allowDuplicates>
</li>
<li Class="ArachnaeSwarm.CompProperties_AutoMechCarrier">
<freeProduction>false</freeProduction>
<freeProduction>true</freeProduction>
<disableHediff>ARA_RaceBaseSwarmProduceSwitchHediff</disableHediff>
<fixedIngredient>ARA_InsectJelly</fixedIngredient>
<maxIngredientCount>1</maxIngredientCount>
@@ -1063,24 +1058,6 @@
<addChance>1.0</addChance>
<allowDuplicates>false</allowDuplicates>
</li>
<li Class="ArachnaeSwarm.CompProperties_AutoMechCarrier">
<freeProduction>true</freeProduction>
<disableHediff>ARA_RaceBaseSwarmProduceSwitchHediff</disableHediff>
<fixedIngredient>ARA_InsectJelly</fixedIngredient>
<maxIngredientCount>1</maxIngredientCount>
<startingIngredientCount>1</startingIngredientCount>
<costPerPawn>999</costPerPawn>
<cooldownTicks>9999</cooldownTicks>
<productionQueue>
<li>
<!-- <pawnKind>ArachnaeBase_Race_Slavey</pawnKind> -->
<pawnKind>ArachnaeBase_Race_Maid</pawnKind>
<count>3</count>
<cooldownTicks>1000</cooldownTicks>
</li>
</productionQueue>
<spawnEffecter>CocoonDestroyed</spawnEffecter>
</li>
<li Class="ArachnaeSwarm.CompProperties_NodeSwarmLifetime">
<immuteHediff>ARA_Cycle_Suppression_Hediff</immuteHediff>
<lifespanHediff>ARA_LifespanHediff</lifespanHediff>
@@ -1419,24 +1396,6 @@
<addChance>1.0</addChance>
<allowDuplicates>false</allowDuplicates>
</li>
<li Class="ArachnaeSwarm.CompProperties_AutoMechCarrier">
<freeProduction>true</freeProduction>
<disableHediff>ARA_RaceBaseSwarmProduceSwitchHediff</disableHediff>
<fixedIngredient>ARA_InsectJelly</fixedIngredient>
<maxIngredientCount>1</maxIngredientCount>
<startingIngredientCount>1</startingIngredientCount>
<costPerPawn>999</costPerPawn>
<cooldownTicks>9999</cooldownTicks>
<productionQueue>
<li>
<!-- <pawnKind>ArachnaeBase_Race_Slavey</pawnKind> -->
<pawnKind>ArachnaeBase_Race_Maid</pawnKind>
<count>5</count>
<cooldownTicks>1000</cooldownTicks>
</li>
</productionQueue>
<spawnEffecter>CocoonDestroyed</spawnEffecter>
</li>
<li Class="ArachnaeSwarm.CompProperties_NodeSwarmLifetime">
<immuteHediff>ARA_Cycle_Suppression_Hediff</immuteHediff>
<lifespanHediff>ARA_LifespanHediff</lifespanHediff>

View File

@@ -169,6 +169,14 @@
<addChance>1.0</addChance>
<allowDuplicates>false</allowDuplicates>
</li>
<!-- 甲壳剥离组件 -->
<li Class="ArachnaeSwarm.CompProperties_ChitinStripping">
<canStripChitin>true</canStripChitin>
<stripThreshold>0.8</stripThreshold>
<minStripAmount>1</minStripAmount>
<stripInterval>3000</stripInterval>
<carapaceThingDef>ARA_Carapace</carapaceThingDef>
</li>
</comps>
</ThingDef>
<AlienRace.ThingDef_AlienRace Name="ARA_QueenBase" ParentName="ARA_PawnBase">
@@ -420,8 +428,7 @@
<!-- 可以驯服的宠物,主要是防止小虫由别人驯服 -->
<petList>
<li>ArachnaeBase_Race_Scavenger</li>
<li>ArachnaeBase_Race_HardJaw</li>
<li>ArachnaeBase_Race_Maid</li>
<li>ArachnaeBase_Race_Larva</li>
</petList>
<onlyTameRaceRestrictedPets>true</onlyTameRaceRestrictedPets>
<!-- 可以穿戴的衣服 -->

View File

@@ -73,9 +73,7 @@
<comps>
<li Class="ArachnaeSwarm.CompProperties_ExtraIncubationInfo">
<cocoonDefs>
<li>ARA_Cocoon_Weapon</li>
<li>ARA_Cocoon_Weapon_From_Death</li>
<li>ARA_BioforgeIncubator_Thing</li>
<li>ARA_Equipment_Ootheca</li>
</cocoonDefs>
</li>
</comps>

View File

@@ -495,9 +495,16 @@
</relatedTerrain>
</building>
<comps>
<li Class="ArachnaeSwarm.CompProperties_DelayedTerrainSpawn">
<terrainToSpawn>ARA_InsectCreep</terrainToSpawn>
<spawnRadius>3</spawnRadius>
<li Class="ArachnaeSwarm.CompProperties_SwarmMaintenance">
<maxMaintenance>100</maxMaintenance>
<maintenanceDecayPerDay>15</maintenanceDecayPerDay>
<damagePerSecondWhenEmpty>2</damagePerSecondWhenEmpty>
<warningThreshold>0.2</warningThreshold>
<assignJobCheckInterval>1800</assignJobCheckInterval>
<maintenanceThresholdForJob>0.5</maintenanceThresholdForJob>
<allowedRaces>
<li>ArachnaeNode_Race_WeaponSmith</li>
</allowedRaces>
</li>
</comps>
</ThingDef>
@@ -558,13 +565,20 @@
</relatedTerrain>
</building>
<comps Inherit="False">
<li Class="ArachnaeSwarm.CompProperties_SwarmMaintenance">
<maxMaintenance>100</maxMaintenance>
<maintenanceDecayPerDay>15</maintenanceDecayPerDay>
<damagePerSecondWhenEmpty>2</damagePerSecondWhenEmpty>
<warningThreshold>0.2</warningThreshold>
<assignJobCheckInterval>1800</assignJobCheckInterval>
<maintenanceThresholdForJob>0.5</maintenanceThresholdForJob>
<allowedRaces>
<li>ArachnaeNode_Race_WeaponSmith</li>
</allowedRaces>
</li>
<li Class="CompProperties_ReportWorkSpeed">
<workSpeedStat>ResearchSpeedFactor</workSpeedStat>
</li>
<li Class="ArachnaeSwarm.CompProperties_DelayedTerrainSpawn">
<terrainToSpawn>ARA_InsectCreep</terrainToSpawn>
<spawnRadius>4</spawnRadius>
</li>
</comps>
</ThingDef>
@@ -627,6 +641,17 @@
</researchPrerequisites>
<designationCategory>ARA_Buildings</designationCategory>
<comps>
<li Class="ArachnaeSwarm.CompProperties_SwarmMaintenance">
<maxMaintenance>100</maxMaintenance>
<maintenanceDecayPerDay>15</maintenanceDecayPerDay>
<damagePerSecondWhenEmpty>2</damagePerSecondWhenEmpty>
<warningThreshold>0.2</warningThreshold>
<assignJobCheckInterval>1800</assignJobCheckInterval>
<maintenanceThresholdForJob>0.5</maintenanceThresholdForJob>
<allowedRaces>
<li>ArachnaeNode_Race_WeaponSmith</li>
</allowedRaces>
</li>
<!-- 提供开关按钮 -->
<li Class="CompProperties_Flickable">
<commandTexture>UI/Commands/Vent</commandTexture>
@@ -638,10 +663,6 @@
<!-- 这是设备的热交换功率。数值越大,制冷/制热速度越快。-->
<energyPerSecond>25</energyPerSecond>
</li>
<li Class="ArachnaeSwarm.CompProperties_DelayedTerrainSpawn">
<terrainToSpawn>ARA_InsectCreep</terrainToSpawn>
<spawnRadius>3</spawnRadius>
</li>
</comps>
</ThingDef>
@@ -691,9 +712,16 @@
</relatedTerrain>
</building>
<comps>
<li Class="ArachnaeSwarm.CompProperties_DelayedTerrainSpawn">
<terrainToSpawn>ARA_InsectCreep</terrainToSpawn>
<spawnRadius>3</spawnRadius>
<li Class="ArachnaeSwarm.CompProperties_SwarmMaintenance">
<maxMaintenance>100</maxMaintenance>
<maintenanceDecayPerDay>15</maintenanceDecayPerDay>
<damagePerSecondWhenEmpty>2</damagePerSecondWhenEmpty>
<warningThreshold>0.2</warningThreshold>
<assignJobCheckInterval>1800</assignJobCheckInterval>
<maintenanceThresholdForJob>0.5</maintenanceThresholdForJob>
<allowedRaces>
<li>ArachnaeNode_Race_WeaponSmith</li>
</allowedRaces>
</li>
<li Class="CompProperties_Glower">
<glowRadius>3</glowRadius>
@@ -759,9 +787,16 @@
</relatedTerrain>
</building>
<comps>
<li Class="ArachnaeSwarm.CompProperties_DelayedTerrainSpawn">
<terrainToSpawn>ARA_InsectCreep</terrainToSpawn>
<spawnRadius>2</spawnRadius>
<li Class="ArachnaeSwarm.CompProperties_SwarmMaintenance">
<maxMaintenance>100</maxMaintenance>
<maintenanceDecayPerDay>15</maintenanceDecayPerDay>
<damagePerSecondWhenEmpty>2</damagePerSecondWhenEmpty>
<warningThreshold>0.2</warningThreshold>
<assignJobCheckInterval>1800</assignJobCheckInterval>
<maintenanceThresholdForJob>0.5</maintenanceThresholdForJob>
<allowedRaces>
<li>ArachnaeNode_Race_WeaponSmith</li>
</allowedRaces>
</li>
<li Class="CompProperties_Glower">
<glowRadius>3</glowRadius>
@@ -833,9 +868,16 @@
<li>PlaceWorker_GlowRadius</li>
</placeWorkers>
<comps>
<li Class="ArachnaeSwarm.CompProperties_DelayedTerrainSpawn">
<terrainToSpawn>ARA_InsectCreep</terrainToSpawn>
<spawnRadius>2</spawnRadius>
<li Class="ArachnaeSwarm.CompProperties_SwarmMaintenance">
<maxMaintenance>100</maxMaintenance>
<maintenanceDecayPerDay>15</maintenanceDecayPerDay>
<damagePerSecondWhenEmpty>2</damagePerSecondWhenEmpty>
<warningThreshold>0.2</warningThreshold>
<assignJobCheckInterval>1800</assignJobCheckInterval>
<maintenanceThresholdForJob>0.5</maintenanceThresholdForJob>
<allowedRaces>
<li>ArachnaeNode_Race_WeaponSmith</li>
</allowedRaces>
</li>
<li Class="CompProperties_Glower">
<glowRadius>7</glowRadius>
@@ -998,9 +1040,16 @@
</relatedTerrain>
</building>
<comps>
<li Class="ArachnaeSwarm.CompProperties_DelayedTerrainSpawn">
<terrainToSpawn>ARA_InsectCreep</terrainToSpawn>
<spawnRadius>5</spawnRadius>
<li Class="ArachnaeSwarm.CompProperties_SwarmMaintenance">
<maxMaintenance>100</maxMaintenance>
<maintenanceDecayPerDay>15</maintenanceDecayPerDay>
<damagePerSecondWhenEmpty>2</damagePerSecondWhenEmpty>
<warningThreshold>0.2</warningThreshold>
<assignJobCheckInterval>1800</assignJobCheckInterval>
<maintenanceThresholdForJob>0.5</maintenanceThresholdForJob>
<allowedRaces>
<li>ArachnaeNode_Race_WeaponSmith</li>
</allowedRaces>
</li>
<li Class="CompProperties_Glower">
<glowRadius>4</glowRadius>
@@ -1047,9 +1096,16 @@
</building>
<!-- 不可建造,只能通过变形生成 -->
<comps Inherit="False">
<li Class="ArachnaeSwarm.CompProperties_DelayedTerrainSpawn">
<terrainToSpawn>ARA_InsectCreep</terrainToSpawn>
<spawnRadius>3</spawnRadius>
<li Class="ArachnaeSwarm.CompProperties_SwarmMaintenance">
<maxMaintenance>100</maxMaintenance>
<maintenanceDecayPerDay>15</maintenanceDecayPerDay>
<damagePerSecondWhenEmpty>2</damagePerSecondWhenEmpty>
<warningThreshold>0.2</warningThreshold>
<assignJobCheckInterval>1800</assignJobCheckInterval>
<maintenanceThresholdForJob>0.5</maintenanceThresholdForJob>
<allowedRaces>
<li>ArachnaeNode_Race_WeaponSmith</li>
</allowedRaces>
</li>
<li Class="ArachnaeSwarm.CompProperties_Morphable">
<!-- 休息速度增益例如1.0代表200%的速度 -->

View File

@@ -33,19 +33,6 @@
<ARA_Carapace>20</ARA_Carapace>
<ARA_InsectJelly>3</ARA_InsectJelly>
</costList>
<comps>
<li Class="CompProperties_Transporter">
<massCapacity>300</massCapacity>
<restEffectiveness>0.8</restEffectiveness>
<canChangeAssignedThingsAfterStarting>true</canChangeAssignedThingsAfterStarting>
<!-- <max1PerGroup>true</max1PerGroup> -->
</li>
<li Class="CompProperties_Launchable_TransportPod">
<skyfallerLeaving>ARA_DropPodLeaving</skyfallerLeaving>
<requiresFuelingPort>false</requiresFuelingPort>
<fixedLaunchDistanceMax>53</fixedLaunchDistanceMax> <!-- 80% of full transport pod range -->
</li>
</comps>
<researchPrerequisites>
<li>ARA_Technology_8POD</li>
</researchPrerequisites>
@@ -58,6 +45,30 @@
<uiIconScale>0.65</uiIconScale>
<dropPodFaller>ARA_DropPodIncoming</dropPodFaller>
<dropPodActive>ARA_ActiveDropPod</dropPodActive>
<comps>
<li Class="ArachnaeSwarm.CompProperties_SwarmMaintenance">
<maxMaintenance>100</maxMaintenance>
<maintenanceDecayPerDay>15</maintenanceDecayPerDay>
<damagePerSecondWhenEmpty>2</damagePerSecondWhenEmpty>
<warningThreshold>0.2</warningThreshold>
<assignJobCheckInterval>1800</assignJobCheckInterval>
<maintenanceThresholdForJob>0.5</maintenanceThresholdForJob>
<allowedRaces>
<li>ArachnaeNode_Race_WeaponSmith</li>
</allowedRaces>
</li>
<li Class="CompProperties_Transporter">
<massCapacity>300</massCapacity>
<restEffectiveness>0.8</restEffectiveness>
<canChangeAssignedThingsAfterStarting>true</canChangeAssignedThingsAfterStarting>
<!-- <max1PerGroup>true</max1PerGroup> -->
</li>
<li Class="CompProperties_Launchable_TransportPod">
<skyfallerLeaving>ARA_DropPodLeaving</skyfallerLeaving>
<requiresFuelingPort>false</requiresFuelingPort>
<fixedLaunchDistanceMax>53</fixedLaunchDistanceMax> <!-- 80% of full transport pod range -->
</li>
</comps>
</ThingDef>
<ThingDef ParentName="ActiveDropPod">

View File

@@ -36,6 +36,17 @@
</costList>
<terrainAffordanceNeeded>ARA_Creep</terrainAffordanceNeeded>
<comps>
<li Class="ArachnaeSwarm.CompProperties_SwarmMaintenance">
<maxMaintenance>100</maxMaintenance>
<maintenanceDecayPerDay>15</maintenanceDecayPerDay>
<damagePerSecondWhenEmpty>2</damagePerSecondWhenEmpty>
<warningThreshold>0.2</warningThreshold>
<assignJobCheckInterval>1800</assignJobCheckInterval>
<maintenanceThresholdForJob>0.5</maintenanceThresholdForJob>
<allowedRaces>
<li>ArachnaeNode_Race_WeaponSmith</li>
</allowedRaces>
</li>
<li Class="CompProperties_Glower">
<overlightRadius>7.0</overlightRadius>
<glowRadius>14</glowRadius>
@@ -162,6 +173,18 @@
<constructionSkillPrerequisite>4</constructionSkillPrerequisite>
<terrainAffordanceNeeded>ARA_Creep</terrainAffordanceNeeded>
<comps>
<li Class="ArachnaeSwarm.CompProperties_SwarmMaintenance">
<maxMaintenance>100</maxMaintenance>
<maintenanceDecayPerDay>15</maintenanceDecayPerDay>
<damagePerSecondWhenEmpty>2</damagePerSecondWhenEmpty>
<warningThreshold>0.2</warningThreshold>
<assignJobCheckInterval>1800</assignJobCheckInterval>
<maintenanceThresholdForJob>0.5</maintenanceThresholdForJob>
<allowedRaces>
<li>ArachnaeNode_Race_WeaponSmith</li>
</allowedRaces>
</li>
<!-- 1. 作为消费者,自己也需要燃料 -->
<li Class="ArachnaeSwarm.CompProperties_RefuelableNutrition">
<fuelCapacity>5.0</fuelCapacity>

View File

@@ -98,11 +98,37 @@
<!-- <claimable>false</claimable> -->
<deconstructible>false</deconstructible>
<repairable>false</repairable>
<isTargetable>false</isTargetable>
<!-- <isTargetable>false</isTargetable> -->
<!-- <expandHomeArea>false</expandHomeArea> -->
<workTableRoomRole>ARA_Incubator_Room</workTableRoomRole>
</building>
<!-- 添加 ModExtension 配置 -->
<modExtensions>
<li Class="ArachnaeSwarm.OothecaIncubatorExtension">
<!-- 营养液检测半径 -->
<nutrientSolutionRadius>5</nutrientSolutionRadius>
<!-- 其他卵距离检测半径 -->
<nearbyOothecaRadius>5</nearbyOothecaRadius>
<!-- 是否检查同房间内的其他卵 -->
<checkSameRoomForOotheca>false</checkSameRoomForOotheca>
<!-- 营养液加成比例 -->
<!-- <nutrientSolutionBonusPerTile>0.015</nutrientSolutionBonusPerTile> -->
<!-- 附近其他卵的惩罚比例 -->
<!-- <nearbyOothecaPenaltyPerUnit>0.08</nearbyOothecaPenaltyPerUnit> -->
<!-- 幼虫搜索半径 -->
<!-- <larvaSearchRadius>30</larvaSearchRadius> -->
<!-- 是否需要在孵化间内才能正常工作 -->
<!-- <requiresIncubatorRoom>true</requiresIncubatorRoom> -->
<!-- 不在孵化间内的速度惩罚 -->
<!-- <speedPenaltyOutsideIncubator>0.7</speedPenaltyOutsideIncubator> -->
<!-- 质量因子房间检查 -->
<!-- <useRoomQualityFactor>true</useRoomQualityFactor> -->
<!-- 建筑血量影响质量 -->
<!-- <healthAffectsQuality>true</healthAffectsQuality> -->
</li>
</modExtensions>
<!-- ITab配置 -->
<inspectorTabs>
<li>ArachnaeSwarm.ITab_Ootheca_Incubation</li>
@@ -112,6 +138,17 @@
<li>ArachnaeSwarm.PlaceWorker_CustomRadius</li>
</placeWorkers>
<comps>
<li Class="ArachnaeSwarm.CompProperties_SwarmMaintenance">
<maxMaintenance>100</maxMaintenance>
<maintenanceDecayPerDay>15</maintenanceDecayPerDay>
<damagePerSecondWhenEmpty>2</damagePerSecondWhenEmpty>
<warningThreshold>0.2</warningThreshold>
<assignJobCheckInterval>1800</assignJobCheckInterval>
<maintenanceThresholdForJob>0.5</maintenanceThresholdForJob>
<allowedRaces>
<li>ArachnaeNode_Race_WeaponSmith</li>
</allowedRaces>
</li>
<li Class="ArachnaeSwarm.CompProperties_CustomRadius">
<radius>5</radius> <!-- 半径大小 -->
<color>(0.5, 1, 1)</color> <!-- 绿色圆圈 -->
@@ -144,7 +181,7 @@
<pawnKind>ArachnaeNode_Race_Fighter</pawnKind>
<daysRequired>2</daysRequired>
<buttonIconPath>UI/Buttons/IncubateUnitA</buttonIconPath>
<!-- <requiredResearch>ARA_Technology_1KYC</requiredResearch> -->
<requiredResearch>ARA_Technology_1KYC</requiredResearch>
</li>
<li>
<pawnKind>ArachnaeNode_Race_Myrmecocystus</pawnKind>
@@ -203,4 +240,125 @@
</li>
</comps>
</ThingDef>
<ThingDef ParentName="BuildingNaturalBase">
<defName>ARA_Equipment_Ootheca</defName>
<label>阿拉克涅茧</label>
<description>一个脆弱、易燃、黏滑的囊状物,是阿拉克涅女皇种所诞之卵,内含哺育一只新督虫所需的营养和遗传物质。</description>
<statBases>
<MarketValue>1000</MarketValue>
</statBases>
<thingClass>ArachnaeSwarm.Building_EquipmentOotheca</thingClass>
<category>Building</category>
<size>(1,1)</size>
<designationCategory>ARA_Buildings</designationCategory>
<graphicData>
<texPath>ArachnaeSwarm/Building/ARA_Cocoon</texPath>
<graphicClass>Graphic_Single</graphicClass>
<drawSize>(1.1,1.1)</drawSize>
<shadowData>
<volume>(0.7, 0.4, 0.7)</volume>
<offset>(0,0,-0.1)</offset>
</shadowData>
</graphicData>
<altitudeLayer>Building</altitudeLayer>
<passability>PassThroughOnly</passability>
<fillPercent>0.3</fillPercent>
<rotatable>false</rotatable>
<tickerType>Normal</tickerType>
<generateCommonality>0</generateCommonality>
<tradeability>None</tradeability>
<thingSetMakerTags Inherit="False" />
<terrainAffordanceNeeded>ARA_Incubator_Nutrient_Solution</terrainAffordanceNeeded>
<!-- 交互设置 -->
<interactionCellOffset>(0, 0, 1)</interactionCellOffset>
<hasInteractionCell>true</hasInteractionCell>
<statBases>
<Mass>10</Mass>
<MaxHitPoints>150</MaxHitPoints>
<Flammability>1</Flammability>
<Beauty>-6</Beauty>
</statBases>
<building>
<isInert>true</isInert>
<!-- <claimable>false</claimable> -->
<deconstructible>false</deconstructible>
<repairable>false</repairable>
<!-- <isTargetable>false</isTargetable> -->
<!-- <expandHomeArea>false</expandHomeArea> -->
<workTableRoomRole>ARA_Incubator_Room</workTableRoomRole>
</building>
<!-- 添加 ModExtension 配置 -->
<modExtensions>
<li Class="ArachnaeSwarm.OothecaIncubatorExtension">
<!-- 营养液检测半径 -->
<nutrientSolutionRadius>3</nutrientSolutionRadius>
<!-- 其他卵距离检测半径 -->
<nearbyOothecaRadius>3</nearbyOothecaRadius>
<!-- 是否检查同房间内的其他卵 -->
<checkSameRoomForOotheca>false</checkSameRoomForOotheca>
<!-- 营养液加成比例 -->
<nutrientSolutionBonusPerTile>0.03</nutrientSolutionBonusPerTile>
<!-- 附近其他卵的惩罚比例 -->
<!-- <nearbyOothecaPenaltyPerUnit>0.08</nearbyOothecaPenaltyPerUnit> -->
<!-- 幼虫搜索半径 -->
<!-- <larvaSearchRadius>30</larvaSearchRadius> -->
<!-- 是否需要在孵化间内才能正常工作 -->
<!-- <requiresIncubatorRoom>true</requiresIncubatorRoom> -->
<!-- 不在孵化间内的速度惩罚 -->
<!-- <speedPenaltyOutsideIncubator>0.7</speedPenaltyOutsideIncubator> -->
<!-- 质量因子房间检查 -->
<!-- <useRoomQualityFactor>true</useRoomQualityFactor> -->
<!-- 建筑血量影响质量 -->
<!-- <healthAffectsQuality>true</healthAffectsQuality> -->
</li>
</modExtensions>
<!-- ITab配置 -->
<inspectorTabs>
<li>ArachnaeSwarm.ITab_EquipmentOotheca_Incubation</li>
</inspectorTabs>
<placeWorkers>
<li>ArachnaeSwarm.PlaceWorker_CustomRadius</li>
</placeWorkers>
<comps>
<li Class="ArachnaeSwarm.CompProperties_SwarmMaintenance">
<maxMaintenance>100</maxMaintenance>
<maintenanceDecayPerDay>15</maintenanceDecayPerDay>
<damagePerSecondWhenEmpty>2</damagePerSecondWhenEmpty>
<warningThreshold>0.2</warningThreshold>
<assignJobCheckInterval>1800</assignJobCheckInterval>
<maintenanceThresholdForJob>0.5</maintenanceThresholdForJob>
<allowedRaces>
<li>ArachnaeNode_Race_WeaponSmith</li>
</allowedRaces>
</li>
<li Class="ArachnaeSwarm.CompProperties_CustomRadius">
<radius>3</radius> <!-- 半径大小 -->
<color>(0.5, 1, 1)</color> <!-- 绿色圆圈 -->
<radiusOffset>0</radiusOffset> <!-- 半径偏移 -->
<showInGUI>true</showInGUI>
<label>吸收半径</label>
<description>这个卵在孵化过程中的吸收半径,确保这些地格中铺满阿拉克涅营养液,并且没有其他的卵,以获得最佳的孵化速度和孵化质量。</description>
<defaultVisible>false</defaultVisible>
</li>
<li Class="ArachnaeSwarm.CompProperties_EquipmentIncubatorData">
<!-- autoScanThingDefs默认为true会自动扫描所有ThingDef -->
</li>
<li Class="CompProperties_SpawnEffecterOnDestroy">
<effect>CocoonDestroyed</effect>
</li>
<li Class="CompProperties_Glower">
<glowRadius>6</glowRadius>
<glowColor>(113,141,117,0)</glowColor>
</li>
<li Class="ArachnaeSwarm.CompProperties_TemperatureRuinableDamage">
<minSafeTemperature>-10</minSafeTemperature>
<maxSafeTemperature>20</maxSafeTemperature>
<damagePerTick>0.015</damagePerTick>
</li>
</comps>
</ThingDef>
</Defs>

View File

@@ -63,6 +63,18 @@
<constructionSkillPrerequisite>4</constructionSkillPrerequisite>
<terrainAffordanceNeeded>ARA_Creep</terrainAffordanceNeeded>
<comps>
<li Class="ArachnaeSwarm.CompProperties_SwarmMaintenance">
<maxMaintenance>100</maxMaintenance>
<maintenanceDecayPerDay>15</maintenanceDecayPerDay>
<damagePerSecondWhenEmpty>2</damagePerSecondWhenEmpty>
<warningThreshold>0.2</warningThreshold>
<assignJobCheckInterval>1800</assignJobCheckInterval>
<maintenanceThresholdForJob>0.5</maintenanceThresholdForJob>
<allowedRaces>
<li>ArachnaeNode_Race_WeaponSmith</li>
</allowedRaces>
</li>
<!-- 标准的 CompRefuelable -->
<li Class="ArachnaeSwarm.CompProperties_ProductStorage">
<fuelLabel>精华素</fuelLabel>

View File

@@ -288,6 +288,17 @@
<tickerType>Normal</tickerType>
<terrainAffordanceNeeded>Heavy</terrainAffordanceNeeded>
<comps>
<li Class="ArachnaeSwarm.CompProperties_SwarmMaintenance">
<maxMaintenance>100</maxMaintenance>
<maintenanceDecayPerDay>15</maintenanceDecayPerDay>
<damagePerSecondWhenEmpty>2</damagePerSecondWhenEmpty>
<warningThreshold>0.2</warningThreshold>
<assignJobCheckInterval>1800</assignJobCheckInterval>
<maintenanceThresholdForJob>0.5</maintenanceThresholdForJob>
<allowedRaces>
<li>ArachnaeNode_Race_WeaponSmith</li>
</allowedRaces>
</li>
<li Class="CompProperties_Forbiddable" />
<li Class="ArachnaeSwarm.CompProperties_RefuelableNutrition_WithKey">
<saveKeysPrefix>nutrition</saveKeysPrefix>
@@ -305,10 +316,6 @@
<consumeFuelOnlyWhenUsed>true</consumeFuelOnlyWhenUsed>
</li>
<li Class="ArachnaeSwarm.CompProperties_ForceTargetable" />
<li Class="ArachnaeSwarm.CompProperties_DelayedTerrainSpawn">
<terrainToSpawn>ARA_InsectCreep</terrainToSpawn>
<spawnRadius>6</spawnRadius>
</li>
<li Class="CompProperties_AffectedByFacilities">
<linkableFacilities>
<li>ARA_NutrientNetworkTower</li>
@@ -430,6 +437,17 @@
<tickerType>Normal</tickerType>
<terrainAffordanceNeeded>Heavy</terrainAffordanceNeeded>
<comps>
<li Class="ArachnaeSwarm.CompProperties_SwarmMaintenance">
<maxMaintenance>100</maxMaintenance>
<maintenanceDecayPerDay>15</maintenanceDecayPerDay>
<damagePerSecondWhenEmpty>2</damagePerSecondWhenEmpty>
<warningThreshold>0.2</warningThreshold>
<assignJobCheckInterval>1800</assignJobCheckInterval>
<maintenanceThresholdForJob>0.5</maintenanceThresholdForJob>
<allowedRaces>
<li>ArachnaeNode_Race_WeaponSmith</li>
</allowedRaces>
</li>
<li Class="CompProperties_Forbiddable" />
<li Class="ArachnaeSwarm.CompProperties_RefuelableNutrition_WithKey">
<saveKeysPrefix>nutrition</saveKeysPrefix>
@@ -446,10 +464,6 @@
<consumeFuelOnlyWhenUsed>true</consumeFuelOnlyWhenUsed>
</li>
<li Class="ArachnaeSwarm.CompProperties_ForceTargetable" />
<li Class="ArachnaeSwarm.CompProperties_DelayedTerrainSpawn">
<terrainToSpawn>ARA_InsectCreep</terrainToSpawn>
<spawnRadius>6</spawnRadius>
</li>
<li Class="CompProperties_AffectedByFacilities">
<linkableFacilities>
<li>ARA_NutrientNetworkTower</li>
@@ -595,6 +609,17 @@
</statBases>
<tickerType>Normal</tickerType>
<comps>
<li Class="ArachnaeSwarm.CompProperties_SwarmMaintenance">
<maxMaintenance>100</maxMaintenance>
<maintenanceDecayPerDay>15</maintenanceDecayPerDay>
<damagePerSecondWhenEmpty>2</damagePerSecondWhenEmpty>
<warningThreshold>0.2</warningThreshold>
<assignJobCheckInterval>1800</assignJobCheckInterval>
<maintenanceThresholdForJob>0.5</maintenanceThresholdForJob>
<allowedRaces>
<li>ArachnaeNode_Race_WeaponSmith</li>
</allowedRaces>
</li>
<li Class="ArachnaeSwarm.CompProperties_RefuelableNutrition_WithKey">
<saveKeysPrefix>nutrition</saveKeysPrefix>
<fuelCapacity>50.0</fuelCapacity>
@@ -611,10 +636,6 @@
<li Class="CompProperties_Forbiddable" />
<li Class="CompProperties_Breakdownable" />
<li Class="ArachnaeSwarm.CompProperties_ForceTargetable" />
<li Class="ArachnaeSwarm.CompProperties_DelayedTerrainSpawn">
<terrainToSpawn>ARA_InsectCreep</terrainToSpawn>
<spawnRadius>8</spawnRadius>
</li>
<li Class="CompProperties_AffectedByFacilities">
<linkableFacilities>
<li>ARA_NutrientNetworkTower</li>

View File

@@ -39,6 +39,17 @@
<ARA_Carapace>200</ARA_Carapace>
</costList>
<comps>
<li Class="ArachnaeSwarm.CompProperties_SwarmMaintenance">
<maxMaintenance>100</maxMaintenance>
<maintenanceDecayPerDay>15</maintenanceDecayPerDay>
<damagePerSecondWhenEmpty>2</damagePerSecondWhenEmpty>
<warningThreshold>0.2</warningThreshold>
<assignJobCheckInterval>1800</assignJobCheckInterval>
<maintenanceThresholdForJob>0.5</maintenanceThresholdForJob>
<allowedRaces>
<li>ArachnaeNode_Race_WeaponSmith</li>
</allowedRaces>
</li>
<li Class="CompProperties_Glower">
<overlightRadius>7.0</overlightRadius>
<glowRadius>14</glowRadius>

View File

@@ -896,6 +896,35 @@
<insertTag>Humanlike_PreMain</insertTag>
</li>
<!-- 蜜罐特殊思考:喂食 -->
<li Class="ThinkNode_ConditionalPawnKind">
<pawnKind>ArachnaeNode_Race_Myrmecocystus</pawnKind>
<subNodes>
<li Class="ArachnaeSwarm.ThinkNode_JobGiver_FeedWithHoney" />
</subNodes>
</li>
<li Class="ThinkNode_ConditionalPawnKind">
<pawnKind>ArachnaeNode_Race_Myrmecocystus</pawnKind>
<subNodes>
<li Class="ArachnaeSwarm.ThinkNode_JobGiver_ExtractHoney" />
</subNodes>
</li>
<li Class="ThinkNode_ConditionalHasHediff">
<hediff>ARA_HiveMindMaster</hediff>
<severityRange>0~5</severityRange>
<subNodes>
<li Class="ArachnaeSwarm.ThinkNode_JobGiver_StripChitin" />
</subNodes>
</li>
<li Class="ThinkNode_ConditionalHasHediff">
<hediff>ARA_HiveMindDrone</hediff>
<severityRange>0~5</severityRange>
<subNodes>
<li Class="ArachnaeSwarm.ThinkNode_JobGiver_StripChitin" />
</subNodes>
</li>
<!-- Main colonist behavior core -->
<li Class="ThinkNode_ConditionalColonist">
<subNodes>

View File

@@ -8,7 +8,7 @@
<Target>目标</Target>
<SpeedMultiplier>速度乘数</SpeedMultiplier>
<QualityMultiplier>质量乘数</QualityMultiplier>
<LarvaIsOperating>幼虫种进入中</LarvaIsOperating>
<LarvaIsOperating>幼虫种进入中</LarvaIsOperating>
<SRemaining>剩余秒数</SRemaining>
<LarvaIsOnTheWay>幼虫种正在路上</LarvaIsOnTheWay>
<TimeRemaining>剩余时间</TimeRemaining>
@@ -97,4 +97,76 @@
<!-- Research Related -->
<ResearchRequired>需要研究</ResearchRequired>
<!-- 孵化系统翻译 -->
<ARA_OothecaIncubator.SpeedFactors>速度因子</ARA_OothecaIncubator.SpeedFactors>
<ARA_OothecaIncubator.InIncubatorRoom>✓ 位于孵化间内</ARA_OothecaIncubator.InIncubatorRoom>
<ARA_OothecaIncubator.NotInIncubatorRoom>✗ 不在孵化间内</ARA_OothecaIncubator.NotInIncubatorRoom>
<ARA_OothecaIncubator.NutrientSolutions>营养液: {0} (+{1}%)</ARA_OothecaIncubator.NutrientSolutions>
<ARA_OothecaIncubator.NoNutrientSolutionsNearby>无附近营养液</ARA_OothecaIncubator.NoNutrientSolutionsNearby>
<ARA_OothecaIncubator.NutrientDetectionRadius>营养液检测半径: {0}格</ARA_OothecaIncubator.NutrientDetectionRadius>
<ARA_OothecaIncubator.TotalSpeedMultiplier>总速度乘数: {0}</ARA_OothecaIncubator.TotalSpeedMultiplier>
<ARA_OothecaIncubator.QualityFactors>质量因子</ARA_OothecaIncubator.QualityFactors>
<ARA_OothecaIncubator.BuildingHealth>建筑健康度: {0}</ARA_OothecaIncubator.BuildingHealth>
<ARA_OothecaIncubator.RoomFactorNormal>房间因子: 正常</ARA_OothecaIncubator.RoomFactorNormal>
<ARA_OothecaIncubator.RoomFactorModified>房间因子: </ARA_OothecaIncubator.RoomFactorModified>
<ARA_OothecaIncubator.NearbyOothecas>附近卵: {0} (-{1}%)</ARA_OothecaIncubator.NearbyOothecas>
<ARA_OothecaIncubator.NoNearbyOothecas>无附近卵</ARA_OothecaIncubator.NoNearbyOothecas>
<ARA_OothecaIncubator.OothecaDetectionRadius>卵检测半径: {0}格</ARA_OothecaIncubator.OothecaDetectionRadius>
<ARA_OothecaIncubator.TotalQualityMultiplier>总质量乘数: {0}</ARA_OothecaIncubator.TotalQualityMultiplier>
<ARA_OothecaIncubator.CallLarvaTitle>呼叫幼虫</ARA_OothecaIncubator.CallLarvaTitle>
<ARA_OothecaIncubator.LarvaWillCome>幼虫将前往卵荚进行孵化</ARA_OothecaIncubator.LarvaWillCome>
<ARA_OothecaIncubator.LarvaSearchRadius>幼虫搜索半径: {0}格</ARA_OothecaIncubator.LarvaSearchRadius>
<ARA_OothecaIncubator.AlreadyIncubating>已经在孵化中</ARA_OothecaIncubator.AlreadyIncubating>
<ARA_OothecaIncubator.CancelFirst>请先取消当前孵化</ARA_OothecaIncubator.CancelFirst>
<ARA_OothecaIncubator.LarvaAlreadyOnWay>幼虫已在途中</ARA_OothecaIncubator.LarvaAlreadyOnWay>
<ARA_OothecaIncubator.NoLarvaeFound>未找到可用幼虫</ARA_OothecaIncubator.NoLarvaeFound>
<ARA_OothecaIncubator.LarvaMustBeRace>必须是幼虫种</ARA_OothecaIncubator.LarvaMustBeRace>
<ARA_OothecaIncubator.LarvaCalled>幼虫已呼叫</ARA_OothecaIncubator.LarvaCalled>
<ARA_OothecaIncubator.ArriveShortly>即将到达</ARA_OothecaIncubator.ArriveShortly>
<ARA_OothecaIncubator.LarvaArrived>幼虫已到达</ARA_OothecaIncubator.LarvaArrived>
<ARA_OothecaIncubator.ActivatingOotheca>正在激活卵荚</ARA_OothecaIncubator.ActivatingOotheca>
<ARA_OothecaIncubator.IncubationStarted>开始孵化</ARA_OothecaIncubator.IncubationStarted>
<ARA_OothecaIncubator.ProcessWillComplete>过程将在</ARA_OothecaIncubator.ProcessWillComplete>
<ARA_OothecaIncubator.DaysBaseTime>天内完成(基础时间)</ARA_OothecaIncubator.DaysBaseTime>
<ARA_OothecaIncubator.IncubationCancelled>孵化已取消</ARA_OothecaIncubator.IncubationCancelled>
<ARA_OothecaIncubator.ContentsLost>内容物已丢失</ARA_OothecaIncubator.ContentsLost>
<ARA_OothecaIncubator.IncubationComplete>孵化完成</ARA_OothecaIncubator.IncubationComplete>
<ARA_OothecaIncubator.HasEmergedWith>已出现,质量为</ARA_OothecaIncubator.HasEmergedWith>
<ARA_OothecaIncubator.QualityExcellent>优秀</ARA_OothecaIncubator.QualityExcellent>
<ARA_OothecaIncubator.QualityGood>良好</ARA_OothecaIncubator.QualityGood>
<ARA_OothecaIncubator.QualityAverage>一般</ARA_OothecaIncubator.QualityAverage>
<ARA_OothecaIncubator.QualityPoor>较差</ARA_OothecaIncubator.QualityPoor>
<ARA_OothecaIncubator.QualityVeryPoor>很差</ARA_OothecaIncubator.QualityVeryPoor>
<ARA_OothecaIncubator.Quality>质量</ARA_OothecaIncubator.Quality>
<ARA_OothecaIncubator.Incubating>正在孵化</ARA_OothecaIncubator.Incubating>
<ARA_OothecaIncubator.Progress>进度</ARA_OothecaIncubator.Progress>
<ARA_OothecaIncubator.TimeRemaining>剩余时间</ARA_OothecaIncubator.TimeRemaining>
<ARA_OothecaIncubator.Days></ARA_OothecaIncubator.Days>
<ARA_OothecaIncubator.Hours>小时</ARA_OothecaIncubator.Hours>
<ARA_OothecaIncubator.Speed>速度</ARA_OothecaIncubator.Speed>
<ARA_OothecaIncubator.Quality>质量</ARA_OothecaIncubator.Quality>
<ARA_OothecaIncubator.LarvaOperating>幼虫正在操作</ARA_OothecaIncubator.LarvaOperating>
<ARA_OothecaIncubator.SRemaining>秒剩余</ARA_OothecaIncubator.SRemaining>
<ARA_OothecaIncubator.LarvaOnWay>幼虫在途中</ARA_OothecaIncubator.LarvaOnWay>
<ARA_OothecaIncubator.Target>目标</ARA_OothecaIncubator.Target>
<ARA_OothecaIncubator.SpeedMultiplier>速度乘数</ARA_OothecaIncubator.SpeedMultiplier>
<ARA_OothecaIncubator.QualityMultiplier>质量乘数</ARA_OothecaIncubator.QualityMultiplier>
<ARA_OothecaIncubator.CallLarva>呼叫幼虫</ARA_OothecaIncubator.CallLarva>
<ARA_OothecaIncubator.CancelIncubation>取消孵化</ARA_OothecaIncubator.CancelIncubation>
<ARA_OothecaIncubator.CancelIncubationDesc>取消当前孵化过程,卵荚内容物将丢失</ARA_OothecaIncubator.CancelIncubationDesc>
<ARA_OothecaIncubator.IncubateLabel>孵化: {0}</ARA_OothecaIncubator.IncubateLabel>
<ARA_OothecaIncubator.ButtonDesc>选择要孵化的生物类型。幼虫将前来激活卵荚。</ARA_OothecaIncubator.ButtonDesc>
<ARA_OothecaIncubator.ButtonLabel>当前选择: {0}</ARA_OothecaIncubator.ButtonLabel>
<ARA_OothecaIncubator.IncubationTime>孵化时间: {0}天</ARA_OothecaIncubator.IncubationTime>
<ARA_OothecaIncubator.MenuTitle>选择孵化目标</ARA_OothecaIncubator.MenuTitle>
<ARA_OothecaIncubator.ResearchRequired>需要研究: </ARA_OothecaIncubator.ResearchRequired>
<ARA_OothecaIncubator.TargetSwitched>孵化目标已切换为: {0}</ARA_OothecaIncubator.TargetSwitched>
</LanguageData>

View File

@@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8"?>
<LanguageData>
<!-- 装备孵化系统翻译 -->
<ARA_EquipmentIncubator.SpeedFactors>速度因子</ARA_EquipmentIncubator.SpeedFactors>
<ARA_EquipmentIncubator.InIncubatorRoom>✓ 位于孵化间内</ARA_EquipmentIncubator.InIncubatorRoom>
<ARA_EquipmentIncubator.NotInIncubatorRoom>✗ 不在孵化间内</ARA_EquipmentIncubator.NotInIncubatorRoom>
<ARA_EquipmentIncubator.NutrientSolutions>营养液: {0} (+{1}%)</ARA_EquipmentIncubator.NutrientSolutions>
<ARA_EquipmentIncubator.NoNutrientSolutionsNearby>无附近营养液</ARA_EquipmentIncubator.NoNutrientSolutionsNearby>
<ARA_EquipmentIncubator.NutrientDetectionRadius>营养液检测半径: {0}格</ARA_EquipmentIncubator.NutrientDetectionRadius>
<ARA_EquipmentIncubator.TotalSpeedMultiplier>总速度乘数: {0}</ARA_EquipmentIncubator.TotalSpeedMultiplier>
<ARA_EquipmentIncubator.QualityFactors>质量因子</ARA_EquipmentIncubator.QualityFactors>
<ARA_EquipmentIncubator.BuildingHealth>建筑健康度: {0}</ARA_EquipmentIncubator.BuildingHealth>
<ARA_EquipmentIncubator.RoomFactorNormal>房间因子: 正常</ARA_EquipmentIncubator.RoomFactorNormal>
<ARA_EquipmentIncubator.RoomFactorModified>房间因子: </ARA_EquipmentIncubator.RoomFactorModified>
<ARA_EquipmentIncubator.NearbyOothecas>附近卵: {0} (-{1}%)</ARA_EquipmentIncubator.NearbyOothecas>
<ARA_EquipmentIncubator.NoNearbyOothecas>无附近卵</ARA_EquipmentIncubator.NoNearbyOothecas>
<ARA_EquipmentIncubator.OothecaDetectionRadius>卵检测半径: {0}格</ARA_EquipmentIncubator.OothecaDetectionRadius>
<ARA_EquipmentIncubator.TotalQualityMultiplier>总质量乘数: {0}</ARA_EquipmentIncubator.TotalQualityMultiplier>
<ARA_EquipmentIncubator.CallLarvaTitle>呼叫幼虫</ARA_EquipmentIncubator.CallLarvaTitle>
<ARA_EquipmentIncubator.LarvaWillCome>幼虫将前往卵荚生产装备</ARA_EquipmentIncubator.LarvaWillCome>
<ARA_EquipmentIncubator.LarvaSearchRadius>幼虫搜索半径: {0}格</ARA_EquipmentIncubator.LarvaSearchRadius>
<ARA_EquipmentIncubator.AlreadyIncubating>已经在生产中</ARA_EquipmentIncubator.AlreadyIncubating>
<ARA_EquipmentIncubator.CancelFirst>请先取消当前生产</ARA_EquipmentIncubator.CancelFirst>
<ARA_EquipmentIncubator.LarvaAlreadyOnWay>幼虫已在途中</ARA_EquipmentIncubator.LarvaAlreadyOnWay>
<ARA_EquipmentIncubator.NoLarvaeFound>未找到可用幼虫</ARA_EquipmentIncubator.NoLarvaeFound>
<ARA_EquipmentIncubator.LarvaMustBeRace>幼虫必须是ArachnaeBase_Race_Larva种族</ARA_EquipmentIncubator.LarvaMustBeRace>
<ARA_EquipmentIncubator.LarvaCalled>幼虫已呼叫</ARA_EquipmentIncubator.LarvaCalled>
<ARA_EquipmentIncubator.ArriveShortly>即将到达</ARA_EquipmentIncubator.ArriveShortly>
<ARA_EquipmentIncubator.LarvaArrived>幼虫已到达</ARA_EquipmentIncubator.LarvaArrived>
<ARA_EquipmentIncubator.ActivatingOotheca>正在激活装备卵荚</ARA_EquipmentIncubator.ActivatingOotheca>
<ARA_EquipmentIncubator.IncubationStarted>开始生产</ARA_EquipmentIncubator.IncubationStarted>
<ARA_EquipmentIncubator.ProcessWillComplete>过程将在</ARA_EquipmentIncubator.ProcessWillComplete>
<ARA_EquipmentIncubator.DaysBaseTime>天内完成(基础时间)</ARA_EquipmentIncubator.DaysBaseTime>
<ARA_EquipmentIncubator.IncubationCancelled>生产已取消</ARA_EquipmentIncubator.IncubationCancelled>
<ARA_EquipmentIncubator.ContentsLost>内容物已丢失</ARA_EquipmentIncubator.ContentsLost>
<ARA_EquipmentIncubator.IncubationComplete>生产完成</ARA_EquipmentIncubator.IncubationComplete>
<ARA_EquipmentIncubator.HasEmergedWith>已生产出,质量为</ARA_EquipmentIncubator.HasEmergedWith>
<ARA_EquipmentIncubator.QualityExcellent>传奇</ARA_EquipmentIncubator.QualityExcellent>
<ARA_EquipmentIncubator.QualityGood>杰作</ARA_EquipmentIncubator.QualityGood>
<ARA_EquipmentIncubator.QualityAverage>精良</ARA_EquipmentIncubator.QualityAverage>
<ARA_EquipmentIncubator.QualityPoor>良好</ARA_EquipmentIncubator.QualityPoor>
<ARA_EquipmentIncubator.QualityVeryPoor>一般</ARA_EquipmentIncubator.QualityVeryPoor>
<ARA_EquipmentIncubator.Quality>质量</ARA_EquipmentIncubator.Quality>
<ARA_EquipmentIncubator.Incubating>正在生产</ARA_EquipmentIncubator.Incubating>
<ARA_EquipmentIncubator.Progress>进度</ARA_EquipmentIncubator.Progress>
<ARA_EquipmentIncubator.TimeRemaining>剩余时间</ARA_EquipmentIncubator.TimeRemaining>
<ARA_EquipmentIncubator.Days></ARA_EquipmentIncubator.Days>
<ARA_EquipmentIncubator.Hours>小时</ARA_EquipmentIncubator.Hours>
<ARA_EquipmentIncubator.Speed>速度</ARA_EquipmentIncubator.Speed>
<ARA_EquipmentIncubator.Quality>质量</ARA_EquipmentIncubator.Quality>
<ARA_EquipmentIncubator.LarvaOperating>幼虫正在操作</ARA_EquipmentIncubator.LarvaOperating>
<ARA_EquipmentIncubator.SRemaining>秒剩余</ARA_EquipmentIncubator.SRemaining>
<ARA_EquipmentIncubator.LarvaOnWay>幼虫在途中</ARA_EquipmentIncubator.LarvaOnWay>
<ARA_EquipmentIncubator.Target>目标</ARA_EquipmentIncubator.Target>
<ARA_EquipmentIncubator.SpeedMultiplier>速度乘数</ARA_EquipmentIncubator.SpeedMultiplier>
<ARA_EquipmentIncubator.QualityMultiplier>质量乘数</ARA_EquipmentIncubator.QualityMultiplier>
<ARA_EquipmentIncubator.CallLarva>呼叫幼虫</ARA_EquipmentIncubator.CallLarva>
<ARA_EquipmentIncubator.CancelIncubation>取消生产</ARA_EquipmentIncubator.CancelIncubation>
<ARA_EquipmentIncubator.CancelIncubationDesc>取消当前生产过程,卵荚内容物将丢失</ARA_EquipmentIncubator.CancelIncubationDesc>
<ARA_EquipmentIncubator.IncubateLabel>生产: {0}</ARA_EquipmentIncubator.IncubateLabel>
<ARA_EquipmentIncubator.ButtonDesc>选择要生产的装备类型。幼虫将前来激活装备卵荚。</ARA_EquipmentIncubator.ButtonDesc>
<ARA_EquipmentIncubator.ButtonLabel>当前选择: {0}</ARA_EquipmentIncubator.ButtonLabel>
<ARA_EquipmentIncubator.IncubationTime>生产时间: {0}天</ARA_EquipmentIncubator.IncubationTime>
<ARA_EquipmentIncubator.MenuTitle>选择生产目标</ARA_EquipmentIncubator.MenuTitle>
<ARA_EquipmentIncubator.ResearchRequired>需要研究: </ARA_EquipmentIncubator.ResearchRequired>
<ARA_EquipmentIncubator.TargetSwitched>生产目标已切换为: {0}</ARA_EquipmentIncubator.TargetSwitched>
<ARA_EquipmentIncubator.IncubationTab>装备生产</ARA_EquipmentIncubator.IncubationTab>
<ARA_EquipmentIncubator.NotAnEquipmentOotheca>这不是装备卵荚</ARA_EquipmentIncubator.NotAnEquipmentOotheca>
<ARA_EquipmentIncubator.IncubationProgress>生产进度</ARA_EquipmentIncubator.IncubationProgress>
<ARA_EquipmentIncubator.IncubationProgressLabel>生产进度</ARA_EquipmentIncubator.IncubationProgressLabel>
<ARA_EquipmentIncubator.QualityProgress>质量进度</ARA_EquipmentIncubator.QualityProgress>
<ARA_EquipmentIncubator.LarvaIsActivatingOotheca>幼虫正在激活卵荚</ARA_EquipmentIncubator.LarvaIsActivatingOotheca>
<ARA_EquipmentIncubator.SecondsRemaining>秒剩余</ARA_EquipmentIncubator.SecondsRemaining>
<ARA_EquipmentIncubator.LarvaIsOnTheWay>幼虫在途中</ARA_EquipmentIncubator.LarvaIsOnTheWay>
<ARA_EquipmentIncubator.ReadyToIncubate>准备生产</ARA_EquipmentIncubator.ReadyToIncubate>
<ARA_EquipmentIncubator.NoIncubationTargetSelected>未选择生产目标</ARA_EquipmentIncubator.NoIncubationTargetSelected>
<ARA_EquipmentIncubator.IncubationTarget>生产目标:{0}</ARA_EquipmentIncubator.IncubationTarget>
<ARA_EquipmentIncubator.Requires>需要</ARA_EquipmentIncubator.Requires>
<ARA_EquipmentIncubator.NoDescription>无描述</ARA_EquipmentIncubator.NoDescription>
<ARA_EquipmentIncubator.ResearchCompleted>研究: {0} (已完成)</ARA_EquipmentIncubator.ResearchCompleted>
<ARA_EquipmentIncubator.ResearchRequired>研究: {0} (需要)</ARA_EquipmentIncubator.ResearchRequired>
</LanguageData>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<LanguageData>
<!-- Need_HoneyProduction.cs 中的翻译键 -->
<ARA_HoneyProduction.CurrentLevel>当前储量</ARA_HoneyProduction.CurrentLevel>
<ARA_HoneyProduction.Category>储量等级</ARA_HoneyProduction.Category>
<ARA_HoneyProduction.Efficiency>生产效率:(饮食消耗速率的)</ARA_HoneyProduction.Efficiency>
<ARA_HoneyProduction.FoodDrainRate>食物消耗速率</ARA_HoneyProduction.FoodDrainRate>
<ARA_HoneyProduction.HoneyGainRate>蜜罐增长速率</ARA_HoneyProduction.HoneyGainRate>
<ARA_HoneyProduction.FullWarning>警告:蜜罐已满,停止生产!</ARA_HoneyProduction.FullWarning>
<ARA_HoneyProduction.Full>满仓</ARA_HoneyProduction.Full>
<ARA_HoneyProduction.High>高存量</ARA_HoneyProduction.High>
<ARA_HoneyProduction.Medium>中存量</ARA_HoneyProduction.Medium>
<ARA_HoneyProduction.Low>低存量</ARA_HoneyProduction.Low>
<ARA_HoneyProduction.Empty>空仓</ARA_HoneyProduction.Empty>
<!-- JobDriver_ExtractHoney.cs 中的翻译键 -->
<ARA_ExtractHoney_Message>挤出了 {0} 个虫蜜</ARA_ExtractHoney_Message>
<!-- JobDriver_FeedWithHoney.cs 中的翻译键 -->
<ARA_Message_HoneyFed>{0} 用虫蜜喂养了 {1}</ARA_Message_HoneyFed>
<!-- 工作相关的翻译键 -->
<ARA_ExtractHoney>挤出虫蜜</ARA_ExtractHoney>
<ARA_FeedWithHoney>用虫蜜喂养</ARA_FeedWithHoney>
<!-- 通用描述 -->
<ARA_HoneyProduction>蜜罐生产</ARA_HoneyProduction>
<ARA_InsectJelly>虫蜜</ARA_InsectJelly>
</LanguageData>

View File

@@ -18,7 +18,7 @@
<ARA_NeedSpecificArachnaeToStartIncubation>未孵化,需要 {0} 交互\n\n在生产完成时剩余的营养将重新转变为物品。</ARA_NeedSpecificArachnaeToStartIncubation>
<ARA_AnyArachnaeRace>任何阿拉克涅虫族</ARA_AnyArachnaeRace>
<ARA_ItemsAvailable>{0} 个物品可用</ARA_ItemsAvailable>
<QualityProgress>物品品质</QualityProgress>
<QualityProgress>孵化品质</QualityProgress>
<!-- Added for CompQueuedPawnSpawner -->
<NutritionNeeded>所需营养</NutritionNeeded>
@@ -31,4 +31,14 @@
<ProductionSlots>生产槽位: {0} / {1}</ProductionSlots>
<ProductionQueue>等待队列: {0}</ProductionQueue>
<ProductionSpeed>生产速度</ProductionSpeed>
<ARA_Chitin.ShellParts>甲壳部位数量</ARA_Chitin.ShellParts>
<ARA_Chitin.GrowthRate>生长速率</ARA_Chitin.GrowthRate>
<ARA_ChitinStripping_Toggle>甲壳剥离</ARA_ChitinStripping_Toggle>
<ARA_ChitinStripping_ToggleDesc>切换是否允许此单位自动剥离甲壳。当甲壳过厚,单位会自动剥离甲壳以产生甲壳素。</ARA_ChitinStripping_ToggleDesc>
<ARA_StripChitin_Message>剥离了{0}个甲壳</ARA_StripChitin_Message>
<ARA_ChitinStripping_Disabled>甲壳剥离功能已禁用</ARA_ChitinStripping_Disabled>
<ARA_ChitinStripping_Cooldown>冷却中:{0}秒后可以再次剥离</ARA_ChitinStripping_Cooldown>
<ARA_ChitinStripping_Threshold>甲壳存量未达到阈值({0}%</ARA_ChitinStripping_Threshold>
<ARA_ChitinStripping_Ready>可以开始剥离甲壳</ARA_ChitinStripping_Ready>
</LanguageData>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<LanguageData>
<!-- 维护系统翻译 -->
<ARA_SwarmMaintenance.MaintenanceLevel>维护度: {0}/{1} ({2})</ARA_SwarmMaintenance.MaintenanceLevel>
<ARA_SwarmMaintenance.Depleted>维护度耗尽!建筑正在损坏!</ARA_SwarmMaintenance.Depleted>
<ARA_SwarmMaintenance.Critical>维护度严重不足!</ARA_SwarmMaintenance.Critical>
<ARA_SwarmMaintenance.NeedsMaintenance>需要维护</ARA_SwarmMaintenance.NeedsMaintenance>
<ARA_SwarmMaintenance.DailyDecay>每日维护度递减: {0}</ARA_SwarmMaintenance.DailyDecay>
<ARA_SwarmMaintenance.SeekingMaintainer>正在寻找空闲的维护者...</ARA_SwarmMaintenance.SeekingMaintainer>
<ARA_SwarmMaintenance.NoMaintainerFound>找不到符合条件的空闲维护者</ARA_SwarmMaintenance.NoMaintainerFound>
<ARA_SwarmMaintenance.AllowedRaces>允许维护的种族:</ARA_SwarmMaintenance.AllowedRaces>
<ARA_SwarmMaintenance.NoRacesSpecified>未指定可维护的种族</ARA_SwarmMaintenance.NoRacesSpecified>
</LanguageData>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<LanguageData>
<!-- 温度损坏系统翻译 -->
<ARA_TemperatureRuinable.SafeTemperatureRange>安全温度范围: {0} - {1}</ARA_TemperatureRuinable.SafeTemperatureRange>
<ARA_TemperatureRuinable.CurrentTemperature>当前温度: {0}</ARA_TemperatureRuinable.CurrentTemperature>
<ARA_TemperatureRuinable.DamageProgress>损坏进度: {0}</ARA_TemperatureRuinable.DamageProgress>
<ARA_TemperatureRuinable.TakingDamage>正在受到温度伤害</ARA_TemperatureRuinable.TakingDamage>
<ARA_TemperatureRuinable.NoDamage>未受到温度影响</ARA_TemperatureRuinable.NoDamage>
<ARA_TemperatureRuinable.TooCold>过冷</ARA_TemperatureRuinable.TooCold>
<ARA_TemperatureRuinable.TooHot>过热</ARA_TemperatureRuinable.TooHot>
<ARA_TemperatureRuinable.DamageLetterLabel>{0} 受到温度伤害</ARA_TemperatureRuinable.DamageLetterLabel>
<ARA_TemperatureRuinable.DamageLetterText>{0} 因{1}正在受到温度伤害。\n\n当前温度: {2}\n安全温度范围: {3} - {4}\n\n建议尽快调整温度至安全范围以避免结构损坏。</ARA_TemperatureRuinable.DamageLetterText>
</LanguageData>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@@ -1,58 +1,58 @@
{
"Version": 1,
"WorkspaceRootPath": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\",
"WorkspaceRootPath": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\",
"Documents": [
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|d:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\buildings\\building_ootheca\\building_ootheca.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|e:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\buildings\\building_equipmentootheca\\building_equipmentootheca.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:buildings\\building_equipmentootheca\\building_equipmentootheca.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|e:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\buildings\\building_equipmentootheca\\compproperties_equipmentincubatordata.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:buildings\\building_equipmentootheca\\compproperties_equipmentincubatordata.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|e:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\buildings\\building_ootheca\\oothecaincubatorextension.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:buildings\\building_ootheca\\oothecaincubatorextension.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|e:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\ara_hediffdefof.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:ara_hediffdefof.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|e:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\thing_comps\\ara_compextraincubationinfo\\compproperties_extraincubationinfo.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:thing_comps\\ara_compextraincubationinfo\\compproperties_extraincubationinfo.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|e:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\thing_comps\\ara_compextraincubationinfo\\compextraincubationinfo.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:thing_comps\\ara_compextraincubationinfo\\compextraincubationinfo.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|e:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\buildings\\building_ootheca\\building_ootheca.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:buildings\\building_ootheca\\building_ootheca.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|d:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\placeworker\\compproperties_customradius.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:placeworker\\compproperties_customradius.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|e:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\building_comps\\ara_swarmmaintenance\\comp_swarmmaintenance.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:building_comps\\ara_swarmmaintenance\\comp_swarmmaintenance.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\flyover\\ara_groundstrafing\\compgroundstrafing.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:flyover\\ara_groundstrafing\\compgroundstrafing.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|e:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\building_comps\\ara_buildingterrainspawn\\compdelayedterrainspawn.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:building_comps\\ara_buildingterrainspawn\\compdelayedterrainspawn.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\flyover\\ara_sectorsurveillance\\compsectorsurveillance.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:flyover\\ara_sectorsurveillance\\compsectorsurveillance.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|e:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\building_comps\\comptemperatureruinabledamage.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:building_comps\\comptemperatureruinabledamage.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\flyover\\thingclassflyover.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:flyover\\thingclassflyover.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\flyover\\ara_aircrafthangar\\compabilityeffect_aircraftstrike.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:flyover\\ara_aircrafthangar\\compabilityeffect_aircraftstrike.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\abilities\\ara_givehediffwithskillduration\\compabilityeffect_givehediffwithskillduration.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:abilities\\ara_givehediffwithskillduration\\compabilityeffect_givehediffwithskillduration.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\abilities\\ara_showtemperaturerange\\compabilityeffect_abilityshowtemperaturerange.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|e:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\abilities\\ara_showtemperaturerange\\compabilityeffect_abilityshowtemperaturerange.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:abilities\\ara_showtemperaturerange\\compabilityeffect_abilityshowtemperaturerange.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\abilities\\ara_showspawnablepawnslist\\compabilityeffect_abilityshowspawnablepawns.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:abilities\\ara_showspawnablepawnslist\\compabilityeffect_abilityshowspawnablepawns.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|e:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\jobs\\jobdriver_swarmmaintain\\jobdriver_swarmmaintain.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:jobs\\jobdriver_swarmmaintain\\jobdriver_swarmmaintain.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\flyover\\ara_spawnflyover\\compabilityeffect_spawnflyover.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:flyover\\ara_spawnflyover\\compabilityeffect_spawnflyover.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\flyover\\ara_aircrafthangar\\compaircrafthangar.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:flyover\\ara_aircrafthangar\\compaircrafthangar.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\flyover\\ara_aircrafthangar\\worldcomponent_aircraftmanager.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:flyover\\ara_aircrafthangar\\worldcomponent_aircraftmanager.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\flyover\\ara_flyoverescort\\compproperties_flyoverescort.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:flyover\\ara_flyoverescort\\compproperties_flyoverescort.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|e:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\jobs\\jobdriver_feedwithhoney\\jobdriver_feedwithhoney.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:jobs\\jobdriver_feedwithhoney\\jobdriver_feedwithhoney.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
}
],
"DocumentGroupContainers": [
@@ -62,21 +62,8 @@
"DocumentGroups": [
{
"DockedWidth": 200,
"SelectedChildIndex": 0,
"SelectedChildIndex": 3,
"Children": [
{
"$type": "Document",
"DocumentIndex": 0,
"Title": "Building_Ootheca.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\Building_Ootheca.cs",
"RelativeDocumentMoniker": "Buildings\\Building_Ootheca\\Building_Ootheca.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\Building_Ootheca.cs",
"RelativeToolTip": "Buildings\\Building_Ootheca\\Building_Ootheca.cs",
"ViewState": "AgIAAK8CAAAAAAAAAAA9wPACAAAMAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-12-15T07:28:18.272Z",
"EditorCaption": ""
},
{
"$type": "Bookmark",
"Name": "ST:0:0:{1c4feeaa-4718-4aa9-859d-94ce25d182ba}"
@@ -84,147 +71,171 @@
{
"$type": "Document",
"DocumentIndex": 1,
"Title": "CompProperties_CustomRadius.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Placeworker\\CompProperties_CustomRadius.cs",
"RelativeDocumentMoniker": "Placeworker\\CompProperties_CustomRadius.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Placeworker\\CompProperties_CustomRadius.cs",
"RelativeToolTip": "Placeworker\\CompProperties_CustomRadius.cs",
"ViewState": "AgIAAAIAAAAAAAAAAAAAACMAAAA/AAAAAAAAAA==",
"Title": "CompProperties_EquipmentIncubatorData.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_EquipmentOotheca\\CompProperties_EquipmentIncubatorData.cs",
"RelativeDocumentMoniker": "Buildings\\Building_EquipmentOotheca\\CompProperties_EquipmentIncubatorData.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_EquipmentOotheca\\CompProperties_EquipmentIncubatorData.cs",
"RelativeToolTip": "Buildings\\Building_EquipmentOotheca\\CompProperties_EquipmentIncubatorData.cs",
"ViewState": "AgIAAAQAAAAAAAAAAAAmwDMBAAAlAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-12-15T07:21:04.756Z",
"WhenOpened": "2025-12-15T17:55:40.041Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 3,
"Title": "CompSectorSurveillance.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_SectorSurveillance\\CompSectorSurveillance.cs",
"RelativeDocumentMoniker": "Flyover\\ARA_SectorSurveillance\\CompSectorSurveillance.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_SectorSurveillance\\CompSectorSurveillance.cs",
"RelativeToolTip": "Flyover\\ARA_SectorSurveillance\\CompSectorSurveillance.cs",
"ViewState": "AgIAAPACAAAAAAAAAAAAABEDAAAAAAAAAAAAAA==",
"Title": "ARA_HediffDefOf.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\ARA_HediffDefOf.cs",
"RelativeDocumentMoniker": "ARA_HediffDefOf.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\ARA_HediffDefOf.cs",
"RelativeToolTip": "ARA_HediffDefOf.cs",
"ViewState": "AgIAAAAAAAAAAAAAAADwvxgAAAAdAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-30T13:52:54.896Z"
"WhenOpened": "2025-12-15T17:32:18.493Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 2,
"Title": "CompGroundStrafing.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_GroundStrafing\\CompGroundStrafing.cs",
"RelativeDocumentMoniker": "Flyover\\ARA_GroundStrafing\\CompGroundStrafing.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_GroundStrafing\\CompGroundStrafing.cs",
"RelativeToolTip": "Flyover\\ARA_GroundStrafing\\CompGroundStrafing.cs",
"ViewState": "AgIAAGwBAAAAAAAAAAArwIYBAAAFAAAAAAAAAA==",
"DocumentIndex": 0,
"Title": "Building_EquipmentOotheca.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_EquipmentOotheca\\Building_EquipmentOotheca.cs",
"RelativeDocumentMoniker": "Buildings\\Building_EquipmentOotheca\\Building_EquipmentOotheca.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_EquipmentOotheca\\Building_EquipmentOotheca.cs*",
"RelativeToolTip": "Buildings\\Building_EquipmentOotheca\\Building_EquipmentOotheca.cs*",
"ViewState": "AgIAADIBAAAAAAAAAAAAAEQBAAAIAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-30T13:00:11.18Z"
},
{
"$type": "Document",
"DocumentIndex": 6,
"Title": "CompAbilityEffect_GiveHediffWithSkillDuration.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_GiveHediffWithSkillDuration\\CompAbilityEffect_GiveHediffWithSkillDuration.cs",
"RelativeDocumentMoniker": "Abilities\\ARA_GiveHediffWithSkillDuration\\CompAbilityEffect_GiveHediffWithSkillDuration.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_GiveHediffWithSkillDuration\\CompAbilityEffect_GiveHediffWithSkillDuration.cs",
"RelativeToolTip": "Abilities\\ARA_GiveHediffWithSkillDuration\\CompAbilityEffect_GiveHediffWithSkillDuration.cs",
"ViewState": "AgIAAEsAAAAAAAAAAAAWwGAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-29T14:40:47.422Z"
},
{
"$type": "Document",
"DocumentIndex": 7,
"Title": "CompAbilityEffect_AbilityShowTemperatureRange.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_ShowTemperatureRange\\CompAbilityEffect_AbilityShowTemperatureRange.cs",
"RelativeDocumentMoniker": "Abilities\\ARA_ShowTemperatureRange\\CompAbilityEffect_AbilityShowTemperatureRange.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_ShowTemperatureRange\\CompAbilityEffect_AbilityShowTemperatureRange.cs",
"RelativeToolTip": "Abilities\\ARA_ShowTemperatureRange\\CompAbilityEffect_AbilityShowTemperatureRange.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAABcAAAArAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-29T14:40:43.525Z"
"WhenOpened": "2025-12-15T17:32:08.87Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 5,
"Title": "CompAbilityEffect_AircraftStrike.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_AircraftHangar\\CompAbilityEffect_AircraftStrike.cs",
"RelativeDocumentMoniker": "Flyover\\ARA_AircraftHangar\\CompAbilityEffect_AircraftStrike.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_AircraftHangar\\CompAbilityEffect_AircraftStrike.cs",
"RelativeToolTip": "Flyover\\ARA_AircraftHangar\\CompAbilityEffect_AircraftStrike.cs",
"ViewState": "AgIAAHYAAAAAAAAAAAAtwJcAAAArAAAAAAAAAA==",
"Title": "CompExtraIncubationInfo.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Thing_Comps\\ARA_CompExtraIncubationInfo\\CompExtraIncubationInfo.cs",
"RelativeDocumentMoniker": "Thing_Comps\\ARA_CompExtraIncubationInfo\\CompExtraIncubationInfo.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Thing_Comps\\ARA_CompExtraIncubationInfo\\CompExtraIncubationInfo.cs",
"RelativeToolTip": "Thing_Comps\\ARA_CompExtraIncubationInfo\\CompExtraIncubationInfo.cs",
"ViewState": "AgIAAAAAAAAAAAAAAADwvwAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-29T11:22:34.783Z"
"WhenOpened": "2025-12-15T17:13:20.87Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 4,
"Title": "ThingclassFlyOver.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ThingclassFlyOver.cs",
"RelativeDocumentMoniker": "Flyover\\ThingclassFlyOver.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ThingclassFlyOver.cs",
"RelativeToolTip": "Flyover\\ThingclassFlyOver.cs",
"ViewState": "AgIAAIkCAAAAAAAAAAAawI8CAAANAAAAAAAAAA==",
"Title": "CompProperties_ExtraIncubationInfo.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Thing_Comps\\ARA_CompExtraIncubationInfo\\CompProperties_ExtraIncubationInfo.cs",
"RelativeDocumentMoniker": "Thing_Comps\\ARA_CompExtraIncubationInfo\\CompProperties_ExtraIncubationInfo.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Thing_Comps\\ARA_CompExtraIncubationInfo\\CompProperties_ExtraIncubationInfo.cs",
"RelativeToolTip": "Thing_Comps\\ARA_CompExtraIncubationInfo\\CompProperties_ExtraIncubationInfo.cs",
"ViewState": "AgIAAAAAAAAAAAAAAADwvwAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-29T14:17:06.867Z"
"WhenOpened": "2025-12-15T17:13:20.069Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 2,
"Title": "OothecaIncubatorExtension.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\OothecaIncubatorExtension.cs",
"RelativeDocumentMoniker": "Buildings\\Building_Ootheca\\OothecaIncubatorExtension.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\OothecaIncubatorExtension.cs",
"RelativeToolTip": "Buildings\\Building_Ootheca\\OothecaIncubatorExtension.cs",
"ViewState": "AgIAAAAAAAAAAAAAAADwvxIAAAA4AAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-12-15T17:11:54.114Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 6,
"Title": "Building_Ootheca.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\Building_Ootheca.cs",
"RelativeDocumentMoniker": "Buildings\\Building_Ootheca\\Building_Ootheca.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\Building_Ootheca.cs",
"RelativeToolTip": "Buildings\\Building_Ootheca\\Building_Ootheca.cs",
"ViewState": "AgIAAA0DAAAAAAAAAAAAACAAAAArAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-12-15T17:10:05.509Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 7,
"Title": "Comp_SwarmMaintenance.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_SwarmMaintenance\\Comp_SwarmMaintenance.cs",
"RelativeDocumentMoniker": "Building_Comps\\ARA_SwarmMaintenance\\Comp_SwarmMaintenance.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_SwarmMaintenance\\Comp_SwarmMaintenance.cs",
"RelativeToolTip": "Building_Comps\\ARA_SwarmMaintenance\\Comp_SwarmMaintenance.cs",
"ViewState": "AgIAABkBAAAAAAAAAAAAAE0BAABIAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-12-15T16:59:28.717Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 8,
"Title": "CompAbilityEffect_AbilityShowSpawnablePawns.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_ShowSpawnablePawnsList\\CompAbilityEffect_AbilityShowSpawnablePawns.cs",
"RelativeDocumentMoniker": "Abilities\\ARA_ShowSpawnablePawnsList\\CompAbilityEffect_AbilityShowSpawnablePawns.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_ShowSpawnablePawnsList\\CompAbilityEffect_AbilityShowSpawnablePawns.cs",
"RelativeToolTip": "Abilities\\ARA_ShowSpawnablePawnsList\\CompAbilityEffect_AbilityShowSpawnablePawns.cs",
"ViewState": "AgIAABYAAAAAAAAAAAAuwBYAAAArAAAAAAAAAA==",
"Title": "CompDelayedTerrainSpawn.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_BuildingTerrainSpawn\\CompDelayedTerrainSpawn.cs",
"RelativeDocumentMoniker": "Building_Comps\\ARA_BuildingTerrainSpawn\\CompDelayedTerrainSpawn.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_BuildingTerrainSpawn\\CompDelayedTerrainSpawn.cs",
"RelativeToolTip": "Building_Comps\\ARA_BuildingTerrainSpawn\\CompDelayedTerrainSpawn.cs",
"ViewState": "AgIAAAAAAAAAAAAAAADwvwAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-29T14:40:40.237Z"
},
{
"$type": "Document",
"DocumentIndex": 10,
"Title": "CompAircraftHangar.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_AircraftHangar\\CompAircraftHangar.cs",
"RelativeDocumentMoniker": "Flyover\\ARA_AircraftHangar\\CompAircraftHangar.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_AircraftHangar\\CompAircraftHangar.cs",
"RelativeToolTip": "Flyover\\ARA_AircraftHangar\\CompAircraftHangar.cs",
"ViewState": "AgIAABcAAAAAAAAAAAAQwCQAAAAnAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-29T11:39:22.563Z"
"WhenOpened": "2025-12-15T16:59:21.314Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 9,
"Title": "CompAbilityEffect_SpawnFlyOver.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_SpawnFlyOver\\CompAbilityEffect_SpawnFlyOver.cs",
"RelativeDocumentMoniker": "Flyover\\ARA_SpawnFlyOver\\CompAbilityEffect_SpawnFlyOver.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_SpawnFlyOver\\CompAbilityEffect_SpawnFlyOver.cs",
"RelativeToolTip": "Flyover\\ARA_SpawnFlyOver\\CompAbilityEffect_SpawnFlyOver.cs",
"ViewState": "AgIAAFMDAAAAAAAAAAAawG8DAAAAAAAAAAAAAA==",
"Title": "CompTemperatureRuinableDamage.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\CompTemperatureRuinableDamage.cs",
"RelativeDocumentMoniker": "Building_Comps\\CompTemperatureRuinableDamage.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\CompTemperatureRuinableDamage.cs",
"RelativeToolTip": "Building_Comps\\CompTemperatureRuinableDamage.cs",
"ViewState": "AgIAAB8AAAAAAAAAAAAAwDoAAAAlAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-29T13:37:35.758Z"
"WhenOpened": "2025-12-15T16:52:05.743Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 10,
"Title": "CompAbilityEffect_AbilityShowTemperatureRange.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_ShowTemperatureRange\\CompAbilityEffect_AbilityShowTemperatureRange.cs",
"RelativeDocumentMoniker": "Abilities\\ARA_ShowTemperatureRange\\CompAbilityEffect_AbilityShowTemperatureRange.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_ShowTemperatureRange\\CompAbilityEffect_AbilityShowTemperatureRange.cs",
"RelativeToolTip": "Abilities\\ARA_ShowTemperatureRange\\CompAbilityEffect_AbilityShowTemperatureRange.cs",
"ViewState": "AgIAAHUAAAAAAAAAAAAAAJIAAABEAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-12-15T16:52:00.971Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 11,
"Title": "WorldComponent_AircraftManager.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_AircraftHangar\\WorldComponent_AircraftManager.cs",
"RelativeDocumentMoniker": "Flyover\\ARA_AircraftHangar\\WorldComponent_AircraftManager.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_AircraftHangar\\WorldComponent_AircraftManager.cs",
"RelativeToolTip": "Flyover\\ARA_AircraftHangar\\WorldComponent_AircraftManager.cs",
"ViewState": "AgIAAJUAAAAAAAAAAAAowK4AAAAUAAAAAAAAAA==",
"Title": "JobDriver_SwarmMaintain.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_SwarmMaintain\\JobDriver_SwarmMaintain.cs",
"RelativeDocumentMoniker": "Jobs\\JobDriver_SwarmMaintain\\JobDriver_SwarmMaintain.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_SwarmMaintain\\JobDriver_SwarmMaintain.cs",
"RelativeToolTip": "Jobs\\JobDriver_SwarmMaintain\\JobDriver_SwarmMaintain.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAB0AAABPAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-29T14:05:39.817Z"
"WhenOpened": "2025-12-15T16:36:34.047Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 12,
"Title": "CompProperties_FlyOverEscort.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_FlyOverEscort\\CompProperties_FlyOverEscort.cs",
"RelativeDocumentMoniker": "Flyover\\ARA_FlyOverEscort\\CompProperties_FlyOverEscort.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_FlyOverEscort\\CompProperties_FlyOverEscort.cs",
"RelativeToolTip": "Flyover\\ARA_FlyOverEscort\\CompProperties_FlyOverEscort.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAABAAAAAvAAAAAAAAAA==",
"Title": "JobDriver_FeedWithHoney.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_FeedWithHoney\\JobDriver_FeedWithHoney.cs",
"RelativeDocumentMoniker": "Jobs\\JobDriver_FeedWithHoney\\JobDriver_FeedWithHoney.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_FeedWithHoney\\JobDriver_FeedWithHoney.cs",
"RelativeToolTip": "Jobs\\JobDriver_FeedWithHoney\\JobDriver_FeedWithHoney.cs",
"ViewState": "AgIAACEAAAAAAAAAAAAYwEsAAAAOAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-29T12:59:07.753Z"
"WhenOpened": "2025-12-15T16:35:50.511Z",
"EditorCaption": ""
}
]
}

View File

@@ -19,10 +19,22 @@ namespace ArachnaeSwarm
public static class ARA_JobDefOf
{
public static JobDef ARA_OperateIncubator;
public static JobDef ARA_OperateEquipmentIncubator;
public static JobDef ARA_SwarmMaintain;
static ARA_JobDefOf()
{
DefOfHelper.EnsureInitializedInCtor(typeof(ARA_JobDefOf));
}
}
[DefOf]
public static class ARA_EffecterDefOf
{
public static EffecterDef EatVegetarian;
static ARA_EffecterDefOf()
{
DefOfHelper.EnsureInitializedInCtor(typeof(ARA_EffecterDefOf));
}
}
}

View File

@@ -114,11 +114,16 @@
<Compile Include="Abilities\CompAbilityEffect_RandomHediff.cs" />
<Compile Include="Abilities\CompAbilityEffect_TransformCorpse.cs" />
<Compile Include="Buildings\Building_ArachnaeGravEngine.cs" />
<Compile Include="Buildings\Building_EquipmentOotheca\Building_EquipmentOotheca.cs" />
<Compile Include="Buildings\Building_EquipmentOotheca\CompProperties_EquipmentIncubatorData.cs" />
<Compile Include="Buildings\Building_EquipmentOotheca\ITab_EquipmentOotheca_Incubation.cs" />
<Compile Include="Buildings\Building_EquipmentOotheca\JobDriver_OperateEquipmentIncubator.cs" />
<Compile Include="Buildings\Building_Incubatable.cs" />
<Compile Include="Buildings\Building_Ootheca\Building_Ootheca.cs" />
<Compile Include="Buildings\Building_Ootheca\CompProperties_IncubatorData.cs" />
<Compile Include="Buildings\Building_Ootheca\ITab_Ootheca_Incubation.cs" />
<Compile Include="Buildings\Building_Ootheca\JobDriver_OperateIncubator.cs" />
<Compile Include="Buildings\Building_Ootheca\OothecaIncubatorExtension.cs" />
<Compile Include="Buildings\Building_Ootheca\RoomRoleWorker_Incubator.cs" />
<Compile Include="Buildings\Building_TurretGunHasSpeed.cs" />
<Compile Include="Building_Comps\ARA_Building_RefuelingVat\Building_RefuelingVat.cs" />
@@ -131,6 +136,8 @@
<Compile Include="Building_Comps\ARA_ForwardClearance\PlaceWorker_ForwardClearance.cs" />
<Compile Include="Building_Comps\ARA_ProductStorage\CompProductStorage.cs" />
<Compile Include="Building_Comps\ARA_ProductStorage\CompProperties_ProductStorage.cs" />
<Compile Include="Building_Comps\ARA_SwarmMaintenance\CompProperties_SwarmMaintenance.cs" />
<Compile Include="Building_Comps\ARA_SwarmMaintenance\Comp_SwarmMaintenance.cs" />
<Compile Include="Building_Comps\CompBreakdownDisabler.cs" />
<Compile Include="EventSystem\CompOpenCustomUI.cs" />
<Compile Include="EventSystem\Condition.cs" />
@@ -164,7 +171,18 @@
<Compile Include="Hediffs\ARA_HediffTerrainSpawn\CompProperties_HediffTerrainSpawn.cs" />
<Compile Include="Hediffs\HediffComp_LifespanDisplay.cs" />
<Compile Include="Jobs\JobDriver_CarryPrisonerToRefuelingVat.cs" />
<Compile Include="Jobs\JobDriver_FeedWithHoney\JobDriver_ExtractHoney.cs" />
<Compile Include="Jobs\JobDriver_FeedWithHoney\JobDriver_FeedWithHoney.cs" />
<Compile Include="Jobs\JobDriver_FeedWithHoney\ThinkNode_JobGiver_ExtractHoney.cs" />
<Compile Include="Jobs\JobDriver_FeedWithHoney\ThinkNode_JobGiver_FeedWithHoney.cs" />
<Compile Include="Jobs\JobDriver_StripChitin\CompProperties_ChitinStripping.cs" />
<Compile Include="Jobs\JobDriver_StripChitin\Comp_ChitinStripping.cs" />
<Compile Include="Jobs\JobDriver_StripChitin\JobDriver_StripChitin.cs" />
<Compile Include="Jobs\JobDriver_StripChitin\ThinkNode_JobGiver_StripChitin.cs" />
<Compile Include="Jobs\JobDriver_SwarmMaintain\JobDriver_SwarmMaintain.cs" />
<Compile Include="MentalState\MentalState_HiveMindCascade.cs" />
<Compile Include="Needs\Need_ChitinArmor.cs" />
<Compile Include="Needs\Need_HoneyProduction.cs" />
<Compile Include="Pawn_Comps\ARA_UniquePawn\CompProperties_UniquePawn.cs" />
<Compile Include="Pawn_Comps\ARA_UniquePawn\CompUniquePawn.cs" />
<Compile Include="Pawn_Comps\ARA_UniquePawn\Patch_UniquePawn.cs" />
@@ -196,7 +214,7 @@
<Compile Include="Building_Comps\ARA_CompInteractiveProducer\CompInteractiveProducer.cs" />
<Compile Include="Building_Comps\ARA_CompInteractiveProducer\CompQueuedInteractiveProducer.cs" />
<Compile Include="Building_Comps\CompRefuelableNutrition.cs" />
<Compile Include="Building_Comps\ARA_CompInteractiveProducer\CompTemperatureRuinableDamage.cs" />
<Compile Include="Building_Comps\CompTemperatureRuinableDamage.cs" />
<Compile Include="Building_Comps\ARA_CompInteractiveProducer\DataContracts.cs" />
<Compile Include="Building_Comps\ARA_CompInteractiveProducer\JobDriver_AddProcessToQueue.cs" />
<Compile Include="Building_Comps\ARA_CompInteractiveProducer\JobDriver_StartProduction.cs" />

View File

@@ -1,91 +0,0 @@
using RimWorld;
using Verse;
namespace ArachnaeSwarm
{
public class CompProperties_TemperatureRuinableDamage : CompProperties
{
public float minSafeTemperature;
public float maxSafeTemperature = 100f;
public float progressPerDegreePerTick = 1E-05f; // 修改参数名以匹配标准调用方式
public float damagePerTick = 1f; // 每tick造成的伤害值
public float recoveryRate = 0.001f; // 温度恢复正常时的恢复速率
public CompProperties_TemperatureRuinableDamage()
{
compClass = typeof(CompTemperatureRuinableDamage);
}
}
public class CompTemperatureRuinableDamage : ThingComp
{
private float ruinedPercent; // 修改变量名以匹配标准
private bool isRuined; // 修改变量名以匹配标准
public CompProperties_TemperatureRuinableDamage Props => (CompProperties_TemperatureRuinableDamage)props;
public override void CompTick()
{
base.CompTick();
if (parent.AmbientTemperature < Props.minSafeTemperature || parent.AmbientTemperature > Props.maxSafeTemperature)
{
float tempDelta = 0f;
if (parent.AmbientTemperature < Props.minSafeTemperature)
{
tempDelta = Props.minSafeTemperature - parent.AmbientTemperature;
}
else if (parent.AmbientTemperature > Props.maxSafeTemperature)
{
tempDelta = parent.AmbientTemperature - Props.maxSafeTemperature;
}
// 累积损坏进度
ruinedPercent += tempDelta * Props.progressPerDegreePerTick;
// 只有在已损坏的情况下才每tick造成持续伤害
if (isRuined)
{
parent.TakeDamage(new DamageInfo(DamageDefOf.Deterioration, Props.damagePerTick));
}
// 标记为已受损
isRuined = true;
}
else
{
// 当温度恢复正常时,逐渐减少损坏进度而不是重置
if (isRuined && ruinedPercent > 0f)
{
ruinedPercent -= Props.recoveryRate;
if (ruinedPercent <= 0f)
{
ruinedPercent = 0f;
isRuined = false;
}
}
// 即使温度正常,如果已损坏也要继续造成伤害直到恢复
if (isRuined)
{
parent.TakeDamage(new DamageInfo(DamageDefOf.Deterioration, Props.damagePerTick));
}
}
}
public override void PostExposeData()
{
base.PostExposeData();
Scribe_Values.Look(ref ruinedPercent, "ruinedPercent", 0f);
Scribe_Values.Look(ref isRuined, "isRuined", false);
}
public override string CompInspectStringExtra()
{
if (ruinedPercent > 0f)
{
return "CocoonRuinedByTemperature".Translate() + ": " + ruinedPercent.ToStringPercent();
}
return base.CompInspectStringExtra();
}
}
}

View File

@@ -0,0 +1,36 @@
// File: Comps/CompProperties_SwarmMaintenance.cs
using RimWorld;
using System.Collections.Generic;
using Verse;
namespace ArachnaeSwarm
{
public class CompProperties_SwarmMaintenance : CompProperties
{
// 最大维护度
public float maxMaintenance = 100f;
// 每天下降的维护度
public float maintenanceDecayPerDay = 10f;
// 维护度为0时每秒受到的伤害
public float damagePerSecondWhenEmpty = 1f;
// 维护度达到多少时开始显示警告(百分比)
public float warningThreshold = 0.3f;
// 可维护的种族列表
public List<ThingDef> allowedRaces;
// 检查分配工作的间隔ticks
public int assignJobCheckInterval = 600; // 10秒
// 维护度低于多少时开始寻找维护者(百分比)
public float maintenanceThresholdForJob = 0.9f;
public CompProperties_SwarmMaintenance()
{
compClass = typeof(Comp_SwarmMaintenance);
}
}
}

View File

@@ -0,0 +1,399 @@
// File: Comps/Comp_SwarmMaintenance.cs
using System.Collections.Generic;
using RimWorld;
using UnityEngine;
using Verse;
using Verse.AI;
namespace ArachnaeSwarm
{
public class Comp_SwarmMaintenance : ThingComp
{
// 属性访问器
public CompProperties_SwarmMaintenance Props => (CompProperties_SwarmMaintenance)props;
// 当前维护度
private float currentMaintenance;
// 上次更新维护度的时间
private int lastMaintenanceTick = -99999;
// 上次检查分配工作的时间
private int lastJobCheckTick = -99999;
// 当维护度为0时每秒受到的伤害计时器
private int nextDamageTick = -99999;
// 是否正在寻找维护者
private bool seekingMaintainer = false;
// 找不到维护者的提示信息
private string noMaintainerWarning = "";
private int lastWarningTick = -99999;
private const int WarningDuration = 2500; // 警告显示41.67秒
// 属性访问
public float CurrentMaintenance => currentMaintenance;
public float MaxMaintenance => Props.maxMaintenance;
public float MaintenancePercentage => currentMaintenance / MaxMaintenance;
public bool NeedsMaintenance => currentMaintenance < MaxMaintenance * Props.maintenanceThresholdForJob;
public bool IsCritical => currentMaintenance <= MaxMaintenance * Props.warningThreshold;
public bool IsEmpty => currentMaintenance <= 0f;
// 初始化
public override void Initialize(CompProperties props)
{
base.Initialize(props);
currentMaintenance = Props.maxMaintenance; // 初始时满维护度
lastMaintenanceTick = Find.TickManager.TicksGame;
lastJobCheckTick = Find.TickManager.TicksGame;
}
// 序列化
public override void PostExposeData()
{
base.PostExposeData();
Scribe_Values.Look(ref currentMaintenance, "currentMaintenance", Props.maxMaintenance);
Scribe_Values.Look(ref lastMaintenanceTick, "lastMaintenanceTick", -99999);
Scribe_Values.Look(ref lastJobCheckTick, "lastJobCheckTick", -99999);
Scribe_Values.Look(ref nextDamageTick, "nextDamageTick", -99999);
Scribe_Values.Look(ref seekingMaintainer, "seekingMaintainer", false);
Scribe_Values.Look(ref noMaintainerWarning, "noMaintainerWarning", "");
Scribe_Values.Look(ref lastWarningTick, "lastWarningTick", -99999);
}
// 每tick更新
public override void CompTick()
{
base.CompTick();
int currentTick = Find.TickManager.TicksGame;
// 每2500tick约41.67秒)更新一次维护度递减
if (currentTick - lastMaintenanceTick >= 2500)
{
UpdateMaintenanceDecay();
lastMaintenanceTick = currentTick;
}
// 定期检查是否需要分配维护工作
if (currentTick - lastJobCheckTick >= Props.assignJobCheckInterval)
{
CheckAndAssignMaintenanceJob();
lastJobCheckTick = currentTick;
}
// 如果维护度为0每秒造成伤害
if (currentMaintenance <= 0f && currentTick >= nextDamageTick)
{
ApplyDamageWhenEmpty();
nextDamageTick = currentTick + 60; // 60ticks = 1秒
}
// 更新警告信息显示时间
if (currentTick - lastWarningTick >= WarningDuration)
{
noMaintainerWarning = "";
}
}
// 更新维护度递减
private void UpdateMaintenanceDecay()
{
if (parent == null || parent.Map == null)
return;
// 计算递减量每天递减量转换为每2500tick约0.347天)的递减量
float decayAmount = Props.maintenanceDecayPerDay * (2500f / 60000f);
currentMaintenance -= decayAmount;
// 确保不低于0
if (currentMaintenance < 0f)
currentMaintenance = 0f;
}
// 当维护度为0时应用伤害
private void ApplyDamageWhenEmpty()
{
if (parent == null || parent.Destroyed)
return;
// 每秒造成伤害
float damageAmount = Props.damagePerSecondWhenEmpty;
// 应用伤害到建筑
parent.TakeDamage(new DamageInfo(
DamageDefOf.Deterioration,
damageAmount,
armorPenetration: 999f, // 高穿甲,确保能造成伤害
instigator: null
));
}
// 检查并分配维护工作
private void CheckAndAssignMaintenanceJob()
{
if (parent == null || parent.Map == null || parent.Faction == null)
return;
// 如果不需要维护,重置状态
if (!NeedsMaintenance)
{
seekingMaintainer = false;
return;
}
// 如果正在寻找维护者,尝试分配工作
if (!seekingMaintainer)
{
seekingMaintainer = true;
}
// 尝试寻找符合条件的Pawn
Pawn maintainer = FindAvailableMaintainer();
if (maintainer != null)
{
// 分配维护工作
AssignMaintenanceJobTo(maintainer);
seekingMaintainer = false;
noMaintainerWarning = "";
}
else
{
// 记录找不到维护者的警告
if (noMaintainerWarning == "")
{
noMaintainerWarning = "ARA_SwarmMaintenance.NoMaintainerFound".Translate();
lastWarningTick = Find.TickManager.TicksGame;
}
}
}
// 寻找可用的维护者
private Pawn FindAvailableMaintainer()
{
if (parent.Map == null || parent.Faction == null)
return null;
// 查找地图中所有属于玩家阵营的Pawn
List<Pawn> allPawns = parent.Map.mapPawns.SpawnedPawnsInFaction(parent.Faction);
foreach (Pawn pawn in allPawns)
{
if (CanPawnMaintain(pawn))
{
return pawn;
}
}
return null;
}
// 判断Pawn是否可以维护
private bool CanPawnMaintain(Pawn pawn)
{
if (pawn == null || pawn.Dead || pawn.Downed || pawn.InMentalState)
return false;
// 检查种族是否在允许列表中
if (Props.allowedRaces != null && Props.allowedRaces.Count > 0)
{
if (!Props.allowedRaces.Contains(pawn.def))
return false;
}
// 检查Pawn是否处于GotoWander或Wait_Wander状态
if (!IsPawnWandering(pawn))
return false;
// 检查Pawn是否已经有维护工作
if (HasMaintenanceJob(pawn))
return false;
return true;
}
// 检查Pawn是否处于漫游状态
private bool IsPawnWandering(Pawn pawn)
{
if (pawn.jobs == null || pawn.jobs.curJob == null)
return false;
Job curJob = pawn.jobs.curJob;
// 检查是否是漫游工作
if (curJob.def == JobDefOf.GotoWander || curJob.def == JobDefOf.Wait_Wander)
{
return true;
}
return false;
}
// 检查Pawn是否已经有维护工作
private bool HasMaintenanceJob(Pawn pawn)
{
if (pawn.jobs == null)
return false;
// 检查当前工作是否是维护工作
if (pawn.jobs.curJob != null && pawn.jobs.curJob.def == ARA_JobDefOf.ARA_SwarmMaintain)
{
return true;
}
// 检查工作队列中是否有维护工作
if (pawn.jobs.jobQueue != null && pawn.jobs.jobQueue.Count > 0)
{
foreach (QueuedJob queuedJob in pawn.jobs.jobQueue)
{
if (queuedJob.job.def == ARA_JobDefOf.ARA_SwarmMaintain)
{
return true;
}
}
}
return false;
}
// 为Pawn分配维护工作
private void AssignMaintenanceJobTo(Pawn pawn)
{
if (pawn == null || pawn.jobs == null)
return;
// 创建维护工作
Job job = JobMaker.MakeJob(ARA_JobDefOf.ARA_SwarmMaintain, parent);
job.expiryInterval = 30000; // 工作过期时间
job.ignoreForbidden = false;
// 记录Pawn原来的工作以便恢复
Job oldJob = pawn.jobs.curJob;
// 将工作添加到队列末尾
pawn.jobs.TryTakeOrderedJob(job, JobTag.MiscWork);
// 如果Pawn原来在漫游我们可以在维护工作完成后恢复漫游状态
if (oldJob != null && (oldJob.def == JobDefOf.GotoWander || oldJob.def == JobDefOf.Wait_Wander))
{
// 我们可以在维护工作完成后添加一个恢复漫游的工作
// 这里暂时不做处理因为维护工作完成后Pawn会回到空闲状态
// 如果需要更精细的控制可以在JobDriver_SwarmMaintain完成时添加漫游工作
}
}
// 添加维护度
public void AddMaintenance(float amount)
{
currentMaintenance += amount;
if (currentMaintenance > MaxMaintenance)
currentMaintenance = MaxMaintenance;
}
// 重置维护度
public void ResetMaintenance()
{
currentMaintenance = MaxMaintenance;
}
// 在建筑信息面板中追加维护信息
public override string CompInspectStringExtra()
{
// 基础信息
string text = "ARA_SwarmMaintenance.MaintenanceLevel".Translate(currentMaintenance.ToString("F1"), MaxMaintenance.ToString("F1"), MaintenancePercentage.ToString("P1"));
// 添加维护度递减信息
text += "\n" + "ARA_SwarmMaintenance.DailyDecay".Translate(Props.maintenanceDecayPerDay.ToString("F1"));
// 显示找不到维护者的警告
if (noMaintainerWarning != "")
{
text += "\n<color=orange>" + noMaintainerWarning + "</color>";
}
return text;
}
// 获取Gizmos命令按钮- 只保留调试功能
public override IEnumerable<Gizmo> CompGetGizmosExtra()
{
// 只在玩家控制下显示
if (parent.Faction?.IsPlayer == true)
{
// 调试按钮:手动触发寻找维护者
if (Prefs.DevMode)
{
yield return new Command_Action
{
defaultLabel = "Debug: Find Maintainer",
action = () =>
{
Pawn maintainer = FindAvailableMaintainer();
if (maintainer != null)
{
Messages.Message($"找到维护者: {maintainer.LabelShort} (当前工作: {maintainer.jobs.curJob?.def.defName ?? ""})", MessageTypeDefOf.PositiveEvent);
AssignMaintenanceJobTo(maintainer);
}
else
{
Messages.Message("未找到符合条件的空闲维护者", MessageTypeDefOf.NegativeEvent);
// 列出所有符合条件的Pawn及其状态
List<Pawn> allPawns = parent.Map.mapPawns.SpawnedPawnsInFaction(parent.Faction);
List<string> pawnStatus = new List<string>();
foreach (Pawn pawn in allPawns)
{
if (Props.allowedRaces != null && Props.allowedRaces.Count > 0 && !Props.allowedRaces.Contains(pawn.def))
continue;
string status = $"{pawn.LabelShort}: ";
if (pawn.Dead) status += "死亡";
else if (pawn.Downed) status += "倒地";
else if (pawn.InMentalState) status += "精神崩溃";
else if (pawn.jobs?.curJob != null)
{
status += $"{pawn.jobs.curJob.def.defName}";
if (HasMaintenanceJob(pawn)) status += " (已有维护工作)";
}
else
{
status += "空闲";
}
pawnStatus.Add(status);
}
if (pawnStatus.Count > 0)
{
Messages.Message("符合条件的Pawn状态:\n" + string.Join("\n", pawnStatus), MessageTypeDefOf.SilentInput);
}
}
}
};
yield return new Command_Action
{
defaultLabel = "Debug: Reset Maintenance",
action = () => ResetMaintenance()
};
yield return new Command_Action
{
defaultLabel = "Debug: Reduce 50%",
action = () => currentMaintenance = MaxMaintenance * 0.5f
};
yield return new Command_Action
{
defaultLabel = "Debug: Reduce to 0",
action = () => currentMaintenance = 0f
};
}
}
}
}
}

View File

@@ -0,0 +1,178 @@
// File: Comps/CompTemperatureRuinableDamage.cs
using RimWorld;
using Verse;
namespace ArachnaeSwarm
{
public class CompProperties_TemperatureRuinableDamage : CompProperties
{
public float minSafeTemperature;
public float maxSafeTemperature = 100f;
public float progressPerDegreePerTick = 1E-05f; // 修改参数名以匹配标准调用方式
public float damagePerTick = 1f; // 每tick造成的伤害值
public float recoveryRate = 0.001f; // 温度恢复正常时的恢复速率
public bool sendDamageLetter = true; // 是否发送伤害警告信件
public CompProperties_TemperatureRuinableDamage()
{
compClass = typeof(CompTemperatureRuinableDamage);
}
}
public class CompTemperatureRuinableDamage : ThingComp
{
private float ruinedPercent; // 修改变量名以匹配标准
private bool isRuined; // 修改变量名以匹配标准
private bool damageLetterSent = false; // 是否已发送伤害警告信件
private int lastDamageTick = -99999; // 上次造成伤害的时间
private const int DamageLetterInterval = 60000; // 伤害信件间隔60秒游戏时间
public CompProperties_TemperatureRuinableDamage Props => (CompProperties_TemperatureRuinableDamage)props;
public override void CompTick()
{
base.CompTick();
int currentTick = Find.TickManager.TicksGame;
bool wasRuined = isRuined;
if (parent.AmbientTemperature < Props.minSafeTemperature || parent.AmbientTemperature > Props.maxSafeTemperature)
{
float tempDelta = 0f;
if (parent.AmbientTemperature < Props.minSafeTemperature)
{
tempDelta = Props.minSafeTemperature - parent.AmbientTemperature;
}
else if (parent.AmbientTemperature > Props.maxSafeTemperature)
{
tempDelta = parent.AmbientTemperature - Props.maxSafeTemperature;
}
// 累积损坏进度
ruinedPercent += tempDelta * Props.progressPerDegreePerTick;
// 只有在已损坏的情况下才每tick造成持续伤害
if (isRuined)
{
parent.TakeDamage(new DamageInfo(DamageDefOf.Deterioration, Props.damagePerTick));
lastDamageTick = currentTick;
// 发送伤害警告信件(如果启用)
if (Props.sendDamageLetter && !damageLetterSent && currentTick - lastDamageTick >= DamageLetterInterval)
{
SendDamageLetter();
damageLetterSent = true;
lastDamageTick = currentTick;
}
}
// 标记为已受损
isRuined = true;
// 如果是刚刚变为损坏状态,发送警告信件
if (!wasRuined && Props.sendDamageLetter)
{
SendDamageLetter();
damageLetterSent = true;
}
}
else
{
// 当温度恢复正常时,逐渐减少损坏进度而不是重置
if (isRuined && ruinedPercent > 0f)
{
ruinedPercent -= Props.recoveryRate;
if (ruinedPercent <= 0f)
{
ruinedPercent = 0f;
isRuined = false;
damageLetterSent = false; // 重置信件状态,以便下次损坏时可以再次发送
}
}
// 即使温度正常,如果已损坏也要继续造成伤害直到恢复
if (isRuined)
{
parent.TakeDamage(new DamageInfo(DamageDefOf.Deterioration, Props.damagePerTick));
lastDamageTick = currentTick;
// 发送伤害警告信件(如果启用)
if (Props.sendDamageLetter && !damageLetterSent && currentTick - lastDamageTick >= DamageLetterInterval)
{
SendDamageLetter();
damageLetterSent = true;
lastDamageTick = currentTick;
}
}
}
}
public override void PostExposeData()
{
base.PostExposeData();
Scribe_Values.Look(ref ruinedPercent, "ruinedPercent", 0f);
Scribe_Values.Look(ref isRuined, "isRuined", false);
Scribe_Values.Look(ref damageLetterSent, "damageLetterSent", false);
Scribe_Values.Look(ref lastDamageTick, "lastDamageTick", -99999);
}
public override string CompInspectStringExtra()
{
// 总是显示安全温度范围
string text = "ARA_TemperatureRuinable.SafeTemperatureRange".Translate(
Props.minSafeTemperature.ToStringTemperature("F1"),
Props.maxSafeTemperature.ToStringTemperature("F1")
);
// 显示当前温度状态
text += "\n" + "ARA_TemperatureRuinable.CurrentTemperature".Translate(parent.AmbientTemperature.ToStringTemperature("F1"));
// 显示损坏状态
if (ruinedPercent > 0f)
{
text += "\n" + "ARA_TemperatureRuinable.DamageProgress".Translate(ruinedPercent.ToStringPercent());
if (isRuined)
{
text += "\n<color=red>" + "ARA_TemperatureRuinable.TakingDamage".Translate() + "</color>";
}
}
else
{
text += "\n" + "ARA_TemperatureRuinable.NoDamage".Translate();
}
return text;
}
// 发送伤害警告信件
private void SendDamageLetter()
{
if (parent == null || parent.Map == null)
return;
// 确定伤害类型(太冷或太热)
string temperatureType;
if (parent.AmbientTemperature < Props.minSafeTemperature)
{
temperatureType = "ARA_TemperatureRuinable.TooCold".Translate();
}
else
{
temperatureType = "ARA_TemperatureRuinable.TooHot".Translate();
}
// 创建信件
string label = "ARA_TemperatureRuinable.DamageLetterLabel".Translate(parent.Label);
string text = "ARA_TemperatureRuinable.DamageLetterText".Translate(
parent.Label,
temperatureType,
parent.AmbientTemperature.ToStringTemperature("F1"),
Props.minSafeTemperature.ToStringTemperature("F1"),
Props.maxSafeTemperature.ToStringTemperature("F1")
);
// 发送信件
Find.LetterStack.ReceiveLetter(label, text, LetterDefOf.NegativeEvent, new LookTargets(parent));
}
}
}

View File

@@ -0,0 +1,882 @@
// File: Buildings/Building_EquipmentOotheca.cs
using RimWorld;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using Verse;
using Verse.AI;
using System;
namespace ArachnaeSwarm
{
public class Building_EquipmentOotheca : Building
{
// 引用组件
public CompEquipmentIncubatorData EquipmentIncubatorData => this.TryGetComp<CompEquipmentIncubatorData>();
// 孵化状态
public bool isIncubating = false;
public float incubationProgress = 0f;
public float incubationDuration = 0f;
public ThingDef incubatingThingDef = null;
// 幼虫交互相关
public Pawn assignedLarva = null;
public int larvaOperateTicksRemaining = 0;
// 速度乘数系统
private float speedMultiplier = 1.0f;
private int lastMultiplierUpdateTick = -1;
private const int MultiplierUpdateInterval = 250;
// 质量系统
private float qualityMultiplier = 1.0f;
private float qualityProgress = 0f;
private float qualityTotal = 0f;
// 缓存的ModExtension
private OothecaIncubatorExtension cachedExtension;
// 获取ModExtension的辅助属性
private OothecaIncubatorExtension Ext
{
get
{
if (cachedExtension == null)
{
cachedExtension = def.GetModExtension<OothecaIncubatorExtension>() ?? OothecaIncubatorExtension.Default;
}
return cachedExtension;
}
}
// 属性访问器
public float SpeedMultiplier
{
get
{
if (lastMultiplierUpdateTick < 0 || Find.TickManager.TicksGame - lastMultiplierUpdateTick >= MultiplierUpdateInterval)
{
UpdateSpeedMultiplier();
}
return speedMultiplier;
}
}
// 质量属性
public float QualityMultiplier => qualityMultiplier;
public float QualityProgress => qualityProgress;
public float QualityPercent => qualityTotal > 0 ? qualityProgress / qualityTotal : 0f;
// 进度百分比
public float AdjustedProgressPercent
{
get
{
if (incubationDuration <= 0) return 0f;
return incubationProgress / incubationDuration;
}
}
// 获取速度因子描述
public string GetSpeedFactorsDescription()
{
var builder = new StringBuilder();
builder.AppendLine("ARA_EquipmentIncubator.SpeedFactors".Translate());
builder.AppendLine();
// 1. 检查是否在孵化间中
bool inIncubatorRoom = IsInIncubatorRoom();
if (Ext.requiresIncubatorRoom)
{
builder.AppendLine(inIncubatorRoom ?
"ARA_EquipmentIncubator.InIncubatorRoom".Translate() :
"ARA_EquipmentIncubator.NotInIncubatorRoom".Translate());
}
// 2. 检查营养液数量
int nutrientSolutionCount = CountNearbyNutrientSolutions();
if (nutrientSolutionCount > 0)
{
builder.AppendLine("ARA_EquipmentIncubator.NutrientSolutions".Translate(
nutrientSolutionCount,
nutrientSolutionCount * Ext.nutrientSolutionBonusPerTile * 100));
}
else
{
builder.AppendLine("ARA_EquipmentIncubator.NoNutrientSolutionsNearby".Translate());
}
builder.AppendLine();
builder.AppendLine("ARA_EquipmentIncubator.NutrientDetectionRadius".Translate(Ext.nutrientSolutionRadius));
builder.AppendLine();
builder.AppendLine("ARA_EquipmentIncubator.TotalSpeedMultiplier".Translate(SpeedMultiplier.ToStringPercent()));
return builder.ToString().TrimEndNewlines();
}
// 获取质量因子描述
public string GetQualityFactorsDescription()
{
var builder = new StringBuilder();
builder.AppendLine("ARA_EquipmentIncubator.QualityFactors".Translate());
builder.AppendLine();
// 1. 建筑血量损失百分比
if (Ext.healthAffectsQuality)
{
float healthPercent = (float)HitPoints / MaxHitPoints;
builder.AppendLine("ARA_EquipmentIncubator.BuildingHealth".Translate(healthPercent.ToStringPercent()));
}
// 2. 房间质量因子
if (Ext.useRoomQualityFactor)
{
float roomFactor = GetRoomQualityFactor();
builder.AppendLine(roomFactor == 1.0f ?
"ARA_EquipmentIncubator.RoomFactorNormal".Translate() :
$"{(roomFactor > 1.0f ? "" : "")} {"ARA_EquipmentIncubator.RoomFactorModified".Translate()}{roomFactor.ToStringPercent()}");
}
// 3. 附近其他卵
int nearbyOothecaCount = CountNearbyOtherOothecas();
if (nearbyOothecaCount > 0)
{
builder.AppendLine("ARA_EquipmentIncubator.NearbyOothecas".Translate(
nearbyOothecaCount,
Mathf.Min(nearbyOothecaCount * Ext.nearbyOothecaPenaltyPerUnit * 100, 100)));
}
else
{
builder.AppendLine("ARA_EquipmentIncubator.NoNearbyOothecas".Translate());
}
builder.AppendLine();
builder.AppendLine("ARA_EquipmentIncubator.OothecaDetectionRadius".Translate(Ext.nearbyOothecaRadius));
builder.AppendLine();
builder.AppendLine("ARA_EquipmentIncubator.TotalQualityMultiplier".Translate(QualityMultiplier.ToStringPercent()));
return builder.ToString().TrimEndNewlines();
}
// 构建呼叫幼虫描述
private string BuildCallLarvaDescription(EquipmentIncubationConfig config)
{
var builder = new StringBuilder();
builder.AppendLine("ARA_EquipmentIncubator.CallLarvaTitle".Translate());
builder.AppendLine();
builder.AppendLine("ARA_EquipmentIncubator.LarvaWillCome".Translate());
builder.AppendLine(config.thingDef.LabelCap);
builder.AppendLine();
if (Ext.larvaSearchRadius < 999f)
{
builder.AppendLine("ARA_EquipmentIncubator.LarvaSearchRadius".Translate(Ext.larvaSearchRadius));
}
return builder.ToString().TrimEndNewlines();
}
// 呼叫幼虫
private void CallLarva()
{
if (isIncubating)
{
Messages.Message("ARA_EquipmentIncubator.AlreadyIncubating".Translate() + " " + "ARA_EquipmentIncubator.CancelFirst".Translate(),
MessageTypeDefOf.RejectInput);
return;
}
if (assignedLarva != null)
{
Messages.Message("ARA_EquipmentIncubator.LarvaAlreadyOnWay".Translate(),
MessageTypeDefOf.RejectInput);
return;
}
var larva = FindLarva();
if (larva == null)
{
Messages.Message("ARA_EquipmentIncubator.NoLarvaeFound".Translate() + " " + "ARA_EquipmentIncubator.LarvaMustBeRace".Translate(),
MessageTypeDefOf.RejectInput);
return;
}
var job = JobMaker.MakeJob(ARA_JobDefOf.ARA_OperateEquipmentIncubator, this);
job.count = 1;
larva.jobs.TryTakeOrderedJob(job, JobTag.MiscWork);
assignedLarva = larva;
Messages.Message("ARA_EquipmentIncubator.LarvaCalled".Translate() + " " + "ARA_EquipmentIncubator.ArriveShortly".Translate(),
MessageTypeDefOf.PositiveEvent);
}
// 幼虫到达
public void NotifyLarvaArrived(Pawn larva)
{
if (larva.def.defName != "ArachnaeBase_Race_Larva")
{
ArachnaeLog.Debug($"Invalid larva arrived: {larva.def.defName}");
return;
}
larvaOperateTicksRemaining = 180;
assignedLarva = larva;
Messages.Message("ARA_EquipmentIncubator.LarvaArrived".Translate() + " " + "ARA_EquipmentIncubator.ActivatingOotheca".Translate(),
MessageTypeDefOf.SilentInput);
}
// 幼虫完成操作
public void NotifyLarvaOperationComplete(Pawn larva)
{
if (larva != assignedLarva)
{
ArachnaeLog.Debug("Larva operation complete called with wrong larva.");
return;
}
var config = EquipmentIncubatorData?.SelectedConfig;
if (config == null)
{
ArachnaeLog.Debug("No incubation config selected when larva completed operation.");
return;
}
incubatingThingDef = config.thingDef;
incubationDuration = config.DaysRequired * 60000f;
incubationProgress = 0f;
isIncubating = true;
qualityTotal = incubationDuration;
qualityProgress = 0f;
UpdateQualityMultiplier();
UpdateSpeedMultiplier();
assignedLarva = null;
larvaOperateTicksRemaining = 0;
Messages.Message("ARA_EquipmentIncubator.IncubationStarted".Translate() + " " + incubatingThingDef.LabelCap + ". " +
"ARA_EquipmentIncubator.ProcessWillComplete".Translate() + " " + config.DaysRequired + " " + "ARA_EquipmentIncubator.DaysBaseTime".Translate(),
MessageTypeDefOf.PositiveEvent);
}
// 取消孵化
private void CancelIncubation()
{
if (!isIncubating) return;
isIncubating = false;
incubationProgress = 0f;
incubationDuration = 0f;
incubatingThingDef = null;
qualityProgress = 0f;
qualityTotal = 0f;
Messages.Message("ARA_EquipmentIncubator.IncubationCancelled".Translate() + " " + "ARA_EquipmentIncubator.ContentsLost".Translate(),
MessageTypeDefOf.NeutralEvent);
}
// 完成孵化
private void CompleteIncubation()
{
if (incubatingThingDef == null) return;
float finalQualityPercent = QualityPercent;
// 生成物品
Thing thing = ThingMaker.MakeThing(incubatingThingDef);
// 应用质量影响
ApplyQualityEffects(thing, finalQualityPercent);
// 放置物品
var spawnPos = Position;
GenPlace.TryPlaceThing(thing, spawnPos, Map, ThingPlaceMode.Near);
// 重置状态
isIncubating = false;
incubationProgress = 0f;
incubationDuration = 0f;
incubatingThingDef = null;
qualityProgress = 0f;
qualityTotal = 0f;
// 显示消息
string qualityText = finalQualityPercent >= 0.9f ? "ARA_EquipmentIncubator.QualityExcellent".Translate() :
finalQualityPercent >= 0.7f ? "ARA_EquipmentIncubator.QualityGood".Translate() :
finalQualityPercent >= 0.5f ? "ARA_EquipmentIncubator.QualityAverage".Translate() :
finalQualityPercent >= 0.3f ? "ARA_EquipmentIncubator.QualityPoor".Translate() : "ARA_EquipmentIncubator.QualityVeryPoor".Translate();
Messages.Message("ARA_EquipmentIncubator.IncubationComplete".Translate() + " " + thing.LabelCap + " " +
"ARA_EquipmentIncubator.HasEmergedWith".Translate() + " " + qualityText + " " +
"ARA_EquipmentIncubator.Quality".Translate() + " (" + finalQualityPercent.ToStringPercent() + ").",
MessageTypeDefOf.PositiveEvent);
Destroy();
}
// 应用质量效果
private void ApplyQualityEffects(Thing thing, float qualityPercent)
{
// 应用质量效果到装备
if (thing.TryGetComp<CompQuality>() is CompQuality compQuality)
{
// 根据质量百分比设置质量等级
QualityCategory qualityCategory = qualityPercent >= 0.99f ? QualityCategory.Legendary :
qualityPercent >= 0.75f ? QualityCategory.Masterwork :
qualityPercent >= 0.6f ? QualityCategory.Excellent :
qualityPercent >= 0.45f ? QualityCategory.Good :
qualityPercent >= 0.3f ? QualityCategory.Normal : QualityCategory.Poor;
compQuality.SetQuality(qualityCategory, ArtGenerationContext.Outsider);
}
// 设置生命值百分比
if (qualityPercent < 1.0f)
{
float healthFactor = Mathf.Lerp(0.5f, 1.0f, qualityPercent);
thing.HitPoints = Mathf.RoundToInt(thing.MaxHitPoints * healthFactor);
}
}
// 获取剩余时间
public float GetRemainingTicks()
{
if (!isIncubating || incubationDuration <= incubationProgress) return 0f;
float remainingProgress = incubationDuration - incubationProgress;
float currentSpeed = SpeedMultiplier;
if (currentSpeed <= 0) return float.MaxValue;
return remainingProgress / currentSpeed;
}
public float GetRemainingDays()
{
return GetRemainingTicks() / 60000f;
}
public float GetRemainingHours()
{
float remainingTicks = GetRemainingTicks();
return (remainingTicks % 60000f) / 2500f;
}
// 检查字符串
public override string GetInspectString()
{
var baseString = base.GetInspectString();
var builder = new StringBuilder();
if (!string.IsNullOrEmpty(baseString))
{
builder.Append(baseString);
}
if (isIncubating && incubatingThingDef != null)
{
float progressPercent = AdjustedProgressPercent;
float daysRemaining = GetRemainingDays();
float hoursRemaining = GetRemainingHours();
if (builder.Length > 0) builder.AppendLine();
builder.Append("ARA_EquipmentIncubator.Incubating".Translate() + ": " + incubatingThingDef.LabelCap);
builder.AppendLine();
builder.Append("ARA_EquipmentIncubator.Progress".Translate() + ": " + progressPercent.ToStringPercent());
builder.AppendLine();
string timeText = "ARA_EquipmentIncubator.TimeRemaining".Translate() + ": " + daysRemaining.ToString("F1") + " " + "ARA_EquipmentIncubator.Days".Translate();
if (hoursRemaining > 0.1f && daysRemaining < 1f)
{
timeText += " (" + hoursRemaining.ToString("F1") + " " + "ARA_EquipmentIncubator.Hours".Translate() + ")";
}
builder.Append(timeText);
builder.AppendLine();
builder.Append("ARA_EquipmentIncubator.Speed".Translate() + ": " + SpeedMultiplier.ToStringPercent() + ", " +
"ARA_EquipmentIncubator.Quality".Translate() + ": " + QualityMultiplier.ToStringPercent());
}
else if (assignedLarva != null)
{
if (builder.Length > 0) builder.AppendLine();
if (larvaOperateTicksRemaining > 0)
{
float secondsRemaining = larvaOperateTicksRemaining / 60f;
builder.Append("ARA_EquipmentIncubator.LarvaOperating".Translate() + ": " + secondsRemaining.ToString("F1") + " " + "ARA_EquipmentIncubator.SRemaining".Translate());
}
else
{
builder.Append("ARA_EquipmentIncubator.LarvaOnWay".Translate());
}
}
else if (!isIncubating)
{
var config = EquipmentIncubatorData?.SelectedConfig;
if (config != null)
{
if (builder.Length > 0) builder.AppendLine();
builder.Append("ARA_EquipmentIncubator.Target".Translate() + ": " + config.thingDef.LabelCap);
builder.AppendLine();
builder.Append("ARA_EquipmentIncubator.SpeedMultiplier".Translate() + ": " + SpeedMultiplier.ToStringPercent() + ", " +
"ARA_EquipmentIncubator.QualityMultiplier".Translate() + ": " + QualityMultiplier.ToStringPercent());
}
}
return builder.ToString().TrimEndNewlines();
}
// Gizmos
public override IEnumerable<Gizmo> GetGizmos()
{
foreach (var gizmo in base.GetGizmos())
{
yield return gizmo;
}
if (Faction == Faction.OfPlayer)
{
if (!isIncubating && EquipmentIncubatorData?.IncubationConfigs?.Count > 0)
{
yield return CreateTargetSwitchGizmo();
}
var config = EquipmentIncubatorData?.SelectedConfig;
if (!isIncubating && config != null && config.IsResearchComplete)
{
yield return new Command_Action
{
defaultLabel = "ARA_EquipmentIncubator.CallLarva".Translate(),
defaultDesc = BuildCallLarvaDescription(config),
icon = ContentFinder<Texture2D>.Get("ArachnaeSwarm/UI/Commands/ARA_CallLarva", false) ?? BaseContent.BadTex,
action = CallLarva,
hotKey = KeyBindingDefOf.Misc3
};
}
if (isIncubating)
{
yield return new Command_Action
{
defaultLabel = "ARA_EquipmentIncubator.CancelIncubation".Translate(),
defaultDesc = "ARA_EquipmentIncubator.CancelIncubationDesc".Translate(),
icon = ContentFinder<Texture2D>.Get("UI/Commands/Cancel", false) ?? TexCommand.ClearPrioritizedWork,
action = CancelIncubation
};
}
}
}
// 创建切换目标Gizmo - 现在使用装备的图标
private Gizmo CreateTargetSwitchGizmo()
{
var configs = EquipmentIncubatorData?.IncubationConfigs;
if (configs == null || configs.Count == 0) return null;
var props = EquipmentIncubatorData?.props as CompProperties_EquipmentIncubatorData;
var selectedConfig = EquipmentIncubatorData?.SelectedConfig;
var switchButton = new Command_Action
{
defaultLabel = BuildSwitchButtonLabel(selectedConfig, props),
defaultDesc = BuildSwitchButtonDescription(selectedConfig, props),
icon = GetConfigIcon(selectedConfig),
action = ShowSelectionMenu,
hotKey = KeyBindingDefOf.Misc2
};
if (selectedConfig != null && !selectedConfig.IsResearchComplete)
{
if (selectedConfig.requiredResearch != null)
{
switchButton.Disable($"Requires research: {selectedConfig.requiredResearch.LabelCap}");
}
}
return switchButton;
}
// 获取配置图标 - 现在直接从ThingDef获取
private Texture2D GetConfigIcon(EquipmentIncubationConfig config)
{
if (config == null)
return BaseContent.BadTex;
// 如果配置中没有缓存图标尝试直接获取ThingDef的uiIcon
if (config.thingDef?.uiIcon != null)
return config.thingDef.uiIcon;
// 回退到默认图标
return ContentFinder<Texture2D>.Get("UI/Commands/Default", false) ?? BaseContent.BadTex;
}
private string BuildSwitchButtonLabel(EquipmentIncubationConfig config, CompProperties_EquipmentIncubatorData props)
{
if (config != null && config.thingDef != null)
{
return (props?.buttonLabel ?? "ARA_EquipmentIncubator.IncubateLabel").Translate(config.thingDef.LabelCap);
}
return (props?.buttonLabel ?? "ARA_EquipmentIncubator.IncubateLabel").Translate("None");
}
private string BuildSwitchButtonDescription(EquipmentIncubationConfig config, CompProperties_EquipmentIncubatorData props)
{
var builder = new StringBuilder();
builder.AppendLine((props?.buttonDesc ?? "ARA_EquipmentIncubator.ButtonDesc").Translate());
builder.AppendLine();
if (config != null)
{
if (config.thingDef != null)
{
builder.AppendLine($"ARA_EquipmentIncubator.ButtonLabel".Translate(config.thingDef.LabelCap));
if (!string.IsNullOrEmpty(config.thingDef.description))
{
builder.AppendLine(config.thingDef.description);
}
}
builder.AppendLine($"ARA_EquipmentIncubator.IncubationTime".Translate(config.DaysRequired));
if (config.requiredResearch != null)
{
if (config.requiredResearch.IsFinished)
builder.AppendLine($"Research: {config.requiredResearch.LabelCap} (Completed)");
else
builder.AppendLine($"Research: {config.requiredResearch.LabelCap} (Required)");
}
}
builder.AppendLine();
builder.AppendLine("ARA_EquipmentIncubator.ButtonDesc".Translate());
return builder.ToString().TrimEndNewlines();
}
private void ShowSelectionMenu()
{
var configs = EquipmentIncubatorData?.IncubationConfigs;
var props = EquipmentIncubatorData?.props as CompProperties_EquipmentIncubatorData;
if (configs == null || configs.Count == 0) return;
var options = new List<FloatMenuOption>();
int currentIndex = EquipmentIncubatorData.GetSelectedIndex();
for (int i = 0; i < configs.Count; i++)
{
int index = i;
var config = configs[i];
if (config == null || config.thingDef == null) continue;
string label = config.thingDef.LabelCap;
string description = config.GetDescription();
string prefix = (i == currentIndex) ? "✓ " : " ";
// 使用原版FloatMenuOption的构造函数直接传入图标
FloatMenuOption option;
// 尝试获取ThingDef的图标
Texture2D icon = config.thingDef.uiIcon;
if (icon != null)
{
// 使用带有Texture2D图标的构造函数
option = new FloatMenuOption(
prefix + label,
() => SwitchToConfig(index),
icon,
Color.white,
MenuOptionPriority.Default,
null, // mouseoverGuiAction
null, // revalidateClickTarget
0f, // extraPartWidth
null, // extraPartOnGUI
null, // revalidateWorldClickTarget
true, // playSelectionSound
0, // orderInPriority
HorizontalJustification.Left, // iconJustification
false // extraPartRightJustified
);
}
else
{
// 如果没有图标,使用普通构造函数
option = new FloatMenuOption(
prefix + label,
() => SwitchToConfig(index)
);
}
// 设置工具提示
option.tooltip = description;
// 如果研究未完成,禁用选项
if (!config.IsResearchComplete)
{
option.Label = prefix + label;
option.Disabled = true;
option.tooltip = description + "\n\n " + "ARA_EquipmentIncubator.ResearchRequired".Translate() + " " + config.requiredResearch.LabelCap;
}
options.Add(option);
}
if (options.Count > 0)
{
Find.WindowStack.Add(new FloatMenu(options,
(props?.menuTitle ?? "ARA_EquipmentIncubator.MenuTitle").Translate()));
}
}
private void SwitchToConfig(int index)
{
if (EquipmentIncubatorData != null)
{
EquipmentIncubatorData.SwitchToConfig(index);
var config = EquipmentIncubatorData.SelectedConfig;
if (config != null && config.thingDef != null)
{
Messages.Message($"ARA_EquipmentIncubator.TargetSwitched".Translate(config.thingDef.LabelCap),
this, MessageTypeDefOf.SilentInput);
}
}
}
// 查找幼虫
private Pawn FindLarva()
{
var map = Map;
if (map == null) return null;
float searchRadius = Ext.larvaSearchRadius;
foreach (var pawn in map.mapPawns.SpawnedPawnsInFaction(Faction))
{
if (pawn.def.defName == "ArachnaeBase_Race_Larva")
{
if (searchRadius < 999f)
{
float distance = pawn.Position.DistanceTo(Position);
if (distance > searchRadius)
continue;
}
if (!pawn.Downed && !pawn.InMentalState &&
pawn.mindState != null &&
(pawn.CurJob == null || pawn.CurJob.def != ARA_JobDefOf.ARA_OperateEquipmentIncubator))
{
return pawn;
}
}
}
return null;
}
// 每tick更新
protected override void Tick()
{
base.Tick();
if (larvaOperateTicksRemaining > 0)
{
larvaOperateTicksRemaining--;
}
if (isIncubating)
{
if (lastMultiplierUpdateTick < 0 || Find.TickManager.TicksGame - lastMultiplierUpdateTick >= MultiplierUpdateInterval)
{
UpdateSpeedMultiplier();
UpdateQualityMultiplier();
}
float currentSpeed = SpeedMultiplier;
incubationProgress += currentSpeed;
qualityProgress += currentSpeed * QualityMultiplier;
if (incubationProgress >= incubationDuration)
{
CompleteIncubation();
}
}
}
// 检查是否在孵化间中
private bool IsInIncubatorRoom()
{
if (!Ext.requiresIncubatorRoom)
return true;
var room = this.GetRoom();
if (room == null) return false;
return room.Role != null && room.Role.defName == "ARA_Incubator_Room";
}
// 计算营养液数量
private int CountNearbyNutrientSolutions()
{
var map = Map;
if (map == null) return 0;
int count = 0;
int radius = Ext.NutrientSolutionRadiusInt;
for (int x = -radius; x <= radius; x++)
{
for (int y = -radius; y <= radius; y++)
{
IntVec3 cell = Position + new IntVec3(x, 0, y);
if (cell.InBounds(map))
{
TerrainDef terrain = map.terrainGrid.TerrainAt(cell);
if (terrain != null && terrain.defName == "ARA_Incubator_Nutrient_Solution")
{
count++;
}
}
}
}
return count;
}
// 计算房间质量因子
private float GetRoomQualityFactor()
{
if (!Ext.useRoomQualityFactor)
return 1.0f;
var room = this.GetRoom();
if (room == null) return 1.0f;
var statDef = DefDatabase<RoomStatDef>.GetNamedSilentFail("ARA_IncubatorQualityFactor");
if (statDef != null)
{
return room.GetStat(statDef);
}
return 1.0f;
}
// 计算附近其他卵的数量
private int CountNearbyOtherOothecas()
{
var map = Map;
if (map == null) return 0;
int count = 0;
var allBuildings = map.listerThings.ThingsOfDef(this.def);
foreach (var building in allBuildings)
{
if (building == this) continue;
if (building.def.defName == "ARA_Pawn_Ootheca" || building.def.defName == "ARA_Equipment_Ootheca")
{
bool isNearby = false;
if (Ext.checkSameRoomForOotheca)
{
var room = building.GetRoom();
if (room != null && room == this.GetRoom())
{
isNearby = true;
}
}
if (!isNearby)
{
float distance = building.Position.DistanceTo(this.Position);
if (distance <= Ext.nearbyOothecaRadius)
{
isNearby = true;
}
}
if (isNearby)
{
count++;
}
}
}
return count;
}
// 更新速度乘数
private void UpdateSpeedMultiplier()
{
float multiplier = 1.0f;
if (Ext.requiresIncubatorRoom && !IsInIncubatorRoom())
{
multiplier *= Ext.speedPenaltyOutsideIncubator;
}
int nutrientSolutionCount = CountNearbyNutrientSolutions();
float nutrientBonus = 1.0f + (nutrientSolutionCount * Ext.nutrientSolutionBonusPerTile);
multiplier *= nutrientBonus;
speedMultiplier = multiplier;
lastMultiplierUpdateTick = Find.TickManager.TicksGame;
}
// 更新质量乘数
private void UpdateQualityMultiplier()
{
float multiplier = 1.0f;
if (Ext.healthAffectsQuality)
{
float healthPercent = (float)HitPoints / MaxHitPoints;
multiplier *= healthPercent;
}
if (Ext.useRoomQualityFactor)
{
float roomFactor = GetRoomQualityFactor();
multiplier *= roomFactor;
}
int nearbyOothecaCount = CountNearbyOtherOothecas();
float oothecaPenalty = Mathf.Max(0f, 1.0f - (nearbyOothecaCount * Ext.nearbyOothecaPenaltyPerUnit));
multiplier *= oothecaPenalty;
qualityMultiplier = Mathf.Clamp(multiplier, 0f, 1.0f);
}
// 保存/加载
public override void ExposeData()
{
base.ExposeData();
Scribe_Values.Look(ref isIncubating, "isIncubating", false);
Scribe_Values.Look(ref incubationProgress, "incubationProgress", 0f);
Scribe_Values.Look(ref incubationDuration, "incubationDuration", 0f);
Scribe_Defs.Look(ref incubatingThingDef, "incubatingThingDef");
Scribe_References.Look(ref assignedLarva, "assignedLarva");
Scribe_Values.Look(ref larvaOperateTicksRemaining, "larvaOperateTicksRemaining", 0);
Scribe_Values.Look(ref speedMultiplier, "speedMultiplier", 1.0f);
Scribe_Values.Look(ref lastMultiplierUpdateTick, "lastMultiplierUpdateTick", -1);
Scribe_Values.Look(ref qualityMultiplier, "qualityMultiplier", 1.0f);
Scribe_Values.Look(ref qualityProgress, "qualityProgress", 0f);
Scribe_Values.Look(ref qualityTotal, "qualityTotal", 0f);
}
}
}

View File

@@ -0,0 +1,332 @@
// File: Comps/CompProperties_EquipmentIncubatorData.cs
using RimWorld;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using Verse;
namespace ArachnaeSwarm
{
// 装备孵化配置
public class EquipmentIncubationConfig : IExposable
{
public ThingDef thingDef;
public ResearchProjectDef requiredResearch;
public float daysRequired; // 从stat中读取
public string buttonIconPath;
// 缓存的生产时间避免重复获取stat
private float? cachedDaysRequired;
public EquipmentIncubationConfig() { }
public EquipmentIncubationConfig(ThingDef thingDef, ResearchProjectDef requiredResearch = null,
string buttonIconPath = null)
{
this.thingDef = thingDef;
this.requiredResearch = requiredResearch;
this.buttonIconPath = buttonIconPath;
cachedDaysRequired = null;
}
public void ExposeData()
{
Scribe_Defs.Look(ref thingDef, "thingDef");
Scribe_Defs.Look(ref requiredResearch, "requiredResearch");
Scribe_Values.Look(ref buttonIconPath, "buttonIconPath");
// 不保存缓存值,重新计算
if (Scribe.mode == LoadSaveMode.LoadingVars)
{
cachedDaysRequired = null;
}
}
// 获取生产时间(天)
public float DaysRequired
{
get
{
if (cachedDaysRequired.HasValue)
return cachedDaysRequired.Value;
if (thingDef == null)
{
cachedDaysRequired = 1f;
return cachedDaysRequired.Value;
}
// 从stat中读取ARA_IncubationTime
var statDef = DefDatabase<StatDef>.GetNamedSilentFail("ARA_IncubationTime");
if (statDef != null)
{
cachedDaysRequired = thingDef.GetStatValueAbstract(statDef, null);
}
else
{
// 默认值
cachedDaysRequired = 1f;
}
return cachedDaysRequired.Value;
}
}
// 检查是否满足研究要求
public bool IsResearchComplete => requiredResearch == null || requiredResearch.IsFinished;
// 获取描述
public string GetDescription()
{
var builder = new StringBuilder();
if (thingDef != null)
{
builder.AppendLine(thingDef.description ?? "ARA_EquipmentIncubator.NoDescription".Translate());
builder.AppendLine();
builder.AppendLine("ARA_EquipmentIncubator.IncubationTime".Translate(DaysRequired));
}
if (requiredResearch != null)
{
if (requiredResearch.IsFinished)
builder.AppendLine("ARA_EquipmentIncubator.ResearchCompleted".Translate(requiredResearch.LabelCap));
else
builder.AppendLine("ARA_EquipmentIncubator.ResearchRequired".Translate(requiredResearch.LabelCap));
}
return builder.ToString().TrimEndNewlines();
}
}
// 装备孵化器组件属性
public class CompProperties_EquipmentIncubatorData : CompProperties
{
// 支持手动指定配置列表(可选)
public List<EquipmentIncubationConfig> incubationConfigs;
// 默认选择索引
public int defaultIndex = 0;
// Gizmo相关配置
public string buttonLabel = "ARA_EquipmentIncubator.IncubateLabel";
public string buttonDesc = "ARA_EquipmentIncubator.ButtonDesc";
public string menuTitle = "ARA_EquipmentIncubator.MenuTitle";
public string defaultIconPath = "UI/Commands/Default";
// 是否自动扫描所有ThingDef来构建配置列表
public bool autoScanThingDefs = true;
// 手动指定要扫描的ThingDef类型可选
public List<string> thingDefCategories;
public List<ThingCategoryDef> thingCategoryDefs;
public CompProperties_EquipmentIncubatorData()
{
compClass = typeof(CompEquipmentIncubatorData);
}
}
// 装备孵化器数据组件
public class CompEquipmentIncubatorData : ThingComp
{
private CompProperties_EquipmentIncubatorData Props => (CompProperties_EquipmentIncubatorData)props;
// 当前选择的配置索引
private int selectedIndex = -1;
// 缓存的配置列表
private List<EquipmentIncubationConfig> cachedConfigs;
private bool configsBuilt = false;
// 公开获取孵化配置列表的方法
public List<EquipmentIncubationConfig> IncubationConfigs
{
get
{
if (!configsBuilt)
{
BuildIncubationConfigs();
}
return cachedConfigs ?? new List<EquipmentIncubationConfig>();
}
}
// 获取当前选择的配置
public EquipmentIncubationConfig SelectedConfig
{
get
{
var configs = IncubationConfigs;
if (configs.Count == 0) return null;
// 初始化选择
if (selectedIndex == -1)
{
selectedIndex = Mathf.Clamp(Props.defaultIndex, 0, configs.Count - 1);
}
if (selectedIndex < 0 || selectedIndex >= configs.Count)
selectedIndex = 0;
return configs[selectedIndex];
}
}
// 获取当前选择的ThingDef
public ThingDef SelectedThingDef => SelectedConfig?.thingDef;
// 构建孵化配置列表
private void BuildIncubationConfigs()
{
cachedConfigs = new List<EquipmentIncubationConfig>();
configsBuilt = true;
// 优先使用手动配置的列表
if (Props.incubationConfigs != null && Props.incubationConfigs.Count > 0)
{
foreach (var config in Props.incubationConfigs)
{
if (config?.thingDef != null)
{
cachedConfigs.Add(config);
}
}
if (cachedConfigs.Count > 0)
return;
}
// 如果没有手动配置且启用了自动扫描则扫描所有ThingDef
if (Props.autoScanThingDefs)
{
ScanThingDefsForConfigs();
}
}
// 扫描所有ThingDef来构建配置列表
private void ScanThingDefsForConfigs()
{
var allThingDefs = DefDatabase<ThingDef>.AllDefsListForReading;
foreach (var thingDef in allThingDefs)
{
// 检查该ThingDef是否包含CompProperties_ExtraIncubationInfo组件
var extraInfoProps = thingDef.comps?.FirstOrDefault(c => c is CompProperties_ExtraIncubationInfo)
as CompProperties_ExtraIncubationInfo;
if (extraInfoProps == null)
continue;
// 检查cocoonDefs是否包含当前建筑
bool isForThisCocoon = false;
if (extraInfoProps.cocoonDefs != null && extraInfoProps.cocoonDefs.Count > 0)
{
isForThisCocoon = extraInfoProps.cocoonDefs.Contains(parent.def);
}
else if (extraInfoProps.cocoonDef != null)
{
// 向后兼容检查单个cocoonDef
isForThisCocoon = extraInfoProps.cocoonDef == parent.def;
}
if (!isForThisCocoon)
continue;
// 检查是否有ARA_IncubationTime这个stat
var incubationTimeStat = DefDatabase<StatDef>.GetNamedSilentFail("ARA_IncubationTime");
if (incubationTimeStat == null)
{
Log.Warning($"ThingDef {thingDef.defName} has CompProperties_ExtraIncubationInfo but ARA_IncubationTime stat is not defined.");
continue;
}
// 获取孵化时间
float daysRequired = thingDef.GetStatValueAbstract(incubationTimeStat, null);
if (daysRequired <= 0)
{
Log.Warning($"ThingDef {thingDef.defName} has invalid incubation time: {daysRequired}");
continue;
}
// 创建配置
var config = new EquipmentIncubationConfig
{
thingDef = thingDef,
daysRequired = daysRequired,
// 可以在这里添加其他配置,比如所需研究等
// requiredResearch = ...
// buttonIconPath = ...
};
cachedConfigs.Add(config);
}
// 按物品名称排序
cachedConfigs.Sort((a, b) => string.Compare(a.thingDef?.label ?? "", b.thingDef?.label ?? ""));
Log.Message($"Built {cachedConfigs.Count} equipment incubation configs for {parent.def.defName}");
}
// 切换到特定索引
public void SwitchToConfig(int index)
{
if (index >= 0 && index < IncubationConfigs.Count)
{
selectedIndex = index;
}
}
// 检查配置是否可用(研究是否完成)
public bool IsConfigAvailable(int index)
{
if (index < 0 || index >= IncubationConfigs.Count)
return false;
var config = IncubationConfigs[index];
return config?.IsResearchComplete ?? false;
}
// 获取配置索引
public int GetSelectedIndex()
{
return selectedIndex;
}
// 存档加载
public override void PostExposeData()
{
base.PostExposeData();
Scribe_Values.Look(ref selectedIndex, "selectedIndex", -1);
Scribe_Values.Look(ref configsBuilt, "configsBuilt", false);
if (Scribe.mode == LoadSaveMode.LoadingVars)
{
// 重置缓存,在需要时重新构建
cachedConfigs = null;
configsBuilt = false;
}
}
// 在建筑信息中显示额外信息
public override string CompInspectStringExtra()
{
var current = SelectedConfig;
if (current != null && current.thingDef != null)
{
string status = "ARA_EquipmentIncubator.IncubationTarget".Translate(current.thingDef.LabelCap);
if (current.requiredResearch != null && !current.requiredResearch.IsFinished)
{
status += " (" + "ARA_EquipmentIncubator.Requires".Translate() + ": " + current.requiredResearch.LabelCap + ")";
}
return status;
}
return base.CompInspectStringExtra();
}
}
}

View File

@@ -0,0 +1,237 @@
// File: ITabs/ITab_EquipmentOotheca_Incubation.cs
using UnityEngine;
using Verse;
using System.Collections.Generic;
using RimWorld;
using Verse.Sound;
namespace ArachnaeSwarm
{
public class ITab_EquipmentOotheca_Incubation : ITab
{
private const float BarHeight = 20f;
private const float Margin = 10f;
private const float LabelHeight = 30f;
private const float SmallLabelHeight = 20f;
private const float ButtonHeight = 25f;
private const float TabWidth = 320f;
private const float TabHeight = 420f;
private Vector2 scrollPosition = Vector2.zero;
private const float ViewHeight = 450f;
public override bool IsVisible
{
get
{
return SelThing.Faction == Faction.OfPlayer;
}
}
public ITab_EquipmentOotheca_Incubation()
{
size = new Vector2(TabWidth, TabHeight);
labelKey = "ARA_EquipmentIncubator.IncubationTab";
tutorTag = "EquipmentIncubation";
}
protected override void FillTab()
{
Rect rect = new Rect(0f, 0f, size.x, size.y).ContractedBy(Margin);
Widgets.DrawMenuSection(rect);
Building_EquipmentOotheca ootheca = SelThing as Building_EquipmentOotheca;
if (ootheca == null)
{
Widgets.Label(rect, "ARA_EquipmentIncubator.NotAnEquipmentOotheca".Translate());
return;
}
rect = rect.ContractedBy(5f);
Rect viewRect = new Rect(0f, 0f, rect.width - 16f, ViewHeight);
Rect scrollRect = new Rect(rect.x, rect.y, rect.width, rect.height);
Widgets.BeginScrollView(scrollRect, ref scrollPosition, viewRect);
float curY = 0f;
Rect titleRect = new Rect(0f, curY, viewRect.width, LabelHeight);
string title = "ARA_EquipmentIncubator.IncubationProgress".Translate();
Text.Font = GameFont.Medium;
Widgets.Label(titleRect, title);
Text.Font = GameFont.Small;
curY += LabelHeight + 15f;
float buttonWidth = (viewRect.width - 10f) / 2f;
Rect speedButtonRect = new Rect(0f, curY, buttonWidth, ButtonHeight);
string speedText = "ARA_EquipmentIncubator.Speed".Translate() + ": " + ootheca.SpeedMultiplier.ToStringPercent();
Color speedColor = Color.white;
if (ootheca.SpeedMultiplier != 1.0f)
{
speedColor = ootheca.SpeedMultiplier > 1.0f ?
new Color(0.2f, 0.8f, 0.2f) :
new Color(0.8f, 0.8f, 0.2f);
}
if (Widgets.ButtonText(speedButtonRect, speedText, true, true, speedColor))
{
// 可选:显示详细信息
}
TooltipHandler.TipRegion(speedButtonRect, () => ootheca.GetSpeedFactorsDescription(), 987654321);
Rect qualityButtonRect = new Rect(buttonWidth + 10f, curY, buttonWidth, ButtonHeight);
string qualityText = "ARA_EquipmentIncubator.Quality".Translate() + ": " + ootheca.QualityMultiplier.ToStringPercent();
Color qualityColor = Color.white;
float qualityMultiplier = ootheca.QualityMultiplier;
if (qualityMultiplier != 1.0f)
{
if (qualityMultiplier > 0.9f)
qualityColor = new Color(0.2f, 0.8f, 0.2f);
else if (qualityMultiplier > 0.7f)
qualityColor = new Color(0.8f, 0.8f, 0.2f);
else if (qualityMultiplier > 0.5f)
qualityColor = new Color(0.9f, 0.6f, 0.2f);
else
qualityColor = new Color(0.8f, 0.2f, 0.2f);
}
if (Widgets.ButtonText(qualityButtonRect, qualityText, true, true, qualityColor))
{
// 可选:显示详细信息
}
TooltipHandler.TipRegion(qualityButtonRect, () => ootheca.GetQualityFactorsDescription(), 987654322);
curY += ButtonHeight + 25f;
if (ootheca.isIncubating && ootheca.incubatingThingDef != null)
{
float progressPercent = ootheca.AdjustedProgressPercent;
float qualityPercent = ootheca.QualityPercent;
float daysRemaining = ootheca.GetRemainingDays();
float hoursRemaining = ootheca.GetRemainingHours();
Rect targetRect = new Rect(0f, curY, viewRect.width, SmallLabelHeight);
Widgets.Label(targetRect, "ARA_EquipmentIncubator.Target".Translate() + ": " + ootheca.incubatingThingDef.LabelCap);
curY += SmallLabelHeight + 20f;
Rect progressBarRect = new Rect(0f, curY, viewRect.width, BarHeight);
Rect progressLabelRect = new Rect(progressBarRect.x, progressBarRect.y - 20, progressBarRect.width, 18);
Text.Anchor = TextAnchor.MiddleCenter;
GUI.color = new Color(0.9f, 0.9f, 0.9f, 1f);
Widgets.Label(progressLabelRect, "ARA_EquipmentIncubator.IncubationProgressLabel".Translate());
Text.Anchor = TextAnchor.UpperLeft;
GUI.color = Color.white;
Widgets.FillableBar(progressBarRect, progressPercent, SolidColorMaterials.NewSolidColorTexture(new Color(0.2f, 0.8f, 0.2f, 0.5f)));
Widgets.FillableBarChangeArrows(progressBarRect, progressPercent);
string progressText = $"{progressPercent:P0}";
Text.Anchor = TextAnchor.MiddleCenter;
Widgets.Label(progressBarRect, progressText);
Text.Anchor = TextAnchor.UpperLeft;
curY += BarHeight + 30f;
Rect qualityBarRect = new Rect(0f, curY, viewRect.width, BarHeight);
Rect qualityLabelRect = new Rect(qualityBarRect.x, qualityBarRect.y - 20, qualityBarRect.width, 18);
Text.Anchor = TextAnchor.MiddleCenter;
GUI.color = new Color(0.9f, 0.9f, 0.9f, 1f);
Widgets.Label(qualityLabelRect, "ARA_EquipmentIncubator.QualityProgress".Translate());
Text.Anchor = TextAnchor.UpperLeft;
GUI.color = Color.white;
Widgets.FillableBar(qualityBarRect, qualityPercent, SolidColorMaterials.NewSolidColorTexture(new Color(0.1f, 0.4f, 0.8f, 0.5f)));
string qualityProgressText = $"{qualityPercent:P0}";
Text.Anchor = TextAnchor.MiddleCenter;
Widgets.Label(qualityBarRect, qualityProgressText);
Text.Anchor = TextAnchor.UpperLeft;
Rect targetQualityRect = new Rect(qualityBarRect.x + qualityBarRect.width - 40, qualityBarRect.y, 40, BarHeight);
GUI.color = new Color(0.8f, 0.8f, 0.8f, 0.7f);
Text.Anchor = TextAnchor.MiddleRight;
Widgets.Label(targetQualityRect, $"{ootheca.QualityMultiplier:P0}");
Text.Anchor = TextAnchor.UpperLeft;
GUI.color = Color.white;
curY += BarHeight + 25f;
Rect timeRect = new Rect(0f, curY, viewRect.width, SmallLabelHeight);
string timeText = "ARA_EquipmentIncubator.TimeRemaining".Translate() + ": " + daysRemaining.ToString("F1") + " " + "ARA_EquipmentIncubator.Days".Translate();
if (hoursRemaining > 0.1f && daysRemaining < 1f)
{
timeText += " (" + hoursRemaining.ToString("F1") + " " + "ARA_EquipmentIncubator.Hours".Translate() + ")";
}
Widgets.Label(timeRect, timeText);
}
else if (ootheca.assignedLarva != null)
{
Rect statusRect = new Rect(0f, curY, viewRect.width, SmallLabelHeight * 2);
if (ootheca.larvaOperateTicksRemaining > 0)
{
float secondsRemaining = ootheca.larvaOperateTicksRemaining / 60f;
Widgets.Label(statusRect, "ARA_EquipmentIncubator.LarvaIsActivatingOotheca".Translate() + "\n" +
secondsRemaining.ToString("F1") + " " + "ARA_EquipmentIncubator.SecondsRemaining".Translate());
}
else
{
Widgets.Label(statusRect, "ARA_EquipmentIncubator.LarvaIsOnTheWay".Translate());
}
curY += SmallLabelHeight * 2 + 15f;
var config = ootheca.EquipmentIncubatorData?.SelectedConfig;
if (config != null)
{
curY += 10f;
Rect targetRect = new Rect(0f, curY, viewRect.width, SmallLabelHeight * 3);
string targetText = "ARA_EquipmentIncubator.ReadyToIncubate".Translate() + "\n" + config.thingDef.LabelCap;
if (!config.IsResearchComplete && config.requiredResearch != null)
{
targetText += "\n" + "(" + "ARA_EquipmentIncubator.Requires".Translate() + ": " + config.requiredResearch.LabelCap + ")";
}
Widgets.Label(targetRect, targetText);
}
}
else
{
var config = ootheca.EquipmentIncubatorData?.SelectedConfig;
if (config != null)
{
Rect targetRect = new Rect(0f, curY, viewRect.width, SmallLabelHeight * 3);
string targetText = "ARA_EquipmentIncubator.ReadyToIncubate".Translate() + "\n" + config.thingDef.LabelCap;
if (!config.IsResearchComplete && config.requiredResearch != null)
{
targetText += "\n" + "(" + "ARA_EquipmentIncubator.Requires".Translate() + ": " + config.requiredResearch.LabelCap + ")";
}
Widgets.Label(targetRect, targetText);
curY += SmallLabelHeight * 3 + 10f;
}
else
{
Rect noTargetRect = new Rect(0f, curY, viewRect.width, SmallLabelHeight);
Widgets.Label(noTargetRect, "ARA_EquipmentIncubator.NoIncubationTargetSelected".Translate());
curY += SmallLabelHeight + 10f;
}
}
curY += 20f;
viewRect.height = curY;
Widgets.EndScrollView();
}
protected override void UpdateSize()
{
size = new Vector2(TabWidth, TabHeight);
}
}
}

View File

@@ -0,0 +1,68 @@
// File: JobDrivers/JobDriver_OperateEquipmentIncubator.cs
using RimWorld;
using System.Collections.Generic;
using Verse;
using Verse.AI;
namespace ArachnaeSwarm
{
public class JobDriver_OperateEquipmentIncubator : JobDriver
{
private const int OperationDuration = 180;
private Building_EquipmentOotheca EquipmentOotheca => (Building_EquipmentOotheca)job.targetA.Thing;
public override bool TryMakePreToilReservations(bool errorOnFailed)
{
return pawn.Reserve(job.targetA, job, 1, -1, null, errorOnFailed);
}
protected override IEnumerable<Toil> MakeNewToils()
{
this.FailOnDespawnedNullOrForbidden(TargetIndex.A);
this.FailOn(() => EquipmentOotheca == null);
yield return Toils_Goto.GotoThing(TargetIndex.A, PathEndMode.InteractionCell)
.FailOnSomeonePhysicallyInteracting(TargetIndex.A);
yield return Toils_General.WaitWith(TargetIndex.A, 10, true, true);
var operate = new Toil();
operate.initAction = () =>
{
EquipmentOotheca?.NotifyLarvaArrived(pawn);
};
operate.tickAction = () =>
{
pawn.rotationTracker.FaceCell(EquipmentOotheca.Position);
};
operate.defaultCompleteMode = ToilCompleteMode.Delay;
operate.defaultDuration = OperationDuration;
operate.WithProgressBar(TargetIndex.A, () =>
(float)(OperationDuration - operate.actor.jobs.curDriver.ticksLeftThisToil) / OperationDuration);
yield return operate;
yield return new Toil
{
initAction = () =>
{
if (EquipmentOotheca != null && pawn != null && pawn.def.defName == "ArachnaeBase_Race_Larva")
{
EquipmentOotheca.NotifyLarvaOperationComplete(pawn);
pawn.Destroy(DestroyMode.Vanish);
}
},
defaultCompleteMode = ToilCompleteMode.Instant
};
}
public override string GetReport()
{
if (EquipmentOotheca != null)
{
return "ARA_EquipmentIncubator.ActivatingOotheca".Translate();
}
return base.GetReport();
}
}
}

View File

@@ -1,4 +1,5 @@
using RimWorld;
// File: Building_Ootheca.cs
using RimWorld;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
@@ -32,6 +33,22 @@ namespace ArachnaeSwarm
private float qualityProgress = 0f;
private float qualityTotal = 0f;
// 缓存的ModExtension
private OothecaIncubatorExtension cachedExtension;
// 获取ModExtension的辅助属性
private OothecaIncubatorExtension Ext
{
get
{
if (cachedExtension == null)
{
cachedExtension = def.GetModExtension<OothecaIncubatorExtension>() ?? OothecaIncubatorExtension.Default;
}
return cachedExtension;
}
}
// 属性访问器
public float SpeedMultiplier
{
@@ -65,92 +82,122 @@ namespace ArachnaeSwarm
{
var builder = new StringBuilder();
builder.AppendLine("IncubationSpeedFactors".Translate());
builder.AppendLine("ARA_OothecaIncubator.SpeedFactors".Translate());
builder.AppendLine();
// 1. 检查是否在孵化间中
bool inIncubatorRoom = IsInIncubatorRoom();
builder.AppendLine(inIncubatorRoom ?
"InIncubatorRoom".Translate() :
"NotInIncubatorRoom".Translate());
if (Ext.requiresIncubatorRoom)
{
builder.AppendLine(inIncubatorRoom ?
"ARA_OothecaIncubator.InIncubatorRoom".Translate() :
"ARA_OothecaIncubator.NotInIncubatorRoom".Translate());
}
// 2. 检查营养液数量
int nutrientSolutionCount = CountNearbyNutrientSolutions();
if (nutrientSolutionCount > 0)
{
builder.AppendLine("NutrientSolutions".Translate(nutrientSolutionCount, nutrientSolutionCount));
builder.AppendLine("ARA_OothecaIncubator.NutrientSolutions".Translate(
nutrientSolutionCount,
nutrientSolutionCount * Ext.nutrientSolutionBonusPerTile * 100));
}
else
{
builder.AppendLine("NoNutrientSolutionsNearby".Translate());
builder.AppendLine("ARA_OothecaIncubator.NoNutrientSolutionsNearby".Translate());
}
// 显示检测半径
builder.AppendLine();
builder.AppendLine("ARA_OothecaIncubator.NutrientDetectionRadius".Translate(Ext.nutrientSolutionRadius));
builder.AppendLine();
builder.AppendLine("TotalSpeedMultiplier".Translate(SpeedMultiplier.ToStringPercent()));
builder.AppendLine("ARA_OothecaIncubator.TotalSpeedMultiplier".Translate(SpeedMultiplier.ToStringPercent()));
return builder.ToString().TrimEndNewlines();
}
// 获取质量乘数的详细因子信息(用于工具提示)
public string GetQualityFactorsDescription()
{
var builder = new StringBuilder();
builder.AppendLine("IncubationQualityFactors".Translate());
builder.AppendLine("ARA_OothecaIncubator.QualityFactors".Translate());
builder.AppendLine();
// 1. 建筑血量损失百分比
float healthPercent = (float)HitPoints / MaxHitPoints;
builder.AppendLine("BuildingHealth".Translate(healthPercent.ToStringPercent()));
// 1. 建筑血量损失百分比(如果启用)
if (Ext.healthAffectsQuality)
{
float healthPercent = (float)HitPoints / MaxHitPoints;
builder.AppendLine("ARA_OothecaIncubator.BuildingHealth".Translate(healthPercent.ToStringPercent()));
}
// 2. 房间的ARA_IncubatorQualityFactor
float roomFactor = GetRoomQualityFactor();
builder.AppendLine(roomFactor == 1.0f ?
"RoomFactorNormal".Translate() :
$"{(roomFactor > 1.0f ? "" : "")} {"RoomFactorModified".Translate()}{roomFactor.ToStringPercent()}");
// 2. 房间的ARA_IncubatorQualityFactor(如果启用)
if (Ext.useRoomQualityFactor)
{
float roomFactor = GetRoomQualityFactor();
builder.AppendLine(roomFactor == 1.0f ?
"ARA_OothecaIncubator.RoomFactorNormal".Translate() :
$"{(roomFactor > 1.0f ? "" : "")} {"ARA_OothecaIncubator.RoomFactorModified".Translate()}{roomFactor.ToStringPercent()}");
}
// 3. 附近每一个ARA_Pawn_Ootheca,每一个-10%
// 3. 附近每一个ARA_Pawn_Ootheca的惩罚
int nearbyOothecaCount = CountNearbyOtherOothecas();
if (nearbyOothecaCount > 0)
{
builder.AppendLine("NearbyOothecas".Translate(nearbyOothecaCount, Mathf.Min(nearbyOothecaCount * 10, 100)));
builder.AppendLine("ARA_OothecaIncubator.NearbyOothecas".Translate(
nearbyOothecaCount,
Mathf.Min(nearbyOothecaCount * Ext.nearbyOothecaPenaltyPerUnit * 100, 100)));
}
else
{
builder.AppendLine("NoNearbyOothecas".Translate());
builder.AppendLine("ARA_OothecaIncubator.NoNearbyOothecas".Translate());
}
// 显示检测半径
builder.AppendLine();
builder.AppendLine("ARA_OothecaIncubator.OothecaDetectionRadius".Translate(Ext.nearbyOothecaRadius));
builder.AppendLine();
builder.AppendLine("TotalQualityMultiplier".Translate(QualityMultiplier.ToStringPercent()));
builder.AppendLine("ARA_OothecaIncubator.TotalQualityMultiplier".Translate(QualityMultiplier.ToStringPercent()));
return builder.ToString().TrimEndNewlines();
}
// 构建呼叫幼虫描述
private string BuildCallLarvaDescription(IncubationConfig config)
{
var builder = new StringBuilder();
builder.AppendLine("CallALarvaToActivate".Translate());
builder.AppendLine("ARA_OothecaIncubator.CallLarvaTitle".Translate());
builder.AppendLine();
builder.AppendLine("LarvaWillComeToTheOotheca".Translate());
builder.AppendLine("ARA_OothecaIncubator.LarvaWillCome".Translate());
builder.AppendLine(config.pawnKind.LabelCap);
builder.AppendLine();
// 显示幼虫搜索半径
if (Ext.larvaSearchRadius < 999f)
{
builder.AppendLine("ARA_OothecaIncubator.LarvaSearchRadius".Translate(Ext.larvaSearchRadius));
}
return builder.ToString().TrimEndNewlines();
}
// 呼叫幼虫
private void CallLarva()
{
// 检查是否已经在孵化中或有幼虫在任务中
if (isIncubating)
{
Messages.Message("AlreadyIncubating".Translate() + " " + "CancelCurrentIncubationFirst".Translate(),
Messages.Message("ARA_OothecaIncubator.AlreadyIncubating".Translate() + " " + "ARA_OothecaIncubator.CancelFirst".Translate(),
MessageTypeDefOf.RejectInput);
return;
}
if (assignedLarva != null)
{
Messages.Message("LarvaAlreadyOnTheWay".Translate(),
Messages.Message("ARA_OothecaIncubator.LarvaAlreadyOnWay".Translate(),
MessageTypeDefOf.RejectInput);
return;
}
@@ -159,7 +206,7 @@ namespace ArachnaeSwarm
var larva = FindLarva();
if (larva == null)
{
Messages.Message("NoAvailableLarvaeFound".Translate() + " " + "LarvaMustBeOfRace".Translate(),
Messages.Message("ARA_OothecaIncubator.NoLarvaeFound".Translate() + " " + "ARA_OothecaIncubator.LarvaMustBeRace".Translate(),
MessageTypeDefOf.RejectInput);
return;
}
@@ -171,9 +218,10 @@ namespace ArachnaeSwarm
assignedLarva = larva;
Messages.Message("LarvaCalled".Translate() + " " + "ItWillArriveShortly".Translate(),
Messages.Message("ARA_OothecaIncubator.LarvaCalled".Translate() + " " + "ARA_OothecaIncubator.ArriveShortly".Translate(),
MessageTypeDefOf.PositiveEvent);
}
// 幼虫开始操作
public void NotifyLarvaArrived(Pawn larva)
{
@@ -189,9 +237,10 @@ namespace ArachnaeSwarm
assignedLarva = larva;
// 显示消息
Messages.Message("LarvaHasArrived".Translate() + " " + "AndIsActivatingTheOotheca".Translate(),
Messages.Message("ARA_OothecaIncubator.LarvaArrived".Translate() + " " + "ARA_OothecaIncubator.ActivatingOotheca".Translate(),
MessageTypeDefOf.SilentInput);
}
// 幼虫完成操作由JobDriver调用
public void NotifyLarvaOperationComplete(Pawn larva)
{
@@ -228,10 +277,11 @@ namespace ArachnaeSwarm
larvaOperateTicksRemaining = 0;
// 显示消息
Messages.Message("IncubationStartedFor".Translate() + " " + incubatingPawnKind.LabelCap + ". " +
"ProcessWillCompleteIn".Translate() + " " + config.daysRequired + " " + "DaysBaseTime".Translate(),
Messages.Message("ARA_OothecaIncubator.IncubationStarted".Translate() + " " + incubatingPawnKind.LabelCap + ". " +
"ARA_OothecaIncubator.ProcessWillComplete".Translate() + " " + config.daysRequired + " " + "ARA_OothecaIncubator.DaysBaseTime".Translate(),
MessageTypeDefOf.PositiveEvent);
}
// 取消孵化
private void CancelIncubation()
{
@@ -244,9 +294,10 @@ namespace ArachnaeSwarm
qualityProgress = 0f;
qualityTotal = 0f;
Messages.Message("IncubationCancelled".Translate() + " " + "ContentsLost".Translate(),
Messages.Message("ARA_OothecaIncubator.IncubationCancelled".Translate() + " " + "ARA_OothecaIncubator.ContentsLost".Translate(),
MessageTypeDefOf.NeutralEvent);
}
// 完成孵化
private void CompleteIncubation()
{
@@ -277,16 +328,19 @@ namespace ArachnaeSwarm
qualityTotal = 0f;
// 显示消息
string qualityText = finalQualityPercent >= 0.9f ? "QualityExcellent".Translate() :
finalQualityPercent >= 0.7f ? "QualityGood".Translate() :
finalQualityPercent >= 0.5f ? "QualityAverage".Translate() :
finalQualityPercent >= 0.3f ? "QualityPoor".Translate() : "QualityVeryPoor".Translate();
string qualityText = finalQualityPercent >= 0.9f ? "ARA_OothecaIncubator.QualityExcellent".Translate() :
finalQualityPercent >= 0.7f ? "ARA_OothecaIncubator.QualityGood".Translate() :
finalQualityPercent >= 0.5f ? "ARA_OothecaIncubator.QualityAverage".Translate() :
finalQualityPercent >= 0.3f ? "ARA_OothecaIncubator.QualityPoor".Translate() : "ARA_OothecaIncubator.QualityVeryPoor".Translate();
Messages.Message("IncubationComplete".Translate() + " " + pawn.LabelCap + " " +
"HasEmergedWith".Translate() + " " + qualityText + " " +
"quality".Translate() + " (" + finalQualityPercent.ToStringPercent() + ").",
Messages.Message("ARA_OothecaIncubator.IncubationComplete".Translate() + " " + pawn.LabelCap + " " +
"ARA_OothecaIncubator.HasEmergedWith".Translate() + " " + qualityText + " " +
"ARA_OothecaIncubator.Quality".Translate() + " (" + finalQualityPercent.ToStringPercent() + ").",
MessageTypeDefOf.PositiveEvent);
Destroy();
}
// 显示额外信息(简化版本,只显示速率,不显示因子)
public override string GetInspectString()
{
@@ -305,23 +359,23 @@ namespace ArachnaeSwarm
float hoursRemaining = GetRemainingHours();
if (builder.Length > 0) builder.AppendLine();
builder.Append("Incubating".Translate() + ": " + incubatingPawnKind.LabelCap);
builder.Append("ARA_OothecaIncubator.Incubating".Translate() + ": " + incubatingPawnKind.LabelCap);
builder.AppendLine();
builder.Append("Progress".Translate() + ": " + progressPercent.ToStringPercent());
builder.Append("ARA_OothecaIncubator.Progress".Translate() + ": " + progressPercent.ToStringPercent());
builder.AppendLine();
// 显示剩余时间
string timeText = "TimeRemaining".Translate() + ": " + daysRemaining.ToString("F1") + " " + "Days".Translate();
string timeText = "ARA_OothecaIncubator.TimeRemaining".Translate() + ": " + daysRemaining.ToString("F1") + " " + "ARA_OothecaIncubator.Days".Translate();
if (hoursRemaining > 0.1f && daysRemaining < 1f)
{
timeText += " (" + hoursRemaining.ToString("F1") + " " + "Hours".Translate() + ")";
timeText += " (" + hoursRemaining.ToString("F1") + " " + "ARA_OothecaIncubator.Hours".Translate() + ")";
}
builder.Append(timeText);
// 显示速度和质量(简化版本)
builder.AppendLine();
builder.Append("Speed".Translate() + ": " + SpeedMultiplier.ToStringPercent() + ", " +
"Quality".Translate() + ": " + QualityMultiplier.ToStringPercent());
builder.Append("ARA_OothecaIncubator.Speed".Translate() + ": " + SpeedMultiplier.ToStringPercent() + ", " +
"ARA_OothecaIncubator.Quality".Translate() + ": " + QualityMultiplier.ToStringPercent());
}
else if (assignedLarva != null)
{
@@ -329,11 +383,11 @@ namespace ArachnaeSwarm
if (larvaOperateTicksRemaining > 0)
{
float secondsRemaining = larvaOperateTicksRemaining / 60f;
builder.Append("LarvaIsOperating".Translate() + ": " + secondsRemaining.ToString("F1") + " " + "SRemaining".Translate());
builder.Append("ARA_OothecaIncubator.LarvaOperating".Translate() + ": " + secondsRemaining.ToString("F1") + " " + "ARA_OothecaIncubator.SRemaining".Translate());
}
else
{
builder.Append("LarvaIsOnTheWay".Translate());
builder.Append("ARA_OothecaIncubator.LarvaOnWay".Translate());
}
}
else if (!isIncubating)
@@ -342,12 +396,12 @@ namespace ArachnaeSwarm
if (config != null)
{
if (builder.Length > 0) builder.AppendLine();
builder.Append("Target".Translate() + ": " + config.pawnKind.LabelCap);
builder.Append("ARA_OothecaIncubator.Target".Translate() + ": " + config.pawnKind.LabelCap);
// 只显示当前乘数,不显示条件详情
builder.AppendLine();
builder.Append("SpeedMultiplier".Translate() + ": " + SpeedMultiplier.ToStringPercent() + ", " +
"QualityMultiplier".Translate() + ": " + QualityMultiplier.ToStringPercent());
builder.Append("ARA_OothecaIncubator.SpeedMultiplier".Translate() + ": " + SpeedMultiplier.ToStringPercent() + ", " +
"ARA_OothecaIncubator.QualityMultiplier".Translate() + ": " + QualityMultiplier.ToStringPercent());
}
}
@@ -404,9 +458,9 @@ namespace ArachnaeSwarm
{
yield return new Command_Action
{
defaultLabel = "CallLarva".Translate(),
defaultLabel = "ARA_OothecaIncubator.CallLarva".Translate(),
defaultDesc = BuildCallLarvaDescription(config),
icon = ContentFinder<Texture2D>.Get("UI/Commands/CallLarva", false) ?? BaseContent.BadTex,
icon = ContentFinder<Texture2D>.Get("ArachnaeSwarm/UI/Commands/ARA_CallLarva", false) ?? BaseContent.BadTex,
action = CallLarva,
hotKey = KeyBindingDefOf.Misc3
};
@@ -417,8 +471,8 @@ namespace ArachnaeSwarm
{
yield return new Command_Action
{
defaultLabel = "CancelIncubation".Translate(),
defaultDesc = "CancelIncubationDesc".Translate(),
defaultLabel = "ARA_OothecaIncubator.CancelIncubation".Translate(),
defaultDesc = "ARA_OothecaIncubator.CancelIncubationDesc".Translate(),
icon = ContentFinder<Texture2D>.Get("UI/Commands/Cancel", false) ?? TexCommand.ClearPrioritizedWork,
action = CancelIncubation
};
@@ -461,9 +515,9 @@ namespace ArachnaeSwarm
{
if (config != null && config.pawnKind != null)
{
return (props?.buttonLabel ?? "Incubate: {0}").Translate(config.pawnKind.LabelCap);
return (props?.buttonLabel ?? "ARA_OothecaIncubator.IncubateLabel").Translate(config.pawnKind.LabelCap);
}
return (props?.buttonLabel ?? "Incubate: {0}").Translate("None");
return (props?.buttonLabel ?? "ARA_OothecaIncubator.IncubateLabel").Translate("None");
}
// 构建切换按钮描述
@@ -472,7 +526,7 @@ namespace ArachnaeSwarm
var builder = new StringBuilder();
// 第一部分:按钮功能说明
builder.AppendLine((props?.buttonDesc ?? "IncubatorButtonDesc").Translate());
builder.AppendLine((props?.buttonDesc ?? "ARA_OothecaIncubator.ButtonDesc").Translate());
builder.AppendLine();
if (config != null)
@@ -480,14 +534,14 @@ namespace ArachnaeSwarm
// 第二部分:当前选择的详细信息
if (config.pawnKind != null)
{
builder.AppendLine($"IncubatorButtonLabel".Translate(config.pawnKind.LabelCap));
builder.AppendLine($"ARA_OothecaIncubator.ButtonLabel".Translate(config.pawnKind.LabelCap));
if (!string.IsNullOrEmpty(config.pawnKind.description))
{
builder.AppendLine(config.pawnKind.description);
}
}
builder.AppendLine($"IncubationTime".Translate(config.daysRequired));
builder.AppendLine($"ARA_OothecaIncubator.IncubationTime".Translate(config.daysRequired));
if (config.requiredResearch != null)
{
@@ -503,7 +557,7 @@ namespace ArachnaeSwarm
}
builder.AppendLine();
builder.AppendLine("IncubatorButtonDesc".Translate());
builder.AppendLine("ARA_OothecaIncubator.ButtonDesc".Translate());
return builder.ToString().TrimEndNewlines();
}
@@ -555,7 +609,7 @@ namespace ArachnaeSwarm
{
option.Label = prefix + label;
option.Disabled = true;
option.tooltip = description + "\n\n "+ "ResearchRequired".Translate() + " " + config.requiredResearch.LabelCap;
option.tooltip = description + "\n\n "+ "ARA_OothecaIncubator.ResearchRequired".Translate() + " " + config.requiredResearch.LabelCap;
}
options.Add(option);
@@ -564,7 +618,7 @@ namespace ArachnaeSwarm
if (options.Count > 0)
{
Find.WindowStack.Add(new FloatMenu(options,
(props?.menuTitle ?? "Select Incubation Target").Translate()));
(props?.menuTitle ?? "ARA_OothecaIncubator.MenuTitle").Translate()));
}
}
@@ -577,24 +631,35 @@ namespace ArachnaeSwarm
var config = IncubatorData.SelectedConfig;
if (config != null && config.pawnKind != null)
{
Messages.Message($"Incubation target switched to: {config.pawnKind.LabelCap}",
Messages.Message($"ARA_OothecaIncubator.TargetSwitched".Translate(config.pawnKind.LabelCap),
this, MessageTypeDefOf.SilentInput);
}
}
}
// 查找幼虫 - 现在通过种族查找
// 查找幼虫 - 现在通过种族查找,并考虑搜索半径
private Pawn FindLarva()
{
// 查找地图中属于玩家派系的ArachnaeBase_Race_Larva幼虫
var map = Map;
if (map == null) return null;
// 获取搜索半径
float searchRadius = Ext.larvaSearchRadius;
foreach (var pawn in map.mapPawns.SpawnedPawnsInFaction(Faction))
{
// 检查pawn种族是否为幼虫
if (pawn.def.defName == "ArachnaeBase_Race_Larva")
{
// 检查是否在搜索半径内如果搜索半径小于999
if (searchRadius < 999f)
{
float distance = pawn.Position.DistanceTo(Position);
if (distance > searchRadius)
continue;
}
// 检查pawn是否能够移动且没有其他重要任务
if (!pawn.Downed && !pawn.InMentalState &&
pawn.mindState != null &&
@@ -647,62 +712,33 @@ namespace ArachnaeSwarm
// 应用质量效果到生成的pawn
private void ApplyQualityEffects(Pawn pawn, float qualityPercent)
{
//// 质量影响根据质量百分比调整pawn的属性
//if (qualityPercent < 1.0f)
//{
// // 1. 健康影响
// float healthFactor = Mathf.Lerp(0.5f, 1.0f, qualityPercent);
// if (healthFactor < 1.0f)
// {
// // 减少最大生命值
// foreach (var part in pawn.health.hediffSet.GetNotMissingParts())
// {
// var healthDiff = part.def.GetMaxHealth(pawn) * (1.0f - healthFactor);
// if (healthDiff > 0)
// {
// pawn.health.AddHediff(HediffDefOf.Bruise, part);
// }
// }
// }
// // 2. 年龄影响如果质量低pawn可能会以较大年龄出生
// if (qualityPercent < 0.6f)
// {
// float ageBonus = (1.0f - qualityPercent) * 5; // 最多增加5岁
// pawn.ageTracker.AgeBiologicalTicks += (long)(ageBonus * 3600000f); // 每岁约3600000ticks
// }
// // 3. 能力影响
// if (pawn.skills != null && qualityPercent < 0.8f)
// {
// float skillFactor = Mathf.Lerp(0.5f, 1.0f, qualityPercent);
// foreach (var skill in pawn.skills.skills)
// {
// skill.levelInt = Mathf.RoundToInt(skill.levelInt * skillFactor);
// }
// }
//}
// 质量影响根据质量百分比调整pawn的属性
// 这里可以添加具体的质量效果逻辑
}
// 检查是否在孵化间中
private bool IsInIncubatorRoom()
{
// 如果不要求孵化间总是返回true
if (!Ext.requiresIncubatorRoom)
return true;
var room = this.GetRoom();
if (room == null) return false;
return room.Role != null && room.Role.defName == "ARA_Incubator_Room";
}
// 计算周围5格内的营养液数量
// 计算周围指定半径内的营养液数量
private int CountNearbyNutrientSolutions()
{
var map = Map;
if (map == null) return 0;
int count = 0;
int radius = 5; // 4格半径
int radius = Ext.NutrientSolutionRadiusInt;
// 检查周围5格范围内的所有单元格
// 检查指定半径范围内的所有单元格
for (int x = -radius; x <= radius; x++)
{
for (int y = -radius; y <= radius; y++)
@@ -728,6 +764,10 @@ namespace ArachnaeSwarm
// 计算房间质量因子
private float GetRoomQualityFactor()
{
// 如果不使用房间质量因子返回1.0
if (!Ext.useRoomQualityFactor)
return 1.0f;
var room = this.GetRoom();
if (room == null) return 1.0f;
@@ -759,17 +799,32 @@ namespace ArachnaeSwarm
// 检查是否为ARA_Pawn_Ootheca
if (building.def.defName == "ARA_Pawn_Ootheca")
{
// 检查是否在同一个房间或附近
var room = building.GetRoom();
if (room != null)
bool isNearby = false;
// 检查是否在同房间内
if (Ext.checkSameRoomForOotheca)
{
// 如果在同一个房间或者距离较近5格内
float distance = building.Position.DistanceTo(this.Position);
if (room == this.GetRoom() || distance <= 5f)
var room = building.GetRoom();
if (room != null && room == this.GetRoom())
{
count++;
isNearby = true;
}
}
// 检查是否在指定半径内
if (!isNearby)
{
float distance = building.Position.DistanceTo(this.Position);
if (distance <= Ext.nearbyOothecaRadius)
{
isNearby = true;
}
}
if (isNearby)
{
count++;
}
}
}
@@ -781,15 +836,15 @@ namespace ArachnaeSwarm
{
float multiplier = 1.0f;
// 1. 检查是否在孵化间中
if (!IsInIncubatorRoom())
// 1. 检查是否在孵化间中(如果要求)
if (Ext.requiresIncubatorRoom && !IsInIncubatorRoom())
{
multiplier *= 0.8f; // 不在孵化间中速度80%
multiplier *= Ext.speedPenaltyOutsideIncubator; // 应用惩罚
}
// 2. 计算周围营养液的加成
int nutrientSolutionCount = CountNearbyNutrientSolutions();
float nutrientBonus = 1.0f + (nutrientSolutionCount * 0.01f); // 每个+1%
float nutrientBonus = 1.0f + (nutrientSolutionCount * Ext.nutrientSolutionBonusPerTile);
multiplier *= nutrientBonus;
@@ -802,17 +857,23 @@ namespace ArachnaeSwarm
{
float multiplier = 1.0f;
// 1. 建筑血量损失百分比
float healthPercent = (float)HitPoints / MaxHitPoints;
multiplier *= healthPercent;
// 1. 建筑血量损失百分比(如果启用)
if (Ext.healthAffectsQuality)
{
float healthPercent = (float)HitPoints / MaxHitPoints;
multiplier *= healthPercent;
}
// 2. 房间的ARA_IncubatorQualityFactor
float roomFactor = GetRoomQualityFactor();
multiplier *= roomFactor;
// 2. 房间的ARA_IncubatorQualityFactor(如果启用)
if (Ext.useRoomQualityFactor)
{
float roomFactor = GetRoomQualityFactor();
multiplier *= roomFactor;
}
// 3. 附近每一个ARA_Pawn_Ootheca,每一个-10%
// 3. 附近每一个ARA_Pawn_Ootheca的惩罚
int nearbyOothecaCount = CountNearbyOtherOothecas();
float oothecaPenalty = Mathf.Max(0f, 1.0f - (nearbyOothecaCount * 0.10f)); // 最多减到0
float oothecaPenalty = Mathf.Max(0f, 1.0f - (nearbyOothecaCount * Ext.nearbyOothecaPenaltyPerUnit));
multiplier *= oothecaPenalty;
// 确保乘数在合理范围内0-1

View File

@@ -0,0 +1,64 @@
// File: ModExtensions/OothecaIncubatorExtension.cs
using RimWorld;
using Verse;
namespace ArachnaeSwarm
{
public class OothecaIncubatorExtension : DefModExtension
{
// 营养液检测半径(单位:格)
public float nutrientSolutionRadius = 5f;
// 其他卵距离检测半径(单位:格)
public float nearbyOothecaRadius = 5f;
// 是否检查同房间内的其他卵(默认:是)
public bool checkSameRoomForOotheca = true;
// 营养液加成比例(每个营养液增加多少百分比的速度)
public float nutrientSolutionBonusPerTile = 0.01f; // 默认每个+1%
// 附近其他卵的惩罚比例(每个减少多少百分比的质量)
public float nearbyOothecaPenaltyPerUnit = 0.10f; // 默认每个-10%
// 幼虫搜索半径(单位:格)
public float larvaSearchRadius = 999f; // 默认在整个地图搜索
// 是否需要在孵化间内才能正常工作(默认:否)
public bool requiresIncubatorRoom = false;
// 不在孵化间内的速度惩罚(百分比)
public float speedPenaltyOutsideIncubator = 0.8f; // 默认80%
// 质量因子房间检查(默认:是)
public bool useRoomQualityFactor = true;
// 建筑血量影响质量(默认:是)
public bool healthAffectsQuality = true;
// 获取营养液检测半径的整数形式(用于循环)
public int NutrientSolutionRadiusInt => (int)nutrientSolutionRadius;
// 获取其他卵距离检测半径的整数形式
public int NearbyOothecaRadiusInt => (int)nearbyOothecaRadius;
public static OothecaIncubatorExtension Get(Thing thing)
{
if (thing?.def?.GetModExtension<OothecaIncubatorExtension>() is OothecaIncubatorExtension ext)
return ext;
return null;
}
// 验证配置是否有效
public bool IsValid()
{
return nutrientSolutionRadius >= 0f &&
nearbyOothecaRadius >= 0f &&
nutrientSolutionBonusPerTile >= 0f &&
nearbyOothecaPenaltyPerUnit >= 0f;
}
// 获取默认扩展(用于兼容性)
public static OothecaIncubatorExtension Default => new OothecaIncubatorExtension();
}
}

View File

@@ -0,0 +1,156 @@
// File: JobDriver_ExtractHoney.cs
using System.Collections.Generic;
using RimWorld;
using Verse;
using Verse.AI;
namespace ArachnaeSwarm
{
public class JobDriver_ExtractHoney : JobDriver
{
private const int ExtractDurationTicks = 180; // 3秒 = 180 ticks
private Need_HoneyProduction HoneyNeed => pawn.needs?.TryGetNeed<Need_HoneyProduction>();
// 使用挤出效果
private static readonly EffecterDef ExtractEffect = ARA_EffecterDefOf.EatVegetarian;
private static readonly SoundDef ExtractSound = SoundDefOf.RawMeat_Eat;
public override bool TryMakePreToilReservations(bool errorOnFailed)
{
// 只需要保留目标位置
return pawn.Reserve(job.targetA, job, 1, -1, null, errorOnFailed);
}
protected override IEnumerable<Toil> MakeNewToils()
{
// 确保自身有蜜罐需求且达到挤出阈值
this.FailOn(() => HoneyNeed == null || !CanExtract(HoneyNeed));
// Toil 1: 移动到目标位置
yield return Toils_Goto.GotoCell(TargetIndex.A, PathEndMode.Touch);
// Toil 2: 执行挤出工作
Toil extractToil = new Toil
{
initAction = delegate
{
// 初始化挤出动作
},
tickAction = delegate
{
// 什么都不做,等待完成
},
defaultCompleteMode = ToilCompleteMode.Delay,
defaultDuration = ExtractDurationTicks
};
// 添加特效和音效
extractToil.WithEffect(() => ExtractEffect, TargetIndex.A);
extractToil.PlaySustainerOrSound(() => ExtractSound);
extractToil.AddFinishAction(delegate
{
// 执行挤出逻辑
ExtractHoney();
});
extractToil.WithProgressBar(TargetIndex.A,
() => extractToil.actor.jobs.curDriver.ticksLeftThisToil / (float)ExtractDurationTicks);
yield return extractToil;
// Toil 3: 完成后的清理
yield return new Toil
{
initAction = delegate
{
// 可以在这里检查是否还需要继续挤出
// 如果蜜罐存量达到阈值,可以安排下一个挤出工作
if (HoneyNeed != null && CanExtract(HoneyNeed))
{
pawn.jobs.jobQueue.EnqueueLast(CreateExtractJob());
}
},
defaultCompleteMode = ToilCompleteMode.Instant
};
}
// 检查是否可以挤出根据MaxLevel决定阈值
private bool CanExtract(Need_HoneyProduction honeyNeed)
{
// 如果MaxLevel大于1.5需要超过80%储量才考虑挤蜜
if (honeyNeed.MaxLevel > 1.5f)
{
return honeyNeed.CurLevelPercentage > 0.8f;
}
// 否则在大于1时挤蜜
else
{
return honeyNeed.CurLevel >= 1.0f;
}
}
// 获取挤出阈值(用于显示等)
private float GetExtractThreshold(Need_HoneyProduction honeyNeed)
{
if (honeyNeed.MaxLevel > 1.5f)
{
return honeyNeed.MaxLevel * 0.8f;
}
else
{
return 1.0f;
}
}
// 挤出蜂蜜的逻辑
private void ExtractHoney()
{
if (HoneyNeed == null)
return;
// 计算要生成的虫蜜数量(蜜罐等级整数部分)
int jellyCount = (int)HoneyNeed.CurLevel;
// 确保至少生成1个
if (jellyCount < 1)
return;
// 创建虫蜜
Thing jelly = ThingMaker.MakeThing(DefDatabase<ThingDef>.GetNamed("ARA_InsectJelly"));
jelly.stackCount = jellyCount;
// 尝试将虫蜜放在目标位置
GenPlace.TryPlaceThing(jelly, job.targetA.Cell, pawn.Map, ThingPlaceMode.Near);
// 减少蜜罐存量
HoneyNeed.ExtractHoney(jellyCount);
// 播放视觉反馈
MoteMaker.ThrowText(job.targetA.Cell.ToVector3Shifted(), pawn.Map,"ARA_ExtractHoney_Message".Translate(jellyCount));
}
// 创建挤出工作的辅助方法
private Job CreateExtractJob()
{
// 寻找最近的空地
IntVec3 targetCell = FindNearestEmptyCell(pawn.Position, pawn.Map);
Job job = JobMaker.MakeJob(DefDatabase<JobDef>.GetNamed("ARA_ExtractHoney"), targetCell);
return job;
}
// 寻找最近的空单元格
private IntVec3 FindNearestEmptyCell(IntVec3 from, Map map)
{
if (CellFinder.TryFindBestPawnStandCell(pawn, out IntVec3 cell, false))
{
return cell;
}
// 如果找不到最佳位置,使用原位置
return from;
}
}
}

View File

@@ -0,0 +1,105 @@
// File: JobDriver_FeedWithHoney.cs
using System.Collections.Generic;
using RimWorld;
using Verse;
using Verse.AI;
namespace ArachnaeSwarm
{
public class JobDriver_FeedWithHoney : JobDriver
{
private const int FeedDurationTicks = 180; // 3秒 = 180 ticks (60 ticks/秒)
private const float FoodTransferPerTick = 0.01f; // 每tick传输的食物量
private Pawn TargetPawn => job.targetA.Pawn;
private Need_HoneyProduction HoneyNeed => pawn.needs?.TryGetNeed<Need_HoneyProduction>();
private Need_Food TargetFoodNeed => TargetPawn.needs?.TryGetNeed<Need_Food>();
// 使用喂养效果 - 参考Toils_Ingest中的用法
private static readonly EffecterDef FeedEffect = ARA_EffecterDefOf.EatVegetarian;
private static readonly SoundDef FeedSound = SoundDefOf.RawMeat_Eat;
public override bool TryMakePreToilReservations(bool errorOnFailed)
{
if (TargetPawn == null || TargetFoodNeed == null || HoneyNeed == null)
return false;
// 保留目标pawn的位置以便接近
return pawn.Reserve(TargetPawn, job, 1, -1, null, errorOnFailed);
}
protected override IEnumerable<Toil> MakeNewToils()
{
// 确保目标有效
this.FailOnDespawnedOrNull(TargetIndex.A);
this.FailOn(() => TargetPawn.Dead || TargetFoodNeed == null || HoneyNeed == null);
// Toil 1: 移动到目标面前
yield return Toils_Goto.GotoThing(TargetIndex.A, PathEndMode.Touch)
.FailOnDespawnedOrNull(TargetIndex.A);
// Toil 2: 进行喂养工作参考Toils_Ingest中的效果添加方式
Toil feedToil = new Toil
{
initAction = delegate
{
// 初始化喂养动作
},
tickAction = delegate
{
// 每tick检查是否还能继续喂养
if (HoneyNeed.CurLevel <= 0 || TargetFoodNeed.CurLevelPercentage >= 1.0f)
{
ReadyForNextToil();
return;
}
// 计算本次tick传输的量
float transferAmount = FoodTransferPerTick;
float honeyAvailable = HoneyNeed.CurLevel;
// 确保不超过蜜罐存量
if (transferAmount > honeyAvailable)
transferAmount = honeyAvailable;
// 确保不超过目标的需求
float targetFoodSpace = TargetFoodNeed.MaxLevel - TargetFoodNeed.CurLevel;
if (transferAmount > targetFoodSpace)
transferAmount = targetFoodSpace;
// 执行传输
HoneyNeed.ExtractHoney(transferAmount);
TargetFoodNeed.CurLevel += transferAmount;
},
defaultCompleteMode = ToilCompleteMode.Delay,
defaultDuration = FeedDurationTicks
};
// 添加特效和音效参考Toils_Ingest.AddIngestionEffects
feedToil.WithEffect(() => FeedEffect, TargetIndex.A);
feedToil.PlaySustainerOrSound(() => FeedSound);
feedToil.AddFinishAction(delegate
{
// 喂养完成后给予社交互动奖励
TargetPawn.interactions?.TryInteractWith(pawn, InteractionDefOf.Chitchat);
});
feedToil.WithProgressBar(TargetIndex.A,
() => feedToil.actor.jobs.curDriver.ticksLeftThisToil / (float)FeedDurationTicks,
interpolateBetweenActorAndTarget: true);
yield return feedToil;
// 修改移除了第三个Toil中的提示消息只剩下一个简单的完成Toil
yield return new Toil
{
initAction = delegate
{
// 这里可以放置任何清理代码,但不要显示消息
},
defaultCompleteMode = ToilCompleteMode.Instant
};
}
}
}

View File

@@ -0,0 +1,105 @@
// File: ThinkNode_JobGiver_ExtractHoney.cs
using System.Collections.Generic;
using System.Linq;
using RimWorld;
using Verse;
using Verse.AI;
namespace ArachnaeSwarm
{
public class ThinkNode_JobGiver_ExtractHoney : ThinkNode_JobGiver
{
private const int ScanIntervalTicks = 300; // 5秒扫描一次
private int lastScanTick = -99999;
protected override Job TryGiveJob(Pawn pawn)
{
// 检查是否应该扫描
int currentTick = Find.TickManager.TicksGame;
if (currentTick - lastScanTick < ScanIntervalTicks)
return null;
lastScanTick = currentTick;
// 检查甲壳剥离组件和开关状态
Comp_ChitinStripping stripComp = pawn.TryGetComp<Comp_ChitinStripping>();
// 检查开关是否开启
if (stripComp == null || !stripComp.CanStripChitin || !stripComp.CanStripNow(pawn))
return null;
// 检查自身需求
Need_HoneyProduction honeyNeed = pawn.needs?.TryGetNeed<Need_HoneyProduction>();
if (honeyNeed == null || !CanExtract(honeyNeed))
return null;
// 如果已经有挤出工作,则不分配新工作
if (pawn.CurJob != null && pawn.CurJob.def == DefDatabase<JobDef>.GetNamed("ARA_ExtractHoney"))
return null;
// 寻找最近的空地
IntVec3 targetCell = FindNearestEmptyCell(pawn);
if (!targetCell.IsValid)
return null;
// 确保可以到达
if (!pawn.CanReach(targetCell, PathEndMode.Touch, Danger.Some))
return null;
// 创建挤出工作
Job job = JobMaker.MakeJob(DefDatabase<JobDef>.GetNamed("ARA_ExtractHoney"), targetCell);
job.count = 1;
return job;
}
// 检查是否可以挤出与JobDriver中的逻辑保持一致
private bool CanExtract(Need_HoneyProduction honeyNeed)
{
// 如果MaxLevel大于1.5需要超过80%储量才考虑挤蜜
if (honeyNeed.MaxLevel > 1.5f)
{
return honeyNeed.CurLevelPercentage > 0.8f;
}
// 否则在大于1时挤蜜
else
{
return honeyNeed.CurLevel >= 1.0f;
}
}
// 寻找最近的空单元格
private IntVec3 FindNearestEmptyCell(Pawn pawn)
{
// 首先检查当前位置周围3格内是否有空地
for (int radius = 1; radius <= 3; radius++)
{
foreach (IntVec3 cell in GenRadial.RadialCellsAround(pawn.Position, radius, true))
{
if (cell.InBounds(pawn.Map) &&
cell.Standable(pawn.Map) &&
cell.GetFirstItem(pawn.Map) == null &&
!cell.GetTerrain(pawn.Map).IsWater)
{
return cell;
}
}
}
// 如果找不到使用CellFinder寻找
if (CellFinder.TryFindBestPawnStandCell(pawn, out IntVec3 bestCell, false))
{
return bestCell;
}
// 最后尝试使用原位置(确保不是水面)
if (!pawn.Position.GetTerrain(pawn.Map).IsWater)
{
return pawn.Position;
}
return IntVec3.Invalid;
}
}
}

View File

@@ -0,0 +1,107 @@
// File: ThinkNode_JobGiver_FeedWithHoney.cs
using System.Collections.Generic;
using System.Linq;
using RimWorld;
using Verse;
using Verse.AI;
namespace ArachnaeSwarm
{
public class ThinkNode_JobGiver_FeedWithHoney : ThinkNode_JobGiver
{
private const float MinFoodNeedThreshold = 0.2f; // 目标需要喂养的阈值
private const float MinHoneyThreshold = 0.1f; // 蜜罐低于10%时不喂养
private const int ScanIntervalTicks = 250; // 扫描间隔 (约4秒)
private const float MaxDistance = 30f; // 最大寻找距离
private int lastScanTick = -99999;
protected override Job TryGiveJob(Pawn pawn)
{
// 检查是否应该扫描
int currentTick = Find.TickManager.TicksGame;
if (currentTick - lastScanTick < ScanIntervalTicks)
return null;
lastScanTick = currentTick;
// 检查自身需求
Need_HoneyProduction honeyNeed = pawn.needs?.TryGetNeed<Need_HoneyProduction>();
// 修改检查蜜罐存量是否低于10%,如果是则不喂养
if (honeyNeed == null || honeyNeed.IsEmpty || honeyNeed.CurLevelPercentage < MinHoneyThreshold)
return null;
// 扫描潜在的喂养目标
List<Pawn> potentialTargets = new List<Pawn>();
// 获取地图上所有pawn
foreach (Pawn target in pawn.Map.mapPawns.AllPawnsSpawned)
{
// 跳过自己
if (target == pawn)
continue;
// 检查是否属于己方势力(殖民者、奴隶、囚犯)
if (!IsFriendlyPawn(pawn, target))
continue;
// 检查距离
if (pawn.Position.DistanceTo(target.Position) > MaxDistance)
continue;
// 检查是否需要食物
Need_Food targetFoodNeed = target.needs?.TryGetNeed<Need_Food>();
if (targetFoodNeed == null || targetFoodNeed.CurLevelPercentage > MinFoodNeedThreshold)
continue;
// 检查目标是否可到达
if (!pawn.CanReserveAndReach(target, PathEndMode.Touch, Danger.Some))
continue;
potentialTargets.Add(target);
}
// 如果没有目标
if (potentialTargets.Count == 0)
return null;
// 按需求紧急程度排序(最饿的优先)
potentialTargets.Sort((a, b) =>
{
Need_Food foodA = a.needs?.TryGetNeed<Need_Food>();
Need_Food foodB = b.needs?.TryGetNeed<Need_Food>();
return foodA?.CurLevelPercentage.CompareTo(foodB?.CurLevelPercentage) ?? 0;
});
// 选择最饥饿的目标
Pawn targetPawn = potentialTargets[0];
// 创建喂养工作
Job job = JobMaker.MakeJob(DefDatabase<JobDef>.GetNamed("ARA_FeedWithHoney"), targetPawn);
job.count = 1;
return job;
}
private bool IsFriendlyPawn(Pawn feeder, Pawn target)
{
// 检查目标是否是殖民者、奴隶或囚犯
if (target.Faction == Faction.OfPlayer)
return true;
// 检查是否是囚犯(属于玩家)
if (target.IsPrisonerOfColony)
return true;
// 检查是否是奴隶
if (target.IsSlaveOfColony)
return true;
// 检查是否和喂养者同一派系
if (target.Faction != null && target.Faction == feeder.Faction)
return true;
return false;
}
}
}

View File

@@ -0,0 +1,29 @@
// File: Comps/CompProperties_ChitinStripping.cs
using RimWorld;
using Verse;
namespace ArachnaeSwarm
{
public class CompProperties_ChitinStripping : CompProperties
{
// 是否允许剥离甲壳
public bool canStripChitin = true;
// 剥离的阈值(甲壳存量的百分比)
public float stripThreshold = 0.8f;
// 每次剥离产生的最小甲壳数量
public int minStripAmount = 1;
// 剥离工作的间隔ticks
public int stripInterval = 3000; // 50秒
// 甲壳物品定义
public ThingDef carapaceThingDef = null;
public CompProperties_ChitinStripping()
{
compClass = typeof(Comp_ChitinStripping);
}
}
}

View File

@@ -0,0 +1,103 @@
// File: Comps/Comp_ChitinStripping.cs
using System.Collections.Generic;
using RimWorld;
using UnityEngine;
using Verse;
namespace ArachnaeSwarm
{
public class Comp_ChitinStripping : ThingComp
{
public CompProperties_ChitinStripping Props => (CompProperties_ChitinStripping)props;
// 剥离开关状态
private bool stripEnabled = true;
// 上次剥离时间
private int lastStripTick = -99999;
// 属性访问器
public bool StripEnabled
{
get => stripEnabled;
set => stripEnabled = value;
}
public bool CanStripChitin => Props.canStripChitin && stripEnabled;
public float StripThreshold => Props.stripThreshold;
// 获取甲壳物品定义
public ThingDef CarapaceThingDef
{
get
{
if (Props.carapaceThingDef != null)
return Props.carapaceThingDef;
return DefDatabase<ThingDef>.GetNamed("ARA_Carapace", false);
}
}
// 检查是否可以剥离
public bool CanStripNow(Pawn pawn)
{
if (!CanStripChitin || pawn == null || pawn.Dead)
return false;
// 检查间隔
int currentTick = Find.TickManager.TicksGame;
if (currentTick - lastStripTick < Props.stripInterval)
return false;
// 检查甲壳需求
Need_ChitinArmor chitinNeed = pawn.needs?.TryGetNeed<Need_ChitinArmor>();
if (chitinNeed == null)
return false;
// 检查是否达到阈值
return chitinNeed.CurLevelPercentage >= StripThreshold;
}
// 记录剥离时间
public void NotifyStripped()
{
lastStripTick = Find.TickManager.TicksGame;
}
// 获取上次剥离到现在的时间
public int TicksSinceLastStrip => Find.TickManager.TicksGame - lastStripTick;
// 获取剩余冷却时间
public int CooldownTicksRemaining => Mathf.Max(0, Props.stripInterval - TicksSinceLastStrip);
// 序列化
public override void PostExposeData()
{
base.PostExposeData();
Scribe_Values.Look(ref stripEnabled, "stripEnabled", true);
Scribe_Values.Look(ref lastStripTick, "lastStripTick", -99999);
}
// 获取Gizmos命令按钮
public override IEnumerable<Gizmo> CompGetGizmosExtra()
{
// 只在玩家控制的pawn上显示
if (parent is Pawn pawn && pawn.Faction?.IsPlayer == true)
{
// 甲壳剥离开关按钮
yield return new Command_Toggle
{
defaultLabel = "ARA_ChitinStripping_Toggle".Translate(),
defaultDesc = "ARA_ChitinStripping_ToggleDesc".Translate(),
icon = ContentFinder<Texture2D>.Get("ArachnaeSwarm/UI/Commands/ARA_StripChitin"),
isActive = () => stripEnabled,
toggleAction = () =>
{
stripEnabled = !stripEnabled;
}
};
}
}
}
}

View File

@@ -0,0 +1,158 @@
// File: JobDriver_StripChitin.cs
using System.Collections.Generic;
using RimWorld;
using Verse;
using Verse.AI;
namespace ArachnaeSwarm
{
public class JobDriver_StripChitin : JobDriver
{
private const int StripDurationTicks = 300; // 5秒 = 300 ticks
// 获取甲壳需求
private Need_ChitinArmor ChitinNeed => pawn.needs?.TryGetNeed<Need_ChitinArmor>();
// 获取甲壳剥离组件
private Comp_ChitinStripping StripComp
{
get
{
if (pawn == null)
return null;
// 从pawn的def中获取Comp
CompProperties_ChitinStripping compProps = pawn.def.GetCompProperties<CompProperties_ChitinStripping>();
if (compProps != null)
{
return pawn.TryGetComp<Comp_ChitinStripping>();
}
return null;
}
}
// 特效和音效
private static readonly EffecterDef StripEffect = ARA_EffecterDefOf.EatVegetarian;
private static readonly SoundDef StripSound = SoundDefOf.RawMeat_Eat;
public override bool TryMakePreToilReservations(bool errorOnFailed)
{
// 只需要保留目标位置
return pawn.Reserve(job.targetA, job, 1, -1, null, errorOnFailed);
}
protected override IEnumerable<Toil> MakeNewToils()
{
// 检查条件
this.FailOn(() => ChitinNeed == null);
this.FailOn(() => StripComp == null);
this.FailOn(() => !StripComp.CanStripNow(pawn));
this.FailOn(() => !StripComp.CanStripChitin); // 检查开关
// Toil 1: 移动到目标位置
yield return Toils_Goto.GotoCell(TargetIndex.A, PathEndMode.Touch);
// Toil 2: 执行剥离工作
Toil stripToil = new Toil
{
initAction = delegate
{
// 初始化剥离动作
},
tickAction = delegate
{
// 什么都不做,等待完成
},
defaultCompleteMode = ToilCompleteMode.Delay,
defaultDuration = StripDurationTicks
};
// 添加特效和音效
stripToil.WithEffect(() => StripEffect, TargetIndex.A);
stripToil.PlaySustainerOrSound(() => StripSound);
stripToil.AddFinishAction(delegate
{
// 执行剥离逻辑
StripChitin();
});
stripToil.WithProgressBar(TargetIndex.A,
() => stripToil.actor.jobs.curDriver.ticksLeftThisToil / (float)StripDurationTicks,
interpolateBetweenActorAndTarget: true);
yield return stripToil;
// Toil 3: 完成后的清理
yield return new Toil
{
initAction = delegate
{
// 记录剥离时间
StripComp?.NotifyStripped();
// 如果甲壳存量仍然达到阈值,可以安排下一个剥离工作
if (ChitinNeed != null && StripComp != null && StripComp.CanStripNow(pawn))
{
pawn.jobs.jobQueue.EnqueueLast(CreateStripJob());
}
},
defaultCompleteMode = ToilCompleteMode.Instant
};
}
// 剥离甲壳的逻辑
private void StripChitin()
{
if (ChitinNeed == null || StripComp == null)
return;
// 计算要剥离的甲壳数量
// 通常剥离整数部分但至少剥离1个
int stripCount = (int)ChitinNeed.CurLevel;
if (stripCount < StripComp.Props.minStripAmount)
stripCount = StripComp.Props.minStripAmount;
// 获取甲壳物品定义
ThingDef carapaceDef = StripComp.CarapaceThingDef;
if (carapaceDef == null)
return;
// 创建甲壳物品
Thing carapace = ThingMaker.MakeThing(carapaceDef);
carapace.stackCount = stripCount;
// 尝试将甲壳放在目标位置
GenPlace.TryPlaceThing(carapace, job.targetA.Cell, pawn.Map, ThingPlaceMode.Near);
// 减少甲壳存量
ChitinNeed.ReduceChitin(stripCount);
// 播放视觉反馈
MoteMaker.ThrowText(job.targetA.Cell.ToVector3Shifted(), pawn.Map,
"ARA_StripChitin_Message".Translate(stripCount));
}
// 创建剥离工作的辅助方法
private Job CreateStripJob()
{
// 寻找最近的空地
IntVec3 targetCell = FindNearestEmptyCell(pawn.Position, pawn.Map);
Job job = JobMaker.MakeJob(DefDatabase<JobDef>.GetNamed("ARA_StripChitin"), targetCell);
return job;
}
// 寻找最近的空单元格
private IntVec3 FindNearestEmptyCell(IntVec3 from, Map map)
{
if (CellFinder.TryFindBestPawnStandCell(pawn, out IntVec3 cell, false))
{
return cell;
}
// 如果找不到最佳位置,使用原位置
return from;
}
}
}

View File

@@ -0,0 +1,83 @@
// File: ThinkNode_JobGiver_StripChitin.cs
using System.Collections.Generic;
using System.Linq;
using RimWorld;
using Verse;
using Verse.AI;
namespace ArachnaeSwarm
{
public class ThinkNode_JobGiver_StripChitin : ThinkNode_JobGiver
{
private const int ScanIntervalTicks = 500; // 8.3秒扫描一次
private int lastScanTick = -99999;
protected override Job TryGiveJob(Pawn pawn)
{
// 检查是否应该扫描
int currentTick = Find.TickManager.TicksGame;
if (currentTick - lastScanTick < ScanIntervalTicks)
return null;
lastScanTick = currentTick;
// 检查甲壳剥离组件
Comp_ChitinStripping stripComp = pawn.TryGetComp<Comp_ChitinStripping>();
if (stripComp == null || !stripComp.CanStripNow(pawn))
return null;
// 如果已经有剥离工作,则不分配新工作
if (pawn.CurJob != null && pawn.CurJob.def == DefDatabase<JobDef>.GetNamed("ARA_StripChitin"))
return null;
// 寻找最近的空地
IntVec3 targetCell = FindNearestEmptyCell(pawn);
if (!targetCell.IsValid)
return null;
// 确保可以到达
if (!pawn.CanReach(targetCell, PathEndMode.Touch, Danger.Some))
return null;
// 创建剥离工作
Job job = JobMaker.MakeJob(DefDatabase<JobDef>.GetNamed("ARA_StripChitin"), targetCell);
job.count = 1;
return job;
}
// 寻找最近的空单元格
private IntVec3 FindNearestEmptyCell(Pawn pawn)
{
// 首先检查当前位置周围3格内是否有空地
for (int radius = 1; radius <= 3; radius++)
{
foreach (IntVec3 cell in GenRadial.RadialCellsAround(pawn.Position, radius, true))
{
if (cell.InBounds(pawn.Map) &&
cell.Standable(pawn.Map) &&
cell.GetFirstItem(pawn.Map) == null &&
!cell.GetTerrain(pawn.Map).IsWater)
{
return cell;
}
}
}
// 如果找不到使用CellFinder寻找
if (CellFinder.TryFindBestPawnStandCell(pawn, out IntVec3 bestCell, false))
{
return bestCell;
}
// 最后尝试使用原位置(确保不是水面)
if (!pawn.Position.GetTerrain(pawn.Map).IsWater)
{
return pawn.Position;
}
return IntVec3.Invalid;
}
}
}

View File

@@ -0,0 +1,78 @@
// File: Jobs/JobDriver_SwarmMaintain.cs
using RimWorld;
using Verse;
using Verse.AI;
using System.Collections.Generic;
namespace ArachnaeSwarm
{
public class JobDriver_SwarmMaintain : JobDriver
{
// 工作完成后增加的维护度
public const float MaintenancePerWork = 25f;
// 工作时间ticks
private const int WorkDuration = 600; // 10秒
// 使用喂养效果 - 参考Toils_Ingest中的用法
private static readonly EffecterDef MaintainEffect = ARA_EffecterDefOf.EatVegetarian;
private static readonly SoundDef MaintainSound = SoundDefOf.RawMeat_Eat;
public override bool TryMakePreToilReservations(bool errorOnFailed)
{
return pawn.Reserve(job.targetA, job, 1, -1, null, errorOnFailed);
}
protected override IEnumerable<Toil> MakeNewToils()
{
// 1. 前往目标建筑
this.FailOnDespawnedNullOrForbidden(TargetIndex.A);
yield return Toils_Goto.GotoCell(TargetIndex.A, PathEndMode.Touch);
// 2. 执行维护工作
Toil maintain = new Toil();
maintain.tickAction = () =>
{
Pawn actor = maintain.actor;
Thing target = actor.CurJob.targetA.Thing;
// 检查目标是否仍然有效
if (target == null || target.Destroyed)
{
actor.jobs.EndCurrentJob(JobCondition.Incompletable);
return;
}
// 如果有技能要求,可以在这里检查
// actor.skills.Learn(SkillDefOf.Construction, 0.05f);
};
// 添加特效和音效参考Toils_Ingest.AddIngestionEffects
maintain.WithEffect(() => MaintainEffect, TargetIndex.A);
maintain.PlaySustainerOrSound(() => MaintainSound);
maintain.FailOnDespawnedNullOrForbidden(TargetIndex.A);
maintain.FailOnCannotTouch(TargetIndex.A, PathEndMode.Touch);
maintain.WithProgressBar(TargetIndex.A, () => (float)maintain.actor.jobs.curDriver.ticksLeftThisToil / WorkDuration);
maintain.defaultCompleteMode = ToilCompleteMode.Delay;
maintain.defaultDuration = WorkDuration;
yield return maintain;
// 3. 完成工作,增加维护度
yield return new Toil
{
initAction = () =>
{
Thing target = TargetA.Thing;
if (target != null && !target.Destroyed)
{
Comp_SwarmMaintenance comp = target.TryGetComp<Comp_SwarmMaintenance>();
if (comp != null)
{
comp.AddMaintenance(MaintenancePerWork);
}
}
}
};
}
}
}

View File

@@ -0,0 +1,438 @@
// File: Need_ChitinArmor.cs
using RimWorld;
using System.Collections.Generic;
using System.Xml.Serialization;
using UnityEngine;
using Verse;
namespace ArachnaeSwarm
{
public class NeedDefExtension_ChitinLevels : DefModExtension
{
// 关联的Hediff
public HediffDef hediff = null;
// 严重性映射范围默认是1:1映射即Need值=严重性值)
[XmlElement("severityRange")]
public FloatRange severityRange = new FloatRange(0f, 1f);
// 是否死亡时移除Hediff
public bool removeOnDeath = true;
// 基础增长速率系数
public float baseGrowthRate = 0.1f;
// 甲壳数量平方的系数
public float squareCoefficient = 1f / 100f; // 默认 1/100
}
public class Need_ChitinArmor : Need
{
// 甲壳增长基础值每tick
private const float BaseChitinGainPerTick = 0.0001f;
// 关联的Hediff
private Hediff chitinHediff;
// 上次更新Hediff的时间
private int lastHediffUpdateTick = -99999;
private const int HediffUpdateInterval = 60; // 每60tick更新一次Hediff
// 甲壳部位名称
private const string CHITIN_SHELL_PART_NAME = "ARA_Chitin_Shell";
// 缓存的身体部位数量(用于减少计算)
private int cachedShellPartCount = -1;
private int lastPartCountCheckTick = -99999;
private const int PartCountCheckInterval = 120; // 每120tick检查一次
// 获取ModExtension配置
public NeedDefExtension_ChitinLevels Extension => def.GetModExtension<NeedDefExtension_ChitinLevels>();
// 计算甲壳最大值基础值1 + 每个ARA_Chitin_Shell部位增加1
public override float MaxLevel
{
get
{
int shellCount = GetChitinShellPartCount();
return 1f + shellCount;
}
}
// 获取当前等级百分比用于UI显示
public new float CurLevelPercentage => CurLevel / MaxLevel;
// 获取甲壳增长速率
public float GrowthRatePerTick
{
get
{
int shellCount = GetChitinShellPartCount();
// 计算增长速率:((甲壳数量的平方) * 系数) + 基础值
float squareCoefficient = Extension?.squareCoefficient ?? (1f / 100f);
float baseRate = Extension?.baseGrowthRate ?? 0.1f;
return ((shellCount * shellCount) * squareCoefficient) + baseRate;
}
}
// 获取每秒增长速率用于UI显示
public float GrowthRatePerSecond
{
get
{
// RimWorld中1秒 = 60ticks
// 实际每秒增长量 = 每tick增长速率 * 60
// 注意BaseChitinGainPerTick是基础增长值GrowthRatePerTick是增长系数
// 实际增长公式每tick增长量 = BaseChitinGainPerTick * GrowthRatePerTick
// 所以每秒增长量 = BaseChitinGainPerTick * GrowthRatePerTick * 60
return BaseChitinGainPerTick * GrowthRatePerTick * 60f;
}
}
// 获取每2.5秒增长速率用于UI显示每150tick
public float GrowthRatePer2_5Seconds => GrowthRatePerSecond * 2.5f;
public Need_ChitinArmor(Pawn newPawn) : base(newPawn)
{
// 初始化时设置为空
curLevelInt = 0f;
// 设置默认阈值点
SetDefaultThresholds();
// 初始化缓存
UpdateCachedShellPartCount();
}
public override void ExposeData()
{
base.ExposeData();
Scribe_References.Look(ref chitinHediff, "chitinHediff");
Scribe_Values.Look(ref cachedShellPartCount, "cachedShellPartCount", -1);
Scribe_Values.Look(ref lastPartCountCheckTick, "lastPartCountCheckTick", -99999);
}
private void SetDefaultThresholds()
{
// 默认阈值25%、50%、75%用于UI显示
threshPercents = new List<float> { 0.25f, 0.5f, 0.75f };
}
public override void NeedInterval()
{
// 检查是否需要冻结
if (IsFrozen)
{
return;
}
// 定期更新身体部位数量缓存
int currentTick = Find.TickManager.TicksGame;
if (currentTick - lastPartCountCheckTick >= PartCountCheckInterval)
{
UpdateCachedShellPartCount();
lastPartCountCheckTick = currentTick;
}
// 计算增长量:基础增长速率 * 每150tick的间隔 * 甲壳增长速率系数
float growthAmount = BaseChitinGainPerTick * 150f * GrowthRatePerTick;
// 应用增长
CurLevel += growthAmount;
// 确保不超过最大值不低于0
float maxLevel = MaxLevel; // 计算一次并缓存
if (CurLevel > maxLevel)
CurLevel = maxLevel;
else if (CurLevel < 0f)
CurLevel = 0f;
// 定期更新Hediff每60tick一次
if (currentTick - lastHediffUpdateTick >= HediffUpdateInterval)
{
UpdateChitinHediff();
lastHediffUpdateTick = currentTick;
}
}
// 获取ARA_Chitin_Shell身体部位的数量
private int GetChitinShellPartCount()
{
// 使用缓存值(如果有效)
if (cachedShellPartCount >= 0 && pawn != null && !pawn.Dead)
{
return cachedShellPartCount;
}
// 重新计算
UpdateCachedShellPartCount();
return cachedShellPartCount;
}
// 更新缓存的身体部位数量
private void UpdateCachedShellPartCount()
{
if (pawn == null || pawn.Dead || pawn.RaceProps == null || pawn.RaceProps.body == null)
{
cachedShellPartCount = 0;
return;
}
// 计算身体部位数量
int count = 0;
// 方法1从pawn的身体部位记录中查找
foreach (BodyPartRecord part in pawn.RaceProps.body.AllParts)
{
if (part.def.defName == CHITIN_SHELL_PART_NAME)
{
count++;
}
}
// 方法2如果找不到检查Hediff中是否有甲壳作为备用方法
if (count == 0 && pawn.health != null && pawn.health.hediffSet != null)
{
foreach (Hediff hediff in pawn.health.hediffSet.hediffs)
{
if (hediff.Part != null && hediff.Part.def != null &&
hediff.Part.def.defName == CHITIN_SHELL_PART_NAME)
{
count++;
}
}
}
cachedShellPartCount = count;
}
// 重新计算身体部位数量(当身体发生变化时调用)
public void RecalculateShellPartCount()
{
UpdateCachedShellPartCount();
}
// 更新甲壳Hediff
private void UpdateChitinHediff()
{
if (Extension == null || Extension.hediff == null || pawn.Dead)
return;
// 直接使用CurLevel作为严重性但可以根据配置的范围进行调整
float severity = CurLevel;
// 如果需要,可以应用范围限制
FloatRange severityRange = Extension.severityRange;
if (severity > severityRange.max)
severity = severityRange.max;
else if (severity < severityRange.min)
severity = severityRange.min;
// 如果严重性为0或接近0移除Hediff
if (severity <= 0.001f)
{
RemoveChitinHediff();
return;
}
// 确保Hediff存在
EnsureChitinHediff();
// 设置严重性
if (chitinHediff != null)
{
chitinHediff.Severity = severity;
}
}
// 确保Hediff存在
private void EnsureChitinHediff()
{
if (chitinHediff == null || chitinHediff.pawn != pawn)
{
// 移除旧的Hediff
if (chitinHediff != null && chitinHediff.pawn == pawn)
{
pawn.health.RemoveHediff(chitinHediff);
}
// 创建新的Hediff
chitinHediff = HediffMaker.MakeHediff(Extension.hediff, pawn);
pawn.health.AddHediff(chitinHediff);
}
}
// 移除Hediff
private void RemoveChitinHediff()
{
if (chitinHediff != null && chitinHediff.pawn == pawn)
{
pawn.health.RemoveHediff(chitinHediff);
chitinHediff = null;
}
}
// 减少甲壳存量(例如受伤时)
public void ReduceChitin(float amount)
{
CurLevel -= amount;
if (CurLevel < 0f)
CurLevel = 0f;
// 立即更新Hediff
UpdateChitinHediff();
}
// 强制清空甲壳
public void EmptyChitin()
{
CurLevel = 0f;
RemoveChitinHediff();
}
// 重置到初始状态
public override void SetInitialLevel()
{
// 初始为空
CurLevel = 0f;
RemoveChitinHediff();
UpdateCachedShellPartCount();
}
// 获取当前严重性
public float GetCurrentSeverity()
{
if (chitinHediff == null)
return 0f;
return chitinHediff.Severity;
}
// 获取提示字符串
public override string GetTipString()
{
string text = (LabelCap + ": " + CurLevelPercentage.ToStringPercent()).Colorize(ColoredText.TipSectionTitleColor);
text += "\n" + def.description;
int shellCount = GetChitinShellPartCount();
text += $"\n{"ARA_Chitin.ShellParts".Translate()}: {shellCount}";
// 显示每秒增长速率(更直观)
text += $"\n{"ARA_Chitin.GrowthRate".Translate()}: {GrowthRatePerSecond:0.#####}/ {"LetterSecond".Translate()}";
return text;
}
// 在UI上绘制
public override void DrawOnGUI(Rect rect, int maxThresholdMarkers = int.MaxValue, float customMargin = -1f, bool drawArrows = true, bool doTooltip = true, Rect? rectForTooltip = null, bool drawLabel = true)
{
// 确保阈值已设置
if (threshPercents == null)
{
SetDefaultThresholds();
}
base.DrawOnGUI(rect, maxThresholdMarkers, customMargin, false, doTooltip, rectForTooltip, drawLabel);
}
// 是否冻结
protected override bool IsFrozen
{
get
{
// 如果基础条件冻结,则甲壳生长也冻结
if (base.IsFrozen)
return true;
// 如果生物死亡,则冻结
if (pawn.Dead)
return true;
return false;
}
}
// GUI变化箭头总是显示增长
public override int GUIChangeArrow => 1;
// 调试调整百分比
protected override void OffsetDebugPercent(float offsetPercent)
{
base.OffsetDebugPercent(offsetPercent);
UpdateChitinHediff();
}
// 当生物死亡时清理Hediff
public void OnPawnDeath()
{
if (Extension?.removeOnDeath ?? true)
{
RemoveChitinHediff();
}
}
// 当生物重生时恢复Hediff
public void OnPawnResurrected()
{
if (CurLevel > 0f && Extension?.hediff != null)
{
UpdateChitinHediff();
}
UpdateCachedShellPartCount();
}
// 当身体部位发生变化时调用
public void NotifyBodyPartChanged()
{
UpdateCachedShellPartCount();
// 如果最大容量减少,确保当前值不超过新最大值
if (CurLevel > MaxLevel)
{
CurLevel = MaxLevel;
UpdateChitinHediff();
}
}
public Comp_ChitinStripping GetStripComp()
{
if (pawn == null)
return null;
return pawn.TryGetComp<Comp_ChitinStripping>();
}
// 获取是否可以剥离
public bool CanStripChitin()
{
var stripComp = GetStripComp();
if (stripComp == null)
return false;
return stripComp.CanStripNow(pawn);
}
// 获取剥离信息字符串
public string GetStripInfoString()
{
var stripComp = GetStripComp();
if (stripComp == null)
return "ARA_ChitinStripping_Disabled".Translate();
if (!stripComp.CanStripNow(pawn))
{
int cooldownTicks = stripComp.CooldownTicksRemaining;
if (cooldownTicks > 0)
{
float cooldownSeconds = cooldownTicks / 60f;
return "ARA_ChitinStripping_Cooldown".Translate(cooldownSeconds.ToString("F1"));
}
else
{
return "ARA_ChitinStripping_Threshold".Translate((stripComp.StripThreshold * 100f).ToString("F0"));
}
}
return "ARA_ChitinStripping_Ready".Translate();
}
}
}

View File

@@ -1,13 +1,52 @@
// File: Need_HoneyProduction.cs
using RimWorld;
using UnityEngine;
using Verse;
namespace ArachnaeSwarm
{
public class HoneyProductionExtension : DefModExtension
{
// 蜜罐生产的基础转化率(相对于食物流失的百分比)
public float baseConversionRate = 0.5f;
// 最大蜜罐容量(可选,覆盖默认值)
public float maxHoneyCapacity = 1f;
// 是否启用蜜罐生产
public bool enableHoneyProduction = true;
// 生产速率乘数(影响生产速度)
public float productionSpeedFactor = 1f;
// 蜜罐类别对应的生产效率(可选,覆盖默认值)
public float fullProductionEfficiency = 1.5f;
public float highProductionEfficiency = 1.2f;
public float mediumProductionEfficiency = 1f;
public float lowProductionEfficiency = 0.5f;
public float emptyProductionEfficiency = 0f;
// 生产间隔ticks可选覆盖默认值
public int productionInterval = 150;
public static HoneyProductionExtension Get(Pawn pawn)
{
if (pawn?.def?.GetModExtension<HoneyProductionExtension>() is HoneyProductionExtension ext)
return ext;
return null;
}
// 获取转化率
public float GetConversionRate()
{
return baseConversionRate * productionSpeedFactor;
}
}
public class Need_HoneyProduction : Need
{
// 基础流失速率(与食物需要对应)
private const float BaseHoneyGainPerTick = 2.6666667E-05f * 0.5f; // 食物流失速率的50%
private const float BaseHoneyGainPerTick = 2.6666667E-05f * 0.5f; // 食物流失速率的50%作为默认值
// 用于存储对食物需要的引用
private Need_Food cachedFoodNeed;
@@ -18,8 +57,22 @@ namespace ArachnaeSwarm
// 上次满的时间点
private int lastFullTick = -99999;
// 蜜罐的最大容量(可能需要调整)
public override float MaxLevel => 1f;
// 缓存的ModExtension
private HoneyProductionExtension cachedExtension;
// 蜜罐的最大容量 - 优先使用ModExtension的值
public override float MaxLevel
{
get
{
var ext = GetExtension();
if (ext != null && ext.maxHoneyCapacity > 0)
{
return ext.maxHoneyCapacity;
}
return FoodNeed?.MaxLevel ?? 1f;
}
}
// 当前类别
public HoneyProductionCategory CurCategory => curCategoryInt;
@@ -35,6 +88,29 @@ namespace ArachnaeSwarm
{
get
{
var ext = GetExtension();
// 如果有扩展定义,使用扩展的值
if (ext != null)
{
switch (curCategoryInt)
{
case HoneyProductionCategory.Full:
return ext.fullProductionEfficiency;
case HoneyProductionCategory.High:
return ext.highProductionEfficiency;
case HoneyProductionCategory.Medium:
return ext.mediumProductionEfficiency;
case HoneyProductionCategory.Low:
return ext.lowProductionEfficiency;
case HoneyProductionCategory.Empty:
return ext.emptyProductionEfficiency;
default:
return 0f;
}
}
// 默认值
switch (curCategoryInt)
{
case HoneyProductionCategory.Full:
@@ -53,12 +129,32 @@ namespace ArachnaeSwarm
}
}
// 获取扩展
private HoneyProductionExtension GetExtension()
{
if (cachedExtension == null && pawn != null)
{
cachedExtension = pawn.def?.GetModExtension<HoneyProductionExtension>();
}
return cachedExtension;
}
// 是否启用蜜罐生产
private bool IsEnabled
{
get
{
var ext = GetExtension();
return ext == null || ext.enableHoneyProduction; // 默认启用
}
}
// 获取食物需要的引用
private Need_Food FoodNeed
{
get
{
if (cachedFoodNeed == null || cachedFoodNeed.pawn != pawn)
if (cachedFoodNeed == null)
{
cachedFoodNeed = pawn.needs?.TryGetNeed<Need_Food>();
}
@@ -86,7 +182,9 @@ namespace ArachnaeSwarm
public override void NeedInterval()
{
base.NeedInterval();
// 如果不启用,直接返回
if (!IsEnabled)
return;
// 检查是否需要冻结(与食物需要类似的条件)
if (IsFrozen)
@@ -97,11 +195,21 @@ namespace ArachnaeSwarm
// 获取食物需要的流失速率
float foodFallRate = GetFoodFallRate();
// 蜜罐的增长速率是食物流失速率的50%
float honeyGainRate = foodFallRate * 0.5f;
// 获取转化率从ModExtension或使用默认值
float conversionRate = GetExtension()?.GetConversionRate() ?? 0.5f;
// 应用150 ticks的间隔
CurLevel += honeyGainRate * 150f;
// 蜜罐的增长速率 = 食物流失速率 × 转化率
float honeyGainRate = foodFallRate * conversionRate;
// 获取生产间隔
int interval = GetExtension()?.productionInterval ?? 150;
// 应用间隔
CurLevel += honeyGainRate * interval;
// 确保不超过最大容量
if (CurLevel > MaxLevel)
CurLevel = MaxLevel;
// 更新类别
UpdateCategory();
@@ -172,15 +280,39 @@ namespace ArachnaeSwarm
{
string text = (LabelCap + ": " + CurLevelPercentage.ToStringPercent()).Colorize(ColoredText.TipSectionTitleColor);
text += "\n" + def.description;
text += $"\n\n{"AS.HoneyProduction.CurrentLevel".Translate()}: {CurLevel:0.##} / {MaxLevel:0.##}";
text += $"\n{"AS.HoneyProduction.Category".Translate()}: {GetCategoryLabel().CapitalizeFirst()}";
text += $"\n{"AS.HoneyProduction.Efficiency".Translate()}: {ProductionEfficiency.ToStringPercent()}";
text += $"\n{"AS.HoneyProduction.FoodDrainRate".Translate()}: {GetFoodFallRate():0.#####}/tick";
text += $"\n{"AS.HoneyProduction.HoneyGainRate".Translate()}: {(GetFoodFallRate() * 0.5f):0.#####}/tick";
text += $"\n{"ARA_HoneyProduction.Efficiency".Translate()} {ProductionEfficiency.ToStringPercent()}";
// 获取每tick的速率
float foodFallPerTick = GetFoodFallRate();
float conversionRate = GetExtension()?.GetConversionRate() ?? 0.5f;
float honeyGainPerTick = foodFallPerTick * conversionRate;
// 转换为每秒1秒 = 60tick
float foodFallPerSecond = foodFallPerTick * 60f;
float honeyGainPerSecond = honeyGainPerTick * 60f;
text += $"\n{"ARA_HoneyProduction.FoodDrainRate".Translate()}: {foodFallPerSecond:0.#####}/ {"LetterSecond".Translate()}";
text += $"\n{"ARA_HoneyProduction.HoneyGainRate".Translate()}: {honeyGainPerSecond:0.#####}/ {"LetterSecond".Translate()}";
// 显示转化率信息
if (GetExtension() != null)
{
text += $"\n{"ARA_HoneyProduction.ConversionRate".Translate()}: {conversionRate:P1}";
if (GetExtension().productionSpeedFactor != 1f)
{
text += $"\n{"ARA_HoneyProduction.SpeedFactor".Translate()}: {GetExtension().productionSpeedFactor:F2}";
}
}
if (IsFull)
{
text += $"\n\n{"AS.HoneyProduction.FullWarning".Translate()}";
text += $"\n\n{"ARA_HoneyProduction.FullWarning".Translate()}";
}
// 显示是否启用
if (!IsEnabled)
{
text += $"\n\n<color=orange>{"ARA_HoneyProduction.Disabled".Translate()}</color>";
}
return text;
@@ -192,15 +324,15 @@ namespace ArachnaeSwarm
switch (curCategoryInt)
{
case HoneyProductionCategory.Full:
return "AS.HoneyProduction.Full".Translate();
return "ARA_HoneyProduction.Full".Translate();
case HoneyProductionCategory.High:
return "AS.HoneyProduction.High".Translate();
return "ARA_HoneyProduction.High".Translate();
case HoneyProductionCategory.Medium:
return "AS.HoneyProduction.Medium".Translate();
return "ARA_HoneyProduction.Medium".Translate();
case HoneyProductionCategory.Low:
return "AS.HoneyProduction.Low".Translate();
return "ARA_HoneyProduction.Low".Translate();
case HoneyProductionCategory.Empty:
return "AS.HoneyProduction.Empty".Translate();
return "ARA_HoneyProduction.Empty".Translate();
default:
return "Unknown";
}
@@ -209,6 +341,10 @@ namespace ArachnaeSwarm
// 在UI上绘制
public override void DrawOnGUI(Rect rect, int maxThresholdMarkers = int.MaxValue, float customMargin = -1f, bool drawArrows = true, bool doTooltip = true, Rect? rectForTooltip = null, bool drawLabel = true)
{
// 如果不启用,不显示
if (!IsEnabled)
return;
if (threshPercents == null)
{
threshPercents = new System.Collections.Generic.List<float>
@@ -229,10 +365,6 @@ namespace ArachnaeSwarm
if (base.IsFrozen)
return true;
// 如果没有食物需要,或者食物需要被冻结,则蜜罐生产也冻结
if (FoodNeed == null || FoodNeed.IsFrozen)
return true;
// 如果生物死亡,则冻结
if (pawn.Dead)
return true;
@@ -242,7 +374,7 @@ namespace ArachnaeSwarm
}
// GUI变化箭头总是显示增长
public override int GUIChangeArrow => 1;
public override int GUIChangeArrow => IsEnabled ? 1 : 0;
// 调试调整百分比
protected override void OffsetDebugPercent(float offsetPercent)

View File

@@ -80,7 +80,7 @@ namespace ArachnaeSwarm
// 尝试加载图标,如果失败则使用默认图标
try
{
toggleCommand.icon = ContentFinder<Texture2D>.Get("Wula/UI/Commands/WULA_ShowRadius", false);
toggleCommand.icon = ContentFinder<Texture2D>.Get("ArachnaeSwarm/UI/Commands/ARA_ShowRadius", false);
if (toggleCommand.icon == null)
{
// 使用一个简单的占位符图标