This commit is contained in:
Tourswen
2026-03-30 00:02:25 +08:00
parent f757abad5e
commit 4dd6e71b50
33 changed files with 217 additions and 2535 deletions

Binary file not shown.

View File

@@ -943,22 +943,30 @@
<displayGizmoWhileUndrafted>false</displayGizmoWhileUndrafted>
<aiCanUse>false</aiCanUse>
<verbProperties>
<verbClass>Verb_AbilityShoot</verbClass>
<defaultProjectile>Bullet_ARA_MimicNematode_Needle</defaultProjectile>
<verbClass>Verb_CastAbility</verbClass>
<range>5</range>
<warmupTime>0.1</warmupTime>
<soundCast>Heatspikes_Shot</soundCast>
<soundCastTail>Heatspikes_Tail</soundCastTail>
<muzzleFlashScale>9</muzzleFlashScale>
<ticksBetweenBurstShots>2</ticksBetweenBurstShots>
<warmupTime>0</warmupTime>
<burstShotCount>32</burstShotCount>
<accuracyTouch>1</accuracyTouch>
<accuracyShort>1</accuracyShort>
<accuracyMedium>1</accuracyMedium>
<accuracyLong>1</accuracyLong>
<ai_IsWeapon>false</ai_IsWeapon>
<violent>false</violent>
<targetable>false</targetable>
<targetParams>
<canTargetSelf>true</canTargetSelf>
</targetParams>
</verbProperties>
<comps>
<li Class="ArachnaeSwarm.CompProperties_AbilityLaunchMultiProjectile">
<!-- 默认状态参数 -->
<projectileDef>Bullet_ARA_MimicNematode_Needle</projectileDef>
<numProjectiles>32</numProjectiles>
<!-- 偏移配置 -->
<offsetRadius>1</offsetRadius>
<useRandomOffset>true</useRandomOffset>
<offsetInCircle>true</offsetInCircle>
<avoidOverlap>false</avoidOverlap>
<shotIntervalTicks>3</shotIntervalTicks>
</li>
<li Class="ArachnaeSwarm.CompProperties_AbilityNeedCost">
<customLabel>饮食</customLabel>
<showProgressBar>true</showProgressBar>
@@ -975,6 +983,52 @@
</li>
</comps>
</AbilityDef>
<AbilityDef>
<defName>ARA_SymbioticStabilizer</defName>
<label>拟线控制</label>
<description>向目标注射阿拉克涅拟线种的控制溶液,溶液将保证目标不会因为阿拉克涅拟线种在体内的扩散而死亡。</description>
<iconPath>ArachnaeSwarm/UI/Abilities/ARA_Dissolver_Touch</iconPath>
<cooldownTicksRange>6000</cooldownTicksRange>
<charges>5</charges>
<cooldownPerCharge>true</cooldownPerCharge>
<hostile>false</hostile>
<casterMustBeCapableOfViolence>false</casterMustBeCapableOfViolence>
<stunTargetWhileCasting>true</stunTargetWhileCasting>
<aiCanUse>true</aiCanUse>
<!-- <warmupStartSound>AcidSpray_Warmup</warmupStartSound> -->
<jobDef>CastAbilityOnThingMelee</jobDef>
<verbProperties>
<verbClass>Verb_CastAbilityTouch</verbClass>
<violent>false</violent>
<forceNormalTimeSpeed>false</forceNormalTimeSpeed>
<warmupTime>0.5</warmupTime>
<range>-1</range>
<requireLineOfSight>false</requireLineOfSight>
<soundCast>AcidSpray_Resolve</soundCast>
<targetParams>
<canTargetPawns>true</canTargetPawns>
<canTargetAnimals>true</canTargetAnimals>
<canTargetMechs>false</canTargetMechs>
<canTargetSubhumans>true</canTargetSubhumans>
<canTargetEntities>false</canTargetEntities>
<canTargetBuildings>false</canTargetBuildings>
</targetParams>
</verbProperties>
<comps>
<li Class="ArachnaeSwarm.CompProperties_AbilityNeedCost">
<customLabel>饮食</customLabel>
<showProgressBar>true</showProgressBar>
<needDef>Food</needDef>
<needCost>0.2</needCost>
<failMessage>营养值不足,需要进食</failMessage>
</li>
<li Class="CompProperties_AbilityGiveHediff">
<compClass>CompAbilityEffect_GiveHediff</compClass>
<hediffDef>ARA_SymbioticStabilizer</hediffDef>
<replaceExisting>true</replaceExisting>
</li>
</comps>
</AbilityDef>
<ThingDef ParentName="BaseBullet">
<defName>Bullet_ARA_MimicNematode_Needle</defName>
<label>拟线种寄生针</label>

