This commit is contained in:
2025-09-10 12:14:30 +08:00
parent 54de15873f
commit cc5206c3c6
10 changed files with 760 additions and 149 deletions

Binary file not shown.

View File

@@ -197,142 +197,5 @@
</comps>
</HediffDef>
<HediffDef>
<defName>ARA_MimicNematode</defName>
<label>拟线种虫族寄生</label>
<description>被阿拉克涅拟线种虫族寄生了,如果不加以干预,拟线虫最终会杀死宿主并将其转化为没有意识的寄生体。拟线虫会通过寄生体的攻击行为感染其他躯体。</description>
<!-- 1. 指向我们创建的包含Comp的Hediff类 -->
<hediffClass>ArachnaeSwarm.Hediff_NecroticVirus</hediffClass>
<defaultLabelColor>(0.6, 0.4, 0.8)</defaultLabelColor>
<isBad>true</isBad>
<maxSeverity>1.0</maxSeverity>
<lethalSeverity>1.0</lethalSeverity>
<minSeverity>0.001</minSeverity>
<initialSeverity>0.001</initialSeverity>
<everCurableByItem>false</everCurableByItem>
<stages>
<li>
<label>潜伏期</label>
<becomeVisible>false</becomeVisible>
<vomitMtbDays>5</vomitMtbDays>
</li>
<li>
<label>活跃期</label>
<minSeverity>0.5</minSeverity>
<vomitMtbDays>1</vomitMtbDays>
<painFactor>0.5</painFactor>
<becomeVisible>false</becomeVisible>
<capMods>
<li>
<capacity>Talking</capacity>
<postFactor>0</postFactor>
</li>
</capMods>
</li>
<li>
<label>终末期</label>
<minSeverity>0.7</minSeverity>
<deathMtbDays>0.5</deathMtbDays>
<painFactor>0</painFactor>
<lifeThreatening>true</lifeThreatening>
<becomeVisible>true</becomeVisible>
<capMods>
<li>
<capacity>Talking</capacity>
<postFactor>0</postFactor>
</li>
<li>
<capacity>Consciousness</capacity>
<postFactor>0.7</postFactor>
</li>
</capMods>
</li>
<li>
<label>即将转化</label>
<minSeverity>0.9</minSeverity>
<deathMtbDays>0.5</deathMtbDays>
<painFactor>2.0</painFactor>
<lifeThreatening>true</lifeThreatening>
<becomeVisible>true</becomeVisible>
<capMods>
<li>
<capacity>Talking</capacity>
<postFactor>0</postFactor>
</li>
<li>
<capacity>Consciousness</capacity>
<setMax>0.1</setMax>
</li>
</capMods>
</li>
</stages>
<tendable>true</tendable>
<!-- 2. 这是最关键的配置部分 -->
<comps>
<li Class="HediffCompProperties_SeverityPerDay">
<severityPerDay>0.3</severityPerDay>
</li>
<li Class="HediffCompProperties_TendDuration">
<severityPerDayTended>-1</severityPerDayTended>
<baseTendDurationHours>24</baseTendDurationHours>
</li>
<li Class="HediffCompProperties_Disappears">
<disappearsAfterTicks>1800000~2400000</disappearsAfterTicks> <!-- 30 ! 45 days -->
<showRemainingTime>true</showRemainingTime>
</li>
<li Class="ArachnaeSwarm.HediffCompProperties_NecroticTransformation">
<!-- 3. 在这里指定你希望转化成的MutantDef的defName -->
<!-- 例如: Shambler, Ghoul, 或者您自己定义的其他变异体 -->
<mutantDef>Shambler</mutantDef>
</li>
</comps>
<modExtensions>
<li Class="ArachnaeSwarm.ProphecyGearEffect">
<!-- 只有当攻击者同时拥有'ARA_MimicNematode'这个仿生体时,以下效果才会触发 -->
<requiredBionicHediff>ARA_MimicNematode</requiredBionicHediff>
<!-- 额外造成5%的'ARA_MimicNematodeBite'伤害 -->
<enableExtraDamage>true</enableExtraDamage>
<extraDamageFactor>0.05</extraDamageFactor>
<extraDamageType>ARA_MimicNematodeBite</extraDamageType>
</li>
</modExtensions>
</HediffDef>
<RecipeDef ParentName="SurgeryFlesh">
<defName>ARA_CureBloodRot</defName>
<label>清除拟线虫感染</label>
<description>通过多种药物联合靶向治疗清除患者体内的阿拉克涅拟线种虫族感染。</description>
<workerClass>Recipe_RemoveHediff</workerClass>
<jobString>清除拟线虫感染.</jobString>
<workAmount>2000</workAmount>
<hideBodyPartNames>true</hideBodyPartNames>
<isViolation>false</isViolation>
<targetsBodyPart>false</targetsBodyPart>
<removesHediff>ARA_MimicNematode</removesHediff>
<successfullyRemovedHediffMessage>{0} 成功清除了 {1} 体内的拟线虫感染.</successfullyRemovedHediffMessage>
<skillRequirements>
<Medicine>5</Medicine>
</skillRequirements>
<ingredients>
<li>
<filter>
<thingDefs>
<li>MedicineUltratech</li>
</thingDefs>
</filter>
<count>10</count>
</li>
</ingredients>
<fixedIngredientFilter>
<categories>
<li>Medicine</li>
</categories>
</fixedIngredientFilter>
</RecipeDef>
</Defs>

