This commit is contained in:
Tourswen
2025-12-15 02:12:09 +08:00
parent 79f461c9e3
commit 8e5cbb1b15
15 changed files with 1719 additions and 454 deletions

Binary file not shown.

View File

@@ -1,90 +1,12 @@
<?xml version="1.0" encoding="utf-8" ?>
<Defs>
<RoomRoleDef>
<defName>None</defName>
<label>none</label>
<workerClass>RoomRoleWorker_None</workerClass>
</RoomRoleDef>
<RoomRoleDef>
<defName>Room</defName>
<label>room</label>
<workerClass>RoomRoleWorker_Room</workerClass>
</RoomRoleDef>
<RoomRoleDef>
<defName>Bedroom</defName>
<label>bedroom</label>
<workerClass>RoomRoleWorker_Bedroom</workerClass>
<avoidViewingArtIfUnowned>true</avoidViewingArtIfUnowned>
<defName>ARA_Incubator_Room</defName>
<label>阿拉克涅孵化间</label>
<workerClass>ArachnaeSwarm.RoomRoleWorker_Incubator</workerClass>
<relatedStats>
<li>Beauty</li>
<li>Cleanliness</li>
<li>Wealth</li>
<li>Space</li>
<li>Impressiveness</li>
</relatedStats>
</RoomRoleDef>
<RoomRoleDef>
<defName>PrisonCell</defName>
<label>prison cell</label>
<workerClass>RoomRoleWorker_PrisonCell</workerClass>
<avoidViewingArtIfUnowned>true</avoidViewingArtIfUnowned>
<relatedStats>
<li>Beauty</li>
<li>Cleanliness</li>
<li>Wealth</li>
<li>Space</li>
<li>Impressiveness</li>
</relatedStats>
</RoomRoleDef>
<RoomRoleDef>
<defName>DiningRoom</defName>
<label>dining room</label>
<workerClass>RoomRoleWorker_DiningRoom</workerClass>
<relatedStats>
<li>Beauty</li>
<li>Cleanliness</li>
<li>Wealth</li>
<li>Space</li>
<li>Impressiveness</li>
</relatedStats>
</RoomRoleDef>
<RoomRoleDef>
<defName>RecRoom</defName>
<label>rec room</label>
<workerClass>RoomRoleWorker_RecRoom</workerClass>
<relatedStats>
<li>Beauty</li>
<li>Cleanliness</li>
<li>Wealth</li>
<li>Space</li>
<li>Impressiveness</li>
</relatedStats>
</RoomRoleDef>
<RoomRoleDef>
<defName>Hospital</defName>
<label>hospital</label>
<workerClass>RoomRoleWorker_Hospital</workerClass>
<avoidViewingArtIfUnowned>true</avoidViewingArtIfUnowned>
<relatedStats>
<li>Beauty</li>
<li>Cleanliness</li>
<li>Space</li>
</relatedStats>
</RoomRoleDef>
<RoomRoleDef>
<defName>Laboratory</defName>
<label>laboratory</label>
<workerClass>RoomRoleWorker_Laboratory</workerClass>
<relatedStats>
<li>Cleanliness</li>
<li>ARA_IncubatorRateFactor</li>
</relatedStats>
</RoomRoleDef>
</Defs>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8" ?>
<Defs>
<RoomStatDef>
<defName>ARA_IncubatorQualityFactor</defName>
<label>孵化质量</label>
<workerClass>RoomStatWorker_FromStatByCurve</workerClass>
<updatePriority>0</updatePriority>
<isHidden>true</isHidden>
<roomlessScore>0.75</roomlessScore>
<inputStat>Space</inputStat>
<curve>
<points>
<li>(-5.0, 0.65 )</li>
<li>(-2.5, 0.75 )</li>
<li>( 0.0, 0.85 )</li>
<li>( 1.0, 1.00 )</li>
</points>
</curve>
</RoomStatDef>
</Defs>

View File

@@ -1,308 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<Defs>
<!--======= Primary stats (calcualated from physical conditions) =======-->
<RoomStatDef>
<defName>Impressiveness</defName>
<label>impressiveness</label>
<workerClass>RoomStatWorker_Impressiveness</workerClass>
<updatePriority>1</updatePriority>
<displayRounded>true</displayRounded>
<roomlessScore>0</roomlessScore>
<scoreStages>
<li>
<label>awful</label>
</li>
<li>
<minScore>20</minScore>
<label>dull</label>
</li>
<li>
<minScore>30</minScore>
<label>mediocre</label>
</li>
<li>
<minScore>40</minScore>
<label>decent</label>
</li>
<li>
<minScore>50</minScore>
<label>slightly impressive</label>
</li>
<li>
<minScore>65</minScore>
<label>somewhat impressive</label>
</li>
<li>
<minScore>85</minScore>
<label>very impressive</label>
</li>
<li>
<minScore>120</minScore>
<label>extremely impressive</label>
</li>
<li>
<minScore>170</minScore>
<label>unbelievably impressive</label>
</li>
<li>
<minScore>240</minScore>
<label>wondrously impressive</label>
</li>
</scoreStages>
</RoomStatDef>
<RoomStatDef>
<defName>Wealth</defName>
<label>wealth</label>
<workerClass>RoomStatWorker_Wealth</workerClass>
<updatePriority>2</updatePriority>
<displayRounded>true</displayRounded>
<roomlessScore>0</roomlessScore>
<scoreStages>
<li>
<label>impoverished</label>
</li>
<li>
<minScore>500</minScore>
<label>somewhat poor</label>
</li>
<li>
<minScore>700</minScore>
<label>mediocre</label>
</li>
<li>
<minScore>2000</minScore>
<label>somewhat rich</label>
</li>
<li>
<minScore>4000</minScore>
<label>rich</label>
</li>
<li>
<minScore>10000</minScore>
<label>luxurious</label>
</li>
<li>
<minScore>40000</minScore>
<label>very luxurious</label>
</li>
<li>
<minScore>100000</minScore>
<label>extremely luxurious</label>
</li>
<li>
<minScore>1000000</minScore>
<label>unbelievably luxurious</label>
</li>
</scoreStages>
</RoomStatDef>
<RoomStatDef>
<defName>Space</defName>
<label>space</label>
<workerClass>RoomStatWorker_Space</workerClass>
<updatePriority>2</updatePriority>
<displayRounded>true</displayRounded>
<roomlessScore>350</roomlessScore>
<scoreStages>
<li>
<label>cramped</label>
</li>
<li>
<minScore>12.5</minScore>
<label>rather tight</label>
</li>
<li>
<minScore>29</minScore>
<label>average-sized</label>
</li>
<li>
<minScore>55</minScore>
<label>somewhat spacious</label>
</li>
<li>
<minScore>70</minScore>
<label>quite spacious</label>
</li>
<li>
<minScore>130</minScore>
<label>very spacious</label>
</li>
<li>
<minScore>349.5</minScore>
<label>extremely spacious</label>
</li>
</scoreStages>
</RoomStatDef>
<RoomStatDef>
<defName>Beauty</defName>
<label>beauty</label>
<workerClass>RoomStatWorker_Beauty</workerClass>
<updatePriority>2</updatePriority>
<roomlessScore>0</roomlessScore>
<scoreStages>
<li>
<label>hideous</label>
</li>
<li>
<minScore>-3.5</minScore>
<label>ugly</label>
</li>
<li>
<minScore>0.0</minScore>
<label>neutral</label>
</li>
<li>
<minScore>2.4</minScore>
<label>pretty</label>
</li>
<li>
<minScore>5.0</minScore>
<label>beautiful</label>
</li>
<li>
<minScore>15.0</minScore>
<label>very beautiful</label>
</li>
<li>
<minScore>50.0</minScore>
<label>extremely beautiful</label>
</li>
<li>
<minScore>100.0</minScore>
<label>unbelievably beautiful</label>
</li>
</scoreStages>
</RoomStatDef>
<RoomStatDef>
<defName>Cleanliness</defName>
<label>cleanliness</label>
<workerClass>RoomStatWorker_Cleanliness</workerClass>
<updatePriority>2</updatePriority>
<roomlessScore>0</roomlessScore>
<scoreStages>
<li>
<label>very dirty</label>
</li>
<li>
<minScore>-1.1</minScore>
<label>dirty</label>
</li>
<li>
<minScore>-0.4</minScore>
<label>slightly dirty</label>
</li>
<li>
<minScore>-0.05</minScore>
<label>clean</label>
</li>
<li>
<minScore>0.4</minScore>
<label>sterile</label>
</li>
</scoreStages>
</RoomStatDef>
<RoomStatDef>
<defName>ReadingBonus</defName>
<label>reading bonus</label>
<workerClass>RoomStatWorker_ReadingBonus</workerClass>
<roomlessScore>1</roomlessScore>
<updatePriority>0</updatePriority>
<isHidden>true</isHidden>
</RoomStatDef>
<!--======= Dependent stats (calculated from other stats) =======-->
<RoomStatDef>
<defName>InfectionChanceFactor</defName>
<label>infection chance factor</label>
<workerClass>RoomStatWorker_FromStatByCurve</workerClass>
<updatePriority>0</updatePriority>
<isHidden>true</isHidden>
<roomlessScore>1.0</roomlessScore>
<inputStat>Cleanliness</inputStat>
<curve>
<points>
<li>(-5, 1.0)</li> <!-- Never worse than no tending at all -->
<li>( 0, 0.5)</li>
<li>( 1, 0.2)</li>
</points>
</curve>
</RoomStatDef>
<RoomStatDef>
<defName>SurgerySuccessChanceCleanlinessFactor</defName>
<label>surgery success chance cleanliness factor</label>
<workerClass>RoomStatWorker_FromStatByCurve</workerClass>
<updatePriority>0</updatePriority>
<isHidden>true</isHidden>
<roomlessScore>0.6</roomlessScore>
<inputStat>Cleanliness</inputStat>
<curve>
<points>
<li>(-5, 0.6)</li>
<li>( 0, 1.0)</li>
<li>( 1, 1.10)</li>
<li>( 5, 1.15)</li>
</points>
</curve>
</RoomStatDef>
<RoomStatDef>
<defName>ARA_ResearchSpeedFactor</defName>
<label>research speed factor</label>
<workerClass>RoomStatWorker_FromStatByCurve</workerClass>
<updatePriority>0</updatePriority>
<isHidden>true</isHidden>
<roomlessScore>0.75</roomlessScore>
<inputStat>Space</inputStat>
<curve>
<points>
<li>(-5.0, 0.75 )</li>
<li>(-2.5, 0.85 )</li>
<li>( 0.0, 1.00 )</li>
<li>( 1.0, 1.15 )</li>
</points>
</curve>
</RoomStatDef>
<RoomStatDef>
<defName>GraveVisitingJoyGainFactor</defName>
<label>grave visiting recreation factor</label>
<workerClass>RoomStatWorker_FromStatByCurve</workerClass>
<updatePriority>0</updatePriority>
<isHidden>true</isHidden>
<roomlessScore>1</roomlessScore>
<inputStat>Impressiveness</inputStat>
<curve>
<points>
<li>(-150, 1.0)</li>
<li>( 0, 1.0)</li>
<li>( 150, 1.4)</li>
</points>
</curve>
</RoomStatDef>
<RoomStatDef>
<defName>FoodPoisonChance</defName>
<label>food poison chance</label>
<workerClass>RoomStatWorker_FromStatByCurve</workerClass>
<updatePriority>0</updatePriority>
<isHidden>true</isHidden>
<roomlessScore>0.02</roomlessScore>
<inputStat>Cleanliness</inputStat>
<curve>
<points>
<li>(-5, 0.05)</li>
<li>(-3.5, 0.025)</li>
<li>(-2, 0)</li>
</points>
</curve>
</RoomStatDef>
</Defs>

