411 lines
16 KiB
C#
411 lines
16 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using Verse;
|
||
using RimWorld;
|
||
|
||
namespace ArachnaeSwarm
|
||
{
|
||
public class CompHediffGiver : ThingComp
|
||
{
|
||
private bool hediffsApplied = false; // 标记是否已经应用过hediff
|
||
|
||
// 用于记录添加的hediff和部位(用于调试和撤销)
|
||
private Dictionary<HediffDef, List<BodyPartRecord>> appliedHediffParts =
|
||
new Dictionary<HediffDef, List<BodyPartRecord>>();
|
||
|
||
public CompProperties_HediffGiver Props => (CompProperties_HediffGiver)this.props;
|
||
|
||
public override void PostSpawnSetup(bool respawningAfterLoad)
|
||
{
|
||
base.PostSpawnSetup(respawningAfterLoad);
|
||
|
||
// 只有当thing是pawn时才添加hediff
|
||
if (this.parent is Pawn pawn)
|
||
{
|
||
// 检查是否已经应用过hediff,或者是否是读档
|
||
if (!hediffsApplied && !respawningAfterLoad)
|
||
{
|
||
AddHediffsToPawn(pawn);
|
||
hediffsApplied = true; // 标记为已应用
|
||
}
|
||
}
|
||
}
|
||
|
||
private void AddHediffsToPawn(Pawn pawn)
|
||
{
|
||
// 检查是否有hediff列表
|
||
if (Props.hediffs == null || Props.hediffs.Count == 0)
|
||
return;
|
||
|
||
// 检查概率
|
||
if (Props.addChance < 1.0f && Rand.Value > Props.addChance)
|
||
return;
|
||
|
||
// 显示应用消息(如果有)
|
||
if (Props.showApplicationMessage && pawn.Faction == Faction.OfPlayer)
|
||
{
|
||
string message = Props.applicationMessageKey != null
|
||
? Props.applicationMessageKey.Translate(pawn.LabelShort)
|
||
: "ARA_HediffGiver_Applied".Translate(pawn.LabelShort, Props.hediffs.Count);
|
||
|
||
Messages.Message(message, pawn, MessageTypeDefOf.NeutralEvent);
|
||
}
|
||
|
||
// 为每个hediff添加到pawn
|
||
foreach (HediffDef hediffDef in Props.hediffs)
|
||
{
|
||
// 检查是否允许重复添加
|
||
if (!Props.allowDuplicates && pawn.health.hediffSet.HasHediff(hediffDef))
|
||
continue;
|
||
|
||
// === 新增:获取应应用的部位 ===
|
||
BodyPartDef bodyPartDef = Props.GetBodyPartForHediff(hediffDef);
|
||
List<BodyPartRecord> bodyParts = GetBodyPartsForHediff(pawn, hediffDef, bodyPartDef);
|
||
|
||
// 如果没有指定部位,或者没有找到匹配部位,则添加到全身
|
||
if (bodyParts == null || bodyParts.Count == 0)
|
||
{
|
||
AddHediffToPawn(pawn, hediffDef, null);
|
||
continue;
|
||
}
|
||
|
||
// 根据选择规则添加hediff到相应部位
|
||
foreach (var bodyPart in bodyParts)
|
||
{
|
||
AddHediffToPawn(pawn, hediffDef, bodyPart);
|
||
}
|
||
|
||
// 记录应用到的部位
|
||
if (bodyParts.Count > 0)
|
||
{
|
||
if (!appliedHediffParts.ContainsKey(hediffDef))
|
||
{
|
||
appliedHediffParts[hediffDef] = new List<BodyPartRecord>();
|
||
}
|
||
appliedHediffParts[hediffDef].AddRange(bodyParts);
|
||
}
|
||
}
|
||
|
||
// 播放应用效果(如果有)
|
||
if (Props.applicationEffect != null && pawn.Spawned)
|
||
{
|
||
Effecter effecter = Props.applicationEffect.Spawn();
|
||
effecter.Trigger(pawn, pawn);
|
||
effecter.Cleanup();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 添加hediff到pawn的指定部位
|
||
/// </summary>
|
||
private void AddHediffToPawn(Pawn pawn, HediffDef hediffDef, BodyPartRecord bodyPart)
|
||
{
|
||
try
|
||
{
|
||
Hediff hediff = HediffMaker.MakeHediff(hediffDef, pawn, bodyPart);
|
||
|
||
// 设置严重度(如果有指定)
|
||
if (Props.initialSeverity >= 0f)
|
||
{
|
||
hediff.Severity = Props.initialSeverity;
|
||
}
|
||
|
||
// 添加hediff
|
||
pawn.health.AddHediff(hediff);
|
||
|
||
ArachnaeLog.Debug($"Added hediff {hediffDef.defName} to {pawn.Label} " +
|
||
$"{(bodyPart != null ? $"on {bodyPart.def.defName}" : "to whole body")}");
|
||
}
|
||
catch (Exception)
|
||
{
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取hediff应应用的身体部位列表
|
||
/// </summary>
|
||
private List<BodyPartRecord> GetBodyPartsForHediff(Pawn pawn, HediffDef hediffDef, BodyPartDef bodyPartDef)
|
||
{
|
||
if (pawn == null || pawn.RaceProps?.body == null)
|
||
return null;
|
||
|
||
// 如果没有指定身体部位,返回空列表
|
||
if (bodyPartDef == null)
|
||
return null;
|
||
|
||
try
|
||
{
|
||
// 获取所有匹配的身体部位
|
||
List<BodyPartRecord> allMatchingParts = pawn.RaceProps.body.GetPartsWithDef(bodyPartDef);
|
||
|
||
if (allMatchingParts == null || allMatchingParts.Count == 0)
|
||
{
|
||
// 如果没有找到匹配部位,检查是否有备用部位
|
||
var mapping = Props.GetMappingForHediff(hediffDef);
|
||
if (mapping != null && mapping.fallbackToDefault)
|
||
{
|
||
// 使用默认方式添加(全身)
|
||
return null;
|
||
}
|
||
return new List<BodyPartRecord>();
|
||
}
|
||
|
||
// 检查部位有效性
|
||
if (Props.checkPartValidity)
|
||
{
|
||
allMatchingParts = allMatchingParts.Where(part => IsBodyPartValid(pawn, part)).ToList();
|
||
}
|
||
|
||
if (allMatchingParts.Count == 0)
|
||
{
|
||
return new List<BodyPartRecord>();
|
||
}
|
||
|
||
// 获取选择规则
|
||
BodyPartSelectionRule rule = Props.GetPartSelectionRuleForHediff(hediffDef);
|
||
var mappingConfig = Props.GetMappingForHediff(hediffDef);
|
||
|
||
// 根据规则选择部位
|
||
List<BodyPartRecord> selectedParts = new List<BodyPartRecord>();
|
||
|
||
if (rule == null)
|
||
{
|
||
// 默认规则:使用第一个匹配的部位
|
||
selectedParts.Add(allMatchingParts[0]);
|
||
}
|
||
else
|
||
{
|
||
// 根据规则选择部位
|
||
switch (rule.mode)
|
||
{
|
||
case BodyPartSelectionMode.First:
|
||
selectedParts.Add(allMatchingParts[0]);
|
||
break;
|
||
|
||
case BodyPartSelectionMode.Random:
|
||
int count = Math.Min(rule.maxParts, allMatchingParts.Count);
|
||
selectedParts = allMatchingParts.InRandomOrder().Take(count).ToList();
|
||
break;
|
||
|
||
case BodyPartSelectionMode.All:
|
||
selectedParts = allMatchingParts;
|
||
break;
|
||
|
||
case BodyPartSelectionMode.MostDamaged:
|
||
var mostDamaged = allMatchingParts.OrderByDescending(p => GetPartDamage(pawn, p)).FirstOrDefault();
|
||
if (mostDamaged != null) selectedParts.Add(mostDamaged);
|
||
break;
|
||
|
||
case BodyPartSelectionMode.LeastDamaged:
|
||
var leastDamaged = allMatchingParts.OrderBy(p => GetPartDamage(pawn, p)).FirstOrDefault();
|
||
if (leastDamaged != null) selectedParts.Add(leastDamaged);
|
||
break;
|
||
}
|
||
|
||
// 如果配置了随机选择部位
|
||
if (mappingConfig != null && mappingConfig.chooseRandomPart)
|
||
{
|
||
selectedParts = allMatchingParts.InRandomOrder().Take(1).ToList();
|
||
}
|
||
|
||
// 处理对称性
|
||
if (rule.symmetrical && selectedParts.Count > 0)
|
||
{
|
||
selectedParts = GetSymmetricalParts(pawn, selectedParts[0], allMatchingParts);
|
||
}
|
||
}
|
||
|
||
return selectedParts;
|
||
}
|
||
catch (Exception)
|
||
{
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取对称的身体部位
|
||
/// </summary>
|
||
private List<BodyPartRecord> GetSymmetricalParts(Pawn pawn, BodyPartRecord referencePart, List<BodyPartRecord> allParts)
|
||
{
|
||
List<BodyPartRecord> symmetricalParts = new List<BodyPartRecord> { referencePart };
|
||
|
||
// 查找对称部位(如左臂对应的右臂)
|
||
foreach (var part in allParts)
|
||
{
|
||
if (part != referencePart && part.def == referencePart.def)
|
||
{
|
||
// 简单的对称检测:检查部位标签或名称
|
||
if (IsSymmetricalPart(referencePart, part))
|
||
{
|
||
symmetricalParts.Add(part);
|
||
}
|
||
}
|
||
}
|
||
|
||
return symmetricalParts;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查两个部位是否对称
|
||
/// </summary>
|
||
private bool IsSymmetricalPart(BodyPartRecord part1, BodyPartRecord part2)
|
||
{
|
||
// 简单的对称检测逻辑
|
||
string label1 = part1.customLabel ?? part1.def.label;
|
||
string label2 = part2.customLabel ?? part2.def.label;
|
||
|
||
// 检查是否包含对称标识符
|
||
string[] symmetryKeywords = { "Left", "Right", "Left", "Right", "Front", "Back", "Upper", "Lower" };
|
||
|
||
foreach (var keyword in symmetryKeywords)
|
||
{
|
||
if (label1.Contains(keyword) && label2.Contains(keyword))
|
||
{
|
||
// 检查是否是对称的关键词对
|
||
if ((keyword == "Left" && label2.Contains("Right")) ||
|
||
(keyword == "Right" && label2.Contains("Left")) ||
|
||
(keyword == "Front" && label2.Contains("Back")) ||
|
||
(keyword == "Back" && label2.Contains("Front")))
|
||
{
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查身体部位是否有效
|
||
/// </summary>
|
||
private bool IsBodyPartValid(Pawn pawn, BodyPartRecord part)
|
||
{
|
||
if (part == null)
|
||
return false;
|
||
|
||
// 检查部位是否已完全损坏
|
||
if (pawn.health.hediffSet.PartIsMissing(part))
|
||
return false;
|
||
|
||
// 检查部位是否被覆盖(如被装备遮挡)
|
||
// 这里可以添加更多有效性检查
|
||
|
||
return true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取部位的伤害值
|
||
/// </summary>
|
||
private float GetPartDamage(Pawn pawn, BodyPartRecord part)
|
||
{
|
||
if (part == null)
|
||
return 0f;
|
||
|
||
float totalDamage = 0f;
|
||
foreach (var hediff in pawn.health.hediffSet.hediffs)
|
||
{
|
||
if (hediff.Part == part && hediff is Hediff_Injury injury)
|
||
{
|
||
totalDamage += injury.Severity;
|
||
}
|
||
}
|
||
|
||
return totalDamage;
|
||
}
|
||
|
||
// 序列化hediffsApplied标记
|
||
public override void PostExposeData()
|
||
{
|
||
base.PostExposeData();
|
||
Scribe_Values.Look(ref hediffsApplied, "hediffsApplied", false);
|
||
|
||
// 新增:序列化已应用的hediff部位记录
|
||
Scribe_Collections.Look(ref appliedHediffParts, "appliedHediffParts",
|
||
LookMode.Def, LookMode.Deep);
|
||
}
|
||
|
||
// 调试方法,用于手动触发hediff添加(仅开发模式)
|
||
public void DebugApplyHediffs()
|
||
{
|
||
if (this.parent is Pawn pawn && !hediffsApplied)
|
||
{
|
||
AddHediffsToPawn(pawn);
|
||
hediffsApplied = true;
|
||
ArachnaeLog.Debug($"Debug: Applied hediffs to {pawn.Label}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取已应用的hediff信息(用于调试)
|
||
/// </summary>
|
||
public string GetAppliedHediffInfo()
|
||
{
|
||
if (!hediffsApplied || !(this.parent is Pawn pawn))
|
||
return "No hediffs applied";
|
||
|
||
var result = new System.Text.StringBuilder();
|
||
result.AppendLine("Applied hediffs:");
|
||
|
||
foreach (var hediffDef in Props.hediffs)
|
||
{
|
||
var hediffs = pawn.health.hediffSet.hediffs.Where(h => h.def == hediffDef).ToList();
|
||
|
||
if (hediffs.Count > 0)
|
||
{
|
||
result.AppendLine($"- {hediffDef.defName} ({hediffs.Count} instances):");
|
||
foreach (var hediff in hediffs)
|
||
{
|
||
string partInfo = hediff.Part?.def?.defName ?? "No specific part";
|
||
result.AppendLine($" * On {partInfo}, Severity: {hediff.Severity:F2}");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
result.AppendLine($"- {hediffDef.defName} (not applied)");
|
||
}
|
||
}
|
||
|
||
return result.ToString();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 新增:移除所有由该组件添加的hediff
|
||
/// </summary>
|
||
public void RemoveAppliedHediffs()
|
||
{
|
||
if (!(this.parent is Pawn pawn) || !hediffsApplied)
|
||
return;
|
||
|
||
int removedCount = 0;
|
||
foreach (var hediffDef in Props.hediffs)
|
||
{
|
||
var hediffs = pawn.health.hediffSet.hediffs.Where(h => h.def == hediffDef).ToList();
|
||
foreach (var hediff in hediffs)
|
||
{
|
||
pawn.health.RemoveHediff(hediff);
|
||
removedCount++;
|
||
}
|
||
}
|
||
|
||
hediffsApplied = false;
|
||
appliedHediffParts.Clear();
|
||
|
||
ArachnaeLog.Debug($"Removed {removedCount} hediffs from {pawn.Label}");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 新增:检查特定hediff是否已应用到指定部位
|
||
/// </summary>
|
||
public bool HasHediffOnPart(HediffDef hediffDef, BodyPartRecord part)
|
||
{
|
||
if (!(this.parent is Pawn pawn) || !hediffsApplied)
|
||
return false;
|
||
|
||
return pawn.health.hediffSet.hediffs
|
||
.Any(h => h.def == hediffDef && h.Part == part);
|
||
}
|
||
}
|
||
}
|