This commit is contained in:
Tourswen
2025-11-26 21:37:25 +08:00
parent 21cb3f4ecb
commit c91c222801
24 changed files with 536 additions and 471 deletions

View File

@@ -1,107 +0,0 @@
using RimWorld;
using Verse;
using UnityEngine;
namespace WulaFallenEmpire
{
public class CompDamageReceiver : ThingComp
{
private CompProperties_DamageReceiver Props => (CompProperties_DamageReceiver)props;
private float currentDamage;
private int lastDamageTick;
public float CurrentDamage => currentDamage;
public float MaxDamageCapacity => Props.maxDamageCapacity;
public float DamageRatio => currentDamage / Props.maxDamageCapacity;
public override void PostExposeData()
{
base.PostExposeData();
Scribe_Values.Look(ref currentDamage, "currentDamage", 0f);
Scribe_Values.Look(ref lastDamageTick, "lastDamageTick", 0);
}
public override void CompTick()
{
base.CompTick();
// 定期衰减伤害
if (Find.TickManager.TicksGame % Props.damageDecayInterval == 0 && currentDamage > 0)
{
currentDamage = Mathf.Max(0f, currentDamage - Props.damageDecayRate);
// 如果伤害为0重置最后伤害时间
if (currentDamage <= 0f)
{
lastDamageTick = 0;
}
}
}
/// <summary>
/// 接收伤害
/// </summary>
public bool ReceiveDamage(float damageAmount, Pawn sourcePawn = null)
{
float oldDamage = currentDamage;
currentDamage += damageAmount;
lastDamageTick = Find.TickManager.TicksGame;
// 检查是否超过容量
if (currentDamage >= Props.maxDamageCapacity)
{
if (Props.canBeDestroyedByDamage)
{
// 摧毁建筑
parent.Destroy(DestroyMode.Vanish);
}
else
{
// 只是达到上限,不再接收更多伤害
currentDamage = Props.maxDamageCapacity;
}
return false; // 无法接收更多伤害
}
// 触发效果
OnDamageReceived(damageAmount, sourcePawn);
return true;
}
private void OnDamageReceived(float damageAmount, Pawn sourcePawn)
{
// 记录日志
Log.Message($"[DamageReceiver] {parent.Label} 接收 {damageAmount} 点伤害,当前伤害: {currentDamage}/{Props.maxDamageCapacity}");
}
public override void PostDraw()
{
base.PostDraw();
// 绘制伤害条
if (Props.showDamageBar && currentDamage > 0f)
{
Vector3 drawPos = parent.DrawPos;
drawPos.y += 0.5f; // 在建筑上方显示
GenDraw.DrawFillableBar(new GenDraw.FillableBarRequest
{
center = drawPos,
size = new Vector2(1f, 0.15f),
fillPercent = DamageRatio,
filledMat = SolidColorMaterials.SimpleSolidColorMaterial(Color.red),
unfilledMat = SolidColorMaterials.SimpleSolidColorMaterial(Color.gray),
margin = 0.1f,
rotation = Rot4.North
});
}
}
// 获取接收器状态
public string GetStatusString()
{
return $"伤害吸收: {currentDamage:F0}/{Props.maxDamageCapacity:F0} ({DamageRatio * 100:F1}%)";
}
}
}

View File