View File

@@ -773,7 +773,6 @@
</comps>
</ThingDef>
<ApparelLayerDef>
<defName>ARA_Shield</defName>
<label>盾牌和共生肌群</label>

View File

@@ -100,6 +100,7 @@
<repairable>false</repairable>
<isTargetable>true</isTargetable>
<expandHomeArea>false</expandHomeArea>
<workTableRoomRole>ARA_Incubator_Room</workTableRoomRole>
</building>
<!-- ITab配置 -->

View File

@@ -0,0 +1,100 @@
<?xml version="1.0" encoding="utf-8"?>
<LanguageData>
<!-- Building_Ootheca.cs -->
<Incubating>正在孵化</Incubating>
<Progress>进度</Progress>
<Speed>速度</Speed>
<Quality>质量</Quality>
<Target>目标</Target>
<SpeedMultiplier>速度乘数</SpeedMultiplier>
<QualityMultiplier>质量乘数</QualityMultiplier>
<LarvaIsOperating>幼虫种种进入中</LarvaIsOperating>
<SRemaining>剩余秒数</SRemaining>
<LarvaIsOnTheWay>幼虫种正在路上</LarvaIsOnTheWay>
<TimeRemaining>剩余时间</TimeRemaining>
<Days></Days>
<Hours>小时</Hours>
<CallLarva>指派幼虫种</CallLarva>
<CancelIncubation>取消孵化</CancelIncubation>
<CancelIncubationDesc>取消当前的孵化进度,幼虫种将死在卵囊中,卵囊则会重新张开以接纳其他幼虫种</CancelIncubationDesc>
<Incubate>孵化</Incubate>
<None></None>
<SelectWhatToIncubate>选择要孵化的目标,点击更改目标。</SelectWhatToIncubate>
<NoTargetSelected>未选择目标</NoTargetSelected>
<BaseIncubationTime>基础孵化时间</BaseIncubationTime>
<DaysRequired></DaysRequired>
<Research>研究</Research>
<Completed>已完成</Completed>
<Required>需要</Required>
<ClickToSelectDifferentTarget>点击选择不同的孵化目标。</ClickToSelectDifferentTarget>
<AlreadyIncubating>已经在孵化中</AlreadyIncubating>
<CancelCurrentIncubationFirst>请先取消当前孵化</CancelCurrentIncubationFirst>
<LarvaAlreadyOnTheWay>一只幼虫种已经在前往这个卵囊的路上</LarvaAlreadyOnTheWay>
<NoAvailableLarvaeFound>没有找到可用的幼虫种</NoAvailableLarvaeFound>
<LarvaMustBeOfRace>必须是阿拉克涅幼虫种,并且属于你的派系</LarvaMustBeOfRace>
<LarvaCalled>已召唤幼虫种</LarvaCalled>
<ItWillArriveShortly>它将很快到达并激活卵囊</ItWillArriveShortly>
<LarvaHasArrived>幼虫种已到达</LarvaHasArrived>
<AndIsActivatingTheOotheca>正在激活卵囊...</AndIsActivatingTheOotheca>
<IncubationStartedFor>已开始孵化</IncubationStartedFor>
<ProcessWillCompleteIn>过程将在</ProcessWillCompleteIn>
<DaysBaseTime>天后完成(基础时间)</DaysBaseTime>
<IncubationCancelled>孵化已取消</IncubationCancelled>
<ContentsLost>内容已丢失</ContentsLost>
<IncubationComplete>孵化完成</IncubationComplete>
<HasEmergedWith>已诞生,质量为</HasEmergedWith>
<QualityExcellent>优秀</QualityExcellent>
<QualityGood>良好</QualityGood>
<QualityAverage>一般</QualityAverage>
<QualityPoor></QualityPoor>
<QualityVeryPoor>极差</QualityVeryPoor>
<IncubationSpeedFactors>孵化速度因子:</IncubationSpeedFactors>
<InIncubatorRoom>✓ 在孵化间中 (100%)</InIncubatorRoom>
<NotInIncubatorRoom>✗ 不在孵化间中 (80%速度)</NotInIncubatorRoom>
<NutrientSolutions>✓ 营养液</NutrientSolutions>
<NoNutrientSolutionsNearby>✗ 附近没有营养液</NoNutrientSolutionsNearby>
<TotalSpeedMultiplier>总速度乘数:{0}</TotalSpeedMultiplier>
<IncubationQualityFactors>孵化质量因子:</IncubationQualityFactors>
<BuildingHealth>✓ 建筑血量:</BuildingHealth>
<RoomFactorNormal>✓ 房间因子100%</RoomFactorNormal>
<RoomFactorModified>房间因子:</RoomFactorModified>
<NearbyOothecas>✗ 附近卵囊:</NearbyOothecas>
<NoNearbyOothecas>✓ 附近没有卵囊</NoNearbyOothecas>
<TotalQualityMultiplier>总质量乘数:{0}</TotalQualityMultiplier>
<CallALarvaToActivate>召唤一只幼虫种来进入这个卵囊</CallALarvaToActivate>
<LarvaWillComeToTheOotheca>幼虫种将来到卵囊,进入它,然后开始孵化:</LarvaWillComeToTheOotheca>
<CurrentSpeedMultiplier>当前速度乘数:</CurrentSpeedMultiplier>
<CurrentQualityMultiplier>当前质量乘数:</CurrentQualityMultiplier>
<CancelTheCurrentIncubationProcess>取消当前的孵化过程,在其中的幼虫种将会死亡</CancelTheCurrentIncubationProcess>
<!-- CompProperties_IncubatorData.cs -->
<IncubatorButtonLabel>孵化:{0}</IncubatorButtonLabel>
<IncubatorButtonDesc>点击更改目标</IncubatorButtonDesc>
<IncubatorMenuTitle>选择孵化目标</IncubatorMenuTitle>
<IncubationTarget>孵化目标:</IncubationTarget>
<Requires>需要:</Requires>
<NoDescriptionAvailable>无描述可用</NoDescriptionAvailable>
<IncubationTime>孵化时间</IncubationTime>
<IncubatorButtonTime>孵化时间:{0}天</IncubatorButtonTime>
<!-- ITab_Ootheca_Incubation.cs -->
<IncubationTab>孵化状态</IncubationTab>
<IncubationProgress>孵化进度</IncubationProgress>
<NotAnOotheca>这不是一个卵囊</NotAnOotheca>
<ReadyToIncubate>准备孵化:</ReadyToIncubate>
<NoIncubationTargetSelected>未选择孵化目标</NoIncubationTargetSelected>
<LarvaIsActivatingOotheca>幼虫种正在激活卵囊...</LarvaIsActivatingOotheca>
<SecondsRemaining>剩余秒数</SecondsRemaining>
<QualityBelowTargetAffectsFinalOutcome>质量:低于目标(影响最终结果)</QualityBelowTargetAffectsFinalOutcome>
<QualityProgress>质量进度</QualityProgress>
<IncubationProgressLabel>孵化进度</IncubationProgressLabel>
<!-- JobDriver_OperateIncubator.cs -->
<ActivatingOotheca>正在激活卵囊...</ActivatingOotheca>
<!-- Quality Status Messages -->
<QualityBelowTarget>质量低于目标</QualityBelowTarget>
<!-- Research Related -->
<ResearchRequired>需要研究</ResearchRequired>
</LanguageData>