View File

@@ -0,0 +1,249 @@
<?xml version="1.0" encoding="utf-8" ?>
<Defs>
<HediffDef>
<defName>ARA_MimicNematode</defName>
<label>拟线种虫族寄生</label>
<description>被阿拉克涅拟线种虫族寄生了,如果不加以干预,拟线虫最终会杀死宿主并将其转化为没有意识的寄生体。拟线虫会通过寄生体的攻击行为感染其他躯体。</description>
<!-- 1. 指向我们创建的包含Comp的Hediff类 -->
<hediffClass>ArachnaeSwarm.Hediff_NecroticVirus</hediffClass>
<defaultLabelColor>(0.6, 0.4, 0.8)</defaultLabelColor>
<isBad>true</isBad>
<maxSeverity>1.0</maxSeverity>
<lethalSeverity>1.0</lethalSeverity>
<minSeverity>0.001</minSeverity>
<initialSeverity>0.001</initialSeverity>
<everCurableByItem>false</everCurableByItem>
<stages>
<li>
<label>潜伏期</label>
<becomeVisible>false</becomeVisible>
<vomitMtbDays>5</vomitMtbDays>
</li>
<li>
<label>活跃期</label>
<minSeverity>0.5</minSeverity>
<vomitMtbDays>1</vomitMtbDays>
<painFactor>0.5</painFactor>
<becomeVisible>false</becomeVisible>
<capMods>
<li>
<capacity>Talking</capacity>
<postFactor>0</postFactor>
</li>
</capMods>
</li>
<li>
<label>终末期</label>
<minSeverity>0.7</minSeverity>
<deathMtbDays>0.5</deathMtbDays>
<painFactor>0</painFactor>
<lifeThreatening>true</lifeThreatening>
<becomeVisible>true</becomeVisible>
<capMods>
<li>
<capacity>Talking</capacity>
<postFactor>0</postFactor>
</li>
<li>
<capacity>Consciousness</capacity>
<postFactor>0.7</postFactor>
</li>
</capMods>
<regeneration>50</regeneration>
</li>
<li>
<label>即将转化</label>
<minSeverity>0.9</minSeverity>
<deathMtbDays>0.5</deathMtbDays>
<painFactor>2.0</painFactor>
<lifeThreatening>true</lifeThreatening>
<becomeVisible>true</becomeVisible>
<capMods>
<li>
<capacity>Talking</capacity>
<postFactor>0</postFactor>
</li>
<li>
<capacity>Consciousness</capacity>
<setMax>0.1</setMax>
</li>
</capMods>
<regeneration>50</regeneration>
</li>
</stages>
<tendable>true</tendable>
<!-- 2. 这是最关键的配置部分 -->
<comps>
<li Class="HediffCompProperties_SeverityPerDay">
<severityPerDay>0.3</severityPerDay>
</li>
<li Class="HediffCompProperties_TendDuration">
<severityPerDayTended>-1</severityPerDayTended>
<baseTendDurationHours>24</baseTendDurationHours>
</li>
<li Class="HediffCompProperties_Disappears">
<disappearsAfterTicks>1800000~2400000</disappearsAfterTicks> <!-- 30 ! 45 days -->
<showRemainingTime>true</showRemainingTime>
</li>
<li Class="ArachnaeSwarm.HediffCompProperties_NecroticTransformation">
<!-- 3. 在这里指定你希望转化成的MutantDef的defName -->
<!-- 例如: Shambler, Ghoul, 或者您自己定义的其他变异体 -->
<mutantDef>ARA_MimicNematodeShambler</mutantDef>
<triggerSeverity>0.7</triggerSeverity>
</li>
</comps>
<modExtensions>
<li Class="ArachnaeSwarm.ProphecyGearEffect">
<!-- 只有当攻击者同时拥有'ARA_MimicNematode'这个仿生体时,以下效果才会触发 -->
<requiredBionicHediff>ARA_MimicNematode</requiredBionicHediff>
<!-- 额外造成5%的'ARA_MimicNematodeBite'伤害 -->
<enableExtraDamage>true</enableExtraDamage>
<extraDamageFactor>0.05</extraDamageFactor>
<extraDamageType>ARA_MimicNematodeBite</extraDamageType>
</li>
</modExtensions>
</HediffDef>
<RecipeDef ParentName="SurgeryFlesh">
<defName>ARA_CureBloodRot</defName>
<label>清除拟线虫感染</label>
<description>通过多种药物联合靶向治疗清除患者体内的阿拉克涅拟线种虫族感染。</description>
<workerClass>Recipe_RemoveHediff</workerClass>
<jobString>清除拟线虫感染.</jobString>
<workAmount>2000</workAmount>
<hideBodyPartNames>true</hideBodyPartNames>
<isViolation>false</isViolation>
<targetsBodyPart>false</targetsBodyPart>
<removesHediff>ARA_MimicNematode</removesHediff>
<successfullyRemovedHediffMessage>{0} 成功清除了 {1} 体内的拟线虫感染.</successfullyRemovedHediffMessage>
<skillRequirements>
<Medicine>5</Medicine>
</skillRequirements>
<ingredients>
<li>
<filter>
<thingDefs>
<li>MedicineUltratech</li>
</thingDefs>
</filter>
<count>10</count>
</li>
</ingredients>
<fixedIngredientFilter>
<categories>
<li>Medicine</li>
</categories>
</fixedIngredientFilter>
</RecipeDef>
<!-- 这是我们的主要变异体Hediff现在使用我们自己的可配置类 -->
<HediffDef>
<defName>ARA_MimicNematodeShambler</defName>
<label>阿拉克涅拟线种寄生体</label>
<description>这具尸体被一种阿拉克涅拟线虫所寄生并重新激活。被寄生的生物行动迟缓、没有心智,只会无情地攻击任何活物。在活动几天后,寄生体将因宿主新陈代谢衰竭而死亡。被捕获并固定后,寄生体无法移动,因此其生命活动会暂停。</description>
<hediffClass>ArachnaeSwarm.Hediff_ConfigurableMutant</hediffClass> <!-- 指向我们自己的类 -->
<defaultLabelColor>(0.6, 0.4, 0.8)</defaultLabelColor>
<everCurableByItem>false</everCurableByItem>
<duplicationAllowed>false</duplicationAllowed>
<keepOnBodyPartRestoration>True</keepOnBodyPartRestoration>
<stages>
<li>
<naturalHealingFactor>0</naturalHealingFactor>
<painFactor>0</painFactor>
<statFactors>
<MeleeCooldownFactor>1.5</MeleeCooldownFactor>
<PsychicSensitivity>0</PsychicSensitivity>
</statFactors>
<statOffsets>
<ComfyTemperatureMin>-60</ComfyTemperatureMin>
<ComfyTemperatureMax>30</ComfyTemperatureMax>
<MinimumContainmentStrength>25</MinimumContainmentStrength>
<ToxicResistance>1</ToxicResistance>
</statOffsets>
<capMods>
<li><capacity>Talking</capacity><postFactor>0</postFactor></li>
<li><capacity>Consciousness</capacity><postFactor>0.7</postFactor></li>
</capMods>
<regeneration>200</regeneration>
</li>
</stages>
<comps>
<!-- 原版的消失组件保持不变-->
<li Class="HediffCompProperties_DisappearsAndKills">
<compClass>HediffComp_DisappearsAndKills_Shambler</compClass>
<disappearsAfterTicks>1800000~2400000</disappearsAfterTicks> <!-- 30 ! 45 days -->
<showRemainingTime>true</showRemainingTime>
</li>
<li Class="HediffCompProperties_DisappearsOnDeath" />
<li Class="HediffCompProperties_AttachPoints" />
<!-- 添加我们的新配置组件 -->
<li Class="ArachnaeSwarm.HediffCompProperties_ConfigurableMutant">
<!-- 1. 定义起身和尸体状态 -->
<risingHediff>ARA_Rising</risingHediff>
<corpseHediff>ARA_MimicNematodeShamblerShamblerCorpse</corpseHediff>
<!-- 2. (可选) 覆盖其他默认值 -->
<bioferriteOnDeathChance>0</bioferriteOnDeathChance>
<bioferriteAmountOnDeath>0</bioferriteAmountOnDeath>
</li>
</comps>
</HediffDef>
<!-- ====================================================================== -->
<!-- 以下是上面引用的两个Hediff的定义保持不变 -->
<HediffDef>
<defName>ARA_Rising</defName>
<label>起身中</label>
<description>阿拉克涅拟线虫寄生赋予的强大再生能力正在复苏这具身体。</description>
<everCurableByItem>false</everCurableByItem>
<recordDownedTale>false</recordDownedTale>
<defaultLabelColor>(0.6, 0.4, 0.8)</defaultLabelColor>
<stages>
<li>
<painFactor>0</painFactor>
<capMods>
<li><capacity>Moving</capacity><postFactor>0</postFactor></li>
<li><capacity>Manipulation</capacity><postFactor>0</postFactor></li>
<li><capacity>Talking</capacity><postFactor>0</postFactor></li>
<li><capacity>Consciousness</capacity><setMax>0.1</setMax></li>
</capMods>
</li>
</stages>
</HediffDef>
<HediffDef>
<defName>ARA_MimicNematodeShamblerShamblerCorpse</defName>
<label>拟线种寄生体尸体</label>
<description>这具身体刚刚被阿拉克涅拟线种寄生"复活"过。</description>
<forceRemoveOnResurrection>true</forceRemoveOnResurrection>
<defaultLabelColor>(0.6, 0.4, 0.8)</defaultLabelColor>
<renderNodeProperties>
<li Class="PawnRenderNodeProperties_Overlay">
<debugLabel>Shambler wounds</debugLabel>
<workerClass>PawnRenderNodeWorker_OverlayShambler</workerClass>
<overlayLayer>Body</overlayLayer>
<baseLayer>20</baseLayer>
<pawnType>HumanlikeOnly</pawnType>
</li>
<li>
<debugLabel>Shambler wounds</debugLabel>
<nodeClass>PawnRenderNode_AnimalPart</nodeClass>
<workerClass>PawnRenderNodeWorker_OverlayShambler</workerClass>
<overlayLayer>Body</overlayLayer>
<baseLayer>20</baseLayer>
<pawnType>NonHumanlikeOnly</pawnType>
</li>
</renderNodeProperties>
</HediffDef>
</Defs>

