This commit is contained in:
Tourswen
2025-10-08 02:00:44 +08:00
parent 722a285827
commit ae226bc4f6
54 changed files with 3089 additions and 169 deletions

Binary file not shown.

View File

@@ -3815,4 +3815,386 @@
</parts>
</corePart>
</BodyDef>
<BodyDef>
<defName>ArachnaePraetorian_Body</defName>
<label>阿拉克涅禁卫种</label> <!-- EN: beetle-like with claw -->
<corePart>
<def>Torso</def>
<height>Middle</height>
<depth>Outside</depth>
<groups>
<li>Torso</li>
</groups>
<parts>
<li>
<def>Ribcage</def>
<coverage>0.036</coverage>
<depth>Inside</depth>
<groups>
<li>Torso</li>
</groups>
</li>
<li>
<def>Sternum</def>
<coverage>0.015</coverage>
<depth>Inside</depth>
<groups>
<li>Torso</li>
</groups>
</li>
<li>
<def>Pelvis</def>
<coverage>0.025</coverage>
<height>Bottom</height>
<depth>Inside</depth>
<groups>
<li>Torso</li>
</groups>
</li>
<li>
<def>Spine</def>
<coverage>0.025</coverage>
<depth>Inside</depth>
<groups>
<li>Torso</li>
</groups>
</li>
<li>
<def>Stomach</def>
<coverage>0.025</coverage>
<depth>Inside</depth>
<groups>
<li>Torso</li>
</groups>
</li>
<li>
<def>Heart</def>
<coverage>0.020</coverage>
<depth>Inside</depth>
<groups>
<li>Torso</li>
</groups>
</li>
<li>
<def>Lung</def>
<customLabel>左肺</customLabel>
<coverage>0.025</coverage>
<depth>Inside</depth>
<groups>
<li>Torso</li>
</groups>
</li>
<li>
<def>Lung</def>
<customLabel>右肺</customLabel>
<coverage>0.025</coverage>
<depth>Inside</depth>
<groups>
<li>Torso</li>
</groups>
</li>
<li>
<def>Kidney</def>
<customLabel>左肾</customLabel>
<coverage>0.017</coverage>
<depth>Inside</depth>
<groups>
<li>Torso</li>
</groups>
</li>
<li>
<def>Kidney</def>
<customLabel>右肾</customLabel>
<coverage>0.017</coverage>
<depth>Inside</depth>
<groups>
<li>Torso</li>
</groups>
</li>
<li>
<def>Liver</def>
<coverage>0.025</coverage>
<depth>Inside</depth>
<groups>
<li>Torso</li>
</groups>
</li>
<li>
<def>Neck</def>
<coverage>0.07</coverage>
<height>Top</height>
<groups>
<li>Neck</li>
</groups>
<parts>
<li>
<def>Head</def>
<coverage>0.80</coverage>
<groups>
<li>UpperHead</li>
<li>FullHead</li>
<li>HeadAttackTool</li>
</groups>
<parts>
<li>
<def>Skull</def>
<coverage>0.18</coverage>
<depth>Inside</depth>
<groups>
<li>UpperHead</li>
<li>Eyes</li>
<li>FullHead</li>
</groups>
<parts>
<li>
<def>Brain</def>
<coverage>0.8</coverage>
<groups>
<li>UpperHead</li>
<li>Eyes</li>
<li>FullHead</li>
</groups>
</li>
</parts>
</li>
<li>
<def>Eye</def>
<customLabel>left eye</customLabel>
<coverage>0.07</coverage>
<groups>
<li>FullHead</li>
<li>Eyes</li>
</groups>
<woundAnchorTag>LeftEye</woundAnchorTag>
<flipGraphic>true</flipGraphic>
<visibleHediffRots>
<li>South</li>
<li>West</li>
</visibleHediffRots>
</li>
<li>
<def>Eye</def>
<customLabel>right eye</customLabel>
<coverage>0.07</coverage>
<groups>
<li>FullHead</li>
<li>Eyes</li>
</groups>
<woundAnchorTag>RightEye</woundAnchorTag>
<visibleHediffRots>
<li>South</li>
<li>East</li>
</visibleHediffRots>
</li>
<li>
<def>Ear</def>
<customLabel>左耳</customLabel>
<coverage>0.07</coverage>
<flipGraphic>true</flipGraphic>
<groups>
<li>UpperHead</li>
<li>FullHead</li>
</groups>
</li>
<li>
<def>Ear</def>
<customLabel>右耳</customLabel>
<coverage>0.07</coverage>
<groups>
<li>UpperHead</li>
<li>FullHead</li>
</groups>
</li>
<li>
<def>Nose</def>
<coverage>0.10</coverage>
<groups>
<li>FullHead</li>
</groups>
</li>
<li>
<def>Jaw</def>
<coverage>0.15</coverage>
<groups>
<li>Teeth</li>
<li>FullHead</li>
<li>Mouth</li>
</groups>
<parts>
<li>
<def>Tongue</def>
<coverage>0.001</coverage>
<depth>Inside</depth>
<groups>
<li>FullHead</li>
</groups>
</li>
</parts>
</li>
</parts>
</li>
</parts>
</li>
<li>
<def>Shoulder</def>
<customLabel>左肩</customLabel>
<coverage>0.12</coverage>
<woundAnchorTag>LeftShoulder</woundAnchorTag>
<groups>
<li>Shoulders</li>
</groups>
<parts>
<li>
<def>Arm</def>
<customLabel>左辅肢</customLabel>
<coverage>0.77</coverage>
<groups>
<li>Arms</li>
</groups>
<parts>
<li>
<def>Hand</def>
<customLabel>左手</customLabel>
<coverage>0.14</coverage>
<height>Bottom</height>
<groups>
<li>Hands</li>
</groups>
</li>
</parts>
</li>
</parts>
</li>
<li>
<def>Shoulder</def>
<customLabel>右肩</customLabel>
<coverage>0.12</coverage>
<woundAnchorTag>RightShoulder</woundAnchorTag>
<groups>
<li>Shoulders</li>
</groups>
<parts>
<li>
<def>Arm</def>
<customLabel>右辅肢</customLabel>
<coverage>0.77</coverage>
<groups>
<li>Arms</li>
</groups>
<parts>
<li>
<def>Hand</def>
<customLabel>右手</customLabel>
<coverage>0.14</coverage>
<height>Bottom</height>
<groups>
<li>Hands</li>
</groups>
</li>
</parts>
</li>
</parts>
</li>
<li>
<def>Waist</def>
<coverage>0</coverage>
<height>Bottom</height>
<groups>
<li>Waist</li>
</groups>
</li>
<li>
<def>Leg</def>
<customLabel>左腿</customLabel>
<coverage>0.14</coverage>
<height>Bottom</height>
<groups>
<li>Legs</li>
</groups>
<woundAnchorTag>LeftLeg</woundAnchorTag>
<flipGraphic>true</flipGraphic>
<parts>
<li>
<def>Foot</def>
<customLabel>左足</customLabel>
<coverage>0.1</coverage>
<flipGraphic>true</flipGraphic>
<groups>
<li>Feet</li>
</groups>
</li>
</parts>
</li>
<li>
<def>Leg</def>
<customLabel>右腿</customLabel>
<coverage>0.14</coverage>
<height>Bottom</height>
<groups>
<li>Legs</li>
</groups>
<woundAnchorTag>RightLeg</woundAnchorTag>
<parts>
<li>
<def>Foot</def>
<customLabel>右足</customLabel>
<coverage>0.1</coverage>
<groups>
<li>Feet</li>
</groups>
</li>
</parts>
</li>
<!-- 尾部组织群 -->
<li>
<def>ARA_Tail</def>
<height>Bottom</height>
<coverage>0.05</coverage>
<depth>Outside</depth>
<groups>
<li>Torso</li>
</groups>
<parts>
<!-- 甲片,防御作用 -->
<li>
<def>ARA_Chitin_Shell</def>
<customLabel>尾部护甲</customLabel>
<coverage>0.01</coverage>
<depth>Outside</depth>
<groups>
<li>Torso</li>
</groups>
</li>
<!-- 蜕荚 -->
<li>
<def>ARA_Pouch</def>
<coverage>0.001</coverage>
<depth>Inside</depth>
<groups>
<li>ARA_Pouchs</li>
</groups>
</li>
</parts>
</li>
<li>
<def>ARA_Chitin_Shell</def>
<customLabel>左胯护甲</customLabel>
<coverage>0.05</coverage>
<depth>Outside</depth>
<groups>
<li>Torso</li>
</groups>
</li>
<li>
<def>ARA_Chitin_Shell</def>
<customLabel>右胯护甲</customLabel>
<coverage>0.05</coverage>
<depth>Outside</depth>
<groups>
<li>Torso</li>
</groups>
</li>
</parts>
</corePart>
</BodyDef>
</Defs>

View File

@@ -0,0 +1,196 @@
<?xml version="1.0" encoding="utf-8" ?>
<Defs>
<ThingDef>
<defName>ARA_Guide_Pheromone</defName>
<label>信息素残留(新手引导)</label>
<description>来源未知的信息素,使用后可以短暂链接蜂巢意志,获取运营虫巢的引导。</description>
<thingClass>ThingWithComps</thingClass>
<category>Item</category>
<drawerType>MapMeshOnly</drawerType>
<techLevel>Animal</techLevel>
<useHitPoints>false</useHitPoints>
<pathCost>14</pathCost>
<selectable>true</selectable>
<altitudeLayer>Item</altitudeLayer>
<tickerType>Never</tickerType>
<alwaysHaulable>true</alwaysHaulable>
<resourceReadoutPriority>Middle</resourceReadoutPriority>
<thingCategories>
<li>Items</li>
</thingCategories>
<statBases>
<Beauty>0</Beauty>
<Mass>0.01</Mass>
<MarketValue>0</MarketValue>
<WorkToMake>15000</WorkToMake>
</statBases>
<graphicData>
<texPath>ArachnaeSwarm/Item/ARA_Guide_Pheromone</texPath>
<graphicClass>Graphic_Single</graphicClass>
</graphicData>
<recipeMaker>
<unfinishedThingDef>UnfinishedHealthItemProsthetic</unfinishedThingDef>
<useIngredientsForColor>false</useIngredientsForColor>
<workSpeedStat>GeneralLaborSpeed</workSpeedStat>
<workSkill>Crafting</workSkill>
<effectWorking>Smith</effectWorking>
<soundWorking>Recipe_Machining</soundWorking>
<displayPriority>650</displayPriority>
</recipeMaker>
<tradeability>None</tradeability>
<comps>
<li Class="CompProperties_Forbiddable"/>
<li Class="CompProperties_Usable">
<useJob>UseNeurotrainer</useJob>
<useLabel>获取教程</useLabel>
<showUseGizmo>true</showUseGizmo>
</li>
<li Class="CompProperties_UseEffectGiveQuest">
<quest>ARA_QuestEvent_1</quest>
</li>
</comps>
</ThingDef>
<QuestScriptDef>
<defName>ARA_QuestEvent_1</defName>
<label>阿拉克涅新手教学</label>
<description>信息素中包含了一些预先写下的信息。</description>
<root Class="QuestNode_Sequence">
<nodes>
<li Class="QuestNode_ResolveQuestName">
<rules>
<rulesStrings>
<li>questName->信息素残留(新手引导)</li>
</rulesStrings>
</rules>
</li>
<li Class="QuestNode_ResolveQuestDescription">
<rules>
<rulesStrings>
<li>questDescription->信息素中包含了一些预先写下的信息。</li>
</rulesStrings>
</rules>
</li>
<li Class="ArachnaeSwarm.QuestNode_Root_EventLetter">
<letterLabel>信息素残留(新手引导)</letterLabel>
<letterTitle>信息素残留(新手引导)</letterTitle>
<letterText>信息素中包含了一些预先写下的信息。</letterText>
<options>
<li>
<label>阅览消息</label>
<optionEffects>
<li>
<effects>
<li Class="ArachnaeSwarm.Effect_OpenCustomUI">
<defName>ARA_Event_Guide_1</defName>
</li>
</effects>
</li>
</optionEffects>
</li>
<li>
<label>无视</label>
</li>
</options>
</li>
</nodes>
</root>
</QuestScriptDef>
<ArachnaeSwarm.EventDef>
<defName>ARA_Event_Guide_1</defName>
<label><![CDATA[Ciallo(∠・ω< )⌒☆]]></label>
<portraitPath>ArachnaeSwarm/Events/Portraits/ARA_Fighter_1</portraitPath>
<characterName>蜂巢意志</characterName>
<description>
<li><![CDATA[<b>信息素的残留还很浓厚,你可以在有限的范围内问出自己的问题</b>。]]></li>
</description>
<immediateEffects>
<li>
</li>
</immediateEffects>
<options>
<li>
<label>你是谁···我们是谁?</label>
<optionEffects>
<li>
<effects>
<li Class="WulaFallenEmpire.Effect_OpenCustomUI">
<defName>ARA_Event_Guide_10</defName>
</li>
<li Class="ArachnaeSwarm.Effect_CloseDialog" />
</effects>
</li>
</optionEffects>
</li>
<li>
<label>我们的蜂巢刚刚建立,我们需要帮助</label>
<optionEffects>
<li>
<effects>
<li Class="WulaFallenEmpire.Effect_OpenCustomUI">
<defName>ARA_Event_Guide_100</defName>
</li>
<li Class="ArachnaeSwarm.Effect_CloseDialog" />
</effects>
</li>
</optionEffects>
</li>
<li>
<label>驱散信息素</label>
<optionEffects>
<li>
<effects>
<li Class="ArachnaeSwarm.Effect_CloseDialog" />
</effects>
</li>
</optionEffects>
</li>
</options>
</ArachnaeSwarm.EventDef>
<ArachnaeSwarm.EventDef>
<defName>ARA_Event_Guide_100</defName>
<label>我们的蜂巢刚刚建立,我们需要帮助</label>
<portraitPath>ArachnaeSwarm/Events/Portraits/ARA_Fighter_1</portraitPath>
<characterName>蜂巢意志</characterName>
<description>
<li><![CDATA[<b>虚影正在等待你的进一步问题。</b>。]]></li>
</description>
<options>
<li>
<label>我们要怎么</label>
<optionEffects>
<li>
<effects>
<li Class="WulaFallenEmpire.Effect_OpenCustomUI">
<defName>ARA_Event_Guide_10</defName>
</li>
<li Class="ArachnaeSwarm.Effect_CloseDialog" />
</effects>
</li>
</optionEffects>
</li>
<li>
<label>我们的蜂巢刚刚建立,我们需要帮助</label>
<optionEffects>
<li>
<effects>
<li Class="WulaFallenEmpire.Effect_OpenCustomUI">
<defName>ARA_Event_Guide_100</defName>
</li>
<li Class="ArachnaeSwarm.Effect_CloseDialog" />
</effects>
</li>
</optionEffects>
</li>
<li>
<label>驱散信息素</label>
<optionEffects>
<li>
<effects>
<li Class="ArachnaeSwarm.Effect_CloseDialog" />
</effects>
</li>
</optionEffects>
</li>
</options>
</ArachnaeSwarm.EventDef>
</Defs>

