diff --git a/1.6/1.6/Assemblies/ArachnaeSwarm.dll b/1.6/1.6/Assemblies/ArachnaeSwarm.dll
index 399b41d..9474e7e 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/HediffDefs/ARA_Hediffs_HiveMind.xml b/1.6/1.6/Defs/HediffDefs/ARA_Hediffs_HiveMind.xml
index 1347f1f..d88280f 100644
--- a/1.6/1.6/Defs/HediffDefs/ARA_Hediffs_HiveMind.xml
+++ b/1.6/1.6/Defs/HediffDefs/ARA_Hediffs_HiveMind.xml
@@ -226,15 +226,6 @@
-
- ARA_NonPlayerHediffSet
-
-
- ARA_NonPlayer_HiveMindDroneHediff
- 0.1
-
-
-
ARA_HiveMindWorker
diff --git a/1.6/1.6/Defs/PawnKindDef/ARA_Hostile_Hive_PawnKinds.xml b/1.6/1.6/Defs/PawnKindDef/ARA_Hostile_Hive_PawnKinds.xml
index 33b2cbd..45abbde 100644
--- a/1.6/1.6/Defs/PawnKindDef/ARA_Hostile_Hive_PawnKinds.xml
+++ b/1.6/1.6/Defs/PawnKindDef/ARA_Hostile_Hive_PawnKinds.xml
@@ -21,7 +21,10 @@
300~500
300~500
100
-
+
+
+ ARA_NonPlayer_HiveMindDroneHediff
+
CrumblingMind
0.01
@@ -53,7 +56,10 @@
300~500
500~1200
150
-
+
+
+ ARA_NonPlayer_HiveMindDroneHediff
+
CrumblingMind
0.01
@@ -85,7 +91,10 @@
400~700
500~1200
100
-
+
+
+ ARA_NonPlayer_HiveMindDroneHediff
+
CrumblingMind
0.01
@@ -117,7 +126,10 @@
500~900
800~1800
250
-
+
+
+ ARA_NonPlayer_HiveMindDroneHediff
+
CrumblingMind
0.01
@@ -145,7 +157,10 @@
600~1200
600~1000
320
-
+
+
+ ARA_NonPlayer_HiveMindDroneHediff
+
CrumblingMind
0.01
@@ -183,7 +198,10 @@
500~900
300~1200
380
-
+
+
+ ARA_NonPlayer_HiveMindDroneHediff
+
CrumblingMind
0.01
@@ -215,7 +233,10 @@
600~1500
1200~2500
450
-
+
+
+ ARA_NonPlayer_HiveMindDroneHediff
+
CrumblingMind
0.01
@@ -243,7 +264,10 @@
1000~2500
2000~3000
600
-
+
+
+ ARA_NonPlayer_HiveMindDroneHediff
+
CrumblingMind
0.01
diff --git a/1.6/1.6/Defs/PawnKindDef/ARA_PawnKinds.xml b/1.6/1.6/Defs/PawnKindDef/ARA_PawnKinds.xml
index 67c0dab..bd51210 100644
--- a/1.6/1.6/Defs/PawnKindDef/ARA_PawnKinds.xml
+++ b/1.6/1.6/Defs/PawnKindDef/ARA_PawnKinds.xml
@@ -188,6 +188,8 @@
false
ARA_Suicide_Ability
+ ARA_RaceBaseSwarmProduceOff
+ ARA_RaceBaseSwarmProduceOn
@@ -211,6 +213,9 @@
Violent
+
+ ARA_HiveMindDrone
+
@@ -234,8 +239,6 @@
- ARA_RaceBaseSwarmProduceOff
- ARA_RaceBaseSwarmProduceOn
ARA_AcidSprayBurst
@@ -259,10 +262,6 @@
0
-
- ARA_RaceBaseSwarmProduceOff
- ARA_RaceBaseSwarmProduceOn
-
ArachnaeNode_Race_WeaponSmith
@@ -281,10 +280,6 @@
0
-
- ARA_RaceBaseSwarmProduceOff
- ARA_RaceBaseSwarmProduceOn
-
ArachnaeNode_Race_Facehugger
@@ -322,6 +317,11 @@
+
+
+ ARA_RaceBaseSwarmProduceSwitchHediff
+
+
0
@@ -340,12 +340,11 @@
-
- ARA_RaceBaseSwarmProduceOff
- ARA_RaceBaseSwarmProduceOn
-
-
-
+
+
+ ARA_RaceBaseSwarmProduceSwitchHediff
+
+
0
@@ -370,8 +369,11 @@
ARA_Skyraider_jump
-
-
+
+
+ ARA_RaceBaseSwarmProduceSwitchHediff
+
+
0
@@ -426,8 +428,11 @@
-
-
+
+
+ ARA_RaceBaseSwarmProduceSwitchHediff
+
+
0
diff --git a/1.6/1.6/Defs/ThingDef_Races/ARA_RaceNodeSwarm.xml b/1.6/1.6/Defs/ThingDef_Races/ARA_RaceNodeSwarm.xml
index b66e988..a47ae7c 100644
--- a/1.6/1.6/Defs/ThingDef_Races/ARA_RaceNodeSwarm.xml
+++ b/1.6/1.6/Defs/ThingDef_Races/ARA_RaceNodeSwarm.xml
@@ -648,7 +648,6 @@
- ARA_HiveMindDrone
ARA_HiveStrength_Low
ARA_Hive_Cannot_Speak
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 f391eb8..2883517 100644
--- a/1.6/1.6/Defs/ThingDef_Races/ARA_RaceQueen.xml
+++ b/1.6/1.6/Defs/ThingDef_Races/ARA_RaceQueen.xml
@@ -119,7 +119,6 @@
OrganicStandard
Human
- ARA_NonPlayerHediffSet
diff --git a/1.6/1.6/Defs/Thing_building/ARA_WormholeDefs.xml b/1.6/1.6/Defs/Thing_building/ARA_WormholeDefs.xml
index b692bd8..e8dd46e 100644
--- a/1.6/1.6/Defs/Thing_building/ARA_WormholeDefs.xml
+++ b/1.6/1.6/Defs/Thing_building/ARA_WormholeDefs.xml
@@ -126,4 +126,99 @@
TraversePitGate
+
+
+ ARA_WormholePortal_Spawner
+
+ 一只阿拉克涅坑道种的尾部正在地下活动——毫无疑问,它很快就要破土而出了!
+ ArachnaeSwarm.BuildingGroundSpawner_WormholePortal
+ false
+ true
+ true
+ Normal
+ false
+ RealtimeOnly
+ (2, 2)
+ UI/Icons/UndergroundEmergence
+
+ EmergencePointSustained2X2
+ EmergencePointComplete2X2
+ ARA_WormholePortal_B
+ 600
+ true
+ PitBurrowOpening
+
+
+
+
+ 600
+ 600
+ false
+ 3
+ true
+ false
+
+
+
+
+ ARA_WormholePortal_Enermy
+
+ 阿拉克涅坑道种的尾巴,是其狭长身躯形成的地下通路的出口。
+ false
+
+ ArachnaeSwarm/Building/ARA_Wormhole_B
+ Graphic_Single
+ CutoutComplex
+ (4,4)
+
+ (2.6, 2.5, 1.6)
+ (0,0,-0.1)
+
+
+ Building
+ Impassable
+
+ 500
+ 0.5
+
+ (2,2)
+
+
+ 60
+ 30
+ 60
+ 120
+
+
+
+
+
+
+ false
+
+
+
+ None
+
+
+
+ ARA_WormholePortal
+
+ 通过虫洞传送门到达
+ ArachnaeSwarm.PawnsArrivalModeWorker_WormholePortal
+
+
+ (0, 0)
+ (500, 0.3)
+ (1000, 0.5)
+ (2000, 0.7)
+
+
+ false
+ Industrial
+
\ No newline at end of file
diff --git a/Source/ArachnaeSwarm/.vs/ArachnaeSwarm/v17/.suo b/Source/ArachnaeSwarm/.vs/ArachnaeSwarm/v17/.suo
index f351ef6..d45c276 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 5cd4f8b..3bf5e0b 100644
--- a/Source/ArachnaeSwarm/.vs/ArachnaeSwarm/v17/DocumentLayout.json
+++ b/Source/ArachnaeSwarm/.vs/ArachnaeSwarm/v17/DocumentLayout.json
@@ -2,28 +2,40 @@
"Version": 1,
"WorkspaceRootPath": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\",
"Documents": [
+ {
+ "AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|d:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\pawnsarrivalmode\\pawnsarrivalmodeworker_wormholeportal.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:pawnsarrivalmode\\pawnsarrivalmodeworker_wormholeportal.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\\building_comps\\ara_wormholeportal\\comp_wormholeportal.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:building_comps\\ara_wormholeportal\\comp_wormholeportal.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\\building_comps\\ara_wormholeportal\\compproperties_wormholeportal.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:building_comps\\ara_wormholeportal\\compproperties_wormholeportal.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\\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\\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\\needs\\need_honeyproduction.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\\needs\\need_honeyproduction.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:needs\\need_honeyproduction.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\\jobs\\jobdriver_extracthoney\\jobdriver_extracthoney.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\\jobs\\jobdriver_extracthoney\\jobdriver_extracthoney.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:jobs\\jobdriver_extracthoney\\jobdriver_extracthoney.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\\needs\\need_chitinarmor.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\\needs\\need_chitinarmor.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:needs\\need_chitinarmor.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\\abilities\\ara_psychicloaddump\\compabilityeffect_psychicloaddump.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\\abilities\\ara_psychicloaddump\\compabilityeffect_psychicloaddump.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:abilities\\ara_psychicloaddump\\compabilityeffect_psychicloaddump.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
@@ -111,19 +123,58 @@
{
"$type": "Document",
"DocumentIndex": 0,
+ "Title": "PawnsArrivalModeWorker_WormholePortal.cs",
+ "DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\PawnsArrivalMode\\PawnsArrivalModeWorker_WormholePortal.cs",
+ "RelativeDocumentMoniker": "PawnsArrivalMode\\PawnsArrivalModeWorker_WormholePortal.cs",
+ "ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\PawnsArrivalMode\\PawnsArrivalModeWorker_WormholePortal.cs*",
+ "RelativeToolTip": "PawnsArrivalMode\\PawnsArrivalModeWorker_WormholePortal.cs*",
+ "ViewState": "AgIAADEBAAAAAAAAAAAkwEABAABSAAAAAAAAAA==",
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
+ "WhenOpened": "2026-02-06T09:04:02.296Z",
+ "EditorCaption": ""
+ },
+ {
+ "$type": "Document",
+ "DocumentIndex": 2,
+ "Title": "CompProperties_WormholePortal.cs",
+ "DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_WormholePortal\\CompProperties_WormholePortal.cs",
+ "RelativeDocumentMoniker": "Building_Comps\\ARA_WormholePortal\\CompProperties_WormholePortal.cs",
+ "ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_WormholePortal\\CompProperties_WormholePortal.cs",
+ "RelativeToolTip": "Building_Comps\\ARA_WormholePortal\\CompProperties_WormholePortal.cs",
+ "ViewState": "AgIAAAMAAAAAAAAAAAAuwBoAAAAIAAAAAAAAAA==",
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
+ "WhenOpened": "2026-02-06T09:03:50.674Z",
+ "EditorCaption": ""
+ },
+ {
+ "$type": "Document",
+ "DocumentIndex": 1,
+ "Title": "Comp_WormholePortal.cs",
+ "DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_WormholePortal\\Comp_WormholePortal.cs",
+ "RelativeDocumentMoniker": "Building_Comps\\ARA_WormholePortal\\Comp_WormholePortal.cs",
+ "ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_WormholePortal\\Comp_WormholePortal.cs",
+ "RelativeToolTip": "Building_Comps\\ARA_WormholePortal\\Comp_WormholePortal.cs",
+ "ViewState": "AgIAABQBAAAAAAAAAAAowCcBAAAJAAAAAAAAAA==",
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
+ "WhenOpened": "2026-02-06T09:01:19.98Z",
+ "EditorCaption": ""
+ },
+ {
+ "$type": "Document",
+ "DocumentIndex": 3,
"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": "AgIAAIYAAAAAAAAAAAAUwJ4AAAANAAAAAAAAAA==",
+ "ViewState": "AgIAAHQAAAAAAAAAAAAiwJEAAAAJAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-02-05T03:36:35.207Z",
"EditorCaption": ""
},
{
"$type": "Document",
- "DocumentIndex": 1,
+ "DocumentIndex": 4,
"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",
@@ -131,12 +182,11 @@
"RelativeToolTip": "Pawn_Comps\\ARA_UniquePawn\\CompUniquePawn.cs",
"ViewState": "AgIAAKgAAAAAAAAAAAAtwLkAAAARAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
- "WhenOpened": "2026-02-05T03:36:24.458Z",
- "EditorCaption": ""
+ "WhenOpened": "2026-02-05T03:36:24.458Z"
},
{
"$type": "Document",
- "DocumentIndex": 3,
+ "DocumentIndex": 6,
"Title": "JobDriver_ExtractHoney.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_ExtractHoney\\JobDriver_ExtractHoney.cs",
"RelativeDocumentMoniker": "Jobs\\JobDriver_ExtractHoney\\JobDriver_ExtractHoney.cs",
@@ -144,12 +194,11 @@
"RelativeToolTip": "Jobs\\JobDriver_ExtractHoney\\JobDriver_ExtractHoney.cs",
"ViewState": "AgIAAAQAAAAAAAAAAAAswBMAAAAJAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
- "WhenOpened": "2026-02-05T03:32:58.466Z",
- "EditorCaption": ""
+ "WhenOpened": "2026-02-05T03:32:58.466Z"
},
{
"$type": "Document",
- "DocumentIndex": 2,
+ "DocumentIndex": 5,
"Title": "Need_HoneyProduction.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Needs\\Need_HoneyProduction.cs",
"RelativeDocumentMoniker": "Needs\\Need_HoneyProduction.cs",
@@ -157,12 +206,11 @@
"RelativeToolTip": "Needs\\Need_HoneyProduction.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAoAAAAIAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
- "WhenOpened": "2026-02-05T03:21:22.655Z",
- "EditorCaption": ""
+ "WhenOpened": "2026-02-05T03:21:22.655Z"
},
{
"$type": "Document",
- "DocumentIndex": 4,
+ "DocumentIndex": 7,
"Title": "Need_ChitinArmor.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Needs\\Need_ChitinArmor.cs",
"RelativeDocumentMoniker": "Needs\\Need_ChitinArmor.cs",
@@ -170,12 +218,11 @@
"RelativeToolTip": "Needs\\Need_ChitinArmor.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
- "WhenOpened": "2026-02-05T03:21:22.016Z",
- "EditorCaption": ""
+ "WhenOpened": "2026-02-05T03:21:22.016Z"
},
{
"$type": "Document",
- "DocumentIndex": 6,
+ "DocumentIndex": 9,
"Title": "CompProperties_AbilityPsychicLoadDump.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_PsychicLoadDump\\CompProperties_AbilityPsychicLoadDump.cs",
"RelativeDocumentMoniker": "Abilities\\ARA_PsychicLoadDump\\CompProperties_AbilityPsychicLoadDump.cs",
@@ -187,7 +234,7 @@
},
{
"$type": "Document",
- "DocumentIndex": 5,
+ "DocumentIndex": 8,
"Title": "CompAbilityEffect_PsychicLoadDump.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_PsychicLoadDump\\CompAbilityEffect_PsychicLoadDump.cs",
"RelativeDocumentMoniker": "Abilities\\ARA_PsychicLoadDump\\CompAbilityEffect_PsychicLoadDump.cs",
@@ -195,12 +242,11 @@
"RelativeToolTip": "Abilities\\ARA_PsychicLoadDump\\CompAbilityEffect_PsychicLoadDump.cs",
"ViewState": "AgIAAAgAAAAAAAAAAAAowBIAAAASAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
- "WhenOpened": "2026-02-04T08:36:23.647Z",
- "EditorCaption": ""
+ "WhenOpened": "2026-02-04T08:36:23.647Z"
},
{
"$type": "Document",
- "DocumentIndex": 10,
+ "DocumentIndex": 13,
"Title": "CompAbilityEffect_PsychicResearchHarvest.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_PsychicResearchHarvest\\CompAbilityEffect_PsychicResearchHarvest.cs",
"RelativeDocumentMoniker": "Abilities\\ARA_PsychicResearchHarvest\\CompAbilityEffect_PsychicResearchHarvest.cs",
@@ -212,7 +258,7 @@
},
{
"$type": "Document",
- "DocumentIndex": 14,
+ "DocumentIndex": 17,
"Title": "CompProperties_AbilityPsychicLoadCost.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_PsychicLoadCost\\CompProperties_AbilityPsychicLoadCost.cs",
"RelativeDocumentMoniker": "Abilities\\ARA_PsychicLoadCost\\CompProperties_AbilityPsychicLoadCost.cs",
@@ -224,7 +270,7 @@
},
{
"$type": "Document",
- "DocumentIndex": 13,
+ "DocumentIndex": 16,
"Title": "SwarmSpellUtility.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Pawn_Comps\\ARA_SwarmSpellHolder\\SwarmSpellUtility.cs",
"RelativeDocumentMoniker": "Pawn_Comps\\ARA_SwarmSpellHolder\\SwarmSpellUtility.cs",
@@ -236,7 +282,7 @@
},
{
"$type": "Document",
- "DocumentIndex": 9,
+ "DocumentIndex": 12,
"Title": "CompAbilityEffect_PsychicLoadCost.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_PsychicLoadCost\\CompAbilityEffect_PsychicLoadCost.cs",
"RelativeDocumentMoniker": "Abilities\\ARA_PsychicLoadCost\\CompAbilityEffect_PsychicLoadCost.cs",
@@ -248,7 +294,7 @@
},
{
"$type": "Document",
- "DocumentIndex": 7,
+ "DocumentIndex": 10,
"Title": "Comp_SwarmSpellHolder.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Pawn_Comps\\ARA_SwarmSpellHolder\\Comp_SwarmSpellHolder.cs",
"RelativeDocumentMoniker": "Pawn_Comps\\ARA_SwarmSpellHolder\\Comp_SwarmSpellHolder.cs",
@@ -260,7 +306,7 @@
},
{
"$type": "Document",
- "DocumentIndex": 8,
+ "DocumentIndex": 11,
"Title": "Gizmo_SwarmSpellStatus.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Pawn_Comps\\ARA_SwarmSpellHolder\\Gizmo_SwarmSpellStatus.cs",
"RelativeDocumentMoniker": "Pawn_Comps\\ARA_SwarmSpellHolder\\Gizmo_SwarmSpellStatus.cs",
@@ -272,7 +318,7 @@
},
{
"$type": "Document",
- "DocumentIndex": 15,
+ "DocumentIndex": 18,
"Title": "CompAbilityEffect_HediffGacha.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_HediffGacha\\CompAbilityEffect_HediffGacha.cs",
"RelativeDocumentMoniker": "Abilities\\ARA_HediffGacha\\CompAbilityEffect_HediffGacha.cs",
@@ -284,7 +330,7 @@
},
{
"$type": "Document",
- "DocumentIndex": 12,
+ "DocumentIndex": 15,
"Title": "CompFighterInvisible.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Pawn_Comps\\ARA_Fighter_Invisible\\CompFighterInvisible.cs",
"RelativeDocumentMoniker": "Pawn_Comps\\ARA_Fighter_Invisible\\CompFighterInvisible.cs",
@@ -296,7 +342,7 @@
},
{
"$type": "Document",
- "DocumentIndex": 11,
+ "DocumentIndex": 14,
"Title": "CompProperties_AbilityPsychicResearchHarvest.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_PsychicResearchHarvest\\CompProperties_AbilityPsychicResearchHarvest.cs",
"RelativeDocumentMoniker": "Abilities\\ARA_PsychicResearchHarvest\\CompProperties_AbilityPsychicResearchHarvest.cs",
@@ -308,7 +354,7 @@
},
{
"$type": "Document",
- "DocumentIndex": 16,
+ "DocumentIndex": 19,
"Title": "CompAbilityEffect_HediffRestriction.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_HediffRestriction\\CompAbilityEffect_HediffRestriction.cs",
"RelativeDocumentMoniker": "Abilities\\ARA_HediffRestriction\\CompAbilityEffect_HediffRestriction.cs",
@@ -320,7 +366,7 @@
},
{
"$type": "Document",
- "DocumentIndex": 17,
+ "DocumentIndex": 20,
"Title": "Window_HediffSelection.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_HediffGacha\\Window_HediffSelection.cs",
"RelativeDocumentMoniker": "Abilities\\ARA_HediffGacha\\Window_HediffSelection.cs",
@@ -332,7 +378,7 @@
},
{
"$type": "Document",
- "DocumentIndex": 18,
+ "DocumentIndex": 21,
"Title": "ARA_PowerArmor.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\PowerArmor\\ARA_PowerArmor.cs",
"RelativeDocumentMoniker": "PowerArmor\\ARA_PowerArmor.cs",
@@ -344,7 +390,7 @@
},
{
"$type": "Document",
- "DocumentIndex": 19,
+ "DocumentIndex": 22,
"Title": "PawnCapacityWorker_PsychicStange.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\PawnCapacityWorker\\PawnCapacityWorker_PsychicStange.cs",
"RelativeDocumentMoniker": "PawnCapacityWorker\\PawnCapacityWorker_PsychicStange.cs",
@@ -356,7 +402,7 @@
},
{
"$type": "Document",
- "DocumentIndex": 21,
+ "DocumentIndex": 24,
"Title": "CompAbilityEffect_PsychicBrainburn.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\PsychicBrainburn\\CompAbilityEffect_PsychicBrainburn.cs",
"RelativeDocumentMoniker": "Abilities\\PsychicBrainburn\\CompAbilityEffect_PsychicBrainburn.cs",
@@ -368,7 +414,7 @@
},
{
"$type": "Document",
- "DocumentIndex": 20,
+ "DocumentIndex": 23,
"Title": "ARA_DefOf.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\ARA_DefOf.cs",
"RelativeDocumentMoniker": "ARA_DefOf.cs",
@@ -380,7 +426,7 @@
},
{
"$type": "Document",
- "DocumentIndex": 22,
+ "DocumentIndex": 25,
"Title": "CompAutoMechCarrier.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Pawn_Comps\\ARA_AutoMechCarrier\\CompAutoMechCarrier.cs",
"RelativeDocumentMoniker": "Pawn_Comps\\ARA_AutoMechCarrier\\CompAutoMechCarrier.cs",
diff --git a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj
index 6e56256..848f48c 100644
--- a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj
+++ b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj
@@ -42,9 +42,13 @@
+
+
+
+
@@ -339,6 +343,7 @@
+
diff --git a/Source/ArachnaeSwarm/Building_Comps/ARA_WormholePortal/CompProperties_WormholePortal.cs b/Source/ArachnaeSwarm/Building_Comps/ARA_WormholePortal/CompProperties_WormholePortal.cs
new file mode 100644
index 0000000..59beade
--- /dev/null
+++ b/Source/ArachnaeSwarm/Building_Comps/ARA_WormholePortal/CompProperties_WormholePortal.cs
@@ -0,0 +1,91 @@
+using RimWorld;
+using UnityEngine;
+using Verse;
+
+namespace ArachnaeSwarm
+{
+ ///
+ /// 虫洞传送门组件属性
+ ///
+ public class CompProperties_WormholePortal : CompProperties
+ {
+ #region 时间设置
+ ///
+ /// 初始延迟(ticks)
+ ///
+ public int initialDelayTicks = 60; // 1秒
+
+ ///
+ /// 单位跳出间隔(ticks)
+ ///
+ public int jumpIntervalTicks = 30; // 0.5秒
+
+ ///
+ /// 单位跳出时的眩晕时间(ticks)
+ ///
+ public int stunDurationTicks = 60; // 1秒
+
+ #endregion
+
+ #region 效果设置
+ ///
+ /// 激活音效
+ ///
+ public SoundDef activationSound = null;
+
+ ///
+ /// 停用音效
+ ///
+ public SoundDef deactivationSound = null;
+
+ ///
+ /// 跳出音效
+ ///
+ public SoundDef jumpSound = null;
+
+ ///
+ /// 激活效果器
+ ///
+ public EffecterDef activationEffecter = null;
+
+ ///
+ /// 停用效果器
+ ///
+ public EffecterDef deactivationEffecter = null;
+
+ ///
+ /// 每次跳出效果器
+ ///
+ public EffecterDef perJumpEffecter = null;
+
+ ///
+ /// 单位跳出效果器
+ ///
+ public EffecterDef jumpEffecter = null;
+ #endregion
+
+ #region 外观设置
+ ///
+ /// 活跃时的材质
+ ///
+ public GraphicData activeGraphicData = null;
+
+ ///
+ /// 颜色循环
+ ///
+ public bool useColorCycling = true;
+
+ ///
+ /// 颜色循环速度
+ ///
+ public float colorCycleSpeed = 0.5f;
+ #endregion
+
+ #region 构造函数
+ public CompProperties_WormholePortal()
+ {
+ compClass = typeof(Comp_WormholePortal);
+ }
+ #endregion
+ }
+}
diff --git a/Source/ArachnaeSwarm/Building_Comps/ARA_WormholePortal/Comp_WormholePortal.cs b/Source/ArachnaeSwarm/Building_Comps/ARA_WormholePortal/Comp_WormholePortal.cs
new file mode 100644
index 0000000..c19b67b
--- /dev/null
+++ b/Source/ArachnaeSwarm/Building_Comps/ARA_WormholePortal/Comp_WormholePortal.cs
@@ -0,0 +1,336 @@
+using RimWorld;
+using System.Collections.Generic;
+using System.Linq;
+using Verse;
+using Verse.Sound;
+
+namespace ArachnaeSwarm
+{
+ ///
+ /// 虫洞传送门组件
+ /// 处理从虫洞中跳出单位
+ ///
+ public class Comp_WormholePortal : ThingComp
+ {
+ #region 字段
+ private List emergingPawns = new List();
+ private int currentPawnIndex = 0;
+ private int ticksUntilNextJump = 0;
+ private bool isActive = false;
+ private int activateTick = -1;
+ #endregion
+
+ #region 属性
+ public CompProperties_WormholePortal Props => (CompProperties_WormholePortal)props;
+
+ ///
+ /// 还有多少单位等待跳出
+ ///
+ public int RemainingPawns => emergingPawns.Count - currentPawnIndex;
+
+ ///
+ /// 虫洞是否活跃
+ ///
+ public bool IsActive => isActive;
+
+ ///
+ /// 激活后经过的时间
+ ///
+ public int TicksSinceActivation
+ {
+ get
+ {
+ if (!isActive || activateTick < 0)
+ return 0;
+ return Find.TickManager.TicksGame - activateTick;
+ }
+ }
+ #endregion
+
+ #region 生命周期
+ public override void PostSpawnSetup(bool respawningAfterLoad)
+ {
+ base.PostSpawnSetup(respawningAfterLoad);
+
+ if (respawningAfterLoad)
+ return;
+
+ // 非加载生成时,准备激活
+ PrepareForActivation();
+ }
+
+ public override void CompTick()
+ {
+ base.CompTick();
+
+ if (!isActive || emergingPawns.NullOrEmpty() || currentPawnIndex >= emergingPawns.Count)
+ return;
+
+ // 计时下一个单位跳出
+ if (ticksUntilNextJump > 0)
+ {
+ ticksUntilNextJump--;
+ return;
+ }
+
+ // 跳出当前单位
+ JumpNextPawn();
+
+ // 设置下一个单位的跳出间隔
+ if (currentPawnIndex < emergingPawns.Count)
+ {
+ ticksUntilNextJump = Props.jumpIntervalTicks;
+ }
+ }
+
+ public void PostDeSpawn(Map map)
+ {
+ // 虫洞消失时,销毁剩余未跳出的单位
+ DestroyRemainingPawns();
+ }
+
+ public override void PostDestroy(DestroyMode mode, Map previousMap)
+ {
+ base.PostDestroy(mode, previousMap);
+
+ // 虫洞被摧毁时,销毁剩余未跳出的单位
+ DestroyRemainingPawns();
+ }
+ #endregion
+
+ #region 公共方法
+ ///
+ /// 设置要跳出的单位
+ ///
+ public void SetEmergingPawns(List pawns)
+ {
+ emergingPawns = pawns ?? new List();
+ currentPawnIndex = 0;
+ }
+
+ ///
+ /// 激活虫洞
+ ///
+ public void Activate()
+ {
+ if (isActive)
+ return;
+
+ isActive = true;
+ activateTick = Find.TickManager.TicksGame;
+
+ // 设置第一个单位的跳出计时
+ if (emergingPawns.Count > 0)
+ {
+ ticksUntilNextJump = Props.initialDelayTicks;
+ }
+
+ // 播放激活效果
+ PlayActivationEffects();
+ }
+
+ ///
+ /// 获取虫洞状态描述
+ ///
+ public string GetStatusDescription()
+ {
+ if (!isActive)
+ return "休眠中";
+
+ if (RemainingPawns <= 0)
+ return "已完成";
+
+ return $"跳出中: {RemainingPawns} 个单位剩余";
+ }
+ #endregion
+
+ #region 私有方法
+ ///
+ /// 准备激活
+ ///
+ private void PrepareForActivation()
+ {
+ // 可以在这里添加预热效果
+ // 例如:粒子效果、声音等
+ }
+
+ ///
+ /// 跳出下一个单位
+ ///
+ private void JumpNextPawn()
+ {
+ if (currentPawnIndex >= emergingPawns.Count)
+ return;
+
+ Pawn pawn = emergingPawns[currentPawnIndex];
+ currentPawnIndex++;
+
+ // 确保单位存在且有效
+ if (pawn == null || pawn.Destroyed)
+ {
+ Log.Warning("[虫洞传送门] 尝试跳出无效单位");
+ return;
+ }
+
+ // 生成单位
+ TrySpawnPawn(pawn);
+
+ // 播放跳出效果
+ PlayJumpEffects();
+
+ // 如果所有单位都已跳出,关闭虫洞
+ if (currentPawnIndex >= emergingPawns.Count)
+ {
+ StartDeactivation();
+ }
+ }
+
+ ///
+ /// 尝试生成单位
+ ///
+ private void TrySpawnPawn(Pawn pawn)
+ {
+ try
+ {
+ Map map = parent.Map;
+ if (map == null)
+ return;
+
+ // 找到虫洞周围的可行走位置
+ if (CellFinder.TryFindRandomCellNear(parent.Position, map, 2,
+ c => c.Walkable(map) && c.InBounds(map) && !c.Fogged(map), out IntVec3 spawnCell))
+ {
+ // 生成单位
+ GenSpawn.Spawn(pawn, spawnCell, map);
+
+ // 应用跳出效果
+ ApplyJumpEffectsToPawn(pawn, spawnCell);
+ }
+ else
+ {
+ // 如果找不到位置,在虫洞位置生成
+ GenSpawn.Spawn(pawn, parent.Position, map);
+ Log.Warning($"[虫洞传送门] 无法找到合适位置,在虫洞位置生成 {pawn.Label}");
+ }
+ }
+ catch (System.Exception ex)
+ {
+ Log.Error($"[虫洞传送门] 跳出单位失败: {ex.Message}");
+ }
+ }
+
+ ///
+ /// 应用跳出效果给单位
+ ///
+ private void ApplyJumpEffectsToPawn(Pawn pawn, IntVec3 spawnCell)
+ {
+ // 短暂眩晕效果
+ if (Props.stunDurationTicks > 0)
+ {
+ pawn.stances.stunner.StunFor(Props.stunDurationTicks, parent);
+ }
+
+ // 播放音效
+ if (Props.jumpSound != null)
+ {
+ Props.jumpSound.PlayOneShot(new TargetInfo(spawnCell, parent.Map));
+ }
+
+ // 显示效果
+ if (Props.jumpEffecter != null)
+ {
+ Effecter effecter = Props.jumpEffecter.Spawn();
+ effecter.Trigger(new TargetInfo(spawnCell, parent.Map), new TargetInfo(spawnCell, parent.Map));
+ effecter.Cleanup();
+ }
+ }
+
+ ///
+ /// 播放激活效果
+ ///
+ private void PlayActivationEffects()
+ {
+ if (Props.activationSound != null)
+ {
+ Props.activationSound.PlayOneShot(new TargetInfo(parent.Position, parent.Map));
+ }
+
+ if (Props.activationEffecter != null)
+ {
+ Effecter effecter = Props.activationEffecter.Spawn();
+ effecter.Trigger(new TargetInfo(parent.Position, parent.Map), new TargetInfo(parent.Position, parent.Map));
+ effecter.Cleanup();
+ }
+ }
+
+ ///
+ /// 播放跳出效果
+ ///
+ private void PlayJumpEffects()
+ {
+ if (Props.perJumpEffecter != null)
+ {
+ Effecter effecter = Props.perJumpEffecter.Spawn();
+ effecter.Trigger(new TargetInfo(parent.Position, parent.Map), new TargetInfo(parent.Position, parent.Map));
+ effecter.Cleanup();
+ }
+ }
+
+ ///
+ /// 开始停用虫洞
+ ///
+ private void StartDeactivation()
+ {
+ // 播放停用效果
+ if (Props.deactivationSound != null)
+ {
+ Props.deactivationSound.PlayOneShot(new TargetInfo(parent.Position, parent.Map));
+ }
+
+ if (Props.deactivationEffecter != null)
+ {
+ Effecter effecter = Props.deactivationEffecter.Spawn();
+ effecter.Trigger(new TargetInfo(parent.Position, parent.Map), new TargetInfo(parent.Position, parent.Map));
+ effecter.Cleanup();
+ }
+ }
+
+ ///
+ /// 销毁剩余未跳出的单位
+ ///
+ private void DestroyRemainingPawns()
+ {
+ if (emergingPawns.NullOrEmpty())
+ return;
+
+ for (int i = currentPawnIndex; i < emergingPawns.Count; i++)
+ {
+ Pawn pawn = emergingPawns[i];
+ if (pawn != null && !pawn.Destroyed)
+ {
+ pawn.Destroy();
+ }
+ }
+ }
+ #endregion
+
+ #region 序列化
+ public override void PostExposeData()
+ {
+ base.PostExposeData();
+
+ Scribe_Collections.Look(ref emergingPawns, "emergingPawns", LookMode.Reference);
+ Scribe_Values.Look(ref currentPawnIndex, "currentPawnIndex", 0);
+ Scribe_Values.Look(ref ticksUntilNextJump, "ticksUntilNextJump", 0);
+ Scribe_Values.Look(ref isActive, "isActive", false);
+ Scribe_Values.Look(ref activateTick, "activateTick", -1);
+
+ if (Scribe.mode == LoadSaveMode.PostLoadInit)
+ {
+ // 清理空引用
+ emergingPawns?.RemoveAll(p => p == null);
+ }
+ }
+ #endregion
+ }
+}
diff --git a/Source/ArachnaeSwarm/Building_Comps/ARA_WormholePortal/WormholePortalSpawnerExtension.cs b/Source/ArachnaeSwarm/Building_Comps/ARA_WormholePortal/WormholePortalSpawnerExtension.cs
new file mode 100644
index 0000000..976d078
--- /dev/null
+++ b/Source/ArachnaeSwarm/Building_Comps/ARA_WormholePortal/WormholePortalSpawnerExtension.cs
@@ -0,0 +1,40 @@
+using Verse;
+
+namespace ArachnaeSwarm
+{
+ ///
+ /// 虫洞传送门生成器扩展
+ ///
+ public class WormholePortalSpawnerExtension : DefModExtension
+ {
+ ///
+ /// 最小跳出延迟(ticks)
+ ///
+ public int minEmergeDelayTicks = 180; // 3秒
+
+ ///
+ /// 最大跳出延迟(ticks)
+ ///
+ public int maxEmergeDelayTicks = 300; // 5秒
+
+ ///
+ /// 是否需要露天
+ ///
+ public bool requiresOpenSky = true;
+
+ ///
+ /// 最小空间半径
+ ///
+ public int minSpaceRadius = 3;
+
+ ///
+ /// 是否破坏地形
+ ///
+ public bool destroyTerrain = true;
+
+ ///
+ /// 是否破坏建筑
+ ///
+ public bool destroyBuildings = false;
+ }
+}
diff --git a/Source/ArachnaeSwarm/Buildings/BuildingGroundSpawner_WormholePortal.cs b/Source/ArachnaeSwarm/Buildings/BuildingGroundSpawner_WormholePortal.cs
new file mode 100644
index 0000000..fabd41b
--- /dev/null
+++ b/Source/ArachnaeSwarm/Buildings/BuildingGroundSpawner_WormholePortal.cs
@@ -0,0 +1,54 @@
+using RimWorld;
+using System.Collections.Generic;
+using Verse;
+
+namespace ArachnaeSwarm
+{
+ ///
+ /// 自定义虫洞传送门生成器
+ ///
+ public class BuildingGroundSpawner_WormholePortal : BuildingGroundSpawner
+ {
+ // 存储要传递的Pawn列表
+ public List emergingPawns = new List();
+
+ // 延迟参数
+ public IntRange? customEmergeDelay = null;
+
+ protected override void PostMakeInt()
+ {
+ base.PostMakeInt();
+
+ // 创建虫洞传送门
+ ThingDef portalDef = DefDatabase.GetNamed("ARA_WormholePortal");
+ if (portalDef != null)
+ {
+ thingToSpawn = ThingMaker.MakeThing(portalDef);
+
+ // 设置Pawn列表
+ Comp_WormholePortal comp = thingToSpawn.TryGetComp();
+ if (comp != null && emergingPawns != null)
+ {
+ comp.SetEmergingPawns(emergingPawns);
+ }
+ }
+ }
+
+ protected override IntRange ResultSpawnDelay
+ {
+ get
+ {
+ if (customEmergeDelay.HasValue)
+ return customEmergeDelay.Value;
+ return base.ResultSpawnDelay;
+ }
+ }
+
+ public override void ExposeData()
+ {
+ base.ExposeData();
+ Scribe_Collections.Look(ref emergingPawns, "emergingPawns", LookMode.Reference);
+ Scribe_Values.Look(ref customEmergeDelay, "customEmergeDelay");
+ }
+ }
+}
diff --git a/Source/ArachnaeSwarm/PawnsArrivalMode/PawnsArrivalModeWorker_WormholePortal.cs b/Source/ArachnaeSwarm/PawnsArrivalMode/PawnsArrivalModeWorker_WormholePortal.cs
new file mode 100644
index 0000000..8aa5ece
--- /dev/null
+++ b/Source/ArachnaeSwarm/PawnsArrivalMode/PawnsArrivalModeWorker_WormholePortal.cs
@@ -0,0 +1,387 @@
+using RimWorld;
+using System.Collections.Generic;
+using System.Linq;
+using Verse;
+
+namespace ArachnaeSwarm
+{
+ ///
+ /// 虫洞传送门到达模式
+ /// 在地图边缘生成虫洞传送门生成器,预热后生成虫洞传送门,单位从虫洞中跳出
+ ///
+ public class PawnsArrivalModeWorker_WormholePortal : PawnsArrivalModeWorker
+ {
+ #region 常量
+ private const float MIN_EDGE_DISTANCE = 5f; // 最小边缘距离
+ private const float MAX_EDGE_DISTANCE = 20f; // 最大边缘距离
+ private const int MIN_SPACE_RADIUS = 3; // 需要的最小空间半径
+ private const int MAX_SPAWNERS_PER_GROUP = 3; // 每组最大生成器数量
+ private const int DEFAULT_EMERGE_DELAY_TICKS = 60; // 默认跳出延迟(1秒)
+ #endregion
+
+ #region 公共方法
+ ///
+ /// 单位到达
+ ///
+ public override void Arrive(List pawns, IncidentParms parms)
+ {
+ Map map = parms.target as Map;
+ if (map == null || pawns.NullOrEmpty())
+ return;
+
+ if (!TryResolveRaidSpawnCenter(parms))
+ return;
+
+ // 将Pawn分组,每组由一个传送门生成器处理
+ List> pawnGroups = SplitPawnsIntoGroups(pawns, parms);
+
+ // 为每组生成一个虫洞传送门生成器
+ int spawnedCount = 0;
+ foreach (var group in pawnGroups)
+ {
+ if (TrySpawnWormholePortalGroup(group, parms))
+ {
+ spawnedCount++;
+ }
+
+ // 限制每组数量
+ if (spawnedCount >= MAX_SPAWNERS_PER_GROUP)
+ break;
+ }
+ }
+
+ ///
+ /// 旅行运输器到达
+ ///
+ public override void TravellingTransportersArrived(List transporters, Map map)
+ {
+ // 虫洞传送门不支持运输器到达
+ Log.Warning("[虫洞传送门] 不支持运输器到达模式");
+ base.TravellingTransportersArrived(transporters, map);
+ }
+
+ ///
+ /// 尝试解析袭击生成中心
+ ///
+ public override bool TryResolveRaidSpawnCenter(IncidentParms parms)
+ {
+ Map map = parms.target as Map;
+ if (map == null)
+ return false;
+
+ // 如果没有指定生成中心,找到地图边缘的位置
+ if (!parms.spawnCenter.IsValid)
+ {
+ if (!TryFindEdgeSpawnCenter(map, out IntVec3 spawnCenter))
+ {
+ // 如果找不到边缘位置,使用默认的掉落中心
+ spawnCenter = DropCellFinder.FindRaidDropCenterDistant(map);
+ }
+ parms.spawnCenter = spawnCenter;
+ }
+
+ parms.spawnRotation = Rot4.Random;
+ return true;
+ }
+
+ ///
+ /// 判断是否可以在指定地图使用
+ ///
+ public override bool CanUseOnMap(Map map)
+ {
+ // 检查地图是否允许此到达模式
+ if (map == null)
+ return false;
+
+ // 检查是否有足够的空间生成虫洞
+ return HasEnoughSpaceForWormhole(map);
+ }
+ #endregion
+
+ #region 私有方法
+ ///
+ /// 将Pawn分成组
+ ///
+ private List> SplitPawnsIntoGroups(List pawns, IncidentParms parms)
+ {
+ List> groups = new List>();
+
+ if (pawns.NullOrEmpty())
+ return groups;
+
+ // 根据点数计算每组大小
+ int groupSize = CalculateGroupSize(parms);
+
+ // 如果组大小为1或更小,每个Pawn单独一组
+ if (groupSize <= 1)
+ {
+ foreach (var pawn in pawns)
+ {
+ groups.Add(new List { pawn });
+ }
+ return groups;
+ }
+
+ // 分组
+ List currentGroup = new List();
+ foreach (var pawn in pawns)
+ {
+ currentGroup.Add(pawn);
+
+ if (currentGroup.Count >= groupSize)
+ {
+ groups.Add(currentGroup);
+ currentGroup = new List();
+ }
+ }
+
+ // 添加剩余的Pawn
+ if (currentGroup.Count > 0)
+ {
+ groups.Add(currentGroup);
+ }
+
+ return groups;
+ }
+
+ ///
+ /// 计算每组大小
+ ///
+ private int CalculateGroupSize(IncidentParms parms)
+ {
+ // 根据点数动态调整组大小
+ float points = parms.points;
+
+ if (points <= 500f)
+ return 1;
+ else if (points <= 1000f)
+ return 2;
+ else if (points <= 2000f)
+ return 3;
+ else
+ return 4;
+ }
+
+ ///
+ /// 尝试生成虫洞传送门组
+ ///
+ private bool TrySpawnWormholePortalGroup(List pawns, IncidentParms parms)
+ {
+ Map map = parms.target as Map;
+ if (map == null || pawns.NullOrEmpty())
+ return false;
+
+ // 找到合适的生成位置(靠近边缘,有足够空间)
+ if (!TryFindWormholeSpawnPosition(map, out IntVec3 spawnPosition))
+ return false;
+
+ // 创建虫洞传送门生成器
+ return SpawnWormholePortalSpawner(map, spawnPosition, pawns, parms);
+ }
+
+ ///
+ /// 尝试找到虫洞生成位置
+ ///
+ private bool TryFindWormholeSpawnPosition(Map map, out IntVec3 spawnPosition)
+ {
+ spawnPosition = IntVec3.Invalid;
+
+ // 尝试在地图边缘区域查找
+ // 使用CellRect.WholeMap而不是CellsInMapRandomOrder
+ CellRect mapRect = CellRect.WholeMap(map);
+
+ // 定义边缘区域(地图边界向内一定距离)
+ int edgeWidth = 5;
+ CellRect edgeRect = new CellRect(
+ edgeWidth,
+ edgeWidth,
+ map.Size.x - edgeWidth * 2,
+ map.Size.z - edgeWidth * 2);
+
+ // 在地图边界但不是边缘区域内部查找
+ for (int attempts = 0; attempts < 30; attempts++)
+ {
+ IntVec3 candidate;
+
+ // 随机选择边界
+ if (Rand.Value < 0.25f) // 北边界
+ {
+ candidate = new IntVec3(Rand.Range(0, map.Size.x), 0, map.Size.z - 1);
+ }
+ else if (Rand.Value < 0.5f) // 南边界
+ {
+ candidate = new IntVec3(Rand.Range(0, map.Size.x), 0, 0);
+ }
+ else if (Rand.Value < 0.75f) // 东边界
+ {
+ candidate = new IntVec3(map.Size.x - 1, 0, Rand.Range(0, map.Size.z));
+ }
+ else // 西边界
+ {
+ candidate = new IntVec3(0, 0, Rand.Range(0, map.Size.z));
+ }
+
+ // 检查位置是否有效
+ if (IsValidWormholePosition(candidate, map))
+ {
+ spawnPosition = candidate;
+ return true;
+ }
+ }
+
+ // 如果找不到边界位置,尝试在地图任意位置查找
+ for (int attempts = 0; attempts < 20; attempts++)
+ {
+ IntVec3 candidate = CellFinder.RandomCell(map);
+ if (IsValidWormholePosition(candidate, map))
+ {
+ spawnPosition = candidate;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ ///
+ /// 检查是否为有效的虫洞位置
+ ///
+ private bool IsValidWormholePosition(IntVec3 position, Map map)
+ {
+ if (!position.InBounds(map))
+ return false;
+
+ if (!position.Standable(map))
+ return false;
+
+ if (position.Fogged(map))
+ return false;
+
+ // 检查是否有足够的空间(3x3区域)
+ CellRect requiredRect = new CellRect(position.x - 1, position.z - 1, 3, 3);
+
+ // 修复:检查矩形是否完全包含在地图内
+ if (!requiredRect.InBounds(map))
+ return false;
+
+ // 检查区域内所有单元格是否都可行走
+ foreach (IntVec3 cell in requiredRect)
+ {
+ if (!cell.InBounds(map) || !cell.Walkable(map))
+ return false;
+
+ // 检查是否有其他建筑阻挡
+ if (cell.GetEdifice(map) != null)
+ return false;
+
+ // 检查是否有屋顶(虫洞需要露天)
+ if (cell.Roofed(map))
+ return false;
+ }
+
+ return true;
+ }
+
+ ///
+ /// 生成虫洞传送门生成器
+ ///
+ private bool SpawnWormholePortalSpawner(Map map, IntVec3 position, List pawns, IncidentParms parms)
+ {
+ try
+ {
+ ThingDef spawnerDef = DefDatabase.GetNamedSilentFail("ARA_WormholePortal_Spawner");
+ if (spawnerDef == null)
+ {
+ Log.Error("[虫洞传送门] 未找到ARA_WormholePortal_Spawner定义");
+ return false;
+ }
+
+ // 创建自定义生成器
+ BuildingGroundSpawner_WormholePortal spawner = (BuildingGroundSpawner_WormholePortal)ThingMaker.MakeThing(spawnerDef);
+
+ // 设置参数
+ spawner.emergingPawns = pawns;
+
+ // 设置延迟
+ WormholePortalSpawnerExtension spawnerExt = spawnerDef.GetModExtension();
+ if (spawnerExt != null)
+ {
+ spawner.customEmergeDelay = new IntRange(
+ spawnerExt.minEmergeDelayTicks,
+ spawnerExt.maxEmergeDelayTicks);
+ }
+
+ // 生成到地图
+ GenSpawn.Spawn(spawner, position, map);
+
+ return true;
+ }
+ catch (System.Exception ex)
+ {
+ Log.Error($"[虫洞传送门] 生成虫洞传送门生成器失败: {ex.Message}\n{ex.StackTrace}");
+ return false;
+ }
+ }
+
+ ///
+ /// 尝试找到边缘生成中心
+ ///
+ private bool TryFindEdgeSpawnCenter(Map map, out IntVec3 spawnCenter)
+ {
+ spawnCenter = IntVec3.Invalid;
+
+ // 尝试在地图四个边缘区域查找
+ List edgeCells = new List();
+
+ // 北边缘
+ for (int x = 0; x < map.Size.x; x += 5)
+ {
+ IntVec3 cell = new IntVec3(x, 0, map.Size.z - 1);
+ if (cell.InBounds(map) && cell.Walkable(map) && !cell.Fogged(map))
+ edgeCells.Add(cell);
+ }
+
+ // 南边缘
+ for (int x = 0; x < map.Size.x; x += 5)
+ {
+ IntVec3 cell = new IntVec3(x, 0, 0);
+ if (cell.InBounds(map) && cell.Walkable(map) && !cell.Fogged(map))
+ edgeCells.Add(cell);
+ }
+
+ // 东边缘
+ for (int z = 0; z < map.Size.z; z += 5)
+ {
+ IntVec3 cell = new IntVec3(map.Size.x - 1, 0, z);
+ if (cell.InBounds(map) && cell.Walkable(map) && !cell.Fogged(map))
+ edgeCells.Add(cell);
+ }
+
+ // 西边缘
+ for (int z = 0; z < map.Size.z; z += 5)
+ {
+ IntVec3 cell = new IntVec3(0, 0, z);
+ if (cell.InBounds(map) && cell.Walkable(map) && !cell.Fogged(map))
+ edgeCells.Add(cell);
+ }
+
+ if (edgeCells.Count > 0)
+ {
+ spawnCenter = edgeCells.RandomElement();
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
+ /// 检查地图是否有足够的空间生成虫洞
+ ///
+ private bool HasEnoughSpaceForWormhole(Map map)
+ {
+ // 简单检查:地图是否至少有一定大小
+ return map.Size.x >= 50 && map.Size.z >= 50;
+ }
+ #endregion
+ }
+}