View File

@@ -1,96 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Defs>
<IncidentDef>
<defName>ARA_Loop_Raid_Incident</defName>
<label>虫群袭击</label>
<category>Misc</category>
<targetTags>
<li>Map_PlayerHome</li>
</targetTags>
<!-- This uses the vanilla quest giver worker -->
<workerClass>IncidentWorker_GiveQuest</workerClass>
<!-- This points to our custom QuestScriptDef -->
<questScriptDef>ARA_Loop_Raid_Quest</questScriptDef>
<!--
<baseChance>1.0</baseChance>
<earliestDay>0.001</earliestDay>
<minRefireDays>99999</minRefireDays>
-->
</IncidentDef>
<QuestScriptDef>
<defName>ARA_Loop_Raid_Quest</defName>
<label>虫群袭击</label>
<description>虫群袭击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->虫群袭击questDescription</li>
</rulesStrings>
</rules>
</li>
<li Class="ArachnaeSwarm.QuestNode_Root_EventLetter">
<letterLabel>虫群袭击Label</letterLabel>
<letterTitle>虫群袭击Title</letterTitle>
<letterText>虫群袭击Text</letterText>
<options>
<li>
<label>袭击开始</label>
<optionEffects>
<li>
<effects>
<li Class="ArachnaeSwarm.Effect_OpenCustomUI">
<defName>ARA_Loop_Raid_Main_Event_1</defName>
</li>
</effects>
</li>
</optionEffects>
</li>
</options>
</li>
</nodes>
</root>
</QuestScriptDef>
<ArachnaeSwarm.EventDef>
<defName>ARA_Loop_Raid_Main_Event_1</defName>
<characterName>嘻嘻</characterName>
<hiddenWindow>true</hiddenWindow>
<immediateEffects>
<li>
<!-- 系统会从这个列表中根据权重随机选择一个执行 -->
<randomlistEffects>
<li Class="ArachnaeSwarm.Effect_TriggerRaid">
<points>10000</points>
<faction>ARA_Hostile_Hive</faction>
<raidStrategy>ImmediateAttack</raidStrategy>
<raidArrivalMode>EdgeWalkIn</raidArrivalMode>
<groupKind>Combat</groupKind>
<pawnGroupMakers>
<li>
<kindDef>Combat</kindDef>
<commonality>100</commonality>
<options>
<ArachnaeBase_Race_Acidcut_Enermy>7</ArachnaeBase_Race_Acidcut_Enermy>
<ArachnaeNode_Race_ShieldHead_Enermy>3</ArachnaeNode_Race_ShieldHead_Enermy>
</options>
</li>
</pawnGroupMakers>
</li>
</randomlistEffects>
<effects>
</effects>
</li>
</immediateEffects>
</ArachnaeSwarm.EventDef>
</Defs>

View File

@@ -221,6 +221,7 @@
<li Class="HediffCompProperties_GiveAbility">
<abilityDefs>
<li>ARA_MimicNematode_Needle_Fire</li>
<li>ARA_SymbioticStabilizer</li>
</abilityDefs>
</li>
</comps>

View File

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

View File

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

View File