View File

@@ -833,7 +833,7 @@
<HediffDef>
<defName>ARA_Smokepop_Production_Bacterium</defName>
<label>亚种-育菌种</label>
<description>这只阿拉克涅迷雾种已经获得拔耀,前部甲壳脱落且无法再喷射信息素,转而换取了生产活化钜菌的能力。一只育菌种每天产出3份活化钜菌。</description>
<description>这只阿拉克涅迷雾种已经获得拔耀,前部甲壳脱落且无法再喷射信息素,转而换取了生产活化钜菌的能力。一只育菌种每天产出5份活化钜菌。</description>
<descriptionHyperlinks>
<ThingDef>ARA_Activated_Bacterium</ThingDef>
</descriptionHyperlinks>

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8" ?>
<Defs>
<ArachnaeSwarm.EventUIConfigDef>
<defName>ARA_EventUIConfig</defName>
<!-- General Style -->
<labelFont>Small</labelFont>
<drawBorders>false</drawBorders>
<showDefName>false</showDefName>
<showLabel>true</showLabel>
<defaultBackgroundImagePath></defaultBackgroundImagePath>
<!-- Virtual Layout Dimensions -->
<lihuiSize>(500, 800)</lihuiSize>
<nameSize>(650, 130)</nameSize>
<textSize>(650, 350)</textSize>
<optionsWidth>750</optionsWidth>
<!-- Virtual Layout Offsets -->
<textNameOffset>0</textNameOffset>
<optionsTextOffset>0</optionsTextOffset>
<defaultWindowSize>(750, 600)</defaultWindowSize>
<!-- New Layout Dimensions -->
<newLayoutNameSize>(200, 50)</newLayoutNameSize>
<newLayoutLihuiSize>(600, 200)</newLayoutLihuiSize>
<newLayoutTextSize>(600, 200)</newLayoutTextSize>
<newLayoutOptionsWidth>600</newLayoutOptionsWidth>
<newLayoutPadding>20</newLayoutPadding>
<newLayoutTextNameOffset>20</newLayoutTextNameOffset>
<newLayoutOptionsTextOffset>20</newLayoutOptionsTextOffset>
</ArachnaeSwarm.EventUIConfigDef>
</Defs>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8" ?>
<Defs>
<LetterDef>
<defName>ARA_EventChoiceLetter</defName>
<letterClass>ArachnaeSwarm.Letter_EventChoice</letterClass>
<arriveSound>LetterArrive_Good</arriveSound>
<color>(120, 150, 255)</color>
</LetterDef>
</Defs>

View File

@@ -138,7 +138,7 @@
<defName>ARA_Technology_2KYC</defName>
<label>节点KYC-2"飞行翼膜"</label>
<description>允许女皇种孵化新的虫族——空天种,敏捷而致命的精锐虫族,拥有以飞行姿态穿梭于战场的能力。</description>
<baseCost>1200</baseCost>
<baseCost>1600</baseCost>
<researchViewX>5.50</researchViewX>
<researchViewY>4.30</researchViewY>
<requiredResearchBuilding>ARA_ResearchBench</requiredResearchBuilding>
@@ -173,7 +173,7 @@
<ResearchProjectDef ParentName="ARA_techBase">
<defName>ARA_Technology_6KYC</defName>
<label>节点KYC-6"灵脑"</label>
<description>允许女皇种孵化新的虫族——织域种,一种寿命长且拥有强大灵能的特殊,不仅能协助虫群的科研工作,也能承担战场指挥官的责任</description>
<description>允许女皇种孵化新的虫族——织域种,一种寿命长且拥有强大灵能的特殊虫族,不仅能协助虫群的科研工作,也是一个强大的施法者</description>
<baseCost>800</baseCost>
<researchViewX>7.50</researchViewX>
<researchViewY>2.70</researchViewY>
@@ -182,6 +182,18 @@
<li>ARA_Technology_1WMT</li>
</prerequisites>
</ResearchProjectDef>
<ResearchProjectDef ParentName="ARA_techBase">
<defName>ARA_Technology_7KYC</defName>
<label>节点KYC-7"亲卫"</label>
<description>允许女皇种孵化新的虫族——禁卫种,一种寿命较其他虫族更长的精锐虫族,拥有优秀的远程作战能力和社交能力,同时也可以作为指挥官指挥虫群。</description>
<baseCost>2800</baseCost>
<researchViewX>10.00</researchViewX>
<researchViewY>3.80</researchViewY>
<requiredResearchBuilding>ARA_ResearchBench</requiredResearchBuilding>
<prerequisites>
<li>ARA_Technology_2WMT</li>
</prerequisites>
</ResearchProjectDef>
<!-- 织物发展 -->
<ResearchProjectDef ParentName="ARA_techBase">
<defName>ARA_Technology_4DIL</defName>
@@ -393,8 +405,8 @@
</ResearchProjectDef>
<ResearchProjectDef ParentName="ARA_techBase">
<defName>ARA_Technology_6GUT</defName>
<label>节点GUT-6"酿造"</label>
<description>允许虫族建造一种利用活体钜菌和生物质产出阿拉克涅虫蜜的建筑。</description>
<label>节点GUT-6"催化"</label>
<description>允许虫利用活体钜菌的催化能力,使虫群可以建造一系列产出建筑。</description>
<baseCost>1000</baseCost>
<researchViewX>6.50</researchViewX>
<researchViewY>0.90</researchViewY>
@@ -567,7 +579,7 @@
<defName>ARA_Technology_6LOD</defName>
<label>节点LOD-6"巢之主"</label>
<description>允许空天种进行定向进化,以牺牲高速和高空机动的能力换取向敌人投射大量天巢种的能力。</description>
<baseCost>2500</baseCost>
<baseCost>3500</baseCost>
<researchViewX>10.00</researchViewX>
<researchViewY>5.30</researchViewY>
<requiredResearchBuilding>ARA_ResearchBench</requiredResearchBuilding> <!-- ARA_MorphableResearchBench-->

View File

@@ -64,6 +64,11 @@
<thingDef>ARA_InteractiveEggSac_Start</thingDef>
<count>4</count>
</li>
<!-- <li Class="ScenPart_StartingThing_Defined">
<def>StartingThing_Defined</def>
<thingDef>ARA_Guide_Pheromone</thingDef>
<count>1</count>
</li> -->
<!-- 附近的物品 -->
<li Class="ScenPart_ScatterThingsNearPlayerStart">
<def>ScatterThingsNearPlayerStart</def>

View File

@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<Defs>
<StorytellerDef ParentName="BaseStoryteller">
<defName>ARA_Lyne</defName>
<label>织域种」蛉</label>
<description>是一只阿拉克涅织域种督虫,作为讲述者时行为和「经典」卡桑德拉相近,但是会加强大型袭击的难度。此外,选择蛉作为讲述者时,将会开启阿拉克涅虫巢的新手引导,以帮助新巢穴适应这个世界(引导暂未完成,敬请期待)</description>
<portraitLarge>ArachnaeSwarm/Storyteller/ARA_Lyne</portraitLarge>
<portraitTiny>ArachnaeSwarm/Storyteller/ARA_Lyne_TINY</portraitTiny>
<defName>ARA_Chrony</defName>
<label>战士种」克洛妮</label>
<description>克洛妮是一只阿拉克涅战士种督虫,虽然身材娇小但是侵略性极强,会在周期内发起更强大的大型袭击。此外,她会安排更少的访客和路过流浪者,并且不会在殖民地即将沦陷时派发黑衣人</description>
<portraitLarge>ArachnaeSwarm/Storyteller/ARA_Chrony</portraitLarge>
<portraitTiny>ArachnaeSwarm/Storyteller/ARA_Chrony_TINY</portraitTiny>
<listOrder>20</listOrder>
<comps>
<!-- Intro -->
@@ -86,7 +86,7 @@
</li>
<li Class="StorytellerCompProperties_FactionInteraction">
<incident>VisitorGroup</incident>
<minDaysPassed>3</minDaysPassed>
<minDaysPassed>6</minDaysPassed>
<baseIncidentsPerYear>4</baseIncidentsPerYear>
<minSpacingDays>5</minSpacingDays>
<allowedTargetTags>
@@ -95,7 +95,7 @@
</li>
<li Class="StorytellerCompProperties_FactionInteraction">
<incident>TravelerGroup</incident>
<minDaysPassed>1</minDaysPassed>
<minDaysPassed>3</minDaysPassed>
<baseIncidentsPerYear>6</baseIncidentsPerYear>
<minSpacingDays>1</minSpacingDays>
<allowedTargetTags>
@@ -170,8 +170,8 @@
<allowedTargetTags>
<li>World</li>
</allowedTargetTags>
<minDaysPassed>15</minDaysPassed>
<mtbDays>15</mtbDays>
<minDaysPassed>10</minDaysPassed>
<mtbDays>10</mtbDays>
</li>
<!-- Orbital trader -->
<li Class="StorytellerCompProperties_OnOffCycle">
@@ -180,11 +180,6 @@
<offDays>8</offDays>
<numIncidentsRange>1</numIncidentsRange>
</li>
<!-- Triggered -->
<li Class="StorytellerCompProperties_Triggered">
<incident>StrangerInBlackJoin</incident>
<delayTicks>180</delayTicks>
</li>
</comps>
</StorytellerDef>
</Defs>

View File

@@ -625,6 +625,7 @@
<recipes Inherit="False">
<li>ARA_Surgery_Install_Plasteel</li>
<li>ARA_Surgery_Install_Carapace_Shell</li>
<li>ARA_Surgery_Install_Huge_Stomach</li>
<li>ARA_Surgery_Install_Cycle_Suppression</li>
<li>ARA_Surgery_Install_Shell_Thorn</li>
@@ -1173,7 +1174,7 @@
<race>
<!-- 身体类型 -->
<body>ArachnaeFighter_Body</body>
<baseBodySize>0.8</baseBodySize>
<baseBodySize>0.7</baseBodySize>
<baseHealthScale>2</baseHealthScale>
</race>
@@ -1885,7 +1886,7 @@
<race>
<!-- 身体类型 -->
<body>ArachnaeFighter_Body</body>
<body>ArachnaePraetorian_Body</body>
<baseBodySize>2</baseBodySize>
<baseHealthScale>5</baseHealthScale>
</race>

View File

