diff --git a/1.6/1.6/Assemblies/ArachnaeSwarm.dll b/1.6/1.6/Assemblies/ArachnaeSwarm.dll index 389f4d8..f1fb7c0 100644 Binary files a/1.6/1.6/Assemblies/ArachnaeSwarm.dll and b/1.6/1.6/Assemblies/ArachnaeSwarm.dll differ diff --git a/1.6/1.6/Defs/AbilityDefs/ARA_Abilities.xml b/1.6/1.6/Defs/AbilityDefs/ARA_Abilities.xml index 874a59d..c9159c6 100644 --- a/1.6/1.6/Defs/AbilityDefs/ARA_Abilities.xml +++ b/1.6/1.6/Defs/AbilityDefs/ARA_Abilities.xml @@ -271,57 +271,6 @@ - - ARA_BaseRace_Acid_Launcher - - 将一只阿拉克涅酸噬种辅虫发射到指定地点,落地后这只寿命有限的辅虫将立刻对敌人展开攻击。 - ArachnaeSwarm/UI/Abilities/ARA_BaseRace_Launcher - 5000 - true - 300 - false - AcidSpray_Warmup - 3 - true - - Verb_CastAbility - 24 - 1 - AcidSpray_Resolve - false - - true - - - -
  • - ArachnaeBase_Race_Acidcut_Proj -
  • -
  • - 饮食 - true - Food - 0.2 - 营养值不足,需要进食 -
  • -
    -
    - - ArachnaeBase_Race_Acidcut_Proj - - Projectile_SpawnsPawnZeroAge - - ArachnaeSwarm/Things/ARA_Acidcut/Bodies/Naked_Thin - Graphic_Multi - - - 41 - ArachnaeBase_Race_Acidcut - true - ARA_AcidBurn - 10 - - ARA_Toxic_Needle_Fire diff --git a/1.6/1.6/Defs/EvolutionDefs/ARA_Evolution.xml b/1.6/1.6/Defs/EvolutionDefs/ARA_Evolution.xml index 4b6f61e..de19d49 100644 --- a/1.6/1.6/Defs/EvolutionDefs/ARA_Evolution.xml +++ b/1.6/1.6/Defs/EvolutionDefs/ARA_Evolution.xml @@ -381,6 +381,11 @@ --> +
  • + ARA_Fighter_Base_Turret + 0 + true +
  • ARA_AcidSprayBurst
  • @@ -830,7 +835,7 @@ ARA_ShieldHead_Base - 盾头种虽然身材较小而没有大型阿拉克涅虫族那样的压迫感,但是得益于其覆盖全身的甲壳,防御力可一点不比那些大型虫族弱。它的甲壳被特意设置为会过度生长并脱落,虫族则可以利用其脱落的甲壳作为建设巢穴的工具。一只盾头种每天产出15份甲壳素。 + 盾头种虽然身材较小而没有大型阿拉克涅虫族那样的压迫感,但是得益于其覆盖全身的甲壳,防御力可一点不比那些大型虫族弱。 HediffWithComps (0.6, 0.4, 0.8) false @@ -1052,7 +1057,7 @@ ARA_ShieldHead_HiveBuilder - 这只阿拉克涅盾头种已经获得拔耀,获得了强大的产出能力和建造能力。一只盾头种每天产出25份甲壳素。 + 这只阿拉克涅盾头种已经获得拔耀,获得了强大的产出能力和建造能力。一只盾头种每天额外产出15份甲壳素。 HediffWithComps (0.6, 0.4, 0.8) false @@ -1075,7 +1080,7 @@ ARA_Carapace - 25 + 15
  • ARA_Technology_5ESS
  • - + diff --git a/1.6/1.6/Defs/Thing_Misc/Weapons/ARA_Weapon.xml b/1.6/1.6/Defs/Thing_Misc/Weapons/ARA_Weapon.xml index 376480d..5645ed9 100644 --- a/1.6/1.6/Defs/Thing_Misc/Weapons/ARA_Weapon.xml +++ b/1.6/1.6/Defs/Thing_Misc/Weapons/ARA_Weapon.xml @@ -1681,7 +1681,6 @@ SpitterSpawn - ARA_Technology_5PAV UnfinishedWeapon @@ -1974,6 +1973,88 @@ 40 + + ARA_Fighter_Base_Turret + + 阿拉克涅战士种身上的共生器官,会对敌方附近自动投射酸噬种辅虫。 + + ArachnaeSwarm/Weapon/ARA_Weapon_Empty + Graphic_Single + + + ARA_Skyraider_Hivelord + + + 1 + 1 + 1 + 1 + 0 + + +
  • + ArachnaeSwarm.Verb_ShootSelfUnderfoot + true + ArachnaeBase_Race_Acidcut_Proj + 18 + 1 + 2 + SpitterSpit + GunTail_Medium + 9 + 12 +
  • +
    +
    + + ARA_Praetorian_Base_Turret + + 阿拉克涅禁卫种身上的共生器官,会对敌方附近自动投射酸噬种辅虫。 + + ArachnaeSwarm/Weapon/ARA_Weapon_Empty + Graphic_Single + + + ARA_Skyraider_Hivelord + + + 1 + 1 + 1 + 1 + 0 + + +
  • + ArachnaeSwarm.Verb_ShootSelfUnderfoot + true + ArachnaeBase_Race_Acidcut_Proj + 65 + 1 + 2 + SpitterSpit + GunTail_Medium + 9 + 2 +
  • +
    +
    + + ArachnaeBase_Race_Acidcut_Proj + + Projectile_SpawnsPawnZeroAge + + ArachnaeSwarm/Things/ARA_Acidcut/Bodies/Naked_Thin + Graphic_Multi + + + 41 + ArachnaeBase_Race_Acidcut + true + ARA_AcidBurn + 10 + + ARA_Skyraider_Hivelord_Turret diff --git a/1.6/1.6/Defs/Thing_building/ARA_Building.xml b/1.6/1.6/Defs/Thing_building/ARA_Building.xml index c52f6a8..0930b44 100644 --- a/1.6/1.6/Defs/Thing_building/ARA_Building.xml +++ b/1.6/1.6/Defs/Thing_building/ARA_Building.xml @@ -10,6 +10,7 @@ TerrainFadeRoughSoftLight (0.95, 0.95, 0.93, 1) (240,240,240) + (209, 207, 184) FadeRough 0 @@ -59,6 +60,7 @@ ArachnaeSwarm/Terrain/Surfaces/ARA_InsectCreepTile Terrain/Surfaces/AncientMegastructure TerrainFadeRoughSoftLight + (0.95, 0.95, 0.93, 1) (240,240,240) (209, 207, 184) @@ -163,29 +165,64 @@ - + ARA_SmoothedInsectWall 阿拉克涅工蜂将硬质材料和甲壳素混合堆起来形成的墙壁,虫族对这片墙壁进行了精心打磨,质地坚硬的同时看起来美观多了。 Things/Building/Linked/WallSmooth_MenuIcon Things/Building/Linked/RockSmooth_Atlas + Graphic_Single + CutoutComplex (209, 207, 184) + + (0.7, 0.4, 0.2) + (0,0,-0.1) + + + true + 0 - 7 + 5 1 1000 + 1500 + 0 + 0 - false + true + + + ArachnaeSwarm/Building/Linked/ARA_InsectWall_Blueprint + true false - - true + +
  • ARA_InsectDoor
  • +
    - false - Repair + false + Heavy + EatVegetarian + 2 + +
  • Metallic
  • +
    + + 1 + + +
  • + Bomb + 0.1 +
  • +
  • + Thump + 0.1 +
  • +
    ARA_InsectDoor @@ -329,7 +366,7 @@ 1200 0 1000 - 0 + 1 2
    10 @@ -353,7 +390,6 @@ false Heavy - ARA_Buildings true 0 true @@ -588,7 +624,7 @@
  • 100 - 15 + 85 2 0.2 1800 @@ -658,7 +694,7 @@
  • 100 - 15 + 85 2 0.2 1800 @@ -734,7 +770,7 @@
  • 100 - 15 + 85 2 0.2 1800 @@ -805,7 +841,7 @@
  • 100 - 15 + 85 2 0.2 1800 @@ -880,7 +916,7 @@
  • 100 - 15 + 85 2 0.2 1800 @@ -961,7 +997,7 @@
  • 100 - 15 + 85 2 0.2 1800 @@ -1127,7 +1163,7 @@
  • 100 - 15 + 85 2 0.2 1800 @@ -1183,7 +1219,7 @@
  • 100 - 15 + 85 2 0.2 1800 diff --git a/1.6/1.6/Defs/Thing_building/ARA_DropPod.xml b/1.6/1.6/Defs/Thing_building/ARA_DropPod.xml index cda739c..6727842 100644 --- a/1.6/1.6/Defs/Thing_building/ARA_DropPod.xml +++ b/1.6/1.6/Defs/Thing_building/ARA_DropPod.xml @@ -48,7 +48,7 @@
  • 100 - 15 + 85 2 0.2 1800 diff --git a/1.6/1.6/Defs/Thing_building/ARA_NutrientNetworkBuilding.xml b/1.6/1.6/Defs/Thing_building/ARA_NutrientNetworkBuilding.xml index a9f32fe..f304da7 100644 --- a/1.6/1.6/Defs/Thing_building/ARA_NutrientNetworkBuilding.xml +++ b/1.6/1.6/Defs/Thing_building/ARA_NutrientNetworkBuilding.xml @@ -38,7 +38,7 @@
  • 100 - 15 + 85 2 0.2 1800 @@ -173,7 +173,7 @@
  • 100 - 15 + 85 2 0.2 1800 @@ -410,7 +410,7 @@ ARA_JellyVat - 一个阿拉克涅虫族活体组织,可以充分利用活体钜菌的溶解能力,通过消化生物质,来分泌出营养丰富的阿拉克涅虫蜜。 + 一个阿拉克涅虫族活体组织,可以充分利用活体钜菌的溶解能力,通过消化生物质,来分泌出营养丰富的阿拉克涅虫蜜。阿拉克涅虫蜜会在房间内蔓延,形成蜜池,而生物质酿造池会定期标记这些流淌的蜜浆,命令其他虫族来收集它们。 Building ArachnaeSwarm/Building/ARA_JellyVat @@ -464,20 +464,53 @@ ARA_Creep
  • - + +
  • + 100 + 85 + 2 + 0.2 + 1800 + 0.5 + +
  • ArachnaeNode_Race_WeaponSmith
  • + + + + +
  • + ARA_InsectJelly_Terrain + 600 + true + 1 + 1 + 10 + true + true + false + 1 + + + true + 600 + 10 + 0 + + + + + true + + AcidSpray_Resolve + AcidSpray_Resolve +
  • Biomass 生物质 @@ -499,50 +532,42 @@ true false
  • -
  • - 200 - 0.25 - 2000 - 1 -
  • - -
  • - 虫蜜 - 虫蜜 - - 25 - 0 - true - - - -
  • ARA_InsectJelly
  • - - - - false - false - false - false - false - true - - - -
  • - 60 - 0.99 - true - true - false -
  • - -
  • - ARA_InsectCreep - 6 -
  • + + ARA_InsectJelly_Terrain + + 250 + ArachnaeSwarm/Terrain/Surfaces/ARA_InsectJelly_Terrain + ARA_InsectCreep + (231, 224, 188) + Terrain/Surfaces/AncientMegastructure + TerrainFadeRoughSoftLight + + 85 + 0.22 + + + 2 + + true + Freshwater + Map/WaterDepth + 100 + Water + Misc + 390 + false + 1 + false + 600 + + false + +
  • None
  • +
    +
    ARA_Carapace_Productor @@ -601,39 +626,109 @@
  • - -
  • - Steels - 钢铁 - 钢铁 - - -
  • Steel
  • - - - 50 - 50 - true - - true - true + +
  • + ARA_Carapace_Column + 1 + 1200 + 10 + false + false + + + + + + + true + + PowerOnSmall + DropPod_Leaving + ResearchComplete + + + true + true + + + true + 1200 + 10 + 0 + + + + + true
  • - -
  • - 60000~60000 - -
  • - ARA_Carapace - 25 -
  • - - true - - -
  • - ARA_InsectCreep - 6 + +
  • + 100 + 85 + 2 + 0.2 + 1800 + 0.5 + +
  • ArachnaeNode_Race_WeaponSmith
  • +
    + + ARA_Carapace_Column + + 阿拉克涅的骨花通过催化尸体而转变得到的柱子,。 + 2040 + Building + PassThroughOnly + 0.25 + 0 + 0.8 + + (1.25,1.25) + (0,0,0.2) + ArachnaeSwarm/Building/ARA_Column + Graphic_Single + CutoutComplex + + (0.3, 0.5, 0.3) + (0,0,-0.23) + + + (0.2,0.2,0.6,0.6) + + + + 20 + 750 + 10 + 0 + 0 + + 0 + + + 20 + + true + false + false + 0 + + true + false + true + + \ No newline at end of file diff --git a/1.6/1.6/Defs/Thing_building/ARA_Ootheca.xml b/1.6/1.6/Defs/Thing_building/ARA_Ootheca.xml index 68cf541..6554f2e 100644 --- a/1.6/1.6/Defs/Thing_building/ARA_Ootheca.xml +++ b/1.6/1.6/Defs/Thing_building/ARA_Ootheca.xml @@ -56,7 +56,7 @@ - 1 + 3 @@ -145,13 +145,17 @@
  • ArachnaeSwarm.ITab_Ootheca_Incubation
  • + + 25 + +
  • ArachnaeSwarm.PlaceWorker_CustomRadius
  • 100 - 15 + 85 2 0.2 1800 @@ -175,7 +179,7 @@ 孵化··· 选择孵化的阿拉克涅督虫··· 孵化菜单 - UI/Commands/IncubatorSwitch + ArachnaeSwarm/UI/Commands/ARA_NodeSwarmIcon @@ -220,7 +224,6 @@
  • ArachnaeNode_Race_ShieldHead 2 - UI/Buttons/IncubateUnitA
  • ARA_Incubator_1_Reward_Hediffs
  • ARA_Incubator_2_Reward_Hediffs
  • @@ -235,7 +238,6 @@
  • ArachnaeNode_Race_WeaponSmith 2 - UI/Buttons/IncubateUnitA
  • ARA_Incubator_1_Reward_Hediffs
  • ARA_Incubator_2_Reward_Hediffs
  • @@ -250,7 +252,6 @@
  • ArachnaeNode_Race_Fighter 2 - UI/Buttons/IncubateUnitA ARA_Technology_1KYC
  • ARA_Incubator_1_Reward_Hediffs
  • @@ -266,7 +267,6 @@
  • ArachnaeNode_Race_Myrmecocystus 3 - UI/Buttons/IncubateUnitA
  • ARA_Incubator_1_Reward_Hediffs
  • ARA_Incubator_2_Reward_Hediffs
  • @@ -281,7 +281,6 @@
  • ArachnaeNode_Race_Facehugger 2 - UI/Buttons/IncubateUnitA ARA_Technology_4KYC
  • ARA_Incubator_1_Reward_Hediffs
  • @@ -297,7 +296,6 @@
  • ARA_MimicNematodeShamblerSwarmer 1 - UI/Buttons/IncubateUnitA ARA_Technology_6MEN
  • ARA_Incubator_1_Reward_Hediffs
  • @@ -313,7 +311,6 @@
  • ArachnaeNode_Race_Smokepop 6 - UI/Buttons/IncubateUnitA ARA_Technology_5KYC
  • ARA_Incubator_1_Reward_Hediffs
  • @@ -329,7 +326,6 @@
  • ArachnaeNode_Race_NeuroSwarm 10 - UI/Buttons/IncubateUnitA ARA_Technology_6KYC
  • ARA_Incubator_1_Reward_Hediffs
  • @@ -345,7 +341,6 @@
  • ArachnaeNode_Race_Skyraider 6 - UI/Buttons/IncubateUnitA ARA_Technology_2KYC
  • ARA_Incubator_1_Reward_Hediffs
  • @@ -361,7 +356,6 @@
  • ArachnaeNode_Race_Praetorian 12 - UI/Buttons/IncubateUnitA ARA_Technology_7KYC
  • ARA_Incubator_1_Reward_Hediffs
  • @@ -384,8 +378,8 @@ (113,141,117,0)
  • - -10 - 20 + 6 + 36 0.015
  • @@ -465,6 +459,10 @@ + + 25 + +
  • ArachnaeSwarm.ITab_EquipmentOotheca_Incubation
  • @@ -476,7 +474,7 @@
  • 100 - 15 + 85 2 0.2 1800 @@ -505,8 +503,8 @@ (113,141,117,0)
  • - -10 - 20 + 6 + 36 0.015
  • @@ -527,11 +525,13 @@ (1,1) + ARA_Buildings Building PassThroughOnly 0.3 false Normal + ARA_Incubator_Nutrient_Solution 2000 50 @@ -545,6 +545,10 @@ ARA_Gene_Essence + + 25 + +
  • @@ -576,12 +580,23 @@ 30
  • - -30 - 55 + 6 + 36 0.00005 0.005 0.001
  • +
  • + 100 + 85 + 2 + 0.2 + 1800 + 0.5 + +
  • ArachnaeNode_Race_WeaponSmith
  • + +
    \ No newline at end of file diff --git a/1.6/1.6/Defs/Thing_building/ARA_RefuelingVat.xml b/1.6/1.6/Defs/Thing_building/ARA_RefuelingVat.xml index 8bc3562..3637a3d 100644 --- a/1.6/1.6/Defs/Thing_building/ARA_RefuelingVat.xml +++ b/1.6/1.6/Defs/Thing_building/ARA_RefuelingVat.xml @@ -65,7 +65,7 @@
  • 100 - 15 + 85 2 0.2 1800 diff --git a/1.6/1.6/Defs/Thing_building/ARA_SwarmTurret.xml b/1.6/1.6/Defs/Thing_building/ARA_SwarmTurret.xml index f92e1cd..44d6476 100644 --- a/1.6/1.6/Defs/Thing_building/ARA_SwarmTurret.xml +++ b/1.6/1.6/Defs/Thing_building/ARA_SwarmTurret.xml @@ -290,7 +290,7 @@
  • 100 - 15 + 85 2 0.2 1800 @@ -439,7 +439,7 @@
  • 100 - 15 + 85 2 0.2 1800 @@ -611,7 +611,7 @@
  • 100 - 15 + 85 2 0.2 1800 diff --git a/1.6/1.6/Defs/Thing_building/ARA_WormholeDefs.xml b/1.6/1.6/Defs/Thing_building/ARA_WormholeDefs.xml index 61a4ee9..ffbad6b 100644 --- a/1.6/1.6/Defs/Thing_building/ARA_WormholeDefs.xml +++ b/1.6/1.6/Defs/Thing_building/ARA_WormholeDefs.xml @@ -41,7 +41,7 @@
  • 100 - 15 + 85 2 0.2 1800 diff --git a/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/ARA_CorpseConverter.xml b/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/ARA_CorpseConverter.xml new file mode 100644 index 0000000..c91a8dd --- /dev/null +++ b/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/ARA_CorpseConverter.xml @@ -0,0 +1,39 @@ + + + + 生物质不足,无法进行尸体转换 + 生物质不足,无法标记拆除 + 已创建尸体储存区,包含{0}个单元格 + 自动标记拆除功能已启用 + 自动标记拆除功能已禁用 + + + 电力:{0} + 生物质:{0} / {1} + 每次转换消耗生物质:{0} + 未找到生物质组件! + + 工作中:{0} + 目标尸体:{0} + 下次转换:{0}天后 + + 转换半径:{0}格 + + + 创建尸体储存区 + 在转换器范围内创建尸体储存区,便于集中尸体进行转换 + 切换自动标记 + 启用或禁用自动标记拆除功能。启用后,设备会定期标记范围内的目标建筑为拆除。 + + + DEV: Test Conversion + DEV: Reset Timer + DEV: Test Marking + + + 未找到有效尸体 + 未找到有效建筑 + + + 尸体转换区 + diff --git a/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/ARA_TerrainChanger.xml b/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/ARA_TerrainChanger.xml new file mode 100644 index 0000000..cc117e0 --- /dev/null +++ b/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/ARA_TerrainChanger.xml @@ -0,0 +1,36 @@ + + + + 生物质不足,无法生成地形 + 生物质不足,无法标记拆除 + 自动标记拆除功能已启用 + 自动标记拆除功能已禁用 + + + 切换自动标记 + 启用或禁用自动标记拆除功能。启用后,该结构会定期呼叫附近的虫族收集分泌物。 + + + 电力:{0} + 生物质:{0} / {1} + 每次转换消耗生物质:{0} + 每次标记消耗生物质:{0} + 未找到生物质组件! + + 工作中:{0} + 目标位置:{0} + 下次分泌:{0}天后 + + 目标地形:{0} + 分泌半径:{0}格 + + 自动标记:{0} + 下次标记:{0}天后 + 标记中:{0} + 标记目标:{0} + 标记半径:{0}格 + + + 已启用 + 已禁用 + diff --git a/Content/Textures/ArachnaeSwarm/Terrain/Surfaces/ARA_InsectJelly_Terrain.png b/Content/Textures/ArachnaeSwarm/Terrain/Surfaces/ARA_InsectJelly_Terrain.png new file mode 100644 index 0000000..51ee441 Binary files /dev/null and b/Content/Textures/ArachnaeSwarm/Terrain/Surfaces/ARA_InsectJelly_Terrain.png differ diff --git a/Content/Textures/ArachnaeSwarm/UI/Commands/ARA_NodeSwarmIcon.png b/Content/Textures/ArachnaeSwarm/UI/Commands/ARA_NodeSwarmIcon.png new file mode 100644 index 0000000..74e3976 Binary files /dev/null and b/Content/Textures/ArachnaeSwarm/UI/Commands/ARA_NodeSwarmIcon.png differ diff --git a/Source/ArachnaeSwarm/.vs/ArachnaeSwarm/v17/.suo b/Source/ArachnaeSwarm/.vs/ArachnaeSwarm/v17/.suo index 06a7edd..9cae529 100644 Binary files a/Source/ArachnaeSwarm/.vs/ArachnaeSwarm/v17/.suo and b/Source/ArachnaeSwarm/.vs/ArachnaeSwarm/v17/.suo differ diff --git a/Source/ArachnaeSwarm/.vs/ArachnaeSwarm/v17/DocumentLayout.json b/Source/ArachnaeSwarm/.vs/ArachnaeSwarm/v17/DocumentLayout.json index 1ef1ca3..cb49570 100644 --- a/Source/ArachnaeSwarm/.vs/ArachnaeSwarm/v17/DocumentLayout.json +++ b/Source/ArachnaeSwarm/.vs/ArachnaeSwarm/v17/DocumentLayout.json @@ -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" } ] } diff --git a/Source/ArachnaeSwarm/ARA_HediffDefOf.cs b/Source/ArachnaeSwarm/ARA_HediffDefOf.cs index 7620d1e..1034789 100644 --- a/Source/ArachnaeSwarm/ARA_HediffDefOf.cs +++ b/Source/ArachnaeSwarm/ARA_HediffDefOf.cs @@ -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)); + } + } } diff --git a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj index e1f2062..d904c86 100644 --- a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj +++ b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj @@ -124,13 +124,15 @@ - + + + @@ -138,6 +140,8 @@ + + @@ -195,6 +199,7 @@ + diff --git a/Source/ArachnaeSwarm/Building_Comps/ARA_Building_RefuelingVat/Building_RefuelingVat.cs b/Source/ArachnaeSwarm/Building_Comps/ARA_Building_RefuelingVat/Building_RefuelingVat.cs index ef697f2..d5ea650 100644 --- a/Source/ArachnaeSwarm/Building_Comps/ARA_Building_RefuelingVat/Building_RefuelingVat.cs +++ b/Source/ArachnaeSwarm/Building_Comps/ARA_Building_RefuelingVat/Building_RefuelingVat.cs @@ -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, // 将建筑设为伤害来源 diff --git a/Source/ArachnaeSwarm/Building_Comps/ARA_CorpseConverter/CompCorpseConverter.cs b/Source/ArachnaeSwarm/Building_Comps/ARA_CorpseConverter/CompCorpseConverter.cs new file mode 100644 index 0000000..0ad5f1b --- /dev/null +++ b/Source/ArachnaeSwarm/Building_Comps/ARA_CorpseConverter/CompCorpseConverter.cs @@ -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 tmpRadialCells = new List(); + + // 获取径向单元格(转换半径内) + private IEnumerable RadialCells => GenRadial.RadialCellsAround(parent.Position, Props.conversionRadius, useCenter: true); + + // 获取燃料组件 + private CompRefuelableNutrition_WithKey CompRefuelable + { + get + { + if (!refuelableComponentCached) + { + compRefuelable = parent.TryGetComp(); + 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(); + 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 candidateCorpses = new List(); + + 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 candidateBuildings = new List(); + + 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 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.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; + } + } + } +} diff --git a/Source/ArachnaeSwarm/Building_Comps/ARA_CorpseConverter/CompProperties_CorpseConverter.cs b/Source/ArachnaeSwarm/Building_Comps/ARA_CorpseConverter/CompProperties_CorpseConverter.cs new file mode 100644 index 0000000..90aae18 --- /dev/null +++ b/Source/ArachnaeSwarm/Building_Comps/ARA_CorpseConverter/CompProperties_CorpseConverter.cs @@ -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 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; + } + } + } +} diff --git a/Source/ArachnaeSwarm/Building_Comps/ARA_SwarmMaintenance/Comp_SwarmMaintenance.cs b/Source/ArachnaeSwarm/Building_Comps/ARA_SwarmMaintenance/Comp_SwarmMaintenance.cs index 99fdb48..770cafd 100644 --- a/Source/ArachnaeSwarm/Building_Comps/ARA_SwarmMaintenance/Comp_SwarmMaintenance.cs +++ b/Source/ArachnaeSwarm/Building_Comps/ARA_SwarmMaintenance/Comp_SwarmMaintenance.cs @@ -323,8 +323,8 @@ namespace ArachnaeSwarm // 只在玩家控制下显示 if (parent.Faction?.IsPlayer == true) { - // 调试按钮:手动触发寻找维护者 - if (Prefs.DevMode) + // 调试按钮:手动触发寻找维护者 - 仅在GodMode下显示 + if (DebugSettings.godMode) { yield return new Command_Action { diff --git a/Source/ArachnaeSwarm/Building_Comps/ARA_TerrainChanger/CompProperties_TerrainChanger.cs b/Source/ArachnaeSwarm/Building_Comps/ARA_TerrainChanger/CompProperties_TerrainChanger.cs new file mode 100644 index 0000000..f2839af --- /dev/null +++ b/Source/ArachnaeSwarm/Building_Comps/ARA_TerrainChanger/CompProperties_TerrainChanger.cs @@ -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 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; + } + } + } +} diff --git a/Source/ArachnaeSwarm/Building_Comps/ARA_TerrainChanger/CompTerrainChanger.cs b/Source/ArachnaeSwarm/Building_Comps/ARA_TerrainChanger/CompTerrainChanger.cs new file mode 100644 index 0000000..70e040f --- /dev/null +++ b/Source/ArachnaeSwarm/Building_Comps/ARA_TerrainChanger/CompTerrainChanger.cs @@ -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(); + 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(); + 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 candidateCells = new List(); + + 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 candidateCells = new List(); + + 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 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.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; + } + } + } +} diff --git a/Source/ArachnaeSwarm/Hediffs/ARA_HediffComp_TopTurret/HediffComp_TopTurret.cs b/Source/ArachnaeSwarm/Hediffs/ARA_HediffComp_TopTurret/HediffComp_TopTurret.cs index 83c2ef0..c486341 100644 --- a/Source/ArachnaeSwarm/Hediffs/ARA_HediffComp_TopTurret/HediffComp_TopTurret.cs +++ b/Source/ArachnaeSwarm/Hediffs/ARA_HediffComp_TopTurret/HediffComp_TopTurret.cs @@ -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(); } } @@ -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 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.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(); - if (comp == null) - { - ArachnaeLog.Debug("HediffComp_TopTurret: CompEquippable is null"); - return; - } - - List allVerbs = comp.AllVerbs; + List allVerbs = this.gun.TryGetComp().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(ref this.gun, "gun", Array.Empty()); Scribe_Values.Look(ref this.fireAtWill, "fireAtWill", true, false); + // 保存启用状态 + Scribe_Values.Look(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; } diff --git a/Source/ArachnaeSwarm/Jobs/JobDriver_StripChitin/JobDriver_StripChitin.cs b/Source/ArachnaeSwarm/Jobs/JobDriver_StripChitin/JobDriver_StripChitin.cs index 1811cec..71c0129 100644 --- a/Source/ArachnaeSwarm/Jobs/JobDriver_StripChitin/JobDriver_StripChitin.cs +++ b/Source/ArachnaeSwarm/Jobs/JobDriver_StripChitin/JobDriver_StripChitin.cs @@ -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) diff --git a/Source/ArachnaeSwarm/Buildings/Building_Ootheca/RoomRoleWorker_Incubator.cs b/Source/ArachnaeSwarm/RoomRole/RoomRoleWorker_Incubator.cs similarity index 100% rename from Source/ArachnaeSwarm/Buildings/Building_Ootheca/RoomRoleWorker_Incubator.cs rename to Source/ArachnaeSwarm/RoomRole/RoomRoleWorker_Incubator.cs diff --git a/Source/ArachnaeSwarm/Verbs/Verb_ShootSelfUnderfoot.cs b/Source/ArachnaeSwarm/Verbs/Verb_ShootSelfUnderfoot.cs new file mode 100644 index 0000000..4973d07 --- /dev/null +++ b/Source/ArachnaeSwarm/Verbs/Verb_ShootSelfUnderfoot.cs @@ -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; + } + } +} diff --git a/非公开资源/Content/Textures/Item/ARA_Carapace.sai2 b/非公开资源/Content/Textures/Item/ARA_Carapace.sai2 index b944f12..282f7ca 100644 Binary files a/非公开资源/Content/Textures/Item/ARA_Carapace.sai2 and b/非公开资源/Content/Textures/Item/ARA_Carapace.sai2 differ diff --git a/非公开资源/Content/Textures/Terrain/Surfaces/ARA_InsectJelly_Terrain.png b/非公开资源/Content/Textures/Terrain/Surfaces/ARA_InsectJelly_Terrain.png new file mode 100644 index 0000000..51ee441 Binary files /dev/null and b/非公开资源/Content/Textures/Terrain/Surfaces/ARA_InsectJelly_Terrain.png differ diff --git a/非公开资源/Content/Textures/UI/Abilities/ARA_EggSpew.sai2 b/非公开资源/Content/Textures/UI/Abilities/ARA_EggSpew.sai2 index 91359d7..f323feb 100644 Binary files a/非公开资源/Content/Textures/UI/Abilities/ARA_EggSpew.sai2 and b/非公开资源/Content/Textures/UI/Abilities/ARA_EggSpew.sai2 differ diff --git a/非公开资源/Content/Textures/UI/Commands/ARA_NodeSwarmIcon.sai2 b/非公开资源/Content/Textures/UI/Commands/ARA_NodeSwarmIcon.sai2 new file mode 100644 index 0000000..414dd53 Binary files /dev/null and b/非公开资源/Content/Textures/UI/Commands/ARA_NodeSwarmIcon.sai2 differ diff --git a/非公开资源/Content/Textures/UI/Commands/ARA_StripChitin.sai2 b/非公开资源/Content/Textures/UI/Commands/ARA_StripChitin.sai2 index 9ff5052..f87e5c4 100644 Binary files a/非公开资源/Content/Textures/UI/Commands/ARA_StripChitin.sai2 and b/非公开资源/Content/Textures/UI/Commands/ARA_StripChitin.sai2 differ