@@ -774,6 +774,7 @@
<body>ArachnaeMyrmecocystus_Body</body>
<!-- 血量上限 -->
<baseHealthScale>2</baseHealthScale>
<ageGenerationCurve Inherit="False" IsNull="True"/>
</race>
<comps>
@@ -927,6 +928,7 @@
<baseBodySize>1.5</baseBodySize>
<!-- 血量上限 -->
<baseHealthScale>3</baseHealthScale>
<ageGenerationCurve Inherit="False" IsNull="True"/>
</race>
<comps>
@@ -1041,6 +1043,7 @@
<body>ArachnaeWeaponSmith_Body</body>
<baseBodySize>0.8</baseBodySize>
<baseHealthScale>0.75</baseHealthScale>
<ageGenerationCurve Inherit="False" IsNull="True"/>
</race>
<comps>
@@ -1187,6 +1190,7 @@
<body>ArachnaeFighter_Body</body>
<baseBodySize>0.7</baseBodySize>
<baseHealthScale>3</baseHealthScale>
<ageGenerationCurve Inherit="False" IsNull="True"/>
</race>
<comps>
@@ -1313,6 +1317,7 @@
<body>ArachnaeFacehugger_Body</body>
<baseBodySize>0.5</baseBodySize>
<baseHealthScale>0.25</baseHealthScale>
<ageGenerationCurve Inherit="False" IsNull="True"/>
</race>
<comps>
<li Class="ArachnaeSwarm.CompProperties_HediffGiver">
@@ -1394,6 +1399,7 @@
<!-- 身体类型 -->
<body>ArachnaeSmokepop_Body</body>
<baseHealthScale>4</baseHealthScale>
<ageGenerationCurve Inherit="False" IsNull="True"/>
</race>
<comps>
@@ -1519,6 +1525,7 @@
<flightStartChanceOnJobStart>1.0</flightStartChanceOnJobStart>
<!-- <baseBodySize>0.8</baseBodySize> -->
<baseHealthScale>2</baseHealthScale>
<ageGenerationCurve Inherit="False" IsNull="True"/>
</race>
<comps>
@@ -1735,6 +1742,7 @@
<flightStartChanceOnJobStart>1.0</flightStartChanceOnJobStart>
<baseBodySize>0.8</baseBodySize>
<baseHealthScale>2</baseHealthScale>
<ageGenerationCurve Inherit="False" IsNull="True"/>
</race>
<comps>
@@ -1942,6 +1950,7 @@
<body>ArachnaePraetorian_Body</body>
<baseBodySize>2</baseBodySize>
<baseHealthScale>3</baseHealthScale>
<ageGenerationCurve Inherit="False" IsNull="True"/>
</race>
<comps>

View File

@@ -84,15 +84,15 @@
<defName>ARA_SymbioticStabilizer</defName>
<label>信息素抑制</label>
<labelNoun>一种信息素抑制效果</labelNoun>
<description>阿拉克涅信息素溶剂的效果正在生效,它模拟了虫群的共生信号,抑制了体内寄生体的活性。在溶剂生效时不会使得拟线种虫族寄生的进度超过80%。</description>
<description>阿拉克涅信息素溶剂的效果正在生效它模拟了虫群的共生信号抑制了体内寄生体的活性不会使得拟线种虫族寄生的进度超过80%。</description>
<hediffClass>HediffWithComps</hediffClass>
<defaultLabelColor>(0.7, 1.0, 0.7)</defaultLabelColor>
<isBad>false</isBad>
<comps>
<li Class="HediffCompProperties_SeverityPerDay">
<severityPerDay>-0.34</severityPerDay> <!-- 严重性每天降低0.34大约3天从1.0掉到0 -->
<!-- <li Class="HediffCompProperties_SeverityPerDay">
<severityPerDay>-0.34</severityPerDay>
<showHoursToRecover>true</showHoursToRecover>
</li>
</li> -->
</comps>
</HediffDef>

View File