@@ -19,6 +19,9 @@
<shaderType>CutoutComplex</shaderType>
<drawSize>1</drawSize>
</graphicData>
<generateCommonality>0</generateCommonality>
<tradeability>None</tradeability>
<thingSetMakerTags Inherit="False"/>
<uiIconScale>1</uiIconScale>
<!-- <equippedAngleOffset>-65</equippedAngleOffset> -->
<techLevel>Animal</techLevel>
@@ -65,9 +68,6 @@
<researchPrerequisite Inherit="False" />
<unfinishedThingDef>UnfinishedWeapon</unfinishedThingDef>
</recipeMaker>
<thingSetMakerTags>
<li>RewardStandardQualitySuper</li>
</thingSetMakerTags>
<comps>
<li Class="ArachnaeSwarm.CompProperties_ExtraIncubationInfo">
<cocoonDefs>
@@ -134,9 +134,9 @@
<researchPrerequisite>ARA_Technology_2MEL</researchPrerequisite>
<unfinishedThingDef>UnfinishedWeapon</unfinishedThingDef>
</recipeMaker>
<thingSetMakerTags>
<li>RewardStandardQualitySuper</li>
</thingSetMakerTags>
<generateCommonality>0</generateCommonality>
<tradeability>None</tradeability>
<thingSetMakerTags Inherit="False"/>
<comps>
<li Class="ArachnaeSwarm.CompProperties_ExtraIncubationInfo">
<cocoonDefs>
@@ -212,9 +212,9 @@
<researchPrerequisite>ARA_Technology_3MEL</researchPrerequisite>
<unfinishedThingDef>UnfinishedWeapon</unfinishedThingDef>
</recipeMaker>
<thingSetMakerTags>
<li>RewardStandardQualitySuper</li>
</thingSetMakerTags>
<generateCommonality>0</generateCommonality>
<tradeability>None</tradeability>
<thingSetMakerTags Inherit="False"/>
<comps>
<li Class="ArachnaeSwarm.CompProperties_ExtraIncubationInfo">
<cocoonDefs>
@@ -316,9 +316,9 @@
<li>ARA_Armed_Organ_Ranged</li>
<li>ARA_Armed_Organ_T1</li>
</weaponTags>
<thingSetMakerTags>
<li>RewardStandardQualitySuper</li>
</thingSetMakerTags>
<generateCommonality>0</generateCommonality>
<tradeability>None</tradeability>
<thingSetMakerTags Inherit="False"/>
<tools Inherit="False">
<li>
<label>拳针枪拳击</label>
@@ -437,9 +437,9 @@
<li>ARA_Armed_Organ_Ranged</li>
<li>ARA_Armed_Organ_T2</li>
</weaponTags>
<thingSetMakerTags>
<li>RewardStandardQualitySuper</li>
</thingSetMakerTags>
<generateCommonality>0</generateCommonality>
<tradeability>None</tradeability>
<thingSetMakerTags Inherit="False"/>
<comps>
<li Class="ArachnaeSwarm.CompProperties_ExtraIncubationInfo">
<cocoonDefs>
@@ -522,9 +522,9 @@
<li>ARA_Armed_Organ_Ranged</li>
<li>ARA_Armed_Organ_T2</li>
</weaponTags>
<thingSetMakerTags>
<li>RewardStandardQualitySuper</li>
</thingSetMakerTags>
<generateCommonality>0</generateCommonality>
<tradeability>None</tradeability>
<thingSetMakerTags Inherit="False"/>
<comps>
<li Class="ArachnaeSwarm.CompProperties_ExtraIncubationInfo">
<cocoonDefs>
@@ -621,9 +621,9 @@
<li>ARA_Armed_Organ_Ranged</li>
<li>ARA_Armed_Organ_T2</li>
</weaponTags>
<thingSetMakerTags>
<li>RewardStandardQualitySuper</li>
</thingSetMakerTags>
<generateCommonality>0</generateCommonality>
<tradeability>None</tradeability>
<thingSetMakerTags Inherit="False"/>
<comps>
<li Class="ArachnaeSwarm.CompProperties_ExtraIncubationInfo">
<cocoonDefs>
@@ -734,9 +734,9 @@
<li>ARA_Armed_Organ_Ranged</li>
<li>ARA_Armed_Organ_T2</li>
</weaponTags>
<thingSetMakerTags>
<li>RewardStandardQualitySuper</li>
</thingSetMakerTags>
<generateCommonality>0</generateCommonality>
<tradeability>None</tradeability>
<thingSetMakerTags Inherit="False"/>
<comps>
<li Class="ArachnaeSwarm.CompProperties_ExtraIncubationInfo">
<cocoonDefs>
@@ -834,9 +834,9 @@
<li>ARA_Armed_Organ_Ranged</li>
<li>ARA_Armed_Organ_T1</li>
</weaponTags>
<thingSetMakerTags>
<li>RewardStandardQualitySuper</li>
</thingSetMakerTags>
<generateCommonality>0</generateCommonality>
<tradeability>None</tradeability>
<thingSetMakerTags Inherit="False"/>
<comps>
<li Class="ArachnaeSwarm.CompProperties_ExtraIncubationInfo">
<cocoonDefs>
@@ -947,9 +947,9 @@
<li>ARA_Armed_Organ_Ranged</li>
<li>ARA_Armed_Organ_T2</li>
</weaponTags>
<thingSetMakerTags>
<li>RewardStandardQualitySuper</li>
</thingSetMakerTags>
<generateCommonality>0</generateCommonality>
<tradeability>None</tradeability>
<thingSetMakerTags Inherit="False"/>
<comps>
<li Class="ArachnaeSwarm.CompProperties_ExtraIncubationInfo">
<cocoonDefs>
@@ -1094,9 +1094,9 @@
<li>ARA_Armed_Organ_Ranged</li>
<li>ARA_Armed_Organ_T2</li>
</weaponTags>
<thingSetMakerTags>
<li>RewardStandardQualitySuper</li>
</thingSetMakerTags>
<generateCommonality>0</generateCommonality>
<tradeability>None</tradeability>
<thingSetMakerTags Inherit="False"/>
<comps>
<li Class="ArachnaeSwarm.CompProperties_ExtraIncubationInfo">
<cocoonDefs>
@@ -1210,9 +1210,9 @@
<li>ARA_Armed_Organ_Ranged</li>
<li>ARA_Armed_Organ_T3</li>
</weaponTags>
<thingSetMakerTags>
<li>RewardStandardQualitySuper</li>
</thingSetMakerTags>
<generateCommonality>0</generateCommonality>
<tradeability>None</tradeability>
<thingSetMakerTags Inherit="False"/>
<comps>
<li Class="ArachnaeSwarm.CompProperties_ExtraIncubationInfo">
<cocoonDefs>
@@ -1342,9 +1342,9 @@
<li>ARA_Armed_Organ_Ranged</li>
<li>ARA_Armed_Organ_T3</li>
</weaponTags>
<thingSetMakerTags>
<li>RewardStandardQualitySuper</li>
</thingSetMakerTags>
<generateCommonality>0</generateCommonality>
<tradeability>None</tradeability>
<thingSetMakerTags Inherit="False"/>
<comps>
<li Class="ArachnaeSwarm.CompProperties_ExtraIncubationInfo">
<cocoonDefs>
@@ -1476,9 +1476,9 @@
<li>ARA_Armed_Organ_Ranged</li>
<li>ARA_Armed_Organ_T1</li>
</weaponTags>
<thingSetMakerTags>
<li>RewardStandardQualitySuper</li>
</thingSetMakerTags>
<generateCommonality>0</generateCommonality>
<tradeability>None</tradeability>
<thingSetMakerTags Inherit="False"/>
<comps>
<li Class="ArachnaeSwarm.CompProperties_ExtraIncubationInfo">
<cocoonDefs>
@@ -1605,9 +1605,9 @@
<researchPrerequisite Inherit="False" />
<unfinishedThingDef>UnfinishedWeapon</unfinishedThingDef>
</recipeMaker>
<thingSetMakerTags>
<li>RewardStandardQualitySuper</li>
</thingSetMakerTags>
<generateCommonality>0</generateCommonality>
<tradeability>None</tradeability>
<thingSetMakerTags Inherit="False"/>
</ThingDef>
<!-- 炮塔 -->

View File

@@ -588,6 +588,9 @@
<offset>(0,0,-0.1)</offset>
</shadowData>
</graphicData>
<researchPrerequisites>
<li>ARA_Base_Technology</li>
</researchPrerequisites>
<uiIconScale>0.8</uiIconScale>
<castEdgeShadows>false</castEdgeShadows>
<staticSunShadowHeight>0</staticSunShadowHeight>
@@ -630,6 +633,9 @@
<offset>(0,0,-0.1)</offset>
</shadowData>
</graphicData>
<researchPrerequisites>
<li>ARA_Base_Technology</li>
</researchPrerequisites>
<altitudeLayer>Building</altitudeLayer>
<statBases>
<MaxHitPoints>100</MaxHitPoints>
@@ -700,6 +706,9 @@
<costList>
<ARA_Carapace>10</ARA_Carapace>
</costList>
<researchPrerequisites>
<li>ARA_Base_Technology</li>
</researchPrerequisites>
<stuffCategories Inherit="False"/>
<costStuffCount>0</costStuffCount>
<holdsRoof>true</holdsRoof>
@@ -743,12 +752,18 @@
<passability>PassThroughOnly</passability>
<pathCost>100</pathCost>
<rotatable>false</rotatable>
<researchPrerequisites Inherit="False">
<li>ARA_Base_Technology</li>
</researchPrerequisites>
<statBases>
<Beauty>-5</Beauty>
<Flammability>0</Flammability>
<Mass>8</Mass>
<WorkToBuild>2500</WorkToBuild>
</statBases>
<costList>
<ARA_Carapace>50</ARA_Carapace>
</costList>
<size>(2,2)</size>
<inspectorTabs>
<li>ITab_Storage</li>

View File

@@ -219,7 +219,6 @@
<drawSize>(1.2,1.2)</drawSize>
</graphicData>
<researchPrerequisites>
<li>ARA_Technology_2KYC</li>
<li>ARA_Technology_4KYC</li>
</researchPrerequisites>
@@ -287,6 +286,7 @@
<researchPrerequisites>
<li>ARA_Technology_5KYC</li>
<li>ARA_Technology_6KYC</li>
<li>ARA_Technology_2KYC</li>
</researchPrerequisites>
<comps>
@@ -307,7 +307,7 @@
</li>
<li>
<pawnKind>ArachnaeNode_Race_NeuroSwarm</pawnKind>
<delayTicks>880000</delayTicks>
<delayTicks>840000</delayTicks>
<requiredResearch>ARA_Technology_6KYC</requiredResearch>
</li>
<li>
@@ -414,6 +414,9 @@
<color>(0.9, 0.9 ,0.5)</color>
<drawSize>(1.4,1.4)</drawSize>
</graphicData>
<researchPrerequisites>
<li>ARA_Technology_7KYC</li>
</researchPrerequisites>
<comps>
<li Class="CompProperties_Glower">
@@ -433,7 +436,7 @@
</li>
<li>
<pawnKind>ArachnaeNode_Race_NeuroSwarm</pawnKind>
<delayTicks>1440000</delayTicks>
<delayTicks>840000</delayTicks>
<requiredResearch>ARA_Technology_6KYC</requiredResearch>
</li>
<li>
@@ -444,7 +447,7 @@
<li>
<pawnKind>ArachnaeNode_Race_Praetorian</pawnKind>
<delayTicks>520000</delayTicks>
<requiredResearch>ARA_Technology_2KYC</requiredResearch>
<requiredResearch>ARA_Technology_7KYC</requiredResearch>
</li>
</spawnablePawns>
<whitelist>

View File

@@ -811,7 +811,8 @@
<comps>
<li Class="CompProperties_Flickable"/>
<li Class="ArachnaeSwarm.CompProperties_RefuelableNutrition_WithKey">
<!-- First fuel component: Steel -->
<li Class="ArachnaeSwarm.CompProperties_RefuelableWithKey">
<saveKeysPrefix>Steels</saveKeysPrefix>
<fuelLabel>钢铁</fuelLabel>
<fuelGizmoLabel>钢铁</fuelGizmoLabel>
@@ -821,46 +822,22 @@
</thingDefs>
</fuelFilter>
<fuelCapacity>50</fuelCapacity>
<fuelConsumptionRate>0</fuelConsumptionRate>
<fuelConsumptionRate>50</fuelConsumptionRate>
<consumeFuelOnlyWhenUsed>true</consumeFuelOnlyWhenUsed>
<targetFuelLevelConfigurable>true</targetFuelLevelConfigurable>
<showAllowAutoRefuelToggle>true</showAllowAutoRefuelToggle>
</li>
<li Class="CompProperties_Refuelable">
<fuelLabel>甲壳素</fuelLabel>
<fuelGizmoLabel>甲壳素</fuelGizmoLabel>
<fuelCapacity>100.0</fuelCapacity>
<fuelConsumptionRate>0</fuelConsumptionRate>
<consumeFuelOnlyWhenUsed>true</consumeFuelOnlyWhenUsed>
<autoRefuelPercent>0</autoRefuelPercent>
<initialAllowAutoRefuel>false</initialAllowAutoRefuel>
<showAllowAutoRefuelToggle>false</showAllowAutoRefuelToggle>
<fuelFilter>
<thingDefs>
<li>ARA_Carapace</li>
</thingDefs>
</fuelFilter>
<targetFuelLevelConfigurable>false</targetFuelLevelConfigurable>
<showAllowAutoRefuelToggle>true</showAllowAutoRefuelToggle>
<canEjectFuel>true</canEjectFuel>
</li>
<li Class="ArachnaeSwarm.CompProperties_NutritionToFuelConverter">
<checkInterval>200</checkInterval>
<nutritionCost>1</nutritionCost>
<workAmount>2000</workAmount>
<fuelAmount>1</fuelAmount>
</li>
<!-- 燃料满了自动弹出 -->
<li Class="ArachnaeSwarm.CompProperties_AutoEjector">
<!-- 精确指定要监控的燃料组件 -->
<targetComp>CompRefuelable</targetComp>
<!-- 在燃料达到99%时弹出 -->
<ejectAtPercent>0.99</ejectAtPercent>
<!-- Our simple spawner component -->
<li Class="ArachnaeSwarm.CompProperties_MultiFuelSpawner">
<spawnIntervalRange>60000~60000</spawnIntervalRange>
<products>
<li>
<thingDef>ARA_Carapace</thingDef>
<count>25</count>
</li>
</products>
<showMessageIfOwned>true</showMessageIfOwned>
</li>
<li Class="ArachnaeSwarm.CompProperties_DelayedTerrainSpawn">

View File

@@ -359,7 +359,8 @@
<Beauty>-20</Beauty>
</statBases>
<costList>
<ARA_Carapace>50</ARA_Carapace>
<ARA_Activated_Bacterium>70</ARA_Activated_Bacterium>
<ARA_Carapace>400</ARA_Carapace>
</costList>
<damageMultipliers>
<li>
@@ -512,7 +513,8 @@
<Beauty>-20</Beauty>
</statBases>
<costList>
<ARA_Carapace>50</ARA_Carapace>
<ARA_Activated_Bacterium>100</ARA_Activated_Bacterium>
<ARA_Carapace>300</ARA_Carapace>
</costList>
<damageMultipliers>
<li>
@@ -602,7 +604,7 @@
<texPath>ArachnaeSwarm/Building/ARA_CatastropheMissileSilo_Base</texPath>
<graphicClass>Graphic_Single</graphicClass>
<drawSize>(5,5)</drawSize>
<drawOffset>(0,0,0.5)</drawOffset>
<drawOffset>(0,0,0)</drawOffset>
<shadowData>
<volume>(2, 2, 2.5)</volume>
<offset>(0,0,-0.15)</offset>
@@ -683,13 +685,14 @@
<turretGunDef>CatastropheMissile_Weapon</turretGunDef>
<turretBurstCooldownTime>15.0</turretBurstCooldownTime>
<turretTopDrawSize>7.0</turretTopDrawSize>
<turretTopOffset>(0,1.2)</turretTopOffset>
<turretTopOffset>(0,0)</turretTopOffset>
<buildingTags>
<li>Artillery</li>
</buildingTags>
</building>
<costList>
<ARA_Carapace>50</ARA_Carapace>
<ARA_Activated_Bacterium>150</ARA_Activated_Bacterium>
<ARA_Carapace>500</ARA_Carapace>
</costList>
<designationCategory>ARA_Buildings</designationCategory>
<constructionSkillPrerequisite>12</constructionSkillPrerequisite>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 336 KiB

View File

