This commit is contained in:
2026-01-28 17:29:08 +08:00
parent 525883470a
commit cf17deb49f
17 changed files with 802 additions and 918 deletions

View File

@@ -1,7 +1,28 @@
{
"Version": 1,
"WorkspaceRootPath": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\",
"Documents": [],
"Documents": [
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|d:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\pawn_comps\\ara_uniquepawn\\compuniquepawn.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:pawn_comps\\ara_uniquepawn\\compuniquepawn.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|d:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\pawn_comps\\ara_uniquepawn\\uniquepawnmanager.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:pawn_comps\\ara_uniquepawn\\uniquepawnmanager.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|d:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\pawn_comps\\ara_uniquepawn\\compproperties_uniquepawn.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:pawn_comps\\ara_uniquepawn\\compproperties_uniquepawn.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|d:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\pawn_comps\\ara_flight\\pawn_flighttrackerpatches.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:pawn_comps\\ara_flight\\pawn_flighttrackerpatches.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|d:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\pawn_comps\\ara_flight\\compproperties_pawnflight.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:pawn_comps\\ara_flight\\compproperties_pawnflight.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
}
],
"DocumentGroupContainers": [
{
"Orientation": 0,
@@ -9,8 +30,73 @@
"DocumentGroups": [
{
"DockedWidth": 200,
"SelectedChildIndex": -1,
"SelectedChildIndex": 1,
"Children": [
{
"$type": "Document",
"DocumentIndex": 1,
"Title": "UniquePawnManager.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Pawn_Comps\\ARA_UniquePawn\\UniquePawnManager.cs",
"RelativeDocumentMoniker": "Pawn_Comps\\ARA_UniquePawn\\UniquePawnManager.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Pawn_Comps\\ARA_UniquePawn\\UniquePawnManager.cs",
"RelativeToolTip": "Pawn_Comps\\ARA_UniquePawn\\UniquePawnManager.cs",
"ViewState": "AgIAAIIBAAAAAAAAAAAtwJUBAAANAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-01-28T09:15:15.046Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 0,
"Title": "CompUniquePawn.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Pawn_Comps\\ARA_UniquePawn\\CompUniquePawn.cs",
"RelativeDocumentMoniker": "Pawn_Comps\\ARA_UniquePawn\\CompUniquePawn.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Pawn_Comps\\ARA_UniquePawn\\CompUniquePawn.cs",
"RelativeToolTip": "Pawn_Comps\\ARA_UniquePawn\\CompUniquePawn.cs",
"ViewState": "AgIAAFwAAAAAAAAAAAApwHcAAAANAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-01-28T09:15:14.392Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 2,
"Title": "CompProperties_UniquePawn.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Pawn_Comps\\ARA_UniquePawn\\CompProperties_UniquePawn.cs",
"RelativeDocumentMoniker": "Pawn_Comps\\ARA_UniquePawn\\CompProperties_UniquePawn.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Pawn_Comps\\ARA_UniquePawn\\CompProperties_UniquePawn.cs",
"RelativeToolTip": "Pawn_Comps\\ARA_UniquePawn\\CompProperties_UniquePawn.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAABcAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-01-28T09:15:09.995Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 3,
"Title": "Pawn_FlightTrackerPatches.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Pawn_Comps\\ARA_Flight\\Pawn_FlightTrackerPatches.cs",
"RelativeDocumentMoniker": "Pawn_Comps\\ARA_Flight\\Pawn_FlightTrackerPatches.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Pawn_Comps\\ARA_Flight\\Pawn_FlightTrackerPatches.cs",
"RelativeToolTip": "Pawn_Comps\\ARA_Flight\\Pawn_FlightTrackerPatches.cs",
"ViewState": "AgIAAC8AAAAAAAAAAAAawEEAAAAQAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-01-28T08:32:19.011Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 4,
"Title": "CompProperties_PawnFlight.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Pawn_Comps\\ARA_Flight\\CompProperties_PawnFlight.cs",
"RelativeDocumentMoniker": "Pawn_Comps\\ARA_Flight\\CompProperties_PawnFlight.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Pawn_Comps\\ARA_Flight\\CompProperties_PawnFlight.cs",
"RelativeToolTip": "Pawn_Comps\\ARA_Flight\\CompProperties_PawnFlight.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAuwAgAAAAWAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-01-28T08:30:19.937Z",
"EditorCaption": ""
},
{
"$type": "Bookmark",
"Name": "ST:0:0:{1c4feeaa-4718-4aa9-859d-94ce25d182ba}"

View File

@@ -361,14 +361,14 @@
<Compile Include="Pawn_Comps\ARA_TrainingWork\CompAdvancedTraining.cs" />
<Compile Include="Jobs\JobDriver_Clean\JobGiver_Cleaner.cs" />
<Compile Include="Jobs\JobDriver_Clean\ThinkNode_ConditionalAnimalShouldDoCleaningWork.cs" />
<Compile Include="Pawn_Comps\ARA_UniquePawn\CompProperties_UniquePawn.cs" />
<Compile Include="Pawn_Comps\ARA_UniquePawn\CompUniquePawn.cs" />
<Compile Include="Pawn_Comps\ARA_UniquePawn\UniquePawnManager.cs" />
<Compile Include="WorkGivers\WorkGiver_ArachnaeClean.cs" />
<Compile Include="Jobs\JobDriver_Plant\JobGiver_Grower.cs" />
<Compile Include="Jobs\JobDriver_Plant\ThinkNode_ConditionalAnimalShouldDoGrowingWork.cs" />
<Compile Include="WorkGivers\WorkGiver_ArachnaeSow.cs" />
<Compile Include="Pawn_Comps\ARA_TrainingWork\TrainingSystem_Patcher.cs" />
<Compile Include="Pawn_Comps\ARA_UniquePawn\CompProperties_UniquePawn.cs" />
<Compile Include="Pawn_Comps\ARA_UniquePawn\CompUniquePawn.cs" />
<Compile Include="Pawn_Comps\ARA_UniquePawn\Patch_UniquePawn.cs" />
<Compile Include="Placeworker\CompProperties_CustomRadius.cs" />
<Compile Include="Plants\Plant_Transforming.cs" />
<Compile Include="PowerArmor\ARA_PowerArmor.cs" />
@@ -439,6 +439,7 @@
<Compile Include="WorkGivers\WorkGiver_StripChitin.cs" />
<Compile Include="WorkGivers\WorkGiver_SwarmMaintain.cs" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- 自定义清理任务删除obj文件夹中的临时文件 -->
<Target Name="CleanDebugFiles" AfterTargets="Build">

View File

@@ -6,6 +6,7 @@ namespace ArachnaeSwarm
public enum FlightCondition
{
Drafted,
DraftedAndMove,
Always
}

View File

@@ -68,6 +68,10 @@ namespace ArachnaeSwarm
{
shouldBeFlying = true;
}
else if (compProps.flightCondition == FlightCondition.DraftedAndMove && ___pawn.Drafted || ___pawn.pather.MovingNow)
{
shouldBeFlying = true;
}
else if (compProps.flightCondition == FlightCondition.Drafted && ___pawn.Drafted)
{
shouldBeFlying = true;

View File

@@ -4,7 +4,7 @@ namespace ArachnaeSwarm
{
public class CompProperties_UniquePawn : CompProperties
{
// 全局变量的唯一标识符
// 唯一标识符
public string globalVariable;
// 可选杀死pawn时是否显示消息
@@ -15,6 +15,12 @@ namespace ArachnaeSwarm
// 可选杀死pawn的方式
public DamageDef killDamageDef = null;
// 验证间隔ticks
public int verificationInterval = 300;
// 是否启用调试日志
public bool enableDebugLogging = false;
public CompProperties_UniquePawn()
{

View File

@@ -1,4 +1,4 @@
using System;
using System;
using RimWorld;
using Verse;
@@ -6,30 +6,61 @@ namespace ArachnaeSwarm
{
public class CompUniquePawn : ThingComp
{
#region
private bool _checked = false;
private bool _scheduledForCheck = false;
private int _lastVerificationTick = -1;
private const int VERIFICATION_SIGNAL_INTERVAL = 300; // 每5秒发送一次验证信号
#endregion
#region
public CompProperties_UniquePawn Props => (CompProperties_UniquePawn)this.props;
public string GlobalVariable
{
get
{
if (Props == null || string.IsNullOrEmpty(Props.globalVariable))
{
Log.Warning("CompUniquePawn: Props.globalVariable is null or empty");
return null;
}
return Props.globalVariable;
}
}
public int SpawnTick { get; private set; } = -1;
#endregion
#region
public override void PostSpawnSetup(bool respawningAfterLoad)
{
base.PostSpawnSetup(respawningAfterLoad);
// 基本安全检查
if (this.parent == null || !(this.parent is Pawn pawn) || !pawn.Spawned)
return;
// 只在首次生成时检查,不是加载存档时
if (respawningAfterLoad || _checked || _scheduledForCheck)
// 记录生成时间
if (SpawnTick < 0)
SpawnTick = Find.TickManager.TicksGame;
// 如果是重新生成,检查是否有效
if (respawningAfterLoad)
{
HandleRespawnAfterLoad(pawn);
return;
}
// 首次生成时检查
if (_checked || _scheduledForCheck)
return;
// 确保属性有效
if (this.props == null || string.IsNullOrEmpty(Props?.globalVariable))
if (string.IsNullOrEmpty(GlobalVariable))
return;
try
{
// 延迟整个检查过程到下一帧
// 延迟检查
_scheduledForCheck = true;
LongEventHandler.QueueLongEvent(() =>
@@ -45,50 +76,135 @@ namespace ArachnaeSwarm
}
catch (Exception ex)
{
ArachnaeLog.Debug($"Error in delayed unique pawn check: {ex}");
Log.Error($"Error in delayed unique pawn check: {ex}");
_scheduledForCheck = false;
}
}, "ArachnaeSwarm_UniquePawnCheck", false, null);
}
catch (Exception ex)
{
ArachnaeLog.Debug($"Error in CompUniquePawn.PostSpawnSetup: {ex}");
Log.Error($"Error in CompUniquePawn.PostSpawnSetup: {ex}");
_scheduledForCheck = false;
}
}
public override void CompTick()
{
base.CompTick();
// 定期发送验证信号
if (Find.TickManager.TicksGame - _lastVerificationTick > VERIFICATION_SIGNAL_INTERVAL)
{
SendVerificationSignal();
_lastVerificationTick = Find.TickManager.TicksGame;
}
}
public override void PostDestroy(DestroyMode mode, Map previousMap)
{
base.PostDestroy(mode, previousMap);
// Pawn被销毁时注销
if (this.parent is Pawn pawn && !string.IsNullOrEmpty(GlobalVariable))
{
UniquePawnManager.Instance?.UnregisterPawn(pawn, GlobalVariable);
}
}
public void PostDeSpawn(Map map)
{
// Pawn被反生成时也注销
if (this.parent is Pawn pawn && !string.IsNullOrEmpty(GlobalVariable))
{
UniquePawnManager.Instance?.UnregisterPawn(pawn, GlobalVariable);
}
}
#endregion
#region
/// <summary>
/// 发送验证信号给管理器
/// </summary>
public void SendVerificationSignal()
{
if (this.parent is Pawn pawn && !string.IsNullOrEmpty(GlobalVariable) && pawn.Spawned && !pawn.Destroyed)
{
UniquePawnManager.Instance?.SendVerificationSignal(pawn, GlobalVariable);
}
}
/// <summary>
/// 检查是否是有效的唯一Pawn
/// </summary>
public bool IsValidUniquePawn()
{
if (this.parent is Pawn pawn && !string.IsNullOrEmpty(GlobalVariable))
{
return UniquePawnManager.Instance?.IsEarliestPawn(pawn, GlobalVariable) ?? false;
}
return false;
}
#endregion
#region
private void CheckAndHandleUniquePawn(Pawn pawn)
{
try
{
string variable = Props.globalVariable;
string variable = GlobalVariable;
if (string.IsNullOrEmpty(variable))
{
ArachnaeLog.Debug("CompUniquePawn: globalVariable is null or empty");
Log.Error("CompUniquePawn: globalVariable is null or empty");
return;
}
// 检查变量是否已存在
if (GlobalVariableManager.HasVariable(variable))
// 向管理器注册
bool registered = UniquePawnManager.Instance.RegisterPawn(pawn, variable);
if (!registered)
{
// 变量已存在杀死pawn
// 注册失败说明有更早的Pawn存在
KillPawn(pawn, variable);
}
else
{
// 变量不存在,添加变量
GlobalVariableManager.SetVariable(variable);
if (Prefs.DevMode)
{
ArachnaeLog.Debug($"Added global variable '{variable}' for pawn {pawn.Label}");
}
Log.Message($"[唯一Pawn系统] 成功注册Pawn: {pawn.Label} (变量: {variable})");
}
}
catch (Exception ex)
{
ArachnaeLog.Debug($"Error in CheckAndHandleUniquePawn: {ex}");
Log.Error($"Error in CheckAndHandleUniquePawn: {ex}");
}
}
private void HandleRespawnAfterLoad(Pawn pawn)
{
try
{
string variable = GlobalVariable;
if (string.IsNullOrEmpty(variable))
return;
// 检查是否仍然是最早的Pawn
bool isEarliest = UniquePawnManager.Instance?.IsEarliestPawn(pawn, variable) ?? false;
if (!isEarliest)
{
// 有更早的Pawn存在杀死当前Pawn
KillPawn(pawn, variable);
}
else
{
// 重新发送验证信号
SendVerificationSignal();
_checked = true;
}
}
catch (Exception ex)
{
Log.Error($"Error in HandleRespawnAfterLoad: {ex}");
}
}
@@ -102,16 +218,13 @@ namespace ArachnaeSwarm
string deathMessage = Props.deathMessageKey.Translate(pawn.Label, variable);
if (!string.IsNullOrEmpty(deathMessage))
{
Messages.Message(deathMessage, MessageTypeDefOf.NegativeEvent);
Messages.Message(deathMessage, pawn, MessageTypeDefOf.NegativeEvent);
}
}
if (Prefs.DevMode)
{
ArachnaeLog.Debug($"Killing pawn {pawn.Label} because global variable '{variable}' already exists");
}
Log.Message($"[唯一Pawn系统] 移除重复Pawn: {pawn.Label} (变量: {variable})");
// 使用更安全的延迟执行
// 延迟执行杀死操作
LongEventHandler.QueueLongEvent(() =>
{
try
@@ -131,21 +244,24 @@ namespace ArachnaeSwarm
}
catch (Exception ex)
{
ArachnaeLog.Debug($"Error in delayed pawn kill: {ex}");
Log.Error($"Error in delayed pawn kill: {ex}");
}
}, "ArachnaeSwarm_KillDuplicatePawn", false, null);
}
catch (Exception ex)
{
ArachnaeLog.Debug($"Error in KillPawn: {ex}");
Log.Error($"Error in KillPawn: {ex}");
}
}
#endregion
public override void PostExposeData()
#region
public void ExposeData()
{
base.PostExposeData();
Scribe_Values.Look(ref _checked, "checked", false);
Scribe_Values.Look(ref _scheduledForCheck, "scheduledForCheck", false);
Scribe_Values.Look(ref _lastVerificationTick, "lastVerificationTick", -1);
}
#endregion
}
}

View File

@@ -1,299 +0,0 @@
using System.Collections.Generic;
using System.Reflection;
using HarmonyLib;
using RimWorld;
using Verse;
namespace ArachnaeSwarm
{
[StaticConstructorOnStartup]
public static class ModInit
{
static ModInit()
{
var harmony = new Harmony("ArachnaeSwarm.Mod");
harmony.PatchAll();
// 初始化全局变量管理器
GlobalVariableManager.Initialize();
}
}
[HarmonyPatch(typeof(Game), "ExposeData")]
public static class Game_ExposeData_Patch
{
public static void Postfix(Game __instance)
{
GlobalVariableManager.ExposeData();
}
}
// 新增:在游戏开始或结束时清理变量
[HarmonyPatch(typeof(Game), "InitNewGame")]
public static class Game_InitNewGame_Patch
{
public static void Postfix()
{
// 新游戏开始时清理所有变量
GlobalVariableManager.ClearAllVariables();
if (Prefs.DevMode)
{
ArachnaeLog.Debug("GlobalVariableManager: Cleared all variables for new game");
}
}
}
// 新增:在游戏加载时确保变量正确
[HarmonyPatch(typeof(Game), "LoadGame")]
public static class Game_LoadGame_Patch
{
public static void Postfix()
{
// 确保变量管理器已初始化
GlobalVariableManager.Initialize();
if (Prefs.DevMode)
{
ArachnaeLog.Debug("GlobalVariableManager: Initialized for loaded game");
}
}
}
// 新增:在返回主菜单时清理变量
[HarmonyPatch(typeof(Game), "FinalizeInit")]
public static class Game_FinalizeInit_Patch
{
public static void Postfix()
{
// 确保变量管理器已初始化
GlobalVariableManager.Initialize();
}
}
// 复活拦截补丁
[HarmonyPatch]
public static class ResurrectionUtility_Patch
{
private static MethodInfo TargetMethod()
{
// 尝试找到复活方法
return AccessTools.Method("ResurrectionUtility:TryResurrect") ??
AccessTools.Method("RimWorld.ResurrectionUtility:TryResurrect");
}
[HarmonyPrefix]
public static bool Prefix(Pawn pawn)
{
try
{
// 检查 pawn 是否有 CompUniquePawn 组件
var comp = pawn?.GetComp<CompUniquePawn>();
if (comp != null && !string.IsNullOrEmpty(comp.Props?.globalVariable))
{
string variable = comp.Props.globalVariable;
// 如果全局变量已存在,阻止复活
if (GlobalVariableManager.HasVariable(variable))
{
// 显示阻止复活的消息
if (comp.Props.showDeathMessage && !string.IsNullOrEmpty(comp.Props.deathMessageKey))
{
string preventMessage = "ARA_ResurrectionPrevented".Translate(pawn.Label, variable);
if (string.IsNullOrEmpty(preventMessage) || preventMessage == "ARA_ResurrectionPrevented")
{
preventMessage = "无法复活 {0},因为 {1} 已经存在。".Translate(pawn.Label, variable);
}
Messages.Message(preventMessage, MessageTypeDefOf.NegativeEvent);
}
if (Prefs.DevMode)
{
ArachnaeLog.Debug($"阻止复活 {pawn.Label},因为全局变量 '{variable}' 已存在");
}
// __result = false; // void methods cannot return a value
return false; // 跳过原始方法
}
}
}
catch (System.Exception ex)
{
ArachnaeLog.Debug($"Error in resurrection prevention: {ex}");
}
return true; // 继续执行原始方法
}
}
// 带副作用的复活拦截补丁
[HarmonyPatch]
public static class ResurrectionUtility_Patch2
{
private static MethodInfo TargetMethod()
{
// 尝试找到带副作用的复活方法
return AccessTools.Method("ResurrectionUtility:TryResurrectWithSideEffects") ??
AccessTools.Method("RimWorld.ResurrectionUtility:TryResurrectWithSideEffects");
}
[HarmonyPrefix]
public static bool Prefix(Pawn pawn)
{
try
{
// 检查 pawn 是否有 CompUniquePawn 组件
if (pawn == null) return true;
var comp = pawn.GetComp<CompUniquePawn>();
if (comp != null && !string.IsNullOrEmpty(comp.Props?.globalVariable))
{
string variable = comp.Props.globalVariable;
// 如果全局变量已存在,阻止复活
if (GlobalVariableManager.HasVariable(variable))
{
// 显示阻止复活的消息
if (comp.Props.showDeathMessage && !string.IsNullOrEmpty(comp.Props.deathMessageKey))
{
string preventMessage = "ARA_ResurrectionPrevented".Translate(pawn.Label, variable);
if (string.IsNullOrEmpty(preventMessage) || preventMessage == "ARA_ResurrectionPrevented")
{
preventMessage = "无法复活 {0},因为 {1} 已经存在。".Translate(pawn.Label, variable);
}
Messages.Message(preventMessage, MessageTypeDefOf.NegativeEvent);
}
if (Prefs.DevMode)
{
ArachnaeLog.Debug($"阻止复活 {pawn.Label},因为全局变量 '{variable}' 已存在");
}
// __result = false; // void methods cannot return a value
return false; // 跳过原始方法
}
}
}
catch (System.Exception ex)
{
ArachnaeLog.Debug($"Error in resurrection prevention: {ex}");
}
return true; // 继续执行原始方法
}
}
public static class GlobalVariableManager
{
private static HashSet<string> _globalVariables;
private static bool _initialized = false;
public static void Initialize()
{
if (!_initialized)
{
_globalVariables = new HashSet<string>();
_initialized = true;
if (Prefs.DevMode)
{
ArachnaeLog.Debug("GlobalVariableManager: Initialized");
}
}
}
public static void ExposeData()
{
try
{
// 确保在保存或加载前已初始化
Initialize();
if (Scribe.mode == LoadSaveMode.Saving)
{
if (_globalVariables.Count > 0)
{
List<string> variablesList = new List<string>(_globalVariables);
Scribe_Collections.Look(ref variablesList, "ArachnaeSwarm_GlobalVariables", LookMode.Value);
if (Prefs.DevMode)
{
ArachnaeLog.Debug($"GlobalVariableManager: Saved {_globalVariables.Count} variables");
}
}
}
else if (Scribe.mode == LoadSaveMode.LoadingVars)
{
List<string> variablesList = null;
Scribe_Collections.Look(ref variablesList, "ArachnaeSwarm_GlobalVariables", LookMode.Value);
if (variablesList != null)
{
_globalVariables = new HashSet<string>(variablesList);
if (Prefs.DevMode)
{
ArachnaeLog.Debug($"GlobalVariableManager: Loaded {_globalVariables.Count} variables");
}
}
else
{
_globalVariables = new HashSet<string>();
if (Prefs.DevMode)
{
ArachnaeLog.Debug("GlobalVariableManager: No variables found in save, initializing empty set");
}
}
}
}
catch (System.Exception ex)
{
ArachnaeLog.Debug($"Error in GlobalVariableManager.ExposeData: {ex}");
}
}
public static bool HasVariable(string variable)
{
Initialize();
return _globalVariables.Contains(variable);
}
public static void SetVariable(string variable)
{
Initialize();
_globalVariables.Add(variable);
if (Prefs.DevMode)
{
ArachnaeLog.Debug($"GlobalVariableManager: Added variable '{variable}'");
}
}
public static bool RemoveVariable(string variable)
{
Initialize();
bool removed = _globalVariables.Remove(variable);
if (removed && Prefs.DevMode)
{
ArachnaeLog.Debug($"GlobalVariableManager: Removed variable '{variable}'");
}
return removed;
}
public static IEnumerable<string> GetAllVariables()
{
Initialize();
return _globalVariables;
}
public static void ClearAllVariables()
{
Initialize();
int count = _globalVariables.Count;
_globalVariables.Clear();
if (Prefs.DevMode)
{
ArachnaeLog.Debug($"GlobalVariableManager: Cleared {count} variables");
}
}
}
}

View File

@@ -0,0 +1,459 @@
using RimWorld;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Verse;
namespace ArachnaeSwarm
{
/// <summary>
/// 唯一Pawn管理器
/// 管理所有具有唯一标识的Pawn确保同一标识只有一个存活
/// </summary>
public class UniquePawnManager : GameComponent
{
#region 访
private static UniquePawnManager _instance;
public static UniquePawnManager Instance
{
get
{
if (_instance == null && Current.Game != null)
{
_instance = Current.Game.GetComponent<UniquePawnManager>();
if (_instance == null)
{
_instance = new UniquePawnManager(Current.Game);
Current.Game.components.Add(_instance);
}
}
return _instance;
}
}
#endregion
#region
/// <summary>
/// Pawn注册信息
/// </summary>
public class UniquePawnInfo : IExposable
{
public string globalVariable;
public int thingID;
public int spawnTick;
public Map map;
public string pawnName;
public bool isValid = true;
public int lastVerificationTick = -1;
public void ExposeData()
{
Scribe_Values.Look(ref globalVariable, "globalVariable");
Scribe_Values.Look(ref thingID, "thingID");
Scribe_Values.Look(ref spawnTick, "spawnTick");
Scribe_References.Look(ref map, "map");
Scribe_Values.Look(ref pawnName, "pawnName");
Scribe_Values.Look(ref isValid, "isValid", true);
Scribe_Values.Look(ref lastVerificationTick, "lastVerificationTick", -1);
}
}
#endregion
#region
private List<UniquePawnInfo> registeredPawns = new List<UniquePawnInfo>();
private int lastVerificationTick = -1;
private const int VERIFICATION_INTERVAL = 600; // 每10秒验证一次
private const int VERIFICATION_TIMEOUT = 1200; // 20秒无响应视为失效
private object syncLock = new object();
#endregion
#region
public UniquePawnManager(Game game) : base()
{
Initialize();
}
private void Initialize()
{
lock (syncLock)
{
if (registeredPawns == null)
registeredPawns = new List<UniquePawnInfo>();
}
}
#endregion
#region
/// <summary>
/// 注册唯一Pawn
/// </summary>
public bool RegisterPawn(Pawn pawn, string globalVariable)
{
if (pawn == null || string.IsNullOrEmpty(globalVariable))
{
Log.Warning("尝试注册空Pawn或空全局变量");
return false;
}
lock (syncLock)
{
// 清理无效注册
CleanupInvalidRegistrations();
// 获取当前tick
int currentTick = Find.TickManager.TicksGame;
// 检查是否已存在相同全局变量的Pawn
var existingInfos = GetInfosForVariable(globalVariable);
var validExistingInfos = existingInfos.Where(info => info.isValid).ToList();
// 如果有有效的已注册Pawn
if (validExistingInfos.Count > 0)
{
// 按生成时间排序,保留最早的
var earliestInfo = validExistingInfos.OrderBy(info => info.spawnTick).First();
// 如果当前Pawn不是最早的则杀死
if (earliestInfo.thingID != pawn.thingIDNumber)
{
Log.Message($"[唯一Pawn系统] 检测到重复Pawn: {pawn.Label} (变量: {globalVariable})");
Log.Message($"[唯一Pawn系统] 保留最早Pawn: {earliestInfo.pawnName} (生成于tick: {earliestInfo.spawnTick})");
// 杀死当前Pawn
KillDuplicatePawn(pawn, globalVariable, earliestInfo.pawnName);
return false;
}
else
{
// 当前Pawn就是最早的更新验证时间
var info = registeredPawns.FirstOrDefault(i => i.globalVariable == globalVariable && i.thingID == pawn.thingIDNumber);
if (info != null)
{
info.lastVerificationTick = currentTick;
info.isValid = true;
}
return true;
}
}
else
{
// 创建新注册信息
var newInfo = new UniquePawnInfo
{
globalVariable = globalVariable,
thingID = pawn.thingIDNumber,
spawnTick = currentTick,
map = pawn.Map,
pawnName = pawn.Label,
isValid = true,
lastVerificationTick = currentTick
};
registeredPawns.Add(newInfo);
Log.Message($"[唯一Pawn系统] 注册新Pawn: {pawn.Label} (变量: {globalVariable}, ID: {pawn.thingIDNumber})");
return true;
}
}
}
/// <summary>
/// 注销Pawn
/// </summary>
public void UnregisterPawn(Pawn pawn, string globalVariable)
{
if (pawn == null || string.IsNullOrEmpty(globalVariable))
return;
lock (syncLock)
{
var info = registeredPawns.FirstOrDefault(i =>
i.globalVariable == globalVariable &&
i.thingID == pawn.thingIDNumber);
if (info != null)
{
info.isValid = false;
Log.Message($"[唯一Pawn系统] 注销Pawn: {pawn.Label} (变量: {globalVariable})");
}
}
}
/// <summary>
/// 发送验证信号
/// </summary>
public void SendVerificationSignal(Pawn pawn, string globalVariable)
{
if (pawn == null || string.IsNullOrEmpty(globalVariable) || pawn.Destroyed || !pawn.Spawned)
return;
lock (syncLock)
{
var info = registeredPawns.FirstOrDefault(i =>
i.globalVariable == globalVariable &&
i.thingID == pawn.thingIDNumber);
if (info != null)
{
info.lastVerificationTick = Find.TickManager.TicksGame;
info.isValid = true;
info.map = pawn.Map;
}
else
{
// Pawn未注册尝试重新注册
RegisterPawn(pawn, globalVariable);
}
}
}
/// <summary>
/// 检查Pawn是否是最早的
/// </summary>
public bool IsEarliestPawn(Pawn pawn, string globalVariable)
{
if (pawn == null || string.IsNullOrEmpty(globalVariable))
return false;
lock (syncLock)
{
var existingInfos = GetInfosForVariable(globalVariable);
var validExistingInfos = existingInfos.Where(info => info.isValid).ToList();
if (validExistingInfos.Count == 0)
return true;
var earliestInfo = validExistingInfos.OrderBy(info => info.spawnTick).First();
return earliestInfo.thingID == pawn.thingIDNumber;
}
}
/// <summary>
/// 获取指定变量的所有Pawn信息
/// </summary>
public List<UniquePawnInfo> GetInfosForVariable(string globalVariable)
{
lock (syncLock)
{
return registeredPawns.Where(info => info.globalVariable == globalVariable).ToList();
}
}
/// <summary>
/// 获取调试信息
/// </summary>
public string GetDebugInfo()
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
sb.AppendLine("=== 唯一Pawn管理器调试信息 ===");
sb.AppendLine($"已注册Pawn数量: {registeredPawns.Count}");
lock (syncLock)
{
var grouped = registeredPawns
.Where(info => info.isValid)
.GroupBy(info => info.globalVariable)
.OrderBy(g => g.Key);
foreach (var group in grouped)
{
sb.AppendLine($"变量: {group.Key}");
var ordered = group.OrderBy(info => info.spawnTick).ToList();
for (int i = 0; i < ordered.Count; i++)
{
var info = ordered[i];
string status = i == 0 ? "[最早]" : "[重复]";
int ageTicks = Find.TickManager.TicksGame - info.spawnTick;
int lastVerifyAgo = Find.TickManager.TicksGame - info.lastVerificationTick;
sb.AppendLine($" {status} {info.pawnName} (ID: {info.thingID})");
sb.AppendLine($" 生成时间: {info.spawnTick} ({ageTicks} ticks前)");
sb.AppendLine($" 最后验证: {info.lastVerificationTick} ({lastVerifyAgo} ticks前)");
sb.AppendLine($" 地图: {info.map?.Index ?? -1}");
}
}
}
return sb.ToString();
}
#endregion
#region
/// <summary>
/// 清理无效注册
/// </summary>
private void CleanupInvalidRegistrations()
{
int currentTick = Find.TickManager.TicksGame;
int removedCount = 0;
for (int i = registeredPawns.Count - 1; i >= 0; i--)
{
var info = registeredPawns[i];
// 移除无效的
if (!info.isValid)
{
registeredPawns.RemoveAt(i);
removedCount++;
continue;
}
// 检查超时
if (info.lastVerificationTick > 0 &&
currentTick - info.lastVerificationTick > VERIFICATION_TIMEOUT)
{
// 尝试查找Pawn
Pawn pawn = FindPawnByID(info.thingID, info.map);
if (pawn == null || pawn.Destroyed || !pawn.Spawned)
{
info.isValid = false;
registeredPawns.RemoveAt(i);
removedCount++;
Log.Message($"[唯一Pawn系统] 清理超时Pawn: {info.pawnName} (变量: {info.globalVariable})");
}
else
{
// Pawn仍然存在更新验证时间
info.lastVerificationTick = currentTick;
}
}
}
}
/// <summary>
/// 通过ID查找Pawn
/// </summary>
private Pawn FindPawnByID(int thingID, Map map)
{
if (map == null || thingID <= 0)
return null;
try
{
return map.listerThings.ThingsInGroup(ThingRequestGroup.Pawn)
.OfType<Pawn>()
.FirstOrDefault(p => p.thingIDNumber == thingID);
}
catch (Exception ex)
{
Log.Error($"查找Pawn失败: {ex.Message}");
return null;
}
}
/// <summary>
/// 杀死重复的Pawn
/// </summary>
private void KillDuplicatePawn(Pawn pawn, string globalVariable, string earliestPawnName)
{
try
{
if (pawn == null || pawn.Destroyed || !pawn.Spawned)
return;
// 显示死亡消息
string deathMessage = $"{pawn.Label} 被移除,因为 {earliestPawnName} 是更早生成的唯一Pawn ({globalVariable})";
Messages.Message(deathMessage, pawn, MessageTypeDefOf.NegativeEvent);
// 使用安全的杀死方法
pawn.Kill(null);
Log.Message($"[唯一Pawn系统] 移除重复Pawn: {pawn.Label} (变量: {globalVariable})");
}
catch (Exception ex)
{
Log.Error($"杀死重复Pawn失败: {ex.Message}");
}
}
/// <summary>
/// 定期验证所有Pawn
/// </summary>
private void VerifyAllPawns()
{
lock (syncLock)
{
int currentTick = Find.TickManager.TicksGame;
int verifiedCount = 0;
int timeoutCount = 0;
foreach (var info in registeredPawns.Where(i => i.isValid))
{
// 查找Pawn
Pawn pawn = FindPawnByID(info.thingID, info.map);
if (pawn == null || pawn.Destroyed || !pawn.Spawned)
{
// Pawn已不存在
info.isValid = false;
timeoutCount++;
}
else if (currentTick - info.lastVerificationTick > VERIFICATION_INTERVAL)
{
// 需要发送验证请求
SendVerificationRequest(pawn, info.globalVariable);
}
else
{
verifiedCount++;
}
}
// 清理无效的
CleanupInvalidRegistrations();
}
}
/// <summary>
/// 发送验证请求给Pawn
/// </summary>
private void SendVerificationRequest(Pawn pawn, string globalVariable)
{
// 这里可以发送信号或事件给Pawn
// 为了简化,我们直接更新验证时间
// 实际应用中Pawn应该定期主动发送验证信号
var comp = pawn.TryGetComp<CompUniquePawn>();
if (comp != null)
{
comp.SendVerificationSignal();
}
}
#endregion
#region GameComponent实现
public override void GameComponentTick()
{
base.GameComponentTick();
if (Find.TickManager.TicksGame - lastVerificationTick > VERIFICATION_INTERVAL)
{
VerifyAllPawns();
lastVerificationTick = Find.TickManager.TicksGame;
}
}
public override void ExposeData()
{
base.ExposeData();
lock (syncLock)
{
Scribe_Collections.Look(ref registeredPawns, "registeredPawns", LookMode.Deep);
if (Scribe.mode == LoadSaveMode.PostLoadInit)
{
// 加载后初始化
Initialize();
// 清理无效数据
CleanupInvalidRegistrations();
Log.Message($"[唯一Pawn系统] 加载了 {registeredPawns?.Count ?? 0} 个Pawn注册信息");
}
}
}
#endregion
}
}