@@ -1,26 +1,42 @@
{
"Version": 1,
"WorkspaceRootPath": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\",
"WorkspaceRootPath": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\",
"Documents": [
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|d:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\abilities\\ara_fanshapedstunknockback\\safer_pawnflyer.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:abilities\\ara_fanshapedstunknockback\\safer_pawnflyer.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|e:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\jobs\\jobdriver_feedwithhoney\\thinknode_jobgiver_feedwithhoney.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:jobs\\jobdriver_feedwithhoney\\thinknode_jobgiver_feedwithhoney.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|d:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\abilities\\ara_fanshapedstunknockback\\compproperties_abilityfanshapedstunknockback.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:abilities\\ara_fanshapedstunknockback\\compproperties_abilityfanshapedstunknockback.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|e:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\jobs\\jobdriver_plant\\thinknode_conditionalanimalshoulddogrowingwork.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:jobs\\jobdriver_plant\\thinknode_conditionalanimalshoulddogrowingwork.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|d:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\hediffs\\ara_gestaltnode\\compgestalt.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:hediffs\\ara_gestaltnode\\compgestalt.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|e:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\jobs\\jobdriver_clean\\thinknode_conditionalanimalshoulddocleaningwork.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:jobs\\jobdriver_clean\\thinknode_conditionalanimalshoulddocleaningwork.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\hediffs\\ara_hivemind\\hediff_hivemindmaster.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:hediffs\\ara_hivemind\\hediff_hivemindmaster.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\\hediffs\\ara_spawner\\hediffcomp_spawner.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:hediffs\\ara_spawner\\hediffcomp_spawner.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\arachnaelog.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:arachnaelog.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\\pawn_comps\\ara_trainingwork\\compadvancedtraining.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:pawn_comps\\ara_trainingwork\\compadvancedtraining.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|e:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\abilities\\ara_showinteractivething\\compabilityeffect_showinteractivething.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:abilities\\ara_showinteractivething\\compabilityeffect_showinteractivething.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|e:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\ara_defof.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:ara_defof.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\\pawn_comps\\ara_automechcarrier\\compautomechcarrier.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:pawn_comps\\ara_automechcarrier\\compautomechcarrier.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\\pawn_comps\\ara_automechcarrier\\compproducedbymechcarrier.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:pawn_comps\\ara_automechcarrier\\compproducedbymechcarrier.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
}
],
"DocumentGroupContainers": [
@@ -30,7 +46,7 @@
"DocumentGroups": [
{
"DockedWidth": 200,
"SelectedChildIndex": 2,
"SelectedChildIndex": 1,
"Children": [
{
"$type": "Bookmark",
@@ -38,66 +54,120 @@
},
{
"$type": "Document",
"DocumentIndex": 1,
"Title": "CompProperties_AbilityFanShapedStunKnockback.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_FanShapedStunKnockback\\CompProperties_AbilityFanShapedStunKnockback.cs",
"RelativeDocumentMoniker": "Abilities\\ARA_FanShapedStunKnockback\\CompProperties_AbilityFanShapedStunKnockback.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_FanShapedStunKnockback\\CompProperties_AbilityFanShapedStunKnockback.cs",
"RelativeToolTip": "Abilities\\ARA_FanShapedStunKnockback\\CompProperties_AbilityFanShapedStunKnockback.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAMAAAAXAAAAAAAAAA==",
"DocumentIndex": 0,
"Title": "ThinkNode_JobGiver_FeedWithHoney.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_FeedWithHoney\\ThinkNode_JobGiver_FeedWithHoney.cs",
"RelativeDocumentMoniker": "Jobs\\JobDriver_FeedWithHoney\\ThinkNode_JobGiver_FeedWithHoney.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_FeedWithHoney\\ThinkNode_JobGiver_FeedWithHoney.cs",
"RelativeToolTip": "Jobs\\JobDriver_FeedWithHoney\\ThinkNode_JobGiver_FeedWithHoney.cs",
"ViewState": "AgIAAEsAAAAAAAAAAAAawGcAAAAZAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-03-27T07:30:03.977Z",
"WhenOpened": "2026-03-29T09:23:09.491Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 0,
"Title": "Safer_PawnFlyer.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_FanShapedStunKnockback\\Safer_PawnFlyer.cs",
"RelativeDocumentMoniker": "Abilities\\ARA_FanShapedStunKnockback\\Safer_PawnFlyer.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_FanShapedStunKnockback\\Safer_PawnFlyer.cs",
"RelativeToolTip": "Abilities\\ARA_FanShapedStunKnockback\\Safer_PawnFlyer.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAA0AAAAJAAAAAAAAAA==",
"DocumentIndex": 1,
"Title": "ThinkNode_ConditionalAnimalShouldDoGrowingWork.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_Plant\\ThinkNode_ConditionalAnimalShouldDoGrowingWork.cs",
"RelativeDocumentMoniker": "Jobs\\JobDriver_Plant\\ThinkNode_ConditionalAnimalShouldDoGrowingWork.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_Plant\\ThinkNode_ConditionalAnimalShouldDoGrowingWork.cs",
"RelativeToolTip": "Jobs\\JobDriver_Plant\\ThinkNode_ConditionalAnimalShouldDoGrowingWork.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAABQAAABPAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-03-27T07:30:02.935Z",
"WhenOpened": "2026-03-29T09:22:20.81Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 2,
"Title": "CompGestalt.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Hediffs\\ARA_GestaltNode\\CompGestalt.cs",
"RelativeDocumentMoniker": "Hediffs\\ARA_GestaltNode\\CompGestalt.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Hediffs\\ARA_GestaltNode\\CompGestalt.cs",
"RelativeToolTip": "Hediffs\\ARA_GestaltNode\\CompGestalt.cs",
"ViewState": "AgIAAEIAAAAAAAAAAAAQwD0AAABCAAAAAAAAAA==",
"Title": "ThinkNode_ConditionalAnimalShouldDoCleaningWork.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_Clean\\ThinkNode_ConditionalAnimalShouldDoCleaningWork.cs",
"RelativeDocumentMoniker": "Jobs\\JobDriver_Clean\\ThinkNode_ConditionalAnimalShouldDoCleaningWork.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_Clean\\ThinkNode_ConditionalAnimalShouldDoCleaningWork.cs",
"RelativeToolTip": "Jobs\\JobDriver_Clean\\ThinkNode_ConditionalAnimalShouldDoCleaningWork.cs",
"ViewState": "AgIAAAoAAAAAAAAAAAAAABEAAAAgAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-03-27T03:57:27.664Z",
"WhenOpened": "2026-03-29T09:22:16.941Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 3,
"Title": "Hediff_HiveMindMaster.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Hediffs\\ARA_HiveMind\\Hediff_HiveMindMaster.cs",
"RelativeDocumentMoniker": "Hediffs\\ARA_HiveMind\\Hediff_HiveMindMaster.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Hediffs\\ARA_HiveMind\\Hediff_HiveMindMaster.cs",
"RelativeToolTip": "Hediffs\\ARA_HiveMind\\Hediff_HiveMindMaster.cs",
"ViewState": "AgIAADQAAAAAAAAAAAAuwFkAAAAuAAAAAAAAAA==",
"Title": "HediffComp_Spawner.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Hediffs\\ARA_Spawner\\HediffComp_Spawner.cs",
"RelativeDocumentMoniker": "Hediffs\\ARA_Spawner\\HediffComp_Spawner.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Hediffs\\ARA_Spawner\\HediffComp_Spawner.cs",
"RelativeToolTip": "Hediffs\\ARA_Spawner\\HediffComp_Spawner.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAIcBAAAmAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-03-27T03:54:02.567Z"
"WhenOpened": "2026-03-29T09:22:11.004Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 4,
"Title": "ArachnaeLog.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\ArachnaeLog.cs",
"RelativeDocumentMoniker": "ArachnaeLog.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\ArachnaeLog.cs",
"RelativeToolTip": "ArachnaeLog.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAABAAAAANAAAAAAAAAA==",
"Title": "CompAdvancedTraining.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Pawn_Comps\\ARA_TrainingWork\\CompAdvancedTraining.cs",
"RelativeDocumentMoniker": "Pawn_Comps\\ARA_TrainingWork\\CompAdvancedTraining.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Pawn_Comps\\ARA_TrainingWork\\CompAdvancedTraining.cs",
"RelativeToolTip": "Pawn_Comps\\ARA_TrainingWork\\CompAdvancedTraining.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAYAAAAwAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-03-26T06:10:50.583Z"
"WhenOpened": "2026-03-29T09:21:10.952Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 5,
"Title": "CompAbilityEffect_ShowInteractiveThing.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_ShowInteractiveThing\\CompAbilityEffect_ShowInteractiveThing.cs",
"RelativeDocumentMoniker": "Abilities\\ARA_ShowInteractiveThing\\CompAbilityEffect_ShowInteractiveThing.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_ShowInteractiveThing\\CompAbilityEffect_ShowInteractiveThing.cs",
"RelativeToolTip": "Abilities\\ARA_ShowInteractiveThing\\CompAbilityEffect_ShowInteractiveThing.cs",
"ViewState": "AgIAAD4AAAAAAAAAAAAAAFIAAAAiAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-03-29T09:21:03.275Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 6,
"Title": "ARA_DefOf.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\ARA_DefOf.cs",
"RelativeDocumentMoniker": "ARA_DefOf.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\ARA_DefOf.cs",
"RelativeToolTip": "ARA_DefOf.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAA4AAABAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-03-29T09:20:59.349Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 8,
"Title": "CompProducedByMechCarrier.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Pawn_Comps\\ARA_AutoMechCarrier\\CompProducedByMechCarrier.cs",
"RelativeDocumentMoniker": "Pawn_Comps\\ARA_AutoMechCarrier\\CompProducedByMechCarrier.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Pawn_Comps\\ARA_AutoMechCarrier\\CompProducedByMechCarrier.cs",
"RelativeToolTip": "Pawn_Comps\\ARA_AutoMechCarrier\\CompProducedByMechCarrier.cs",
"ViewState": "AgIAAJQAAAAAAAAAAAAowOoAAAARAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-03-29T09:06:28.908Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 7,
"Title": "CompAutoMechCarrier.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Pawn_Comps\\ARA_AutoMechCarrier\\CompAutoMechCarrier.cs",
"RelativeDocumentMoniker": "Pawn_Comps\\ARA_AutoMechCarrier\\CompAutoMechCarrier.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Pawn_Comps\\ARA_AutoMechCarrier\\CompAutoMechCarrier.cs",
"RelativeToolTip": "Pawn_Comps\\ARA_AutoMechCarrier\\CompAutoMechCarrier.cs",
"ViewState": "AgIAAIACAAAAAAAAAAAqwI8CAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-03-29T08:49:14.026Z",
"EditorCaption": ""
}
]
}

View File

@@ -238,19 +238,6 @@
<Compile Include="Building_Comps\WULA_MutiFuelSpawner\CompRefuelableWithKey.cs" />
<Compile Include="Building_Comps\WULA_MutiFuelSpawner\IFuelSource.cs" />
<Compile Include="Building_Comps\WULA_MutiFuelSpawner\Patch_CompRefuelableWithKey.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="FacialAnimation\FaceAnimationStaticGenerator.cs" />
<Compile Include="Flyover\ARA_AircraftHangar\CompAbilityEffect_AircraftStrike.cs" />
<Compile Include="Flyover\ARA_AircraftHangar\CompAircraftHangar.cs" />

View File

@@ -1,61 +0,0 @@
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
{
ArachnaeLog.Debug($"[CompOpenCustomUI] Could not find EventDef named '{Props.uiDefName}'.");
}
});
yield return option;
}
}
}