View File

@@ -3,14 +3,14 @@
<MutantDef ParentName="BaseMutantEntity">
<defName>ARA_MimicNematodeShambler</defName>
<label>MimicNematode shambler</label>
<description>A creature who has been raised from the dead by corrupted nano-scale archites.\n\nAbout shamblers: Shamblers are corpses which have been reanimated by archotechnology. They attack living creatures relentlessly and their immunity to pain makes them difficult to kill. However, the archites which animate them cannot sustain the rotting body for long, so shamblers expire after a few days of movement.\n\nThere are ways to raise your own shamblers and make yourself invisible to them, so they attack only outsiders.</description>
<hediff>Shambler</hediff>
<label>阿拉克涅拟线种寄生体</label>
<description>阿拉克涅拟线种是虫群中最神秘的分支之一。它们与原虫种同源,但在进化之路上并未发展出高级智慧,而是走向了一条截然不同的道路:寄生。拟线种是无法脱离宿主独立存活的寄生虫。一旦寄生于生物体内,它们会长期潜伏,最终逐步接管宿主的神经与代谢系统,将其变为受其操控的行尸走肉——“寄生体”。得益于超凡的细胞复制与再生能力,拟线种能够模仿并替代宿主的细胞结构,这使得寄生体拥有了惊人的自我修复能力。然而,这种寄生关系也极具侵略性:拟线种的虫卵遍布寄生体体表,任何被寄生体攻击的生物都有可能被感染,最终沦为新的寄生体。尽管拟线虫将寄生体的新陈代谢维持在极低水平,但寄生体本身并不会主动觅食。因此,当能量耗尽后,它们终将迎来“死亡”。</description>
<hediff>ARA_MimicNematodeShambler</hediff>
<thinkTree>Shambler</thinkTree>
<thinkTreeConstant>ShamblerConstant</thinkTreeConstant>
<hideLabel>true</hideLabel>
<namePrefix>shambler </namePrefix>
<useCorpseGraphics>true</useCorpseGraphics>
<namePrefix>拟线种寄生体 </namePrefix>
<useCorpseGraphics>false</useCorpseGraphics>
<isConsideredCorpse>true</isConsideredCorpse>
<bloodDef>Filth_DarkBlood</bloodDef>
<bloodSmearDef>Filth_DarkBloodSmear</bloodSmearDef>
@@ -34,7 +34,7 @@
<woundColor>(0.3, 0.3, 0.0, 1.0)</woundColor>
<anomalyKnowledgeOffset>0</anomalyKnowledgeOffset>
<knowledgeCategory>Basic</knowledgeCategory>
<codexEntry>Shambler</codexEntry>
<!--<codexEntry>Shambler</codexEntry>-->
<!-- Sounds -->
<soundAttackChance>0.25</soundAttackChance>
<soundCall>Pawn_Shambler_Call</soundCall>
@@ -64,7 +64,7 @@
<li>
<label>teeth</label>
<capacities>
<li>Bite</li>
<li>ARA_MimicNematodeShamblerBite</li>
</capacities>
<power>8.2</power>
<cooldownTime>2</cooldownTime>
@@ -100,14 +100,32 @@
</tools>
</MutantDef>
<ToolCapacityDef>
<defName>ARA_MimicNematodeShamblerBite</defName>
<label>撕咬</label>
</ToolCapacityDef>
<ManeuverDef>
<defName>ARA_MimicNematodeShamblerBite</defName>
<requiredCapacity>ARA_MimicNematodeShamblerBite</requiredCapacity>
<verb>
<verbClass>Verb_MeleeAttackDamage</verbClass>
<meleeDamageDef>ARA_MimicNematodeBite</meleeDamageDef>
</verb>
<logEntryDef>MeleeAttack</logEntryDef>
<combatLogRulesHit>Maneuver_Slash_MeleeHit</combatLogRulesHit>
<combatLogRulesDeflect>Maneuver_Slash_MeleeDeflect</combatLogRulesDeflect>
<combatLogRulesMiss>Maneuver_Slash_MeleeMiss</combatLogRulesMiss>
<combatLogRulesDodge>Maneuver_Slash_MeleeDodge</combatLogRulesDodge>
</ManeuverDef>
<PawnKindDef ParentName="MutantBase" Name="ARA_ShamblerBase" Abstract="True">
<mutant>Shambler</mutant>
<mutant>ARA_MimicNematodeShambler</mutant>
<generateInitialNonFamilyRelations>false</generateInitialNonFamilyRelations>
</PawnKindDef>
<PawnKindDef ParentName="ARA_ShamblerBase">
<defName>ARA_MimicNematodeShamblerSwarmer</defName>
<label>shambler swarmer</label>
<label>阿拉克涅拟线种寄生体</label>
<combatPower>40</combatPower>
<gearHealthRange>0.2~0.4</gearHealthRange>
<itemQuality>Poor</itemQuality>

