# 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功能的基石。