@@ -1,29 +1,30 @@
{
"Version": 1,
"WorkspaceRootPath": "C:\\Steam\\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|c:\\steam\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\jobs\\jobdriver_supercarry\\floatmenuoptionprovider_supercarry.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:jobs\\jobdriver_supercarry\\floatmenuoptionprovider_supercarry.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\\eventsystem\\questnode_root_eventletter.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:eventsystem\\questnode_root_eventletter.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|c:\\steam\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\thing_comps\\ara_thingcomp_guardianpsyfield\\harmony_projectileinterceptor.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:thing_comps\\ara_thingcomp_guardianpsyfield\\harmony_projectileinterceptor.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\\eventsystem\\dialog_newlayoutdisplay.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:eventsystem\\dialog_newlayoutdisplay.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|c:\\steam\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\thing_comps\\ara_thingcomp_guardianpsyfield\\thingcomp_guardianpsyfield.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:thing_comps\\ara_thingcomp_guardianpsyfield\\thingcomp_guardianpsyfield.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\\eventsystem\\dialog_customdisplay.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:eventsystem\\dialog_customdisplay.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|C:\\Steam\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\building_comps\\ara_nutrientvat\\building_nutrientvat.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:building_comps\\ara_nutrientvat\\building_nutrientvat.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\\eventsystem\\effect.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:eventsystem\\effect.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|C:\\Steam\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\building_comps\\ara_nutrientnetwork\\compnutrientprovider.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:building_comps\\ara_nutrientnetwork\\compnutrientprovider.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\\eventsystem\\compopencustomui.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:eventsystem\\compopencustomui.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_NutrientNetwork\\CompLineDrawer.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\\eventsystem\\eventuiconfigdef.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:eventsystem\\eventuiconfigdef.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
}
],
"DocumentGroupContainers": [
@@ -42,75 +43,80 @@
{
"$type": "Document",
"DocumentIndex": 0,
"Title": "FloatMenuOptionProvider_SuperCarry.cs",
"DocumentMoniker": "C:\\Steam\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_SuperCarry\\FloatMenuOptionProvider_SuperCarry.cs",
"RelativeDocumentMoniker": "Jobs\\JobDriver_SuperCarry\\FloatMenuOptionProvider_SuperCarry.cs",
"ToolTip": "C:\\Steam\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_SuperCarry\\FloatMenuOptionProvider_SuperCarry.cs",
"RelativeToolTip": "Jobs\\JobDriver_SuperCarry\\FloatMenuOptionProvider_SuperCarry.cs",
"ViewState": "AQIAAAAAAAAAAAAAAADwvwAAAAAAAAAA",
"Title": "QuestNode_Root_EventLetter.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\EventSystem\\QuestNode_Root_EventLetter.cs",
"RelativeDocumentMoniker": "EventSystem\\QuestNode_Root_EventLetter.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\EventSystem\\QuestNode_Root_EventLetter.cs",
"RelativeToolTip": "EventSystem\\QuestNode_Root_EventLetter.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAABoAAAA1AAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-04T07:00:14.106Z",
"WhenOpened": "2025-10-07T09:56:01.195Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 1,
"Title": "Harmony_ProjectileInterceptor.cs",
"DocumentMoniker": "C:\\Steam\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Thing_Comps\\ARA_ThingComp_GuardianPsyField\\Harmony_ProjectileInterceptor.cs",
"RelativeDocumentMoniker": "Thing_Comps\\ARA_ThingComp_GuardianPsyField\\Harmony_ProjectileInterceptor.cs",
"ToolTip": "C:\\Steam\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Thing_Comps\\ARA_ThingComp_GuardianPsyField\\Harmony_ProjectileInterceptor.cs",
"RelativeToolTip": "Thing_Comps\\ARA_ThingComp_GuardianPsyField\\Harmony_ProjectileInterceptor.cs",
"ViewState": "AQIAAAAAAAAAAAAAAADwvwAAAAAAAAAA",
"Title": "Dialog_NewLayoutDisplay.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\EventSystem\\Dialog_NewLayoutDisplay.cs",
"RelativeDocumentMoniker": "EventSystem\\Dialog_NewLayoutDisplay.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\EventSystem\\Dialog_NewLayoutDisplay.cs",
"RelativeToolTip": "EventSystem\\Dialog_NewLayoutDisplay.cs",
"ViewState": "AgIAAAsAAAAAAAAAAAAuwDYAAAARAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-04T07:00:14.029Z",
"WhenOpened": "2025-10-07T09:39:47.15Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 2,
"Title": "ThingComp_GuardianPsyField.cs",
"DocumentMoniker": "C:\\Steam\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Thing_Comps\\ARA_ThingComp_GuardianPsyField\\ThingComp_GuardianPsyField.cs",
"RelativeDocumentMoniker": "Thing_Comps\\ARA_ThingComp_GuardianPsyField\\ThingComp_GuardianPsyField.cs",
"ToolTip": "C:\\Steam\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Thing_Comps\\ARA_ThingComp_GuardianPsyField\\ThingComp_GuardianPsyField.cs",
"RelativeToolTip": "Thing_Comps\\ARA_ThingComp_GuardianPsyField\\ThingComp_GuardianPsyField.cs",
"ViewState": "AQIAAAAAAAAAAAAAAADwvwAAAAAAAAAA",
"Title": "Dialog_CustomDisplay.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\EventSystem\\Dialog_CustomDisplay.cs",
"RelativeDocumentMoniker": "EventSystem\\Dialog_CustomDisplay.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\EventSystem\\Dialog_CustomDisplay.cs",
"RelativeToolTip": "EventSystem\\Dialog_CustomDisplay.cs",
"ViewState": "AgIAAAsAAAAAAAAAAAAuwBcAAABWAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-02T16:24:06.176Z",
"WhenOpened": "2025-10-07T09:39:43.149Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 3,
"Title": "Building_NutrientVat.cs",
"DocumentMoniker": "C:\\Steam\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_NutrientVat\\Building_NutrientVat.cs",
"RelativeDocumentMoniker": "Building_Comps\\ARA_NutrientVat\\Building_NutrientVat.cs",
"ToolTip": "C:\\Steam\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_NutrientVat\\Building_NutrientVat.cs",
"RelativeToolTip": "Building_Comps\\ARA_NutrientVat\\Building_NutrientVat.cs",
"ViewState": "AgIAAF8AAAAAAAAAAAAswJkBAAANAAAAAAAAAA==",
"Title": "Effect.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\EventSystem\\Effect.cs",
"RelativeDocumentMoniker": "EventSystem\\Effect.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\EventSystem\\Effect.cs",
"RelativeToolTip": "EventSystem\\Effect.cs",
"ViewState": "AgIAAAAAAAAAAAAAAADwvwwAAAA7AAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-02T15:30:05.897Z"
},
{
"$type": "Document",
"DocumentIndex": 4,
"Title": "CompNutrientProvider.cs",
"DocumentMoniker": "C:\\Steam\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_NutrientNetwork\\CompNutrientProvider.cs",
"RelativeDocumentMoniker": "Building_Comps\\ARA_NutrientNetwork\\CompNutrientProvider.cs",
"ToolTip": "C:\\Steam\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_NutrientNetwork\\CompNutrientProvider.cs",
"RelativeToolTip": "Building_Comps\\ARA_NutrientNetwork\\CompNutrientProvider.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAABkAAAAZAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-02T15:29:28.358Z"
"WhenOpened": "2025-10-07T09:35:57.661Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 5,
"Title": "CompLineDrawer.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_NutrientNetwork\\CompLineDrawer.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_NutrientNetwork\\CompLineDrawer.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAABkAAAAAAAAAAAAAAA==",
"Title": "EventUIConfigDef.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\EventSystem\\EventUIConfigDef.cs",
"RelativeDocumentMoniker": "EventSystem\\EventUIConfigDef.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\EventSystem\\EventUIConfigDef.cs",
"RelativeToolTip": "EventSystem\\EventUIConfigDef.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAABYAAAAqAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-02T15:11:03.083Z"
"WhenOpened": "2025-10-07T08:37:06.577Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 4,
"Title": "CompOpenCustomUI.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\EventSystem\\CompOpenCustomUI.cs",
"RelativeDocumentMoniker": "EventSystem\\CompOpenCustomUI.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\EventSystem\\CompOpenCustomUI.cs",
"RelativeToolTip": "EventSystem\\CompOpenCustomUI.cs",
"ViewState": "AgIAAAAAAAAAAAAAAADwvxQAAAAtAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-07T08:36:51.993Z",
"EditorCaption": ""
}
]
}

View File

@@ -98,6 +98,19 @@
<Compile Include="Abilities\ARA_ShowSpawnablePawnsList\CompAbilityEffect_AbilityShowSpawnablePawns.cs" />
<Compile Include="Abilities\ARA_ShowSpawnablePawnsList\CompProperties_AbilityShowSpawnablePawns.cs" />
<Compile Include="Buildings\Building_TurretGunHasSpeed.cs" />
<Compile Include="EventSystem\CompOpenCustomUI.cs" />
<Compile Include="EventSystem\Condition.cs" />
<Compile Include="EventSystem\DebugActions.cs" />
<Compile Include="EventSystem\DelayedActionManager.cs" />
<Compile Include="EventSystem\Dialog_CustomDisplay.cs" />
<Compile Include="EventSystem\Dialog_ManageEventVariables.cs" />
<Compile Include="EventSystem\Dialog_NewLayoutDisplay.cs" />
<Compile Include="EventSystem\Effect.cs" />
<Compile Include="EventSystem\EventDef.cs" />
<Compile Include="EventSystem\EventUIConfigDef.cs" />
<Compile Include="EventSystem\EventVariableManager.cs" />
<Compile Include="EventSystem\Letter_EventChoice.cs" />
<Compile Include="EventSystem\QuestNode_Root_EventLetter.cs" />
<Compile Include="Verbs\Verb_ShootWithOffset.cs" />
<Compile Include="Abilities\ARA_ShowTemperatureRange\CompAbilityEffect_AbilityShowTemperatureRange.cs" />
<Compile Include="Abilities\ARA_ShowTemperatureRange\CompProperties_AbilityShowTemperatureRange.cs" />

View File

@@ -0,0 +1,61 @@
using System; // Required for Activator
using RimWorld;
using Verse;
using System.Collections.Generic;
using Verse.AI;
namespace ArachnaeSwarm
{
public class CompProperties_OpenCustomUI : CompProperties
{
public string uiDefName;
public string label; // The text to display in the float menu
public string failReason; // Optional: Custom text to show if the pawn can't reach the building
public CompProperties_OpenCustomUI()
{
this.compClass = typeof(CompOpenCustomUI);
}
}
public class CompOpenCustomUI : ThingComp
{
public CompProperties_OpenCustomUI Props => (CompProperties_OpenCustomUI)this.props;
public override IEnumerable<FloatMenuOption> CompFloatMenuOptions(Pawn selPawn)
{
// Check if the pawn can interact with the building
if (!selPawn.CanReserveAndReach(this.parent, PathEndMode.InteractionCell, Danger.Deadly))
{
string reason = Props.failReason ?? "CannotUseNoPath".Translate();
yield return new FloatMenuOption(reason, null);
yield break;
}
// Check for power if the building has a power component
CompPowerTrader powerComp = this.parent.GetComp<CompPowerTrader>();
if (powerComp != null && !powerComp.PowerOn)
{
yield return new FloatMenuOption("CannotUseNoPower".Translate(), null);
yield break;
}
string label = Props.label ?? "Open Custom UI"; // Use default label if not provided
FloatMenuOption option = new FloatMenuOption(label, delegate()
{
EventDef uiDef = DefDatabase<EventDef>.GetNamed(Props.uiDefName, false);
if (uiDef != null)
{
Find.WindowStack.Add((Window)Activator.CreateInstance(uiDef.windowType, uiDef));
}
else
{
Log.Error($"[CompOpenCustomUI] Could not find EventDef named '{Props.uiDefName}'.");
}
});
yield return option;
}
}
}

View File