@@ -1,123 +0,0 @@
using RimWorld;
using Verse;
using System.Collections.Generic;
using Verse.Sound;
namespace WulaFallenEmpire
{
public class CompDamageTransfer : ThingComp
{
private CompProperties_DamageTransfer Props => (CompProperties_DamageTransfer)props;
private Pawn Pawn => (Pawn)parent;
public override void PostPostApplyDamage(DamageInfo dinfo, float totalDamageDealt)
{
base.PostPostApplyDamage(dinfo, totalDamageDealt);
// 检查是否应该转移伤害
if (ShouldTransferDamage(dinfo, totalDamageDealt))
{
TryTransferDamage(dinfo, totalDamageDealt);
}
}
private bool ShouldTransferDamage(DamageInfo dinfo, float totalDamageDealt)
{
if (parent == null || !parent.Spawned)
return false;
// 检查生命值阈值
if (Pawn.health != null)
{
float healthRatio = Pawn.health.summaryHealth.SummaryHealthPercent;
if (healthRatio < Props.healthThreshold.min || healthRatio > Props.healthThreshold.max)
return false;
}
// 检查伤害类型
if (!Props.transferAllDamageTypes)
{
// 这里可以添加特定伤害类型检查
// 例如:只转移物理伤害,不转移火焰伤害等
}
return true;
}
private void TryTransferDamage(DamageInfo dinfo, float totalDamageDealt)
{
// 计算转移的伤害量
float transferDamage = totalDamageDealt * Props.damageTransferRatio;
// 寻找可用的伤害接收器
CompDamageReceiver receiver = FindAvailableDamageReceiver();
if (receiver != null)
{
// 执行伤害转移
if (receiver.ReceiveDamage(transferDamage, Pawn))
{
OnDamageTransferred(dinfo, transferDamage, receiver);
// 记录日志
Log.Message($"[DamageTransfer] {Pawn.LabelShort} 将 {transferDamage} 点伤害转移至 {receiver.parent.Label}");
}
}
}
private CompDamageReceiver FindAvailableDamageReceiver()
{
if (parent?.Map == null)
return null;
var map = parent.Map;
var faction = parent.Faction;
// 搜索范围内的同派系建筑
foreach (var thing in GenRadial.RadialDistinctThingsAround(parent.Position, map, Props.maxTransferRange, true))
{
if (thing is Building building &&
building.Faction == faction &&
building != parent)
{
var receiver = building.TryGetComp<CompDamageReceiver>();
if (receiver != null && receiver.CurrentDamage < receiver.MaxDamageCapacity)
{
// 检查视线(如果需要)
if (Props.requireLineOfSight)
{
if (!GenSight.LineOfSight(parent.Position, building.Position, map))
continue;
}
return receiver;
}
}
}
return null;
}
private void OnDamageTransferred(DamageInfo dinfo, float transferDamage, CompDamageReceiver receiver)
{
// 创建转移效果
if (Props.transferEffecter != null)
{
Effecter effect = Props.transferEffecter.Spawn();
effect.Trigger(new TargetInfo(parent.Position, parent.Map), new TargetInfo(receiver.parent.Position, parent.Map));
effect.Cleanup();
}
// 播放音效
if (Props.transferSound != null)
{
Props.transferSound.PlayOneShot(new TargetInfo(parent.Position, parent.Map));
}
}
// 获取组件状态
public string GetStatusString()
{
return $"伤害转移: {Props.damageTransferRatio * 100}% (范围: {Props.maxTransferRange})";
}
}
}

View File

@@ -1,19 +0,0 @@
using RimWorld;
using Verse;
namespace WulaFallenEmpire
{
public class CompProperties_DamageReceiver : CompProperties
{
public float maxDamageCapacity = 1000f; // 最大伤害容量
public float damageDecayRate = 5f; // 每 tick 衰减的伤害量
public float damageDecayInterval = 60f; // 伤害衰减间隔ticks
public bool showDamageBar = true; // 是否显示伤害条
public bool canBeDestroyedByDamage = true; // 是否可以被伤害摧毁
public CompProperties_DamageReceiver()
{
compClass = typeof(CompDamageReceiver);
}
}
}

View File

@@ -1,23 +0,0 @@
using RimWorld;
using Verse;
namespace WulaFallenEmpire
{
public class CompProperties_DamageTransfer : CompProperties
{
public float damageTransferRatio = 0.8f; // 伤害转移比例 (80%)
public float maxTransferRange = 30f; // 最大转移范围
public bool requireLineOfSight = false; // 是否需要视线
public bool transferAllDamageTypes = true; // 是否转移所有伤害类型
public FloatRange healthThreshold = new FloatRange(0f, 1f); // 生命值阈值(低于此值才触发)
// 效果设置
public EffecterDef transferEffecter;
public SoundDef transferSound;
public CompProperties_DamageTransfer()
{
compClass = typeof(CompDamageTransfer);
}
}
}

View File

