diff --git a/1.6/1.6/Assemblies/ArachnaeSwarm.dll b/1.6/1.6/Assemblies/ArachnaeSwarm.dll index 8df7244..664bd3f 100644 Binary files a/1.6/1.6/Assemblies/ArachnaeSwarm.dll and b/1.6/1.6/Assemblies/ArachnaeSwarm.dll differ diff --git a/Source/ArachnaeSwarm/ARA_HuggingFace/CompAbilityEffect_Possess.cs b/Source/ArachnaeSwarm/ARA_HuggingFace/CompAbilityEffect_Possess.cs index 8712fac..8a0ced4 100644 --- a/Source/ArachnaeSwarm/ARA_HuggingFace/CompAbilityEffect_Possess.cs +++ b/Source/ArachnaeSwarm/ARA_HuggingFace/CompAbilityEffect_Possess.cs @@ -46,11 +46,16 @@ namespace ArachnaeSwarm private void DoPossession(Pawn caster, Pawn targetPawn) { if (targetPawn == null || caster == null) return; - + Log.Message($"[夺舍] 开始执行。施法者: {caster.LabelShort}, 目标: {targetPawn.LabelShort}"); - // 为了保留原始宿主的技能数据,在灵魂转移前先复制一份 - var originalTargetSkills = new Dictionary(); + // 1. 捕获原宿主的完整数据,用于死亡后恢复尸体 + OriginalPawnData originalHostData = new OriginalPawnData(); + originalHostData.CaptureData(targetPawn); + Log.Message($"[夺舍] 已捕获原始宿主 {targetPawn.LabelShort} 的完整数据。"); + + // 2. 备份原宿主的技能,用于后续合并 + var originalTargetSkills = new Dictionary(); if (targetPawn.skills != null) { foreach (var skill in targetPawn.skills.skills) @@ -59,19 +64,20 @@ namespace ArachnaeSwarm } } - // 储存原始抱脸虫 + // 3. 准备抱脸虫和Hediff Pawn originalCaster = caster.SplitOff(1) as Pawn; - Hediff_Possession hediff = (Hediff_Possession)HediffMaker.MakeHediff(HediffDef.Named("ARA_Possession"), targetPawn); - if (hediff.GetDirectlyHeldThings().TryAdd(originalCaster, true)) + hediff.originalHostData = originalHostData; // 将宿主数据存入Hediff + + // 4. 将抱脸虫存入Hediff + if (hediff.casterContainer.TryAdd(originalCaster, true)) { Log.Message($"[夺舍] 成功将 {caster.LabelShort} 的原始副本存入Hediff。"); - // 灵魂转移,此时 targetPawn 的技能被 caster 的技能覆盖 + // 5. 灵魂转移,此时 targetPawn 的技能被 caster 的技能覆盖 PawnDataUtility.TransferSoul(caster, targetPawn); - // --- 技能合并 --- - // 在灵魂转移后,直接在最终的身体 (targetPawn) 上进行合并 + // 6. 技能合并:在灵魂转移后,直接在最终的身体 (targetPawn) 上进行合并 if (targetPawn.skills != null) { Log.Message("[夺舍] 开始合并技能..."); @@ -80,21 +86,21 @@ namespace ArachnaeSwarm if (originalTargetSkills.TryGetValue(skillRecord.def, out var originalSkill)) { // 比较等级 - if (originalSkill.Item1 > skillRecord.levelInt) + if (originalSkill.level > skillRecord.levelInt) { - skillRecord.levelInt = originalSkill.Item1; + skillRecord.levelInt = originalSkill.level; } // 比较热情 - if (originalSkill.Item2 > skillRecord.passion) + if (originalSkill.passion > skillRecord.passion) { - skillRecord.passion = originalSkill.Item2; + skillRecord.passion = originalSkill.passion; } } } Log.Message("[夺舍] 技能合并完成。"); } - + // 7. 将Hediff添加到最终身体上 targetPawn.health.AddHediff(hediff); if (Props.hediffToApplyOnSuccess != null) @@ -107,6 +113,7 @@ namespace ArachnaeSwarm else { Log.Error($"[夺舍] 无法将 {caster.LabelShort} 的副本存入Hediff。中止操作。"); + if(originalCaster != null && !originalCaster.Destroyed) originalCaster.Destroy(); } } diff --git a/Source/ArachnaeSwarm/ARA_HuggingFace/Hediff_Possession.cs b/Source/ArachnaeSwarm/ARA_HuggingFace/Hediff_Possession.cs index f7a0c96..1c58127 100644 --- a/Source/ArachnaeSwarm/ARA_HuggingFace/Hediff_Possession.cs +++ b/Source/ArachnaeSwarm/ARA_HuggingFace/Hediff_Possession.cs @@ -6,14 +6,15 @@ namespace ArachnaeSwarm { public class Hediff_Possession : HediffWithComps, IThingHolder { - private ThingOwner innerContainer; + public ThingOwner casterContainer; + public OriginalPawnData originalHostData; public Hediff_Possession() { - this.innerContainer = new ThingOwner(this, false, LookMode.Deep); + this.casterContainer = new ThingOwner(this, false, LookMode.Deep); } - public Pawn StoredCasterPawn => innerContainer.Count > 0 ? innerContainer[0] as Pawn : null; + public Pawn StoredCasterPawn => casterContainer.Count > 0 ? casterContainer[0] as Pawn : null; public IThingHolder ParentHolder => this.pawn; @@ -24,7 +25,7 @@ namespace ArachnaeSwarm public ThingOwner GetDirectlyHeldThings() { - return innerContainer; + return casterContainer; } // PostAdd现在只在游戏加载时起作用,我们不需要在这里做任何特殊操作。 @@ -37,19 +38,28 @@ namespace ArachnaeSwarm Pawn deadBody = this.pawn; Pawn storedCaster = this.StoredCasterPawn; - if (storedCaster == null) + if (originalHostData != null) + { + Log.Message($"[夺舍结束] 正在将 {deadBody.LabelShort}'s 的灵魂恢复为原始宿主数据。"); + originalHostData.RestoreData(deadBody); + } + else + { + Log.Error("Possessed pawn died, but no original host data was found to restore."); + } + + if (storedCaster != null) + { + EjectCaster(); + } + else { Log.Error("Possessed pawn died, but no caster soul was found inside."); - return; } - - Log.Message($"Host {deadBody.LabelShort} died. Transferring experience back to {storedCaster.LabelShort} and ejecting."); - // PawnDataUtility.TransferSoul(deadBody, storedCaster); // 注释掉这一行,因为我们不希望宿主的技能在死亡时传回给抱脸虫。 - this.EjectContents(); } - public void EjectContents() + public void EjectCaster() { if (StoredCasterPawn == null) return; @@ -68,13 +78,14 @@ namespace ArachnaeSwarm } Log.Message($"[夺舍] 准备在地图 {map.ToString()} 的位置 {cell.ToString()} 处重生 {StoredCasterPawn.LabelShort}。"); - this.innerContainer.TryDropAll(cell, map, ThingPlaceMode.Near); + this.casterContainer.TryDropAll(cell, map, ThingPlaceMode.Near); } public override void ExposeData() { base.ExposeData(); - Scribe_Deep.Look(ref innerContainer, "innerContainer", this); + Scribe_Deep.Look(ref casterContainer, "casterContainer", this); + Scribe_Deep.Look(ref originalHostData, "originalHostData"); } } } \ No newline at end of file diff --git a/Source/ArachnaeSwarm/ARA_HuggingFace/OriginalPawnData.cs b/Source/ArachnaeSwarm/ARA_HuggingFace/OriginalPawnData.cs new file mode 100644 index 0000000..295e436 --- /dev/null +++ b/Source/ArachnaeSwarm/ARA_HuggingFace/OriginalPawnData.cs @@ -0,0 +1,180 @@ +using RimWorld; +using Verse; +using System.Collections.Generic; +using System.Linq; +using RimWorld.Planet; + +namespace ArachnaeSwarm +{ + // A container for all essential data of a pawn that needs to be restored. + public class OriginalPawnData : IExposable + { + // Core Identity + public Name name; + public BackstoryDef childhood; + public BackstoryDef adulthood; + public List traits = new List(); + public Faction faction; + + // Growth & Experience + public List skills = new List(); + + // Mind & Settings + public List memories = new List(); + public Dictionary workSettings = new Dictionary(); + public List timetable; + public HostilityResponseMode hostilityResponse; + public MedicalCareCategory medCare; + public bool selfTend; + public ApparelPolicy apparelPolicy; + public DrugPolicy drugPolicy; + public FoodPolicy foodPolicy; + + // DLC & Social + public Ideo ideo; + public List royalTitles = new List(); + + public void ExposeData() + { + Scribe_Deep.Look(ref name, "name"); + Scribe_Defs.Look(ref childhood, "childhood"); + Scribe_Defs.Look(ref adulthood, "adulthood"); + Scribe_Collections.Look(ref traits, "traits", LookMode.Deep); + Scribe_References.Look(ref faction, "faction"); + Scribe_Collections.Look(ref skills, "skills", LookMode.Deep); + Scribe_Collections.Look(ref memories, "memories", LookMode.Deep); + Scribe_Collections.Look(ref workSettings, "workSettings", LookMode.Def, LookMode.Value); + Scribe_Collections.Look(ref timetable, "timetable", LookMode.Def); + Scribe_Values.Look(ref hostilityResponse, "hostilityResponse"); + Scribe_Values.Look(ref medCare, "medCare"); + Scribe_Values.Look(ref selfTend, "selfTend"); + Scribe_References.Look(ref apparelPolicy, "apparelPolicy"); + Scribe_References.Look(ref drugPolicy, "drugPolicy"); + Scribe_References.Look(ref foodPolicy, "foodPolicy"); + + if (ModsConfig.IdeologyActive) Scribe_References.Look(ref ideo, "ideo"); + if (ModsConfig.RoyaltyActive) + { + Scribe_Collections.Look(ref royalTitles, "royalTitles", LookMode.Deep); + } + } + + // Populates this object with data from a living pawn. + public void CaptureData(Pawn pawn) + { + this.name = pawn.Name; + this.childhood = pawn.story.Childhood; + this.adulthood = pawn.story.Adulthood; + this.traits = new List(pawn.story.traits.allTraits); + this.faction = pawn.Faction; + + this.skills = pawn.skills.skills.Select(s => new SkillRecordData + { + def = s.def, + level = s.levelInt, + xpSinceLastLevel = s.xpSinceLastLevel, + passion = s.passion + }).ToList(); + + if (pawn.needs?.mood?.thoughts?.memories != null) this.memories = new List(pawn.needs.mood.thoughts.memories.Memories); + if (pawn.workSettings != null) this.workSettings = DefDatabase.AllDefs.ToDictionary(def => def, def => pawn.workSettings.GetPriority(def)); + if (pawn.timetable != null) this.timetable = new List(pawn.timetable.times); + if (pawn.playerSettings != null) + { + this.hostilityResponse = pawn.playerSettings.hostilityResponse; + this.medCare = pawn.playerSettings.medCare; + this.selfTend = pawn.playerSettings.selfTend; + } + if (pawn.outfits != null) this.apparelPolicy = pawn.outfits.CurrentApparelPolicy; + if (pawn.drugs != null) this.drugPolicy = pawn.drugs.CurrentPolicy; + if (pawn.foodRestriction != null) this.foodPolicy = pawn.foodRestriction.CurrentFoodPolicy; + + if (ModsConfig.IdeologyActive && pawn.ideo != null) this.ideo = pawn.ideo.Ideo; + if (ModsConfig.RoyaltyActive && pawn.royalty != null) + { + this.royalTitles = pawn.royalty.AllTitlesForReading.Select(t => new RoyalTitleData { defName = t.def.defName, faction = t.faction }).ToList(); + } + } + + // Applies the stored data back to a living pawn. + public void RestoreData(Pawn pawn) + { + pawn.Name = this.name; + pawn.story.Childhood = this.childhood; + pawn.story.Adulthood = this.adulthood; + pawn.story.traits.allTraits.Clear(); + this.traits.ForEach(t => pawn.story.traits.GainTrait(t)); + if (pawn.Faction != this.faction) pawn.SetFaction(this.faction); + + pawn.skills.skills.Clear(); + this.skills.ForEach(s => pawn.skills.skills.Add(new SkillRecord(pawn, s.def) { levelInt = s.level, xpSinceLastLevel = s.xpSinceLastLevel, passion = s.passion })); + + if (pawn.needs?.mood?.thoughts?.memories != null) + { + pawn.needs.mood.thoughts.memories.Memories.Clear(); + this.memories.ForEach(m => pawn.needs.mood.thoughts.memories.TryGainMemory(m)); + } + if (pawn.workSettings != null) + { + pawn.workSettings.EnableAndInitialize(); + foreach(var ws in this.workSettings) pawn.workSettings.SetPriority(ws.Key, ws.Value); + } + if (pawn.timetable != null) pawn.timetable.times = new List(this.timetable); + if (pawn.playerSettings != null) + { + pawn.playerSettings.hostilityResponse = this.hostilityResponse; + pawn.playerSettings.medCare = this.medCare; + pawn.playerSettings.selfTend = this.selfTend; + } + if (pawn.outfits != null) pawn.outfits.CurrentApparelPolicy = this.apparelPolicy; + if (pawn.drugs != null) pawn.drugs.CurrentPolicy = this.drugPolicy; + if (pawn.foodRestriction != null) pawn.foodRestriction.CurrentFoodPolicy = this.foodPolicy; + + if (ModsConfig.IdeologyActive && pawn.ideo != null && this.ideo != null) pawn.ideo.SetIdeo(this.ideo); + if (ModsConfig.RoyaltyActive && pawn.royalty != null) + { + pawn.royalty.AllTitlesForReading.Clear(); + pawn.royalty.AllFactionPermits.Clear(); // Clear existing permits on the body + this.royalTitles.ForEach(t => { + RoyalTitleDef titleDef = DefDatabase.GetNamed(t.defName, false); + if (titleDef != null) + { + pawn.royalty.SetTitle(t.faction, titleDef, true, false, false); + } + }); + pawn.royalty.UpdateAvailableAbilities(); + } + + pawn.Drawer.renderer.SetAllGraphicsDirty(); + } + } + + public class SkillRecordData : IExposable + { + public SkillDef def; + public int level; + public float xpSinceLastLevel; + public Passion passion; + + public void ExposeData() + { + Scribe_Defs.Look(ref def, "def"); + Scribe_Values.Look(ref level, "level"); + Scribe_Values.Look(ref xpSinceLastLevel, "xpSinceLastLevel"); + Scribe_Values.Look(ref passion, "passion"); + } + } + + public class RoyalTitleData : IExposable + { + public Faction faction; + public string defName; + + public void ExposeData() + { + Scribe_References.Look(ref faction, "faction"); + Scribe_Values.Look(ref defName, "defName"); + } + } + +} \ No newline at end of file diff --git a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj index 2929c0b..3e58564 100644 --- a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj +++ b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj @@ -121,6 +121,7 @@ +