# “抱脸虫夺舍”技能实现说明书 (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`并将其附加到目标身上,从而启动整个夺舍流程。 ## 3. 最终数据迁移规范 (Final Data Transfer Specification) 通过对真实存档文件 (`Human.xml`) 的深度分析,我们最终确定了“灵魂”与“肉体”的数据边界。`PawnDataUtility.TransferSoul` 方法将严格遵循以下规范进行数据迁移: ### 3.1 必须复制的“灵魂”数据 这些数据定义了Pawn的身份、经历、思想和核心能力,将**完全从抱脸虫(源)复制到宿主(目标)**。 - **核心身份 (`Name`, `Story`, `Faction`)**: - `Name`: 姓名与昵称。 - `Story`: 童年和成年背景 (`Childhood`, `Adulthood`)。 - `Traits`: 所有特性。 - `Faction`: 所属阵营。 - **成长与经历 (`Skills`, `Records`)**: - `Skills`: 所有技能的等级、经验和热情。 - `Records`: 全部生平记录 (如击杀数、建造数等)。 - **思想与设定 (`Needs`, `WorkSettings`, etc.)**: - `Needs`: 主要是指`thoughts.memories` (思想和记忆)。 - `WorkSettings`: 工作优先级。 - `Timetable`: 时间表。 - `PlayerSettings`: 玩家设定 (如医疗策略)。 - `Ownership`: 对床、王座等的所有权。 - `Outfits` & `Drugs`: 穿着和药物策略。 - `FoodRestriction`: 食物策略。 - **DLC核心数据 (`Ideo`, `Royalty`)**: - `Ideo`: 完整的信仰体系。 - `Royalty`: 完整的贵族系统,包括头衔、恩惠、许可、灵能和相关技能 (`abilities`)。 - **社交 (`Relations`)**: - 将采用**简化处理**:清空目标的旧关系,然后只复制源的**非亲属**直接关系 (如朋友、对手、爱人)。这可以避免破坏家族树。 ### 3.2 必须保留的“肉体”数据 这些数据属于物理身体的范畴,在夺舍过程中将**完全保留宿主原有的数据**,不进行任何复制。 - **健康与生理 (`Health`, `Age`)**: - `Health`: 所有伤口、疤痕、疾病和植入物。 - `Age`: 生物年龄和时间年龄。 - **外观与基因 (`Style`, `Genes`, `BodyType`)**: - `Style`: 发型、胡须、纹身。 - `Genes`: 所有内生和异种基因。 - `BodyType`, `HeadType`, `HairColor`: 身体类型、头型和发色。 - **装备与物品 (`Apparel`, `Equipment`, `Inventory`)**: - `Apparel`: 身上穿着的衣物。 - `Equipment`: 手中持有的装备。 - `Inventory`: 物品栏中的物品。 - **物理状态 (`Position`, `Stances`, `Pather`)**: - Pawn在世界中的位置、姿态和寻路信息。 --- ```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,并附有详细的注释,解释了每一步的作用和它们之间的关联。