@@ -1,54 +0,0 @@
using HarmonyLib;
using RimWorld;
using Verse;
namespace WulaFallenEmpire
{
[HarmonyPatch(typeof(Pawn), "PostApplyDamage")]
public static class Patch_Pawn_PostApplyDamage
{
[HarmonyPostfix]
public static void Postfix(Pawn __instance, DamageInfo dinfo, float totalDamageDealt)
{
// 检查Pawn是否有伤害转移组件
var transferComp = __instance.TryGetComp<CompDamageTransfer>();
if (transferComp != null)
{
// 组件会在PostPostApplyDamage中自动处理
// 这里主要用于调试和日志记录
Log.Message($"[DamageTransfer] {__instance.LabelShort} 受到 {totalDamageDealt} 点伤害,转移组件已激活");
}
}
}
[HarmonyPatch(typeof(Pawn), "PreApplyDamage")]
public static class Patch_Pawn_PreApplyDamage
{
[HarmonyPrefix]
public static bool Prefix(Pawn __instance, ref DamageInfo dinfo, out bool __state)
{
__state = false;
// 检查Pawn是否有伤害转移组件
var transferComp = __instance.TryGetComp<CompDamageTransfer>();
if (transferComp != null && __instance.Spawned && !__instance.Dead)
{
// 这里可以添加预处理逻辑
// 例如:在某些条件下完全阻止伤害
__state = true;
}
return true;
}
[HarmonyPostfix]
public static void Postfix(Pawn __instance, DamageInfo dinfo, bool __state)
{
if (__state)
{
// 后处理逻辑
// 例如:记录伤害转移统计
}
}
}
}

View File

@@ -0,0 +1,137 @@
using RimWorld;
using Verse;
using System.Collections.Generic;
using Verse.Sound;
using HarmonyLib;
namespace WulaFallenEmpire
{
public class CompDamageInterceptor : ThingComp
{
private CompProperties_DamageInterceptor Props => (CompProperties_DamageInterceptor)props;
private Pawn Pawn => (Pawn)parent;
// 使用Harmony补丁在伤害应用前完全拦截
public bool PreApplyDamage(ref DamageInfo dinfo)
{
if (!ShouldInterceptDamage(dinfo))
return true; // 继续应用伤害
// 计算要转移的伤害量(完全拦截)
float transferDamage = dinfo.Amount * Props.damageTransferRatio;
// 寻找可用的目标建筑
Building targetBuilding = FindTargetBuilding();
if (targetBuilding != null)
{
// 将伤害完全转移到建筑
ApplyDamageToBuilding(transferDamage, targetBuilding, dinfo);
OnDamageIntercepted(dinfo, transferDamage, targetBuilding);
// 完全拦截伤害 - 将伤害设置为0
dinfo.SetAmount(0f);
Log.Message($"[DamageInterceptor] {Pawn.LabelShort} 完全拦截 {transferDamage} 点伤害并转移至 {targetBuilding.Label}自身承受0伤害");
return true; // 继续应用修改后的伤害0伤害
}
return true; // 没有找到建筑,正常应用伤害
}
private bool ShouldInterceptDamage(DamageInfo dinfo)
{
if (parent == null || !parent.Spawned || Pawn.Dead)
return false;
// 检查生命值阈值
if (Pawn.health != null)
{
float healthRatio = Pawn.health.summaryHealth.SummaryHealthPercent;
if (healthRatio < Props.healthThreshold.min || healthRatio > Props.healthThreshold.max)
return false;
}
return true;
}
private Building FindTargetBuilding()
{
if (parent?.Map == null)
return null;
var map = parent.Map;
var faction = parent.Faction;
// 在全图范围内搜索目标建筑
List<Building> targetBuildings = new List<Building>();
foreach (var building in map.listerBuildings.allBuildingsColonist)
{
if (building.def.defName == Props.targetBuildingDefName &&
!building.Destroyed)
{
// 检查派系(如果需要)
if (Props.requireSameFaction && building.Faction != faction)
continue;
targetBuildings.Add(building);
}
}
// 随机选择一个建筑
if (targetBuildings.Count > 0)
{
return targetBuildings.RandomElement();
}
return null;
}
/// <summary>
/// 将伤害直接应用到目标建筑的生命值上
/// </summary>
private void ApplyDamageToBuilding(float damageAmount, Building building, DamageInfo originalDinfo)
{
// 创建新的伤害信息,使用原始伤害类型
DamageInfo buildingDamage = new DamageInfo(
originalDinfo.Def, // 使用相同的伤害类型
damageAmount,
originalDinfo.ArmorPenetrationInt,
originalDinfo.Angle,
originalDinfo.Instigator,
originalDinfo.HitPart,
originalDinfo.Weapon,
originalDinfo.Category,
originalDinfo.IntendedTarget
);
// 对建筑造成伤害
building.TakeDamage(buildingDamage);
Log.Message($"[DamageInterceptor] 对建筑 {building.Label} 造成 {damageAmount} 点伤害,剩余生命值: {building.HitPoints}/{building.MaxHitPoints}");
}
private void OnDamageIntercepted(DamageInfo dinfo, float interceptDamage, Building targetBuilding)
{
// 创建拦截效果
if (Props.interceptEffecter != null)
{
Effecter effect = Props.interceptEffecter.Spawn();
effect.Trigger(new TargetInfo(parent.Position, parent.Map), new TargetInfo(targetBuilding.Position, parent.Map));
effect.Cleanup();
}
// 播放音效
if (Props.interceptSound != null)
{
Props.interceptSound.PlayOneShot(new TargetInfo(parent.Position, parent.Map));
}
}
// 获取组件状态
public string GetStatusString()
{
return $"伤害拦截: {Props.damageTransferRatio * 100}% → {Props.targetBuildingDefName}";
}
}
}

