This commit is contained in:
2026-02-24 12:02:38 +08:00
parent 1af5f0c1d8
commit 96bc1d4c5a
57 changed files with 6595 additions and 1170 deletions

View File

@@ -0,0 +1,325 @@
// CompMechSelfDestruct.cs
using System.Collections.Generic;
using RimWorld;
using UnityEngine;
using Verse;
using Verse.Sound;
namespace WulaFallenEmpire
{
public class CompMechSelfDestruct : ThingComp
{
public bool wickStarted;
public int wickTicksLeft;
private Thing instigator;
private List<Thing> thingsIgnoredByExplosion;
private Sustainer wickSoundSustainer;
private OverlayHandle? overlayBurningWick;
public CompProperties_MechSelfDestruct Props => (CompProperties_MechSelfDestruct)props;
protected Pawn MechPawn => parent as Pawn;
// 获取当前健康百分比
private float CurrentHealthPercent
{
get
{
if (MechPawn == null || MechPawn.Dead || MechPawn.health == null)
return 0f;
return MechPawn.health.summaryHealth.SummaryHealthPercent;
}
}
// 获取健康阈值(基于百分比)
private float HealthThreshold => Props.healthPercentThreshold;
protected virtual bool CanEverExplode
{
get
{
if (Props.chanceNeverExplode >= 1f)
return false;
if (Props.chanceNeverExplode <= 0f)
return true;
Rand.PushState();
Rand.Seed = parent.thingIDNumber.GetHashCode();
bool result = Rand.Value > Props.chanceNeverExplode;
Rand.PopState();
return result;
}
}
public void AddThingsIgnoredByExplosion(List<Thing> things)
{
if (thingsIgnoredByExplosion == null)
{
thingsIgnoredByExplosion = new List<Thing>();
}
thingsIgnoredByExplosion.AddRange(things);
}
public override void PostExposeData()
{
base.PostExposeData();
Scribe_References.Look(ref instigator, "instigator");
Scribe_Collections.Look(ref thingsIgnoredByExplosion, "thingsIgnoredByExplosion", LookMode.Reference);
Scribe_Values.Look(ref wickStarted, "wickStarted", defaultValue: false);
Scribe_Values.Look(ref wickTicksLeft, "wickTicksLeft", 0);
}
public override void PostSpawnSetup(bool respawningAfterLoad)
{
UpdateOverlays();
}
public override void CompTick()
{
if (MechPawn == null || MechPawn.Dead)
return;
// 检查健康阈值每60ticks检查一次以提高性能
if (Find.TickManager.TicksGame % 60 == 0)
{
CheckHealthThreshold();
}
if (!wickStarted)
return;
// 处理引信
if (wickSoundSustainer == null)
{
StartWickSustainer();
}
else
{
wickSoundSustainer.Maintain();
}
wickTicksLeft--;
if (wickTicksLeft <= 0)
{
Detonate(parent.MapHeld);
}
}
private void CheckHealthThreshold()
{
if (wickStarted || !CanEverExplode || !Props.triggerOnHealthThreshold)
return;
float currentHealthPercent = CurrentHealthPercent;
if (currentHealthPercent <= HealthThreshold && currentHealthPercent > 0f)
{
StartWick();
}
}
private void StartWickSustainer()
{
SoundDefOf.MetalHitImportant.PlayOneShot(new TargetInfo(parent.PositionHeld, parent.MapHeld));
SoundInfo info = SoundInfo.InMap(parent, MaintenanceType.PerTick);
wickSoundSustainer = SoundDefOf.HissSmall.TrySpawnSustainer(info);
}
private void EndWickSustainer()
{
if (wickSoundSustainer != null)
{
wickSoundSustainer.End();
wickSoundSustainer = null;
}
}
private void UpdateOverlays()
{
if (parent.Spawned && Props.drawWick)
{
parent.Map.overlayDrawer.Disable(parent, ref overlayBurningWick);
if (wickStarted)
{
overlayBurningWick = parent.Map.overlayDrawer.Enable(parent, OverlayTypes.BurningWick);
}
}
}
public override void PostDestroy(DestroyMode mode, Map previousMap)
{
if (Props.triggerOnDeath && mode == DestroyMode.KillFinalize)
{
Detonate(previousMap, ignoreUnspawned: true);
}
}
public override void PostDeSpawn(Map map, DestroyMode mode = DestroyMode.Vanish)
{
base.PostDeSpawn(map, mode);
EndWickSustainer();
StopWick();
}
public override void PostPreApplyDamage(ref DamageInfo dinfo, out bool absorbed)
{
absorbed = false;
if (!CanEverExplode || MechPawn == null || MechPawn.Dead)
return;
// 特定伤害类型触发自毁
if (!wickStarted && Props.startWickOnDamageTaken != null &&
Props.startWickOnDamageTaken.Contains(dinfo.Def))
{
StartWick(dinfo.Instigator);
}
}
public override void PostPostApplyDamage(DamageInfo dinfo, float totalDamageDealt)
{
if (!CanEverExplode || MechPawn == null || MechPawn.Dead || wickStarted)
return;
// 内部伤害触发自毁
if (Props.startWickOnInternalDamageTaken != null &&
Props.startWickOnInternalDamageTaken.Contains(dinfo.Def))
{
StartWick(dinfo.Instigator);
}
// 特定伤害类型触发自毁
if (!wickStarted && Props.startWickOnDamageTaken != null &&
Props.startWickOnDamageTaken.Contains(dinfo.Def))
{
StartWick(dinfo.Instigator);
}
// 检查是否需要停止引信(如眩晕)
if (wickStarted && dinfo.Def == DamageDefOf.Stun)
{
StopWick();
}
}
public void StartWick(Thing instigator = null)
{
if (!wickStarted && ExplosiveRadius() > 0f && CanEverExplode)
{
this.instigator = instigator;
wickStarted = true;
wickTicksLeft = Props.wickTicks.RandomInRange;
StartWickSustainer();
GenExplosion.NotifyNearbyPawnsOfDangerousExplosive(parent, Props.explosiveDamageType, null, instigator);
UpdateOverlays();
}
}
public void StopWick()
{
wickStarted = false;
instigator = null;
UpdateOverlays();
EndWickSustainer();
}
public float ExplosiveRadius()
{
float radius = Props.explosiveRadius;
// 根据堆叠数量扩展
if (parent.stackCount > 1 && Props.explosiveExpandPerStackcount > 0f)
{
radius += Mathf.Sqrt((parent.stackCount - 1) * Props.explosiveExpandPerStackcount);
}
// 根据燃料扩展
if (Props.explosiveExpandPerFuel > 0f && parent.GetComp<CompRefuelable>() != null)
{
radius += Mathf.Sqrt(parent.GetComp<CompRefuelable>().Fuel * Props.explosiveExpandPerFuel);
}
return radius;
}
protected void Detonate(Map map, bool ignoreUnspawned = false)
{
if (!ignoreUnspawned && !parent.SpawnedOrAnyParentSpawned)
return;
float radius = ExplosiveRadius();
if (radius <= 0f)
return;
Thing responsible = (instigator == null || (instigator.HostileTo(parent.Faction) && parent.Faction != Faction.OfPlayer))
? parent
: instigator;
// 消耗燃料
if (Props.explosiveExpandPerFuel > 0f && parent.GetComp<CompRefuelable>() != null)
{
parent.GetComp<CompRefuelable>().ConsumeFuel(parent.GetComp<CompRefuelable>().Fuel);
}
EndWickSustainer();
wickStarted = false;
UpdateOverlays();
if (map == null)
{
Log.Warning("Tried to detonate CompMechSelfDestruct in a null map.");
return;
}
// 播放爆炸效果
if (Props.explosionEffect != null)
{
Effecter effecter = Props.explosionEffect.Spawn();
effecter.Trigger(new TargetInfo(parent.PositionHeld, map), new TargetInfo(parent.PositionHeld, map));
effecter.Cleanup();
}
// 执行爆炸
GenExplosion.DoExplosion(
parent.PositionHeld,
map,
radius,
Props.explosiveDamageType,
responsible,
Props.damageAmountBase,
Props.armorPenetrationBase,
Props.explosionSound,
null, null, null,
Props.postExplosionSpawnThingDef,
Props.postExplosionSpawnChance,
Props.postExplosionSpawnThingCount,
Props.postExplosionGasType,
Props.postExplosionGasRadiusOverride,
Props.postExplosionGasAmount,
Props.applyDamageToExplosionCellsNeighbors,
Props.preExplosionSpawnThingDef,
Props.preExplosionSpawnChance,
Props.preExplosionSpawnThingCount,
Props.chanceToStartFire,
Props.damageFalloff,
null,
thingsIgnoredByExplosion,
null,
Props.doVisualEffects,
Props.propagationSpeed,
0f,
Props.doSoundEffects,
null, 1f, null, null,
Props.postExplosionSpawnSingleThingDef,
Props.preExplosionSpawnSingleThingDef
);
if (!MechPawn.Dead)
{
MechPawn.Kill(null, null);
}
}
}
}