@@ -0,0 +1,240 @@
using Verse;
using RimWorld;
namespace ArachnaeSwarm
{
public abstract class Condition
{
public abstract bool IsMet(out string reason);
}
public class Condition_VariableEquals : Condition
{
public string name;
public string value;
public string valueVariableName;
public override bool IsMet(out string reason)
{
var eventVarManager = Find.World.GetComponent<EventVariableManager>();
if (!eventVarManager.HasVariable(name))
{
reason = $"Variable '{name}' not found.";
return false;
}
object variable = eventVarManager.GetVariable<object>(name);
string compareValueStr = value;
if (!string.IsNullOrEmpty(valueVariableName))
{
compareValueStr = eventVarManager.GetVariable<object>(valueVariableName)?.ToString();
if (compareValueStr == null)
{
reason = $"Comparison variable '{valueVariableName}' not set.";
return false;
}
}
bool met = false;
try
{
if (variable is int)
{
met = (int)variable == int.Parse(compareValueStr);
}
else if (variable is float)
{
met = (float)variable == float.Parse(compareValueStr);
}
else if (variable is bool)
{
met = (bool)variable == bool.Parse(compareValueStr);
}
else
{
met = variable?.ToString() == compareValueStr;
}
}
catch (System.Exception e)
{
Log.Warning($"[EventSystem] Condition_VariableEquals: Could not compare '{variable}' and '{compareValueStr}'. Error: {e.Message}");
reason = "Type mismatch or parsing error during comparison.";
return false;
}
if (!met)
{
reason = $"Requires {name} = {compareValueStr} (Current: {variable})";
}
else
{
reason = "";
}
return met;
}
}
public abstract class Condition_CompareVariable : Condition
{
public string name;
public float value;
public string valueVariableName;
protected abstract bool Compare(float var1, float var2);
protected abstract string GetOperatorString();
public override bool IsMet(out string reason)
{
var eventVarManager = Find.World.GetComponent<EventVariableManager>();
if (!eventVarManager.HasVariable(name))
{
Log.Message($"[EventSystem] {GetType().Name}: Variable '{name}' not found, defaulting to 0f.");
eventVarManager.SetVariable(name, 0f);
}
float variable = eventVarManager.GetVariable<float>(name);
float compareValue = value;
if (!string.IsNullOrEmpty(valueVariableName))
{
compareValue = eventVarManager.GetVariable<float>(valueVariableName, float.NaN);
if (float.IsNaN(compareValue))
{
reason = $"Comparison variable '{valueVariableName}' not set or not a number.";
Log.Warning($"[EventSystem] {GetType().Name} check for '{name}' failed: {reason}");
return false;
}
}
bool met = Compare(variable, compareValue);
Log.Message($"[EventSystem] {GetType().Name} check: Name='{name}', CurrentValue='{variable}', CompareValue='{compareValue}', Met={met}");
if (!met)
{
reason = $"Requires {name} {GetOperatorString()} {compareValue} (Current: {variable})";
}
else
{
reason = "";
}
return met;
}
}
public class Condition_VariableGreaterThan : Condition_CompareVariable
{
protected override bool Compare(float var1, float var2) => var1 > var2;
protected override string GetOperatorString() => ">";
}
public class Condition_VariableLessThan : Condition_CompareVariable
{
protected override bool Compare(float var1, float var2) => var1 < var2;
protected override string GetOperatorString() => "<";
}
public class Condition_VariableGreaterThanOrEqual : Condition_CompareVariable
{
protected override bool Compare(float var1, float var2) => var1 >= var2;
protected override string GetOperatorString() => ">=";
}
public class Condition_VariableLessThanOrEqual : Condition_CompareVariable
{
protected override bool Compare(float var1, float var2) => var1 <= var2;
protected override string GetOperatorString() => "<=";
}
public class Condition_VariableNotEqual : Condition
{
public string name;
public string value;
public string valueVariableName;
public override bool IsMet(out string reason)
{
var eventVarManager = Find.World.GetComponent<EventVariableManager>();
if (!eventVarManager.HasVariable(name))
{
reason = $"Variable '{name}' not found.";
return false;
}
object variable = eventVarManager.GetVariable<object>(name);
string compareValueStr = value;
if (!string.IsNullOrEmpty(valueVariableName))
{
compareValueStr = eventVarManager.GetVariable<object>(valueVariableName)?.ToString();
if (compareValueStr == null)
{
reason = $"Comparison variable '{valueVariableName}' not set.";
return false;
}
}
bool met = false;
try
{
if (variable is int)
{
met = (int)variable != int.Parse(compareValueStr);
}
else if (variable is float)
{
met = (float)variable != float.Parse(compareValueStr);
}
else if (variable is bool)
{
met = (bool)variable != bool.Parse(compareValueStr);
}
else
{
met = variable?.ToString() != compareValueStr;
}
}
catch (System.Exception e)
{
Log.Warning($"[EventSystem] Condition_VariableNotEqual: Could not compare '{variable}' and '{compareValueStr}'. Error: {e.Message}");
reason = "Type mismatch or parsing error during comparison.";
return false;
}
Log.Message($"[EventSystem] Condition_VariableNotEqual check: Name='{name}', Type='{variable?.GetType().Name ?? "null"}', CurrentValue='{variable}', CompareValue='{compareValueStr}', Met={met}");
if (!met)
{
reason = $"Requires {name} != {compareValueStr} (Current: {variable})";
}
else
{
reason = "";
}
return met;
}
}
public class Condition_FactionExists : Condition
{
public FactionDef factionDef;
public override bool IsMet(out string reason)
{
if (factionDef == null)
{
reason = "FactionDef not specified in Condition_FactionExists.";
return false;
}
bool exists = Find.FactionManager.FirstFactionOfDef(factionDef) != null;
if (!exists)
{
reason = $"Faction '{factionDef.label}' does not exist in the world.";
}
else
{
reason = "";
}
return exists;
}
}
}

View File

@@ -0,0 +1,65 @@
using System; // Required for Activator
using System.Collections.Generic;
using LudeonTK;
using Verse;
using RimWorld;
namespace ArachnaeSwarm
{
public static class WulaDebugActions
{
[DebugAction("Wula Fallen Empire", "Open Custom UI...", actionType = DebugActionType.Action, allowedGameStates = AllowedGameStates.Playing)]
private static void OpenCustomUI()
{
List<DebugMenuOption> list = new List<DebugMenuOption>();
foreach (EventDef localDef in DefDatabase<EventDef>.AllDefs)
{
EventDef currentDef = localDef;
list.Add(new DebugMenuOption(currentDef.defName, DebugMenuOptionMode.Action, delegate
{
if (currentDef.hiddenWindow)
{
if (!currentDef.dismissEffects.NullOrEmpty())
{
foreach (var conditionalEffect in currentDef.dismissEffects)
{
string reason;
bool conditionsMet = true;
if (!conditionalEffect.conditions.NullOrEmpty())
{
foreach (var condition in conditionalEffect.conditions)
{
if (!condition.IsMet(out reason))
{
conditionsMet = false;
break;
}
}
}
if (conditionsMet)
{
conditionalEffect.Execute(null);
}
}
}
}
else
{
Find.WindowStack.Add((Window)Activator.CreateInstance(currentDef.windowType, currentDef));
}
}));
}
Find.WindowStack.Add(new Dialog_DebugOptionListLister(list));
}
}
public static class WulaDebugActionsVariables
{
[DebugAction("Wula Fallen Empire", "Manage Event Variables", actionType = DebugActionType.Action, allowedGameStates = AllowedGameStates.PlayingOnMap)]
private static void ManageEventVariables()
{
Find.WindowStack.Add(new Dialog_ManageEventVariables());
}
}
}

View File

@@ -0,0 +1,95 @@
using System;
using System.Collections.Generic;
using RimWorld;
using RimWorld.Planet;
using Verse;
namespace ArachnaeSwarm
{
public class DelayedActionManager : WorldComponent
{
// Nested class must be public to be accessible for serialization
public class DelayedAction : IExposable
{
public int TicksRemaining;
public string eventDefName;
// Parameterless constructor for Scribe
public DelayedAction() { }
public DelayedAction(string eventDefName, int ticks)
{
this.eventDefName = eventDefName;
this.TicksRemaining = ticks;
}
public void ExposeData()
{
Scribe_Values.Look(ref TicksRemaining, "ticksRemaining", 0);
Scribe_Values.Look(ref eventDefName, "eventDefName");
}
}
private List<DelayedAction> actions = new List<DelayedAction>();
public DelayedActionManager(World world) : base(world)
{
}
public void AddAction(string eventDefName, int delayTicks)
{
if (string.IsNullOrEmpty(eventDefName) || delayTicks <= 0)
{
return;
}
actions.Add(new DelayedAction(eventDefName, delayTicks));
}
public override void WorldComponentTick()
{
base.WorldComponentTick();
for (int i = actions.Count - 1; i >= 0; i--)
{
DelayedAction delayedAction = actions[i];
delayedAction.TicksRemaining--;
if (delayedAction.TicksRemaining <= 0)
{
try
{
ExecuteAction(delayedAction.eventDefName);
}
catch (Exception ex)
{
Log.Error($"[WulaFallenEmpire] Error executing delayed action for event '{delayedAction.eventDefName}': {ex}");
}
actions.RemoveAt(i);
}
}
}
private void ExecuteAction(string defName)
{
EventDef nextDef = DefDatabase<EventDef>.GetNamed(defName, false);
if (nextDef != null)
{
// This logic is simplified from Effect_OpenCustomUI.OpenUI
// It assumes delayed actions always open a new dialog.
Find.WindowStack.Add((Window)Activator.CreateInstance(nextDef.windowType, nextDef));
}
else
{
Log.Error($"[WulaFallenEmpire] DelayedActionManager could not find EventDef named '{defName}'");
}
}
public override void ExposeData()
{
base.ExposeData();
Scribe_Collections.Look(ref actions, "delayedActions", LookMode.Deep);
if (actions == null)
{
actions = new List<DelayedAction>();
}
}
}
}

View File

@@ -0,0 +1,274 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using UnityEngine;
using Verse;
namespace ArachnaeSwarm
{
public class Dialog_CustomDisplay : Window
{
private EventDef def;
private Texture2D portrait;
private Texture2D background;
private string selectedDescription;
private static EventUIConfigDef config;
public static EventUIConfigDef Config
{
get
{
if (config == null)
{
config = DefDatabase<EventUIConfigDef>.GetNamed("ARA_EventUIConfig");
}
return config;
}
}
public override Vector2 InitialSize
{
get
{
if (def.windowSize != Vector2.zero)
{
return def.windowSize;
}
return Config.defaultWindowSize;
}
}
public Dialog_CustomDisplay(EventDef def)
{
this.def = def;
this.forcePause = true;
this.absorbInputAroundWindow = true;
this.doCloseX = true;
var eventVarManager = Find.World.GetComponent<EventVariableManager>();
if (!def.descriptions.NullOrEmpty())
{
if (def.descriptionMode == DescriptionSelectionMode.Random)
{
selectedDescription = def.descriptions.RandomElement();
}
else
{
string indexVarName = $"_seq_desc_index_{def.defName}";
int currentIndex = eventVarManager.GetVariable<int>(indexVarName, 0);
selectedDescription = def.descriptions[currentIndex];
int nextIndex = (currentIndex + 1) % def.descriptions.Count;
eventVarManager.SetVariable(indexVarName, nextIndex);
}
}
else
{
selectedDescription = "Error: No descriptions found in def.";
}
}
public override void PreOpen()
{
base.PreOpen();
if (!def.portraitPath.NullOrEmpty())
{
portrait = ContentFinder<Texture2D>.Get(def.portraitPath);
}
string bgPath = !def.backgroundImagePath.NullOrEmpty() ? def.backgroundImagePath : Config.defaultBackgroundImagePath;
if (!bgPath.NullOrEmpty())
{
background = ContentFinder<Texture2D>.Get(bgPath);
}
HandleAction(def.immediateEffects);
if (!def.conditionalDescriptions.NullOrEmpty())
{
foreach (var condDesc in def.conditionalDescriptions)
{
string reason;
if (AreConditionsMet(condDesc.conditions, out reason))
{
selectedDescription += "\n\n" + condDesc.text;
}
}
}
selectedDescription = FormatDescription(selectedDescription);
}
public override void DoWindowContents(Rect inRect)
{
if (background != null)
{
GUI.DrawTexture(inRect, background, ScaleMode.ScaleToFit);
}
if (Config.showDefName)
{
Text.Font = GameFont.Tiny;
GUI.color = Color.gray;
Widgets.Label(new Rect(5, 5, inRect.width - 10, 20f), def.defName);
GUI.color = Color.white;
}
if (Config.showLabel)
{
Text.Font = Config.labelFont;
Widgets.Label(new Rect(5, 20f, inRect.width - 10, 30f), def.label);
Text.Font = GameFont.Small;
}
float virtualWidth = Config.lihuiSize.x + Config.textSize.x;
float virtualHeight = Config.lihuiSize.y;
float scaleX = inRect.width / virtualWidth;
float scaleY = inRect.height / virtualHeight;
float scale = Mathf.Min(scaleX, scaleY) * 0.95f;
float scaledLihuiWidth = Config.lihuiSize.x * scale;
float scaledLihuiHeight = Config.lihuiSize.y * scale;
float scaledNameWidth = Config.nameSize.x * scale;
float scaledNameHeight = Config.nameSize.y * scale;
float scaledTextWidth = Config.textSize.x * scale;
float scaledTextHeight = Config.textSize.y * scale;
float scaledOptionsWidth = Config.optionsWidth * scale;
float totalContentWidth = scaledLihuiWidth + scaledTextWidth;
float totalContentHeight = scaledLihuiHeight;
float startX = (inRect.width - totalContentWidth) / 2;
float startY = (inRect.height - totalContentHeight) / 2;
Rect lihuiRect = new Rect(startX, startY, scaledLihuiWidth, scaledLihuiHeight);
if (portrait != null)
{
GUI.DrawTexture(lihuiRect, portrait, ScaleMode.ScaleToFit);
}
if (Config.drawBorders)
{
Widgets.DrawBox(lihuiRect);
}
Rect nameRect = new Rect(lihuiRect.xMax, lihuiRect.y, scaledNameWidth, scaledNameHeight);
if (Config.drawBorders)
{
Widgets.DrawBox(nameRect);
}
Text.Anchor = TextAnchor.MiddleCenter;
Text.Font = GameFont.Medium;
Widgets.Label(nameRect, def.characterName);
Text.Font = GameFont.Small;
Text.Anchor = TextAnchor.UpperLeft;
Rect textRect = new Rect(nameRect.x, nameRect.yMax + Config.textNameOffset * scale, scaledTextWidth, scaledTextHeight);
if (Config.drawBorders)
{
Widgets.DrawBox(textRect);
}
Rect textInnerRect = textRect.ContractedBy(10f * scale);
Widgets.Label(textInnerRect, selectedDescription);
Rect optionRect = new Rect(nameRect.x, textRect.yMax + Config.optionsTextOffset * scale, scaledOptionsWidth, lihuiRect.height - nameRect.height - textRect.height - (Config.textNameOffset + Config.optionsTextOffset) * scale);
Listing_Standard listing = new Listing_Standard();
listing.Begin(optionRect.ContractedBy(10f * scale));
if (def.options != null)
{
foreach (var option in def.options)
{
string reason;
bool conditionsMet = AreConditionsMet(option.conditions, out reason);
if (conditionsMet)
{
if (listing.ButtonText(option.label))
{
HandleAction(option.optionEffects);
}
}
else
{
if (option.hideWhenDisabled)
{
continue;
}
Rect rect = listing.GetRect(30f);
Widgets.ButtonText(rect, option.label, false, true, false);
TooltipHandler.TipRegion(rect, GetDisabledReason(option, reason));
}
}
}
listing.End();
}
private void HandleAction(List<ConditionalEffects> conditionalEffects)
{
if (conditionalEffects.NullOrEmpty())
{
return;
}
foreach (var ce in conditionalEffects)
{
if (AreConditionsMet(ce.conditions, out _))
{
ce.Execute(this);
}
}
}
private bool AreConditionsMet(List<Condition> conditions, out string reason)
{
reason = "";
if (conditions.NullOrEmpty())
{
return true;
}
foreach (var condition in conditions)
{
if (!condition.IsMet(out string singleReason))
{
reason = singleReason;
return false;
}
}
return true;
}
private string GetDisabledReason(EventOption option, string reason)
{
if (!option.disabledReason.NullOrEmpty())
{
return option.disabledReason;
}
return reason;
}
public override void PostClose()
{
base.PostClose();
HandleAction(def.dismissEffects);
}
private string FormatDescription(string description)
{
var eventVarManager = Find.World.GetComponent<EventVariableManager>();
// Use regex to find all placeholders like {variableName}
return Regex.Replace(description, @"\{(.+?)\}", match =>
{
string varName = match.Groups[1].Value;
if (eventVarManager.HasVariable(varName))
{
// Important: GetVariable<object> to get any type
return eventVarManager.GetVariable<object>(varName)?.ToString() ?? "";
}
return match.Value; // Keep placeholder if variable not found
});
}
}
}