View File

@@ -0,0 +1,133 @@
using RimWorld;
using Verse;
using System.Collections.Generic;
using Verse.Sound;
namespace WulaFallenEmpire
{
public class CompDamageRelay : ThingComp
{
private CompProperties_DamageRelay Props => (CompProperties_DamageRelay)props;
private Building Building => (Building)parent;
public override void PostPostApplyDamage(DamageInfo dinfo, float totalDamageDealt)
{
base.PostPostApplyDamage(dinfo, totalDamageDealt);
// 检查是否应该传递伤害
if (ShouldRelayDamage(dinfo, totalDamageDealt))
{
TryRelayDamage(dinfo, totalDamageDealt);
}
}
private bool ShouldRelayDamage(DamageInfo dinfo, float totalDamageDealt)
{
if (parent == null || !parent.Spawned || parent.Destroyed)
return false;
// 检查生命值阈值
float healthRatio = (float)Building.HitPoints / Building.MaxHitPoints;
if (healthRatio < Props.healthThreshold.min || healthRatio > Props.healthThreshold.max)
return false;
return true;
}
private void TryRelayDamage(DamageInfo dinfo, float totalDamageDealt)
{
// 计算传递的伤害量
float relayDamage = totalDamageDealt * Props.damageRelayRatio;
// 寻找可用的同派系建筑
Building targetBuilding = FindAvailableBuilding();
if (targetBuilding != null)
{
// 执行伤害传递
ApplyDamageToBuilding(relayDamage, targetBuilding, dinfo);
OnDamageRelayed(dinfo, relayDamage, targetBuilding);
// 记录日志
Log.Message($"[DamageRelay] {Building.Label} 将 {relayDamage} 点伤害传递给 {targetBuilding.Label}");
}
}
private Building FindAvailableBuilding()
{
if (parent?.Map == null)
return null;
var map = parent.Map;
var faction = parent.Faction;
// 在全图范围内搜索建筑
List<Building> availableBuildings = new List<Building>();
foreach (var building in map.listerBuildings.allBuildingsColonist)
{
if (building != parent && !building.Destroyed)
{
// 检查派系(如果需要)
if (Props.relayOnlyToSameFaction && building.Faction != faction)
continue;
availableBuildings.Add(building);
}
}
// 随机选择一个建筑
if (availableBuildings.Count > 0)
{
return availableBuildings.RandomElement();
}
return null;
}
/// <summary>
/// 将伤害直接应用到目标建筑的生命值上
/// </summary>
private void ApplyDamageToBuilding(float damageAmount, Building building, DamageInfo originalDinfo)
{
// 创建新的伤害信息,使用原始伤害类型
DamageInfo buildingDamage = new DamageInfo(
originalDinfo.Def, // 使用相同的伤害类型
damageAmount,
originalDinfo.ArmorPenetrationInt,
originalDinfo.Angle,
originalDinfo.Instigator,
originalDinfo.HitPart,
originalDinfo.Weapon,
originalDinfo.Category,
originalDinfo.IntendedTarget
);
// 对建筑造成伤害
building.TakeDamage(buildingDamage);
Log.Message($"[DamageRelay] 对建筑 {building.Label} 造成 {damageAmount} 点伤害,剩余生命值: {building.HitPoints}/{building.MaxHitPoints}");
}
private void OnDamageRelayed(DamageInfo dinfo, float relayDamage, Building targetBuilding)
{
// 创建传递效果
if (Props.relayEffecter != null)
{
Effecter effect = Props.relayEffecter.Spawn();
effect.Trigger(new TargetInfo(parent.Position, parent.Map), new TargetInfo(targetBuilding.Position, parent.Map));
effect.Cleanup();
}
// 播放音效
if (Props.relaySound != null)
{
Props.relaySound.PlayOneShot(new TargetInfo(parent.Position, parent.Map));
}
}
// 获取组件状态
public string GetStatusString()
{
return $"伤害传递: {Props.damageRelayRatio * 100}%";
}
}
}

