diff --git a/1.6/1.6/Assemblies/ArachnaeSwarm.dll b/1.6/1.6/Assemblies/ArachnaeSwarm.dll index e45e816..fd734bf 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 241fdd1..ca0c8b3 100644 --- a/1.6/1.6/Defs/AbilityDefs/ARA_Abilities.xml +++ b/1.6/1.6/Defs/AbilityDefs/ARA_Abilities.xml @@ -312,7 +312,6 @@
  • ARA_Technology_6VXI - 需要科技 节点VXI-6"酸囊" 以解锁技能
  • @@ -394,7 +393,6 @@
  • ARA_Technology_5PAV - 需要科技 节点PAV-5"毒刺" 以解锁技能
  • @@ -440,7 +438,6 @@
  • ARA_Technology_6VXI - 需要科技 节点VXI-6"酸囊" 以解锁技能
  • @@ -934,7 +931,7 @@ - + ARA_MimicNematode_Needle_Fire @@ -975,7 +972,6 @@ -->
  • ARA_Technology_2MED - 需要科技 节点MED-2"拟线寄生" 以解锁技能
  • @@ -993,6 +989,86 @@ 40 + + ARA_FireSpew + + 爆燃种经过短暂蓄力后,向前大范围地喷出火焰。这种火焰喷射距离不是太长,但是可以形成一道致密的火墙。 + ArachnaeSwarm/UI/Abilities/ARA_FireSpew + 5000 + true + true + true + FireSpew_Warmup + 3 + true + + Verb_CastAbility + 10 + 1 + FireSpew_Resolve + + true + + + +
  • + 饮食 + true + Food + 0.25 + 营养值不足,需要进食 +
  • +
  • + 10 + 35 + Filth_FlammableBile + Fire_Spew + true +
  • +
    +
    + + ARA_Dissolver_Touch + + 在极近的范围内向目标泼洒溶脂强酸,目标的器官会快速分解,变成大量的虫蜜。因为准头很差,无法用于战场上,只能对不反抗的目标使用。 + ArachnaeSwarm/UI/Abilities/ARA_Fighter_Invisibility_jump + 1000 + 3 + true + false + false + true + true + AcidSpray_Warmup + CastAbilityOnThingMelee + + Verb_CastAbilityTouch + false + false + 0.5 + -1 + false + AcidSpray_Resolve + + true + true + + + +
  • + 饮食 + true + Food + 0.1 + 营养值不足,需要进食 +
  • +
  • + CompAbilityEffect_GiveHediff + ARA_Dissolver_Touch_Damage + true +
  • +
    +
    @@ -1066,46 +1142,6 @@ - - - ARA_FireSpew - - 爆燃种经过短暂蓄力后,向前大范围地喷出火焰。这种火焰喷射距离不是太长,但是可以形成一道致密的火墙。 - ArachnaeSwarm/UI/Abilities/ARA_FireSpew - 5000 - true - true - true - FireSpew_Warmup - 3 - true - - Verb_CastAbility - 10 - 1 - FireSpew_Resolve - - true - - - -
  • - 饮食 - true - Food - 0.25 - 营养值不足,需要进食 -
  • -
  • - 10 - 35 - Filth_FlammableBile - Fire_Spew - true -
  • -
    -
    - ARA_Genestealer_ExtractGene diff --git a/1.6/1.6/Defs/EvolutionDefs/ARA_Evolution.xml b/1.6/1.6/Defs/EvolutionDefs/ARA_Evolution.xml index b7b01e3..68eb296 100644 --- a/1.6/1.6/Defs/EvolutionDefs/ARA_Evolution.xml +++ b/1.6/1.6/Defs/EvolutionDefs/ARA_Evolution.xml @@ -55,12 +55,14 @@
  • ARA_Myrmecocystus_Production_Medicine
  • ARA_Myrmecocystus_Production_Fuel
  • +
  • ARA_Myrmecocystus_Production_Dissolver
  • ARA_Myrmecocystus_Production_Medicine
  • ARA_Myrmecocystus_Production_Fuel
  • +
  • ARA_Myrmecocystus_Production_Dissolver
  • @@ -118,7 +120,6 @@
  • ARA_Technology_1MED - 需要科技 节点MED-1"疗愈种" 以解锁进化
  • @@ -172,7 +173,7 @@ -
  • +
  • @@ -470,7 +564,6 @@
  • ARA_Technology_4CLO - 需要科技 节点CLO-4"追猎种" 以解锁进化
  • @@ -555,7 +648,6 @@
  • ARA_Technology_5STL - 需要科技 节点STL-5"基因窃取" 以解锁进化
  • @@ -692,7 +784,6 @@
  • ARA_Technology_1BAC - 需要科技 节点BAC-1"育菌种" 以解锁进化
  • @@ -714,7 +805,7 @@ -
  • +
  • + + false + + ARA_InsectJelly + + 30 + + + 0.1 + + 0.1 + 0 + + + false + + true + + true + + 20 + + + false + + false + + true +
  • +
    + \ No newline at end of file diff --git a/1.6/1.6/Defs/HediffDefs/ARA_Hediffs_Incubator_Reward.xml b/1.6/1.6/Defs/HediffDefs/ARA_Hediffs_Incubator_Reward.xml index c8af12d..fe87e05 100644 --- a/1.6/1.6/Defs/HediffDefs/ARA_Hediffs_Incubator_Reward.xml +++ b/1.6/1.6/Defs/HediffDefs/ARA_Hediffs_Incubator_Reward.xml @@ -235,7 +235,6 @@ - ARA_Incubator_6_Flaw_Hediffs @@ -250,7 +249,6 @@ - ARA_Incubator_7_Flaw_Hediffs @@ -267,7 +265,6 @@ - ARA_Incubator_8_Flaw_Hediffs @@ -305,7 +302,6 @@ - ARA_Incubator_Elite_AdaptiveArmor @@ -327,7 +323,6 @@ - ARA_Incubator_Elite_BerserkGlands @@ -346,7 +341,6 @@ - ARA_Incubator_Elite_PsychicResonance diff --git a/1.6/1.6/Defs/ResearchProjectDefs/ARA_ResearchProjects.xml b/1.6/1.6/Defs/ResearchProjectDefs/ARA_ResearchProjects.xml index 82e75bb..7cd00b8 100644 --- a/1.6/1.6/Defs/ResearchProjectDefs/ARA_ResearchProjects.xml +++ b/1.6/1.6/Defs/ResearchProjectDefs/ARA_ResearchProjects.xml @@ -31,7 +31,7 @@ ARA_Base_Technology - <color=#887E78><i>阿拉克涅虫群-主巢触须\n主巢触须的进化路径是包含于每一支虫群中的通用进化路径,它们奠定了虫群在生物学上的优越性。</i></color>\n\n解锁虫群的基础科技,允许孵化基本的辅虫和武装器官。\n\n阿拉克涅虫群所有需要蓝图的科技,其研究只能通过基因试验卵进行。 + <color=#887E78><i>阿拉克涅虫群-主巢触须\n主巢触须的进化路径是包含于每一支虫群中的通用进化路径,它们奠定了虫群在生物学上的优越性。</i></color>\n\n解锁虫群的基础科技,允许孵化基本的辅虫和武装器官。\n\n阿拉克涅虫群所有需要蓝图的科技,都需要使用其触须分支特有的研究方式完成研究。 100 0.00 3.20 @@ -39,7 +39,7 @@ ARA_Technology_1WMT - <color=#887E78><i>阿拉克涅虫群-主巢触须\n主巢触须的进化路径是包含于每一支虫群中的通用进化路径,它们奠定了虫群在生物学上的优越性。</i></color>\n\n允许女皇种和工艺种进化到下一个阶段。\n\n阿拉克涅虫群所有需要蓝图的科技,其研究只能通过基因试验卵进行。 + <color=#887E78><i>阿拉克涅虫群-主巢触须\n主巢触须的进化路径是包含于每一支虫群中的通用进化路径,它们奠定了虫群在生物学上的优越性。</i></color>\n\n允许女皇种和工艺种进化到下一个阶段。\n\n阿拉克涅虫群所有需要蓝图的科技,都需要使用其触须分支特有的研究方式完成研究。 3000 4.50 3.20 @@ -56,7 +56,7 @@ ARA_Technology_2WMT - <color=#887E78><i>阿拉克涅虫群-主巢触须\n主巢触须的进化路径是包含于每一支虫群中的通用进化路径,它们奠定了虫群在生物学上的优越性。</i></color>\n\n允许女皇种和工艺种进化到下一个阶段。\n\n阿拉克涅虫群所有需要蓝图的科技,其研究只能通过基因试验卵进行。 + <color=#887E78><i>阿拉克涅虫群-主巢触须\n主巢触须的进化路径是包含于每一支虫群中的通用进化路径,它们奠定了虫群在生物学上的优越性。</i></color>\n\n允许女皇种和工艺种进化到下一个阶段。\n\n阿拉克涅虫群所有需要蓝图的科技,都需要使用其触须分支特有的研究方式完成研究。 7000 9.00 3.20 @@ -169,7 +169,7 @@ ARA_Technology_1KYC - <color=#887E78><i>阿拉克涅虫群-主巢触须\n主巢触须的进化路径是包含于每一支虫群中的通用进化路径,它们奠定了虫群在生物学上的优越性。</i></color>\n\n允许女皇种孵化新的虫族——战士种。\n\n阿拉克涅虫群所有需要蓝图的科技,其研究只能通过基因试验卵进行。 + <color=#887E78><i>阿拉克涅虫群-主巢触须\n主巢触须的进化路径是包含于每一支虫群中的通用进化路径,它们奠定了虫群在生物学上的优越性。</i></color>\n\n允许女皇种孵化新的虫族——战士种。\n\n阿拉克涅虫群所有需要蓝图的科技,都需要使用其触须分支特有的研究方式完成研究。 150 3.00 4.80 @@ -180,7 +180,7 @@ ARA_Technology_4KYC - <color=#887E78><i>阿拉克涅虫群-主巢触须\n主巢触须的进化路径是包含于每一支虫群中的通用进化路径,它们奠定了虫群在生物学上的优越性。</i></color>\n\n允许女皇种孵化新的虫族——原虫种,一种可以寄生在别的种族身上以控制它们的特殊督虫。\n\n阿拉克涅虫群所有需要蓝图的科技,其研究只能通过基因试验卵进行。 + <color=#887E78><i>阿拉克涅虫群-主巢触须\n主巢触须的进化路径是包含于每一支虫群中的通用进化路径,它们奠定了虫群在生物学上的优越性。</i></color>\n\n允许女皇种孵化新的虫族——原虫种,一种可以寄生在别的种族身上以控制它们的特殊督虫。\n\n阿拉克涅虫群所有需要蓝图的科技,都需要使用其触须分支特有的研究方式完成研究。 200 3.00 4.30 @@ -191,7 +191,7 @@ ARA_Technology_2KYC - <color=#887E78><i>阿拉克涅虫群-主巢触须\n主巢触须的进化路径是包含于每一支虫群中的通用进化路径,它们奠定了虫群在生物学上的优越性。</i></color>\n\n允许女皇种孵化新的虫族——空天种,敏捷而致命的精锐虫族,拥有以飞行姿态穿梭于战场的能力。\n\n阿拉克涅虫群所有需要蓝图的科技,其研究只能通过基因试验卵进行。 + <color=#887E78><i>阿拉克涅虫群-主巢触须\n主巢触须的进化路径是包含于每一支虫群中的通用进化路径,它们奠定了虫群在生物学上的优越性。</i></color>\n\n允许女皇种孵化新的虫族——空天种,敏捷而致命的精锐虫族,拥有以飞行姿态穿梭于战场的能力。\n\n阿拉克涅虫群所有需要蓝图的科技,都需要使用其触须分支特有的研究方式完成研究。 1600 5.50 4.30 @@ -205,7 +205,7 @@ ARA_Technology_5KYC - <color=#887E78><i>阿拉克涅虫群-主巢触须\n主巢触须的进化路径是包含于每一支虫群中的通用进化路径,它们奠定了虫群在生物学上的优越性。</i></color>\n\n允许女皇种孵化新的虫族——迷雾种,一种拥有厚重甲壳的大型督虫,可以释放烟雾、阻燃剂和召唤虫族增援的信息素以协助虫群进行集群冲击。\n\n阿拉克涅虫群所有需要蓝图的科技,其研究只能通过基因试验卵进行。 + <color=#887E78><i>阿拉克涅虫群-主巢触须\n主巢触须的进化路径是包含于每一支虫群中的通用进化路径,它们奠定了虫群在生物学上的优越性。</i></color>\n\n允许女皇种孵化新的虫族——迷雾种,一种拥有厚重甲壳的大型督虫,可以释放烟雾、阻燃剂和召唤虫族增援的信息素以协助虫群进行集群冲击。\n\n阿拉克涅虫群所有需要蓝图的科技,都需要使用其触须分支特有的研究方式完成研究。 800 5.50 4.80 @@ -219,7 +219,7 @@ ARA_Technology_6KYC - <color=#887E78><i>阿拉克涅虫群-主巢触须\n主巢触须的进化路径是包含于每一支虫群中的通用进化路径,它们奠定了虫群在生物学上的优越性。</i></color>\n\n允许女皇种孵化新的虫族——织域种,一种寿命长且拥有强大灵能的特殊虫族,不仅能协助虫群的科研工作,也是一个强大的施法者。\n\n阿拉克涅虫群所有需要蓝图的科技,其研究只能通过基因试验卵进行。 + <color=#887E78><i>阿拉克涅虫群-主巢触须\n主巢触须的进化路径是包含于每一支虫群中的通用进化路径,它们奠定了虫群在生物学上的优越性。</i></color>\n\n允许女皇种孵化新的虫族——织域种,一种寿命长且拥有强大灵能的特殊虫族,不仅能协助虫群的科研工作,也是一个强大的施法者。\n\n阿拉克涅虫群所有需要蓝图的科技,都需要使用其触须分支特有的研究方式完成研究。 800 6.50 2.70 @@ -233,7 +233,7 @@ ARA_Technology_7KYC - <color=#887E78><i>阿拉克涅虫群-主巢触须\n主巢触须的进化路径是包含于每一支虫群中的通用进化路径,它们奠定了虫群在生物学上的优越性。</i></color>\n\n允许女皇种孵化新的虫族——禁卫种,一种寿命较其他虫族更长的精锐虫族,拥有优秀的远程作战能力和社交能力,同时也可以作为指挥官指挥虫群。\n\n阿拉克涅虫群所有需要蓝图的科技,其研究只能通过基因试验卵进行。 + <color=#887E78><i>阿拉克涅虫群-主巢触须\n主巢触须的进化路径是包含于每一支虫群中的通用进化路径,它们奠定了虫群在生物学上的优越性。</i></color>\n\n允许女皇种孵化新的虫族——禁卫种,一种寿命较其他虫族更长的精锐虫族,拥有优秀的远程作战能力和社交能力,同时也可以作为指挥官指挥虫群。\n\n阿拉克涅虫群所有需要蓝图的科技,都需要使用其触须分支特有的研究方式完成研究。 2800 10.00 3.80 @@ -247,7 +247,7 @@ ARA_Technology_6MEN - <color=#887E78><i>阿拉克涅虫群-主巢触须\n主巢触须的进化路径是包含于每一支虫群中的通用进化路径,它们奠定了虫群在生物学上的优越性。</i></color>\n\n允许女皇种孵化新的虫族——拟线体,一种出生时躯体便已经被阿拉克涅拟线种寄生的虫族,便宜廉价并且可以快速得到的炮灰,倒是没什么纪律性就是了。\n\n阿拉克涅虫群所有需要蓝图的科技,其研究只能通过基因试验卵进行。 + <color=#887E78><i>阿拉克涅虫群-主巢触须\n主巢触须的进化路径是包含于每一支虫群中的通用进化路径,它们奠定了虫群在生物学上的优越性。</i></color>\n\n允许女皇种孵化新的虫族——拟线体,一种出生时躯体便已经被阿拉克涅拟线种寄生的虫族,便宜廉价并且可以快速得到的炮灰,倒是没什么纪律性就是了。\n\n阿拉克涅虫群所有需要蓝图的科技,都需要使用其触须分支特有的研究方式完成研究。 1800 7.50 2.10 @@ -572,7 +572,7 @@ ARA_Technology_5STL - <color=#887E78><i>阿拉克涅虫群-主巢触须\n主巢触须的进化路径是包含于每一支虫群中的通用进化路径,它们奠定了虫群在生物学上的优越性。</i></color>\n\n允许战士种进行定向进化,抛弃其战斗技能以换取其从殖民者、囚犯和奴隶身上抽取和注入基因的能力。\n\n阿拉克涅虫群所有需要蓝图的科技,其研究只能通过基因试验卵进行。 + <color=#887E78><i>阿拉克涅虫群-主巢触须\n主巢触须的进化路径是包含于每一支虫群中的通用进化路径,它们奠定了虫群在生物学上的优越性。</i></color>\n\n允许战士种进行定向进化,抛弃其战斗技能以换取其从殖民者、囚犯和奴隶身上抽取和注入基因的能力。\n\n阿拉克涅虫群所有需要蓝图的科技,都需要使用其触须分支特有的研究方式完成研究。 500 4.00 4.80 @@ -583,7 +583,7 @@ ARA_Technology_1VTE - <color=#887E78><i>阿拉克涅虫群-主巢触须\n主巢触须的进化路径是包含于每一支虫群中的通用进化路径,它们奠定了虫群在生物学上的优越性。</i></color>\n\n允许盾头种进行定向进化,抛弃其产出甲壳素和建造建筑的能力以换取战斗能力、移动能力和冲撞攻击的技能。\n\n阿拉克涅虫群所有需要蓝图的科技,其研究只能通过基因试验卵进行。 + <color=#887E78><i>阿拉克涅虫群-主巢触须\n主巢触须的进化路径是包含于每一支虫群中的通用进化路径,它们奠定了虫群在生物学上的优越性。</i></color>\n\n允许盾头种进行定向进化,抛弃其产出甲壳素和建造建筑的能力以换取战斗能力、移动能力和冲撞攻击的技能。\n\n阿拉克涅虫群所有需要蓝图的科技,都需要使用其触须分支特有的研究方式完成研究。 1200 6.50 4.80 @@ -597,7 +597,7 @@ ARA_Technology_1MED - <color=#887E78><i>阿拉克涅虫群-主巢触须\n主巢触须的进化路径是包含于每一支虫群中的通用进化路径,它们奠定了虫群在生物学上的优越性。</i></color>\n\n允许蜜罐种进行定向进化,抛弃生产虫蜜的能力,以强化其自身的医疗能力并定期产出药物。\n\n阿拉克涅虫群所有需要蓝图的科技,其研究只能通过基因试验卵进行。 + <color=#887E78><i>阿拉克涅虫群-主巢触须\n主巢触须的进化路径是包含于每一支虫群中的通用进化路径,它们奠定了虫群在生物学上的优越性。</i></color>\n\n允许蜜罐种进行定向进化,抛弃生产虫蜜的能力,以强化其自身的医疗能力并定期产出药物。\n\n阿拉克涅虫群所有需要蓝图的科技,都需要使用其触须分支特有的研究方式完成研究。 1200 5.50 2.10 @@ -608,7 +608,7 @@ ARA_Technology_4CLO - <color=#887E78><i>阿拉克涅虫群-主巢触须\n主巢触须的进化路径是包含于每一支虫群中的通用进化路径,它们奠定了虫群在生物学上的优越性。</i></color>\n\n允许战士种进行定向进化,牺牲其使用远程武器的能力以换取强大的近战和永久隐身的能力。\n\n阿拉克涅虫群所有需要蓝图的科技,其研究只能通过基因试验卵进行。 + <color=#887E78><i>阿拉克涅虫群-主巢触须\n主巢触须的进化路径是包含于每一支虫群中的通用进化路径,它们奠定了虫群在生物学上的优越性。</i></color>\n\n允许战士种进行定向进化,牺牲其使用远程武器的能力以换取强大的近战和永久隐身的能力。\n\n阿拉克涅虫群所有需要蓝图的科技,都需要使用其触须分支特有的研究方式完成研究。 1800 5.50 5.30 @@ -622,7 +622,7 @@ ARA_Technology_1BAC - <color=#887E78><i>阿拉克涅虫群-主巢触须\n主巢触须的进化路径是包含于每一支虫群中的通用进化路径,它们奠定了虫群在生物学上的优越性。</i></color>\n\n允许迷雾种进行定向进化,牺牲其护甲和喷射信息素的能力以换成生产虫群所需高级资源"活化钜菌"的能力。\n\n阿拉克涅虫群所有需要蓝图的科技,其研究只能通过基因试验卵进行。 + <color=#887E78><i>阿拉克涅虫群-主巢触须\n主巢触须的进化路径是包含于每一支虫群中的通用进化路径,它们奠定了虫群在生物学上的优越性。</i></color>\n\n允许迷雾种进行定向进化,牺牲其护甲和喷射信息素的能力以换成生产虫群所需高级资源"活化钜菌"的能力。\n\n阿拉克涅虫群所有需要蓝图的科技,都需要使用其触须分支特有的研究方式完成研究。 1000 7.50 3.80 @@ -634,7 +634,7 @@ ARA_Technology_9WID - <color=#887E78><i>阿拉克涅虫群-主巢触须\n主巢触须的进化路径是包含于每一支虫群中的通用进化路径,它们奠定了虫群在生物学上的优越性。</i></color>\n\n允许空天种进行定向进化,以牺牲高速和高空机动的能力换取向敌人投射大量磁暴种的能力,这种虫群可以以自杀性攻击的方式释放EMP以对抗机械族。\n\n阿拉克涅虫群所有需要蓝图的科技,其研究只能通过基因试验卵进行。 + <color=#887E78><i>阿拉克涅虫群-主巢触须\n主巢触须的进化路径是包含于每一支虫群中的通用进化路径,它们奠定了虫群在生物学上的优越性。</i></color>\n\n允许空天种进行定向进化,以牺牲高速和高空机动的能力换取向敌人投射大量磁暴种的能力,这种虫群可以以自杀性攻击的方式释放EMP以对抗机械族。\n\n阿拉克涅虫群所有需要蓝图的科技,都需要使用其触须分支特有的研究方式完成研究。 1500 7.50 1.50 @@ -648,7 +648,7 @@ ARA_Technology_6LOD - <color=#887E78><i>阿拉克涅虫群-主巢触须\n主巢触须的进化路径是包含于每一支虫群中的通用进化路径,它们奠定了虫群在生物学上的优越性。</i></color>\n\n允许空天种进行定向进化,以牺牲高速和高空机动的能力换取向敌人投射大量天巢种的能力,这种飞行辅虫速度很快,并且在近战中很难缠。\n\n阿拉克涅虫群所有需要蓝图的科技,其研究只能通过基因试验卵进行。 + <color=#887E78><i>阿拉克涅虫群-主巢触须\n主巢触须的进化路径是包含于每一支虫群中的通用进化路径,它们奠定了虫群在生物学上的优越性。</i></color>\n\n允许空天种进行定向进化,以牺牲高速和高空机动的能力换取向敌人投射大量天巢种的能力,这种飞行辅虫速度很快,并且在近战中很难缠。\n\n阿拉克涅虫群所有需要蓝图的科技,都需要使用其触须分支特有的研究方式完成研究。 3500 10.00 5.30 @@ -662,7 +662,7 @@ ARA_Technology_3CON - <color=#887E78><i>阿拉克涅虫群-主巢触须\n主巢触须的进化路径是包含于每一支虫群中的通用进化路径,它们奠定了虫群在生物学上的优越性。</i></color>\n\n允许盾头种进行定向进化,抛弃其防御能力以换取更强大的甲壳素产出能力和建造建筑的能力。\n\n阿拉克涅虫群所有需要蓝图的科技,其研究只能通过基因试验卵进行。 + <color=#887E78><i>阿拉克涅虫群-主巢触须\n主巢触须的进化路径是包含于每一支虫群中的通用进化路径,它们奠定了虫群在生物学上的优越性。</i></color>\n\n允许盾头种进行定向进化,抛弃其防御能力以换取更强大的甲壳素产出能力和建造建筑的能力。\n\n阿拉克涅虫群所有需要蓝图的科技,都需要使用其触须分支特有的研究方式完成研究。 300 2.00 2.70 @@ -673,7 +673,7 @@ ARA_Technology_8FEL - <color=#887E78><i>阿拉克涅虫群-主巢触须\n主巢触须的进化路径是包含于每一支虫群中的通用进化路径,它们奠定了虫群在生物学上的优越性。</i></color>\n\n允许蜜罐种进行定向进化,抛弃生产虫蜜的能力,以获得防御力增强、生产化合燃料和喷射火焰的能力。\n\n阿拉克涅虫群所有需要蓝图的科技,其研究只能通过基因试验卵进行。 + <color=#887E78><i>阿拉克涅虫群-主巢触须\n主巢触须的进化路径是包含于每一支虫群中的通用进化路径,它们奠定了虫群在生物学上的优越性。</i></color>\n\n允许蜜罐种进行定向进化,抛弃生产虫蜜的能力,以获得防御力增强、生产化合燃料和喷射火焰的能力。\n\n阿拉克涅虫群所有需要蓝图的科技,都需要使用其触须分支特有的研究方式完成研究。 300 2.00 2.10 @@ -684,7 +684,7 @@ ARA_Technology_3CRP - <color=#887E78><i>阿拉克涅虫群-主巢触须\n主巢触须的进化路径是包含于每一支虫群中的通用进化路径,它们奠定了虫群在生物学上的优越性。</i></color>\n\n允许迷雾种进行定向进化,抛弃喷射信息素的能力,以获得防御力、移动速度的增强和大规模铺设菌毯的能力。\n\n阿拉克涅虫群所有需要蓝图的科技,其研究只能通过基因试验卵进行。 + <color=#887E78><i>阿拉克涅虫群-主巢触须\n主巢触须的进化路径是包含于每一支虫群中的通用进化路径,它们奠定了虫群在生物学上的优越性。</i></color>\n\n允许迷雾种进行定向进化,抛弃喷射信息素的能力,以获得防御力、移动速度的增强和大规模铺设菌毯的能力。\n\n阿拉克涅虫群所有需要蓝图的科技,都需要使用其触须分支特有的研究方式完成研究。 1300 7.50 0.90 @@ -698,7 +698,7 @@ ARA_Technology_9NAV - <color=#887E78><i>阿拉克涅虫群-主巢触须\n主巢触须的进化路径是包含于每一支虫群中的通用进化路径,它们奠定了虫群在生物学上的优越性。</i></color>\n\n允许禁卫种进行定向进化,抛弃直接战斗能力和指挥地面虫群部队的能力,换取指挥空中兽虫群的能力。\n\n阿拉克涅虫群所有需要蓝图的科技,其研究只能通过基因试验卵进行。 + <color=#887E78><i>阿拉克涅虫群-主巢触须\n主巢触须的进化路径是包含于每一支虫群中的通用进化路径,它们奠定了虫群在生物学上的优越性。</i></color>\n\n允许禁卫种进行定向进化,抛弃直接战斗能力和指挥地面虫群部队的能力,换取指挥空中兽虫群的能力。\n\n阿拉克涅虫群所有需要蓝图的科技,都需要使用其触须分支特有的研究方式完成研究。 3500 11.00 3.80 @@ -846,20 +846,6 @@ - - ARA_Technology_8SLA - - <color=#887E78><i>阿拉克涅虫群-主巢触须\n主巢触须的进化路径是包含于每一支虫群中的通用进化路径,它们奠定了虫群在生物学上的优越性。</i></color>\n\n允许女皇种孵化新的兽虫——暴屠种。\n\n阿拉克涅虫群所有需要蓝图的科技,其研究只能通过基因试验卵进行。 - 3000 - 10.00 - 5.80 - -
  • ARA_Technology_2HAG
  • -
    - -
  • ARA_Technology_2WMT
  • -
    -
    ARA_Technology_4COV diff --git a/1.6/1.6/Defs/ResearchProjectDefs/ARA_ResearchProjects_Titan.xml b/1.6/1.6/Defs/ResearchProjectDefs/ARA_ResearchProjects_Titan.xml index 941fb1c..8078c09 100644 --- a/1.6/1.6/Defs/ResearchProjectDefs/ARA_ResearchProjects_Titan.xml +++ b/1.6/1.6/Defs/ResearchProjectDefs/ARA_ResearchProjects_Titan.xml @@ -9,4 +9,33 @@ 16.00 3.20 + + ARA_Technology_8SLA + + <color=#915A30><i>阿拉克涅虫群-泰坦触须\n泰坦触须是阿拉克涅虫群的主力军团,包含阿拉克涅虫群中最坚韧、最具有适应力的族群,承担在战场上维持战线的任务。这个分支下的虫群拥有均衡的攻防能力,擅长以硬碰硬的模式消灭对手。</i></color>\n\n允许女皇种孵化新的兽虫——暴屠种。\n\n阿拉克涅虫群 泰坦触须所有需要蓝图的科技,其研究只能通过基因试验卵进行。 + 3000 + 10.00 + 5.80 + +
  • ARA_Technology_2HAG
  • +
  • ARA_Technology_2WMT
  • +
    + +
  • ARA_Titan_Base_Technology
  • +
    +
    + + ARA_Technology_7ACD_T + + <color=#915A30><i>阿拉克涅虫群-泰坦触须\n泰坦触须是阿拉克涅虫群的主力军团,包含阿拉克涅虫群中最坚韧、最具有适应力的族群,承担在战场上维持战线的任务。这个分支下的虫群拥有均衡的攻防能力,擅长以硬碰硬的模式消灭对手。</i></color>\n\n允许蜜罐种进行定向进化,抛弃孵化辅虫的能力,换取溶解囚犯和俘虏以快速换取虫蜜的溶脂强酸。\n\n阿拉克涅虫群 泰坦触须所有需要蓝图的科技,其研究只能通过基因试验卵进行。 + 500 + 10.00 + 5.80 + +
  • ARA_Technology_7VXI
  • +
    + +
  • ARA_Titan_Base_Technology
  • +
    +
    \ No newline at end of file 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 4d691d4..28784b9 100644 --- a/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/ArachnaeSwarm_Keys.xml +++ b/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/ArachnaeSwarm_Keys.xml @@ -156,4 +156,9 @@ {0} 没有可转储的神经束负荷 无法向 {0} 转储神经束负荷 + + {0} 无法被选为目标 + 目标不能被此技能影响 + + 需要科技 {0} 以解锁进化 \ No newline at end of file diff --git a/About/About.xml b/About/About.xml index dce076a..18cacb8 100644 --- a/About/About.xml +++ b/About/About.xml @@ -37,12 +37,7 @@ erdelf.HumanoidAlienRaces Humanoid Alien Races 2.0 https://steamcommunity.com/sharedfiles/filedetails/?id=839005762 - -
  • - Nals.FacialAnimation - [NL] Facial Animation - WIP - https://steamcommunity.com/sharedfiles/filedetails/?id=1635901197 -
  • + diff --git a/Source/ArachnaeSwarm/.vs/ArachnaeSwarm/v17/.suo b/Source/ArachnaeSwarm/.vs/ArachnaeSwarm/v17/.suo index f760c80..d991922 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 85b51d3..4c51178 100644 --- a/Source/ArachnaeSwarm/.vs/ArachnaeSwarm/v17/DocumentLayout.json +++ b/Source/ArachnaeSwarm/.vs/ArachnaeSwarm/v17/DocumentLayout.json @@ -2,6 +2,26 @@ "Version": 1, "WorkspaceRootPath": "D:\\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\\abilities\\compabilityeffect_researchprereq.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", + "RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:abilities\\compabilityeffect_researchprereq.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" + }, + { + "AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|d:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\hediffs\\ara_spawner\\hediffcompproperties_spawner.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", + "RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:hediffs\\ara_spawner\\hediffcompproperties_spawner.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" + }, + { + "AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|d:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\hediffs\\ara_productionqueue\\hediffcompproperties_productionqueue.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", + "RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:hediffs\\ara_productionqueue\\hediffcompproperties_productionqueue.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" + }, + { + "AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|d:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\hediffs\\ara_spawner\\hediffcomp_spawner.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", + "RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:hediffs\\ara_spawner\\hediffcomp_spawner.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" + }, + { + "AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|d:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\hediffs\\ara_productionqueue\\hediffcomp_productionqueue.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", + "RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:hediffs\\ara_productionqueue\\hediffcomp_productionqueue.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" + }, { "AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|d:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\hediffs\\ara_gestaltnode\\hediffcomp_gestaltnode.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", "RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:hediffs\\ara_gestaltnode\\hediffcomp_gestaltnode.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" @@ -66,15 +86,80 @@ "DocumentGroups": [ { "DockedWidth": 200, - "SelectedChildIndex": 2, + "SelectedChildIndex": 0, "Children": [ + { + "$type": "Document", + "DocumentIndex": 0, + "Title": "CompAbilityEffect_ResearchPrereq.cs", + "DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\CompAbilityEffect_ResearchPrereq.cs", + "RelativeDocumentMoniker": "Abilities\\CompAbilityEffect_ResearchPrereq.cs", + "ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\CompAbilityEffect_ResearchPrereq.cs", + "RelativeToolTip": "Abilities\\CompAbilityEffect_ResearchPrereq.cs", + "ViewState": "AgIAAAAAAAAAAAAAAAAAAAwAAABBAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2026-02-14T07:44:51.729Z", + "EditorCaption": "" + }, { "$type": "Bookmark", "Name": "ST:0:0:{1c4feeaa-4718-4aa9-859d-94ce25d182ba}" }, + { + "$type": "Document", + "DocumentIndex": 2, + "Title": "HediffCompProperties_ProductionQueue.cs", + "DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Hediffs\\ARA_ProductionQueue\\HediffCompProperties_ProductionQueue.cs", + "RelativeDocumentMoniker": "Hediffs\\ARA_ProductionQueue\\HediffCompProperties_ProductionQueue.cs", + "ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Hediffs\\ARA_ProductionQueue\\HediffCompProperties_ProductionQueue.cs", + "RelativeToolTip": "Hediffs\\ARA_ProductionQueue\\HediffCompProperties_ProductionQueue.cs", + "ViewState": "AgIAAAAAAAAAAAAAAAAAAAwAAAARAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2026-02-14T07:21:19.792Z", + "EditorCaption": "" + }, + { + "$type": "Document", + "DocumentIndex": 4, + "Title": "HediffComp_ProductionQueue.cs", + "DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Hediffs\\ARA_ProductionQueue\\HediffComp_ProductionQueue.cs", + "RelativeDocumentMoniker": "Hediffs\\ARA_ProductionQueue\\HediffComp_ProductionQueue.cs", + "ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Hediffs\\ARA_ProductionQueue\\HediffComp_ProductionQueue.cs", + "RelativeToolTip": "Hediffs\\ARA_ProductionQueue\\HediffComp_ProductionQueue.cs", + "ViewState": "AgIAAAAAAAAAAAAAAAAAAAoAAAArAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2026-02-14T07:19:42.058Z", + "EditorCaption": "" + }, { "$type": "Document", "DocumentIndex": 1, + "Title": "HediffCompProperties_Spawner.cs", + "DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Hediffs\\ARA_Spawner\\HediffCompProperties_Spawner.cs", + "RelativeDocumentMoniker": "Hediffs\\ARA_Spawner\\HediffCompProperties_Spawner.cs", + "ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Hediffs\\ARA_Spawner\\HediffCompProperties_Spawner.cs", + "RelativeToolTip": "Hediffs\\ARA_Spawner\\HediffCompProperties_Spawner.cs", + "ViewState": "AgIAAFcAAAAAAAAAAADgv3gAAAApAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2026-02-14T07:09:01.203Z", + "EditorCaption": "" + }, + { + "$type": "Document", + "DocumentIndex": 3, + "Title": "HediffComp_Spawner.cs", + "DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Hediffs\\ARA_Spawner\\HediffComp_Spawner.cs", + "RelativeDocumentMoniker": "Hediffs\\ARA_Spawner\\HediffComp_Spawner.cs", + "ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Hediffs\\ARA_Spawner\\HediffComp_Spawner.cs", + "RelativeToolTip": "Hediffs\\ARA_Spawner\\HediffComp_Spawner.cs", + "ViewState": "AgIAAFwAAAAAAAAAAAAvwHEAAAAoAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2026-02-14T07:08:13.475Z", + "EditorCaption": "" + }, + { + "$type": "Document", + "DocumentIndex": 6, "Title": "CompNodeSwarmLifetime.cs", "DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Pawn_Comps\\ARA_NodeSwarmLifetime\\CompNodeSwarmLifetime.cs", "RelativeDocumentMoniker": "Pawn_Comps\\ARA_NodeSwarmLifetime\\CompNodeSwarmLifetime.cs", @@ -86,20 +171,20 @@ }, { "$type": "Document", - "DocumentIndex": 0, + "DocumentIndex": 5, "Title": "HediffComp_GestaltNode.cs", "DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Hediffs\\ARA_GestaltNode\\HediffComp_GestaltNode.cs", "RelativeDocumentMoniker": "Hediffs\\ARA_GestaltNode\\HediffComp_GestaltNode.cs", "ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Hediffs\\ARA_GestaltNode\\HediffComp_GestaltNode.cs", "RelativeToolTip": "Hediffs\\ARA_GestaltNode\\HediffComp_GestaltNode.cs", - "ViewState": "AgIAAAAAAAAAAAAAAAAAAA4AAAAnAAAAAAAAAA==", + "ViewState": "AgIAABMAAAAAAAAAAAAAwBUAAAAVAAAAAAAAAA==", "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", "WhenOpened": "2026-02-14T02:36:39.249Z", "EditorCaption": "" }, { "$type": "Document", - "DocumentIndex": 4, + "DocumentIndex": 9, "Title": "ARA_DefOf.cs", "DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\ARA_DefOf.cs", "RelativeDocumentMoniker": "ARA_DefOf.cs", @@ -111,7 +196,7 @@ }, { "$type": "Document", - "DocumentIndex": 5, + "DocumentIndex": 10, "Title": "JobDriver_CastAbilityMaintainMultiProjectile.cs", "DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_LaunchMultiProjectile\\JobDriver_CastAbilityMaintainMultiProjectile.cs", "RelativeDocumentMoniker": "Abilities\\ARA_LaunchMultiProjectile\\JobDriver_CastAbilityMaintainMultiProjectile.cs", @@ -123,7 +208,7 @@ }, { "$type": "Document", - "DocumentIndex": 2, + "DocumentIndex": 7, "Title": "CompProperties_AbilityLaunchMultiProjectile.cs", "DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_LaunchMultiProjectile\\CompProperties_AbilityLaunchMultiProjectile.cs", "RelativeDocumentMoniker": "Abilities\\ARA_LaunchMultiProjectile\\CompProperties_AbilityLaunchMultiProjectile.cs", @@ -135,7 +220,7 @@ }, { "$type": "Document", - "DocumentIndex": 6, + "DocumentIndex": 11, "Title": "CompAbilityEffect_Transform.cs", "DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_Morphable\\CompAbilityEffect_Transform.cs", "RelativeDocumentMoniker": "Abilities\\ARA_Morphable\\CompAbilityEffect_Transform.cs", @@ -147,7 +232,7 @@ }, { "$type": "Document", - "DocumentIndex": 3, + "DocumentIndex": 8, "Title": "CompAbilityEffect_LaunchMultiProjectile.cs", "DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_LaunchMultiProjectile\\CompAbilityEffect_LaunchMultiProjectile.cs", "RelativeDocumentMoniker": "Abilities\\ARA_LaunchMultiProjectile\\CompAbilityEffect_LaunchMultiProjectile.cs", @@ -159,7 +244,7 @@ }, { "$type": "Document", - "DocumentIndex": 8, + "DocumentIndex": 13, "Title": "CompProperties_AbilityPsychicLoadCost.cs", "DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_PsychicLoadCost\\CompProperties_AbilityPsychicLoadCost.cs", "RelativeDocumentMoniker": "Abilities\\ARA_PsychicLoadCost\\CompProperties_AbilityPsychicLoadCost.cs", @@ -171,7 +256,7 @@ }, { "$type": "Document", - "DocumentIndex": 9, + "DocumentIndex": 14, "Title": "Gizmo_PawnResearchProgress.cs", "DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Pawn_Comps\\ARA_PawnResearchBlueprintReader\\Gizmo_PawnResearchProgress.cs", "RelativeDocumentMoniker": "Pawn_Comps\\ARA_PawnResearchBlueprintReader\\Gizmo_PawnResearchProgress.cs", @@ -183,7 +268,7 @@ }, { "$type": "Document", - "DocumentIndex": 7, + "DocumentIndex": 12, "Title": "CompAbilityEffect_PsychicLoadCost.cs", "DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_PsychicLoadCost\\CompAbilityEffect_PsychicLoadCost.cs", "RelativeDocumentMoniker": "Abilities\\ARA_PsychicLoadCost\\CompAbilityEffect_PsychicLoadCost.cs", @@ -195,7 +280,7 @@ }, { "$type": "Document", - "DocumentIndex": 12, + "DocumentIndex": 17, "Title": "CompAbilityEffect_PsychicBrainburn.cs", "DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\PsychicBrainburn\\CompAbilityEffect_PsychicBrainburn.cs", "RelativeDocumentMoniker": "Abilities\\PsychicBrainburn\\CompAbilityEffect_PsychicBrainburn.cs", @@ -207,7 +292,7 @@ }, { "$type": "Document", - "DocumentIndex": 11, + "DocumentIndex": 16, "Title": "Gizmo_ResearchProgress.cs", "DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_ResearchBlueprintReader\\Gizmo_ResearchProgress.cs", "RelativeDocumentMoniker": "Buildings\\Building_ResearchBlueprintReader\\Gizmo_ResearchProgress.cs", @@ -219,7 +304,7 @@ }, { "$type": "Document", - "DocumentIndex": 13, + "DocumentIndex": 18, "Title": "ResearchBlueprintData.cs", "DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_ResearchBlueprintReader\\ResearchBlueprintData.cs", "RelativeDocumentMoniker": "Buildings\\Building_ResearchBlueprintReader\\ResearchBlueprintData.cs", @@ -231,7 +316,7 @@ }, { "$type": "Document", - "DocumentIndex": 10, + "DocumentIndex": 15, "Title": "Comp_PawnResearchBlueprintReader.cs", "DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Pawn_Comps\\ARA_PawnResearchBlueprintReader\\Comp_PawnResearchBlueprintReader.cs", "RelativeDocumentMoniker": "Pawn_Comps\\ARA_PawnResearchBlueprintReader\\Comp_PawnResearchBlueprintReader.cs", diff --git a/Source/ArachnaeSwarm/Abilities/ARA_HediffBlacklist/CompAbilityEffect_HediffBlacklist.cs b/Source/ArachnaeSwarm/Abilities/ARA_HediffBlacklist/CompAbilityEffect_HediffBlacklist.cs new file mode 100644 index 0000000..bee398c --- /dev/null +++ b/Source/ArachnaeSwarm/Abilities/ARA_HediffBlacklist/CompAbilityEffect_HediffBlacklist.cs @@ -0,0 +1,62 @@ +using RimWorld; +using Verse; + +namespace ArachnaeSwarm +{ + public class CompAbilityEffect_HediffBlacklist : CompAbilityEffect + { + public new CompProperties_AbilityHediffBlacklist Props => (CompProperties_AbilityHediffBlacklist)props; + + public override bool Valid(LocalTargetInfo target, bool throwMessages = false) + { + Pawn targetPawn = target.Pawn; + + // 如果不是Pawn,返回父类验证结果 + if (targetPawn == null) + return base.Valid(target, throwMessages); + + // 检查目标是否拥有黑名单中的任意一个Hediff + if (HasBlacklistedHediff(targetPawn)) + { + if (throwMessages) + { + Messages.Message(Props.blockedMessage.Translate(targetPawn.LabelShort), + targetPawn, MessageTypeDefOf.RejectInput, false); + } + return false; + } + + return base.Valid(target, throwMessages); + } + + /// + /// 检查目标Pawn是否拥有黑名单中的任意一个Hediff + /// + private bool HasBlacklistedHediff(Pawn pawn) + { + if (pawn?.health?.hediffSet == null || Props.blacklistedHediffs.NullOrEmpty()) + return false; + + foreach (HediffDef hediffDef in Props.blacklistedHediffs) + { + if (hediffDef != null && pawn.health.hediffSet.HasHediff(hediffDef)) + return true; + } + + return false; + } + + /// + /// 在能力描述中显示黑名单信息 + /// + public override string ExtraLabelMouseAttachment(LocalTargetInfo target) + { + Pawn targetPawn = target.Pawn; + if (targetPawn != null && HasBlacklistedHediff(targetPawn)) + { + return "CannotTargetPawnWithBlacklistedHediff".Translate(); + } + return null; + } + } +} diff --git a/Source/ArachnaeSwarm/Abilities/ARA_HediffBlacklist/CompProperties_AbilityHediffBlacklist.cs b/Source/ArachnaeSwarm/Abilities/ARA_HediffBlacklist/CompProperties_AbilityHediffBlacklist.cs new file mode 100644 index 0000000..409b00e --- /dev/null +++ b/Source/ArachnaeSwarm/Abilities/ARA_HediffBlacklist/CompProperties_AbilityHediffBlacklist.cs @@ -0,0 +1,24 @@ +using RimWorld; +using System.Collections.Generic; +using Verse; + +namespace ArachnaeSwarm +{ + public class CompProperties_AbilityHediffBlacklist : CompProperties_AbilityEffect + { + /// + /// 黑名单Hediff列表,拥有这些Hediff的Pawn无法成为目标 + /// + public List blacklistedHediffs; + + /// + /// 目标被阻止时显示的消息 + /// + public string blockedMessage = "ARA_BlacklistedHediff_Blocked"; + + public CompProperties_AbilityHediffBlacklist() + { + this.compClass = typeof(CompAbilityEffect_HediffBlacklist); + } + } +} diff --git a/Source/ArachnaeSwarm/Abilities/PsychicBrainburn/CompAbilityEffect_PsychicBrainburn.cs b/Source/ArachnaeSwarm/Abilities/ARA_PsychicBrainburn/CompAbilityEffect_PsychicBrainburn.cs similarity index 100% rename from Source/ArachnaeSwarm/Abilities/PsychicBrainburn/CompAbilityEffect_PsychicBrainburn.cs rename to Source/ArachnaeSwarm/Abilities/ARA_PsychicBrainburn/CompAbilityEffect_PsychicBrainburn.cs diff --git a/Source/ArachnaeSwarm/Abilities/PsychicBrainburn/CompProperties_PsychicBrainburn.cs b/Source/ArachnaeSwarm/Abilities/ARA_PsychicBrainburn/CompProperties_PsychicBrainburn.cs similarity index 100% rename from Source/ArachnaeSwarm/Abilities/PsychicBrainburn/CompProperties_PsychicBrainburn.cs rename to Source/ArachnaeSwarm/Abilities/ARA_PsychicBrainburn/CompProperties_PsychicBrainburn.cs diff --git a/Source/ArachnaeSwarm/Abilities/ARA_QueenAbility/CompAbilityEffect_SprayLiquidMulti.cs b/Source/ArachnaeSwarm/Abilities/ARA_SprayLiquidMulti/CompAbilityEffect_SprayLiquidMulti.cs similarity index 100% rename from Source/ArachnaeSwarm/Abilities/ARA_QueenAbility/CompAbilityEffect_SprayLiquidMulti.cs rename to Source/ArachnaeSwarm/Abilities/ARA_SprayLiquidMulti/CompAbilityEffect_SprayLiquidMulti.cs diff --git a/Source/ArachnaeSwarm/Abilities/ARA_QueenAbility/CompProperties_AbilitySprayLiquidMulti.cs b/Source/ArachnaeSwarm/Abilities/ARA_SprayLiquidMulti/CompProperties_AbilitySprayLiquidMulti.cs similarity index 100% rename from Source/ArachnaeSwarm/Abilities/ARA_QueenAbility/CompProperties_AbilitySprayLiquidMulti.cs rename to Source/ArachnaeSwarm/Abilities/ARA_SprayLiquidMulti/CompProperties_AbilitySprayLiquidMulti.cs diff --git a/Source/ArachnaeSwarm/Abilities/TrackingCharge/CompAbilityEffect_TrackingCharge.cs b/Source/ArachnaeSwarm/Abilities/ARA_TrackingCharge/CompAbilityEffect_TrackingCharge.cs similarity index 100% rename from Source/ArachnaeSwarm/Abilities/TrackingCharge/CompAbilityEffect_TrackingCharge.cs rename to Source/ArachnaeSwarm/Abilities/ARA_TrackingCharge/CompAbilityEffect_TrackingCharge.cs diff --git a/Source/ArachnaeSwarm/Abilities/TrackingCharge/CompProperties_TrackingCharge.cs b/Source/ArachnaeSwarm/Abilities/ARA_TrackingCharge/CompProperties_TrackingCharge.cs similarity index 100% rename from Source/ArachnaeSwarm/Abilities/TrackingCharge/CompProperties_TrackingCharge.cs rename to Source/ArachnaeSwarm/Abilities/ARA_TrackingCharge/CompProperties_TrackingCharge.cs diff --git a/Source/ArachnaeSwarm/Abilities/TrackingCharge/PawnFlyer_TrackingCharge.cs b/Source/ArachnaeSwarm/Abilities/ARA_TrackingCharge/PawnFlyer_TrackingCharge.cs similarity index 100% rename from Source/ArachnaeSwarm/Abilities/TrackingCharge/PawnFlyer_TrackingCharge.cs rename to Source/ArachnaeSwarm/Abilities/ARA_TrackingCharge/PawnFlyer_TrackingCharge.cs diff --git a/Source/ArachnaeSwarm/Abilities/TrackingCharge/Verb_CastAbilityTrackingCharge.cs b/Source/ArachnaeSwarm/Abilities/ARA_TrackingCharge/Verb_CastAbilityTrackingCharge.cs similarity index 100% rename from Source/ArachnaeSwarm/Abilities/TrackingCharge/Verb_CastAbilityTrackingCharge.cs rename to Source/ArachnaeSwarm/Abilities/ARA_TrackingCharge/Verb_CastAbilityTrackingCharge.cs diff --git a/Source/ArachnaeSwarm/Abilities/ARA_QueenAbility/CompAbilityEffect_BodyPartCheck.cs b/Source/ArachnaeSwarm/Abilities/CompAbilityEffect_BodyPartCheck.cs similarity index 100% rename from Source/ArachnaeSwarm/Abilities/ARA_QueenAbility/CompAbilityEffect_BodyPartCheck.cs rename to Source/ArachnaeSwarm/Abilities/CompAbilityEffect_BodyPartCheck.cs diff --git a/Source/ArachnaeSwarm/Abilities/ARA_QueenAbility/CompAbilityEffect_NeedCost.cs b/Source/ArachnaeSwarm/Abilities/CompAbilityEffect_NeedCost.cs similarity index 100% rename from Source/ArachnaeSwarm/Abilities/ARA_QueenAbility/CompAbilityEffect_NeedCost.cs rename to Source/ArachnaeSwarm/Abilities/CompAbilityEffect_NeedCost.cs diff --git a/Source/ArachnaeSwarm/Abilities/ARA_QueenAbility/CompAbilityEffect_ResearchPrereq.cs b/Source/ArachnaeSwarm/Abilities/CompAbilityEffect_ResearchPrereq.cs similarity index 85% rename from Source/ArachnaeSwarm/Abilities/ARA_QueenAbility/CompAbilityEffect_ResearchPrereq.cs rename to Source/ArachnaeSwarm/Abilities/CompAbilityEffect_ResearchPrereq.cs index f2c9a5d..7d39b28 100644 --- a/Source/ArachnaeSwarm/Abilities/ARA_QueenAbility/CompAbilityEffect_ResearchPrereq.cs +++ b/Source/ArachnaeSwarm/Abilities/CompAbilityEffect_ResearchPrereq.cs @@ -6,7 +6,7 @@ namespace ArachnaeSwarm public class CompProperties_AbilityResearchPrereq : CompProperties_AbilityEffect { public ResearchProjectDef requiredResearch; - public string failMessage = "Research not completed."; + public string failMessage; public CompProperties_AbilityResearchPrereq() { @@ -22,7 +22,7 @@ namespace ArachnaeSwarm { if (Props.requiredResearch != null && !Props.requiredResearch.IsFinished) { - reason = Props.failMessage; + reason = Props.failMessage ?? "ARA_AbilityRequiresTech".Translate(Props.requiredResearch); return true; } diff --git a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj index 433d4c6..efd4f06 100644 --- a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj +++ b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj @@ -118,11 +118,11 @@ - - - - - + + + + + @@ -134,12 +134,12 @@ - - - - - - + + + + + + @@ -309,9 +309,8 @@ - - - + + diff --git a/Source/ArachnaeSwarm/Hediffs/ARA_Spawner/HediffCompProperties_Spawner.cs b/Source/ArachnaeSwarm/Hediffs/ARA_Spawner/HediffCompProperties_Spawner.cs new file mode 100644 index 0000000..3a4376f --- /dev/null +++ b/Source/ArachnaeSwarm/Hediffs/ARA_Spawner/HediffCompProperties_Spawner.cs @@ -0,0 +1,123 @@ +using System; +using Verse; + +namespace ArachnaeSwarm +{ + public class HediffCompProperties_Spawner : HediffCompProperties + { + public HediffCompProperties_Spawner() + { + this.compClass = typeof(HediffComp_Spawner); + } + + /// + /// 要生成的物品的ThingDef。如果animalThing为false,则使用此项。 + /// + public ThingDef thingToSpawn; + + /// + /// 每次生成的基础物品数量。 + /// + public int spawnCount = 1; + + /// + /// 如果为true,则生成一个Pawn(动物)。如果为false,则生成一个Thing。 + /// + public bool animalThing; + + /// + /// 要生成的动物的PawnKindDef。如果animalThing为true,则使用此项。 + /// + public PawnKindDef animalToSpawn; + + /// + /// 如果为true,生成的动物将属于玩家派系。 + /// + public bool factionOfPlayerAnimal; + + /// + /// 下一次生成事件发生前的最少天数。 + /// + public float minDaysB4Next = 1f; + + /// + /// 下一次生成事件发生前的最大天数。 + /// + public float maxDaysB4Next = 2f; + + /// + /// 生成后进入宽限期(延迟下一次生成)的几率(0.0到1.0)。 + /// + public float randomGrace; + + /// + /// 如果触发,宽限期的持续时间(天)。 + /// + public float graceDays = 0.5f; + + /// + /// 附近允许的相同Pawn的最大数量。如果超过该数量,则暂停生成。-1为禁用。 + /// + public int spawnMaxAdjacent = -1; + + /// + /// 如果为true,生成的物品将被禁用。 + /// + public bool spawnForbidden; + + /// + /// 如果为true,当宿主Pawn饥饿时,生成将暂停。 + /// + public bool hungerRelative; + + /// + /// 如果为true,当宿主Pawn受伤时,生成将暂停。 + /// + public bool healthRelative; + + /// + /// 如果为true,生成数量将根据宿主的年龄进行调整。 + /// + public bool ageWeightedQuantity; + + /// + /// 如果为true,生成周期(两次生成之间的时间)将根据宿主的年龄进行调整。 + /// + public bool ageWeightedPeriod; + + /// + /// 如果为true且ageWeightedPeriod为true,则随着宿主年龄增长,生成周期变短。如果为false,则变长。 + /// + public bool olderSmallerPeriod; + + /// + /// 如果为true且ageWeightedQuantity为true,则随着宿主年龄增长,生成数量变多。如果为false,则变少。 + /// + public bool olderBiggerQuantity; + + /// + /// 如果为true且ageWeightedQuantity为true,则随年龄增长的数量缩放将是指数性的而非线性的。 + /// + public bool exponentialQuantity; + + /// + /// 指数级数量缩放的最大乘数,以防止出现荒谬的数字。 + /// + public int exponentialRatioLimit = 15; + + /// + /// 生成时显示的消息的翻译键(例如,"下了一个蛋")。 + /// + public string spawnVerb = "delivery"; + + /// + /// 如果为true,则为此组件启用详细的调试日志记录。 + /// + public bool debug; + + /// + /// 新增:生成物品时是否销毁随机身体部位 + /// + public bool destroyRandomBodyPart = false; + } +} diff --git a/Source/ArachnaeSwarm/Hediffs/ARA_Spawner/HediffComp_Spawner.cs b/Source/ArachnaeSwarm/Hediffs/ARA_Spawner/HediffComp_Spawner.cs new file mode 100644 index 0000000..6e3f1d8 --- /dev/null +++ b/Source/ArachnaeSwarm/Hediffs/ARA_Spawner/HediffComp_Spawner.cs @@ -0,0 +1,883 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using RimWorld; +using RimWorld.Planet; +using Verse; + +namespace ArachnaeSwarm +{ + public class HediffComp_Spawner : HediffComp + { + public HediffCompProperties_Spawner Props + { + get + { + return (HediffCompProperties_Spawner)this.props; + } + } + + public override void CompExposeData() + { + Scribe_Values.Look(ref this.ticksUntilSpawn, "ticksUntilSpawn", 0, false); + Scribe_Values.Look(ref this.initialTicksUntilSpawn, "initialTicksUntilSpawn", 0, false); + Scribe_Values.Look(ref this.calculatedMinDaysB4Next, "calculatedMinDaysB4Next", 0f, false); + Scribe_Values.Look(ref this.calculatedMaxDaysB4Next, "calculatedMaxDaysB4Next", 0f, false); + Scribe_Values.Look(ref this.calculatedQuantity, "calculatedQuantity", 0, false); + Scribe_Values.Look(ref this.graceTicks, "graceTicks", 0, false); + } + + public override void CompPostMake() + { + this.myDebug = this.Props.debug; + Warn(string.Concat(new string[] + { + ">>> ", + this.parent.pawn.Label, + " - ", + this.parent.def.defName, + " - CompPostMake start" + }), this.myDebug); + TraceProps(); + CheckProps(); + CalculateValues(); + CheckCalculatedValues(); + TraceCalculatedValues(); + if (this.initialTicksUntilSpawn == 0) + { + Warn("Reseting countdown bc initialTicksUntilSpawn == 0 (comppostmake)", this.myDebug); + ResetCountdown(); + } + } + + public override void CompPostTick(ref float severityAdjustment) + { + this.pawn = this.parent.pawn; + if (!OkPawn(this.pawn)) + { + return; + } + if (this.blockSpawn) + { + return; + } + if (this.graceTicks > 0) + { + this.graceTicks--; + return; + } + if (this.Props.hungerRelative && this.pawn.Starving()) + { + int num = (int)(this.RandomGraceDays() * 60000f); + this.hungerReset++; + this.graceTicks = num; + return; + } + if (this.Props.healthRelative && this.pawn.Starving()) + { + int num2 = (int)(this.RandomGraceDays() * 60000f); + this.healthReset++; + this.graceTicks = num2; + return; + } + this.hungerReset = (this.healthReset = 0); + if (this.CheckShouldSpawn()) + { + Warn("Reseting countdown bc spawned thing", this.myDebug); + CalculateValues(); + CheckCalculatedValues(); + ResetCountdown(); + if (Rand.Chance(this.Props.randomGrace)) + { + int num3 = (int)(this.RandomGraceDays() * 60000f); + this.graceTicks = num3; + } + } + } + + private void TraceProps() + { + Warn(string.Concat(new string[] + { + "Props => minDaysB4Next: ", + this.Props.minDaysB4Next.ToString(), + "; maxDaysB4Next: ", + this.Props.maxDaysB4Next.ToString(), + "; randomGrace: ", + this.Props.randomGrace.ToString(), + "; graceDays: ", + this.Props.graceDays.ToString(), + "; hungerRelative: ", + this.Props.hungerRelative.ToString(), + "; healthRelative: ", + this.Props.healthRelative.ToString(), + "; destroyRandomBodyPart: ", + this.Props.destroyRandomBodyPart.ToString(), + "; " + }), this.myDebug); + if (this.Props.animalThing) + { + Warn(string.Concat(new string[] + { + "animalThing: ", + this.Props.animalThing.ToString(), + "; animalName: ", + this.Props.animalToSpawn.defName, + "; factionOfPlayerAnimal: ", + this.Props.factionOfPlayerAnimal.ToString(), + "; " + }), this.myDebug); + } + if (this.Props.ageWeightedQuantity) + { + Warn(string.Concat(new string[] + { + "ageWeightedQuantity:", + this.Props.ageWeightedQuantity.ToString(), + "; olderBiggerQuantity:", + this.Props.olderBiggerQuantity.ToString(), + "; ", + this.myDebug.ToString() + }), false); + if (this.Props.exponentialQuantity) + { + Warn(string.Concat(new string[] + { + "exponentialQuantity:", + this.Props.exponentialQuantity.ToString(), + "; exponentialRatioLimit:", + this.Props.exponentialRatioLimit.ToString(), + "; " + }), this.myDebug); + } + } + Warn(string.Concat(new string[] + { + "ageWeightedPeriod:", + this.Props.ageWeightedPeriod.ToString(), + "; olderSmallerPeriod:", + this.Props.olderSmallerPeriod.ToString(), + "; ", + this.myDebug.ToString() + }), false); + } + + private void CalculateValues() + { + float num = GetPawnAgeOverlifeExpectancyRatio(this.parent.pawn, this.myDebug); + num = ((num > 1f) ? 1f : num); + this.calculatedMinDaysB4Next = this.Props.minDaysB4Next; + this.calculatedMaxDaysB4Next = this.Props.maxDaysB4Next; + if (this.Props.ageWeightedPeriod) + { + float num2 = this.Props.olderSmallerPeriod ? (-num) : num; + this.calculatedMinDaysB4Next = this.Props.minDaysB4Next * (1f + num2); + this.calculatedMaxDaysB4Next = this.Props.maxDaysB4Next * (1f + num2); + Warn(string.Concat(new string[] + { + " ageWeightedPeriod: ", + this.Props.ageWeightedPeriod.ToString(), + " ageRatio: ", + num.ToString(), + " minDaysB4Next: ", + this.Props.minDaysB4Next.ToString(), + " maxDaysB4Next: ", + this.Props.maxDaysB4Next.ToString(), + " daysAgeRatio: ", + num2.ToString(), + " calculatedMinDaysB4Next: ", + this.calculatedMinDaysB4Next.ToString(), + "; calculatedMaxDaysB4Next: ", + this.calculatedMaxDaysB4Next.ToString(), + "; " + }), this.myDebug); + } + this.calculatedQuantity = this.Props.spawnCount; + if (this.Props.ageWeightedQuantity) + { + float num3 = this.Props.olderBiggerQuantity ? num : (-num); + Warn("quantityAgeRatio: " + num3.ToString(), this.myDebug); + this.calculatedQuantity = (int)Math.Round((double)this.Props.spawnCount * (double)(1f + num3)); + if (this.Props.exponentialQuantity) + { + num3 = 1f - num; + if (num3 == 0f) + { + Warn(">ERROR< quantityAgeRatio is f* up : " + num3.ToString(), this.myDebug); + this.blockSpawn = true; + DestroyParentHediff(this.parent, this.myDebug); + return; + } + float num4 = this.Props.olderBiggerQuantity ? (1f / num3) : (num3 * num3); + bool flag = false; + bool flag2 = false; + if (num4 > (float)this.Props.exponentialRatioLimit) + { + num4 = (float)this.Props.exponentialRatioLimit; + flag = true; + } + this.calculatedQuantity = (int)Math.Round((double)this.Props.spawnCount * (double)num4); + if (this.calculatedQuantity < 1) + { + this.calculatedQuantity = 1; + flag2 = true; + } + Warn(string.Concat(new string[] + { + " exponentialQuantity: ", + this.Props.exponentialQuantity.ToString(), + "; expoFactor: ", + num4.ToString(), + "; gotLimited: ", + flag.ToString(), + "; gotAugmented: ", + flag2.ToString() + }), this.myDebug); + } + Warn("; Props.spawnCount: " + this.Props.spawnCount.ToString() + "; calculatedQuantity: " + this.calculatedQuantity.ToString(), this.myDebug); + } + } + + private void CheckCalculatedValues() + { + if (this.calculatedQuantity > this.errorSpawnCount) + { + Warn(string.Concat(new string[] + { + ">ERROR< calculatedQuantity is too high: ", + this.calculatedQuantity.ToString(), + "(>", + this.errorSpawnCount.ToString(), + "), check and adjust your hediff props" + }), this.myDebug); + this.blockSpawn = true; + DestroyParentHediff(this.parent, this.myDebug); + return; + } + if (this.calculatedMinDaysB4Next < this.errorMinDaysB4Next) + { + this.calculatedMinDaysB4Next = this.errorMinDaysB4Next; + } + if (this.calculatedMaxDaysB4Next < this.errorMinDaysB4Next) + { + this.calculatedMaxDaysB4Next = this.errorMinDaysB4Next; + } + } + + private void TraceCalculatedValues() + { + Warn("calculatedMinDaysB4Next:" + this.calculatedMinDaysB4Next.ToString(), this.myDebug); + Warn("calculatedMaxDaysB4Next:" + this.calculatedMaxDaysB4Next.ToString(), this.myDebug); + Warn("calculatedQuantity:" + this.calculatedQuantity.ToString(), this.myDebug); + } + + private void CheckProps() + { + if (this.Props.animalThing && this.Props.animalToSpawn == null) + { + Warn(this.parent.pawn.Label + " has a hediffcomp_spawner with animalflag but without animalToSpawn", true); + this.blockSpawn = true; + DestroyParentHediff(this.parent, this.myDebug); + return; + } + if (this.Props.minDaysB4Next <= 0f) + { + Warn(this.parent.pawn.Label + " has a hediffcomp_spawner with null/negative minDaysB4Next", true); + this.blockSpawn = true; + DestroyParentHediff(this.parent, this.myDebug); + return; + } + if (this.Props.maxDaysB4Next <= 0f) + { + Warn(this.parent.pawn.Label + " has a hediffcomp_spawner with null/negative maxDaysB4Next", true); + this.blockSpawn = true; + DestroyParentHediff(this.parent, this.myDebug); + return; + } + if (this.Props.maxDaysB4Next < this.Props.minDaysB4Next) + { + Warn(this.parent.pawn.Label + " has a hediffcomp_spawner with maxDaysB4Next < minDaysB4Next", true); + this.blockSpawn = true; + DestroyParentHediff(this.parent, this.myDebug); + return; + } + if (this.Props.spawnCount <= 0) + { + Warn(this.parent.pawn.Label + " has a hediffcomp_spawner with null/negative spawnCount", true); + this.blockSpawn = true; + DestroyParentHediff(this.parent, this.myDebug); + return; + } + if (!this.Props.animalThing && this.Props.thingToSpawn == null) + { + Warn(this.parent.pawn.Label + " has a hediffcomp_spawner without thingToSpawn", true); + this.blockSpawn = true; + DestroyParentHediff(this.parent, this.myDebug); + return; + } + if (this.Props.ageWeightedQuantity && this.Props.exponentialQuantity && this.Props.exponentialRatioLimit > this.errorExponentialLimit) + { + Warn(string.Concat(new string[] + { + this.parent.pawn.Label, + " has a hediffcomp_spawner with exponentialRatioLimit>", + this.errorExponentialLimit.ToString(), + " this is not allowed. It will be set to ", + this.errorExponentialLimit.ToString() + }), true); + this.Props.exponentialRatioLimit = this.errorExponentialLimit; + } + } + + private bool CheckShouldSpawn() + { + this.ticksUntilSpawn--; + if (this.ticksUntilSpawn <= 0) + { + if (this.TryDoSpawn()) + { + return true; + } + Warn("Did not spawn, reseting countdown", this.myDebug); + ResetCountdown(); + } + return false; + } + + public bool TryDoSpawn() + { + Pawn pawn = this.parent.pawn; + + if (this.Props.animalThing) + { + // 动物生成逻辑保持不变 + if (this.Props.spawnMaxAdjacent > 0 && pawn.Map.mapPawns.AllPawns.Where(delegate(Pawn mP) + { + ThingDef defToCompare = this.Props.animalThing ? this.Props.animalToSpawn?.race : this.Props.thingToSpawn; + if (defToCompare?.race == null) + { + return false; + } + return mP.def == defToCompare && mP.Position.InHorDistOf(pawn.Position, (float)this.Props.spawnMaxAdjacent); + }).Count() >= this.Props.spawnMaxAdjacent) + { + return false; + } + + if (this.Props.animalToSpawn == null) + { + return false; + } + + Faction faction = this.Props.factionOfPlayerAnimal ? Faction.OfPlayer : null; + int i = 0; + while (i < this.calculatedQuantity) + { + IntVec3 intVec; + if (!this.TryFindSpawnCell(out intVec)) + { + return false; + } + Pawn pawn2 = PawnGenerator.GeneratePawn(this.Props.animalToSpawn, faction); + if (pawn2 == null) + { + return false; + } + GenSpawn.Spawn(pawn2, intVec, pawn.Map, WipeMode.Vanish); + pawn2.SetFaction(faction, null); + FilthMaker.TryMakeFilth(intVec, pawn.Map, ThingDefOf.Filth_AmnioticFluid, pawn.LabelIndefinite(), 5, FilthSourceFlags.None); + if (!this.Props.spawnForbidden) + { + pawn2.playerSettings.Master = pawn; + pawn2.training.Train(TrainableDefOf.Obedience, pawn, true); + } + i++; + continue; + } + if (PawnUtility.ShouldSendNotificationAbout(pawn) || PawnUtility.ShouldSendNotificationAbout(pawn)) + { + Messages.Message(this.Props.spawnVerb.Translate(pawn.Named("PAWN")), pawn, MessageTypeDefOf.PositiveEvent, true); + } + return true; + } + else + { + // 重新设计物品生成逻辑:按优先级顺序尝试 + bool success = TrySpawnItemWithPriority(pawn); + + // === 新增:如果配置了销毁随机部位,在成功生成物品后执行 === + if (success && this.Props.destroyRandomBodyPart) + { + DestroyRandomBodyPart(pawn); + } + + return success; + } + } + + // 新增:销毁随机身体部位(参考 CompAbilityEffect_DestroyOwnBodyPart) + private void DestroyRandomBodyPart(Pawn pawn) + { + try + { + if (pawn == null || pawn.Dead) + { + Warn($"Cannot destroy body part for null or dead pawn", this.myDebug); + return; + } + + // 获取所有可以销毁的身体部位 + List possibleParts = GetDestroyableBodyParts(pawn); + + if (possibleParts.Count == 0) + { + Warn($"No destroyable body parts found for {pawn.LabelShort}", this.myDebug); + return; + } + + // 随机选择一个部位 + BodyPartRecord partToDestroy = possibleParts.RandomElement(); + + if (partToDestroy == null) + { + Warn($"Selected null body part for {pawn.LabelShort}", this.myDebug); + return; + } + + // 记录部位名称 + string partName = partToDestroy.def?.label ?? "未知部位"; + + // 添加缺失部位hediff + pawn.health.AddHediff(HediffDefOf.MissingBodyPart, partToDestroy); + + Warn($"Destroyed {partName} on {pawn.LabelShort}", this.myDebug); + } + catch (Exception ex) + { + Log.Error($"Error destroying random body part for {pawn?.LabelShort}: {ex.Message}"); + } + } + + // 新增:获取可以销毁的身体部位列表 + private List GetDestroyableBodyParts(Pawn pawn) + { + List destroyableParts = new List(); + + if (pawn?.health?.hediffSet == null) + return destroyableParts; + + // 获取所有身体部位 + List allParts = pawn.RaceProps.body.AllParts; + + foreach (BodyPartRecord part in allParts) + { + // 排除核心部位(避免死亡) + if (IsCriticalBodyPart(part)) + continue; + + // 排除已经缺失的部位 + if (pawn.health.hediffSet.PartIsMissing(part)) + continue; + + // 排除已经有严重伤害的部位(可选) + if (pawn.health.hediffSet.PartOrAnyAncestorHasDirectlyAddedParts(part)) + continue; + + destroyableParts.Add(part); + } + + return destroyableParts; + } + + // 新增:检查是否是关键身体部位 + private bool IsCriticalBodyPart(BodyPartRecord part) + { + if (part == null) + return false; + + // 根据标签判断是否为关键部位 + if (part.def.tags != null) + { + // 这些标签通常表示关键部位 + if (part.def.tags.Contains(BodyPartTagDefOf.ConsciousnessSource) || // 意识源(大脑) + part.def.tags.Contains(BodyPartTagDefOf.BloodPumpingSource) || // 血液泵源(心脏) + part.def.tags.Contains(BodyPartTagDefOf.BreathingSource) || // 呼吸源(肺) + part.def.tags.Contains(BodyPartTagDefOf.MetabolismSource) // 代谢源(肝脏) + ) + { + return true; + } + } + + // 根据深度判断(深度较深的通常是内部器官) + if (part.depth == BodyPartDepth.Inside && part.parent == null) + return true; + + return false; + } + + // 新增:按优先级顺序尝试生成物品 + private bool TrySpawnItemWithPriority(Pawn pawn) + { + Thing thing = ThingMaker.MakeThing(this.Props.thingToSpawn, null); + if (thing == null) + { + Warn("Failed to create thing: " + this.Props.thingToSpawn?.defName, this.myDebug); + return false; + } + thing.stackCount = this.calculatedQuantity; + + // 记录原始物品用于校验 + ThingDef originalThingDef = thing.def; + int originalStackCount = thing.stackCount; + + // 按优先级顺序尝试生成 + bool success = false; + string spawnMethod = ""; + + // 优先级1: 尝试在pawn附近空地生成 + if (!success) + { + success = TrySpawnAtNearbyEmptyCell(pawn, thing, ref spawnMethod); + } + + // 优先级2: 尝试在pawn脚底生成 + if (!success) + { + success = TrySpawnAtPawnPosition(pawn, thing, ref spawnMethod); + } + + // 优先级3: 尝试放入pawn物品栏 + if (!success) + { + success = TrySpawnInInventory(pawn, thing, ref spawnMethod); + } + + // 生成后校验 + if (success) + { + bool verified = VerifySpawnSuccess(pawn, originalThingDef, originalStackCount, spawnMethod); + + // 如果校验失败且不是在物品栏中生成的,尝试在物品栏中重新生成 + if (!verified && spawnMethod != "inventory") + { + Warn($"Spawn verification failed for {spawnMethod}, attempting inventory fallback", this.myDebug); + + // 重新创建物品 + Thing fallbackThing = ThingMaker.MakeThing(originalThingDef, null); + fallbackThing.stackCount = originalStackCount; + + success = TrySpawnInInventory(pawn, fallbackThing, ref spawnMethod); + + if (success) + { + verified = VerifySpawnSuccess(pawn, originalThingDef, originalStackCount, "inventory_fallback"); + if (!verified) + { + Warn("Inventory fallback also failed verification", this.myDebug); + success = false; + } + } + } + + if (success && verified) + { + if (PawnUtility.ShouldSendNotificationAbout(pawn)) + { + Messages.Message(this.Props.spawnVerb.Translate(pawn.Named("PAWN"), thing.Named("THING")), + spawnMethod == "inventory" || spawnMethod == "inventory_fallback" ? pawn : thing, + MessageTypeDefOf.PositiveEvent, true); + } + Warn($"Successfully spawned {originalStackCount}x {originalThingDef.defName} via {spawnMethod}", this.myDebug); + return true; + } + } + + Warn($"Failed to spawn {originalStackCount}x {originalThingDef.defName} after all attempts", this.myDebug); + return false; + } + + // 新增:尝试在附近空地生成 + private bool TrySpawnAtNearbyEmptyCell(Pawn pawn, Thing thing, ref string spawnMethod) + { + IntVec3 spawnCell; + if (TryFindSpawnCell(out spawnCell)) + { + if (this.Props.spawnForbidden) + { + thing.SetForbidden(true, true); + } + + if (GenPlace.TryPlaceThing(thing, spawnCell, pawn.Map, ThingPlaceMode.Direct, null, null, default(Rot4))) + { + spawnMethod = "nearby_cell"; + return true; + } + } + return false; + } + + // 新增:尝试在pawn位置生成 + private bool TrySpawnAtPawnPosition(Pawn pawn, Thing thing, ref string spawnMethod) + { + IntVec3 pawnPosition = pawn.Position; + if (pawnPosition.IsValid && pawnPosition.Walkable(pawn.Map)) + { + if (this.Props.spawnForbidden) + { + thing.SetForbidden(true, true); + } + + if (GenPlace.TryPlaceThing(thing, pawnPosition, pawn.Map, ThingPlaceMode.Direct, null, null, default(Rot4))) + { + spawnMethod = "pawn_position"; + return true; + } + } + return false; + } + + // 新增:尝试在物品栏生成 + private bool TrySpawnInInventory(Pawn pawn, Thing thing, ref string spawnMethod) + { + if (pawn.inventory == null) + { + Warn($"Pawn {pawn.Label} does not have an inventory", this.myDebug); + return false; + } + + if (pawn.inventory.innerContainer.TryAdd(thing)) + { + spawnMethod = "inventory"; + return true; + } + return false; + } + + // 新增:生成后校验 + private bool VerifySpawnSuccess(Pawn pawn, ThingDef thingDef, int expectedCount, string spawnMethod) + { + bool verificationSuccess = false; + + switch (spawnMethod) + { + case "nearby_cell": + case "pawn_position": + case "inventory_fallback": + // 检查地图上是否有生成的物品 + verificationSuccess = pawn.Map.listerThings.ThingsOfDef(thingDef) + .Any(t => t.stackCount >= expectedCount && t.Position.InHorDistOf(pawn.Position, 2f)); + break; + + case "inventory": + // 检查物品栏中是否有生成的物品 + verificationSuccess = pawn.inventory.innerContainer.Any(t => t.def == thingDef && t.stackCount >= expectedCount); + break; + } + + if (!verificationSuccess) + { + Warn($"Spawn verification failed for {thingDef.defName} via {spawnMethod}", this.myDebug); + } + else + { + Warn($"Spawn verification successful for {thingDef.defName} via {spawnMethod}", this.myDebug); + } + + return verificationSuccess; + } + + private bool TryFindSpawnCell(out IntVec3 result) + { + result = IntVec3.Invalid; + bool result2; + if (this.pawn == null) + { + result2 = false; + } + else + { + Map map = this.pawn.Map; + if (map == null) + { + result2 = false; + } + else + { + // 修改这里:将半径从5减少到2,让生成位置更靠近pawn + int searchRadius = 2; + + // 首先尝试在pawn的相邻单元格生成(半径为1) + result = CellFinder.RandomClosewalkCellNear(this.pawn.Position, map, 1, null); + + // 如果相邻单元格找不到合适位置,再尝试稍远一点(半径为2) + if (!result.IsValid) + { + result = CellFinder.RandomClosewalkCellNear(this.pawn.Position, map, searchRadius, null); + } + + // 如果还是找不到,尝试pawn当前位置(作为最后手段) + if (!result.IsValid && this.pawn.Position.IsValid && this.pawn.Position.Walkable(map)) + { + result = this.pawn.Position; + } + + result2 = result.IsValid; + } + } + return result2; + } + + private void ResetCountdown() + { + this.ticksUntilSpawn = (int)(this.RandomDays2wait() * 60000f); + this.initialTicksUntilSpawn = this.ticksUntilSpawn; + } + + private float RandomDays2wait() + { + return Rand.Range(this.calculatedMinDaysB4Next, this.calculatedMaxDaysB4Next); + } + + private float RandomGraceDays() + { + return Rand.Range(this.Props.graceDays / 2f, this.Props.graceDays); + } + + public override string CompTipStringExtra + { + get + { + if (!this.myDebug) + { + return null; + } + string text = "ticksUntilSpawn: " + this.ticksUntilSpawn.ToString() + "\n"; + string text2 = text; + text = string.Concat(new string[] + { + text2, + "initialTicksUntilSpawn: ", + this.initialTicksUntilSpawn.ToString(), + "\n" + }); + text2 = text; + text = string.Concat(new string[] + { + text2, + "graceTicks: ", + this.graceTicks.ToString(), + "\n" + }); + text2 = text; + text = string.Concat(new string[] + { + text2, + "hunger resets: ", + this.hungerReset.ToString(), + "\n" + }); + text2 = text; + text = string.Concat(new string[] + { + text2, + "health resets: ", + this.healthReset.ToString(), + "\n" + }); + text2 = text; + text = string.Concat(new string[] + { + text2, + "calculatedMinDaysB4Next: ", + this.calculatedMinDaysB4Next.ToString(), + "\n" + }); + text2 = text; + text = string.Concat(new string[] + { + text2, + "calculatedMaxDaysB4Next: ", + this.calculatedMaxDaysB4Next.ToString(), + "\n" + }); + text2 = text; + text = string.Concat(new string[] + { + text2, + "calculatedQuantity: ", + this.calculatedQuantity.ToString(), + "\n" + }); + return text + "blockSpawn: " + this.blockSpawn.ToString(); + } + } + + // === 整合的 Tools 方法 === + + private void DestroyParentHediff(Hediff parentHediff, bool debug = false) + { + if (parentHediff.pawn != null && parentHediff.def.defName != null && debug) + { + Warn(parentHediff.pawn.Label + "'s Hediff: " + parentHediff.def.defName + " says goodbye.", debug); + } + parentHediff.Severity = 0f; + } + + private float GetPawnAgeOverlifeExpectancyRatio(Pawn pawn, bool debug = false) + { + float result = 1f; + if (pawn == null) + { + if (debug) + { + Warn("GetPawnAgeOverlifeExpectancyRatio pawn NOT OK", debug); + } + return result; + } + result = pawn.ageTracker.AgeBiologicalYearsFloat / pawn.RaceProps.lifeExpectancy; + if (debug) + { + Warn(string.Concat(new string[] + { + pawn.Label, + " Age: ", + pawn.ageTracker.AgeBiologicalYearsFloat.ToString(), + "; lifeExpectancy: ", + pawn.RaceProps.lifeExpectancy.ToString(), + "; ratio:", + result.ToString() + }), debug); + } + return result; + } + + private bool OkPawn(Pawn pawn) + { + return pawn != null && pawn.Map != null; + } + + private void Warn(string warning, bool debug = false) + { + if (debug) + { + Log.Message($"[HediffComp_Spawner] {warning}"); + } + } + + private int ticksUntilSpawn; + private int initialTicksUntilSpawn; + private int hungerReset; + private int healthReset; + private int graceTicks; + private Pawn pawn; + private float calculatedMaxDaysB4Next = 2f; + private float calculatedMinDaysB4Next = 1f; + private int calculatedQuantity = 1; + private bool blockSpawn; + private bool myDebug; + private readonly float errorMinDaysB4Next = 0.001f; + private readonly int errorExponentialLimit = 20; + private readonly int errorSpawnCount = 750; + } +} \ No newline at end of file diff --git a/Source/ArachnaeSwarm/Hediffs/MoharHediffs/HediffCompProperties_Spawner.cs b/Source/ArachnaeSwarm/Hediffs/MoharHediffs/HediffCompProperties_Spawner.cs deleted file mode 100644 index 89d45d9..0000000 --- a/Source/ArachnaeSwarm/Hediffs/MoharHediffs/HediffCompProperties_Spawner.cs +++ /dev/null @@ -1,118 +0,0 @@ -using System; -using Verse; - -namespace ArachnaeSwarm.MoharHediffs -{ - public class HediffCompProperties_Spawner : HediffCompProperties - { - public HediffCompProperties_Spawner() - { - this.compClass = typeof(HediffComp_Spawner); - } - - /// - /// 要生成的物品的ThingDef。如果animalThing为false,则使用此项。 - /// - public ThingDef thingToSpawn; - - /// - /// 每次生成的基础物品数量。 - /// - public int spawnCount = 1; - - /// - /// 如果为true,则生成一个Pawn(动物)。如果为false,则生成一个Thing。 - /// - public bool animalThing; - - /// - /// 要生成的动物的PawnKindDef。如果animalThing为true,则使用此项。 - /// - public PawnKindDef animalToSpawn; - - /// - /// 如果为true,生成的动物将属于玩家派系。 - /// - public bool factionOfPlayerAnimal; - - /// - /// 下一次生成事件发生前的最少天数。 - /// - public float minDaysB4Next = 1f; - - /// - /// 下一次生成事件发生前的最大天数。 - /// - public float maxDaysB4Next = 2f; - - /// - /// 生成后进入宽限期(延迟下一次生成)的几率(0.0到1.0)。 - /// - public float randomGrace; - - /// - /// 如果触发,宽限期的持续时间(天)。 - /// - public float graceDays = 0.5f; - - /// - /// 附近允许的相同Pawn的最大数量。如果超过该数量,则暂停生成。-1为禁用。 - /// - public int spawnMaxAdjacent = -1; - - /// - /// 如果为true,生成的物品将被禁用。 - /// - public bool spawnForbidden; - - /// - /// 如果为true,当宿主Pawn饥饿时,生成将暂停。 - /// - public bool hungerRelative; - - /// - /// 如果为true,当宿主Pawn受伤时,生成将暂停。 - /// - public bool healthRelative; - - /// - /// 如果为true,生成数量将根据宿主的年龄进行调整。 - /// - public bool ageWeightedQuantity; - - /// - /// 如果为true,生成周期(两次生成之间的时间)将根据宿主的年龄进行调整。 - /// - public bool ageWeightedPeriod; - - /// - /// 如果为true且ageWeightedPeriod为true,则随着宿主年龄增长,生成周期变短。如果为false,则变长。 - /// - public bool olderSmallerPeriod; - - /// - /// 如果为true且ageWeightedQuantity为true,则随着宿主年龄增长,生成数量变多。如果为false,则变少。 - /// - public bool olderBiggerQuantity; - - /// - /// 如果为true且ageWeightedQuantity为true,则随年龄增长的数量缩放将是指数性的而非线性的。 - /// - public bool exponentialQuantity; - - /// - /// 指数级数量缩放的最大乘数,以防止出现荒谬的数字。 - /// - public int exponentialRatioLimit = 15; - - /// - /// 生成时显示的消息的翻译键(例如,“{PAWN}下了一个蛋。”)。 - /// - public string spawnVerb = "delivery"; - - /// - /// 如果为true,则为此组件启用详细的调试日志记录。 - /// - public bool debug; - } -} \ No newline at end of file diff --git a/Source/ArachnaeSwarm/Hediffs/MoharHediffs/HediffComp_Spawner.cs b/Source/ArachnaeSwarm/Hediffs/MoharHediffs/HediffComp_Spawner.cs deleted file mode 100644 index a545cfe..0000000 --- a/Source/ArachnaeSwarm/Hediffs/MoharHediffs/HediffComp_Spawner.cs +++ /dev/null @@ -1,739 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using RimWorld; -using RimWorld.Planet; -using Verse; - -namespace ArachnaeSwarm.MoharHediffs -{ - public class HediffComp_Spawner : HediffComp - { - public HediffCompProperties_Spawner Props - { - get - { - return (HediffCompProperties_Spawner)this.props; - } - } - - public override void CompExposeData() - { - Scribe_Values.Look(ref this.ticksUntilSpawn, "ticksUntilSpawn", 0, false); - Scribe_Values.Look(ref this.initialTicksUntilSpawn, "initialTicksUntilSpawn", 0, false); - Scribe_Values.Look(ref this.calculatedMinDaysB4Next, "calculatedMinDaysB4Next", 0f, false); - Scribe_Values.Look(ref this.calculatedMaxDaysB4Next, "calculatedMaxDaysB4Next", 0f, false); - Scribe_Values.Look(ref this.calculatedQuantity, "calculatedQuantity", 0, false); - Scribe_Values.Look(ref this.graceTicks, "graceTicks", 0, false); - } - - public override void CompPostMake() - { - this.myDebug = this.Props.debug; - Tools.Warn(string.Concat(new string[] - { - ">>> ", - this.parent.pawn.Label, - " - ", - this.parent.def.defName, - " - CompPostMake start" - }), this.myDebug); - this.TraceProps(); - this.CheckProps(); - this.CalculateValues(); - this.CheckCalculatedValues(); - this.TraceCalculatedValues(); - if (this.initialTicksUntilSpawn == 0) - { - Tools.Warn("Reseting countdown bc initialTicksUntilSpawn == 0 (comppostmake)", this.myDebug); - this.ResetCountdown(); - } - } - - public override void CompPostTick(ref float severityAdjustment) - { - this.pawn = this.parent.pawn; - if (!Tools.OkPawn(this.pawn)) - { - return; - } - if (this.blockSpawn) - { - return; - } - if (this.graceTicks > 0) - { - this.graceTicks--; - return; - } - if (this.Props.hungerRelative && this.pawn.IsHungry(this.myDebug)) - { - int num = (int)(this.RandomGraceDays() * 60000f); - this.hungerReset++; - this.graceTicks = num; - return; - } - if (this.Props.healthRelative && this.pawn.IsInjured(this.myDebug)) - { - int num2 = (int)(this.RandomGraceDays() * 60000f); - this.healthReset++; - this.graceTicks = num2; - return; - } - this.hungerReset = (this.healthReset = 0); - if (this.CheckShouldSpawn()) - { - Tools.Warn("Reseting countdown bc spawned thing", this.myDebug); - this.CalculateValues(); - this.CheckCalculatedValues(); - this.ResetCountdown(); - if (Rand.Chance(this.Props.randomGrace)) - { - int num3 = (int)(this.RandomGraceDays() * 60000f); - this.graceTicks = num3; - } - } - } - - private void TraceProps() - { - Tools.Warn(string.Concat(new string[] - { - "Props => minDaysB4Next: ", - this.Props.minDaysB4Next.ToString(), - "; maxDaysB4Next: ", - this.Props.maxDaysB4Next.ToString(), - "; randomGrace: ", - this.Props.randomGrace.ToString(), - "; graceDays: ", - this.Props.graceDays.ToString(), - "; hungerRelative: ", - this.Props.hungerRelative.ToString(), - "; healthRelative: ", - this.Props.healthRelative.ToString(), - "; " - }), this.myDebug); - if (this.Props.animalThing) - { - Tools.Warn(string.Concat(new string[] - { - "animalThing: ", - this.Props.animalThing.ToString(), - "; animalName: ", - this.Props.animalToSpawn.defName, - "; factionOfPlayerAnimal: ", - this.Props.factionOfPlayerAnimal.ToString(), - "; " - }), this.myDebug); - } - if (this.Props.ageWeightedQuantity) - { - Tools.Warn(string.Concat(new string[] - { - "ageWeightedQuantity:", - this.Props.ageWeightedQuantity.ToString(), - "; olderBiggerQuantity:", - this.Props.olderBiggerQuantity.ToString(), - "; ", - this.myDebug.ToString() - }), false); - if (this.Props.exponentialQuantity) - { - Tools.Warn(string.Concat(new string[] - { - "exponentialQuantity:", - this.Props.exponentialQuantity.ToString(), - "; exponentialRatioLimit:", - this.Props.exponentialRatioLimit.ToString(), - "; " - }), this.myDebug); - } - } - Tools.Warn(string.Concat(new string[] - { - "ageWeightedPeriod:", - this.Props.ageWeightedPeriod.ToString(), - "; olderSmallerPeriod:", - this.Props.olderSmallerPeriod.ToString(), - "; ", - this.myDebug.ToString() - }), false); - } - - private void CalculateValues() - { - float num = Tools.GetPawnAgeOverlifeExpectancyRatio(this.parent.pawn, this.myDebug); - num = ((num > 1f) ? 1f : num); - this.calculatedMinDaysB4Next = this.Props.minDaysB4Next; - this.calculatedMaxDaysB4Next = this.Props.maxDaysB4Next; - if (this.Props.ageWeightedPeriod) - { - float num2 = this.Props.olderSmallerPeriod ? (-num) : num; - this.calculatedMinDaysB4Next = this.Props.minDaysB4Next * (1f + num2); - this.calculatedMaxDaysB4Next = this.Props.maxDaysB4Next * (1f + num2); - Tools.Warn(string.Concat(new string[] - { - " ageWeightedPeriod: ", - this.Props.ageWeightedPeriod.ToString(), - " ageRatio: ", - num.ToString(), - " minDaysB4Next: ", - this.Props.minDaysB4Next.ToString(), - " maxDaysB4Next: ", - this.Props.maxDaysB4Next.ToString(), - " daysAgeRatio: ", - num2.ToString(), - " calculatedMinDaysB4Next: ", - this.calculatedMinDaysB4Next.ToString(), - "; calculatedMaxDaysB4Next: ", - this.calculatedMaxDaysB4Next.ToString(), - "; " - }), this.myDebug); - } - this.calculatedQuantity = this.Props.spawnCount; - if (this.Props.ageWeightedQuantity) - { - float num3 = this.Props.olderBiggerQuantity ? num : (-num); - Tools.Warn("quantityAgeRatio: " + num3.ToString(), this.myDebug); - this.calculatedQuantity = (int)Math.Round((double)this.Props.spawnCount * (double)(1f + num3)); - if (this.Props.exponentialQuantity) - { - num3 = 1f - num; - if (num3 == 0f) - { - Tools.Warn(">ERROR< quantityAgeRatio is f* up : " + num3.ToString(), this.myDebug); - this.blockSpawn = true; - Tools.DestroyParentHediff(this.parent, this.myDebug); - return; - } - float num4 = this.Props.olderBiggerQuantity ? (1f / num3) : (num3 * num3); - bool flag = false; - bool flag2 = false; - if (num4 > (float)this.Props.exponentialRatioLimit) - { - num4 = (float)this.Props.exponentialRatioLimit; - flag = true; - } - this.calculatedQuantity = (int)Math.Round((double)this.Props.spawnCount * (double)num4); - if (this.calculatedQuantity < 1) - { - this.calculatedQuantity = 1; - flag2 = true; - } - Tools.Warn(string.Concat(new string[] - { - " exponentialQuantity: ", - this.Props.exponentialQuantity.ToString(), - "; expoFactor: ", - num4.ToString(), - "; gotLimited: ", - flag.ToString(), - "; gotAugmented: ", - flag2.ToString() - }), this.myDebug); - } - Tools.Warn("; Props.spawnCount: " + this.Props.spawnCount.ToString() + "; calculatedQuantity: " + this.calculatedQuantity.ToString(), this.myDebug); - } - } - - private void CheckCalculatedValues() - { - if (this.calculatedQuantity > this.errorSpawnCount) - { - Tools.Warn(string.Concat(new string[] - { - ">ERROR< calculatedQuantity is too high: ", - this.calculatedQuantity.ToString(), - "(>", - this.errorSpawnCount.ToString(), - "), check and adjust your hediff props" - }), this.myDebug); - this.blockSpawn = true; - Tools.DestroyParentHediff(this.parent, this.myDebug); - return; - } - if (this.calculatedMinDaysB4Next < this.errorMinDaysB4Next) - { - this.calculatedMinDaysB4Next = this.errorMinDaysB4Next; - } - if (this.calculatedMaxDaysB4Next < this.errorMinDaysB4Next) - { - this.calculatedMaxDaysB4Next = this.errorMinDaysB4Next; - } - } - - private void TraceCalculatedValues() - { - Tools.Warn("calculatedMinDaysB4Next:" + this.calculatedMinDaysB4Next.ToString(), this.myDebug); - Tools.Warn("calculatedMaxDaysB4Next:" + this.calculatedMaxDaysB4Next.ToString(), this.myDebug); - Tools.Warn("calculatedQuantity:" + this.calculatedQuantity.ToString(), this.myDebug); - } - - private void CheckProps() - { - if (this.Props.animalThing && this.Props.animalToSpawn == null) - { - Tools.Warn(this.parent.pawn.Label + " has a hediffcomp_spawner with animalflag but without animalToSpawn", true); - this.blockSpawn = true; - Tools.DestroyParentHediff(this.parent, this.myDebug); - return; - } - if (this.Props.minDaysB4Next <= 0f) - { - Tools.Warn(this.parent.pawn.Label + " has a hediffcomp_spawner with null/negative minDaysB4Next", true); - this.blockSpawn = true; - Tools.DestroyParentHediff(this.parent, this.myDebug); - return; - } - if (this.Props.maxDaysB4Next <= 0f) - { - Tools.Warn(this.parent.pawn.Label + " has a hediffcomp_spawner with null/negative maxDaysB4Next", true); - this.blockSpawn = true; - Tools.DestroyParentHediff(this.parent, this.myDebug); - return; - } - if (this.Props.maxDaysB4Next < this.Props.minDaysB4Next) - { - Tools.Warn(this.parent.pawn.Label + " has a hediffcomp_spawner with maxDaysB4Next < minDaysB4Next", true); - this.blockSpawn = true; - Tools.DestroyParentHediff(this.parent, this.myDebug); - return; - } - if (this.Props.spawnCount <= 0) - { - Tools.Warn(this.parent.pawn.Label + " has a hediffcomp_spawner with null/negative spawnCount", true); - this.blockSpawn = true; - Tools.DestroyParentHediff(this.parent, this.myDebug); - return; - } - if (!this.Props.animalThing && this.Props.thingToSpawn == null) - { - Tools.Warn(this.parent.pawn.Label + " has a hediffcomp_spawner without thingToSpawn", true); - this.blockSpawn = true; - Tools.DestroyParentHediff(this.parent, this.myDebug); - return; - } - if (this.Props.ageWeightedQuantity && this.Props.exponentialQuantity && this.Props.exponentialRatioLimit > this.errorExponentialLimit) - { - Tools.Warn(string.Concat(new string[] - { - this.parent.pawn.Label, - " has a hediffcomp_spawner with exponentialRatioLimit>", - this.errorExponentialLimit.ToString(), - " this is not allowed. It will be set to ", - this.errorExponentialLimit.ToString() - }), true); - this.Props.exponentialRatioLimit = this.errorExponentialLimit; - } - } - - private bool CheckShouldSpawn() - { - this.ticksUntilSpawn--; - if (this.ticksUntilSpawn <= 0) - { - if (this.TryDoSpawn()) - { - return true; - } - Tools.Warn("Did not spawn, reseting countdown", this.myDebug); - this.ResetCountdown(); - } - return false; - } - - private PawnKindDef MyPawnKindDefNamed(string myDefName) - { - return DefDatabase.GetNamed(myDefName, true); - } - - public bool TryDoSpawn() - { - Pawn pawn = this.parent.pawn; - - if (this.Props.animalThing) - { - // 动物生成逻辑保持不变 - if (this.Props.spawnMaxAdjacent > 0 && pawn.Map.mapPawns.AllPawns.Where(delegate(Pawn mP) - { - ThingDef defToCompare = this.Props.animalThing ? this.Props.animalToSpawn?.race : this.Props.thingToSpawn; - if (defToCompare?.race == null) - { - return false; - } - return mP.def == defToCompare && mP.Position.InHorDistOf(pawn.Position, (float)this.Props.spawnMaxAdjacent); - }).Count() >= this.Props.spawnMaxAdjacent) - { - return false; - } - - if (this.Props.animalToSpawn == null) - { - return false; - } - - Faction faction = this.Props.factionOfPlayerAnimal ? Faction.OfPlayer : null; - int i = 0; - while (i < this.calculatedQuantity) - { - IntVec3 intVec; - if (!this.TryFindSpawnCell(out intVec)) - { - return false; - } - Pawn pawn2 = PawnGenerator.GeneratePawn(this.Props.animalToSpawn, faction); - if (pawn2 == null) - { - return false; - } - GenSpawn.Spawn(pawn2, intVec, pawn.Map, WipeMode.Vanish); - pawn2.SetFaction(faction, null); - FilthMaker.TryMakeFilth(intVec, pawn.Map, ThingDefOf.Filth_AmnioticFluid, pawn.LabelIndefinite(), 5, FilthSourceFlags.None); - if (!this.Props.spawnForbidden) - { - pawn2.playerSettings.Master = pawn; - pawn2.training.Train(TrainableDefOf.Obedience, pawn, true); - } - i++; - continue; - } - if (PawnUtility.ShouldSendNotificationAbout(pawn) || PawnUtility.ShouldSendNotificationAbout(pawn)) - { - Messages.Message(this.Props.spawnVerb.Translate(pawn.Named("PAWN")), pawn, MessageTypeDefOf.PositiveEvent, true); - } - return true; - } - else - { - // 重新设计物品生成逻辑:按优先级顺序尝试 - return TrySpawnItemWithPriority(pawn); - } - } - - // 新增:按优先级顺序尝试生成物品 - private bool TrySpawnItemWithPriority(Pawn pawn) - { - Thing thing = ThingMaker.MakeThing(this.Props.thingToSpawn, null); - if (thing == null) - { - Tools.Warn("Failed to create thing: " + this.Props.thingToSpawn?.defName, this.myDebug); - return false; - } - thing.stackCount = this.calculatedQuantity; - - // 记录原始物品用于校验 - ThingDef originalThingDef = thing.def; - int originalStackCount = thing.stackCount; - - // 按优先级顺序尝试生成 - bool success = false; - string spawnMethod = ""; - - // 优先级1: 尝试在pawn附近空地生成 - if (!success) - { - success = TrySpawnAtNearbyEmptyCell(pawn, thing, ref spawnMethod); - } - - // 优先级2: 尝试在pawn脚底生成 - if (!success) - { - success = TrySpawnAtPawnPosition(pawn, thing, ref spawnMethod); - } - - // 优先级3: 尝试放入pawn物品栏 - if (!success) - { - success = TrySpawnInInventory(pawn, thing, ref spawnMethod); - } - - // 生成后校验 - if (success) - { - bool verified = VerifySpawnSuccess(pawn, originalThingDef, originalStackCount, spawnMethod); - - // 如果校验失败且不是在物品栏中生成的,尝试在物品栏中重新生成 - if (!verified && spawnMethod != "inventory") - { - Tools.Warn($"Spawn verification failed for {spawnMethod}, attempting inventory fallback", this.myDebug); - - // 重新创建物品 - Thing fallbackThing = ThingMaker.MakeThing(originalThingDef, null); - fallbackThing.stackCount = originalStackCount; - - success = TrySpawnInInventory(pawn, fallbackThing, ref spawnMethod); - - if (success) - { - verified = VerifySpawnSuccess(pawn, originalThingDef, originalStackCount, "inventory_fallback"); - if (!verified) - { - Tools.Warn("Inventory fallback also failed verification", this.myDebug); - success = false; - } - } - } - - if (success && verified) - { - if (PawnUtility.ShouldSendNotificationAbout(pawn)) - { - Messages.Message(this.Props.spawnVerb.Translate(pawn.Named("PAWN"), thing.Named("THING")), - spawnMethod == "inventory" || spawnMethod == "inventory_fallback" ? pawn : thing, - MessageTypeDefOf.PositiveEvent, true); - } - Tools.Warn($"Successfully spawned {originalStackCount}x {originalThingDef.defName} via {spawnMethod}", this.myDebug); - return true; - } - } - - Tools.Warn($"Failed to spawn {originalStackCount}x {originalThingDef.defName} after all attempts", this.myDebug); - return false; - } - - // 新增:尝试在附近空地生成 - private bool TrySpawnAtNearbyEmptyCell(Pawn pawn, Thing thing, ref string spawnMethod) - { - IntVec3 spawnCell; - if (TryFindSpawnCell(out spawnCell)) - { - if (this.Props.spawnForbidden) - { - thing.SetForbidden(true, true); - } - - if (GenPlace.TryPlaceThing(thing, spawnCell, pawn.Map, ThingPlaceMode.Direct, null, null, default(Rot4))) - { - spawnMethod = "nearby_cell"; - return true; - } - } - return false; - } - - // 新增:尝试在pawn位置生成 - private bool TrySpawnAtPawnPosition(Pawn pawn, Thing thing, ref string spawnMethod) - { - IntVec3 pawnPosition = pawn.Position; - if (pawnPosition.IsValid && pawnPosition.Walkable(pawn.Map)) - { - if (this.Props.spawnForbidden) - { - thing.SetForbidden(true, true); - } - - if (GenPlace.TryPlaceThing(thing, pawnPosition, pawn.Map, ThingPlaceMode.Direct, null, null, default(Rot4))) - { - spawnMethod = "pawn_position"; - return true; - } - } - return false; - } - - // 新增:尝试在物品栏生成 - private bool TrySpawnInInventory(Pawn pawn, Thing thing, ref string spawnMethod) - { - if (pawn.inventory == null) - { - Tools.Warn($"Pawn {pawn.Label} does not have an inventory", this.myDebug); - return false; - } - - if (pawn.inventory.innerContainer.TryAdd(thing)) - { - spawnMethod = "inventory"; - return true; - } - return false; - } - - // 新增:生成后校验 - private bool VerifySpawnSuccess(Pawn pawn, ThingDef thingDef, int expectedCount, string spawnMethod) - { - bool verificationSuccess = false; - - switch (spawnMethod) - { - case "nearby_cell": - case "pawn_position": - case "inventory_fallback": - // 检查地图上是否有生成的物品 - verificationSuccess = pawn.Map.listerThings.ThingsOfDef(thingDef) - .Any(t => t.stackCount >= expectedCount && t.Position.InHorDistOf(pawn.Position, 2f)); - break; - - case "inventory": - // 检查物品栏中是否有生成的物品 - verificationSuccess = pawn.inventory.innerContainer.Any(t => t.def == thingDef && t.stackCount >= expectedCount); - break; - } - - if (!verificationSuccess) - { - Tools.Warn($"Spawn verification failed for {thingDef.defName} via {spawnMethod}", this.myDebug); - } - else - { - Tools.Warn($"Spawn verification successful for {thingDef.defName} via {spawnMethod}", this.myDebug); - } - - return verificationSuccess; - } - - private bool TryFindSpawnCell(out IntVec3 result) - { - result = IntVec3.Invalid; - bool result2; - if (this.pawn == null) - { - result2 = false; - } - else - { - Map map = this.pawn.Map; - if (map == null) - { - result2 = false; - } - else - { - // 修改这里:将半径从5减少到2,让生成位置更靠近pawn - int searchRadius = 2; - - // 首先尝试在pawn的相邻单元格生成(半径为1) - result = CellFinder.RandomClosewalkCellNear(this.pawn.Position, map, 1, null); - - // 如果相邻单元格找不到合适位置,再尝试稍远一点(半径为2) - if (!result.IsValid) - { - result = CellFinder.RandomClosewalkCellNear(this.pawn.Position, map, searchRadius, null); - } - - // 如果还是找不到,尝试pawn当前位置(作为最后手段) - if (!result.IsValid && this.pawn.Position.IsValid && this.pawn.Position.Walkable(map)) - { - result = this.pawn.Position; - } - - result2 = result.IsValid; - } - } - return result2; - } - - - private void ResetCountdown() - { - this.ticksUntilSpawn = (int)(this.RandomDays2wait() * 60000f); - this.initialTicksUntilSpawn = this.ticksUntilSpawn; - } - - private float RandomDays2wait() - { - return Rand.Range(this.calculatedMinDaysB4Next, this.calculatedMaxDaysB4Next); - } - - private float RandomGraceDays() - { - return Rand.Range(this.Props.graceDays / 2f, this.Props.graceDays); - } - - public override string CompTipStringExtra - { - get - { - if (!this.myDebug) - { - return null; - } - string text = "ticksUntilSpawn: " + this.ticksUntilSpawn.ToString() + "\n"; - string text2 = text; - text = string.Concat(new string[] - { - text2, - "initialTicksUntilSpawn: ", - this.initialTicksUntilSpawn.ToString(), - "\n" - }); - text2 = text; - text = string.Concat(new string[] - { - text2, - "graceTicks: ", - this.graceTicks.ToString(), - "\n" - }); - text2 = text; - text = string.Concat(new string[] - { - text2, - "hunger resets: ", - this.hungerReset.ToString(), - "\n" - }); - text2 = text; - text = string.Concat(new string[] - { - text2, - "health resets: ", - this.healthReset.ToString(), - "\n" - }); - text2 = text; - text = string.Concat(new string[] - { - text2, - "calculatedMinDaysB4Next: ", - this.calculatedMinDaysB4Next.ToString(), - "\n" - }); - text2 = text; - text = string.Concat(new string[] - { - text2, - "calculatedMaxDaysB4Next: ", - this.calculatedMaxDaysB4Next.ToString(), - "\n" - }); - text2 = text; - text = string.Concat(new string[] - { - text2, - "calculatedQuantity: ", - this.calculatedQuantity.ToString(), - "\n" - }); - return text + "blockSpawn: " + this.blockSpawn.ToString(); - } - } - - private int ticksUntilSpawn; - - private int initialTicksUntilSpawn; - - private int hungerReset; - - private int healthReset; - - private int graceTicks; - - private Pawn pawn; - - private float calculatedMaxDaysB4Next = 2f; - - private float calculatedMinDaysB4Next = 1f; - - private int calculatedQuantity = 1; - - private bool blockSpawn; - - private bool myDebug; - - private readonly float errorMinDaysB4Next = 0.001f; - - private readonly int errorExponentialLimit = 20; - - private readonly int errorSpawnCount = 750; - } -} diff --git a/Source/ArachnaeSwarm/Hediffs/MoharHediffs/Tools.cs b/Source/ArachnaeSwarm/Hediffs/MoharHediffs/Tools.cs deleted file mode 100644 index 21a78e7..0000000 --- a/Source/ArachnaeSwarm/Hediffs/MoharHediffs/Tools.cs +++ /dev/null @@ -1,106 +0,0 @@ -using System; -using System.Collections.Generic; -using RimWorld; -using UnityEngine; -using Verse; - -namespace ArachnaeSwarm.MoharHediffs -{ - public static class Tools - { - public static void DestroyParentHediff(Hediff parentHediff, bool debug = false) - { - if (parentHediff.pawn != null && parentHediff.def.defName != null && debug) - { - ArachnaeLog.Debug(parentHediff.pawn.Label + "'s Hediff: " + parentHediff.def.defName + " says goodbye."); - } - parentHediff.Severity = 0f; - } - - public static float GetPawnAgeOverlifeExpectancyRatio(Pawn pawn, bool debug = false) - { - float result = 1f; - if (pawn == null) - { - if (debug) - { - ArachnaeLog.Debug("GetPawnAgeOverlifeExpectancyRatio pawn NOT OK"); - } - return result; - } - result = pawn.ageTracker.AgeBiologicalYearsFloat / pawn.RaceProps.lifeExpectancy; - if (debug) - { - ArachnaeLog.Debug(string.Concat(new string[] - { - pawn.Label, - " Age: ", - pawn.ageTracker.AgeBiologicalYearsFloat.ToString(), - "; lifeExpectancy: ", - pawn.RaceProps.lifeExpectancy.ToString(), - "; ratio:", - result.ToString() - })); - } - return result; - } - - public static bool IsInjured(this Pawn pawn, bool debug = false) - { - if (pawn == null) - { - if (debug) - { - ArachnaeLog.Debug("pawn is null - wounded "); - } - return false; - } - float num = 0f; - List hediffs = pawn.health.hediffSet.hediffs; - for (int i = 0; i < hediffs.Count; i++) - { - if (hediffs[i] is Hediff_Injury && !hediffs[i].IsPermanent()) - { - num += hediffs[i].Severity; - } - } - if (debug && num > 0f) - { - ArachnaeLog.Debug(pawn.Label + " is wounded "); - } - return num > 0f; - } - - public static bool IsHungry(this Pawn pawn, bool debug = false) - { - if (pawn == null) - { - if (debug) - { - ArachnaeLog.Debug("pawn is null - IsHungry "); - } - return false; - } - bool flag = pawn.needs.food != null && pawn.needs.food.CurCategory == HungerCategory.Starving; - if (debug && flag) - { - ArachnaeLog.Debug(pawn.Label + " is hungry "); - } - return flag; - } - - public static bool OkPawn(Pawn pawn) - { - return pawn != null && pawn.Map != null; - } - - public static void Warn(string warning, bool debug = false) - { - if (debug) - { - ArachnaeLog.Debug(warning); - } - } - - } -} \ No newline at end of file