This commit is contained in:
Tourswen
2025-12-16 23:54:36 +08:00
parent 18fd9ec2d9
commit 3706bdb241
37 changed files with 3212 additions and 407 deletions

View File

@@ -1,25 +1,81 @@
{
"Version": 1,
"WorkspaceRootPath": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\",
"WorkspaceRootPath": "E:\\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\\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|e:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\jobs\\jobdriver_stripchitin\\jobdriver_stripchitin.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:jobs\\jobdriver_stripchitin\\jobdriver_stripchitin.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\\compproperties_incubatordata.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:buildings\\building_ootheca\\compproperties_incubatordata.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|e:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\jobs\\jobdriver_stripchitin\\compproperties_chitinstripping.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:jobs\\jobdriver_stripchitin\\compproperties_chitinstripping.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_building_refuelingvat\\building_refuelingvat.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|e:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\jobs\\jobdriver_stripchitin\\comp_chitinstripping.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:jobs\\jobdriver_stripchitin\\comp_chitinstripping.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|e:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\verbs\\verb_shootselfunderfoot.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:verbs\\verb_shootselfunderfoot.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|e:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\hediffs\\ara_hediffcomp_topturret\\hediffcomp_topturret.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:hediffs\\ara_hediffcomp_topturret\\hediffcomp_topturret.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|e:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\roomrole\\roomroleworker_incubator.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:roomrole\\roomroleworker_incubator.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\building_comps\\ara_compinteractiveproducer\\compresearchproducer.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:building_comps\\ara_compinteractiveproducer\\compresearchproducer.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\ara_hediffdefof.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:ara_hediffdefof.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\building_comps\\ara_corpseconverter\\compcorpseconverter.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:building_comps\\ara_corpseconverter\\compcorpseconverter.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\building_comps\\ara_corpseconverter\\compproperties_corpseconverter.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:building_comps\\ara_corpseconverter\\compproperties_corpseconverter.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\building_comps\\ara_terrainchanger\\compterrainchanger.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:building_comps\\ara_terrainchanger\\compterrainchanger.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\building_comps\\ara_terrainchanger\\compproperties_terrainchanger.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:building_comps\\ara_terrainchanger\\compproperties_terrainchanger.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\building_comps\\wula_mutifuelspawner\\comprefuelablenutrition_withkey.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:building_comps\\wula_mutifuelspawner\\comprefuelablenutrition_withkey.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\building_comps\\ara_building_refuelingvat\\building_refuelingvat.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:building_comps\\ara_building_refuelingvat\\building_refuelingvat.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\\itab_ootheca_incubation.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:buildings\\building_ootheca\\itab_ootheca_incubation.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\buildings\\building_ootheca\\compproperties_incubatordata.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:buildings\\building_ootheca\\compproperties_incubatordata.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}",
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\buildings\\building_ootheca\\oothecaincubatorextension.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:buildings\\building_ootheca\\oothecaincubatorextension.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|E:\\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|E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\buildings\\building_equipmentootheca\\compproperties_equipmentincubatordata.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:buildings\\building_equipmentootheca\\compproperties_equipmentincubatordata.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|E:\\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}"
}
],
@@ -30,76 +86,245 @@
"DocumentGroups": [
{
"DockedWidth": 200,
"SelectedChildIndex": 0,
"SelectedChildIndex": 1,
"Children": [
{
"$type": "Document",
"DocumentIndex": 0,
"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": "AgIAAAAAAAAAAAAAAAAAABQAAAAMAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-12-16T08:35:48.647Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 1,
"Title": "CompProperties_IncubatorData.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\CompProperties_IncubatorData.cs",
"RelativeDocumentMoniker": "Buildings\\Building_Ootheca\\CompProperties_IncubatorData.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\CompProperties_IncubatorData.cs",
"RelativeToolTip": "Buildings\\Building_Ootheca\\CompProperties_IncubatorData.cs",
"ViewState": "AgIAAP4AAAAAAAAAAAAWwAIBAABCAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-12-16T08:17:40.867Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 3,
"Title": "ITab_Ootheca_Incubation.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\ITab_Ootheca_Incubation.cs",
"RelativeDocumentMoniker": "Buildings\\Building_Ootheca\\ITab_Ootheca_Incubation.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\ITab_Ootheca_Incubation.cs",
"RelativeToolTip": "Buildings\\Building_Ootheca\\ITab_Ootheca_Incubation.cs",
"ViewState": "AgIAAJUAAAAAAAAAAAAewMkAAAANAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-12-16T08:14:08.752Z",
"EditorCaption": ""
},
{
"$type": "Bookmark",
"Name": "ST:0:0:{1c4feeaa-4718-4aa9-859d-94ce25d182ba}"
},
{
"$type": "Document",
"DocumentIndex": 4,
"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": "AgIAALgCAAAAAAAAAAAAAMQCAAAjAAAAAAAAAA==",
"DocumentIndex": 0,
"Title": "JobDriver_StripChitin.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_StripChitin\\JobDriver_StripChitin.cs",
"RelativeDocumentMoniker": "Jobs\\JobDriver_StripChitin\\JobDriver_StripChitin.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_StripChitin\\JobDriver_StripChitin.cs*",
"RelativeToolTip": "Jobs\\JobDriver_StripChitin\\JobDriver_StripChitin.cs*",
"ViewState": "AgIAAFcAAAAAAAAAAAAAAHMAAAAoAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-12-16T08:13:46.678Z",
"WhenOpened": "2025-12-16T15:50:38.09Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 1,
"Title": "CompProperties_ChitinStripping.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_StripChitin\\CompProperties_ChitinStripping.cs",
"RelativeDocumentMoniker": "Jobs\\JobDriver_StripChitin\\CompProperties_ChitinStripping.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_StripChitin\\CompProperties_ChitinStripping.cs",
"RelativeToolTip": "Jobs\\JobDriver_StripChitin\\CompProperties_ChitinStripping.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAABcAAAAtAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-12-16T15:50:04.277Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 2,
"Title": "Building_RefuelingVat.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_Building_RefuelingVat\\Building_RefuelingVat.cs",
"RelativeDocumentMoniker": "Building_Comps\\ARA_Building_RefuelingVat\\Building_RefuelingVat.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_Building_RefuelingVat\\Building_RefuelingVat.cs",
"RelativeToolTip": "Building_Comps\\ARA_Building_RefuelingVat\\Building_RefuelingVat.cs",
"ViewState": "AgIAAP4AAAAAAAAAAAAewA8BAAAAAAAAAAAAAA==",
"Title": "Comp_ChitinStripping.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_StripChitin\\Comp_ChitinStripping.cs",
"RelativeDocumentMoniker": "Jobs\\JobDriver_StripChitin\\Comp_ChitinStripping.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_StripChitin\\Comp_ChitinStripping.cs",
"RelativeToolTip": "Jobs\\JobDriver_StripChitin\\Comp_ChitinStripping.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAoAAABWAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-12-16T04:00:20.165Z",
"WhenOpened": "2025-12-16T15:49:51.675Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 3,
"Title": "Verb_ShootSelfUnderfoot.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Verbs\\Verb_ShootSelfUnderfoot.cs",
"RelativeDocumentMoniker": "Verbs\\Verb_ShootSelfUnderfoot.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Verbs\\Verb_ShootSelfUnderfoot.cs",
"RelativeToolTip": "Verbs\\Verb_ShootSelfUnderfoot.cs",
"ViewState": "AgIAAJAAAAAAAAAAAAAcwJwAAAAoAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-12-16T15:07:22.127Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 4,
"Title": "HediffComp_TopTurret.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Hediffs\\ARA_HediffComp_TopTurret\\HediffComp_TopTurret.cs",
"RelativeDocumentMoniker": "Hediffs\\ARA_HediffComp_TopTurret\\HediffComp_TopTurret.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Hediffs\\ARA_HediffComp_TopTurret\\HediffComp_TopTurret.cs",
"RelativeToolTip": "Hediffs\\ARA_HediffComp_TopTurret\\HediffComp_TopTurret.cs",
"ViewState": "AgIAACcBAAAAAAAAAAAkwD0BAAAjAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-12-16T14:52:46.325Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 5,
"Title": "RoomRoleWorker_Incubator.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\RoomRole\\RoomRoleWorker_Incubator.cs",
"RelativeDocumentMoniker": "RoomRole\\RoomRoleWorker_Incubator.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\RoomRole\\RoomRoleWorker_Incubator.cs",
"RelativeToolTip": "RoomRole\\RoomRoleWorker_Incubator.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAABsAAAARAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-12-16T14:32:31.389Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 6,
"Title": "CompResearchProducer.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_CompInteractiveProducer\\CompResearchProducer.cs",
"RelativeDocumentMoniker": "Building_Comps\\ARA_CompInteractiveProducer\\CompResearchProducer.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_CompInteractiveProducer\\CompResearchProducer.cs",
"RelativeToolTip": "Building_Comps\\ARA_CompInteractiveProducer\\CompResearchProducer.cs",
"ViewState": "AgIAAAAAAAAAAAAAAADwvy0AAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-12-16T14:29:05.969Z"
},
{
"$type": "Document",
"DocumentIndex": 8,
"Title": "CompCorpseConverter.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_CorpseConverter\\CompCorpseConverter.cs",
"RelativeDocumentMoniker": "Building_Comps\\ARA_CorpseConverter\\CompCorpseConverter.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_CorpseConverter\\CompCorpseConverter.cs",
"RelativeToolTip": "Building_Comps\\ARA_CorpseConverter\\CompCorpseConverter.cs",
"ViewState": "AgIAABwDAAAAAAAAAAAIwCoDAAARAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-12-16T12:23:40.696Z"
},
{
"$type": "Document",
"DocumentIndex": 7,
"Title": "ARA_HediffDefOf.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\ARA_HediffDefOf.cs",
"RelativeDocumentMoniker": "ARA_HediffDefOf.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\ARA_HediffDefOf.cs",
"RelativeToolTip": "ARA_HediffDefOf.cs",
"ViewState": "AgIAAAAAAAAAAAAAAADwvy0AAAAJAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-12-15T17:32:18.493Z"
},
{
"$type": "Document",
"DocumentIndex": 9,
"Title": "CompProperties_CorpseConverter.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_CorpseConverter\\CompProperties_CorpseConverter.cs",
"RelativeDocumentMoniker": "Building_Comps\\ARA_CorpseConverter\\CompProperties_CorpseConverter.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_CorpseConverter\\CompProperties_CorpseConverter.cs",
"RelativeToolTip": "Building_Comps\\ARA_CorpseConverter\\CompProperties_CorpseConverter.cs",
"ViewState": "AgIAAAAAAAAAAAAAAADwvwAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-12-16T12:23:39.636Z"
},
{
"$type": "Document",
"DocumentIndex": 12,
"Title": "CompRefuelableNutrition_WithKey.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\WULA_MutiFuelSpawner\\CompRefuelableNutrition_WithKey.cs",
"RelativeDocumentMoniker": "Building_Comps\\WULA_MutiFuelSpawner\\CompRefuelableNutrition_WithKey.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\WULA_MutiFuelSpawner\\CompRefuelableNutrition_WithKey.cs",
"RelativeToolTip": "Building_Comps\\WULA_MutiFuelSpawner\\CompRefuelableNutrition_WithKey.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAACUAAABAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-12-16T10:38:33.135Z"
},
{
"$type": "Document",
"DocumentIndex": 10,
"Title": "CompTerrainChanger.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_TerrainChanger\\CompTerrainChanger.cs",
"RelativeDocumentMoniker": "Building_Comps\\ARA_TerrainChanger\\CompTerrainChanger.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_TerrainChanger\\CompTerrainChanger.cs",
"RelativeToolTip": "Building_Comps\\ARA_TerrainChanger\\CompTerrainChanger.cs",
"ViewState": "AgIAAK0CAAAAAAAAAAAcwPYCAAAMAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-12-16T10:30:16.921Z"
},
{
"$type": "Document",
"DocumentIndex": 11,
"Title": "CompProperties_TerrainChanger.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_TerrainChanger\\CompProperties_TerrainChanger.cs",
"RelativeDocumentMoniker": "Building_Comps\\ARA_TerrainChanger\\CompProperties_TerrainChanger.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_TerrainChanger\\CompProperties_TerrainChanger.cs",
"RelativeToolTip": "Building_Comps\\ARA_TerrainChanger\\CompProperties_TerrainChanger.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAUAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-12-16T10:30:15.601Z"
},
{
"$type": "Document",
"DocumentIndex": 13,
"Title": "Building_RefuelingVat.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_Building_RefuelingVat\\Building_RefuelingVat.cs",
"RelativeDocumentMoniker": "Building_Comps\\ARA_Building_RefuelingVat\\Building_RefuelingVat.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_Building_RefuelingVat\\Building_RefuelingVat.cs",
"RelativeToolTip": "Building_Comps\\ARA_Building_RefuelingVat\\Building_RefuelingVat.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-12-16T10:28:54.756Z"
},
{
"$type": "Document",
"DocumentIndex": 14,
"Title": "CompProperties_IncubatorData.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\CompProperties_IncubatorData.cs",
"RelativeDocumentMoniker": "Buildings\\Building_Ootheca\\CompProperties_IncubatorData.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\CompProperties_IncubatorData.cs",
"RelativeToolTip": "Buildings\\Building_Ootheca\\CompProperties_IncubatorData.cs",
"ViewState": "AgIAANcAAAAAAAAAAIA1wPoAAAAxAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-12-16T04:37:03.042Z"
},
{
"$type": "Document",
"DocumentIndex": 15,
"Title": "OothecaIncubatorExtension.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\OothecaIncubatorExtension.cs",
"RelativeDocumentMoniker": "Buildings\\Building_Ootheca\\OothecaIncubatorExtension.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\OothecaIncubatorExtension.cs",
"RelativeToolTip": "Buildings\\Building_Ootheca\\OothecaIncubatorExtension.cs",
"ViewState": "AgIAAAAAAAAAAAAAAADwvxUAAABBAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-12-16T04:36:59.394Z"
},
{
"$type": "Document",
"DocumentIndex": 18,
"Title": "Building_EquipmentOotheca.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_EquipmentOotheca\\Building_EquipmentOotheca.cs",
"RelativeDocumentMoniker": "Buildings\\Building_EquipmentOotheca\\Building_EquipmentOotheca.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_EquipmentOotheca\\Building_EquipmentOotheca.cs",
"RelativeToolTip": "Buildings\\Building_EquipmentOotheca\\Building_EquipmentOotheca.cs",
"ViewState": "AgIAACcAAAAAAAAAAAAAADoDAABSAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-12-15T18:22:14.171Z"
},
{
"$type": "Document",
"DocumentIndex": 16,
"Title": "Building_Ootheca.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\Building_Ootheca.cs",
"RelativeDocumentMoniker": "Buildings\\Building_Ootheca\\Building_Ootheca.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\Building_Ootheca.cs",
"RelativeToolTip": "Buildings\\Building_Ootheca\\Building_Ootheca.cs",
"ViewState": "AgIAALcCAAAAAAAAAAAewNgCAAAVAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-12-15T18:22:12.217Z"
},
{
"$type": "Document",
"DocumentIndex": 17,
"Title": "CompProperties_EquipmentIncubatorData.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_EquipmentOotheca\\CompProperties_EquipmentIncubatorData.cs",
"RelativeDocumentMoniker": "Buildings\\Building_EquipmentOotheca\\CompProperties_EquipmentIncubatorData.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_EquipmentOotheca\\CompProperties_EquipmentIncubatorData.cs",
"RelativeToolTip": "Buildings\\Building_EquipmentOotheca\\CompProperties_EquipmentIncubatorData.cs",
"ViewState": "AgIAAA4AAAAAAAAAAADwvyYAAAAaAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-12-15T17:55:40.041Z"
}
]
}

View File

@@ -37,4 +37,14 @@ namespace ArachnaeSwarm
DefOfHelper.EnsureInitializedInCtor(typeof(ARA_EffecterDefOf));
}
}
[DefOf]
public static class ARA_SoundDefOf
{
public static SoundDef AcidSpray_Resolve;
static ARA_SoundDefOf()
{
DefOfHelper.EnsureInitializedInCtor(typeof(ARA_SoundDefOf));
}
}
}