View File

@@ -2,10 +2,6 @@
"Version": 1,
"WorkspaceRootPath": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\",
"Documents": [
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|e:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\buildings\\building_ootheca\\itab_ootheca_incubation.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:buildings\\building_ootheca\\itab_ootheca_incubation.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}"
@@ -14,28 +10,36 @@
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|e:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\buildings\\building_ootheca\\compproperties_incubatordata.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:buildings\\building_ootheca\\compproperties_incubatordata.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\\itab_ootheca_incubation.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:buildings\\building_ootheca\\itab_ootheca_incubation.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\\jobdriver_operateincubator.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:buildings\\building_ootheca\\jobdriver_operateincubator.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\\powerarmor\\jobdriver_enterpowerarmor.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\\roomroleworker_incubator.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:buildings\\building_ootheca\\roomroleworker_incubator.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\\powerarmor\\jobdriver_enterpowerarmor.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:powerarmor\\jobdriver_enterpowerarmor.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}",
"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\\powerarmor\\ara_powerarmor.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\\powerarmor\\ara_powerarmor.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:powerarmor\\ara_powerarmor.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_arachnaegravengine.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_arachnaegravengine.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:buildings\\building_arachnaegravengine.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\\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\\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}"
},
{
@@ -86,23 +90,49 @@
"DocumentGroups": [
{
"DockedWidth": 200,
"SelectedChildIndex": 1,
"SelectedChildIndex": 2,
"Children": [
{
"$type": "Bookmark",
"Name": "ST:0:0:{1c4feeaa-4718-4aa9-859d-94ce25d182ba}"
},
{
"$type": "Document",
"DocumentIndex": 1,
"Title": "CompProperties_IncubatorData.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\CompProperties_IncubatorData.cs",
"RelativeDocumentMoniker": "Buildings\\Building_Ootheca\\CompProperties_IncubatorData.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\CompProperties_IncubatorData.cs",
"RelativeToolTip": "Buildings\\Building_Ootheca\\CompProperties_IncubatorData.cs",
"ViewState": "AgIAAH0AAAAAAAAAAAAAAK8AAAAJAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-12-14T12:41:01.454Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 0,
"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": "AgIAAK4BAAAAAAAAAAAAAOUBAABIAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-12-14T13:27:29.463Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 2,
"Title": "ITab_Ootheca_Incubation.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\ITab_Ootheca_Incubation.cs",
"RelativeDocumentMoniker": "Buildings\\Building_Ootheca\\ITab_Ootheca_Incubation.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\ITab_Ootheca_Incubation.cs",
"RelativeToolTip": "Buildings\\Building_Ootheca\\ITab_Ootheca_Incubation.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAFQAAAAQAAAAAAAAAA==",
"ViewState": "AgIAAKwAAAAAAAAAAADwv+MAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-12-14T15:14:43.715Z",
"WhenOpened": "2025-12-14T16:21:37.25Z",
"EditorCaption": ""
},
{
@@ -113,40 +143,27 @@
"RelativeDocumentMoniker": "Buildings\\Building_Ootheca\\JobDriver_OperateIncubator.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\JobDriver_OperateIncubator.cs",
"RelativeToolTip": "Buildings\\Building_Ootheca\\JobDriver_OperateIncubator.cs",
"ViewState": "AgIAABQAAAAAAAAAAAA0wDgAAAAlAAAAAAAAAA==",
"ViewState": "AgIAAA4AAAAAAAAAAAAAwC0AAAAgAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-12-14T13:42:44.544Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 1,
"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": "AgIAABAAAAAAAAAAAAAAwMsBAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-12-14T13:27:29.463Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 2,
"Title": "CompProperties_IncubatorData.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\CompProperties_IncubatorData.cs",
"RelativeDocumentMoniker": "Buildings\\Building_Ootheca\\CompProperties_IncubatorData.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\CompProperties_IncubatorData.cs",
"RelativeToolTip": "Buildings\\Building_Ootheca\\CompProperties_IncubatorData.cs",
"ViewState": "AgIAAFgAAAAAAAAAAIA+wJ8AAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-12-14T12:41:01.454Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 4,
"Title": "RoomRoleWorker_Incubator.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\RoomRoleWorker_Incubator.cs",
"RelativeDocumentMoniker": "Buildings\\Building_Ootheca\\RoomRoleWorker_Incubator.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\RoomRoleWorker_Incubator.cs",
"RelativeToolTip": "Buildings\\Building_Ootheca\\RoomRoleWorker_Incubator.cs",
"ViewState": "AgIAAAAAAAAAAAAAAADwvxYAAAA8AAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-12-14T16:16:56.635Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 5,
"Title": "JobDriver_EnterPowerArmor.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\PowerArmor\\JobDriver_EnterPowerArmor.cs",
"RelativeDocumentMoniker": "PowerArmor\\JobDriver_EnterPowerArmor.cs",
@@ -154,12 +171,11 @@
"RelativeToolTip": "PowerArmor\\JobDriver_EnterPowerArmor.cs",
"ViewState": "AgIAAAAAAAAAAAAAAADwvwAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-12-14T13:53:00.443Z",
"EditorCaption": ""
"WhenOpened": "2025-12-14T13:53:00.443Z"
},
{
"$type": "Document",
"DocumentIndex": 5,
"DocumentIndex": 6,
"Title": "ARA_HediffDefOf.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\ARA_HediffDefOf.cs",
"RelativeDocumentMoniker": "ARA_HediffDefOf.cs",
@@ -167,12 +183,11 @@
"RelativeToolTip": "ARA_HediffDefOf.cs",
"ViewState": "AgIAAAAAAAAAAAAAAADwvxIAAAAkAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-12-14T13:29:05.1Z",
"EditorCaption": ""
"WhenOpened": "2025-12-14T13:29:05.1Z"
},
{
"$type": "Document",
"DocumentIndex": 6,
"DocumentIndex": 7,
"Title": "ARA_PowerArmor.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\PowerArmor\\ARA_PowerArmor.cs",
"RelativeDocumentMoniker": "PowerArmor\\ARA_PowerArmor.cs",
@@ -180,12 +195,11 @@
"RelativeToolTip": "PowerArmor\\ARA_PowerArmor.cs",
"ViewState": "AgIAAAAAAAAAAAAAAADwvxAAAABMAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-12-14T12:42:55.951Z",
"EditorCaption": ""
"WhenOpened": "2025-12-14T12:42:55.951Z"
},
{
"$type": "Document",
"DocumentIndex": 7,
"DocumentIndex": 8,
"Title": "Building_ArachnaeGravEngine.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_ArachnaeGravEngine.cs",
"RelativeDocumentMoniker": "Buildings\\Building_ArachnaeGravEngine.cs",
@@ -193,12 +207,11 @@
"RelativeToolTip": "Buildings\\Building_ArachnaeGravEngine.cs",
"ViewState": "AgIAAAAAAAAAAAAAAADwvxcAAAAiAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-12-14T12:37:14.137Z",
"EditorCaption": ""
"WhenOpened": "2025-12-14T12:37:14.137Z"
},
{
"$type": "Document",
"DocumentIndex": 9,
"DocumentIndex": 10,
"Title": "CompSectorSurveillance.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_SectorSurveillance\\CompSectorSurveillance.cs",
"RelativeDocumentMoniker": "Flyover\\ARA_SectorSurveillance\\CompSectorSurveillance.cs",
@@ -210,7 +223,7 @@
},
{
"$type": "Document",
"DocumentIndex": 8,
"DocumentIndex": 9,
"Title": "CompGroundStrafing.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_GroundStrafing\\CompGroundStrafing.cs",
"RelativeDocumentMoniker": "Flyover\\ARA_GroundStrafing\\CompGroundStrafing.cs",
@@ -218,12 +231,11 @@
"RelativeToolTip": "Flyover\\ARA_GroundStrafing\\CompGroundStrafing.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAABsAAAAMAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-30T13:00:11.18Z",
"EditorCaption": ""
"WhenOpened": "2025-10-30T13:00:11.18Z"
},
{
"$type": "Document",
"DocumentIndex": 12,
"DocumentIndex": 13,
"Title": "CompAbilityEffect_GiveHediffWithSkillDuration.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_GiveHediffWithSkillDuration\\CompAbilityEffect_GiveHediffWithSkillDuration.cs",
"RelativeDocumentMoniker": "Abilities\\ARA_GiveHediffWithSkillDuration\\CompAbilityEffect_GiveHediffWithSkillDuration.cs",
@@ -235,7 +247,7 @@
},
{
"$type": "Document",
"DocumentIndex": 13,
"DocumentIndex": 14,
"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",
@@ -247,7 +259,7 @@
},
{
"$type": "Document",
"DocumentIndex": 11,
"DocumentIndex": 12,
"Title": "CompAbilityEffect_AircraftStrike.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_AircraftHangar\\CompAbilityEffect_AircraftStrike.cs",
"RelativeDocumentMoniker": "Flyover\\ARA_AircraftHangar\\CompAbilityEffect_AircraftStrike.cs",
@@ -259,7 +271,7 @@
},
{
"$type": "Document",
"DocumentIndex": 10,
"DocumentIndex": 11,
"Title": "ThingclassFlyOver.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ThingclassFlyOver.cs",
"RelativeDocumentMoniker": "Flyover\\ThingclassFlyOver.cs",
@@ -271,7 +283,7 @@
},
{
"$type": "Document",
"DocumentIndex": 14,
"DocumentIndex": 15,
"Title": "CompAbilityEffect_AbilityShowSpawnablePawns.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_ShowSpawnablePawnsList\\CompAbilityEffect_AbilityShowSpawnablePawns.cs",
"RelativeDocumentMoniker": "Abilities\\ARA_ShowSpawnablePawnsList\\CompAbilityEffect_AbilityShowSpawnablePawns.cs",
@@ -283,7 +295,7 @@
},
{
"$type": "Document",
"DocumentIndex": 16,
"DocumentIndex": 17,
"Title": "CompAircraftHangar.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_AircraftHangar\\CompAircraftHangar.cs",
"RelativeDocumentMoniker": "Flyover\\ARA_AircraftHangar\\CompAircraftHangar.cs",
@@ -295,7 +307,7 @@
},
{
"$type": "Document",
"DocumentIndex": 15,
"DocumentIndex": 16,
"Title": "CompAbilityEffect_SpawnFlyOver.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_SpawnFlyOver\\CompAbilityEffect_SpawnFlyOver.cs",
"RelativeDocumentMoniker": "Flyover\\ARA_SpawnFlyOver\\CompAbilityEffect_SpawnFlyOver.cs",
@@ -307,7 +319,7 @@
},
{
"$type": "Document",
"DocumentIndex": 17,
"DocumentIndex": 18,
"Title": "WorldComponent_AircraftManager.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_AircraftHangar\\WorldComponent_AircraftManager.cs",
"RelativeDocumentMoniker": "Flyover\\ARA_AircraftHangar\\WorldComponent_AircraftManager.cs",
@@ -319,7 +331,7 @@
},
{
"$type": "Document",
"DocumentIndex": 18,
"DocumentIndex": 19,
"Title": "CompProperties_FlyOverEscort.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_FlyOverEscort\\CompProperties_FlyOverEscort.cs",
"RelativeDocumentMoniker": "Flyover\\ARA_FlyOverEscort\\CompProperties_FlyOverEscort.cs",

View File

@@ -119,6 +119,7 @@
<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\RoomRoleWorker_Incubator.cs" />
<Compile Include="Buildings\Building_TurretGunHasSpeed.cs" />
<Compile Include="Building_Comps\ARA_Building_RefuelingVat\Building_RefuelingVat.cs" />
<Compile Include="Building_Comps\ARA_Building_RefuelingVat\CompProperties_RefuelingVat.cs" />

View File

@@ -0,0 +1,840 @@
using RimWorld;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using Verse;
using Verse.AI;
namespace ArachnaeSwarm
{
public class Building_Ootheca : Building
{
// 引用组件
public CompIncubatorData IncubatorData => this.TryGetComp<CompIncubatorData>();
// 孵化状态 - 设为public以便ITab访问
public bool isIncubating = false;
public float incubationProgress = 0f;
public float incubationDuration = 0f;
public PawnKindDef incubatingPawnKind = null;
// 幼虫交互相关 - 设为public以便ITab访问
public Pawn assignedLarva = null;
public int larvaOperateTicksRemaining = 0;
// 速度乘数系统
private float speedMultiplier = 1.0f;
private int lastMultiplierUpdateTick = -1;
private const int MultiplierUpdateInterval = 250; // 每250ticks更新一次
// 质量系统
private float qualityMultiplier = 1.0f;
private float qualityProgress = 0f;
private float qualityTotal = 0f;
// 属性访问器
public float SpeedMultiplier
{
get
{
// 每MultiplierUpdateInterval ticks更新一次
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("IncubationSpeedFactors".Translate());
builder.AppendLine();
// 1. 检查是否在孵化间中
bool inIncubatorRoom = IsInIncubatorRoom();
builder.AppendLine(inIncubatorRoom ?
"InIncubatorRoom".Translate() :
"NotInIncubatorRoom".Translate());
// 2. 检查营养液数量
int nutrientSolutionCount = CountNearbyNutrientSolutions();
if (nutrientSolutionCount > 0)
{
builder.AppendLine("NutrientSolutions".Translate(nutrientSolutionCount, nutrientSolutionCount));
}
else
{
builder.AppendLine("NoNutrientSolutionsNearby".Translate());
}
builder.AppendLine();
builder.AppendLine("TotalSpeedMultiplier".Translate(SpeedMultiplier.ToStringPercent()));
return builder.ToString().TrimEndNewlines();
}
// 获取质量乘数的详细因子信息(用于工具提示)
public string GetQualityFactorsDescription()
{
var builder = new StringBuilder();
builder.AppendLine("IncubationQualityFactors".Translate());
builder.AppendLine();
// 1. 建筑血量损失百分比
float healthPercent = (float)HitPoints / MaxHitPoints;
builder.AppendLine("BuildingHealth".Translate(healthPercent.ToStringPercent()));
// 2. 房间的ARA_IncubatorQualityFactor
float roomFactor = GetRoomQualityFactor();
builder.AppendLine(roomFactor == 1.0f ?
"RoomFactorNormal".Translate() :
$"{(roomFactor > 1.0f ? "" : "")} {"RoomFactorModified".Translate()}{roomFactor.ToStringPercent()}");
// 3. 附近每一个ARA_Pawn_Ootheca每一个-10%
int nearbyOothecaCount = CountNearbyOtherOothecas();
if (nearbyOothecaCount > 0)
{
builder.AppendLine("NearbyOothecas".Translate(nearbyOothecaCount, Mathf.Min(nearbyOothecaCount * 10, 100)));
}
else
{
builder.AppendLine("NoNearbyOothecas".Translate());
}
builder.AppendLine();
builder.AppendLine("TotalQualityMultiplier".Translate(QualityMultiplier.ToStringPercent()));
return builder.ToString().TrimEndNewlines();
}
// 构建呼叫幼虫描述
private string BuildCallLarvaDescription(IncubationConfig config)
{
var builder = new StringBuilder();
builder.AppendLine("CallALarvaToActivate".Translate());
builder.AppendLine();
builder.AppendLine("LarvaWillComeToTheOotheca".Translate());
builder.AppendLine(config.pawnKind.LabelCap);
builder.AppendLine();
return builder.ToString().TrimEndNewlines();
}
// 呼叫幼虫
private void CallLarva()
{
// 检查是否已经在孵化中或有幼虫在任务中
if (isIncubating)
{
Messages.Message("AlreadyIncubating".Translate() + " " + "CancelCurrentIncubationFirst".Translate(),
MessageTypeDefOf.RejectInput);
return;
}
if (assignedLarva != null)
{
Messages.Message("LarvaAlreadyOnTheWay".Translate(),
MessageTypeDefOf.RejectInput);
return;
}
// 查找可用的幼虫
var larva = FindLarva();
if (larva == null)
{
Messages.Message("NoAvailableLarvaeFound".Translate() + " " + "LarvaMustBeOfRace".Translate(),
MessageTypeDefOf.RejectInput);
return;
}
// 给幼虫分配任务
var job = JobMaker.MakeJob(ARA_JobDefOf.ARA_OperateIncubator, this);
job.count = 1;
larva.jobs.TryTakeOrderedJob(job, JobTag.MiscWork);
assignedLarva = larva;
Messages.Message("LarvaCalled".Translate() + " " + "ItWillArriveShortly".Translate(),
MessageTypeDefOf.PositiveEvent);
}
// 幼虫开始操作
public void NotifyLarvaArrived(Pawn larva)
{
// 验证幼虫种族
if (larva.def.defName != "ArachnaeBase_Race_Larva")
{
Log.Warning($"Invalid larva arrived: {larva.def.defName}");
return;
}
// 开始操作计时3秒 = 180 ticks
larvaOperateTicksRemaining = 180;
assignedLarva = larva;
// 显示消息
Messages.Message("LarvaHasArrived".Translate() + " " + "AndIsActivatingTheOotheca".Translate(),
MessageTypeDefOf.SilentInput);
}
// 幼虫完成操作由JobDriver调用
public void NotifyLarvaOperationComplete(Pawn larva)
{
// 验证是当前分配的幼虫
if (larva != assignedLarva)
{
Log.Warning("Larva operation complete called with wrong larva.");
return;
}
var config = IncubatorData?.SelectedConfig;
if (config == null)
{
Log.Error("No incubation config selected when larva completed operation.");
return;
}
// 保存孵化信息
incubatingPawnKind = config.pawnKind;
incubationDuration = config.daysRequired * 60000f; // RimWorld中1天=60000ticks
incubationProgress = 0f;
isIncubating = true;
// 初始化质量系统
qualityTotal = incubationDuration; // 质量总量与孵化时间相同
qualityProgress = 0f;
UpdateQualityMultiplier();
// 更新速度乘数
UpdateSpeedMultiplier();
// 清除幼虫引用幼虫已在JobDriver中被删除
assignedLarva = null;
larvaOperateTicksRemaining = 0;
// 显示消息
Messages.Message("IncubationStartedFor".Translate() + " " + incubatingPawnKind.LabelCap + ". " +
"ProcessWillCompleteIn".Translate() + " " + config.daysRequired + " " + "DaysBaseTime".Translate(),
MessageTypeDefOf.PositiveEvent);
}
// 取消孵化
private void CancelIncubation()
{
if (!isIncubating) return;
isIncubating = false;
incubationProgress = 0f;
incubationDuration = 0f;
incubatingPawnKind = null;
qualityProgress = 0f;
qualityTotal = 0f;
Messages.Message("IncubationCancelled".Translate() + " " + "ContentsLost".Translate(),
MessageTypeDefOf.NeutralEvent);
}
// 完成孵化
private void CompleteIncubation()
{
if (incubatingPawnKind == null) return;
// 计算最终质量百分比
float finalQualityPercent = QualityPercent;
// 生成新的pawn
var pawn = PawnGenerator.GeneratePawn(incubatingPawnKind, Faction);
// 应用质量影响
ApplyQualityEffects(pawn, finalQualityPercent);
// 尝试将pawn放置在地图上
var spawnPos = Position;
if (!pawn.Spawned)
{
GenSpawn.Spawn(pawn, spawnPos, Map);
}
// 重置孵化状态
isIncubating = false;
incubationProgress = 0f;
incubationDuration = 0f;
incubatingPawnKind = null;
qualityProgress = 0f;
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();
Messages.Message("IncubationComplete".Translate() + " " + pawn.LabelCap + " " +
"HasEmergedWith".Translate() + " " + qualityText + " " +
"quality".Translate() + " (" + finalQualityPercent.ToStringPercent() + ").",
MessageTypeDefOf.PositiveEvent);
}
// 显示额外信息(简化版本,只显示速率,不显示因子)
public override string GetInspectString()
{
var baseString = base.GetInspectString();
var builder = new StringBuilder();
if (!string.IsNullOrEmpty(baseString))
{
builder.Append(baseString);
}
if (isIncubating && incubatingPawnKind != null)
{
float progressPercent = AdjustedProgressPercent;
float daysRemaining = GetRemainingDays();
float hoursRemaining = GetRemainingHours();
if (builder.Length > 0) builder.AppendLine();
builder.Append("Incubating".Translate() + ": " + incubatingPawnKind.LabelCap);
builder.AppendLine();
builder.Append("Progress".Translate() + ": " + progressPercent.ToStringPercent());
builder.AppendLine();
// 显示剩余时间
string timeText = "TimeRemaining".Translate() + ": " + daysRemaining.ToString("F1") + " " + "Days".Translate();
if (hoursRemaining > 0.1f && daysRemaining < 1f)
{
timeText += " (" + hoursRemaining.ToString("F1") + " " + "Hours".Translate() + ")";
}
builder.Append(timeText);
// 显示速度和质量(简化版本)
builder.AppendLine();
builder.Append("Speed".Translate() + ": " + SpeedMultiplier.ToStringPercent() + ", " +
"Quality".Translate() + ": " + QualityMultiplier.ToStringPercent());
}
else if (assignedLarva != null)
{
if (builder.Length > 0) builder.AppendLine();
if (larvaOperateTicksRemaining > 0)
{
float secondsRemaining = larvaOperateTicksRemaining / 60f;
builder.Append("LarvaIsOperating".Translate() + ": " + secondsRemaining.ToString("F1") + " " + "SRemaining".Translate());
}
else
{
builder.Append("LarvaIsOnTheWay".Translate());
}
}
else if (!isIncubating)
{
var config = IncubatorData?.SelectedConfig;
if (config != null)
{
if (builder.Length > 0) builder.AppendLine();
builder.Append("Target".Translate() + ": " + config.pawnKind.LabelCap);
// 只显示当前乘数,不显示条件详情
builder.AppendLine();
builder.Append("SpeedMultiplier".Translate() + ": " + SpeedMultiplier.ToStringPercent() + ", " +
"QualityMultiplier".Translate() + ": " + QualityMultiplier.ToStringPercent());
}
}
return builder.ToString().TrimEndNewlines();
}
// 获取剩余时间ticks
public float GetRemainingTicks()
{
if (!isIncubating || incubationDuration <= incubationProgress) return 0f;
float remainingProgress = incubationDuration - incubationProgress;
float currentSpeed = SpeedMultiplier;
// 如果速度为0或负数返回一个很大的数
if (currentSpeed <= 0) return float.MaxValue;
return remainingProgress / currentSpeed;
}
// 获取剩余天数
public float GetRemainingDays()
{
return GetRemainingTicks() / 60000f; // RimWorld中1天=60000ticks
}
// 获取剩余小时
public float GetRemainingHours()
{
float remainingTicks = GetRemainingTicks();
return (remainingTicks % 60000f) / 2500f;
}
// Gizmos
public override IEnumerable<Gizmo> GetGizmos()
{
foreach (var gizmo in base.GetGizmos())
{
yield return gizmo;
}
// 只有玩家派系才显示Gizmo
if (Faction == Faction.OfPlayer)
{
// 不在孵化中时才显示切换目标按钮
if (!isIncubating && IncubatorData?.IncubationConfigs?.Count > 0)
{
yield return CreateTargetSwitchGizmo();
}
// 不在孵化中且研究完成时才显示呼叫按钮
var config = IncubatorData?.SelectedConfig;
if (!isIncubating && config != null && config.IsResearchComplete)
{
yield return new Command_Action
{
defaultLabel = "CallLarva".Translate(),
defaultDesc = BuildCallLarvaDescription(config),
icon = ContentFinder<Texture2D>.Get("UI/Commands/CallLarva", false) ?? BaseContent.BadTex,
action = CallLarva,
hotKey = KeyBindingDefOf.Misc3
};
}
// 如果正在孵化,显示取消按钮
if (isIncubating)
{
yield return new Command_Action
{
defaultLabel = "CancelIncubation".Translate(),
defaultDesc = "CancelIncubationDesc".Translate(),
icon = ContentFinder<Texture2D>.Get("UI/Commands/Cancel", false) ?? TexCommand.ClearPrioritizedWork,
action = CancelIncubation
};
}
}
}
// 创建目标切换Gizmo
private Gizmo CreateTargetSwitchGizmo()
{
var configs = IncubatorData?.IncubationConfigs;
if (configs == null || configs.Count == 0) return null;
var props = IncubatorData?.props as CompProperties_IncubatorData;
var selectedConfig = IncubatorData?.SelectedConfig;
var switchButton = new Command_Action
{
defaultLabel = BuildSwitchButtonLabel(selectedConfig, props),
defaultDesc = BuildSwitchButtonDescription(selectedConfig, props),
icon = LoadIcon(selectedConfig?.buttonIconPath ?? props?.defaultIconPath),
action = ShowSelectionMenu,
hotKey = KeyBindingDefOf.Misc2
};
// 如果当前配置不可用(研究未完成),禁用按钮
if (selectedConfig != null && !selectedConfig.IsResearchComplete)
{
if (selectedConfig.requiredResearch != null)
{
switchButton.Disable($"Requires research: {selectedConfig.requiredResearch.LabelCap}");
}
}
return switchButton;
}
// 构建切换按钮标签
private string BuildSwitchButtonLabel(IncubationConfig config, CompProperties_IncubatorData props)
{
if (config != null && config.pawnKind != null)
{
return (props?.buttonLabel ?? "Incubate: {0}").Translate(config.pawnKind.LabelCap);
}
return (props?.buttonLabel ?? "Incubate: {0}").Translate("None");
}
// 构建切换按钮描述
private string BuildSwitchButtonDescription(IncubationConfig config, CompProperties_IncubatorData props)
{
var builder = new StringBuilder();
// 第一部分:按钮功能说明
builder.AppendLine((props?.buttonDesc ?? "IncubatorButtonDesc").Translate());
builder.AppendLine();
if (config != null)
{
// 第二部分:当前选择的详细信息
if (config.pawnKind != null)
{
builder.AppendLine($"IncubatorButtonLabel".Translate(config.pawnKind.LabelCap));
if (!string.IsNullOrEmpty(config.pawnKind.description))
{
builder.AppendLine(config.pawnKind.description);
}
}
builder.AppendLine($"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)");
}
}
else
{
builder.AppendLine("No target selected");
}
builder.AppendLine();
builder.AppendLine("IncubatorButtonDesc".Translate());
return builder.ToString().TrimEndNewlines();
}
// 加载图标
private Texture2D LoadIcon(string path)
{
if (!string.IsNullOrEmpty(path))
{
var icon = ContentFinder<Texture2D>.Get(path, false);
if (icon != null) return icon;
}
return ContentFinder<Texture2D>.Get("UI/Commands/Default", false) ?? BaseContent.BadTex;
}
// 显示选择菜单
private void ShowSelectionMenu()
{
var configs = IncubatorData?.IncubationConfigs;
var props = IncubatorData?.props as CompProperties_IncubatorData;
if (configs == null || configs.Count == 0) return;
var options = new List<FloatMenuOption>();
int currentIndex = IncubatorData.GetSelectedIndex();
for (int i = 0; i < configs.Count; i++)
{
int index = i; // 捕获索引
var config = configs[i];
if (config == null || config.pawnKind == null) continue;
string label = config.pawnKind.LabelCap;
string description = config.GetDescription();
// 标记当前选择的项目
string prefix = (i == currentIndex) ? "✓ " : " ";
// 创建选项
var 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 "+ "ResearchRequired".Translate() + " " + config.requiredResearch.LabelCap;
}
options.Add(option);
}
if (options.Count > 0)
{
Find.WindowStack.Add(new FloatMenu(options,
(props?.menuTitle ?? "Select Incubation Target").Translate()));
}
}
// 切换到特定配置
private void SwitchToConfig(int index)
{
if (IncubatorData != null)
{
IncubatorData.SwitchToConfig(index);
var config = IncubatorData.SelectedConfig;
if (config != null && config.pawnKind != null)
{
Messages.Message($"Incubation target switched to: {config.pawnKind.LabelCap}",
this, MessageTypeDefOf.SilentInput);
}
}
}
// 查找幼虫 - 现在通过种族查找
private Pawn FindLarva()
{
// 查找地图中属于玩家派系的ArachnaeBase_Race_Larva幼虫
var map = Map;
if (map == null) return null;
foreach (var pawn in map.mapPawns.SpawnedPawnsInFaction(Faction))
{
// 检查pawn种族是否为幼虫
if (pawn.def.defName == "ArachnaeBase_Race_Larva")
{
// 检查pawn是否能够移动且没有其他重要任务
if (!pawn.Downed && !pawn.InMentalState &&
pawn.mindState != null &&
(pawn.CurJob == null || pawn.CurJob.def != ARA_JobDefOf.ARA_OperateIncubator))
{
return pawn;
}
}
}
return null;
}
// 孵化进度 - 使用override而不是new
protected override void Tick()
{
base.Tick();
// 减少操作剩余时间(仅用于显示)
if (larvaOperateTicksRemaining > 0)
{
larvaOperateTicksRemaining--;
// 注意孵化启动现在由JobDriver触发这里只更新显示
}
// 更新孵化进度和质量进度
if (isIncubating)
{
// 更新质量乘数(与速度乘数同步更新)
if (lastMultiplierUpdateTick < 0 || Find.TickManager.TicksGame - lastMultiplierUpdateTick >= MultiplierUpdateInterval)
{
UpdateSpeedMultiplier();
UpdateQualityMultiplier();
}
// 应用速度乘数
float currentSpeed = SpeedMultiplier;
incubationProgress += currentSpeed;
// 质量进度与速度同步增长,但乘以质量乘数
qualityProgress += currentSpeed * QualityMultiplier;
if (incubationProgress >= incubationDuration)
{
CompleteIncubation();
}
}
}
// 应用质量效果到生成的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);
// }
// }
//}
}
// 检查是否在孵化间中
private bool IsInIncubatorRoom()
{
var room = this.GetRoom();
if (room == null) return false;
return room.Role != null && room.Role.defName == "ARA_Incubator_Room";
}
// 计算周围4格内的营养液数量
private int CountNearbyNutrientSolutions()
{
var map = Map;
if (map == null) return 0;
int count = 0;
int radius = 4; // 4格半径
// 检查周围4格范围内的所有单元格
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()
{
var room = this.GetRoom();
if (room == null) return 1.0f;
// 获取房间的ARA_IncubatorQualityFactor统计值
// 如果没有这个统计,返回默认值
var statDef = DefDatabase<RoomStatDef>.GetNamedSilentFail("ARA_IncubatorQualityFactor");
if (statDef != null)
{
return room.GetStat(statDef);
}
return 1.0f;
}
// 计算附近其他Ootheca的数量
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;
// 检查是否为ARA_Pawn_Ootheca
if (building.def.defName == "ARA_Pawn_Ootheca")
{
// 检查是否在同一个房间或附近
var room = building.GetRoom();
if (room != null)
{
// 如果在同一个房间或者距离较近10格内
float distance = building.Position.DistanceTo(this.Position);
if (room == this.GetRoom() || distance <= 10f)
{
count++;
}
}
}
}
return count;
}
// 更新速度乘数
private void UpdateSpeedMultiplier()
{
float multiplier = 1.0f;
// 1. 检查是否在孵化间中
if (!IsInIncubatorRoom())
{
multiplier *= 0.8f; // 不在孵化间中速度80%
}
// 2. 计算周围营养液的加成
int nutrientSolutionCount = CountNearbyNutrientSolutions();
float nutrientBonus = 1.0f + (nutrientSolutionCount * 0.01f); // 每个+1%
multiplier *= nutrientBonus;
speedMultiplier = multiplier;
lastMultiplierUpdateTick = Find.TickManager.TicksGame;
}
// 更新质量乘数
private void UpdateQualityMultiplier()
{
float multiplier = 1.0f;
// 1. 建筑血量损失百分比
float healthPercent = (float)HitPoints / MaxHitPoints;
multiplier *= healthPercent;
// 2. 房间的ARA_IncubatorQualityFactor
float roomFactor = GetRoomQualityFactor();
multiplier *= roomFactor;
// 3. 附近每一个ARA_Pawn_Ootheca每一个-10%
int nearbyOothecaCount = CountNearbyOtherOothecas();
float oothecaPenalty = Mathf.Max(0f, 1.0f - (nearbyOothecaCount * 0.10f)); // 最多减到0
multiplier *= oothecaPenalty;
// 确保乘数在合理范围内0-1
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 incubatingPawnKind, "incubatingPawnKind");
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,178 @@
using System.Collections.Generic;
using System.Text;
using RimWorld;
using UnityEngine;
using Verse;
namespace ArachnaeSwarm
{
// IncubationConfig 保持不变
public class IncubationConfig : IExposable
{
public PawnKindDef pawnKind;
public ResearchProjectDef requiredResearch;
public float daysRequired;
public string buttonIconPath;
public IncubationConfig() { }
public IncubationConfig(PawnKindDef pawnKind, ResearchProjectDef requiredResearch = null,
float daysRequired = 1f, string buttonIconPath = null)
{
this.pawnKind = pawnKind;
this.requiredResearch = requiredResearch;
this.daysRequired = daysRequired;
this.buttonIconPath = buttonIconPath;
}
public void ExposeData()
{
Scribe_Defs.Look(ref pawnKind, "pawnKind");
Scribe_Defs.Look(ref requiredResearch, "requiredResearch");
Scribe_Values.Look(ref daysRequired, "daysRequired", 1f);
Scribe_Values.Look(ref buttonIconPath, "buttonIconPath");
}
// 检查是否满足研究要求
public bool IsResearchComplete => requiredResearch == null || requiredResearch.IsFinished;
// 在配置描述中使用翻译
public string GetDescription()
{
var builder = new StringBuilder();
builder.AppendLine(pawnKind?.description ?? "NoDescriptionAvailable".Translate());
builder.AppendLine();
builder.AppendLine("IncubationTime".Translate() + " " + daysRequired + " " + "DaysRequired".Translate());
if (requiredResearch != null)
{
if (requiredResearch.IsFinished)
builder.AppendLine("Research".Translate() + ": " + requiredResearch.LabelCap + " (" + "Completed".Translate() + ")");
else
builder.AppendLine("Research".Translate() + ": " + requiredResearch.LabelCap + " (" + "Required".Translate() + ")");
}
return builder.ToString().TrimEndNewlines();
}
}
// 组件属性定义
public class CompProperties_IncubatorData : CompProperties
{
public List<IncubationConfig> incubationConfigs = new List<IncubationConfig>();
// 默认选择索引
public int defaultIndex = 0;
// Gizmo相关配置
public string buttonLabel = "IncubatorButtonLabel"; // 按钮标签翻译键
public string buttonDesc = "IncubatorButtonDesc"; // 按钮描述翻译键
public string menuTitle = "IncubatorMenuTitle"; // 菜单标题翻译键
public string defaultIconPath = "UI/Commands/Default";
public CompProperties_IncubatorData()
{
compClass = typeof(CompIncubatorData);
}
public override void ResolveReferences(ThingDef parentDef)
{
base.ResolveReferences(parentDef);
if (incubationConfigs == null)
incubationConfigs = new List<IncubationConfig>();
}
}
// 数据存储组件仅数据无Gizmo
public class CompIncubatorData : ThingComp
{
private CompProperties_IncubatorData Props => (CompProperties_IncubatorData)props;
// 当前选择的配置索引
private int selectedIndex = -1;
// 公开获取孵化配置列表的方法
public List<IncubationConfig> IncubationConfigs => Props?.incubationConfigs ?? new List<IncubationConfig>();
// 获取当前选择的配置
public IncubationConfig 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];
}
}
// 获取当前选择的PawnKind
public PawnKindDef SelectedPawnKind => SelectedConfig?.pawnKind;
// 切换到特定索引
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);
if (Scribe.mode == LoadSaveMode.PostLoadInit)
{
// 验证索引有效性
if (selectedIndex >= IncubationConfigs.Count)
selectedIndex = Mathf.Clamp(Props.defaultIndex, 0, IncubationConfigs.Count - 1);
}
}
// 在建筑信息中显示额外信息
public override string CompInspectStringExtra()
{
var current = SelectedConfig;
if (current != null)
{
string status = "IncubationTarget".Translate() + current.pawnKind.LabelCap;
if (current.requiredResearch != null && !current.requiredResearch.IsFinished)
{
status += " (" + "Requires".Translate() + ": " + current.requiredResearch.LabelCap + ")";
}
return status;
}
return base.CompInspectStringExtra();
}
}
}

