diff --git a/1.6/1.6/Assemblies/ArachnaeSwarm.dll b/1.6/1.6/Assemblies/ArachnaeSwarm.dll index 46d05d1..8d1c09f 100644 Binary files a/1.6/1.6/Assemblies/ArachnaeSwarm.dll and b/1.6/1.6/Assemblies/ArachnaeSwarm.dll differ diff --git a/1.6/1.6/Defs/DamageDefs/ARA_Damages.xml b/1.6/1.6/Defs/DamageDefs/ARA_Damages.xml index 5868f5b..2fb47e4 100644 --- a/1.6/1.6/Defs/DamageDefs/ARA_Damages.xml +++ b/1.6/1.6/Defs/DamageDefs/ARA_Damages.xml @@ -54,6 +54,16 @@ + + ARA_MimicNematodeBite + +
  • + ARA_MimicNematode + 0.001 +
  • +
    +
    + ARA_ReinforceGas diff --git a/1.6/1.6/Defs/HediffDefs/ARA_Hediffs_Damage.xml b/1.6/1.6/Defs/HediffDefs/ARA_Hediffs_Damage.xml index 37399df..d23ca6d 100644 --- a/1.6/1.6/Defs/HediffDefs/ARA_Hediffs_Damage.xml +++ b/1.6/1.6/Defs/HediffDefs/ARA_Hediffs_Damage.xml @@ -196,4 +196,6 @@
  • + + \ No newline at end of file diff --git a/1.6/1.6/Defs/HediffDefs/ARA_Hediffs_Mutants_Configurable.xml b/1.6/1.6/Defs/HediffDefs/ARA_Hediffs_Mutants_Configurable.xml new file mode 100644 index 0000000..6540a37 --- /dev/null +++ b/1.6/1.6/Defs/HediffDefs/ARA_Hediffs_Mutants_Configurable.xml @@ -0,0 +1,266 @@ + + + + + ARA_MimicNematode + + 被阿拉克涅拟线种虫族寄生了,如果不加以干预,拟线虫最终会杀死宿主并将其转化为没有意识的寄生体。拟线虫会通过寄生体的攻击行为感染其他躯体。 + + + ArachnaeSwarm.Hediff_NecroticVirus + (0.6, 0.4, 0.8) + + true + 1.0 + 1.0 + 0.001 + 0.001 + false + + +
  • + + false + 5 +
  • +
  • + + 0.5 + 1 + 0.5 + false + +
  • + Talking + 0 +
  • + + +
  • + + 0.7 + 0.5 + 0 + true + true + +
  • + Talking + 0 +
  • +
  • + Consciousness + 0.7 +
  • + + 50 + +
  • + + 0.9 + 0.5 + 2.0 + true + true + +
  • + Talking + 0 +
  • +
  • + Consciousness + 0.1 +
  • + + 50 + + + true + + +
  • + 0.3 +
  • +
  • + -1 + 24 +
  • +
  • + 1800000~2400000 + true +
  • +
  • + + + ARA_MimicNematodeShambler + 0.7 +
  • +
  • + + + +
  • + + ARA_MimicNematode + + + true + 0.05 + ARA_MimicNematodeBite +
  • + + + + + + ARA_CureBloodRot + + 通过多种药物联合靶向治疗清除患者体内的阿拉克涅拟线种虫族感染。 + Recipe_RemoveHediff + 清除拟线虫感染. + 2000 + true + false + false + ARA_MimicNematode + {0} 成功清除了 {1} 体内的拟线虫感染. + + 5 + + +
  • + + +
  • MedicineUltratech
  • + + + 10 + +
    + + +
  • Medicine
  • +
    +
    +
    + + + + ARA_MimicNematodeShambler + + 这具尸体被一种阿拉克涅拟线虫所寄生并重新激活。被寄生的生物行动迟缓、没有心智,只会无情地攻击任何活物。在活动几天后,寄生体将因宿主新陈代谢衰竭而死亡。被捕获并固定后,寄生体无法移动,因此其生命活动会暂停。 + ArachnaeSwarm.Hediff_ConfigurableMutant + (0.6, 0.4, 0.8) + false + false + True + +
  • + 0 + 0 + + 1.5 + 0 + + + -60 + 30 + 25 + 1 + + +
  • Talking0
  • +
  • Consciousness0.7
  • + + 200 + +
    + + +
  • + HediffComp_DisappearsAndKills_Shambler + 1800000~2400000 + true +
  • +
  • +
  • + + +
  • + + ARA_Rising + ARA_MimicNematodeShamblerShamblerCorpse + + + 0 + 0 +
  • +
    + +
  • + ARA_Swarm_claws + PawnRenderNodeWorker_AttachmentBody + +
  • ArachnaeSwarm/Things/ARA_HiveNode/Addons/ArachnaeNode_Race_Addons_Fighter_Claw
  • + + 60 + 1 + + + true + + + +
    +
    + + + + + + + ARA_Rising + + 阿拉克涅拟线虫寄生赋予的强大再生能力正在复苏这具身体。 + false + false + (0.6, 0.4, 0.8) + +
  • + 0 + +
  • Moving0
  • +
  • Manipulation0
  • +
  • Talking0
  • +
  • Consciousness0.1
  • + + +
    +
    + + + ARA_MimicNematodeShamblerShamblerCorpse + + 这具身体刚刚被阿拉克涅拟线种寄生"复活"过。 + true + (0.6, 0.4, 0.8) + +
  • + Shambler wounds + PawnRenderNodeWorker_OverlayShambler + Body + 20 + HumanlikeOnly +
  • +
  • + Shambler wounds + PawnRenderNode_AnimalPart + PawnRenderNodeWorker_OverlayShambler + Body + 20 + NonHumanlikeOnly +
  • +
    +
    + + \ No newline at end of file diff --git a/1.6/1.6/Defs/ThingDef_Races/ARA_RaceMutant.xml b/1.6/1.6/Defs/ThingDef_Races/ARA_RaceMutant.xml new file mode 100644 index 0000000..ca57ffe --- /dev/null +++ b/1.6/1.6/Defs/ThingDef_Races/ARA_RaceMutant.xml @@ -0,0 +1,146 @@ + + + + + ARA_MimicNematodeShambler + + 阿拉克涅拟线种是虫群中最神秘的分支之一。它们与原虫种同源,但在进化之路上并未发展出高级智慧,而是走向了一条截然不同的道路:寄生。拟线种是无法脱离宿主独立存活的寄生虫。一旦寄生于生物体内,它们会长期潜伏,最终逐步接管宿主的神经与代谢系统,将其变为受其操控的行尸走肉——“寄生体”。得益于超凡的细胞复制与再生能力,拟线种能够模仿并替代宿主的细胞结构,这使得寄生体拥有了惊人的自我修复能力。然而,这种寄生关系也极具侵略性:拟线种的虫卵遍布寄生体体表,任何被寄生体攻击的生物都有可能被感染,最终沦为新的寄生体。尽管拟线虫将寄生体的新陈代谢维持在极低水平,但寄生体本身并不会主动觅食。因此,当能量耗尽后,它们终将迎来“死亡”。 + ARA_MimicNematodeShambler + Shambler + ShamblerConstant + true + 拟线种寄生体 + false + true + Filth_DarkBlood + Filth_DarkBloodSmear + false + true + true + Entities + ShamblerSway + false + false + false + true + false + true + false + true + false + false + true + 0.25 + (0.3, 0.3, 0.0, 1.0) + 0 + Basic + + + 0.25 + Pawn_Shambler_Call + Pawn_Shambler_Attack + Pawn_Shambler_Wounded + Pawn_Shambler_Killed + +
  • ARA_MimicNematode
  • +
    + + +
  • + Shambler wounds + PawnRenderNodeWorker_OverlayShambler + Body + 20 + HumanlikeOnly +
  • +
  • + Shambler wounds + PawnRenderNode_AnimalPart + PawnRenderNodeWorker_OverlayShambler + Body + 20 + NonHumanlikeOnly +
  • +
    + + +
  • + + +
  • ARA_MimicNematodeShamblerBite
  • + + 8.2 + 2 + Teeth + 1 + Pawn_Melee_HumanBite_Hit + Pawn_Melee_HumanBite_Miss + +
  • + + +
  • Cut
  • + + 15 + 2.5 + LeftHand + + 0.5 + true + +
  • + + +
  • Cut
  • + + 15 + 2.5 + RightHand + + 0.5 + true + +
    +
    + + + ARA_MimicNematodeShamblerBite + + + + ARA_MimicNematodeShamblerBite + ARA_MimicNematodeShamblerBite + + Verb_MeleeAttackDamage + ARA_MimicNematodeBite + + MeleeAttack + Maneuver_Slash_MeleeHit + Maneuver_Slash_MeleeDeflect + Maneuver_Slash_MeleeMiss + Maneuver_Slash_MeleeDodge + + + + ARA_MimicNematodeShambler + false + + + + ARA_MimicNematodeShamblerSwarmer + + 40 + 0.2~0.4 + Poor + 0~100 + 0 + +
  • IndustrialBasic
  • +
  • Neolithic
  • +
    + +
  • EntityAttacked
  • +
    +
    + +
    \ No newline at end of file diff --git a/1.6/1.6/Defs/Thing_Misc/Weapons/ARA_Weapon.xml b/1.6/1.6/Defs/Thing_Misc/Weapons/ARA_Weapon.xml index 1e39d82..0f2eedd 100644 --- a/1.6/1.6/Defs/Thing_Misc/Weapons/ARA_Weapon.xml +++ b/1.6/1.6/Defs/Thing_Misc/Weapons/ARA_Weapon.xml @@ -564,4 +564,68 @@
    + + + ARA_MW_Mimic_Niddle + + 阿拉克涅虫群督虫使用基础近战武装器官,通过多根外露神经束与督虫的辅肢相连。这根毒针中藏有休眠中的阿拉克涅拟线种虫卵,攻击将感染受害者使其最终成为被拟线虫操控的寄生体。 + +
  • ARA_Armed_Organ
  • +
  • ARA_Armed_Organ_Melee
  • +
  • ARA_Armed_Organ_T1
  • +
    + + ArachnaeSwarm/Weapon/ARA_MW_Bone_Sword + Graphic_Single + CutoutComplex + 1 + + 1 + + Animal + + 0 + + + 50 + + + 1000 + 5 + + +
  • + + +
  • Cut
  • + + 15 + 1.3 + 0.50 + +
  • + + +
  • Poke
  • + + 12 + 0.20 + 1.5 + +
  • + ARA_MimicNematodeBite + 4 +
  • +
    + +
    + + + + UnfinishedWeapon + + +
  • RewardStandardQualitySuper
  • +
    +
    \ No newline at end of file diff --git a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj index fc3601e..cd753c7 100644 --- a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj +++ b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj @@ -189,6 +189,8 @@ + + diff --git a/Source/ArachnaeSwarm/HediffComp_NecroticTransformation.cs b/Source/ArachnaeSwarm/HediffComp_NecroticTransformation.cs index ac96aa0..6219903 100644 --- a/Source/ArachnaeSwarm/HediffComp_NecroticTransformation.cs +++ b/Source/ArachnaeSwarm/HediffComp_NecroticTransformation.cs @@ -10,6 +10,9 @@ namespace ArachnaeSwarm { // 在XML中需要指定的MutantDef的defName public MutantDef mutantDef; + + // 在XML中配置触发转变所需的严重性阈值 + public float triggerSeverity = 0.7f; public HediffCompProperties_NecroticTransformation() { diff --git a/Source/ArachnaeSwarm/HediffComp_Symbiosis.cs b/Source/ArachnaeSwarm/HediffComp_Symbiosis.cs new file mode 100644 index 0000000..3b0c5c2 --- /dev/null +++ b/Source/ArachnaeSwarm/HediffComp_Symbiosis.cs @@ -0,0 +1,61 @@ +using Verse; +using RimWorld; + +namespace ArachnaeSwarm +{ + /// + /// XML配置类:定义共生关系 + /// + public class HediffCompProperties_Symbiosis : HediffCompProperties + { + // 实现共存所需的Hediff + public HediffDef requiredHediff; + + // 共存状态下的最大严重性 + public float newMaxSeverity = 0.9f; + + public HediffCompProperties_Symbiosis() + { + compClass = typeof(HediffComp_Symbiosis); + } + } + + /// + /// HediffComp:实现共生逻辑 + /// + public class HediffComp_Symbiosis : HediffComp + { + private HediffCompProperties_Symbiosis Props => (HediffCompProperties_Symbiosis)props; + + // 重写CompPostTick,它会在游戏每一帧,在计算完严重性增量之后,应用增量之前被调用 + public override void CompPostTick(ref float severityAdjustment) + { + base.CompPostTick(ref severityAdjustment); + + // 检查宿主Pawn是否存在,以及配置是否完整 + if (this.Pawn == null || Props.requiredHediff == null) + { + return; + } + + // 检查Pawn是否拥有“共存”所需的Hediff + if (this.Pawn.health.hediffSet.HasHediff(Props.requiredHediff)) + { + // 如果当前严重性已经达到或超过上限 + if (this.parent.Severity >= Props.newMaxSeverity) + { + // 将当前严重性强制拉回到上限 + this.parent.Severity = Props.newMaxSeverity; + // 并且,阻止任何将要发生的严重性增加(将增量设为0) + severityAdjustment = 0; + } + // 如果当前严重性加上即将发生的增量会超过上限 + else if (this.parent.Severity + severityAdjustment > Props.newMaxSeverity) + { + // 重新计算增量,使其恰好达到上限 + severityAdjustment = Props.newMaxSeverity - this.parent.Severity; + } + } + } + } +} \ No newline at end of file diff --git a/Source/ArachnaeSwarm/Hediff_ConfigurableMutant.cs b/Source/ArachnaeSwarm/Hediff_ConfigurableMutant.cs new file mode 100644 index 0000000..5cb0165 --- /dev/null +++ b/Source/ArachnaeSwarm/Hediff_ConfigurableMutant.cs @@ -0,0 +1,373 @@ +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(); + + 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方法 + 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 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); + } + } +} \ No newline at end of file diff --git a/Source/ArachnaeSwarm/Hediff_NecroticVirus_Configurable.cs b/Source/ArachnaeSwarm/Hediff_NecroticVirus_Configurable.cs index 8192b5c..d5970cf 100644 --- a/Source/ArachnaeSwarm/Hediff_NecroticVirus_Configurable.cs +++ b/Source/ArachnaeSwarm/Hediff_NecroticVirus_Configurable.cs @@ -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; } @@ -57,6 +64,9 @@ namespace ArachnaeSwarm // **调用我们自己的工具方法,传入从XML获取的mutantDef** NecroticTransformationUtility.ResurrectAsCustomMutant(pawn, comp.Props.mutantDef, faction); + // **关键修复:在成功转化后,立即移除导致转化的Hediff本身,防止其残留** + pawn.health.RemoveHediff(this); + // 添加转化特效 FleckMaker.ThrowSmoke(position.ToVector3Shifted(), map, 1.5f); FleckMaker.ThrowDustPuff(position.ToVector3Shifted(), map, 1.2f); diff --git a/Source/ArachnaeSwarm/NecroticTransformationUtility.cs b/Source/ArachnaeSwarm/NecroticTransformationUtility.cs index ba24fca..190d4b8 100644 --- a/Source/ArachnaeSwarm/NecroticTransformationUtility.cs +++ b/Source/ArachnaeSwarm/NecroticTransformationUtility.cs @@ -5,9 +5,22 @@ namespace ArachnaeSwarm { public static class NecroticTransformationUtility { + /// + /// 检查一个尸体是否可以被我们的逻辑转化为变异体。 + /// 这是对原版 MutantUtility.CanResurrectAsShambler 的复制,但移除了对 canBecomeShambler 的检查。 + /// + public static bool CanResurrect(Corpse corpse, bool ignoreIndoors = false) + { + // 只保留最核心、最必要的检查,移除所有可能导致不稳定的条件 + if (corpse?.InnerPawn == null) return false; + if (!corpse.InnerPawn.RaceProps.IsFlesh) return false; + + return true; + } + /// /// 将一个Pawn复活为指定的自定义变异体. - /// 这个方法是模仿原版 MutantUtility.ResurrectAsShambler, + /// 这个方法是模仿原版 MutantUtility.ResurrectAsShambler, /// 但允许传入一个自定义的 MutantDef. /// public static void ResurrectAsCustomMutant(Pawn pawn, MutantDef mutantDef, Faction faction = null, int lifespanTicks = -1) @@ -22,13 +35,16 @@ namespace ArachnaeSwarm // 创建并附加Pawn_MutantTracker,使用我们从XML传入的mutantDef pawn.mutant = new Pawn_MutantTracker(pawn, mutantDef, rotStage); - // 添加变异体核心Hediff - Hediff hediff = pawn.health.AddHediff(mutantDef.hediff); + // **调用原版健康再生方法,此方法会读取MutantDef中的配置(如removeAllInjuries)并执行** + MutantUtility.RegenerateHealth(pawn); + + // 添加变异体核心Hediff + Hediff hediff = pawn.health.AddHediff(mutantDef.hediff); - // 如果是蹒跚者类型的Hediff,则调用其上升动画 - if (hediff is Hediff_Shambler shamblerHediff) + // 如果是我们自己的可配置变异体Hediff,则调用其上升动画 + if (hediff is Hediff_ConfigurableMutant configurableMutant) { - shamblerHediff.StartRising(lifespanTicks); + configurableMutant.StartRising(lifespanTicks); } // 设置生命周期 @@ -58,8 +74,10 @@ namespace ArachnaeSwarm pawn.SetFaction(faction); } - // 立即执行转变 - pawn.mutant.Turn(clearLord: true); + // 移除 pawn.mutant.Turn(clearLord: true); + // Turn() 方法应该由 Hediff_Shambler.FinishRising() 在复活过程的最后阶段调用, + // 而不应该由我们在这里手动调用。 + // Hediff_Shambler 的 StartRising() 会启动这个流程。 } } } \ No newline at end of file