Merge branch 'master' of https://git.ra3battle.cn/Kalospacer/ArachnaeSwarm
This commit is contained in:
Binary file not shown.
43
1.6/1.6/Defs/AbilityDefs/AbilityDef_PsychicBrainburn.xml
Normal file
43
1.6/1.6/Defs/AbilityDefs/AbilityDef_PsychicBrainburn.xml
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<Defs>
|
||||||
|
|
||||||
|
<AbilityDef>
|
||||||
|
<defName>ARA_PsychicBrainburn</defName>
|
||||||
|
<label>心灵烧灼</label>
|
||||||
|
<description>通过一次强力的心灵冲击,直接摧毁目标生物的意识核心,使其永久失去知觉。</description>
|
||||||
|
<iconPath>UI/Abilities/Slaughter</iconPath>
|
||||||
|
<cooldownTicksRange>5000</cooldownTicksRange>
|
||||||
|
<aiCanUse>false</aiCanUse>
|
||||||
|
<displayOrder>300</displayOrder>
|
||||||
|
<displayGizmoWhileUndrafted>true</displayGizmoWhileUndrafted>
|
||||||
|
<disableGizmoWhileUndrafted>false</disableGizmoWhileUndrafted>
|
||||||
|
<showPsycastEffects>false</showPsycastEffects>
|
||||||
|
<sendMessageOnCooldownComplete>true</sendMessageOnCooldownComplete>
|
||||||
|
<stunTargetWhileCasting>true</stunTargetWhileCasting>
|
||||||
|
<moteOffsetAmountTowardsTarget>0.5</moteOffsetAmountTowardsTarget>
|
||||||
|
<warmupMote>Mote_HoraxSmallSpellWarmup</warmupMote>
|
||||||
|
<warmupEffecter>HoraxianAbilityCasting</warmupEffecter>
|
||||||
|
<warmupSound>AnomalyAbilityWarmup</warmupSound>
|
||||||
|
<writeCombatLog>true</writeCombatLog>
|
||||||
|
<verbProperties>
|
||||||
|
<verbClass>Verb_CastAbility</verbClass>
|
||||||
|
<warmupTime>1.5</warmupTime>
|
||||||
|
<range>25</range>
|
||||||
|
<targetParams>
|
||||||
|
<canTargetPawns>true</canTargetPawns>
|
||||||
|
<canTargetBuildings>false</canTargetBuildings>
|
||||||
|
<canTargetSelf>false</canTargetSelf>
|
||||||
|
</targetParams>
|
||||||
|
</verbProperties>
|
||||||
|
<comps>
|
||||||
|
<li Class="ArachnaeSwarm.CompProperties_PsychicBrainburn">
|
||||||
|
<!-- 视觉效果 -->
|
||||||
|
<effecterDef>Skip_Entry</effecterDef>
|
||||||
|
|
||||||
|
<!-- 设为 true 则只能对血肉生物使用。设为 false 则也可以对机械体使用。 -->
|
||||||
|
<requiresFlesh>false</requiresFlesh>
|
||||||
|
</li>
|
||||||
|
</comps>
|
||||||
|
</AbilityDef>
|
||||||
|
|
||||||
|
</Defs>
|
||||||
61
1.6/1.6/Defs/HediffDefs/ARA_HediffDef_TerrainExample.xml
Normal file
61
1.6/1.6/Defs/HediffDefs/ARA_HediffDef_TerrainExample.xml
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<Defs>
|
||||||
|
|
||||||
|
<HediffDef>
|
||||||
|
<defName>ARA_TerrainBasedHediff</defName>
|
||||||
|
<label>菌毯刺激</label>
|
||||||
|
<description>根据所处地形而强化自身。</description>
|
||||||
|
<hediffClass>HediffWithComps</hediffClass>
|
||||||
|
<maxSeverity>1.0</maxSeverity>
|
||||||
|
<minSeverity>-0.01</minSeverity>
|
||||||
|
<isBad>false</isBad>
|
||||||
|
<comps>
|
||||||
|
<li Class="ArachnaeSwarm.HediffCompProperties_TerrainBasedSeverity">
|
||||||
|
<!-- 每60 ticks(1秒)检查一次 -->
|
||||||
|
<interval>60</interval>
|
||||||
|
|
||||||
|
<!-- 目标地形列表 -->
|
||||||
|
<terrainDefs>
|
||||||
|
<!--<li>SterileTile</li> 无菌地砖 -->
|
||||||
|
<!-- 您可以在这里添加更多地形, 比如: -->
|
||||||
|
<li>ARA_InsectCreep</li>
|
||||||
|
</terrainDefs>
|
||||||
|
|
||||||
|
<!-- 站在目标地形上时,每次检查的严重性变化量 -->
|
||||||
|
<severityOnTerrain>0.0167</severityOnTerrain>
|
||||||
|
|
||||||
|
<!-- 不在目标地形上时,每次检查的严重性变化量 (负数表示减少) -->
|
||||||
|
<severityOffTerrain>-0.0083</severityOffTerrain>
|
||||||
|
</li>
|
||||||
|
<li Class="HediffCompProperties_RemoveIfApparelDropped" />
|
||||||
|
</comps>
|
||||||
|
<stages>
|
||||||
|
<li>
|
||||||
|
<becomeVisible>false</becomeVisible>
|
||||||
|
<minSeverity>0</minSeverity>
|
||||||
|
<label>无</label>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<minSeverity>0.4</minSeverity>
|
||||||
|
<label>适应</label>
|
||||||
|
<capMods>
|
||||||
|
<li>
|
||||||
|
<capacity>Consciousness</capacity>
|
||||||
|
<offset>0.1</offset>
|
||||||
|
</li>
|
||||||
|
</capMods>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<minSeverity>0.8</minSeverity>
|
||||||
|
<label>舒适</label>
|
||||||
|
<capMods>
|
||||||
|
<li>
|
||||||
|
<capacity>Consciousness</capacity>
|
||||||
|
<offset>0.25</offset>
|
||||||
|
</li>
|
||||||
|
</capMods>
|
||||||
|
</li>
|
||||||
|
</stages>
|
||||||
|
</HediffDef>
|
||||||
|
|
||||||
|
</Defs>
|
||||||
@@ -182,6 +182,11 @@
|
|||||||
<MoveSpeed>0.25</MoveSpeed>
|
<MoveSpeed>0.25</MoveSpeed>
|
||||||
</equippedStatOffsets>
|
</equippedStatOffsets>
|
||||||
<costStuffCount>0</costStuffCount>
|
<costStuffCount>0</costStuffCount>
|
||||||
|
<comps>
|
||||||
|
<li Class="CompProperties_CauseHediff_Apparel">
|
||||||
|
<hediff>ARA_TerrainBasedHediff</hediff>
|
||||||
|
</li>
|
||||||
|
</comps>
|
||||||
</ThingDef>
|
</ThingDef>
|
||||||
|
|
||||||
<ApparelLayerDef>
|
<ApparelLayerDef>
|
||||||
|
|||||||
@@ -9,6 +9,10 @@
|
|||||||
<graphicData>
|
<graphicData>
|
||||||
<texPath>Things/Building/Misc/ToolCabinet</texPath>
|
<texPath>Things/Building/Misc/ToolCabinet</texPath>
|
||||||
<graphicClass>Graphic_Multi</graphicClass>
|
<graphicClass>Graphic_Multi</graphicClass>
|
||||||
|
<shadowData>
|
||||||
|
<volume>(0.7, 0.4, 0.7)</volume>
|
||||||
|
<offset>(0,0,-0.1)</offset>
|
||||||
|
</shadowData>
|
||||||
</graphicData>
|
</graphicData>
|
||||||
<size>(1,1)</size>
|
<size>(1,1)</size>
|
||||||
<comps>
|
<comps>
|
||||||
@@ -32,6 +36,10 @@
|
|||||||
<graphicClass>Graphic_Single</graphicClass>
|
<graphicClass>Graphic_Single</graphicClass>
|
||||||
<shaderType>CutoutComplex</shaderType>
|
<shaderType>CutoutComplex</shaderType>
|
||||||
<drawSize>(5,6)</drawSize>
|
<drawSize>(5,6)</drawSize>
|
||||||
|
<shadowData>
|
||||||
|
<volume>(4.0, 0.5, 4.0)</volume>
|
||||||
|
<offset>(0,0,-0.1)</offset>
|
||||||
|
</shadowData>
|
||||||
</graphicData>
|
</graphicData>
|
||||||
<size>(5,5)</size>
|
<size>(5,5)</size>
|
||||||
<tickerType>Normal</tickerType>
|
<tickerType>Normal</tickerType>
|
||||||
@@ -149,12 +157,16 @@
|
|||||||
<label>生物质孵化池</label>
|
<label>生物质孵化池</label>
|
||||||
<description>一个大型的、需要消耗大量营养物质的孵化设施,可以同时孵化多个单位,并能通过链接外部设备来提高效率。</description>
|
<description>一个大型的、需要消耗大量营养物质的孵化设施,可以同时孵化多个单位,并能通过链接外部设备来提高效率。</description>
|
||||||
<graphicData>
|
<graphicData>
|
||||||
<texPath>Things/Building/AncientHeatVent</texPath>
|
<texPath>ArachnaeSwarm/Building/ARA_BioforgeIncubatorPawn</texPath>
|
||||||
<graphicClass>Graphic_Single</graphicClass>
|
<graphicClass>Graphic_Single</graphicClass>
|
||||||
<shaderType>CutoutComplex</shaderType>
|
<shaderType>CutoutComplex</shaderType>
|
||||||
<drawSize>(7,7)</drawSize>
|
<drawSize>(8,8)</drawSize>
|
||||||
|
<shadowData>
|
||||||
|
<volume>(6.0, 0.6, 4.0)</volume>
|
||||||
|
<offset>(0,0,-0.1)</offset>
|
||||||
|
</shadowData>
|
||||||
</graphicData>
|
</graphicData>
|
||||||
<size>(7,7)</size>
|
<size>(7,5)</size>
|
||||||
<tickerType>Normal</tickerType>
|
<tickerType>Normal</tickerType>
|
||||||
<stuffCategories Inherit="False" />
|
<stuffCategories Inherit="False" />
|
||||||
<costStuffCount>0</costStuffCount>
|
<costStuffCount>0</costStuffCount>
|
||||||
@@ -236,4 +248,99 @@
|
|||||||
|
|
||||||
</comps>
|
</comps>
|
||||||
</ThingDef>
|
</ThingDef>
|
||||||
|
|
||||||
|
<ThingDef ParentName="BuildingBase">
|
||||||
|
<defName>ARA_JellyVat</defName> <!-- defName is changed to reflect its purpose -->
|
||||||
|
<label>生物质酿造池</label>
|
||||||
|
<description>一个活体虫族器官,通过分别消化植物和肉类物质,来缓慢培育出营养丰富的阿拉克涅虫蜜。需要同时填充素食和肉类才能工作。</description>
|
||||||
|
<thingClass>Building</thingClass>
|
||||||
|
<graphicData>
|
||||||
|
<texPath>ArachnaeSwarm/Building/ARA_JellyVat</texPath>
|
||||||
|
<graphicClass>Graphic_Single</graphicClass>
|
||||||
|
<shaderType>CutoutComplex</shaderType>
|
||||||
|
<drawSize>(2.2,2.2)</drawSize>
|
||||||
|
<shadowData>
|
||||||
|
<volume>(1.6, 0.5, 1.6)</volume>
|
||||||
|
<offset>(0,0,-0.1)</offset>
|
||||||
|
</shadowData>
|
||||||
|
</graphicData>
|
||||||
|
<size>(2,2)</size>
|
||||||
|
<tickerType>Normal</tickerType>
|
||||||
|
<stuffCategories Inherit="False" />
|
||||||
|
<costStuffCount>0</costStuffCount>
|
||||||
|
<costList>
|
||||||
|
<ARA_Carapace>50</ARA_Carapace>
|
||||||
|
</costList>
|
||||||
|
<castEdgeShadows>false</castEdgeShadows>
|
||||||
|
<staticSunShadowHeight>0</staticSunShadowHeight>
|
||||||
|
<altitudeLayer>Building</altitudeLayer>
|
||||||
|
<passability>PassThroughOnly</passability>
|
||||||
|
<terrainAffordanceNeeded>ARA_Creep</terrainAffordanceNeeded>
|
||||||
|
<pathCost>50</pathCost>
|
||||||
|
<statBases>
|
||||||
|
<MaxHitPoints>250</MaxHitPoints>
|
||||||
|
<WorkToBuild>2800</WorkToBuild>
|
||||||
|
<Flammability>1.0</Flammability>
|
||||||
|
</statBases>
|
||||||
|
<placeWorkers>
|
||||||
|
<li>PlaceWorker_PreventInteractionSpotOverlap</li>
|
||||||
|
</placeWorkers>
|
||||||
|
<fillPercent>0.8</fillPercent>
|
||||||
|
<interactionCellOffset>(0,0,-1)</interactionCellOffset>
|
||||||
|
<hasInteractionCell>true</hasInteractionCell>
|
||||||
|
<designationCategory>ARA_Buildings</designationCategory>
|
||||||
|
<uiOrder>2600</uiOrder>
|
||||||
|
<surfaceType>Item</surfaceType>
|
||||||
|
<building>
|
||||||
|
<workTableRoomRole>Laboratory</workTableRoomRole>
|
||||||
|
<workTableNotInRoomRoleFactor>0.8</workTableNotInRoomRoleFactor>
|
||||||
|
</building>
|
||||||
|
<comps>
|
||||||
|
<li Class="CompProperties_Flickable"/>
|
||||||
|
|
||||||
|
<li Class="ArachnaeSwarm.CompProperties_MultiFuelSpawner">
|
||||||
|
<spawnIntervalRange>
|
||||||
|
<min>120000</min> <!-- 2天 -->
|
||||||
|
<max>120000</max>
|
||||||
|
</spawnIntervalRange>
|
||||||
|
<products>
|
||||||
|
<li>
|
||||||
|
<thingDef>ARA_InsectJelly</thingDef>
|
||||||
|
<count>150</count>
|
||||||
|
</li>
|
||||||
|
</products>
|
||||||
|
<showMessageIfOwned>true</showMessageIfOwned>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<!-- 燃料槽 1: 素食 -->
|
||||||
|
<li Class="ArachnaeSwarm.CompProperties_RefuelableNutrition_WithKey">
|
||||||
|
<saveKeysPrefix>veg_vat</saveKeysPrefix>
|
||||||
|
<fuelLabel>素食</fuelLabel>
|
||||||
|
<fuelFilter>
|
||||||
|
<categories>
|
||||||
|
<li>PlantFoodRaw</li>
|
||||||
|
</categories>
|
||||||
|
</fuelFilter>
|
||||||
|
<fuelCapacity>50</fuelCapacity>
|
||||||
|
<fuelConsumptionRate>12.5</fuelConsumptionRate>
|
||||||
|
<consumeFuelOnlyWhenUsed>true</consumeFuelOnlyWhenUsed>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<!-- 燃料槽 2: 肉类 -->
|
||||||
|
<li Class="ArachnaeSwarm.CompProperties_RefuelableNutrition_WithKey">
|
||||||
|
<saveKeysPrefix>meat_vat</saveKeysPrefix>
|
||||||
|
<fuelLabel>肉食</fuelLabel>
|
||||||
|
<fuelFilter>
|
||||||
|
<categories>
|
||||||
|
<li>MeatRaw</li>
|
||||||
|
<li>AnimalProductRaw</li>
|
||||||
|
</categories>
|
||||||
|
</fuelFilter>
|
||||||
|
<fuelCapacity>50</fuelCapacity>
|
||||||
|
<fuelConsumptionRate>12.5</fuelConsumptionRate>
|
||||||
|
<consumeFuelOnlyWhenUsed>true</consumeFuelOnlyWhenUsed>
|
||||||
|
</li>
|
||||||
|
</comps>
|
||||||
|
|
||||||
|
</ThingDef>
|
||||||
</Defs>
|
</Defs>
|
||||||
@@ -58,10 +58,15 @@
|
|||||||
<texPath>ArachnaeSwarm/Building/Linked/ARA_InsectWall</texPath>
|
<texPath>ArachnaeSwarm/Building/Linked/ARA_InsectWall</texPath>
|
||||||
<graphicClass>Graphic_Single</graphicClass>
|
<graphicClass>Graphic_Single</graphicClass>
|
||||||
<shaderType>CutoutComplex</shaderType>
|
<shaderType>CutoutComplex</shaderType>
|
||||||
|
<shadowData>
|
||||||
|
<volume>(0.7, 0.4, 0.2)</volume>
|
||||||
|
<offset>(0,0,-0.1)</offset>
|
||||||
|
</shadowData>
|
||||||
</graphicData>
|
</graphicData>
|
||||||
<designationCategory>ARA_Buildings</designationCategory>
|
<designationCategory>ARA_Buildings</designationCategory>
|
||||||
<!-- <mineable>true</mineable> -->
|
<!-- <mineable>true</mineable> -->
|
||||||
<blockLight>true</blockLight>
|
<blockLight>true</blockLight>
|
||||||
|
<staticSunShadowHeight>0</staticSunShadowHeight>
|
||||||
<statBases>
|
<statBases>
|
||||||
<MarketValue>0</MarketValue>
|
<MarketValue>0</MarketValue>
|
||||||
<Beauty>-6</Beauty>
|
<Beauty>-6</Beauty>
|
||||||
@@ -119,6 +124,10 @@
|
|||||||
<graphicData>
|
<graphicData>
|
||||||
<texPath>ArachnaeSwarm/Building/Door/ARA_InsectDoor</texPath>
|
<texPath>ArachnaeSwarm/Building/Door/ARA_InsectDoor</texPath>
|
||||||
<graphicClass>Graphic_Multi</graphicClass>
|
<graphicClass>Graphic_Multi</graphicClass>
|
||||||
|
<shadowData>
|
||||||
|
<volume>(0.7, 0.6, 0.7)</volume>
|
||||||
|
<offset>(0,0,-0.1)</offset>
|
||||||
|
</shadowData>
|
||||||
<damageData>
|
<damageData>
|
||||||
<!-- no damage marks because they don't move with the door
|
<!-- no damage marks because they don't move with the door
|
||||||
<rect>(0,0.12,1,0.76)</rect>-->
|
<rect>(0,0.12,1,0.76)</rect>-->
|
||||||
@@ -151,7 +160,7 @@
|
|||||||
<!-- <terrainAffordanceNeeded>ARA_Creep</terrainAffordanceNeeded> -->
|
<!-- <terrainAffordanceNeeded>ARA_Creep</terrainAffordanceNeeded> -->
|
||||||
<designationCategory>ARA_Buildings</designationCategory>
|
<designationCategory>ARA_Buildings</designationCategory>
|
||||||
<holdsRoof>true</holdsRoof>
|
<holdsRoof>true</holdsRoof>
|
||||||
<staticSunShadowHeight>1.0</staticSunShadowHeight>
|
<staticSunShadowHeight>0</staticSunShadowHeight>
|
||||||
<blockLight>true</blockLight>
|
<blockLight>true</blockLight>
|
||||||
<drawerType>RealtimeOnly</drawerType>
|
<drawerType>RealtimeOnly</drawerType>
|
||||||
<repairEffect>EatVegetarian</repairEffect>
|
<repairEffect>EatVegetarian</repairEffect>
|
||||||
@@ -342,6 +351,10 @@
|
|||||||
<graphicClass>Graphic_Multi</graphicClass>
|
<graphicClass>Graphic_Multi</graphicClass>
|
||||||
<shaderType>CutoutComplex</shaderType>
|
<shaderType>CutoutComplex</shaderType>
|
||||||
<drawSize>(3,4.5)</drawSize>
|
<drawSize>(3,4.5)</drawSize>
|
||||||
|
<shadowData>
|
||||||
|
<volume>(2.5, 0.5, 2.5)</volume>
|
||||||
|
<offset>(0,0,-0.1)</offset>
|
||||||
|
</shadowData>
|
||||||
</graphicData>
|
</graphicData>
|
||||||
<castEdgeShadows>false</castEdgeShadows>
|
<castEdgeShadows>false</castEdgeShadows>
|
||||||
<staticSunShadowHeight>0</staticSunShadowHeight>
|
<staticSunShadowHeight>0</staticSunShadowHeight>
|
||||||
@@ -388,6 +401,10 @@
|
|||||||
<graphicClass>Graphic_Multi</graphicClass>
|
<graphicClass>Graphic_Multi</graphicClass>
|
||||||
<shaderType>CutoutComplex</shaderType>
|
<shaderType>CutoutComplex</shaderType>
|
||||||
<drawSize>(2,2)</drawSize>
|
<drawSize>(2,2)</drawSize>
|
||||||
|
<shadowData>
|
||||||
|
<volume>(0.8, 0.4, 1.7)</volume>
|
||||||
|
<offset>(0,0,-0.1)</offset>
|
||||||
|
</shadowData>
|
||||||
</graphicData>
|
</graphicData>
|
||||||
<staticSunShadowHeight Inherit="False" IsNull="True" />
|
<staticSunShadowHeight Inherit="False" IsNull="True" />
|
||||||
<castEdgeShadows>False</castEdgeShadows>
|
<castEdgeShadows>False</castEdgeShadows>
|
||||||
|
|||||||
@@ -174,7 +174,7 @@
|
|||||||
<comps>
|
<comps>
|
||||||
<li Class="CompProperties_Glower">
|
<li Class="CompProperties_Glower">
|
||||||
<glowRadius>6</glowRadius>
|
<glowRadius>6</glowRadius>
|
||||||
<color>(0.9, 0.9 ,0.5, 0)</color>
|
<glowColor>(230, 230, 128, 0)</glowColor>
|
||||||
</li>
|
</li>
|
||||||
<li Class="ArachnaeSwarm.CompProperties_SpawnPawnFromList">
|
<li Class="ArachnaeSwarm.CompProperties_SpawnPawnFromList">
|
||||||
<spawnablePawns>
|
<spawnablePawns>
|
||||||
@@ -288,7 +288,7 @@
|
|||||||
<comps>
|
<comps>
|
||||||
<li Class="CompProperties_Glower">
|
<li Class="CompProperties_Glower">
|
||||||
<glowRadius>6</glowRadius>
|
<glowRadius>6</glowRadius>
|
||||||
<color>(0.9, 0.9 ,0.5, 0)</color>
|
<glowColor>(230, 230, 128, 0)</glowColor>
|
||||||
</li>
|
</li>
|
||||||
<li Class="ArachnaeSwarm.CompProperties_SpawnPawnFromList">
|
<li Class="ArachnaeSwarm.CompProperties_SpawnPawnFromList">
|
||||||
<spawnablePawns>
|
<spawnablePawns>
|
||||||
|
|||||||
@@ -6,6 +6,10 @@
|
|||||||
<texPath>ArachnaeSwarm/Building/ARA_Cocoon</texPath>
|
<texPath>ArachnaeSwarm/Building/ARA_Cocoon</texPath>
|
||||||
<graphicClass>Graphic_Single</graphicClass>
|
<graphicClass>Graphic_Single</graphicClass>
|
||||||
<drawSize>(1.1,1.1)</drawSize>
|
<drawSize>(1.1,1.1)</drawSize>
|
||||||
|
<shadowData>
|
||||||
|
<volume>(0.7, 0.4, 0.7)</volume>
|
||||||
|
<offset>(0,0,-0.1)</offset>
|
||||||
|
</shadowData>
|
||||||
</graphicData>
|
</graphicData>
|
||||||
<size>(1,1)</size>
|
<size>(1,1)</size>
|
||||||
<altitudeLayer>Building</altitudeLayer>
|
<altitudeLayer>Building</altitudeLayer>
|
||||||
@@ -261,4 +265,5 @@
|
|||||||
</li>
|
</li>
|
||||||
</comps>
|
</comps>
|
||||||
</ThingDef>
|
</ThingDef>
|
||||||
|
|
||||||
</Defs>
|
</Defs>
|
||||||
146
1.6/1.6/Defs/Thing_building/Building_SmartThermostat.xml
Normal file
146
1.6/1.6/Defs/Thing_building/Building_SmartThermostat.xml
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<Defs>
|
||||||
|
|
||||||
|
<ThingDef ParentName="BuildingBase">
|
||||||
|
<defName>ARA_SmartThermostat</defName>
|
||||||
|
<label>阿拉克涅纤管虫</label>
|
||||||
|
<description>一个不耗电的温控虫虫。它是一个可逆的热泵,能自动制热或制冷以维持设定的目标温度。必须像制冷器一样安装在墙上。</description>
|
||||||
|
<thingClass>ArachnaeSwarm.Building_SmartThermostat</thingClass> <!-- 使用我们新的建筑类 -->
|
||||||
|
<graphicData>
|
||||||
|
<texPath>Things/Building/Misc/TempControl/Cooler</texPath> <!-- 暂时使用制冷器的贴图 -->
|
||||||
|
<graphicClass>Graphic_Multi</graphicClass>
|
||||||
|
<shadowData>
|
||||||
|
<volume>(0.6, 0.4, 0.3)</volume>
|
||||||
|
<offset>(0,0,-0.1)</offset>
|
||||||
|
</shadowData>
|
||||||
|
</graphicData>
|
||||||
|
<altitudeLayer>Building</altitudeLayer>
|
||||||
|
<passability>Impassable</passability>
|
||||||
|
<blockWind>true</blockWind>
|
||||||
|
<fillPercent>1</fillPercent>
|
||||||
|
<coversFloor>true</coversFloor>
|
||||||
|
<blockLight>true</blockLight>
|
||||||
|
<blockWeather>true</blockWeather>
|
||||||
|
<castEdgeShadows>true</castEdgeShadows>
|
||||||
|
<canOverlapZones>false</canOverlapZones>
|
||||||
|
<staticSunShadowHeight>0</staticSunShadowHeight>
|
||||||
|
<statBases>
|
||||||
|
<WorkToBuild>400</WorkToBuild>
|
||||||
|
<MaxHitPoints>100</MaxHitPoints>
|
||||||
|
<Flammability>1.0</Flammability>
|
||||||
|
</statBases>
|
||||||
|
<tickerType>Rare</tickerType>
|
||||||
|
<costList>
|
||||||
|
<ARA_Carapace>30</ARA_Carapace>
|
||||||
|
</costList>
|
||||||
|
<terrainAffordanceNeeded>Medium</terrainAffordanceNeeded>
|
||||||
|
<placeWorkers>
|
||||||
|
<li>PlaceWorker_Vent</li>
|
||||||
|
</placeWorkers>
|
||||||
|
<drawPlaceWorkersWhileSelected>true</drawPlaceWorkersWhileSelected>
|
||||||
|
<building>
|
||||||
|
<canPlaceOverWall>true</canPlaceOverWall>
|
||||||
|
<canExchangeVacuum>true</canExchangeVacuum>
|
||||||
|
<isAirtight>true</isAirtight>
|
||||||
|
</building>
|
||||||
|
<researchPrerequisites>
|
||||||
|
<li>AirConditioning</li>
|
||||||
|
</researchPrerequisites>
|
||||||
|
<designationCategory>ARA_Buildings</designationCategory>
|
||||||
|
<comps>
|
||||||
|
<!-- 提供开关按钮 -->
|
||||||
|
<li Class="CompProperties_Flickable">
|
||||||
|
<commandTexture>UI/Commands/Vent</commandTexture>
|
||||||
|
<commandLabelKey>CommandDesignateOpenCloseVentLabel</commandLabelKey>
|
||||||
|
<commandDescKey>CommandDesignateOpenCloseVentDesc</commandDescKey>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<!-- 提供温度控制UI和逻辑 -->
|
||||||
|
<li Class="ArachnaeSwarm.CompProperties_TempControl_Fixed">
|
||||||
|
<!-- 这是设备的热交换功率。数值越大,制冷/制热速度越快。-->
|
||||||
|
<energyPerSecond>34</energyPerSecond>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li Class="CompProperties_Breakdownable"/>
|
||||||
|
</comps>
|
||||||
|
</ThingDef>
|
||||||
|
|
||||||
|
<ThingDef ParentName="BuildingBase">
|
||||||
|
<defName>ARA_GrowthVat</defName>
|
||||||
|
<label>阿拉克涅捕获茧</label>
|
||||||
|
<description>用来存放猎物的茧。</description>
|
||||||
|
<thingClass>ArachnaeSwarm.Building_NutrientVat</thingClass>
|
||||||
|
<containedPawnsSelectable>true</containedPawnsSelectable>
|
||||||
|
<tickerType>Normal</tickerType>
|
||||||
|
<graphicData>
|
||||||
|
<texPath>ArachnaeSwarm/Building/ARA_GrowthVat</texPath>
|
||||||
|
<graphicClass>Graphic_Single</graphicClass>
|
||||||
|
<shaderType>CutoutComplex</shaderType>
|
||||||
|
<drawSize>(2.5,2.5)</drawSize>
|
||||||
|
<shadowData>
|
||||||
|
<volume>(0.85, 0.3, 1.7)</volume>
|
||||||
|
</shadowData>
|
||||||
|
</graphicData>
|
||||||
|
<castEdgeShadows>true</castEdgeShadows>
|
||||||
|
<defaultPlacingRot>North</defaultPlacingRot>
|
||||||
|
<size>(1,2)</size>
|
||||||
|
<statBases>
|
||||||
|
<MaxHitPoints>500</MaxHitPoints>
|
||||||
|
<WorkToBuild>8000</WorkToBuild>
|
||||||
|
<Mass>30</Mass>
|
||||||
|
<Flammability>0.5</Flammability>
|
||||||
|
</statBases>
|
||||||
|
<costList>
|
||||||
|
<Steel>150</Steel>
|
||||||
|
<ComponentIndustrial>4</ComponentIndustrial>
|
||||||
|
</costList>
|
||||||
|
<altitudeLayer>Building</altitudeLayer>
|
||||||
|
<passability>PassThroughOnly</passability>
|
||||||
|
<pathCost>42</pathCost>
|
||||||
|
<blockWind>true</blockWind>
|
||||||
|
<drawerType>MapMeshAndRealTime</drawerType>
|
||||||
|
<fillPercent>0.5</fillPercent>
|
||||||
|
<canOverlapZones>false</canOverlapZones>
|
||||||
|
<designationCategory>ARA_Buildings</designationCategory>
|
||||||
|
<uiOrder>2200</uiOrder>
|
||||||
|
<hasInteractionCell>true</hasInteractionCell>
|
||||||
|
<interactionCellOffset>(0,0,-1)</interactionCellOffset>
|
||||||
|
<rotatable>false</rotatable>
|
||||||
|
<inspectorTabs>
|
||||||
|
<li>ITab_BiosculpterNutritionStorage</li>
|
||||||
|
<li>ITab_Genes</li>
|
||||||
|
</inspectorTabs>
|
||||||
|
<researchPrerequisites>
|
||||||
|
<li>GrowthVats</li>
|
||||||
|
</researchPrerequisites>
|
||||||
|
<building>
|
||||||
|
<ai_chillDestination>false</ai_chillDestination>
|
||||||
|
<haulToContainerDuration>120</haulToContainerDuration>
|
||||||
|
<workTableRoomRole>Laboratory</workTableRoomRole>
|
||||||
|
</building>
|
||||||
|
<constructionSkillPrerequisite>4</constructionSkillPrerequisite>
|
||||||
|
<!-- ... 其他建筑属性 ... -->
|
||||||
|
<comps>
|
||||||
|
<li Class="ArachnaeSwarm.CompProperties_RefuelableNutrition">
|
||||||
|
<fuelCapacity>100.0</fuelCapacity>
|
||||||
|
<fuelFilter>
|
||||||
|
<categories>
|
||||||
|
<li>Foods</li>
|
||||||
|
</categories>
|
||||||
|
</fuelFilter>
|
||||||
|
<fuelGizmoLabel>生物质</fuelGizmoLabel>
|
||||||
|
<showAllowAutoRefuelToggle>true</showAllowAutoRefuelToggle>
|
||||||
|
<targetFuelLevelConfigurable>true</targetFuelLevelConfigurable>
|
||||||
|
</li>
|
||||||
|
</comps>
|
||||||
|
<modExtensions>
|
||||||
|
<li Class="ArachnaeSwarm.DefModExtension_NutrientVat">
|
||||||
|
<!-- 在这里配置您的顶部贴图 -->
|
||||||
|
<topGraphicPath>ArachnaeSwarm/Building/ARA_GrowthVatTop</topGraphicPath>
|
||||||
|
<!-- 如果是单张贴图,使用 Graphic_Single -->
|
||||||
|
<graphicClass>Graphic_Single</graphicClass>
|
||||||
|
</li>
|
||||||
|
</modExtensions>
|
||||||
|
</ThingDef>
|
||||||
|
|
||||||
|
</Defs>
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 214 KiB |
BIN
Content/Textures/ArachnaeSwarm/Building/ARA_GrowthVat.png
Normal file
BIN
Content/Textures/ArachnaeSwarm/Building/ARA_GrowthVat.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 167 KiB |
BIN
Content/Textures/ArachnaeSwarm/Building/ARA_GrowthVatTop.png
Normal file
BIN
Content/Textures/ArachnaeSwarm/Building/ARA_GrowthVatTop.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 171 KiB |
BIN
Content/Textures/ArachnaeSwarm/Building/ARA_JellyVat.png
Normal file
BIN
Content/Textures/ArachnaeSwarm/Building/ARA_JellyVat.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 185 KiB |
@@ -17,7 +17,7 @@ namespace ArachnaeSwarm
|
|||||||
}
|
}
|
||||||
|
|
||||||
[StaticConstructorOnStartup]
|
[StaticConstructorOnStartup]
|
||||||
public class CompRefuelableNutrition : CompRefuelable
|
public class CompRefuelableNutrition : CompRefuelable, IFuelSource
|
||||||
{
|
{
|
||||||
public float currentConsumptionRate = 0f;
|
public float currentConsumptionRate = 0f;
|
||||||
public float NutritionStored => Fuel;
|
public float NutritionStored => Fuel;
|
||||||
@@ -90,5 +90,13 @@ namespace ArachnaeSwarm
|
|||||||
|
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public new void Notify_UsedThisTick()
|
||||||
|
{
|
||||||
|
if (Props.consumeFuelOnlyWhenUsed)
|
||||||
|
{
|
||||||
|
ConsumeFuel(Props.fuelConsumptionRate / 60000f);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -133,6 +133,8 @@
|
|||||||
<Compile Include="ARA_CompInteractiveProducer\CompInteractiveProducer.cs" />
|
<Compile Include="ARA_CompInteractiveProducer\CompInteractiveProducer.cs" />
|
||||||
<Compile Include="ARA_CompInteractiveProducer\JobDriver_StartProduction.cs" />
|
<Compile Include="ARA_CompInteractiveProducer\JobDriver_StartProduction.cs" />
|
||||||
<Compile Include="ARA_CompInteractiveProducer\CompRefuelableNutrition.cs" />
|
<Compile Include="ARA_CompInteractiveProducer\CompRefuelableNutrition.cs" />
|
||||||
|
<Compile Include="Building_NutrientVat.cs" />
|
||||||
|
<Compile Include="DefModExtension_NutrientVat.cs" />
|
||||||
<Compile Include="ARA_CompInteractiveProducer\DataContracts.cs" />
|
<Compile Include="ARA_CompInteractiveProducer\DataContracts.cs" />
|
||||||
<Compile Include="ARA_CompInteractiveProducer\CompTemperatureRuinableDamage.cs" />
|
<Compile Include="ARA_CompInteractiveProducer\CompTemperatureRuinableDamage.cs" />
|
||||||
<Compile Include="ARA_CompInteractiveProducer\CompQueuedInteractiveProducer.cs" />
|
<Compile Include="ARA_CompInteractiveProducer\CompQueuedInteractiveProducer.cs" />
|
||||||
@@ -205,6 +207,27 @@
|
|||||||
<Compile Include="Morphable\CompAbilityEffect_Transform.cs" />
|
<Compile Include="Morphable\CompAbilityEffect_Transform.cs" />
|
||||||
<Compile Include="Morphable\Building_Morphable.cs" />
|
<Compile Include="Morphable\Building_Morphable.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="HediffCompProperties_TerrainBasedSeverity.cs" />
|
||||||
|
<Compile Include="HediffComp_TerrainBasedSeverity.cs" />
|
||||||
|
<Compile Include="CompAbilityEffect_PsychicBrainburn.cs" />
|
||||||
|
<Compile Include="CompProperties_PsychicBrainburn.cs" />
|
||||||
|
<Compile Include="Building_SmartThermostat.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="WULA_HediffDamgeShield\DRMDamageShield.cs" />
|
||||||
|
<Compile Include="WULA_HediffDamgeShield\Hediff_DamageShield.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="WULA_MutiFuelSpawner\IFuelSource.cs" />
|
||||||
|
<Compile Include="WULA_MutiFuelSpawner\CompMultiFuelSpawner.cs" />
|
||||||
|
<Compile Include="WULA_MutiFuelSpawner\CompRefuelableWithKey.cs" />
|
||||||
|
<Compile Include="WULA_MutiFuelSpawner\Patch_CompRefuelableWithKey.cs" />
|
||||||
|
<Compile Include="WULA_MutiFuelSpawner\CompRefuelableNutrition_WithKey.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="Utils\CompTempControl_Fixed.cs" />
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<!-- 自定义清理任务,删除obj文件夹中的临时文件 -->
|
<!-- 自定义清理任务,删除obj文件夹中的临时文件 -->
|
||||||
<Target Name="CleanDebugFiles" AfterTargets="Build">
|
<Target Name="CleanDebugFiles" AfterTargets="Build">
|
||||||
|
|||||||
369
Source/ArachnaeSwarm/Building_NutrientVat.cs
Normal file
369
Source/ArachnaeSwarm/Building_NutrientVat.cs
Normal file
@@ -0,0 +1,369 @@
|
|||||||
|
using RimWorld;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using UnityEngine;
|
||||||
|
using Verse;
|
||||||
|
using Verse.AI;
|
||||||
|
|
||||||
|
namespace ArachnaeSwarm
|
||||||
|
{
|
||||||
|
[StaticConstructorOnStartup]
|
||||||
|
public class Building_NutrientVat : Building_Enterable, IThingHolder, IThingHolderWithDrawnPawn
|
||||||
|
{
|
||||||
|
private CompRefuelableNutrition cachedRefuelableComp;
|
||||||
|
private Graphic cachedTopGraphic;
|
||||||
|
|
||||||
|
// IThingHolderWithDrawnPawn implementation
|
||||||
|
public float HeldPawnDrawPos_Y => DrawPos.y + 0.03658537f;
|
||||||
|
public float HeldPawnBodyAngle => base.Rotation.AsAngle;
|
||||||
|
public PawnPosture HeldPawnPosture => PawnPosture.LayingOnGroundFaceUp;
|
||||||
|
|
||||||
|
private Graphic TopGraphic
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (cachedTopGraphic == null)
|
||||||
|
{
|
||||||
|
var modExtension = def.GetModExtension<DefModExtension_NutrientVat>();
|
||||||
|
if (modExtension != null && !modExtension.topGraphicPath.NullOrEmpty())
|
||||||
|
{
|
||||||
|
cachedTopGraphic = GraphicDatabase.Get(modExtension.graphicClass, modExtension.topGraphicPath, ShaderDatabase.Transparent, def.graphicData.drawSize, Color.white, Color.white);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cachedTopGraphic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constants for BioStarvation
|
||||||
|
private const float BiostarvationGainPerDayNoFood = 0.5f;
|
||||||
|
private const float BiostarvationFallPerDayFed = 0.1f;
|
||||||
|
|
||||||
|
public override Vector3 PawnDrawOffset => Vector3.zero;
|
||||||
|
|
||||||
|
public CompRefuelableNutrition RefuelableComp
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (cachedRefuelableComp == null)
|
||||||
|
{
|
||||||
|
cachedRefuelableComp = this.TryGetComp<CompRefuelableNutrition>();
|
||||||
|
}
|
||||||
|
return cachedRefuelableComp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasNutrition => RefuelableComp != null && RefuelableComp.HasFuel;
|
||||||
|
|
||||||
|
public float BiostarvationDailyOffset
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!base.Working)
|
||||||
|
{
|
||||||
|
return 0f;
|
||||||
|
}
|
||||||
|
return HasNutrition ? -BiostarvationFallPerDayFed : BiostarvationGainPerDayNoFood;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public float NutritionConsumedPerDay
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (selectedPawn != null)
|
||||||
|
{
|
||||||
|
// Let's use the base consumption rate from the original GrowthVat
|
||||||
|
float num = 3f;
|
||||||
|
Hediff biostarvation = selectedPawn.health.hediffSet.GetFirstHediffOfDef(HediffDefOf.BioStarvation);
|
||||||
|
if (biostarvation != null && biostarvation.Severity > 0)
|
||||||
|
{
|
||||||
|
// Increase consumption when biostarving, same as original
|
||||||
|
num *= 1.1f;
|
||||||
|
}
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
return 0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SpawnSetup(Map map, bool respawningAfterLoad)
|
||||||
|
{
|
||||||
|
base.SpawnSetup(map, respawningAfterLoad);
|
||||||
|
cachedRefuelableComp = this.TryGetComp<CompRefuelableNutrition>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void DeSpawn(DestroyMode mode = DestroyMode.Vanish)
|
||||||
|
{
|
||||||
|
if (mode != DestroyMode.WillReplace)
|
||||||
|
{
|
||||||
|
if (selectedPawn != null && innerContainer.Contains(selectedPawn))
|
||||||
|
{
|
||||||
|
Notify_PawnRemoved();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
base.DeSpawn(mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Tick()
|
||||||
|
{
|
||||||
|
base.Tick();
|
||||||
|
|
||||||
|
if (selectedPawn != null && (selectedPawn.Destroyed || !innerContainer.Contains(selectedPawn)))
|
||||||
|
{
|
||||||
|
OnStop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (base.Working && selectedPawn != null)
|
||||||
|
{
|
||||||
|
// Update BioStarvation
|
||||||
|
float biostarvationOffset = BiostarvationDailyOffset / 60000f * HediffDefOf.BioStarvation.maxSeverity;
|
||||||
|
Hediff biostarvation = selectedPawn.health.hediffSet.GetFirstHediffOfDef(HediffDefOf.BioStarvation);
|
||||||
|
|
||||||
|
if (biostarvation != null)
|
||||||
|
{
|
||||||
|
biostarvation.Severity += biostarvationOffset;
|
||||||
|
if (biostarvation.ShouldRemove)
|
||||||
|
{
|
||||||
|
selectedPawn.health.RemoveHediff(biostarvation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (biostarvationOffset > 0f)
|
||||||
|
{
|
||||||
|
Hediff hediff = HediffMaker.MakeHediff(HediffDefOf.BioStarvation, selectedPawn);
|
||||||
|
hediff.Severity = biostarvationOffset;
|
||||||
|
selectedPawn.health.AddHediff(hediff);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for failure
|
||||||
|
if (biostarvation != null && biostarvation.Severity >= HediffDefOf.BioStarvation.maxSeverity)
|
||||||
|
{
|
||||||
|
Fail();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update nutrition consumption rate on the component
|
||||||
|
RefuelableComp.currentConsumptionRate = NutritionConsumedPerDay;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If not working, consumption is zero
|
||||||
|
if(RefuelableComp != null)
|
||||||
|
{
|
||||||
|
RefuelableComp.currentConsumptionRate = 0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override AcceptanceReport CanAcceptPawn(Pawn pawn)
|
||||||
|
{
|
||||||
|
if (base.Working)
|
||||||
|
{
|
||||||
|
return "Occupied".Translate();
|
||||||
|
}
|
||||||
|
if (selectedPawn != null && selectedPawn != pawn)
|
||||||
|
{
|
||||||
|
return "WaitingForPawn".Translate(selectedPawn.Named("PAWN"));
|
||||||
|
}
|
||||||
|
if (pawn.health.hediffSet.HasHediff(HediffDefOf.BioStarvation))
|
||||||
|
{
|
||||||
|
return "PawnBiostarving".Translate(pawn.Named("PAWN"));
|
||||||
|
}
|
||||||
|
return pawn.IsColonist && !pawn.IsQuestLodger();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void TryAcceptPawn(Pawn pawn)
|
||||||
|
{
|
||||||
|
if (!CanAcceptPawn(pawn))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedPawn = pawn;
|
||||||
|
bool deselected = pawn.DeSpawnOrDeselect();
|
||||||
|
if (innerContainer.TryAddOrTransfer(pawn))
|
||||||
|
{
|
||||||
|
startTick = Find.TickManager.TicksGame;
|
||||||
|
}
|
||||||
|
if (deselected)
|
||||||
|
{
|
||||||
|
Find.Selector.Select(pawn, playSound: false, forceDesignatorDeselect: false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Finish()
|
||||||
|
{
|
||||||
|
if (selectedPawn != null && innerContainer.Contains(selectedPawn))
|
||||||
|
{
|
||||||
|
Notify_PawnRemoved();
|
||||||
|
innerContainer.TryDrop(selectedPawn, InteractionCell, base.Map, ThingPlaceMode.Near, 1, out var _);
|
||||||
|
OnStop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Fail()
|
||||||
|
{
|
||||||
|
if (selectedPawn != null && innerContainer.Contains(selectedPawn))
|
||||||
|
{
|
||||||
|
Notify_PawnRemoved();
|
||||||
|
innerContainer.TryDrop(selectedPawn, InteractionCell, base.Map, ThingPlaceMode.Near, 1, out var _);
|
||||||
|
Hediff firstHediffOfDef = selectedPawn.health.hediffSet.GetFirstHediffOfDef(HediffDefOf.BioStarvation);
|
||||||
|
selectedPawn.Kill(null, firstHediffOfDef);
|
||||||
|
}
|
||||||
|
OnStop();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnStop()
|
||||||
|
{
|
||||||
|
selectedPawn = null;
|
||||||
|
startTick = -1;
|
||||||
|
if (RefuelableComp != null)
|
||||||
|
{
|
||||||
|
RefuelableComp.currentConsumptionRate = 0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Notify_PawnRemoved()
|
||||||
|
{
|
||||||
|
// You can add sound effects here if you want, e.g., SoundDefOf.GrowthVat_Open.PlayOneShot(SoundInfo.InMap(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IEnumerable<Gizmo> GetGizmos()
|
||||||
|
{
|
||||||
|
// Keep base gizmos
|
||||||
|
foreach (Gizmo gizmo in base.GetGizmos())
|
||||||
|
{
|
||||||
|
yield return gizmo;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (base.Working)
|
||||||
|
{
|
||||||
|
yield return new Command_Action
|
||||||
|
{
|
||||||
|
defaultLabel = "CommandCancelGrowth".Translate(), // Label can be changed
|
||||||
|
defaultDesc = "CommandCancelGrowthDesc".Translate(), // Desc can be changed
|
||||||
|
icon = ContentFinder<Texture2D>.Get("UI/Designators/Cancel"),
|
||||||
|
action = () =>
|
||||||
|
{
|
||||||
|
Finish();
|
||||||
|
innerContainer.TryDropAll(InteractionCell, base.Map, ThingPlaceMode.Near);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (selectedPawn != null)
|
||||||
|
{
|
||||||
|
yield return new Command_Action
|
||||||
|
{
|
||||||
|
defaultLabel = "CommandCancelLoad".Translate(),
|
||||||
|
defaultDesc = "CommandCancelLoadDesc".Translate(),
|
||||||
|
icon = ContentFinder<Texture2D>.Get("UI/Designators/Cancel"),
|
||||||
|
action = () =>
|
||||||
|
{
|
||||||
|
if (selectedPawn?.CurJobDef == JobDefOf.EnterBuilding)
|
||||||
|
{
|
||||||
|
selectedPawn.jobs.EndCurrentJob(JobCondition.InterruptForced);
|
||||||
|
}
|
||||||
|
OnStop();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var command_Action = new Command_Action
|
||||||
|
{
|
||||||
|
defaultLabel = "InsertPerson".Translate() + "...",
|
||||||
|
defaultDesc = "InsertPersonGrowthVatDesc".Translate(), // Desc can be changed
|
||||||
|
icon = Building_GrowthVat.InsertPawnIcon.Texture,
|
||||||
|
action = () =>
|
||||||
|
{
|
||||||
|
List<FloatMenuOption> list = new List<FloatMenuOption>();
|
||||||
|
foreach (Pawn p in base.Map.mapPawns.AllPawnsSpawned)
|
||||||
|
{
|
||||||
|
if ((bool)CanAcceptPawn(p))
|
||||||
|
{
|
||||||
|
list.Add(new FloatMenuOption(p.LabelCap, () => SelectPawn(p), p, Color.white));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!list.Any())
|
||||||
|
{
|
||||||
|
list.Add(new FloatMenuOption("NoViablePawns".Translate(), null));
|
||||||
|
}
|
||||||
|
Find.WindowStack.Add(new FloatMenu(list));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (!base.AnyAcceptablePawns)
|
||||||
|
{
|
||||||
|
command_Action.Disable("NoViablePawns".Translate());
|
||||||
|
}
|
||||||
|
yield return command_Action;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string GetInspectString()
|
||||||
|
{
|
||||||
|
StringBuilder stringBuilder = new StringBuilder();
|
||||||
|
stringBuilder.Append(base.GetInspectString());
|
||||||
|
|
||||||
|
if (base.Working && selectedPawn != null)
|
||||||
|
{
|
||||||
|
stringBuilder.AppendLineIfNotEmpty().Append("CasketContains".Translate().ToString() + ": " + selectedPawn.NameShortColored.Resolve());
|
||||||
|
|
||||||
|
Hediff biostarvation = selectedPawn.health.hediffSet.GetFirstHediffOfDef(HediffDefOf.BioStarvation);
|
||||||
|
if (biostarvation != null && biostarvation.Severity > 0f)
|
||||||
|
{
|
||||||
|
string text = ((BiostarvationDailyOffset >= 0f) ? "+" : string.Empty);
|
||||||
|
stringBuilder.AppendLineIfNotEmpty().Append(string.Format("{0}: {1} ({2})", "Biostarvation".Translate(), biostarvation.Severity.ToStringPercent(), "PerDay".Translate(text + BiostarvationDailyOffset.ToStringPercent())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (selectedPawn != null)
|
||||||
|
{
|
||||||
|
stringBuilder.AppendLineIfNotEmpty().Append("WaitingForPawn".Translate(selectedPawn.Named("PAWN")).Resolve());
|
||||||
|
}
|
||||||
|
|
||||||
|
// The inspect string from CompRefuelableNutrition will be automatically added by the game.
|
||||||
|
return stringBuilder.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IEnumerable<FloatMenuOption> GetFloatMenuOptions(Pawn selPawn)
|
||||||
|
{
|
||||||
|
foreach (FloatMenuOption floatMenuOption in base.GetFloatMenuOptions(selPawn))
|
||||||
|
{
|
||||||
|
yield return floatMenuOption;
|
||||||
|
}
|
||||||
|
if (!selPawn.CanReach(this, PathEndMode.InteractionCell, Danger.Deadly))
|
||||||
|
{
|
||||||
|
yield return new FloatMenuOption("CannotEnterBuilding".Translate(this) + ": " + "NoPath".Translate().CapitalizeFirst(), null);
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
AcceptanceReport acceptanceReport = CanAcceptPawn(selPawn);
|
||||||
|
if (acceptanceReport.Accepted)
|
||||||
|
{
|
||||||
|
yield return FloatMenuUtility.DecoratePrioritizedTask(new FloatMenuOption("EnterBuilding".Translate(this), () => SelectPawn(selPawn)), selPawn, this);
|
||||||
|
}
|
||||||
|
else if (!acceptanceReport.Reason.NullOrEmpty())
|
||||||
|
{
|
||||||
|
yield return new FloatMenuOption("CannotEnterBuilding".Translate(this) + ": " + acceptanceReport.Reason.CapitalizeFirst(), null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void DynamicDrawPhaseAt(DrawPhase phase, Vector3 drawLoc, bool flip = false)
|
||||||
|
{
|
||||||
|
if (base.Working && selectedPawn != null && innerContainer.Contains(selectedPawn))
|
||||||
|
{
|
||||||
|
selectedPawn.Drawer.renderer.DynamicDrawPhaseAt(phase, drawLoc + PawnDrawOffset, null, neverAimWeapon: true);
|
||||||
|
}
|
||||||
|
base.DynamicDrawPhaseAt(phase, drawLoc, flip);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void DrawAt(Vector3 drawLoc, bool flip = false)
|
||||||
|
{
|
||||||
|
base.DrawAt(drawLoc, flip);
|
||||||
|
// Draw the top graphic if it exists
|
||||||
|
if (TopGraphic != null)
|
||||||
|
{
|
||||||
|
TopGraphic.Draw(DrawPos + Altitudes.AltIncVect * 2f, base.Rotation, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
81
Source/ArachnaeSwarm/Building_SmartThermostat.cs
Normal file
81
Source/ArachnaeSwarm/Building_SmartThermostat.cs
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using Verse;
|
||||||
|
using RimWorld;
|
||||||
|
|
||||||
|
namespace ArachnaeSwarm
|
||||||
|
{
|
||||||
|
public class Building_SmartThermostat : Building_TempControl
|
||||||
|
{
|
||||||
|
private CompFlickable flickableComp;
|
||||||
|
private const float HeatOutputMultiplier = 1.25f;
|
||||||
|
private const float EfficiencyLossPerDegreeDifference = 1f / 130f;
|
||||||
|
|
||||||
|
public override void SpawnSetup(Map map, bool respawningAfterLoad)
|
||||||
|
{
|
||||||
|
base.SpawnSetup(map, respawningAfterLoad);
|
||||||
|
flickableComp = GetComp<CompFlickable>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void TickRare()
|
||||||
|
{
|
||||||
|
// 如果设备被玩家关闭,则不工作
|
||||||
|
if (flickableComp != null && !flickableComp.SwitchIsOn)
|
||||||
|
{
|
||||||
|
compTempControl.operatingAtHighPower = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
IntVec3 indoorCell = Position + IntVec3.South.RotatedBy(Rotation);
|
||||||
|
IntVec3 outdoorCell = Position + IntVec3.North.RotatedBy(Rotation);
|
||||||
|
|
||||||
|
if (indoorCell.Impassable(Map) || outdoorCell.Impassable(Map))
|
||||||
|
{
|
||||||
|
compTempControl.operatingAtHighPower = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float indoorTemp = indoorCell.GetTemperature(Map);
|
||||||
|
float outdoorTemp = outdoorCell.GetTemperature(Map);
|
||||||
|
float targetTemp = compTempControl.TargetTemperature;
|
||||||
|
|
||||||
|
float tempDifference = indoorTemp - outdoorTemp;
|
||||||
|
float efficiency = 1f - Mathf.Abs(tempDifference) * EfficiencyLossPerDegreeDifference;
|
||||||
|
if (efficiency < 0f)
|
||||||
|
{
|
||||||
|
efficiency = 0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operating = false;
|
||||||
|
|
||||||
|
if (indoorTemp > targetTemp) // 制冷
|
||||||
|
{
|
||||||
|
float coolingEnergy = compTempControl.Props.energyPerSecond * efficiency * 4.1666665f;
|
||||||
|
float tempChange = GenTemperature.ControlTemperatureTempChange(indoorCell, Map, -coolingEnergy, targetTemp);
|
||||||
|
|
||||||
|
if (!Mathf.Approximately(tempChange, 0f))
|
||||||
|
{
|
||||||
|
indoorCell.GetRoom(Map).Temperature += tempChange;
|
||||||
|
GenTemperature.PushHeat(outdoorCell, Map, coolingEnergy * HeatOutputMultiplier);
|
||||||
|
operating = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (indoorTemp < targetTemp) // 制热
|
||||||
|
{
|
||||||
|
float heatingEnergy = compTempControl.Props.energyPerSecond * efficiency * 4.1666665f;
|
||||||
|
float tempChange = GenTemperature.ControlTemperatureTempChange(indoorCell, Map, heatingEnergy, targetTemp);
|
||||||
|
|
||||||
|
if (!Mathf.Approximately(tempChange, 0f))
|
||||||
|
{
|
||||||
|
if (outdoorTemp > -100)
|
||||||
|
{
|
||||||
|
indoorCell.GetRoom(Map).Temperature += tempChange;
|
||||||
|
GenTemperature.PushHeat(outdoorCell, Map, -heatingEnergy / HeatOutputMultiplier);
|
||||||
|
operating = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compTempControl.operatingAtHighPower = operating;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
80
Source/ArachnaeSwarm/CompAbilityEffect_PsychicBrainburn.cs
Normal file
80
Source/ArachnaeSwarm/CompAbilityEffect_PsychicBrainburn.cs
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
using Verse;
|
||||||
|
using RimWorld;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace ArachnaeSwarm
|
||||||
|
{
|
||||||
|
public class CompAbilityEffect_PsychicBrainburn : CompAbilityEffect
|
||||||
|
{
|
||||||
|
public new CompProperties_PsychicBrainburn Props => (CompProperties_PsychicBrainburn)props;
|
||||||
|
|
||||||
|
public override void Apply(LocalTargetInfo target, LocalTargetInfo dest)
|
||||||
|
{
|
||||||
|
base.Apply(target, dest);
|
||||||
|
|
||||||
|
Pawn pawn = target.Pawn;
|
||||||
|
if (pawn == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找作为意识来源的身体部位
|
||||||
|
BodyPartRecord brain = pawn.health.hediffSet.GetNotMissingParts()
|
||||||
|
.FirstOrDefault(p => p.def.tags.Contains(BodyPartTagDefOf.ConsciousnessSource));
|
||||||
|
|
||||||
|
if (brain != null)
|
||||||
|
{
|
||||||
|
// 施加巨大伤害以摧毁大脑
|
||||||
|
float damageAmount = 99999f;
|
||||||
|
float penetration = 999f;
|
||||||
|
pawn.TakeDamage(new DamageInfo(DamageDefOf.Burn, damageAmount, penetration, -1f, parent.pawn, brain));
|
||||||
|
|
||||||
|
// 如果在XML中定义了效果,则生成它
|
||||||
|
if (Props.effecterDef != null)
|
||||||
|
{
|
||||||
|
Props.effecterDef.Spawn(pawn.Position, pawn.Map, 1f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Valid(LocalTargetInfo target, bool throwMessages = false)
|
||||||
|
{
|
||||||
|
Pawn pawn = target.Pawn;
|
||||||
|
if (pawn == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查目标是否是血肉生物(如果XML中设置为需要)
|
||||||
|
if (Props.requiresFlesh && !pawn.RaceProps.IsFlesh)
|
||||||
|
{
|
||||||
|
if (throwMessages)
|
||||||
|
{
|
||||||
|
Messages.Message("MessageCannotUseOnMechanical".Translate(pawn.Named("PAWN")), pawn, MessageTypeDefOf.RejectInput);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查目标是否有意识来源部位
|
||||||
|
BodyPartRecord brain = pawn.health.hediffSet.GetNotMissingParts()
|
||||||
|
.FirstOrDefault(p => p.def.tags.Contains(BodyPartTagDefOf.ConsciousnessSource));
|
||||||
|
|
||||||
|
if (brain == null)
|
||||||
|
{
|
||||||
|
if (throwMessages)
|
||||||
|
{
|
||||||
|
Messages.Message("MessageTargetHasNoBrain".Translate(pawn.Named("PAWN")), pawn, MessageTypeDefOf.RejectInput);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.Valid(target, throwMessages);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool AICanTargetNow(LocalTargetInfo target)
|
||||||
|
{
|
||||||
|
// AI不应主动使用此技能
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
20
Source/ArachnaeSwarm/CompProperties_PsychicBrainburn.cs
Normal file
20
Source/ArachnaeSwarm/CompProperties_PsychicBrainburn.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using Verse;
|
||||||
|
using RimWorld;
|
||||||
|
|
||||||
|
namespace ArachnaeSwarm
|
||||||
|
{
|
||||||
|
public class CompProperties_PsychicBrainburn : CompProperties_AbilityEffect
|
||||||
|
{
|
||||||
|
// 如果为true,则技能只能对血肉生物使用。
|
||||||
|
// 如果为false,则可以对机械体等非血肉生物使用。
|
||||||
|
public bool requiresFlesh = true;
|
||||||
|
|
||||||
|
// 在目标身上产生的视觉效果。
|
||||||
|
public EffecterDef effecterDef;
|
||||||
|
|
||||||
|
public CompProperties_PsychicBrainburn()
|
||||||
|
{
|
||||||
|
compClass = typeof(CompAbilityEffect_PsychicBrainburn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Source/ArachnaeSwarm/DefModExtension_NutrientVat.cs
Normal file
11
Source/ArachnaeSwarm/DefModExtension_NutrientVat.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using System;
|
||||||
|
using Verse;
|
||||||
|
|
||||||
|
namespace ArachnaeSwarm
|
||||||
|
{
|
||||||
|
public class DefModExtension_NutrientVat : DefModExtension
|
||||||
|
{
|
||||||
|
public string topGraphicPath;
|
||||||
|
public Type graphicClass = typeof(Graphic_Multi); // Default to Graphic_Multi if not specified in XML
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Verse;
|
||||||
|
|
||||||
|
namespace ArachnaeSwarm
|
||||||
|
{
|
||||||
|
public class HediffCompProperties_TerrainBasedSeverity : HediffCompProperties
|
||||||
|
{
|
||||||
|
// 检查效果的时间间隔(以ticks为单位)
|
||||||
|
public int interval = 60;
|
||||||
|
|
||||||
|
// 当角色站在此列表中的任何地形上时,严重性的变化值
|
||||||
|
public float severityOnTerrain = 0f;
|
||||||
|
|
||||||
|
// 当角色不在任何目标地形上时,严重性的变化值
|
||||||
|
public float severityOffTerrain = 0f;
|
||||||
|
|
||||||
|
// 目标地形的defName列表
|
||||||
|
public List<TerrainDef> terrainDefs;
|
||||||
|
|
||||||
|
public HediffCompProperties_TerrainBasedSeverity()
|
||||||
|
{
|
||||||
|
compClass = typeof(HediffComp_TerrainBasedSeverity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
42
Source/ArachnaeSwarm/HediffComp_TerrainBasedSeverity.cs
Normal file
42
Source/ArachnaeSwarm/HediffComp_TerrainBasedSeverity.cs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
using Verse;
|
||||||
|
|
||||||
|
namespace ArachnaeSwarm
|
||||||
|
{
|
||||||
|
public class HediffComp_TerrainBasedSeverity : HediffComp
|
||||||
|
{
|
||||||
|
public HediffCompProperties_TerrainBasedSeverity Props => (HediffCompProperties_TerrainBasedSeverity)props;
|
||||||
|
|
||||||
|
public override void CompPostTick(ref float severityAdjustment)
|
||||||
|
{
|
||||||
|
Pawn pawn = parent.pawn;
|
||||||
|
|
||||||
|
// 按照设定的时间间隔执行
|
||||||
|
if (pawn.IsHashIntervalTick(Props.interval))
|
||||||
|
{
|
||||||
|
// 确保角色在地图上
|
||||||
|
if (pawn.Spawned)
|
||||||
|
{
|
||||||
|
// 获取角色当前位置的地形
|
||||||
|
TerrainDef currentTerrain = pawn.Position.GetTerrain(pawn.Map);
|
||||||
|
|
||||||
|
// 检查当前地形是否存在于XML定义的列表中
|
||||||
|
if (Props.terrainDefs != null && Props.terrainDefs.Contains(currentTerrain))
|
||||||
|
{
|
||||||
|
// 如果在目标地形上,增加 severityOnTerrain
|
||||||
|
severityAdjustment += Props.severityOnTerrain;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 如果不在目标地形上,增加 severityOffTerrain
|
||||||
|
severityAdjustment += Props.severityOffTerrain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 如果角色不在地图上(例如在运输舱里),则总是应用 off-terrain 的效果
|
||||||
|
severityAdjustment += Props.severityOffTerrain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
33
Source/ArachnaeSwarm/Utils/CompTempControl_Fixed.cs
Normal file
33
Source/ArachnaeSwarm/Utils/CompTempControl_Fixed.cs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
using RimWorld;
|
||||||
|
using Verse;
|
||||||
|
|
||||||
|
namespace ArachnaeSwarm
|
||||||
|
{
|
||||||
|
// First, we need a new properties class that points to our new component class
|
||||||
|
public class CompProperties_TempControl_Fixed : CompProperties_TempControl
|
||||||
|
{
|
||||||
|
public CompProperties_TempControl_Fixed()
|
||||||
|
{
|
||||||
|
compClass = typeof(CompTempControl_Fixed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is our new component class that inherits from the original
|
||||||
|
public class CompTempControl_Fixed : CompTempControl
|
||||||
|
{
|
||||||
|
// We override the problematic method
|
||||||
|
public override string CompInspectStringExtra()
|
||||||
|
{
|
||||||
|
// Call the original method to get its string
|
||||||
|
string baseString = base.CompInspectStringExtra();
|
||||||
|
|
||||||
|
// If the string is not null, trim any whitespace from the end and return it
|
||||||
|
if (!string.IsNullOrEmpty(baseString))
|
||||||
|
{
|
||||||
|
return baseString.TrimEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
return baseString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
211
Source/ArachnaeSwarm/WULA_HediffDamgeShield/DRMDamageShield.cs
Normal file
211
Source/ArachnaeSwarm/WULA_HediffDamgeShield/DRMDamageShield.cs
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
using RimWorld;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using UnityEngine;
|
||||||
|
using Verse;
|
||||||
|
using Verse.Sound;
|
||||||
|
using HarmonyLib; // For AccessTools
|
||||||
|
|
||||||
|
namespace ArachnaeSwarm
|
||||||
|
{
|
||||||
|
// 自定义 CompProperties_Shield 变体
|
||||||
|
public class DRMCompShieldProp : CompProperties
|
||||||
|
{
|
||||||
|
public int startingTicksToReset = 3200;
|
||||||
|
public float minDrawSize = 1.2f;
|
||||||
|
public float maxDrawSize = 1.55f;
|
||||||
|
public float energyLossPerDamage = 0.033f;
|
||||||
|
public float energyOnReset = 0.2f;
|
||||||
|
public bool blocksRangedWeapons = true;
|
||||||
|
|
||||||
|
public DRMCompShieldProp()
|
||||||
|
{
|
||||||
|
compClass = typeof(DRMDamageShield);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DRMDamageShield : ThingComp
|
||||||
|
{
|
||||||
|
// 从 Hediff_DamageShield 获取层数作为能量
|
||||||
|
public float Energy
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
Hediff_DamageShield hediff = PawnOwner?.health?.hediffSet.GetFirstHediff<Hediff_DamageShield>();
|
||||||
|
return hediff?.ShieldCharges ?? 0;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
Hediff_DamageShield hediff = PawnOwner?.health?.hediffSet.GetFirstHediff<Hediff_DamageShield>();
|
||||||
|
if (hediff != null)
|
||||||
|
{
|
||||||
|
hediff.ShieldCharges = (int)value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public float MaxEnergy
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
Hediff_DamageShield hediff = PawnOwner?.health?.hediffSet.GetFirstHediff<Hediff_DamageShield>();
|
||||||
|
return hediff?.def.maxSeverity ?? 0;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
// MaxEnergy 由 HediffDef 控制,这里不需要设置
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsActive = false; // 控制护盾是否激活,由 Hediff_DamageShield 管理
|
||||||
|
|
||||||
|
// 复制自 CompShield
|
||||||
|
protected int ticksToReset = -1;
|
||||||
|
protected int lastKeepDisplayTick = -9999;
|
||||||
|
private Vector3 impactAngleVect;
|
||||||
|
private int lastAbsorbDamageTick = -9999;
|
||||||
|
|
||||||
|
public DRMCompShieldProp Props => (DRMCompShieldProp)props;
|
||||||
|
|
||||||
|
public ShieldState ShieldState
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (PawnOwner == null || !IsActive || Energy <= 0)
|
||||||
|
{
|
||||||
|
return ShieldState.Disabled;
|
||||||
|
}
|
||||||
|
if (ticksToReset <= 0)
|
||||||
|
{
|
||||||
|
return ShieldState.Active;
|
||||||
|
}
|
||||||
|
return ShieldState.Resetting;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Pawn PawnOwner
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return parent as Pawn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PostExposeData()
|
||||||
|
{
|
||||||
|
base.PostExposeData();
|
||||||
|
Scribe_Values.Look(ref ticksToReset, "ticksToReset", -1);
|
||||||
|
Scribe_Values.Look(ref lastKeepDisplayTick, "lastKeepDisplayTick", 0);
|
||||||
|
Scribe_Values.Look(ref IsActive, "isActive", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void CompTick()
|
||||||
|
{
|
||||||
|
base.CompTick();
|
||||||
|
if (PawnOwner == null || !IsActive)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ShieldState == ShieldState.Resetting)
|
||||||
|
{
|
||||||
|
ticksToReset--;
|
||||||
|
if (ticksToReset <= 0)
|
||||||
|
{
|
||||||
|
Reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ShieldState == ShieldState.Active)
|
||||||
|
{
|
||||||
|
// 护盾能量(层数)通过 Hediff_DamageShield 的 Tick 方法管理,这里不需要额外回复
|
||||||
|
// 如果需要自动回复层数,可以在这里实现
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PostPreApplyDamage(ref DamageInfo dinfo, out bool absorbed)
|
||||||
|
{
|
||||||
|
absorbed = false;
|
||||||
|
// 获取 Hediff_DamageShield 实例
|
||||||
|
Hediff_DamageShield damageShield = PawnOwner?.health?.hediffSet.GetFirstHediff<Hediff_DamageShield>();
|
||||||
|
|
||||||
|
if (ShieldState != ShieldState.Active || !IsActive || damageShield == null || damageShield.ShieldCharges <= 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 我们的护盾阻挡所有伤害类型,但不包含手术
|
||||||
|
// 如果伤害类型不被认为是“有益的”(例如,不是手术),则阻挡
|
||||||
|
if (!dinfo.Def.consideredHelpful)
|
||||||
|
{
|
||||||
|
// 消耗一层护盾
|
||||||
|
damageShield.ShieldCharges--;
|
||||||
|
|
||||||
|
// 触发护盾吸收效果
|
||||||
|
Notify_DamageAbsorbed(dinfo);
|
||||||
|
|
||||||
|
// 护盾抖动效果
|
||||||
|
PawnOwner.Drawer.renderer.wiggler.SetToCustomRotation(Rand.Range(-0.05f, 0.05f));
|
||||||
|
// 移除文字提示
|
||||||
|
// 移除粒子效果
|
||||||
|
|
||||||
|
absorbed = true; // 伤害被吸收
|
||||||
|
|
||||||
|
// 如果护盾层数归零,触发护盾击穿效果
|
||||||
|
if (damageShield.ShieldCharges <= 0)
|
||||||
|
{
|
||||||
|
Notify_ShieldBreak();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Notify_DamageAbsorbed(DamageInfo dinfo)
|
||||||
|
{
|
||||||
|
// 复制自 CompShield.AbsorbedDamage
|
||||||
|
SoundDefOf.EnergyShield_AbsorbDamage.PlayOneShot(new TargetInfo(PawnOwner.Position, PawnOwner.Map));
|
||||||
|
impactAngleVect = Vector3Utility.HorizontalVectorFromAngle(dinfo.Angle);
|
||||||
|
// 移除 FleckMaker.Static 和 FleckMaker.ThrowDustPuff
|
||||||
|
lastAbsorbDamageTick = Find.TickManager.TicksGame;
|
||||||
|
KeepDisplaying();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Notify_ShieldBreak()
|
||||||
|
{
|
||||||
|
// 复制自 CompShield.Break
|
||||||
|
if (parent.Spawned)
|
||||||
|
{
|
||||||
|
float scale = Mathf.Lerp(Props.minDrawSize, Props.maxDrawSize, Energy / MaxEnergy); // 根据当前能量比例调整大小
|
||||||
|
EffecterDefOf.Shield_Break.SpawnAttached(parent, parent.MapHeld, scale);
|
||||||
|
// 移除 FleckMaker.Static 和 FleckMaker.ThrowDustPuff
|
||||||
|
}
|
||||||
|
ticksToReset = Props.startingTicksToReset;
|
||||||
|
// 护盾层数归零将由 Hediff_DamageShield 负责移除 Hediff
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Reset()
|
||||||
|
{
|
||||||
|
// 复制自 CompShield.Reset
|
||||||
|
if (PawnOwner.Spawned)
|
||||||
|
{
|
||||||
|
SoundDefOf.EnergyShield_Reset.PlayOneShot(new TargetInfo(PawnOwner.Position, PawnOwner.Map));
|
||||||
|
// 移除 FleckMaker.ThrowLightningGlow
|
||||||
|
}
|
||||||
|
ticksToReset = -1;
|
||||||
|
// 能量恢复由 Hediff_DamageShield 负责,这里不需要设置 Energy
|
||||||
|
// 这里可以添加逻辑,让 Hediff_DamageShield 恢复层数
|
||||||
|
Hediff_DamageShield hediff = PawnOwner?.health?.hediffSet.GetFirstHediff<Hediff_DamageShield>();
|
||||||
|
if (hediff != null)
|
||||||
|
{
|
||||||
|
hediff.ShieldCharges = (int)hediff.def.initialSeverity; // 重置时恢复到初始层数
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void KeepDisplaying()
|
||||||
|
{
|
||||||
|
lastKeepDisplayTick = Find.TickManager.TicksGame;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
using Verse;
|
||||||
|
using System; // Add for Activator
|
||||||
|
using System.Text;
|
||||||
|
using RimWorld;
|
||||||
|
using UnityEngine;
|
||||||
|
using HarmonyLib; // Needed for AccessTools if you use it here directly
|
||||||
|
|
||||||
|
namespace ArachnaeSwarm
|
||||||
|
{
|
||||||
|
public class Hediff_DamageShield : HediffWithComps
|
||||||
|
{
|
||||||
|
// 伤害抵挡层数
|
||||||
|
public int ShieldCharges
|
||||||
|
{
|
||||||
|
get => (int)severityInt;
|
||||||
|
set => severityInt = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取或创建 DRMDamageShield 组件
|
||||||
|
public DRMDamageShield ShieldComp
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
DRMDamageShield comp = pawn.GetComp<DRMDamageShield>();
|
||||||
|
if (comp == null)
|
||||||
|
{
|
||||||
|
comp = (DRMDamageShield)Activator.CreateInstance(typeof(DRMDamageShield));
|
||||||
|
comp.parent = pawn;
|
||||||
|
comp.props = new DRMCompShieldProp(); // 确保有属性,即使是默认的
|
||||||
|
pawn.AllComps.Add(comp);
|
||||||
|
comp.Initialize(comp.props);
|
||||||
|
}
|
||||||
|
return comp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public override string LabelInBrackets
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (ShieldCharges > 0)
|
||||||
|
{
|
||||||
|
return "层数: " + ShieldCharges;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string TipStringExtra
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.Append(base.TipStringExtra);
|
||||||
|
if (ShieldCharges > 0)
|
||||||
|
{
|
||||||
|
sb.AppendLine(" - 每层抵挡一次伤害。当前层数: " + ShieldCharges);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.AppendLine(" - 没有可用的抵挡层数。");
|
||||||
|
}
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ExposeData()
|
||||||
|
{
|
||||||
|
base.ExposeData();
|
||||||
|
// severityInt 会自动保存,所以不需要额外处理 ShieldCharges
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PostAdd(DamageInfo? dinfo)
|
||||||
|
{
|
||||||
|
base.PostAdd(dinfo);
|
||||||
|
// 确保 Pawn 拥有 DRMCompShield 组件
|
||||||
|
DRMDamageShield comp = ShieldComp; // 访问属性以确保组件被添加
|
||||||
|
if (comp != null)
|
||||||
|
{
|
||||||
|
comp.IsActive = true; // 激活护盾组件
|
||||||
|
// 能量同步将在 Tick() 中完成
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PostRemoved()
|
||||||
|
{
|
||||||
|
base.PostRemoved();
|
||||||
|
// 当 Hediff 被移除时,移除对应的 DRMDamageShield 组件
|
||||||
|
DRMDamageShield comp = pawn.GetComp<DRMDamageShield>();
|
||||||
|
if (comp != null)
|
||||||
|
{
|
||||||
|
pawn.AllComps.Remove(comp);
|
||||||
|
comp.IsActive = false; // 确保禁用
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Tick()
|
||||||
|
{
|
||||||
|
base.Tick();
|
||||||
|
// 如果层数归零,移除 Hediff
|
||||||
|
if (ShieldCharges <= 0)
|
||||||
|
{
|
||||||
|
pawn.health.RemoveHediff(this);
|
||||||
|
}
|
||||||
|
// 同步能量到 ShieldComp
|
||||||
|
DRMDamageShield comp = pawn.GetComp<DRMDamageShield>(); // 每次 Tick 获取,确保是最新的
|
||||||
|
if (comp != null && comp.IsActive)
|
||||||
|
{
|
||||||
|
comp.Energy = ShieldCharges;
|
||||||
|
comp.MaxEnergy = (int)def.maxSeverity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using RimWorld;
|
||||||
|
using Verse;
|
||||||
|
|
||||||
|
namespace ArachnaeSwarm
|
||||||
|
{
|
||||||
|
public class SpawnerProduct
|
||||||
|
{
|
||||||
|
public ThingDef thingDef;
|
||||||
|
public int count = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CompProperties_MultiFuelSpawner : CompProperties
|
||||||
|
{
|
||||||
|
public List<SpawnerProduct> products;
|
||||||
|
public IntRange spawnIntervalRange = new IntRange(100, 100);
|
||||||
|
public bool spawnForbidden;
|
||||||
|
public bool inheritFaction;
|
||||||
|
public bool showMessageIfOwned;
|
||||||
|
|
||||||
|
public CompProperties_MultiFuelSpawner()
|
||||||
|
{
|
||||||
|
compClass = typeof(CompMultiFuelSpawner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CompMultiFuelSpawner : ThingComp
|
||||||
|
{
|
||||||
|
private int ticksUntilSpawn;
|
||||||
|
private List<IFuelSource> fuelComps;
|
||||||
|
|
||||||
|
public CompProperties_MultiFuelSpawner Props => (CompProperties_MultiFuelSpawner)props;
|
||||||
|
|
||||||
|
public override void PostSpawnSetup(bool respawningAfterLoad)
|
||||||
|
{
|
||||||
|
base.PostSpawnSetup(respawningAfterLoad);
|
||||||
|
if (!respawningAfterLoad)
|
||||||
|
{
|
||||||
|
ResetCountdown();
|
||||||
|
}
|
||||||
|
fuelComps = parent.GetComps<ThingComp>().OfType<IFuelSource>().ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PostExposeData()
|
||||||
|
{
|
||||||
|
base.PostExposeData();
|
||||||
|
Scribe_Values.Look(ref ticksUntilSpawn, "ticksUntilSpawn", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void CompTick()
|
||||||
|
{
|
||||||
|
base.CompTick();
|
||||||
|
|
||||||
|
if (fuelComps.NullOrEmpty()) return;
|
||||||
|
|
||||||
|
bool allFuelsOk = fuelComps.All(c => c.HasFuel);
|
||||||
|
|
||||||
|
if (allFuelsOk && (parent.GetComp<CompPowerTrader>()?.PowerOn ?? true))
|
||||||
|
{
|
||||||
|
// CORRECTED LOGIC: Consume fuel every tick
|
||||||
|
foreach (var comp in fuelComps)
|
||||||
|
{
|
||||||
|
comp.Notify_UsedThisTick();
|
||||||
|
}
|
||||||
|
|
||||||
|
ticksUntilSpawn--;
|
||||||
|
if (ticksUntilSpawn <= 0)
|
||||||
|
{
|
||||||
|
TryDoSpawn();
|
||||||
|
ResetCountdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TryDoSpawn()
|
||||||
|
{
|
||||||
|
if (Props.products.NullOrEmpty()) return;
|
||||||
|
|
||||||
|
foreach (var product in Props.products)
|
||||||
|
{
|
||||||
|
Thing thing = ThingMaker.MakeThing(product.thingDef);
|
||||||
|
thing.stackCount = product.count;
|
||||||
|
|
||||||
|
if (Props.inheritFaction && thing.Faction != parent.Faction)
|
||||||
|
{
|
||||||
|
thing.SetFaction(parent.Faction);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GenPlace.TryPlaceThing(thing, parent.Position, parent.Map, ThingPlaceMode.Near, out Thing resultingThing))
|
||||||
|
{
|
||||||
|
if (Props.spawnForbidden)
|
||||||
|
{
|
||||||
|
resultingThing.SetForbidden(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Props.showMessageIfOwned && parent.Faction == Faction.OfPlayer)
|
||||||
|
{
|
||||||
|
Messages.Message("MessageCompSpawnerSpawnedItem".Translate(resultingThing.LabelCap), resultingThing, MessageTypeDefOf.PositiveEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResetCountdown()
|
||||||
|
{
|
||||||
|
ticksUntilSpawn = Props.spawnIntervalRange.RandomInRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string CompInspectStringExtra()
|
||||||
|
{
|
||||||
|
string text = base.CompInspectStringExtra();
|
||||||
|
|
||||||
|
if (!fuelComps.NullOrEmpty() && fuelComps.All(c => c.HasFuel))
|
||||||
|
{
|
||||||
|
if (!text.NullOrEmpty())
|
||||||
|
{
|
||||||
|
text += "\n";
|
||||||
|
}
|
||||||
|
string productsStr = Props.products.Select(p => (string)p.thingDef.LabelCap).ToCommaList();
|
||||||
|
text += "NextSpawnedItemIn".Translate(productsStr) + ": " + ticksUntilSpawn.ToStringTicksToPeriod();
|
||||||
|
}
|
||||||
|
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
using RimWorld;
|
||||||
|
using Verse;
|
||||||
|
using System.Reflection;
|
||||||
|
using HarmonyLib;
|
||||||
|
|
||||||
|
namespace ArachnaeSwarm
|
||||||
|
{
|
||||||
|
public class CompProperties_RefuelableNutrition_WithKey : CompProperties_RefuelableNutrition
|
||||||
|
{
|
||||||
|
public string saveKeysPrefix;
|
||||||
|
|
||||||
|
public CompProperties_RefuelableNutrition_WithKey()
|
||||||
|
{
|
||||||
|
compClass = typeof(CompRefuelableNutrition_WithKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CompRefuelableNutrition_WithKey : CompRefuelableNutrition, IFuelSource
|
||||||
|
{
|
||||||
|
public new CompProperties_RefuelableNutrition_WithKey Props => (CompProperties_RefuelableNutrition_WithKey)props;
|
||||||
|
|
||||||
|
public override void PostExposeData()
|
||||||
|
{
|
||||||
|
string prefix = Props.saveKeysPrefix;
|
||||||
|
if (prefix.NullOrEmpty())
|
||||||
|
{
|
||||||
|
Log.ErrorOnce($"CompRefuelableNutrition_WithKey on {parent.def.defName} has a null or empty saveKeysPrefix. Defaulting to standard save.", GetHashCode());
|
||||||
|
base.PostExposeData();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Accessing private fields from CompRefuelable (base of CompRefuelableNutrition) ---
|
||||||
|
FieldInfo fuelField = AccessTools.Field(typeof(CompRefuelable), "fuel");
|
||||||
|
FieldInfo configuredTargetFuelLevelField = AccessTools.Field(typeof(CompRefuelable), "configuredTargetFuelLevel");
|
||||||
|
FieldInfo allowAutoRefuelField = AccessTools.Field(typeof(CompRefuelable), "allowAutoRefuel");
|
||||||
|
|
||||||
|
// Get current values
|
||||||
|
float currentFuel = (float)fuelField.GetValue(this);
|
||||||
|
float currentConfiguredLevel = (float)configuredTargetFuelLevelField.GetValue(this);
|
||||||
|
bool currentAllowAuto = (bool)allowAutoRefuelField.GetValue(this);
|
||||||
|
|
||||||
|
// Scribe with prefix
|
||||||
|
Scribe_Values.Look(ref currentFuel, prefix + "_fuel", 0f);
|
||||||
|
Scribe_Values.Look(ref currentConfiguredLevel, prefix + "_configuredTargetFuelLevel", -1f);
|
||||||
|
Scribe_Values.Look(ref currentAllowAuto, prefix + "_allowAutoRefuel", true);
|
||||||
|
|
||||||
|
// Set values back if loading
|
||||||
|
if (Scribe.mode == LoadSaveMode.LoadingVars)
|
||||||
|
{
|
||||||
|
fuelField.SetValue(this, currentFuel);
|
||||||
|
configuredTargetFuelLevelField.SetValue(this, currentConfiguredLevel);
|
||||||
|
allowAutoRefuelField.SetValue(this, currentAllowAuto);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Accessing private fields from CompRefuelableNutrition ---
|
||||||
|
// (Assuming there are any. If not, this part is not needed)
|
||||||
|
// Example:
|
||||||
|
// FieldInfo someOtherField = AccessTools.Field(typeof(CompRefuelableNutrition), "someOtherPrivateField");
|
||||||
|
// ... and so on
|
||||||
|
}
|
||||||
|
|
||||||
|
public new void Notify_UsedThisTick()
|
||||||
|
{
|
||||||
|
if (Props.consumeFuelOnlyWhenUsed)
|
||||||
|
{
|
||||||
|
ConsumeFuel(Props.fuelConsumptionRate / 60000f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
using RimWorld;
|
||||||
|
using Verse;
|
||||||
|
|
||||||
|
namespace ArachnaeSwarm
|
||||||
|
{
|
||||||
|
public class CompProperties_RefuelableWithKey : CompProperties_Refuelable
|
||||||
|
{
|
||||||
|
public string saveKeysPrefix;
|
||||||
|
|
||||||
|
public CompProperties_RefuelableWithKey()
|
||||||
|
{
|
||||||
|
compClass = typeof(CompRefuelableWithKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CompRefuelableWithKey : CompRefuelable, IFuelSource
|
||||||
|
{
|
||||||
|
public new CompProperties_RefuelableWithKey Props => (CompProperties_RefuelableWithKey)props;
|
||||||
|
|
||||||
|
public new void Notify_UsedThisTick()
|
||||||
|
{
|
||||||
|
if (Props.consumeFuelOnlyWhenUsed)
|
||||||
|
{
|
||||||
|
ConsumeFuel(Props.fuelConsumptionRate / 60000f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
8
Source/ArachnaeSwarm/WULA_MutiFuelSpawner/IFuelSource.cs
Normal file
8
Source/ArachnaeSwarm/WULA_MutiFuelSpawner/IFuelSource.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace ArachnaeSwarm
|
||||||
|
{
|
||||||
|
public interface IFuelSource
|
||||||
|
{
|
||||||
|
bool HasFuel { get; }
|
||||||
|
void Notify_UsedThisTick();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using HarmonyLib;
|
||||||
|
using RimWorld;
|
||||||
|
using Verse;
|
||||||
|
|
||||||
|
namespace ArachnaeSwarm
|
||||||
|
{
|
||||||
|
[HarmonyPatch(typeof(CompRefuelable), "PostExposeData")]
|
||||||
|
public static class Patch_CompRefuelableWithKey_PostExposeData
|
||||||
|
{
|
||||||
|
public static bool Prefix(CompRefuelable __instance)
|
||||||
|
{
|
||||||
|
if (!(__instance is CompRefuelableWithKey refuelableWithKey))
|
||||||
|
{
|
||||||
|
return true; // If it's not our class, run the original method
|
||||||
|
}
|
||||||
|
|
||||||
|
var props = (CompProperties_RefuelableWithKey)refuelableWithKey.Props;
|
||||||
|
string prefix = props.saveKeysPrefix;
|
||||||
|
|
||||||
|
if (prefix.NullOrEmpty())
|
||||||
|
{
|
||||||
|
Log.ErrorOnce($"CompRefuelableWithKey on {refuelableWithKey.parent.def.defName} has a null or empty saveKeysPrefix. Defaulting to standard save.", refuelableWithKey.GetHashCode());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use reflection to get/set private fields from the base class
|
||||||
|
FieldInfo fuelField = AccessTools.Field(typeof(CompRefuelable), "fuel");
|
||||||
|
FieldInfo configuredTargetFuelLevelField = AccessTools.Field(typeof(CompRefuelable), "configuredTargetFuelLevel");
|
||||||
|
FieldInfo allowAutoRefuelField = AccessTools.Field(typeof(CompRefuelable), "allowAutoRefuel");
|
||||||
|
|
||||||
|
float fuel = (float)fuelField.GetValue(refuelableWithKey);
|
||||||
|
float configuredTargetFuelLevel = (float)configuredTargetFuelLevelField.GetValue(refuelableWithKey);
|
||||||
|
bool allowAutoRefuel = (bool)allowAutoRefuelField.GetValue(refuelableWithKey);
|
||||||
|
|
||||||
|
Scribe_Values.Look(ref fuel, prefix + "_fuel", 0f);
|
||||||
|
Scribe_Values.Look(ref configuredTargetFuelLevel, prefix + "_configuredTargetFuelLevel", -1f);
|
||||||
|
Scribe_Values.Look(ref allowAutoRefuel, prefix + "_allowAutoRefuel", true);
|
||||||
|
|
||||||
|
if (Scribe.mode == LoadSaveMode.LoadingVars)
|
||||||
|
{
|
||||||
|
fuelField.SetValue(refuelableWithKey, fuel);
|
||||||
|
configuredTargetFuelLevelField.SetValue(refuelableWithKey, configuredTargetFuelLevel);
|
||||||
|
allowAutoRefuelField.SetValue(refuelableWithKey, allowAutoRefuel);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false; // Prevent the original PostExposeData from running
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
非公开资源/Content/Textures/Building/ARA_GrowthVat.psd
Normal file
BIN
非公开资源/Content/Textures/Building/ARA_GrowthVat.psd
Normal file
Binary file not shown.
Reference in New Issue
Block a user