View File

@@ -0,0 +1,97 @@
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Verse;
namespace ArachnaeSwarm
{
public class Dialog_ManageEventVariables : Window
{
private Vector2 scrollPosition;
private Dictionary<string, string> editBuffers = new Dictionary<string, string>();
private EventVariableManager manager;
public override Vector2 InitialSize => new Vector2(800f, 600f);
public Dialog_ManageEventVariables()
{
forcePause = true;
doCloseX = true;
doCloseButton = true;
closeOnClickedOutside = true;
absorbInputAroundWindow = true;
manager = Find.World.GetComponent<EventVariableManager>();
RefreshBuffers();
}
private void RefreshBuffers()
{
editBuffers.Clear();
foreach (var kvp in manager.GetAllVariables())
{
editBuffers[kvp.Key] = kvp.Value?.ToString() ?? "";
}
}
public override void DoWindowContents(Rect inRect)
{
Listing_Standard listing = new Listing_Standard();
listing.Begin(inRect);
if (listing.ButtonText("Refresh"))
{
RefreshBuffers();
}
if (listing.ButtonText("Clear All Variables"))
{
manager.ClearAll();
RefreshBuffers();
}
listing.GapLine();
Rect viewRect = new Rect(0f, 0f, inRect.width - 16f, manager.GetAllVariables().Count * 32f);
Widgets.BeginScrollView(listing.GetRect(inRect.height - 100f), ref scrollPosition, viewRect);
Listing_Standard varListing = new Listing_Standard();
varListing.Begin(viewRect);
var allVars = manager.GetAllVariables().OrderBy(kvp => kvp.Key).ToList();
foreach (var kvp in allVars)
{
Rect rowRect = varListing.GetRect(30f);
string key = kvp.Key;
object value = kvp.Value;
string typeName = value?.GetType().Name ?? "null";
Widgets.Label(rowRect.LeftPart(0.4f).Rounded(), $"{key} ({typeName})");
string buffer = editBuffers[key];
string newValue = Widgets.TextField(rowRect.RightPart(0.6f).LeftPart(0.8f).Rounded(), buffer);
editBuffers[key] = newValue;
if (Widgets.ButtonText(rowRect.RightPart(0.1f).Rounded(), "Set"))
{
// Attempt to parse and set the variable
if (value is int)
{
if (int.TryParse(newValue, out int intVal)) manager.SetVariable(key, intVal);
}
else if (value is float)
{
if (float.TryParse(newValue, out float floatVal)) manager.SetVariable(key, floatVal);
}
else
{
manager.SetVariable(key, newValue);
}
}
}
varListing.End();
Widgets.EndScrollView();
listing.End();
}
}
}

View File

@@ -0,0 +1,277 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using UnityEngine;
using Verse;
namespace ArachnaeSwarm
{
public class Dialog_NewLayoutDisplay : Window
{
private EventDef def;
private Texture2D portrait;
private Texture2D background;
private string selectedDescription;
private static EventUIConfigDef config;
public static EventUIConfigDef Config
{
get
{
if (config == null)
{
config = DefDatabase<EventUIConfigDef>.GetNamed("ARA_EventUIConfig");
}
return config;
}
}
public override Vector2 InitialSize
{
get
{
if (def.windowSize != Vector2.zero)
{
return def.windowSize;
}
return Config.defaultWindowSize;
}
}
public Dialog_NewLayoutDisplay(EventDef def)
{
this.def = def;
this.forcePause = true;
this.absorbInputAroundWindow = true;
this.doCloseX = true;
var eventVarManager = Find.World.GetComponent<EventVariableManager>();
if (!def.descriptions.NullOrEmpty())
{
if (def.descriptionMode == DescriptionSelectionMode.Random)
{
selectedDescription = def.descriptions.RandomElement();
}
else
{
string indexVarName = $"_seq_desc_index_{def.defName}";
int currentIndex = eventVarManager.GetVariable<int>(indexVarName, 0);
selectedDescription = def.descriptions[currentIndex];
int nextIndex = (currentIndex + 1) % def.descriptions.Count;
eventVarManager.SetVariable(indexVarName, nextIndex);
}
}
else
{
selectedDescription = "Error: No descriptions found in def.";
}
}
public override void PreOpen()
{
base.PreOpen();
if (!def.portraitPath.NullOrEmpty())
{
portrait = ContentFinder<Texture2D>.Get(def.portraitPath);
}
string bgPath = !def.backgroundImagePath.NullOrEmpty() ? def.backgroundImagePath : Config.defaultBackgroundImagePath;
if (!bgPath.NullOrEmpty())
{
background = ContentFinder<Texture2D>.Get(bgPath);
}
HandleAction(def.immediateEffects);
if (!def.conditionalDescriptions.NullOrEmpty())
{
foreach (var condDesc in def.conditionalDescriptions)
{
string reason;
if (AreConditionsMet(condDesc.conditions, out reason))
{
selectedDescription += "\n\n" + condDesc.text;
}
}
}
selectedDescription = FormatDescription(selectedDescription);
}
public override void DoWindowContents(Rect inRect)
{
if (background != null)
{
GUI.DrawTexture(inRect, background, ScaleMode.ScaleToFit);
}
if (Config.showDefName)
{
Text.Font = GameFont.Tiny;
GUI.color = Color.gray;
Widgets.Label(new Rect(5, 5, inRect.width - 10, 20f), def.defName);
GUI.color = Color.white;
}
if (Config.showLabel)
{
Text.Font = Config.labelFont;
Widgets.Label(new Rect(5, 20f, inRect.width - 10, 30f), def.label);
Text.Font = GameFont.Small;
}
// 假设一个统一的边距
float padding = Config.newLayoutPadding;
// 名称区域
float nameHeight = Config.newLayoutNameSize.y;
float nameWidth = Config.newLayoutNameSize.x;
Rect nameRect = new Rect(inRect.x + (inRect.width - nameWidth) / 2f, inRect.y + padding, nameWidth, nameHeight);
if (Config.drawBorders)
{
Widgets.DrawBox(nameRect);
}
Text.Anchor = TextAnchor.MiddleCenter;
Text.Font = GameFont.Medium;
Widgets.Label(nameRect, def.characterName);
Text.Font = GameFont.Small;
Text.Anchor = TextAnchor.UpperLeft;
// 立绘区域
float lihuiWidth = Config.newLayoutLihuiSize.x;
float lihuiHeight = Config.newLayoutLihuiSize.y;
Rect lihuiRect = new Rect(inRect.x + (inRect.width - lihuiWidth) / 2f, nameRect.yMax + padding, lihuiWidth, lihuiHeight);
if (portrait != null)
{
GUI.DrawTexture(lihuiRect, portrait, ScaleMode.ScaleToFit);
}
if (Config.drawBorders)
{
Widgets.DrawBox(lihuiRect);
}
// 选项区域 (预先计算高度)
float optionButtonHeight = 30f; // 每个按钮的高度
float optionSpacing = 5f; // 按钮之间的间距
float calculatedOptionHeight = 0f;
if (def.options != null && def.options.Any())
{
calculatedOptionHeight = def.options.Count * optionButtonHeight + (def.options.Count - 1) * optionSpacing;
}
calculatedOptionHeight = Mathf.Max(calculatedOptionHeight, 100f); // 最小高度
float optionsWidth = Config.newLayoutOptionsWidth;
Rect optionRect = new Rect(inRect.x + (inRect.width - optionsWidth) / 2f, inRect.yMax - padding - calculatedOptionHeight, optionsWidth, calculatedOptionHeight);
// 描述区域
float textWidth = Config.newLayoutTextSize.x;
Rect textRect = new Rect(inRect.x + (inRect.width - textWidth) / 2f, lihuiRect.yMax + padding, textWidth, optionRect.y - (lihuiRect.yMax + padding) - padding);
if (Config.drawBorders)
{
Widgets.DrawBox(textRect);
}
Rect textInnerRect = textRect.ContractedBy(padding);
Widgets.Label(textInnerRect, selectedDescription);
// 选项列表的绘制
Listing_Standard listing = new Listing_Standard();
listing.Begin(optionRect); // 使用完整的 optionRect
if (def.options != null)
{
foreach (var option in def.options)
{
string reason;
bool conditionsMet = AreConditionsMet(option.conditions, out reason);
if (conditionsMet)
{
if (listing.ButtonText(option.label))
{
HandleAction(option.optionEffects);
}
}
else
{
if (option.hideWhenDisabled)
{
continue;
}
Rect rect = listing.GetRect(30f);
Widgets.ButtonText(rect, option.label, false, true, false);
TooltipHandler.TipRegion(rect, GetDisabledReason(option, reason));
}
}
}
listing.End();
}
private void HandleAction(List<ConditionalEffects> conditionalEffects)
{
if (conditionalEffects.NullOrEmpty())
{
return;
}
foreach (var ce in conditionalEffects)
{
if (AreConditionsMet(ce.conditions, out _))
{
ce.Execute(this);
}
}
}
private bool AreConditionsMet(List<Condition> conditions, out string reason)
{
reason = "";
if (conditions.NullOrEmpty())
{
return true;
}
foreach (var condition in conditions)
{
if (!condition.IsMet(out string singleReason))
{
reason = singleReason;
return false;
}
}
return true;
}
private string GetDisabledReason(EventOption option, string reason)
{
if (!option.disabledReason.NullOrEmpty())
{
return option.disabledReason;
}
return reason;
}
public override void PostClose()
{
base.PostClose();
HandleAction(def.dismissEffects);
}
private string FormatDescription(string description)
{
var eventVarManager = Find.World.GetComponent<EventVariableManager>();
// Use regex to find all placeholders like {variableName}
return Regex.Replace(description, @"\{(.+?)\}", match =>
{
string varName = match.Groups[1].Value;
if (eventVarManager.HasVariable(varName))
{
// Important: GetVariable<object> to get any type
return eventVarManager.GetVariable<object>(varName)?.ToString() ?? "";
}
return match.Value; // Keep placeholder if variable not found
});
}
}
}

View File