View File

@@ -0,0 +1,22 @@
using RimWorld;
using Verse;
namespace WulaFallenEmpire
{
public class CompProperties_DamageInterceptor : CompProperties
{
public float damageTransferRatio = 1f; // 完全拦截并转移伤害
public string targetBuildingDefName = "WULA_Sky_Lock"; // 目标建筑类型
public bool requireSameFaction = true; // 是否需要同派系
public FloatRange healthThreshold = new FloatRange(0f, 1f); // 生命值阈值范围
// 效果设置
public EffecterDef interceptEffecter;
public SoundDef interceptSound;
public CompProperties_DamageInterceptor()
{
compClass = typeof(CompDamageInterceptor);
}
}
}

View File

@@ -0,0 +1,21 @@
using RimWorld;
using Verse;
namespace WulaFallenEmpire
{
public class CompProperties_DamageRelay : CompProperties
{
public float damageRelayRatio = 0.3f; // 继续传递伤害的比例
public bool relayOnlyToSameFaction = true; // 是否只传递给同派系建筑
public FloatRange healthThreshold = new FloatRange(0.1f, 1f); // 生命值阈值(低于此值才开始传递)
// 效果设置
public EffecterDef relayEffecter;
public SoundDef relaySound;
public CompProperties_DamageRelay()
{
compClass = typeof(CompDamageRelay);
}
}
}

View File

@@ -0,0 +1,41 @@
using HarmonyLib;
using RimWorld;
using Verse;
namespace WulaFallenEmpire
{
[HarmonyPatch(typeof(Pawn), "PreApplyDamage")]
public static class Patch_Pawn_PreApplyDamage
{
[HarmonyPrefix]
public static bool Prefix(Pawn __instance, ref DamageInfo dinfo)
{
// 检查Pawn是否有伤害拦截组件
var interceptorComp = __instance.TryGetComp<CompDamageInterceptor>();
if (interceptorComp != null)
{
Log.Message($"[DamageInterceptor] {__instance.LabelShort} 即将受到 {dinfo.Amount} 点伤害,拦截组件激活");
// 让拦截组件处理伤害
return interceptorComp.PreApplyDamage(ref dinfo);
}
return true; // 继续正常处理伤害
}
}
[HarmonyPatch(typeof(Pawn), "PostApplyDamage")]
public static class Patch_Pawn_PostApplyDamage
{
[HarmonyPostfix]
public static void Postfix(Pawn __instance, DamageInfo dinfo, float totalDamageDealt)
{
// 记录实际承受的伤害
var interceptorComp = __instance.TryGetComp<CompDamageInterceptor>();
if (interceptorComp != null && totalDamageDealt == 0f)
{
Log.Message($"[DamageInterceptor] {__instance.LabelShort} 成功拦截所有伤害实际承受0点伤害");
}
}
}
}