View File

@@ -1,240 +0,0 @@
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)
{
ArachnaeLog.Debug($"[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))
{
ArachnaeLog.Debug($"[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.";
ArachnaeLog.Debug($"[EventSystem] {GetType().Name} check for '{name}' failed: {reason}");
return false;
}
}
bool met = Compare(variable, compareValue);
ArachnaeLog.Debug($"[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)
{
ArachnaeLog.Debug($"[EventSystem] Condition_VariableNotEqual: Could not compare '{variable}' and '{compareValueStr}'. Error: {e.Message}");
reason = "Type mismatch or parsing error during comparison.";
return false;
}
ArachnaeLog.Debug($"[EventSystem] Condition_VariableNotEqual check: Name='{name}', Type='{variable?.GetType().Name ?? "null"}', CurrentValue='{variable}', CompareValue='{compareValueStr}', Met={met}");
if (!met)
{
reason = $"Requires {name} != {compareValueStr} (Current: {variable})";
}
else
{
reason = "";
}
return met;
}
}
public class Condition_FactionExists : Condition
{
public FactionDef factionDef;
public override bool IsMet(out string reason)
{
if (factionDef == null)
{
reason = "FactionDef not specified in Condition_FactionExists.";
return false;
}
bool exists = Find.FactionManager.FirstFactionOfDef(factionDef) != null;
if (!exists)
{
reason = $"Faction '{factionDef.label}' does not exist in the world.";
}
else
{
reason = "";
}
return exists;
}
}
}