@@ -0,0 +1,655 @@
using System; // Required for Activator
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Verse;
using RimWorld;
namespace ArachnaeSwarm
{
public abstract class Effect
{
public float weight = 1.0f;
public abstract void Execute(Window dialog = null);
}
public class Effect_OpenCustomUI : Effect
{
public string defName;
public int delayTicks = 0;
public override void Execute(Window dialog = null)
{
if (delayTicks > 0)
{
var actionManager = Find.World.GetComponent<DelayedActionManager>();
if (actionManager != null)
{
actionManager.AddAction(defName, delayTicks);
}
else
{
Log.Error("[WulaFallenEmpire] DelayedActionManager not found. Cannot schedule delayed UI opening.");
}
}
else
{
OpenUI();
}
}
private void OpenUI()
{
EventDef nextDef = DefDatabase<EventDef>.GetNamed(defName);
if (nextDef != null)
{
if (nextDef.hiddenWindow)
{
if (!nextDef.dismissEffects.NullOrEmpty())
{
foreach (var conditionalEffect in nextDef.dismissEffects)
{
string reason;
if (AreConditionsMet(conditionalEffect.conditions, out reason))
{
conditionalEffect.Execute(null);
}
}
}
}
else
{
Find.WindowStack.Add((Window)Activator.CreateInstance(nextDef.windowType, nextDef));
}
}
else
{
Log.Error($"[WulaFallenEmpire] Effect_OpenCustomUI could not find EventDef named '{defName}'");
}
}
private bool AreConditionsMet(List<Condition> conditions, out string reason)
{
reason = "";
if (conditions.NullOrEmpty())
{
return true;
}
foreach (var condition in conditions)
{
if (!condition.IsMet(out string singleReason))
{
reason = singleReason;
return false;
}
}
return true;
}
}
public class Effect_CloseDialog : Effect
{
public override void Execute(Window dialog = null)
{
dialog?.Close();
}
}
public class Effect_ShowMessage : Effect
{
public string message;
public MessageTypeDef messageTypeDef;
public override void Execute(Window dialog = null)
{
if (messageTypeDef == null)
{
messageTypeDef = MessageTypeDefOf.PositiveEvent;
}
Messages.Message(message, messageTypeDef);
}
}
public class Effect_FireIncident : Effect
{
public IncidentDef incident;
public override void Execute(Window dialog = null)
{
if (incident == null)
{
Log.Error("[WulaFallenEmpire] Effect_FireIncident has a null incident Def.");
return;
}
IncidentParms parms = new IncidentParms
{
target = Find.CurrentMap,
forced = true
};
if (!incident.Worker.TryExecute(parms))
{
Log.Error($"[WulaFallenEmpire] Could not fire incident {incident.defName}");
}
}
}
public class Effect_ChangeFactionRelation : Effect
{
public FactionDef faction;
public int goodwillChange;
public override void Execute(Window dialog = null)
{
if (faction == null)
{
Log.Error("[WulaFallenEmpire] Effect_ChangeFactionRelation has a null faction Def.");
return;
}
Faction targetFaction = Find.FactionManager.FirstFactionOfDef(faction);
if (targetFaction == null)
{
Log.Warning($"[WulaFallenEmpire] Could not find an active faction for FactionDef '{faction.defName}'.");
return;
}
Faction.OfPlayer.TryAffectGoodwillWith(targetFaction, goodwillChange, canSendMessage: true, canSendHostilityLetter: true, reason: null, lookTarget: null);
}
}
public class Effect_SetVariable : Effect
{
public string name;
public string value;
public string type; // Int, Float, String, Bool
public bool forceSet = false;
public override void Execute(Window dialog = null)
{
var eventVarManager = Find.World.GetComponent<EventVariableManager>();
if (!forceSet && eventVarManager.HasVariable(name))
{
return;
}
object realValue = value;
if (!string.IsNullOrEmpty(type))
{
if (type.Equals("int", System.StringComparison.OrdinalIgnoreCase) && int.TryParse(value, out int intVal))
{
realValue = intVal;
}
else if (type.Equals("float", System.StringComparison.OrdinalIgnoreCase) && float.TryParse(value, out float floatVal))
{
realValue = floatVal;
}
else if (type.Equals("bool", System.StringComparison.OrdinalIgnoreCase) && bool.TryParse(value, out bool boolVal))
{
realValue = boolVal;
}
}
eventVarManager.SetVariable(name, realValue);
}
}
public class Effect_ChangeFactionRelation_FromVariable : Effect
{
public FactionDef faction;
public string goodwillVariableName;
public override void Execute(Window dialog = null)
{
if (faction == null)
{
Log.Error("[WulaFallenEmpire] Effect_ChangeFactionRelation_FromVariable has a null faction Def.");
return;
}
Faction targetFaction = Find.FactionManager.FirstFactionOfDef(faction);
if (targetFaction == null)
{
Log.Warning($"[WulaFallenEmpire] Could not find an active faction for FactionDef '{faction.defName}'.");
return;
}
int goodwillChange = Find.World.GetComponent<EventVariableManager>().GetVariable<int>(goodwillVariableName);
Faction.OfPlayer.TryAffectGoodwillWith(targetFaction, goodwillChange, canSendMessage: true, canSendHostilityLetter: true, reason: null, lookTarget: null);
}
}
public class Effect_SpawnPawnAndStore : Effect
{
public PawnKindDef kindDef;
public int count = 1;
public string storeAs;
public override void Execute(Window dialog = null)
{
if (kindDef == null)
{
Log.Error("[WulaFallenEmpire] Effect_SpawnPawnAndStore has a null kindDef.");
return;
}
if (storeAs.NullOrEmpty())
{
Log.Error("[WulaFallenEmpire] Effect_SpawnPawnAndStore needs a 'storeAs' variable name.");
return;
}
var eventVarManager = Find.World.GetComponent<EventVariableManager>();
List<Pawn> spawnedPawns = new List<Pawn>();
for (int i = 0; i < count; i++)
{
Pawn newPawn = PawnGenerator.GeneratePawn(kindDef, Faction.OfPlayer);
IntVec3 loc = CellFinder.RandomSpawnCellForPawnNear(Find.CurrentMap.mapPawns.FreeColonists.First().Position, Find.CurrentMap, 10);
GenSpawn.Spawn(newPawn, loc, Find.CurrentMap);
spawnedPawns.Add(newPawn);
}
if (count == 1)
{
eventVarManager.SetVariable(storeAs, spawnedPawns.First());
}
else
{
eventVarManager.SetVariable(storeAs, spawnedPawns);
}
}
}
public class Effect_GiveThing : Effect
{
public ThingDef thingDef;
public int count = 1;
public override void Execute(Window dialog = null)
{
if (thingDef == null)
{
Log.Error("[WulaFallenEmpire] Effect_GiveThing has a null thingDef.");
return;
}
Map currentMap = Find.CurrentMap;
if (currentMap == null)
{
Log.Error("[WulaFallenEmpire] Effect_GiveThing cannot execute without a current map.");
return;
}
Thing thing = ThingMaker.MakeThing(thingDef);
thing.stackCount = count;
IntVec3 dropCenter = DropCellFinder.TradeDropSpot(currentMap);
DropPodUtility.DropThingsNear(dropCenter, currentMap, new List<Thing> { thing }, 110, false, false, false, false);
Messages.Message("LetterLabelCargoPodCrash".Translate(), new TargetInfo(dropCenter, currentMap), MessageTypeDefOf.PositiveEvent);
}
}
public class Effect_SpawnPawn : Effect
{
public PawnKindDef kindDef;
public int count = 1;
public bool joinPlayerFaction = true;
public string letterLabel;
public string letterText;
public LetterDef letterDef;
public override void Execute(Window dialog = null)
{
if (kindDef == null)
{
Log.Error("[WulaFallenEmpire] Effect_SpawnPawn has a null kindDef.");
return;
}
Map map = Find.CurrentMap;
if (map == null)
{
Log.Error("[WulaFallenEmpire] Effect_SpawnPawn cannot execute without a current map.");
return;
}
for (int i = 0; i < count; i++)
{
Faction faction = joinPlayerFaction ? Faction.OfPlayer : null;
PawnGenerationRequest request = new PawnGenerationRequest(
kindDef, faction, PawnGenerationContext.NonPlayer, -1, true, false, false, false,
true, 20f, false, true, false, true, true, false, false, false, false, 0f, 0f, null, 1f,
null, null, null, null, null, null, null, null, null, null, null, null, false
);
Pawn pawn = PawnGenerator.GeneratePawn(request);
if (!CellFinder.TryFindRandomEdgeCellWith((IntVec3 c) => map.reachability.CanReachColony(c) && !c.Fogged(map), map, CellFinder.EdgeRoadChance_Neutral, out IntVec3 cell))
{
cell = DropCellFinder.RandomDropSpot(map);
}
GenSpawn.Spawn(pawn, cell, map, WipeMode.Vanish);
if (!string.IsNullOrEmpty(letterLabel) && !string.IsNullOrEmpty(letterText))
{
TaggedString finalLabel = letterLabel.Formatted(pawn.Named("PAWN")).AdjustedFor(pawn);
TaggedString finalText = letterText.Formatted(pawn.Named("PAWN")).AdjustedFor(pawn);
PawnRelationUtility.TryAppendRelationsWithColonistsInfo(ref finalText, ref finalLabel, pawn);
Find.LetterStack.ReceiveLetter(finalLabel, finalText, letterDef ?? LetterDefOf.PositiveEvent, pawn);
}
}
}
}
public enum VariableOperation
{
Add,
Subtract,
Multiply,
Divide
}
public class Effect_ModifyVariable : Effect
{
public string name;
public string value;
public string valueVariableName;
public VariableOperation operation;
public override void Execute(Window dialog = null)
{
if (string.IsNullOrEmpty(name))
{
Log.Error("[WulaFallenEmpire] Effect_ModifyVariable has a null or empty name.");
return;
}
var eventVarManager = Find.World.GetComponent<EventVariableManager>();
// Determine the value to modify by
string valueStr = value;
if (!string.IsNullOrEmpty(valueVariableName))
{
valueStr = eventVarManager.GetVariable<object>(valueVariableName)?.ToString();
if (valueStr == null)
{
Log.Error($"[WulaFallenEmpire] Effect_ModifyVariable: valueVariableName '{valueVariableName}' not found.");
return;
}
}
// Get the target variable, or initialize it
object variable = eventVarManager.GetVariable<object>(name);
if (variable == null)
{
Log.Message($"[EventSystem] Effect_ModifyVariable: Variable '{name}' not found, initializing to 0.");
variable = 0;
}
object originalValue = variable;
object newValue = null;
// Perform operation based on type
try
{
if (variable is int || (variable is float && !valueStr.Contains("."))) // Allow int ops
{
int currentVal = System.Convert.ToInt32(variable);
int modVal = int.Parse(valueStr);
newValue = (int)Modify((float)currentVal, (float)modVal, operation);
}
else // Default to float operation
{
float currentVal = System.Convert.ToSingle(variable);
float modVal = float.Parse(valueStr);
newValue = Modify(currentVal, modVal, operation);
}
Log.Message($"[EventSystem] Modifying variable '{name}'. Operation: {operation}. Value: {valueStr}. From: {originalValue} To: {newValue}");
eventVarManager.SetVariable(name, newValue);
}
catch (System.Exception e)
{
Log.Error($"[WulaFallenEmpire] Effect_ModifyVariable: Could not parse or operate on value '{valueStr}' for variable '{name}'. Error: {e.Message}");
}
}
private float Modify(float current, float modifier, VariableOperation op)
{
switch (op)
{
case VariableOperation.Add: return current + modifier;
case VariableOperation.Subtract: return current - modifier;
case VariableOperation.Multiply: return current * modifier;
case VariableOperation.Divide:
if (modifier != 0) return current / modifier;
Log.Error($"[WulaFallenEmpire] Effect_ModifyVariable tried to divide by zero.");
return current;
default: return current;
}
}
}
public class Effect_ClearVariable : Effect
{
public string name;
public override void Execute(Window dialog = null)
{
if (string.IsNullOrEmpty(name))
{
Log.Error("[WulaFallenEmpire] Effect_ClearVariable has a null or empty name.");
return;
}
Find.World.GetComponent<EventVariableManager>().ClearVariable(name);
}
}
public class Effect_AddQuest : Effect
{
public QuestScriptDef quest;
public override void Execute(Window dialog = null)
{
if (quest == null)
{
Log.Error("[WulaFallenEmpire] Effect_AddQuest has a null quest Def.");
return;
}
Quest newQuest = Quest.MakeRaw();
newQuest.root = quest;
newQuest.id = Find.UniqueIDsManager.GetNextQuestID();
Find.QuestManager.Add(newQuest);
}
}
public class Effect_FinishResearch : Effect
{
public ResearchProjectDef research;
public override void Execute(Window dialog = null)
{
if (research == null)
{
Log.Error("[WulaFallenEmpire] Effect_FinishResearch has a null research Def.");
return;
}
Find.ResearchManager.FinishProject(research);
}
}
public class Effect_TriggerRaid : Effect
{
public float points;
public FactionDef faction;
public RaidStrategyDef raidStrategy;
public PawnsArrivalModeDef raidArrivalMode;
public PawnGroupKindDef groupKind;
public List<PawnGroupMaker> pawnGroupMakers;
public string letterLabel;
public string letterText;
public override void Execute(Window dialog = null)
{
Map map = Find.CurrentMap;
if (map == null)
{
Log.Error("[WulaFallenEmpire] Effect_TriggerRaid cannot execute without a current map.");
return;
}
Faction factionInst = Find.FactionManager.FirstFactionOfDef(this.faction);
if (factionInst == null)
{
Log.Error($"[WulaFallenEmpire] Effect_TriggerRaid could not find an active faction for FactionDef '{this.faction?.defName}'.");
return;
}
IncidentParms parms = new IncidentParms
{
target = map,
points = this.points,
faction = factionInst,
raidStrategy = this.raidStrategy,
raidArrivalMode = this.raidArrivalMode,
pawnGroupMakerSeed = Rand.Int,
forced = true
};
if (!RCellFinder.TryFindRandomPawnEntryCell(out parms.spawnCenter, map, CellFinder.EdgeRoadChance_Hostile))
{
Log.Error("[WulaFallenEmpire] Effect_TriggerRaid could not find a valid spawn center.");
return;
}
PawnGroupMakerParms groupMakerParms = new PawnGroupMakerParms
{
groupKind = this.groupKind ?? PawnGroupKindDefOf.Combat,
tile = map.Tile,
points = this.points,
faction = factionInst,
raidStrategy = this.raidStrategy,
seed = parms.pawnGroupMakerSeed
};
List<Pawn> pawns;
if (!pawnGroupMakers.NullOrEmpty())
{
var groupMaker = pawnGroupMakers.RandomElementByWeight(x => x.commonality);
pawns = groupMaker.GeneratePawns(groupMakerParms).ToList();
}
else
{
pawns = PawnGroupMakerUtility.GeneratePawns(groupMakerParms).ToList();
}
if (pawns.Any())
{
raidArrivalMode.Worker.Arrive(pawns, parms);
// Assign Lord and LordJob to make the pawns actually perform the raid.
raidStrategy.Worker.MakeLords(parms, pawns);
if (!string.IsNullOrEmpty(letterLabel) && !string.IsNullOrEmpty(letterText))
{
Find.LetterStack.ReceiveLetter(letterLabel, letterText, LetterDefOf.ThreatBig, pawns[0]);
}
}
}
}
public class Effect_CheckFactionGoodwill : Effect
{
public FactionDef factionDef;
public string variableName;
public override void Execute(Window dialog = null)
{
if (factionDef == null || string.IsNullOrEmpty(variableName))
{
Log.Error("[WulaFallenEmpire] Effect_CheckFactionGoodwill is not configured correctly.");
return;
}
var eventVarManager = Find.World.GetComponent<EventVariableManager>();
Faction faction = Find.FactionManager.FirstFactionOfDef(factionDef);
if (faction != null)
{
int goodwill = faction.GoodwillWith(Faction.OfPlayer);
Log.Message($"[EventSystem] Storing goodwill for faction '{faction.Name}' ({goodwill}) into variable '{variableName}'.");
eventVarManager.SetVariable(variableName, goodwill);
}
else
{
Log.Warning($"[EventSystem] Effect_CheckFactionGoodwill: Faction '{factionDef.defName}' not found. Storing 0 in variable '{variableName}'.");
eventVarManager.SetVariable(variableName, 0);
}
}
}
public class Effect_StoreRealPlayTime : Effect
{
public string variableName;
public override void Execute(Window dialog = null)
{
if (string.IsNullOrEmpty(variableName))
{
Log.Error("[WulaFallenEmpire] Effect_StoreRealPlayTime is not configured correctly (missing variableName).");
return;
}
var eventVarManager = Find.World.GetComponent<EventVariableManager>();
float realPlayTime = Find.GameInfo.RealPlayTimeInteracting;
Log.Message($"[EventSystem] Storing real play time ({realPlayTime}s) into variable '{variableName}'.");
eventVarManager.SetVariable(variableName, realPlayTime);
}
}
public class Effect_StoreDaysPassed : Effect
{
public string variableName;
public override void Execute(Window dialog = null)
{
if (string.IsNullOrEmpty(variableName))
{
Log.Error("[WulaFallenEmpire] Effect_StoreDaysPassed is not configured correctly (missing variableName).");
return;
}
var eventVarManager = Find.World.GetComponent<EventVariableManager>();
int daysPassed = GenDate.DaysPassed;
Log.Message($"[EventSystem] Storing days passed ({daysPassed}) into variable '{variableName}'.");
eventVarManager.SetVariable(variableName, daysPassed);
}
}
public class Effect_StoreColonyWealth : Effect
{
public string variableName;
public override void Execute(Window dialog = null)
{
if (string.IsNullOrEmpty(variableName))
{
Log.Error("[WulaFallenEmpire] Effect_StoreColonyWealth is not configured correctly (missing variableName).");
return;
}
Map currentMap = Find.CurrentMap;
if (currentMap == null)
{
Log.Error("[WulaFallenEmpire] Effect_StoreColonyWealth cannot execute without a current map.");
return;
}
var eventVarManager = Find.World.GetComponent<EventVariableManager>();
float wealth = currentMap.wealthWatcher.WealthTotal;
Log.Message($"[EventSystem] Storing colony wealth ({wealth}) into variable '{variableName}'.");
eventVarManager.SetVariable(variableName, wealth);
}
}
}