View File

@@ -564,4 +564,68 @@
</li>
</comps>
</ThingDef>
<ThingDef ParentName="BaseMeleeWeapon_Sharp_Quality">
<defName>ARA_MW_Mimic_Niddle</defName>
<label>武装器官"拟线种毒针"</label>
<description>阿拉克涅虫群督虫使用基础近战武装器官,通过多根外露神经束与督虫的辅肢相连。这根毒针中藏有休眠中的阿拉克涅拟线种虫卵,攻击将感染受害者使其最终成为被拟线虫操控的寄生体。</description>
<weaponTags>
<li>ARA_Armed_Organ</li>
<li>ARA_Armed_Organ_Melee</li>
<li>ARA_Armed_Organ_T1</li>
</weaponTags>
<graphicData>
<texPath>ArachnaeSwarm/Weapon/ARA_MW_Bone_Sword</texPath>
<graphicClass>Graphic_Single</graphicClass>
<shaderType>CutoutComplex</shaderType>
<drawSize>1</drawSize>
</graphicData>
<uiIconScale>1</uiIconScale>
<!-- <equippedAngleOffset>-65</equippedAngleOffset> -->
<techLevel>Animal</techLevel>
<!-- <equippedAngleOffset>-25</equippedAngleOffset> -->
<costStuffCount>0</costStuffCount>
<stuffCategories Inherit="False"/>
<costList Inherit="False">
<ARA_Carapace>50</ARA_Carapace>
</costList>
<statBases>
<WorkToMake>1000</WorkToMake>
<Mass>5</Mass>
</statBases>
<tools Inherit="False">
<li>
<label></label>
<capacities>
<li>Cut</li>
</capacities>
<power>15</power>
<cooldownTime>1.3</cooldownTime>
<armorPenetration>0.50</armorPenetration>
</li>
<li>
<label></label>
<capacities>
<li>Poke</li>
</capacities>
<power>12</power>
<armorPenetration>0.20</armorPenetration>
<cooldownTime>1.5</cooldownTime>
<extraMeleeDamages>
<li>
<def>ARA_MimicNematodeBite</def>
<amount>4</amount>
</li>
</extraMeleeDamages>
</li>
</tools>
<recipeMaker>
<recipeUsers Inherit="False" />
<researchPrerequisite Inherit="False" />
<unfinishedThingDef>UnfinishedWeapon</unfinishedThingDef>
</recipeMaker>
<thingSetMakerTags>
<li>RewardStandardQualitySuper</li>
</thingSetMakerTags>
</ThingDef>
</Defs>