View File

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

View File

@@ -1,95 +0,0 @@
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)
{
ArachnaeLog.Debug($"[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
{
ArachnaeLog.Debug($"[WulaFallenEmpire] DelayedActionManager could not find EventDef named '{defName}'");
}
}
public override void ExposeData()
{
base.ExposeData();
Scribe_Collections.Look(ref actions, "delayedActions", LookMode.Deep);
if (actions == null)
{
actions = new List<DelayedAction>();
}
}
}
}

View File

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

View File

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

View File

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

View File

@@ -1,655 +0,0 @@
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
{
ArachnaeLog.Debug("[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
{
ArachnaeLog.Debug($"[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)
{
ArachnaeLog.Debug("[WulaFallenEmpire] Effect_FireIncident has a null incident Def.");
return;
}
IncidentParms parms = new IncidentParms
{
target = Find.CurrentMap,
forced = true
};
if (!incident.Worker.TryExecute(parms))
{
ArachnaeLog.Debug($"[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)
{
ArachnaeLog.Debug("[WulaFallenEmpire] Effect_ChangeFactionRelation has a null faction Def.");
return;
}
Faction targetFaction = Find.FactionManager.FirstFactionOfDef(faction);
if (targetFaction == null)
{
ArachnaeLog.Debug($"[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)
{
ArachnaeLog.Debug("[WulaFallenEmpire] Effect_ChangeFactionRelation_FromVariable has a null faction Def.");
return;
}
Faction targetFaction = Find.FactionManager.FirstFactionOfDef(faction);
if (targetFaction == null)
{
ArachnaeLog.Debug($"[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)
{
ArachnaeLog.Debug("[WulaFallenEmpire] Effect_SpawnPawnAndStore has a null kindDef.");
return;
}
if (storeAs.NullOrEmpty())
{
ArachnaeLog.Debug("[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)
{
ArachnaeLog.Debug("[WulaFallenEmpire] Effect_GiveThing has a null thingDef.");
return;
}
Map currentMap = Find.CurrentMap;
if (currentMap == null)
{
ArachnaeLog.Debug("[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)
{
ArachnaeLog.Debug("[WulaFallenEmpire] Effect_SpawnPawn has a null kindDef.");
return;
}
Map map = Find.CurrentMap;
if (map == null)
{
ArachnaeLog.Debug("[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))
{
ArachnaeLog.Debug("[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)
{
ArachnaeLog.Debug($"[WulaFallenEmpire] Effect_ModifyVariable: valueVariableName '{valueVariableName}' not found.");
return;
}
}
// Get the target variable, or initialize it
object variable = eventVarManager.GetVariable<object>(name);
if (variable == null)
{
ArachnaeLog.Debug($"[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);
}
ArachnaeLog.Debug($"[EventSystem] Modifying variable '{name}'. Operation: {operation}. Value: {valueStr}. From: {originalValue} To: {newValue}");
eventVarManager.SetVariable(name, newValue);
}
catch (System.Exception e)
{
ArachnaeLog.Debug($"[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;
ArachnaeLog.Debug($"[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))
{
ArachnaeLog.Debug("[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)
{
ArachnaeLog.Debug("[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)
{
ArachnaeLog.Debug("[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)
{
ArachnaeLog.Debug("[WulaFallenEmpire] Effect_TriggerRaid cannot execute without a current map.");
return;
}
Faction factionInst = Find.FactionManager.FirstFactionOfDef(this.faction);
if (factionInst == null)
{
ArachnaeLog.Debug($"[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))
{
ArachnaeLog.Debug("[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))
{
ArachnaeLog.Debug("[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);
ArachnaeLog.Debug($"[EventSystem] Storing goodwill for faction '{faction.Name}' ({goodwill}) into variable '{variableName}'.");
eventVarManager.SetVariable(variableName, goodwill);
}
else
{
ArachnaeLog.Debug($"[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))
{
ArachnaeLog.Debug("[WulaFallenEmpire] Effect_StoreRealPlayTime is not configured correctly (missing variableName).");
return;
}
var eventVarManager = Find.World.GetComponent<EventVariableManager>();
float realPlayTime = Find.GameInfo.RealPlayTimeInteracting;
ArachnaeLog.Debug($"[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))
{
ArachnaeLog.Debug("[WulaFallenEmpire] Effect_StoreDaysPassed is not configured correctly (missing variableName).");
return;
}
var eventVarManager = Find.World.GetComponent<EventVariableManager>();
int daysPassed = GenDate.DaysPassed;
ArachnaeLog.Debug($"[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))
{
ArachnaeLog.Debug("[WulaFallenEmpire] Effect_StoreColonyWealth is not configured correctly (missing variableName).");
return;
}
Map currentMap = Find.CurrentMap;
if (currentMap == null)
{
ArachnaeLog.Debug("[WulaFallenEmpire] Effect_StoreColonyWealth cannot execute without a current map.");
return;
}
var eventVarManager = Find.World.GetComponent<EventVariableManager>();
float wealth = currentMap.wealthWatcher.WealthTotal;
ArachnaeLog.Debug($"[EventSystem] Storing colony wealth ({wealth}) into variable '{variableName}'.");
eventVarManager.SetVariable(variableName, wealth);
}
}
}

View File

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

View File

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

View File

@@ -1,177 +0,0 @@
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
ArachnaeLog.Debug($"[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();
ArachnaeLog.Debug($"[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)
{
ArachnaeLog.Debug($"[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))
{
ArachnaeLog.Debug($"[EventSystem] Clearing variable '{name}'.");
}
intVars.Remove(name);
floatVars.Remove(name);
stringVars.Remove(name);
pawnVars.Remove(name);
pawnListVars.Remove(name);
}
public void ClearAll()
{
intVars.Clear();
floatVars.Clear();
stringVars.Clear();
pawnVars.Clear();
pawnListVars.Clear();
}
public Dictionary<string, object> GetAllVariables()
{
var allVars = new Dictionary<string, object>();
foreach (var kvp in intVars) allVars[kvp.Key] = kvp.Value;
foreach (var kvp in floatVars) allVars[kvp.Key] = kvp.Value;
foreach (var kvp in stringVars) allVars[kvp.Key] = kvp.Value;
foreach (var kvp in pawnVars) allVars[kvp.Key] = kvp.Value;
foreach (var kvp in pawnListVars) allVars[kvp.Key] = kvp.Value;
return allVars;
}
}
}

View File

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

View File

@@ -1,49 +0,0 @@
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;
}
}
}

View File

@@ -128,7 +128,6 @@ namespace ArachnaeSwarm
count = entry.count,
cost = entry.cost,
cooldownTicks = entry.cooldownTicks,
conditions = entry.conditions?.ToList(), // 复制条件列表
customInfo = entry.customInfo
};
entryMap[entry.pawnKind] = newEntry;

View File

@@ -32,37 +32,12 @@ namespace ArachnaeSwarm
foreach (var entry in queue)
{
// 检查前置条件
if (CheckEntryConditions(entry, pawn))
{
availableQueue.Add(entry);
}
availableQueue.Add(entry);
}
return availableQueue;
}
/// <summary>
/// 检查条目条件是否满足
/// </summary>
private bool CheckEntryConditions(PawnProductionEntry entry, Pawn pawn)
{
// 检查自定义条件
if (!entry.conditions.NullOrEmpty())
{
foreach (var condition in entry.conditions)
{
string reason;
if (!condition.IsMet(out reason))
{
return false;
}
}
}
return true;
}
/// <summary>
/// 根据PawnKindDef查找生产队列条目
/// </summary>

View File

@@ -98,7 +98,7 @@ namespace ArachnaeSwarm
return true;
// 检查是否和喂养者同一派系
if (target.Faction != null && target.Faction == feeder.Faction)
if (target.Faction != null && target.Faction == feeder.Faction && !target.IsAnimal)
return true;
return false;

View File

@@ -5,6 +5,7 @@ using System.Linq;
using System.Reflection;
using UnityEngine;
using Verse;
using Verse.AI;
using Verse.AI.Group;
namespace ArachnaeSwarm
@@ -654,7 +655,13 @@ namespace ArachnaeSwarm
}
if (parent is Pawn p && p.GetLord() != null)
{
p.GetLord().AddPawn(pawn);
if (pawn.mindState.duty == null && p.mindState.duty != null)
{
pawn.mindState.duty = p.mindState.duty;
}
}
if (!AutoProps.freeProduction)
{

View File

@@ -230,11 +230,19 @@ namespace ArachnaeSwarm
// 检查生产者是否仍然有效
if (producer != null && (producer.Destroyed || (producer is Pawn p && (p.Dead || p.Downed))))
{
Log.Warning($"生产者无效: {producer?.LabelCap}, 清除引用");
producer = null;
producerComp = null;
}
else if (producer != null && producer is Pawn)
{
Pawn pawnProducer = producer as Pawn;
Pawn creation = parent as Pawn;
if (creation.mindState.duty == null && pawnProducer.mindState.duty != null)
{
creation.mindState.duty = pawnProducer.mindState.duty;
}
}
// 检查自身状态
CheckSelfStatus();
}
@@ -252,7 +260,6 @@ namespace ArachnaeSwarm
{
wasDead = true;
deathTick = Find.TickManager.TicksGame;
Log.Message($"子单位死亡: {pawn.LabelCap}, 准备通知生产者");
}
// 死亡后延迟清理

View File

@@ -26,9 +26,6 @@ namespace ArachnaeSwarm
// Optional: custom information for display
public string customInfo;
// Optional: conditions that must be met for this entry to be available
public List<Condition> conditions;
// Optional: priority (lower number = higher priority)
public int priority = 0;
@@ -76,21 +73,6 @@ namespace ArachnaeSwarm
return false;
}
}
// 检查自定义条件
if (!conditions.NullOrEmpty())
{
foreach (var condition in conditions)
{
string conditionReason;
if (!condition.IsMet(out conditionReason))
{
reason = conditionReason;
return false;
}
}
}
return true;
}
@@ -106,7 +88,6 @@ namespace ArachnaeSwarm
cooldownTicks = cooldownTicks,
cost = cost,
customInfo = customInfo,
conditions = conditions?.ToList(), // 深拷贝条件列表
priority = priority,
weight = weight,
enabled = enabled,

Binary file not shown.