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