View File

@@ -0,0 +1,369 @@
using UnityEngine;
using Verse;
using System.Collections.Generic;
using System.Linq;
using RimWorld;
using Verse.Sound;
namespace ArachnaeSwarm
{
public class ITab_Ootheca_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_Ootheca_Incubation()
{
size = new Vector2(TabWidth, TabHeight);
labelKey = "IncubationTab";
tutorTag = "Incubation";
}
protected override void FillTab()
{
Rect rect = new Rect(0f, 0f, size.x, size.y).ContractedBy(Margin);
Widgets.DrawMenuSection(rect);
Building_Ootheca ootheca = SelThing as Building_Ootheca;
if (ootheca == null)
{
Widgets.Label(rect, "NotAnOotheca".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 = "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 = "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 = "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.incubatingPawnKind != 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, "Target".Translate() + ": " + ootheca.incubatingPawnKind.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, "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, "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 = "TimeRemaining".Translate() + ": " + daysRemaining.ToString("F1") + " " + "Days".Translate();
if (hoursRemaining > 0.1f && daysRemaining < 1f)
{
timeText += " (" + hoursRemaining.ToString("F1") + " " + "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, "LarvaIsActivatingOotheca".Translate() + "\n" +
secondsRemaining.ToString("F1") + " " + "SecondsRemaining".Translate());
}
else
{
Widgets.Label(statusRect, "LarvaIsOnTheWay".Translate());
}
// 为幼虫状态增加一些间距
curY += SmallLabelHeight * 2 + 15f;
// 不在孵化中时,也显示目标配置
var config = ootheca.IncubatorData?.SelectedConfig;
if (config != null)
{
curY += 10f;
Rect targetRect = new Rect(0f, curY, viewRect.width, SmallLabelHeight * 3);
string targetText = "ReadyToIncubate".Translate() + "\n" + config.pawnKind.LabelCap;
if (!config.IsResearchComplete && config.requiredResearch != null)
{
targetText += "\n" + "(" + "Requires".Translate() + ": " + config.requiredResearch.LabelCap + ")";
}
Widgets.Label(targetRect, targetText);
}
}
else
{
// 不在孵化中,显示当前选择的目标
var config = ootheca.IncubatorData?.SelectedConfig;
if (config != null)
{
Rect targetRect = new Rect(0f, curY, viewRect.width, SmallLabelHeight * 3);
string targetText = "ReadyToIncubate".Translate() + "\n" + config.pawnKind.LabelCap;
if (!config.IsResearchComplete && config.requiredResearch != null)
{
targetText += "\n" + "(" + "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, "NoIncubationTargetSelected".Translate());
curY += SmallLabelHeight + 10f;
}
}
// 添加一些底部空白区域,让布局看起来不那么拥挤
curY += 20f;
// 更新滚动区域的实际高度
viewRect.height = curY;
Widgets.EndScrollView();
}
// 检查是否在孵化间中ITab内部使用
private bool IsInIncubatorRoom(Building_Ootheca ootheca)
{
var room = ootheca.GetRoom();
if (room == null) return false;
return room.Role != null && room.Role.defName == "ARA_Incubator_Room";
}
// 计算周围4格内的营养液数量ITab内部使用
private int CountNearbyNutrientSolutions(Building_Ootheca ootheca)
{
var map = ootheca.Map;
if (map == null) return 0;
int count = 0;
int radius = 4; // 4格半径
var position = ootheca.Position;
// 检查周围4格范围内的所有单元格
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;
}
// 计算房间质量因子ITab内部使用
private float GetRoomQualityFactor(Building_Ootheca ootheca)
{
var room = ootheca.GetRoom();
if (room == null) return 1.0f;
// 获取房间的ARA_IncubatorQualityFactor统计值
var statDef = DefDatabase<RoomStatDef>.GetNamedSilentFail("ARA_IncubatorQualityFactor");
if (statDef != null)
{
return room.GetStat(statDef);
}
return 1.0f;
}
// 计算附近其他Ootheca的数量ITab内部使用
private int CountNearbyOtherOothecas(Building_Ootheca ootheca)
{
var map = ootheca.Map;
if (map == null) return 0;
int count = 0;
var position = ootheca.Position;
var room = ootheca.GetRoom();
// 查找地图上所有的Ootheca
var allOothecas = map.listerThings.ThingsOfDef(ootheca.def);
foreach (var thing in allOothecas)
{
// 排除自己
if (thing == ootheca) continue;
// 检查是否在同一个房间或附近10格内
float distance = thing.Position.DistanceTo(position);
var otherRoom = thing.GetRoom();
if ((room != null && room == otherRoom) || distance <= 10f)
{
count++;
}
}
return count;
}
protected override void UpdateSize()
{
// 固定大小
size = new Vector2(TabWidth, TabHeight);
}
}
}