View File

@@ -124,13 +124,15 @@
<Compile Include="Buildings\Building_Ootheca\ITab_Ootheca_Incubation.cs" />
<Compile Include="Buildings\Building_Ootheca\JobDriver_OperateIncubator.cs" />
<Compile Include="Buildings\Building_Ootheca\OothecaIncubatorExtension.cs" />
<Compile Include="Buildings\Building_Ootheca\RoomRoleWorker_Incubator.cs" />
<Compile Include="RoomRole\RoomRoleWorker_Incubator.cs" />
<Compile Include="Buildings\Building_TurretGunHasSpeed.cs" />
<Compile Include="Building_Comps\ARA_Building_RefuelingVat\Building_RefuelingVat.cs" />
<Compile Include="Building_Comps\ARA_Building_RefuelingVat\CompProperties_RefuelingVat.cs" />
<Compile Include="Building_Comps\ARA_Building_RefuelingVat\CompRefuelingVat.cs" />
<Compile Include="Building_Comps\ARA_CompInteractiveProducer\CompResearchProducer.cs" />
<Compile Include="Building_Comps\ARA_CompInteractiveProducer\JobDriver_StartResearchProduction.cs" />
<Compile Include="Building_Comps\ARA_CorpseConverter\CompCorpseConverter.cs" />
<Compile Include="Building_Comps\ARA_CorpseConverter\CompProperties_CorpseConverter.cs" />
<Compile Include="Building_Comps\ARA_ForwardClearance\CompForwardClearance.cs" />
<Compile Include="Building_Comps\ARA_ForwardClearance\CompProperties_ForwardClearance.cs" />
<Compile Include="Building_Comps\ARA_ForwardClearance\PlaceWorker_ForwardClearance.cs" />
@@ -138,6 +140,8 @@
<Compile Include="Building_Comps\ARA_ProductStorage\CompProperties_ProductStorage.cs" />
<Compile Include="Building_Comps\ARA_SwarmMaintenance\CompProperties_SwarmMaintenance.cs" />
<Compile Include="Building_Comps\ARA_SwarmMaintenance\Comp_SwarmMaintenance.cs" />
<Compile Include="Building_Comps\ARA_TerrainChanger\CompProperties_TerrainChanger.cs" />
<Compile Include="Building_Comps\ARA_TerrainChanger\CompTerrainChanger.cs" />
<Compile Include="Building_Comps\CompBreakdownDisabler.cs" />
<Compile Include="EventSystem\CompOpenCustomUI.cs" />
<Compile Include="EventSystem\Condition.cs" />
@@ -195,6 +199,7 @@
<Compile Include="Storyteller\RaidWavePoolDef.cs" />
<Compile Include="Flyover\ThingclassFlyOver.cs" />
<Compile Include="Flyover\ARA_FlyOverDropPod\CompProperties_FlyOverDropPod.cs" />
<Compile Include="Verbs\Verb_ShootSelfUnderfoot.cs" />
<Compile Include="Verbs\Verb_ShootWithOffset.cs" />
<Compile Include="Abilities\ARA_ShowTemperatureRange\CompAbilityEffect_AbilityShowTemperatureRange.cs" />
<Compile Include="Abilities\ARA_ShowTemperatureRange\CompProperties_AbilityShowTemperatureRange.cs" />

View File

