1
This commit is contained in:
Binary file not shown.
@@ -50,6 +50,7 @@
|
|||||||
<race>
|
<race>
|
||||||
<body>BeetleLikeWithClaw</body>
|
<body>BeetleLikeWithClaw</body>
|
||||||
<thinkTreeMain>ARA_Insect_WithPlanting</thinkTreeMain>
|
<thinkTreeMain>ARA_Insect_WithPlanting</thinkTreeMain>
|
||||||
|
<thinkTreeConstant>ARA_Insect_Thinktree_Constant</thinkTreeConstant>
|
||||||
<foodType>CarnivoreAnimal,OvivoreAnimal</foodType>
|
<foodType>CarnivoreAnimal,OvivoreAnimal</foodType>
|
||||||
<baseHungerRate>0.1</baseHungerRate>
|
<baseHungerRate>0.1</baseHungerRate>
|
||||||
<baseBodySize>0.5</baseBodySize>
|
<baseBodySize>0.5</baseBodySize>
|
||||||
|
|||||||
@@ -757,6 +757,36 @@
|
|||||||
</thinkRoot>
|
</thinkRoot>
|
||||||
</ThinkTreeDef>
|
</ThinkTreeDef>
|
||||||
|
|
||||||
|
<ThinkTreeDef>
|
||||||
|
<defName>ARA_Insect_Thinktree_Constant</defName>
|
||||||
|
<thinkRoot Class="ThinkNode_Priority">
|
||||||
|
<subNodes>
|
||||||
|
<!-- Despawned -->
|
||||||
|
<li Class="ThinkNode_Subtree">
|
||||||
|
<treeDef>Despawned</treeDef>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li Class="ThinkNode_ConditionalCanDoConstantThinkTreeJobNow">
|
||||||
|
<subNodes>
|
||||||
|
<!-- Flee explosion -->
|
||||||
|
<li Class="JobGiver_FleePotentialExplosion" />
|
||||||
|
|
||||||
|
<!-- Avoid vacuums -->
|
||||||
|
<li Class="JobGiver_FindOxygen" />
|
||||||
|
|
||||||
|
<!-- Board/leave gravship -->
|
||||||
|
<li Class="JobGiver_BoardOrLeaveGravship" />
|
||||||
|
|
||||||
|
<!-- Join auto joinable caravan -->
|
||||||
|
<li Class="ThinkNode_Subtree">
|
||||||
|
<treeDef>JoinAutoJoinableCaravan</treeDef>
|
||||||
|
</li>
|
||||||
|
</subNodes>
|
||||||
|
</li>
|
||||||
|
</subNodes>
|
||||||
|
</thinkRoot>
|
||||||
|
</ThinkTreeDef>
|
||||||
|
|
||||||
<ThinkTreeDef>
|
<ThinkTreeDef>
|
||||||
<defName>ARA_Insect_Larva_Thinktree</defName>
|
<defName>ARA_Insect_Larva_Thinktree</defName>
|
||||||
<thinkRoot Class="ThinkNode_Priority">
|
<thinkRoot Class="ThinkNode_Priority">
|
||||||
|
|||||||
Binary file not shown.
@@ -1,92 +1,7 @@
|
|||||||
{
|
{
|
||||||
"Version": 1,
|
"Version": 1,
|
||||||
"WorkspaceRootPath": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\",
|
"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\\buildings\\building_ootheca\\building_ootheca.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
|
||||||
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:buildings\\building_ootheca\\building_ootheca.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\\comprefuelablenutrition.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
|
||||||
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:building_comps\\comprefuelablenutrition.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_productstorage\\compproperties_productstorage.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
|
||||||
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:building_comps\\ara_productstorage\\compproperties_productstorage.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_compinteractiveproducer\\compinteractiveproducer.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
|
||||||
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:building_comps\\ara_compinteractiveproducer\\compinteractiveproducer.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\\compabilityeffect_transformcorpse.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
|
||||||
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:abilities\\compabilityeffect_transformcorpse.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_followproducer\\thinknode_conditionalnotproducedbymechcarrier.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
|
||||||
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:jobs\\jobdriver_followproducer\\thinknode_conditionalnotproducedbymechcarrier.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\\buildings\\building_ootheca\\gizmo_pawnprogressbar.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
|
||||||
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:buildings\\building_ootheca\\gizmo_pawnprogressbar.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\\buildings\\building_ootheca\\gizmo_neutronflux.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
|
||||||
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:buildings\\building_ootheca\\gizmo_neutronflux.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\\hediffs\\ara_configurablemutant\\necrotictransformationutility.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
|
||||||
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:hediffs\\ara_configurablemutant\\necrotictransformationutility.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_nodeswarmlifetime\\compnodeswarmlifetime.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
|
||||||
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:pawn_comps\\ara_nodeswarmlifetime\\compnodeswarmlifetime.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\\buildings\\building_ootheca\\gizmo_queuedincubationprogress.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
|
||||||
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:buildings\\building_ootheca\\gizmo_queuedincubationprogress.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\\buildings\\building_equipmentootheca\\building_equipmentootheca.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
|
||||||
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:buildings\\building_equipmentootheca\\building_equipmentootheca.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\\buildings\\building_corpsevat\\building_corpsevat.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
|
||||||
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:buildings\\building_corpsevat\\building_corpsevat.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\\buildings\\building_corpsevat\\corpsevatextension.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
|
||||||
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:buildings\\building_corpsevat\\corpsevatextension.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_plant\\jobgiver_grower.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
|
||||||
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:jobs\\jobdriver_plant\\jobgiver_grower.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_clean\\jobgiver_cleaner.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
|
||||||
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:jobs\\jobdriver_clean\\jobgiver_cleaner.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_huggingface\\hediff_possession.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
|
||||||
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:abilities\\ara_huggingface\\hediff_possession.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_huggingface\\compabilityeffect_possess.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
|
||||||
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:abilities\\ara_huggingface\\compabilityeffect_possess.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_buildingterrainspawn\\compdelayedterrainspawn.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
|
||||||
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:building_comps\\ara_buildingterrainspawn\\compdelayedterrainspawn.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\\hediffs\\ara_hediffterrainspawn\\compproperties_hediffterrainspawn.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
|
||||||
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:hediffs\\ara_hediffterrainspawn\\compproperties_hediffterrainspawn.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\\hediffs\\ara_hediffterrainspawn\\comphediffterrainspawn.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
|
||||||
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:hediffs\\ara_hediffterrainspawn\\comphediffterrainspawn.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"DocumentGroupContainers": [
|
"DocumentGroupContainers": [
|
||||||
{
|
{
|
||||||
"Orientation": 0,
|
"Orientation": 0,
|
||||||
@@ -94,271 +9,11 @@
|
|||||||
"DocumentGroups": [
|
"DocumentGroups": [
|
||||||
{
|
{
|
||||||
"DockedWidth": 200,
|
"DockedWidth": 200,
|
||||||
"SelectedChildIndex": 6,
|
"SelectedChildIndex": -1,
|
||||||
"Children": [
|
"Children": [
|
||||||
{
|
{
|
||||||
"$type": "Bookmark",
|
"$type": "Bookmark",
|
||||||
"Name": "ST:0:0:{1c4feeaa-4718-4aa9-859d-94ce25d182ba}"
|
"Name": "ST:0:0:{1c4feeaa-4718-4aa9-859d-94ce25d182ba}"
|
||||||
},
|
|
||||||
{
|
|
||||||
"$type": "Document",
|
|
||||||
"DocumentIndex": 1,
|
|
||||||
"Title": "CompRefuelableNutrition.cs",
|
|
||||||
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\CompRefuelableNutrition.cs",
|
|
||||||
"RelativeDocumentMoniker": "Building_Comps\\CompRefuelableNutrition.cs",
|
|
||||||
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\CompRefuelableNutrition.cs",
|
|
||||||
"RelativeToolTip": "Building_Comps\\CompRefuelableNutrition.cs",
|
|
||||||
"ViewState": "AgIAAAAAAAAAAAAAAAAAABAAAAAyAAAAAAAAAA==",
|
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
|
||||||
"WhenOpened": "2026-01-27T03:51:40.77Z",
|
|
||||||
"EditorCaption": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$type": "Document",
|
|
||||||
"DocumentIndex": 2,
|
|
||||||
"Title": "CompProperties_ProductStorage.cs",
|
|
||||||
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_ProductStorage\\CompProperties_ProductStorage.cs",
|
|
||||||
"RelativeDocumentMoniker": "Building_Comps\\ARA_ProductStorage\\CompProperties_ProductStorage.cs",
|
|
||||||
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_ProductStorage\\CompProperties_ProductStorage.cs",
|
|
||||||
"RelativeToolTip": "Building_Comps\\ARA_ProductStorage\\CompProperties_ProductStorage.cs",
|
|
||||||
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAUAAABKAAAAAAAAAA==",
|
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
|
||||||
"WhenOpened": "2026-01-27T03:51:33.86Z",
|
|
||||||
"EditorCaption": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$type": "Document",
|
|
||||||
"DocumentIndex": 3,
|
|
||||||
"Title": "CompInteractiveProducer.cs",
|
|
||||||
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_CompInteractiveProducer\\CompInteractiveProducer.cs",
|
|
||||||
"RelativeDocumentMoniker": "Building_Comps\\ARA_CompInteractiveProducer\\CompInteractiveProducer.cs",
|
|
||||||
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_CompInteractiveProducer\\CompInteractiveProducer.cs",
|
|
||||||
"RelativeToolTip": "Building_Comps\\ARA_CompInteractiveProducer\\CompInteractiveProducer.cs",
|
|
||||||
"ViewState": "AgIAAFICAAAAAAAAAAAAAFICAAAtAAAAAAAAAA==",
|
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
|
||||||
"WhenOpened": "2026-01-27T03:51:32.573Z",
|
|
||||||
"EditorCaption": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$type": "Document",
|
|
||||||
"DocumentIndex": 4,
|
|
||||||
"Title": "CompAbilityEffect_TransformCorpse.cs",
|
|
||||||
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\CompAbilityEffect_TransformCorpse.cs",
|
|
||||||
"RelativeDocumentMoniker": "Abilities\\CompAbilityEffect_TransformCorpse.cs",
|
|
||||||
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\CompAbilityEffect_TransformCorpse.cs",
|
|
||||||
"RelativeToolTip": "Abilities\\CompAbilityEffect_TransformCorpse.cs",
|
|
||||||
"ViewState": "AgIAAFQAAAAAAAAAAAAuwGkAAABdAAAAAAAAAA==",
|
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
|
||||||
"WhenOpened": "2026-01-27T03:51:29.604Z",
|
|
||||||
"EditorCaption": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$type": "Document",
|
|
||||||
"DocumentIndex": 5,
|
|
||||||
"Title": "ThinkNode_ConditionalNotProducedByMechCarrier.cs",
|
|
||||||
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_FollowProducer\\ThinkNode_ConditionalNotProducedByMechCarrier.cs",
|
|
||||||
"RelativeDocumentMoniker": "Jobs\\JobDriver_FollowProducer\\ThinkNode_ConditionalNotProducedByMechCarrier.cs",
|
|
||||||
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_FollowProducer\\ThinkNode_ConditionalNotProducedByMechCarrier.cs",
|
|
||||||
"RelativeToolTip": "Jobs\\JobDriver_FollowProducer\\ThinkNode_ConditionalNotProducedByMechCarrier.cs",
|
|
||||||
"ViewState": "AgIAAAAAAAAAAAAAAAAAABQAAAAIAAAAAAAAAA==",
|
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
|
||||||
"WhenOpened": "2026-01-27T00:51:15.459Z",
|
|
||||||
"EditorCaption": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$type": "Document",
|
|
||||||
"DocumentIndex": 0,
|
|
||||||
"Title": "Building_Ootheca.cs",
|
|
||||||
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\Building_Ootheca.cs",
|
|
||||||
"RelativeDocumentMoniker": "Buildings\\Building_Ootheca\\Building_Ootheca.cs",
|
|
||||||
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\Building_Ootheca.cs",
|
|
||||||
"RelativeToolTip": "Buildings\\Building_Ootheca\\Building_Ootheca.cs",
|
|
||||||
"ViewState": "AgIAABwCAAAAAAAAAAAvwDICAAAJAAAAAAAAAA==",
|
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
|
||||||
"WhenOpened": "2026-01-23T08:31:14.555Z",
|
|
||||||
"EditorCaption": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$type": "Document",
|
|
||||||
"DocumentIndex": 8,
|
|
||||||
"Title": "NecroticTransformationUtility.cs",
|
|
||||||
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Hediffs\\ARA_ConfigurableMutant\\NecroticTransformationUtility.cs",
|
|
||||||
"RelativeDocumentMoniker": "Hediffs\\ARA_ConfigurableMutant\\NecroticTransformationUtility.cs",
|
|
||||||
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Hediffs\\ARA_ConfigurableMutant\\NecroticTransformationUtility.cs",
|
|
||||||
"RelativeToolTip": "Hediffs\\ARA_ConfigurableMutant\\NecroticTransformationUtility.cs",
|
|
||||||
"ViewState": "AgIAAA8AAAAAAAAAAAAtwCIAAAAcAAAAAAAAAA==",
|
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
|
||||||
"WhenOpened": "2026-01-26T08:44:42.184Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$type": "Document",
|
|
||||||
"DocumentIndex": 9,
|
|
||||||
"Title": "CompNodeSwarmLifetime.cs",
|
|
||||||
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Pawn_Comps\\ARA_NodeSwarmLifetime\\CompNodeSwarmLifetime.cs",
|
|
||||||
"RelativeDocumentMoniker": "Pawn_Comps\\ARA_NodeSwarmLifetime\\CompNodeSwarmLifetime.cs",
|
|
||||||
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Pawn_Comps\\ARA_NodeSwarmLifetime\\CompNodeSwarmLifetime.cs",
|
|
||||||
"RelativeToolTip": "Pawn_Comps\\ARA_NodeSwarmLifetime\\CompNodeSwarmLifetime.cs",
|
|
||||||
"ViewState": "AgIAADYAAAAAAAAAAAAtwEoAAAAbAAAAAAAAAA==",
|
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
|
||||||
"WhenOpened": "2026-01-26T08:44:35.266Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$type": "Document",
|
|
||||||
"DocumentIndex": 10,
|
|
||||||
"Title": "Gizmo_QueuedIncubationProgress.cs",
|
|
||||||
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\Gizmo_QueuedIncubationProgress.cs",
|
|
||||||
"RelativeDocumentMoniker": "Buildings\\Building_Ootheca\\Gizmo_QueuedIncubationProgress.cs",
|
|
||||||
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\Gizmo_QueuedIncubationProgress.cs",
|
|
||||||
"RelativeToolTip": "Buildings\\Building_Ootheca\\Gizmo_QueuedIncubationProgress.cs",
|
|
||||||
"ViewState": "AgIAAAAAAAAAAAAAAAAAABEAAAAuAAAAAAAAAA==",
|
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
|
||||||
"WhenOpened": "2026-01-26T08:11:04.23Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$type": "Document",
|
|
||||||
"DocumentIndex": 6,
|
|
||||||
"Title": "Gizmo_PawnProgressBar.cs",
|
|
||||||
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\Gizmo_PawnProgressBar.cs",
|
|
||||||
"RelativeDocumentMoniker": "Buildings\\Building_Ootheca\\Gizmo_PawnProgressBar.cs",
|
|
||||||
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\Gizmo_PawnProgressBar.cs",
|
|
||||||
"RelativeToolTip": "Buildings\\Building_Ootheca\\Gizmo_PawnProgressBar.cs",
|
|
||||||
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAwAAAAFAAAAAAAAAA==",
|
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
|
||||||
"WhenOpened": "2026-01-26T08:12:03.772Z",
|
|
||||||
"EditorCaption": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$type": "Document",
|
|
||||||
"DocumentIndex": 7,
|
|
||||||
"Title": "Gizmo_NeutronFlux.cs",
|
|
||||||
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\Gizmo_NeutronFlux.cs",
|
|
||||||
"RelativeDocumentMoniker": "Buildings\\Building_Ootheca\\Gizmo_NeutronFlux.cs",
|
|
||||||
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\Gizmo_NeutronFlux.cs",
|
|
||||||
"RelativeToolTip": "Buildings\\Building_Ootheca\\Gizmo_NeutronFlux.cs",
|
|
||||||
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAkAAAAiAAAAAAAAAA==",
|
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
|
||||||
"WhenOpened": "2026-01-26T08:11:53.324Z",
|
|
||||||
"EditorCaption": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$type": "Document",
|
|
||||||
"DocumentIndex": 11,
|
|
||||||
"Title": "Building_EquipmentOotheca.cs",
|
|
||||||
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_EquipmentOotheca\\Building_EquipmentOotheca.cs",
|
|
||||||
"RelativeDocumentMoniker": "Buildings\\Building_EquipmentOotheca\\Building_EquipmentOotheca.cs",
|
|
||||||
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_EquipmentOotheca\\Building_EquipmentOotheca.cs",
|
|
||||||
"RelativeToolTip": "Buildings\\Building_EquipmentOotheca\\Building_EquipmentOotheca.cs",
|
|
||||||
"ViewState": "AgIAAEIBAAAAAAAAAAAAAEMBAAAAAAAAAAAAAA==",
|
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
|
||||||
"WhenOpened": "2026-01-26T07:52:41.869Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$type": "Document",
|
|
||||||
"DocumentIndex": 12,
|
|
||||||
"Title": "Building_CorpseVat.cs",
|
|
||||||
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_CorpseVat\\Building_CorpseVat.cs",
|
|
||||||
"RelativeDocumentMoniker": "Buildings\\Building_CorpseVat\\Building_CorpseVat.cs",
|
|
||||||
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_CorpseVat\\Building_CorpseVat.cs",
|
|
||||||
"RelativeToolTip": "Buildings\\Building_CorpseVat\\Building_CorpseVat.cs",
|
|
||||||
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAgAAAASAAAAAAAAAA==",
|
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
|
||||||
"WhenOpened": "2026-01-23T10:24:46.264Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$type": "Document",
|
|
||||||
"DocumentIndex": 14,
|
|
||||||
"Title": "JobGiver_Grower.cs",
|
|
||||||
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_Plant\\JobGiver_Grower.cs",
|
|
||||||
"RelativeDocumentMoniker": "Jobs\\JobDriver_Plant\\JobGiver_Grower.cs",
|
|
||||||
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_Plant\\JobGiver_Grower.cs",
|
|
||||||
"RelativeToolTip": "Jobs\\JobDriver_Plant\\JobGiver_Grower.cs",
|
|
||||||
"ViewState": "AgIAAFMAAAAAAAAAAAAQwGkAAAANAAAAAAAAAA==",
|
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
|
||||||
"WhenOpened": "2026-01-23T08:51:03.439Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$type": "Document",
|
|
||||||
"DocumentIndex": 16,
|
|
||||||
"Title": "Hediff_Possession.cs",
|
|
||||||
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_HuggingFace\\Hediff_Possession.cs",
|
|
||||||
"RelativeDocumentMoniker": "Abilities\\ARA_HuggingFace\\Hediff_Possession.cs",
|
|
||||||
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_HuggingFace\\Hediff_Possession.cs",
|
|
||||||
"RelativeToolTip": "Abilities\\ARA_HuggingFace\\Hediff_Possession.cs",
|
|
||||||
"ViewState": "AgIAAAAAAAAAAAAAAAAAABMAAAAAAAAAAAAAAA==",
|
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
|
||||||
"WhenOpened": "2026-01-23T08:50:05.221Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$type": "Document",
|
|
||||||
"DocumentIndex": 17,
|
|
||||||
"Title": "CompAbilityEffect_Possess.cs",
|
|
||||||
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_HuggingFace\\CompAbilityEffect_Possess.cs",
|
|
||||||
"RelativeDocumentMoniker": "Abilities\\ARA_HuggingFace\\CompAbilityEffect_Possess.cs",
|
|
||||||
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_HuggingFace\\CompAbilityEffect_Possess.cs",
|
|
||||||
"RelativeToolTip": "Abilities\\ARA_HuggingFace\\CompAbilityEffect_Possess.cs",
|
|
||||||
"ViewState": "AgIAAHcAAAAAAAAAAAAAAIkAAAA5AAAAAAAAAA==",
|
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
|
||||||
"WhenOpened": "2026-01-23T08:50:01.549Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$type": "Document",
|
|
||||||
"DocumentIndex": 13,
|
|
||||||
"Title": "CorpseVatExtension.cs",
|
|
||||||
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_CorpseVat\\CorpseVatExtension.cs",
|
|
||||||
"RelativeDocumentMoniker": "Buildings\\Building_CorpseVat\\CorpseVatExtension.cs",
|
|
||||||
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_CorpseVat\\CorpseVatExtension.cs",
|
|
||||||
"RelativeToolTip": "Buildings\\Building_CorpseVat\\CorpseVatExtension.cs",
|
|
||||||
"ViewState": "AgIAAAAAAAAAAAAAAADwvx0AAAAoAAAAAAAAAA==",
|
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
|
||||||
"WhenOpened": "2026-01-23T10:25:01.777Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$type": "Document",
|
|
||||||
"DocumentIndex": 18,
|
|
||||||
"Title": "CompDelayedTerrainSpawn.cs",
|
|
||||||
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_BuildingTerrainSpawn\\CompDelayedTerrainSpawn.cs",
|
|
||||||
"RelativeDocumentMoniker": "Building_Comps\\ARA_BuildingTerrainSpawn\\CompDelayedTerrainSpawn.cs",
|
|
||||||
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_BuildingTerrainSpawn\\CompDelayedTerrainSpawn.cs",
|
|
||||||
"RelativeToolTip": "Building_Comps\\ARA_BuildingTerrainSpawn\\CompDelayedTerrainSpawn.cs",
|
|
||||||
"ViewState": "AgIAAFEAAAAAAAAAAAAAAF8AAAA+AAAAAAAAAA==",
|
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
|
||||||
"WhenOpened": "2026-01-23T08:46:06.784Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$type": "Document",
|
|
||||||
"DocumentIndex": 19,
|
|
||||||
"Title": "CompProperties_HediffTerrainSpawn.cs",
|
|
||||||
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Hediffs\\ARA_HediffTerrainSpawn\\CompProperties_HediffTerrainSpawn.cs",
|
|
||||||
"RelativeDocumentMoniker": "Hediffs\\ARA_HediffTerrainSpawn\\CompProperties_HediffTerrainSpawn.cs",
|
|
||||||
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Hediffs\\ARA_HediffTerrainSpawn\\CompProperties_HediffTerrainSpawn.cs",
|
|
||||||
"RelativeToolTip": "Hediffs\\ARA_HediffTerrainSpawn\\CompProperties_HediffTerrainSpawn.cs",
|
|
||||||
"ViewState": "AgIAAAUAAAAAAAAAAAAtwBkAAAAxAAAAAAAAAA==",
|
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
|
||||||
"WhenOpened": "2026-01-23T08:44:14.682Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$type": "Document",
|
|
||||||
"DocumentIndex": 15,
|
|
||||||
"Title": "JobGiver_Cleaner.cs",
|
|
||||||
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_Clean\\JobGiver_Cleaner.cs",
|
|
||||||
"RelativeDocumentMoniker": "Jobs\\JobDriver_Clean\\JobGiver_Cleaner.cs",
|
|
||||||
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_Clean\\JobGiver_Cleaner.cs",
|
|
||||||
"RelativeToolTip": "Jobs\\JobDriver_Clean\\JobGiver_Cleaner.cs",
|
|
||||||
"ViewState": "AgIAAAAAAAAAAAAAAAAAABYAAABwAAAAAAAAAA==",
|
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
|
||||||
"WhenOpened": "2026-01-23T08:51:58.899Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$type": "Document",
|
|
||||||
"DocumentIndex": 20,
|
|
||||||
"Title": "CompHediffTerrainSpawn.cs",
|
|
||||||
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Hediffs\\ARA_HediffTerrainSpawn\\CompHediffTerrainSpawn.cs",
|
|
||||||
"RelativeDocumentMoniker": "Hediffs\\ARA_HediffTerrainSpawn\\CompHediffTerrainSpawn.cs",
|
|
||||||
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Hediffs\\ARA_HediffTerrainSpawn\\CompHediffTerrainSpawn.cs",
|
|
||||||
"RelativeToolTip": "Hediffs\\ARA_HediffTerrainSpawn\\CompHediffTerrainSpawn.cs",
|
|
||||||
"ViewState": "AgIAAKIAAAAAAAAAAAAawLQAAAAMAAAAAAAAAA==",
|
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
|
||||||
"WhenOpened": "2026-01-23T08:44:10.075Z"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -334,6 +334,7 @@
|
|||||||
<Compile Include="Pawn_Comps\ARA_AutoMechCarrier\CompProducedByMechCarrier.cs" />
|
<Compile Include="Pawn_Comps\ARA_AutoMechCarrier\CompProducedByMechCarrier.cs" />
|
||||||
<Compile Include="Pawn_Comps\ARA_AutoMechCarrier\CompProperties_AutoMechCarrier.cs" />
|
<Compile Include="Pawn_Comps\ARA_AutoMechCarrier\CompProperties_AutoMechCarrier.cs" />
|
||||||
<Compile Include="Pawn_Comps\ARA_AutoMechCarrier\CompProperties_ProducedByMechCarrier.cs" />
|
<Compile Include="Pawn_Comps\ARA_AutoMechCarrier\CompProperties_ProducedByMechCarrier.cs" />
|
||||||
|
<Compile Include="Pawn_Comps\ARA_AutoMechCarrier\IBidirectionalValidator.cs" />
|
||||||
<Compile Include="Pawn_Comps\ARA_AutoMechCarrier\PawnProductionEntry.cs" />
|
<Compile Include="Pawn_Comps\ARA_AutoMechCarrier\PawnProductionEntry.cs" />
|
||||||
<Compile Include="Pawn_Comps\ARA_CompHediffGiver\CompHediffGiver.cs" />
|
<Compile Include="Pawn_Comps\ARA_CompHediffGiver\CompHediffGiver.cs" />
|
||||||
<Compile Include="Pawn_Comps\ARA_CompHediffGiver\CompProperties_HediffGiver.cs" />
|
<Compile Include="Pawn_Comps\ARA_CompHediffGiver\CompProperties_HediffGiver.cs" />
|
||||||
|
|||||||
@@ -561,20 +561,30 @@ namespace ArachnaeSwarm
|
|||||||
}
|
}
|
||||||
public override IEnumerable<Gizmo> GetGizmos()
|
public override IEnumerable<Gizmo> GetGizmos()
|
||||||
{
|
{
|
||||||
base.GetGizmos();
|
foreach (var gizmo in base.GetGizmos())
|
||||||
|
{
|
||||||
|
if (gizmo is Command_Action cmd && cmd.defaultLabel != null)
|
||||||
|
{
|
||||||
|
string label = cmd.defaultLabel.ToString();
|
||||||
|
if (label.Contains("拆除") || label.Contains("Deconstruct") || label.Contains("半径") || label.Contains("Radius"))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 强制将基础组件(如 Refuelable)甚至默认排序为 -100 的东西移到后面
|
||||||
|
if (gizmo.Order >= -100f && gizmo.Order <= 0f)
|
||||||
|
{
|
||||||
|
gizmo.Order = -90f;
|
||||||
|
}
|
||||||
|
|
||||||
|
yield return gizmo;
|
||||||
|
}
|
||||||
|
|
||||||
// 首先获取选中的同类建筑
|
// 首先获取选中的同类建筑
|
||||||
var selectedOothecas = GetSelectedOothecas();
|
var selectedOothecas = GetSelectedOothecas();
|
||||||
bool isMultiSelect = selectedOothecas.Count > 1;
|
bool isMultiSelect = selectedOothecas.Count > 1;
|
||||||
|
|
||||||
if (Faction == Faction.OfPlayer)
|
if (Faction == Faction.OfPlayer)
|
||||||
{
|
{
|
||||||
// 在多选时,只在第一个建筑上显示进度条和通量条
|
|
||||||
if (!isMultiSelect || (isMultiSelect && selectedOothecas.First() == this))
|
|
||||||
{
|
|
||||||
yield return new Gizmo_PawnProgressBar(this);
|
|
||||||
yield return new Gizmo_NeutronFlux(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
var config = IncubatorData?.SelectedConfig;
|
var config = IncubatorData?.SelectedConfig;
|
||||||
|
|
||||||
// 添加订单按钮(多选时合并)
|
// 添加订单按钮(多选时合并)
|
||||||
@@ -657,6 +667,7 @@ namespace ArachnaeSwarm
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 为多选建筑显示订单菜单
|
/// 为多选建筑显示订单菜单
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
using RimWorld;
|
using RimWorld;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using Verse;
|
using Verse;
|
||||||
using Verse.AI;
|
using Verse.AI;
|
||||||
|
|
||||||
@@ -14,6 +16,12 @@ namespace ArachnaeSwarm
|
|||||||
// 是否仅防御征召状态的生产者(针对pawn类型生产者)
|
// 是否仅防御征召状态的生产者(针对pawn类型生产者)
|
||||||
public bool onlyDefendDrafted = true;
|
public bool onlyDefendDrafted = true;
|
||||||
|
|
||||||
|
// 是否排除休眠的机械族
|
||||||
|
public bool excludeDormantMechs = true;
|
||||||
|
|
||||||
|
// 是否将炮塔纳入考虑
|
||||||
|
public bool includeHostileTurrets = true;
|
||||||
|
|
||||||
protected override Pawn GetDefendee(Pawn pawn)
|
protected override Pawn GetDefendee(Pawn pawn)
|
||||||
{
|
{
|
||||||
// 我们不需要返回Pawn类型的防御者,因为我们实际上防御的是Thing
|
// 我们不需要返回Pawn类型的防御者,因为我们实际上防御的是Thing
|
||||||
@@ -79,7 +87,7 @@ namespace ArachnaeSwarm
|
|||||||
float defendRadiusValue = GetFlagRadius(pawn);
|
float defendRadiusValue = GetFlagRadius(pawn);
|
||||||
|
|
||||||
// 寻找附近的威胁
|
// 寻找附近的威胁
|
||||||
Pawn enemy = FindEnemy(pawn, defendRadiusValue);
|
Thing enemy = FindEnemy(pawn, defendRadiusValue);
|
||||||
if (enemy != null)
|
if (enemy != null)
|
||||||
{
|
{
|
||||||
// 创建攻击工作
|
// 创建攻击工作
|
||||||
@@ -94,7 +102,8 @@ namespace ArachnaeSwarm
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Pawn FindEnemy(Pawn pawn, float radius)
|
|
||||||
|
protected virtual Thing FindEnemy(Pawn pawn, float radius)
|
||||||
{
|
{
|
||||||
CompProducedByMechCarrier producerComp = pawn.TryGetComp<CompProducedByMechCarrier>();
|
CompProducedByMechCarrier producerComp = pawn.TryGetComp<CompProducedByMechCarrier>();
|
||||||
if (producerComp == null || !producerComp.HasValidProducer)
|
if (producerComp == null || !producerComp.HasValidProducer)
|
||||||
@@ -103,10 +112,10 @@ namespace ArachnaeSwarm
|
|||||||
Thing producer = producerComp.Producer;
|
Thing producer = producerComp.Producer;
|
||||||
IntVec3 center = producer.Position;
|
IntVec3 center = producer.Position;
|
||||||
|
|
||||||
return (Pawn)AttackTargetFinder.BestAttackTarget(
|
return (Thing)AttackTargetFinder.BestAttackTarget(
|
||||||
pawn,
|
pawn,
|
||||||
TargetScanFlags.NeedLOSToAll,
|
TargetScanFlags.NeedThreat,
|
||||||
target => target is Pawn p && pawn.HostileTo(p) && p.Spawned && !p.Downed && !p.Dead,
|
(Thing x) => ExtraTargetValidator(pawn, x),
|
||||||
0f,
|
0f,
|
||||||
radius,
|
radius,
|
||||||
center,
|
center,
|
||||||
@@ -121,6 +130,9 @@ namespace ArachnaeSwarm
|
|||||||
JobGiver_AIDefendProducer obj = (JobGiver_AIDefendProducer)base.DeepCopy(resolve);
|
JobGiver_AIDefendProducer obj = (JobGiver_AIDefendProducer)base.DeepCopy(resolve);
|
||||||
obj.attackMeleeThreatEvenIfNotHostile = attackMeleeThreatEvenIfNotHostile;
|
obj.attackMeleeThreatEvenIfNotHostile = attackMeleeThreatEvenIfNotHostile;
|
||||||
obj.defendRadius = defendRadius;
|
obj.defendRadius = defendRadius;
|
||||||
|
obj.onlyDefendDrafted = onlyDefendDrafted;
|
||||||
|
obj.excludeDormantMechs = excludeDormantMechs;
|
||||||
|
obj.includeHostileTurrets = includeHostileTurrets;
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,18 +10,6 @@ namespace ArachnaeSwarm
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class ThinkNode_ConditionalNotProducedByMechCarrier : ThinkNode_Conditional
|
public class ThinkNode_ConditionalNotProducedByMechCarrier : ThinkNode_Conditional
|
||||||
{
|
{
|
||||||
// 可选:是否检查生产者是否存活
|
|
||||||
private bool checkProducerAlive = true;
|
|
||||||
|
|
||||||
// 可选:是否检查生产者是否在同一地图
|
|
||||||
private bool checkSameMap = false;
|
|
||||||
|
|
||||||
// 可选:是否检查生产者是否可到达
|
|
||||||
private bool checkReachable = false;
|
|
||||||
|
|
||||||
// 可选:是否检查生产者类型(pawn必须征召才跟随)
|
|
||||||
private bool checkProducerTypeConditions = true;
|
|
||||||
|
|
||||||
public ThinkNode_ConditionalNotProducedByMechCarrier()
|
public ThinkNode_ConditionalNotProducedByMechCarrier()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -29,7 +17,7 @@ namespace ArachnaeSwarm
|
|||||||
protected override bool Satisfied(Pawn pawn)
|
protected override bool Satisfied(Pawn pawn)
|
||||||
{
|
{
|
||||||
// 基础检查:如果不是生产者生产的,返回true
|
// 基础检查:如果不是生产者生产的,返回true
|
||||||
bool isProduced = IsProducedByMechCarrier(pawn);
|
bool isProduced = HasProducer(pawn);
|
||||||
|
|
||||||
// 如果是生产者生产的,再检查其他条件
|
// 如果是生产者生产的,再检查其他条件
|
||||||
if (isProduced)
|
if (isProduced)
|
||||||
@@ -44,7 +32,7 @@ namespace ArachnaeSwarm
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 检查pawn是否由生产者生产
|
/// 检查pawn是否由生产者生产
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private bool IsProducedByMechCarrier(Pawn pawn)
|
private bool HasProducer(Pawn pawn)
|
||||||
{
|
{
|
||||||
if (!pawn.Spawned)
|
if (!pawn.Spawned)
|
||||||
return false;
|
return false;
|
||||||
@@ -58,33 +46,6 @@ namespace ArachnaeSwarm
|
|||||||
if (producer == null || producer.Destroyed)
|
if (producer == null || producer.Destroyed)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// 检查生产者是否在同一地图
|
|
||||||
if (checkSameMap && producer.Map != pawn.Map)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// 检查是否可以到达生产者
|
|
||||||
if (checkReachable && !pawn.CanReach(producer, PathEndMode.OnCell, Danger.Deadly))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// 根据生产者类型检查特定条件
|
|
||||||
if (checkProducerTypeConditions && producer is Pawn pawnProducer)
|
|
||||||
{
|
|
||||||
// 如果生产者是pawn,则只在征召状态下才跟随
|
|
||||||
// 这是为了模拟原版动物跟随主人的逻辑
|
|
||||||
if (checkProducerAlive && (pawnProducer.Dead || pawnProducer.Downed))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// 只有在征召状态下才认为需要跟随
|
|
||||||
if (!pawnProducer.Drafted)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (checkProducerTypeConditions && producer is Building buildingProducer)
|
|
||||||
{
|
|
||||||
// 如果生产者是建筑,默认返回true
|
|
||||||
// 可以根据需要添加其他条件
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,48 +9,63 @@ using Verse.AI.Group;
|
|||||||
|
|
||||||
namespace ArachnaeSwarm
|
namespace ArachnaeSwarm
|
||||||
{
|
{
|
||||||
public class CompAutoMechCarrier : CompMechCarrier
|
public class CompAutoMechCarrier : CompMechCarrier, IBidirectionalValidator
|
||||||
{
|
{
|
||||||
#region Reflected Fields
|
#region Reflected Fields
|
||||||
private static FieldInfo spawnedPawnsField;
|
private static FieldInfo spawnedPawnsField;
|
||||||
private static FieldInfo cooldownTicksRemainingField;
|
private static FieldInfo cooldownTicksRemainingField;
|
||||||
private static FieldInfo innerContainerField;
|
private static FieldInfo innerContainerField;
|
||||||
|
|
||||||
|
// 安全锁
|
||||||
|
private object validationLock = new object();
|
||||||
|
|
||||||
private List<Pawn> SpawnedPawns
|
private List<Pawn> SpawnedPawns
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
{
|
||||||
|
lock (validationLock)
|
||||||
{
|
{
|
||||||
if (spawnedPawnsField == null)
|
if (spawnedPawnsField == null)
|
||||||
spawnedPawnsField = typeof(CompMechCarrier).GetField("spawnedPawns", BindingFlags.NonPublic | BindingFlags.Instance);
|
spawnedPawnsField = typeof(CompMechCarrier).GetField("spawnedPawns", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||||
return (List<Pawn>)spawnedPawnsField.GetValue(this);
|
return (List<Pawn>)spawnedPawnsField.GetValue(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private int CooldownTicksRemaining
|
private int CooldownTicksRemaining
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
{
|
||||||
|
lock (validationLock)
|
||||||
{
|
{
|
||||||
if (cooldownTicksRemainingField == null)
|
if (cooldownTicksRemainingField == null)
|
||||||
cooldownTicksRemainingField = typeof(CompMechCarrier).GetField("cooldownTicksRemaining", BindingFlags.NonPublic | BindingFlags.Instance);
|
cooldownTicksRemainingField = typeof(CompMechCarrier).GetField("cooldownTicksRemaining", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||||
return (int)cooldownTicksRemainingField.GetValue(this);
|
return (int)cooldownTicksRemainingField.GetValue(this);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
set
|
set
|
||||||
|
{
|
||||||
|
lock (validationLock)
|
||||||
{
|
{
|
||||||
if (cooldownTicksRemainingField == null)
|
if (cooldownTicksRemainingField == null)
|
||||||
cooldownTicksRemainingField = typeof(CompMechCarrier).GetField("cooldownTicksRemaining", BindingFlags.NonPublic | BindingFlags.Instance);
|
cooldownTicksRemainingField = typeof(CompMechCarrier).GetField("cooldownTicksRemaining", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||||
cooldownTicksRemainingField.SetValue(this, value);
|
cooldownTicksRemainingField.SetValue(this, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private ThingOwner InnerContainer
|
private ThingOwner InnerContainer
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
{
|
||||||
|
lock (validationLock)
|
||||||
{
|
{
|
||||||
if (innerContainerField == null)
|
if (innerContainerField == null)
|
||||||
innerContainerField = typeof(CompMechCarrier).GetField("innerContainer", BindingFlags.NonPublic | BindingFlags.Instance);
|
innerContainerField = typeof(CompMechCarrier).GetField("innerContainer", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||||
return (ThingOwner)innerContainerField.GetValue(this);
|
return (ThingOwner)innerContainerField.GetValue(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Custom Follow Position
|
#region Custom Follow Position
|
||||||
@@ -60,7 +75,6 @@ namespace ArachnaeSwarm
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取或设置自定义跟随位置
|
/// 获取或设置自定义跟随位置
|
||||||
/// 如果此位置不为空,生成的单位将以此位置而非父单位位置为跟随中心点
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IntVec3? CustomFollowPosition
|
public IntVec3? CustomFollowPosition
|
||||||
{
|
{
|
||||||
@@ -85,14 +99,13 @@ namespace ArachnaeSwarm
|
|||||||
customFollowPositionMap = null;
|
customFollowPositionMap = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 通知所有已生成的单位更新他们的跟随位置
|
|
||||||
NotifyFollowPositionChanged();
|
NotifyFollowPositionChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 检查自定义位置是否有效(位置不为空且在正确的地图上)
|
/// 检查自定义位置是否有效
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool HasValidCustomFollowPosition
|
public bool HasValidCustomFollowPosition
|
||||||
{
|
{
|
||||||
@@ -101,7 +114,6 @@ namespace ArachnaeSwarm
|
|||||||
if (!customFollowPosition.HasValue || customFollowPositionMap == null)
|
if (!customFollowPosition.HasValue || customFollowPositionMap == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// 如果父单位已销毁或不在同一地图,自定义位置也无效
|
|
||||||
if (parent == null || parent.Destroyed || !parent.Spawned)
|
if (parent == null || parent.Destroyed || !parent.Spawned)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -111,7 +123,6 @@ namespace ArachnaeSwarm
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取有效的跟随位置
|
/// 获取有效的跟随位置
|
||||||
/// 优先返回自定义位置,如果无效则返回父单位位置
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IntVec3 GetEffectiveFollowPosition()
|
public IntVec3 GetEffectiveFollowPosition()
|
||||||
{
|
{
|
||||||
@@ -120,7 +131,6 @@ namespace ArachnaeSwarm
|
|||||||
return customFollowPosition.Value;
|
return customFollowPosition.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 返回父单位位置
|
|
||||||
if (parent != null && parent.Spawned)
|
if (parent != null && parent.Spawned)
|
||||||
{
|
{
|
||||||
if (parent is Building building)
|
if (parent is Building building)
|
||||||
@@ -156,19 +166,16 @@ namespace ArachnaeSwarm
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void NotifyFollowPositionChanged()
|
private void NotifyFollowPositionChanged()
|
||||||
{
|
{
|
||||||
if (SpawnedPawns == null || !SpawnedPawns.Any())
|
var spawned = GetSafeSpawnedPawns();
|
||||||
|
if (spawned == null || !spawned.Any())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// 清理无效的Pawn引用
|
foreach (var pawn in spawned)
|
||||||
SpawnedPawns.RemoveAll(p => p == null || p.Destroyed);
|
|
||||||
|
|
||||||
// 通知每个Pawn的CompProducedByMechCarrier
|
|
||||||
foreach (Pawn spawnedPawn in SpawnedPawns)
|
|
||||||
{
|
{
|
||||||
if (spawnedPawn == null || spawnedPawn.Destroyed || !spawnedPawn.Spawned)
|
if (pawn == null || pawn.Destroyed || !pawn.Spawned)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var producedComp = spawnedPawn.TryGetComp<CompProducedByMechCarrier>();
|
var producedComp = pawn.TryGetComp<CompProducedByMechCarrier>();
|
||||||
if (producedComp != null)
|
if (producedComp != null)
|
||||||
{
|
{
|
||||||
producedComp.TryUpdateProducerStatus();
|
producedComp.TryUpdateProducerStatus();
|
||||||
@@ -187,8 +194,6 @@ namespace ArachnaeSwarm
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 设置自定义跟随位置
|
/// 设置自定义跟随位置
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="position">新的跟随位置</param>
|
|
||||||
/// <returns>是否设置成功</returns>
|
|
||||||
public bool SetCustomFollowPosition(IntVec3 position)
|
public bool SetCustomFollowPosition(IntVec3 position)
|
||||||
{
|
{
|
||||||
if (parent == null || !parent.Spawned)
|
if (parent == null || !parent.Spawned)
|
||||||
@@ -202,23 +207,18 @@ namespace ArachnaeSwarm
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 临时设置自定义跟随位置(在一段时间后自动清除)
|
/// 临时设置自定义跟随位置
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="position">临时位置</param>
|
|
||||||
/// <param name="durationTicks">持续时间(tick)</param>
|
|
||||||
public void SetTemporaryFollowPosition(IntVec3 position, int durationTicks)
|
public void SetTemporaryFollowPosition(IntVec3 position, int durationTicks)
|
||||||
{
|
{
|
||||||
if (!SetCustomFollowPosition(position))
|
if (!SetCustomFollowPosition(position))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// 启动定时器清除临时位置
|
|
||||||
StartPositionClearTimer(durationTicks);
|
StartPositionClearTimer(durationTicks);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void StartPositionClearTimer(int durationTicks)
|
private void StartPositionClearTimer(int durationTicks)
|
||||||
{
|
{
|
||||||
// 这里可以使用TickManager来安排定时清除
|
|
||||||
// 简单实现:记录时间并在Tick中检查
|
|
||||||
temporaryPositionExpiryTick = Find.TickManager.TicksGame + durationTicks;
|
temporaryPositionExpiryTick = Find.TickManager.TicksGame + durationTicks;
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
@@ -238,35 +238,245 @@ namespace ArachnaeSwarm
|
|||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region 双向验证字段
|
||||||
|
// 子单位死亡通知队列
|
||||||
|
private Queue<Thing> childDeathQueue = new Queue<Thing>();
|
||||||
|
private int lastChildCleanupTick = -1;
|
||||||
|
private const int CHILD_CLEANUP_INTERVAL = 30;
|
||||||
|
|
||||||
|
// 已验证的子单位缓存
|
||||||
|
private HashSet<Thing> validatedChildren = new HashSet<Thing>();
|
||||||
|
private int lastValidationCacheClearTick = -1;
|
||||||
|
private const int VALIDATION_CACHE_CLEAR_INTERVAL = 300;
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region IBidirectionalValidator 实现
|
||||||
|
public bool IsProducerValid
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
lock (validationLock)
|
||||||
|
{
|
||||||
|
if (parent == null || parent.Destroyed)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (parent is Pawn pawn && (pawn.Dead || pawn.Downed))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Thing GetProducer()
|
||||||
|
{
|
||||||
|
lock (validationLock)
|
||||||
|
{
|
||||||
|
return IsProducerValid ? parent : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsProducerAlive
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
lock (validationLock)
|
||||||
|
{
|
||||||
|
return IsProducerValid && parent.Spawned;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsChildValid(Thing child)
|
||||||
|
{
|
||||||
|
if (child == null || child.Destroyed)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!(child is Pawn))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (child is Pawn pawn && (pawn.Dead || pawn.Downed))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsChildAlive(Thing child)
|
||||||
|
{
|
||||||
|
return IsChildValid(child) && child.Spawned;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ValidateBidirectional(Thing child)
|
||||||
|
{
|
||||||
|
lock (validationLock)
|
||||||
|
{
|
||||||
|
if (!IsProducerValid)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!IsChildValid(child))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return ValidateBidirectionalReference(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ValidateAndExecute(Thing child, System.Action action)
|
||||||
|
{
|
||||||
|
if (!ValidateBidirectional(child))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
action?.Invoke();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error($"生产者双向验证执行失败: {ex.Message}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ValidateBidirectionalReference(Thing child)
|
||||||
|
{
|
||||||
|
if (validatedChildren.Contains(child))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
var spawned = GetSafeSpawnedPawns();
|
||||||
|
bool isInList = spawned.Contains(child as Pawn);
|
||||||
|
|
||||||
|
var childComp = (child as Pawn)?.TryGetComp<CompProducedByMechCarrier>();
|
||||||
|
bool hasValidReference = childComp != null && childComp.IsProducerValid && childComp.GetProducer() == parent;
|
||||||
|
|
||||||
|
bool isValid = isInList && hasValidReference;
|
||||||
|
|
||||||
|
if (isValid)
|
||||||
|
{
|
||||||
|
validatedChildren.Add(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
return isValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证子单位引用
|
||||||
|
/// </summary>
|
||||||
|
public bool ValidateChildReference(Thing child)
|
||||||
|
{
|
||||||
|
return ValidateBidirectionalReference(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 通知子单位死亡
|
||||||
|
/// </summary>
|
||||||
|
public void NotifyChildDeath(Thing child)
|
||||||
|
{
|
||||||
|
lock (validationLock)
|
||||||
|
{
|
||||||
|
childDeathQueue.Enqueue(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 安全移除子单位
|
||||||
|
/// </summary>
|
||||||
|
private void RemoveChildSafe(Thing child)
|
||||||
|
{
|
||||||
|
lock (validationLock)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var spawned = SpawnedPawns;
|
||||||
|
if (spawned != null && child is Pawn pawn)
|
||||||
|
{
|
||||||
|
bool removed = spawned.Remove(pawn);
|
||||||
|
if (removed && AutoProps.debugLogging)
|
||||||
|
{
|
||||||
|
Log.Message($"安全移除子单位: {pawn.LabelCap}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
validatedChildren.Remove(child);
|
||||||
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error($"移除子单位失败: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 清理无效的子单位引用
|
||||||
|
/// </summary>
|
||||||
|
private void CleanupInvalidChildren()
|
||||||
|
{
|
||||||
|
lock (validationLock)
|
||||||
|
{
|
||||||
|
// 处理死亡通知队列
|
||||||
|
while (childDeathQueue.Count > 0)
|
||||||
|
{
|
||||||
|
var deadChild = childDeathQueue.Dequeue();
|
||||||
|
if (deadChild != null)
|
||||||
|
{
|
||||||
|
RemoveChildSafe(deadChild);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理spawnedPawns列表
|
||||||
|
var spawned = SpawnedPawns;
|
||||||
|
if (spawned != null)
|
||||||
|
{
|
||||||
|
int before = spawned.Count;
|
||||||
|
spawned.RemoveAll(p => p == null || p.Destroyed || (p is Pawn pawn && pawn.Dead));
|
||||||
|
|
||||||
|
if (before != spawned.Count && AutoProps.debugLogging)
|
||||||
|
{
|
||||||
|
Log.Message($"清理无效子单位: {before} -> {spawned.Count}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理验证缓存
|
||||||
|
if (Find.TickManager.TicksGame - lastValidationCacheClearTick > VALIDATION_CACHE_CLEAR_INTERVAL)
|
||||||
|
{
|
||||||
|
validatedChildren.RemoveWhere(c => c == null || c.Destroyed);
|
||||||
|
lastValidationCacheClearTick = Find.TickManager.TicksGame;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
public CompProperties_AutoMechCarrier AutoProps => (CompProperties_AutoMechCarrier)props;
|
public CompProperties_AutoMechCarrier AutoProps => (CompProperties_AutoMechCarrier)props;
|
||||||
|
|
||||||
// 缓存的合并生产队列
|
// 缓存的合并生产队列
|
||||||
private List<PawnProductionEntry> cachedProductionQueue = null;
|
private List<PawnProductionEntry> cachedProductionQueue = null;
|
||||||
private int lastProductionQueueUpdateTick = -1;
|
private int lastProductionQueueUpdateTick = -1;
|
||||||
private const int PRODUCTION_QUEUE_CACHE_TICKS = 30; // 30 tick缓存
|
private const int PRODUCTION_QUEUE_CACHE_TICKS = 30;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取合并后的生产队列
|
/// 获取合并后的生产队列
|
||||||
/// 包括从HediffComp扫描的和动态添加的
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<PawnProductionEntry> GetProductionQueue()
|
public List<PawnProductionEntry> GetProductionQueue()
|
||||||
{
|
{
|
||||||
// 使用缓存提高性能
|
|
||||||
if (cachedProductionQueue != null &&
|
if (cachedProductionQueue != null &&
|
||||||
Find.TickManager.TicksGame - lastProductionQueueUpdateTick < PRODUCTION_QUEUE_CACHE_TICKS)
|
Find.TickManager.TicksGame - lastProductionQueueUpdateTick < PRODUCTION_QUEUE_CACHE_TICKS)
|
||||||
{
|
{
|
||||||
return cachedProductionQueue;
|
return cachedProductionQueue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取Pawn引用
|
try
|
||||||
|
{
|
||||||
var pawn = parent as Pawn;
|
var pawn = parent as Pawn;
|
||||||
|
|
||||||
// 从AutoProps获取合并后的生产队列
|
|
||||||
cachedProductionQueue = AutoProps.GetMergedProductionQueue(pawn);
|
cachedProductionQueue = AutoProps.GetMergedProductionQueue(pawn);
|
||||||
lastProductionQueueUpdateTick = Find.TickManager.TicksGame;
|
lastProductionQueueUpdateTick = Find.TickManager.TicksGame;
|
||||||
|
|
||||||
return cachedProductionQueue;
|
return cachedProductionQueue;
|
||||||
}
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error($"获取生产队列失败: {ex.Message}");
|
||||||
|
return new List<PawnProductionEntry>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 强制刷新生产队列缓存
|
/// 强制刷新生产队列缓存
|
||||||
@@ -277,6 +487,23 @@ namespace ArachnaeSwarm
|
|||||||
lastProductionQueueUpdateTick = -1;
|
lastProductionQueueUpdateTick = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 安全获取已生成的Pawn列表
|
||||||
|
/// </summary>
|
||||||
|
private List<Pawn> GetSafeSpawnedPawns()
|
||||||
|
{
|
||||||
|
lock (validationLock)
|
||||||
|
{
|
||||||
|
var spawned = SpawnedPawns;
|
||||||
|
if (spawned == null)
|
||||||
|
return new List<Pawn>();
|
||||||
|
|
||||||
|
spawned.RemoveAll(p => p == null || p.Destroyed || (p is Pawn pawn && pawn.Dead));
|
||||||
|
|
||||||
|
return spawned;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取总容量
|
/// 获取总容量
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -284,14 +511,17 @@ namespace ArachnaeSwarm
|
|||||||
|
|
||||||
private int LiveSpawnedPawnsCount(PawnKindDef kind)
|
private int LiveSpawnedPawnsCount(PawnKindDef kind)
|
||||||
{
|
{
|
||||||
SpawnedPawns.RemoveAll(p => p == null || p.Destroyed);
|
var spawned = GetSafeSpawnedPawns();
|
||||||
return SpawnedPawns.Count(p => p.kindDef == kind);
|
return spawned.Count(p => p.kindDef == kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
private AcceptanceReport CanSpawnNow(PawnKindDef kind)
|
private AcceptanceReport CanSpawnNow(PawnKindDef kind)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
if (parent is Pawn pawn && (pawn.IsSelfShutdown() || !pawn.Awake() || pawn.Downed || pawn.Dead || !pawn.Spawned))
|
if (parent is Pawn pawn && (pawn.IsSelfShutdown() || !pawn.Awake() || pawn.Downed || pawn.Dead || !pawn.Spawned))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (CooldownTicksRemaining > 0)
|
if (CooldownTicksRemaining > 0)
|
||||||
return "CooldownTime".Translate() + " " + CooldownTicksRemaining.ToStringSecondsFromTicks();
|
return "CooldownTime".Translate() + " " + CooldownTicksRemaining.ToStringSecondsFromTicks();
|
||||||
|
|
||||||
@@ -304,10 +534,19 @@ namespace ArachnaeSwarm
|
|||||||
|
|
||||||
if (!AutoProps.freeProduction && InnerContainer.TotalStackCountOfDef(Props.fixedIngredient) < cost)
|
if (!AutoProps.freeProduction && InnerContainer.TotalStackCountOfDef(Props.fixedIngredient) < cost)
|
||||||
return "MechCarrierNotEnoughResources".Translate();
|
return "MechCarrierNotEnoughResources".Translate();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error($"CanSpawnNow检查失败: {ex.Message}");
|
||||||
|
return $"生产检查失败: {ex.Message}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void TrySpawnPawn(PawnKindDef kind)
|
private void TrySpawnPawn(PawnKindDef kind)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
PawnGenerationRequest request = new PawnGenerationRequest(kind, parent.Faction, PawnGenerationContext.NonPlayer, -1, forceGenerateNewPawn: true);
|
PawnGenerationRequest request = new PawnGenerationRequest(kind, parent.Faction, PawnGenerationContext.NonPlayer, -1, forceGenerateNewPawn: true);
|
||||||
Pawn pawn = PawnGenerator.GeneratePawn(request);
|
Pawn pawn = PawnGenerator.GeneratePawn(request);
|
||||||
@@ -316,7 +555,6 @@ namespace ArachnaeSwarm
|
|||||||
var producedComp = pawn.TryGetComp<CompProducedByMechCarrier>();
|
var producedComp = pawn.TryGetComp<CompProducedByMechCarrier>();
|
||||||
if (producedComp == null)
|
if (producedComp == null)
|
||||||
{
|
{
|
||||||
// 如果pawn没有这个Comp,添加它
|
|
||||||
var compProps = new CompProperties_ProducedByMechCarrier();
|
var compProps = new CompProperties_ProducedByMechCarrier();
|
||||||
compProps.compClass = typeof(CompProducedByMechCarrier);
|
compProps.compClass = typeof(CompProducedByMechCarrier);
|
||||||
var newComp = (CompProducedByMechCarrier)Activator.CreateInstance(compProps.compClass);
|
var newComp = (CompProducedByMechCarrier)Activator.CreateInstance(compProps.compClass);
|
||||||
@@ -326,10 +564,15 @@ namespace ArachnaeSwarm
|
|||||||
producedComp = newComp;
|
producedComp = newComp;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化生产者信息
|
// 初始化生产者信息 - 使用安全初始化
|
||||||
producedComp.Initialize(parent, this);
|
bool initSuccess = producedComp.InitializeSafe(parent, this);
|
||||||
|
if (!initSuccess)
|
||||||
|
{
|
||||||
|
Log.Error($"初始化CompProducedByMechCarrier失败");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 生成位置:使用自定义跟随位置(如果有效),否则使用父单位位置
|
// 生成位置
|
||||||
IntVec3 spawnPosition;
|
IntVec3 spawnPosition;
|
||||||
Map spawnMap;
|
Map spawnMap;
|
||||||
|
|
||||||
@@ -344,8 +587,20 @@ namespace ArachnaeSwarm
|
|||||||
spawnMap = parent.Map;
|
spawnMap = parent.Map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 验证生成位置
|
||||||
|
if (!spawnPosition.IsValid || !spawnPosition.InBounds(spawnMap))
|
||||||
|
{
|
||||||
|
spawnPosition = parent.Position;
|
||||||
|
spawnMap = parent.Map;
|
||||||
|
}
|
||||||
|
|
||||||
GenSpawn.Spawn(pawn, spawnPosition, spawnMap);
|
GenSpawn.Spawn(pawn, spawnPosition, spawnMap);
|
||||||
SpawnedPawns.Add(pawn);
|
|
||||||
|
// 添加到已生成列表
|
||||||
|
lock (validationLock)
|
||||||
|
{
|
||||||
|
SpawnedPawns?.Add(pawn);
|
||||||
|
}
|
||||||
|
|
||||||
if (parent is Pawn p && p.GetLord() != null)
|
if (parent is Pawn p && p.GetLord() != null)
|
||||||
p.GetLord().AddPawn(pawn);
|
p.GetLord().AddPawn(pawn);
|
||||||
@@ -373,38 +628,61 @@ namespace ArachnaeSwarm
|
|||||||
EffecterTrigger(Props.spawnedMechEffecter, Props.attachSpawnedMechEffecter, pawn);
|
EffecterTrigger(Props.spawnedMechEffecter, Props.attachSpawnedMechEffecter, pawn);
|
||||||
if (Props.spawnEffecter != null)
|
if (Props.spawnEffecter != null)
|
||||||
EffecterTrigger(Props.spawnEffecter, Props.attachSpawnedEffecter, parent);
|
EffecterTrigger(Props.spawnEffecter, Props.attachSpawnedEffecter, parent);
|
||||||
|
|
||||||
|
if (AutoProps.debugLogging)
|
||||||
|
Log.Message($"成功生成子单位: {pawn.LabelCap}");
|
||||||
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error($"生成Pawn失败: {ex.Message}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void EffecterTrigger(EffecterDef effecterDef, bool attach, Thing target)
|
private void EffecterTrigger(EffecterDef effecterDef, bool attach, Thing target)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
Effecter effecter = new Effecter(effecterDef);
|
Effecter effecter = new Effecter(effecterDef);
|
||||||
effecter.Trigger(attach ? ((TargetInfo)target) : new TargetInfo(target.Position, target.Map), TargetInfo.Invalid);
|
effecter.Trigger(attach ? ((TargetInfo)target) : new TargetInfo(target.Position, target.Map), TargetInfo.Invalid);
|
||||||
effecter.Cleanup();
|
effecter.Cleanup();
|
||||||
}
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error($"触发效果失败: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override void CompTick()
|
public override void CompTick()
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
base.CompTick();
|
base.CompTick();
|
||||||
|
|
||||||
|
// 清理无效的子单位引用
|
||||||
|
if (Find.TickManager.TicksGame - lastChildCleanupTick > CHILD_CLEANUP_INTERVAL)
|
||||||
|
{
|
||||||
|
CleanupInvalidChildren();
|
||||||
|
lastChildCleanupTick = Find.TickManager.TicksGame;
|
||||||
|
}
|
||||||
|
|
||||||
// 检查临时位置是否过期
|
// 检查临时位置是否过期
|
||||||
CheckTemporaryPositionExpiry();
|
CheckTemporaryPositionExpiry();
|
||||||
|
|
||||||
if (parent.IsHashIntervalTick(60)) // 每秒检查一次
|
if (parent.IsHashIntervalTick(60))
|
||||||
{
|
{
|
||||||
// 检查是否有抑制生产的Hediff
|
// 检查是否有抑制生产的Hediff
|
||||||
if (AutoProps.disableHediff != null && (parent as Pawn)?.health.hediffSet.HasHediff(AutoProps.disableHediff) == true)
|
if (AutoProps.disableHediff != null && (parent as Pawn)?.health.hediffSet.HasHediff(AutoProps.disableHediff) == true)
|
||||||
{
|
{
|
||||||
return; // 有Hediff,停止生产
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取当前生产队列
|
|
||||||
var productionQueue = GetProductionQueue();
|
var productionQueue = GetProductionQueue();
|
||||||
if (productionQueue == null || productionQueue.Count == 0)
|
if (productionQueue == null || productionQueue.Count == 0)
|
||||||
{
|
{
|
||||||
return; // 生产队列为空,不进行生产
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. 先检查是否满员
|
// 检查是否满员
|
||||||
bool isFull = true;
|
bool isFull = true;
|
||||||
foreach (var entry in productionQueue)
|
foreach (var entry in productionQueue)
|
||||||
{
|
{
|
||||||
@@ -417,13 +695,11 @@ namespace ArachnaeSwarm
|
|||||||
|
|
||||||
if (isFull)
|
if (isFull)
|
||||||
{
|
{
|
||||||
return; // 如果已满员,则不进行任何操作,包括冷却计时
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 如果未满员,才检查冷却时间
|
|
||||||
if (CooldownTicksRemaining > 0) return;
|
if (CooldownTicksRemaining > 0) return;
|
||||||
|
|
||||||
// 3. 寻找空位并生产
|
|
||||||
foreach (var entry in productionQueue)
|
foreach (var entry in productionQueue)
|
||||||
{
|
{
|
||||||
if (LiveSpawnedPawnsCount(entry.pawnKind) < entry.count)
|
if (LiveSpawnedPawnsCount(entry.pawnKind) < entry.count)
|
||||||
@@ -431,12 +707,17 @@ namespace ArachnaeSwarm
|
|||||||
if (CanSpawnNow(entry.pawnKind).Accepted)
|
if (CanSpawnNow(entry.pawnKind).Accepted)
|
||||||
{
|
{
|
||||||
TrySpawnPawn(entry.pawnKind);
|
TrySpawnPawn(entry.pawnKind);
|
||||||
break; // 每次只生产一个
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error($"CompAutoMechCarrier Tick错误: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 添加动态生产队列条目
|
/// 添加动态生产队列条目
|
||||||
@@ -523,21 +804,21 @@ namespace ArachnaeSwarm
|
|||||||
|
|
||||||
public override IEnumerable<Gizmo> CompGetGizmosExtra()
|
public override IEnumerable<Gizmo> CompGetGizmosExtra()
|
||||||
{
|
{
|
||||||
// 移除所有Gizmo逻辑
|
|
||||||
return Enumerable.Empty<Gizmo>();
|
return Enumerable.Empty<Gizmo>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string CompInspectStringExtra()
|
public override string CompInspectStringExtra()
|
||||||
{
|
{
|
||||||
SpawnedPawns.RemoveAll(p => p == null || p.Destroyed);
|
try
|
||||||
string text = "Pawns: " + SpawnedPawns.Count + " / " + TotalPawnCapacity;
|
{
|
||||||
|
var spawned = GetSafeSpawnedPawns();
|
||||||
|
string text = "Pawns: " + spawned.Count + " / " + TotalPawnCapacity;
|
||||||
|
|
||||||
var productionQueue = GetProductionQueue();
|
var productionQueue = GetProductionQueue();
|
||||||
foreach (var entry in productionQueue)
|
foreach (var entry in productionQueue)
|
||||||
{
|
{
|
||||||
text += $"\n- {entry.pawnKind.LabelCap}: {LiveSpawnedPawnsCount(entry.pawnKind)} / {entry.count}";
|
text += $"\n- {entry.pawnKind.LabelCap}: {LiveSpawnedPawnsCount(entry.pawnKind)} / {entry.count}";
|
||||||
|
|
||||||
// 显示自定义信息
|
|
||||||
if (entry.customInfo != null)
|
if (entry.customInfo != null)
|
||||||
{
|
{
|
||||||
text += $" ({entry.customInfo})";
|
text += $" ({entry.customInfo})";
|
||||||
@@ -554,7 +835,6 @@ namespace ArachnaeSwarm
|
|||||||
text += "\n" + base.CompInspectStringExtra();
|
text += "\n" + base.CompInspectStringExtra();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 显示自定义跟随位置信息
|
|
||||||
if (HasValidCustomFollowPosition)
|
if (HasValidCustomFollowPosition)
|
||||||
{
|
{
|
||||||
text += $"\nCustom Follow Position: {customFollowPosition.Value}";
|
text += $"\nCustom Follow Position: {customFollowPosition.Value}";
|
||||||
@@ -568,44 +848,59 @@ namespace ArachnaeSwarm
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 显示验证状态
|
||||||
|
if (AutoProps.debugLogging)
|
||||||
|
{
|
||||||
|
text += $"\n验证缓存: {validatedChildren.Count}";
|
||||||
|
text += $"\n死亡队列: {childDeathQueue.Count}";
|
||||||
|
}
|
||||||
|
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
return $"状态获取错误: {ex.Message}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取调试信息
|
/// 获取调试信息
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string GetDebugInfo()
|
public string GetDebugInfo()
|
||||||
{
|
{
|
||||||
var pawn = parent as Pawn;
|
|
||||||
System.Text.StringBuilder sb = new System.Text.StringBuilder();
|
System.Text.StringBuilder sb = new System.Text.StringBuilder();
|
||||||
sb.AppendLine("=== CompAutoMechCarrier 调试信息 ===");
|
sb.AppendLine("=== CompAutoMechCarrier 调试信息 ===");
|
||||||
|
|
||||||
// CompAutoMechCarrier基本信息
|
// 生产者信息
|
||||||
sb.AppendLine($"父单位: {parent?.LabelCap ?? "NULL"}");
|
sb.AppendLine($"生产者有效: {IsProducerValid}");
|
||||||
sb.AppendLine($"自定义跟随位置: {CustomFollowPosition}");
|
sb.AppendLine($"生产者存活: {IsProducerAlive}");
|
||||||
sb.AppendLine($"有效跟随位置: {GetEffectiveFollowPosition()}");
|
|
||||||
sb.AppendLine($"临时位置过期tick: {temporaryPositionExpiryTick}");
|
|
||||||
|
|
||||||
// 已生成的Pawn
|
// 已生成的Pawn
|
||||||
sb.AppendLine($"已生成Pawn数: {SpawnedPawns?.Count ?? 0}");
|
var spawned = GetSafeSpawnedPawns();
|
||||||
if (SpawnedPawns != null && SpawnedPawns.Count > 0)
|
sb.AppendLine($"已生成Pawn数: {spawned.Count}");
|
||||||
{
|
foreach (var spawnedPawn in spawned)
|
||||||
foreach (var spawnedPawn in SpawnedPawns)
|
|
||||||
{
|
{
|
||||||
if (spawnedPawn != null && !spawnedPawn.Destroyed)
|
if (spawnedPawn != null && !spawnedPawn.Destroyed)
|
||||||
{
|
{
|
||||||
sb.AppendLine($" • {spawnedPawn.LabelCap} ({spawnedPawn.kindDef?.label ?? "未知"})");
|
sb.AppendLine($" • {spawnedPawn.LabelCap} ({spawnedPawn.kindDef?.label ?? "未知"})");
|
||||||
}
|
|
||||||
|
// 检查双向验证
|
||||||
|
var childComp = spawnedPawn.TryGetComp<CompProducedByMechCarrier>();
|
||||||
|
bool valid = childComp != null && childComp.IsProducerValid;
|
||||||
|
sb.AppendLine($" 双向验证: {valid}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 冷却时间
|
// 验证缓存
|
||||||
sb.AppendLine($"冷却时间剩余: {CooldownTicksRemaining}");
|
sb.AppendLine($"验证缓存数: {validatedChildren.Count}");
|
||||||
|
sb.AppendLine($"死亡队列: {childDeathQueue.Count}");
|
||||||
|
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PostExposeData()
|
public override void PostExposeData()
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
base.PostExposeData();
|
base.PostExposeData();
|
||||||
|
|
||||||
@@ -613,12 +908,20 @@ namespace ArachnaeSwarm
|
|||||||
Scribe_Values.Look(ref customFollowPosition, "customFollowPosition");
|
Scribe_Values.Look(ref customFollowPosition, "customFollowPosition");
|
||||||
Scribe_Values.Look(ref temporaryPositionExpiryTick, "temporaryPositionExpiryTick", -1);
|
Scribe_Values.Look(ref temporaryPositionExpiryTick, "temporaryPositionExpiryTick", -1);
|
||||||
|
|
||||||
// 注意:Map不能直接序列化,需要在PostLoadInit中重新获取
|
// 保存验证缓存
|
||||||
|
Scribe_Collections.Look(ref validatedChildren, "validatedChildren", LookMode.Reference);
|
||||||
|
Scribe_Collections.Look(ref childDeathQueue, "childDeathQueue", LookMode.Reference);
|
||||||
|
|
||||||
if (Scribe.mode == LoadSaveMode.LoadingVars)
|
if (Scribe.mode == LoadSaveMode.LoadingVars)
|
||||||
{
|
{
|
||||||
customFollowPositionMap = parent?.Map;
|
customFollowPositionMap = parent?.Map;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error($"CompAutoMechCarrier 序列化错误: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override void PostSpawnSetup(bool respawningAfterLoad)
|
public override void PostSpawnSetup(bool respawningAfterLoad)
|
||||||
{
|
{
|
||||||
@@ -629,7 +932,6 @@ namespace ArachnaeSwarm
|
|||||||
{
|
{
|
||||||
customFollowPositionMap = parent?.Map;
|
customFollowPositionMap = parent?.Map;
|
||||||
|
|
||||||
// 如果自定义位置无效,清除它
|
|
||||||
if (customFollowPosition.HasValue &&
|
if (customFollowPosition.HasValue &&
|
||||||
(customFollowPositionMap == null ||
|
(customFollowPositionMap == null ||
|
||||||
!customFollowPosition.Value.InBounds(customFollowPositionMap)))
|
!customFollowPosition.Value.InBounds(customFollowPositionMap)))
|
||||||
@@ -640,6 +942,45 @@ namespace ArachnaeSwarm
|
|||||||
|
|
||||||
// 初始化生产队列缓存
|
// 初始化生产队列缓存
|
||||||
RefreshProductionQueue();
|
RefreshProductionQueue();
|
||||||
|
|
||||||
|
// 初始化验证系统
|
||||||
|
lock (validationLock)
|
||||||
|
{
|
||||||
|
if (validatedChildren == null)
|
||||||
|
validatedChildren = new HashSet<Thing>();
|
||||||
|
|
||||||
|
if (childDeathQueue == null)
|
||||||
|
childDeathQueue = new Queue<Thing>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PostDestroy(DestroyMode mode, Map previousMap)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 清理所有子单位引用
|
||||||
|
lock (validationLock)
|
||||||
|
{
|
||||||
|
var spawned = GetSafeSpawnedPawns();
|
||||||
|
foreach (var pawn in spawned)
|
||||||
|
{
|
||||||
|
var comp = pawn.TryGetComp<CompProducedByMechCarrier>();
|
||||||
|
if (comp != null)
|
||||||
|
{
|
||||||
|
comp.TryUpdateProducerStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
validatedChildren.Clear();
|
||||||
|
childDeathQueue.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error($"销毁时清理失败: {ex.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
base.PostDestroy(mode, previousMap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,15 +6,39 @@ using Verse.AI;
|
|||||||
|
|
||||||
namespace ArachnaeSwarm
|
namespace ArachnaeSwarm
|
||||||
{
|
{
|
||||||
public class CompProducedByMechCarrier : ThingComp
|
public class CompProducedByMechCarrier : ThingComp, IBidirectionalValidator
|
||||||
{
|
{
|
||||||
private Thing producer;
|
private Thing producer;
|
||||||
private CompAutoMechCarrier producerComp;
|
private CompAutoMechCarrier producerComp;
|
||||||
private int lastProducerCheckTick = -1;
|
private int lastProducerCheckTick = -1;
|
||||||
private const int PRODUCER_CHECK_INTERVAL = 60;
|
private const int PRODUCER_CHECK_INTERVAL = 60;
|
||||||
|
|
||||||
public Thing Producer => producer;
|
// 死亡检测相关
|
||||||
public CompAutoMechCarrier ProducerComp => producerComp;
|
private bool wasDead = false;
|
||||||
|
private int deathTick = -1;
|
||||||
|
private const int DEATH_CLEANUP_DELAY = 60; // 死后60tick进行清理
|
||||||
|
|
||||||
|
// 安全锁
|
||||||
|
private object validationLock = new object();
|
||||||
|
|
||||||
|
// === IBidirectionalValidator 实现 ===
|
||||||
|
public bool IsProducerValid
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
lock (validationLock)
|
||||||
|
{
|
||||||
|
if (producer == null || producer.Destroyed)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// 如果是pawn,检查是否死亡或倒下
|
||||||
|
if (producer is Pawn pawn && (pawn.Dead || pawn.Downed))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 公开属性,用于其他类访问
|
// 公开属性,用于其他类访问
|
||||||
public bool HasValidProducer
|
public bool HasValidProducer
|
||||||
@@ -32,12 +56,121 @@ namespace ArachnaeSwarm
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Thing GetProducer()
|
||||||
|
{
|
||||||
|
lock (validationLock)
|
||||||
|
{
|
||||||
|
return IsProducerValid ? producer : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsProducerAlive
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
lock (validationLock)
|
||||||
|
{
|
||||||
|
return IsProducerValid && producer.Spawned;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsChildValid(Thing child)
|
||||||
|
{
|
||||||
|
if (child == null || child.Destroyed)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// 检查是否为pawn
|
||||||
|
if (!(child is Pawn))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// 检查是否死亡
|
||||||
|
if (child is Pawn pawn && (pawn.Dead || pawn.Downed))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsChildAlive(Thing child)
|
||||||
|
{
|
||||||
|
return IsChildValid(child) && child.Spawned;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ValidateBidirectional(Thing child)
|
||||||
|
{
|
||||||
|
lock (validationLock)
|
||||||
|
{
|
||||||
|
// 验证子单位
|
||||||
|
if (!IsChildValid(child))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// 验证生产者
|
||||||
|
if (!IsProducerValid)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// 验证双向引用
|
||||||
|
return ValidateBidirectionalReference(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ValidateAndExecute(Thing child, System.Action action)
|
||||||
|
{
|
||||||
|
if (!ValidateBidirectional(child))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
action?.Invoke();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error($"双向验证执行失败: {ex.Message}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ValidateBidirectionalReference(Thing child)
|
||||||
|
{
|
||||||
|
// 检查生产者是否包含这个子单位
|
||||||
|
var producerCarrier = GetProducerComp();
|
||||||
|
if (producerCarrier == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// 验证生产者是否知道这个子单位
|
||||||
|
return producerCarrier.ValidateChildReference(child);
|
||||||
|
}
|
||||||
|
// === 接口实现结束 ===
|
||||||
|
|
||||||
|
public Thing Producer => GetProducer();
|
||||||
|
public CompAutoMechCarrier ProducerComp => GetProducerComp();
|
||||||
|
|
||||||
|
// 获取生产者组件的安全方法
|
||||||
|
private CompAutoMechCarrier GetProducerComp()
|
||||||
|
{
|
||||||
|
lock (validationLock)
|
||||||
|
{
|
||||||
|
if (!IsProducerValid)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// 如果组件为空,尝试获取
|
||||||
|
if (producerComp == null || producerComp.parent != producer)
|
||||||
|
{
|
||||||
|
producerComp = producer.TryGetComp<CompAutoMechCarrier>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return producerComp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 检查是否应该跟随生产者
|
// 检查是否应该跟随生产者
|
||||||
public bool ShouldFollowProducer
|
public bool ShouldFollowProducer
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (!HasValidProducer)
|
lock (validationLock)
|
||||||
|
{
|
||||||
|
if (!IsProducerValid)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// 确保pawn是有效的
|
// 确保pawn是有效的
|
||||||
@@ -49,16 +182,39 @@ namespace ArachnaeSwarm
|
|||||||
if (producer.Map != pawn.Map || !pawn.CanReach(producer, PathEndMode.OnCell, Danger.Deadly))
|
if (producer.Map != pawn.Map || !pawn.CanReach(producer, PathEndMode.OnCell, Danger.Deadly))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// 可以根据需要添加更多条件
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化方法 - 安全版本
|
||||||
|
public bool InitializeSafe(Thing producer, CompAutoMechCarrier producerComp)
|
||||||
|
{
|
||||||
|
lock (validationLock)
|
||||||
|
{
|
||||||
|
// 验证生产者
|
||||||
|
if (producer == null || producer.Destroyed)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// 验证生产者组件
|
||||||
|
if (producerComp == null || producerComp.parent != producer)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
this.producer = producer;
|
||||||
|
this.producerComp = producerComp;
|
||||||
|
|
||||||
|
// 记录初始化时间
|
||||||
|
lastProducerCheckTick = Find.TickManager.TicksGame;
|
||||||
|
|
||||||
|
Log.Message($"双向验证: {parent?.LabelCap} -> {producer.LabelCap} 初始化成功");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化方法
|
// 原始初始化方法(保持向后兼容)
|
||||||
public void Initialize(Thing producer, CompAutoMechCarrier producerComp)
|
public void Initialize(Thing producer, CompAutoMechCarrier producerComp)
|
||||||
{
|
{
|
||||||
this.producer = producer;
|
InitializeSafe(producer, producerComp);
|
||||||
this.producerComp = producerComp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 尝试更新生产者状态
|
// 尝试更新生产者状态
|
||||||
@@ -69,9 +225,61 @@ namespace ArachnaeSwarm
|
|||||||
|
|
||||||
lastProducerCheckTick = Find.TickManager.TicksGame;
|
lastProducerCheckTick = Find.TickManager.TicksGame;
|
||||||
|
|
||||||
|
lock (validationLock)
|
||||||
|
{
|
||||||
// 检查生产者是否仍然有效
|
// 检查生产者是否仍然有效
|
||||||
if (producer != null && (producer.Destroyed || (producer is Pawn p && (p.Dead || p.Downed))))
|
if (producer != null && (producer.Destroyed || (producer is Pawn p && (p.Dead || p.Downed))))
|
||||||
{
|
{
|
||||||
|
Log.Warning($"生产者无效: {producer?.LabelCap}, 清除引用");
|
||||||
|
producer = null;
|
||||||
|
producerComp = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查自身状态
|
||||||
|
CheckSelfStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查自身状态
|
||||||
|
private void CheckSelfStatus()
|
||||||
|
{
|
||||||
|
// 检查是否死亡
|
||||||
|
if (parent is Pawn pawn)
|
||||||
|
{
|
||||||
|
if (pawn.Dead)
|
||||||
|
{
|
||||||
|
if (!wasDead)
|
||||||
|
{
|
||||||
|
wasDead = true;
|
||||||
|
deathTick = Find.TickManager.TicksGame;
|
||||||
|
Log.Message($"子单位死亡: {pawn.LabelCap}, 准备通知生产者");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 死亡后延迟清理
|
||||||
|
if (deathTick >= 0 && Find.TickManager.TicksGame - deathTick >= DEATH_CLEANUP_DELAY)
|
||||||
|
{
|
||||||
|
NotifyProducerOfDeath();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wasDead = false;
|
||||||
|
deathTick = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通知生产者自己死亡
|
||||||
|
private void NotifyProducerOfDeath()
|
||||||
|
{
|
||||||
|
lock (validationLock)
|
||||||
|
{
|
||||||
|
if (producerComp != null && !producerComp.parent.Destroyed)
|
||||||
|
{
|
||||||
|
producerComp.NotifyChildDeath(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清除引用
|
||||||
producer = null;
|
producer = null;
|
||||||
producerComp = null;
|
producerComp = null;
|
||||||
}
|
}
|
||||||
@@ -80,16 +288,21 @@ namespace ArachnaeSwarm
|
|||||||
// 获取生产者的位置
|
// 获取生产者的位置
|
||||||
public IntVec3 GetProducerPosition()
|
public IntVec3 GetProducerPosition()
|
||||||
{
|
{
|
||||||
if (!HasValidProducer)
|
lock (validationLock)
|
||||||
|
{
|
||||||
|
if (!IsProducerValid)
|
||||||
return IntVec3.Invalid;
|
return IntVec3.Invalid;
|
||||||
|
|
||||||
return producer.Position;
|
return producer.Position;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 获取生产者的交互单元格
|
// 获取生产者的交互单元格
|
||||||
public IntVec3 GetProducerInteractionCell()
|
public IntVec3 GetProducerInteractionCell()
|
||||||
{
|
{
|
||||||
if (!HasValidProducer)
|
lock (validationLock)
|
||||||
|
{
|
||||||
|
if (!IsProducerValid)
|
||||||
return IntVec3.Invalid;
|
return IntVec3.Invalid;
|
||||||
|
|
||||||
if (producer is Building building)
|
if (producer is Building building)
|
||||||
@@ -97,21 +310,90 @@ namespace ArachnaeSwarm
|
|||||||
|
|
||||||
return producer.Position;
|
return producer.Position;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 检查是否在生产者附近
|
// 检查是否在生产者附近
|
||||||
public bool IsNearProducer(Pawn pawn, float radius)
|
public bool IsNearProducer(Pawn pawn, float radius)
|
||||||
{
|
{
|
||||||
if (!HasValidProducer)
|
lock (validationLock)
|
||||||
|
{
|
||||||
|
if (!IsProducerValid)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return pawn.Position.DistanceTo(producer.Position) <= radius;
|
return pawn.Position.DistanceTo(producer.Position) <= radius;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// === Tick 方法 ===
|
||||||
|
public override void CompTick()
|
||||||
|
{
|
||||||
|
base.CompTick();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 定期检查生产者状态
|
||||||
|
TryUpdateProducerStatus();
|
||||||
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error($"CompProducedByMechCarrier Tick错误: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void CompTickRare()
|
||||||
|
{
|
||||||
|
base.CompTickRare();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 定期清理
|
||||||
|
lock (validationLock)
|
||||||
|
{
|
||||||
|
if (producer != null && producer.Destroyed)
|
||||||
|
{
|
||||||
|
producer = null;
|
||||||
|
producerComp = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error($"CompProducedByMechCarrier TickRare错误: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override void PostExposeData()
|
public override void PostExposeData()
|
||||||
{
|
{
|
||||||
base.PostExposeData();
|
base.PostExposeData();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
Scribe_References.Look(ref producer, "producer");
|
Scribe_References.Look(ref producer, "producer");
|
||||||
Scribe_Values.Look(ref lastProducerCheckTick, "lastProducerCheckTick", -1);
|
Scribe_Values.Look(ref lastProducerCheckTick, "lastProducerCheckTick", -1);
|
||||||
|
Scribe_Values.Look(ref wasDead, "wasDead", false);
|
||||||
|
Scribe_Values.Look(ref deathTick, "deathTick", -1);
|
||||||
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error($"CompProducedByMechCarrier 序列化错误: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// === 调试方法 ===
|
||||||
|
public string GetValidationStatus()
|
||||||
|
{
|
||||||
|
lock (validationLock)
|
||||||
|
{
|
||||||
|
System.Text.StringBuilder sb = new System.Text.StringBuilder();
|
||||||
|
sb.AppendLine($"=== {parent?.LabelCap} 验证状态 ===");
|
||||||
|
sb.AppendLine($"生产者有效: {IsProducerValid}");
|
||||||
|
sb.AppendLine($"生产者存活: {IsProducerAlive}");
|
||||||
|
sb.AppendLine($"生产者: {producer?.LabelCap ?? "NULL"}");
|
||||||
|
sb.AppendLine($"生产者组件: {producerComp != null}");
|
||||||
|
sb.AppendLine($"上次检查: {lastProducerCheckTick}");
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
using RimWorld;
|
||||||
|
using UnityEngine;
|
||||||
|
using Verse;
|
||||||
|
|
||||||
|
namespace ArachnaeSwarm
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 双向安全校验接口
|
||||||
|
/// 确保生产者和子单位在操作前互相验证存在
|
||||||
|
/// </summary>
|
||||||
|
public interface IBidirectionalValidator
|
||||||
|
{
|
||||||
|
// 生产者相关
|
||||||
|
bool IsProducerValid { get; }
|
||||||
|
Thing GetProducer();
|
||||||
|
bool IsProducerAlive { get; }
|
||||||
|
|
||||||
|
// 子单位相关
|
||||||
|
bool IsChildValid(Thing child);
|
||||||
|
bool IsChildAlive(Thing child);
|
||||||
|
|
||||||
|
// 双向验证
|
||||||
|
bool ValidateBidirectional(Thing child);
|
||||||
|
bool ValidateAndExecute(Thing child, System.Action action);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user