diff --git a/1.6/1.6/Assemblies/ArachnaeSwarm.dll b/1.6/1.6/Assemblies/ArachnaeSwarm.dll index fa13e81..af51cdd 100644 Binary files a/1.6/1.6/Assemblies/ArachnaeSwarm.dll and b/1.6/1.6/Assemblies/ArachnaeSwarm.dll differ diff --git a/1.6/1.6/Defs/AbilityDefs/ARA_Abilities.xml b/1.6/1.6/Defs/AbilityDefs/ARA_Abilities.xml index bcb672c..78681df 100644 --- a/1.6/1.6/Defs/AbilityDefs/ARA_Abilities.xml +++ b/1.6/1.6/Defs/AbilityDefs/ARA_Abilities.xml @@ -39,7 +39,8 @@ 601 false true - false + true + false false Verb_CastAbility @@ -68,7 +69,8 @@ 601 false true - false + true + false false Verb_CastAbility diff --git a/1.6/1.6/Defs/AbilityDefs/Abilities_EggSpew.xml b/1.6/1.6/Defs/AbilityDefs/Abilities_EggSpew.xml index 2eab46a..3510b85 100644 --- a/1.6/1.6/Defs/AbilityDefs/Abilities_EggSpew.xml +++ b/1.6/1.6/Defs/AbilityDefs/Abilities_EggSpew.xml @@ -214,6 +214,27 @@ + + ARA_EggSpew_Techprint + + 从卵巢中排出一颗特殊的虫卵,它无法孵化任何督虫,而是可以孵化一些特定的科技蓝图,阿拉克涅女皇种可以通过与其交互将其激活。\n\n该虫卵需要使用大量精华素维持工作,并且能研究什么以其落地时的研究完成度为准,那些尚未解锁的科技将无法孵化其蓝图。 + ArachnaeSwarm/UI/Abilities/ARA_EggSpew_Huge + +
  • + ARA_Proj_EggSac_Techprint +
  • +
  • + ARA_InteractiveEggSac_Techprint + 温度要求 + true +
  • +
  • + ARA_Technology_5ESS + 需要科技 节点ESS-5"精华萃取" 以解锁技能 +
  • +
    +
    + ARA_Cocoon_Cloth diff --git a/1.6/1.6/Defs/FactionDefs/ARA_Factions_Player.xml b/1.6/1.6/Defs/FactionDefs/ARA_Factions_Player.xml index 1f5cb5b..1d71bc0 100644 --- a/1.6/1.6/Defs/FactionDefs/ARA_Factions_Player.xml +++ b/1.6/1.6/Defs/FactionDefs/ARA_Factions_Player.xml @@ -9,6 +9,7 @@ colonist colonists Animal + ARA_New_Hive
  • diff --git a/1.6/1.6/Defs/JobDefs/ARA_Jobs.xml b/1.6/1.6/Defs/JobDefs/ARA_Jobs.xml index 76740e7..b0d2855 100644 --- a/1.6/1.6/Defs/JobDefs/ARA_Jobs.xml +++ b/1.6/1.6/Defs/JobDefs/ARA_Jobs.xml @@ -19,4 +19,12 @@ entering TargetA. true + + + + ARA_CarryPrisonerToRefuelingVat + ArachnaeSwarm.JobDriver_CarryPrisonerToRefuelingVat + 将 TargetA 带到 TargetB. + false + \ No newline at end of file diff --git a/1.6/1.6/Defs/JobDefs/ARA_Jobs_Interactive.xml b/1.6/1.6/Defs/JobDefs/ARA_Jobs_Interactive.xml index bf7dc77..eeaf5b9 100644 --- a/1.6/1.6/Defs/JobDefs/ARA_Jobs_Interactive.xml +++ b/1.6/1.6/Defs/JobDefs/ARA_Jobs_Interactive.xml @@ -22,4 +22,11 @@ true + + + ARA_StartResearchProduction + ArachnaeSwarm.JobDriver_StartResearchProduction + 正在启动孵化 TargetA. + true + \ 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 aadf60d..e29b107 100644 --- a/1.6/1.6/Defs/PawnKindDef/ARA_PawnKinds.xml +++ b/1.6/1.6/Defs/PawnKindDef/ARA_PawnKinds.xml @@ -62,6 +62,7 @@
  • ARA_BindDrone
  • ARA_AcidSprayBurst_Queen
  • +
  • ARA_EggSpew_Techprint
  • ARA_TumorSpew
  • diff --git a/1.6/1.6/Defs/RecipeDefs/ARA_Recipes.xml b/1.6/1.6/Defs/RecipeDefs/ARA_Recipes.xml index 9cfe4a0..775799a 100644 --- a/1.6/1.6/Defs/RecipeDefs/ARA_Recipes.xml +++ b/1.6/1.6/Defs/RecipeDefs/ARA_Recipes.xml @@ -68,7 +68,7 @@
  • -
  • ARA_Carapace
  • +
  • ARA_Gene_Essence
  • 30 @@ -122,7 +122,7 @@ 20 30 - 30 + 30 @@ -146,7 +146,7 @@
  • -
  • ARA_Carapace
  • +
  • ARA_Gene_Essence
  • 20 @@ -204,7 +204,7 @@ 15 - 20 + 20 @@ -227,7 +227,7 @@
  • -
  • ARA_Carapace
  • +
  • ARA_Gene_Essence
  • 5 @@ -270,7 +270,7 @@ 1 - 5 + 5 @@ -278,14 +278,14 @@ 以甲壳素为阿拉克涅虫族的甲壳进行加厚,获得额外的防御力。 - ARA_Carapace_Shell_Hediff + ARA_Gene_Essence_Shell_Hediff 正在实施定向变异
  • -
  • ARA_Carapace
  • +
  • ARA_Gene_Essence
  • 25 @@ -293,17 +293,17 @@
    -
  • ARA_Carapace
  • +
  • ARA_Gene_Essence
  • ARA_Chitin_Shell
  • - ARA_Carapace_Shell_Hediff + ARA_Gene_Essence_Shell_Hediff ARA_Technology_1EVO
    - ARA_Carapace_Shell_Hediff + ARA_Gene_Essence_Shell_Hediff 这只阿拉克涅虫族的甲壳以甲壳素进行了增厚,获得了额外防御力。 Hediff_Implant @@ -325,7 +325,7 @@ 1 - ARA_Carapace_Shell + ARA_Gene_Essence_Shell 阿拉克涅虫族的反应甲壳可以使其免受一次外来伤害,但是随后这块甲片就会弹开,不再给予防御力。该手术不需要制作部件,可以直接在阿拉克涅督虫身上实施。 @@ -335,7 +335,7 @@ ARA_Technology_1EVO - 25 + 25 @@ -360,7 +360,7 @@
  • -
  • ARA_Carapace
  • +
  • ARA_Gene_Essence
  • 20 @@ -415,7 +415,7 @@ ARA_Technology_6EVO - 20 + 20 6 @@ -439,7 +439,7 @@
  • -
  • ARA_Carapace
  • +
  • ARA_Gene_Essence
  • 20 @@ -526,7 +526,7 @@ ARA_Technology_3EVO - 20 + 20 6 diff --git a/1.6/1.6/Defs/ResearchProjectDefs/ARA_ResearchProjects.xml b/1.6/1.6/Defs/ResearchProjectDefs/ARA_ResearchProjects.xml index 43c0799..61aa518 100644 --- a/1.6/1.6/Defs/ResearchProjectDefs/ARA_ResearchProjects.xml +++ b/1.6/1.6/Defs/ResearchProjectDefs/ARA_ResearchProjects.xml @@ -5,33 +5,48 @@ ARA_ResearchTab + + Animal + ARA_ResearchTab + 1 + 0 + 1 + +
  • ARA_New_Hive
  • +
    +
    ARA_Base_Technology - 解锁虫群的基础科技,允许孵化基本的辅虫和武装器官 + 解锁虫群的基础科技,允许孵化基本的辅虫和武装器官。\n\n阿拉克涅虫群所有需要蓝图的科技,其蓝图只能通过女皇种的基因试验卵获取。 100 0.00 3.20 - ARA_ResearchBench - + ARA_Technology_1WMT - 允许女皇种和工艺种进化到下一个阶段。 + 允许女皇种和工艺种进化到下一个阶段。\n\n阿拉克涅虫群所有需要蓝图的科技,其蓝图只能通过女皇种的基因试验卵获取。 3000 4.50 3.20 ARA_ResearchBench + 1 + 0 + 1 + +
  • ARA_New_Hive
  • +
  • ARA_Base_Technology
  • - + ARA_Technology_2WMT - 允许女皇种和工艺种进化到下一个阶段。 + 允许女皇种和工艺种进化到下一个阶段。\n\n阿拉克涅虫群所有需要蓝图的科技,其蓝图只能通过女皇种的基因试验卵获取。 7000 9.00 3.20 @@ -128,18 +143,18 @@ 允许工艺种孵化新的灵能闪电系武器。 2500 - 8.50 - 2.10 + 7.50 + 2.70 ARA_ResearchBench
  • ARA_Technology_6KYC
  • - + ARA_Technology_1KYC - 允许女皇种孵化新的虫族——战士种。 + 允许女皇种孵化新的虫族——战士种。\n\n阿拉克涅虫群所有需要蓝图的科技,其蓝图只能通过女皇种的基因试验卵获取。 150 1.00 5.40 @@ -148,10 +163,10 @@
  • ARA_Base_Technology
  • - + ARA_Technology_4KYC - 允许女皇种孵化新的虫族——原虫种,一种可以寄生在别的种族身上以控制它们的特殊督虫。 + 允许女皇种孵化新的虫族——原虫种,一种可以寄生在别的种族身上以控制它们的特殊督虫。\n\n阿拉克涅虫群所有需要蓝图的科技,其蓝图只能通过女皇种的基因试验卵获取。 200 1.00 4.90 @@ -160,10 +175,10 @@
  • ARA_Base_Technology
  • - + ARA_Technology_2KYC - 允许女皇种孵化新的虫族——空天种,敏捷而致命的精锐虫族,拥有以飞行姿态穿梭于战场的能力。 + 允许女皇种孵化新的虫族——空天种,敏捷而致命的精锐虫族,拥有以飞行姿态穿梭于战场的能力。\n\n阿拉克涅虫群所有需要蓝图的科技,其蓝图只能通过女皇种的基因试验卵获取。 1600 5.50 4.30 @@ -172,10 +187,10 @@
  • ARA_Technology_1WMT
  • - + ARA_Technology_5KYC - 允许女皇种孵化新的虫族——浓雾种,一种拥有厚重甲壳的大型督虫,可以释放烟雾、阻燃剂和召唤虫族增援的信息素以协助虫群进行集群冲击。 + 允许女皇种孵化新的虫族——浓雾种,一种拥有厚重甲壳的大型督虫,可以释放烟雾、阻燃剂和召唤虫族增援的信息素以协助虫群进行集群冲击。\n\n阿拉克涅虫群所有需要蓝图的科技,其蓝图只能通过女皇种的基因试验卵获取。 800 5.50 4.80 @@ -184,22 +199,22 @@
  • ARA_Technology_1WMT
  • - + ARA_Technology_6KYC - 允许女皇种孵化新的虫族——织域种,一种寿命长且拥有强大灵能的特殊虫族,不仅能协助虫群的科研工作,也是一个强大的施法者。 + 允许女皇种孵化新的虫族——织域种,一种寿命长且拥有强大灵能的特殊虫族,不仅能协助虫群的科研工作,也是一个强大的施法者。\n\n阿拉克涅虫群所有需要蓝图的科技,其蓝图只能通过女皇种的基因试验卵获取。 800 - 7.50 + 6.50 2.70 ARA_ResearchBench
  • ARA_Technology_1WMT
  • - + ARA_Technology_7KYC - 允许女皇种孵化新的虫族——禁卫种,一种寿命较其他虫族更长的精锐虫族,拥有优秀的远程作战能力和社交能力,同时也可以作为指挥官指挥虫群。 + 允许女皇种孵化新的虫族——禁卫种,一种寿命较其他虫族更长的精锐虫族,拥有优秀的远程作战能力和社交能力,同时也可以作为指挥官指挥虫群。\n\n阿拉克涅虫群所有需要蓝图的科技,其蓝图只能通过女皇种的基因试验卵获取。 2800 10.00 3.80 @@ -343,7 +358,7 @@ 允许工艺种孵化新的武器。 600 - 6.50 + 5.50 2.70 ARA_ResearchBench @@ -366,6 +381,18 @@ + + ARA_Technology_5ESS + + 允许阿拉克涅虫群建造特殊的活体建筑,将俘虏投入以产出精华素。 + 250 + 1.00 + 4.40 + ARA_ResearchBench + +
  • ARA_Base_Technology
  • +
    +
    ARA_Technology_1NPT @@ -453,7 +480,7 @@
  • ARA_Technology_2NPT
  • - + ARA_Technology_4NPT 允许虫族建造孵化池。一种专用于批量生产虫族的孵化场地。孵化池的孵化效率比孵化茧更高。 @@ -534,10 +561,10 @@ - + ARA_Technology_1VTE - 允许盾头种进行定向进化,抛弃其产出甲壳素和建造建筑的能力以换取战斗能力、移动能力和冲撞攻击的技能。 + 允许盾头种进行定向进化,抛弃其产出甲壳素和建造建筑的能力以换取战斗能力、移动能力和冲撞攻击的技能。\n\n阿拉克涅虫群所有需要蓝图的科技,其蓝图只能通过女皇种的基因试验卵获取。 1200 6.50 4.80 @@ -549,10 +576,10 @@
  • ARA_Technology_5KYC
  • - + ARA_Technology_1MED - 允许蜜罐种进行定向进化,以强化其自身的医疗能力并解锁孵化医药茧的技能。 + 允许蜜罐种进行定向进化,以强化其自身的医疗能力并解锁孵化医药茧的技能。\n\n阿拉克涅虫群所有需要蓝图的科技,其蓝图只能通过女皇种的基因试验卵获取。 1200 5.50 2.10 @@ -561,10 +588,10 @@
  • ARA_Technology_1WMT
  • - + ARA_Technology_4CLO - 允许战士种进行定向进化,牺牲其使用远程武器的能力以换取强大的近战和永久隐身的能力。 + 允许战士种进行定向进化,牺牲其使用远程武器的能力以换取强大的近战和永久隐身的能力。\n\n阿拉克涅虫群所有需要蓝图的科技,其蓝图只能通过女皇种的基因试验卵获取。 1800 5.50 5.30 @@ -576,10 +603,10 @@
  • ARA_Technology_1WMT
  • - + ARA_Technology_1BAC - 允许迷雾种进行定向进化,牺牲其护甲和喷射信息素的能力以换成生产虫群所需高级资源"活化钜菌"的能力。 + 允许迷雾种进行定向进化,牺牲其护甲和喷射信息素的能力以换成生产虫群所需高级资源"活化钜菌"的能力。\n\n阿拉克涅虫群所有需要蓝图的科技,其蓝图只能通过女皇种的基因试验卵获取。 1000 7.50 3.80 @@ -589,10 +616,10 @@
  • ARA_Technology_5KYC
  • - + ARA_Technology_6LOD - 允许空天种进行定向进化,以牺牲高速和高空机动的能力换取向敌人投射大量天巢种的能力。 + 允许空天种进行定向进化,以牺牲高速和高空机动的能力换取向敌人投射大量天巢种的能力。\n\n阿拉克涅虫群所有需要蓝图的科技,其蓝图只能通过女皇种的基因试验卵获取。 3500 10.00 5.30 @@ -610,11 +637,11 @@ 允许实行新的阿拉克涅进化手术,使得阿拉克涅虫族获得更大的营养储存能力和更厚的甲壳。 250 - 1.00 + 2.00 4.40 ARA_ResearchBench -
  • ARA_Base_Technology
  • +
  • ARA_Technology_5ESS
  • @@ -625,6 +652,9 @@ 6.50 4.30 ARA_ResearchBench + +
  • ARA_Technology_5ESS
  • +
  • ARA_Technology_2KYC
  • @@ -637,6 +667,9 @@ 6.50 1.50 ARA_ResearchBench + +
  • ARA_Technology_5ESS
  • +
  • ARA_Technology_8VXI
  • @@ -649,6 +682,9 @@ 7.50 4.30 ARA_ResearchBench + +
  • ARA_Technology_5ESS
  • +
  • ARA_Technology_1VTE
  • @@ -661,6 +697,9 @@ 6.50 5.30 ARA_ResearchBench + +
  • ARA_Technology_5ESS
  • +
  • ARA_Technology_4CLO
  • @@ -673,6 +712,9 @@ 7.50 4.80 ARA_ResearchBench + +
  • ARA_Technology_5ESS
  • +
  • ARA_Technology_1VTE
  • @@ -685,6 +727,9 @@ 10.00 4.30 ARA_ResearchBench + +
  • ARA_Technology_5ESS
  • +
  • ARA_Technology_5EVO
  • @@ -700,6 +745,9 @@ 11.00 4.30 ARA_ResearchBench + +
  • ARA_Technology_5ESS
  • +
  • ARA_Technology_2WMT
  • diff --git a/1.6/1.6/Defs/Thing_Misc/ARA_Things_Items.xml b/1.6/1.6/Defs/Thing_Misc/ARA_Things_Items.xml index 1417b3c..8633aa0 100644 --- a/1.6/1.6/Defs/Thing_Misc/ARA_Things_Items.xml +++ b/1.6/1.6/Defs/Thing_Misc/ARA_Things_Items.xml @@ -165,4 +165,34 @@ 80 50 + + + ARA_Gene_Essence + + 由一只小型辅虫——阿拉克涅精华种——保存在体内的异族遗传物质,非常脆弱,通常由精华提取腔从俘虏身上获取,是阿拉克涅虫群重要的资源之一。 + + ArachnaeSwarm/Item/ARA_Gene_Essence + Graphic_Single + 0.9 + + false + Standard_Drop + Standard_Drop + true + false + 30 + + 120 + 10 + 0.01 + + +
  • ResourcesRaw
  • +
    + false + false + Medium + 80 + 50 +
    \ No newline at end of file diff --git a/1.6/1.6/Defs/Thing_building/ARA_InteractiveEggSac.xml b/1.6/1.6/Defs/Thing_building/ARA_InteractiveEggSac.xml index f1b7aff..3eca348 100644 --- a/1.6/1.6/Defs/Thing_building/ARA_InteractiveEggSac.xml +++ b/1.6/1.6/Defs/Thing_building/ARA_InteractiveEggSac.xml @@ -1,4 +1,4 @@ - + Building @@ -492,8 +492,58 @@ + + + ARA_Proj_EggSac_Techprint + + ARA_InteractiveEggSac_Techprint + + + + ARA_InteractiveEggSac_Techprint + + 一个脆弱、易燃、黏滑的囊状物,它无法孵化任何督虫,而是可以孵化一些特定的科技蓝图,阿拉克涅女皇种可以通过与其交互将其激活。\n\n该虫卵需要使用大量精华素维持工作,并且能研究的项目以其落地时的研究完成度为准,那些尚未解锁的科技将无法孵化其蓝图。 + + 2000 + + + + +
  • + 500 + 精华素 + + +
  • ARA_Gene_Essence
  • + + + 0 + true + 0.02 + 1 + + +
  • + +
  • ARA_ArachnaeQueen
  • + + true + 0.2 + 1.0 + 0.06 + 30 + +
  • + -30 + 55 + 0.00005 + 0.005 + 0.001 +
  • +
    +
    + - Building @@ -728,7 +778,7 @@ 5000 - +
  • @@ -983,7 +1033,7 @@ 10000 - +
  • @@ -1082,6 +1132,7 @@ 一个脆弱、易燃、黏滑的囊状物,是阿拉克涅蜜罐种所诞之卵,内含孵化一组医疗用品或化学品的营养物质,可以通过阿拉克涅蜜罐种的交互完成激活进程——参阅茧的超链接,了解其能生产的所有装备的特点。\n\n孵化茧对温度极度敏感(该类型的茧适温为0~12°C),需要小心保护! + ARA_Medicine ARA_PheromoneSolvent 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 ee95e1e..4f8e3bf 100644 --- a/1.6/1.6/Defs/Thing_building/ARA_NutrientNetworkBuilding.xml +++ b/1.6/1.6/Defs/Thing_building/ARA_NutrientNetworkBuilding.xml @@ -104,7 +104,7 @@ ARA_GrowthVat - 阿拉克涅虫群用来存放和消化猎物的茧,那些被扔进茧中的倒霉鬼将逐渐被酸蚀溶解,从而为阿拉克涅营养供给塔网络提供额外的生物质传输效率和孵化速度。 + 阿拉克涅虫群用来存放和消化猎物的茧,那些被扔进茧中的倒霉鬼将逐渐被酸蚀溶解,从而为阿拉克涅营养供给塔网络提供额外的生物质传输效率和孵化速度,一个网络内最大可启用15个消化茧。 ArachnaeSwarm.Building_NutrientVat true Normal @@ -182,7 +182,7 @@
  • ARA_BioforgeIncubator
  • ARA_BioforgeIncubator_Thing
  • - 10 + 15 20 0.05 diff --git a/1.6/1.6/Defs/Thing_building/ARA_RefuelingVat.xml b/1.6/1.6/Defs/Thing_building/ARA_RefuelingVat.xml new file mode 100644 index 0000000..d81b3da --- /dev/null +++ b/1.6/1.6/Defs/Thing_building/ARA_RefuelingVat.xml @@ -0,0 +1,119 @@ + + + + ARA_RefuelingVat + + 一个特殊的活体建筑,可以置入阿拉克涅虫群的俘虏,使用缓慢而痛苦的方式提取其遗传物质,将其转换为精华素以供虫群使用。\n\n——怎么采集的你别问。 + ArachnaeSwarm.Building_RefuelingVat + true + Normal + + ARA_Gene_Essence + + ArachnaeSwarm/Building/ARA_RefuelingVat_Icon + + ArachnaeSwarm/Building/ARA_RefuelingVat + Graphic_Multi + CutoutComplex + (2,2) + + (1.25, 0.2, 0.5) + (0,0,-0.45) + + + false + North + (2,2) + + 500 + 8000 + 30 + 0.5 + + + 20 + 3 + + Building + PassThroughOnly + 42 + true + MapMeshAndRealTime + 0.5 + false + ARA_Buildings + 2200 + true + (0,0,-1) + +
  • ITab_BiosculpterNutritionStorage
  • +
  • ITab_Genes
  • +
    + +
  • GrowthVats
  • +
    + + false + 120 + Laboratory + +
  • ARA_InsectCreep
  • +
    +
    + 4 + ARA_Creep + + +
  • + 精华素 + 精华素 + + 20 + 0 + true + 0 + true + false + + + +
  • ARA_Gene_Essence
  • + + + + false + true + true + + + +
  • + + CompRefuelable + + 0.99 +
  • + +
  • + 5 + 1 +
  • + +
  • + ARA_InsectCreep + 3 +
  • +
    + +
  • + + ArachnaeSwarm/Building/ARA_RefuelingVatTop + + Graphic_Multi +
  • +
    + +
  • ARA_Technology_5ESS
  • +
    +
    +
    diff --git a/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/ArachnaeSwarm_Keys.xml b/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/ArachnaeSwarm_Keys.xml index af7de11..98ea168 100644 --- a/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/ArachnaeSwarm_Keys.xml +++ b/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/ArachnaeSwarm_Keys.xml @@ -51,13 +51,6 @@ 施法者已死亡 没有可破坏的身体部位 - 为囚犯/奴隶分配运送者 - 选择一个虫群成员将囚犯或奴隶带到阿拉克涅消化茧中 - 没有可用的虫群成员来携带囚犯 - 已指派{0}将{1}运送到阿拉克涅消化茧 - 没有可用的虫群成员运送者 - 没有可用的囚犯或奴隶 - 禁止物品 {0} 不能用作 {1} 的燃料 @@ -68,4 +61,32 @@ {0}不足 可以施放 + 已将{0}放入提取腔。 + + + + 生产资源 + 生产效率 + 资源已满 + 当前储存的资源 + 下次造成猎物损伤的进度 + 建筑闲置 - 放入非虫族殖民者、俘虏和囚犯以生产燃料 + 生产已停止 + 选择一名殖民者、囚犯或奴隶,将其安置在提取腔中,他们会缓慢地受到伤害以产出资源。 + 为囚犯/奴隶分配运送者 + 选择一个虫群成员将囚犯或奴隶带到阿拉克涅建筑中 + 没有可用的虫群成员来携带囚犯 + 已指派{0}将{1}运送到活体建筑 + 没有可用的虫群成员运送者 + 没有可用的囚犯或奴隶 + + + 孵化{0}的蓝图 + 正在生成蓝图:{0} + 待生产的蓝图 + 该研究已完成 + 产出 {0} 蓝图 + 需要{0}才能开始研究生产 + 可用的研究项目 + 生产完成:{0}个{1}({2}研究所需) \ No newline at end of file diff --git a/Content/Textures/ArachnaeSwarm/Building/ARA_RefuelingVatTop_east.png b/Content/Textures/ArachnaeSwarm/Building/ARA_RefuelingVatTop_east.png new file mode 100644 index 0000000..07a389d Binary files /dev/null and b/Content/Textures/ArachnaeSwarm/Building/ARA_RefuelingVatTop_east.png differ diff --git a/Content/Textures/ArachnaeSwarm/Building/ARA_RefuelingVatTop_north.png b/Content/Textures/ArachnaeSwarm/Building/ARA_RefuelingVatTop_north.png new file mode 100644 index 0000000..07a389d Binary files /dev/null and b/Content/Textures/ArachnaeSwarm/Building/ARA_RefuelingVatTop_north.png differ diff --git a/Content/Textures/ArachnaeSwarm/Building/ARA_RefuelingVatTop_south.png b/Content/Textures/ArachnaeSwarm/Building/ARA_RefuelingVatTop_south.png new file mode 100644 index 0000000..07a389d Binary files /dev/null and b/Content/Textures/ArachnaeSwarm/Building/ARA_RefuelingVatTop_south.png differ diff --git a/Content/Textures/ArachnaeSwarm/Building/ARA_RefuelingVatTop_west.png b/Content/Textures/ArachnaeSwarm/Building/ARA_RefuelingVatTop_west.png new file mode 100644 index 0000000..07a389d Binary files /dev/null and b/Content/Textures/ArachnaeSwarm/Building/ARA_RefuelingVatTop_west.png differ diff --git a/Content/Textures/ArachnaeSwarm/Building/ARA_RefuelingVat_Icon.png b/Content/Textures/ArachnaeSwarm/Building/ARA_RefuelingVat_Icon.png new file mode 100644 index 0000000..f5b9b7c Binary files /dev/null and b/Content/Textures/ArachnaeSwarm/Building/ARA_RefuelingVat_Icon.png differ diff --git a/Content/Textures/ArachnaeSwarm/Building/ARA_RefuelingVat_east.png b/Content/Textures/ArachnaeSwarm/Building/ARA_RefuelingVat_east.png new file mode 100644 index 0000000..7122c29 Binary files /dev/null and b/Content/Textures/ArachnaeSwarm/Building/ARA_RefuelingVat_east.png differ diff --git a/Content/Textures/ArachnaeSwarm/Building/ARA_RefuelingVat_north.png b/Content/Textures/ArachnaeSwarm/Building/ARA_RefuelingVat_north.png new file mode 100644 index 0000000..7122c29 Binary files /dev/null and b/Content/Textures/ArachnaeSwarm/Building/ARA_RefuelingVat_north.png differ diff --git a/Content/Textures/ArachnaeSwarm/Building/ARA_RefuelingVat_south.png b/Content/Textures/ArachnaeSwarm/Building/ARA_RefuelingVat_south.png new file mode 100644 index 0000000..7122c29 Binary files /dev/null and b/Content/Textures/ArachnaeSwarm/Building/ARA_RefuelingVat_south.png differ diff --git a/Content/Textures/ArachnaeSwarm/Building/ARA_RefuelingVat_west.png b/Content/Textures/ArachnaeSwarm/Building/ARA_RefuelingVat_west.png new file mode 100644 index 0000000..7122c29 Binary files /dev/null and b/Content/Textures/ArachnaeSwarm/Building/ARA_RefuelingVat_west.png differ diff --git a/Content/Textures/ArachnaeSwarm/Building/ARA_Wormhole_A.png b/Content/Textures/ArachnaeSwarm/Building/ARA_Wormhole_A.png index ad8e7b0..a3080c2 100644 Binary files a/Content/Textures/ArachnaeSwarm/Building/ARA_Wormhole_A.png and b/Content/Textures/ArachnaeSwarm/Building/ARA_Wormhole_A.png differ diff --git a/Content/Textures/ArachnaeSwarm/Item/ARA_Gene_Essence.png b/Content/Textures/ArachnaeSwarm/Item/ARA_Gene_Essence.png new file mode 100644 index 0000000..5d7a13f Binary files /dev/null and b/Content/Textures/ArachnaeSwarm/Item/ARA_Gene_Essence.png differ diff --git a/Source/ArachnaeSwarm/.vs/ArachnaeSwarm/v17/.suo b/Source/ArachnaeSwarm/.vs/ArachnaeSwarm/v17/.suo index 8139c83..82b384b 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 a9a7db9..cf83e67 100644 --- a/Source/ArachnaeSwarm/.vs/ArachnaeSwarm/v17/DocumentLayout.json +++ b/Source/ArachnaeSwarm/.vs/ArachnaeSwarm/v17/DocumentLayout.json @@ -1,18 +1,14 @@ { "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\\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\\building_comps\\ara_compinteractiveproducer\\compinteractiveproducer.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", + "RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:building_comps\\ara_compinteractiveproducer\\compinteractiveproducer.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" }, { - "AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\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|D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\building_comps\\ara_buildingterrainspawn\\compdelayedterrainspawn.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", - "RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:building_comps\\ara_buildingterrainspawn\\compdelayedterrainspawn.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" + "AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|e:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\building_comps\\ara_compinteractiveproducer\\compresearchproducer.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", + "RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:building_comps\\ara_compinteractiveproducer\\compresearchproducer.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" } ], "DocumentGroupContainers": [ @@ -22,48 +18,37 @@ "DocumentGroups": [ { "DockedWidth": 200, - "SelectedChildIndex": 2, + "SelectedChildIndex": 1, "Children": [ { "$type": "Bookmark", "Name": "ST:0:0:{1c4feeaa-4718-4aa9-859d-94ce25d182ba}" }, - { - "$type": "Document", - "DocumentIndex": 1, - "Title": "CompProperties_ExtraIncubationInfo.cs", - "DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Thing_Comps\\ARA_CompExtraIncubationInfo\\CompProperties_ExtraIncubationInfo.cs", - "RelativeDocumentMoniker": "Thing_Comps\\ARA_CompExtraIncubationInfo\\CompProperties_ExtraIncubationInfo.cs", - "ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Thing_Comps\\ARA_CompExtraIncubationInfo\\CompProperties_ExtraIncubationInfo.cs", - "RelativeToolTip": "Thing_Comps\\ARA_CompExtraIncubationInfo\\CompProperties_ExtraIncubationInfo.cs", - "ViewState": "AgIAAAAAAAAAAAAAAAAAABMAAAAAAAAAAAAAAA==", - "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", - "WhenOpened": "2025-10-13T03:27:00.382Z" - }, { "$type": "Document", "DocumentIndex": 0, - "Title": "CompExtraIncubationInfo.cs", - "DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Thing_Comps\\ARA_CompExtraIncubationInfo\\CompExtraIncubationInfo.cs", - "RelativeDocumentMoniker": "Thing_Comps\\ARA_CompExtraIncubationInfo\\CompExtraIncubationInfo.cs", - "ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Thing_Comps\\ARA_CompExtraIncubationInfo\\CompExtraIncubationInfo.cs", - "RelativeToolTip": "Thing_Comps\\ARA_CompExtraIncubationInfo\\CompExtraIncubationInfo.cs", - "ViewState": "AgIAAAAAAAAAAAAAAAAAAA4AAAAAAAAAAAAAAA==", + "Title": "CompInteractiveProducer.cs", + "DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_CompInteractiveProducer\\CompInteractiveProducer.cs", + "RelativeDocumentMoniker": "Building_Comps\\ARA_CompInteractiveProducer\\CompInteractiveProducer.cs", + "ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_CompInteractiveProducer\\CompInteractiveProducer.cs", + "RelativeToolTip": "Building_Comps\\ARA_CompInteractiveProducer\\CompInteractiveProducer.cs", + "ViewState": "AgIAAEMBAAAAAAAAAAAswIQBAABUAAAAAAAAAA==", "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", - "WhenOpened": "2025-10-13T03:26:37.426Z", + "WhenOpened": "2025-10-14T17:41:44.207Z", "EditorCaption": "" }, { "$type": "Document", - "DocumentIndex": 2, - "Title": "CompDelayedTerrainSpawn.cs", - "DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_BuildingTerrainSpawn\\CompDelayedTerrainSpawn.cs", - "RelativeDocumentMoniker": "Building_Comps\\ARA_BuildingTerrainSpawn\\CompDelayedTerrainSpawn.cs", - "ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_BuildingTerrainSpawn\\CompDelayedTerrainSpawn.cs", - "RelativeToolTip": "Building_Comps\\ARA_BuildingTerrainSpawn\\CompDelayedTerrainSpawn.cs", - "ViewState": "AgIAAA4AAAAAAAAAAAAAwD0AAAAhAAAAAAAAAA==", + "DocumentIndex": 1, + "Title": "CompResearchProducer.cs", + "DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_CompInteractiveProducer\\CompResearchProducer.cs", + "RelativeDocumentMoniker": "Building_Comps\\ARA_CompInteractiveProducer\\CompResearchProducer.cs", + "ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_CompInteractiveProducer\\CompResearchProducer.cs", + "RelativeToolTip": "Building_Comps\\ARA_CompInteractiveProducer\\CompResearchProducer.cs", + "ViewState": "AgIAACkBAAAAAAAAAAAQwFYBAAApAAAAAAAAAA==", "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", - "WhenOpened": "2025-10-12T15:30:31.391Z" + "WhenOpened": "2025-10-14T17:02:58.556Z", + "EditorCaption": "" } ] } diff --git a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj index 1d82a4d..c3a7155 100644 --- a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj +++ b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj @@ -100,6 +100,11 @@ + + + + + @@ -113,6 +118,7 @@ + diff --git a/Source/ArachnaeSwarm/Building_Comps/ARA_SacrificialFuelTank/Building_SacrificialFuelTank.cs b/Source/ArachnaeSwarm/Building_Comps/ARA_Building_RefuelingVat/Building_RefuelingVat.cs similarity index 64% rename from Source/ArachnaeSwarm/Building_Comps/ARA_SacrificialFuelTank/Building_SacrificialFuelTank.cs rename to Source/ArachnaeSwarm/Building_Comps/ARA_Building_RefuelingVat/Building_RefuelingVat.cs index 76f5f28..427336e 100644 --- a/Source/ArachnaeSwarm/Building_Comps/ARA_SacrificialFuelTank/Building_SacrificialFuelTank.cs +++ b/Source/ArachnaeSwarm/Building_Comps/ARA_Building_RefuelingVat/Building_RefuelingVat.cs @@ -10,9 +10,11 @@ using Verse.AI; namespace ArachnaeSwarm { [StaticConstructorOnStartup] - public class Building_SacrificialFuelTank : Building_Enterable, IThingHolder, IThingHolderWithDrawnPawn + public class Building_RefuelingVat : Building_Enterable, IThingHolder, IThingHolderWithDrawnPawn { - private CompRefuelable refuelableComp; + private CompRefuelable cachedRefuelableComp; + private CompRefuelingVat cachedRefuelingVatComp; + private CompAutoEjector cachedAutoEjectorComp; private Graphic cachedTopGraphic; // IThingHolderWithDrawnPawn implementation @@ -20,10 +22,9 @@ namespace ArachnaeSwarm public float HeldPawnBodyAngle => base.Rotation.AsAngle; public PawnPosture HeldPawnPosture => PawnPosture.LayingOnGroundFaceUp; - // 燃料转换配置 - private const float DAMAGE_TO_FUEL_RATIO = 0.1f; // 每点伤害转换为0.1燃料 - private const float DAMAGE_INTERVAL_TICKS = 250; // 每250ticks造成一次伤害 - private const float DAMAGE_PER_INTERVAL = 5f; // 每次间隔造成5点伤害 + // 从 Comp 中获取属性 + public float FuelProductionPerDay => RefuelingVatComp?.FuelProductionPerDay ?? 10f; + public float FuelProductionEfficiency => RefuelingVatComp?.FuelProductionEfficiency ?? 1f; private Graphic TopGraphic { @@ -31,7 +32,7 @@ namespace ArachnaeSwarm { if (cachedTopGraphic == null) { - var modExtension = def.GetModExtension(); + var modExtension = def.GetModExtension(); if (modExtension != null && !modExtension.topGraphicPath.NullOrEmpty()) { cachedTopGraphic = GraphicDatabase.Get(modExtension.graphicClass, modExtension.topGraphicPath, ShaderDatabase.Transparent, def.graphicData.drawSize, Color.white, Color.white); @@ -41,77 +42,110 @@ namespace ArachnaeSwarm } } + public override Vector3 PawnDrawOffset => Vector3.zero; + public CompRefuelable RefuelableComp { get { - if (refuelableComp == null) + if (cachedRefuelableComp == null) { - refuelableComp = this.TryGetComp(); + cachedRefuelableComp = this.TryGetComp(); } - return refuelableComp; + return cachedRefuelableComp; + } + } + + public CompRefuelingVat RefuelingVatComp + { + get + { + if (cachedRefuelingVatComp == null) + { + cachedRefuelingVatComp = this.TryGetComp(); + } + return cachedRefuelingVatComp; } } - public override Vector3 PawnDrawOffset => Vector3.zero; + public CompAutoEjector AutoEjectorComp + { + get + { + if (cachedAutoEjectorComp == null) + { + cachedAutoEjectorComp = this.TryGetComp(); + } + return cachedAutoEjectorComp; + } + } + + public bool HasFuelCapacity => RefuelableComp != null && RefuelableComp.Fuel < RefuelableComp.Props.fuelCapacity; + public bool IsFuelFull => RefuelableComp != null && RefuelableComp.Fuel >= RefuelableComp.Props.fuelCapacity; + + // 燃料生产速率(每Tick) + public float FuelProductionPerTick + { + get + { + if (selectedPawn != null && base.Working) + { + return (FuelProductionPerDay * FuelProductionEfficiency) / 60000f; + } + return 0f; + } + } + + // 酸蚀伤害间隔(每6000 tick约1/10天) + private const int AcidDamageInterval = 6000; + private Dictionary pawnTickCounters = new Dictionary(); public override void SpawnSetup(Map map, bool respawningAfterLoad) { base.SpawnSetup(map, respawningAfterLoad); - refuelableComp = this.TryGetComp(); + cachedRefuelableComp = this.TryGetComp(); + cachedRefuelingVatComp = this.TryGetComp(); + cachedAutoEjectorComp = this.TryGetComp(); } public override void DeSpawn(DestroyMode mode = DestroyMode.Vanish) { - if (mode != DestroyMode.WillReplace && selectedPawn != null && innerContainer.Contains(selectedPawn)) + if (mode != DestroyMode.WillReplace) { - Notify_PawnRemoved(); + if (selectedPawn != null && innerContainer.Contains(selectedPawn)) + { + Notify_PawnRemoved(); + } } base.DeSpawn(mode); } - // 造成伤害并转换为燃料 - private void ApplySacrificialDamage(Pawn pawn) + private void ApplyAcidDamage(Pawn pawn) { try { - if (pawn.Dead || pawn.Downed) return; - - // 选择随机的身体部位 BodyPartRecord targetPart = GetRandomVulnerablePart(pawn); - if (targetPart == null) return; - // 造成伤害 - DamageInfo damage = new DamageInfo( - DamageDefOf.Cut, // 使用切割伤害 - DAMAGE_PER_INTERVAL, - 0.5f, // 中等护甲穿透 - -1f, + DamageDef acidDamageDef = DefDatabase.GetNamed("AcidBurn") ?? DamageDefOf.Burn; + + DamageInfo acidDamage = new DamageInfo( + acidDamageDef, + 0.1f, // 每次0.1点伤害 + 2f, // 护甲穿透 + -1f, // 随机角度 instigator: null, hitPart: targetPart ); - float damageDealt = pawn.TakeDamage(damage).TotalDamageDealt; - - // 将伤害转换为燃料 - if (damageDealt > 0 && RefuelableComp != null) - { - float fuelToAdd = damageDealt * DAMAGE_TO_FUEL_RATIO; - RefuelableComp.Refuel(fuelToAdd); - } - - // 如果pawn死亡,停止处理 - if (pawn.Dead) - { - OnPawnDied(); - } + // 应用伤害 + pawn.TakeDamage(acidDamage); } catch (Exception ex) { - Log.Error($"Error applying sacrificial damage to {pawn}: {ex.Message}"); + Log.Error($"Error applying acid damage to {pawn}: {ex.Message}"); } } - + private BodyPartRecord GetRandomVulnerablePart(Pawn pawn) { // 优先选择外部身体部位 @@ -121,11 +155,11 @@ namespace ArachnaeSwarm part.def.hitPoints > 0) .ToList(); - return vulnerableParts.Count > 0 ? vulnerableParts.RandomElement() : pawn.RaceProps.body.corePart; + return vulnerableParts.Count > 0 ? + vulnerableParts.RandomElement() : + pawn.RaceProps.body.corePart; } - - private int damageTickCounter = 0; - + protected override void Tick() { base.Tick(); @@ -136,39 +170,41 @@ namespace ArachnaeSwarm return; } - if (base.Working && selectedPawn != null && !selectedPawn.Dead) + if (base.Working && selectedPawn != null) { - damageTickCounter++; - - // 定期造成伤害 - if (damageTickCounter >= DAMAGE_INTERVAL_TICKS) + // 酸蚀伤害逻辑 + if (pawnTickCounters.TryGetValue(selectedPawn, out int tickCount)) { - ApplySacrificialDamage(selectedPawn); - damageTickCounter = 0; + tickCount++; + pawnTickCounters[selectedPawn] = tickCount; + + if (tickCount >= AcidDamageInterval) + { + ApplyAcidDamage(selectedPawn); + pawnTickCounters[selectedPawn] = 0; + } + } + else + { + pawnTickCounters[selectedPawn] = 0; } - // 检查pawn是否死亡 + // 燃料生产逻辑 + if (HasFuelCapacity) + { + float fuelToAdd = FuelProductionPerTick; + RefuelableComp.Refuel(fuelToAdd); + } + + // 检查俘虏是否死亡 if (selectedPawn.Dead) { - OnPawnDied(); + Finish(); + return; } } } - private void OnPawnDied() - { - if (selectedPawn != null && innerContainer.Contains(selectedPawn)) - { - // 弹出死亡的pawn - Notify_PawnRemoved(); - innerContainer.TryDrop(selectedPawn, InteractionCell, base.Map, ThingPlaceMode.Near, 1, out var _); - OnStop(); - - // 可以选择在这里添加一些效果,比如声音或信息 - Messages.Message("SacrificialFuelTank_PawnDied".Translate(selectedPawn.LabelShort), this, MessageTypeDefOf.NeutralEvent); - } - } - public override AcceptanceReport CanAcceptPawn(Pawn pawn) { if (base.Working) @@ -179,12 +215,7 @@ namespace ArachnaeSwarm { return "WaitingForPawn".Translate(selectedPawn.Named("PAWN")); } - if (pawn.Dead || pawn.Downed) - { - return "PawnMustBeAlive".Translate(); - } - - // 禁止虫群成员 + // 禁止置入虫群成员 if (pawn.health.hediffSet.HasHediff(ARA_HediffDefOf.ARA_HiveMindMaster) || pawn.health.hediffSet.HasHediff(ARA_HediffDefOf.ARA_HiveMindDrone) || pawn.health.hediffSet.HasHediff(ARA_HediffDefOf.ARA_HiveMindWorker)) @@ -192,11 +223,12 @@ namespace ArachnaeSwarm return "PawnIsHiveMember".Translate(pawn.Named("PAWN")); } - // 允许殖民者、囚犯和奴隶,但不能是任务寄宿者 + // 允许殖民者、囚犯和奴隶 bool isColonist = pawn.IsColonist; bool isPrisoner = pawn.IsPrisonerOfColony; bool isSlave = pawn.IsSlaveOfColony; + // 允许殖民者、囚犯或奴隶,但不能是任务寄宿者 return (isColonist || isPrisoner || isSlave) && !pawn.IsQuestLodger(); } @@ -212,7 +244,7 @@ namespace ArachnaeSwarm if (innerContainer.TryAddOrTransfer(pawn)) { startTick = Find.TickManager.TicksGame; - damageTickCounter = 0; // 重置伤害计数器 + pawnTickCounters[pawn] = 0; // 初始化伤害计数器 } if (deselected) { @@ -220,33 +252,50 @@ namespace ArachnaeSwarm } } + private void Finish() + { + if (selectedPawn != null && innerContainer.Contains(selectedPawn)) + { + Notify_PawnRemoved(); + innerContainer.TryDrop(selectedPawn, InteractionCell, base.Map, ThingPlaceMode.Near, 1, out var _); + OnStop(); + } + } + private void OnStop() { + if (selectedPawn != null) + { + pawnTickCounters.Remove(selectedPawn); + } selectedPawn = null; startTick = -1; - damageTickCounter = 0; } private void Notify_PawnRemoved() { - // 可以在这里添加声音效果 + // 可以在这里添加音效 } public override IEnumerable GetGizmos() { + // 原有的基础Gizmos foreach (Gizmo gizmo in base.GetGizmos()) { yield return gizmo; } - if (!base.Working) + if (base.Working) { - // 插入人员的操作 - var insertCommand = new Command_Action + } + else + { + // 选择殖民者的操作 + var command_Action = new Command_Action { - defaultLabel = "InsertSacrificialPawn".Translate() + "...", - defaultDesc = "InsertSacrificialPawnDesc".Translate(), - icon = ContentFinder.Get("UI/Commands/Sacrifice"), + defaultLabel = "InsertPerson".Translate() + "...", + defaultDesc = "InsertPersonRefuelingVatDesc".Translate(), + icon = Building_GrowthVat.InsertPawnIcon.Texture, action = () => { List list = new List(); @@ -266,19 +315,21 @@ namespace ArachnaeSwarm }; if (!base.AnyAcceptablePawns) { - insertCommand.Disable("NoViablePawns".Translate()); + command_Action.Disable("NoViablePawns".Translate()); } - yield return insertCommand; + yield return command_Action; // 指派搬运囚犯/奴隶的操作 var command_CarryPrisoner = new Command_Action { defaultLabel = "AssignCarryPrisoner".Translate() + "...", - defaultDesc = "AssignCarryPrisonerSacrificialDesc".Translate(), + defaultDesc = "AssignCarryPrisonerDesc".Translate(), icon = ContentFinder.Get("UI/Commands/Attack"), action = () => { List list = new List(); + + // 获取所有可接受的囚犯和奴隶 foreach (Pawn p in base.Map.mapPawns.AllPawnsSpawned) { if (CanAcceptPawn(p).Accepted && (p.IsPrisonerOfColony || p.IsSlaveOfColony)) @@ -291,6 +342,7 @@ namespace ArachnaeSwarm )); } } + if (!list.Any()) { list.Add(new FloatMenuOption("NoPrisonersOrSlaves".Translate(), null)); @@ -299,6 +351,7 @@ namespace ArachnaeSwarm } }; + // 检查是否有可用的囚犯/奴隶 bool hasPrisonersOrSlaves = base.Map.mapPawns.AllPawnsSpawned .Any(p => CanAcceptPawn(p).Accepted && (p.IsPrisonerOfColony || p.IsSlaveOfColony)); @@ -320,23 +373,43 @@ namespace ArachnaeSwarm { stringBuilder.AppendLineIfNotEmpty().Append("CasketContains".Translate().ToString() + ": " + selectedPawn.NameShortColored.Resolve()); - // 显示燃料转换效率 - if (RefuelableComp != null) + // 显示燃料生产信息 + if (HasFuelCapacity) { - stringBuilder.AppendLineIfNotEmpty().Append("FuelConversionRate".Translate() + ": " + (DAMAGE_TO_FUEL_RATIO * 100).ToString("F1") + "%"); - stringBuilder.AppendLineIfNotEmpty().Append("CurrentFuel".Translate() + ": " + RefuelableComp.Fuel.ToString("F1") + " / " + RefuelableComp.Props.fuelCapacity.ToString("F1")); + stringBuilder.AppendLineIfNotEmpty().Append("FuelProduction".Translate() + ": " + FuelProductionPerDay.ToString("F1") + " per day"); + stringBuilder.AppendLineIfNotEmpty().Append("ProductionEfficiency".Translate() + ": " + FuelProductionEfficiency.ToStringPercent()); + + // 显示当前燃料状态 + if (RefuelableComp != null) + { + stringBuilder.AppendLineIfNotEmpty().Append("CurrentFuel".Translate() + ": " + RefuelableComp.Fuel.ToString("F1") + " / " + RefuelableComp.Props.fuelCapacity); + } + } + else + { + stringBuilder.AppendLineIfNotEmpty().Append("FuelTankFull".Translate()); } - // 显示pawn的健康状态 - if (selectedPawn.health.summaryHealth.SummaryHealthPercent < 1.0f) + // 显示酸蚀伤害信息 + if (pawnTickCounters.TryGetValue(selectedPawn, out int tickCount)) { - stringBuilder.AppendLineIfNotEmpty().Append("PawnHealth".Translate() + ": " + selectedPawn.health.summaryHealth.SummaryHealthPercent.ToStringPercent()); + float damageProgress = (float)tickCount / AcidDamageInterval; + stringBuilder.AppendLineIfNotEmpty().Append("AcidDamageProgress".Translate() + ": " + damageProgress.ToStringPercent()); } } else if (selectedPawn != null) { stringBuilder.AppendLineIfNotEmpty().Append("WaitingForPawn".Translate(selectedPawn.Named("PAWN")).Resolve()); } + else + { + // 显示空闲状态信息 + stringBuilder.AppendLineIfNotEmpty().Append("RefuelingVatIdle".Translate()); + if (RefuelableComp != null) + { + stringBuilder.AppendLineIfNotEmpty().Append("CurrentFuel".Translate() + ": " + RefuelableComp.Fuel.ToString("F1") + " / " + RefuelableComp.Props.fuelCapacity); + } + } return stringBuilder.ToString(); } @@ -355,7 +428,7 @@ namespace ArachnaeSwarm AcceptanceReport acceptanceReport = CanAcceptPawn(selPawn); if (acceptanceReport.Accepted) { - yield return FloatMenuUtility.DecoratePrioritizedTask(new FloatMenuOption("EnterSacrificialTank".Translate(this), () => SelectPawn(selPawn)), selPawn, this); + yield return FloatMenuUtility.DecoratePrioritizedTask(new FloatMenuOption("EnterBuilding".Translate(this), () => SelectPawn(selPawn)), selPawn, this); } else if (!acceptanceReport.Reason.NullOrEmpty()) { @@ -375,6 +448,7 @@ namespace ArachnaeSwarm protected override void DrawAt(Vector3 drawLoc, bool flip = false) { base.DrawAt(drawLoc, flip); + // 绘制顶部图形 if (TopGraphic != null) { TopGraphic.Draw(DrawPos + Altitudes.AltIncVect * 2f, base.Rotation, this); @@ -387,11 +461,13 @@ namespace ArachnaeSwarm return null; if (!CanAcceptPawn(prisoner).Accepted) return null; - - JobDef carryJobDef = DefDatabase.GetNamed("ARA_CarryPrisonerToSacrificialTank"); + // 创建搬运工作定义 - 使用新的 JobDef + JobDef carryJobDef = DefDatabase.GetNamed("ARA_CarryPrisonerToRefuelingVat"); if (carryJobDef == null) + { + Log.Error("ARA_CarryPrisonerToRefuelingVat JobDef not found!"); return null; - + } Job job = JobMaker.MakeJob(carryJobDef, prisoner, this); job.count = 1; return job; @@ -401,10 +477,12 @@ namespace ArachnaeSwarm { foreach (Pawn pawn in base.Map.mapPawns.AllPawnsSpawned) { + // 检查是否是虫群成员 if (pawn.health.hediffSet.HasHediff(ARA_HediffDefOf.ARA_HiveMindDrone) || pawn.health.hediffSet.HasHediff(ARA_HediffDefOf.ARA_HiveMindWorker) || pawn.health.hediffSet.HasHediff(ARA_HediffDefOf.ARA_HiveMindMaster)) { + // 检查是否能够工作且不是囚犯 if (pawn.workSettings != null && pawn.workSettings.WorkIsActive(WorkTypeDefOf.Hauling) && !pawn.Downed && !pawn.IsPrisoner && pawn.CanReach(this, PathEndMode.InteractionCell, Danger.Some)) { @@ -418,15 +496,17 @@ namespace ArachnaeSwarm { if (prisoner == null) return; - + // 获取可用的搬运者 var availableCarriers = GetAvailableCarriers().ToList(); + if (!availableCarriers.Any()) { Messages.Message("NoAvailableHiveCarriers".Translate(), MessageTypeDefOf.RejectInput); return; } - + // 创建浮动菜单选择搬运者 List options = new List(); + foreach (Pawn carrier in availableCarriers) { options.Add(new FloatMenuOption( @@ -444,7 +524,6 @@ namespace ArachnaeSwarm Color.white )); } - if (options.Any()) { Find.WindowStack.Add(new FloatMenu(options)); @@ -454,5 +533,12 @@ namespace ArachnaeSwarm Messages.Message("NoAvailableCarriers".Translate(), MessageTypeDefOf.RejectInput); } } + + // 保存和加载数据 + public override void ExposeData() + { + base.ExposeData(); + Scribe_Collections.Look(ref pawnTickCounters, "pawnTickCounters", LookMode.Reference, LookMode.Value); + } } } diff --git a/Source/ArachnaeSwarm/Building_Comps/ARA_Building_RefuelingVat/CompProperties_RefuelingVat.cs b/Source/ArachnaeSwarm/Building_Comps/ARA_Building_RefuelingVat/CompProperties_RefuelingVat.cs new file mode 100644 index 0000000..a5e8120 --- /dev/null +++ b/Source/ArachnaeSwarm/Building_Comps/ARA_Building_RefuelingVat/CompProperties_RefuelingVat.cs @@ -0,0 +1,16 @@ +using RimWorld; +using Verse; + +namespace ArachnaeSwarm +{ + public class CompProperties_RefuelingVat : CompProperties + { + public float fuelProductionPerDay = 10f; + public float fuelProductionEfficiency = 1f; + + public CompProperties_RefuelingVat() + { + this.compClass = typeof(CompRefuelingVat); + } + } +} diff --git a/Source/ArachnaeSwarm/Building_Comps/ARA_Building_RefuelingVat/CompRefuelingVat.cs b/Source/ArachnaeSwarm/Building_Comps/ARA_Building_RefuelingVat/CompRefuelingVat.cs new file mode 100644 index 0000000..6397786 --- /dev/null +++ b/Source/ArachnaeSwarm/Building_Comps/ARA_Building_RefuelingVat/CompRefuelingVat.cs @@ -0,0 +1,20 @@ +using RimWorld; +using Verse; + +namespace ArachnaeSwarm +{ + public class CompRefuelingVat : ThingComp + { + public CompProperties_RefuelingVat Props => (CompProperties_RefuelingVat)props; + + public float FuelProductionPerDay => Props.fuelProductionPerDay; + public float FuelProductionEfficiency => Props.fuelProductionEfficiency; + + // 允许运行时修改效率(如果需要) + public void SetEfficiency(float newEfficiency) + { + // 如果需要运行时修改,可以在这里添加逻辑 + // 注意:这会修改实例,不会影响 XML 定义 + } + } +} diff --git a/Source/ArachnaeSwarm/Building_Comps/ARA_CompInteractiveProducer/CompInteractiveProducer.cs b/Source/ArachnaeSwarm/Building_Comps/ARA_CompInteractiveProducer/CompInteractiveProducer.cs index e438d08..2e110b3 100644 --- a/Source/ArachnaeSwarm/Building_Comps/ARA_CompInteractiveProducer/CompInteractiveProducer.cs +++ b/Source/ArachnaeSwarm/Building_Comps/ARA_CompInteractiveProducer/CompInteractiveProducer.cs @@ -319,7 +319,7 @@ namespace ArachnaeSwarm yield return new FloatMenuOption(label, () => { this._selectedProcess = process; - Job job = JobMaker.MakeJob(DefDatabase.GetNamed("ARA_StartInteractiveProduction"), parent); + Job job = JobMaker.MakeJob(DefDatabase.GetNamed("ARA_StartResearchProduction"), parent); selPawn.jobs.TryTakeOrderedJob(job, JobTag.Misc); }); } diff --git a/Source/ArachnaeSwarm/Building_Comps/ARA_CompInteractiveProducer/CompResearchProducer.cs b/Source/ArachnaeSwarm/Building_Comps/ARA_CompInteractiveProducer/CompResearchProducer.cs new file mode 100644 index 0000000..93652b0 --- /dev/null +++ b/Source/ArachnaeSwarm/Building_Comps/ARA_CompInteractiveProducer/CompResearchProducer.cs @@ -0,0 +1,482 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text; +using RimWorld; +using UnityEngine; +using Verse; +using Verse.AI; + +namespace ArachnaeSwarm +{ + public class CompProperties_ResearchProducer : CompProperties + { + public List whitelist; + public bool destroyOnSpawn; + public float damagePerTickWhenUnfueled = 0.2f; + public float minNutritionToStart = 0.1f; + + // 营养转换系数:研究点数 -> 营养 + public float nutritionPerResearchPoint = 0.1f; + + // 生产时间系数:研究点数 -> 生产时间(ticks) + public float productionTicksPerResearchPoint = 10f; + + public CompProperties_ResearchProducer() + { + compClass = typeof(CompResearchProducer); + } + } + + [StaticConstructorOnStartup] + public class CompResearchProducer : ThingComp + { + private ResearchProcessDef _selectedProcess; + private int productionUntilTick = -1; + private List _cachedProcesses; + private int spawnTick = -1; + private const int COOLDOWN_TICKS = 120; + + private CompRefuelableNutrition _fuelComp; + private static readonly Texture2D CancelIcon = ContentFinder.Get("UI/Designators/Cancel"); + + public bool InProduction => _selectedProcess != null; + public bool InCooldown => spawnTick > 0 && Find.TickManager.TicksGame < spawnTick + COOLDOWN_TICKS; + public CompProperties_ResearchProducer Props => (CompProperties_ResearchProducer)props; + + // 生产进度(0-1) + public float ProductionProgress + { + get + { + if (!InProduction || productionUntilTick <= 0) return 0f; + int totalTicks = _selectedProcess.productionTicks; + int elapsedTicks = totalTicks - (productionUntilTick - Find.TickManager.TicksGame); + return Mathf.Clamp01((float)elapsedTicks / totalTicks); + } + } + + // 自动生成的 ResearchProcessDef 列表 + public List Processes + { + get + { + if (_cachedProcesses == null) + { + BuildProcessList(); + } + return _cachedProcesses; + } + } + + private CompRefuelableNutrition FuelComp + { + get + { + if (_fuelComp == null) _fuelComp = parent.GetComp(); + return _fuelComp; + } + } + + public override void PostSpawnSetup(bool respawningAfterLoad) + { + base.PostSpawnSetup(respawningAfterLoad); + _fuelComp = parent.GetComp(); + BuildProcessList(); + + if (!respawningAfterLoad) + { + spawnTick = Find.TickManager.TicksGame; + } + } + + public override void PostExposeData() + { + base.PostExposeData(); + + // 序列化 selectedProcess 的 researchDef + ResearchProjectDef selectedProcessResearchDef = _selectedProcess?.researchDef; + Scribe_Defs.Look(ref selectedProcessResearchDef, "selectedProcessResearchDef"); + + Scribe_Values.Look(ref productionUntilTick, "productionUntilTick", -1); + Scribe_Values.Look(ref spawnTick, "spawnTick", -1); + + // 加载时重建 selectedProcess + if (Scribe.mode == LoadSaveMode.LoadingVars && selectedProcessResearchDef != null) + { + if (_cachedProcesses == null) + { + BuildProcessList(); + } + + _selectedProcess = Processes.FirstOrDefault(p => p.researchDef == selectedProcessResearchDef); + + if (_selectedProcess == null) + { + Log.Warning($"Could not find ResearchProcessDef for {selectedProcessResearchDef.defName} after loading. Resetting production."); + ResetProduction(); + } + else if (productionUntilTick > 0) + { + if (Find.TickManager.TicksGame >= productionUntilTick) + { + Log.Warning($"Production time already passed for {selectedProcessResearchDef.defName}. Finishing immediately."); + FinishProduction(); + } + else if (productionUntilTick - Find.TickManager.TicksGame > _selectedProcess.productionTicks * 10) + { + Log.Warning($"Abnormal production time detected for {selectedProcessResearchDef.defName}. Recalculating."); + productionUntilTick = Find.TickManager.TicksGame + _selectedProcess.productionTicks; + } + } + } + } + + // 扫描 ARA_ResearchTab 下所有需要 techprint 的研究项目 + private void BuildProcessList() + { + _cachedProcesses = new List(); + // 获取 ARA_ResearchTab 的 ResearchTabDef + ResearchTabDef araResearchTab = DefDatabase.GetNamedSilentFail("ARA_ResearchTab"); + if (araResearchTab == null) + { + Log.Warning("ARA_ResearchTab not found. No research processes will be available."); + return; + } + int totalScanned = 0; + int araTabCount = 0; + int techprintCount = 0; + int prerequisitesMetCount = 0; + + // 扫描所有研究项目 + foreach (ResearchProjectDef researchDef in DefDatabase.AllDefs) + { + totalScanned++; + // 检查是否在 ARA_ResearchTab 下 - 使用 ResearchTabDef 比较 + if (researchDef.tab == araResearchTab) + { + araTabCount++; + // 检查是否需要 techprint + if (researchDef.TechprintCount > 0) + { + techprintCount++; + // 检查前置条件是否满足 + if (!ArePrerequisitesMet(researchDef)) + { + continue; // 前置条件不满足,跳过这个研究项目 + } + prerequisitesMetCount++; + // 根据命名规则构建 techprint 的 defName + string techprintDefName = "Techprint_" + researchDef.defName; + ThingDef techprintDef = DefDatabase.GetNamedSilentFail(techprintDefName); + if (techprintDef != null) + { + // 计算营养消耗和生产时间 + float nutritionCost = researchDef.baseCost * Props.nutritionPerResearchPoint; + int productionTicks = Mathf.RoundToInt(researchDef.baseCost * Props.productionTicksPerResearchPoint); + ResearchProcessDef process = new ResearchProcessDef + { + researchDef = researchDef, + techprintDef = techprintDef, + productionTicks = productionTicks, + totalNutritionNeeded = nutritionCost, + techprintCount = researchDef.TechprintCount + }; + _cachedProcesses.Add(process); + } + else + { + Log.Warning($"Techprint ThingDef not found for research project {researchDef.defName}. Expected defName: {techprintDefName}"); + } + } + } + } + // 按研究项目名称排序 + _cachedProcesses.SortBy(p => p.researchDef.label); + // 记录详细的扫描结果 + Log.Message($"Research production scanner: Scanned {totalScanned} research projects, " + + $"{araTabCount} in ARA_ResearchTab, " + + $"{techprintCount} require techprints, " + + $"{prerequisitesMetCount} have prerequisites met, " + + $"{_cachedProcesses.Count} available for production"); + } + // 检查研究项目的前置条件是否满足 + private bool ArePrerequisitesMet(ResearchProjectDef researchDef) + { + // 检查 prerequisites + if (researchDef.prerequisites != null) + { + foreach (var prerequisite in researchDef.prerequisites) + { + if (!prerequisite.IsFinished) + { + return false; // 有一个前置研究未完成 + } + } + } + // 检查 hiddenPrerequisites + if (researchDef.hiddenPrerequisites != null) + { + foreach (var hiddenPrerequisite in researchDef.hiddenPrerequisites) + { + if (!hiddenPrerequisite.IsFinished) + { + return false; // 有一个隐藏前置研究未完成 + } + } + } + return true; // 所有前置条件都满足 + } + + public override void CompTick() + { + base.CompTick(); + + if (InProduction && productionUntilTick > 0) + { + if (productionUntilTick <= 0) + { + Log.Error($"Invalid productionUntilTick: {productionUntilTick}. Resetting production."); + ResetProduction(); + return; + } + + if (FuelComp == null) return; + + bool hasFuel = FuelComp.HasFuel; + + // 营养归零时持续伤害建筑 + if (!hasFuel) + { + parent.TakeDamage(new DamageInfo(DamageDefOf.Rotting, Props.damagePerTickWhenUnfueled)); + return; + } + + // 检查生产是否完成 + if (productionUntilTick > 0 && Find.TickManager.TicksGame >= productionUntilTick) + { + FinishProduction(); + } + } + } + + public override IEnumerable CompFloatMenuOptions(Pawn selPawn) + { + if (InCooldown) + { + int remainingTicks = (spawnTick + COOLDOWN_TICKS) - Find.TickManager.TicksGame; + yield return new FloatMenuOption($"ARA_CocoonCooldown".Translate(remainingTicks.ToStringTicksToPeriod()), null); + yield break; + } + + if (InProduction || !selPawn.CanReach(parent, PathEndMode.InteractionCell, Danger.Deadly)) yield break; + if (Props.whitelist != null && !Props.whitelist.Contains(selPawn.kindDef)) yield break; + if (FuelComp == null) yield break; + if (!FuelComp.HasFuel || FuelComp.NutritionStored < Props.minNutritionToStart) + { + yield return new FloatMenuOption("CannotStartProduction".Translate() + ": " + "NoFuel".Translate(), null); + yield break; + } + + foreach (var process in Processes) + { + float nutritionCost = process.totalNutritionNeeded; + + string label = "StartResearchProduction".Translate(process.researchDef.label) + " " + + "ARA_ProductionCost".Translate(nutritionCost.ToString("F0")) + " ->" + + "ARA_TechprintCount".Translate(process.techprintCount); + + // 检查研究是否已经完成 + if (process.researchDef.IsFinished) + { + yield return new FloatMenuOption(label + " (" + "ResearchAlreadyCompleted".Translate() + ")", null); + } + else + { + yield return new FloatMenuOption(label, () => + { + this._selectedProcess = process; + Job job = JobMaker.MakeJob(DefDatabase.GetNamed("ARA_StartResearchProduction"), parent); + selPawn.jobs.TryTakeOrderedJob(job, JobTag.Misc); + }); + } + } + } + + public void StartProduction() + { + if (InCooldown) + { + Log.Warning("Attempted to start production during cooldown period."); + return; + } + + if (_selectedProcess == null) return; + productionUntilTick = Find.TickManager.TicksGame + _selectedProcess.productionTicks; + + float nutritionPerDay = (_selectedProcess.totalNutritionNeeded / _selectedProcess.productionTicks) * 60000f; + FuelComp.currentConsumptionRate = nutritionPerDay; + } + + private void FinishProduction() + { + if (_selectedProcess == null) + { + Log.Warning("FinishProduction called but _selectedProcess is null. Resetting."); + ResetProduction(); + return; + } + try + { + // 生成科技蓝图 + Thing techprint = ThingMaker.MakeThing(_selectedProcess.techprintDef); + techprint.stackCount = _selectedProcess.techprintCount; + + GenPlace.TryPlaceThing(techprint, parent.Position, parent.Map, ThingPlaceMode.Near); + // 添加消息提示 + string messageText = "ARA_TechprintProductionComplete".Translate( + _selectedProcess.techprintCount, + _selectedProcess.techprintDef.label, + _selectedProcess.researchDef.label + ); + + Messages.Message(messageText, parent, MessageTypeDefOf.PositiveEvent); + if (Props.destroyOnSpawn) + { + parent.Destroy(DestroyMode.Vanish); + } + } + catch (System.Exception ex) + { + Log.Error($"Error in FinishProduction: {ex.Message}"); + } + finally + { + ResetProduction(); + } + } + + private void ResetProduction() + { + if (FuelComp != null) FuelComp.currentConsumptionRate = 0f; + _selectedProcess = null; + productionUntilTick = -1; + } + + // 进度条显示 + private string GetProgressBar(float progress, int barLength = 20) + { + int filledLength = Mathf.RoundToInt(progress * barLength); + int emptyLength = barLength - filledLength; + + StringBuilder bar = new StringBuilder(); + bar.Append("["); + for (int i = 0; i < filledLength; i++) bar.Append("█"); + for (int i = 0; i < emptyLength; i++) bar.Append("-"); + bar.Append("]"); + + return bar.ToString(); + } + + public override string CompInspectStringExtra() + { + if (InCooldown) + { + int remainingTicks = (spawnTick + COOLDOWN_TICKS) - Find.TickManager.TicksGame; + return "ARA_CocoonCooldown".Translate(remainingTicks.ToStringTicksToPeriod()); + } + + if (InProduction) + { + StringBuilder sb = new StringBuilder(); + sb.AppendLine("ProducingTechprint".Translate(this._selectedProcess.researchDef.label)); + + float progress = ProductionProgress; + int remainingTicks = productionUntilTick - Find.TickManager.TicksGame; + sb.AppendLine("Progress".Translate() + ": " + GetProgressBar(progress) + " " + progress.ToStringPercent("F0")); + sb.AppendLine("TimeLeft".Translate() + ": " + remainingTicks.ToStringTicksToPeriod()); + sb.AppendLine("TechprintsToProduce".Translate() + ": " + _selectedProcess.techprintCount); + + return sb.ToString().TrimEnd(); + } + + if (!InProduction) + { + int availableProcesses = Processes.Count(p => !p.researchDef.IsFinished); + + string allowedRaces = ""; + if (Props.whitelist != null && Props.whitelist.Count > 0) + { + var raceNames = Props.whitelist.Select(pkd => pkd.LabelCap).Distinct(); + allowedRaces = string.Join(", ", raceNames); + } + else + { + allowedRaces = "ARA_AnyArachnaeRace".Translate(); + } + + return "ARA_NeedSpecificArachnaeToStartResearchProduction".Translate(allowedRaces) + + $" ({availableProcesses} " + "researchProjectsAvailable".Translate() + ")"; + } + return null; + } + + public override void PostDraw() + { + base.PostDraw(); + + if (InProduction) + { + Vector3 drawPos = parent.DrawPos; + drawPos.y += 0.15f; + + float progress = ProductionProgress; + GenDraw.DrawFillableBar(new GenDraw.FillableBarRequest + { + center = drawPos, + size = new Vector2(1f, 0.15f), + fillPercent = progress, + filledMat = SolidColorMaterials.SimpleSolidColorMaterial(Color.green), + unfilledMat = SolidColorMaterials.SimpleSolidColorMaterial(Color.gray), + margin = 0.1f, + rotation = Rot4.North + }); + } + } + + public override IEnumerable CompGetGizmosExtra() + { + foreach (var g in base.CompGetGizmosExtra()) yield return g; + + if (InCooldown) yield break; + + if (InProduction) + { + yield return new Command_Action + { + defaultLabel = "CommandCancelProduction".Translate(), + icon = CancelIcon, + action = () => ResetProduction() + }; + + if (Prefs.DevMode) + { + yield return new Command_Action + { + defaultLabel = "Debug: Force Finish", + action = () => FinishProduction() + }; + } + } + } + } + + // 研究进程定义类 + public class ResearchProcessDef + { + public ResearchProjectDef researchDef; + public ThingDef techprintDef; + public int productionTicks; + public float totalNutritionNeeded; + public int techprintCount; + } +} diff --git a/Source/ArachnaeSwarm/Building_Comps/ARA_CompInteractiveProducer/JobDriver_StartResearchProduction.cs b/Source/ArachnaeSwarm/Building_Comps/ARA_CompInteractiveProducer/JobDriver_StartResearchProduction.cs new file mode 100644 index 0000000..8f23870 --- /dev/null +++ b/Source/ArachnaeSwarm/Building_Comps/ARA_CompInteractiveProducer/JobDriver_StartResearchProduction.cs @@ -0,0 +1,46 @@ +using System.Collections.Generic; +using RimWorld; +using Verse; +using Verse.AI; + +namespace ArachnaeSwarm +{ + public class JobDriver_StartResearchProduction : JobDriver + { + private const TargetIndex BuildingInd = TargetIndex.A; + + protected Building Building => (Building)job.GetTarget(BuildingInd).Thing; + + public override bool TryMakePreToilReservations(bool errorOnFailed) + { + return pawn.Reserve(Building, job, 1, -1, null, errorOnFailed); + } + + protected override IEnumerable MakeNewToils() + { + // 检查目标建筑是否有效 + this.FailOnDespawnedNullOrForbidden(BuildingInd); + this.FailOnBurningImmobile(BuildingInd); + + // 第一步:走到建筑 + yield return Toils_Goto.GotoThing(BuildingInd, PathEndMode.InteractionCell); + + // 第二步:启动生产 + Toil work = ToilMaker.MakeToil("MakeNewToils"); + work.initAction = delegate + { + var comp = Building.GetComp(); + if (comp != null) + { + comp.StartProduction(); + } + else + { + Log.Error($"CompResearchProducer not found on {Building.Label}"); + } + }; + work.defaultCompleteMode = ToilCompleteMode.Instant; + yield return work; + } + } +} diff --git a/Source/ArachnaeSwarm/Building_Comps/ARA_SacrificialFuelTank/DefModExtension_SacrificialFuelTank.cs b/Source/ArachnaeSwarm/Building_Comps/ARA_SacrificialFuelTank/DefModExtension_SacrificialFuelTank.cs deleted file mode 100644 index d13a2d5..0000000 --- a/Source/ArachnaeSwarm/Building_Comps/ARA_SacrificialFuelTank/DefModExtension_SacrificialFuelTank.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using Verse; - -namespace ArachnaeSwarm -{ - public class DefModExtension_SacrificialFuelTank : DefModExtension - { - public string topGraphicPath; - public Type graphicClass = typeof(Graphic_Multi); - - // 可配置的伤害和燃料转换参数 - public float damageToFuelRatio = 0.1f; - public int damageIntervalTicks = 250; - public float damagePerInterval = 5f; - } -} diff --git a/Source/ArachnaeSwarm/Building_Comps/CompAutoEjector.cs b/Source/ArachnaeSwarm/Building_Comps/CompAutoEjector.cs index 411fa1a..2533445 100644 --- a/Source/ArachnaeSwarm/Building_Comps/CompAutoEjector.cs +++ b/Source/ArachnaeSwarm/Building_Comps/CompAutoEjector.cs @@ -2,15 +2,16 @@ using System; using System.Linq; using RimWorld; using Verse; +using UnityEngine; namespace ArachnaeSwarm { - // Properties for the new component public class CompProperties_AutoEjector : CompProperties { - public int checkInterval = 250; // Check roughly every 4 seconds - public float ejectAtPercent = 1.0f; // Eject when fuel reaches this percentage of max capacity (1.0 = 100%) - public Type targetComp = typeof(CompRefuelable); // The specific CompRefuelable class to target, can be overridden in XML + public int checkInterval = 250; + public float ejectAtPercent = 1.0f; + public Type targetComp = typeof(CompRefuelable); + public bool allowEjectedFuel = true; // 新增:控制弹出的燃料是否允许操作 public CompProperties_AutoEjector() { @@ -18,7 +19,6 @@ namespace ArachnaeSwarm } } - // The component logic public class CompAutoEjector : ThingComp { private CompProperties_AutoEjector Props => (CompProperties_AutoEjector)this.props; @@ -28,7 +28,6 @@ namespace ArachnaeSwarm { base.PostSpawnSetup(respawningAfterLoad); - // Find the specific refuelable component specified in XML this.refuelableComp = this.parent.GetComps() .FirstOrDefault(comp => comp.GetType() == this.Props.targetComp); @@ -42,17 +41,47 @@ namespace ArachnaeSwarm { base.CompTick(); - // Check if we have a valid refuelable comp and it's time to check if (this.refuelableComp != null && this.parent.IsHashIntervalTick(this.Props.checkInterval)) { - // Check if fuel has reached or exceeded the configured percentage of the MAX capacity if (this.refuelableComp.FuelPercentOfMax >= this.Props.ejectAtPercent) { - // Call the public EjectFuel() method. - this.refuelableComp.EjectFuel(); + // 使用自定义的弹出方法 + EjectFuelWithoutForbid(); } } } + + /// + /// 自定义弹出燃料方法,弹出的物品允许操作 + /// + private void EjectFuelWithoutForbid() + { + var props = refuelableComp.Props; + ThingDef thingDef = props.fuelFilter.AllowedThingDefs.First(); + int num = Mathf.FloorToInt(refuelableComp.Fuel); + + while (num > 0) + { + Thing thing = ThingMaker.MakeThing(thingDef); + thing.stackCount = Mathf.Min(num, thingDef.stackLimit); + num -= thing.stackCount; + + // 放置物品 + GenPlace.TryPlaceThing(thing, parent.Position, parent.Map, ThingPlaceMode.Near); + + // 根据配置决定是否允许操作 + if (Props.allowEjectedFuel) + { + thing.SetForbidden(false); // 允许操作 + } + } + + // 重置燃料并发送信号 + typeof(CompRefuelable).GetField("fuel", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance) + ?.SetValue(refuelableComp, 0f); + + parent.BroadcastCompSignal("RanOutOfFuel"); + } } -} \ No newline at end of file +} diff --git a/Source/ArachnaeSwarm/Jobs/JobDriver_CarryPrisonerToRefuelingVat.cs b/Source/ArachnaeSwarm/Jobs/JobDriver_CarryPrisonerToRefuelingVat.cs new file mode 100644 index 0000000..e78ef74 --- /dev/null +++ b/Source/ArachnaeSwarm/Jobs/JobDriver_CarryPrisonerToRefuelingVat.cs @@ -0,0 +1,64 @@ +using RimWorld; +using System.Collections.Generic; +using Verse; +using Verse.AI; + +namespace ArachnaeSwarm +{ + public class JobDriver_CarryPrisonerToRefuelingVat : JobDriver + { + private const TargetIndex PrisonerInd = TargetIndex.A; + private const TargetIndex VatInd = TargetIndex.B; + + protected Pawn Prisoner => (Pawn)job.GetTarget(PrisonerInd).Thing; + protected Building_RefuelingVat Vat => (Building_RefuelingVat)job.GetTarget(VatInd).Thing; + + public override bool TryMakePreToilReservations(bool errorOnFailed) + { + return pawn.Reserve(Prisoner, job, 1, -1, null, errorOnFailed) + && pawn.Reserve(Vat, job, 1, -1, null, errorOnFailed); + } + + protected override IEnumerable MakeNewToils() + { + // 验证目标 + this.FailOnDestroyedOrNull(PrisonerInd); + this.FailOnDestroyedOrNull(VatInd); + this.FailOn(() => !Vat.CanAcceptPawn(Prisoner).Accepted); + + // 1. 前往囚犯位置 + yield return Toils_Goto.GotoThing(PrisonerInd, PathEndMode.OnCell) + .FailOn(() => Prisoner.IsInRefuelingVat()) + .FailOnSomeonePhysicallyInteracting(PrisonerInd); + + // 2. 开始搬运囚犯 + yield return Toils_Haul.StartCarryThing(PrisonerInd); + + // 3. 前往燃料转化罐 + yield return Toils_Goto.GotoThing(VatInd, PathEndMode.InteractionCell); + + // 4. 将囚犯放入燃料转化罐 + yield return new Toil + { + initAction = () => + { + if (Vat.CanAcceptPawn(Prisoner).Accepted) + { + Vat.TryAcceptPawn(Prisoner); + Messages.Message("PrisonerPlacedInRefuelingVat".Translate(Prisoner.LabelShort), MessageTypeDefOf.NeutralEvent); + } + }, + defaultCompleteMode = ToilCompleteMode.Instant + }; + } + } + + // 扩展方法检查pawn是否在燃料转化罐中 + public static class RefuelingVatPawnExtensions + { + public static bool IsInRefuelingVat(this Pawn pawn) + { + return pawn?.Spawned == false && pawn.ParentHolder is Building_RefuelingVat; + } + } +} diff --git a/Source/ArachnaeSwarm/Thing_Comps/ARA_CompExtraIncubationInfo/CompExtraIncubationInfo.cs b/Source/ArachnaeSwarm/Thing_Comps/ARA_CompExtraIncubationInfo/CompExtraIncubationInfo.cs index 4c4a2ea..b85a173 100644 --- a/Source/ArachnaeSwarm/Thing_Comps/ARA_CompExtraIncubationInfo/CompExtraIncubationInfo.cs +++ b/Source/ArachnaeSwarm/Thing_Comps/ARA_CompExtraIncubationInfo/CompExtraIncubationInfo.cs @@ -11,4 +11,4 @@ namespace ArachnaeSwarm // 公开属性,供其他组件读取 public List CocoonDefs => Props.cocoonDefs; } -} +} \ No newline at end of file diff --git a/非公开资源/Content/Textures/Building/ARA_GrowthVat.sai2 b/非公开资源/Content/Textures/Building/ARA_GrowthVat.sai2 index 9aa085a..2f403f0 100644 Binary files a/非公开资源/Content/Textures/Building/ARA_GrowthVat.sai2 and b/非公开资源/Content/Textures/Building/ARA_GrowthVat.sai2 differ diff --git a/非公开资源/Content/Textures/Building/ARA_WormholePortal_A.sai2 b/非公开资源/Content/Textures/Building/ARA_WormholePortal_A.sai2 index 8eda643..cb6adc7 100644 Binary files a/非公开资源/Content/Textures/Building/ARA_WormholePortal_A.sai2 and b/非公开资源/Content/Textures/Building/ARA_WormholePortal_A.sai2 differ diff --git a/非公开资源/Content/Textures/Item/ARA_Gene_Essence.sai2 b/非公开资源/Content/Textures/Item/ARA_Gene_Essence.sai2 new file mode 100644 index 0000000..3779ca3 Binary files /dev/null and b/非公开资源/Content/Textures/Item/ARA_Gene_Essence.sai2 differ