diff --git a/1.6/1.6/Assemblies/ArachnaeSwarm.dll b/1.6/1.6/Assemblies/ArachnaeSwarm.dll
index c16048f..fa9343a 100644
Binary files a/1.6/1.6/Assemblies/ArachnaeSwarm.dll and b/1.6/1.6/Assemblies/ArachnaeSwarm.dll differ
diff --git a/1.6/1.6/Defs/AbilityDefs/ARA_Abilities.xml b/1.6/1.6/Defs/AbilityDefs/ARA_Abilities.xml
index a16c3aa..78fe037 100644
--- a/1.6/1.6/Defs/AbilityDefs/ARA_Abilities.xml
+++ b/1.6/1.6/Defs/AbilityDefs/ARA_Abilities.xml
@@ -121,6 +121,36 @@
+
+ ARA_Suicide_Fast_Ability
+ 自杀
+ 只要一声令下,阿拉克涅虫族随时可以为了虫巢的大业立刻死去
+ ArachnaeSwarm/UI/Abilities/ARA_Suicide_Ability
+ 1
+ false
+ 9900
+ true
+ false
+ AcidSpray_Warmup
+
+ Verb_CastAbility
+ 24
+ 2
+ AcidSpray_Resolve
+ false
+ false
+
+ true
+
+
+
+
+
+ Brain
+
+
+
+
diff --git a/1.6/1.6/Defs/PawnKindDef/ARA_PawnKinds.xml b/1.6/1.6/Defs/PawnKindDef/ARA_PawnKinds.xml
index cfa1894..9e5cb95 100644
--- a/1.6/1.6/Defs/PawnKindDef/ARA_PawnKinds.xml
+++ b/1.6/1.6/Defs/PawnKindDef/ARA_PawnKinds.xml
@@ -42,8 +42,8 @@
-
- ARA_ArachnaeQueen
+
+ ArachnaeQueen_Race_Titan
阿拉克涅女皇种
ArachnaeQueen_Race_Titan
PlayerColony
@@ -64,6 +64,26 @@
ARA_TumorSpew
+
+ ArachnaeQueen_Race_Neurotyrant
+ 阿拉克涅女皇种
+ ArachnaeQueen_Race_Neurotyrant
+ PlayerColony
+ 0
+
+
+
+ ArachnaeQueen_spawnCategoriesA
+ ArachnaeQueen_spawnCategoriesB
+
+
+
+
+
+ 0
+
+
+
ArachnaeQueen_Race_TitanSettings
@@ -72,7 +92,7 @@
- ARA_ArachnaeQueen
+ ArachnaeQueen_Race_Titan
100.0
@@ -87,7 +107,7 @@
- ARA_ArachnaeQueen
+ ArachnaeQueen_Race_Titan
ArachnaeNode_Race_ShieldHead
ArachnaeNode_Race_WeaponSmith
ArachnaeNode_Race_Fighter
@@ -110,7 +130,7 @@
- ARA_ArachnaeQueen
+ ArachnaeQueen_Race_Titan
ArachnaeNode_Race_ShieldHead
ArachnaeNode_Race_WeaponSmith
ArachnaeNode_Race_Fighter
@@ -128,7 +148,7 @@
- ARA_ArachnaeQueen
+ ArachnaeQueen_Race_Titan
ArachnaeNode_Race_ShieldHead
ArachnaeNode_Race_WeaponSmith
ArachnaeNode_Race_Fighter
@@ -444,6 +464,9 @@
3.0
+
+ ARA_Suicide_Fast_Ability
+
ArachnaeBase_Race_Larva
diff --git a/1.6/1.6/Defs/Scenarios/ARA_Scenarios.xml b/1.6/1.6/Defs/Scenarios/ARA_Scenarios.xml
index 222c634..f17e46f 100644
--- a/1.6/1.6/Defs/Scenarios/ARA_Scenarios.xml
+++ b/1.6/1.6/Defs/Scenarios/ARA_Scenarios.xml
@@ -22,7 +22,7 @@
True
- ARA_ArachnaeQueen
+ ArachnaeQueen_Race_Titan
diff --git a/1.6/1.6/Defs/ThingDef_Races/ARA_RaceBaseSwarm.xml b/1.6/1.6/Defs/ThingDef_Races/ARA_RaceBaseSwarm.xml
index f3db801..5456f41 100644
--- a/1.6/1.6/Defs/ThingDef_Races/ARA_RaceBaseSwarm.xml
+++ b/1.6/1.6/Defs/ThingDef_Races/ARA_RaceBaseSwarm.xml
@@ -675,7 +675,7 @@
true
- Always
+ DraftedAndMove
ARA_Psi_FlyNorth
ARA_Psi_FlyNorth
diff --git a/1.6/1.6/Defs/ThingDef_Races/ARA_RaceQueen.xml b/1.6/1.6/Defs/ThingDef_Races/ARA_RaceQueen.xml
index c11c6de..f1fd747 100644
--- a/1.6/1.6/Defs/ThingDef_Races/ARA_RaceQueen.xml
+++ b/1.6/1.6/Defs/ThingDef_Races/ARA_RaceQueen.xml
@@ -693,6 +693,11 @@
+
+ true
+ ARA_RaceBaseSwarmProduceSwitchHediff
+ CocoonDestroyed
+
Unique_Arachnae_Queen
true
@@ -786,11 +791,6 @@
-
- true
- ARA_RaceBaseSwarmProduceSwitchHediff
- CocoonDestroyed
-
3
180
@@ -805,601 +805,58 @@
-
+
ArachnaeQueen_Race_Neurotyrant
阿拉克涅灵吸种
阿拉克涅灵吸种是女皇种亚种之一,归属于阿拉克涅的灵能触须。她们负责维持虫族蜂巢灵能网路的通讯,并作为中继节点链接各战区女皇种和虫巢舰队。\n\n但是灵吸种可不是脆弱的“文官”,她们强大的灵能使得她们可以通过超自然力量主宰当地战局,随着她们不断吞噬本地物种,其灵能能力还能得到不断的进化。
-
-
-
-
- 0
-
- true
-
- 15
-
- false
-
- 0
-
- false
-
- false
-
-
-
-
- Female_AverageNormal
-
-
-
-
- Thin
-
-
-
-
-
- skin
-
- 0
- 0
-
-
-
-
- hair
-
-
-
- 15
- (0.4,0.3,0.5)
- (0.6,0.1,0.7)
-
-
- 6
- (0.9,0.9,0.9)
- (0.9,0.9,0.9)
-
-
- 6
- (1,0.8,0.8)
- (1,0.9,0.9)
-
-
- 3
- (1,1,1)
- (1,1,1)
-
-
-
-
-
-
-
-
-
- 4
- 4
- (4,4)
- (1.0,1.0)
- (2,2)
-
-
- 0
-
-
-
-
- true
-
-
- (0,1)
-
-
-
-
- true
-
-
- (0,1)
-
-
-
-
- true
-
-
- (0,1)
-
-
-
-
- true
-
-
- (0,1)
-
-
-
-
- true
-
-
- (0,1)
-
-
-
-
- true
-
-
- (0, 1)
-
-
-
-
-
-
- Male
-
-
- (0,0)
- (1,0)
- (999,0)
- (9999,0)
-
-
-
-
-
- (0,0)
- (1,0)
- (999,0)
- (9999,0)
-
-
-
-
-
-
- (1,1,1,1)
-
- Cutout
- ArachnaeSwarm/Things/ARA_HiveQueen/Bodies/
- ArachnaeSwarm/Things/ARA_HiveQueen/Heads/
- Things/Pawn/Humanlike/Bodies/Dessicated/Dessicated_Thin
- ArachnaeSwarm/Things/ARA_HiveQueen/Bodies/
-
-
-
-
-
- HairDef
-
- true
-
- Bald
-
-
-
-
-
- TattooDef
-
- false
-
-
-
-
- BeardDef
-
- false
-
-
-
-
-
-
-
- HairColor
- Melanin
- BodyType
- Ears
- Nose
- Voice
- Headbone
- Head
- Jaw
-
-
-
- EyeColor
- Tail
- BeardStyle
- Fur
-
-
-
- Furskin
- Brow_Heavy
-
-
-
- Dirtmole
- Genie
- Hussar
- Sanguophage
- Neanderthal
- Pigskin
- Impid
- Waster
- Yttakin
- Highmate
-
-
-
-
- true
-
-
- ARA_InsectJelly
- ARA_NutrientPasteMeal
- ARA_PheromoneSolvent
-
- true
-
-
- ArachnaeBase_Race_Scavenger
- ArachnaeBase_Race_Larva
-
- true
-
-
-
-
-
-
- true
-
- true
- false
-
-
-
-
-
-
-
-
- BodyPurist
- Cannibal
- CreepyBreathing
- DislikesWomen
- Gourmand
- QuickSleeper
- Wimp
- Beauty
- DrugDesire
- Immunity
- PsychicSensitivity
-
-
-
-
-
-
- ColonistLost
- KnowColonistDied
- PawnWithGoodOpinionDied
- PsychicDrone
- Naked
- AnyBodyPartButGroinCovered_Disapproved_Female
- AnyBodyPartButGroinCovered_Disapproved_Male
- AnyBodyPartButGroinCovered_Disapproved_Memory
- AnyBodyPartButGroinCovered_Disapproved_Social_Female
- AnyBodyPartButGroinCovered_Disapproved_Social_Male
- AnyBodyPartCovered_Disapproved_Female
- AnyBodyPartCovered_Disapproved_Male
- AnyBodyPartCovered_Disapproved_Memory
- AnyBodyPartCovered_Disapproved_Social_Female
- AnyBodyPartCovered_Disapproved_Social_Male
- GroinChestHairOrFaceUncovered_Disapproved_Female
- GroinChestHairOrFaceUncovered_Disapproved_Male
- GroinChestHairOrFaceUncovered_Disapproved_Social_Female
- GroinChestHairOrFaceUncovered_Disapproved_Social_Male
- GroinChestOrHairUncovered_Disapproved_Female
- GroinChestOrHairUncovered_Disapproved_Male
- GroinChestOrHairUncovered_Disapproved_Social_Female
- GroinChestOrHairUncovered_Disapproved_Social_Male
- GroinOrChestUncovered_Disapproved_Female
- GroinOrChestUncovered_Disapproved_Male
- GroinOrChestUncovered_Disapproved_Social_Female
- GroinOrChestUncovered_Disapproved_Social_Male
- GroinUncovered_Disapproved_Female
- GroinUncovered_Disapproved_Male
- GroinUncovered_Disapproved_Social_Female
- GroinUncovered_Disapproved_Social_Male
- WitnessedDeathAlly
- ObservedLayingCorpse
- ObservedLayingRottingCorpse
- ApparelDamaged
- ProsthophileHappy
- ProsthophobeUnhappy
- BrawlerUnhappy
- PyromaniacHappy
- Greedy
- Jealous
- SharedBed
- AteWithoutTable
- SleptOutside
- SleptOnGround
- SleptInCold
- SleptInHeat
- Ugly
- AteKibble
- AteInsectMeatDirect
- AteInsectMeatAsIngredient
- AteRawFood
- AteHumanlikeMeatDirect
- AteHumanlikeMeatAsIngredient
- KnowButcheredHumanlikeCorpse
- ButcheredHumanlikeCorpseOpinion
- AteRawHumanlikeMeat
-
-
-
-
-
-
-
- 0
- 0
-
- 0
- 0
- 0
-
- 0
- 0
- 0
-
-
-
-
- ARA_CureBloodRot
- ARA_CureAcid
- RemovePorcupineQuill
- SurgicalInspection
- ARA_Surgery_Install_Plasteel
- ARA_Surgery_Install_Carapace_Shell
- ARA_Surgery_Install_Huge_Stomach
- ARA_Surgery_Install_Cycle_Suppression
- ARA_Surgery_Install_Shell_Thorn
- ARA_Surgery_Install_Reactive_Shell
- ARA_Surgery_Install_Strengthening_Tendon
- ARA_Surgery_Install_Slide_Patagium
- ARA_Surgery_Install_Baneling_Pouch
- ARA_Surgery_Install_Tumor_Pouch
- ARA_Surgery_Install_Internal_Circulation_Lung
-
-
-
-
2000
- 5
-
- 1.75
+ 2.5
250
-
-
2
-
- 0.5
+ 1
-
200
- 450
- 600
+ 350
+ 300
-
- 1
-
+ 1.25
2.5
-
0
-
0.95
0.95
-
- 0.1
+ 0.2
-
- 0.25
-
+ 0.4
-
-
-
-
+ 0.35
+ 0.35
+ 0.25
-
- 0.6
- 0.8
- 0.5
+ 2
-
- 5
-
-
- 0
-
- 15
+ 18
1
-
ArachnaeQueen_Body
- Normal
-
- ARA_Humanlike
-
- Humanlike
-
- HumanStandard
- Filth_BloodInsect
- Filth_BloodSmear
-
- 10
-
- 10
-
- ARA_Carapace
-
- Pawn_Melee_BigBash_HitPawn
- Pawn_Melee_BigBash_HitBuilding
- Pawn_Melee_BigBash_Miss
- Pawn_MeleeDodge
-
- 300
-
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
-
-
-
- HumanlikeBaby
- 0
-
-
- HumanlikeChild
- 0.25
-
-
- HumanlikePreTeenager
- 0.5
-
-
- HumanlikeTeenager
- 0.75
-
-
- HumanlikeAdult
- 1
- Pawn_HiveQueen_Wounded
- Pawn_HiveQueen_Death
- Pawn_HiveQueen_Call
- Pawn_HiveQueen_Angry
-
-
- false
+ 5
+ 4
+ true
-
-
-
- 头顶
-
- Poke
-
- 16
- 2
- HeadAttackTool
- true
- 0.01
-
-
- 踩踏
-
- Blunt
- Poke
-
- 35
- 2.5
- Legs
-
-
- 腿部穿刺
-
- Stab
-
- 50
- 3
- Legs
-
-
- 钳击
-
- Cut
-
- 30
- 2
- Hands
-
-
-
-
- Unique_Arachnae_Queen
- true
- ARA_QueenAlreadyExists
-
+
+ DraftedAndMove
+
+ ARA_Psi_FlyNorth
+ ARA_Psi_FlyEast
+ ARA_Psi_FlySouth
-
-
- ARA_HiveStrength
- ARA_HiveMindMaster
-
- 1.0
- false
-
-
- true
- ARA_RaceBaseSwarmProduceSwitchHediff
- CocoonDestroyed
-
-
- 3
- 180
- Crush
- 10
- false
- ARA_Area_Crush
- false
- 践踏
- 这只阿拉克涅虫族的身躯是如此巨大,以至于靠近它的敌人会被直接一脚踩死
- ArachnaeSwarm/UI/Abilities/ARA_Area_Crush
-
-
-
\ No newline at end of file
diff --git a/1.6/1.6/Defs/Thing_building/ARA_InteractiveEggSac.xml b/1.6/1.6/Defs/Thing_building/ARA_InteractiveEggSac.xml
index 9dca102..929656a 100644
--- a/1.6/1.6/Defs/Thing_building/ARA_InteractiveEggSac.xml
+++ b/1.6/1.6/Defs/Thing_building/ARA_InteractiveEggSac.xml
@@ -76,7 +76,7 @@
- ARA_ArachnaeQueen
+ ArachnaeQueen_Race_Titan
true
@@ -124,12 +124,12 @@
- ARA_ArachnaeQueen
+ ArachnaeQueen_Race_Titan
180000
true
diff --git a/Source/ArachnaeSwarm/.vs/ArachnaeSwarm/v17/.suo b/Source/ArachnaeSwarm/.vs/ArachnaeSwarm/v17/.suo
index 71d5ba1..8783c4a 100644
Binary files a/Source/ArachnaeSwarm/.vs/ArachnaeSwarm/v17/.suo and b/Source/ArachnaeSwarm/.vs/ArachnaeSwarm/v17/.suo differ
diff --git a/Source/ArachnaeSwarm/.vs/ArachnaeSwarm/v17/DocumentLayout.json b/Source/ArachnaeSwarm/.vs/ArachnaeSwarm/v17/DocumentLayout.json
index 990db67..a10e023 100644
--- a/Source/ArachnaeSwarm/.vs/ArachnaeSwarm/v17/DocumentLayout.json
+++ b/Source/ArachnaeSwarm/.vs/ArachnaeSwarm/v17/DocumentLayout.json
@@ -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}"
diff --git a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj
index ff583ad..05c750b 100644
--- a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj
+++ b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj
@@ -361,14 +361,14 @@
+
+
+
-
-
-
@@ -439,6 +439,7 @@
+
diff --git a/Source/ArachnaeSwarm/Pawn_Comps/ARA_Flight/CompProperties_PawnFlight.cs b/Source/ArachnaeSwarm/Pawn_Comps/ARA_Flight/CompProperties_PawnFlight.cs
index f5a62e5..5d6e2a4 100644
--- a/Source/ArachnaeSwarm/Pawn_Comps/ARA_Flight/CompProperties_PawnFlight.cs
+++ b/Source/ArachnaeSwarm/Pawn_Comps/ARA_Flight/CompProperties_PawnFlight.cs
@@ -6,6 +6,7 @@ namespace ArachnaeSwarm
public enum FlightCondition
{
Drafted,
+ DraftedAndMove,
Always
}
diff --git a/Source/ArachnaeSwarm/Pawn_Comps/ARA_Flight/Pawn_FlightTrackerPatches.cs b/Source/ArachnaeSwarm/Pawn_Comps/ARA_Flight/Pawn_FlightTrackerPatches.cs
index f3c7cd8..eb10b51 100644
--- a/Source/ArachnaeSwarm/Pawn_Comps/ARA_Flight/Pawn_FlightTrackerPatches.cs
+++ b/Source/ArachnaeSwarm/Pawn_Comps/ARA_Flight/Pawn_FlightTrackerPatches.cs
@@ -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;
diff --git a/Source/ArachnaeSwarm/Pawn_Comps/ARA_UniquePawn/CompProperties_UniquePawn.cs b/Source/ArachnaeSwarm/Pawn_Comps/ARA_UniquePawn/CompProperties_UniquePawn.cs
index 1d3afeb..39e4321 100644
--- a/Source/ArachnaeSwarm/Pawn_Comps/ARA_UniquePawn/CompProperties_UniquePawn.cs
+++ b/Source/ArachnaeSwarm/Pawn_Comps/ARA_UniquePawn/CompProperties_UniquePawn.cs
@@ -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()
{
diff --git a/Source/ArachnaeSwarm/Pawn_Comps/ARA_UniquePawn/CompUniquePawn.cs b/Source/ArachnaeSwarm/Pawn_Comps/ARA_UniquePawn/CompUniquePawn.cs
index 1f273bf..3f07b0b 100644
--- a/Source/ArachnaeSwarm/Pawn_Comps/ARA_UniquePawn/CompUniquePawn.cs
+++ b/Source/ArachnaeSwarm/Pawn_Comps/ARA_UniquePawn/CompUniquePawn.cs
@@ -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 公共方法
+ ///
+ /// 发送验证信号给管理器
+ ///
+ public void SendVerificationSignal()
+ {
+ if (this.parent is Pawn pawn && !string.IsNullOrEmpty(GlobalVariable) && pawn.Spawned && !pawn.Destroyed)
+ {
+ UniquePawnManager.Instance?.SendVerificationSignal(pawn, GlobalVariable);
+ }
+ }
+
+ ///
+ /// 检查是否是有效的唯一Pawn
+ ///
+ 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
}
}
diff --git a/Source/ArachnaeSwarm/Pawn_Comps/ARA_UniquePawn/Patch_UniquePawn.cs b/Source/ArachnaeSwarm/Pawn_Comps/ARA_UniquePawn/Patch_UniquePawn.cs
deleted file mode 100644
index bbb70d0..0000000
--- a/Source/ArachnaeSwarm/Pawn_Comps/ARA_UniquePawn/Patch_UniquePawn.cs
+++ /dev/null
@@ -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();
- 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();
- 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 _globalVariables;
- private static bool _initialized = false;
-
- public static void Initialize()
- {
- if (!_initialized)
- {
- _globalVariables = new HashSet();
- _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 variablesList = new List(_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 variablesList = null;
- Scribe_Collections.Look(ref variablesList, "ArachnaeSwarm_GlobalVariables", LookMode.Value);
-
- if (variablesList != null)
- {
- _globalVariables = new HashSet(variablesList);
- if (Prefs.DevMode)
- {
- ArachnaeLog.Debug($"GlobalVariableManager: Loaded {_globalVariables.Count} variables");
- }
- }
- else
- {
- _globalVariables = new HashSet();
- 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 GetAllVariables()
- {
- Initialize();
- return _globalVariables;
- }
-
- public static void ClearAllVariables()
- {
- Initialize();
- int count = _globalVariables.Count;
- _globalVariables.Clear();
-
- if (Prefs.DevMode)
- {
- ArachnaeLog.Debug($"GlobalVariableManager: Cleared {count} variables");
- }
- }
- }
-}
diff --git a/Source/ArachnaeSwarm/Pawn_Comps/ARA_UniquePawn/UniquePawnManager.cs b/Source/ArachnaeSwarm/Pawn_Comps/ARA_UniquePawn/UniquePawnManager.cs
new file mode 100644
index 0000000..e94309c
--- /dev/null
+++ b/Source/ArachnaeSwarm/Pawn_Comps/ARA_UniquePawn/UniquePawnManager.cs
@@ -0,0 +1,459 @@
+using RimWorld;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using UnityEngine;
+using Verse;
+
+namespace ArachnaeSwarm
+{
+ ///
+ /// 唯一Pawn管理器
+ /// 管理所有具有唯一标识的Pawn,确保同一标识只有一个存活
+ ///
+ public class UniquePawnManager : GameComponent
+ {
+ #region 单例和静态访问
+ private static UniquePawnManager _instance;
+ public static UniquePawnManager Instance
+ {
+ get
+ {
+ if (_instance == null && Current.Game != null)
+ {
+ _instance = Current.Game.GetComponent();
+ if (_instance == null)
+ {
+ _instance = new UniquePawnManager(Current.Game);
+ Current.Game.components.Add(_instance);
+ }
+ }
+ return _instance;
+ }
+ }
+ #endregion
+
+ #region 数据定义
+ ///
+ /// Pawn注册信息
+ ///
+ 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 registeredPawns = new List();
+ 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();
+ }
+ }
+ #endregion
+
+ #region 公共接口
+ ///
+ /// 注册唯一Pawn
+ ///
+ 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;
+ }
+ }
+ }
+
+ ///
+ /// 注销Pawn
+ ///
+ 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})");
+ }
+ }
+ }
+
+ ///
+ /// 发送验证信号
+ ///
+ 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);
+ }
+ }
+ }
+
+ ///
+ /// 检查Pawn是否是最早的
+ ///
+ 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;
+ }
+ }
+
+ ///
+ /// 获取指定变量的所有Pawn信息
+ ///
+ public List GetInfosForVariable(string globalVariable)
+ {
+ lock (syncLock)
+ {
+ return registeredPawns.Where(info => info.globalVariable == globalVariable).ToList();
+ }
+ }
+
+ ///
+ /// 获取调试信息
+ ///
+ 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 内部方法
+ ///
+ /// 清理无效注册
+ ///
+ 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;
+ }
+ }
+ }
+ }
+
+ ///
+ /// 通过ID查找Pawn
+ ///
+ private Pawn FindPawnByID(int thingID, Map map)
+ {
+ if (map == null || thingID <= 0)
+ return null;
+
+ try
+ {
+ return map.listerThings.ThingsInGroup(ThingRequestGroup.Pawn)
+ .OfType()
+ .FirstOrDefault(p => p.thingIDNumber == thingID);
+ }
+ catch (Exception ex)
+ {
+ Log.Error($"查找Pawn失败: {ex.Message}");
+ return null;
+ }
+ }
+
+ ///
+ /// 杀死重复的Pawn
+ ///
+ 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}");
+ }
+ }
+
+ ///
+ /// 定期验证所有Pawn
+ ///
+ 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();
+ }
+ }
+
+ ///
+ /// 发送验证请求给Pawn
+ ///
+ private void SendVerificationRequest(Pawn pawn, string globalVariable)
+ {
+ // 这里可以发送信号或事件给Pawn
+ // 为了简化,我们直接更新验证时间
+ // 实际应用中,Pawn应该定期主动发送验证信号
+ var comp = pawn.TryGetComp();
+ 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
+ }
+}
diff --git a/非公开资源/Content/Textures/Things/ARA_Neurotyrant/Bodies/Naked_Thin_east.sai2 b/非公开资源/Content/Textures/Things/ARA_Neurotyrant/Bodies/Naked_Thin_east.sai2
index 0d81feb..18cf89f 100644
Binary files a/非公开资源/Content/Textures/Things/ARA_Neurotyrant/Bodies/Naked_Thin_east.sai2 and b/非公开资源/Content/Textures/Things/ARA_Neurotyrant/Bodies/Naked_Thin_east.sai2 differ