View File

@@ -0,0 +1,80 @@
using RimWorld;
using System.Collections.Generic;
using Verse;
using Verse.AI;
namespace ArachnaeSwarm
{
public class JobDriver_OperateIncubator : JobDriver
{
private const int OperationDuration = 180; // 3 seconds = 180 ticks
// 目标建筑
private Building_Ootheca Ootheca => (Building_Ootheca)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(() => Ootheca == null);
// 1. 移动到建筑
yield return Toils_Goto.GotoThing(TargetIndex.A, PathEndMode.InteractionCell)
.FailOnSomeonePhysicallyInteracting(TargetIndex.A);
// 2. 等待片刻(让动物有时间转身)
yield return Toils_General.WaitWith(TargetIndex.A, 10, true, true);
// 3. 开始操作3秒
var operate = new Toil();
operate.initAction = () =>
{
// 通知建筑幼虫已到达
Ootheca?.NotifyLarvaArrived(pawn);
};
operate.tickAction = () =>
{
// 面向建筑
pawn.rotationTracker.FaceCell(Ootheca.Position);
};
operate.defaultCompleteMode = ToilCompleteMode.Delay;
operate.defaultDuration = OperationDuration;
operate.WithProgressBar(TargetIndex.A, () =>
(float)(OperationDuration - operate.actor.jobs.curDriver.ticksLeftThisToil) / OperationDuration);
yield return operate;
// 4. 完成操作并删除幼虫
yield return new Toil
{
initAction = () =>
{
// 操作完成,删除幼虫并开始孵化
if (Ootheca != null && pawn != null && pawn.def.defName == "ArachnaeBase_Race_Larva")
{
// 通知建筑幼虫操作完成
Ootheca.NotifyLarvaOperationComplete(pawn);
// 删除幼虫
pawn.Destroy(DestroyMode.Vanish);
}
},
defaultCompleteMode = ToilCompleteMode.Instant
};
}
public override string GetReport()
{
if (Ootheca != null)
{
return "ActivatingOotheca".Translate();
}
return base.GetReport();
}
}
}

