diff --git a/1.6/1.6/Assemblies/ArachnaeSwarm.dll b/1.6/1.6/Assemblies/ArachnaeSwarm.dll
index 84e0e9c..5157529b 100644
Binary files a/1.6/1.6/Assemblies/ArachnaeSwarm.dll and b/1.6/1.6/Assemblies/ArachnaeSwarm.dll differ
diff --git a/1.6/1.6/Defs/HediffDefs/ARA_Hediffs_Possession.xml b/1.6/1.6/Defs/HediffDefs/ARA_Hediffs_Possession.xml
new file mode 100644
index 0000000..095afb3
--- /dev/null
+++ b/1.6/1.6/Defs/HediffDefs/ARA_Hediffs_Possession.xml
@@ -0,0 +1,19 @@
+
+
+
+
+ ARA_Possession
+
+ 这个生物的身体正被另一个实体所控制。
+ ArachnaeSwarm.Possession.Hediff_Possession
+ false
+ false
+ 1.0
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/1.6/1.6/Defs/Misc/ARA_Possession_Defs.xml b/1.6/1.6/Defs/Misc/ARA_Possession_Defs.xml
new file mode 100644
index 0000000..40efc0a
--- /dev/null
+++ b/1.6/1.6/Defs/Misc/ARA_Possession_Defs.xml
@@ -0,0 +1,166 @@
+
+
+
+
+
+
+ ARA_Ability_Possess
+
+ 将你的意识注入另一个生物的身体,完全占据它。
+ UI/Abilities/Possess
+ 600
+
+ Verb_CastAbility
+ 1.5
+ 5.9
+
+ true
+ false
+ false
+ false
+
+
+
+
+
+
+
+
+ ARA_Facehugger
+ Humanlike_PostMentalState
+ 100
+
+
+
+
+
+ Possess
+
+
+
+
+
+
+
+
+
+ ARA_Facehugger
+
+ ARA_FacehuggerRace
+ 25
+
+
+
+ Things/Pawn/Animal/ARA_Facehugger
+ 0.8
+
+
+ Things/Pawn/Animal/Dessicated/CritterDessicated
+ 0.8
+
+
+
+ ARA_Facehugger
+
+ ARA_Ability_Possess
+
+
+
+
+
+
+ ARA_FacehuggerRace
+
+ 一种小型的、脆弱的寄生生物,其唯一的生存目的就是寻找并占据一个更强大的宿主。它通过将自己的意识注入目标来完成这一过程。
+
+ 4.0
+ 50
+ -10
+ 50
+
+
+
+
+
+ Scratch
+
+ 2
+ 1.5
+
+
+
+ Animal
+ ARA_FacehuggerBody
+ 0.2
+ 0.3
+ 0.1
+
+
+ AnimalAdult
+ 0
+
+
+
+
+
+
+ ARA_FacehuggerBody
+
+
+ Body
+ 20
+ 20
+
+
+ Head
+ 0.3
+
+
+ Skull
+ 0.2
+ Inside
+
+
+ Brain
+ 0.1
+ Inside
+
+
+
+
+ Eye
+ left eye
+ 0.07
+
+
+ Eye
+ right eye
+ 0.07
+
+
+
+
+ Leg
+ front left leg
+ 0.1
+
+
+ Leg
+ front right leg
+ 0.1
+
+
+ Leg
+ rear left leg
+ 0.1
+
+
+ Leg
+ rear right leg
+ 0.1
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj
index fa02310..39029b2 100644
--- a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj
+++ b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj
@@ -112,6 +112,12 @@
+
+
+
+
+
+
diff --git a/Source/ArachnaeSwarm/Possession/CompAbilityEffect_Possess.cs b/Source/ArachnaeSwarm/Possession/CompAbilityEffect_Possess.cs
new file mode 100644
index 0000000..f0e2b8b
--- /dev/null
+++ b/Source/ArachnaeSwarm/Possession/CompAbilityEffect_Possess.cs
@@ -0,0 +1,33 @@
+using RimWorld;
+using Verse;
+
+namespace ArachnaeSwarm.Possession
+{
+ public class CompAbilityEffect_Possess : CompAbilityEffect
+ {
+ public override void Apply(LocalTargetInfo target, LocalTargetInfo dest)
+ {
+ base.Apply(target, dest);
+
+ Pawn caster = this.parent.pawn;
+ Pawn targetPawn = target.Pawn;
+
+ if (targetPawn == null)
+ {
+ return;
+ }
+
+ // Optional: Add checks here. E.g., cannot possess mechanical, already possessed, etc.
+ // if (targetPawn.RaceProps.IsMechanoid)
+ // {
+ // Messages.Message("Cannot possess a mechanoid.", MessageTypeDefOf.RejectInput);
+ // return;
+ // }
+
+ Hediff_Possession hediff = (Hediff_Possession)HediffMaker.MakeHediff(HediffDef.Named("ARA_Possession"), targetPawn);
+ hediff.SetCaster(caster);
+
+ targetPawn.health.AddHediff(hediff);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/ArachnaeSwarm/Possession/CompProperties_AbilityPossess.cs b/Source/ArachnaeSwarm/Possession/CompProperties_AbilityPossess.cs
new file mode 100644
index 0000000..7cea6a7
--- /dev/null
+++ b/Source/ArachnaeSwarm/Possession/CompProperties_AbilityPossess.cs
@@ -0,0 +1,12 @@
+using RimWorld;
+
+namespace ArachnaeSwarm.Possession
+{
+ public class CompProperties_AbilityPossess : CompProperties_AbilityEffect
+ {
+ public CompProperties_AbilityPossess()
+ {
+ this.compClass = typeof(CompAbilityEffect_Possess);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/ArachnaeSwarm/Possession/Hediff_Possession.cs b/Source/ArachnaeSwarm/Possession/Hediff_Possession.cs
new file mode 100644
index 0000000..7054207
--- /dev/null
+++ b/Source/ArachnaeSwarm/Possession/Hediff_Possession.cs
@@ -0,0 +1,90 @@
+using System.Collections.Generic;
+using RimWorld;
+using Verse;
+
+namespace ArachnaeSwarm.Possession
+{
+ public class Hediff_Possession : HediffWithComps, IThingHolder
+ {
+ private ThingOwner innerContainer;
+ private Pawn originalCaster;
+
+ public Hediff_Possession()
+ {
+ this.innerContainer = new ThingOwner(this, false, LookMode.Deep);
+ }
+
+ public Pawn StoredCasterPawn => innerContainer.Count > 0 ? innerContainer[0] as Pawn : null;
+
+ public IThingHolder ParentHolder => this.pawn;
+
+ public void GetChildHolders(List outChildren)
+ {
+ ThingOwnerUtility.AppendThingHoldersFromThings(outChildren, this.GetDirectlyHeldThings());
+ }
+
+ public ThingOwner GetDirectlyHeldThings()
+ {
+ return innerContainer;
+ }
+
+ public override void PostAdd(DamageInfo? dinfo)
+ {
+ base.PostAdd(dinfo);
+
+ if (this.originalCaster == null)
+ {
+ Log.Error("Hediff_Possession was added without an original caster.");
+ return;
+ }
+
+ this.innerContainer.TryAdd(this.originalCaster);
+ PawnDataUtility.TransferSoul(this.originalCaster, this.pawn);
+
+ if (!this.originalCaster.Destroyed)
+ {
+ this.originalCaster.Destroy(DestroyMode.Vanish);
+ }
+
+ Log.Message($"{this.pawn.LabelShort} has been possessed by {StoredCasterPawn.LabelShort}!");
+ }
+
+ public override void Notify_PawnDied(DamageInfo? dinfo, Hediff culprit = null)
+ {
+ base.Notify_PawnDied(dinfo, culprit);
+
+ Pawn deadBody = this.pawn;
+ Pawn storedCaster = this.StoredCasterPawn;
+
+ if (storedCaster == null)
+ {
+ 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 SetCaster(Pawn caster)
+ {
+ this.originalCaster = caster;
+ }
+
+ public void EjectContents()
+ {
+ if (StoredCasterPawn != null)
+ {
+ this.innerContainer.TryDropAll(this.pawn.Position, this.pawn.Map, ThingPlaceMode.Near);
+ }
+ }
+
+ public override void ExposeData()
+ {
+ base.ExposeData();
+ Scribe_Deep.Look(ref innerContainer, "innerContainer", this);
+ Scribe_References.Look(ref originalCaster, "originalCaster");
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/ArachnaeSwarm/Possession/PawnDataUtility.cs b/Source/ArachnaeSwarm/Possession/PawnDataUtility.cs
new file mode 100644
index 0000000..6b78f42
--- /dev/null
+++ b/Source/ArachnaeSwarm/Possession/PawnDataUtility.cs
@@ -0,0 +1,106 @@
+using System.Collections.Generic;
+using RimWorld;
+using Verse;
+
+namespace ArachnaeSwarm.Possession
+{
+ public static class PawnDataUtility
+ {
+ public static void TransferSoul(Pawn soulSource, Pawn bodyTarget)
+ {
+ if (soulSource == null || bodyTarget == null)
+ {
+ Log.Error("Cannot transfer soul: source or target is null.");
+ return;
+ }
+
+ Log.Message($"Beginning soul transfer from {soulSource.LabelShort} to {bodyTarget.LabelShort}.");
+
+ // Name
+ bodyTarget.Name = soulSource.Name;
+
+ // Story (Backstory and Traits)
+ bodyTarget.story.Childhood = soulSource.story.Childhood;
+ bodyTarget.story.Adulthood = soulSource.story.Adulthood;
+ bodyTarget.story.traits.allTraits.Clear();
+ foreach (Trait trait in soulSource.story.traits.allTraits)
+ {
+ bodyTarget.story.traits.GainTrait(trait);
+ }
+
+ // Skills
+ bodyTarget.skills.skills.Clear();
+ foreach (SkillRecord skill in soulSource.skills.skills)
+ {
+ SkillRecord newSkill = new SkillRecord(bodyTarget, skill.def)
+ {
+ levelInt = skill.levelInt,
+ xpSinceLastLevel = skill.xpSinceLastLevel,
+ passion = skill.passion
+ };
+ bodyTarget.skills.skills.Add(newSkill);
+ }
+
+ // Faction
+ if (bodyTarget.Faction != soulSource.Faction)
+ {
+ bodyTarget.SetFaction(soulSource.Faction, soulSource);
+ }
+
+ // Thoughts and Memories
+ if (bodyTarget.needs.mood?.thoughts?.memories != null)
+ {
+ bodyTarget.needs.mood.thoughts.memories.Memories.Clear();
+ }
+ if (soulSource.needs.mood?.thoughts?.memories != null)
+ {
+ foreach (Thought_Memory memory in soulSource.needs.mood.thoughts.memories.Memories)
+ {
+ bodyTarget.needs.mood.thoughts.memories.TryGainMemory(memory);
+ }
+ }
+
+ // Work Settings
+ if (soulSource.workSettings != null && bodyTarget.workSettings != null)
+ {
+ bodyTarget.workSettings.EnableAndInitialize();
+ foreach (WorkTypeDef workDef in DefDatabase.AllDefs)
+ {
+ bodyTarget.workSettings.SetPriority(workDef, soulSource.workSettings.GetPriority(workDef));
+ }
+ }
+
+ // Timetable
+ if (soulSource.timetable != null && bodyTarget.timetable != null)
+ {
+ bodyTarget.timetable.times = new List(soulSource.timetable.times);
+ }
+
+ // Social Relations
+ if (soulSource.relations != null && bodyTarget.relations != null)
+ {
+ bodyTarget.relations.ClearAllRelations();
+ foreach (DirectPawnRelation relation in soulSource.relations.DirectRelations)
+ {
+ bodyTarget.relations.AddDirectRelation(relation.def, relation.otherPawn);
+ }
+ }
+
+ // Guest status
+ if (soulSource.guest != null && bodyTarget.guest != null)
+ {
+ bodyTarget.guest.SetGuestStatus(soulSource.guest.HostFaction, soulSource.guest.GuestStatus);
+ if (soulSource.guest.IsPrisoner)
+ {
+ bodyTarget.guest.SetExclusiveInteraction(soulSource.guest.ExclusiveInteractionMode);
+ }
+ bodyTarget.guest.joinStatus = soulSource.guest.joinStatus;
+ }
+
+ // Refresh the UI and game state to reflect the changes.
+ bodyTarget.Drawer.renderer.SetAllGraphicsDirty();
+
+ Log.Message("Soul transfer complete.");
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/Documents/Human.xml b/Source/Documents/Human.xml
new file mode 100644
index 0000000..2879174
--- /dev/null
+++ b/Source/Documents/Human.xml
@@ -0,0 +1,787 @@
+
+ Human
+ 2
+ Human846
+
+ (146, 0, 131)
+ 1
+ Faction_18
+
+ 0
+ -1
+ True
+ null
+ -9999999
+
+
+
+
+
+ base
+ hair
+ skin
+ skinBase
+ tattoo
+ favorite
+ ideo
+ mech
+
+
+
+ RGBA(1.000, 1.000, 1.000, 1.000)
+ RGBA(1.000, 1.000, 1.000, 1.000)
+
+
+ RGBA(0.343, 0.310, 0.288, 1.000)
+
+
+ RGBA(1.000, 0.937, 0.788, 1.000)
+
+
+ RGBA(1.000, 0.937, 0.788, 1.000)
+
+
+ RGBA(1.000, 0.937, 0.788, 0.800)
+
+
+ RGBA(0.890, 0.451, 1.000, 1.000)
+ RGBA(0.890, 0.451, 1.000, 1.000)
+
+
+ RGBA(0.600, 0.500, 0.900, 1.000)
+ RGBA(0.549, 0.458, 0.824, 1.000)
+
+
+ RGBA(0.000, 0.737, 0.847, 1.000)
+
+
+
+
+
+
+
+ 0
+ 0
+ 0
+ 0
+
+ Colonist
+ Female
+
+ Yue
+ Moon
+ Ren
+
+ null
+
+ null
+ null
+ null
+ null
+ null
+ (0, 0, 0)
+
+
+
+
+ Idle
+ 6673
+ -99999
+ -99999
+ True
+ -99999
+
+
+
+
+
+
+
+
+
+
+ (-1000, -1000, -1000)
+
+ -99999
+
+
+
+
+
+
+
+
+
+
+ 0
+ 16777
+
+
+
+ null
+ null
+ null
+ null
+ null
+ GotoWander
+ 221
+ (156, 0, 116)
+
+
+
+ 1576
+ True
+
+ Walk
+ Humanlike
+ -1
+ null
+ null
+ -1
+ -1672709817
+
+
+ 0
+ -203
+ 1576
+ null
+
+
+
+
+ -1
+
+
+
+ True
+
+
+
+
+
+
+
+
+
+ True
+
+
+
+
+
+
+
+
+ Thing_Human846_0_Smash
+ (0, 0, 0)
+ (0, 0, 0)
+ -999999
+ True
+
+
+ Thing_Human846_1_Smash
+ (0, 0, 0)
+ (0, 0, 0)
+ -999999
+ True
+
+
+ Thing_Human846_2_Bite
+ (0, 0, 0)
+ (0, 0, 0)
+ -999999
+ True
+
+
+ Thing_Human846_3_Smash
+ (0, 0, 0)
+ (0, 0, 0)
+ -999999
+ True
+
+
+
+
+
+
+
+
+
+ null
+
+
+
+
+ (147, 0, 130)
+ 48
+ 50
+ OnCell
+ 4
+ 1777
+ 1779
+ (156, 0, 116)
+
+
+
+ 1
+
+
+
+
+
+
+
+ Apparel_Pants
+ Apparel_Pants847
+ 130
+ 1
+ Synthread
+
+ -1
+ Normal
+ null
+ True
+
+
+
+ Apparel_Parka
+ Apparel_Parka848
+ 235
+ 1
+ Synthread
+
+ -1
+ Normal
+ null
+ True
+
+
+
+
+
+ 111
+
+
+ Fat
+ Elisabeth
+ RGBA(0.343, 0.310, 0.288, 1.000)
+
+
+
+ SpeedOffset
+ null
+ -1
+ null
+
+
+ Jealous
+ null
+ null
+
+
+
+ Ren
+ LightPurple
+ Female_AveragePointy
+ WarRefugee51
+ MedievalMinstrel95
+
+
+
+
+
+ null
+
+
+
+
+
+ 139350193
+ -297730916
+ 1
+ 9223372036854775807
+ 141208416
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 1840
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+
+
+ null
+
+
+
+
+
+
+
+
+
+ Filth_Sand
+
+
+ Filth_Sand
+ Filth_Sand14506
+
+ -1
+
+
+
+
+ null
+
+
+
+
+
+ Mood
+ 0.586400032
+
+
+
+
+ CrashedTogether
+ null
+ Thing_Human849
+ 1800
+ 25
+
+
+ CrashedTogether
+ null
+ Thing_Human852
+ 1800
+ 25
+
+
+ NewColonyOptimism
+ null
+ null
+ 1800
+
+
+ Chitchat
+ null
+ Thing_Human852
+ 0.773389459
+ 1650
+ 0.510437071
+
+
+
+
+
+ 1711
+ 1711
+
+
+
+ Food
+ 0.751999915
+ 1711
+
+
+ Rest
+ 0.879273593
+
+
+ Joy
+ 0.513921499
+
+
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+
+
+
+
+ False
+ False
+ False
+ False
+ False
+ False
+ False
+ False
+ False
+ False
+
+
+
+
+ Beauty
+ 0.442400098
+
+
+ Comfort
+ 0.47119987
+
+
+ Outdoors
+ 1
+
+
+ DrugDesire
+ 0.5
+
+
+ RoomSize
+ 1
+
+
+
+
+ null
+ null
+ JoinAsColonist
+ MaintainOnly
+ NoInteraction
+ (-1000, -1000, -1000)
+ -1
+ null
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ExSpouse
+ Thing_Human849
+
+
+ Parent
+ Thing_Human853
+
+
+ Parent
+ Thing_Human857
+
+
+
+ null
+
+
+
+
+ -1
+
+
+ True
+
+
+
+ null
+ null
+ null
+ null
+ null
+
+
+ Chitchat
+ 121
+ Chitchat
+
+
+
+
+ Shooting
+ 3
+
+
+ Melee
+ 5
+
+
+ Construction
+ 2
+
+
+ Mining
+ 2
+
+
+ Cooking
+ 7
+
+
+ Plants
+ 1
+
+
+ Animals
+ 3
+ Minor
+
+
+ Crafting
+ 3
+
+
+ Artistic
+ 5
+ Minor
+
+
+ Medicine
+ 2
+ Minor
+
+
+ Social
+ 7
+ Major
+
+
+ Intellectual
+
+
+ -1
+
+
+
+
+
+ Ideo_10
+
+ 0.605378687
+
+
+
+
+
+ 3
+ 3
+ 0
+ 3
+ 3
+ 3
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 3
+ 0
+ 3
+ 0
+ 0
+
+
+
+
+
+ ApparelPolicy_任意_1
+
+
+
+
+
+ DrugPolicy_社交成瘾品_1
+
+
+
+ null
+
+
+
+
+ Sleep
+ Sleep
+ Sleep
+ Sleep
+ Sleep
+ Sleep
+ Anything
+ Anything
+ Anything
+ Anything
+ Anything
+ Anything
+ Anything
+ Anything
+ Anything
+ Anything
+ Anything
+ Anything
+ Anything
+ Anything
+ Anything
+ Anything
+ Sleep
+ Sleep
+
+
+
+ Best
+
+
+
+
+ null
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Skin_Melanin3
+ Thing_Human846
+ null
+ 132
+
+
+ Hair_MidBlack
+ Thing_Human846
+ null
+ 133
+
+
+ Baseliner
+
+
+
+ null
+
+
+
+
+
\ No newline at end of file
diff --git a/Source/Documents/PawnStorageInHediff_Analysis.md b/Source/Documents/PawnStorageInHediff_Analysis.md
new file mode 100644
index 0000000..62cdef5
--- /dev/null
+++ b/Source/Documents/PawnStorageInHediff_Analysis.md
@@ -0,0 +1,188 @@
+# RimWorld Modding: 利用Hediff存储Pawn的深度解析
+
+在RimWorld的Mod开发中,有时需要将一个`Pawn`(人物、动物等)从游戏世界中临时移除,并将其数据完整保存起来,之后再释放回游戏中。一个非常精妙且强大的实现方式就是让`Hediff`(健康效果)扮演一个“容器”的角色。
+
+本文档将以`HediffAbility_PaintedSkin`为例,深入剖析其实现`Pawn`存储的核心机制。
+
+## 核心概念
+
+该功能主要依赖于RimWorld框架中的两个核心组件:
+
+1. **`IThingHolder`接口**: 一个对象(如建筑、Hediff、Pawn的装备栏等)如果实现了这个接口,就等于向游戏声明:“我是一个可以容纳其他物品(`Thing`)的容器”。
+2. **`ThingOwner`类**: 这是实现存储功能的“袋子”。它是一个专门用于管理一组`Thing`对象的集合,并负责处理这些物品的保存、加载和所有权关系。
+
+## 案例分析: `HediffAbility_PaintedSkin`
+
+以下是`HediffAbility_PaintedSkin`的完整源代码,它完美地展示了如何利用`Hediff`来存储一个`Pawn`。
+
+```csharp
+using System;
+using System.Collections.Generic;
+using RimWorld;
+using Verse;
+using Verse.AI;
+using Verse.Sound;
+
+namespace RigorMortis
+{
+ public class HediffAbility_PaintedSkin : HediffWithComps, IHediffAbility, IThingHolder
+ {
+ // 1. 核心存储容器
+ protected ThingOwner innerContainer;
+
+ private CompYinAndMalevolent compYin;
+ public Pawn victim;
+
+ // 构造函数:初始化容器
+ public HediffAbility_PaintedSkin()
+ {
+ // 'this'表示容器的所有者是当前Hediff实例
+ // 'LookMode.Deep'是关键,确保能完整保存Pawn的所有数据
+ this.innerContainer = new ThingOwner(this, false, LookMode.Deep, true);
+ }
+
+ // --- IThingHolder 接口实现 ---
+
+ public IThingHolder ParentHolder
+ {
+ get
+ {
+ // 对于Hediff来说,它的父容器就是持有它的Pawn
+ return this.pawn;
+ }
+ }
+
+ public void GetChildHolders(List outChildren)
+ {
+ ThingOwnerUtility.AppendThingHoldersFromThings(outChildren, this.GetDirectlyHeldThings());
+ }
+
+
+
+ public ThingOwner GetDirectlyHeldThings()
+ {
+ return this.innerContainer;
+ }
+
+ // --- 容器内容访问 ---
+
+ public Thing ContainedThing
+ {
+ get
+ {
+ return this.innerContainer.Count > 0 ? this.innerContainer[0] : null;
+ }
+ }
+
+ public Pawn Zombie
+ {
+ get
+ {
+ // 提供一个便捷的属性来访问被存储的Pawn
+ return this.ContainedThing as Pawn;
+ }
+ }
+
+ public bool HasAnyContents
+ {
+ get
+ {
+ return this.innerContainer.Count > 0;
+ }
+ }
+
+ // --- 存入/取出逻辑 ---
+
+ public virtual bool Accepts(Thing thing)
+ {
+ return this.innerContainer.CanAcceptAnyOf(thing, true);
+ }
+
+ public virtual bool TryAcceptThing(Thing thing, bool allowSpecialEffects = true)
+ {
+ if (!this.Accepts(thing))
+ {
+ return false;
+ }
+ bool flag;
+ if (thing.holdingOwner != null)
+ {
+ // 将Pawn从当前持有者(通常是地图)转移到我们的容器中
+ thing.holdingOwner.TryTransferToContainer(thing, this.innerContainer, thing.stackCount, true);
+ flag = true;
+ }
+ else
+ {
+ // 如果Pawn没有持有者(例如是新生成的),直接添加
+ flag = this.innerContainer.TryAdd(thing, true);
+ }
+ return flag;
+ }
+
+ public virtual void EjectContents()
+ {
+ // 决定在何处释放Pawn
+ Map map = this.pawn.MapHeld ?? Find.AnyPlayerHomeMap;
+ IntVec3 cell = (this.pawn.Spawned || (this.pawn.Corpse != null && this.pawn.Corpse.Spawned)) ? this.pawn.PositionHeld : ((this.pawn.CarriedBy != null) ? this.pawn.CarriedBy.PositionHeld : map.Center);
+
+ // 将容器内的所有东西(即被存储的Pawn)扔到地图上
+ this.innerContainer.TryDropAll(cell, map, ThingPlaceMode.Direct, null, null, true);
+ }
+
+ // --- 存档/读档 ---
+
+ public override void ExposeData()
+ {
+ base.ExposeData();
+ Scribe_References.Look(ref this.victim, "victim", false);
+
+ // 2. 深度保存容器内容
+ // 'Scribe_Deep.Look' 会序列化容器内的Pawn的所有数据
+ Scribe_Deep.Look(ref this.innerContainer, "innerContainer", new object[]
+ {
+ this
+ });
+
+ // 兼容性处理:确保旧存档在加载后也能正确初始化容器
+ if (Scribe.mode == LoadSaveMode.PostLoadInit)
+ {
+ if (this.innerContainer == null)
+ {
+ this.innerContainer = new ThingOwner(this, false, LookMode.Deep, true);
+ }
+ }
+ }
+
+ // --- 其他逻辑 ---
+ // (为了简洁,此处省略了PostTick, End, AbsolutelyKill等与存储机制非直接相关的代码)
+ // ...
+ }
+}
+```
+
+## 机制剖析
+
+### 1. 声明容器身份 (`IThingHolder`)
+
+通过在类声明中加入 `IThingHolder`,`HediffAbility_PaintedSkin` 就获得了“容器”的资格。这要求它必须实现接口定义的属性和方法,如 `ParentHolder` 和 `GetDirectlyHeldThings()`。`GetDirectlyHeldThings()` 方法必须返回真正的存储实例,也就是我们的 `innerContainer`。
+
+### 2. 初始化存储核心 (`ThingOwner`)
+
+在构造函数中,我们创建了一个 `ThingOwner` 实例。这里的关键在于 `LookMode.Deep` 参数。
+
+* `LookMode.Value`: 只保存简单值类型(如int, float, string)。
+* `LookMode.Reference`: 只保存一个对物体的引用ID。加载时,游戏会尝试在世界中找到这个ID对应的物体。如果物体已被销毁,引用会丢失。**这不适用于存储Pawn**,因为Pawn在被存入容器时已经从世界中移除了。
+* **`LookMode.Deep`**: 这才是我们的选择。它告诉序列化系统:“请将这个物体(`Pawn`)的所有数据——健康、技能、装备、Hediff、人际关系、思想等等——完完整整地打包保存起来。” 当游戏加载时,它会用这些数据重建一个一模一样的`Pawn`实例。
+
+### 3. 序列化 (`ExposeData`)
+
+`ExposeData` 方法是RimWorld存档机制的核心。
+
+* `Scribe_Deep.Look(ref this.innerContainer, ...)`: 这行代码是魔法发生的地方。当游戏保存时,`Scribe_Deep` 会深入到 `innerContainer` 内部,并因为我们之前设置了 `LookMode.Deep`,它会对容器里的每一个 `Pawn` 进行递归式的深度保存。
+* 当游戏加载时,`Scribe_Deep` 会读取存档中的数据,重建 `innerContainer`,并利用深度保存的数据重建一个与存入时状态完全一致的 `Pawn`。
+
+## 总结
+
+通过实现 `IThingHolder` 接口并利用一个配置为 `LookMode.Deep` 的 `ThingOwner` 容器,我们可以将一个 `Hediff` 转变为一个功能强大的、能够随宿主移动的“Pawn胶囊”。这个“胶囊”可以安全地携带一个`Pawn`穿越存档的海洋,确保其数据的完整性和一致性。
+
+这项技术是实现诸如吞噬、俘获、传送、特殊休眠仓等高级Mod功能的基石。
\ No newline at end of file
diff --git a/Source/Documents/Possession_Implementation_Guide.md b/Source/Documents/Possession_Implementation_Guide.md
new file mode 100644
index 0000000..d10dcdc
--- /dev/null
+++ b/Source/Documents/Possession_Implementation_Guide.md
@@ -0,0 +1,569 @@
+# “抱脸虫夺舍”技能实现说明书 (V3 - 最终版)
+
+## 1. 功能概述
+
+本功能实现了一个名为“阿拉克涅寄生”的特殊技能,允许一个特定的“阿拉克涅原虫”(抱脸虫)Pawn将自己的意识(灵魂)注入另一个生物的身体,从而完全占据并控制它。
+
+**核心玩法循环:**
+1. **夺舍**: 抱脸虫使用技能后,其物理实体消失,其“灵魂”(名字、背景、技能、特性等)将完全覆盖目标的身体。
+2. **成长**: 玩家将控制这个新的身体进行游戏,所有获得的经验、技能和记忆都将积累在这个身体上。
+3. **重生**: 当被夺舍的身体死亡时,抱脸虫的灵魂会带着所有新的成长,从尸体中“重生”,变回一个独立的、更强大的阿拉克涅原虫Pawn,准备寻找下一个宿主。
+
+这是一个高风险、高回报的玩法,允许玩家以一种独特的方式延续一个核心角色的“生命”和成长。
+
+## 2. 实现流程
+
+```mermaid
+graph TD
+ A[抱脸虫] -- 使用“阿拉克涅寄生”技能 --> B(目标Pawn);
+ B -- 添加 Hediff_Possession --> C{执行灵魂覆盖};
+ C -- 1. 存储抱脸虫Pawn的完整数据 --> D[Hediff容器];
+ C -- 2. 将抱脸虫的“灵魂”数据覆盖到目标身上 --> E[被夺舍的身体];
+ D -- 3. 抱脸虫物理实体消失 --> F([Vanish]);
+ E -- 玩家控制,积累经验和记忆 --> E;
+ E -- 受到致命伤害 --> G{身体死亡};
+ G -- 触发Hediff.Notify_PawnDied --> H{反向同步成长};
+ D -- 获取存储的抱脸虫数据 --> H;
+ E -- 获取身体上的成长数据 --> H;
+ H -- 将成长更新到抱脸虫数据上 --> I[更新后的抱脸虫];
+ I -- 从Hediff中释放 --> J(更强大的抱脸虫重生);
+ J -- 等待下一次夺舍 --> A;
+```
+
+---
+
+## 3. 代码详解
+
+### 3.1 `CompAbilityEffect_Possess.cs` - 技能效果的起点
+
+这是技能被使用时第一个被调用的C#文件。它的职责是创建`Hediff_Possession`并将其附加到目标身上,从而启动整个夺舍流程。
+
+```csharp
+// 路径: Source/ArachnaeSwarm/Possession/CompAbilityEffect_Possess.cs
+using RimWorld;
+using Verse;
+
+namespace ArachnaeSwarm.Possession
+{
+ // 继承自CompAbilityEffect,这是所有技能效果组件的基类
+ public class CompAbilityEffect_Possess : CompAbilityEffect
+ {
+ // 当技能成功施放时,游戏会调用这个Apply方法
+ public override void Apply(LocalTargetInfo target, LocalTargetInfo dest)
+ {
+ base.Apply(target, dest); // 调用基类方法,确保标准流程执行
+
+ // 获取施法者 (我们的抱脸虫)
+ Pawn caster = this.parent.pawn;
+ // 获取目标Pawn
+ Pawn targetPawn = target.Pawn;
+
+ // 安全检查:如果目标不是一个Pawn,则直接返回
+ if (targetPawn == null)
+ {
+ return;
+ }
+
+ // TODO: 在此可以添加更多的限制条件,例如:
+ // 1. 不能夺舍机械体
+ // if (targetPawn.RaceProps.IsMechanoid) { ... }
+ // 2. 不能夺舍已经被夺舍的目标
+ // if (targetPawn.health.hediffSet.HasHediff(HediffDef.Named("ARA_Possession"))) { ... }
+
+ // 步骤1: 创建Hediff实例
+ // HediffMaker.MakeHediff会根据XML定义创建一个新的Hediff对象
+ Hediff_Possession hediff = (Hediff_Possession)HediffMaker.MakeHediff(HediffDef.Named("ARA_Possession"), targetPawn);
+
+ // 步骤2: 注入施法者灵魂
+ // 在Hediff被正式添加到目标身上之前,将施法者的引用传递进去。
+ // 这是关键一步,确保Hediff在执行PostAdd逻辑时能知道谁是施法者。
+ hediff.SetCaster(caster);
+
+ // 步骤3: 将Hediff添加到目标身上
+ // 这会触发Hediff_Possession类中的PostAdd方法,从而启动真正的夺舍逻辑。
+ targetPawn.health.AddHediff(hediff);
+ }
+ }
+}
+```
+
+### 3.2 `Hediff_Possession.cs` - 夺舍与重生的核心
+
+这个Hediff是整个功能的核心。它作为“灵魂容器”,负责存储抱脸虫的本体,并在恰当的时机执行“夺舍”和“重生”的逻辑。
+
+```csharp
+// 路径: Source/ArachnaeSwarm/Possession/Hediff_Possession.cs
+using System.Collections.Generic;
+using RimWorld;
+using Verse;
+
+namespace ArachnaeSwarm.Possession
+{
+ // 继承HediffWithComps以支持组件,并实现IThingHolder接口来表明自己是容器
+ public class Hediff_Possession : HediffWithComps, IThingHolder
+ {
+ // --- 核心字段 ---
+ private ThingOwner innerContainer; // 实际存储灵魂(抱脸虫Pawn)的容器
+ private Pawn originalCaster; // 临时保存对原始施法者的引用
+
+ // --- 构造与属性 ---
+ public Hediff_Possession()
+ {
+ // 初始化容器。LookMode.Deep是关键,它能确保Pawn的所有数据都被完整保存。
+ this.innerContainer = new ThingOwner(this, false, LookMode.Deep);
+ }
+
+ // 提供一个方便的只读属性来获取容器中存储的Pawn
+ public Pawn StoredCasterPawn => innerContainer.Count > 0 ? innerContainer[0] as Pawn : null;
+
+ // --- IThingHolder 接口实现 ---
+ public IThingHolder ParentHolder => this.pawn; // 容器的父级就是持有该Hediff的Pawn
+
+ public void GetChildHolders(List outChildren)
+ {
+ ThingOwnerUtility.AppendThingHoldersFromThings(outChildren, this.GetDirectlyHeldThings());
+ }
+
+ public ThingOwner GetDirectlyHeldThings()
+ {
+ return innerContainer;
+ }
+
+ // --- 核心逻辑 ---
+
+ // 当Hediff被成功添加到目标身上后,此方法被自动调用
+ public override void PostAdd(DamageInfo? dinfo)
+ {
+ base.PostAdd(dinfo);
+
+ if (this.originalCaster == null)
+ {
+ Log.Error("Hediff_Possession was added without an original caster.");
+ return;
+ }
+
+ // 1. 存储灵魂:将施法者的Pawn对象完整地存入容器
+ this.innerContainer.TryAdd(this.originalCaster);
+
+ // 2. 灵魂覆盖:调用工具类,执行数据迁移
+ PawnDataUtility.TransferSoul(this.originalCaster, this.pawn);
+
+ // 3. 销毁施法者的物理实体,因为它现在“活”在目标的身体里了
+ if (!this.originalCaster.Destroyed)
+ {
+ this.originalCaster.Destroy(DestroyMode.Vanish);
+ }
+
+ Log.Message($"{this.pawn.LabelShort} has been possessed by {StoredCasterPawn.LabelShort}!");
+ }
+
+ // 当持有此Hediff的Pawn(即宿主)死亡时,此方法被自动调用
+ public override void Notify_PawnDied(DamageInfo? dinfo, Hediff culprit = null)
+ {
+ base.Notify_PawnDied(dinfo, culprit);
+
+ Pawn deadBody = this.pawn;
+ Pawn storedCaster = this.StoredCasterPawn;
+
+ if (storedCaster == null)
+ {
+ 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.");
+
+ // 1. 灵魂更新:反向调用工具类,将宿主身体上的成长同步回抱脸虫的灵魂
+ PawnDataUtility.TransferSoul(deadBody, storedCaster);
+
+ // 2. 重生:将更新后的抱脸虫灵魂从容器中释放到地图上
+ this.EjectContents();
+ }
+
+ // --- 公共方法 ---
+
+ // 由CompAbilityEffect调用,用于在添加Hediff前设置施法者
+ public void SetCaster(Pawn caster)
+ {
+ this.originalCaster = caster;
+ }
+
+ // 将容器内的东西(抱脸虫)扔到地图上
+ public void EjectContents()
+ {
+ if (StoredCasterPawn != null)
+ {
+ this.innerContainer.TryDropAll(this.pawn.Position, this.pawn.Map, ThingPlaceMode.Near);
+ }
+ }
+
+ // --- 存档/读档 ---
+ public override void ExposeData()
+ {
+ base.ExposeData();
+ // Scribe_Deep是关键,确保容器内的Pawn被深度保存
+ Scribe_Deep.Look(ref innerContainer, "innerContainer", this);
+ // 保存对原始施法者的引用(虽然它很快会被销毁,但以防万一)
+ Scribe_References.Look(ref originalCaster, "originalCaster");
+ }
+ }
+}
+```
+
+### 3.3 `PawnDataUtility.cs` - “灵魂”数据迁移的执行者
+
+这是一个静态工具类,集中处理所有与Pawn数据复制相关的复杂逻辑,使得其他部分的代码更整洁。
+
+```csharp
+// 路径: Source/ArachnaeSwarm/Possession/PawnDataUtility.cs
+using System.Collections.Generic;
+using RimWorld;
+using Verse;
+
+namespace ArachnaeSwarm.Possession
+{
+ public static class PawnDataUtility
+ {
+ // 核心方法:将soulSource的“灵魂”数据转移到bodyTarget上
+ public static void TransferSoul(Pawn soulSource, Pawn bodyTarget)
+ {
+ if (soulSource == null || bodyTarget == null)
+ {
+ Log.Error("Cannot transfer soul: source or target is null.");
+ return;
+ }
+
+ Log.Message($"Beginning soul transfer from {soulSource.LabelShort} to {bodyTarget.LabelShort}.");
+
+ // --- 1. 核心身份数据 ---
+
+ // 姓名
+ bodyTarget.Name = soulSource.Name;
+
+ // 故事 (背景和特性)
+ bodyTarget.story.Childhood = soulSource.story.Childhood;
+ bodyTarget.story.Adulthood = soulSource.story.Adulthood;
+ // 先清空目标的所有特性,再逐一添加源的特性
+ bodyTarget.story.traits.allTraits.Clear();
+ foreach (Trait trait in soulSource.story.traits.allTraits)
+ {
+ bodyTarget.story.traits.GainTrait(trait);
+ }
+
+ // 技能
+ // 同样地,清空后逐一添加
+ bodyTarget.skills.skills.Clear();
+ foreach (SkillRecord skill in soulSource.skills.skills)
+ {
+ SkillRecord newSkill = new SkillRecord(bodyTarget, skill.def)
+ {
+ levelInt = skill.levelInt,
+ xpSinceLastLevel = skill.xpSinceLastLevel,
+ passion = skill.passion
+ };
+ bodyTarget.skills.skills.Add(newSkill);
+ }
+
+ // 阵营
+ if (bodyTarget.Faction != soulSource.Faction)
+ {
+ bodyTarget.SetFaction(soulSource.Faction, soulSource);
+ }
+
+ // --- 2. 思想、社交和设定 ---
+
+ // 思想和记忆
+ // 清空目标的记忆,然后复制源的记忆
+ if (bodyTarget.needs.mood?.thoughts?.memories != null)
+ {
+ bodyTarget.needs.mood.thoughts.memories.Memories.Clear();
+ }
+ if (soulSource.needs.mood?.thoughts?.memories != null)
+ {
+ foreach (Thought_Memory memory in soulSource.needs.mood.thoughts.memories.Memories)
+ {
+ bodyTarget.needs.mood.thoughts.memories.TryGainMemory(memory);
+ }
+ }
+
+ // 工作设置
+ if (soulSource.workSettings != null && bodyTarget.workSettings != null)
+ {
+ bodyTarget.workSettings.EnableAndInitialize();
+ foreach (WorkTypeDef workDef in DefDatabase.AllDefs)
+ {
+ bodyTarget.workSettings.SetPriority(workDef, soulSource.workSettings.GetPriority(workDef));
+ }
+ }
+
+ // 时间表
+ if (soulSource.timetable != null && bodyTarget.timetable != null)
+ {
+ bodyTarget.timetable.times = new List(soulSource.timetable.times);
+ }
+
+ // 社交关系 (简化处理)
+ // 警告: 直接复制关系可能很危险,这里采用清空再添加直接关系的方式
+ if (soulSource.relations != null && bodyTarget.relations != null)
+ {
+ bodyTarget.relations.ClearAllRelations();
+ foreach (DirectPawnRelation relation in soulSource.relations.DirectRelations)
+ {
+ bodyTarget.relations.AddDirectRelation(relation.def, relation.otherPawn);
+ }
+ }
+
+ // 访客/囚犯状态
+ if (soulSource.guest != null && bodyTarget.guest != null)
+ {
+ // 使用游戏提供的标准方法来设置,而不是直接赋值
+ bodyTarget.guest.SetGuestStatus(soulSource.guest.HostFaction, soulSource.guest.GuestStatus);
+ if (soulSource.guest.IsPrisoner)
+ {
+ bodyTarget.guest.SetExclusiveInteraction(soulSource.guest.ExclusiveInteractionMode);
+ }
+ bodyTarget.guest.joinStatus = soulSource.guest.joinStatus;
+ }
+
+ // --- 3. 收尾工作 ---
+
+ // 强制刷新Pawn的渲染缓存,确保外观(如名字)能立刻更新
+ bodyTarget.Drawer.renderer.SetAllGraphicsDirty();
+
+ Log.Message("Soul transfer complete.");
+ }
+ }
+}
+```
+
+### 3.4 `CompProperties_AbilityPossess.cs` - 技能属性类
+
+这是一个简单的属性类,用于将我们的技能效果组件(`CompAbilityEffect_Possess`)连接到XML定义上。
+
+```csharp
+// 路径: Source/ArachnaeSwarm/Possession/CompProperties_AbilityPossess.cs
+using RimWorld;
+
+namespace ArachnaeSwarm.Possession
+{
+ // CompProperties类用于在XML中配置Comp组件的参数
+ public class CompProperties_AbilityPossess : CompProperties_AbilityEffect
+ {
+ public CompProperties_AbilityPossess()
+ {
+ // 将这个属性类与我们的技能效果实现类关联起来
+ this.compClass = typeof(CompAbilityEffect_Possess);
+ }
+ }
+}
+```
+
+---
+
+## 4. XML 定义详解
+
+### 4.1 `ARA_Hediffs_Possession.xml` - 定义Hediff
+
+```xml
+
+
+
+
+
+
+ ARA_Possession
+
+
+
+ 这个生物的身体正被另一个实体所控制。
+
+ ArachnaeSwarm.Possession.Hediff_Possession
+
+ false
+ false
+ 1.0
+
+
+
+
+
+
+
+
+```
+
+### 4.2 `ARA_Possession_Defs.xml` - 定义技能、种族和身体
+
+这个文件定义了技能本身,以及我们的“阿拉克涅原虫”作为一个完整的生物所需的一切。
+
+```xml
+
+
+
+
+
+
+ ARA_Ability_Possess
+
+ 将你的意识注入另一个生物的身体,完全占据它。
+ UI/Abilities/Possess
+ 600
+
+ Verb_CastAbility
+ 1.5
+ 5.9
+
+ true
+ false
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+ ARA_Facehugger
+ Humanlike_PostMentalState
+ 100
+
+
+
+
+
+
+
+
+
+ ARA_Facehugger
+
+
+ ARA_FacehuggerRace
+ 25
+
+
+
+ Things/Pawn/Animal/ARA_Facehugger
+ 0.8
+
+
+ Things/Pawn/Animal/Dessicated/CritterDessicated
+ 0.8
+
+
+
+ ARA_Facehugger
+
+
+ ARA_Ability_Possess
+
+
+
+
+
+ ARA_FacehuggerRace
+
+ 一种小型的、脆弱的寄生生物,其唯一的生存目的就是寻找并占据一个更强大的宿主。它通过将自己的意识注入目标来完成这一过程。
+
+ 4.0
+ 50
+ -10
+ 50
+
+
+
+
+
+ Scratch
+
+ 2
+ 1.5
+
+
+
+ Animal
+
+ ARA_FacehuggerBody
+ 0.2
+ 0.3
+ 0.1
+
+
+ AnimalAdult
+ 0
+
+
+
+
+
+
+
+ ARA_FacehuggerBody
+
+
+ Body
+ 20
+ 20
+
+
+ Head
+ 0.3
+
+
+ Skull
+ 0.2
+ Inside
+
+
+ Brain
+ 0.1
+ Inside
+
+
+
+
+ Eye
+ left eye
+ 0.07
+
+
+ Eye
+ right eye
+ 0.07
+
+
+
+
+ Leg
+ front left leg
+ 0.1
+
+
+ Leg
+ front right leg
+ 0.1
+
+
+ Leg
+ rear left leg
+ 0.1
+
+
+ Leg
+ rear right leg
+ 0.1
+
+
+
+
+
+
+```
+
+---
+
+这份详尽的文档现在包含了我们所有的最终代码和XML,并附有详细的注释,解释了每一步的作用和它们之间的关联。
\ No newline at end of file