View File

@@ -189,6 +189,7 @@
<Compile Include="Hediff_NecroticVirus_Configurable.cs" />
<Compile Include="NecroticTransformationUtility.cs" />
<Compile Include="ProphecyGearEffect.cs" />
<Compile Include="Hediff_ConfigurableMutant.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- 自定义清理任务删除obj文件夹中的临时文件 -->

View File

@@ -10,6 +10,9 @@ namespace ArachnaeSwarm
{
// 在XML中需要指定的MutantDef的defName
public MutantDef mutantDef;
// 在XML中配置触发转变所需的严重性阈值
public float triggerSeverity = 0.7f;
public HediffCompProperties_NecroticTransformation()
{

View File

@@ -0,0 +1,374 @@
using System.Collections.Generic;
using RimWorld;
using UnityEngine;
using Verse;
using Verse.AI;
using Verse.AI.Group;
using Verse.Sound;
namespace ArachnaeSwarm
{
public class HediffCompProperties_ConfigurableMutant : HediffCompProperties
{
// --- 可配置的Hediff ---
public HediffDef risingHediff;
public HediffDef corpseHediff;
// --- 可配置的时间和范围 ---
public FloatRange resurrectionSecondsRange = new FloatRange(5f, 10f);
public FloatRange stunSecondsRange = new FloatRange(1f, 3f);
public FloatRange alertSecondsRange = new FloatRange(0.5f, 3f);
public IntRange selfRaiseHoursRange = new IntRange(3, 4);
public IntRange checkForTargetTicksInterval = new IntRange(900, 1800);
// --- 可配置的杂项值 ---
public float particleSpawnMTBSeconds = 1f;
public float extinguishFireMTB = 45f;
public float bioferriteOnDeathChance = 0.04f;
public int bioferriteAmountOnDeath = 10;
public HediffCompProperties_ConfigurableMutant()
{
this.compClass = typeof(HediffComp_ConfigurableMutant);
}
}
public class HediffComp_ConfigurableMutant : HediffComp
{
public HediffCompProperties_ConfigurableMutant Props => (HediffCompProperties_ConfigurableMutant)this.props;
}
public class Hediff_ConfigurableMutant : HediffWithComps
{
// 运行时状态字段,保持不变
public float headRotation;
private float resurrectTimer;
private float selfRaiseTimer;
private float alertTimer;
private int nextTargetCheckTick = -99999;
private Thing alertedTarget;
private Effecter riseEffecter;
private Sustainer riseSustainer;
private float corpseDamagePct = 1f;
private HediffComp_ConfigurableMutant PropsComp => this.TryGetComp<HediffComp_ConfigurableMutant>();
public bool IsRising => PropsComp?.Props.risingHediff != null && pawn.health.hediffSet.HasHediff(PropsComp.Props.risingHediff);
public override void PostMake()
{
base.PostMake();
headRotation = Rand.RangeSeeded(-20f, 20f, pawn.thingIDNumber);
if (!pawn.Dead)
{
pawn.timesRaisedAsShambler++;
}
}
public override void PostAdd(DamageInfo? dinfo)
{
if (!ModLister.CheckAnomaly("Shambler"))
{
pawn.health.RemoveHediff(this);
}
else
{
base.PostAdd(dinfo);
}
}
public override void Notify_Spawned()
{
base.Notify_Spawned();
pawn.Map.mapPawns.RegisterShambler(pawn);
}
public override void TickInterval(int delta)
{
base.TickInterval(delta);
var compProps = PropsComp?.Props;
if (compProps == null) return; // 如果没有Comp则不执行任何操作
if (IsRising)
{
if ((float)Find.TickManager.TicksGame > resurrectTimer)
{
FinishRising();
}
if (!pawn.Spawned) return;
if ((float)Find.TickManager.TicksGame > resurrectTimer - 15f)
{
riseSustainer?.End();
}
else if (IsRising)
{
if (riseSustainer == null || riseSustainer.Ended)
{
riseSustainer = SoundDefOf.Pawn_Shambler_Rise.TrySpawnSustainer(SoundInfo.InMap(pawn, MaintenanceType.PerTick));
}
if (riseEffecter == null)
{
riseEffecter = EffecterDefOf.ShamblerRaise.Spawn(pawn, pawn.Map);
}
if (pawn.Drawer.renderer.CurAnimation != AnimationDefOf.ShamblerRise)
{
pawn.Drawer.renderer.SetAnimation(AnimationDefOf.ShamblerRise);
}
riseSustainer.Maintain();
riseEffecter.EffectTick(pawn, TargetInfo.Invalid);
}
return;
}
if (Rand.MTBEventOccurs(compProps.particleSpawnMTBSeconds, 60f, 1f))
{
FleckMaker.ThrowShamblerParticles(pawn);
}
if (pawn.IsBurning() && !pawn.Downed && Rand.MTBEventOccurs(compProps.extinguishFireMTB, 60f, 1f))
{
((Fire)pawn.GetAttachment(ThingDefOf.Fire))?.Destroy();
pawn.records.Increment(RecordDefOf.FiresExtinguished);
}
if (pawn.Spawned && pawn.mutant != null && !pawn.mutant.IsPassive && !pawn.Drafted)
{
if (Find.TickManager.TicksGame > nextTargetCheckTick)
{
nextTargetCheckTick = Find.TickManager.TicksGame + compProps.checkForTargetTicksInterval.RandomInRange;
Thing thing = MutantUtility.FindShamblerTarget(pawn);
if (thing != null)
{
Notify_DelayedAlert(thing);
MutantUtility.ActivateNearbyShamblers(pawn, thing);
}
}
if (alertedTarget != null && (float)Find.TickManager.TicksGame > alertTimer)
{
pawn.mindState.enemyTarget = alertedTarget;
pawn.mindState.lastEngageTargetTick = Find.TickManager.TicksGame; // 直接给public字段赋值绕过internal方法
pawn.jobs.EndCurrentJob(JobCondition.InterruptForced);
alertedTarget = null;
if (DebugViewSettings.drawShamblerAlertMote)
{
MoteMaker.MakeColonistActionOverlay(pawn, ThingDefOf.Mote_ShamblerAlert);
}
SoundDefOf.Pawn_Shambler_Alert.PlayOneShot(pawn);
}
}
if (ShouldSelfRaise())
{
StartRising();
}
}
private bool ShouldSelfRaise()
{
if (pawn.DevelopmentalStage == DevelopmentalStage.Baby) return false;
return pawn.Downed && pawn.CarriedBy == null && (float)Find.TickManager.TicksGame > selfRaiseTimer;
}
public void StartRising(int lifespanTicks = -1)
{
var compProps = PropsComp?.Props;
if (compProps?.risingHediff == null)
{
Log.Error($"[ConfigurableMutant] risingHediff is not defined in XML for {this.def.defName}");
return;
}
if (!pawn.Dead && !pawn.Downed)
{
Log.Error("Tried to raise non dead/downed pawn as shambler");
if(pawn.mutant != null) pawn.mutant.Turn(clearLord: true);
return;
}
MutantUtility.RestoreBodyParts(pawn);
pawn.Notify_DisabledWorkTypesChanged();
if (!pawn.Dead || ResurrectionUtility.TryResurrect(pawn, new ResurrectionParams { noLord = true, restoreMissingParts = false, removeDiedThoughts = false }))
{
pawn.jobs?.EndCurrentJob(JobCondition.InterruptForced);
resurrectTimer = Find.TickManager.TicksGame + compProps.resurrectionSecondsRange.RandomInRange.SecondsToTicks();
pawn.health.AddHediff(compProps.risingHediff);
}
}
private void CancelRising()
{
var risingHediff = PropsComp?.Props.risingHediff;
if (risingHediff == null) return;
riseSustainer?.End();
resurrectTimer = -99999f;
if (pawn.health.hediffSet.TryGetHediff(risingHediff, out var hediff))
{
pawn.health.RemoveHediff(hediff);
}
if (!pawn.Dead)
{
pawn.Kill(null, null);
}
}
private void FinishRising(bool stun = true)
{
var compProps = PropsComp?.Props;
if (compProps?.risingHediff != null && pawn.health.hediffSet.TryGetHediff(compProps.risingHediff, out var hediff))
{
pawn.health.RemoveHediff(hediff);
}
if (pawn.ParentHolder is Pawn_CarryTracker pawn_CarryTracker)
{
pawn_CarryTracker.TryDropCarriedThing(pawn_CarryTracker.pawn.Position, ThingPlaceMode.Near, out var _);
pawn_CarryTracker.pawn.jobs.EndCurrentJob(JobCondition.InterruptForced);
}
if (pawn.mutant != null && !pawn.mutant.HasTurned)
{
pawn.mutant.Turn();
}
pawn.timesRaisedAsShambler++;
MutantUtility.RestoreUntilNotDowned(pawn);
if (pawn.Spawned && stun)
{
pawn.Rotation = Rot4.South;
if(compProps != null)
pawn.stances.stunner.StunFor(compProps.stunSecondsRange.RandomInRange.SecondsToTicks(), pawn, addBattleLog: false, showMote: false);
}
pawn.Drawer.renderer.SetAnimation(null);
StartSelfRaiseTimer();
}
private void StartSelfRaiseTimer()
{
var selfRaiseHoursRange = PropsComp?.Props.selfRaiseHoursRange ?? new IntRange(3, 4);
selfRaiseTimer = Find.TickManager.TicksGame + 2500 * selfRaiseHoursRange.RandomInRange;
}
public override void Notify_PawnPostApplyDamage(DamageInfo dinfo, float totalDamageDealt)
{
base.Notify_PawnPostApplyDamage(dinfo, totalDamageDealt);
if (dinfo.Instigator != null && pawn.HostileTo(dinfo.Instigator))
{
if (pawn.Spawned && dinfo.Instigator is IAttackTarget && dinfo.Instigator.Spawned && pawn.CanSee(dinfo.Instigator))
{
pawn.mindState.enemyTarget = dinfo.Instigator;
pawn.mindState.lastEngageTargetTick = Find.TickManager.TicksGame; // 直接给public字段赋值绕过internal方法
pawn.jobs.EndCurrentJob(JobCondition.InterruptOptional);
pawn.GetLord()?.Notify_PawnAcquiredTarget(pawn, dinfo.Instigator);
}
MutantUtility.ActivateNearbyShamblers(pawn, dinfo.Instigator);
}
}
public void Notify_DelayedAlert(Thing target)
{
var alertSecondsRange = PropsComp?.Props.alertSecondsRange ?? new FloatRange(0.5f, 3f);
if (pawn.mutant != null && !pawn.mutant.IsPassive && !pawn.Drafted && pawn.mindState.enemyTarget == null)
{
alertTimer = Find.TickManager.TicksGame + alertSecondsRange.RandomInRange.SecondsToTicks();
alertedTarget = target;
pawn.GetLord()?.Notify_PawnAcquiredTarget(pawn, target);
}
}
public override void Notify_Downed()
{
StartSelfRaiseTimer();
}
public override void Notify_PawnKilled()
{
corpseDamagePct = pawn.health.summaryHealth.SummaryHealthPercent;
base.Notify_PawnKilled();
}
public override void Notify_PawnDied(DamageInfo? dinfo, Hediff culprit = null)
{
if (IsRising)
{
CancelRising();
}
var compProps = PropsComp?.Props;
if (compProps != null && pawn.timesRaisedAsShambler == 1 && Rand.Chance(compProps.bioferriteOnDeathChance) && pawn.SpawnedOrAnyParentSpawned)
{
Thing thing = ThingMaker.MakeThing(ThingDefOf.Bioferrite);
thing.stackCount = compProps.bioferriteAmountOnDeath;
GenPlace.TryPlaceThing(thing, pawn.PositionHeld, pawn.MapHeld, ThingPlaceMode.Near, out var lastResultingThing);
lastResultingThing.SetForbidden(value: true);
}
if (pawn.Corpse != null)
{
pawn.Corpse.HitPoints = Mathf.Max(Mathf.RoundToInt((float)pawn.Corpse.MaxHitPoints * corpseDamagePct), 10);
}
if (compProps?.corpseHediff != null)
{
pawn.health.AddHediff(compProps.corpseHediff);
}
base.Notify_PawnDied(dinfo, culprit);
}
public override void PreRemoved()
{
pawn.MapHeld?.mapPawns.DeregisterShambler(pawn);
base.PreRemoved();
}
public override void PostRemoved()
{
base.PostRemoved();
if (pawn.Dead) return;
if (IsRising) CancelRising();
if (pawn.IsMutant)
{
if (pawn.mutant.HasTurned) pawn.mutant.Revert();
else pawn.mutant = null;
}
}
public override IEnumerable<Gizmo> GetGizmos()
{
foreach (Gizmo gizmo in base.GetGizmos())
{
yield return gizmo;
}
if (DebugSettings.ShowDevGizmos && pawn.Downed && !IsRising)
{
var command_Action = new Command_Action
{
defaultLabel = "Self Raise",
action = () => StartRising()
};
yield return command_Action;
}
}
public override string GetInspectString()
{
if (ShouldSelfRaise()) return "ShamblerRegenerating".Translate();
if (IsRising) return "ShamblerRising".Translate();
if (pawn.CurJobDef == JobDefOf.Wait_Wander || pawn.CurJobDef == JobDefOf.Wait_MaintainPosture) return "ShamblerStanding".Translate();
if (pawn.CurJobDef == JobDefOf.GotoWander || pawn.CurJobDef == JobDefOf.Goto) return "ShamblerShuffling".Translate();
return "";
}
public override void ExposeData()
{
base.ExposeData();
Scribe_Values.Look(ref alertTimer, "alertTimer", 0f);
Scribe_Values.Look(ref nextTargetCheckTick, "nextTargetCheckTick", 0);
Scribe_References.Look(ref alertedTarget, "alertedTarget");
Scribe_Values.Look(ref resurrectTimer, "resurrectTimer", 0f);
Scribe_Values.Look(ref selfRaiseTimer, "selfRaiseTimer", 0f);
Scribe_Values.Look(ref headRotation, "headRotation", 0f);
}
}
}

View File

@@ -36,6 +36,12 @@ namespace ArachnaeSwarm
return;
}
// 检查严重性是否达到XML中配置的阈值
if (this.Severity < comp.Props.triggerSeverity)
{
return;
}
try
{
if (pawn.Corpse == null || pawn.Corpse.Destroyed)
@@ -46,9 +52,10 @@ namespace ArachnaeSwarm
Map map = pawn.Corpse.Map;
IntVec3 position = pawn.Corpse.Position;
if (!MutantUtility.CanResurrectAsShambler(pawn.Corpse))
// 使用我们自己的、更安全的检查方法
if (!NecroticTransformationUtility.CanResurrect(pawn.Corpse))
{
Log.Warning($"[NecroticVirus] Cannot resurrect {pawn.LabelShort} as a shambler-like creature.");
Log.Warning($"[NecroticVirus] Pawn {pawn.LabelShort} does not meet conditions for resurrection.");
return;
}

View File

@@ -5,9 +5,41 @@ namespace ArachnaeSwarm
{
public static class NecroticTransformationUtility
{
/// <summary>
/// 检查一个尸体是否可以被我们的逻辑转化为变异体。
/// 这是对原版 MutantUtility.CanResurrectAsShambler 的复制,但移除了对 canBecomeShambler 的检查。
/// </summary>
public static bool CanResurrect(Corpse corpse, bool ignoreIndoors = false)
{
if (corpse?.InnerPawn == null) return false;
if (!corpse.InnerPawn.RaceProps.IsFlesh) return false;
// 我们移除了对 corpse.InnerPawn.RaceProps.canBecomeShambler 的检查
if (corpse.InnerPawn.IsMutant) return false;
if (corpse is UnnaturalCorpse) return false;
Room room = corpse.PositionHeld.GetRoom(corpse.MapHeld);
if (room != null && !ignoreIndoors && corpse.PositionHeld.Roofed(corpse.MapHeld) && (room.ProperRoom || room.IsDoorway))
{
return false;
}
if (!Find.Storyteller.difficulty.childShamblersAllowed && !corpse.InnerPawn.ageTracker.Adult)
{
return false;
}
Hediff_DeathRefusal firstHediff = corpse.InnerPawn.health.hediffSet.GetFirstHediff<Hediff_DeathRefusal>();
if (firstHediff != null && (firstHediff.InProgress || firstHediff.UsesLeft > 0))
{
return false;
}
return true;
}
/// <summary>
/// 将一个Pawn复活为指定的自定义变异体.
/// 这个方法是模仿原版 MutantUtility.ResurrectAsShambler,
/// 这个方法是模仿原版 MutantUtility.ResurrectAsShambler,
/// 但允许传入一个自定义的 MutantDef.
/// </summary>
public static void ResurrectAsCustomMutant(Pawn pawn, MutantDef mutantDef, Faction faction = null, int lifespanTicks = -1)