暂存
This commit is contained in:
Binary file not shown.
19
1.6/1.6/Defs/HediffDefs/ARA_Hediffs_Possession.xml
Normal file
19
1.6/1.6/Defs/HediffDefs/ARA_Hediffs_Possession.xml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<Defs>
|
||||||
|
|
||||||
|
<HediffDef>
|
||||||
|
<defName>ARA_Possession</defName>
|
||||||
|
<label>阿拉克涅原虫寄生</label>
|
||||||
|
<description>这个生物的身体正被另一个实体所控制。</description>
|
||||||
|
<hediffClass>ArachnaeSwarm.Possession.Hediff_Possession</hediffClass>
|
||||||
|
<isBad>false</isBad>
|
||||||
|
<scenarioCanAdd>false</scenarioCanAdd>
|
||||||
|
<maxSeverity>1.0</maxSeverity>
|
||||||
|
<stages>
|
||||||
|
<li>
|
||||||
|
<label>被寄生</label>
|
||||||
|
</li>
|
||||||
|
</stages>
|
||||||
|
</HediffDef>
|
||||||
|
|
||||||
|
</Defs>
|
||||||
166
1.6/1.6/Defs/Misc/ARA_Possession_Defs.xml
Normal file
166
1.6/1.6/Defs/Misc/ARA_Possession_Defs.xml
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<Defs>
|
||||||
|
|
||||||
|
<!-- ==================== Ability Def ==================== -->
|
||||||
|
|
||||||
|
<AbilityDef>
|
||||||
|
<defName>ARA_Ability_Possess</defName>
|
||||||
|
<label>阿拉克涅寄生</label>
|
||||||
|
<description>将你的意识注入另一个生物的身体,完全占据它。</description>
|
||||||
|
<iconPath>UI/Abilities/Possess</iconPath>
|
||||||
|
<cooldownTicks>600</cooldownTicks>
|
||||||
|
<verbProperties>
|
||||||
|
<verbClass>Verb_CastAbility</verbClass>
|
||||||
|
<warmupTime>1.5</warmupTime>
|
||||||
|
<range>5.9</range>
|
||||||
|
<targetParams>
|
||||||
|
<canTargetPawns>true</canTargetPawns>
|
||||||
|
<canTargetBuildings>false</canTargetBuildings>
|
||||||
|
<canTargetSelf>false</canTargetSelf>
|
||||||
|
<canTargetLocations>false</canTargetLocations>
|
||||||
|
</targetParams>
|
||||||
|
</verbProperties>
|
||||||
|
<comps>
|
||||||
|
<li Class="ArachnaeSwarm.Abilities.CompProperties_AbilityPossess"/>
|
||||||
|
</comps>
|
||||||
|
</AbilityDef>
|
||||||
|
|
||||||
|
<ThinkTreeDef>
|
||||||
|
<defName>ARA_Facehugger</defName>
|
||||||
|
<insertTag>Humanlike_PostMentalState</insertTag>
|
||||||
|
<insertPriority>100</insertPriority>
|
||||||
|
<thinkRoot Class="ThinkNode_Priority">
|
||||||
|
<subNodes>
|
||||||
|
<!-- TODO: Add custom AI logic for the facehugger if needed -->
|
||||||
|
<!-- e.g., seek out hosts when idle -->
|
||||||
|
<li Class="ThinkNode_ConditionalHasDuty">
|
||||||
|
<duty>Possess</duty>
|
||||||
|
<subNode Class="JobGiver_AIFightEnemy" />
|
||||||
|
</li>
|
||||||
|
</subNodes>
|
||||||
|
</thinkRoot>
|
||||||
|
</ThinkTreeDef>
|
||||||
|
|
||||||
|
<!-- ==================== Pawn Kind Def ==================== -->
|
||||||
|
|
||||||
|
<PawnKindDef>
|
||||||
|
<defName>ARA_Facehugger</defName>
|
||||||
|
<label>阿拉克涅原虫</label>
|
||||||
|
<race>ARA_FacehuggerRace</race> <!-- We will need to define this race -->
|
||||||
|
<combatPower>25</combatPower>
|
||||||
|
<lifeStages>
|
||||||
|
<li>
|
||||||
|
<bodyGraphicData>
|
||||||
|
<texPath>Things/Pawn/Animal/ARA_Facehugger</texPath>
|
||||||
|
<drawSize>0.8</drawSize>
|
||||||
|
</bodyGraphicData>
|
||||||
|
<dessicatedBodyGraphicData>
|
||||||
|
<texPath>Things/Pawn/Animal/Dessicated/CritterDessicated</texPath>
|
||||||
|
<drawSize>0.8</drawSize>
|
||||||
|
</dessicatedBodyGraphicData>
|
||||||
|
</li>
|
||||||
|
</lifeStages>
|
||||||
|
<aiThinkTree>ARA_Facehugger</aiThinkTree>
|
||||||
|
<abilities>
|
||||||
|
<li>ARA_Ability_Possess</li>
|
||||||
|
</abilities>
|
||||||
|
</PawnKindDef>
|
||||||
|
|
||||||
|
<!-- ==================== Race Def ==================== -->
|
||||||
|
|
||||||
|
<ThingDef ParentName="AnimalThingBase">
|
||||||
|
<defName>ARA_FacehuggerRace</defName>
|
||||||
|
<label>阿拉克涅原虫</label>
|
||||||
|
<description>一种小型的、脆弱的寄生生物,其唯一的生存目的就是寻找并占据一个更强大的宿主。它通过将自己的意识注入目标来完成这一过程。</description>
|
||||||
|
<statBases>
|
||||||
|
<MoveSpeed>4.0</MoveSpeed>
|
||||||
|
<MarketValue>50</MarketValue>
|
||||||
|
<ComfyTemperatureMin>-10</ComfyTemperatureMin>
|
||||||
|
<ComfyTemperatureMax>50</ComfyTemperatureMax>
|
||||||
|
</statBases>
|
||||||
|
<tools>
|
||||||
|
<li>
|
||||||
|
<label>tiny claws</label>
|
||||||
|
<capacities>
|
||||||
|
<li>Scratch</li>
|
||||||
|
</capacities>
|
||||||
|
<power>2</power>
|
||||||
|
<cooldownTime>1.5</cooldownTime>
|
||||||
|
</li>
|
||||||
|
</tools>
|
||||||
|
<race>
|
||||||
|
<thinkTreeMain>Animal</thinkTreeMain>
|
||||||
|
<body>ARA_FacehuggerBody</body> <!-- We will need to define this body -->
|
||||||
|
<baseBodySize>0.2</baseBodySize>
|
||||||
|
<baseHealthScale>0.3</baseHealthScale>
|
||||||
|
<baseHungerRate>0.1</baseHungerRate>
|
||||||
|
<lifeStageAges>
|
||||||
|
<li>
|
||||||
|
<def>AnimalAdult</def>
|
||||||
|
<minAge>0</minAge>
|
||||||
|
</li>
|
||||||
|
</lifeStageAges>
|
||||||
|
</race>
|
||||||
|
</ThingDef>
|
||||||
|
|
||||||
|
<BodyDef>
|
||||||
|
<defName>ARA_FacehuggerBody</defName>
|
||||||
|
<label>facehugger</label>
|
||||||
|
<corePart>
|
||||||
|
<def>Body</def>
|
||||||
|
<height>20</height>
|
||||||
|
<depth>20</depth>
|
||||||
|
<parts>
|
||||||
|
<li>
|
||||||
|
<def>Head</def>
|
||||||
|
<coverage>0.3</coverage>
|
||||||
|
<parts>
|
||||||
|
<li>
|
||||||
|
<def>Skull</def>
|
||||||
|
<coverage>0.2</coverage>
|
||||||
|
<depth>Inside</depth>
|
||||||
|
<parts>
|
||||||
|
<li>
|
||||||
|
<def>Brain</def>
|
||||||
|
<coverage>0.1</coverage>
|
||||||
|
<depth>Inside</depth>
|
||||||
|
</li>
|
||||||
|
</parts>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<def>Eye</def>
|
||||||
|
<customLabel>left eye</customLabel>
|
||||||
|
<coverage>0.07</coverage>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<def>Eye</def>
|
||||||
|
<customLabel>right eye</customLabel>
|
||||||
|
<coverage>0.07</coverage>
|
||||||
|
</li>
|
||||||
|
</parts>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<def>Leg</def>
|
||||||
|
<customLabel>front left leg</customLabel>
|
||||||
|
<coverage>0.1</coverage>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<def>Leg</def>
|
||||||
|
<customLabel>front right leg</customLabel>
|
||||||
|
<coverage>0.1</coverage>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<def>Leg</def>
|
||||||
|
<customLabel>rear left leg</customLabel>
|
||||||
|
<coverage>0.1</coverage>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<def>Leg</def>
|
||||||
|
<customLabel>rear right leg</customLabel>
|
||||||
|
<coverage>0.1</coverage>
|
||||||
|
</li>
|
||||||
|
</parts>
|
||||||
|
</corePart>
|
||||||
|
</BodyDef>
|
||||||
|
|
||||||
|
</Defs>
|
||||||
@@ -112,6 +112,12 @@
|
|||||||
<Compile Include="DataContracts.cs" />
|
<Compile Include="DataContracts.cs" />
|
||||||
<Compile Include="CompTemperatureRuinableDamage.cs" />
|
<Compile Include="CompTemperatureRuinableDamage.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="Possession\Hediff_Possession.cs" />
|
||||||
|
<Compile Include="Possession\PawnDataUtility.cs" />
|
||||||
|
<Compile Include="Possession\CompAbilityEffect_Possess.cs" />
|
||||||
|
<Compile Include="Possession\CompProperties_AbilityPossess.cs" />
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<!-- 自定义清理任务,删除obj文件夹中的临时文件 -->
|
<!-- 自定义清理任务,删除obj文件夹中的临时文件 -->
|
||||||
<Target Name="CleanDebugFiles" AfterTargets="Build">
|
<Target Name="CleanDebugFiles" AfterTargets="Build">
|
||||||
|
|||||||
33
Source/ArachnaeSwarm/Possession/CompAbilityEffect_Possess.cs
Normal file
33
Source/ArachnaeSwarm/Possession/CompAbilityEffect_Possess.cs
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
using RimWorld;
|
||||||
|
|
||||||
|
namespace ArachnaeSwarm.Possession
|
||||||
|
{
|
||||||
|
public class CompProperties_AbilityPossess : CompProperties_AbilityEffect
|
||||||
|
{
|
||||||
|
public CompProperties_AbilityPossess()
|
||||||
|
{
|
||||||
|
this.compClass = typeof(CompAbilityEffect_Possess);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
90
Source/ArachnaeSwarm/Possession/Hediff_Possession.cs
Normal file
90
Source/ArachnaeSwarm/Possession/Hediff_Possession.cs
Normal file
@@ -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<Thing>(this, false, LookMode.Deep);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Pawn StoredCasterPawn => innerContainer.Count > 0 ? innerContainer[0] as Pawn : null;
|
||||||
|
|
||||||
|
public IThingHolder ParentHolder => this.pawn;
|
||||||
|
|
||||||
|
public void GetChildHolders(List<IThingHolder> 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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
106
Source/ArachnaeSwarm/Possession/PawnDataUtility.cs
Normal file
106
Source/ArachnaeSwarm/Possession/PawnDataUtility.cs
Normal file
@@ -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<WorkTypeDef>.AllDefs)
|
||||||
|
{
|
||||||
|
bodyTarget.workSettings.SetPriority(workDef, soulSource.workSettings.GetPriority(workDef));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Timetable
|
||||||
|
if (soulSource.timetable != null && bodyTarget.timetable != null)
|
||||||
|
{
|
||||||
|
bodyTarget.timetable.times = new List<TimeAssignmentDef>(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.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
787
Source/Documents/Human.xml
Normal file
787
Source/Documents/Human.xml
Normal file
@@ -0,0 +1,787 @@
|
|||||||
|
<thing Class="Pawn">
|
||||||
|
<def>Human</def>
|
||||||
|
<tickDelta>2</tickDelta>
|
||||||
|
<id>Human846</id>
|
||||||
|
<map>0</map>
|
||||||
|
<pos>(146, 0, 131)</pos>
|
||||||
|
<rot>1</rot>
|
||||||
|
<faction>Faction_18</faction>
|
||||||
|
<questTags IsNull="True" />
|
||||||
|
<spawnedTick>0</spawnedTick>
|
||||||
|
<despawnedTick>-1</despawnedTick>
|
||||||
|
<beenRevealed>True</beenRevealed>
|
||||||
|
<targetHolder>null</targetHolder>
|
||||||
|
<lastStudiedTick>-9999999</lastStudiedTick>
|
||||||
|
<AlienRaces_AlienComp>
|
||||||
|
<addonVariants />
|
||||||
|
<addonColors />
|
||||||
|
<colorChannels>
|
||||||
|
<keys>
|
||||||
|
<li>base</li>
|
||||||
|
<li>hair</li>
|
||||||
|
<li>skin</li>
|
||||||
|
<li>skinBase</li>
|
||||||
|
<li>tattoo</li>
|
||||||
|
<li>favorite</li>
|
||||||
|
<li>ideo</li>
|
||||||
|
<li>mech</li>
|
||||||
|
</keys>
|
||||||
|
<values>
|
||||||
|
<li>
|
||||||
|
<first>RGBA(1.000, 1.000, 1.000, 1.000)</first>
|
||||||
|
<second>RGBA(1.000, 1.000, 1.000, 1.000)</second>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<first>RGBA(0.343, 0.310, 0.288, 1.000)</first>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<first>RGBA(1.000, 0.937, 0.788, 1.000)</first>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<first>RGBA(1.000, 0.937, 0.788, 1.000)</first>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<first>RGBA(1.000, 0.937, 0.788, 0.800)</first>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<first>RGBA(0.890, 0.451, 1.000, 1.000)</first>
|
||||||
|
<second>RGBA(0.890, 0.451, 1.000, 1.000)</second>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<first>RGBA(0.600, 0.500, 0.900, 1.000)</first>
|
||||||
|
<second>RGBA(0.549, 0.458, 0.824, 1.000)</second>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<first>RGBA(0.000, 0.737, 0.847, 1.000)</first>
|
||||||
|
</li>
|
||||||
|
</values>
|
||||||
|
</colorChannels>
|
||||||
|
<colorChannelLinks>
|
||||||
|
<keys />
|
||||||
|
<values />
|
||||||
|
</colorChannelLinks>
|
||||||
|
<headVariant>0</headVariant>
|
||||||
|
<bodyVariant>0</bodyVariant>
|
||||||
|
<headMaskVariant>0</headMaskVariant>
|
||||||
|
<bodyMaskVariant>0</bodyMaskVariant>
|
||||||
|
</AlienRaces_AlienComp>
|
||||||
|
<kindDef>Colonist</kindDef>
|
||||||
|
<gender>Female</gender>
|
||||||
|
<name Class="NameTriple">
|
||||||
|
<first>Yue</first>
|
||||||
|
<nick>Moon</nick>
|
||||||
|
<last>Ren</last>
|
||||||
|
</name>
|
||||||
|
<deadlifeDustFaction>null</deadlifeDustFaction>
|
||||||
|
<mindState>
|
||||||
|
<meleeThreat>null</meleeThreat>
|
||||||
|
<enemyTarget>null</enemyTarget>
|
||||||
|
<knownExploder>null</knownExploder>
|
||||||
|
<lastMannedThing>null</lastMannedThing>
|
||||||
|
<droppedWeapon>null</droppedWeapon>
|
||||||
|
<lastAttackedTarget>(0, 0, 0)</lastAttackedTarget>
|
||||||
|
<thinkData>
|
||||||
|
<keys />
|
||||||
|
<values />
|
||||||
|
</thinkData>
|
||||||
|
<lastJobTag>Idle</lastJobTag>
|
||||||
|
<nextApparelOptimizeTick>6673</nextApparelOptimizeTick>
|
||||||
|
<lastEngageTargetTick>-99999</lastEngageTargetTick>
|
||||||
|
<lastAttackTargetTick>-99999</lastAttackTargetTick>
|
||||||
|
<canFleeIndividual>True</canFleeIndividual>
|
||||||
|
<lastMeleeThreatHarmTick>-99999</lastMeleeThreatHarmTick>
|
||||||
|
<duty IsNull="True" />
|
||||||
|
<mentalStateHandler>
|
||||||
|
<curState IsNull="True" />
|
||||||
|
</mentalStateHandler>
|
||||||
|
<mentalBreaker />
|
||||||
|
<mentalFitGenerator />
|
||||||
|
<inspirationHandler>
|
||||||
|
<curState IsNull="True" />
|
||||||
|
</inspirationHandler>
|
||||||
|
<priorityWork>
|
||||||
|
<prioritizedCell>(-1000, -1000, -1000)</prioritizedCell>
|
||||||
|
</priorityWork>
|
||||||
|
<lastSelfTendTick>-99999</lastSelfTendTick>
|
||||||
|
<breachingTarget IsNull="True" />
|
||||||
|
<babyAutoBreastfeedMoms>
|
||||||
|
<keys />
|
||||||
|
<values />
|
||||||
|
</babyAutoBreastfeedMoms>
|
||||||
|
<babyCaravanBreastfeed>
|
||||||
|
<keys />
|
||||||
|
<values />
|
||||||
|
</babyCaravanBreastfeed>
|
||||||
|
<resurrectTarget IsNull="True" />
|
||||||
|
<lastRangedHarmTick>0</lastRangedHarmTick>
|
||||||
|
<lastDayInteractionTick>16777</lastDayInteractionTick>
|
||||||
|
</mindState>
|
||||||
|
<jobs>
|
||||||
|
<curJob>
|
||||||
|
<commTarget>null</commTarget>
|
||||||
|
<verbToUse>null</verbToUse>
|
||||||
|
<bill>null</bill>
|
||||||
|
<lord>null</lord>
|
||||||
|
<quest>null</quest>
|
||||||
|
<def>GotoWander</def>
|
||||||
|
<loadID>221</loadID>
|
||||||
|
<targetA>(156, 0, 116)</targetA>
|
||||||
|
<targetQueueA IsNull="True" />
|
||||||
|
<targetQueueB IsNull="True" />
|
||||||
|
<countQueue IsNull="True" />
|
||||||
|
<startTick>1576</startTick>
|
||||||
|
<checkOverrideOnExpire>True</checkOverrideOnExpire>
|
||||||
|
<placedThings IsNull="True" />
|
||||||
|
<locomotionUrgency>Walk</locomotionUrgency>
|
||||||
|
<jobGiverThinkTree>Humanlike</jobGiverThinkTree>
|
||||||
|
<psyfocusTargetLast>-1</psyfocusTargetLast>
|
||||||
|
<ability>null</ability>
|
||||||
|
<source>null</source>
|
||||||
|
<interactableIndex>-1</interactableIndex>
|
||||||
|
<lastJobGiverKey>-1672709817</lastJobGiverKey>
|
||||||
|
</curJob>
|
||||||
|
<curDriver Class="JobDriver_Goto">
|
||||||
|
<curToilIndex>0</curToilIndex>
|
||||||
|
<ticksLeftThisToil>-203</ticksLeftThisToil>
|
||||||
|
<startTick>1576</startTick>
|
||||||
|
<locomotionUrgencySameAs>null</locomotionUrgencySameAs>
|
||||||
|
</curDriver>
|
||||||
|
<jobQueue>
|
||||||
|
<jobs />
|
||||||
|
</jobQueue>
|
||||||
|
<formingCaravanTick>-1</formingCaravanTick>
|
||||||
|
</jobs>
|
||||||
|
<stances>
|
||||||
|
<stunner>
|
||||||
|
<showStunMote>True</showStunMote>
|
||||||
|
<adaptationTicksLeft>
|
||||||
|
<keys />
|
||||||
|
<values />
|
||||||
|
</adaptationTicksLeft>
|
||||||
|
</stunner>
|
||||||
|
<stagger />
|
||||||
|
<curStance Class="Stance_Mobile" />
|
||||||
|
</stances>
|
||||||
|
<infectionVectors>
|
||||||
|
<givenPrearrival>True</givenPrearrival>
|
||||||
|
<pathways>
|
||||||
|
<keys />
|
||||||
|
<values />
|
||||||
|
</pathways>
|
||||||
|
</infectionVectors>
|
||||||
|
<verbTracker>
|
||||||
|
<verbs>
|
||||||
|
<li Class="Verb_MeleeAttackDamage">
|
||||||
|
<loadID>Thing_Human846_0_Smash</loadID>
|
||||||
|
<currentTarget>(0, 0, 0)</currentTarget>
|
||||||
|
<currentDestination>(0, 0, 0)</currentDestination>
|
||||||
|
<lastShotTick>-999999</lastShotTick>
|
||||||
|
<canHitNonTargetPawnsNow>True</canHitNonTargetPawnsNow>
|
||||||
|
</li>
|
||||||
|
<li Class="Verb_MeleeAttackDamage">
|
||||||
|
<loadID>Thing_Human846_1_Smash</loadID>
|
||||||
|
<currentTarget>(0, 0, 0)</currentTarget>
|
||||||
|
<currentDestination>(0, 0, 0)</currentDestination>
|
||||||
|
<lastShotTick>-999999</lastShotTick>
|
||||||
|
<canHitNonTargetPawnsNow>True</canHitNonTargetPawnsNow>
|
||||||
|
</li>
|
||||||
|
<li Class="Verb_MeleeAttackDamage">
|
||||||
|
<loadID>Thing_Human846_2_Bite</loadID>
|
||||||
|
<currentTarget>(0, 0, 0)</currentTarget>
|
||||||
|
<currentDestination>(0, 0, 0)</currentDestination>
|
||||||
|
<lastShotTick>-999999</lastShotTick>
|
||||||
|
<canHitNonTargetPawnsNow>True</canHitNonTargetPawnsNow>
|
||||||
|
</li>
|
||||||
|
<li Class="Verb_MeleeAttackDamage">
|
||||||
|
<loadID>Thing_Human846_3_Smash</loadID>
|
||||||
|
<currentTarget>(0, 0, 0)</currentTarget>
|
||||||
|
<currentDestination>(0, 0, 0)</currentDestination>
|
||||||
|
<lastShotTick>-999999</lastShotTick>
|
||||||
|
<canHitNonTargetPawnsNow>True</canHitNonTargetPawnsNow>
|
||||||
|
</li>
|
||||||
|
</verbs>
|
||||||
|
</verbTracker>
|
||||||
|
<natives>
|
||||||
|
<verbTracker>
|
||||||
|
<verbs IsNull="True" />
|
||||||
|
</verbTracker>
|
||||||
|
</natives>
|
||||||
|
<meleeVerbs>
|
||||||
|
<curMeleeVerb>null</curMeleeVerb>
|
||||||
|
<terrainVerbs IsNull="True" />
|
||||||
|
</meleeVerbs>
|
||||||
|
<rotationTracker />
|
||||||
|
<pather>
|
||||||
|
<nextCell>(147, 0, 130)</nextCell>
|
||||||
|
<nextCellCostLeft>48</nextCellCostLeft>
|
||||||
|
<nextCellCostInitial>50</nextCellCostInitial>
|
||||||
|
<peMode>OnCell</peMode>
|
||||||
|
<cellsUntilClamor>4</cellsUntilClamor>
|
||||||
|
<lastEnteredCellTick>1777</lastEnteredCellTick>
|
||||||
|
<lastMovedTick>1779</lastMovedTick>
|
||||||
|
<destination>(156, 0, 116)</destination>
|
||||||
|
</pather>
|
||||||
|
<carryTracker>
|
||||||
|
<innerContainer>
|
||||||
|
<maxStacks>1</maxStacks>
|
||||||
|
<innerList />
|
||||||
|
</innerContainer>
|
||||||
|
</carryTracker>
|
||||||
|
<apparel>
|
||||||
|
<wornApparel>
|
||||||
|
<innerList>
|
||||||
|
<li>
|
||||||
|
<def>Apparel_Pants</def>
|
||||||
|
<id>Apparel_Pants847</id>
|
||||||
|
<health>130</health>
|
||||||
|
<stackCount>1</stackCount>
|
||||||
|
<stuff>Synthread</stuff>
|
||||||
|
<questTags IsNull="True" />
|
||||||
|
<despawnedTick>-1</despawnedTick>
|
||||||
|
<quality>Normal</quality>
|
||||||
|
<sourcePrecept>null</sourcePrecept>
|
||||||
|
<everSeenByPlayer>True</everSeenByPlayer>
|
||||||
|
<abilities />
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<def>Apparel_Parka</def>
|
||||||
|
<id>Apparel_Parka848</id>
|
||||||
|
<health>235</health>
|
||||||
|
<stackCount>1</stackCount>
|
||||||
|
<stuff>Synthread</stuff>
|
||||||
|
<questTags IsNull="True" />
|
||||||
|
<despawnedTick>-1</despawnedTick>
|
||||||
|
<quality>Normal</quality>
|
||||||
|
<sourcePrecept>null</sourcePrecept>
|
||||||
|
<everSeenByPlayer>True</everSeenByPlayer>
|
||||||
|
<abilities />
|
||||||
|
</li>
|
||||||
|
</innerList>
|
||||||
|
</wornApparel>
|
||||||
|
<lockedApparel IsNull="True" />
|
||||||
|
<lastApparelWearoutTick>111</lastApparelWearoutTick>
|
||||||
|
</apparel>
|
||||||
|
<story>
|
||||||
|
<bodyType>Fat</bodyType>
|
||||||
|
<hairDef>Elisabeth</hairDef>
|
||||||
|
<hairColor>RGBA(0.343, 0.310, 0.288, 1.000)</hairColor>
|
||||||
|
<traits>
|
||||||
|
<allTraits>
|
||||||
|
<li>
|
||||||
|
<def>SpeedOffset</def>
|
||||||
|
<sourceGene>null</sourceGene>
|
||||||
|
<degree>-1</degree>
|
||||||
|
<suppressedBy>null</suppressedBy>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<def>Jealous</def>
|
||||||
|
<sourceGene>null</sourceGene>
|
||||||
|
<suppressedBy>null</suppressedBy>
|
||||||
|
</li>
|
||||||
|
</allTraits>
|
||||||
|
</traits>
|
||||||
|
<birthLastName>Ren</birthLastName>
|
||||||
|
<favoriteColorDef>LightPurple</favoriteColorDef>
|
||||||
|
<headType>Female_AveragePointy</headType>
|
||||||
|
<childhood>WarRefugee51</childhood>
|
||||||
|
<adulthood>MedievalMinstrel95</adulthood>
|
||||||
|
</story>
|
||||||
|
<equipment>
|
||||||
|
<equipment>
|
||||||
|
<innerList />
|
||||||
|
</equipment>
|
||||||
|
<bondedWeapon>null</bondedWeapon>
|
||||||
|
</equipment>
|
||||||
|
<drafter>
|
||||||
|
<autoUndrafter />
|
||||||
|
</drafter>
|
||||||
|
<ageTracker>
|
||||||
|
<ageBiologicalTicks>139350193</ageBiologicalTicks>
|
||||||
|
<birthAbsTicks>-297730916</birthAbsTicks>
|
||||||
|
<growth>1</growth>
|
||||||
|
<nextGrowthCheckTick>9223372036854775807</nextGrowthCheckTick>
|
||||||
|
<ageReversalDemandedAtAgeTicks>141208416</ageReversalDemandedAtAgeTicks>
|
||||||
|
</ageTracker>
|
||||||
|
<healthTracker>
|
||||||
|
<hediffSet>
|
||||||
|
<hediffs />
|
||||||
|
</hediffSet>
|
||||||
|
<surgeryBills>
|
||||||
|
<bills />
|
||||||
|
</surgeryBills>
|
||||||
|
<immunity>
|
||||||
|
<imList />
|
||||||
|
</immunity>
|
||||||
|
</healthTracker>
|
||||||
|
<records>
|
||||||
|
<records>
|
||||||
|
<vals>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>1840</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
</vals>
|
||||||
|
</records>
|
||||||
|
<battleActive>null</battleActive>
|
||||||
|
</records>
|
||||||
|
<inventory>
|
||||||
|
<itemsNotForSale />
|
||||||
|
<unpackedCaravanItems />
|
||||||
|
<innerContainer>
|
||||||
|
<innerList />
|
||||||
|
</innerContainer>
|
||||||
|
</inventory>
|
||||||
|
<filth>
|
||||||
|
<lastTerrainFilthDef>Filth_Sand</lastTerrainFilthDef>
|
||||||
|
<carriedFilth>
|
||||||
|
<li>
|
||||||
|
<def>Filth_Sand</def>
|
||||||
|
<id>Filth_Sand14506</id>
|
||||||
|
<questTags IsNull="True" />
|
||||||
|
<despawnedTick>-1</despawnedTick>
|
||||||
|
</li>
|
||||||
|
</carriedFilth>
|
||||||
|
</filth>
|
||||||
|
<roping>
|
||||||
|
<hitchingPostInt>null</hitchingPostInt>
|
||||||
|
<ropees />
|
||||||
|
</roping>
|
||||||
|
<needs>
|
||||||
|
<needs>
|
||||||
|
<li Class="Need_Mood">
|
||||||
|
<def>Mood</def>
|
||||||
|
<curLevel>0.586400032</curLevel>
|
||||||
|
<thoughts>
|
||||||
|
<memories>
|
||||||
|
<memories>
|
||||||
|
<li Class="Thought_MemorySocial">
|
||||||
|
<def>CrashedTogether</def>
|
||||||
|
<sourcePrecept>null</sourcePrecept>
|
||||||
|
<otherPawn>Thing_Human849</otherPawn>
|
||||||
|
<age>1800</age>
|
||||||
|
<opinionOffset>25</opinionOffset>
|
||||||
|
</li>
|
||||||
|
<li Class="Thought_MemorySocial">
|
||||||
|
<def>CrashedTogether</def>
|
||||||
|
<sourcePrecept>null</sourcePrecept>
|
||||||
|
<otherPawn>Thing_Human852</otherPawn>
|
||||||
|
<age>1800</age>
|
||||||
|
<opinionOffset>25</opinionOffset>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<def>NewColonyOptimism</def>
|
||||||
|
<sourcePrecept>null</sourcePrecept>
|
||||||
|
<otherPawn>null</otherPawn>
|
||||||
|
<age>1800</age>
|
||||||
|
</li>
|
||||||
|
<li Class="Thought_MemorySocialCumulative">
|
||||||
|
<def>Chitchat</def>
|
||||||
|
<sourcePrecept>null</sourcePrecept>
|
||||||
|
<otherPawn>Thing_Human852</otherPawn>
|
||||||
|
<moodPowerFactor>0.773389459</moodPowerFactor>
|
||||||
|
<age>1650</age>
|
||||||
|
<opinionOffset>0.510437071</opinionOffset>
|
||||||
|
</li>
|
||||||
|
</memories>
|
||||||
|
</memories>
|
||||||
|
</thoughts>
|
||||||
|
<recentMemory>
|
||||||
|
<lastLightTick>1711</lastLightTick>
|
||||||
|
<lastOutdoorTick>1711</lastOutdoorTick>
|
||||||
|
</recentMemory>
|
||||||
|
</li>
|
||||||
|
<li Class="Need_Food">
|
||||||
|
<def>Food</def>
|
||||||
|
<curLevel>0.751999915</curLevel>
|
||||||
|
<lastNonStarvingTick>1711</lastNonStarvingTick>
|
||||||
|
</li>
|
||||||
|
<li Class="Need_Rest">
|
||||||
|
<def>Rest</def>
|
||||||
|
<curLevel>0.879273593</curLevel>
|
||||||
|
</li>
|
||||||
|
<li Class="Need_Joy">
|
||||||
|
<def>Joy</def>
|
||||||
|
<curLevel>0.513921499</curLevel>
|
||||||
|
<tolerances>
|
||||||
|
<vals>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
</vals>
|
||||||
|
</tolerances>
|
||||||
|
<bored>
|
||||||
|
<vals>
|
||||||
|
<li>False</li>
|
||||||
|
<li>False</li>
|
||||||
|
<li>False</li>
|
||||||
|
<li>False</li>
|
||||||
|
<li>False</li>
|
||||||
|
<li>False</li>
|
||||||
|
<li>False</li>
|
||||||
|
<li>False</li>
|
||||||
|
<li>False</li>
|
||||||
|
<li>False</li>
|
||||||
|
</vals>
|
||||||
|
</bored>
|
||||||
|
</li>
|
||||||
|
<li Class="Need_Beauty">
|
||||||
|
<def>Beauty</def>
|
||||||
|
<curLevel>0.442400098</curLevel>
|
||||||
|
</li>
|
||||||
|
<li Class="Need_Comfort">
|
||||||
|
<def>Comfort</def>
|
||||||
|
<curLevel>0.47119987</curLevel>
|
||||||
|
</li>
|
||||||
|
<li Class="Need_Outdoors">
|
||||||
|
<def>Outdoors</def>
|
||||||
|
<curLevel>1</curLevel>
|
||||||
|
</li>
|
||||||
|
<li Class="Need_Chemical_Any">
|
||||||
|
<def>DrugDesire</def>
|
||||||
|
<curLevel>0.5</curLevel>
|
||||||
|
</li>
|
||||||
|
<li Class="Need_RoomSize">
|
||||||
|
<def>RoomSize</def>
|
||||||
|
<curLevel>1</curLevel>
|
||||||
|
</li>
|
||||||
|
</needs>
|
||||||
|
</needs>
|
||||||
|
<guest>
|
||||||
|
<hostFaction>null</hostFaction>
|
||||||
|
<slaveFaction>null</slaveFaction>
|
||||||
|
<joinStatus>JoinAsColonist</joinStatus>
|
||||||
|
<interactionMode>MaintainOnly</interactionMode>
|
||||||
|
<slaveInteractionMode>NoInteraction</slaveInteractionMode>
|
||||||
|
<spotToWaitInsteadOfEscaping>(-1000, -1000, -1000)</spotToWaitInsteadOfEscaping>
|
||||||
|
<lastPrisonBreakTicks>-1</lastPrisonBreakTicks>
|
||||||
|
<ideoForConversion>null</ideoForConversion>
|
||||||
|
<recruitable>False</recruitable>
|
||||||
|
<enabledNonExclusiveInteractions />
|
||||||
|
<lastResistanceInteractionData IsNull="True" />
|
||||||
|
<finalResistanceInteractionData IsNull="True" />
|
||||||
|
</guest>
|
||||||
|
<guilt />
|
||||||
|
<royalty>
|
||||||
|
<titles />
|
||||||
|
<favor>
|
||||||
|
<keys />
|
||||||
|
<values />
|
||||||
|
</favor>
|
||||||
|
<highestTitles>
|
||||||
|
<keys />
|
||||||
|
<values />
|
||||||
|
</highestTitles>
|
||||||
|
<heirs>
|
||||||
|
<keys />
|
||||||
|
<values />
|
||||||
|
</heirs>
|
||||||
|
<permits />
|
||||||
|
<abilities />
|
||||||
|
</royalty>
|
||||||
|
<social>
|
||||||
|
<directRelations>
|
||||||
|
<li>
|
||||||
|
<def>ExSpouse</def>
|
||||||
|
<otherPawn>Thing_Human849</otherPawn>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<def>Parent</def>
|
||||||
|
<otherPawn>Thing_Human853</otherPawn>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<def>Parent</def>
|
||||||
|
<otherPawn>Thing_Human857</otherPawn>
|
||||||
|
</li>
|
||||||
|
</directRelations>
|
||||||
|
<virtualRelations />
|
||||||
|
<relativeInvolvedInRescueQuest>null</relativeInvolvedInRescueQuest>
|
||||||
|
<pregnancyApproaches>
|
||||||
|
<keys />
|
||||||
|
<values />
|
||||||
|
</pregnancyApproaches>
|
||||||
|
<romanceEnableTick>-1</romanceEnableTick>
|
||||||
|
</social>
|
||||||
|
<psychicEntropy>
|
||||||
|
<limitEntropyAmount>True</limitEntropyAmount>
|
||||||
|
</psychicEntropy>
|
||||||
|
<shambler IsNull="True" />
|
||||||
|
<ownership>
|
||||||
|
<ownedBed>null</ownedBed>
|
||||||
|
<assignedMeditationSpot>null</assignedMeditationSpot>
|
||||||
|
<assignedGrave>null</assignedGrave>
|
||||||
|
<assignedThrone>null</assignedThrone>
|
||||||
|
<assignedDeathrestCasket>null</assignedDeathrestCasket>
|
||||||
|
</ownership>
|
||||||
|
<interactions>
|
||||||
|
<lastInteraction>Chitchat</lastInteraction>
|
||||||
|
<lastInteractionTime>121</lastInteractionTime>
|
||||||
|
<lastInteractionDef>Chitchat</lastInteractionDef>
|
||||||
|
</interactions>
|
||||||
|
<skills>
|
||||||
|
<skills>
|
||||||
|
<li>
|
||||||
|
<def>Shooting</def>
|
||||||
|
<level>3</level>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<def>Melee</def>
|
||||||
|
<level>5</level>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<def>Construction</def>
|
||||||
|
<level>2</level>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<def>Mining</def>
|
||||||
|
<level>2</level>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<def>Cooking</def>
|
||||||
|
<level>7</level>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<def>Plants</def>
|
||||||
|
<level>1</level>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<def>Animals</def>
|
||||||
|
<level>3</level>
|
||||||
|
<passion>Minor</passion>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<def>Crafting</def>
|
||||||
|
<level>3</level>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<def>Artistic</def>
|
||||||
|
<level>5</level>
|
||||||
|
<passion>Minor</passion>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<def>Medicine</def>
|
||||||
|
<level>2</level>
|
||||||
|
<passion>Minor</passion>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<def>Social</def>
|
||||||
|
<level>7</level>
|
||||||
|
<passion>Major</passion>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<def>Intellectual</def>
|
||||||
|
</li>
|
||||||
|
</skills>
|
||||||
|
<lastXpSinceMidnightResetTimestamp>-1</lastXpSinceMidnightResetTimestamp>
|
||||||
|
</skills>
|
||||||
|
<abilities>
|
||||||
|
<abilities />
|
||||||
|
</abilities>
|
||||||
|
<ideo>
|
||||||
|
<ideo>Ideo_10</ideo>
|
||||||
|
<previousIdeos />
|
||||||
|
<certainty>0.605378687</certainty>
|
||||||
|
<babyIdeoExposure IsNull="True" />
|
||||||
|
</ideo>
|
||||||
|
<workSettings>
|
||||||
|
<priorities>
|
||||||
|
<vals>
|
||||||
|
<li>3</li>
|
||||||
|
<li>3</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>3</li>
|
||||||
|
<li>3</li>
|
||||||
|
<li>3</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>3</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>3</li>
|
||||||
|
<li>0</li>
|
||||||
|
<li>0</li>
|
||||||
|
</vals>
|
||||||
|
</priorities>
|
||||||
|
</workSettings>
|
||||||
|
<trader IsNull="True" />
|
||||||
|
<outfits>
|
||||||
|
<curOutfit>ApparelPolicy_任意_1</curOutfit>
|
||||||
|
<overrideHandler>
|
||||||
|
<forcedAps />
|
||||||
|
</overrideHandler>
|
||||||
|
</outfits>
|
||||||
|
<drugs>
|
||||||
|
<curAssignedDrugs>DrugPolicy_社交成瘾品_1</curAssignedDrugs>
|
||||||
|
<drugTakeRecords />
|
||||||
|
</drugs>
|
||||||
|
<foodRestriction>
|
||||||
|
<curRestriction>null</curRestriction>
|
||||||
|
<allowedBabyFoodTypes IsNull="True" />
|
||||||
|
</foodRestriction>
|
||||||
|
<timetable>
|
||||||
|
<times>
|
||||||
|
<li>Sleep</li>
|
||||||
|
<li>Sleep</li>
|
||||||
|
<li>Sleep</li>
|
||||||
|
<li>Sleep</li>
|
||||||
|
<li>Sleep</li>
|
||||||
|
<li>Sleep</li>
|
||||||
|
<li>Anything</li>
|
||||||
|
<li>Anything</li>
|
||||||
|
<li>Anything</li>
|
||||||
|
<li>Anything</li>
|
||||||
|
<li>Anything</li>
|
||||||
|
<li>Anything</li>
|
||||||
|
<li>Anything</li>
|
||||||
|
<li>Anything</li>
|
||||||
|
<li>Anything</li>
|
||||||
|
<li>Anything</li>
|
||||||
|
<li>Anything</li>
|
||||||
|
<li>Anything</li>
|
||||||
|
<li>Anything</li>
|
||||||
|
<li>Anything</li>
|
||||||
|
<li>Anything</li>
|
||||||
|
<li>Anything</li>
|
||||||
|
<li>Sleep</li>
|
||||||
|
<li>Sleep</li>
|
||||||
|
</times>
|
||||||
|
</timetable>
|
||||||
|
<playerSettings>
|
||||||
|
<medCare>Best</medCare>
|
||||||
|
<allowedAreas>
|
||||||
|
<keys />
|
||||||
|
<values />
|
||||||
|
</allowedAreas>
|
||||||
|
<master>null</master>
|
||||||
|
<displayOrder>1</displayOrder>
|
||||||
|
</playerSettings>
|
||||||
|
<training IsNull="True" />
|
||||||
|
<style>
|
||||||
|
<beardDef>NoBeard</beardDef>
|
||||||
|
<faceTattoo>NoTattoo_Face</faceTattoo>
|
||||||
|
<bodyTattoo>NoTattoo_Body</bodyTattoo>
|
||||||
|
</style>
|
||||||
|
<styleObserver />
|
||||||
|
<connections>
|
||||||
|
<connectedThings />
|
||||||
|
</connections>
|
||||||
|
<inventoryStock>
|
||||||
|
<stockEntries>
|
||||||
|
<keys />
|
||||||
|
<values />
|
||||||
|
</stockEntries>
|
||||||
|
</inventoryStock>
|
||||||
|
<treeSightings>
|
||||||
|
<miniTreeSightings />
|
||||||
|
<fullTreeSightings />
|
||||||
|
<superTreeSightings />
|
||||||
|
</treeSightings>
|
||||||
|
<thinker />
|
||||||
|
<mechanitor IsNull="True" />
|
||||||
|
<genes>
|
||||||
|
<xenogenes />
|
||||||
|
<endogenes>
|
||||||
|
<li>
|
||||||
|
<def>Skin_Melanin3</def>
|
||||||
|
<pawn>Thing_Human846</pawn>
|
||||||
|
<overriddenByGene>null</overriddenByGene>
|
||||||
|
<loadID>132</loadID>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<def>Hair_MidBlack</def>
|
||||||
|
<pawn>Thing_Human846</pawn>
|
||||||
|
<overriddenByGene>null</overriddenByGene>
|
||||||
|
<loadID>133</loadID>
|
||||||
|
</li>
|
||||||
|
</endogenes>
|
||||||
|
<xenotype>Baseliner</xenotype>
|
||||||
|
</genes>
|
||||||
|
<learning IsNull="True" />
|
||||||
|
<reading>
|
||||||
|
<curAssignment>null</curAssignment>
|
||||||
|
</reading>
|
||||||
|
<creepjoiner IsNull="True" />
|
||||||
|
<duplicate />
|
||||||
|
<flight />
|
||||||
|
</thing>
|
||||||
188
Source/Documents/PawnStorageInHediff_Analysis.md
Normal file
188
Source/Documents/PawnStorageInHediff_Analysis.md
Normal file
@@ -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<Thing>(this, false, LookMode.Deep, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- IThingHolder 接口实现 ---
|
||||||
|
|
||||||
|
public IThingHolder ParentHolder
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
// 对于Hediff来说,它的父容器就是持有它的Pawn
|
||||||
|
return this.pawn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetChildHolders(List<IThingHolder> 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<Pawn>(ref this.victim, "victim", false);
|
||||||
|
|
||||||
|
// 2. 深度保存容器内容
|
||||||
|
// 'Scribe_Deep.Look' 会序列化容器内的Pawn的所有数据
|
||||||
|
Scribe_Deep.Look<ThingOwner>(ref this.innerContainer, "innerContainer", new object[]
|
||||||
|
{
|
||||||
|
this
|
||||||
|
});
|
||||||
|
|
||||||
|
// 兼容性处理:确保旧存档在加载后也能正确初始化容器
|
||||||
|
if (Scribe.mode == LoadSaveMode.PostLoadInit)
|
||||||
|
{
|
||||||
|
if (this.innerContainer == null)
|
||||||
|
{
|
||||||
|
this.innerContainer = new ThingOwner<Thing>(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<ThingOwner>(ref this.innerContainer, ...)`: 这行代码是魔法发生的地方。当游戏保存时,`Scribe_Deep` 会深入到 `innerContainer` 内部,并因为我们之前设置了 `LookMode.Deep`,它会对容器里的每一个 `Pawn` 进行递归式的深度保存。
|
||||||
|
* 当游戏加载时,`Scribe_Deep` 会读取存档中的数据,重建 `innerContainer`,并利用深度保存的数据重建一个与存入时状态完全一致的 `Pawn`。
|
||||||
|
|
||||||
|
## 总结
|
||||||
|
|
||||||
|
通过实现 `IThingHolder` 接口并利用一个配置为 `LookMode.Deep` 的 `ThingOwner` 容器,我们可以将一个 `Hediff` 转变为一个功能强大的、能够随宿主移动的“Pawn胶囊”。这个“胶囊”可以安全地携带一个`Pawn`穿越存档的海洋,确保其数据的完整性和一致性。
|
||||||
|
|
||||||
|
这项技术是实现诸如吞噬、俘获、传送、特殊休眠仓等高级Mod功能的基石。
|
||||||
569
Source/Documents/Possession_Implementation_Guide.md
Normal file
569
Source/Documents/Possession_Implementation_Guide.md
Normal file
@@ -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<Thing>(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<IThingHolder> 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<WorkTypeDef>.AllDefs)
|
||||||
|
{
|
||||||
|
bodyTarget.workSettings.SetPriority(workDef, soulSource.workSettings.GetPriority(workDef));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 时间表
|
||||||
|
if (soulSource.timetable != null && bodyTarget.timetable != null)
|
||||||
|
{
|
||||||
|
bodyTarget.timetable.times = new List<TimeAssignmentDef>(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
|
||||||
|
<!-- 路径: 1.6/1.6/Defs/HediffDefs/ARA_Hediffs_Possession.xml -->
|
||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<Defs>
|
||||||
|
|
||||||
|
<HediffDef>
|
||||||
|
<!-- 内部唯一的定义名 -->
|
||||||
|
<defName>ARA_Possession</defName>
|
||||||
|
<!-- 游戏中显示的标签 -->
|
||||||
|
<label>阿拉克涅原虫寄生</label>
|
||||||
|
<!-- 鼠标悬浮时的描述 -->
|
||||||
|
<description>这个生物的身体正被另一个实体所控制。</description>
|
||||||
|
<!-- 关键:将这个XML定义与我们的C#实现类关联起来 -->
|
||||||
|
<hediffClass>ArachnaeSwarm.Possession.Hediff_Possession</hediffClass>
|
||||||
|
<!-- 其他标准Hediff属性 -->
|
||||||
|
<isBad>false</isBad>
|
||||||
|
<scenarioCanAdd>false</scenarioCanAdd>
|
||||||
|
<maxSeverity>1.0</maxSeverity>
|
||||||
|
<stages>
|
||||||
|
<li>
|
||||||
|
<label>被寄生</label>
|
||||||
|
</li>
|
||||||
|
</stages>
|
||||||
|
</HediffDef>
|
||||||
|
|
||||||
|
</Defs>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2 `ARA_Possession_Defs.xml` - 定义技能、种族和身体
|
||||||
|
|
||||||
|
这个文件定义了技能本身,以及我们的“阿拉克涅原虫”作为一个完整的生物所需的一切。
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<!-- 路径: 1.6/1.6/Defs/Misc/ARA_Possession_Defs.xml -->
|
||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<Defs>
|
||||||
|
|
||||||
|
<!-- ==================== 技能定义 ==================== -->
|
||||||
|
<AbilityDef>
|
||||||
|
<defName>ARA_Ability_Possess</defName>
|
||||||
|
<label>阿拉克涅寄生</label>
|
||||||
|
<description>将你的意识注入另一个生物的身体,完全占据它。</description>
|
||||||
|
<iconPath>UI/Abilities/Possess</iconPath> <!-- TODO: 需要一张图标 -->
|
||||||
|
<cooldownTicks>600</cooldownTicks>
|
||||||
|
<verbProperties>
|
||||||
|
<verbClass>Verb_CastAbility</verbClass>
|
||||||
|
<warmupTime>1.5</warmupTime>
|
||||||
|
<range>5.9</range>
|
||||||
|
<targetParams>
|
||||||
|
<canTargetPawns>true</canTargetPawns>
|
||||||
|
<canTargetBuildings>false</canTargetBuildings>
|
||||||
|
<canTargetSelf>false</canTargetSelf>
|
||||||
|
<canTargetLocations>false</canTargetLocations>
|
||||||
|
</targetParams>
|
||||||
|
</verbProperties>
|
||||||
|
<comps>
|
||||||
|
<!-- 关键:将这个技能与我们的C#属性类关联起来 -->
|
||||||
|
<li Class="ArachnaeSwarm.Possession.CompProperties_AbilityPossess"/>
|
||||||
|
</comps>
|
||||||
|
</AbilityDef>
|
||||||
|
|
||||||
|
<!-- ==================== 生物AI定义 (可选) ==================== -->
|
||||||
|
<ThinkTreeDef>
|
||||||
|
<defName>ARA_Facehugger</defName>
|
||||||
|
<insertTag>Humanlike_PostMentalState</insertTag>
|
||||||
|
<insertPriority>100</insertPriority>
|
||||||
|
<thinkRoot Class="ThinkNode_Priority">
|
||||||
|
<subNodes>
|
||||||
|
<!-- 在这里可以为抱脸虫添加自定义AI,例如当它空闲时自动寻找宿主 -->
|
||||||
|
</subNodes>
|
||||||
|
</thinkRoot>
|
||||||
|
</ThinkTreeDef>
|
||||||
|
|
||||||
|
<!-- ==================== 生物类型定义 ==================== -->
|
||||||
|
<PawnKindDef>
|
||||||
|
<defName>ARA_Facehugger</defName>
|
||||||
|
<label>阿拉克涅原虫</label>
|
||||||
|
<!-- 关联下面的种族定义 -->
|
||||||
|
<race>ARA_FacehuggerRace</race>
|
||||||
|
<combatPower>25</combatPower>
|
||||||
|
<lifeStages>
|
||||||
|
<li>
|
||||||
|
<bodyGraphicData>
|
||||||
|
<texPath>Things/Pawn/Animal/ARA_Facehugger</texPath> <!-- TODO: 需要贴图 -->
|
||||||
|
<drawSize>0.8</drawSize>
|
||||||
|
</bodyGraphicData>
|
||||||
|
<dessicatedBodyGraphicData>
|
||||||
|
<texPath>Things/Pawn/Animal/Dessicated/CritterDessicated</texPath>
|
||||||
|
<drawSize>0.8</drawSize>
|
||||||
|
</dessicatedBodyGraphicData>
|
||||||
|
</li>
|
||||||
|
</lifeStages>
|
||||||
|
<aiThinkTree>ARA_Facehugger</aiThinkTree>
|
||||||
|
<abilities>
|
||||||
|
<!-- 赋予该生物我们的夺舍技能 -->
|
||||||
|
<li>ARA_Ability_Possess</li>
|
||||||
|
</abilities>
|
||||||
|
</PawnKindDef>
|
||||||
|
|
||||||
|
<!-- ==================== 种族定义 ==================== -->
|
||||||
|
<ThingDef ParentName="AnimalThingBase">
|
||||||
|
<defName>ARA_FacehuggerRace</defName>
|
||||||
|
<label>阿拉克涅原虫</label>
|
||||||
|
<description>一种小型的、脆弱的寄生生物,其唯一的生存目的就是寻找并占据一个更强大的宿主。它通过将自己的意识注入目标来完成这一过程。</description>
|
||||||
|
<statBases>
|
||||||
|
<MoveSpeed>4.0</MoveSpeed>
|
||||||
|
<MarketValue>50</MarketValue>
|
||||||
|
<ComfyTemperatureMin>-10</ComfyTemperatureMin>
|
||||||
|
<ComfyTemperatureMax>50</ComfyTemperatureMax>
|
||||||
|
</statBases>
|
||||||
|
<tools>
|
||||||
|
<li>
|
||||||
|
<label>tiny claws</label>
|
||||||
|
<capacities>
|
||||||
|
<li>Scratch</li>
|
||||||
|
</capacities>
|
||||||
|
<power>2</power>
|
||||||
|
<cooldownTime>1.5</cooldownTime>
|
||||||
|
</li>
|
||||||
|
</tools>
|
||||||
|
<race>
|
||||||
|
<thinkTreeMain>Animal</thinkTreeMain>
|
||||||
|
<!-- 关联下面的身体定义 -->
|
||||||
|
<body>ARA_FacehuggerBody</body>
|
||||||
|
<baseBodySize>0.2</baseBodySize>
|
||||||
|
<baseHealthScale>0.3</baseHealthScale>
|
||||||
|
<baseHungerRate>0.1</baseHungerRate>
|
||||||
|
<lifeStageAges>
|
||||||
|
<li>
|
||||||
|
<def>AnimalAdult</def>
|
||||||
|
<minAge>0</minAge>
|
||||||
|
</li>
|
||||||
|
</lifeStageAges>
|
||||||
|
</race>
|
||||||
|
</ThingDef>
|
||||||
|
|
||||||
|
<!-- ==================== 身体结构定义 ==================== -->
|
||||||
|
<BodyDef>
|
||||||
|
<defName>ARA_FacehuggerBody</defName>
|
||||||
|
<label>facehugger</label>
|
||||||
|
<corePart>
|
||||||
|
<def>Body</def>
|
||||||
|
<height>20</height>
|
||||||
|
<depth>20</depth>
|
||||||
|
<parts>
|
||||||
|
<li>
|
||||||
|
<def>Head</def>
|
||||||
|
<coverage>0.3</coverage>
|
||||||
|
<parts>
|
||||||
|
<li>
|
||||||
|
<def>Skull</def>
|
||||||
|
<coverage>0.2</coverage>
|
||||||
|
<depth>Inside</depth>
|
||||||
|
<parts>
|
||||||
|
<li>
|
||||||
|
<def>Brain</def>
|
||||||
|
<coverage>0.1</coverage>
|
||||||
|
<depth>Inside</depth>
|
||||||
|
</li>
|
||||||
|
</parts>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<def>Eye</def>
|
||||||
|
<customLabel>left eye</customLabel>
|
||||||
|
<coverage>0.07</coverage>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<def>Eye</def>
|
||||||
|
<customLabel>right eye</customLabel>
|
||||||
|
<coverage>0.07</coverage>
|
||||||
|
</li>
|
||||||
|
</parts>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<def>Leg</def>
|
||||||
|
<customLabel>front left leg</customLabel>
|
||||||
|
<coverage>0.1</coverage>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<def>Leg</def>
|
||||||
|
<customLabel>front right leg</customLabel>
|
||||||
|
<coverage>0.1</coverage>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<def>Leg</def>
|
||||||
|
<customLabel>rear left leg</customLabel>
|
||||||
|
<coverage>0.1</coverage>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<def>Leg</def>
|
||||||
|
<customLabel>rear right leg</customLabel>
|
||||||
|
<coverage>0.1</coverage>
|
||||||
|
</li>
|
||||||
|
</parts>
|
||||||
|
</corePart>
|
||||||
|
</BodyDef>
|
||||||
|
|
||||||
|
</Defs>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
这份详尽的文档现在包含了我们所有的最终代码和XML,并附有详细的注释,解释了每一步的作用和它们之间的关联。
|
||||||
Reference in New Issue
Block a user