更新
@@ -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>
|
||||
196
1.6/1.6/Defs/EventDefs/ARA_Guide_Event.xml
Normal 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>
|
||||
@@ -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>
|
||||
|
||||
37
1.6/1.6/Defs/MiscSettingDefs/EventUIConfig.xml
Normal 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>
|
||||
9
1.6/1.6/Defs/MiscSettingDefs/LetterDefs/EventLetter.xml
Normal 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>
|
||||
@@ -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-->
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
<!-- 炮塔 -->
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 72 KiB |
|
After Width: | Height: | Size: 343 KiB |
|
After Width: | Height: | Size: 343 KiB |
|
After Width: | Height: | Size: 343 KiB |
BIN
Content/Textures/ArachnaeSwarm/Item/ARA_Guide_Pheromone.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
Content/Textures/ArachnaeSwarm/Storyteller/ARA_Chrony.png
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
BIN
Content/Textures/ArachnaeSwarm/Storyteller/ARA_Chrony_TINY.png
Normal file
|
After Width: | Height: | Size: 336 KiB |
@@ -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": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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" />
|
||||
|
||||
61
Source/ArachnaeSwarm/EventSystem/CompOpenCustomUI.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
240
Source/ArachnaeSwarm/EventSystem/Condition.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
65
Source/ArachnaeSwarm/EventSystem/DebugActions.cs
Normal 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
95
Source/ArachnaeSwarm/EventSystem/DelayedActionManager.cs
Normal 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>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
274
Source/ArachnaeSwarm/EventSystem/Dialog_CustomDisplay.cs
Normal 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
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
277
Source/ArachnaeSwarm/EventSystem/Dialog_NewLayoutDisplay.cs
Normal 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
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
655
Source/ArachnaeSwarm/EventSystem/Effect.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
148
Source/ArachnaeSwarm/EventSystem/EventDef.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
34
Source/ArachnaeSwarm/EventSystem/EventUIConfigDef.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
177
Source/ArachnaeSwarm/EventSystem/EventVariableManager.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
94
Source/ArachnaeSwarm/EventSystem/Letter_EventChoice.cs
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 2.2 MiB After Width: | Height: | Size: 2.2 MiB |
BIN
非公开资源/Content/2.png
Normal file
|
After Width: | Height: | Size: 2.2 MiB |
BIN
非公开资源/Content/3.png
Normal file
|
After Width: | Height: | Size: 2.2 MiB |
|
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 1.6 MiB |
|
Before Width: | Height: | Size: 2.4 MiB |
BIN
非公开资源/Content/Textures/Events/Portraits/ARA_Fighter_1.png
Normal file
|
After Width: | Height: | Size: 343 KiB |
BIN
非公开资源/Content/Textures/Events/Portraits/ARA_Fighter_2.png
Normal file
|
After Width: | Height: | Size: 343 KiB |
BIN
非公开资源/Content/Textures/Events/Portraits/ARA_Fighter_3.png
Normal file
|
After Width: | Height: | Size: 343 KiB |
BIN
非公开资源/Content/Textures/Item/ARA_Guide_Pheromone.sai2
Normal file
|
Before Width: | Height: | Size: 2.2 MiB |
|
Before Width: | Height: | Size: 3.5 MiB |