整理
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
using Verse;
|
||||
using RimWorld;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
/// <summary>
|
||||
/// XML配置类,用于在HediffDef的comp中指定转化后的MutantDef
|
||||
/// </summary>
|
||||
public class HediffCompProperties_NecroticTransformation : HediffCompProperties
|
||||
{
|
||||
// 在XML中需要指定的MutantDef的defName
|
||||
public MutantDef mutantDef;
|
||||
|
||||
// 在XML中配置触发转变所需的严重性阈值
|
||||
public float triggerSeverity = 0.7f;
|
||||
|
||||
public HediffCompProperties_NecroticTransformation()
|
||||
{
|
||||
compClass = typeof(HediffComp_NecroticTransformation);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// HediffComp,用于在运行时存储和提供由XML定义的MutantDef
|
||||
/// </summary>
|
||||
public class HediffComp_NecroticTransformation : HediffComp
|
||||
{
|
||||
// 属性,用于方便地从Comp中获取配置
|
||||
public HediffCompProperties_NecroticTransformation Props => (HediffCompProperties_NecroticTransformation)props;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using Verse;
|
||||
using RimWorld;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
/// <summary>
|
||||
/// XML配置类:定义共生关系
|
||||
/// </summary>
|
||||
public class HediffCompProperties_Symbiosis : HediffCompProperties
|
||||
{
|
||||
// 实现共存所需的Hediff
|
||||
public HediffDef requiredHediff;
|
||||
|
||||
// 共存状态下的最大严重性
|
||||
public float newMaxSeverity = 0.9f;
|
||||
|
||||
public HediffCompProperties_Symbiosis()
|
||||
{
|
||||
compClass = typeof(HediffComp_Symbiosis);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// HediffComp:实现共生逻辑
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<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方法
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
using UnityEngine;
|
||||
using System.Linq;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
public class Hediff_NecroticVirus : HediffWithComps
|
||||
{
|
||||
private const int EffectInterval = 600;
|
||||
|
||||
// 用于存储攻击者派系的字段
|
||||
private Faction casterFaction;
|
||||
|
||||
// 属性来获取我们的自定义Comp
|
||||
private HediffComp_NecroticTransformation PropsComp => this.TryGetComp<HediffComp_NecroticTransformation>();
|
||||
|
||||
public override void PostAdd(DamageInfo? dinfo)
|
||||
{
|
||||
base.PostAdd(dinfo);
|
||||
if (dinfo.HasValue && dinfo.Value.Instigator != null)
|
||||
{
|
||||
this.casterFaction = dinfo.Value.Instigator.Faction;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Notify_PawnDied(DamageInfo? dinfo, Hediff culprit = null)
|
||||
{
|
||||
base.Notify_PawnDied(dinfo, culprit);
|
||||
TransformToMutant();
|
||||
}
|
||||
|
||||
public override void PostTick()
|
||||
{
|
||||
base.PostTick();
|
||||
if (pawn.IsHashIntervalTick(EffectInterval) && pawn.Spawned)
|
||||
{
|
||||
ShowVirusEffects();
|
||||
}
|
||||
}
|
||||
|
||||
private void TransformToMutant()
|
||||
{
|
||||
var comp = PropsComp;
|
||||
if (comp == null || comp.Props.mutantDef == null)
|
||||
{
|
||||
Log.Error($"[NecroticVirus] HediffComp_NecroticTransformation or its mutantDef is not configured in XML for {this.def.defName}.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查严重性是否达到XML中配置的阈值
|
||||
if (this.Severity < comp.Props.triggerSeverity)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (pawn.Corpse == null || pawn.Corpse.Destroyed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Map map = pawn.Corpse.Map;
|
||||
IntVec3 position = pawn.Corpse.Position;
|
||||
|
||||
// 使用我们自己的、更安全的检查方法
|
||||
if (!NecroticTransformationUtility.CanResurrect(pawn.Corpse))
|
||||
{
|
||||
Log.Warning($"[NecroticVirus] Pawn {pawn.LabelShort} does not meet conditions for resurrection.");
|
||||
return;
|
||||
}
|
||||
|
||||
// **优先使用攻击者的派系,如果没有,则执行备用逻辑**
|
||||
Faction faction = this.casterFaction ?? GetHostileFaction();
|
||||
|
||||
// **调用我们自己的工具方法,传入从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);
|
||||
|
||||
// 发送转化消息
|
||||
if (PawnUtility.ShouldSendNotificationAbout(pawn))
|
||||
{
|
||||
Messages.Message(
|
||||
"NecroticVirus_TransformationMessage".Translate(pawn.LabelShortCap, pawn.LabelShortCap),
|
||||
new LookTargets(position, map),
|
||||
MessageTypeDefOf.NegativeEvent
|
||||
);
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Log.Error($"[NecroticVirus] Error during transformation: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
private Faction GetHostileFaction()
|
||||
{
|
||||
if (pawn.Faction != null && pawn.Faction.HostileTo(Faction.OfPlayer))
|
||||
{
|
||||
return pawn.Faction;
|
||||
}
|
||||
|
||||
Faction entitiesFaction = Find.FactionManager.AllFactions.FirstOrDefault(f => f.def.defName == "Entities");
|
||||
if (entitiesFaction != null && !entitiesFaction.defeated)
|
||||
{
|
||||
return entitiesFaction;
|
||||
}
|
||||
|
||||
return Find.FactionManager.RandomEnemyFaction(allowNonHumanlike: true, allowHidden: false, allowDefeated: false);
|
||||
}
|
||||
|
||||
private void ShowVirusEffects()
|
||||
{
|
||||
if (pawn == null || !pawn.Spawned) return;
|
||||
|
||||
Vector3 pos = pawn.DrawPos;
|
||||
Map map = pawn.Map;
|
||||
float sizeMultiplier = 1f;
|
||||
if (CurStageIndex == 1) sizeMultiplier = 1.2f;
|
||||
if (CurStageIndex == 2) sizeMultiplier = 1.5f;
|
||||
|
||||
FleckMaker.ThrowMicroSparks(pos, map);
|
||||
FleckMaker.ThrowDustPuff(pos, map, 0.8f * sizeMultiplier);
|
||||
}
|
||||
|
||||
public override string TipStringExtra
|
||||
{
|
||||
get
|
||||
{
|
||||
return base.TipStringExtra + "\n" + "NecroticVirus_EffectTip".Translate();
|
||||
}
|
||||
}
|
||||
|
||||
public override void ExposeData()
|
||||
{
|
||||
base.ExposeData();
|
||||
Scribe_References.Look(ref this.casterFaction, "casterFaction");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
|
||||
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;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将一个Pawn复活为指定的自定义变异体.
|
||||
/// 这个方法是模仿原版 MutantUtility.ResurrectAsShambler,
|
||||
/// 但允许传入一个自定义的 MutantDef.
|
||||
/// </summary>
|
||||
public static void ResurrectAsCustomMutant(Pawn pawn, MutantDef mutantDef, Faction faction = null, int lifespanTicks = -1)
|
||||
{
|
||||
if (pawn?.Corpse == null || mutantDef == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RotStage rotStage = pawn.Corpse.GetRotStage();
|
||||
|
||||
// 创建并附加Pawn_MutantTracker,使用我们从XML传入的mutantDef
|
||||
pawn.mutant = new Pawn_MutantTracker(pawn, mutantDef, rotStage);
|
||||
|
||||
// **调用原版健康再生方法,此方法会读取MutantDef中的配置(如removeAllInjuries)并执行**
|
||||
MutantUtility.RegenerateHealth(pawn);
|
||||
|
||||
// 添加变异体核心Hediff
|
||||
Hediff hediff = pawn.health.AddHediff(mutantDef.hediff);
|
||||
|
||||
// 如果是我们自己的可配置变异体Hediff,则调用其上升动画
|
||||
if (hediff is Hediff_ConfigurableMutant configurableMutant)
|
||||
{
|
||||
configurableMutant.StartRising(lifespanTicks);
|
||||
}
|
||||
|
||||
// 设置生命周期
|
||||
var disappearsComp = hediff.TryGetComp<HediffComp_DisappearsAndKills>();
|
||||
if (disappearsComp != null)
|
||||
{
|
||||
if (lifespanTicks > 0)
|
||||
{
|
||||
disappearsComp.disappearsAfterTicks = lifespanTicks;
|
||||
disappearsComp.ticksToDisappear = lifespanTicks;
|
||||
}
|
||||
else
|
||||
{
|
||||
disappearsComp.disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有提供派系,则尝试使用变异体定义的默认派系
|
||||
if (faction == null && mutantDef.defaultFaction != null)
|
||||
{
|
||||
faction = Find.FactionManager.FirstFactionOfDef(mutantDef.defaultFaction);
|
||||
}
|
||||
|
||||
// 设置派系
|
||||
if (faction != null && pawn.Faction != faction)
|
||||
{
|
||||
pawn.SetFaction(faction);
|
||||
}
|
||||
|
||||
// 移除 pawn.mutant.Turn(clearLord: true);
|
||||
// Turn() 方法应该由 Hediff_Shambler.FinishRising() 在复活过程的最后阶段调用,
|
||||
// 而不应该由我们在这里手动调用。
|
||||
// Hediff_Shambler 的 StartRising() 会启动这个流程。
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user