View File

@@ -0,0 +1,88 @@
// CompProperties_MechSelfDestruct.cs
using System.Collections.Generic;
using RimWorld;
using Verse;
namespace WulaFallenEmpire
{
public class CompProperties_MechSelfDestruct : CompProperties
{
// 爆炸相关属性
public float explosiveRadius = 3.9f;
public DamageDef explosiveDamageType;
public int damageAmountBase = 60;
public float armorPenetrationBase = -1f;
public ThingDef postExplosionSpawnThingDef;
public float postExplosionSpawnChance;
public int postExplosionSpawnThingCount = 1;
public bool applyDamageToExplosionCellsNeighbors;
public ThingDef preExplosionSpawnThingDef;
public float preExplosionSpawnChance;
public int preExplosionSpawnThingCount = 1;
public float chanceToStartFire;
public bool damageFalloff;
public GasType? postExplosionGasType;
public float? postExplosionGasRadiusOverride;
public int postExplosionGasAmount = 255;
public bool doVisualEffects = true;
public bool doSoundEffects = true;
public float propagationSpeed = 1f;
public ThingDef postExplosionSpawnSingleThingDef;
public ThingDef preExplosionSpawnSingleThingDef;
public float explosiveExpandPerStackcount;
public float explosiveExpandPerFuel;
public EffecterDef explosionEffect;
public SoundDef explosionSound;
// 自毁相关属性
public float healthPercentThreshold = 0.2f; // 健康百分比阈值,低于此值启动自毁
public IntRange wickTicks = new IntRange(140, 150); // 自毁延迟ticks范围
public bool drawWick = true; // 是否显示引信
public string extraInspectStringKey; // 额外检查字符串键
public List<WickMessage> wickMessages; // 引信消息
// 自毁触发条件
public bool triggerOnDeath = true; // 死亡时触发
public bool triggerOnHealthThreshold = true; // 健康阈值时触发
public List<DamageDef> startWickOnDamageTaken; // 特定伤害类型触发自毁
public List<DamageDef> startWickOnInternalDamageTaken; // 内部伤害触发自毁
public float chanceNeverExplode = 0f; // 永不爆炸的几率
public float destroyThingOnExplosionSize = 9999f; // 爆炸时摧毁物体的尺寸阈值
public DamageDef requiredDamageTypeToExplode; // 触发爆炸所需的伤害类型
public CompProperties_MechSelfDestruct()
{
compClass = typeof(CompMechSelfDestruct);
}
public override void ResolveReferences(ThingDef parentDef)
{
base.ResolveReferences(parentDef);
if (explosiveDamageType == null)
{
explosiveDamageType = DamageDefOf.Bomb;
}
}
public override IEnumerable<string> ConfigErrors(ThingDef parentDef)
{
foreach (string item in base.ConfigErrors(parentDef))
{
yield return item;
}
if (parentDef.tickerType != TickerType.Normal)
{
yield return "CompMechSelfDestruct requires Normal ticker type";
}
}
}
// 引信消息类
public class WickMessage
{
public int ticksLeft;
public string wickMessagekey;
public MessageTypeDef messageType;
}
}