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 + } +}