View File

@@ -0,0 +1,148 @@
using System; // Add this line
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Verse;
namespace ArachnaeSwarm
{
public enum DescriptionSelectionMode
{
Random,
Sequential
}
public class EventDef : Def
{
public string portraitPath;
public string characterName;
// New system: list of descriptions
public List<string> descriptions;
public DescriptionSelectionMode descriptionMode = DescriptionSelectionMode.Random;
public bool hiddenWindow = false;
// Backwards compatibility: old single description field
public new string description = null;
public Vector2 windowSize = Vector2.zero;
public Type windowType = typeof(Dialog_CustomDisplay); // 默认窗口类型
public List<EventOption> options;
public string backgroundImagePath;
public List<ConditionalEffects> immediateEffects;
public List<ConditionalEffects> dismissEffects;
public List<ConditionalDescription> conditionalDescriptions;
public override void PostLoad()
{
base.PostLoad();
#pragma warning disable 0618
// If the old description field is used, move its value to the new list for processing.
if (!description.NullOrEmpty())
{
if (descriptions.NullOrEmpty())
{
descriptions = new List<string>();
}
descriptions.Insert(0, description);
description = null; // Clear the old field to prevent confusion
}
#pragma warning restore 0618
// If hiddenWindow is true, merge immediateEffects into dismissEffects at load time.
if (hiddenWindow && !immediateEffects.NullOrEmpty())
{
if (dismissEffects.NullOrEmpty())
{
dismissEffects = new List<ConditionalEffects>();
}
dismissEffects.AddRange(immediateEffects);
immediateEffects = null; // Clear to prevent double execution
}
}
}
public class EventOption
{
public string label;
public List<ConditionalEffects> optionEffects;
public List<Condition> conditions;
public string disabledReason;
public bool hideWhenDisabled = false;
}
public class LoopEffects
{
public int count = 1;
public string countVariableName;
public List<Effect> effects;
}
public class ConditionalEffects
{
public List<Condition> conditions;
public List<Effect> effects;
public List<Effect> randomlistEffects;
public List<LoopEffects> loopEffects;
public void Execute(Window dialog)
{
// Execute all standard effects
if (!effects.NullOrEmpty())
{
foreach (var effect in effects)
{
effect.Execute(dialog);
}
}
// Execute one random effect from the random list
if (!randomlistEffects.NullOrEmpty())
{
float totalWeight = randomlistEffects.Sum(e => e.weight);
float randomPoint = Rand.Value * totalWeight;
foreach (var effect in randomlistEffects)
{
if (randomPoint < effect.weight)
{
effect.Execute(dialog);
break;
}
randomPoint -= effect.weight;
}
}
// Execute looped effects
if (!loopEffects.NullOrEmpty())
{
var eventVarManager = Find.World.GetComponent<EventVariableManager>();
foreach (var loop in loopEffects)
{
int loopCount = loop.count;
if (!loop.countVariableName.NullOrEmpty() && eventVarManager.HasVariable(loop.countVariableName))
{
loopCount = eventVarManager.GetVariable<int>(loop.countVariableName);
}
for (int i = 0; i < loopCount; i++)
{
if (!loop.effects.NullOrEmpty())
{
foreach (var effect in loop.effects)
{
effect.Execute(dialog);
}
}
}
}
}
}
}
public class ConditionalDescription
{
public List<Condition> conditions;
public string text;
}
}

View File

@@ -0,0 +1,34 @@
using UnityEngine;
using Verse;
namespace ArachnaeSwarm
{
public class EventUIConfigDef : Def
{
// General Style
public GameFont labelFont = GameFont.Small;
public bool drawBorders = true;
public bool showDefName = true;
public bool showLabel = true;
public string defaultBackgroundImagePath;
public Vector2 defaultWindowSize = new Vector2(750f, 500f);
// Virtual Layout Dimensions
public Vector2 lihuiSize = new Vector2(500f, 800f);
public Vector2 nameSize = new Vector2(260f, 130f);
public Vector2 textSize = new Vector2(650f, 500f);
public float optionsWidth = 610f;
// Virtual Layout Offsets
public float textNameOffset = 20f;
public float optionsTextOffset = 20f;
// New Layout Dimensions
public Vector2 newLayoutNameSize = new Vector2(200f, 50f);
public Vector2 newLayoutLihuiSize = new Vector2(300f, 400f);
public Vector2 newLayoutTextSize = new Vector2(600f, 200f);
public float newLayoutOptionsWidth = 600f;
public float newLayoutPadding = 20f;
public float newLayoutTextNameOffset = 20f;
public float newLayoutOptionsTextOffset = 20f;
}
}

View File

@@ -0,0 +1,177 @@
using System.Collections.Generic;
using Verse;
using RimWorld;
using RimWorld.Planet;
namespace ArachnaeSwarm
{
public class EventVariableManager : WorldComponent
{
private Dictionary<string, int> intVars = new Dictionary<string, int>();
private Dictionary<string, float> floatVars = new Dictionary<string, float>();
private Dictionary<string, string> stringVars = new Dictionary<string, string>();
private Dictionary<string, Pawn> pawnVars = new Dictionary<string, Pawn>();
private Dictionary<string, List<Pawn>> pawnListVars = new Dictionary<string, List<Pawn>>();
// 用于Scribe的辅助列表
private List<string> pawnVarKeys;
private List<Pawn> pawnVarValues;
private List<string> pawnListVarKeys;
private List<List<Pawn>> pawnListVarValues;
// Required for WorldComponent
public EventVariableManager(World world) : base(world)
{
}
public override void ExposeData()
{
base.ExposeData();
Scribe_Collections.Look(ref intVars, "intVars", LookMode.Value, LookMode.Value);
Scribe_Collections.Look(ref floatVars, "floatVars", LookMode.Value, LookMode.Value);
Scribe_Collections.Look(ref stringVars, "stringVars", LookMode.Value, LookMode.Value);
Scribe_Collections.Look(ref pawnVars, "pawnVars", LookMode.Value, LookMode.Reference, ref pawnVarKeys, ref pawnVarValues);
Scribe_Collections.Look(ref pawnListVars, "pawnListVars", LookMode.Value, LookMode.Reference, ref pawnListVarKeys, ref pawnListVarValues);
// Ensure dictionaries are not null after loading
if (Scribe.mode == LoadSaveMode.PostLoadInit)
{
intVars ??= new Dictionary<string, int>();
floatVars ??= new Dictionary<string, float>();
stringVars ??= new Dictionary<string, string>();
pawnVars ??= new Dictionary<string, Pawn>();
pawnListVars ??= new Dictionary<string, List<Pawn>>();
}
}
public void SetVariable(string name, object value)
{
if (string.IsNullOrEmpty(name)) return;
// Log the variable change
Log.Message($"[EventSystem] Setting variable '{name}' to value '{value}' of type {value?.GetType().Name ?? "null"}.");
// Clear any existing variable with the same name to prevent type confusion
ClearVariable(name);
if (value is int intValue)
{
intVars[name] = intValue;
}
else if (value is float floatValue)
{
floatVars[name] = floatValue;
}
else if (value is string stringValue)
{
stringVars[name] = stringValue;
}
else if (value is Pawn pawnValue)
{
pawnVars[name] = pawnValue;
}
else if (value is List<Pawn> pawnListValue)
{
pawnListVars[name] = pawnListValue;
}
else if (value != null)
{
stringVars[name] = value.ToString();
Log.Warning($"[WulaFallenEmpire] EventVariableManager: Variable '{name}' of type {value.GetType()} was converted to string for storage. This may lead to unexpected behavior.");
}
}
public T GetVariable<T>(string name, T defaultValue = default)
{
if (string.IsNullOrEmpty(name)) return defaultValue;
object value = null;
if (pawnListVars.TryGetValue(name, out var pawnListVal))
{
value = pawnListVal;
}
else if (pawnVars.TryGetValue(name, out var pawnVal))
{
value = pawnVal;
}
else if (floatVars.TryGetValue(name, out var floatVal))
{
value = floatVal;
}
else if (intVars.TryGetValue(name, out var intVal))
{
value = intVal;
}
else if (stringVars.TryGetValue(name, out var stringVal))
{
value = stringVal;
}
if (value != null)
{
if (value is T typedValue)
{
return typedValue;
}
try
{
// Handle cases where T is object but the stored value is, e.g., an int
if (typeof(T) == typeof(object))
{
return (T)value;
}
return (T)System.Convert.ChangeType(value, typeof(T));
}
catch (System.Exception e)
{
Log.Warning($"[WulaFallenEmpire] EventVariableManager: Variable '{name}' of type {value.GetType()} could not be converted to {typeof(T)}. Error: {e.Message}");
return defaultValue;
}
}
return defaultValue;
}
public bool HasVariable(string name)
{
return intVars.ContainsKey(name) ||
floatVars.ContainsKey(name) ||
stringVars.ContainsKey(name) ||
pawnVars.ContainsKey(name) ||
pawnListVars.ContainsKey(name);
}
public void ClearVariable(string name)
{
if (HasVariable(name))
{
Log.Message($"[EventSystem] Clearing variable '{name}'.");
}
intVars.Remove(name);
floatVars.Remove(name);
stringVars.Remove(name);
pawnVars.Remove(name);
pawnListVars.Remove(name);
}
public void ClearAll()
{
intVars.Clear();
floatVars.Clear();
stringVars.Clear();
pawnVars.Clear();
pawnListVars.Clear();
}
public Dictionary<string, object> GetAllVariables()
{
var allVars = new Dictionary<string, object>();
foreach (var kvp in intVars) allVars[kvp.Key] = kvp.Value;
foreach (var kvp in floatVars) allVars[kvp.Key] = kvp.Value;
foreach (var kvp in stringVars) allVars[kvp.Key] = kvp.Value;
foreach (var kvp in pawnVars) allVars[kvp.Key] = kvp.Value;
foreach (var kvp in pawnListVars) allVars[kvp.Key] = kvp.Value;
return allVars;
}
}
}

View File

@@ -0,0 +1,94 @@
using RimWorld;
using RimWorld.QuestGen;
using System;
using System.Collections.Generic;
using Verse;
namespace ArachnaeSwarm
{
public class Letter_EventChoice : ChoiceLetter
{
// These fields are now inherited from the base Letter class
// public string letterLabel;
// public string letterTitle;
// public string letterText;
public List<QuestNode_Root_EventLetter.Option> options;
public new Quest quest;
public override IEnumerable<DiaOption> Choices
{
get
{
if (options.NullOrEmpty())
{
yield break;
}
foreach (var optionDef in options)
{
var currentOption = optionDef;
Action choiceAction = delegate
{
if (!currentOption.optionEffects.NullOrEmpty())
{
foreach (var conditionalEffect in currentOption.optionEffects)
{
string reason;
if (AreConditionsMet(conditionalEffect.conditions, out reason))
{
conditionalEffect.Execute(null);
}
}
}
if (quest != null && !quest.hidden && !quest.Historical)
{
quest.End(QuestEndOutcome.Success);
}
Find.LetterStack.RemoveLetter(this);
};
var diaOption = new DiaOption(currentOption.label)
{
action = choiceAction,
resolveTree = true
};
yield return diaOption;
}
}
}
public override bool CanDismissWithRightClick => false;
private bool AreConditionsMet(List<Condition> conditions, out string reason)
{
reason = "";
if (conditions.NullOrEmpty())
{
return true;
}
foreach (var condition in conditions)
{
if (!condition.IsMet(out string singleReason))
{
reason = singleReason;
return false;
}
}
return true;
}
public override void ExposeData()
{
base.ExposeData();
// Scribe_Values.Look(ref letterLabel, "letterLabel"); // Now uses base.label
// Scribe_Values.Look(ref letterTitle, "letterTitle"); // Now uses base.title
// Scribe_Values.Look(ref letterText, "letterText"); // Now uses base.text
Scribe_Collections.Look(ref options, "options", LookMode.Deep);
if (Scribe.mode != LoadSaveMode.Saving || quest != null)
{
Scribe_References.Look(ref quest, "quest");
}
}
}
}

View File

@@ -0,0 +1,49 @@
using RimWorld;
using RimWorld.QuestGen;
using System;
using System.Collections.Generic;
using Verse;
namespace ArachnaeSwarm
{
public class QuestNode_Root_EventLetter : QuestNode
{
// Fields to be set from the QuestScriptDef XML
public SlateRef<string> letterLabel;
public SlateRef<string> letterTitle;
public SlateRef<string> letterText;
public List<Option> options = new List<Option>();
// This is a root node, so it doesn't have a parent signal.
// It runs immediately when the quest starts.
protected override void RunInt()
{
// Get the current slate
Slate slate = QuestGen.slate;
var letter = (Letter_EventChoice)LetterMaker.MakeLetter(DefDatabase<LetterDef>.GetNamed("ARA_EventChoiceLetter"));
letter.Label = letterLabel.GetValue(slate);
letter.title = letterTitle.GetValue(slate);
letter.Text = letterText.GetValue(slate);
letter.options = options;
letter.quest = QuestGen.quest;
letter.lookTargets = slate.Get<LookTargets>("lookTargets");
Find.LetterStack.ReceiveLetter(letter);
}
protected override bool TestRunInt(Slate slate)
{
// This node can always run as long as the slate refs are valid.
// We can add more complex checks here if needed.
return true;
}
// Inner class to hold option data from XML
public class Option
{
public string label;
public List<ConditionalEffects> optionEffects;
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 MiB

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

View File

Before

Width:  |  Height:  |  Size: 1.6 MiB

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 MiB