@@ -138,7 +138,7 @@ namespace ArachnaeSwarm
if (!Destroyed)
{
// 销毁建筑
Destroy(DestroyMode.Vanish);
Kill();
}
}
@@ -272,7 +272,7 @@ namespace ArachnaeSwarm
DamageInfo acidDamage = new DamageInfo(
acidDamageDef,
1000f, // 每次1.5点伤害
1.5f, // 每次1.5点伤害
2f, // 护甲穿透
-1f, // 随机角度
null, // 将建筑设为伤害来源

View File

@@ -0,0 +1,967 @@
// File: Comps/CompCorpseConverter.cs
using RimWorld;
using System.Collections.Generic;
using UnityEngine;
using Verse;
using Verse.Sound;
using System.Text;
using System.Linq;
using Verse.AI;
namespace ArachnaeSwarm
{
public class CompCorpseConverter : ThingComp
{
// 属性引用
private CompProperties_CorpseConverter Props => (CompProperties_CorpseConverter)props;
// 状态变量
private int ticksUntilNextConversion;
private bool isWorking = false;
private int workTicksRemaining = 0;
private Corpse targetCorpse = null;
private Effecter effecter;
private Effecter conversionEffecter;
// --- 新增:自动标记拆除状态 ---
private bool autoMarkForDeconstructionEnabled = true;
private int ticksUntilNextMarkDeconstruction;
private bool isMarking = false;
private int markingTicksRemaining = 0;
private Thing markingTargetBuilding = null;
private Effecter markingEffecter;
// 当前工作速度乘数
private float currentWorkSpeedMultiplier = 1.0f;
// 缓存燃料组件
private CompRefuelableNutrition_WithKey compRefuelable;
private bool refuelableComponentCached = false;
// 储存区图标缓存
private static readonly CachedTexture CreateCorpseStockpileIcon = new CachedTexture("UI/Icons/CorpseStockpileZone");
// 临时列表
private List<IntVec3> tmpRadialCells = new List<IntVec3>();
// 获取径向单元格(转换半径内)
private IEnumerable<IntVec3> RadialCells => GenRadial.RadialCellsAround(parent.Position, Props.conversionRadius, useCenter: true);
// 获取燃料组件
private CompRefuelableNutrition_WithKey CompRefuelable
{
get
{
if (!refuelableComponentCached)
{
compRefuelable = parent.TryGetComp<CompRefuelableNutrition_WithKey>();
refuelableComponentCached = true;
}
return compRefuelable;
}
}
// 检查燃料是否充足
private bool HasSufficientFuel
{
get
{
if (!Props.requiresFuel)
return true;
if (CompRefuelable == null)
return false;
return CompRefuelable.Fuel >= Props.minFuelToOperate;
}
}
// 检查是否有足够的燃料用于标记
private bool HasSufficientFuelForMarking
{
get
{
if (!Props.requiresFuel || Props.fuelConsumptionPerMark <= 0)
return true;
if (CompRefuelable == null)
return false;
return CompRefuelable.Fuel >= Props.fuelConsumptionPerMark;
}
}
// 消耗燃料
private bool ConsumeFuelIfNeeded()
{
if (!Props.requiresFuel || Props.fuelConsumptionPerConversion <= 0)
return true;
if (CompRefuelable == null)
return false;
if (CompRefuelable.Fuel >= Props.fuelConsumptionPerConversion)
{
CompRefuelable.ConsumeFuel(Props.fuelConsumptionPerConversion);
return true;
}
return false;
}
// 消耗标记燃料
private bool ConsumeMarkingFuelIfNeeded()
{
if (!Props.requiresFuel || Props.fuelConsumptionPerMark <= 0)
return true;
if (CompRefuelable == null)
return false;
if (CompRefuelable.Fuel >= Props.fuelConsumptionPerMark)
{
CompRefuelable.ConsumeFuel(Props.fuelConsumptionPerMark);
return true;
}
return false;
}
// 获取电源状态
private bool HasPower
{
get
{
if (!Props.requiresPower)
return true;
var compPower = parent.TryGetComp<CompPowerTrader>();
return compPower != null && compPower.PowerOn;
}
}
// 获取房间
private Room GetRoom()
{
var map = parent.Map;
if (map == null)
return null;
return parent.Position.GetRoom(map);
}
// 检查是否满足操作条件
private bool CanOperate()
{
// 检查是否有电
if (Props.requiresPower && !HasPower)
return false;
// 检查是否有足够的燃料
if (Props.requiresFuel && !HasSufficientFuel)
return false;
// 检查是否在房间内(如果需要)
if (Props.requiresRoom)
{
var room = GetRoom();
if (room == null || !room.ProperRoom)
return false;
// 检查房间评分
if (room.GetStat(RoomStatDefOf.Impressiveness) < Props.minRoomScore)
return false;
}
return true;
}
// 检查是否可以执行标记操作
private bool CanMarkForDeconstruction()
{
if (!Props.enableAutoMarkForDeconstruction || !autoMarkForDeconstructionEnabled)
return false;
if (!CanOperate())
return false;
// 检查标记燃料
if (Props.requiresFuel && !HasSufficientFuelForMarking)
return false;
return true;
}
// 获取有效的目标尸体
private bool TryGetValidTargetCorpse(out Corpse result)
{
result = null;
var map = parent.Map;
if (map == null)
return false;
// 获取搜索范围
var center = parent.Position;
int radius = Mathf.CeilToInt(Props.conversionRadius);
// 获取房间(如果需要)
Room parentRoom = null;
if (Props.requiresRoom)
{
parentRoom = GetRoom();
if (parentRoom == null)
return false;
}
// 收集所有候选尸体
List<Corpse> candidateCorpses = new List<Corpse>();
for (int x = -radius; x <= radius; x++)
{
for (int z = -radius; z <= radius; z++)
{
IntVec3 cell = new IntVec3(center.x + x, 0, center.z + z);
if (!cell.InBounds(map))
continue;
// 检查距离
float distance = cell.DistanceTo(center);
if (distance > Props.conversionRadius)
continue;
// 检查房间(如果需要)
if (Props.requiresRoom)
{
var cellRoom = cell.GetRoom(map);
if (cellRoom == null || cellRoom != parentRoom)
continue;
}
// 获取单元格中的所有东西
var things = cell.GetThingList(map);
foreach (var thing in things)
{
if (thing is Corpse corpse)
{
// 检查尸体是否腐烂
if (corpse.GetRotStage() == RotStage.Fresh || corpse.GetRotStage() == RotStage.Rotting)
{
// 检查是否在可接受尸体列表中(如果有定义)
if (Props.acceptedCorpseDefs != null && Props.acceptedCorpseDefs.Count > 0)
{
if (!Props.acceptedCorpseDefs.Contains(corpse.def))
continue;
}
// 排除机械族尸体(如果启用)
if (Props.excludeMechanoidCorpses)
{
var pawn = corpse.InnerPawn;
if (pawn != null && pawn.RaceProps.IsMechanoid)
{
continue; // 跳过机械族尸体
}
}
candidateCorpses.Add(corpse);
}
}
}
}
}
if (candidateCorpses.Count == 0)
return false;
// 选择最近的尸体
float closestDistance = float.MaxValue;
Corpse closestCorpse = null;
foreach (var corpse in candidateCorpses)
{
float distance = corpse.Position.DistanceTo(center);
if (distance < closestDistance)
{
closestDistance = distance;
closestCorpse = corpse;
}
}
if (closestCorpse != null)
{
result = closestCorpse;
return true;
}
return false;
}
// 获取有效的标记拆除建筑
private bool TryGetValidMarkingBuilding(out Thing result)
{
result = null;
var map = parent.Map;
if (map == null)
return false;
// 获取搜索范围
var center = parent.Position;
int radius = Mathf.CeilToInt(Props.markDeconstructionRadius);
// 获取房间(如果需要)
Room parentRoom = null;
if (Props.requiresRoom)
{
parentRoom = GetRoom();
if (parentRoom == null)
return false;
}
// 收集所有候选建筑
List<Thing> candidateBuildings = new List<Thing>();
for (int x = -radius; x <= radius; x++)
{
for (int z = -radius; z <= radius; z++)
{
IntVec3 cell = new IntVec3(center.x + x, 0, center.z + z);
if (!cell.InBounds(map))
continue;
// 检查距离
float distance = cell.DistanceTo(center);
if (distance > Props.markDeconstructionRadius)
continue;
// 检查房间(如果需要)
if (Props.requiresRoom)
{
var cellRoom = cell.GetRoom(map);
if (cellRoom == null || cellRoom != parentRoom)
continue;
}
// 获取单元格中的所有东西
var things = cell.GetThingList(map);
foreach (var thing in things)
{
// 检查是否是目标建筑
if (thing.def == Props.targetThingDef)
{
// 检查是否已经标记了拆除
if (map.designationManager.DesignationOn(thing, DesignationDefOf.Deconstruct) != null)
continue;
candidateBuildings.Add(thing);
}
}
}
}
if (candidateBuildings.Count == 0)
return false;
// 选择最近的建筑
float closestDistance = float.MaxValue;
Thing closestBuilding = null;
foreach (var building in candidateBuildings)
{
float distance = building.Position.DistanceTo(center);
if (distance < closestDistance)
{
closestDistance = distance;
closestBuilding = building;
}
}
if (closestBuilding != null)
{
result = closestBuilding;
return true;
}
return false;
}
// 开始工作
private void StartWorking(Corpse targetCorpse)
{
this.targetCorpse = targetCorpse;
this.isWorking = true;
// 计算工作时间(基于距离)
float distance = targetCorpse.Position.DistanceTo(parent.Position);
float workTimeFactor = 1f + (distance / Props.conversionRadius) * 0.5f; // 距离越远,时间越长
int baseWorkTime = Mathf.RoundToInt(Props.conversionInterval * 0.1f); // 工作时间为间隔的10%
workTicksRemaining = Mathf.RoundToInt(baseWorkTime * workTimeFactor / currentWorkSpeedMultiplier);
// 播放声音
if (Props.workingSound != null)
{
Props.workingSound.PlayOneShot(new TargetInfo(parent.Position, parent.Map));
}
// 启动工作效果器
if (Props.showVisualEffects && Props.workingEffecter != null)
{
effecter = Props.workingEffecter.Spawn();
effecter.Trigger(parent, targetCorpse);
}
// 启动转换效果器(在尸体上)
if (Props.showVisualEffects && Props.conversionEffecter != null)
{
conversionEffecter = Props.conversionEffecter.Spawn();
conversionEffecter.Trigger(targetCorpse, parent);
}
}
// 开始标记工作
private void StartMarking(Thing targetBuilding)
{
this.markingTargetBuilding = targetBuilding;
this.isMarking = true;
// 计算标记时间(基于距离)
float distance = targetBuilding.Position.DistanceTo(parent.Position);
float workTimeFactor = 1f + (distance / Props.markDeconstructionRadius) * 0.5f;
int baseMarkTime = Mathf.RoundToInt(Props.markDeconstructionInterval * 0.05f); // 标记时间为间隔的5%
markingTicksRemaining = Mathf.RoundToInt(baseMarkTime * workTimeFactor / currentWorkSpeedMultiplier);
// 播放标记声音
if (Props.markSound != null)
{
Props.markSound.PlayOneShot(new TargetInfo(parent.Position, parent.Map));
}
}
// 完成转换
private void CompleteConversion()
{
var map = parent.Map;
if (map == null || targetCorpse == null || targetCorpse.Destroyed || !targetCorpse.Spawned)
{
ResetWorkState();
return;
}
// 检查并消耗燃料
if (!ConsumeFuelIfNeeded())
{
Messages.Message("ARA_CorpseConverter.InsufficientFuel".Translate(),
new TargetInfo(targetCorpse.Position, map), MessageTypeDefOf.NegativeEvent);
ResetWorkState();
return;
}
// 记录尸体的位置和信息
IntVec3 corpsePosition = targetCorpse.Position;
string corpseName = targetCorpse.InnerPawn?.LabelShort ?? "unknown";
// 移除尸体
targetCorpse.Destroy(DestroyMode.Vanish);
// 播放转换声音
if (Props.conversionSound != null)
{
Props.conversionSound.PlayOneShot(new TargetInfo(corpsePosition, map));
}
// 生成目标建筑
if (Props.targetThingDef != null)
{
Thing convertedThing = ThingMaker.MakeThing(Props.targetThingDef);
// 检查是否是建筑
if (convertedThing.def.category == ThingCategory.Building)
{
// 尝试在尸体原位置生成
GenSpawn.Spawn(convertedThing, corpsePosition, map);
// 播放完成声音
if (Props.completionSound != null)
{
Props.completionSound.PlayOneShot(new TargetInfo(corpsePosition, map));
}
// 显示消息(仅开发模式)
if (Prefs.DevMode)
{
Log.Message($"[CorpseConverter] Converted {corpseName} at {corpsePosition} to {convertedThing.LabelCap}");
}
}
else
{
Log.Error($"CorpseConverter: targetThingDef {Props.targetThingDef.defName} is not a building! Cannot spawn.");
}
}
ResetWorkState();
}
// 完成标记工作
private void CompleteMarking()
{
var map = parent.Map;
if (map == null || markingTargetBuilding == null || markingTargetBuilding.Destroyed || !markingTargetBuilding.Spawned)
{
ResetMarkingState();
return;
}
// 检查并消耗标记燃料
if (!ConsumeMarkingFuelIfNeeded())
{
Messages.Message("ARA_CorpseConverter.InsufficientFuelForMarking".Translate(),
new TargetInfo(markingTargetBuilding.Position, map), MessageTypeDefOf.NegativeEvent);
ResetMarkingState();
return;
}
// 确保是目标建筑(安全检查)
if (markingTargetBuilding.def != Props.targetThingDef)
{
ResetMarkingState();
return;
}
// 添加拆除标记
map.designationManager.AddDesignation(new Designation(markingTargetBuilding, DesignationDefOf.Deconstruct));
// 播放完成声音
if (Props.markCompleteSound != null)
{
Props.markCompleteSound.PlayOneShot(new TargetInfo(markingTargetBuilding.Position, map));
}
// 显示消息(仅开发模式)
if (Prefs.DevMode)
{
Log.Message($"[CorpseConverter] Marked building at {markingTargetBuilding.Position} ({markingTargetBuilding.LabelCap}) for deconstruction");
}
ResetMarkingState();
}
// 重置工作状态
private void ResetWorkState()
{
isWorking = false;
workTicksRemaining = 0;
targetCorpse = null;
// 清理效果器
if (effecter != null)
{
effecter.Cleanup();
effecter = null;
}
if (conversionEffecter != null)
{
conversionEffecter.Cleanup();
conversionEffecter = null;
}
}
// 重置标记状态
private void ResetMarkingState()
{
isMarking = false;
markingTicksRemaining = 0;
markingTargetBuilding = null;
// 清理标记效果器
if (markingEffecter != null)
{
markingEffecter.Cleanup();
markingEffecter = null;
}
}
// 更新工作速度
private void UpdateWorkSpeed()
{
float multiplier = 1.0f;
if (Props.requiresPower && HasPower)
{
multiplier *= Props.poweredWorkSpeedMultiplier;
}
if (Props.requiresFuel && HasSufficientFuel)
{
// 燃料充足时可能有额外的速度加成
}
currentWorkSpeedMultiplier = multiplier;
}
// Tick更新
public override void CompTick()
{
base.CompTick();
// --- 处理尸体转换 ---
if (isWorking)
{
workTicksRemaining--;
if (workTicksRemaining <= 0)
{
CompleteConversion();
}
// 更新效果器
if (effecter != null)
{
effecter.EffectTick(parent, targetCorpse);
}
if (conversionEffecter != null)
{
conversionEffecter.EffectTick(targetCorpse, parent);
}
}
else if (CanOperate())
{
UpdateWorkSpeed();
// 等待下一次转换
if (ticksUntilNextConversion <= 0)
{
// 尝试找到有效目标尸体
if (TryGetValidTargetCorpse(out Corpse target))
{
// 在开始工作前再次检查燃料
if (Props.requiresFuel && Props.fuelConsumptionPerConversion > 0)
{
if (CompRefuelable == null || CompRefuelable.Fuel < Props.fuelConsumptionPerConversion)
{
// 燃料不足,重置计时器但跳过这次工作
ticksUntilNextConversion = Mathf.RoundToInt(Props.conversionInterval / currentWorkSpeedMultiplier);
return;
}
}
StartWorking(target);
}
// 重置计时器,无论是否成功找到目标
ticksUntilNextConversion = Mathf.RoundToInt(Props.conversionInterval / currentWorkSpeedMultiplier);
}
else
{
ticksUntilNextConversion--;
}
}
else
{
if (isWorking)
{
ResetWorkState();
}
}
// --- 处理自动标记拆除 ---
if (isMarking)
{
markingTicksRemaining--;
if (markingTicksRemaining <= 0)
{
CompleteMarking();
}
// 更新标记效果器
if (markingEffecter != null)
{
markingEffecter.EffectTick(parent, markingTargetBuilding);
}
}
else if (CanMarkForDeconstruction())
{
// 等待下一次标记
if (ticksUntilNextMarkDeconstruction <= 0)
{
// 尝试找到有效标记建筑
if (TryGetValidMarkingBuilding(out Thing target))
{
// 在开始标记前再次检查燃料
if (Props.requiresFuel && Props.fuelConsumptionPerMark > 0)
{
if (CompRefuelable == null || CompRefuelable.Fuel < Props.fuelConsumptionPerMark)
{
// 燃料不足,重置计时器但跳过这次标记
ticksUntilNextMarkDeconstruction = Mathf.RoundToInt(Props.markDeconstructionInterval / currentWorkSpeedMultiplier);
return;
}
}
StartMarking(target);
}
// 重置计时器,无论是否成功找到目标
ticksUntilNextMarkDeconstruction = Mathf.RoundToInt(Props.markDeconstructionInterval / currentWorkSpeedMultiplier);
}
else
{
ticksUntilNextMarkDeconstruction--;
}
}
}
// 防止长时间不工作
public override void CompTickRare()
{
base.CompTickRare();
if (!isWorking && ticksUntilNextConversion > Props.conversionInterval * 10)
{
// 防止计时器溢出
ticksUntilNextConversion = Props.conversionInterval;
}
if (!isMarking && ticksUntilNextMarkDeconstruction > Props.markDeconstructionInterval * 10)
{
// 防止标记计时器溢出
ticksUntilNextMarkDeconstruction = Props.markDeconstructionInterval;
}
}
// 保存/加载
public override void PostExposeData()
{
base.PostExposeData();
// 尸体转换状态
Scribe_Values.Look(ref ticksUntilNextConversion, "ticksUntilNextConversion", Props.conversionInterval);
Scribe_Values.Look(ref isWorking, "isWorking", false);
Scribe_Values.Look(ref workTicksRemaining, "workTicksRemaining", 0);
// 标记拆除状态
Scribe_Values.Look(ref autoMarkForDeconstructionEnabled, "autoMarkForDeconstructionEnabled", true);
Scribe_Values.Look(ref ticksUntilNextMarkDeconstruction, "ticksUntilNextMarkDeconstruction", Props.markDeconstructionInterval);
Scribe_Values.Look(ref isMarking, "isMarking", false);
Scribe_Values.Look(ref markingTicksRemaining, "markingTicksRemaining", 0);
// 保存目标尸体的引用
if (Scribe.mode == LoadSaveMode.Saving)
{
bool hasTarget = targetCorpse != null && targetCorpse.Spawned;
Scribe_Values.Look(ref hasTarget, "hasTarget", false);
if (hasTarget)
{
Scribe_References.Look(ref targetCorpse, "targetCorpse");
}
bool hasMarkingTarget = markingTargetBuilding != null && markingTargetBuilding.Spawned;
Scribe_Values.Look(ref hasMarkingTarget, "hasMarkingTarget", false);
if (hasMarkingTarget)
{
Scribe_References.Look(ref markingTargetBuilding, "markingTargetBuilding");
}
}
else if (Scribe.mode == LoadSaveMode.LoadingVars)
{
bool hasTarget = false;
Scribe_Values.Look(ref hasTarget, "hasTarget", false);
if (hasTarget)
{
Scribe_References.Look(ref targetCorpse, "targetCorpse");
}
bool hasMarkingTarget = false;
Scribe_Values.Look(ref hasMarkingTarget, "hasMarkingTarget", false);
if (hasMarkingTarget)
{
Scribe_References.Look(ref markingTargetBuilding, "markingTargetBuilding");
}
}
}
// 检查字符串
public override string CompInspectStringExtra()
{
var builder = new StringBuilder();
if (Props.requiresPower)
{
builder.AppendLine("ARA_CorpseConverter.Power".Translate(HasPower ? "On".Translate() : "Off".Translate()));
}
if (Props.requiresFuel)
{
if (CompRefuelable != null)
{
builder.AppendLine("ARA_CorpseConverter.Fuel".Translate(
CompRefuelable.Fuel.ToString("F1"),
CompRefuelable.TargetFuelLevel.ToString("F1")));
if (Props.fuelConsumptionPerConversion > 0)
{
builder.AppendLine("ARA_CorpseConverter.FuelPerConversion".Translate(
Props.fuelConsumptionPerConversion.ToString("F1")));
}
}
else
{
builder.AppendLine("ARA_CorpseConverter.NoFuelComponent".Translate());
}
}
if (isWorking)
{
float progressPercent = 1f - ((float)workTicksRemaining / (Props.conversionInterval * 0.1f));
builder.AppendLine("ARA_CorpseConverter.WorkingProgress".Translate(
progressPercent.ToStringPercent()));
if (targetCorpse != null)
{
builder.AppendLine("ARA_CorpseConverter.TargetCorpse".Translate(
targetCorpse.InnerPawn?.LabelShort ?? "unknown"));
}
}
else
{
float daysUntilConversion = ticksUntilNextConversion / 60000f;
builder.AppendLine("ARA_CorpseConverter.NextConversion".Translate(
daysUntilConversion.ToString("F1")));
}
builder.AppendLine("ARA_CorpseConverter.ConversionRadius".Translate(
Props.conversionRadius.ToString("F1")));
return builder.ToString().TrimEndNewlines();
}
// 切换自动标记功能
private void ToggleAutoMarking()
{
autoMarkForDeconstructionEnabled = !autoMarkForDeconstructionEnabled;
if (autoMarkForDeconstructionEnabled)
{
Messages.Message("ARA_CorpseConverter.AutoMarkEnabled".Translate(),
parent, MessageTypeDefOf.PositiveEvent);
}
else
{
Messages.Message("ARA_CorpseConverter.AutoMarkDisabled".Translate(),
parent, MessageTypeDefOf.NeutralEvent);
}
}
// 获取Gizmos
public override IEnumerable<Gizmo> CompGetGizmosExtra()
{
foreach (var gizmo in base.CompGetGizmosExtra())
{
yield return gizmo;
}
// 自动标记拆除切换按钮
if (Props.enableAutoMarkForDeconstruction && parent.Faction == Faction.OfPlayer)
{
yield return new Command_Toggle
{
defaultLabel = "ARA_CorpseConverter.ToggleAutoMark".Translate(),
defaultDesc = "ARA_CorpseConverter.ToggleAutoMarkDesc".Translate(),
icon = ContentFinder<Texture2D>.Get("UI/Designators/Deconstruct", false) ?? BaseContent.BadTex,
isActive = () => autoMarkForDeconstructionEnabled,
toggleAction = ToggleAutoMarking,
hotKey = KeyBindingDefOf.Misc4
};
}
// 开发模式下的调试命令
if (DebugSettings.ShowDevGizmos)
{
yield return new Command_Action
{
defaultLabel = "DEV: Test Conversion",
action = delegate
{
if (TryGetValidTargetCorpse(out Corpse corpse))
{
StartWorking(corpse);
}
else
{
Messages.Message("No valid corpses found", parent, MessageTypeDefOf.RejectInput);
}
}
};
yield return new Command_Action
{
defaultLabel = "DEV: Reset Timer",
action = delegate
{
ticksUntilNextConversion = 0;
}
};
yield return new Command_Action
{
defaultLabel = "DEV: Test Marking",
action = delegate
{
if (TryGetValidMarkingBuilding(out Thing building))
{
StartMarking(building);
}
else
{
Messages.Message("No valid buildings found", parent, MessageTypeDefOf.RejectInput);
}
}
};
}
}
// 绘制选择时的额外效果
public override void PostDrawExtraSelectionOverlays()
{
base.PostDrawExtraSelectionOverlays();
if (Props.showRadius)
{
// 显示转换半径
GenDraw.DrawRadiusRing(parent.Position, Props.conversionRadius, Color.red);
// 显示标记半径(如果不同)
if (Props.markDeconstructionRadius != Props.conversionRadius)
{
GenDraw.DrawRadiusRing(parent.Position, Props.markDeconstructionRadius, new Color(1f, 0.5f, 0f, 0.5f));
}
}
}
// 建筑被销毁时清理
public override void PostDestroy(DestroyMode mode, Map previousMap)
{
base.PostDestroy(mode, previousMap);
ResetWorkState();
ResetMarkingState();
}
// 建筑生成时初始化
public override void PostSpawnSetup(bool respawningAfterLoad)
{
base.PostSpawnSetup(respawningAfterLoad);
refuelableComponentCached = false; // 重置缓存,重新获取组件
// 初始化计时器
if (!respawningAfterLoad)
{
ticksUntilNextConversion = Props.conversionInterval;
autoMarkForDeconstructionEnabled = Props.enableAutoMarkForDeconstruction;
ticksUntilNextMarkDeconstruction = Props.markDeconstructionInterval;
}
}
}
}

View File

@@ -0,0 +1,125 @@
// File: Comps/CompProperties_CorpseConverter.cs
using RimWorld;
using System.Collections.Generic;
using UnityEngine;
using Verse;
namespace ArachnaeSwarm
{
public class CompProperties_CorpseConverter : CompProperties
{
// 转换目标物品定义(必须是建筑)
public ThingDef targetThingDef;
// 转换数量(每次转换生成多少个目标物品)
public int targetThingCount = 1;
// 基础转换间隔(游戏刻)
public int conversionInterval = 60000; // 默认1天
// 转换半径(以单元格为单位)
public float conversionRadius = 8f;
// 是否需要电源
public bool requiresPower = false;
// 电源开启时的工作速度乘数
public float poweredWorkSpeedMultiplier = 1.5f;
// 需要燃料
public bool requiresFuel = false;
// 每次转换消耗的燃料量
public float fuelConsumptionPerConversion = 5f;
// 最小燃料量要求(低于此值不工作)
public float minFuelToOperate = 0.1f;
// 接受哪些种类的尸体(可选,如果为空则接受所有尸体)
public List<ThingDef> acceptedCorpseDefs;
// 是否显示视觉效果
public bool showVisualEffects = true;
// 工作时的效果器
public EffecterDef workingEffecter;
// 转换时的效果器
public EffecterDef conversionEffecter;
// 工作时的声音
public SoundDef workingSound;
// 转换时的声音
public SoundDef conversionSound;
// 转换完成时的声音
public SoundDef completionSound;
// 是否需要房间
public bool requiresRoom = false;
// 需要的最低房间评分(可选)
public float minRoomScore = -9999f;
// 是否显示转换进度
public bool showProgress = true;
// 是否显示转换半径
public bool showRadius = true;
// --- 新增:自动标记拆除功能 ---
// 是否启用自动标记拆除功能
public bool enableAutoMarkForDeconstruction = true;
// 自动标记拆除间隔(游戏刻)
public int markDeconstructionInterval = 120000; // 默认2天
// 标记拆除半径(可以不同于转换半径)
public float markDeconstructionRadius = 8f;
// 每次标记消耗的燃料量(可选)
public float fuelConsumptionPerMark = 2f;
// 标记效果器
public EffecterDef markEffecter;
// 标记时的声音
public SoundDef markSound;
// 标记完成时的声音
public SoundDef markCompleteSound;
// 是否排除机械族尸体
public bool excludeMechanoidCorpses = true;
public CompProperties_CorpseConverter()
{
compClass = typeof(CompCorpseConverter);
}
// 验证配置
public override void ResolveReferences(ThingDef parentDef)
{
base.ResolveReferences(parentDef);
if (targetThingDef == null)
{
Log.Warning($"CompProperties_CorpseConverter on {parentDef.defName} has no targetThingDef specified!");
}
// 检查目标物品是否是建筑
if (targetThingDef != null && targetThingDef.category != ThingCategory.Building)
{
Log.Warning($"CompProperties_CorpseConverter on {parentDef.defName}: targetThingDef {targetThingDef.defName} is not a building, but auto-deconstruction requires a building!");
}
// 如果未指定标记半径,使用转换半径
if (markDeconstructionRadius <= 0)
{
markDeconstructionRadius = conversionRadius;
}
}
}
}

View File

@@ -323,8 +323,8 @@ namespace ArachnaeSwarm
// 只在玩家控制下显示
if (parent.Faction?.IsPlayer == true)
{
// 调试按钮:手动触发寻找维护者
if (Prefs.DevMode)
// 调试按钮:手动触发寻找维护者 - 仅在GodMode下显示
if (DebugSettings.godMode)
{
yield return new Command_Action
{

View File

@@ -0,0 +1,104 @@
// File: Comps/CompProperties_TerrainChanger.cs
using RimWorld;
using System.Collections.Generic;
using UnityEngine;
using Verse;
namespace ArachnaeSwarm
{
public class CompProperties_TerrainChanger : CompProperties
{
// 目标地形定义
public TerrainDef targetTerrain;
// 基础改变间隔(游戏刻)
public int baseChangeInterval = 60000; // 默认1天
// 改变半径(以单元格为单位)
public float changeRadius = 5f;
// 是否只在房间内改变
public bool onlyInSameRoom = true;
// 是否优先改变最近的地形
public bool prioritizeClosest = true;
// 需要的最低房间评分(可选)
public float minRoomScore = -9999f;
// 是否需要电源
public bool requiresPower = false;
// 电源开启时的工作速度乘数
public float poweredWorkSpeedMultiplier = 2f;
// 需要燃料
public bool requiresFuel = false;
// 每次地形改变消耗的燃料量
public float fuelConsumptionPerChange = 1f;
// 最小燃料量要求(低于此值不工作)
public float minFuelToOperate = 0.1f;
// 可接受的地形类型列表(可选,如果为空则接受所有可通行地形)
public List<TerrainDef> acceptedTerrains;
// 是否显示视觉效果
public bool showVisualEffects = true;
// 效果器定义
public EffecterDef workingEffecter;
// 工作时的声音
public SoundDef workingSound;
// 完成时的声音
public SoundDef completionSound;
// --- 新增:自动标记拆除功能 ---
// 是否启用自动标记拆除功能
public bool enableAutoMarkForRemoval = true;
// 自动标记拆除间隔(游戏刻)
public int markRemovalInterval = 120000; // 默认2天
// 标记拆除半径(可以不同于改变半径)
public float markRemovalRadius = 5f;
// 每次标记消耗的燃料量(可选)
public float fuelConsumptionPerMark = 0.5f;
// 标记效果器
public EffecterDef markEffecter;
// 标记时的声音
public SoundDef markSound;
// 标记完成时的声音
public SoundDef markCompleteSound;
public CompProperties_TerrainChanger()
{
compClass = typeof(CompTerrainChanger);
}
// 验证配置
public override void ResolveReferences(ThingDef parentDef)
{
base.ResolveReferences(parentDef);
if (targetTerrain == null)
{
Log.Warning($"CompProperties_TerrainChanger on {parentDef.defName} has no targetTerrain specified!");
}
// 如果未指定标记半径,使用改变半径
if (markRemovalRadius <= 0)
{
markRemovalRadius = changeRadius;
}
}
}
}

View File

@@ -0,0 +1,825 @@
// File: Comps/CompTerrainChanger.cs
using RimWorld;
using System.Collections.Generic;
using UnityEngine;
using Verse;
using Verse.Sound;
using System.Text;
using Verse.AI;
namespace ArachnaeSwarm
{
public class CompTerrainChanger : ThingComp
{
// 属性引用
private CompProperties_TerrainChanger Props => (CompProperties_TerrainChanger)props;
// 状态变量
private int ticksUntilNextChange;
private bool isWorking = false;
private int workTicksRemaining = 0;
private IntVec3 targetCell = IntVec3.Invalid;
private Effecter effecter;
// --- 新增:自动标记拆除状态 ---
private bool autoMarkForRemovalEnabled = true;
private int ticksUntilNextMarkRemoval;
private bool isMarking = false;
private int markingTicksRemaining = 0;
private IntVec3 markingTargetCell = IntVec3.Invalid;
private Effecter markingEffecter;
// 当前工作速度乘数
private float currentWorkSpeedMultiplier = 1.0f;
// 缓存燃料组件
private CompRefuelableNutrition_WithKey compRefuelable;
private bool refuelableComponentCached = false;
// 获取燃料组件
private CompRefuelableNutrition_WithKey CompRefuelable
{
get
{
if (!refuelableComponentCached)
{
compRefuelable = parent.TryGetComp<CompRefuelableNutrition_WithKey>();
refuelableComponentCached = true;
}
return compRefuelable;
}
}
// 检查燃料是否充足
private bool HasSufficientFuel
{
get
{
if (!Props.requiresFuel)
return true;
if (CompRefuelable == null)
return false;
return CompRefuelable.Fuel >= Props.minFuelToOperate;
}
}
// 检查是否有足够的燃料用于标记
private bool HasSufficientFuelForMarking
{
get
{
if (!Props.requiresFuel || Props.fuelConsumptionPerMark <= 0)
return true;
if (CompRefuelable == null)
return false;
return CompRefuelable.Fuel >= Props.fuelConsumptionPerMark;
}
}
// 消耗燃料(用于地形改变)
private bool ConsumeFuelIfNeeded()
{
if (!Props.requiresFuel || Props.fuelConsumptionPerChange <= 0)
return true;
if (CompRefuelable == null)
return false;
if (CompRefuelable.Fuel >= Props.fuelConsumptionPerChange)
{
CompRefuelable.ConsumeFuel(Props.fuelConsumptionPerChange);
return true;
}
return false;
}
// 消耗标记燃料
private bool ConsumeMarkingFuelIfNeeded()
{
if (!Props.requiresFuel || Props.fuelConsumptionPerMark <= 0)
return true;
if (CompRefuelable == null)
return false;
if (CompRefuelable.Fuel >= Props.fuelConsumptionPerMark)
{
CompRefuelable.ConsumeFuel(Props.fuelConsumptionPerMark);
return true;
}
return false;
}
// 获取电源状态
private bool HasPower
{
get
{
if (!Props.requiresPower)
return true;
var compPower = parent.TryGetComp<CompPowerTrader>();
return compPower != null && compPower.PowerOn;
}
}
// 获取房间
private Room GetRoom()
{
var map = parent.Map;
if (map == null)
return null;
return parent.Position.GetRoom(map);
}
// 检查是否满足基本操作条件
private bool CanOperate()
{
// 检查是否有电
if (Props.requiresPower && !HasPower)
return false;
// 检查是否有足够的燃料
if (Props.requiresFuel && !HasSufficientFuel)
return false;
// 检查是否在房间内(如果需要)
if (Props.onlyInSameRoom)
{
var room = GetRoom();
if (room == null || !room.ProperRoom)
return false;
// 检查房间评分
if (room.GetStat(RoomStatDefOf.Impressiveness) < Props.minRoomScore)
return false;
}
return true;
}
// 检查是否可以执行标记操作
private bool CanMarkForRemoval()
{
if (!Props.enableAutoMarkForRemoval || !autoMarkForRemovalEnabled)
return false;
if (!CanOperate())
return false;
// 检查标记燃料
if (Props.requiresFuel && !HasSufficientFuelForMarking)
return false;
return true;
}
// 获取有效的工作单元格(地形改变)
private bool TryGetValidTargetCell(out IntVec3 result)
{
result = IntVec3.Invalid;
var map = parent.Map;
if (map == null)
return false;
// 获取搜索范围
var center = parent.Position;
int radius = Mathf.CeilToInt(Props.changeRadius);
// 获取房间(如果需要)
Room parentRoom = null;
if (Props.onlyInSameRoom)
{
parentRoom = GetRoom();
if (parentRoom == null)
return false;
}
// 收集所有候选单元格
List<IntVec3> candidateCells = new List<IntVec3>();
for (int x = -radius; x <= radius; x++)
{
for (int z = -radius; z <= radius; z++)
{
IntVec3 cell = new IntVec3(center.x + x, 0, center.z + z);
if (!cell.InBounds(map))
continue;
// 检查距离
float distance = cell.DistanceTo(center);
if (distance > Props.changeRadius)
continue;
// 检查房间(如果需要)
if (Props.onlyInSameRoom)
{
var cellRoom = cell.GetRoom(map);
if (cellRoom == null || cellRoom != parentRoom)
continue;
}
// 获取当前地形
TerrainDef currentTerrain = map.terrainGrid.TerrainAt(cell);
// 如果已经是目标地形,跳过
if (currentTerrain == Props.targetTerrain)
continue;
// 检查是否在可接受地形列表中(如果有定义)
if (Props.acceptedTerrains != null && Props.acceptedTerrains.Count > 0)
{
if (!Props.acceptedTerrains.Contains(currentTerrain))
continue;
}
candidateCells.Add(cell);
}
}
if (candidateCells.Count == 0)
return false;
// 根据设置选择单元格
if (Props.prioritizeClosest)
{
// 找到最近的单元格
float closestDistance = float.MaxValue;
IntVec3 closestCell = IntVec3.Invalid;
foreach (var cell in candidateCells)
{
float distance = cell.DistanceTo(center);
if (distance < closestDistance)
{
closestDistance = distance;
closestCell = cell;
}
}
if (closestCell.IsValid)
{
result = closestCell;
return true;
}
}
else
{
// 随机选择一个单元格
result = candidateCells.RandomElement();
return true;
}
return false;
}
// 获取有效的标记拆除单元格
private bool TryGetValidMarkingCell(out IntVec3 result)
{
result = IntVec3.Invalid;
var map = parent.Map;
if (map == null)
return false;
// 获取搜索范围
var center = parent.Position;
int radius = Mathf.CeilToInt(Props.markRemovalRadius);
// 获取房间(如果需要)
Room parentRoom = null;
if (Props.onlyInSameRoom)
{
parentRoom = GetRoom();
if (parentRoom == null)
return false;
}
// 收集所有候选单元格
List<IntVec3> candidateCells = new List<IntVec3>();
for (int x = -radius; x <= radius; x++)
{
for (int z = -radius; z <= radius; z++)
{
IntVec3 cell = new IntVec3(center.x + x, 0, center.z + z);
if (!cell.InBounds(map))
continue;
// 检查距离
float distance = cell.DistanceTo(center);
if (distance > Props.markRemovalRadius)
continue;
// 检查房间(如果需要)
if (Props.onlyInSameRoom)
{
var cellRoom = cell.GetRoom(map);
if (cellRoom == null || cellRoom != parentRoom)
continue;
}
// 获取当前地形
TerrainDef currentTerrain = map.terrainGrid.TerrainAt(cell);
// 如果不是目标地形,跳过(只标记可以生成的地形)
if (currentTerrain != Props.targetTerrain)
continue;
// 检查是否已经标记了拆除
if (map.designationManager.DesignationAt(cell, DesignationDefOf.RemoveFloor) != null)
continue;
// 检查是否可以移除
if (!map.terrainGrid.CanRemoveTopLayerAt(cell))
continue;
// 检查是否有建筑阻挡
if (WorkGiver_ConstructRemoveFloor.AnyBuildingBlockingFloorRemoval(cell, map))
continue;
candidateCells.Add(cell);
}
}
if (candidateCells.Count == 0)
return false;
// 根据设置选择单元格
if (Props.prioritizeClosest)
{
// 找到最近的单元格
float closestDistance = float.MaxValue;
IntVec3 closestCell = IntVec3.Invalid;
foreach (var cell in candidateCells)
{
float distance = cell.DistanceTo(center);
if (distance < closestDistance)
{
closestDistance = distance;
closestCell = cell;
}
}
if (closestCell.IsValid)
{
result = closestCell;
return true;
}
}
else
{
// 随机选择一个单元格
result = candidateCells.RandomElement();
return true;
}
return false;
}
// 开始工作(地形改变)
private void StartWorking(IntVec3 targetCell)
{
this.targetCell = targetCell;
this.isWorking = true;
// 计算工作时间(基于距离)
float distance = targetCell.DistanceTo(parent.Position);
float workTimeFactor = 1f + (distance / Props.changeRadius) * 0.5f; // 距离越远,时间越长
int baseWorkTime = Mathf.RoundToInt(Props.baseChangeInterval * 0.1f); // 工作时间为间隔的10%
workTicksRemaining = Mathf.RoundToInt(baseWorkTime * workTimeFactor / currentWorkSpeedMultiplier);
// 播放声音
if (Props.workingSound != null)
{
Props.workingSound.PlayOneShot(new TargetInfo(parent.Position, parent.Map));
}
}
// 开始标记工作
private void StartMarking(IntVec3 targetCell)
{
this.markingTargetCell = targetCell;
this.isMarking = true;
// 计算标记时间(基于距离)
float distance = targetCell.DistanceTo(parent.Position);
float workTimeFactor = 1f + (distance / Props.markRemovalRadius) * 0.5f;
int baseMarkTime = Mathf.RoundToInt(Props.markRemovalInterval * 0.05f); // 标记时间为间隔的5%
markingTicksRemaining = Mathf.RoundToInt(baseMarkTime * workTimeFactor / currentWorkSpeedMultiplier);
// 播放标记声音
if (Props.markSound != null)
{
Props.markSound.PlayOneShot(new TargetInfo(parent.Position, parent.Map));
}
}
// 完成工作(地形改变)
private void CompleteWork()
{
var map = parent.Map;
if (map == null || !targetCell.IsValid || !targetCell.InBounds(map))
{
ResetWorkState();
return;
}
// 检查并消耗燃料
if (!ConsumeFuelIfNeeded())
{
Messages.Message("ARA_TerrainChanger.InsufficientFuel".Translate(),
new TargetInfo(targetCell, map), MessageTypeDefOf.NegativeEvent);
ResetWorkState();
return;
}
// 获取当前地形
TerrainDef currentTerrain = map.terrainGrid.TerrainAt(targetCell);
// 记录之前的地形
TerrainDef previousTerrain = currentTerrain;
// 改变地形
map.terrainGrid.SetTerrain(targetCell, Props.targetTerrain);
// 播放完成声音
if (Props.completionSound != null)
{
Props.completionSound.PlayOneShot(new TargetInfo(targetCell, map));
}
// 显示消息(可选)
if (Prefs.DevMode)
{
Log.Message($"[TerrainChanger] Changed terrain at {targetCell} from {previousTerrain?.defName ?? "null"} to {Props.targetTerrain.defName}");
}
ResetWorkState();
}
// 完成标记工作
private void CompleteMarking()
{
var map = parent.Map;
if (map == null || !markingTargetCell.IsValid || !markingTargetCell.InBounds(map))
{
ResetMarkingState();
return;
}
// 检查并消耗标记燃料
if (!ConsumeMarkingFuelIfNeeded())
{
Messages.Message("ARA_TerrainChanger.InsufficientFuelForMarking".Translate(),
new TargetInfo(markingTargetCell, map), MessageTypeDefOf.NegativeEvent);
ResetMarkingState();
return;
}
// 获取当前地形
TerrainDef currentTerrain = map.terrainGrid.TerrainAt(markingTargetCell);
// 确保是目标地形(安全检查)
if (currentTerrain != Props.targetTerrain)
{
ResetMarkingState();
return;
}
// 添加拆除标记
map.designationManager.AddDesignation(new Designation(markingTargetCell, DesignationDefOf.RemoveFloor));
// 播放完成声音
if (Props.markCompleteSound != null)
{
Props.markCompleteSound.PlayOneShot(new TargetInfo(markingTargetCell, map));
}
// 显示消息(可选)
if (Prefs.DevMode)
{
Log.Message($"[TerrainChanger] Marked terrain at {markingTargetCell} ({currentTerrain.defName}) for removal");
}
ResetMarkingState();
}
// 重置工作状态
private void ResetWorkState()
{
isWorking = false;
workTicksRemaining = 0;
targetCell = IntVec3.Invalid;
// 清理效果器
if (effecter != null)
{
effecter.Cleanup();
effecter = null;
}
}
// 重置标记状态
private void ResetMarkingState()
{
isMarking = false;
markingTicksRemaining = 0;
markingTargetCell = IntVec3.Invalid;
// 清理标记效果器
if (markingEffecter != null)
{
markingEffecter.Cleanup();
markingEffecter = null;
}
}
// 更新工作速度
private void UpdateWorkSpeed()
{
float multiplier = 1.0f;
if (Props.requiresPower && HasPower)
{
multiplier *= Props.poweredWorkSpeedMultiplier;
}
if (Props.requiresFuel && HasSufficientFuel)
{
// 燃料充足时可能有额外的速度加成
// 可以在这里添加燃料相关的速度加成
}
currentWorkSpeedMultiplier = multiplier;
}
// Tick更新
public override void CompTick()
{
base.CompTick();
// --- 处理地形改变 ---
if (isWorking)
{
workTicksRemaining--;
if (workTicksRemaining <= 0)
{
CompleteWork();
}
}
else if (CanOperate())
{
UpdateWorkSpeed();
// 等待下一次改变
if (ticksUntilNextChange <= 0)
{
// 尝试找到有效目标单元格
if (TryGetValidTargetCell(out IntVec3 target))
{
// 在开始工作前再次检查燃料
if (Props.requiresFuel && Props.fuelConsumptionPerChange > 0)
{
if (CompRefuelable == null || CompRefuelable.Fuel < Props.fuelConsumptionPerChange)
{
// 燃料不足,重置计时器但跳过这次工作
ticksUntilNextChange = Mathf.RoundToInt(Props.baseChangeInterval / currentWorkSpeedMultiplier);
}
else
{
StartWorking(target);
}
}
else
{
StartWorking(target);
}
}
// 重置计时器,无论是否成功找到目标
ticksUntilNextChange = Mathf.RoundToInt(Props.baseChangeInterval / currentWorkSpeedMultiplier);
}
else
{
ticksUntilNextChange--;
}
}
// --- 处理自动标记拆除 ---
if (isMarking)
{
markingTicksRemaining--;
if (markingTicksRemaining <= 0)
{
CompleteMarking();
}
}
else if (CanMarkForRemoval())
{
// 等待下一次标记
if (ticksUntilNextMarkRemoval <= 0)
{
// 尝试找到有效标记单元格
if (TryGetValidMarkingCell(out IntVec3 target))
{
// 在开始标记前再次检查燃料
if (Props.requiresFuel && Props.fuelConsumptionPerMark > 0)
{
if (CompRefuelable == null || CompRefuelable.Fuel < Props.fuelConsumptionPerMark)
{
// 燃料不足,重置计时器但跳过这次标记
ticksUntilNextMarkRemoval = Mathf.RoundToInt(Props.markRemovalInterval / currentWorkSpeedMultiplier);
}
else
{
StartMarking(target);
}
}
else
{
StartMarking(target);
}
}
// 重置计时器,无论是否成功找到目标
ticksUntilNextMarkRemoval = Mathf.RoundToInt(Props.markRemovalInterval / currentWorkSpeedMultiplier);
}
else
{
ticksUntilNextMarkRemoval--;
}
}
}
// 防止长时间不工作
public override void CompTickRare()
{
base.CompTickRare();
if (!isWorking && ticksUntilNextChange > Props.baseChangeInterval * 10)
{
// 防止计时器溢出
ticksUntilNextChange = Props.baseChangeInterval;
}
if (!isMarking && ticksUntilNextMarkRemoval > Props.markRemovalInterval * 10)
{
// 防止标记计时器溢出
ticksUntilNextMarkRemoval = Props.markRemovalInterval;
}
}
// 保存/加载
public override void PostExposeData()
{
base.PostExposeData();
// 地形改变状态
Scribe_Values.Look(ref ticksUntilNextChange, "ticksUntilNextChange", Props.baseChangeInterval);
Scribe_Values.Look(ref isWorking, "isWorking", false);
Scribe_Values.Look(ref workTicksRemaining, "workTicksRemaining", 0);
Scribe_Values.Look(ref targetCell, "targetCell", IntVec3.Invalid);
// 标记拆除状态
Scribe_Values.Look(ref autoMarkForRemovalEnabled, "autoMarkForRemovalEnabled", true);
Scribe_Values.Look(ref ticksUntilNextMarkRemoval, "ticksUntilNextMarkRemoval", Props.markRemovalInterval);
Scribe_Values.Look(ref isMarking, "isMarking", false);
Scribe_Values.Look(ref markingTicksRemaining, "markingTicksRemaining", 0);
Scribe_Values.Look(ref markingTargetCell, "markingTargetCell", IntVec3.Invalid);
}
// 检查字符串
public override string CompInspectStringExtra()
{
var builder = new StringBuilder();
if (Props.requiresPower)
{
builder.AppendLine("ARA_TerrainChanger.Power".Translate(HasPower ? "On".Translate() : "Off".Translate()));
}
if (Props.requiresFuel)
{
if (CompRefuelable != null)
{
builder.AppendLine("ARA_TerrainChanger.Fuel".Translate(
CompRefuelable.Fuel.ToString("F1"),
CompRefuelable.TargetFuelLevel.ToString("F1")));
if (Props.fuelConsumptionPerChange > 0)
{
builder.AppendLine("ARA_TerrainChanger.FuelPerChange".Translate(
Props.fuelConsumptionPerChange.ToString("F1")));
}
if (Props.fuelConsumptionPerMark > 0)
{
builder.AppendLine("ARA_TerrainChanger.FuelPerMark".Translate(
Props.fuelConsumptionPerMark.ToString("F1")));
}
}
else
{
builder.AppendLine("ARA_TerrainChanger.NoFuelComponent".Translate());
}
}
if (isWorking)
{
float progressPercent = 1f - ((float)workTicksRemaining / (Props.baseChangeInterval * 0.1f));
builder.AppendLine("ARA_TerrainChanger.WorkingProgress".Translate(
progressPercent.ToStringPercent()));
builder.AppendLine("ARA_TerrainChanger.TargetCell".Translate(targetCell));
}
else
{
float daysUntilChange = ticksUntilNextChange / 60000f;
builder.AppendLine("ARA_TerrainChanger.NextChange".Translate(
daysUntilChange.ToString("F1")));
}
builder.AppendLine("ARA_TerrainChanger.TargetTerrain".Translate(
Props.targetTerrain.LabelCap));
builder.AppendLine("ARA_TerrainChanger.ChangeRadius".Translate(
Props.changeRadius.ToString("F1")));
return builder.ToString().TrimEndNewlines();
}
// 切换自动标记功能
private void ToggleAutoMarking()
{
autoMarkForRemovalEnabled = !autoMarkForRemovalEnabled;
if (autoMarkForRemovalEnabled)
{
Messages.Message("ARA_TerrainChanger.AutoMarkEnabled".Translate(),
parent, MessageTypeDefOf.PositiveEvent);
}
else
{
Messages.Message("ARA_TerrainChanger.AutoMarkDisabled".Translate(),
parent, MessageTypeDefOf.NeutralEvent);
}
}
// 获取Gizmos
public override IEnumerable<Gizmo> CompGetGizmosExtra()
{
foreach (var gizmo in base.CompGetGizmosExtra())
{
yield return gizmo;
}
// 只有在启用了自动标记功能时才显示切换按钮
if (Props.enableAutoMarkForRemoval && parent.Faction == Faction.OfPlayer)
{
yield return new Command_Toggle
{
defaultLabel = "ARA_TerrainChanger.ToggleAutoMark".Translate(),
defaultDesc = "ARA_TerrainChanger.ToggleAutoMarkDesc".Translate(),
icon = ContentFinder<Texture2D>.Get("UI/Designators/RemoveFloor", false) ?? BaseContent.BadTex,
isActive = () => autoMarkForRemovalEnabled,
toggleAction = ToggleAutoMarking,
hotKey = KeyBindingDefOf.Misc4
};
}
}
// 建筑被销毁时清理
public override void PostDestroy(DestroyMode mode, Map previousMap)
{
base.PostDestroy(mode, previousMap);
ResetWorkState();
ResetMarkingState();
}
// 建筑生成时初始化
public override void PostSpawnSetup(bool respawningAfterLoad)
{
base.PostSpawnSetup(respawningAfterLoad);
refuelableComponentCached = false; // 重置缓存,重新获取组件
// 初始化自动标记状态
if (!respawningAfterLoad)
{
autoMarkForRemovalEnabled = Props.enableAutoMarkForRemoval;
ticksUntilNextMarkRemoval = Props.markRemovalInterval;
}
}
}
}

View File

@@ -15,29 +15,14 @@ namespace ArachnaeSwarm
}
public ThingDef turretDef;
public float angleOffset;
public bool autoAttack = true;
public bool defaultEnabled = true;
}
[StaticConstructorOnStartup]
public class HediffComp_TopTurret : HediffComp, IAttackTargetSearcher
{
// 添加 null 检查的属性
private HediffCompProperties_TopTurret Props
{
get
{
if (this.props == null)
{
ArachnaeLog.Debug("HediffComp_TopTurret: props is null");
return null;
}
return this.props as HediffCompProperties_TopTurret;
}
}
public Thing Thing
{
get
@@ -46,6 +31,14 @@ namespace ArachnaeSwarm
}
}
private HediffCompProperties_TopTurret Props
{
get
{
return (HediffCompProperties_TopTurret)this.props;
}
}
public Verb CurrentEffectiveVerb
{
get
@@ -74,11 +67,6 @@ namespace ArachnaeSwarm
{
get
{
if (this.gun == null)
{
ArachnaeLog.Debug("HediffComp_TopTurret: gun is null");
return null;
}
return this.gun.TryGetComp<CompEquippable>();
}
}
@@ -87,13 +75,7 @@ namespace ArachnaeSwarm
{
get
{
var comp = this.GunCompEq;
if (comp == null)
{
ArachnaeLog.Debug("HediffComp_TopTurret: GunCompEq is null");
return null;
}
return comp.PrimaryVerb;
return this.GunCompEq.PrimaryVerb;
}
}
@@ -105,10 +87,131 @@ namespace ArachnaeSwarm
}
}
public override void CompPostTick(ref float severityAdjustment)
{
base.CompPostTick(ref severityAdjustment);
if (!TurretEnabled)
{
ResetCurrentTarget();
return;
}
if (!this.CanShoot)
{
return;
}
if (this.currentTarget.IsValid)
{
this.curRotation = (this.currentTarget.Cell.ToVector3Shifted() - this.Pawn.DrawPos).AngleFlat() + this.Props.angleOffset;
}
this.AttackVerb.VerbTick();
if (this.AttackVerb.state != VerbState.Bursting)
{
if (this.WarmingUp)
{
this.burstWarmupTicksLeft--;
if (this.burstWarmupTicksLeft == 0)
{
bool attackSuccess = this.AttackVerb.TryStartCastOn(this.currentTarget, false, true, false, true);
if (attackSuccess)
{
this.lastAttackTargetTick = Find.TickManager.TicksGame;
this.lastAttackedTarget = this.currentTarget;
}
return;
}
}
else
{
if (this.burstCooldownTicksLeft > 0)
{
this.burstCooldownTicksLeft--;
}
if (this.burstCooldownTicksLeft <= 0 && this.Pawn.IsHashIntervalTick(10))
{
// 自动寻找目标
this.currentTarget = (Thing)AttackTargetFinder.BestShootTargetFromCurrentPosition(this, TargetScanFlags.NeedThreat | TargetScanFlags.NeedAutoTargetable, null, 0f, 9999f);
if (this.currentTarget.IsValid)
{
this.burstWarmupTicksLeft = 1;
return;
}
this.ResetCurrentTarget();
}
}
}
}
// 简化的Gizmos - 只有开关按钮
public override IEnumerable<Gizmo> CompGetGizmos()
{
// 只有 pawn 被选中且是玩家派系时才显示按钮
if (this.Pawn.Faction == Faction.OfPlayer && Find.Selector.IsSelected(this.Pawn))
{
yield return new Command_Toggle
{
defaultLabel = "CommandToggleTurret".Translate(),
defaultDesc = "CommandToggleTurretDesc".Translate(),
icon = ContentFinder<Texture2D>.Get("UI/Gizmos/ToggleTurret"),
isActive = () => TurretEnabled,
toggleAction = () => TurretEnabled = !TurretEnabled,
hotKey = KeyBindingDefOf.Misc1
};
}
}
// 简化的提示信息
public override string CompTipStringExtra
{
get
{
string baseString = base.CompTipStringExtra;
string turretStatus = TurretEnabled ? "Turret: Active" : "Turret: Inactive";
string targetStatus = "Target: ";
if (this.currentTarget.IsValid)
{
targetStatus += $"{this.currentTarget.Thing?.LabelCap ?? this.currentTarget.Cell.ToString()}";
}
else
{
targetStatus += "None";
}
string result = turretStatus + "\n" + targetStatus;
return string.IsNullOrEmpty(baseString) ? result : baseString + "\n" + result;
}
}
// 炮塔启用状态
public bool TurretEnabled
{
get { return turretEnabled; }
set
{
turretEnabled = value;
if (!turretEnabled)
{
ResetCurrentTarget(); // 禁用时重置目标
}
}
}
private bool CanShoot
{
get
{
// 检查炮塔是否启用
if (!TurretEnabled)
return false;
Pawn pawn;
if ((pawn = (this.Pawn)) != null)
{
@@ -147,7 +250,7 @@ namespace ArachnaeSwarm
{
get
{
if (this.turretMat == null && this.Props?.turretDef?.graphicData != null)
if (this.turretMat == null)
{
this.turretMat = MaterialPool.MatFrom(this.Props.turretDef.graphicData.texPath);
}
@@ -159,71 +262,27 @@ namespace ArachnaeSwarm
{
get
{
return this.Props?.autoAttack ?? false;
return this.Props.autoAttack;
}
}
public override void CompPostMake()
{
base.CompPostMake();
// 添加 null 检查
if (this.Props == null)
{
ArachnaeLog.Debug("HediffComp_TopTurret: Props is null in CompPostMake");
return;
}
this.MakeGun();
// 设置默认启用状态
TurretEnabled = Props.defaultEnabled;
}
private void MakeGun()
{
// 添加详细的 null 检查
if (this.Props == null)
{
ArachnaeLog.Debug("HediffComp_TopTurret: Props is null in MakeGun");
return;
}
if (this.Props.turretDef == null)
{
ArachnaeLog.Debug("HediffComp_TopTurret: Props.turretDef is null");
return;
}
try
{
this.gun = ThingMaker.MakeThing(this.Props.turretDef, null);
if (this.gun == null)
{
ArachnaeLog.Debug($"HediffComp_TopTurret: Failed to create gun from turretDef '{this.Props.turretDef.defName}'");
return;
}
this.UpdateGunVerbs();
}
catch (Exception ex)
{
ArachnaeLog.Debug($"HediffComp_TopTurret: Exception in MakeGun: {ex}");
}
this.gun = ThingMaker.MakeThing(this.Props.turretDef, null);
this.UpdateGunVerbs();
}
private void UpdateGunVerbs()
{
if (this.gun == null)
{
ArachnaeLog.Debug("HediffComp_TopTurret: gun is null in UpdateGunVerbs");
return;
}
var comp = this.gun.TryGetComp<CompEquippable>();
if (comp == null)
{
ArachnaeLog.Debug("HediffComp_TopTurret: CompEquippable is null");
return;
}
List<Verb> allVerbs = comp.AllVerbs;
List<Verb> allVerbs = this.gun.TryGetComp<CompEquippable>().AllVerbs;
for (int i = 0; i < allVerbs.Count; i++)
{
Verb verb = allVerbs[i];
@@ -235,58 +294,6 @@ namespace ArachnaeSwarm
}
}
public override void CompPostTick(ref float severityAdjustment)
{
base.CompPostTick(ref severityAdjustment);
// 添加 null 检查
if (this.AttackVerb == null)
{
return;
}
if (!this.CanShoot)
{
return;
}
if (this.currentTarget.IsValid)
{
this.curRotation = (this.currentTarget.Cell.ToVector3Shifted() - this.Pawn.DrawPos).AngleFlat() + this.Props.angleOffset;
}
this.AttackVerb.VerbTick();
if (this.AttackVerb.state != VerbState.Bursting)
{
if (this.WarmingUp)
{
this.burstWarmupTicksLeft--;
if (this.burstWarmupTicksLeft == 0)
{
this.AttackVerb.TryStartCastOn(this.currentTarget, false, true, false, true);
this.lastAttackTargetTick = Find.TickManager.TicksGame;
this.lastAttackedTarget = this.currentTarget;
return;
}
}
else
{
if (this.burstCooldownTicksLeft > 0)
{
this.burstCooldownTicksLeft--;
}
if (this.burstCooldownTicksLeft <= 0 && this.Pawn.IsHashIntervalTick(10))
{
this.currentTarget = (Thing)AttackTargetFinder.BestShootTargetFromCurrentPosition(this, TargetScanFlags.NeedThreat | TargetScanFlags.NeedAutoTargetable, null, 0f, 9999f);
if (this.currentTarget.IsValid)
{
this.burstWarmupTicksLeft = 1;
return;
}
this.ResetCurrentTarget();
}
}
}
}
private void ResetCurrentTarget()
{
this.currentTarget = LocalTargetInfo.Invalid;
@@ -301,11 +308,13 @@ namespace ArachnaeSwarm
Scribe_TargetInfo.Look(ref this.currentTarget, "currentTarget");
Scribe_Deep.Look<Thing>(ref this.gun, "gun", Array.Empty<object>());
Scribe_Values.Look<bool>(ref this.fireAtWill, "fireAtWill", true, false);
// 保存启用状态
Scribe_Values.Look<bool>(ref this.turretEnabled, "turretEnabled", Props.defaultEnabled, false);
if (Scribe.mode == LoadSaveMode.PostLoadInit)
{
if (this.gun == null)
{
ArachnaeLog.Debug("CompTurrentGun had null gun after loading. Recreating.");
this.MakeGun();
return;
}
@@ -315,24 +324,18 @@ namespace ArachnaeSwarm
private const int StartShootIntervalTicks = 10;
private static readonly CachedTexture ToggleTurretIcon = new CachedTexture("UI/Gizmos/ToggleTurret");
public Thing gun;
protected int burstCooldownTicksLeft;
protected int burstWarmupTicksLeft;
protected LocalTargetInfo currentTarget = LocalTargetInfo.Invalid;
private bool fireAtWill = true;
private LocalTargetInfo lastAttackedTarget = LocalTargetInfo.Invalid;
private int lastAttackTargetTick;
private float curRotation;
// 炮塔启用状态字段
private bool turretEnabled = true;
[Unsaved(false)]
public Material turretMat;
}

View File

@@ -112,7 +112,9 @@ namespace ArachnaeSwarm
int stripCount = (int)ChitinNeed.CurLevel;
if (stripCount < StripComp.Props.minStripAmount)
stripCount = StripComp.Props.minStripAmount;
stripCount = stripCount * 2;
// 获取甲壳物品定义
ThingDef carapaceDef = StripComp.CarapaceThingDef;
if (carapaceDef == null)

View File

@@ -0,0 +1,281 @@
using System;
using System.Collections.Generic;
using RimWorld;
using UnityEngine;
using Verse;
using Verse.AI;
namespace ArachnaeSwarm
{
public class Verb_ShootSelfUnderfoot : Verb_LaunchProjectile
{
// 重写ShotsPerBurst与Verb_Shoot相同
protected override int ShotsPerBurst => base.BurstShotCount;
// 重写WarmupComplete添加射击技能学习
public override void WarmupComplete()
{
base.WarmupComplete();
// 只有在目标是Pawn时才学习射击技能
if (currentTarget.Thing is Pawn targetPawn &&
!targetPawn.Downed &&
!targetPawn.IsColonyMech &&
CasterIsPawn &&
CasterPawn.skills != null)
{
float xp = targetPawn.HostileTo(caster) ? 170f : 20f;
float num2 = verbProps.AdjustedFullCycleTime(this, CasterPawn);
CasterPawn.skills.Learn(SkillDefOf.Shooting, xp * num2);
}
}
// 核心重写:将目标改为脚下
protected override bool TryCastShot()
{
// 保存原始目标
LocalTargetInfo originalTarget = currentTarget;
try
{
// 将目标改为施法者自己的位置
currentTarget = new LocalTargetInfo(caster.Position);
// 调用基类方法,但使用修改后的目标(脚下)
bool result = base.TryCastShot();
// 如果成功发射,记录射击次数
if (result && CasterIsPawn)
{
CasterPawn.records.Increment(RecordDefOf.ShotsFired);
}
return result;
}
finally
{
// 恢复原始目标(对于连续射击可能重要)
currentTarget = originalTarget;
}
}
// 重写CanHitTarget因为目标是脚下总是可以命中
public override bool CanHitTarget(LocalTargetInfo targ)
{
// 对于向脚下射击,我们总是允许(只要施法者存在)
if (caster == null || !caster.Spawned)
return false;
// 如果目标就是施法者自己,允许
if (targ == caster)
return true;
// 对于其他目标,使用默认逻辑
return base.CanHitTarget(targ);
}
// 重写CanHitTargetFrom对于脚下射击总是返回true
public override bool CanHitTargetFrom(IntVec3 root, LocalTargetInfo targ)
{
// 如果目标是施法者自己(或脚下),总是可以命中
if (targ.Thing == caster || (targ.IsValid && targ.Cell == caster.Position))
return true;
return base.CanHitTargetFrom(root, targ);
}
// 重写TryFindShootLineFromTo对于脚下射击简化逻辑
public new bool TryFindShootLineFromTo(IntVec3 root, LocalTargetInfo targ, out ShootLine resultingLine, bool ignoreRange = false)
{
// 如果目标是脚下,直接返回射击线
if (targ.IsValid && targ.Cell == caster.Position)
{
resultingLine = new ShootLine(root, targ.Cell);
return true;
}
// 否则使用基类逻辑
return base.TryFindShootLineFromTo(root, targ, out resultingLine, ignoreRange);
}
// 重写DrawHighlight简化高亮显示
public override void DrawHighlight(LocalTargetInfo target)
{
// 绘制标准射程环
verbProps.DrawRadiusRing(caster.Position, this);
// 如果目标是有效的,绘制目标高亮
if (target.IsValid)
{
GenDraw.DrawTargetHighlight(target);
// 绘制目标周围的场半径
DrawHighlightFieldRadiusAroundTarget(target);
}
}
// 辅助方法:绘制彩色目标高亮
private void GenDraw_DrawTargetHighlightWithColor(LocalTargetInfo target, Color color)
{
GenDraw.DrawTargetHighlight(target);
}
// 重写OnGUI显示自定义鼠标图标
public override void OnGUI(LocalTargetInfo target)
{
// 使用自定义图标或默认攻击图标
Texture2D icon;
if (!target.IsValid)
{
icon = TexCommand.CannotShoot;
}
else if (target.Cell == caster.Position)
{
// 可以使用自定义图标,这里使用攻击图标
icon = TexCommand.Attack;
}
else
{
icon = (UIIcon != BaseContent.BadTex) ? UIIcon : TexCommand.Attack;
}
GenUI.DrawMouseAttachment(icon);
}
// 重写ValidateTarget允许向自己脚下射击
public override bool ValidateTarget(LocalTargetInfo target, bool showMessages = true)
{
// 如果目标是脚下,总是允许
if (target.IsValid && target.Cell == caster.Position)
return true;
// 否则使用基类验证逻辑
return base.ValidateTarget(target, showMessages);
}
// 重写Available确保有抛射体并允许在近战状态下使用
public override bool Available()
{
// 首先调用基类检查
if (!base.Available())
return false;
// 检查是否有抛射体
if (Projectile == null)
return false;
// 特殊处理:允许在近战威胁下使用
if (CasterIsPawn && CasterPawn.mindState != null && CasterPawn.mindState.MeleeThreatStillThreat)
{
return true;
}
return true;
}
// 重写OrderForceTarget允许在近战距离内强制使用
public override void OrderForceTarget(LocalTargetInfo target)
{
// 如果是近战攻击,使用近战逻辑
if (verbProps.IsMeleeAttack)
{
Job job = JobMaker.MakeJob(JobDefOf.AttackMelee, target);
job.playerForced = true;
if (target.Thing is Pawn pawn)
{
job.killIncappedTarget = pawn.Downed;
}
CasterPawn.jobs.TryTakeOrderedJob(job, JobTag.Misc);
return;
}
// 检查是否在近战范围内,但允许向脚下射击
float minRange = verbProps.EffectiveMinRange(target, CasterPawn);
if (CasterIsPawn &&
(float)CasterPawn.Position.DistanceToSquared(target.Cell) < minRange * minRange &&
CasterPawn.Position.AdjacentTo8WayOrInside(target.Cell))
{
// 如果是向脚下射击,允许
if (target.IsValid && target.Cell == CasterPawn.Position)
{
// 允许向脚下射击
}
else
{
Messages.Message("MessageCantShootInMelee".Translate(), CasterPawn, MessageTypeDefOf.RejectInput, historical: false);
return;
}
}
// 创建射击工作
Job job2 = JobMaker.MakeJob(verbProps.ai_IsWeapon ? JobDefOf.AttackStatic : JobDefOf.UseVerbOnThing);
job2.verbToUse = this;
job2.targetA = target;
job2.endIfCantShootInMelee = false; // 设置为false允许在近战中射击
CasterPawn.jobs.TryTakeOrderedJob(job2, JobTag.Misc);
}
// 重写TryStartCastOn允许在近战状态下开始射击
public override bool TryStartCastOn(LocalTargetInfo castTarg, LocalTargetInfo destTarg, bool surpriseAttack = false, bool canHitNonTargetPawns = true, bool preventFriendlyFire = false, bool nonInterruptingSelfCast = false)
{
// 调用基类方法,但设置一个标志表示这是向脚下射击
bool isShootingUnderfoot = castTarg.IsValid && castTarg.Cell == caster.Position;
// 如果是向脚下射击,临时修改一些属性以允许近战射击
if (isShootingUnderfoot && CasterIsPawn && CasterPawn.mindState != null && CasterPawn.mindState.MeleeThreatStillThreat)
{
// 临时忽略近战威胁检查
bool originalAIProjectileLaunchingIgnoresMeleeThreats = verbProps.ai_ProjectileLaunchingIgnoresMeleeThreats;
verbProps.ai_ProjectileLaunchingIgnoresMeleeThreats = true;
try
{
return base.TryStartCastOn(castTarg, destTarg, surpriseAttack, canHitNonTargetPawns, preventFriendlyFire, nonInterruptingSelfCast);
}
finally
{
// 恢复原始值
verbProps.ai_ProjectileLaunchingIgnoresMeleeThreats = originalAIProjectileLaunchingIgnoresMeleeThreats;
}
}
return base.TryStartCastOn(castTarg, destTarg, surpriseAttack, canHitNonTargetPawns, preventFriendlyFire, nonInterruptingSelfCast);
}
// 添加一个方法,检查是否在近战状态下
public bool IsInMeleeCombat()
{
if (!CasterIsPawn)
return false;
return CasterPawn.mindState?.MeleeThreatStillThreat == true;
}
// 重写BurstingTick在近战状态下也继续射击
public override void BurstingTick()
{
base.BurstingTick();
// 在近战状态下也继续射击逻辑
if (IsInMeleeCombat() && state == VerbState.Bursting)
{
// 可以在这里添加近战状态下的特殊效果
}
}
// 添加自定义属性,用于控制是否总是向脚下发射
private bool alwaysShootUnderfoot = true;
public bool AlwaysShootUnderfoot
{
get => alwaysShootUnderfoot;
set => alwaysShootUnderfoot = value;
}
// 添加一个方法,允许临时关闭向脚下射击
public void SetShootUnderfoot(bool shootUnderfoot)
{
alwaysShootUnderfoot = shootUnderfoot;
}
}
}