View File

@@ -0,0 +1,51 @@
using System.Collections.Generic;
using RimWorld;
using Verse;
namespace ArachnaeSwarm
{
public class RoomRoleWorker_Incubator : RoomRoleWorker
{
// 孵化间的评分 - 比重很小设为10分实验室是60分
private const float ScorePerIncubator = 10f;
// 最低分数要求 - 需要至少1个孵化器才能成为孵化间
private const float MinimumScore = 10f;
public override float GetScore(Room room)
{
int incubatorCount = 0;
// 检查房间内和相邻的所有建筑
List<Thing> containedAndAdjacentThings = room.ContainedAndAdjacentThings;
for (int i = 0; i < containedAndAdjacentThings.Count; i++)
{
Thing thing = containedAndAdjacentThings[i];
// 检查建筑的工作台房间角色是否为ARA_Incubator_Room
if (thing.def.building?.workTableRoomRole != null &&
thing.def.building.workTableRoomRole.defName == "ARA_Incubator_Room")
{
incubatorCount++;
}
}
// 如果有孵化器,返回分数
float score = ScorePerIncubator * incubatorCount;
// 必须有至少一个孵化器才能成为孵化间
return incubatorCount > 0 ? score : 0f;
}
public override float GetScoreDeltaIfBuildingPlaced(Room room, ThingDef buildingDef)
{
// 如果放置的建筑具有ARA_Incubator_Room工作台房间角色返回分数增量
if (buildingDef.building?.workTableRoomRole != null &&
buildingDef.building.workTableRoomRole.defName == "ARA_Incubator_Room")
{
return ScorePerIncubator;
}
return 0f;
}
}
}