diff --git a/1.6/1.6/Assemblies/ArachnaeSwarm.dll b/1.6/1.6/Assemblies/ArachnaeSwarm.dll
index cb12f8a..8512649 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/EvolutionDefs/ARA_Evolution.xml b/1.6/1.6/Defs/EvolutionDefs/ARA_Evolution.xml
index e3942d9..2ae5809 100644
--- a/1.6/1.6/Defs/EvolutionDefs/ARA_Evolution.xml
+++ b/1.6/1.6/Defs/EvolutionDefs/ARA_Evolution.xml
@@ -328,8 +328,8 @@
ARA_Myrmecocystus_Production_InsectJelly
- 产出虫蜜
- 这只阿拉克涅蜜罐种正在产出虫蜜,以滋养虫群。一只蜜罐种每天产出15份阿拉克涅虫蜜。
+ 虫蜜腔室
+ 这只阿拉克涅蜜罐种正在产出虫蜜,以滋养虫群。
ARA_InsectJelly
ARA_Myrmecocystus_Production_Medicine
@@ -368,61 +368,14 @@
-
+
-
-
-
- true
-
- ARA_InsectJelly
-
- 15
-
-
- 1
-
- 1
- 0
-
-
- false
-
- true
-
- true
-
- 20
-
-
- true
-
- false
-
ARA_Myrmecocystus_Production_Medicine
@@ -1203,51 +1156,6 @@
-
-
-
- true
-
- ARA_Carapace
-
- 15
-
-
- 1
-
- 1
- 0
-
-
- false
-
- true
-
- true
-
- 20
-
-
- true
-
- false
-
ARA_ShieldHead_Protector
diff --git a/1.6/1.6/Defs/HediffDefs/ARA_Hediffs_HiveMind.xml b/1.6/1.6/Defs/HediffDefs/ARA_Hediffs_HiveMind.xml
index 263123a..ed18012 100644
--- a/1.6/1.6/Defs/HediffDefs/ARA_Hediffs_HiveMind.xml
+++ b/1.6/1.6/Defs/HediffDefs/ARA_Hediffs_HiveMind.xml
@@ -22,6 +22,7 @@
Outdoors
+ ARA_ChitinArmor
Indoors
@@ -54,6 +55,9 @@
DrugDesire
RoomSize
+
+ ARA_ChitinArmor
+
@@ -95,6 +99,9 @@
DrugDesire
RoomSize
+
+ ARA_ChitinArmor
+
@@ -301,4 +308,76 @@
+
+
+ ARA_ChitinArmor
+ 甲壳护甲
+ 阿拉克涅虫族身上的甲壳,可以根据厚度为阿拉克涅虫族提供保护。
+ HediffWithComps
+
+
+ 0
+ 极薄
+
+ 0.95
+
+
+
+ 0.99
+ 薄
+
+ 0.9
+ 0.9
+
+
+
+ 1.99
+ 适中
+
+ 0.85
+ 0.75
+
+
+
+ 2.99
+ 较厚
+
+ 0.7
+ 0.5
+
+
+
+ 3.99
+ 厚
+
+ 0.5
+ 0.25
+
+
+
+ 4.99
+ 极厚
+
+ 0.35
+ 0
+
+
+
+ 5.99
+ 固若磐石
+
+ 0.25
+ 0
+
+
+
+ 6.99
+ 坚不可摧
+
+ 0.1
+ 0
+
+
+
+
\ No newline at end of file
diff --git a/1.6/1.6/Defs/JobDefs/ARA_Jobs.xml b/1.6/1.6/Defs/JobDefs/ARA_Jobs.xml
index b4f5c58..99b1e3b 100644
--- a/1.6/1.6/Defs/JobDefs/ARA_Jobs.xml
+++ b/1.6/1.6/Defs/JobDefs/ARA_Jobs.xml
@@ -38,4 +38,61 @@
false
true
+
+ ARA_OperateEquipmentIncubator
+ ArachnaeSwarm.JobDriver_OperateEquipmentIncubator
+ 激活阿拉克涅茧。
+ true
+ false
+ true
+ false
+ true
+
+
+
+
+ ARA_FeedWithHoney
+ ArachnaeSwarm.JobDriver_FeedWithHoney
+ 正在喂养TargetA。
+ false
+ true
+ true
+
+ true
+
+
+
+
+ ARA_ExtractHoney
+ ArachnaeSwarm.JobDriver_ExtractHoney
+ 正在挤出虫蜜。
+ false
+ true
+ true
+
+ true
+
+
+
+
+ ARA_StripChitin
+ ArachnaeSwarm.JobDriver_StripChitin
+ 正在剥离甲壳
+ true
+ false
+ true
+
+
+
+ ARA_SwarmMaintain
+ ArachnaeSwarm.JobDriver_SwarmMaintain
+ 正在维护TargetA。
+ true
+
+ false
+ true
+ true
+ false
+
\ No newline at end of file
diff --git a/1.6/1.6/Defs/NeedDefs/ARA_Needs.xml b/1.6/1.6/Defs/NeedDefs/ARA_Needs.xml
index 5ecb541..797c71c 100644
--- a/1.6/1.6/Defs/NeedDefs/ARA_Needs.xml
+++ b/1.6/1.6/Defs/NeedDefs/ARA_Needs.xml
@@ -4,16 +4,57 @@
ARA_HoneyProduction
ArachnaeSwarm.Need_HoneyProduction
蜜罐
- 代表这个生物储存阿拉克涅虫蜜原浆的多少。当其他虫族饥饿时,会尝试直接从有蜜罐腔的生物身上获取虫蜜,如果蜜罐满溢,则生物会尝试将其提取出来。
+ 代表这个生物储存阿拉克涅虫蜜原浆的多少。当其他虫族饥饿时,有蜜罐腔的生物会尝试喂养它们;如果蜜罐满溢,则生物会尝试将其提取出来。
800
true
true
- Wula_Synth
+ ARA_Myrmecocystus_Production_InsectJelly
true
Baby, Child, Adult
true
false
+
+
+
+
+ 0.8
+
+
+ 1
+
+
+
+
+ ARA_ChitinArmor
+ ArachnaeSwarm.Need_ChitinArmor
+ 甲壳
+ 代表这个生物身上的阿拉克涅甲壳的厚度,越厚的甲壳越能为虫族带来强大的防御力。虫族也可以将其从身上剥离下来,以生产甲壳素。
+ 800
+ true
+ true
+
+ ARA_HiveMindMaster
+ ARA_HiveMindDrone
+ ARA_NonPlayer_HiveMindDroneHediff
+
+ true
+ Baby, Child, Adult
+ true
+ false
+
+
+
+ ARA_ChitinArmor
+
+ 0.0
+ 10.0
+
+ true
+ 0.1
+ 0.1
+
+
\ No newline at end of file
diff --git a/1.6/1.6/Defs/PawnKindDef/ARA_PawnKinds.xml b/1.6/1.6/Defs/PawnKindDef/ARA_PawnKinds.xml
index 16468ae..a9f37b5 100644
--- a/1.6/1.6/Defs/PawnKindDef/ARA_PawnKinds.xml
+++ b/1.6/1.6/Defs/PawnKindDef/ARA_PawnKinds.xml
@@ -210,8 +210,6 @@
ARA_AcidSprayBurst
- ARA_RaceBaseSwarmProduceOff
- ARA_RaceBaseSwarmProduceOn
@@ -256,10 +254,6 @@
0
-
- ARA_RaceBaseSwarmProduceOff
- ARA_RaceBaseSwarmProduceOn
-
ArachnaeNode_Race_Facehugger
@@ -498,50 +492,6 @@
-
- ArachnaeBase_Race_HardJaw
- 阿拉克涅坚颚种
- ArachnaeBase_Race_HardJaw
-
-
-
- ArachnaeSwarm/Things/ARA_Scavenger/HardJaw/Naked_Thin
- 1
- (156,148,125)
-
- (0.4, 0.5, 0.37)
- (0,0,-0.15)
-
-
-
- Things/Pawn/Animal/Spelopede/Dessicated_Spelopede
- 1
-
-
-
-
-
- ArachnaeBase_Race_Maid
- 阿拉克涅家政种
- ArachnaeBase_Race_Maid
-
-
-
- ArachnaeSwarm/Things/ARA_Scavenger/Maid/Naked_Thin
- 1
- (156,148,125)
-
- (0.4, 0.5, 0.37)
- (0,0,-0.15)
-
-
-
- Things/Pawn/Animal/Spelopede/Dessicated_Spelopede
- 1
-
-
-
-
ArachnaeBase_Race_Acidcut
阿拉克涅酸噬种
diff --git a/1.6/1.6/Defs/Rooms/ARA_RoomRoles.xml b/1.6/1.6/Defs/Rooms/ARA_RoomRoles.xml
index c5652a4..cd3cfbe 100644
--- a/1.6/1.6/Defs/Rooms/ARA_RoomRoles.xml
+++ b/1.6/1.6/Defs/Rooms/ARA_RoomRoles.xml
@@ -6,7 +6,7 @@
ArachnaeSwarm.RoomRoleWorker_Incubator
Space
- ARA_IncubatorRateFactor
+ ARA_IncubatorQualityFactor
diff --git a/1.6/1.6/Defs/ThingDef_Races/ARA_RaceNodeSwarm.xml b/1.6/1.6/Defs/ThingDef_Races/ARA_RaceNodeSwarm.xml
index 9301adb..5b5f5ec 100644
--- a/1.6/1.6/Defs/ThingDef_Races/ARA_RaceNodeSwarm.xml
+++ b/1.6/1.6/Defs/ThingDef_Races/ARA_RaceNodeSwarm.xml
@@ -775,11 +775,6 @@
-
ARA_Cycle_Suppression_Hediff
ARA_LifespanHediff
@@ -937,7 +932,7 @@
false
- false
+ true
ARA_RaceBaseSwarmProduceSwitchHediff
ARA_InsectJelly
1
@@ -1063,24 +1058,6 @@
1.0
false
-
- true
- ARA_RaceBaseSwarmProduceSwitchHediff
- ARA_InsectJelly
- 1
- 1
- 999
- 9999
-
-
-
- ArachnaeBase_Race_Maid
- 3
- 1000
-
-
- CocoonDestroyed
-
ARA_Cycle_Suppression_Hediff
ARA_LifespanHediff
@@ -1419,24 +1396,6 @@
1.0
false
-
- true
- ARA_RaceBaseSwarmProduceSwitchHediff
- ARA_InsectJelly
- 1
- 1
- 999
- 9999
-
-
-
- ArachnaeBase_Race_Maid
- 5
- 1000
-
-
- CocoonDestroyed
-
ARA_Cycle_Suppression_Hediff
ARA_LifespanHediff
diff --git a/1.6/1.6/Defs/ThingDef_Races/ARA_RaceQueen.xml b/1.6/1.6/Defs/ThingDef_Races/ARA_RaceQueen.xml
index 7c332e5..01830b6 100644
--- a/1.6/1.6/Defs/ThingDef_Races/ARA_RaceQueen.xml
+++ b/1.6/1.6/Defs/ThingDef_Races/ARA_RaceQueen.xml
@@ -169,6 +169,14 @@
1.0
false
+
+
+ true
+ 0.8
+ 1
+ 3000
+ ARA_Carapace
+
@@ -420,8 +428,7 @@
ArachnaeBase_Race_Scavenger
- ArachnaeBase_Race_HardJaw
- ArachnaeBase_Race_Maid
+ ArachnaeBase_Race_Larva
true
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 ce63b00..87dc862 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
@@ -73,9 +73,7 @@
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 9dc872e..828008c 100644
--- a/1.6/1.6/Defs/Thing_building/ARA_Building.xml
+++ b/1.6/1.6/Defs/Thing_building/ARA_Building.xml
@@ -495,9 +495,16 @@
-
- ARA_InsectCreep
- 3
+
+ 100
+ 15
+ 2
+ 0.2
+ 1800
+ 0.5
+
+ ArachnaeNode_Race_WeaponSmith
+
@@ -558,13 +565,20 @@
+
+ 100
+ 15
+ 2
+ 0.2
+ 1800
+ 0.5
+
+ ArachnaeNode_Race_WeaponSmith
+
+
ResearchSpeedFactor
-
- ARA_InsectCreep
- 4
-
@@ -627,6 +641,17 @@
ARA_Buildings
+
+ 100
+ 15
+ 2
+ 0.2
+ 1800
+ 0.5
+
+ ArachnaeNode_Race_WeaponSmith
+
+
UI/Commands/Vent
@@ -638,10 +663,6 @@
25
-
- ARA_InsectCreep
- 3
-
@@ -691,9 +712,16 @@
-
- ARA_InsectCreep
- 3
+
+ 100
+ 15
+ 2
+ 0.2
+ 1800
+ 0.5
+
+ ArachnaeNode_Race_WeaponSmith
+
3
@@ -759,9 +787,16 @@
-
- ARA_InsectCreep
- 2
+
+ 100
+ 15
+ 2
+ 0.2
+ 1800
+ 0.5
+
+ ArachnaeNode_Race_WeaponSmith
+
3
@@ -833,9 +868,16 @@
PlaceWorker_GlowRadius
-
- ARA_InsectCreep
- 2
+
+ 100
+ 15
+ 2
+ 0.2
+ 1800
+ 0.5
+
+ ArachnaeNode_Race_WeaponSmith
+
7
@@ -998,9 +1040,16 @@
-
- ARA_InsectCreep
- 5
+
+ 100
+ 15
+ 2
+ 0.2
+ 1800
+ 0.5
+
+ ArachnaeNode_Race_WeaponSmith
+
4
@@ -1047,9 +1096,16 @@
-
- ARA_InsectCreep
- 3
+
+ 100
+ 15
+ 2
+ 0.2
+ 1800
+ 0.5
+
+ ArachnaeNode_Race_WeaponSmith
+
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 a297a9d..cda739c 100644
--- a/1.6/1.6/Defs/Thing_building/ARA_DropPod.xml
+++ b/1.6/1.6/Defs/Thing_building/ARA_DropPod.xml
@@ -33,19 +33,6 @@
20
3
-
-
- 300
- 0.8
- true
-
-
-
- ARA_DropPodLeaving
- false
- 53
-
-
ARA_Technology_8POD
@@ -58,6 +45,30 @@
0.65
ARA_DropPodIncoming
ARA_ActiveDropPod
+
+
+ 100
+ 15
+ 2
+ 0.2
+ 1800
+ 0.5
+
+ ArachnaeNode_Race_WeaponSmith
+
+
+
+ 300
+ 0.8
+ true
+
+
+
+ ARA_DropPodLeaving
+ false
+ 53
+
+
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 42f9d51..fc48934 100644
--- a/1.6/1.6/Defs/Thing_building/ARA_NutrientNetworkBuilding.xml
+++ b/1.6/1.6/Defs/Thing_building/ARA_NutrientNetworkBuilding.xml
@@ -36,6 +36,17 @@
ARA_Creep
+
+ 100
+ 15
+ 2
+ 0.2
+ 1800
+ 0.5
+
+ ArachnaeNode_Race_WeaponSmith
+
+
7.0
14
@@ -162,6 +173,18 @@
4
ARA_Creep
+
+ 100
+ 15
+ 2
+ 0.2
+ 1800
+ 0.5
+
+ ArachnaeNode_Race_WeaponSmith
+
+
+
5.0
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 4629030..b32c1c9 100644
--- a/1.6/1.6/Defs/Thing_building/ARA_Ootheca.xml
+++ b/1.6/1.6/Defs/Thing_building/ARA_Ootheca.xml
@@ -98,11 +98,37 @@
false
false
- false
+
ARA_Incubator_Room
+
+
+
+
+ 5
+
+ 5
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
ArachnaeSwarm.ITab_Ootheca_Incubation
@@ -112,6 +138,17 @@
ArachnaeSwarm.PlaceWorker_CustomRadius
+
+ 100
+ 15
+ 2
+ 0.2
+ 1800
+ 0.5
+
+ ArachnaeNode_Race_WeaponSmith
+
+
5
(0.5, 1, 1)
@@ -144,7 +181,7 @@
ArachnaeNode_Race_Fighter
2
UI/Buttons/IncubateUnitA
-
+ ARA_Technology_1KYC
ArachnaeNode_Race_Myrmecocystus
@@ -203,4 +240,125 @@
+
+
+ ARA_Equipment_Ootheca
+ 阿拉克涅茧
+ 一个脆弱、易燃、黏滑的囊状物,是阿拉克涅女皇种所诞之卵,内含哺育一只新督虫所需的营养和遗传物质。
+
+ 1000
+
+ ArachnaeSwarm.Building_EquipmentOotheca
+ Building
+ (1,1)
+ ARA_Buildings
+
+ ArachnaeSwarm/Building/ARA_Cocoon
+ Graphic_Single
+ (1.1,1.1)
+
+ (0.7, 0.4, 0.7)
+ (0,0,-0.1)
+
+
+ Building
+ PassThroughOnly
+ 0.3
+ false
+ Normal
+ 0
+ None
+
+ ARA_Incubator_Nutrient_Solution
+
+ (0, 0, 1)
+ true
+
+ 10
+ 150
+ 1
+ -6
+
+
+ true
+
+ false
+ false
+
+
+ ARA_Incubator_Room
+
+
+
+
+
+
+ 3
+
+ 3
+
+ false
+
+ 0.03
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ArachnaeSwarm.ITab_EquipmentOotheca_Incubation
+
+
+
+ ArachnaeSwarm.PlaceWorker_CustomRadius
+
+
+
+ 100
+ 15
+ 2
+ 0.2
+ 1800
+ 0.5
+
+ ArachnaeNode_Race_WeaponSmith
+
+
+
+ 3
+ (0.5, 1, 1)
+ 0
+ true
+ 吸收半径
+ 这个卵在孵化过程中的吸收半径,确保这些地格中铺满阿拉克涅营养液,并且没有其他的卵,以获得最佳的孵化速度和孵化质量。
+ false
+
+
+
+
+
+ CocoonDestroyed
+
+
+ 6
+ (113,141,117,0)
+
+
+ -10
+ 20
+ 0.015
+
+
+
\ 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 37ac4ce..8bc3562 100644
--- a/1.6/1.6/Defs/Thing_building/ARA_RefuelingVat.xml
+++ b/1.6/1.6/Defs/Thing_building/ARA_RefuelingVat.xml
@@ -63,6 +63,18 @@
4
ARA_Creep
+
+ 100
+ 15
+ 2
+ 0.2
+ 1800
+ 0.5
+
+ ArachnaeNode_Race_WeaponSmith
+
+
+
精华素
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 0f71e61..f92e1cd 100644
--- a/1.6/1.6/Defs/Thing_building/ARA_SwarmTurret.xml
+++ b/1.6/1.6/Defs/Thing_building/ARA_SwarmTurret.xml
@@ -288,6 +288,17 @@
Normal
Heavy
+
+ 100
+ 15
+ 2
+ 0.2
+ 1800
+ 0.5
+
+ ArachnaeNode_Race_WeaponSmith
+
+
nutrition
@@ -305,10 +316,6 @@
true
-
- ARA_InsectCreep
- 6
-
ARA_NutrientNetworkTower
@@ -430,6 +437,17 @@
Normal
Heavy
+
+ 100
+ 15
+ 2
+ 0.2
+ 1800
+ 0.5
+
+ ArachnaeNode_Race_WeaponSmith
+
+
nutrition
@@ -446,10 +464,6 @@
true
-
- ARA_InsectCreep
- 6
-
ARA_NutrientNetworkTower
@@ -595,6 +609,17 @@
Normal
+
+ 100
+ 15
+ 2
+ 0.2
+ 1800
+ 0.5
+
+ ArachnaeNode_Race_WeaponSmith
+
+
nutrition
50.0
@@ -611,10 +636,6 @@
-
- ARA_InsectCreep
- 8
-
ARA_NutrientNetworkTower
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 34e32d4..61a4ee9 100644
--- a/1.6/1.6/Defs/Thing_building/ARA_WormholeDefs.xml
+++ b/1.6/1.6/Defs/Thing_building/ARA_WormholeDefs.xml
@@ -39,6 +39,17 @@
200
+
+ 100
+ 15
+ 2
+ 0.2
+ 1800
+ 0.5
+
+ ArachnaeNode_Race_WeaponSmith
+
+
7.0
14
diff --git a/1.6/1.6/Defs/ThinkTreeDefs/ARA_ThinkTrees.xml b/1.6/1.6/Defs/ThinkTreeDefs/ARA_ThinkTrees.xml
index 7adc599..4033614 100644
--- a/1.6/1.6/Defs/ThinkTreeDefs/ARA_ThinkTrees.xml
+++ b/1.6/1.6/Defs/ThinkTreeDefs/ARA_ThinkTrees.xml
@@ -896,6 +896,35 @@
Humanlike_PreMain
+
+
+ ArachnaeNode_Race_Myrmecocystus
+
+
+
+
+
+ ArachnaeNode_Race_Myrmecocystus
+
+
+
+
+
+
+ ARA_HiveMindMaster
+ 0~5
+
+
+
+
+
+ ARA_HiveMindDrone
+ 0~5
+
+
+
+
+
diff --git a/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/ARA_Building_Ootheca.xml b/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/ARA_Building_Ootheca.xml
index 724dca0..05fb611 100644
--- a/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/ARA_Building_Ootheca.xml
+++ b/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/ARA_Building_Ootheca.xml
@@ -8,7 +8,7 @@
目标
速度乘数
质量乘数
- 幼虫种种进入中
+ 幼虫种进入中
剩余秒数
幼虫种正在路上
剩余时间
@@ -97,4 +97,76 @@
需要研究
+
+
+
+ 速度因子
+ ✓ 位于孵化间内
+ ✗ 不在孵化间内
+ 营养液: {0} (+{1}%)
+ 无附近营养液
+ 营养液检测半径: {0}格
+ 总速度乘数: {0}
+
+ 质量因子
+ 建筑健康度: {0}
+ 房间因子: 正常
+ 房间因子:
+ 附近卵: {0} (-{1}%)
+ 无附近卵
+ 卵检测半径: {0}格
+ 总质量乘数: {0}
+
+ 呼叫幼虫
+ 幼虫将前往卵荚进行孵化
+ 幼虫搜索半径: {0}格
+
+ 已经在孵化中
+ 请先取消当前孵化
+ 幼虫已在途中
+ 未找到可用幼虫
+ 必须是幼虫种
+ 幼虫已呼叫
+ 即将到达
+ 幼虫已到达
+ 正在激活卵荚
+ 开始孵化
+ 过程将在
+ 天内完成(基础时间)
+ 孵化已取消
+ 内容物已丢失
+ 孵化完成
+ 已出现,质量为
+ 优秀
+ 良好
+ 一般
+ 较差
+ 很差
+ 质量
+
+ 正在孵化
+ 进度
+ 剩余时间
+ 天
+ 小时
+ 速度
+ 质量
+ 幼虫正在操作
+ 秒剩余
+ 幼虫在途中
+ 目标
+ 速度乘数
+ 质量乘数
+
+ 呼叫幼虫
+ 取消孵化
+ 取消当前孵化过程,卵荚内容物将丢失
+
+ 孵化: {0}
+ 选择要孵化的生物类型。幼虫将前来激活卵荚。
+ 当前选择: {0}
+ 孵化时间: {0}天
+ 选择孵化目标
+ 需要研究:
+ 孵化目标已切换为: {0}
diff --git a/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/ARA_EquipmentIncubator.xml b/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/ARA_EquipmentIncubator.xml
new file mode 100644
index 0000000..439bc1a
--- /dev/null
+++ b/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/ARA_EquipmentIncubator.xml
@@ -0,0 +1,90 @@
+
+
+
+ 速度因子
+ ✓ 位于孵化间内
+ ✗ 不在孵化间内
+ 营养液: {0} (+{1}%)
+ 无附近营养液
+ 营养液检测半径: {0}格
+ 总速度乘数: {0}
+
+ 质量因子
+ 建筑健康度: {0}
+ 房间因子: 正常
+ 房间因子:
+ 附近卵: {0} (-{1}%)
+ 无附近卵
+ 卵检测半径: {0}格
+ 总质量乘数: {0}
+
+ 呼叫幼虫
+ 幼虫将前往卵荚生产装备
+ 幼虫搜索半径: {0}格
+
+ 已经在生产中
+ 请先取消当前生产
+ 幼虫已在途中
+ 未找到可用幼虫
+ 幼虫必须是ArachnaeBase_Race_Larva种族
+ 幼虫已呼叫
+ 即将到达
+ 幼虫已到达
+ 正在激活装备卵荚
+ 开始生产
+ 过程将在
+ 天内完成(基础时间)
+ 生产已取消
+ 内容物已丢失
+ 生产完成
+ 已生产出,质量为
+ 传奇
+ 杰作
+ 精良
+ 良好
+ 一般
+ 质量
+
+ 正在生产
+ 进度
+ 剩余时间
+ 天
+ 小时
+ 速度
+ 质量
+ 幼虫正在操作
+ 秒剩余
+ 幼虫在途中
+ 目标
+ 速度乘数
+ 质量乘数
+
+ 呼叫幼虫
+ 取消生产
+ 取消当前生产过程,卵荚内容物将丢失
+
+ 生产: {0}
+ 选择要生产的装备类型。幼虫将前来激活装备卵荚。
+ 当前选择: {0}
+ 生产时间: {0}天
+ 选择生产目标
+ 需要研究:
+ 生产目标已切换为: {0}
+
+ 装备生产
+ 这不是装备卵荚
+ 生产进度
+ 生产进度
+ 质量进度
+ 幼虫正在激活卵荚
+ 秒剩余
+ 幼虫在途中
+ 准备生产
+ 未选择生产目标
+
+ 生产目标:{0}
+ 需要
+ 无描述
+ 研究: {0} (已完成)
+ 研究: {0} (需要)
+
diff --git a/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/ARA_HoneyProduction.xml b/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/ARA_HoneyProduction.xml
new file mode 100644
index 0000000..16f01fc
--- /dev/null
+++ b/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/ARA_HoneyProduction.xml
@@ -0,0 +1,29 @@
+
+
+
+ 当前储量
+ 储量等级
+ 生产效率:(饮食消耗速率的)
+ 食物消耗速率
+ 蜜罐增长速率
+ 警告:蜜罐已满,停止生产!
+ 满仓
+ 高存量
+ 中存量
+ 低存量
+ 空仓
+
+
+ 挤出了 {0} 个虫蜜
+
+
+ {0} 用虫蜜喂养了 {1}
+
+
+ 挤出虫蜜
+ 用虫蜜喂养
+
+
+ 蜜罐生产
+ 虫蜜
+
\ No newline at end of file
diff --git a/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/ARA_InteractiveProducer_Keys.xml b/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/ARA_InteractiveProducer_Keys.xml
index d015969..a2fbe03 100644
--- a/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/ARA_InteractiveProducer_Keys.xml
+++ b/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/ARA_InteractiveProducer_Keys.xml
@@ -18,7 +18,7 @@
未孵化,需要 {0} 交互\n\n在生产完成时,剩余的营养将重新转变为物品。
任何阿拉克涅虫族
{0} 个物品可用
- 物品品质
+ 孵化品质
所需营养
@@ -31,4 +31,14 @@
生产槽位: {0} / {1}
等待队列: {0}
生产速度
+
+ 甲壳部位数量
+ 生长速率
+ 甲壳剥离
+ 切换是否允许此单位自动剥离甲壳。当甲壳过厚,单位会自动剥离甲壳以产生甲壳素。
+ 剥离了{0}个甲壳
+ 甲壳剥离功能已禁用
+ 冷却中:{0}秒后可以再次剥离
+ 甲壳存量未达到阈值({0}%)
+ 可以开始剥离甲壳
\ No newline at end of file
diff --git a/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/ARA_SwarmMaintenance.xml b/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/ARA_SwarmMaintenance.xml
new file mode 100644
index 0000000..6c47db5
--- /dev/null
+++ b/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/ARA_SwarmMaintenance.xml
@@ -0,0 +1,13 @@
+
+
+
+ 维护度: {0}/{1} ({2})
+ 维护度耗尽!建筑正在损坏!
+ 维护度严重不足!
+ 需要维护
+ 每日维护度递减: {0}
+ 正在寻找空闲的维护者...
+ 找不到符合条件的空闲维护者
+ 允许维护的种族:
+ 未指定可维护的种族
+
diff --git a/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/ARA_TemperatureRuinable.xml b/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/ARA_TemperatureRuinable.xml
new file mode 100644
index 0000000..b60fb0e
--- /dev/null
+++ b/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/ARA_TemperatureRuinable.xml
@@ -0,0 +1,13 @@
+
+
+
+ 安全温度范围: {0} - {1}
+ 当前温度: {0}
+ 损坏进度: {0}
+ 正在受到温度伤害
+ 未受到温度影响
+ 过冷
+ 过热
+ {0} 受到温度伤害
+ {0} 因{1}正在受到温度伤害。\n\n当前温度: {2}\n安全温度范围: {3} - {4}\n\n建议尽快调整温度至安全范围,以避免结构损坏。
+
diff --git a/Content/Textures/ArachnaeSwarm/UI/Commands/ARA_CallLarva.png b/Content/Textures/ArachnaeSwarm/UI/Commands/ARA_CallLarva.png
new file mode 100644
index 0000000..4eb5086
Binary files /dev/null and b/Content/Textures/ArachnaeSwarm/UI/Commands/ARA_CallLarva.png differ
diff --git a/Content/Textures/ArachnaeSwarm/UI/Commands/ARA_ShowRadius.png b/Content/Textures/ArachnaeSwarm/UI/Commands/ARA_ShowRadius.png
new file mode 100644
index 0000000..bfeb9bf
Binary files /dev/null and b/Content/Textures/ArachnaeSwarm/UI/Commands/ARA_ShowRadius.png differ
diff --git a/Content/Textures/ArachnaeSwarm/UI/Commands/ARA_StripChitin.png b/Content/Textures/ArachnaeSwarm/UI/Commands/ARA_StripChitin.png
new file mode 100644
index 0000000..b98d7e4
Binary files /dev/null and b/Content/Textures/ArachnaeSwarm/UI/Commands/ARA_StripChitin.png differ
diff --git a/Source/ArachnaeSwarm/.vs/ArachnaeSwarm/v17/.suo b/Source/ArachnaeSwarm/.vs/ArachnaeSwarm/v17/.suo
index cd88601..4db06b4 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 20e9236..3bc5b0e 100644
--- a/Source/ArachnaeSwarm/.vs/ArachnaeSwarm/v17/DocumentLayout.json
+++ b/Source/ArachnaeSwarm/.vs/ArachnaeSwarm/v17/DocumentLayout.json
@@ -1,58 +1,58 @@
{
"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\\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\\building_equipmentootheca.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:buildings\\building_equipmentootheca\\building_equipmentootheca.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
+ },
+ {
+ "AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|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_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\\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\\thing_comps\\ara_compextraincubationinfo\\compproperties_extraincubationinfo.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:thing_comps\\ara_compextraincubationinfo\\compproperties_extraincubationinfo.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\\thing_comps\\ara_compextraincubationinfo\\compextraincubationinfo.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:thing_comps\\ara_compextraincubationinfo\\compextraincubationinfo.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|d:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\placeworker\\compproperties_customradius.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
- "RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:placeworker\\compproperties_customradius.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_swarmmaintenance\\comp_swarmmaintenance.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:building_comps\\ara_swarmmaintenance\\comp_swarmmaintenance.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\\flyover\\ara_groundstrafing\\compgroundstrafing.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
- "RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:flyover\\ara_groundstrafing\\compgroundstrafing.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_buildingterrainspawn\\compdelayedterrainspawn.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:building_comps\\ara_buildingterrainspawn\\compdelayedterrainspawn.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
- "AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\flyover\\ara_sectorsurveillance\\compsectorsurveillance.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
- "RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:flyover\\ara_sectorsurveillance\\compsectorsurveillance.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\\comptemperatureruinabledamage.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:building_comps\\comptemperatureruinabledamage.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\\flyover\\thingclassflyover.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
- "RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:flyover\\thingclassflyover.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\\flyover\\ara_aircrafthangar\\compabilityeffect_aircraftstrike.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
- "RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:flyover\\ara_aircrafthangar\\compabilityeffect_aircraftstrike.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
- },
- {
- "AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\abilities\\ara_givehediffwithskillduration\\compabilityeffect_givehediffwithskillduration.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
- "RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:abilities\\ara_givehediffwithskillduration\\compabilityeffect_givehediffwithskillduration.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
- },
- {
- "AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\abilities\\ara_showtemperaturerange\\compabilityeffect_abilityshowtemperaturerange.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\\abilities\\ara_showtemperaturerange\\compabilityeffect_abilityshowtemperaturerange.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:abilities\\ara_showtemperaturerange\\compabilityeffect_abilityshowtemperaturerange.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
- "AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\abilities\\ara_showspawnablepawnslist\\compabilityeffect_abilityshowspawnablepawns.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
- "RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:abilities\\ara_showspawnablepawnslist\\compabilityeffect_abilityshowspawnablepawns.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_swarmmaintain\\jobdriver_swarmmaintain.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:jobs\\jobdriver_swarmmaintain\\jobdriver_swarmmaintain.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\\flyover\\ara_spawnflyover\\compabilityeffect_spawnflyover.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
- "RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:flyover\\ara_spawnflyover\\compabilityeffect_spawnflyover.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\\flyover\\ara_aircrafthangar\\compaircrafthangar.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
- "RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:flyover\\ara_aircrafthangar\\compaircrafthangar.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\\flyover\\ara_aircrafthangar\\worldcomponent_aircraftmanager.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
- "RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:flyover\\ara_aircrafthangar\\worldcomponent_aircraftmanager.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\\flyover\\ara_flyoverescort\\compproperties_flyoverescort.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
- "RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:flyover\\ara_flyoverescort\\compproperties_flyoverescort.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_feedwithhoney\\jobdriver_feedwithhoney.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:jobs\\jobdriver_feedwithhoney\\jobdriver_feedwithhoney.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
}
],
"DocumentGroupContainers": [
@@ -62,21 +62,8 @@
"DocumentGroups": [
{
"DockedWidth": 200,
- "SelectedChildIndex": 0,
+ "SelectedChildIndex": 3,
"Children": [
- {
- "$type": "Document",
- "DocumentIndex": 0,
- "Title": "Building_Ootheca.cs",
- "DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\Building_Ootheca.cs",
- "RelativeDocumentMoniker": "Buildings\\Building_Ootheca\\Building_Ootheca.cs",
- "ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\Building_Ootheca.cs",
- "RelativeToolTip": "Buildings\\Building_Ootheca\\Building_Ootheca.cs",
- "ViewState": "AgIAAK8CAAAAAAAAAAA9wPACAAAMAAAAAAAAAA==",
- "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
- "WhenOpened": "2025-12-15T07:28:18.272Z",
- "EditorCaption": ""
- },
{
"$type": "Bookmark",
"Name": "ST:0:0:{1c4feeaa-4718-4aa9-859d-94ce25d182ba}"
@@ -84,147 +71,171 @@
{
"$type": "Document",
"DocumentIndex": 1,
- "Title": "CompProperties_CustomRadius.cs",
- "DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Placeworker\\CompProperties_CustomRadius.cs",
- "RelativeDocumentMoniker": "Placeworker\\CompProperties_CustomRadius.cs",
- "ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Placeworker\\CompProperties_CustomRadius.cs",
- "RelativeToolTip": "Placeworker\\CompProperties_CustomRadius.cs",
- "ViewState": "AgIAAAIAAAAAAAAAAAAAACMAAAA/AAAAAAAAAA==",
+ "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": "AgIAAAQAAAAAAAAAAAAmwDMBAAAlAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
- "WhenOpened": "2025-12-15T07:21:04.756Z",
+ "WhenOpened": "2025-12-15T17:55:40.041Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 3,
- "Title": "CompSectorSurveillance.cs",
- "DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_SectorSurveillance\\CompSectorSurveillance.cs",
- "RelativeDocumentMoniker": "Flyover\\ARA_SectorSurveillance\\CompSectorSurveillance.cs",
- "ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_SectorSurveillance\\CompSectorSurveillance.cs",
- "RelativeToolTip": "Flyover\\ARA_SectorSurveillance\\CompSectorSurveillance.cs",
- "ViewState": "AgIAAPACAAAAAAAAAAAAABEDAAAAAAAAAAAAAA==",
+ "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": "AgIAAAAAAAAAAAAAAADwvxgAAAAdAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
- "WhenOpened": "2025-10-30T13:52:54.896Z"
+ "WhenOpened": "2025-12-15T17:32:18.493Z",
+ "EditorCaption": ""
},
{
"$type": "Document",
- "DocumentIndex": 2,
- "Title": "CompGroundStrafing.cs",
- "DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_GroundStrafing\\CompGroundStrafing.cs",
- "RelativeDocumentMoniker": "Flyover\\ARA_GroundStrafing\\CompGroundStrafing.cs",
- "ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_GroundStrafing\\CompGroundStrafing.cs",
- "RelativeToolTip": "Flyover\\ARA_GroundStrafing\\CompGroundStrafing.cs",
- "ViewState": "AgIAAGwBAAAAAAAAAAArwIYBAAAFAAAAAAAAAA==",
+ "DocumentIndex": 0,
+ "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": "AgIAADIBAAAAAAAAAAAAAEQBAAAIAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
- "WhenOpened": "2025-10-30T13:00:11.18Z"
- },
- {
- "$type": "Document",
- "DocumentIndex": 6,
- "Title": "CompAbilityEffect_GiveHediffWithSkillDuration.cs",
- "DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_GiveHediffWithSkillDuration\\CompAbilityEffect_GiveHediffWithSkillDuration.cs",
- "RelativeDocumentMoniker": "Abilities\\ARA_GiveHediffWithSkillDuration\\CompAbilityEffect_GiveHediffWithSkillDuration.cs",
- "ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_GiveHediffWithSkillDuration\\CompAbilityEffect_GiveHediffWithSkillDuration.cs",
- "RelativeToolTip": "Abilities\\ARA_GiveHediffWithSkillDuration\\CompAbilityEffect_GiveHediffWithSkillDuration.cs",
- "ViewState": "AgIAAEsAAAAAAAAAAAAWwGAAAAAAAAAAAAAAAA==",
- "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
- "WhenOpened": "2025-10-29T14:40:47.422Z"
- },
- {
- "$type": "Document",
- "DocumentIndex": 7,
- "Title": "CompAbilityEffect_AbilityShowTemperatureRange.cs",
- "DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_ShowTemperatureRange\\CompAbilityEffect_AbilityShowTemperatureRange.cs",
- "RelativeDocumentMoniker": "Abilities\\ARA_ShowTemperatureRange\\CompAbilityEffect_AbilityShowTemperatureRange.cs",
- "ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_ShowTemperatureRange\\CompAbilityEffect_AbilityShowTemperatureRange.cs",
- "RelativeToolTip": "Abilities\\ARA_ShowTemperatureRange\\CompAbilityEffect_AbilityShowTemperatureRange.cs",
- "ViewState": "AgIAAAAAAAAAAAAAAAAAABcAAAArAAAAAAAAAA==",
- "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
- "WhenOpened": "2025-10-29T14:40:43.525Z"
+ "WhenOpened": "2025-12-15T17:32:08.87Z",
+ "EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 5,
- "Title": "CompAbilityEffect_AircraftStrike.cs",
- "DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_AircraftHangar\\CompAbilityEffect_AircraftStrike.cs",
- "RelativeDocumentMoniker": "Flyover\\ARA_AircraftHangar\\CompAbilityEffect_AircraftStrike.cs",
- "ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_AircraftHangar\\CompAbilityEffect_AircraftStrike.cs",
- "RelativeToolTip": "Flyover\\ARA_AircraftHangar\\CompAbilityEffect_AircraftStrike.cs",
- "ViewState": "AgIAAHYAAAAAAAAAAAAtwJcAAAArAAAAAAAAAA==",
+ "Title": "CompExtraIncubationInfo.cs",
+ "DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Thing_Comps\\ARA_CompExtraIncubationInfo\\CompExtraIncubationInfo.cs",
+ "RelativeDocumentMoniker": "Thing_Comps\\ARA_CompExtraIncubationInfo\\CompExtraIncubationInfo.cs",
+ "ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Thing_Comps\\ARA_CompExtraIncubationInfo\\CompExtraIncubationInfo.cs",
+ "RelativeToolTip": "Thing_Comps\\ARA_CompExtraIncubationInfo\\CompExtraIncubationInfo.cs",
+ "ViewState": "AgIAAAAAAAAAAAAAAADwvwAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
- "WhenOpened": "2025-10-29T11:22:34.783Z"
+ "WhenOpened": "2025-12-15T17:13:20.87Z",
+ "EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 4,
- "Title": "ThingclassFlyOver.cs",
- "DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ThingclassFlyOver.cs",
- "RelativeDocumentMoniker": "Flyover\\ThingclassFlyOver.cs",
- "ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ThingclassFlyOver.cs",
- "RelativeToolTip": "Flyover\\ThingclassFlyOver.cs",
- "ViewState": "AgIAAIkCAAAAAAAAAAAawI8CAAANAAAAAAAAAA==",
+ "Title": "CompProperties_ExtraIncubationInfo.cs",
+ "DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Thing_Comps\\ARA_CompExtraIncubationInfo\\CompProperties_ExtraIncubationInfo.cs",
+ "RelativeDocumentMoniker": "Thing_Comps\\ARA_CompExtraIncubationInfo\\CompProperties_ExtraIncubationInfo.cs",
+ "ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Thing_Comps\\ARA_CompExtraIncubationInfo\\CompProperties_ExtraIncubationInfo.cs",
+ "RelativeToolTip": "Thing_Comps\\ARA_CompExtraIncubationInfo\\CompProperties_ExtraIncubationInfo.cs",
+ "ViewState": "AgIAAAAAAAAAAAAAAADwvwAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
- "WhenOpened": "2025-10-29T14:17:06.867Z"
+ "WhenOpened": "2025-12-15T17:13:20.069Z",
+ "EditorCaption": ""
+ },
+ {
+ "$type": "Document",
+ "DocumentIndex": 2,
+ "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": "AgIAAAAAAAAAAAAAAADwvxIAAAA4AAAAAAAAAA==",
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
+ "WhenOpened": "2025-12-15T17:11:54.114Z",
+ "EditorCaption": ""
+ },
+ {
+ "$type": "Document",
+ "DocumentIndex": 6,
+ "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": "AgIAAA0DAAAAAAAAAAAAACAAAAArAAAAAAAAAA==",
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
+ "WhenOpened": "2025-12-15T17:10:05.509Z",
+ "EditorCaption": ""
+ },
+ {
+ "$type": "Document",
+ "DocumentIndex": 7,
+ "Title": "Comp_SwarmMaintenance.cs",
+ "DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_SwarmMaintenance\\Comp_SwarmMaintenance.cs",
+ "RelativeDocumentMoniker": "Building_Comps\\ARA_SwarmMaintenance\\Comp_SwarmMaintenance.cs",
+ "ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_SwarmMaintenance\\Comp_SwarmMaintenance.cs",
+ "RelativeToolTip": "Building_Comps\\ARA_SwarmMaintenance\\Comp_SwarmMaintenance.cs",
+ "ViewState": "AgIAABkBAAAAAAAAAAAAAE0BAABIAAAAAAAAAA==",
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
+ "WhenOpened": "2025-12-15T16:59:28.717Z",
+ "EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 8,
- "Title": "CompAbilityEffect_AbilityShowSpawnablePawns.cs",
- "DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_ShowSpawnablePawnsList\\CompAbilityEffect_AbilityShowSpawnablePawns.cs",
- "RelativeDocumentMoniker": "Abilities\\ARA_ShowSpawnablePawnsList\\CompAbilityEffect_AbilityShowSpawnablePawns.cs",
- "ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_ShowSpawnablePawnsList\\CompAbilityEffect_AbilityShowSpawnablePawns.cs",
- "RelativeToolTip": "Abilities\\ARA_ShowSpawnablePawnsList\\CompAbilityEffect_AbilityShowSpawnablePawns.cs",
- "ViewState": "AgIAABYAAAAAAAAAAAAuwBYAAAArAAAAAAAAAA==",
+ "Title": "CompDelayedTerrainSpawn.cs",
+ "DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_BuildingTerrainSpawn\\CompDelayedTerrainSpawn.cs",
+ "RelativeDocumentMoniker": "Building_Comps\\ARA_BuildingTerrainSpawn\\CompDelayedTerrainSpawn.cs",
+ "ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_BuildingTerrainSpawn\\CompDelayedTerrainSpawn.cs",
+ "RelativeToolTip": "Building_Comps\\ARA_BuildingTerrainSpawn\\CompDelayedTerrainSpawn.cs",
+ "ViewState": "AgIAAAAAAAAAAAAAAADwvwAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
- "WhenOpened": "2025-10-29T14:40:40.237Z"
- },
- {
- "$type": "Document",
- "DocumentIndex": 10,
- "Title": "CompAircraftHangar.cs",
- "DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_AircraftHangar\\CompAircraftHangar.cs",
- "RelativeDocumentMoniker": "Flyover\\ARA_AircraftHangar\\CompAircraftHangar.cs",
- "ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_AircraftHangar\\CompAircraftHangar.cs",
- "RelativeToolTip": "Flyover\\ARA_AircraftHangar\\CompAircraftHangar.cs",
- "ViewState": "AgIAABcAAAAAAAAAAAAQwCQAAAAnAAAAAAAAAA==",
- "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
- "WhenOpened": "2025-10-29T11:39:22.563Z"
+ "WhenOpened": "2025-12-15T16:59:21.314Z",
+ "EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 9,
- "Title": "CompAbilityEffect_SpawnFlyOver.cs",
- "DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_SpawnFlyOver\\CompAbilityEffect_SpawnFlyOver.cs",
- "RelativeDocumentMoniker": "Flyover\\ARA_SpawnFlyOver\\CompAbilityEffect_SpawnFlyOver.cs",
- "ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_SpawnFlyOver\\CompAbilityEffect_SpawnFlyOver.cs",
- "RelativeToolTip": "Flyover\\ARA_SpawnFlyOver\\CompAbilityEffect_SpawnFlyOver.cs",
- "ViewState": "AgIAAFMDAAAAAAAAAAAawG8DAAAAAAAAAAAAAA==",
+ "Title": "CompTemperatureRuinableDamage.cs",
+ "DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\CompTemperatureRuinableDamage.cs",
+ "RelativeDocumentMoniker": "Building_Comps\\CompTemperatureRuinableDamage.cs",
+ "ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\CompTemperatureRuinableDamage.cs",
+ "RelativeToolTip": "Building_Comps\\CompTemperatureRuinableDamage.cs",
+ "ViewState": "AgIAAB8AAAAAAAAAAAAAwDoAAAAlAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
- "WhenOpened": "2025-10-29T13:37:35.758Z"
+ "WhenOpened": "2025-12-15T16:52:05.743Z",
+ "EditorCaption": ""
+ },
+ {
+ "$type": "Document",
+ "DocumentIndex": 10,
+ "Title": "CompAbilityEffect_AbilityShowTemperatureRange.cs",
+ "DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_ShowTemperatureRange\\CompAbilityEffect_AbilityShowTemperatureRange.cs",
+ "RelativeDocumentMoniker": "Abilities\\ARA_ShowTemperatureRange\\CompAbilityEffect_AbilityShowTemperatureRange.cs",
+ "ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_ShowTemperatureRange\\CompAbilityEffect_AbilityShowTemperatureRange.cs",
+ "RelativeToolTip": "Abilities\\ARA_ShowTemperatureRange\\CompAbilityEffect_AbilityShowTemperatureRange.cs",
+ "ViewState": "AgIAAHUAAAAAAAAAAAAAAJIAAABEAAAAAAAAAA==",
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
+ "WhenOpened": "2025-12-15T16:52:00.971Z",
+ "EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 11,
- "Title": "WorldComponent_AircraftManager.cs",
- "DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_AircraftHangar\\WorldComponent_AircraftManager.cs",
- "RelativeDocumentMoniker": "Flyover\\ARA_AircraftHangar\\WorldComponent_AircraftManager.cs",
- "ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_AircraftHangar\\WorldComponent_AircraftManager.cs",
- "RelativeToolTip": "Flyover\\ARA_AircraftHangar\\WorldComponent_AircraftManager.cs",
- "ViewState": "AgIAAJUAAAAAAAAAAAAowK4AAAAUAAAAAAAAAA==",
+ "Title": "JobDriver_SwarmMaintain.cs",
+ "DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_SwarmMaintain\\JobDriver_SwarmMaintain.cs",
+ "RelativeDocumentMoniker": "Jobs\\JobDriver_SwarmMaintain\\JobDriver_SwarmMaintain.cs",
+ "ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_SwarmMaintain\\JobDriver_SwarmMaintain.cs",
+ "RelativeToolTip": "Jobs\\JobDriver_SwarmMaintain\\JobDriver_SwarmMaintain.cs",
+ "ViewState": "AgIAAAAAAAAAAAAAAAAAAB0AAABPAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
- "WhenOpened": "2025-10-29T14:05:39.817Z"
+ "WhenOpened": "2025-12-15T16:36:34.047Z",
+ "EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 12,
- "Title": "CompProperties_FlyOverEscort.cs",
- "DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_FlyOverEscort\\CompProperties_FlyOverEscort.cs",
- "RelativeDocumentMoniker": "Flyover\\ARA_FlyOverEscort\\CompProperties_FlyOverEscort.cs",
- "ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_FlyOverEscort\\CompProperties_FlyOverEscort.cs",
- "RelativeToolTip": "Flyover\\ARA_FlyOverEscort\\CompProperties_FlyOverEscort.cs",
- "ViewState": "AgIAAAAAAAAAAAAAAAAAABAAAAAvAAAAAAAAAA==",
+ "Title": "JobDriver_FeedWithHoney.cs",
+ "DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_FeedWithHoney\\JobDriver_FeedWithHoney.cs",
+ "RelativeDocumentMoniker": "Jobs\\JobDriver_FeedWithHoney\\JobDriver_FeedWithHoney.cs",
+ "ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_FeedWithHoney\\JobDriver_FeedWithHoney.cs",
+ "RelativeToolTip": "Jobs\\JobDriver_FeedWithHoney\\JobDriver_FeedWithHoney.cs",
+ "ViewState": "AgIAACEAAAAAAAAAAAAYwEsAAAAOAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
- "WhenOpened": "2025-10-29T12:59:07.753Z"
+ "WhenOpened": "2025-12-15T16:35:50.511Z",
+ "EditorCaption": ""
}
]
}
diff --git a/Source/ArachnaeSwarm/ARA_HediffDefOf.cs b/Source/ArachnaeSwarm/ARA_HediffDefOf.cs
index 419e1be..7620d1e 100644
--- a/Source/ArachnaeSwarm/ARA_HediffDefOf.cs
+++ b/Source/ArachnaeSwarm/ARA_HediffDefOf.cs
@@ -19,10 +19,22 @@ namespace ArachnaeSwarm
public static class ARA_JobDefOf
{
public static JobDef ARA_OperateIncubator;
-
+ public static JobDef ARA_OperateEquipmentIncubator;
+ public static JobDef ARA_SwarmMaintain;
+
static ARA_JobDefOf()
{
DefOfHelper.EnsureInitializedInCtor(typeof(ARA_JobDefOf));
}
}
+ [DefOf]
+ public static class ARA_EffecterDefOf
+ {
+ public static EffecterDef EatVegetarian;
+
+ static ARA_EffecterDefOf()
+ {
+ DefOfHelper.EnsureInitializedInCtor(typeof(ARA_EffecterDefOf));
+ }
+ }
}
diff --git a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj
index 716e987..e1f2062 100644
--- a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj
+++ b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj
@@ -114,11 +114,16 @@
+
+
+
+
+
@@ -131,6 +136,8 @@
+
+
@@ -164,7 +171,18 @@
+
+
+
+
+
+
+
+
+
+
+
@@ -196,7 +214,7 @@
-
+
diff --git a/Source/ArachnaeSwarm/Building_Comps/ARA_CompInteractiveProducer/CompTemperatureRuinableDamage.cs b/Source/ArachnaeSwarm/Building_Comps/ARA_CompInteractiveProducer/CompTemperatureRuinableDamage.cs
deleted file mode 100644
index a4b6075..0000000
--- a/Source/ArachnaeSwarm/Building_Comps/ARA_CompInteractiveProducer/CompTemperatureRuinableDamage.cs
+++ /dev/null
@@ -1,91 +0,0 @@
-using RimWorld;
-using Verse;
-
-namespace ArachnaeSwarm
-{
- public class CompProperties_TemperatureRuinableDamage : CompProperties
- {
- public float minSafeTemperature;
- public float maxSafeTemperature = 100f;
- public float progressPerDegreePerTick = 1E-05f; // 修改参数名以匹配标准调用方式
- public float damagePerTick = 1f; // 每tick造成的伤害值
- public float recoveryRate = 0.001f; // 温度恢复正常时的恢复速率
-
- public CompProperties_TemperatureRuinableDamage()
- {
- compClass = typeof(CompTemperatureRuinableDamage);
- }
- }
-
- public class CompTemperatureRuinableDamage : ThingComp
- {
- private float ruinedPercent; // 修改变量名以匹配标准
- private bool isRuined; // 修改变量名以匹配标准
-
- public CompProperties_TemperatureRuinableDamage Props => (CompProperties_TemperatureRuinableDamage)props;
-
- public override void CompTick()
- {
- base.CompTick();
- if (parent.AmbientTemperature < Props.minSafeTemperature || parent.AmbientTemperature > Props.maxSafeTemperature)
- {
- float tempDelta = 0f;
- if (parent.AmbientTemperature < Props.minSafeTemperature)
- {
- tempDelta = Props.minSafeTemperature - parent.AmbientTemperature;
- }
- else if (parent.AmbientTemperature > Props.maxSafeTemperature)
- {
- tempDelta = parent.AmbientTemperature - Props.maxSafeTemperature;
- }
-
- // 累积损坏进度
- ruinedPercent += tempDelta * Props.progressPerDegreePerTick;
-
- // 只有在已损坏的情况下才每tick造成持续伤害
- if (isRuined)
- {
- parent.TakeDamage(new DamageInfo(DamageDefOf.Deterioration, Props.damagePerTick));
- }
-
- // 标记为已受损
- isRuined = true;
- }
- else
- {
- // 当温度恢复正常时,逐渐减少损坏进度而不是重置
- if (isRuined && ruinedPercent > 0f)
- {
- ruinedPercent -= Props.recoveryRate;
- if (ruinedPercent <= 0f)
- {
- ruinedPercent = 0f;
- isRuined = false;
- }
- }
-
- // 即使温度正常,如果已损坏也要继续造成伤害直到恢复
- if (isRuined)
- {
- parent.TakeDamage(new DamageInfo(DamageDefOf.Deterioration, Props.damagePerTick));
- }
- }
- }
-
- public override void PostExposeData()
- {
- base.PostExposeData();
- Scribe_Values.Look(ref ruinedPercent, "ruinedPercent", 0f);
- Scribe_Values.Look(ref isRuined, "isRuined", false);
- }
-
- public override string CompInspectStringExtra()
- {
- if (ruinedPercent > 0f)
- {
- return "CocoonRuinedByTemperature".Translate() + ": " + ruinedPercent.ToStringPercent();
- }
- return base.CompInspectStringExtra();
- }
- }
-}
\ No newline at end of file
diff --git a/Source/ArachnaeSwarm/Building_Comps/ARA_SwarmMaintenance/CompProperties_SwarmMaintenance.cs b/Source/ArachnaeSwarm/Building_Comps/ARA_SwarmMaintenance/CompProperties_SwarmMaintenance.cs
new file mode 100644
index 0000000..e33b6b8
--- /dev/null
+++ b/Source/ArachnaeSwarm/Building_Comps/ARA_SwarmMaintenance/CompProperties_SwarmMaintenance.cs
@@ -0,0 +1,36 @@
+// File: Comps/CompProperties_SwarmMaintenance.cs
+using RimWorld;
+using System.Collections.Generic;
+using Verse;
+
+namespace ArachnaeSwarm
+{
+ public class CompProperties_SwarmMaintenance : CompProperties
+ {
+ // 最大维护度
+ public float maxMaintenance = 100f;
+
+ // 每天下降的维护度
+ public float maintenanceDecayPerDay = 10f;
+
+ // 维护度为0时每秒受到的伤害
+ public float damagePerSecondWhenEmpty = 1f;
+
+ // 维护度达到多少时开始显示警告(百分比)
+ public float warningThreshold = 0.3f;
+
+ // 可维护的种族列表
+ public List allowedRaces;
+
+ // 检查分配工作的间隔(ticks)
+ public int assignJobCheckInterval = 600; // 10秒
+
+ // 维护度低于多少时开始寻找维护者(百分比)
+ public float maintenanceThresholdForJob = 0.9f;
+
+ public CompProperties_SwarmMaintenance()
+ {
+ compClass = typeof(Comp_SwarmMaintenance);
+ }
+ }
+}
diff --git a/Source/ArachnaeSwarm/Building_Comps/ARA_SwarmMaintenance/Comp_SwarmMaintenance.cs b/Source/ArachnaeSwarm/Building_Comps/ARA_SwarmMaintenance/Comp_SwarmMaintenance.cs
new file mode 100644
index 0000000..99fdb48
--- /dev/null
+++ b/Source/ArachnaeSwarm/Building_Comps/ARA_SwarmMaintenance/Comp_SwarmMaintenance.cs
@@ -0,0 +1,399 @@
+// File: Comps/Comp_SwarmMaintenance.cs
+using System.Collections.Generic;
+using RimWorld;
+using UnityEngine;
+using Verse;
+using Verse.AI;
+
+namespace ArachnaeSwarm
+{
+ public class Comp_SwarmMaintenance : ThingComp
+ {
+ // 属性访问器
+ public CompProperties_SwarmMaintenance Props => (CompProperties_SwarmMaintenance)props;
+
+ // 当前维护度
+ private float currentMaintenance;
+
+ // 上次更新维护度的时间
+ private int lastMaintenanceTick = -99999;
+
+ // 上次检查分配工作的时间
+ private int lastJobCheckTick = -99999;
+
+ // 当维护度为0时,每秒受到的伤害计时器
+ private int nextDamageTick = -99999;
+
+ // 是否正在寻找维护者
+ private bool seekingMaintainer = false;
+
+ // 找不到维护者的提示信息
+ private string noMaintainerWarning = "";
+ private int lastWarningTick = -99999;
+ private const int WarningDuration = 2500; // 警告显示41.67秒
+
+ // 属性访问
+ public float CurrentMaintenance => currentMaintenance;
+ public float MaxMaintenance => Props.maxMaintenance;
+ public float MaintenancePercentage => currentMaintenance / MaxMaintenance;
+ public bool NeedsMaintenance => currentMaintenance < MaxMaintenance * Props.maintenanceThresholdForJob;
+ public bool IsCritical => currentMaintenance <= MaxMaintenance * Props.warningThreshold;
+ public bool IsEmpty => currentMaintenance <= 0f;
+
+ // 初始化
+ public override void Initialize(CompProperties props)
+ {
+ base.Initialize(props);
+ currentMaintenance = Props.maxMaintenance; // 初始时满维护度
+ lastMaintenanceTick = Find.TickManager.TicksGame;
+ lastJobCheckTick = Find.TickManager.TicksGame;
+ }
+
+ // 序列化
+ public override void PostExposeData()
+ {
+ base.PostExposeData();
+ Scribe_Values.Look(ref currentMaintenance, "currentMaintenance", Props.maxMaintenance);
+ Scribe_Values.Look(ref lastMaintenanceTick, "lastMaintenanceTick", -99999);
+ Scribe_Values.Look(ref lastJobCheckTick, "lastJobCheckTick", -99999);
+ Scribe_Values.Look(ref nextDamageTick, "nextDamageTick", -99999);
+ Scribe_Values.Look(ref seekingMaintainer, "seekingMaintainer", false);
+ Scribe_Values.Look(ref noMaintainerWarning, "noMaintainerWarning", "");
+ Scribe_Values.Look(ref lastWarningTick, "lastWarningTick", -99999);
+ }
+
+ // 每tick更新
+ public override void CompTick()
+ {
+ base.CompTick();
+
+ int currentTick = Find.TickManager.TicksGame;
+
+ // 每2500tick(约41.67秒)更新一次维护度递减
+ if (currentTick - lastMaintenanceTick >= 2500)
+ {
+ UpdateMaintenanceDecay();
+ lastMaintenanceTick = currentTick;
+ }
+
+ // 定期检查是否需要分配维护工作
+ if (currentTick - lastJobCheckTick >= Props.assignJobCheckInterval)
+ {
+ CheckAndAssignMaintenanceJob();
+ lastJobCheckTick = currentTick;
+ }
+
+ // 如果维护度为0,每秒造成伤害
+ if (currentMaintenance <= 0f && currentTick >= nextDamageTick)
+ {
+ ApplyDamageWhenEmpty();
+ nextDamageTick = currentTick + 60; // 60ticks = 1秒
+ }
+
+ // 更新警告信息显示时间
+ if (currentTick - lastWarningTick >= WarningDuration)
+ {
+ noMaintainerWarning = "";
+ }
+ }
+
+ // 更新维护度递减
+ private void UpdateMaintenanceDecay()
+ {
+ if (parent == null || parent.Map == null)
+ return;
+
+ // 计算递减量:每天递减量转换为每2500tick(约0.347天)的递减量
+ float decayAmount = Props.maintenanceDecayPerDay * (2500f / 60000f);
+ currentMaintenance -= decayAmount;
+
+ // 确保不低于0
+ if (currentMaintenance < 0f)
+ currentMaintenance = 0f;
+ }
+
+ // 当维护度为0时应用伤害
+ private void ApplyDamageWhenEmpty()
+ {
+ if (parent == null || parent.Destroyed)
+ return;
+
+ // 每秒造成伤害
+ float damageAmount = Props.damagePerSecondWhenEmpty;
+
+ // 应用伤害到建筑
+ parent.TakeDamage(new DamageInfo(
+ DamageDefOf.Deterioration,
+ damageAmount,
+ armorPenetration: 999f, // 高穿甲,确保能造成伤害
+ instigator: null
+ ));
+ }
+
+ // 检查并分配维护工作
+ private void CheckAndAssignMaintenanceJob()
+ {
+ if (parent == null || parent.Map == null || parent.Faction == null)
+ return;
+
+ // 如果不需要维护,重置状态
+ if (!NeedsMaintenance)
+ {
+ seekingMaintainer = false;
+ return;
+ }
+
+ // 如果正在寻找维护者,尝试分配工作
+ if (!seekingMaintainer)
+ {
+ seekingMaintainer = true;
+ }
+
+ // 尝试寻找符合条件的Pawn
+ Pawn maintainer = FindAvailableMaintainer();
+
+ if (maintainer != null)
+ {
+ // 分配维护工作
+ AssignMaintenanceJobTo(maintainer);
+ seekingMaintainer = false;
+ noMaintainerWarning = "";
+ }
+ else
+ {
+ // 记录找不到维护者的警告
+ if (noMaintainerWarning == "")
+ {
+ noMaintainerWarning = "ARA_SwarmMaintenance.NoMaintainerFound".Translate();
+ lastWarningTick = Find.TickManager.TicksGame;
+ }
+ }
+ }
+
+ // 寻找可用的维护者
+ private Pawn FindAvailableMaintainer()
+ {
+ if (parent.Map == null || parent.Faction == null)
+ return null;
+
+ // 查找地图中所有属于玩家阵营的Pawn
+ List allPawns = parent.Map.mapPawns.SpawnedPawnsInFaction(parent.Faction);
+
+ foreach (Pawn pawn in allPawns)
+ {
+ if (CanPawnMaintain(pawn))
+ {
+ return pawn;
+ }
+ }
+
+ return null;
+ }
+
+ // 判断Pawn是否可以维护
+ private bool CanPawnMaintain(Pawn pawn)
+ {
+ if (pawn == null || pawn.Dead || pawn.Downed || pawn.InMentalState)
+ return false;
+
+ // 检查种族是否在允许列表中
+ if (Props.allowedRaces != null && Props.allowedRaces.Count > 0)
+ {
+ if (!Props.allowedRaces.Contains(pawn.def))
+ return false;
+ }
+
+ // 检查Pawn是否处于GotoWander或Wait_Wander状态
+ if (!IsPawnWandering(pawn))
+ return false;
+
+ // 检查Pawn是否已经有维护工作
+ if (HasMaintenanceJob(pawn))
+ return false;
+
+ return true;
+ }
+
+ // 检查Pawn是否处于漫游状态
+ private bool IsPawnWandering(Pawn pawn)
+ {
+ if (pawn.jobs == null || pawn.jobs.curJob == null)
+ return false;
+
+ Job curJob = pawn.jobs.curJob;
+
+ // 检查是否是漫游工作
+ if (curJob.def == JobDefOf.GotoWander || curJob.def == JobDefOf.Wait_Wander)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ // 检查Pawn是否已经有维护工作
+ private bool HasMaintenanceJob(Pawn pawn)
+ {
+ if (pawn.jobs == null)
+ return false;
+
+ // 检查当前工作是否是维护工作
+ if (pawn.jobs.curJob != null && pawn.jobs.curJob.def == ARA_JobDefOf.ARA_SwarmMaintain)
+ {
+ return true;
+ }
+
+ // 检查工作队列中是否有维护工作
+ if (pawn.jobs.jobQueue != null && pawn.jobs.jobQueue.Count > 0)
+ {
+ foreach (QueuedJob queuedJob in pawn.jobs.jobQueue)
+ {
+ if (queuedJob.job.def == ARA_JobDefOf.ARA_SwarmMaintain)
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ // 为Pawn分配维护工作
+ private void AssignMaintenanceJobTo(Pawn pawn)
+ {
+ if (pawn == null || pawn.jobs == null)
+ return;
+
+ // 创建维护工作
+ Job job = JobMaker.MakeJob(ARA_JobDefOf.ARA_SwarmMaintain, parent);
+ job.expiryInterval = 30000; // 工作过期时间
+ job.ignoreForbidden = false;
+
+ // 记录Pawn原来的工作以便恢复
+ Job oldJob = pawn.jobs.curJob;
+
+ // 将工作添加到队列末尾
+ pawn.jobs.TryTakeOrderedJob(job, JobTag.MiscWork);
+
+ // 如果Pawn原来在漫游,我们可以在维护工作完成后恢复漫游状态
+ if (oldJob != null && (oldJob.def == JobDefOf.GotoWander || oldJob.def == JobDefOf.Wait_Wander))
+ {
+ // 我们可以在维护工作完成后添加一个恢复漫游的工作
+ // 这里暂时不做处理,因为维护工作完成后Pawn会回到空闲状态
+ // 如果需要更精细的控制,可以在JobDriver_SwarmMaintain完成时添加漫游工作
+ }
+ }
+
+ // 添加维护度
+ public void AddMaintenance(float amount)
+ {
+ currentMaintenance += amount;
+ if (currentMaintenance > MaxMaintenance)
+ currentMaintenance = MaxMaintenance;
+ }
+
+ // 重置维护度
+ public void ResetMaintenance()
+ {
+ currentMaintenance = MaxMaintenance;
+ }
+
+ // 在建筑信息面板中追加维护信息
+ public override string CompInspectStringExtra()
+ {
+ // 基础信息
+ string text = "ARA_SwarmMaintenance.MaintenanceLevel".Translate(currentMaintenance.ToString("F1"), MaxMaintenance.ToString("F1"), MaintenancePercentage.ToString("P1"));
+
+ // 添加维护度递减信息
+ text += "\n" + "ARA_SwarmMaintenance.DailyDecay".Translate(Props.maintenanceDecayPerDay.ToString("F1"));
+
+
+ // 显示找不到维护者的警告
+ if (noMaintainerWarning != "")
+ {
+ text += "\n" + noMaintainerWarning + " ";
+ }
+
+ return text;
+ }
+
+ // 获取Gizmos(命令按钮)- 只保留调试功能
+ public override IEnumerable CompGetGizmosExtra()
+ {
+ // 只在玩家控制下显示
+ if (parent.Faction?.IsPlayer == true)
+ {
+ // 调试按钮:手动触发寻找维护者
+ if (Prefs.DevMode)
+ {
+ yield return new Command_Action
+ {
+ defaultLabel = "Debug: Find Maintainer",
+ action = () =>
+ {
+ Pawn maintainer = FindAvailableMaintainer();
+ if (maintainer != null)
+ {
+ Messages.Message($"找到维护者: {maintainer.LabelShort} (当前工作: {maintainer.jobs.curJob?.def.defName ?? "无"})", MessageTypeDefOf.PositiveEvent);
+ AssignMaintenanceJobTo(maintainer);
+ }
+ else
+ {
+ Messages.Message("未找到符合条件的空闲维护者", MessageTypeDefOf.NegativeEvent);
+
+ // 列出所有符合条件的Pawn及其状态
+ List allPawns = parent.Map.mapPawns.SpawnedPawnsInFaction(parent.Faction);
+ List pawnStatus = new List();
+
+ foreach (Pawn pawn in allPawns)
+ {
+ if (Props.allowedRaces != null && Props.allowedRaces.Count > 0 && !Props.allowedRaces.Contains(pawn.def))
+ continue;
+
+ string status = $"{pawn.LabelShort}: ";
+ if (pawn.Dead) status += "死亡";
+ else if (pawn.Downed) status += "倒地";
+ else if (pawn.InMentalState) status += "精神崩溃";
+ else if (pawn.jobs?.curJob != null)
+ {
+ status += $"{pawn.jobs.curJob.def.defName}";
+ if (HasMaintenanceJob(pawn)) status += " (已有维护工作)";
+ }
+ else
+ {
+ status += "空闲";
+ }
+
+ pawnStatus.Add(status);
+ }
+
+ if (pawnStatus.Count > 0)
+ {
+ Messages.Message("符合条件的Pawn状态:\n" + string.Join("\n", pawnStatus), MessageTypeDefOf.SilentInput);
+ }
+ }
+ }
+ };
+
+ yield return new Command_Action
+ {
+ defaultLabel = "Debug: Reset Maintenance",
+ action = () => ResetMaintenance()
+ };
+
+ yield return new Command_Action
+ {
+ defaultLabel = "Debug: Reduce 50%",
+ action = () => currentMaintenance = MaxMaintenance * 0.5f
+ };
+
+ yield return new Command_Action
+ {
+ defaultLabel = "Debug: Reduce to 0",
+ action = () => currentMaintenance = 0f
+ };
+ }
+ }
+ }
+ }
+}
diff --git a/Source/ArachnaeSwarm/Building_Comps/CompTemperatureRuinableDamage.cs b/Source/ArachnaeSwarm/Building_Comps/CompTemperatureRuinableDamage.cs
new file mode 100644
index 0000000..954759f
--- /dev/null
+++ b/Source/ArachnaeSwarm/Building_Comps/CompTemperatureRuinableDamage.cs
@@ -0,0 +1,178 @@
+// File: Comps/CompTemperatureRuinableDamage.cs
+using RimWorld;
+using Verse;
+
+namespace ArachnaeSwarm
+{
+ public class CompProperties_TemperatureRuinableDamage : CompProperties
+ {
+ public float minSafeTemperature;
+ public float maxSafeTemperature = 100f;
+ public float progressPerDegreePerTick = 1E-05f; // 修改参数名以匹配标准调用方式
+ public float damagePerTick = 1f; // 每tick造成的伤害值
+ public float recoveryRate = 0.001f; // 温度恢复正常时的恢复速率
+ public bool sendDamageLetter = true; // 是否发送伤害警告信件
+
+ public CompProperties_TemperatureRuinableDamage()
+ {
+ compClass = typeof(CompTemperatureRuinableDamage);
+ }
+ }
+
+ public class CompTemperatureRuinableDamage : ThingComp
+ {
+ private float ruinedPercent; // 修改变量名以匹配标准
+ private bool isRuined; // 修改变量名以匹配标准
+ private bool damageLetterSent = false; // 是否已发送伤害警告信件
+ private int lastDamageTick = -99999; // 上次造成伤害的时间
+ private const int DamageLetterInterval = 60000; // 伤害信件间隔(60秒游戏时间)
+
+ public CompProperties_TemperatureRuinableDamage Props => (CompProperties_TemperatureRuinableDamage)props;
+
+ public override void CompTick()
+ {
+ base.CompTick();
+ int currentTick = Find.TickManager.TicksGame;
+ bool wasRuined = isRuined;
+
+ if (parent.AmbientTemperature < Props.minSafeTemperature || parent.AmbientTemperature > Props.maxSafeTemperature)
+ {
+ float tempDelta = 0f;
+ if (parent.AmbientTemperature < Props.minSafeTemperature)
+ {
+ tempDelta = Props.minSafeTemperature - parent.AmbientTemperature;
+ }
+ else if (parent.AmbientTemperature > Props.maxSafeTemperature)
+ {
+ tempDelta = parent.AmbientTemperature - Props.maxSafeTemperature;
+ }
+
+ // 累积损坏进度
+ ruinedPercent += tempDelta * Props.progressPerDegreePerTick;
+
+ // 只有在已损坏的情况下才每tick造成持续伤害
+ if (isRuined)
+ {
+ parent.TakeDamage(new DamageInfo(DamageDefOf.Deterioration, Props.damagePerTick));
+ lastDamageTick = currentTick;
+
+ // 发送伤害警告信件(如果启用)
+ if (Props.sendDamageLetter && !damageLetterSent && currentTick - lastDamageTick >= DamageLetterInterval)
+ {
+ SendDamageLetter();
+ damageLetterSent = true;
+ lastDamageTick = currentTick;
+ }
+ }
+
+ // 标记为已受损
+ isRuined = true;
+
+ // 如果是刚刚变为损坏状态,发送警告信件
+ if (!wasRuined && Props.sendDamageLetter)
+ {
+ SendDamageLetter();
+ damageLetterSent = true;
+ }
+ }
+ else
+ {
+ // 当温度恢复正常时,逐渐减少损坏进度而不是重置
+ if (isRuined && ruinedPercent > 0f)
+ {
+ ruinedPercent -= Props.recoveryRate;
+ if (ruinedPercent <= 0f)
+ {
+ ruinedPercent = 0f;
+ isRuined = false;
+ damageLetterSent = false; // 重置信件状态,以便下次损坏时可以再次发送
+ }
+ }
+
+ // 即使温度正常,如果已损坏也要继续造成伤害直到恢复
+ if (isRuined)
+ {
+ parent.TakeDamage(new DamageInfo(DamageDefOf.Deterioration, Props.damagePerTick));
+ lastDamageTick = currentTick;
+
+ // 发送伤害警告信件(如果启用)
+ if (Props.sendDamageLetter && !damageLetterSent && currentTick - lastDamageTick >= DamageLetterInterval)
+ {
+ SendDamageLetter();
+ damageLetterSent = true;
+ lastDamageTick = currentTick;
+ }
+ }
+ }
+ }
+
+ public override void PostExposeData()
+ {
+ base.PostExposeData();
+ Scribe_Values.Look(ref ruinedPercent, "ruinedPercent", 0f);
+ Scribe_Values.Look(ref isRuined, "isRuined", false);
+ Scribe_Values.Look(ref damageLetterSent, "damageLetterSent", false);
+ Scribe_Values.Look(ref lastDamageTick, "lastDamageTick", -99999);
+ }
+
+ public override string CompInspectStringExtra()
+ {
+ // 总是显示安全温度范围
+ string text = "ARA_TemperatureRuinable.SafeTemperatureRange".Translate(
+ Props.minSafeTemperature.ToStringTemperature("F1"),
+ Props.maxSafeTemperature.ToStringTemperature("F1")
+ );
+
+ // 显示当前温度状态
+ text += "\n" + "ARA_TemperatureRuinable.CurrentTemperature".Translate(parent.AmbientTemperature.ToStringTemperature("F1"));
+
+ // 显示损坏状态
+ if (ruinedPercent > 0f)
+ {
+ text += "\n" + "ARA_TemperatureRuinable.DamageProgress".Translate(ruinedPercent.ToStringPercent());
+
+ if (isRuined)
+ {
+ text += "\n" + "ARA_TemperatureRuinable.TakingDamage".Translate() + " ";
+ }
+ }
+ else
+ {
+ text += "\n" + "ARA_TemperatureRuinable.NoDamage".Translate();
+ }
+
+ return text;
+ }
+
+ // 发送伤害警告信件
+ private void SendDamageLetter()
+ {
+ if (parent == null || parent.Map == null)
+ return;
+
+ // 确定伤害类型(太冷或太热)
+ string temperatureType;
+ if (parent.AmbientTemperature < Props.minSafeTemperature)
+ {
+ temperatureType = "ARA_TemperatureRuinable.TooCold".Translate();
+ }
+ else
+ {
+ temperatureType = "ARA_TemperatureRuinable.TooHot".Translate();
+ }
+
+ // 创建信件
+ string label = "ARA_TemperatureRuinable.DamageLetterLabel".Translate(parent.Label);
+ string text = "ARA_TemperatureRuinable.DamageLetterText".Translate(
+ parent.Label,
+ temperatureType,
+ parent.AmbientTemperature.ToStringTemperature("F1"),
+ Props.minSafeTemperature.ToStringTemperature("F1"),
+ Props.maxSafeTemperature.ToStringTemperature("F1")
+ );
+
+ // 发送信件
+ Find.LetterStack.ReceiveLetter(label, text, LetterDefOf.NegativeEvent, new LookTargets(parent));
+ }
+ }
+}
diff --git a/Source/ArachnaeSwarm/Buildings/Building_EquipmentOotheca/Building_EquipmentOotheca.cs b/Source/ArachnaeSwarm/Buildings/Building_EquipmentOotheca/Building_EquipmentOotheca.cs
new file mode 100644
index 0000000..7861254
--- /dev/null
+++ b/Source/ArachnaeSwarm/Buildings/Building_EquipmentOotheca/Building_EquipmentOotheca.cs
@@ -0,0 +1,882 @@
+// File: Buildings/Building_EquipmentOotheca.cs
+using RimWorld;
+using System.Collections.Generic;
+using System.Text;
+using UnityEngine;
+using Verse;
+using Verse.AI;
+using System;
+
+namespace ArachnaeSwarm
+{
+ public class Building_EquipmentOotheca : Building
+ {
+ // 引用组件
+ public CompEquipmentIncubatorData EquipmentIncubatorData => this.TryGetComp();
+
+ // 孵化状态
+ public bool isIncubating = false;
+ public float incubationProgress = 0f;
+ public float incubationDuration = 0f;
+ public ThingDef incubatingThingDef = null;
+
+ // 幼虫交互相关
+ public Pawn assignedLarva = null;
+ public int larvaOperateTicksRemaining = 0;
+
+ // 速度乘数系统
+ private float speedMultiplier = 1.0f;
+ private int lastMultiplierUpdateTick = -1;
+ private const int MultiplierUpdateInterval = 250;
+
+ // 质量系统
+ private float qualityMultiplier = 1.0f;
+ private float qualityProgress = 0f;
+ private float qualityTotal = 0f;
+
+ // 缓存的ModExtension
+ private OothecaIncubatorExtension cachedExtension;
+
+ // 获取ModExtension的辅助属性
+ private OothecaIncubatorExtension Ext
+ {
+ get
+ {
+ if (cachedExtension == null)
+ {
+ cachedExtension = def.GetModExtension() ?? OothecaIncubatorExtension.Default;
+ }
+ return cachedExtension;
+ }
+ }
+
+ // 属性访问器
+ public float SpeedMultiplier
+ {
+ get
+ {
+ if (lastMultiplierUpdateTick < 0 || Find.TickManager.TicksGame - lastMultiplierUpdateTick >= MultiplierUpdateInterval)
+ {
+ UpdateSpeedMultiplier();
+ }
+ return speedMultiplier;
+ }
+ }
+
+ // 质量属性
+ public float QualityMultiplier => qualityMultiplier;
+ public float QualityProgress => qualityProgress;
+ public float QualityPercent => qualityTotal > 0 ? qualityProgress / qualityTotal : 0f;
+
+ // 进度百分比
+ public float AdjustedProgressPercent
+ {
+ get
+ {
+ if (incubationDuration <= 0) return 0f;
+ return incubationProgress / incubationDuration;
+ }
+ }
+
+ // 获取速度因子描述
+ public string GetSpeedFactorsDescription()
+ {
+ var builder = new StringBuilder();
+
+ builder.AppendLine("ARA_EquipmentIncubator.SpeedFactors".Translate());
+ builder.AppendLine();
+
+ // 1. 检查是否在孵化间中
+ bool inIncubatorRoom = IsInIncubatorRoom();
+ if (Ext.requiresIncubatorRoom)
+ {
+ builder.AppendLine(inIncubatorRoom ?
+ "ARA_EquipmentIncubator.InIncubatorRoom".Translate() :
+ "ARA_EquipmentIncubator.NotInIncubatorRoom".Translate());
+ }
+
+ // 2. 检查营养液数量
+ int nutrientSolutionCount = CountNearbyNutrientSolutions();
+ if (nutrientSolutionCount > 0)
+ {
+ builder.AppendLine("ARA_EquipmentIncubator.NutrientSolutions".Translate(
+ nutrientSolutionCount,
+ nutrientSolutionCount * Ext.nutrientSolutionBonusPerTile * 100));
+ }
+ else
+ {
+ builder.AppendLine("ARA_EquipmentIncubator.NoNutrientSolutionsNearby".Translate());
+ }
+
+ builder.AppendLine();
+ builder.AppendLine("ARA_EquipmentIncubator.NutrientDetectionRadius".Translate(Ext.nutrientSolutionRadius));
+
+ builder.AppendLine();
+ builder.AppendLine("ARA_EquipmentIncubator.TotalSpeedMultiplier".Translate(SpeedMultiplier.ToStringPercent()));
+
+ return builder.ToString().TrimEndNewlines();
+ }
+
+ // 获取质量因子描述
+ public string GetQualityFactorsDescription()
+ {
+ var builder = new StringBuilder();
+
+ builder.AppendLine("ARA_EquipmentIncubator.QualityFactors".Translate());
+ builder.AppendLine();
+
+ // 1. 建筑血量损失百分比
+ if (Ext.healthAffectsQuality)
+ {
+ float healthPercent = (float)HitPoints / MaxHitPoints;
+ builder.AppendLine("ARA_EquipmentIncubator.BuildingHealth".Translate(healthPercent.ToStringPercent()));
+ }
+
+ // 2. 房间质量因子
+ if (Ext.useRoomQualityFactor)
+ {
+ float roomFactor = GetRoomQualityFactor();
+ builder.AppendLine(roomFactor == 1.0f ?
+ "ARA_EquipmentIncubator.RoomFactorNormal".Translate() :
+ $"{(roomFactor > 1.0f ? "✓" : "✗")} {"ARA_EquipmentIncubator.RoomFactorModified".Translate()}{roomFactor.ToStringPercent()}");
+ }
+
+ // 3. 附近其他卵
+ int nearbyOothecaCount = CountNearbyOtherOothecas();
+ if (nearbyOothecaCount > 0)
+ {
+ builder.AppendLine("ARA_EquipmentIncubator.NearbyOothecas".Translate(
+ nearbyOothecaCount,
+ Mathf.Min(nearbyOothecaCount * Ext.nearbyOothecaPenaltyPerUnit * 100, 100)));
+ }
+ else
+ {
+ builder.AppendLine("ARA_EquipmentIncubator.NoNearbyOothecas".Translate());
+ }
+
+ builder.AppendLine();
+ builder.AppendLine("ARA_EquipmentIncubator.OothecaDetectionRadius".Translate(Ext.nearbyOothecaRadius));
+
+ builder.AppendLine();
+ builder.AppendLine("ARA_EquipmentIncubator.TotalQualityMultiplier".Translate(QualityMultiplier.ToStringPercent()));
+
+ return builder.ToString().TrimEndNewlines();
+ }
+
+ // 构建呼叫幼虫描述
+ private string BuildCallLarvaDescription(EquipmentIncubationConfig config)
+ {
+ var builder = new StringBuilder();
+
+ builder.AppendLine("ARA_EquipmentIncubator.CallLarvaTitle".Translate());
+ builder.AppendLine();
+ builder.AppendLine("ARA_EquipmentIncubator.LarvaWillCome".Translate());
+ builder.AppendLine(config.thingDef.LabelCap);
+ builder.AppendLine();
+
+ if (Ext.larvaSearchRadius < 999f)
+ {
+ builder.AppendLine("ARA_EquipmentIncubator.LarvaSearchRadius".Translate(Ext.larvaSearchRadius));
+ }
+
+ return builder.ToString().TrimEndNewlines();
+ }
+
+ // 呼叫幼虫
+ private void CallLarva()
+ {
+ if (isIncubating)
+ {
+ Messages.Message("ARA_EquipmentIncubator.AlreadyIncubating".Translate() + " " + "ARA_EquipmentIncubator.CancelFirst".Translate(),
+ MessageTypeDefOf.RejectInput);
+ return;
+ }
+
+ if (assignedLarva != null)
+ {
+ Messages.Message("ARA_EquipmentIncubator.LarvaAlreadyOnWay".Translate(),
+ MessageTypeDefOf.RejectInput);
+ return;
+ }
+
+ var larva = FindLarva();
+ if (larva == null)
+ {
+ Messages.Message("ARA_EquipmentIncubator.NoLarvaeFound".Translate() + " " + "ARA_EquipmentIncubator.LarvaMustBeRace".Translate(),
+ MessageTypeDefOf.RejectInput);
+ return;
+ }
+
+ var job = JobMaker.MakeJob(ARA_JobDefOf.ARA_OperateEquipmentIncubator, this);
+ job.count = 1;
+ larva.jobs.TryTakeOrderedJob(job, JobTag.MiscWork);
+
+ assignedLarva = larva;
+
+ Messages.Message("ARA_EquipmentIncubator.LarvaCalled".Translate() + " " + "ARA_EquipmentIncubator.ArriveShortly".Translate(),
+ MessageTypeDefOf.PositiveEvent);
+ }
+
+ // 幼虫到达
+ public void NotifyLarvaArrived(Pawn larva)
+ {
+ if (larva.def.defName != "ArachnaeBase_Race_Larva")
+ {
+ ArachnaeLog.Debug($"Invalid larva arrived: {larva.def.defName}");
+ return;
+ }
+
+ larvaOperateTicksRemaining = 180;
+ assignedLarva = larva;
+
+ Messages.Message("ARA_EquipmentIncubator.LarvaArrived".Translate() + " " + "ARA_EquipmentIncubator.ActivatingOotheca".Translate(),
+ MessageTypeDefOf.SilentInput);
+ }
+
+ // 幼虫完成操作
+ public void NotifyLarvaOperationComplete(Pawn larva)
+ {
+ if (larva != assignedLarva)
+ {
+ ArachnaeLog.Debug("Larva operation complete called with wrong larva.");
+ return;
+ }
+
+ var config = EquipmentIncubatorData?.SelectedConfig;
+ if (config == null)
+ {
+ ArachnaeLog.Debug("No incubation config selected when larva completed operation.");
+ return;
+ }
+
+ incubatingThingDef = config.thingDef;
+ incubationDuration = config.DaysRequired * 60000f;
+ incubationProgress = 0f;
+ isIncubating = true;
+
+ qualityTotal = incubationDuration;
+ qualityProgress = 0f;
+ UpdateQualityMultiplier();
+
+ UpdateSpeedMultiplier();
+
+ assignedLarva = null;
+ larvaOperateTicksRemaining = 0;
+
+ Messages.Message("ARA_EquipmentIncubator.IncubationStarted".Translate() + " " + incubatingThingDef.LabelCap + ". " +
+ "ARA_EquipmentIncubator.ProcessWillComplete".Translate() + " " + config.DaysRequired + " " + "ARA_EquipmentIncubator.DaysBaseTime".Translate(),
+ MessageTypeDefOf.PositiveEvent);
+ }
+
+ // 取消孵化
+ private void CancelIncubation()
+ {
+ if (!isIncubating) return;
+
+ isIncubating = false;
+ incubationProgress = 0f;
+ incubationDuration = 0f;
+ incubatingThingDef = null;
+ qualityProgress = 0f;
+ qualityTotal = 0f;
+
+ Messages.Message("ARA_EquipmentIncubator.IncubationCancelled".Translate() + " " + "ARA_EquipmentIncubator.ContentsLost".Translate(),
+ MessageTypeDefOf.NeutralEvent);
+ }
+
+ // 完成孵化
+ private void CompleteIncubation()
+ {
+ if (incubatingThingDef == null) return;
+
+ float finalQualityPercent = QualityPercent;
+
+ // 生成物品
+ Thing thing = ThingMaker.MakeThing(incubatingThingDef);
+
+ // 应用质量影响
+ ApplyQualityEffects(thing, finalQualityPercent);
+
+ // 放置物品
+ var spawnPos = Position;
+ GenPlace.TryPlaceThing(thing, spawnPos, Map, ThingPlaceMode.Near);
+
+ // 重置状态
+ isIncubating = false;
+ incubationProgress = 0f;
+ incubationDuration = 0f;
+ incubatingThingDef = null;
+ qualityProgress = 0f;
+ qualityTotal = 0f;
+
+ // 显示消息
+ string qualityText = finalQualityPercent >= 0.9f ? "ARA_EquipmentIncubator.QualityExcellent".Translate() :
+ finalQualityPercent >= 0.7f ? "ARA_EquipmentIncubator.QualityGood".Translate() :
+ finalQualityPercent >= 0.5f ? "ARA_EquipmentIncubator.QualityAverage".Translate() :
+ finalQualityPercent >= 0.3f ? "ARA_EquipmentIncubator.QualityPoor".Translate() : "ARA_EquipmentIncubator.QualityVeryPoor".Translate();
+
+ Messages.Message("ARA_EquipmentIncubator.IncubationComplete".Translate() + " " + thing.LabelCap + " " +
+ "ARA_EquipmentIncubator.HasEmergedWith".Translate() + " " + qualityText + " " +
+ "ARA_EquipmentIncubator.Quality".Translate() + " (" + finalQualityPercent.ToStringPercent() + ").",
+ MessageTypeDefOf.PositiveEvent);
+
+ Destroy();
+ }
+
+ // 应用质量效果
+ private void ApplyQualityEffects(Thing thing, float qualityPercent)
+ {
+ // 应用质量效果到装备
+ if (thing.TryGetComp() is CompQuality compQuality)
+ {
+ // 根据质量百分比设置质量等级
+ QualityCategory qualityCategory = qualityPercent >= 0.99f ? QualityCategory.Legendary :
+ qualityPercent >= 0.75f ? QualityCategory.Masterwork :
+ qualityPercent >= 0.6f ? QualityCategory.Excellent :
+ qualityPercent >= 0.45f ? QualityCategory.Good :
+ qualityPercent >= 0.3f ? QualityCategory.Normal : QualityCategory.Poor;
+
+ compQuality.SetQuality(qualityCategory, ArtGenerationContext.Outsider);
+ }
+
+ // 设置生命值百分比
+ if (qualityPercent < 1.0f)
+ {
+ float healthFactor = Mathf.Lerp(0.5f, 1.0f, qualityPercent);
+ thing.HitPoints = Mathf.RoundToInt(thing.MaxHitPoints * healthFactor);
+ }
+ }
+
+ // 获取剩余时间
+ public float GetRemainingTicks()
+ {
+ if (!isIncubating || incubationDuration <= incubationProgress) return 0f;
+
+ float remainingProgress = incubationDuration - incubationProgress;
+ float currentSpeed = SpeedMultiplier;
+
+ if (currentSpeed <= 0) return float.MaxValue;
+
+ return remainingProgress / currentSpeed;
+ }
+
+ public float GetRemainingDays()
+ {
+ return GetRemainingTicks() / 60000f;
+ }
+
+ public float GetRemainingHours()
+ {
+ float remainingTicks = GetRemainingTicks();
+ return (remainingTicks % 60000f) / 2500f;
+ }
+
+ // 检查字符串
+ public override string GetInspectString()
+ {
+ var baseString = base.GetInspectString();
+ var builder = new StringBuilder();
+
+ if (!string.IsNullOrEmpty(baseString))
+ {
+ builder.Append(baseString);
+ }
+
+ if (isIncubating && incubatingThingDef != null)
+ {
+ float progressPercent = AdjustedProgressPercent;
+ float daysRemaining = GetRemainingDays();
+ float hoursRemaining = GetRemainingHours();
+
+ if (builder.Length > 0) builder.AppendLine();
+ builder.Append("ARA_EquipmentIncubator.Incubating".Translate() + ": " + incubatingThingDef.LabelCap);
+ builder.AppendLine();
+ builder.Append("ARA_EquipmentIncubator.Progress".Translate() + ": " + progressPercent.ToStringPercent());
+ builder.AppendLine();
+
+ string timeText = "ARA_EquipmentIncubator.TimeRemaining".Translate() + ": " + daysRemaining.ToString("F1") + " " + "ARA_EquipmentIncubator.Days".Translate();
+ if (hoursRemaining > 0.1f && daysRemaining < 1f)
+ {
+ timeText += " (" + hoursRemaining.ToString("F1") + " " + "ARA_EquipmentIncubator.Hours".Translate() + ")";
+ }
+ builder.Append(timeText);
+
+ builder.AppendLine();
+ builder.Append("ARA_EquipmentIncubator.Speed".Translate() + ": " + SpeedMultiplier.ToStringPercent() + ", " +
+ "ARA_EquipmentIncubator.Quality".Translate() + ": " + QualityMultiplier.ToStringPercent());
+ }
+ else if (assignedLarva != null)
+ {
+ if (builder.Length > 0) builder.AppendLine();
+ if (larvaOperateTicksRemaining > 0)
+ {
+ float secondsRemaining = larvaOperateTicksRemaining / 60f;
+ builder.Append("ARA_EquipmentIncubator.LarvaOperating".Translate() + ": " + secondsRemaining.ToString("F1") + " " + "ARA_EquipmentIncubator.SRemaining".Translate());
+ }
+ else
+ {
+ builder.Append("ARA_EquipmentIncubator.LarvaOnWay".Translate());
+ }
+ }
+ else if (!isIncubating)
+ {
+ var config = EquipmentIncubatorData?.SelectedConfig;
+ if (config != null)
+ {
+ if (builder.Length > 0) builder.AppendLine();
+ builder.Append("ARA_EquipmentIncubator.Target".Translate() + ": " + config.thingDef.LabelCap);
+
+ builder.AppendLine();
+ builder.Append("ARA_EquipmentIncubator.SpeedMultiplier".Translate() + ": " + SpeedMultiplier.ToStringPercent() + ", " +
+ "ARA_EquipmentIncubator.QualityMultiplier".Translate() + ": " + QualityMultiplier.ToStringPercent());
+ }
+ }
+
+ return builder.ToString().TrimEndNewlines();
+ }
+
+ // Gizmos
+ public override IEnumerable GetGizmos()
+ {
+ foreach (var gizmo in base.GetGizmos())
+ {
+ yield return gizmo;
+ }
+
+ if (Faction == Faction.OfPlayer)
+ {
+ if (!isIncubating && EquipmentIncubatorData?.IncubationConfigs?.Count > 0)
+ {
+ yield return CreateTargetSwitchGizmo();
+ }
+
+ var config = EquipmentIncubatorData?.SelectedConfig;
+ if (!isIncubating && config != null && config.IsResearchComplete)
+ {
+ yield return new Command_Action
+ {
+ defaultLabel = "ARA_EquipmentIncubator.CallLarva".Translate(),
+ defaultDesc = BuildCallLarvaDescription(config),
+ icon = ContentFinder.Get("ArachnaeSwarm/UI/Commands/ARA_CallLarva", false) ?? BaseContent.BadTex,
+ action = CallLarva,
+ hotKey = KeyBindingDefOf.Misc3
+ };
+ }
+
+ if (isIncubating)
+ {
+ yield return new Command_Action
+ {
+ defaultLabel = "ARA_EquipmentIncubator.CancelIncubation".Translate(),
+ defaultDesc = "ARA_EquipmentIncubator.CancelIncubationDesc".Translate(),
+ icon = ContentFinder.Get("UI/Commands/Cancel", false) ?? TexCommand.ClearPrioritizedWork,
+ action = CancelIncubation
+ };
+ }
+ }
+ }
+
+ // 创建切换目标Gizmo - 现在使用装备的图标
+ private Gizmo CreateTargetSwitchGizmo()
+ {
+ var configs = EquipmentIncubatorData?.IncubationConfigs;
+ if (configs == null || configs.Count == 0) return null;
+
+ var props = EquipmentIncubatorData?.props as CompProperties_EquipmentIncubatorData;
+ var selectedConfig = EquipmentIncubatorData?.SelectedConfig;
+
+ var switchButton = new Command_Action
+ {
+ defaultLabel = BuildSwitchButtonLabel(selectedConfig, props),
+ defaultDesc = BuildSwitchButtonDescription(selectedConfig, props),
+ icon = GetConfigIcon(selectedConfig),
+ action = ShowSelectionMenu,
+ hotKey = KeyBindingDefOf.Misc2
+ };
+
+ if (selectedConfig != null && !selectedConfig.IsResearchComplete)
+ {
+ if (selectedConfig.requiredResearch != null)
+ {
+ switchButton.Disable($"Requires research: {selectedConfig.requiredResearch.LabelCap}");
+ }
+ }
+
+ return switchButton;
+ }
+
+ // 获取配置图标 - 现在直接从ThingDef获取
+ private Texture2D GetConfigIcon(EquipmentIncubationConfig config)
+ {
+ if (config == null)
+ return BaseContent.BadTex;
+
+ // 如果配置中没有缓存图标,尝试直接获取ThingDef的uiIcon
+ if (config.thingDef?.uiIcon != null)
+ return config.thingDef.uiIcon;
+
+ // 回退到默认图标
+ return ContentFinder.Get("UI/Commands/Default", false) ?? BaseContent.BadTex;
+ }
+
+ private string BuildSwitchButtonLabel(EquipmentIncubationConfig config, CompProperties_EquipmentIncubatorData props)
+ {
+ if (config != null && config.thingDef != null)
+ {
+ return (props?.buttonLabel ?? "ARA_EquipmentIncubator.IncubateLabel").Translate(config.thingDef.LabelCap);
+ }
+ return (props?.buttonLabel ?? "ARA_EquipmentIncubator.IncubateLabel").Translate("None");
+ }
+
+ private string BuildSwitchButtonDescription(EquipmentIncubationConfig config, CompProperties_EquipmentIncubatorData props)
+ {
+ var builder = new StringBuilder();
+
+ builder.AppendLine((props?.buttonDesc ?? "ARA_EquipmentIncubator.ButtonDesc").Translate());
+ builder.AppendLine();
+
+ if (config != null)
+ {
+ if (config.thingDef != null)
+ {
+ builder.AppendLine($"ARA_EquipmentIncubator.ButtonLabel".Translate(config.thingDef.LabelCap));
+ if (!string.IsNullOrEmpty(config.thingDef.description))
+ {
+ builder.AppendLine(config.thingDef.description);
+ }
+ }
+
+ builder.AppendLine($"ARA_EquipmentIncubator.IncubationTime".Translate(config.DaysRequired));
+
+ if (config.requiredResearch != null)
+ {
+ if (config.requiredResearch.IsFinished)
+ builder.AppendLine($"Research: {config.requiredResearch.LabelCap} (Completed)");
+ else
+ builder.AppendLine($"Research: {config.requiredResearch.LabelCap} (Required)");
+ }
+ }
+
+ builder.AppendLine();
+ builder.AppendLine("ARA_EquipmentIncubator.ButtonDesc".Translate());
+
+ return builder.ToString().TrimEndNewlines();
+ }
+
+ private void ShowSelectionMenu()
+ {
+ var configs = EquipmentIncubatorData?.IncubationConfigs;
+ var props = EquipmentIncubatorData?.props as CompProperties_EquipmentIncubatorData;
+ if (configs == null || configs.Count == 0) return;
+
+ var options = new List();
+ int currentIndex = EquipmentIncubatorData.GetSelectedIndex();
+
+ for (int i = 0; i < configs.Count; i++)
+ {
+ int index = i;
+ var config = configs[i];
+ if (config == null || config.thingDef == null) continue;
+
+ string label = config.thingDef.LabelCap;
+ string description = config.GetDescription();
+
+ string prefix = (i == currentIndex) ? "✓ " : " ";
+
+ // 使用原版FloatMenuOption的构造函数,直接传入图标
+ FloatMenuOption option;
+
+ // 尝试获取ThingDef的图标
+ Texture2D icon = config.thingDef.uiIcon;
+
+ if (icon != null)
+ {
+ // 使用带有Texture2D图标的构造函数
+ option = new FloatMenuOption(
+ prefix + label,
+ () => SwitchToConfig(index),
+ icon,
+ Color.white,
+ MenuOptionPriority.Default,
+ null, // mouseoverGuiAction
+ null, // revalidateClickTarget
+ 0f, // extraPartWidth
+ null, // extraPartOnGUI
+ null, // revalidateWorldClickTarget
+ true, // playSelectionSound
+ 0, // orderInPriority
+ HorizontalJustification.Left, // iconJustification
+ false // extraPartRightJustified
+ );
+ }
+ else
+ {
+ // 如果没有图标,使用普通构造函数
+ option = new FloatMenuOption(
+ prefix + label,
+ () => SwitchToConfig(index)
+ );
+ }
+
+ // 设置工具提示
+ option.tooltip = description;
+
+ // 如果研究未完成,禁用选项
+ if (!config.IsResearchComplete)
+ {
+ option.Label = prefix + label;
+ option.Disabled = true;
+ option.tooltip = description + "\n\n " + "ARA_EquipmentIncubator.ResearchRequired".Translate() + " " + config.requiredResearch.LabelCap;
+ }
+
+ options.Add(option);
+ }
+
+ if (options.Count > 0)
+ {
+ Find.WindowStack.Add(new FloatMenu(options,
+ (props?.menuTitle ?? "ARA_EquipmentIncubator.MenuTitle").Translate()));
+ }
+ }
+
+ private void SwitchToConfig(int index)
+ {
+ if (EquipmentIncubatorData != null)
+ {
+ EquipmentIncubatorData.SwitchToConfig(index);
+ var config = EquipmentIncubatorData.SelectedConfig;
+ if (config != null && config.thingDef != null)
+ {
+ Messages.Message($"ARA_EquipmentIncubator.TargetSwitched".Translate(config.thingDef.LabelCap),
+ this, MessageTypeDefOf.SilentInput);
+ }
+ }
+ }
+
+ // 查找幼虫
+ private Pawn FindLarva()
+ {
+ var map = Map;
+ if (map == null) return null;
+
+ float searchRadius = Ext.larvaSearchRadius;
+
+ foreach (var pawn in map.mapPawns.SpawnedPawnsInFaction(Faction))
+ {
+ if (pawn.def.defName == "ArachnaeBase_Race_Larva")
+ {
+ if (searchRadius < 999f)
+ {
+ float distance = pawn.Position.DistanceTo(Position);
+ if (distance > searchRadius)
+ continue;
+ }
+
+ if (!pawn.Downed && !pawn.InMentalState &&
+ pawn.mindState != null &&
+ (pawn.CurJob == null || pawn.CurJob.def != ARA_JobDefOf.ARA_OperateEquipmentIncubator))
+ {
+ return pawn;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ // 每tick更新
+ protected override void Tick()
+ {
+ base.Tick();
+
+ if (larvaOperateTicksRemaining > 0)
+ {
+ larvaOperateTicksRemaining--;
+ }
+
+ if (isIncubating)
+ {
+ if (lastMultiplierUpdateTick < 0 || Find.TickManager.TicksGame - lastMultiplierUpdateTick >= MultiplierUpdateInterval)
+ {
+ UpdateSpeedMultiplier();
+ UpdateQualityMultiplier();
+ }
+
+ float currentSpeed = SpeedMultiplier;
+ incubationProgress += currentSpeed;
+
+ qualityProgress += currentSpeed * QualityMultiplier;
+
+ if (incubationProgress >= incubationDuration)
+ {
+ CompleteIncubation();
+ }
+ }
+ }
+
+ // 检查是否在孵化间中
+ private bool IsInIncubatorRoom()
+ {
+ if (!Ext.requiresIncubatorRoom)
+ return true;
+
+ var room = this.GetRoom();
+ if (room == null) return false;
+
+ return room.Role != null && room.Role.defName == "ARA_Incubator_Room";
+ }
+
+ // 计算营养液数量
+ private int CountNearbyNutrientSolutions()
+ {
+ var map = Map;
+ if (map == null) return 0;
+
+ int count = 0;
+ int radius = Ext.NutrientSolutionRadiusInt;
+
+ for (int x = -radius; x <= radius; x++)
+ {
+ for (int y = -radius; y <= radius; y++)
+ {
+ IntVec3 cell = Position + new IntVec3(x, 0, y);
+
+ if (cell.InBounds(map))
+ {
+ TerrainDef terrain = map.terrainGrid.TerrainAt(cell);
+ if (terrain != null && terrain.defName == "ARA_Incubator_Nutrient_Solution")
+ {
+ count++;
+ }
+ }
+ }
+ }
+
+ return count;
+ }
+
+ // 计算房间质量因子
+ private float GetRoomQualityFactor()
+ {
+ if (!Ext.useRoomQualityFactor)
+ return 1.0f;
+
+ var room = this.GetRoom();
+ if (room == null) return 1.0f;
+
+ var statDef = DefDatabase.GetNamedSilentFail("ARA_IncubatorQualityFactor");
+ if (statDef != null)
+ {
+ return room.GetStat(statDef);
+ }
+
+ return 1.0f;
+ }
+
+ // 计算附近其他卵的数量
+ private int CountNearbyOtherOothecas()
+ {
+ var map = Map;
+ if (map == null) return 0;
+
+ int count = 0;
+ var allBuildings = map.listerThings.ThingsOfDef(this.def);
+
+ foreach (var building in allBuildings)
+ {
+ if (building == this) continue;
+
+ if (building.def.defName == "ARA_Pawn_Ootheca" || building.def.defName == "ARA_Equipment_Ootheca")
+ {
+ bool isNearby = false;
+
+ if (Ext.checkSameRoomForOotheca)
+ {
+ var room = building.GetRoom();
+ if (room != null && room == this.GetRoom())
+ {
+ isNearby = true;
+ }
+ }
+
+ if (!isNearby)
+ {
+ float distance = building.Position.DistanceTo(this.Position);
+ if (distance <= Ext.nearbyOothecaRadius)
+ {
+ isNearby = true;
+ }
+ }
+
+ if (isNearby)
+ {
+ count++;
+ }
+ }
+ }
+
+ return count;
+ }
+
+ // 更新速度乘数
+ private void UpdateSpeedMultiplier()
+ {
+ float multiplier = 1.0f;
+
+ if (Ext.requiresIncubatorRoom && !IsInIncubatorRoom())
+ {
+ multiplier *= Ext.speedPenaltyOutsideIncubator;
+ }
+
+ int nutrientSolutionCount = CountNearbyNutrientSolutions();
+ float nutrientBonus = 1.0f + (nutrientSolutionCount * Ext.nutrientSolutionBonusPerTile);
+
+ multiplier *= nutrientBonus;
+
+ speedMultiplier = multiplier;
+ lastMultiplierUpdateTick = Find.TickManager.TicksGame;
+ }
+
+ // 更新质量乘数
+ private void UpdateQualityMultiplier()
+ {
+ float multiplier = 1.0f;
+
+ if (Ext.healthAffectsQuality)
+ {
+ float healthPercent = (float)HitPoints / MaxHitPoints;
+ multiplier *= healthPercent;
+ }
+
+ if (Ext.useRoomQualityFactor)
+ {
+ float roomFactor = GetRoomQualityFactor();
+ multiplier *= roomFactor;
+ }
+
+ int nearbyOothecaCount = CountNearbyOtherOothecas();
+ float oothecaPenalty = Mathf.Max(0f, 1.0f - (nearbyOothecaCount * Ext.nearbyOothecaPenaltyPerUnit));
+ multiplier *= oothecaPenalty;
+
+ qualityMultiplier = Mathf.Clamp(multiplier, 0f, 1.0f);
+ }
+
+ // 保存/加载
+ public override void ExposeData()
+ {
+ base.ExposeData();
+
+ Scribe_Values.Look(ref isIncubating, "isIncubating", false);
+ Scribe_Values.Look(ref incubationProgress, "incubationProgress", 0f);
+ Scribe_Values.Look(ref incubationDuration, "incubationDuration", 0f);
+ Scribe_Defs.Look(ref incubatingThingDef, "incubatingThingDef");
+ Scribe_References.Look(ref assignedLarva, "assignedLarva");
+ Scribe_Values.Look(ref larvaOperateTicksRemaining, "larvaOperateTicksRemaining", 0);
+ Scribe_Values.Look(ref speedMultiplier, "speedMultiplier", 1.0f);
+ Scribe_Values.Look(ref lastMultiplierUpdateTick, "lastMultiplierUpdateTick", -1);
+ Scribe_Values.Look(ref qualityMultiplier, "qualityMultiplier", 1.0f);
+ Scribe_Values.Look(ref qualityProgress, "qualityProgress", 0f);
+ Scribe_Values.Look(ref qualityTotal, "qualityTotal", 0f);
+ }
+ }
+}
diff --git a/Source/ArachnaeSwarm/Buildings/Building_EquipmentOotheca/CompProperties_EquipmentIncubatorData.cs b/Source/ArachnaeSwarm/Buildings/Building_EquipmentOotheca/CompProperties_EquipmentIncubatorData.cs
new file mode 100644
index 0000000..8be9e6a
--- /dev/null
+++ b/Source/ArachnaeSwarm/Buildings/Building_EquipmentOotheca/CompProperties_EquipmentIncubatorData.cs
@@ -0,0 +1,332 @@
+// File: Comps/CompProperties_EquipmentIncubatorData.cs
+using RimWorld;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using UnityEngine;
+using Verse;
+
+namespace ArachnaeSwarm
+{
+ // 装备孵化配置
+ public class EquipmentIncubationConfig : IExposable
+ {
+ public ThingDef thingDef;
+ public ResearchProjectDef requiredResearch;
+ public float daysRequired; // 从stat中读取
+ public string buttonIconPath;
+
+ // 缓存的生产时间(避免重复获取stat)
+ private float? cachedDaysRequired;
+
+ public EquipmentIncubationConfig() { }
+
+ public EquipmentIncubationConfig(ThingDef thingDef, ResearchProjectDef requiredResearch = null,
+ string buttonIconPath = null)
+ {
+ this.thingDef = thingDef;
+ this.requiredResearch = requiredResearch;
+ this.buttonIconPath = buttonIconPath;
+ cachedDaysRequired = null;
+ }
+
+ public void ExposeData()
+ {
+ Scribe_Defs.Look(ref thingDef, "thingDef");
+ Scribe_Defs.Look(ref requiredResearch, "requiredResearch");
+ Scribe_Values.Look(ref buttonIconPath, "buttonIconPath");
+
+ // 不保存缓存值,重新计算
+ if (Scribe.mode == LoadSaveMode.LoadingVars)
+ {
+ cachedDaysRequired = null;
+ }
+ }
+
+ // 获取生产时间(天)
+ public float DaysRequired
+ {
+ get
+ {
+ if (cachedDaysRequired.HasValue)
+ return cachedDaysRequired.Value;
+
+ if (thingDef == null)
+ {
+ cachedDaysRequired = 1f;
+ return cachedDaysRequired.Value;
+ }
+
+ // 从stat中读取ARA_IncubationTime
+ var statDef = DefDatabase.GetNamedSilentFail("ARA_IncubationTime");
+ if (statDef != null)
+ {
+ cachedDaysRequired = thingDef.GetStatValueAbstract(statDef, null);
+ }
+ else
+ {
+ // 默认值
+ cachedDaysRequired = 1f;
+ }
+
+ return cachedDaysRequired.Value;
+ }
+ }
+
+ // 检查是否满足研究要求
+ public bool IsResearchComplete => requiredResearch == null || requiredResearch.IsFinished;
+
+ // 获取描述
+ public string GetDescription()
+ {
+ var builder = new StringBuilder();
+
+ if (thingDef != null)
+ {
+ builder.AppendLine(thingDef.description ?? "ARA_EquipmentIncubator.NoDescription".Translate());
+ builder.AppendLine();
+ builder.AppendLine("ARA_EquipmentIncubator.IncubationTime".Translate(DaysRequired));
+ }
+
+ if (requiredResearch != null)
+ {
+ if (requiredResearch.IsFinished)
+ builder.AppendLine("ARA_EquipmentIncubator.ResearchCompleted".Translate(requiredResearch.LabelCap));
+ else
+ builder.AppendLine("ARA_EquipmentIncubator.ResearchRequired".Translate(requiredResearch.LabelCap));
+ }
+
+ return builder.ToString().TrimEndNewlines();
+ }
+ }
+
+ // 装备孵化器组件属性
+ public class CompProperties_EquipmentIncubatorData : CompProperties
+ {
+ // 支持手动指定配置列表(可选)
+ public List incubationConfigs;
+
+ // 默认选择索引
+ public int defaultIndex = 0;
+
+ // Gizmo相关配置
+ public string buttonLabel = "ARA_EquipmentIncubator.IncubateLabel";
+ public string buttonDesc = "ARA_EquipmentIncubator.ButtonDesc";
+ public string menuTitle = "ARA_EquipmentIncubator.MenuTitle";
+ public string defaultIconPath = "UI/Commands/Default";
+
+ // 是否自动扫描所有ThingDef来构建配置列表
+ public bool autoScanThingDefs = true;
+
+ // 手动指定要扫描的ThingDef类型(可选)
+ public List thingDefCategories;
+ public List thingCategoryDefs;
+
+ public CompProperties_EquipmentIncubatorData()
+ {
+ compClass = typeof(CompEquipmentIncubatorData);
+ }
+ }
+
+ // 装备孵化器数据组件
+ public class CompEquipmentIncubatorData : ThingComp
+ {
+ private CompProperties_EquipmentIncubatorData Props => (CompProperties_EquipmentIncubatorData)props;
+
+ // 当前选择的配置索引
+ private int selectedIndex = -1;
+
+ // 缓存的配置列表
+ private List cachedConfigs;
+ private bool configsBuilt = false;
+
+ // 公开获取孵化配置列表的方法
+ public List IncubationConfigs
+ {
+ get
+ {
+ if (!configsBuilt)
+ {
+ BuildIncubationConfigs();
+ }
+ return cachedConfigs ?? new List();
+ }
+ }
+
+ // 获取当前选择的配置
+ public EquipmentIncubationConfig SelectedConfig
+ {
+ get
+ {
+ var configs = IncubationConfigs;
+ if (configs.Count == 0) return null;
+
+ // 初始化选择
+ if (selectedIndex == -1)
+ {
+ selectedIndex = Mathf.Clamp(Props.defaultIndex, 0, configs.Count - 1);
+ }
+
+ if (selectedIndex < 0 || selectedIndex >= configs.Count)
+ selectedIndex = 0;
+
+ return configs[selectedIndex];
+ }
+ }
+
+ // 获取当前选择的ThingDef
+ public ThingDef SelectedThingDef => SelectedConfig?.thingDef;
+
+ // 构建孵化配置列表
+ private void BuildIncubationConfigs()
+ {
+ cachedConfigs = new List();
+ configsBuilt = true;
+
+ // 优先使用手动配置的列表
+ if (Props.incubationConfigs != null && Props.incubationConfigs.Count > 0)
+ {
+ foreach (var config in Props.incubationConfigs)
+ {
+ if (config?.thingDef != null)
+ {
+ cachedConfigs.Add(config);
+ }
+ }
+
+ if (cachedConfigs.Count > 0)
+ return;
+ }
+
+ // 如果没有手动配置,且启用了自动扫描,则扫描所有ThingDef
+ if (Props.autoScanThingDefs)
+ {
+ ScanThingDefsForConfigs();
+ }
+ }
+
+ // 扫描所有ThingDef来构建配置列表
+ private void ScanThingDefsForConfigs()
+ {
+ var allThingDefs = DefDatabase.AllDefsListForReading;
+
+ foreach (var thingDef in allThingDefs)
+ {
+ // 检查该ThingDef是否包含CompProperties_ExtraIncubationInfo组件
+ var extraInfoProps = thingDef.comps?.FirstOrDefault(c => c is CompProperties_ExtraIncubationInfo)
+ as CompProperties_ExtraIncubationInfo;
+
+ if (extraInfoProps == null)
+ continue;
+
+ // 检查cocoonDefs是否包含当前建筑
+ bool isForThisCocoon = false;
+
+ if (extraInfoProps.cocoonDefs != null && extraInfoProps.cocoonDefs.Count > 0)
+ {
+ isForThisCocoon = extraInfoProps.cocoonDefs.Contains(parent.def);
+ }
+ else if (extraInfoProps.cocoonDef != null)
+ {
+ // 向后兼容:检查单个cocoonDef
+ isForThisCocoon = extraInfoProps.cocoonDef == parent.def;
+ }
+
+ if (!isForThisCocoon)
+ continue;
+
+ // 检查是否有ARA_IncubationTime这个stat
+ var incubationTimeStat = DefDatabase.GetNamedSilentFail("ARA_IncubationTime");
+ if (incubationTimeStat == null)
+ {
+ Log.Warning($"ThingDef {thingDef.defName} has CompProperties_ExtraIncubationInfo but ARA_IncubationTime stat is not defined.");
+ continue;
+ }
+
+ // 获取孵化时间
+ float daysRequired = thingDef.GetStatValueAbstract(incubationTimeStat, null);
+ if (daysRequired <= 0)
+ {
+ Log.Warning($"ThingDef {thingDef.defName} has invalid incubation time: {daysRequired}");
+ continue;
+ }
+
+ // 创建配置
+ var config = new EquipmentIncubationConfig
+ {
+ thingDef = thingDef,
+ daysRequired = daysRequired,
+ // 可以在这里添加其他配置,比如所需研究等
+ // requiredResearch = ...
+ // buttonIconPath = ...
+ };
+
+ cachedConfigs.Add(config);
+ }
+
+ // 按物品名称排序
+ cachedConfigs.Sort((a, b) => string.Compare(a.thingDef?.label ?? "", b.thingDef?.label ?? ""));
+
+ Log.Message($"Built {cachedConfigs.Count} equipment incubation configs for {parent.def.defName}");
+ }
+
+ // 切换到特定索引
+ public void SwitchToConfig(int index)
+ {
+ if (index >= 0 && index < IncubationConfigs.Count)
+ {
+ selectedIndex = index;
+ }
+ }
+
+ // 检查配置是否可用(研究是否完成)
+ public bool IsConfigAvailable(int index)
+ {
+ if (index < 0 || index >= IncubationConfigs.Count)
+ return false;
+
+ var config = IncubationConfigs[index];
+ return config?.IsResearchComplete ?? false;
+ }
+
+ // 获取配置索引
+ public int GetSelectedIndex()
+ {
+ return selectedIndex;
+ }
+
+ // 存档加载
+ public override void PostExposeData()
+ {
+ base.PostExposeData();
+ Scribe_Values.Look(ref selectedIndex, "selectedIndex", -1);
+ Scribe_Values.Look(ref configsBuilt, "configsBuilt", false);
+
+ if (Scribe.mode == LoadSaveMode.LoadingVars)
+ {
+ // 重置缓存,在需要时重新构建
+ cachedConfigs = null;
+ configsBuilt = false;
+ }
+ }
+
+ // 在建筑信息中显示额外信息
+ public override string CompInspectStringExtra()
+ {
+ var current = SelectedConfig;
+ if (current != null && current.thingDef != null)
+ {
+ string status = "ARA_EquipmentIncubator.IncubationTarget".Translate(current.thingDef.LabelCap);
+
+ if (current.requiredResearch != null && !current.requiredResearch.IsFinished)
+ {
+ status += " (" + "ARA_EquipmentIncubator.Requires".Translate() + ": " + current.requiredResearch.LabelCap + ")";
+ }
+
+ return status;
+ }
+
+ return base.CompInspectStringExtra();
+ }
+ }
+}
diff --git a/Source/ArachnaeSwarm/Buildings/Building_EquipmentOotheca/ITab_EquipmentOotheca_Incubation.cs b/Source/ArachnaeSwarm/Buildings/Building_EquipmentOotheca/ITab_EquipmentOotheca_Incubation.cs
new file mode 100644
index 0000000..2c6573a
--- /dev/null
+++ b/Source/ArachnaeSwarm/Buildings/Building_EquipmentOotheca/ITab_EquipmentOotheca_Incubation.cs
@@ -0,0 +1,237 @@
+// File: ITabs/ITab_EquipmentOotheca_Incubation.cs
+using UnityEngine;
+using Verse;
+using System.Collections.Generic;
+using RimWorld;
+using Verse.Sound;
+
+namespace ArachnaeSwarm
+{
+ public class ITab_EquipmentOotheca_Incubation : ITab
+ {
+ private const float BarHeight = 20f;
+ private const float Margin = 10f;
+ private const float LabelHeight = 30f;
+ private const float SmallLabelHeight = 20f;
+ private const float ButtonHeight = 25f;
+ private const float TabWidth = 320f;
+ private const float TabHeight = 420f;
+
+ private Vector2 scrollPosition = Vector2.zero;
+ private const float ViewHeight = 450f;
+
+ public override bool IsVisible
+ {
+ get
+ {
+ return SelThing.Faction == Faction.OfPlayer;
+ }
+ }
+
+ public ITab_EquipmentOotheca_Incubation()
+ {
+ size = new Vector2(TabWidth, TabHeight);
+ labelKey = "ARA_EquipmentIncubator.IncubationTab";
+ tutorTag = "EquipmentIncubation";
+ }
+
+ protected override void FillTab()
+ {
+ Rect rect = new Rect(0f, 0f, size.x, size.y).ContractedBy(Margin);
+ Widgets.DrawMenuSection(rect);
+ Building_EquipmentOotheca ootheca = SelThing as Building_EquipmentOotheca;
+ if (ootheca == null)
+ {
+ Widgets.Label(rect, "ARA_EquipmentIncubator.NotAnEquipmentOotheca".Translate());
+ return;
+ }
+ rect = rect.ContractedBy(5f);
+
+ Rect viewRect = new Rect(0f, 0f, rect.width - 16f, ViewHeight);
+ Rect scrollRect = new Rect(rect.x, rect.y, rect.width, rect.height);
+
+ Widgets.BeginScrollView(scrollRect, ref scrollPosition, viewRect);
+
+ float curY = 0f;
+
+ Rect titleRect = new Rect(0f, curY, viewRect.width, LabelHeight);
+ string title = "ARA_EquipmentIncubator.IncubationProgress".Translate();
+ Text.Font = GameFont.Medium;
+ Widgets.Label(titleRect, title);
+ Text.Font = GameFont.Small;
+ curY += LabelHeight + 15f;
+
+ float buttonWidth = (viewRect.width - 10f) / 2f;
+
+ Rect speedButtonRect = new Rect(0f, curY, buttonWidth, ButtonHeight);
+ string speedText = "ARA_EquipmentIncubator.Speed".Translate() + ": " + ootheca.SpeedMultiplier.ToStringPercent();
+
+ Color speedColor = Color.white;
+ if (ootheca.SpeedMultiplier != 1.0f)
+ {
+ speedColor = ootheca.SpeedMultiplier > 1.0f ?
+ new Color(0.2f, 0.8f, 0.2f) :
+ new Color(0.8f, 0.8f, 0.2f);
+ }
+
+ if (Widgets.ButtonText(speedButtonRect, speedText, true, true, speedColor))
+ {
+ // 可选:显示详细信息
+ }
+
+ TooltipHandler.TipRegion(speedButtonRect, () => ootheca.GetSpeedFactorsDescription(), 987654321);
+
+ Rect qualityButtonRect = new Rect(buttonWidth + 10f, curY, buttonWidth, ButtonHeight);
+ string qualityText = "ARA_EquipmentIncubator.Quality".Translate() + ": " + ootheca.QualityMultiplier.ToStringPercent();
+
+ Color qualityColor = Color.white;
+ float qualityMultiplier = ootheca.QualityMultiplier;
+ if (qualityMultiplier != 1.0f)
+ {
+ if (qualityMultiplier > 0.9f)
+ qualityColor = new Color(0.2f, 0.8f, 0.2f);
+ else if (qualityMultiplier > 0.7f)
+ qualityColor = new Color(0.8f, 0.8f, 0.2f);
+ else if (qualityMultiplier > 0.5f)
+ qualityColor = new Color(0.9f, 0.6f, 0.2f);
+ else
+ qualityColor = new Color(0.8f, 0.2f, 0.2f);
+ }
+
+ if (Widgets.ButtonText(qualityButtonRect, qualityText, true, true, qualityColor))
+ {
+ // 可选:显示详细信息
+ }
+
+ TooltipHandler.TipRegion(qualityButtonRect, () => ootheca.GetQualityFactorsDescription(), 987654322);
+
+ curY += ButtonHeight + 25f;
+
+ if (ootheca.isIncubating && ootheca.incubatingThingDef != null)
+ {
+ float progressPercent = ootheca.AdjustedProgressPercent;
+ float qualityPercent = ootheca.QualityPercent;
+ float daysRemaining = ootheca.GetRemainingDays();
+ float hoursRemaining = ootheca.GetRemainingHours();
+
+ Rect targetRect = new Rect(0f, curY, viewRect.width, SmallLabelHeight);
+ Widgets.Label(targetRect, "ARA_EquipmentIncubator.Target".Translate() + ": " + ootheca.incubatingThingDef.LabelCap);
+ curY += SmallLabelHeight + 20f;
+
+ Rect progressBarRect = new Rect(0f, curY, viewRect.width, BarHeight);
+ Rect progressLabelRect = new Rect(progressBarRect.x, progressBarRect.y - 20, progressBarRect.width, 18);
+ Text.Anchor = TextAnchor.MiddleCenter;
+ GUI.color = new Color(0.9f, 0.9f, 0.9f, 1f);
+ Widgets.Label(progressLabelRect, "ARA_EquipmentIncubator.IncubationProgressLabel".Translate());
+ Text.Anchor = TextAnchor.UpperLeft;
+ GUI.color = Color.white;
+
+ Widgets.FillableBar(progressBarRect, progressPercent, SolidColorMaterials.NewSolidColorTexture(new Color(0.2f, 0.8f, 0.2f, 0.5f)));
+ Widgets.FillableBarChangeArrows(progressBarRect, progressPercent);
+
+ string progressText = $"{progressPercent:P0}";
+ Text.Anchor = TextAnchor.MiddleCenter;
+ Widgets.Label(progressBarRect, progressText);
+ Text.Anchor = TextAnchor.UpperLeft;
+
+ curY += BarHeight + 30f;
+
+ Rect qualityBarRect = new Rect(0f, curY, viewRect.width, BarHeight);
+ Rect qualityLabelRect = new Rect(qualityBarRect.x, qualityBarRect.y - 20, qualityBarRect.width, 18);
+ Text.Anchor = TextAnchor.MiddleCenter;
+ GUI.color = new Color(0.9f, 0.9f, 0.9f, 1f);
+ Widgets.Label(qualityLabelRect, "ARA_EquipmentIncubator.QualityProgress".Translate());
+ Text.Anchor = TextAnchor.UpperLeft;
+ GUI.color = Color.white;
+
+ Widgets.FillableBar(qualityBarRect, qualityPercent, SolidColorMaterials.NewSolidColorTexture(new Color(0.1f, 0.4f, 0.8f, 0.5f)));
+
+ string qualityProgressText = $"{qualityPercent:P0}";
+ Text.Anchor = TextAnchor.MiddleCenter;
+ Widgets.Label(qualityBarRect, qualityProgressText);
+ Text.Anchor = TextAnchor.UpperLeft;
+
+ Rect targetQualityRect = new Rect(qualityBarRect.x + qualityBarRect.width - 40, qualityBarRect.y, 40, BarHeight);
+ GUI.color = new Color(0.8f, 0.8f, 0.8f, 0.7f);
+ Text.Anchor = TextAnchor.MiddleRight;
+ Widgets.Label(targetQualityRect, $"{ootheca.QualityMultiplier:P0}");
+ Text.Anchor = TextAnchor.UpperLeft;
+ GUI.color = Color.white;
+
+ curY += BarHeight + 25f;
+
+ Rect timeRect = new Rect(0f, curY, viewRect.width, SmallLabelHeight);
+ string timeText = "ARA_EquipmentIncubator.TimeRemaining".Translate() + ": " + daysRemaining.ToString("F1") + " " + "ARA_EquipmentIncubator.Days".Translate();
+ if (hoursRemaining > 0.1f && daysRemaining < 1f)
+ {
+ timeText += " (" + hoursRemaining.ToString("F1") + " " + "ARA_EquipmentIncubator.Hours".Translate() + ")";
+ }
+ Widgets.Label(timeRect, timeText);
+ }
+ else if (ootheca.assignedLarva != null)
+ {
+ Rect statusRect = new Rect(0f, curY, viewRect.width, SmallLabelHeight * 2);
+ if (ootheca.larvaOperateTicksRemaining > 0)
+ {
+ float secondsRemaining = ootheca.larvaOperateTicksRemaining / 60f;
+ Widgets.Label(statusRect, "ARA_EquipmentIncubator.LarvaIsActivatingOotheca".Translate() + "\n" +
+ secondsRemaining.ToString("F1") + " " + "ARA_EquipmentIncubator.SecondsRemaining".Translate());
+ }
+ else
+ {
+ Widgets.Label(statusRect, "ARA_EquipmentIncubator.LarvaIsOnTheWay".Translate());
+ }
+
+ curY += SmallLabelHeight * 2 + 15f;
+
+ var config = ootheca.EquipmentIncubatorData?.SelectedConfig;
+ if (config != null)
+ {
+ curY += 10f;
+ Rect targetRect = new Rect(0f, curY, viewRect.width, SmallLabelHeight * 3);
+ string targetText = "ARA_EquipmentIncubator.ReadyToIncubate".Translate() + "\n" + config.thingDef.LabelCap;
+
+ if (!config.IsResearchComplete && config.requiredResearch != null)
+ {
+ targetText += "\n" + "(" + "ARA_EquipmentIncubator.Requires".Translate() + ": " + config.requiredResearch.LabelCap + ")";
+ }
+
+ Widgets.Label(targetRect, targetText);
+ }
+ }
+ else
+ {
+ var config = ootheca.EquipmentIncubatorData?.SelectedConfig;
+ if (config != null)
+ {
+ Rect targetRect = new Rect(0f, curY, viewRect.width, SmallLabelHeight * 3);
+ string targetText = "ARA_EquipmentIncubator.ReadyToIncubate".Translate() + "\n" + config.thingDef.LabelCap;
+
+ if (!config.IsResearchComplete && config.requiredResearch != null)
+ {
+ targetText += "\n" + "(" + "ARA_EquipmentIncubator.Requires".Translate() + ": " + config.requiredResearch.LabelCap + ")";
+ }
+
+ Widgets.Label(targetRect, targetText);
+ curY += SmallLabelHeight * 3 + 10f;
+ }
+ else
+ {
+ Rect noTargetRect = new Rect(0f, curY, viewRect.width, SmallLabelHeight);
+ Widgets.Label(noTargetRect, "ARA_EquipmentIncubator.NoIncubationTargetSelected".Translate());
+ curY += SmallLabelHeight + 10f;
+ }
+ }
+
+ curY += 20f;
+ viewRect.height = curY;
+
+ Widgets.EndScrollView();
+ }
+
+ protected override void UpdateSize()
+ {
+ size = new Vector2(TabWidth, TabHeight);
+ }
+ }
+}
diff --git a/Source/ArachnaeSwarm/Buildings/Building_EquipmentOotheca/JobDriver_OperateEquipmentIncubator.cs b/Source/ArachnaeSwarm/Buildings/Building_EquipmentOotheca/JobDriver_OperateEquipmentIncubator.cs
new file mode 100644
index 0000000..014676c
--- /dev/null
+++ b/Source/ArachnaeSwarm/Buildings/Building_EquipmentOotheca/JobDriver_OperateEquipmentIncubator.cs
@@ -0,0 +1,68 @@
+// File: JobDrivers/JobDriver_OperateEquipmentIncubator.cs
+using RimWorld;
+using System.Collections.Generic;
+using Verse;
+using Verse.AI;
+
+namespace ArachnaeSwarm
+{
+ public class JobDriver_OperateEquipmentIncubator : JobDriver
+ {
+ private const int OperationDuration = 180;
+
+ private Building_EquipmentOotheca EquipmentOotheca => (Building_EquipmentOotheca)job.targetA.Thing;
+
+ public override bool TryMakePreToilReservations(bool errorOnFailed)
+ {
+ return pawn.Reserve(job.targetA, job, 1, -1, null, errorOnFailed);
+ }
+
+ protected override IEnumerable MakeNewToils()
+ {
+ this.FailOnDespawnedNullOrForbidden(TargetIndex.A);
+ this.FailOn(() => EquipmentOotheca == null);
+
+ yield return Toils_Goto.GotoThing(TargetIndex.A, PathEndMode.InteractionCell)
+ .FailOnSomeonePhysicallyInteracting(TargetIndex.A);
+
+ yield return Toils_General.WaitWith(TargetIndex.A, 10, true, true);
+
+ var operate = new Toil();
+ operate.initAction = () =>
+ {
+ EquipmentOotheca?.NotifyLarvaArrived(pawn);
+ };
+ operate.tickAction = () =>
+ {
+ pawn.rotationTracker.FaceCell(EquipmentOotheca.Position);
+ };
+ operate.defaultCompleteMode = ToilCompleteMode.Delay;
+ operate.defaultDuration = OperationDuration;
+ operate.WithProgressBar(TargetIndex.A, () =>
+ (float)(OperationDuration - operate.actor.jobs.curDriver.ticksLeftThisToil) / OperationDuration);
+ yield return operate;
+
+ yield return new Toil
+ {
+ initAction = () =>
+ {
+ if (EquipmentOotheca != null && pawn != null && pawn.def.defName == "ArachnaeBase_Race_Larva")
+ {
+ EquipmentOotheca.NotifyLarvaOperationComplete(pawn);
+ pawn.Destroy(DestroyMode.Vanish);
+ }
+ },
+ defaultCompleteMode = ToilCompleteMode.Instant
+ };
+ }
+
+ public override string GetReport()
+ {
+ if (EquipmentOotheca != null)
+ {
+ return "ARA_EquipmentIncubator.ActivatingOotheca".Translate();
+ }
+ return base.GetReport();
+ }
+ }
+}
diff --git a/Source/ArachnaeSwarm/Buildings/Building_Ootheca/Building_Ootheca.cs b/Source/ArachnaeSwarm/Buildings/Building_Ootheca/Building_Ootheca.cs
index 79977fb..4324a4d 100644
--- a/Source/ArachnaeSwarm/Buildings/Building_Ootheca/Building_Ootheca.cs
+++ b/Source/ArachnaeSwarm/Buildings/Building_Ootheca/Building_Ootheca.cs
@@ -1,4 +1,5 @@
-using RimWorld;
+// File: Building_Ootheca.cs
+using RimWorld;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
@@ -32,6 +33,22 @@ namespace ArachnaeSwarm
private float qualityProgress = 0f;
private float qualityTotal = 0f;
+ // 缓存的ModExtension
+ private OothecaIncubatorExtension cachedExtension;
+
+ // 获取ModExtension的辅助属性
+ private OothecaIncubatorExtension Ext
+ {
+ get
+ {
+ if (cachedExtension == null)
+ {
+ cachedExtension = def.GetModExtension() ?? OothecaIncubatorExtension.Default;
+ }
+ return cachedExtension;
+ }
+ }
+
// 属性访问器
public float SpeedMultiplier
{
@@ -65,92 +82,122 @@ namespace ArachnaeSwarm
{
var builder = new StringBuilder();
- builder.AppendLine("IncubationSpeedFactors".Translate());
+ builder.AppendLine("ARA_OothecaIncubator.SpeedFactors".Translate());
builder.AppendLine();
// 1. 检查是否在孵化间中
bool inIncubatorRoom = IsInIncubatorRoom();
- builder.AppendLine(inIncubatorRoom ?
- "InIncubatorRoom".Translate() :
- "NotInIncubatorRoom".Translate());
+ if (Ext.requiresIncubatorRoom)
+ {
+ builder.AppendLine(inIncubatorRoom ?
+ "ARA_OothecaIncubator.InIncubatorRoom".Translate() :
+ "ARA_OothecaIncubator.NotInIncubatorRoom".Translate());
+ }
// 2. 检查营养液数量
int nutrientSolutionCount = CountNearbyNutrientSolutions();
if (nutrientSolutionCount > 0)
{
- builder.AppendLine("NutrientSolutions".Translate(nutrientSolutionCount, nutrientSolutionCount));
+ builder.AppendLine("ARA_OothecaIncubator.NutrientSolutions".Translate(
+ nutrientSolutionCount,
+ nutrientSolutionCount * Ext.nutrientSolutionBonusPerTile * 100));
}
else
{
- builder.AppendLine("NoNutrientSolutionsNearby".Translate());
+ builder.AppendLine("ARA_OothecaIncubator.NoNutrientSolutionsNearby".Translate());
}
+
+ // 显示检测半径
+ builder.AppendLine();
+ builder.AppendLine("ARA_OothecaIncubator.NutrientDetectionRadius".Translate(Ext.nutrientSolutionRadius));
builder.AppendLine();
- builder.AppendLine("TotalSpeedMultiplier".Translate(SpeedMultiplier.ToStringPercent()));
+ builder.AppendLine("ARA_OothecaIncubator.TotalSpeedMultiplier".Translate(SpeedMultiplier.ToStringPercent()));
return builder.ToString().TrimEndNewlines();
}
+
// 获取质量乘数的详细因子信息(用于工具提示)
public string GetQualityFactorsDescription()
{
var builder = new StringBuilder();
- builder.AppendLine("IncubationQualityFactors".Translate());
+ builder.AppendLine("ARA_OothecaIncubator.QualityFactors".Translate());
builder.AppendLine();
- // 1. 建筑血量损失百分比
- float healthPercent = (float)HitPoints / MaxHitPoints;
- builder.AppendLine("BuildingHealth".Translate(healthPercent.ToStringPercent()));
+ // 1. 建筑血量损失百分比(如果启用)
+ if (Ext.healthAffectsQuality)
+ {
+ float healthPercent = (float)HitPoints / MaxHitPoints;
+ builder.AppendLine("ARA_OothecaIncubator.BuildingHealth".Translate(healthPercent.ToStringPercent()));
+ }
- // 2. 房间的ARA_IncubatorQualityFactor
- float roomFactor = GetRoomQualityFactor();
- builder.AppendLine(roomFactor == 1.0f ?
- "RoomFactorNormal".Translate() :
- $"{(roomFactor > 1.0f ? "✓" : "✗")} {"RoomFactorModified".Translate()}{roomFactor.ToStringPercent()}");
+ // 2. 房间的ARA_IncubatorQualityFactor(如果启用)
+ if (Ext.useRoomQualityFactor)
+ {
+ float roomFactor = GetRoomQualityFactor();
+ builder.AppendLine(roomFactor == 1.0f ?
+ "ARA_OothecaIncubator.RoomFactorNormal".Translate() :
+ $"{(roomFactor > 1.0f ? "✓" : "✗")} {"ARA_OothecaIncubator.RoomFactorModified".Translate()}{roomFactor.ToStringPercent()}");
+ }
- // 3. 附近每一个ARA_Pawn_Ootheca,每一个-10%
+ // 3. 附近每一个ARA_Pawn_Ootheca的惩罚
int nearbyOothecaCount = CountNearbyOtherOothecas();
if (nearbyOothecaCount > 0)
{
- builder.AppendLine("NearbyOothecas".Translate(nearbyOothecaCount, Mathf.Min(nearbyOothecaCount * 10, 100)));
+ builder.AppendLine("ARA_OothecaIncubator.NearbyOothecas".Translate(
+ nearbyOothecaCount,
+ Mathf.Min(nearbyOothecaCount * Ext.nearbyOothecaPenaltyPerUnit * 100, 100)));
}
else
{
- builder.AppendLine("NoNearbyOothecas".Translate());
+ builder.AppendLine("ARA_OothecaIncubator.NoNearbyOothecas".Translate());
}
+
+ // 显示检测半径
+ builder.AppendLine();
+ builder.AppendLine("ARA_OothecaIncubator.OothecaDetectionRadius".Translate(Ext.nearbyOothecaRadius));
builder.AppendLine();
- builder.AppendLine("TotalQualityMultiplier".Translate(QualityMultiplier.ToStringPercent()));
+ builder.AppendLine("ARA_OothecaIncubator.TotalQualityMultiplier".Translate(QualityMultiplier.ToStringPercent()));
return builder.ToString().TrimEndNewlines();
}
+
// 构建呼叫幼虫描述
private string BuildCallLarvaDescription(IncubationConfig config)
{
var builder = new StringBuilder();
- builder.AppendLine("CallALarvaToActivate".Translate());
+ builder.AppendLine("ARA_OothecaIncubator.CallLarvaTitle".Translate());
builder.AppendLine();
- builder.AppendLine("LarvaWillComeToTheOotheca".Translate());
+ builder.AppendLine("ARA_OothecaIncubator.LarvaWillCome".Translate());
builder.AppendLine(config.pawnKind.LabelCap);
builder.AppendLine();
+
+ // 显示幼虫搜索半径
+ if (Ext.larvaSearchRadius < 999f)
+ {
+ builder.AppendLine("ARA_OothecaIncubator.LarvaSearchRadius".Translate(Ext.larvaSearchRadius));
+ }
return builder.ToString().TrimEndNewlines();
}
+
// 呼叫幼虫
private void CallLarva()
{
// 检查是否已经在孵化中或有幼虫在任务中
if (isIncubating)
{
- Messages.Message("AlreadyIncubating".Translate() + " " + "CancelCurrentIncubationFirst".Translate(),
+ Messages.Message("ARA_OothecaIncubator.AlreadyIncubating".Translate() + " " + "ARA_OothecaIncubator.CancelFirst".Translate(),
MessageTypeDefOf.RejectInput);
return;
}
if (assignedLarva != null)
{
- Messages.Message("LarvaAlreadyOnTheWay".Translate(),
+ Messages.Message("ARA_OothecaIncubator.LarvaAlreadyOnWay".Translate(),
MessageTypeDefOf.RejectInput);
return;
}
@@ -159,7 +206,7 @@ namespace ArachnaeSwarm
var larva = FindLarva();
if (larva == null)
{
- Messages.Message("NoAvailableLarvaeFound".Translate() + " " + "LarvaMustBeOfRace".Translate(),
+ Messages.Message("ARA_OothecaIncubator.NoLarvaeFound".Translate() + " " + "ARA_OothecaIncubator.LarvaMustBeRace".Translate(),
MessageTypeDefOf.RejectInput);
return;
}
@@ -171,9 +218,10 @@ namespace ArachnaeSwarm
assignedLarva = larva;
- Messages.Message("LarvaCalled".Translate() + " " + "ItWillArriveShortly".Translate(),
+ Messages.Message("ARA_OothecaIncubator.LarvaCalled".Translate() + " " + "ARA_OothecaIncubator.ArriveShortly".Translate(),
MessageTypeDefOf.PositiveEvent);
}
+
// 幼虫开始操作
public void NotifyLarvaArrived(Pawn larva)
{
@@ -189,9 +237,10 @@ namespace ArachnaeSwarm
assignedLarva = larva;
// 显示消息
- Messages.Message("LarvaHasArrived".Translate() + " " + "AndIsActivatingTheOotheca".Translate(),
+ Messages.Message("ARA_OothecaIncubator.LarvaArrived".Translate() + " " + "ARA_OothecaIncubator.ActivatingOotheca".Translate(),
MessageTypeDefOf.SilentInput);
}
+
// 幼虫完成操作(由JobDriver调用)
public void NotifyLarvaOperationComplete(Pawn larva)
{
@@ -228,10 +277,11 @@ namespace ArachnaeSwarm
larvaOperateTicksRemaining = 0;
// 显示消息
- Messages.Message("IncubationStartedFor".Translate() + " " + incubatingPawnKind.LabelCap + ". " +
- "ProcessWillCompleteIn".Translate() + " " + config.daysRequired + " " + "DaysBaseTime".Translate(),
+ Messages.Message("ARA_OothecaIncubator.IncubationStarted".Translate() + " " + incubatingPawnKind.LabelCap + ". " +
+ "ARA_OothecaIncubator.ProcessWillComplete".Translate() + " " + config.daysRequired + " " + "ARA_OothecaIncubator.DaysBaseTime".Translate(),
MessageTypeDefOf.PositiveEvent);
}
+
// 取消孵化
private void CancelIncubation()
{
@@ -244,9 +294,10 @@ namespace ArachnaeSwarm
qualityProgress = 0f;
qualityTotal = 0f;
- Messages.Message("IncubationCancelled".Translate() + " " + "ContentsLost".Translate(),
+ Messages.Message("ARA_OothecaIncubator.IncubationCancelled".Translate() + " " + "ARA_OothecaIncubator.ContentsLost".Translate(),
MessageTypeDefOf.NeutralEvent);
}
+
// 完成孵化
private void CompleteIncubation()
{
@@ -277,16 +328,19 @@ namespace ArachnaeSwarm
qualityTotal = 0f;
// 显示消息
- string qualityText = finalQualityPercent >= 0.9f ? "QualityExcellent".Translate() :
- finalQualityPercent >= 0.7f ? "QualityGood".Translate() :
- finalQualityPercent >= 0.5f ? "QualityAverage".Translate() :
- finalQualityPercent >= 0.3f ? "QualityPoor".Translate() : "QualityVeryPoor".Translate();
+ string qualityText = finalQualityPercent >= 0.9f ? "ARA_OothecaIncubator.QualityExcellent".Translate() :
+ finalQualityPercent >= 0.7f ? "ARA_OothecaIncubator.QualityGood".Translate() :
+ finalQualityPercent >= 0.5f ? "ARA_OothecaIncubator.QualityAverage".Translate() :
+ finalQualityPercent >= 0.3f ? "ARA_OothecaIncubator.QualityPoor".Translate() : "ARA_OothecaIncubator.QualityVeryPoor".Translate();
- Messages.Message("IncubationComplete".Translate() + " " + pawn.LabelCap + " " +
- "HasEmergedWith".Translate() + " " + qualityText + " " +
- "quality".Translate() + " (" + finalQualityPercent.ToStringPercent() + ").",
+ Messages.Message("ARA_OothecaIncubator.IncubationComplete".Translate() + " " + pawn.LabelCap + " " +
+ "ARA_OothecaIncubator.HasEmergedWith".Translate() + " " + qualityText + " " +
+ "ARA_OothecaIncubator.Quality".Translate() + " (" + finalQualityPercent.ToStringPercent() + ").",
MessageTypeDefOf.PositiveEvent);
+
+ Destroy();
}
+
// 显示额外信息(简化版本,只显示速率,不显示因子)
public override string GetInspectString()
{
@@ -305,23 +359,23 @@ namespace ArachnaeSwarm
float hoursRemaining = GetRemainingHours();
if (builder.Length > 0) builder.AppendLine();
- builder.Append("Incubating".Translate() + ": " + incubatingPawnKind.LabelCap);
+ builder.Append("ARA_OothecaIncubator.Incubating".Translate() + ": " + incubatingPawnKind.LabelCap);
builder.AppendLine();
- builder.Append("Progress".Translate() + ": " + progressPercent.ToStringPercent());
+ builder.Append("ARA_OothecaIncubator.Progress".Translate() + ": " + progressPercent.ToStringPercent());
builder.AppendLine();
// 显示剩余时间
- string timeText = "TimeRemaining".Translate() + ": " + daysRemaining.ToString("F1") + " " + "Days".Translate();
+ string timeText = "ARA_OothecaIncubator.TimeRemaining".Translate() + ": " + daysRemaining.ToString("F1") + " " + "ARA_OothecaIncubator.Days".Translate();
if (hoursRemaining > 0.1f && daysRemaining < 1f)
{
- timeText += " (" + hoursRemaining.ToString("F1") + " " + "Hours".Translate() + ")";
+ timeText += " (" + hoursRemaining.ToString("F1") + " " + "ARA_OothecaIncubator.Hours".Translate() + ")";
}
builder.Append(timeText);
// 显示速度和质量(简化版本)
builder.AppendLine();
- builder.Append("Speed".Translate() + ": " + SpeedMultiplier.ToStringPercent() + ", " +
- "Quality".Translate() + ": " + QualityMultiplier.ToStringPercent());
+ builder.Append("ARA_OothecaIncubator.Speed".Translate() + ": " + SpeedMultiplier.ToStringPercent() + ", " +
+ "ARA_OothecaIncubator.Quality".Translate() + ": " + QualityMultiplier.ToStringPercent());
}
else if (assignedLarva != null)
{
@@ -329,11 +383,11 @@ namespace ArachnaeSwarm
if (larvaOperateTicksRemaining > 0)
{
float secondsRemaining = larvaOperateTicksRemaining / 60f;
- builder.Append("LarvaIsOperating".Translate() + ": " + secondsRemaining.ToString("F1") + " " + "SRemaining".Translate());
+ builder.Append("ARA_OothecaIncubator.LarvaOperating".Translate() + ": " + secondsRemaining.ToString("F1") + " " + "ARA_OothecaIncubator.SRemaining".Translate());
}
else
{
- builder.Append("LarvaIsOnTheWay".Translate());
+ builder.Append("ARA_OothecaIncubator.LarvaOnWay".Translate());
}
}
else if (!isIncubating)
@@ -342,12 +396,12 @@ namespace ArachnaeSwarm
if (config != null)
{
if (builder.Length > 0) builder.AppendLine();
- builder.Append("Target".Translate() + ": " + config.pawnKind.LabelCap);
+ builder.Append("ARA_OothecaIncubator.Target".Translate() + ": " + config.pawnKind.LabelCap);
// 只显示当前乘数,不显示条件详情
builder.AppendLine();
- builder.Append("SpeedMultiplier".Translate() + ": " + SpeedMultiplier.ToStringPercent() + ", " +
- "QualityMultiplier".Translate() + ": " + QualityMultiplier.ToStringPercent());
+ builder.Append("ARA_OothecaIncubator.SpeedMultiplier".Translate() + ": " + SpeedMultiplier.ToStringPercent() + ", " +
+ "ARA_OothecaIncubator.QualityMultiplier".Translate() + ": " + QualityMultiplier.ToStringPercent());
}
}
@@ -404,9 +458,9 @@ namespace ArachnaeSwarm
{
yield return new Command_Action
{
- defaultLabel = "CallLarva".Translate(),
+ defaultLabel = "ARA_OothecaIncubator.CallLarva".Translate(),
defaultDesc = BuildCallLarvaDescription(config),
- icon = ContentFinder.Get("UI/Commands/CallLarva", false) ?? BaseContent.BadTex,
+ icon = ContentFinder.Get("ArachnaeSwarm/UI/Commands/ARA_CallLarva", false) ?? BaseContent.BadTex,
action = CallLarva,
hotKey = KeyBindingDefOf.Misc3
};
@@ -417,8 +471,8 @@ namespace ArachnaeSwarm
{
yield return new Command_Action
{
- defaultLabel = "CancelIncubation".Translate(),
- defaultDesc = "CancelIncubationDesc".Translate(),
+ defaultLabel = "ARA_OothecaIncubator.CancelIncubation".Translate(),
+ defaultDesc = "ARA_OothecaIncubator.CancelIncubationDesc".Translate(),
icon = ContentFinder.Get("UI/Commands/Cancel", false) ?? TexCommand.ClearPrioritizedWork,
action = CancelIncubation
};
@@ -461,9 +515,9 @@ namespace ArachnaeSwarm
{
if (config != null && config.pawnKind != null)
{
- return (props?.buttonLabel ?? "Incubate: {0}").Translate(config.pawnKind.LabelCap);
+ return (props?.buttonLabel ?? "ARA_OothecaIncubator.IncubateLabel").Translate(config.pawnKind.LabelCap);
}
- return (props?.buttonLabel ?? "Incubate: {0}").Translate("None");
+ return (props?.buttonLabel ?? "ARA_OothecaIncubator.IncubateLabel").Translate("None");
}
// 构建切换按钮描述
@@ -472,7 +526,7 @@ namespace ArachnaeSwarm
var builder = new StringBuilder();
// 第一部分:按钮功能说明
- builder.AppendLine((props?.buttonDesc ?? "IncubatorButtonDesc").Translate());
+ builder.AppendLine((props?.buttonDesc ?? "ARA_OothecaIncubator.ButtonDesc").Translate());
builder.AppendLine();
if (config != null)
@@ -480,14 +534,14 @@ namespace ArachnaeSwarm
// 第二部分:当前选择的详细信息
if (config.pawnKind != null)
{
- builder.AppendLine($"IncubatorButtonLabel".Translate(config.pawnKind.LabelCap));
+ builder.AppendLine($"ARA_OothecaIncubator.ButtonLabel".Translate(config.pawnKind.LabelCap));
if (!string.IsNullOrEmpty(config.pawnKind.description))
{
builder.AppendLine(config.pawnKind.description);
}
}
- builder.AppendLine($"IncubationTime".Translate(config.daysRequired));
+ builder.AppendLine($"ARA_OothecaIncubator.IncubationTime".Translate(config.daysRequired));
if (config.requiredResearch != null)
{
@@ -503,7 +557,7 @@ namespace ArachnaeSwarm
}
builder.AppendLine();
- builder.AppendLine("IncubatorButtonDesc".Translate());
+ builder.AppendLine("ARA_OothecaIncubator.ButtonDesc".Translate());
return builder.ToString().TrimEndNewlines();
}
@@ -555,7 +609,7 @@ namespace ArachnaeSwarm
{
option.Label = prefix + label;
option.Disabled = true;
- option.tooltip = description + "\n\n "+ "ResearchRequired".Translate() + " " + config.requiredResearch.LabelCap;
+ option.tooltip = description + "\n\n "+ "ARA_OothecaIncubator.ResearchRequired".Translate() + " " + config.requiredResearch.LabelCap;
}
options.Add(option);
@@ -564,7 +618,7 @@ namespace ArachnaeSwarm
if (options.Count > 0)
{
Find.WindowStack.Add(new FloatMenu(options,
- (props?.menuTitle ?? "Select Incubation Target").Translate()));
+ (props?.menuTitle ?? "ARA_OothecaIncubator.MenuTitle").Translate()));
}
}
@@ -577,24 +631,35 @@ namespace ArachnaeSwarm
var config = IncubatorData.SelectedConfig;
if (config != null && config.pawnKind != null)
{
- Messages.Message($"Incubation target switched to: {config.pawnKind.LabelCap}",
+ Messages.Message($"ARA_OothecaIncubator.TargetSwitched".Translate(config.pawnKind.LabelCap),
this, MessageTypeDefOf.SilentInput);
}
}
}
- // 查找幼虫 - 现在通过种族查找
+ // 查找幼虫 - 现在通过种族查找,并考虑搜索半径
private Pawn FindLarva()
{
// 查找地图中属于玩家派系的ArachnaeBase_Race_Larva幼虫
var map = Map;
if (map == null) return null;
+ // 获取搜索半径
+ float searchRadius = Ext.larvaSearchRadius;
+
foreach (var pawn in map.mapPawns.SpawnedPawnsInFaction(Faction))
{
// 检查pawn种族是否为幼虫
if (pawn.def.defName == "ArachnaeBase_Race_Larva")
{
+ // 检查是否在搜索半径内(如果搜索半径小于999)
+ if (searchRadius < 999f)
+ {
+ float distance = pawn.Position.DistanceTo(Position);
+ if (distance > searchRadius)
+ continue;
+ }
+
// 检查pawn是否能够移动且没有其他重要任务
if (!pawn.Downed && !pawn.InMentalState &&
pawn.mindState != null &&
@@ -647,62 +712,33 @@ namespace ArachnaeSwarm
// 应用质量效果到生成的pawn
private void ApplyQualityEffects(Pawn pawn, float qualityPercent)
{
- //// 质量影响:根据质量百分比调整pawn的属性
- //if (qualityPercent < 1.0f)
- //{
- // // 1. 健康影响
- // float healthFactor = Mathf.Lerp(0.5f, 1.0f, qualityPercent);
- // if (healthFactor < 1.0f)
- // {
- // // 减少最大生命值
- // foreach (var part in pawn.health.hediffSet.GetNotMissingParts())
- // {
- // var healthDiff = part.def.GetMaxHealth(pawn) * (1.0f - healthFactor);
- // if (healthDiff > 0)
- // {
- // pawn.health.AddHediff(HediffDefOf.Bruise, part);
- // }
- // }
- // }
-
- // // 2. 年龄影响(如果质量低,pawn可能会以较大年龄出生)
- // if (qualityPercent < 0.6f)
- // {
- // float ageBonus = (1.0f - qualityPercent) * 5; // 最多增加5岁
- // pawn.ageTracker.AgeBiologicalTicks += (long)(ageBonus * 3600000f); // 每岁约3600000ticks
- // }
-
- // // 3. 能力影响
- // if (pawn.skills != null && qualityPercent < 0.8f)
- // {
- // float skillFactor = Mathf.Lerp(0.5f, 1.0f, qualityPercent);
- // foreach (var skill in pawn.skills.skills)
- // {
- // skill.levelInt = Mathf.RoundToInt(skill.levelInt * skillFactor);
- // }
- // }
- //}
+ // 质量影响:根据质量百分比调整pawn的属性
+ // 这里可以添加具体的质量效果逻辑
}
// 检查是否在孵化间中
private bool IsInIncubatorRoom()
{
+ // 如果不要求孵化间,总是返回true
+ if (!Ext.requiresIncubatorRoom)
+ return true;
+
var room = this.GetRoom();
if (room == null) return false;
return room.Role != null && room.Role.defName == "ARA_Incubator_Room";
}
- // 计算周围5格内的营养液数量
+ // 计算周围指定半径内的营养液数量
private int CountNearbyNutrientSolutions()
{
var map = Map;
if (map == null) return 0;
int count = 0;
- int radius = 5; // 4格半径
+ int radius = Ext.NutrientSolutionRadiusInt;
- // 检查周围5格范围内的所有单元格
+ // 检查指定半径范围内的所有单元格
for (int x = -radius; x <= radius; x++)
{
for (int y = -radius; y <= radius; y++)
@@ -728,6 +764,10 @@ namespace ArachnaeSwarm
// 计算房间质量因子
private float GetRoomQualityFactor()
{
+ // 如果不使用房间质量因子,返回1.0
+ if (!Ext.useRoomQualityFactor)
+ return 1.0f;
+
var room = this.GetRoom();
if (room == null) return 1.0f;
@@ -759,17 +799,32 @@ namespace ArachnaeSwarm
// 检查是否为ARA_Pawn_Ootheca
if (building.def.defName == "ARA_Pawn_Ootheca")
{
- // 检查是否在同一个房间或附近
- var room = building.GetRoom();
- if (room != null)
+ bool isNearby = false;
+
+ // 检查是否在同房间内
+ if (Ext.checkSameRoomForOotheca)
{
- // 如果在同一个房间,或者距离较近(5格内)
- float distance = building.Position.DistanceTo(this.Position);
- if (room == this.GetRoom() || distance <= 5f)
+ var room = building.GetRoom();
+ if (room != null && room == this.GetRoom())
{
- count++;
+ isNearby = true;
}
}
+
+ // 检查是否在指定半径内
+ if (!isNearby)
+ {
+ float distance = building.Position.DistanceTo(this.Position);
+ if (distance <= Ext.nearbyOothecaRadius)
+ {
+ isNearby = true;
+ }
+ }
+
+ if (isNearby)
+ {
+ count++;
+ }
}
}
@@ -781,15 +836,15 @@ namespace ArachnaeSwarm
{
float multiplier = 1.0f;
- // 1. 检查是否在孵化间中
- if (!IsInIncubatorRoom())
+ // 1. 检查是否在孵化间中(如果要求)
+ if (Ext.requiresIncubatorRoom && !IsInIncubatorRoom())
{
- multiplier *= 0.8f; // 不在孵化间中,速度80%
+ multiplier *= Ext.speedPenaltyOutsideIncubator; // 应用惩罚
}
// 2. 计算周围营养液的加成
int nutrientSolutionCount = CountNearbyNutrientSolutions();
- float nutrientBonus = 1.0f + (nutrientSolutionCount * 0.01f); // 每个+1%
+ float nutrientBonus = 1.0f + (nutrientSolutionCount * Ext.nutrientSolutionBonusPerTile);
multiplier *= nutrientBonus;
@@ -802,17 +857,23 @@ namespace ArachnaeSwarm
{
float multiplier = 1.0f;
- // 1. 建筑血量损失百分比
- float healthPercent = (float)HitPoints / MaxHitPoints;
- multiplier *= healthPercent;
+ // 1. 建筑血量损失百分比(如果启用)
+ if (Ext.healthAffectsQuality)
+ {
+ float healthPercent = (float)HitPoints / MaxHitPoints;
+ multiplier *= healthPercent;
+ }
- // 2. 房间的ARA_IncubatorQualityFactor
- float roomFactor = GetRoomQualityFactor();
- multiplier *= roomFactor;
+ // 2. 房间的ARA_IncubatorQualityFactor(如果启用)
+ if (Ext.useRoomQualityFactor)
+ {
+ float roomFactor = GetRoomQualityFactor();
+ multiplier *= roomFactor;
+ }
- // 3. 附近每一个ARA_Pawn_Ootheca,每一个-10%
+ // 3. 附近每一个ARA_Pawn_Ootheca的惩罚
int nearbyOothecaCount = CountNearbyOtherOothecas();
- float oothecaPenalty = Mathf.Max(0f, 1.0f - (nearbyOothecaCount * 0.10f)); // 最多减到0
+ float oothecaPenalty = Mathf.Max(0f, 1.0f - (nearbyOothecaCount * Ext.nearbyOothecaPenaltyPerUnit));
multiplier *= oothecaPenalty;
// 确保乘数在合理范围内(0-1)
diff --git a/Source/ArachnaeSwarm/Buildings/Building_Ootheca/OothecaIncubatorExtension.cs b/Source/ArachnaeSwarm/Buildings/Building_Ootheca/OothecaIncubatorExtension.cs
new file mode 100644
index 0000000..b90dba3
--- /dev/null
+++ b/Source/ArachnaeSwarm/Buildings/Building_Ootheca/OothecaIncubatorExtension.cs
@@ -0,0 +1,64 @@
+// File: ModExtensions/OothecaIncubatorExtension.cs
+using RimWorld;
+using Verse;
+
+namespace ArachnaeSwarm
+{
+ public class OothecaIncubatorExtension : DefModExtension
+ {
+ // 营养液检测半径(单位:格)
+ public float nutrientSolutionRadius = 5f;
+
+ // 其他卵距离检测半径(单位:格)
+ public float nearbyOothecaRadius = 5f;
+
+ // 是否检查同房间内的其他卵(默认:是)
+ public bool checkSameRoomForOotheca = true;
+
+ // 营养液加成比例(每个营养液增加多少百分比的速度)
+ public float nutrientSolutionBonusPerTile = 0.01f; // 默认每个+1%
+
+ // 附近其他卵的惩罚比例(每个减少多少百分比的质量)
+ public float nearbyOothecaPenaltyPerUnit = 0.10f; // 默认每个-10%
+
+ // 幼虫搜索半径(单位:格)
+ public float larvaSearchRadius = 999f; // 默认在整个地图搜索
+
+ // 是否需要在孵化间内才能正常工作(默认:否)
+ public bool requiresIncubatorRoom = false;
+
+ // 不在孵化间内的速度惩罚(百分比)
+ public float speedPenaltyOutsideIncubator = 0.8f; // 默认80%
+
+ // 质量因子房间检查(默认:是)
+ public bool useRoomQualityFactor = true;
+
+ // 建筑血量影响质量(默认:是)
+ public bool healthAffectsQuality = true;
+
+ // 获取营养液检测半径的整数形式(用于循环)
+ public int NutrientSolutionRadiusInt => (int)nutrientSolutionRadius;
+
+ // 获取其他卵距离检测半径的整数形式
+ public int NearbyOothecaRadiusInt => (int)nearbyOothecaRadius;
+
+ public static OothecaIncubatorExtension Get(Thing thing)
+ {
+ if (thing?.def?.GetModExtension() is OothecaIncubatorExtension ext)
+ return ext;
+ return null;
+ }
+
+ // 验证配置是否有效
+ public bool IsValid()
+ {
+ return nutrientSolutionRadius >= 0f &&
+ nearbyOothecaRadius >= 0f &&
+ nutrientSolutionBonusPerTile >= 0f &&
+ nearbyOothecaPenaltyPerUnit >= 0f;
+ }
+
+ // 获取默认扩展(用于兼容性)
+ public static OothecaIncubatorExtension Default => new OothecaIncubatorExtension();
+ }
+}
diff --git a/Source/ArachnaeSwarm/Jobs/JobDriver_FeedWithHoney/JobDriver_ExtractHoney.cs b/Source/ArachnaeSwarm/Jobs/JobDriver_FeedWithHoney/JobDriver_ExtractHoney.cs
new file mode 100644
index 0000000..d1b0f97
--- /dev/null
+++ b/Source/ArachnaeSwarm/Jobs/JobDriver_FeedWithHoney/JobDriver_ExtractHoney.cs
@@ -0,0 +1,156 @@
+// File: JobDriver_ExtractHoney.cs
+using System.Collections.Generic;
+using RimWorld;
+using Verse;
+using Verse.AI;
+
+namespace ArachnaeSwarm
+{
+ public class JobDriver_ExtractHoney : JobDriver
+ {
+ private const int ExtractDurationTicks = 180; // 3秒 = 180 ticks
+
+ private Need_HoneyProduction HoneyNeed => pawn.needs?.TryGetNeed();
+
+ // 使用挤出效果
+ private static readonly EffecterDef ExtractEffect = ARA_EffecterDefOf.EatVegetarian;
+ private static readonly SoundDef ExtractSound = SoundDefOf.RawMeat_Eat;
+
+ public override bool TryMakePreToilReservations(bool errorOnFailed)
+ {
+ // 只需要保留目标位置
+ return pawn.Reserve(job.targetA, job, 1, -1, null, errorOnFailed);
+ }
+
+ protected override IEnumerable MakeNewToils()
+ {
+ // 确保自身有蜜罐需求且达到挤出阈值
+ this.FailOn(() => HoneyNeed == null || !CanExtract(HoneyNeed));
+
+ // Toil 1: 移动到目标位置
+ yield return Toils_Goto.GotoCell(TargetIndex.A, PathEndMode.Touch);
+
+ // Toil 2: 执行挤出工作
+ Toil extractToil = new Toil
+ {
+ initAction = delegate
+ {
+ // 初始化挤出动作
+ },
+ tickAction = delegate
+ {
+ // 什么都不做,等待完成
+ },
+ defaultCompleteMode = ToilCompleteMode.Delay,
+ defaultDuration = ExtractDurationTicks
+ };
+
+ // 添加特效和音效
+ extractToil.WithEffect(() => ExtractEffect, TargetIndex.A);
+ extractToil.PlaySustainerOrSound(() => ExtractSound);
+
+ extractToil.AddFinishAction(delegate
+ {
+ // 执行挤出逻辑
+ ExtractHoney();
+ });
+
+ extractToil.WithProgressBar(TargetIndex.A,
+ () => extractToil.actor.jobs.curDriver.ticksLeftThisToil / (float)ExtractDurationTicks);
+
+ yield return extractToil;
+
+ // Toil 3: 完成后的清理
+ yield return new Toil
+ {
+ initAction = delegate
+ {
+ // 可以在这里检查是否还需要继续挤出
+ // 如果蜜罐存量达到阈值,可以安排下一个挤出工作
+ if (HoneyNeed != null && CanExtract(HoneyNeed))
+ {
+ pawn.jobs.jobQueue.EnqueueLast(CreateExtractJob());
+ }
+ },
+ defaultCompleteMode = ToilCompleteMode.Instant
+ };
+ }
+
+ // 检查是否可以挤出(根据MaxLevel决定阈值)
+ private bool CanExtract(Need_HoneyProduction honeyNeed)
+ {
+ // 如果MaxLevel大于1.5,需要超过80%储量才考虑挤蜜
+ if (honeyNeed.MaxLevel > 1.5f)
+ {
+ return honeyNeed.CurLevelPercentage > 0.8f;
+ }
+ // 否则在大于1时挤蜜
+ else
+ {
+ return honeyNeed.CurLevel >= 1.0f;
+ }
+ }
+
+ // 获取挤出阈值(用于显示等)
+ private float GetExtractThreshold(Need_HoneyProduction honeyNeed)
+ {
+ if (honeyNeed.MaxLevel > 1.5f)
+ {
+ return honeyNeed.MaxLevel * 0.8f;
+ }
+ else
+ {
+ return 1.0f;
+ }
+ }
+
+ // 挤出蜂蜜的逻辑
+ private void ExtractHoney()
+ {
+ if (HoneyNeed == null)
+ return;
+
+ // 计算要生成的虫蜜数量(蜜罐等级整数部分)
+ int jellyCount = (int)HoneyNeed.CurLevel;
+
+ // 确保至少生成1个
+ if (jellyCount < 1)
+ return;
+
+ // 创建虫蜜
+ Thing jelly = ThingMaker.MakeThing(DefDatabase.GetNamed("ARA_InsectJelly"));
+ jelly.stackCount = jellyCount;
+
+ // 尝试将虫蜜放在目标位置
+ GenPlace.TryPlaceThing(jelly, job.targetA.Cell, pawn.Map, ThingPlaceMode.Near);
+
+ // 减少蜜罐存量
+ HoneyNeed.ExtractHoney(jellyCount);
+
+ // 播放视觉反馈
+ MoteMaker.ThrowText(job.targetA.Cell.ToVector3Shifted(), pawn.Map,"ARA_ExtractHoney_Message".Translate(jellyCount));
+ }
+
+ // 创建挤出工作的辅助方法
+ private Job CreateExtractJob()
+ {
+ // 寻找最近的空地
+ IntVec3 targetCell = FindNearestEmptyCell(pawn.Position, pawn.Map);
+
+ Job job = JobMaker.MakeJob(DefDatabase.GetNamed("ARA_ExtractHoney"), targetCell);
+ return job;
+ }
+
+ // 寻找最近的空单元格
+ private IntVec3 FindNearestEmptyCell(IntVec3 from, Map map)
+ {
+ if (CellFinder.TryFindBestPawnStandCell(pawn, out IntVec3 cell, false))
+ {
+ return cell;
+ }
+
+ // 如果找不到最佳位置,使用原位置
+ return from;
+ }
+ }
+}
diff --git a/Source/ArachnaeSwarm/Jobs/JobDriver_FeedWithHoney/JobDriver_FeedWithHoney.cs b/Source/ArachnaeSwarm/Jobs/JobDriver_FeedWithHoney/JobDriver_FeedWithHoney.cs
new file mode 100644
index 0000000..bd56ad7
--- /dev/null
+++ b/Source/ArachnaeSwarm/Jobs/JobDriver_FeedWithHoney/JobDriver_FeedWithHoney.cs
@@ -0,0 +1,105 @@
+// File: JobDriver_FeedWithHoney.cs
+using System.Collections.Generic;
+using RimWorld;
+using Verse;
+using Verse.AI;
+
+namespace ArachnaeSwarm
+{
+ public class JobDriver_FeedWithHoney : JobDriver
+ {
+ private const int FeedDurationTicks = 180; // 3秒 = 180 ticks (60 ticks/秒)
+ private const float FoodTransferPerTick = 0.01f; // 每tick传输的食物量
+
+ private Pawn TargetPawn => job.targetA.Pawn;
+ private Need_HoneyProduction HoneyNeed => pawn.needs?.TryGetNeed();
+ private Need_Food TargetFoodNeed => TargetPawn.needs?.TryGetNeed();
+
+ // 使用喂养效果 - 参考Toils_Ingest中的用法
+ private static readonly EffecterDef FeedEffect = ARA_EffecterDefOf.EatVegetarian;
+ private static readonly SoundDef FeedSound = SoundDefOf.RawMeat_Eat;
+
+ public override bool TryMakePreToilReservations(bool errorOnFailed)
+ {
+ if (TargetPawn == null || TargetFoodNeed == null || HoneyNeed == null)
+ return false;
+
+ // 保留目标pawn的位置,以便接近
+ return pawn.Reserve(TargetPawn, job, 1, -1, null, errorOnFailed);
+ }
+
+ protected override IEnumerable MakeNewToils()
+ {
+ // 确保目标有效
+ this.FailOnDespawnedOrNull(TargetIndex.A);
+ this.FailOn(() => TargetPawn.Dead || TargetFoodNeed == null || HoneyNeed == null);
+
+ // Toil 1: 移动到目标面前
+ yield return Toils_Goto.GotoThing(TargetIndex.A, PathEndMode.Touch)
+ .FailOnDespawnedOrNull(TargetIndex.A);
+
+ // Toil 2: 进行喂养工作(参考Toils_Ingest中的效果添加方式)
+ Toil feedToil = new Toil
+ {
+ initAction = delegate
+ {
+ // 初始化喂养动作
+ },
+ tickAction = delegate
+ {
+ // 每tick检查是否还能继续喂养
+ if (HoneyNeed.CurLevel <= 0 || TargetFoodNeed.CurLevelPercentage >= 1.0f)
+ {
+ ReadyForNextToil();
+ return;
+ }
+
+ // 计算本次tick传输的量
+ float transferAmount = FoodTransferPerTick;
+ float honeyAvailable = HoneyNeed.CurLevel;
+
+ // 确保不超过蜜罐存量
+ if (transferAmount > honeyAvailable)
+ transferAmount = honeyAvailable;
+
+ // 确保不超过目标的需求
+ float targetFoodSpace = TargetFoodNeed.MaxLevel - TargetFoodNeed.CurLevel;
+ if (transferAmount > targetFoodSpace)
+ transferAmount = targetFoodSpace;
+
+ // 执行传输
+ HoneyNeed.ExtractHoney(transferAmount);
+ TargetFoodNeed.CurLevel += transferAmount;
+ },
+ defaultCompleteMode = ToilCompleteMode.Delay,
+ defaultDuration = FeedDurationTicks
+ };
+
+ // 添加特效和音效(参考Toils_Ingest.AddIngestionEffects)
+ feedToil.WithEffect(() => FeedEffect, TargetIndex.A);
+ feedToil.PlaySustainerOrSound(() => FeedSound);
+
+ feedToil.AddFinishAction(delegate
+ {
+ // 喂养完成后给予社交互动奖励
+ TargetPawn.interactions?.TryInteractWith(pawn, InteractionDefOf.Chitchat);
+ });
+
+ feedToil.WithProgressBar(TargetIndex.A,
+ () => feedToil.actor.jobs.curDriver.ticksLeftThisToil / (float)FeedDurationTicks,
+ interpolateBetweenActorAndTarget: true);
+
+ yield return feedToil;
+
+ // 修改:移除了第三个Toil中的提示消息,只剩下一个简单的完成Toil
+ yield return new Toil
+ {
+ initAction = delegate
+ {
+ // 这里可以放置任何清理代码,但不要显示消息
+ },
+ defaultCompleteMode = ToilCompleteMode.Instant
+ };
+ }
+ }
+}
diff --git a/Source/ArachnaeSwarm/Jobs/JobDriver_FeedWithHoney/ThinkNode_JobGiver_ExtractHoney.cs b/Source/ArachnaeSwarm/Jobs/JobDriver_FeedWithHoney/ThinkNode_JobGiver_ExtractHoney.cs
new file mode 100644
index 0000000..cbe87e0
--- /dev/null
+++ b/Source/ArachnaeSwarm/Jobs/JobDriver_FeedWithHoney/ThinkNode_JobGiver_ExtractHoney.cs
@@ -0,0 +1,105 @@
+// File: ThinkNode_JobGiver_ExtractHoney.cs
+using System.Collections.Generic;
+using System.Linq;
+using RimWorld;
+using Verse;
+using Verse.AI;
+
+namespace ArachnaeSwarm
+{
+ public class ThinkNode_JobGiver_ExtractHoney : ThinkNode_JobGiver
+ {
+ private const int ScanIntervalTicks = 300; // 5秒扫描一次
+
+ private int lastScanTick = -99999;
+
+ protected override Job TryGiveJob(Pawn pawn)
+ {
+ // 检查是否应该扫描
+ int currentTick = Find.TickManager.TicksGame;
+ if (currentTick - lastScanTick < ScanIntervalTicks)
+ return null;
+
+ lastScanTick = currentTick;
+
+ // 检查甲壳剥离组件和开关状态
+ Comp_ChitinStripping stripComp = pawn.TryGetComp();
+
+ // 检查开关是否开启
+ if (stripComp == null || !stripComp.CanStripChitin || !stripComp.CanStripNow(pawn))
+ return null;
+
+ // 检查自身需求
+ Need_HoneyProduction honeyNeed = pawn.needs?.TryGetNeed();
+ if (honeyNeed == null || !CanExtract(honeyNeed))
+ return null;
+
+ // 如果已经有挤出工作,则不分配新工作
+ if (pawn.CurJob != null && pawn.CurJob.def == DefDatabase.GetNamed("ARA_ExtractHoney"))
+ return null;
+
+ // 寻找最近的空地
+ IntVec3 targetCell = FindNearestEmptyCell(pawn);
+ if (!targetCell.IsValid)
+ return null;
+
+ // 确保可以到达
+ if (!pawn.CanReach(targetCell, PathEndMode.Touch, Danger.Some))
+ return null;
+
+ // 创建挤出工作
+ Job job = JobMaker.MakeJob(DefDatabase.GetNamed("ARA_ExtractHoney"), targetCell);
+ job.count = 1;
+
+ return job;
+ }
+
+ // 检查是否可以挤出(与JobDriver中的逻辑保持一致)
+ private bool CanExtract(Need_HoneyProduction honeyNeed)
+ {
+ // 如果MaxLevel大于1.5,需要超过80%储量才考虑挤蜜
+ if (honeyNeed.MaxLevel > 1.5f)
+ {
+ return honeyNeed.CurLevelPercentage > 0.8f;
+ }
+ // 否则在大于1时挤蜜
+ else
+ {
+ return honeyNeed.CurLevel >= 1.0f;
+ }
+ }
+
+ // 寻找最近的空单元格
+ private IntVec3 FindNearestEmptyCell(Pawn pawn)
+ {
+ // 首先检查当前位置周围3格内是否有空地
+ for (int radius = 1; radius <= 3; radius++)
+ {
+ foreach (IntVec3 cell in GenRadial.RadialCellsAround(pawn.Position, radius, true))
+ {
+ if (cell.InBounds(pawn.Map) &&
+ cell.Standable(pawn.Map) &&
+ cell.GetFirstItem(pawn.Map) == null &&
+ !cell.GetTerrain(pawn.Map).IsWater)
+ {
+ return cell;
+ }
+ }
+ }
+
+ // 如果找不到,使用CellFinder寻找
+ if (CellFinder.TryFindBestPawnStandCell(pawn, out IntVec3 bestCell, false))
+ {
+ return bestCell;
+ }
+
+ // 最后尝试使用原位置(确保不是水面)
+ if (!pawn.Position.GetTerrain(pawn.Map).IsWater)
+ {
+ return pawn.Position;
+ }
+
+ return IntVec3.Invalid;
+ }
+ }
+}
diff --git a/Source/ArachnaeSwarm/Jobs/JobDriver_FeedWithHoney/ThinkNode_JobGiver_FeedWithHoney.cs b/Source/ArachnaeSwarm/Jobs/JobDriver_FeedWithHoney/ThinkNode_JobGiver_FeedWithHoney.cs
new file mode 100644
index 0000000..050efbd
--- /dev/null
+++ b/Source/ArachnaeSwarm/Jobs/JobDriver_FeedWithHoney/ThinkNode_JobGiver_FeedWithHoney.cs
@@ -0,0 +1,107 @@
+// File: ThinkNode_JobGiver_FeedWithHoney.cs
+using System.Collections.Generic;
+using System.Linq;
+using RimWorld;
+using Verse;
+using Verse.AI;
+
+namespace ArachnaeSwarm
+{
+ public class ThinkNode_JobGiver_FeedWithHoney : ThinkNode_JobGiver
+ {
+ private const float MinFoodNeedThreshold = 0.2f; // 目标需要喂养的阈值
+ private const float MinHoneyThreshold = 0.1f; // 蜜罐低于10%时不喂养
+ private const int ScanIntervalTicks = 250; // 扫描间隔 (约4秒)
+ private const float MaxDistance = 30f; // 最大寻找距离
+
+ private int lastScanTick = -99999;
+
+ protected override Job TryGiveJob(Pawn pawn)
+ {
+ // 检查是否应该扫描
+ int currentTick = Find.TickManager.TicksGame;
+ if (currentTick - lastScanTick < ScanIntervalTicks)
+ return null;
+
+ lastScanTick = currentTick;
+
+ // 检查自身需求
+ Need_HoneyProduction honeyNeed = pawn.needs?.TryGetNeed();
+ // 修改:检查蜜罐存量是否低于10%,如果是则不喂养
+ if (honeyNeed == null || honeyNeed.IsEmpty || honeyNeed.CurLevelPercentage < MinHoneyThreshold)
+ return null;
+
+ // 扫描潜在的喂养目标
+ List potentialTargets = new List();
+
+ // 获取地图上所有pawn
+ foreach (Pawn target in pawn.Map.mapPawns.AllPawnsSpawned)
+ {
+ // 跳过自己
+ if (target == pawn)
+ continue;
+
+ // 检查是否属于己方势力(殖民者、奴隶、囚犯)
+ if (!IsFriendlyPawn(pawn, target))
+ continue;
+
+ // 检查距离
+ if (pawn.Position.DistanceTo(target.Position) > MaxDistance)
+ continue;
+
+ // 检查是否需要食物
+ Need_Food targetFoodNeed = target.needs?.TryGetNeed();
+ if (targetFoodNeed == null || targetFoodNeed.CurLevelPercentage > MinFoodNeedThreshold)
+ continue;
+
+ // 检查目标是否可到达
+ if (!pawn.CanReserveAndReach(target, PathEndMode.Touch, Danger.Some))
+ continue;
+
+ potentialTargets.Add(target);
+ }
+
+ // 如果没有目标
+ if (potentialTargets.Count == 0)
+ return null;
+
+ // 按需求紧急程度排序(最饿的优先)
+ potentialTargets.Sort((a, b) =>
+ {
+ Need_Food foodA = a.needs?.TryGetNeed();
+ Need_Food foodB = b.needs?.TryGetNeed();
+ return foodA?.CurLevelPercentage.CompareTo(foodB?.CurLevelPercentage) ?? 0;
+ });
+
+ // 选择最饥饿的目标
+ Pawn targetPawn = potentialTargets[0];
+
+ // 创建喂养工作
+ Job job = JobMaker.MakeJob(DefDatabase.GetNamed("ARA_FeedWithHoney"), targetPawn);
+ job.count = 1;
+
+ return job;
+ }
+
+ private bool IsFriendlyPawn(Pawn feeder, Pawn target)
+ {
+ // 检查目标是否是殖民者、奴隶或囚犯
+ if (target.Faction == Faction.OfPlayer)
+ return true;
+
+ // 检查是否是囚犯(属于玩家)
+ if (target.IsPrisonerOfColony)
+ return true;
+
+ // 检查是否是奴隶
+ if (target.IsSlaveOfColony)
+ return true;
+
+ // 检查是否和喂养者同一派系
+ if (target.Faction != null && target.Faction == feeder.Faction)
+ return true;
+
+ return false;
+ }
+ }
+}
diff --git a/Source/ArachnaeSwarm/Jobs/JobDriver_StripChitin/CompProperties_ChitinStripping.cs b/Source/ArachnaeSwarm/Jobs/JobDriver_StripChitin/CompProperties_ChitinStripping.cs
new file mode 100644
index 0000000..4322caa
--- /dev/null
+++ b/Source/ArachnaeSwarm/Jobs/JobDriver_StripChitin/CompProperties_ChitinStripping.cs
@@ -0,0 +1,29 @@
+// File: Comps/CompProperties_ChitinStripping.cs
+using RimWorld;
+using Verse;
+
+namespace ArachnaeSwarm
+{
+ public class CompProperties_ChitinStripping : CompProperties
+ {
+ // 是否允许剥离甲壳
+ public bool canStripChitin = true;
+
+ // 剥离的阈值(甲壳存量的百分比)
+ public float stripThreshold = 0.8f;
+
+ // 每次剥离产生的最小甲壳数量
+ public int minStripAmount = 1;
+
+ // 剥离工作的间隔(ticks)
+ public int stripInterval = 3000; // 50秒
+
+ // 甲壳物品定义
+ public ThingDef carapaceThingDef = null;
+
+ public CompProperties_ChitinStripping()
+ {
+ compClass = typeof(Comp_ChitinStripping);
+ }
+ }
+}
diff --git a/Source/ArachnaeSwarm/Jobs/JobDriver_StripChitin/Comp_ChitinStripping.cs b/Source/ArachnaeSwarm/Jobs/JobDriver_StripChitin/Comp_ChitinStripping.cs
new file mode 100644
index 0000000..5b77165
--- /dev/null
+++ b/Source/ArachnaeSwarm/Jobs/JobDriver_StripChitin/Comp_ChitinStripping.cs
@@ -0,0 +1,103 @@
+// File: Comps/Comp_ChitinStripping.cs
+using System.Collections.Generic;
+using RimWorld;
+using UnityEngine;
+using Verse;
+
+namespace ArachnaeSwarm
+{
+ public class Comp_ChitinStripping : ThingComp
+ {
+ public CompProperties_ChitinStripping Props => (CompProperties_ChitinStripping)props;
+
+ // 剥离开关状态
+ private bool stripEnabled = true;
+
+ // 上次剥离时间
+ private int lastStripTick = -99999;
+
+ // 属性访问器
+ public bool StripEnabled
+ {
+ get => stripEnabled;
+ set => stripEnabled = value;
+ }
+
+ public bool CanStripChitin => Props.canStripChitin && stripEnabled;
+
+ public float StripThreshold => Props.stripThreshold;
+
+ // 获取甲壳物品定义
+ public ThingDef CarapaceThingDef
+ {
+ get
+ {
+ if (Props.carapaceThingDef != null)
+ return Props.carapaceThingDef;
+
+ return DefDatabase.GetNamed("ARA_Carapace", false);
+ }
+ }
+
+ // 检查是否可以剥离
+ public bool CanStripNow(Pawn pawn)
+ {
+ if (!CanStripChitin || pawn == null || pawn.Dead)
+ return false;
+
+ // 检查间隔
+ int currentTick = Find.TickManager.TicksGame;
+ if (currentTick - lastStripTick < Props.stripInterval)
+ return false;
+
+ // 检查甲壳需求
+ Need_ChitinArmor chitinNeed = pawn.needs?.TryGetNeed();
+ if (chitinNeed == null)
+ return false;
+
+ // 检查是否达到阈值
+ return chitinNeed.CurLevelPercentage >= StripThreshold;
+ }
+
+ // 记录剥离时间
+ public void NotifyStripped()
+ {
+ lastStripTick = Find.TickManager.TicksGame;
+ }
+
+ // 获取上次剥离到现在的时间
+ public int TicksSinceLastStrip => Find.TickManager.TicksGame - lastStripTick;
+
+ // 获取剩余冷却时间
+ public int CooldownTicksRemaining => Mathf.Max(0, Props.stripInterval - TicksSinceLastStrip);
+
+ // 序列化
+ public override void PostExposeData()
+ {
+ base.PostExposeData();
+ Scribe_Values.Look(ref stripEnabled, "stripEnabled", true);
+ Scribe_Values.Look(ref lastStripTick, "lastStripTick", -99999);
+ }
+
+ // 获取Gizmos(命令按钮)
+ public override IEnumerable CompGetGizmosExtra()
+ {
+ // 只在玩家控制的pawn上显示
+ if (parent is Pawn pawn && pawn.Faction?.IsPlayer == true)
+ {
+ // 甲壳剥离开关按钮
+ yield return new Command_Toggle
+ {
+ defaultLabel = "ARA_ChitinStripping_Toggle".Translate(),
+ defaultDesc = "ARA_ChitinStripping_ToggleDesc".Translate(),
+ icon = ContentFinder.Get("ArachnaeSwarm/UI/Commands/ARA_StripChitin"),
+ isActive = () => stripEnabled,
+ toggleAction = () =>
+ {
+ stripEnabled = !stripEnabled;
+ }
+ };
+ }
+ }
+ }
+}
diff --git a/Source/ArachnaeSwarm/Jobs/JobDriver_StripChitin/JobDriver_StripChitin.cs b/Source/ArachnaeSwarm/Jobs/JobDriver_StripChitin/JobDriver_StripChitin.cs
new file mode 100644
index 0000000..1811cec
--- /dev/null
+++ b/Source/ArachnaeSwarm/Jobs/JobDriver_StripChitin/JobDriver_StripChitin.cs
@@ -0,0 +1,158 @@
+// File: JobDriver_StripChitin.cs
+using System.Collections.Generic;
+using RimWorld;
+using Verse;
+using Verse.AI;
+
+namespace ArachnaeSwarm
+{
+ public class JobDriver_StripChitin : JobDriver
+ {
+ private const int StripDurationTicks = 300; // 5秒 = 300 ticks
+
+ // 获取甲壳需求
+ private Need_ChitinArmor ChitinNeed => pawn.needs?.TryGetNeed();
+
+ // 获取甲壳剥离组件
+ private Comp_ChitinStripping StripComp
+ {
+ get
+ {
+ if (pawn == null)
+ return null;
+
+ // 从pawn的def中获取Comp
+ CompProperties_ChitinStripping compProps = pawn.def.GetCompProperties();
+ if (compProps != null)
+ {
+ return pawn.TryGetComp();
+ }
+ return null;
+ }
+ }
+
+ // 特效和音效
+ private static readonly EffecterDef StripEffect = ARA_EffecterDefOf.EatVegetarian;
+ private static readonly SoundDef StripSound = SoundDefOf.RawMeat_Eat;
+
+ public override bool TryMakePreToilReservations(bool errorOnFailed)
+ {
+ // 只需要保留目标位置
+ return pawn.Reserve(job.targetA, job, 1, -1, null, errorOnFailed);
+ }
+
+ protected override IEnumerable MakeNewToils()
+ {
+ // 检查条件
+ this.FailOn(() => ChitinNeed == null);
+ this.FailOn(() => StripComp == null);
+ this.FailOn(() => !StripComp.CanStripNow(pawn));
+ this.FailOn(() => !StripComp.CanStripChitin); // 检查开关
+
+ // Toil 1: 移动到目标位置
+ yield return Toils_Goto.GotoCell(TargetIndex.A, PathEndMode.Touch);
+
+ // Toil 2: 执行剥离工作
+ Toil stripToil = new Toil
+ {
+ initAction = delegate
+ {
+ // 初始化剥离动作
+ },
+ tickAction = delegate
+ {
+ // 什么都不做,等待完成
+ },
+ defaultCompleteMode = ToilCompleteMode.Delay,
+ defaultDuration = StripDurationTicks
+ };
+
+ // 添加特效和音效
+ stripToil.WithEffect(() => StripEffect, TargetIndex.A);
+ stripToil.PlaySustainerOrSound(() => StripSound);
+
+ stripToil.AddFinishAction(delegate
+ {
+ // 执行剥离逻辑
+ StripChitin();
+ });
+
+ stripToil.WithProgressBar(TargetIndex.A,
+ () => stripToil.actor.jobs.curDriver.ticksLeftThisToil / (float)StripDurationTicks,
+ interpolateBetweenActorAndTarget: true);
+
+ yield return stripToil;
+
+ // Toil 3: 完成后的清理
+ yield return new Toil
+ {
+ initAction = delegate
+ {
+ // 记录剥离时间
+ StripComp?.NotifyStripped();
+
+ // 如果甲壳存量仍然达到阈值,可以安排下一个剥离工作
+ if (ChitinNeed != null && StripComp != null && StripComp.CanStripNow(pawn))
+ {
+ pawn.jobs.jobQueue.EnqueueLast(CreateStripJob());
+ }
+ },
+ defaultCompleteMode = ToilCompleteMode.Instant
+ };
+ }
+
+ // 剥离甲壳的逻辑
+ private void StripChitin()
+ {
+ if (ChitinNeed == null || StripComp == null)
+ return;
+
+ // 计算要剥离的甲壳数量
+ // 通常剥离整数部分,但至少剥离1个
+ int stripCount = (int)ChitinNeed.CurLevel;
+ if (stripCount < StripComp.Props.minStripAmount)
+ stripCount = StripComp.Props.minStripAmount;
+
+ // 获取甲壳物品定义
+ ThingDef carapaceDef = StripComp.CarapaceThingDef;
+ if (carapaceDef == null)
+ return;
+
+ // 创建甲壳物品
+ Thing carapace = ThingMaker.MakeThing(carapaceDef);
+ carapace.stackCount = stripCount;
+
+ // 尝试将甲壳放在目标位置
+ GenPlace.TryPlaceThing(carapace, job.targetA.Cell, pawn.Map, ThingPlaceMode.Near);
+
+ // 减少甲壳存量
+ ChitinNeed.ReduceChitin(stripCount);
+
+ // 播放视觉反馈
+ MoteMaker.ThrowText(job.targetA.Cell.ToVector3Shifted(), pawn.Map,
+ "ARA_StripChitin_Message".Translate(stripCount));
+ }
+
+ // 创建剥离工作的辅助方法
+ private Job CreateStripJob()
+ {
+ // 寻找最近的空地
+ IntVec3 targetCell = FindNearestEmptyCell(pawn.Position, pawn.Map);
+
+ Job job = JobMaker.MakeJob(DefDatabase.GetNamed("ARA_StripChitin"), targetCell);
+ return job;
+ }
+
+ // 寻找最近的空单元格
+ private IntVec3 FindNearestEmptyCell(IntVec3 from, Map map)
+ {
+ if (CellFinder.TryFindBestPawnStandCell(pawn, out IntVec3 cell, false))
+ {
+ return cell;
+ }
+
+ // 如果找不到最佳位置,使用原位置
+ return from;
+ }
+ }
+}
diff --git a/Source/ArachnaeSwarm/Jobs/JobDriver_StripChitin/ThinkNode_JobGiver_StripChitin.cs b/Source/ArachnaeSwarm/Jobs/JobDriver_StripChitin/ThinkNode_JobGiver_StripChitin.cs
new file mode 100644
index 0000000..90a4ce7
--- /dev/null
+++ b/Source/ArachnaeSwarm/Jobs/JobDriver_StripChitin/ThinkNode_JobGiver_StripChitin.cs
@@ -0,0 +1,83 @@
+// File: ThinkNode_JobGiver_StripChitin.cs
+using System.Collections.Generic;
+using System.Linq;
+using RimWorld;
+using Verse;
+using Verse.AI;
+
+namespace ArachnaeSwarm
+{
+ public class ThinkNode_JobGiver_StripChitin : ThinkNode_JobGiver
+ {
+ private const int ScanIntervalTicks = 500; // 8.3秒扫描一次
+
+ private int lastScanTick = -99999;
+
+ protected override Job TryGiveJob(Pawn pawn)
+ {
+ // 检查是否应该扫描
+ int currentTick = Find.TickManager.TicksGame;
+ if (currentTick - lastScanTick < ScanIntervalTicks)
+ return null;
+
+ lastScanTick = currentTick;
+
+ // 检查甲壳剥离组件
+ Comp_ChitinStripping stripComp = pawn.TryGetComp();
+ if (stripComp == null || !stripComp.CanStripNow(pawn))
+ return null;
+
+ // 如果已经有剥离工作,则不分配新工作
+ if (pawn.CurJob != null && pawn.CurJob.def == DefDatabase.GetNamed("ARA_StripChitin"))
+ return null;
+
+ // 寻找最近的空地
+ IntVec3 targetCell = FindNearestEmptyCell(pawn);
+ if (!targetCell.IsValid)
+ return null;
+
+ // 确保可以到达
+ if (!pawn.CanReach(targetCell, PathEndMode.Touch, Danger.Some))
+ return null;
+
+ // 创建剥离工作
+ Job job = JobMaker.MakeJob(DefDatabase.GetNamed("ARA_StripChitin"), targetCell);
+ job.count = 1;
+
+ return job;
+ }
+
+ // 寻找最近的空单元格
+ private IntVec3 FindNearestEmptyCell(Pawn pawn)
+ {
+ // 首先检查当前位置周围3格内是否有空地
+ for (int radius = 1; radius <= 3; radius++)
+ {
+ foreach (IntVec3 cell in GenRadial.RadialCellsAround(pawn.Position, radius, true))
+ {
+ if (cell.InBounds(pawn.Map) &&
+ cell.Standable(pawn.Map) &&
+ cell.GetFirstItem(pawn.Map) == null &&
+ !cell.GetTerrain(pawn.Map).IsWater)
+ {
+ return cell;
+ }
+ }
+ }
+
+ // 如果找不到,使用CellFinder寻找
+ if (CellFinder.TryFindBestPawnStandCell(pawn, out IntVec3 bestCell, false))
+ {
+ return bestCell;
+ }
+
+ // 最后尝试使用原位置(确保不是水面)
+ if (!pawn.Position.GetTerrain(pawn.Map).IsWater)
+ {
+ return pawn.Position;
+ }
+
+ return IntVec3.Invalid;
+ }
+ }
+}
diff --git a/Source/ArachnaeSwarm/Jobs/JobDriver_SwarmMaintain/JobDriver_SwarmMaintain.cs b/Source/ArachnaeSwarm/Jobs/JobDriver_SwarmMaintain/JobDriver_SwarmMaintain.cs
new file mode 100644
index 0000000..8f666cc
--- /dev/null
+++ b/Source/ArachnaeSwarm/Jobs/JobDriver_SwarmMaintain/JobDriver_SwarmMaintain.cs
@@ -0,0 +1,78 @@
+// File: Jobs/JobDriver_SwarmMaintain.cs
+using RimWorld;
+using Verse;
+using Verse.AI;
+using System.Collections.Generic;
+
+namespace ArachnaeSwarm
+{
+ public class JobDriver_SwarmMaintain : JobDriver
+ {
+ // 工作完成后增加的维护度
+ public const float MaintenancePerWork = 25f;
+
+ // 工作时间(ticks)
+ private const int WorkDuration = 600; // 10秒
+ // 使用喂养效果 - 参考Toils_Ingest中的用法
+ private static readonly EffecterDef MaintainEffect = ARA_EffecterDefOf.EatVegetarian;
+ private static readonly SoundDef MaintainSound = SoundDefOf.RawMeat_Eat;
+
+
+ public override bool TryMakePreToilReservations(bool errorOnFailed)
+ {
+ return pawn.Reserve(job.targetA, job, 1, -1, null, errorOnFailed);
+ }
+
+ protected override IEnumerable MakeNewToils()
+ {
+ // 1. 前往目标建筑
+ this.FailOnDespawnedNullOrForbidden(TargetIndex.A);
+ yield return Toils_Goto.GotoCell(TargetIndex.A, PathEndMode.Touch);
+
+ // 2. 执行维护工作
+ Toil maintain = new Toil();
+ maintain.tickAction = () =>
+ {
+ Pawn actor = maintain.actor;
+ Thing target = actor.CurJob.targetA.Thing;
+
+ // 检查目标是否仍然有效
+ if (target == null || target.Destroyed)
+ {
+ actor.jobs.EndCurrentJob(JobCondition.Incompletable);
+ return;
+ }
+
+
+ // 如果有技能要求,可以在这里检查
+ // actor.skills.Learn(SkillDefOf.Construction, 0.05f);
+ };
+ // 添加特效和音效(参考Toils_Ingest.AddIngestionEffects)
+ maintain.WithEffect(() => MaintainEffect, TargetIndex.A);
+ maintain.PlaySustainerOrSound(() => MaintainSound);
+ maintain.FailOnDespawnedNullOrForbidden(TargetIndex.A);
+ maintain.FailOnCannotTouch(TargetIndex.A, PathEndMode.Touch);
+ maintain.WithProgressBar(TargetIndex.A, () => (float)maintain.actor.jobs.curDriver.ticksLeftThisToil / WorkDuration);
+ maintain.defaultCompleteMode = ToilCompleteMode.Delay;
+ maintain.defaultDuration = WorkDuration;
+ yield return maintain;
+
+ // 3. 完成工作,增加维护度
+ yield return new Toil
+ {
+ initAction = () =>
+ {
+ Thing target = TargetA.Thing;
+ if (target != null && !target.Destroyed)
+ {
+ Comp_SwarmMaintenance comp = target.TryGetComp();
+ if (comp != null)
+ {
+ comp.AddMaintenance(MaintenancePerWork);
+ }
+ }
+ }
+ };
+ }
+ }
+}
diff --git a/Source/ArachnaeSwarm/Needs/Need_ChitinArmor.cs b/Source/ArachnaeSwarm/Needs/Need_ChitinArmor.cs
new file mode 100644
index 0000000..71bd29e
--- /dev/null
+++ b/Source/ArachnaeSwarm/Needs/Need_ChitinArmor.cs
@@ -0,0 +1,438 @@
+// File: Need_ChitinArmor.cs
+using RimWorld;
+using System.Collections.Generic;
+using System.Xml.Serialization;
+using UnityEngine;
+using Verse;
+
+namespace ArachnaeSwarm
+{
+ public class NeedDefExtension_ChitinLevels : DefModExtension
+ {
+ // 关联的Hediff
+ public HediffDef hediff = null;
+
+ // 严重性映射范围(默认是1:1映射,即Need值=严重性值)
+ [XmlElement("severityRange")]
+ public FloatRange severityRange = new FloatRange(0f, 1f);
+
+ // 是否死亡时移除Hediff
+ public bool removeOnDeath = true;
+
+ // 基础增长速率系数
+ public float baseGrowthRate = 0.1f;
+
+ // 甲壳数量平方的系数
+ public float squareCoefficient = 1f / 100f; // 默认 1/100
+ }
+ public class Need_ChitinArmor : Need
+ {
+ // 甲壳增长基础值(每tick)
+ private const float BaseChitinGainPerTick = 0.0001f;
+
+ // 关联的Hediff
+ private Hediff chitinHediff;
+
+ // 上次更新Hediff的时间
+ private int lastHediffUpdateTick = -99999;
+ private const int HediffUpdateInterval = 60; // 每60tick更新一次Hediff
+
+ // 甲壳部位名称
+ private const string CHITIN_SHELL_PART_NAME = "ARA_Chitin_Shell";
+
+ // 缓存的身体部位数量(用于减少计算)
+ private int cachedShellPartCount = -1;
+ private int lastPartCountCheckTick = -99999;
+ private const int PartCountCheckInterval = 120; // 每120tick检查一次
+
+ // 获取ModExtension配置
+ public NeedDefExtension_ChitinLevels Extension => def.GetModExtension();
+
+ // 计算甲壳最大值:基础值1 + 每个ARA_Chitin_Shell部位增加1
+ public override float MaxLevel
+ {
+ get
+ {
+ int shellCount = GetChitinShellPartCount();
+ return 1f + shellCount;
+ }
+ }
+
+ // 获取当前等级百分比(用于UI显示)
+ public new float CurLevelPercentage => CurLevel / MaxLevel;
+
+ // 获取甲壳增长速率
+ public float GrowthRatePerTick
+ {
+ get
+ {
+ int shellCount = GetChitinShellPartCount();
+
+ // 计算增长速率:((甲壳数量的平方) * 系数) + 基础值
+ float squareCoefficient = Extension?.squareCoefficient ?? (1f / 100f);
+ float baseRate = Extension?.baseGrowthRate ?? 0.1f;
+
+ return ((shellCount * shellCount) * squareCoefficient) + baseRate;
+ }
+ }
+
+ // 获取每秒增长速率(用于UI显示)
+ public float GrowthRatePerSecond
+ {
+ get
+ {
+ // RimWorld中,1秒 = 60ticks
+ // 实际每秒增长量 = 每tick增长速率 * 60
+ // 注意:BaseChitinGainPerTick是基础增长值,GrowthRatePerTick是增长系数
+ // 实际增长公式:每tick增长量 = BaseChitinGainPerTick * GrowthRatePerTick
+ // 所以每秒增长量 = BaseChitinGainPerTick * GrowthRatePerTick * 60
+ return BaseChitinGainPerTick * GrowthRatePerTick * 60f;
+ }
+ }
+
+ // 获取每2.5秒增长速率(用于UI显示,每150tick)
+ public float GrowthRatePer2_5Seconds => GrowthRatePerSecond * 2.5f;
+
+ public Need_ChitinArmor(Pawn newPawn) : base(newPawn)
+ {
+ // 初始化时设置为空
+ curLevelInt = 0f;
+
+ // 设置默认阈值点
+ SetDefaultThresholds();
+
+ // 初始化缓存
+ UpdateCachedShellPartCount();
+ }
+
+ public override void ExposeData()
+ {
+ base.ExposeData();
+ Scribe_References.Look(ref chitinHediff, "chitinHediff");
+ Scribe_Values.Look(ref cachedShellPartCount, "cachedShellPartCount", -1);
+ Scribe_Values.Look(ref lastPartCountCheckTick, "lastPartCountCheckTick", -99999);
+ }
+
+ private void SetDefaultThresholds()
+ {
+ // 默认阈值:25%、50%、75%(用于UI显示)
+ threshPercents = new List { 0.25f, 0.5f, 0.75f };
+ }
+
+ public override void NeedInterval()
+ {
+ // 检查是否需要冻结
+ if (IsFrozen)
+ {
+ return;
+ }
+
+ // 定期更新身体部位数量缓存
+ int currentTick = Find.TickManager.TicksGame;
+ if (currentTick - lastPartCountCheckTick >= PartCountCheckInterval)
+ {
+ UpdateCachedShellPartCount();
+ lastPartCountCheckTick = currentTick;
+ }
+
+ // 计算增长量:基础增长速率 * 每150tick的间隔 * 甲壳增长速率系数
+ float growthAmount = BaseChitinGainPerTick * 150f * GrowthRatePerTick;
+
+ // 应用增长
+ CurLevel += growthAmount;
+
+ // 确保不超过最大值,不低于0
+ float maxLevel = MaxLevel; // 计算一次并缓存
+ if (CurLevel > maxLevel)
+ CurLevel = maxLevel;
+ else if (CurLevel < 0f)
+ CurLevel = 0f;
+
+ // 定期更新Hediff(每60tick一次)
+ if (currentTick - lastHediffUpdateTick >= HediffUpdateInterval)
+ {
+ UpdateChitinHediff();
+ lastHediffUpdateTick = currentTick;
+ }
+ }
+
+ // 获取ARA_Chitin_Shell身体部位的数量
+ private int GetChitinShellPartCount()
+ {
+ // 使用缓存值(如果有效)
+ if (cachedShellPartCount >= 0 && pawn != null && !pawn.Dead)
+ {
+ return cachedShellPartCount;
+ }
+
+ // 重新计算
+ UpdateCachedShellPartCount();
+ return cachedShellPartCount;
+ }
+
+ // 更新缓存的身体部位数量
+ private void UpdateCachedShellPartCount()
+ {
+ if (pawn == null || pawn.Dead || pawn.RaceProps == null || pawn.RaceProps.body == null)
+ {
+ cachedShellPartCount = 0;
+ return;
+ }
+
+ // 计算身体部位数量
+ int count = 0;
+
+ // 方法1:从pawn的身体部位记录中查找
+ foreach (BodyPartRecord part in pawn.RaceProps.body.AllParts)
+ {
+ if (part.def.defName == CHITIN_SHELL_PART_NAME)
+ {
+ count++;
+ }
+ }
+
+ // 方法2:如果找不到,检查Hediff中是否有甲壳(作为备用方法)
+ if (count == 0 && pawn.health != null && pawn.health.hediffSet != null)
+ {
+ foreach (Hediff hediff in pawn.health.hediffSet.hediffs)
+ {
+ if (hediff.Part != null && hediff.Part.def != null &&
+ hediff.Part.def.defName == CHITIN_SHELL_PART_NAME)
+ {
+ count++;
+ }
+ }
+ }
+
+ cachedShellPartCount = count;
+ }
+
+ // 重新计算身体部位数量(当身体发生变化时调用)
+ public void RecalculateShellPartCount()
+ {
+ UpdateCachedShellPartCount();
+ }
+
+ // 更新甲壳Hediff
+ private void UpdateChitinHediff()
+ {
+ if (Extension == null || Extension.hediff == null || pawn.Dead)
+ return;
+
+ // 直接使用CurLevel作为严重性,但可以根据配置的范围进行调整
+ float severity = CurLevel;
+
+ // 如果需要,可以应用范围限制
+ FloatRange severityRange = Extension.severityRange;
+ if (severity > severityRange.max)
+ severity = severityRange.max;
+ else if (severity < severityRange.min)
+ severity = severityRange.min;
+
+ // 如果严重性为0或接近0,移除Hediff
+ if (severity <= 0.001f)
+ {
+ RemoveChitinHediff();
+ return;
+ }
+
+ // 确保Hediff存在
+ EnsureChitinHediff();
+
+ // 设置严重性
+ if (chitinHediff != null)
+ {
+ chitinHediff.Severity = severity;
+ }
+ }
+
+ // 确保Hediff存在
+ private void EnsureChitinHediff()
+ {
+ if (chitinHediff == null || chitinHediff.pawn != pawn)
+ {
+ // 移除旧的Hediff
+ if (chitinHediff != null && chitinHediff.pawn == pawn)
+ {
+ pawn.health.RemoveHediff(chitinHediff);
+ }
+
+ // 创建新的Hediff
+ chitinHediff = HediffMaker.MakeHediff(Extension.hediff, pawn);
+ pawn.health.AddHediff(chitinHediff);
+ }
+ }
+
+ // 移除Hediff
+ private void RemoveChitinHediff()
+ {
+ if (chitinHediff != null && chitinHediff.pawn == pawn)
+ {
+ pawn.health.RemoveHediff(chitinHediff);
+ chitinHediff = null;
+ }
+ }
+
+ // 减少甲壳存量(例如受伤时)
+ public void ReduceChitin(float amount)
+ {
+ CurLevel -= amount;
+ if (CurLevel < 0f)
+ CurLevel = 0f;
+
+ // 立即更新Hediff
+ UpdateChitinHediff();
+ }
+
+ // 强制清空甲壳
+ public void EmptyChitin()
+ {
+ CurLevel = 0f;
+ RemoveChitinHediff();
+ }
+
+ // 重置到初始状态
+ public override void SetInitialLevel()
+ {
+ // 初始为空
+ CurLevel = 0f;
+ RemoveChitinHediff();
+ UpdateCachedShellPartCount();
+ }
+
+ // 获取当前严重性
+ public float GetCurrentSeverity()
+ {
+ if (chitinHediff == null)
+ return 0f;
+
+ return chitinHediff.Severity;
+ }
+
+ // 获取提示字符串
+ public override string GetTipString()
+ {
+ string text = (LabelCap + ": " + CurLevelPercentage.ToStringPercent()).Colorize(ColoredText.TipSectionTitleColor);
+ text += "\n" + def.description;
+
+ int shellCount = GetChitinShellPartCount();
+ text += $"\n{"ARA_Chitin.ShellParts".Translate()}: {shellCount}";
+
+ // 显示每秒增长速率(更直观)
+ text += $"\n{"ARA_Chitin.GrowthRate".Translate()}: {GrowthRatePerSecond:0.#####}/ {"LetterSecond".Translate()}";
+
+ return text;
+ }
+
+ // 在UI上绘制
+ public override void DrawOnGUI(Rect rect, int maxThresholdMarkers = int.MaxValue, float customMargin = -1f, bool drawArrows = true, bool doTooltip = true, Rect? rectForTooltip = null, bool drawLabel = true)
+ {
+ // 确保阈值已设置
+ if (threshPercents == null)
+ {
+ SetDefaultThresholds();
+ }
+
+ base.DrawOnGUI(rect, maxThresholdMarkers, customMargin, false, doTooltip, rectForTooltip, drawLabel);
+ }
+
+ // 是否冻结
+ protected override bool IsFrozen
+ {
+ get
+ {
+ // 如果基础条件冻结,则甲壳生长也冻结
+ if (base.IsFrozen)
+ return true;
+
+ // 如果生物死亡,则冻结
+ if (pawn.Dead)
+ return true;
+
+ return false;
+ }
+ }
+
+ // GUI变化箭头(总是显示增长)
+ public override int GUIChangeArrow => 1;
+
+ // 调试调整百分比
+ protected override void OffsetDebugPercent(float offsetPercent)
+ {
+ base.OffsetDebugPercent(offsetPercent);
+ UpdateChitinHediff();
+ }
+
+ // 当生物死亡时清理Hediff
+ public void OnPawnDeath()
+ {
+ if (Extension?.removeOnDeath ?? true)
+ {
+ RemoveChitinHediff();
+ }
+ }
+
+ // 当生物重生时恢复Hediff
+ public void OnPawnResurrected()
+ {
+ if (CurLevel > 0f && Extension?.hediff != null)
+ {
+ UpdateChitinHediff();
+ }
+ UpdateCachedShellPartCount();
+ }
+
+ // 当身体部位发生变化时调用
+ public void NotifyBodyPartChanged()
+ {
+ UpdateCachedShellPartCount();
+
+ // 如果最大容量减少,确保当前值不超过新最大值
+ if (CurLevel > MaxLevel)
+ {
+ CurLevel = MaxLevel;
+ UpdateChitinHediff();
+ }
+ }
+
+ public Comp_ChitinStripping GetStripComp()
+ {
+ if (pawn == null)
+ return null;
+
+ return pawn.TryGetComp();
+ }
+ // 获取是否可以剥离
+ public bool CanStripChitin()
+ {
+ var stripComp = GetStripComp();
+ if (stripComp == null)
+ return false;
+
+ return stripComp.CanStripNow(pawn);
+ }
+ // 获取剥离信息字符串
+ public string GetStripInfoString()
+ {
+ var stripComp = GetStripComp();
+ if (stripComp == null)
+ return "ARA_ChitinStripping_Disabled".Translate();
+
+ if (!stripComp.CanStripNow(pawn))
+ {
+ int cooldownTicks = stripComp.CooldownTicksRemaining;
+ if (cooldownTicks > 0)
+ {
+ float cooldownSeconds = cooldownTicks / 60f;
+ return "ARA_ChitinStripping_Cooldown".Translate(cooldownSeconds.ToString("F1"));
+ }
+ else
+ {
+ return "ARA_ChitinStripping_Threshold".Translate((stripComp.StripThreshold * 100f).ToString("F0"));
+ }
+ }
+
+ return "ARA_ChitinStripping_Ready".Translate();
+ }
+ }
+}
diff --git a/Source/ArachnaeSwarm/Needs/Need_HoneyProduction.cs b/Source/ArachnaeSwarm/Needs/Need_HoneyProduction.cs
index 006c1c8..a927dad 100644
--- a/Source/ArachnaeSwarm/Needs/Need_HoneyProduction.cs
+++ b/Source/ArachnaeSwarm/Needs/Need_HoneyProduction.cs
@@ -1,13 +1,52 @@
+// File: Need_HoneyProduction.cs
using RimWorld;
using UnityEngine;
using Verse;
namespace ArachnaeSwarm
{
+ public class HoneyProductionExtension : DefModExtension
+ {
+ // 蜜罐生产的基础转化率(相对于食物流失的百分比)
+ public float baseConversionRate = 0.5f;
+
+ // 最大蜜罐容量(可选,覆盖默认值)
+ public float maxHoneyCapacity = 1f;
+
+ // 是否启用蜜罐生产
+ public bool enableHoneyProduction = true;
+
+ // 生产速率乘数(影响生产速度)
+ public float productionSpeedFactor = 1f;
+
+ // 蜜罐类别对应的生产效率(可选,覆盖默认值)
+ public float fullProductionEfficiency = 1.5f;
+ public float highProductionEfficiency = 1.2f;
+ public float mediumProductionEfficiency = 1f;
+ public float lowProductionEfficiency = 0.5f;
+ public float emptyProductionEfficiency = 0f;
+
+ // 生产间隔(ticks,可选覆盖默认值)
+ public int productionInterval = 150;
+
+ public static HoneyProductionExtension Get(Pawn pawn)
+ {
+ if (pawn?.def?.GetModExtension() is HoneyProductionExtension ext)
+ return ext;
+ return null;
+ }
+
+ // 获取转化率
+ public float GetConversionRate()
+ {
+ return baseConversionRate * productionSpeedFactor;
+ }
+ }
+
public class Need_HoneyProduction : Need
{
// 基础流失速率(与食物需要对应)
- private const float BaseHoneyGainPerTick = 2.6666667E-05f * 0.5f; // 食物流失速率的50%
+ private const float BaseHoneyGainPerTick = 2.6666667E-05f * 0.5f; // 食物流失速率的50%作为默认值
// 用于存储对食物需要的引用
private Need_Food cachedFoodNeed;
@@ -18,8 +57,22 @@ namespace ArachnaeSwarm
// 上次满的时间点
private int lastFullTick = -99999;
- // 蜜罐的最大容量(可能需要调整)
- public override float MaxLevel => 1f;
+ // 缓存的ModExtension
+ private HoneyProductionExtension cachedExtension;
+
+ // 蜜罐的最大容量 - 优先使用ModExtension的值
+ public override float MaxLevel
+ {
+ get
+ {
+ var ext = GetExtension();
+ if (ext != null && ext.maxHoneyCapacity > 0)
+ {
+ return ext.maxHoneyCapacity;
+ }
+ return FoodNeed?.MaxLevel ?? 1f;
+ }
+ }
// 当前类别
public HoneyProductionCategory CurCategory => curCategoryInt;
@@ -35,6 +88,29 @@ namespace ArachnaeSwarm
{
get
{
+ var ext = GetExtension();
+
+ // 如果有扩展定义,使用扩展的值
+ if (ext != null)
+ {
+ switch (curCategoryInt)
+ {
+ case HoneyProductionCategory.Full:
+ return ext.fullProductionEfficiency;
+ case HoneyProductionCategory.High:
+ return ext.highProductionEfficiency;
+ case HoneyProductionCategory.Medium:
+ return ext.mediumProductionEfficiency;
+ case HoneyProductionCategory.Low:
+ return ext.lowProductionEfficiency;
+ case HoneyProductionCategory.Empty:
+ return ext.emptyProductionEfficiency;
+ default:
+ return 0f;
+ }
+ }
+
+ // 默认值
switch (curCategoryInt)
{
case HoneyProductionCategory.Full:
@@ -53,12 +129,32 @@ namespace ArachnaeSwarm
}
}
+ // 获取扩展
+ private HoneyProductionExtension GetExtension()
+ {
+ if (cachedExtension == null && pawn != null)
+ {
+ cachedExtension = pawn.def?.GetModExtension();
+ }
+ return cachedExtension;
+ }
+
+ // 是否启用蜜罐生产
+ private bool IsEnabled
+ {
+ get
+ {
+ var ext = GetExtension();
+ return ext == null || ext.enableHoneyProduction; // 默认启用
+ }
+ }
+
// 获取食物需要的引用
private Need_Food FoodNeed
{
get
{
- if (cachedFoodNeed == null || cachedFoodNeed.pawn != pawn)
+ if (cachedFoodNeed == null)
{
cachedFoodNeed = pawn.needs?.TryGetNeed();
}
@@ -86,7 +182,9 @@ namespace ArachnaeSwarm
public override void NeedInterval()
{
- base.NeedInterval();
+ // 如果不启用,直接返回
+ if (!IsEnabled)
+ return;
// 检查是否需要冻结(与食物需要类似的条件)
if (IsFrozen)
@@ -97,11 +195,21 @@ namespace ArachnaeSwarm
// 获取食物需要的流失速率
float foodFallRate = GetFoodFallRate();
- // 蜜罐的增长速率是食物流失速率的50%
- float honeyGainRate = foodFallRate * 0.5f;
+ // 获取转化率(从ModExtension或使用默认值)
+ float conversionRate = GetExtension()?.GetConversionRate() ?? 0.5f;
- // 应用150 ticks的间隔
- CurLevel += honeyGainRate * 150f;
+ // 蜜罐的增长速率 = 食物流失速率 × 转化率
+ float honeyGainRate = foodFallRate * conversionRate;
+
+ // 获取生产间隔
+ int interval = GetExtension()?.productionInterval ?? 150;
+
+ // 应用间隔
+ CurLevel += honeyGainRate * interval;
+
+ // 确保不超过最大容量
+ if (CurLevel > MaxLevel)
+ CurLevel = MaxLevel;
// 更新类别
UpdateCategory();
@@ -172,15 +280,39 @@ namespace ArachnaeSwarm
{
string text = (LabelCap + ": " + CurLevelPercentage.ToStringPercent()).Colorize(ColoredText.TipSectionTitleColor);
text += "\n" + def.description;
- text += $"\n\n{"AS.HoneyProduction.CurrentLevel".Translate()}: {CurLevel:0.##} / {MaxLevel:0.##}";
- text += $"\n{"AS.HoneyProduction.Category".Translate()}: {GetCategoryLabel().CapitalizeFirst()}";
- text += $"\n{"AS.HoneyProduction.Efficiency".Translate()}: {ProductionEfficiency.ToStringPercent()}";
- text += $"\n{"AS.HoneyProduction.FoodDrainRate".Translate()}: {GetFoodFallRate():0.#####}/tick";
- text += $"\n{"AS.HoneyProduction.HoneyGainRate".Translate()}: {(GetFoodFallRate() * 0.5f):0.#####}/tick";
+ text += $"\n{"ARA_HoneyProduction.Efficiency".Translate()} {ProductionEfficiency.ToStringPercent()}";
+
+ // 获取每tick的速率
+ float foodFallPerTick = GetFoodFallRate();
+ float conversionRate = GetExtension()?.GetConversionRate() ?? 0.5f;
+ float honeyGainPerTick = foodFallPerTick * conversionRate;
+
+ // 转换为每秒:1秒 = 60tick
+ float foodFallPerSecond = foodFallPerTick * 60f;
+ float honeyGainPerSecond = honeyGainPerTick * 60f;
+
+ text += $"\n{"ARA_HoneyProduction.FoodDrainRate".Translate()}: {foodFallPerSecond:0.#####}/ {"LetterSecond".Translate()}";
+ text += $"\n{"ARA_HoneyProduction.HoneyGainRate".Translate()}: {honeyGainPerSecond:0.#####}/ {"LetterSecond".Translate()}";
+
+ // 显示转化率信息
+ if (GetExtension() != null)
+ {
+ text += $"\n{"ARA_HoneyProduction.ConversionRate".Translate()}: {conversionRate:P1}";
+ if (GetExtension().productionSpeedFactor != 1f)
+ {
+ text += $"\n{"ARA_HoneyProduction.SpeedFactor".Translate()}: {GetExtension().productionSpeedFactor:F2}";
+ }
+ }
if (IsFull)
{
- text += $"\n\n{"AS.HoneyProduction.FullWarning".Translate()}";
+ text += $"\n\n{"ARA_HoneyProduction.FullWarning".Translate()}";
+ }
+
+ // 显示是否启用
+ if (!IsEnabled)
+ {
+ text += $"\n\n{"ARA_HoneyProduction.Disabled".Translate()} ";
}
return text;
@@ -192,15 +324,15 @@ namespace ArachnaeSwarm
switch (curCategoryInt)
{
case HoneyProductionCategory.Full:
- return "AS.HoneyProduction.Full".Translate();
+ return "ARA_HoneyProduction.Full".Translate();
case HoneyProductionCategory.High:
- return "AS.HoneyProduction.High".Translate();
+ return "ARA_HoneyProduction.High".Translate();
case HoneyProductionCategory.Medium:
- return "AS.HoneyProduction.Medium".Translate();
+ return "ARA_HoneyProduction.Medium".Translate();
case HoneyProductionCategory.Low:
- return "AS.HoneyProduction.Low".Translate();
+ return "ARA_HoneyProduction.Low".Translate();
case HoneyProductionCategory.Empty:
- return "AS.HoneyProduction.Empty".Translate();
+ return "ARA_HoneyProduction.Empty".Translate();
default:
return "Unknown";
}
@@ -209,6 +341,10 @@ namespace ArachnaeSwarm
// 在UI上绘制
public override void DrawOnGUI(Rect rect, int maxThresholdMarkers = int.MaxValue, float customMargin = -1f, bool drawArrows = true, bool doTooltip = true, Rect? rectForTooltip = null, bool drawLabel = true)
{
+ // 如果不启用,不显示
+ if (!IsEnabled)
+ return;
+
if (threshPercents == null)
{
threshPercents = new System.Collections.Generic.List
@@ -229,10 +365,6 @@ namespace ArachnaeSwarm
if (base.IsFrozen)
return true;
- // 如果没有食物需要,或者食物需要被冻结,则蜜罐生产也冻结
- if (FoodNeed == null || FoodNeed.IsFrozen)
- return true;
-
// 如果生物死亡,则冻结
if (pawn.Dead)
return true;
@@ -242,7 +374,7 @@ namespace ArachnaeSwarm
}
// GUI变化箭头(总是显示增长)
- public override int GUIChangeArrow => 1;
+ public override int GUIChangeArrow => IsEnabled ? 1 : 0;
// 调试调整百分比
protected override void OffsetDebugPercent(float offsetPercent)
diff --git a/Source/ArachnaeSwarm/Placeworker/CompProperties_CustomRadius.cs b/Source/ArachnaeSwarm/Placeworker/CompProperties_CustomRadius.cs
index 0144cc3..900502e 100644
--- a/Source/ArachnaeSwarm/Placeworker/CompProperties_CustomRadius.cs
+++ b/Source/ArachnaeSwarm/Placeworker/CompProperties_CustomRadius.cs
@@ -80,7 +80,7 @@ namespace ArachnaeSwarm
// 尝试加载图标,如果失败则使用默认图标
try
{
- toggleCommand.icon = ContentFinder.Get("Wula/UI/Commands/WULA_ShowRadius", false);
+ toggleCommand.icon = ContentFinder.Get("ArachnaeSwarm/UI/Commands/ARA_ShowRadius", false);
if (toggleCommand.icon == null)
{
// 使用一个简单的占位符图标
diff --git a/非公开资源/Content/Textures/UI/Commands/ARA_CallLarva.sai2 b/非公开资源/Content/Textures/UI/Commands/ARA_CallLarva.sai2
new file mode 100644
index 0000000..e7dc91f
Binary files /dev/null and b/非公开资源/Content/Textures/UI/Commands/ARA_CallLarva.sai2 differ
diff --git a/非公开资源/Content/Textures/UI/Commands/ARA_StripChitin.sai2 b/非公开资源/Content/Textures/UI/Commands/ARA_StripChitin.sai2
new file mode 100644
index 0000000..9ff5052
Binary files /dev/null and b/非公开资源/Content/Textures/UI/Commands/ARA_StripChitin.sai2 differ