This commit is contained in:
2026-03-27 08:10:23 +08:00
parent 5b4ccf8eb4
commit 75717ba814
19 changed files with 12 additions and 5393 deletions

Binary file not shown.

View File

@@ -587,600 +587,6 @@
</parts> </parts>
</corePart> </corePart>
</BodyDef> </BodyDef>
<BodyDef>
<defName>ArachnaeQueen_Neurotyrant_Body</defName>
<label>阿拉克涅灵吸种</label>
<corePart>
<def>Torso</def>
<height>Middle</height>
<depth>Outside</depth>
<groups>
<li>Torso</li>
</groups>
<parts>
<!-- 背部组织,仅包含骨骼和甲片 -->
<li>
<def>ARA_Dorsum</def>
<coverage>0.14</coverage>
<groups>
<li>Torso</li>
</groups>
<parts>
<!-- 甲片,防御作用 -->
<li>
<def>ARA_Chitin_Shell</def>
<customLabel>背部甲片</customLabel>
<coverage>0.02</coverage>
<depth>Outside</depth>
<groups>
<li>Torso</li>
</groups>
</li>
<!-- 背部外骨骼,承担托举四肢和代替脊椎 -->
<li>
<def>ARA_Exoskeleton_Dorsum</def>
<coverage>0.016</coverage>
<depth>Inside</depth>
<groups>
<li>Torso</li>
</groups>
</li>
</parts>
</li>
<!-- 胸部组织,包含骨骼和各类重点维生器官 -->
<li>
<def>ARA_Sternum</def>
<coverage>0.15</coverage>
<groups>
<li>Torso</li>
</groups>
<parts>
<!-- 胸部外骨骼,代替肋骨,因为没有甲片所以是外露的 -->
<li>
<def>ARA_Exoskeleton_Sternum</def>
<coverage>0.015</coverage>
<depth>Outside</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>Stomach</def>
<coverage>0.025</coverage>
<depth>Inside</depth>
<groups>
<li>Torso</li>
</groups>
</li>
<!-- 肺,4个 -->
<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>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>
<!-- 肾脏,2个 -->
<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>
<!-- 肝脏,2个 -->
<li>
<def>Liver</def>
<coverage>0.025</coverage>
<customLabel>肝脏(其一)</customLabel>
<depth>Inside</depth>
<groups>
<li>Torso</li>
</groups>
</li>
<li>
<def>Liver</def>
<customLabel>肝脏(其二)</customLabel>
<coverage>0.025</coverage>
<depth>Inside</depth>
<groups>
<li>Torso</li>
</groups>
</li>
</parts>
</li>
<!-- 尾部组织群 -->
<li>
<def>ARA_Tail</def>
<height>Bottom</height>
<depth>Inside</depth>
<coverage>0.15</coverage>
<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_Gonad</def>
<coverage>0.005</coverage>
<depth>Inside</depth>
<groups>
<li>ARA_Genitalias</li>
</groups>
</li>
<!-- 卵巢 -->
<li>
<def>ARA_Ovary</def>
<coverage>0.01</coverage>
<depth>Inside</depth>
<groups>
<li>ARA_Genitalias</li>
</groups>
</li>
</parts>
</li>
<!-- 脖子往上头部 -->
<li>
<def>Neck</def>
<coverage>0.075</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>ARA_Chitin_Shell</def>
<customLabel>头部甲片</customLabel>
<coverage>0.02</coverage>
<depth>Outside</depth>
<groups>
<li>UpperHead</li>
</groups>
</li>
<!-- 神经束群 -->
<li>
<def>ARA_Psy_Nerve_Tract</def>
<customLabel>第一神经束</customLabel>
<coverage>0.05</coverage>
<depth>Inside</depth>
<groups>
<li>ARA_Psy_Nerve_Tracts</li>
</groups>
</li>
<li>
<def>ARA_Psy_Nerve_Tract</def>
<customLabel>第二神经束</customLabel>
<coverage>0.05</coverage>
<depth>Inside</depth>
<groups>
<li>ARA_Psy_Nerve_Tracts</li>
</groups>
</li>
<li>
<def>ARA_Psy_Nerve_Tract</def>
<customLabel>第三神经束</customLabel>
<coverage>0.05</coverage>
<depth>Inside</depth>
<groups>
<li>ARA_Psy_Nerve_Tracts</li>
</groups>
</li>
<li>
<def>ARA_Psy_Nerve_Tract</def>
<customLabel>第四神经束</customLabel>
<coverage>0.05</coverage>
<depth>Inside</depth>
<groups>
<li>ARA_Psy_Nerve_Tracts</li>
</groups>
</li>
<li>
<def>ARA_Psy_Nerve_Tract</def>
<customLabel>第四神经束</customLabel>
<coverage>0.05</coverage>
<depth>Inside</depth>
<groups>
<li>ARA_Psy_Nerve_Tracts</li>
</groups>
</li>
<li>
<def>ARA_Psy_Nerve_Tract</def>
<customLabel>第五神经束</customLabel>
<coverage>0.05</coverage>
<depth>Inside</depth>
<groups>
<li>ARA_Psy_Nerve_Tracts</li>
</groups>
</li>
<li>
<def>ARA_Psy_Nerve_Tract</def>
<customLabel>第六四神经束</customLabel>
<coverage>0.05</coverage>
<depth>Inside</depth>
<groups>
<li>ARA_Psy_Nerve_Tracts</li>
</groups>
</li>
<li>
<def>ARA_Psy_Nerve_Tract</def>
<customLabel>第七神经束</customLabel>
<coverage>0.05</coverage>
<depth>Inside</depth>
<groups>
<li>ARA_Psy_Nerve_Tracts</li>
</groups>
</li>
<li>
<def>ARA_Psy_Nerve_Tract</def>
<customLabel>第八神经束</customLabel>
<coverage>0.05</coverage>
<depth>Inside</depth>
<groups>
<li>ARA_Psy_Nerve_Tracts</li>
</groups>
</li>
<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>left ear</customLabel>
<coverage>0.07</coverage>
<flipGraphic>true</flipGraphic>
<groups>
<li>UpperHead</li>
<li>FullHead</li>
</groups>
</li>
<li>
<def>Ear</def>
<customLabel>right ear</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>
<flipGraphic>true</flipGraphic>
<groups>
<li>Shoulders</li>
</groups>
<parts>
<li>
<def>Clavicle</def>
<customLabel>左副肢外骨骼</customLabel>
<coverage>0.09</coverage>
<height>Top</height>
<depth>Outside</depth>
<flipGraphic>true</flipGraphic>
<groups>
<li>Shoulders</li>
</groups>
</li>
<li>
<def>Arm</def>
<customLabel>左副肢</customLabel>
<coverage>0.77</coverage>
<flipGraphic>true</flipGraphic>
<groups>
<li>Arms</li>
</groups>
<parts>
<li>
<def>Hand</def>
<customLabel>左鳌钳</customLabel>
<coverage>0.14</coverage>
<height>Bottom</height>
<flipGraphic>true</flipGraphic>
<groups>
<li>Hands</li>
<li>HeadClaw</li>
</groups>
<parts>
<li>
<def>Finger</def>
<customLabel>右鳌钳口</customLabel>
<coverage>0.14</coverage>
<groups>
<li>Hands</li>
<li>LeftHand</li>
</groups>
</li>
</parts>
</li>
</parts>
</li>
</parts>
</li>
<li>
<def>Shoulder</def>
<customLabel>右副肢关节</customLabel>
<coverage>0.12</coverage>
<woundAnchorTag>LeftShoulder</woundAnchorTag>
<flipGraphic>true</flipGraphic>
<groups>
<li>Shoulders</li>
</groups>
<parts>
<li>
<def>Clavicle</def>
<customLabel>右副肢外骨骼</customLabel>
<coverage>0.09</coverage>
<height>Top</height>
<depth>Outside</depth>
<flipGraphic>true</flipGraphic>
<groups>
<li>Shoulders</li>
</groups>
</li>
<li>
<def>Arm</def>
<customLabel>右副肢</customLabel>
<coverage>0.77</coverage>
<flipGraphic>true</flipGraphic>
<groups>
<li>Arms</li>
</groups>
<parts>
<li>
<def>Hand</def>
<customLabel>右鳌钳</customLabel>
<coverage>0.14</coverage>
<height>Bottom</height>
<flipGraphic>true</flipGraphic>
<groups>
<li>Hands</li>
<li>HeadClaw</li>
</groups>
<parts>
<li>
<def>Finger</def>
<customLabel>右鳌钳口</customLabel>
<coverage>0.14</coverage>
<groups>
<li>Hands</li>
<li>RightHand</li>
</groups>
</li>
</parts>
</li>
</parts>
</li>
</parts>
</li>
<!-- 腰尾链接 -->
<li>
<def>Waist</def>
<customLabel>体节分界</customLabel>
<coverage>0</coverage>
<height>Bottom</height>
<groups>
<li>Waist</li>
</groups>
</li>
<li>
<def>Leg</def>
<customLabel>左肢</customLabel>
<coverage>0.06</coverage>
<height>Bottom</height>
<groups>
<li>Legs</li>
</groups>
<woundAnchorTag>LeftLeg</woundAnchorTag>
<flipGraphic>true</flipGraphic>
<parts>
<!-- 甲片,防御作用 -->
<li>
<def>ARA_Chitin_Shell</def>
<customLabel>左肢甲片</customLabel>
<coverage>0.02</coverage>
<depth>Outside</depth>
<groups>
<li>Legs</li>
</groups>
</li>
<!-- 骨骼 -->
<li>
<def>Femur</def>
<customLabel>左肢外骨骼</customLabel>
<coverage>0.1</coverage>
<depth>Inside</depth>
<groups>
<li>Legs</li>
</groups>
</li>
</parts>
</li>
<li>
<def>Leg</def>
<customLabel>右肢</customLabel>
<coverage>0.06</coverage>
<height>Bottom</height>
<groups>
<li>Legs</li>
</groups>
<woundAnchorTag>LeftLeg</woundAnchorTag>
<flipGraphic>true</flipGraphic>
<parts>
<!-- 甲片,防御作用 -->
<li>
<def>ARA_Chitin_Shell</def>
<customLabel>右肢甲片</customLabel>
<coverage>0.02</coverage>
<depth>Outside</depth>
<groups>
<li>Legs</li>
</groups>
</li>
<!-- 骨骼 -->
<li>
<def>Femur</def>
<customLabel>右肢外骨骼</customLabel>
<coverage>0.1</coverage>
<depth>Inside</depth>
<groups>
<li>Legs</li>
</groups>
</li>
</parts>
</li>
</parts>
</corePart>
</BodyDef>
<BodyPartDef> <BodyPartDef>
<defName>ARA_Dorsum</defName> <defName>ARA_Dorsum</defName>
<label>背部组织群</label> <label>背部组织群</label>

View File

@@ -100,18 +100,6 @@
<displayPriorityInCategory>2201</displayPriorityInCategory> <displayPriorityInCategory>2201</displayPriorityInCategory>
<showIfUndefined>false</showIfUndefined> <showIfUndefined>false</showIfUndefined>
</StatDef> </StatDef>
<StatDef>
<defName>ARA_SwarmSpell_Cooldown_Delay</defName>
<label>灵能神经束冷却延迟(秒)</label>
<description>当阿拉克涅灵吸种使用虫群灵能能力时,产生的灵能负荷会在此时间后开始逐渐下降,任何施术都会重置冷却时间。</description>
<category>Basics</category>
<defaultBaseValue>0</defaultBaseValue>
<toStringStyle>Integer</toStringStyle>
<hideAtValue>0</hideAtValue>
<minValue>0</minValue>
<displayPriorityInCategory>2201</displayPriorityInCategory>
<showIfUndefined>false</showIfUndefined>
</StatDef>
<StatDef> <StatDef>
<defName>ARA_SwarmSpell_Level</defName> <defName>ARA_SwarmSpell_Level</defName>
<label>术法等级</label> <label>术法等级</label>

View File

@@ -3,100 +3,8 @@
"WorkspaceRootPath": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\", "WorkspaceRootPath": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\",
"Documents": [ "Documents": [
{ {
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|d:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\hediffs\\hediffcomp_reflectmeleedamage.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:hediffs\\hediffcomp_reflectmeleedamage.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|D:\\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|D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\jobs\\jobdriver_supercarry\\jobdriver_supercarry.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:jobs\\jobdriver_supercarry\\jobdriver_supercarry.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\\jobs\\jobdriver_swarmmaintain\\jobdriver_swarmmaintain.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:jobs\\jobdriver_swarmmaintain\\jobdriver_swarmmaintain.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\\jobs\\jobdriver_swarmmaintain\\jobgiver_swarmmaintain.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:jobs\\jobdriver_swarmmaintain\\jobgiver_swarmmaintain.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\\jobs\\jobdriver_swarmmaintain\\thinknode_conditionalshouldmaintain.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:jobs\\jobdriver_swarmmaintain\\thinknode_conditionalshouldmaintain.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\\damage\\damageworker_explosionwithterrain.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:damage\\damageworker_explosionwithterrain.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\\damage\\damagedefextension_terraincover.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:damage\\damagedefextension_terraincover.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\\jobs\\jobdriver_stripchitin\\jobdriver_stripchitin.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:jobs\\jobdriver_stripchitin\\jobdriver_stripchitin.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\\jobs\\jobdriver_feedwithhoney\\jobdriver_feedwithhoney.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:jobs\\jobdriver_feedwithhoney\\jobdriver_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\\jobs\\jobdriver_extracthoney\\jobgiver_extracthoney.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:jobs\\jobdriver_extracthoney\\jobgiver_extracthoney.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\\jobs\\jobdriver_extracthoney\\jobdriver_extracthoney.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:jobs\\jobdriver_extracthoney\\jobdriver_extracthoney.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\\compabilityeffect_toggledroppodintercept.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:abilities\\compabilityeffect_toggledroppodintercept.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\\flyover\\gamecomponent_droppodinterceptor.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:flyover\\gamecomponent_droppodinterceptor.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\\flyover\\ara_aircrafthangar\\worldcomponent_aircraftmanager.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:flyover\\ara_aircrafthangar\\worldcomponent_aircraftmanager.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\hediffs\\ara_gestaltnode\\hediffcomp_gestaltnode.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:hediffs\\ara_gestaltnode\\hediffcomp_gestaltnode.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\pawn_comps\\ara_swarmspellholder\\comp_swarmspellholder.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:pawn_comps\\ara_swarmspellholder\\comp_swarmspellholder.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\\compabilityeffect_fanshapedstunknockback.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:abilities\\ara_fanshapedstunknockback\\compabilityeffect_fanshapedstunknockback.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|D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\buildings\\building_researchblueprintreader\\researchblueprintreadermanager.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:buildings\\building_researchblueprintreader\\researchblueprintreadermanager.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\\buildings\\building_researchblueprintreader\\building_researchblueprintreader.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:buildings\\building_researchblueprintreader\\building_researchblueprintreader.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_ejectorgans\\compabilityeffect_ejectorgans.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:abilities\\ara_ejectorgans\\compabilityeffect_ejectorgans.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\\pawn_comps\\ara_comphediffgiver\\compproperties_hediffgiver.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:pawn_comps\\ara_comphediffgiver\\compproperties_hediffgiver.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\\pawn_comps\\ara_comphediffgiver\\comphediffgiver.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:pawn_comps\\ara_comphediffgiver\\comphediffgiver.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
} }
], ],
"DocumentGroupContainers": [ "DocumentGroupContainers": [
@@ -106,300 +14,24 @@
"DocumentGroups": [ "DocumentGroups": [
{ {
"DockedWidth": 200, "DockedWidth": 200,
"SelectedChildIndex": 2, "SelectedChildIndex": 1,
"Children": [ "Children": [
{ {
"$type": "Bookmark", "$type": "Bookmark",
"Name": "ST:0:0:{1c4feeaa-4718-4aa9-859d-94ce25d182ba}" "Name": "ST:0:0:{1c4feeaa-4718-4aa9-859d-94ce25d182ba}"
}, },
{
"$type": "Document",
"DocumentIndex": 1,
"Title": "ARA_DefOf.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\ARA_DefOf.cs",
"RelativeDocumentMoniker": "ARA_DefOf.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\ARA_DefOf.cs",
"RelativeToolTip": "ARA_DefOf.cs",
"ViewState": "AgIAAFQAAAAAAAAAAAAUwGcAAAAmAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-03-24T08:34:43.383Z"
},
{ {
"$type": "Document", "$type": "Document",
"DocumentIndex": 0, "DocumentIndex": 0,
"Title": "HediffComp_ReflectMeleeDamage.cs", "Title": "ArachnaeLog.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Hediffs\\HediffComp_ReflectMeleeDamage.cs", "DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\ArachnaeLog.cs",
"RelativeDocumentMoniker": "Hediffs\\HediffComp_ReflectMeleeDamage.cs", "RelativeDocumentMoniker": "ArachnaeLog.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Hediffs\\HediffComp_ReflectMeleeDamage.cs", "ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\ArachnaeLog.cs",
"RelativeToolTip": "Hediffs\\HediffComp_ReflectMeleeDamage.cs", "RelativeToolTip": "ArachnaeLog.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAABIAAAAoAAAAAAAAAA==", "ViewState": "AgIAAAAAAAAAAAAAAAAAAA0AAAAwAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-03-24T08:33:52.75Z", "WhenOpened": "2026-03-26T06:10:50.583Z",
"EditorCaption": "" "EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 2,
"Title": "JobDriver_SuperCarry.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_SuperCarry\\JobDriver_SuperCarry.cs",
"RelativeDocumentMoniker": "Jobs\\JobDriver_SuperCarry\\JobDriver_SuperCarry.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_SuperCarry\\JobDriver_SuperCarry.cs",
"RelativeToolTip": "Jobs\\JobDriver_SuperCarry\\JobDriver_SuperCarry.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAA8AAAA3AAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-03-06T03:21:58.524Z"
},
{
"$type": "Document",
"DocumentIndex": 3,
"Title": "JobDriver_SwarmMaintain.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_SwarmMaintain\\JobDriver_SwarmMaintain.cs",
"RelativeDocumentMoniker": "Jobs\\JobDriver_SwarmMaintain\\JobDriver_SwarmMaintain.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_SwarmMaintain\\JobDriver_SwarmMaintain.cs",
"RelativeToolTip": "Jobs\\JobDriver_SwarmMaintain\\JobDriver_SwarmMaintain.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAABYAAAAXAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-03-06T03:21:31.585Z"
},
{
"$type": "Document",
"DocumentIndex": 4,
"Title": "JobGiver_SwarmMaintain.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_SwarmMaintain\\JobGiver_SwarmMaintain.cs",
"RelativeDocumentMoniker": "Jobs\\JobDriver_SwarmMaintain\\JobGiver_SwarmMaintain.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_SwarmMaintain\\JobGiver_SwarmMaintain.cs",
"RelativeToolTip": "Jobs\\JobDriver_SwarmMaintain\\JobGiver_SwarmMaintain.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-03-06T03:21:34.528Z"
},
{
"$type": "Document",
"DocumentIndex": 5,
"Title": "ThinkNode_ConditionalShouldMaintain.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_SwarmMaintain\\ThinkNode_ConditionalShouldMaintain.cs",
"RelativeDocumentMoniker": "Jobs\\JobDriver_SwarmMaintain\\ThinkNode_ConditionalShouldMaintain.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_SwarmMaintain\\ThinkNode_ConditionalShouldMaintain.cs",
"RelativeToolTip": "Jobs\\JobDriver_SwarmMaintain\\ThinkNode_ConditionalShouldMaintain.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-03-06T03:21:32.052Z"
},
{
"$type": "Document",
"DocumentIndex": 6,
"Title": "DamageWorker_ExplosionWithTerrain.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Damage\\DamageWorker_ExplosionWithTerrain.cs",
"RelativeDocumentMoniker": "Damage\\DamageWorker_ExplosionWithTerrain.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Damage\\DamageWorker_ExplosionWithTerrain.cs",
"RelativeToolTip": "Damage\\DamageWorker_ExplosionWithTerrain.cs",
"ViewState": "AgIAAA0AAAAAAAAAAAAQwB4AAAARAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-03-06T01:33:29.803Z"
},
{
"$type": "Document",
"DocumentIndex": 7,
"Title": "DamageDefExtension_TerrainCover.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Damage\\DamageDefExtension_TerrainCover.cs",
"RelativeDocumentMoniker": "Damage\\DamageDefExtension_TerrainCover.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Damage\\DamageDefExtension_TerrainCover.cs",
"RelativeToolTip": "Damage\\DamageDefExtension_TerrainCover.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAACQAAAAYAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-03-06T01:33:24.976Z"
},
{
"$type": "Document",
"DocumentIndex": 8,
"Title": "JobDriver_StripChitin.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_StripChitin\\JobDriver_StripChitin.cs",
"RelativeDocumentMoniker": "Jobs\\JobDriver_StripChitin\\JobDriver_StripChitin.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_StripChitin\\JobDriver_StripChitin.cs",
"RelativeToolTip": "Jobs\\JobDriver_StripChitin\\JobDriver_StripChitin.cs",
"ViewState": "AgIAAAAAAAAAAAAAAADwvyEAAAAQAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-03-03T15:51:49.621Z"
},
{
"$type": "Document",
"DocumentIndex": 9,
"Title": "JobDriver_FeedWithHoney.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_FeedWithHoney\\JobDriver_FeedWithHoney.cs",
"RelativeDocumentMoniker": "Jobs\\JobDriver_FeedWithHoney\\JobDriver_FeedWithHoney.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_FeedWithHoney\\JobDriver_FeedWithHoney.cs",
"RelativeToolTip": "Jobs\\JobDriver_FeedWithHoney\\JobDriver_FeedWithHoney.cs",
"ViewState": "AgIAAAAAAAAAAAAAAADwvyUAAAAfAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-03-03T15:51:26.648Z"
},
{
"$type": "Document",
"DocumentIndex": 10,
"Title": "JobGiver_ExtractHoney.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_ExtractHoney\\JobGiver_ExtractHoney.cs",
"RelativeDocumentMoniker": "Jobs\\JobDriver_ExtractHoney\\JobGiver_ExtractHoney.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_ExtractHoney\\JobGiver_ExtractHoney.cs",
"RelativeToolTip": "Jobs\\JobDriver_ExtractHoney\\JobGiver_ExtractHoney.cs",
"ViewState": "AgIAAAAAAAAAAAAAAADwvwAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-03-03T15:51:21.323Z"
},
{
"$type": "Document",
"DocumentIndex": 11,
"Title": "JobDriver_ExtractHoney.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_ExtractHoney\\JobDriver_ExtractHoney.cs",
"RelativeDocumentMoniker": "Jobs\\JobDriver_ExtractHoney\\JobDriver_ExtractHoney.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_ExtractHoney\\JobDriver_ExtractHoney.cs",
"RelativeToolTip": "Jobs\\JobDriver_ExtractHoney\\JobDriver_ExtractHoney.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAuwBYAAAAIAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-03-03T15:50:37.468Z"
},
{
"$type": "Document",
"DocumentIndex": 14,
"Title": "WorldComponent_AircraftManager.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_AircraftHangar\\WorldComponent_AircraftManager.cs",
"RelativeDocumentMoniker": "Flyover\\ARA_AircraftHangar\\WorldComponent_AircraftManager.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_AircraftHangar\\WorldComponent_AircraftManager.cs",
"RelativeToolTip": "Flyover\\ARA_AircraftHangar\\WorldComponent_AircraftManager.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-02-24T02:36:37.479Z"
},
{
"$type": "Document",
"DocumentIndex": 13,
"Title": "GameComponent_DropPodInterceptor.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\GameComponent_DropPodInterceptor.cs",
"RelativeDocumentMoniker": "Flyover\\GameComponent_DropPodInterceptor.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\GameComponent_DropPodInterceptor.cs",
"RelativeToolTip": "Flyover\\GameComponent_DropPodInterceptor.cs",
"ViewState": "AgIAAD0AAAAAAAAAAAA+wFcAAAAiAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-02-24T02:33:53.514Z"
},
{
"$type": "Document",
"DocumentIndex": 12,
"Title": "CompAbilityEffect_ToggleDropPodIntercept.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\CompAbilityEffect_ToggleDropPodIntercept.cs",
"RelativeDocumentMoniker": "Abilities\\CompAbilityEffect_ToggleDropPodIntercept.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\CompAbilityEffect_ToggleDropPodIntercept.cs",
"RelativeToolTip": "Abilities\\CompAbilityEffect_ToggleDropPodIntercept.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAYAAAAFAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-02-24T02:33:22.192Z"
},
{
"$type": "Document",
"DocumentIndex": 15,
"Title": "HediffComp_GestaltNode.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Hediffs\\ARA_GestaltNode\\HediffComp_GestaltNode.cs",
"RelativeDocumentMoniker": "Hediffs\\ARA_GestaltNode\\HediffComp_GestaltNode.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Hediffs\\ARA_GestaltNode\\HediffComp_GestaltNode.cs",
"RelativeToolTip": "Hediffs\\ARA_GestaltNode\\HediffComp_GestaltNode.cs",
"ViewState": "AgIAANwAAAAAAAAAAAAEwPIAAAARAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-02-15T13:37:57.93Z"
},
{
"$type": "Document",
"DocumentIndex": 16,
"Title": "Comp_SwarmSpellHolder.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Pawn_Comps\\ARA_SwarmSpellHolder\\Comp_SwarmSpellHolder.cs",
"RelativeDocumentMoniker": "Pawn_Comps\\ARA_SwarmSpellHolder\\Comp_SwarmSpellHolder.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Pawn_Comps\\ARA_SwarmSpellHolder\\Comp_SwarmSpellHolder.cs",
"RelativeToolTip": "Pawn_Comps\\ARA_SwarmSpellHolder\\Comp_SwarmSpellHolder.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAK4CAAAIAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-02-15T09:09:37.135Z"
},
{
"$type": "Document",
"DocumentIndex": 20,
"Title": "Building_ResearchBlueprintReader.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_ResearchBlueprintReader\\Building_ResearchBlueprintReader.cs",
"RelativeDocumentMoniker": "Buildings\\Building_ResearchBlueprintReader\\Building_ResearchBlueprintReader.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_ResearchBlueprintReader\\Building_ResearchBlueprintReader.cs",
"RelativeToolTip": "Buildings\\Building_ResearchBlueprintReader\\Building_ResearchBlueprintReader.cs",
"ViewState": "AgIAAM4BAAAAAAAAAAAMwOsBAABYAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-02-15T08:15:16.626Z"
},
{
"$type": "Document",
"DocumentIndex": 19,
"Title": "ResearchBlueprintReaderManager.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_ResearchBlueprintReader\\ResearchBlueprintReaderManager.cs",
"RelativeDocumentMoniker": "Buildings\\Building_ResearchBlueprintReader\\ResearchBlueprintReaderManager.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_ResearchBlueprintReader\\ResearchBlueprintReaderManager.cs",
"RelativeToolTip": "Buildings\\Building_ResearchBlueprintReader\\ResearchBlueprintReaderManager.cs",
"ViewState": "AgIAAKMCAAAAAAAAAAAnwL8CAAA4AAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-02-15T08:26:17.331Z"
},
{
"$type": "Document",
"DocumentIndex": 18,
"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": "AgIAABMAAAAAAAAAAAAAADIAAAAWAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-02-15T06:29:46.581Z"
},
{
"$type": "Document",
"DocumentIndex": 17,
"Title": "CompAbilityEffect_FanShapedStunKnockback.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_FanShapedStunKnockback\\CompAbilityEffect_FanShapedStunKnockback.cs",
"RelativeDocumentMoniker": "Abilities\\ARA_FanShapedStunKnockback\\CompAbilityEffect_FanShapedStunKnockback.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_FanShapedStunKnockback\\CompAbilityEffect_FanShapedStunKnockback.cs",
"RelativeToolTip": "Abilities\\ARA_FanShapedStunKnockback\\CompAbilityEffect_FanShapedStunKnockback.cs",
"ViewState": "AgIAAOMAAAAAAAAAAAAuwPcAAAARAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-02-15T06:29:34.172Z"
},
{
"$type": "Document",
"DocumentIndex": 21,
"Title": "CompAbilityEffect_EjectOrgans.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_EjectOrgans\\CompAbilityEffect_EjectOrgans.cs",
"RelativeDocumentMoniker": "Abilities\\ARA_EjectOrgans\\CompAbilityEffect_EjectOrgans.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_EjectOrgans\\CompAbilityEffect_EjectOrgans.cs",
"RelativeToolTip": "Abilities\\ARA_EjectOrgans\\CompAbilityEffect_EjectOrgans.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAQAAAAXAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-02-15T06:29:29.494Z"
},
{
"$type": "Document",
"DocumentIndex": 23,
"Title": "CompHediffGiver.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Pawn_Comps\\ARA_CompHediffGiver\\CompHediffGiver.cs",
"RelativeDocumentMoniker": "Pawn_Comps\\ARA_CompHediffGiver\\CompHediffGiver.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Pawn_Comps\\ARA_CompHediffGiver\\CompHediffGiver.cs",
"RelativeToolTip": "Pawn_Comps\\ARA_CompHediffGiver\\CompHediffGiver.cs",
"ViewState": "AgIAAFAAAAAAAAAAAAAQwBEAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-02-15T01:45:35.176Z"
},
{
"$type": "Document",
"DocumentIndex": 22,
"Title": "CompProperties_HediffGiver.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Pawn_Comps\\ARA_CompHediffGiver\\CompProperties_HediffGiver.cs",
"RelativeDocumentMoniker": "Pawn_Comps\\ARA_CompHediffGiver\\CompProperties_HediffGiver.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Pawn_Comps\\ARA_CompHediffGiver\\CompProperties_HediffGiver.cs",
"RelativeToolTip": "Pawn_Comps\\ARA_CompHediffGiver\\CompProperties_HediffGiver.cs",
"ViewState": "AgIAAAgAAAAAAAAAAAAAAC8AAAAWAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-02-15T01:43:54.125Z"
} }
] ]
} }

View File

@@ -1,305 +0,0 @@
using RimWorld;
using System;
using System.Linq;
using System.Text;
using UnityEngine;
using Verse;
using Verse.AI;
namespace ArachnaeSwarm
{
public class CompAbilityEffect_PsychicLoadCost : CompAbilityEffect
{
public new CompProperties_AbilityPsychicLoadCost Props => (CompProperties_AbilityPsychicLoadCost)props;
public float ActualLoadCost => Props.useFixedCost ? Props.fixedLoadCost : Props.loadCostRange.RandomInRange;
public float GetPredictedLoadCost(bool useMaxCost = false)
{
if (Props.useFixedCost)
{
return Props.fixedLoadCost;
}
return useMaxCost ? Props.loadCostRange.max : Props.loadCostRange.Average;
}
public static float CalculateAbilityLoadCost(Ability ability, bool useMaxCost = false)
{
if (ability == null || ability.comps.NullOrEmpty())
{
return 0f;
}
float totalCost = 0f;
foreach (CompAbilityEffect_PsychicLoadCost loadComp in ability.CompsOfType<CompAbilityEffect_PsychicLoadCost>())
{
totalCost += loadComp.GetPredictedLoadCost(useMaxCost);
}
return totalCost;
}
public static float TotalQueuedLoadCost(Pawn pawn, bool useMaxCost = false)
{
if (pawn?.jobs == null)
{
return 0f;
}
float queuedCost = 0f;
if (pawn.jobs.curJob?.verbToUse is Verb_CastAbility currentVerb)
{
queuedCost += CalculateAbilityLoadCost(currentVerb.ability, useMaxCost);
}
for (int i = 0; i < pawn.jobs.jobQueue.Count; i++)
{
QueuedJob queuedJob = pawn.jobs.jobQueue[i];
if (queuedJob.job?.verbToUse is Verb_CastAbility queuedVerb)
{
queuedCost += CalculateAbilityLoadCost(queuedVerb.ability, useMaxCost);
}
}
return queuedCost;
}
public override void Apply(LocalTargetInfo target, LocalTargetInfo dest)
{
base.Apply(target, dest);
Pawn caster = parent.pawn;
if (caster == null || caster.Dead || caster.Downed)
{
return;
}
Comp_SwarmSpellHolder spellHolder = caster.TryGetComp<Comp_SwarmSpellHolder>();
if (spellHolder == null || !spellHolder.IsSystemInitialized)
{
Log.Warning($"[虫群术法] {caster.LabelCap} 没有有效的术法组件");
return;
}
float loadCost = ActualLoadCost;
bool wasOverloaded = spellHolder.IsOverloaded;
spellHolder.AddPsychicLoad(loadCost, "ARA_UsePsychicLoadSkill".Translate(parent.def.label));
bool isNowOverloaded = spellHolder.IsOverloaded;
if (!wasOverloaded && isNowOverloaded)
{
Messages.Message("ARA_SwarmSpell_Load_OverloadWarning".Translate(caster.LabelShortCap), caster, MessageTypeDefOf.NegativeEvent);
}
if (wasOverloaded && Props.applyOverloadPenalty)
{
ApplyOverloadPenalty(caster, target);
}
}
private void ApplyOverloadPenalty(Pawn caster, LocalTargetInfo target)
{
try
{
if (Props.overloadPenaltyHediff != null)
{
Hediff hediff = HediffMaker.MakeHediff(Props.overloadPenaltyHediff, caster);
caster.health.AddHediff(hediff);
}
if (Props.destroyConsciousnessSource)
{
DestroyConsciousnessSourceParts(caster);
}
Messages.Message("ARA_SwarmSpell_Overload_Penalty".Translate(caster.LabelShortCap), caster, MessageTypeDefOf.NegativeHealthEvent);
}
catch (Exception ex)
{
Log.Error($"[虫群术法] 应用超载惩罚时出错: {ex.Message}");
}
}
private void DestroyConsciousnessSourceParts(Pawn pawn)
{
try
{
var consciousnessParts = pawn.health.hediffSet.GetNotMissingParts()
.Where(part => part.def.tags?.Contains(BodyPartTagDefOf.ConsciousnessSource) == true)
.ToList();
if (consciousnessParts.Any())
{
foreach (BodyPartRecord part in consciousnessParts)
{
float partMaxHealth = part.def.GetMaxHealth(pawn);
float damageAmount = partMaxHealth * Props.consciousnessSourceDamageMult;
DamageInfo damage = new DamageInfo(
DamageDefOf.Burn,
damageAmount,
armorPenetration: 999f,
instigator: pawn,
hitPart: part,
category: DamageInfo.SourceCategory.ThingOrUnknown);
pawn.TakeDamage(damage);
}
foreach (BodyPartRecord part in consciousnessParts)
{
if (!pawn.health.hediffSet.PartIsMissing(part))
{
DamageInfo finalDamage = new DamageInfo(
DamageDefOf.Burn,
99999f,
armorPenetration: 999f,
instigator: pawn,
hitPart: part);
pawn.TakeDamage(finalDamage);
}
}
}
else
{
BodyPartRecord fallbackPart = pawn.RaceProps.body.AllParts
.FirstOrDefault(p => p.def.tags?.Contains(BodyPartTagDefOf.ConsciousnessSource) == true)
?? pawn.RaceProps.body.corePart;
if (fallbackPart != null)
{
float damageAmount = fallbackPart.def.GetMaxHealth(pawn) * 2f;
DamageInfo damage = new DamageInfo(
DamageDefOf.Burn,
damageAmount,
armorPenetration: 999f,
instigator: pawn,
hitPart: fallbackPart);
pawn.TakeDamage(damage);
}
}
}
catch (Exception ex)
{
Log.Error($"[虫群术法] 摧毁意识来源部位时出错: {ex.Message}");
}
}
public override bool GizmoDisabled(out string reason)
{
Pawn caster = parent.pawn;
if (caster == null || caster.Dead || caster.Downed)
{
reason = null;
return false;
}
Comp_SwarmSpellHolder spellHolder = caster.TryGetComp<Comp_SwarmSpellHolder>();
if (spellHolder == null || !spellHolder.IsSystemInitialized)
{
reason = null;
return false;
}
if (spellHolder.LimitPsychicLoadAmount || !Props.ignoreOverloadCheck)
{
float thisCost = GetPredictedLoadCost(useMaxCost: true);
float queuedCost = TotalQueuedLoadCost(caster, useMaxCost: true);
float predictedLoad = spellHolder.PsychicLoad + thisCost + queuedCost;
if (spellHolder.PsychicLoadCapacity > 0f && predictedLoad > spellHolder.PsychicLoadCapacity)
{
reason = "ARA_SwarmSpell_Load_OverloadWarning".Translate().Resolve();
return true;
}
}
reason = null;
return false;
}
public override string ExtraTooltipPart()
{
if (!Props.showCostInGizmo)
{
return null;
}
Pawn caster = parent.pawn;
if (caster == null)
{
return null;
}
Comp_SwarmSpellHolder spellHolder = caster.TryGetComp<Comp_SwarmSpellHolder>();
if (spellHolder == null)
{
return null;
}
StringBuilder sb = new StringBuilder();
string costLabel = Props.customLabel ?? "ARA_SwarmSpell_LoadCost".Translate();
sb.AppendLine(costLabel.Colorize(ColoredText.TipSectionTitleColor));
if (Props.useFixedCost)
{
sb.AppendLine("ARA_SwarmSpell_LoadCost_Fixed".Translate(Props.fixedLoadCost.ToString("F1")));
}
else
{
sb.AppendLine("ARA_SwarmSpell_LoadCost_Range".Translate(Props.loadCostRange.min.ToString("F1"), Props.loadCostRange.max.ToString("F1")));
}
if (spellHolder.IsOverloaded)
{
sb.AppendLine();
sb.AppendLine("ARA_SwarmSpell_Load_OverloadWarning".Translate().Colorize(Color.red));
sb.AppendLine("ARA_SwarmSpell_Overload_Penalty_Description".Translate());
}
return sb.ToString().TrimEndNewlines();
}
public override void DrawEffectPreview(LocalTargetInfo target)
{
base.DrawEffectPreview(target);
}
public override bool AICanTargetNow(LocalTargetInfo target)
{
Pawn caster = parent.pawn;
if (caster == null)
{
return false;
}
Comp_SwarmSpellHolder spellHolder = caster.TryGetComp<Comp_SwarmSpellHolder>();
if (spellHolder == null)
{
return false;
}
if (spellHolder.IsOverloaded)
{
return false;
}
float predictedLoad = spellHolder.PsychicLoad + GetPredictedLoadCost();
if (spellHolder.LimitPsychicLoadAmount && spellHolder.PsychicLoadCapacity > 0f && predictedLoad > spellHolder.PsychicLoadCapacity)
{
return false;
}
float predictedPercent = spellHolder.PsychicLoadCapacity > 0f ? predictedLoad / spellHolder.PsychicLoadCapacity : 0f;
if (predictedPercent > 0.9f)
{
return false;
}
return base.AICanTargetNow(target);
}
}
}

View File

@@ -1,37 +0,0 @@
using RimWorld;
using System;
using UnityEngine;
using Verse;
namespace ArachnaeSwarm
{
/// <summary>
/// 灵能负载消耗属性
/// </summary>
public class CompProperties_AbilityPsychicLoadCost : CompProperties_AbilityEffect
{
#region
public FloatRange loadCostRange = new FloatRange(1f, 5f); // 负载消耗范围
public bool useFixedCost = false; // 是否使用固定值
public float fixedLoadCost = 3f; // 固定负载消耗
public bool showCostInGizmo = true; // 在Gizmo中显示消耗
public string customLabel; // 自定义标签
public bool ignoreOverloadCheck = true; // 忽略超载检查(总是可以施放)
public EffecterDef successEffecter; // 成功施放效果
public EffecterDef overloadEffecter; // 超载施放效果
// 超载惩罚设置
public bool applyOverloadPenalty = true; // 是否应用超载惩罚
public float consciousnessSourceDamageMult = 1f; // 意识来源部位伤害倍数
public HediffDef overloadPenaltyHediff; // 超载惩罚Hediff
public bool destroyConsciousnessSource = true; // 是否摧毁意识来源部位
#endregion
#region
public CompProperties_AbilityPsychicLoadCost()
{
compClass = typeof(CompAbilityEffect_PsychicLoadCost);
}
#endregion
}
}

View File

@@ -1,486 +0,0 @@
using RimWorld;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using Verse;
namespace ArachnaeSwarm
{
/// <summary>
/// 灵能负载转储技能效果
/// 将施法者的灵能负载输出到目标身上,根据目标是否有灵能熵追踪器执行不同效果
/// </summary>
public class CompAbilityEffect_PsychicLoadDump : CompAbilityEffect
{
#region
public new CompProperties_AbilityPsychicLoadDump Props =>
(CompProperties_AbilityPsychicLoadDump)props;
#endregion
#region
public override void Apply(LocalTargetInfo target, LocalTargetInfo dest)
{
base.Apply(target, dest);
Pawn caster = parent.pawn;
Pawn targetPawn = target.Pawn;
if (caster == null || targetPawn == null || targetPawn.Dead || targetPawn.Downed)
{
Log.Warning("[虫群术法] 灵能负载转储: 施法者或目标无效");
return;
}
// 获取施法者的术法持有组件
Comp_SwarmSpellHolder spellHolder = caster.TryGetComp<Comp_SwarmSpellHolder>();
if (spellHolder == null || !spellHolder.IsSystemInitialized)
{
Messages.Message("ARA_SwarmSpell_NoSpellHolder".Translate(caster.LabelShortCap),
MessageTypeDefOf.RejectInput);
return;
}
// 获取施法者当前的灵能负载
float currentLoad = spellHolder.PsychicLoad;
if (currentLoad <= 0f)
{
Messages.Message("ARA_SwarmSpell_NoLoadToDump".Translate(caster.LabelShortCap),
MessageTypeDefOf.RejectInput);
return;
}
// 记录施法者是否超载
bool casterWasOverloaded = spellHolder.IsOverloaded;
// 根据目标是否有灵能熵追踪器执行不同效果
bool hasPsychicTracker = CheckTargetHasPsychicTracker(targetPawn);
if (hasPsychicTracker)
{
// 有灵能熵追踪器转化为PsychicEntropy
ApplyPsychicEntropyEffect(caster, targetPawn, currentLoad, spellHolder);
}
else
{
// 没有灵能熵追踪器:摧毁意识来源器官
ApplyConsciousnessDestruction(caster, targetPawn, currentLoad, spellHolder);
}
// 无论结果如何,清空施法者的灵能负载
spellHolder.ReducePsychicLoad(currentLoad, "转储灵能负载");
// 显示效果和消息
ShowEffectsAndMessages(caster, targetPawn, currentLoad, hasPsychicTracker, casterWasOverloaded);
}
#endregion
#region
/// <summary>
/// 检查目标是否有灵能熵追踪器
/// </summary>
private bool CheckTargetHasPsychicTracker(Pawn target)
{
try
{
// 方法1直接检查psychicEntropy字段
var entropyField = typeof(Pawn).GetField("psychicEntropy");
if (entropyField != null)
{
object entropyObj = entropyField.GetValue(target);
if (entropyObj != null)
{
// 检查是否是Pawn_PsychicEntropyTracker类型
var entropyType = entropyObj.GetType();
if (entropyType.Name == "Pawn_PsychicEntropyTracker")
{
// 进一步检查是否有TryAddEntropy方法
var addEntropyMethod = entropyType.GetMethod("TryAddEntropy");
if (addEntropyMethod != null)
{
return true;
}
}
}
}
// 方法2检查是否有PsychicSensitivity属性
if (target.RaceProps.Humanlike)
{
// 人类类种族通常有灵能熵追踪器
float psychicSensitivity = target.GetStatValue(StatDefOf.PsychicSensitivity);
if (psychicSensitivity > 0.01f)
{
return true;
}
}
// 方法3检查是否有Psylink相关Hediff
Hediff psylink = target.health.hediffSet.GetFirstHediffOfDef(HediffDefOf.PsychicAmplifier);
if (psylink != null)
{
return true;
}
// 方法4检查是否有PsychicEntropy Hediff
Hediff entropyHediff = target.health.hediffSet.GetFirstHediffOfDef(HediffDefOf.PsychicEntropy);
if (entropyHediff != null)
{
return true;
}
return false;
}
catch (System.Exception ex)
{
Log.Warning($"[虫群术法] 检查目标灵能熵追踪器时出错: {ex.Message}");
return false;
}
}
/// <summary>
/// 获取目标的灵能熵追踪器(如果存在)
/// </summary>
private object GetPsychicEntropyTracker(Pawn target)
{
try
{
var entropyField = typeof(Pawn).GetField("psychicEntropy");
if (entropyField != null)
{
return entropyField.GetValue(target);
}
return null;
}
catch (System.Exception)
{
return null;
}
}
#endregion
#region
/// <summary>
/// 将灵能负载转化为PsychicEntropy应用到目标
/// </summary>
private void ApplyPsychicEntropyEffect(Pawn caster, Pawn target, float loadAmount, Comp_SwarmSpellHolder spellHolder)
{
try
{
// 计算转化的熵值1:10转化比
float entropyAmount = loadAmount * Props.conversionRatio;
// 获取目标的灵能熵追踪器
object entropyTracker = GetPsychicEntropyTracker(target);
if (entropyTracker != null)
{
// 使用反射调用TryAddEntropy方法
System.Type entropyType = entropyTracker.GetType();
var addEntropyMethod = entropyType.GetMethod("TryAddEntropy",
new System.Type[] { typeof(float), typeof(Thing), typeof(bool), typeof(bool) });
if (addEntropyMethod != null)
{
bool success = (bool)addEntropyMethod.Invoke(entropyTracker,
new object[] { entropyAmount, caster, Props.scaleBySensitivity, Props.allowOverLimit });
if (success)
{
// 成功添加熵值
ShowEntropyTransferEffect(caster, target, entropyAmount);
}
else
{
// 添加熵值失败(可能达到上限)
Messages.Message("ARA_SwarmSpell_EntropyTransferFailed".Translate(target.LabelShortCap),
target, MessageTypeDefOf.NegativeEvent);
}
}
else
{
// 如果没有TryAddEntropy方法尝试其他方法
ApplyFallbackEffect(target, loadAmount);
}
}
else
{
// 没有找到追踪器,使用后备效果
ApplyFallbackEffect(target, loadAmount);
}
}
catch (System.Exception ex)
{
Log.Error($"[虫群术法] 应用灵能熵效果时出错: {ex.Message}\n{ex.StackTrace}");
ApplyFallbackEffect(target, loadAmount);
}
}
/// <summary>
/// 后备效果(当无法添加熵值时使用)
/// </summary>
private void ApplyFallbackEffect(Pawn target, float loadAmount)
{
// 应用一些伤害作为后备
float damageAmount = loadAmount * Props.fallbackDamageMultiplier;
DamageInfo damage = new DamageInfo(
Props.fallbackDamageType ?? DamageDefOf.Burn,
damageAmount,
armorPenetration: 999f,
instigator: parent.pawn
);
target.TakeDamage(damage);
}
/// <summary>
/// 显示熵值转移效果
/// </summary>
private void ShowEntropyTransferEffect(Pawn caster, Pawn target, float entropyAmount)
{
// 显示效果
if (Props.entropyTransferEffecter != null)
{
Effecter effecter = Props.entropyTransferEffecter.Spawn();
effecter.Trigger(new TargetInfo(caster.Position, caster.Map), new TargetInfo(target.Position, target.Map));
effecter.Cleanup();
}
// 显示熵值转移消息
if (Props.showEntropyTransferMessage)
{
Messages.Message("ARA_SwarmSpell_EntropyTransferred".Translate(
caster.LabelShortCap, target.LabelShortCap, entropyAmount.ToString("F1")),
target, MessageTypeDefOf.NegativeEvent);
}
}
#endregion
#region
/// <summary>
/// 摧毁目标的意识来源器官
/// </summary>
private void ApplyConsciousnessDestruction(Pawn caster, Pawn target, float loadAmount, Comp_SwarmSpellHolder spellHolder)
{
try
{
// 获取所有意识来源部位
var consciousnessParts = target.health.hediffSet.GetNotMissingParts()
.Where(part => part.def.tags?.Contains(BodyPartTagDefOf.ConsciousnessSource) == true)
.ToList();
if (consciousnessParts.Count == 0)
{
// 如果没有意识来源器官,应用普通伤害
ApplyNoConsciousnessFallback(target, loadAmount);
return;
}
int destroyedCount = 0;
float loadPerPart = loadAmount / consciousnessParts.Count;
foreach (var part in consciousnessParts)
{
if (TryDestroyConsciousnessPart(target, part, loadPerPart))
{
destroyedCount++;
// 显示伤害效果
if (Props.consciousnessDestroyFleck != null && target.Map != null)
{
FleckMaker.Static(target.Position, target.Map, Props.consciousnessDestroyFleck);
}
}
}
// 如果摧毁了任何意识来源器官,应用惩罚效果
if (destroyedCount > 0)
{
ApplyConsciousnessPenalty(target);
// 显示摧毁消息
if (Props.showConsciousnessDestroyMessage)
{
Messages.Message("ARA_SwarmSpell_ConsciousnessDestroyed".Translate(
target.LabelShortCap, destroyedCount),
target, MessageTypeDefOf.NegativeHealthEvent);
}
}
}
catch (System.Exception ex)
{
Log.Error($"[虫群术法] 摧毁意识来源器官时出错: {ex.Message}\n{ex.StackTrace}");
}
}
/// <summary>
/// 尝试摧毁意识来源器官
/// </summary>
private bool TryDestroyConsciousnessPart(Pawn pawn, BodyPartRecord part, float loadPerPart)
{
try
{
// 计算伤害(基于负载和配置的伤害倍数)
float damageAmount = part.def.GetMaxHealth(pawn) * Props.partDestructionDamageMult +
(loadPerPart * Props.loadDamageMultiplier);
DamageInfo damage = new DamageInfo(
Props.destroyDamageType ?? DamageDefOf.Burn,
damageAmount,
armorPenetration: 999f,
instigator: parent.pawn,
hitPart: part,
category: DamageInfo.SourceCategory.ThingOrUnknown
);
pawn.TakeDamage(damage);
// 确保部位被摧毁
if (!pawn.health.hediffSet.PartIsMissing(part))
{
DamageInfo finalDamage = new DamageInfo(
Props.destroyDamageType ?? DamageDefOf.Burn,
99999f,
armorPenetration: 999f,
instigator: parent.pawn,
hitPart: part
);
pawn.TakeDamage(finalDamage);
}
return true;
}
catch (System.Exception ex)
{
Log.Error($"[虫群术法] 摧毁部位 {part.Label} 时出错: {ex.Message}");
return false;
}
}
/// <summary>
/// 没有意识来源器官时的后备效果
/// </summary>
private void ApplyNoConsciousnessFallback(Pawn target, float loadAmount)
{
// 对核心部位造成伤害
BodyPartRecord corePart = target.RaceProps.body.corePart;
if (corePart != null)
{
float damageAmount = loadAmount * Props.noConsciousnessDamageMultiplier;
DamageInfo damage = new DamageInfo(
Props.destroyDamageType ?? DamageDefOf.Burn,
damageAmount,
armorPenetration: 999f,
instigator: parent.pawn,
hitPart: corePart
);
target.TakeDamage(damage);
if (Props.showNoConsciousnessMessage)
{
Messages.Message("ARA_SwarmSpell_NoConsciousnessFallback".Translate(
target.LabelShortCap, damageAmount.ToString("F0")),
target, MessageTypeDefOf.NegativeEvent);
}
}
}
/// <summary>
/// 应用意识来源器官被摧毁的惩罚
/// </summary>
private void ApplyConsciousnessPenalty(Pawn pawn)
{
// 添加意识惩罚Hediff
if (Props.consciousnessPenaltyHediff != null)
{
try
{
Hediff penalty = HediffMaker.MakeHediff(Props.consciousnessPenaltyHediff, pawn);
pawn.health.AddHediff(penalty);
}
catch (System.Exception ex)
{
Log.Error($"[虫群术法] 添加意识惩罚Hediff时出错: {ex.Message}");
}
}
// 显示效果
if (Props.penaltyEffecter != null)
{
Effecter effecter = Props.penaltyEffecter.Spawn();
effecter.Trigger(new TargetInfo(pawn.Position, pawn.Map), new TargetInfo(pawn.Position, pawn.Map));
effecter.Cleanup();
}
}
#endregion
#region
/// <summary>
/// 显示效果和消息
/// </summary>
private void ShowEffectsAndMessages(Pawn caster, Pawn target, float loadAmount, bool hasPsychicTracker, bool casterWasOverloaded)
{
// 显示主效果
if (Props.mainEffecter != null)
{
Effecter effecter = Props.mainEffecter.Spawn();
effecter.Trigger(new TargetInfo(caster.Position, caster.Map), new TargetInfo(target.Position, target.Map));
effecter.Cleanup();
}
// 显示负载转储消息
if (Props.showDumpMessage)
{
string messageKey = hasPsychicTracker ?
"ARA_SwarmSpell_LoadDumped_Entropy" :
"ARA_SwarmSpell_LoadDumped_Destruction";
string message = messageKey.Translate(
caster.LabelShortCap,
target.LabelShortCap,
loadAmount.ToString("F1"));
Messages.Message(message, caster, MessageTypeDefOf.PositiveEvent);
}
// 显示施法者状态消息
if (casterWasOverloaded && Props.showOverloadReliefMessage)
{
Messages.Message("ARA_SwarmSpell_OverloadRelieved".Translate(caster.LabelShortCap),
caster, MessageTypeDefOf.PositiveEvent);
}
}
#endregion
#region AI
public override bool AICanTargetNow(LocalTargetInfo target)
{
Pawn caster = parent.pawn;
if (caster == null)
return false;
Pawn targetPawn = target.Pawn;
if (targetPawn == null || targetPawn.Dead || (targetPawn.Downed && !Props.canTargetDowned))
return false;
// 检查施法者是否有术法组件
Comp_SwarmSpellHolder spellHolder = caster.TryGetComp<Comp_SwarmSpellHolder>();
if (spellHolder == null || !spellHolder.IsSystemInitialized)
return false;
// AI不会在没有负载时使用此技能
if (spellHolder.PsychicLoad <= 0f)
return false;
// AI倾向于在超载时使用此技能
float overloadUrgency = spellHolder.IsOverloaded ? 0.8f : (spellHolder.PsychicLoadPercent * 0.5f);
// AI考虑目标的威胁程度
float threatScore = targetPawn.Downed ? 0f : (targetPawn.kindDef.combatPower / 100f);
// 综合评分:超载越严重、目标威胁越大,越可能使用
float totalScore = overloadUrgency + (threatScore * 0.5f);
return totalScore > 0.5f && base.AICanTargetNow(target);
}
#endregion
}
}

View File

@@ -1,127 +0,0 @@
using RimWorld;
using UnityEngine;
using Verse;
namespace ArachnaeSwarm
{
/// <summary>
/// 灵能负载转储技能属性
/// </summary>
public class CompProperties_AbilityPsychicLoadDump : CompProperties_AbilityEffect
{
#region
/// <summary>
/// 转化比例1负载 = X熵值
/// </summary>
public float conversionRatio = 10f;
/// <summary>
/// 是否根据目标的灵能敏感度缩放
/// </summary>
public bool scaleBySensitivity = true;
/// <summary>
/// 是否允许超过目标熵值上限
/// </summary>
public bool allowOverLimit = false;
#endregion
#region
/// <summary>
/// 是否可以对倒下的目标使用
/// </summary>
public bool canTargetDowned = false;
#endregion
#region
/// <summary>
/// 部位摧毁基础伤害倍数(基于部位最大生命值)
/// </summary>
public float partDestructionDamageMult = 1.5f;
/// <summary>
/// 负载伤害倍数(负载 * 此倍数)
/// </summary>
public float loadDamageMultiplier = 5f;
/// <summary>
/// 没有意识来源器官时的伤害倍数
/// </summary>
public float noConsciousnessDamageMultiplier = 3f;
/// <summary>
/// 后备效果伤害倍数(当无法添加熵值时)
/// </summary>
public float fallbackDamageMultiplier = 2f;
/// <summary>
/// 摧毁部位使用的伤害类型
/// </summary>
public DamageDef destroyDamageType = null;
/// <summary>
/// 后备效果伤害类型
/// </summary>
public DamageDef fallbackDamageType = null;
#endregion
#region
/// <summary>
/// 主效果器
/// </summary>
public EffecterDef mainEffecter = null;
/// <summary>
/// 熵值转移效果器
/// </summary>
public EffecterDef entropyTransferEffecter = null;
/// <summary>
/// 意识摧毁特效
/// </summary>
public FleckDef consciousnessDestroyFleck = null;
/// <summary>
/// 惩罚效果器
/// </summary>
public EffecterDef penaltyEffecter = null;
/// <summary>
/// 意识惩罚Hediff
/// </summary>
public HediffDef consciousnessPenaltyHediff = null;
#endregion
#region
/// <summary>
/// 是否显示转储消息
/// </summary>
public bool showDumpMessage = true;
/// <summary>
/// 是否显示熵值转移消息
/// </summary>
public bool showEntropyTransferMessage = true;
/// <summary>
/// 是否显示意识摧毁消息
/// </summary>
public bool showConsciousnessDestroyMessage = true;
/// <summary>
/// 是否显示没有意识来源器官消息
/// </summary>
public bool showNoConsciousnessMessage = true;
/// <summary>
/// 是否显示超载解除消息
/// </summary>
public bool showOverloadReliefMessage = true;
#endregion
public CompProperties_AbilityPsychicLoadDump()
{
compClass = typeof(CompAbilityEffect_PsychicLoadDump);
}
}
}

View File

@@ -1,412 +0,0 @@
using RimWorld;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using Verse;
namespace ArachnaeSwarm
{
/// <summary>
/// 灵能科研点收割技能效果
/// 从目标身上收割灵能科研点,并摧毁目标的意识来源器官
/// </summary>
public class CompAbilityEffect_PsychicResearchHarvest : CompAbilityEffect
{
#region
public new CompProperties_AbilityPsychicResearchHarvest Props =>
(CompProperties_AbilityPsychicResearchHarvest)props;
#endregion
#region
public override void Apply(LocalTargetInfo target, LocalTargetInfo dest)
{
base.Apply(target, dest);
Pawn caster = parent.pawn;
Pawn targetPawn = target.Pawn;
if (caster == null || targetPawn == null || targetPawn.Dead || targetPawn.Downed)
{
Log.Warning("[虫群术法] 灵能科研点收割: 施法者或目标无效");
return;
}
// 获取施法者的术法持有组件
Comp_SwarmSpellHolder spellHolder = caster.TryGetComp<Comp_SwarmSpellHolder>();
if (spellHolder == null || !spellHolder.IsSystemInitialized)
{
Messages.Message("ARA_SwarmSpell_NoSpellHolder".Translate(caster.LabelShortCap),
MessageTypeDefOf.RejectInput);
return;
}
// 计算应获得的科研点
float researchPoints = CalculateResearchPoints(targetPawn);
// 添加到施法者的科研点
bool success = spellHolder.AddPsychicResearchPoints(researchPoints, "收割目标灵能");
// 摧毁目标的意识来源器官
DestroyConsciousnessSourceParts(targetPawn);
// 显示效果和消息
ShowEffectsAndMessages(caster, targetPawn, researchPoints, success);
}
#endregion
#region
/// <summary>
/// 计算从目标身上可收割的灵能科研点
/// </summary>
private float CalculateResearchPoints(Pawn targetPawn)
{
float baseAmount = Props.baseAmount;
float intelligenceMultiplier = Props.intelligenceMultiplier;
float consciousnessMultiplier = Props.consciousnessMultiplier;
// 获取目标的智识Intellectual技能等级
// 如果没有智识技能比如动物视为1
float intelligenceValue = 1f;
if (targetPawn.skills != null)
{
SkillRecord intellectualSkill = targetPawn.skills.GetSkill(SkillDefOf.Intellectual);
if (intellectualSkill != null)
{
// 使用技能等级0-20作为智识值
intelligenceValue = intellectualSkill.Level;
if (intelligenceValue <= 0) intelligenceValue = 1f;
}
}
// 获取目标的意识Consciousness能力值
float consciousnessValue = targetPawn.health.capacities.GetLevel(PawnCapacityDefOf.Consciousness);
if (consciousnessValue < 0) consciousnessValue = 0;
// 计算公式:基础数 + (智识 * 智识倍率) + (意识 * 意识倍率)
float researchPoints = baseAmount +
(intelligenceValue * intelligenceMultiplier) +
(consciousnessValue * consciousnessMultiplier);
// 应用乘数
researchPoints *= Props.overallMultiplier;
// 应用随机范围(如果有)
if (Props.useRandomRange)
{
researchPoints = researchPoints * Props.randomRange.RandomInRange;
}
// 确保最小值
researchPoints = Mathf.Max(researchPoints, Props.minimumAmount);
return researchPoints;
}
#endregion
#region
/// <summary>
/// 摧毁目标的所有意识来源器官
/// </summary>
private void DestroyConsciousnessSourceParts(Pawn targetPawn)
{
if (targetPawn == null || targetPawn.Dead || targetPawn.Downed)
return;
try
{
// 获取所有意识来源部位
var consciousnessParts = targetPawn.health.hediffSet.GetNotMissingParts()
.Where(part => part.def.tags?.Contains(BodyPartTagDefOf.ConsciousnessSource) == true)
.ToList();
if (consciousnessParts.Count == 0)
{
return;
}
int destroyedCount = 0;
foreach (var part in consciousnessParts)
{
// 尝试用最高优先级的方式摧毁部位
if (TryDestroyBodyPart(targetPawn, part))
{
destroyedCount++;
// 显示伤害效果
if (Props.destroyFleck != null && targetPawn.Map != null)
{
FleckMaker.Static(targetPawn.Position, targetPawn.Map, Props.destroyFleck);
}
}
}
// 如果摧毁了任何意识来源器官,应用惩罚效果
if (destroyedCount > 0)
{
ApplyConsciousnessPenalty(targetPawn);
}
}
catch (System.Exception ex)
{
Log.Error($"[虫群术法] 摧毁意识来源器官时出错: {ex.Message}\n{ex.StackTrace}");
}
}
/// <summary>
/// 尝试摧毁身体部位
/// </summary>
private bool TryDestroyBodyPart(Pawn pawn, BodyPartRecord part)
{
try
{
// 方法1直接伤害最可靠
float damageAmount = part.def.GetMaxHealth(pawn) * Props.partDestructionDamageMult;
DamageInfo damage = new DamageInfo(
Props.destroyDamageType ?? DamageDefOf.Burn,
damageAmount,
armorPenetration: 999f,
instigator: parent.pawn,
hitPart: part,
category: DamageInfo.SourceCategory.ThingOrUnknown
);
pawn.TakeDamage(damage);
// 方法2如果部位仍然存在尝试强制移除
if (!pawn.health.hediffSet.PartIsMissing(part))
{
// 施加更大的伤害确保摧毁
DamageInfo finalDamage = new DamageInfo(
Props.destroyDamageType ?? DamageDefOf.Burn,
99999f,
armorPenetration: 999f,
instigator: parent.pawn,
hitPart: part
);
pawn.TakeDamage(finalDamage);
// 如果还是不行尝试添加部件缺失Hediff
if (!pawn.health.hediffSet.PartIsMissing(part))
{
pawn.health.AddHediff(HediffDefOf.MissingBodyPart, part);
}
}
return true;
}
catch (System.Exception ex)
{
Log.Error($"[虫群术法] 摧毁部位 {part.Label} 时出错: {ex.Message}");
return false;
}
}
/// <summary>
/// 应用意识来源器官被摧毁的惩罚
/// </summary>
private void ApplyConsciousnessPenalty(Pawn pawn)
{
// 显示效果
if (Props.penaltyEffecter != null)
{
Effecter effecter = Props.penaltyEffecter.Spawn();
effecter.Trigger(new TargetInfo(pawn.Position, pawn.Map), new TargetInfo(pawn.Position, pawn.Map));
effecter.Cleanup();
}
}
#endregion
#region
/// <summary>
/// 显示效果和消息
/// </summary>
private void ShowEffectsAndMessages(Pawn caster, Pawn target, float researchPoints, bool success)
{
// 显示收割效果
if (Props.harvestEffecter != null)
{
Effecter effecter = Props.harvestEffecter.Spawn();
effecter.Trigger(new TargetInfo(caster.Position, caster.Map), new TargetInfo(target.Position, target.Map));
effecter.Cleanup();
}
// 显示收获消息
if (Props.showHarvestMessage)
{
string message;
if (Props.customHarvestMessageKey.NullOrEmpty())
{
message = "ARA_SwarmSpell_ResearchHarvest".Translate(
caster.LabelShortCap,
target.LabelShortCap,
researchPoints.ToString("F1"));
}
else
{
message = Props.customHarvestMessageKey.Translate(
caster.LabelShortCap,
target.LabelShortCap,
researchPoints.ToString("F1"));
}
Messages.Message(message, caster, MessageTypeDefOf.PositiveEvent);
}
}
#endregion
#region
public override bool Valid(LocalTargetInfo target, bool throwMessages = false)
{
if (!base.Valid(target, throwMessages))
return false;
Pawn targetPawn = target.Pawn;
if (targetPawn == null)
{
if (throwMessages)
Messages.Message("需要选择一个目标单位", MessageTypeDefOf.RejectInput);
return false;
}
// 检查目标是否活着且没有倒下(如果技能不允许对倒下单位使用)
if (!Props.canTargetDowned && targetPawn.Downed)
{
if (throwMessages)
Messages.Message("目标已经倒下,无法收割", MessageTypeDefOf.RejectInput);
return false;
}
// 检查目标是否有意识来源器官(可选检查)
if (Props.requireConsciousnessSource)
{
var consciousnessParts = targetPawn.health.hediffSet.GetNotMissingParts()
.Where(part => part.def.tags?.Contains(BodyPartTagDefOf.ConsciousnessSource) == true)
.ToList();
if (consciousnessParts.Count == 0)
{
if (throwMessages)
Messages.Message("目标没有可收割的意识来源器官", MessageTypeDefOf.RejectInput);
return false;
}
}
// 检查施法者是否有术法组件且科研点未满
Pawn caster = parent.pawn;
if (caster != null)
{
Comp_SwarmSpellHolder spellHolder = caster.TryGetComp<Comp_SwarmSpellHolder>();
if (spellHolder == null)
{
if (throwMessages)
Messages.Message("施法者没有灵能术法系统", MessageTypeDefOf.RejectInput);
return false;
}
if (!spellHolder.IsSystemInitialized)
{
if (throwMessages)
Messages.Message("灵能术法系统未初始化", MessageTypeDefOf.RejectInput);
return false;
}
// 检查科研点是否已满
if (!Props.allowOverflow && spellHolder.PsychicResearchPoints >= spellHolder.MaxPsychicResearchPoints)
{
if (throwMessages)
Messages.Message("灵能科研点已达上限,无法收割", MessageTypeDefOf.RejectInput);
return false;
}
}
return true;
}
#endregion
#region AI
public override bool AICanTargetNow(LocalTargetInfo target)
{
Pawn caster = parent.pawn;
if (caster == null)
return false;
Pawn targetPawn = target.Pawn;
if (targetPawn == null || targetPawn.Dead || (targetPawn.Downed && !Props.canTargetDowned))
return false;
// 检查施法者是否有术法组件
Comp_SwarmSpellHolder spellHolder = caster.TryGetComp<Comp_SwarmSpellHolder>();
if (spellHolder == null || !spellHolder.IsSystemInitialized)
return false;
// AI不会在科研点已满时使用此技能
if (!Props.allowOverflow && spellHolder.PsychicResearchPoints >= spellHolder.MaxPsychicResearchPoints)
return false;
// AI倾向于选择智识和意识高的目标
float predictedPoints = CalculateResearchPoints(targetPawn);
// 计算收益评估
float benefitScore = predictedPoints / spellHolder.MaxPsychicResearchPoints;
// AI考虑目标的威胁程度
float threatScore = targetPawn.Downed ? 0f : (targetPawn.kindDef.combatPower / 100f);
// 综合评分:收益高、威胁低的目标优先
float totalScore = benefitScore - (threatScore * 0.3f);
return totalScore > 0.2f && base.AICanTargetNow(target);
}
#endregion
#region
/// <summary>
/// 获取目标意识来源器官的数量
/// </summary>
public int GetTargetConsciousnessPartCount(Pawn target)
{
if (target == null || target.Dead || target.Downed)
return 0;
return target.health.hediffSet.GetNotMissingParts()
.Count(part => part.def.tags?.Contains(BodyPartTagDefOf.ConsciousnessSource) == true);
}
/// <summary>
/// 获取技能影响的详细描述
/// </summary>
public string GetDetailedEffectDescription(Pawn target)
{
StringBuilder sb = new StringBuilder();
int partCount = GetTargetConsciousnessPartCount(target);
float predictedPoints = CalculateResearchPoints(target);
sb.AppendLine("灵能科研点收割效果预测:");
sb.AppendLine($"目标: {target.LabelCap}");
sb.AppendLine($"意识来源器官: {partCount} 个");
sb.AppendLine($"预计收割科研点: {predictedPoints:F1}");
if (partCount > 0)
{
sb.AppendLine();
sb.AppendLine("将被摧毁的部位:");
var parts = target.health.hediffSet.GetNotMissingParts()
.Where(part => part.def.tags?.Contains(BodyPartTagDefOf.ConsciousnessSource) == true)
.ToList();
foreach (var part in parts)
{
sb.AppendLine($" • {part.LabelCap} (健康: {target.health.hediffSet.GetPartHealth(part):F0}/{part.def.GetMaxHealth(target):F0})");
}
}
return sb.ToString();
}
#endregion
}
}

View File

@@ -1,157 +0,0 @@
using RimWorld;
using UnityEngine;
using Verse;
namespace ArachnaeSwarm
{
/// <summary>
/// 灵能科研点收割技能属性
/// </summary>
public class CompProperties_AbilityPsychicResearchHarvest : CompProperties_AbilityEffect
{
#region
/// <summary>
/// 基础收割量
/// </summary>
public float baseAmount = 100f;
/// <summary>
/// 智识倍率
/// </summary>
public float intelligenceMultiplier = 50f;
/// <summary>
/// 意识倍率
/// </summary>
public float consciousnessMultiplier = 100f;
/// <summary>
/// 全局乘数
/// </summary>
public float overallMultiplier = 1f;
/// <summary>
/// 是否使用随机范围
/// </summary>
public bool useRandomRange = false;
/// <summary>
/// 随机范围(乘数)
/// </summary>
public FloatRange randomRange = new FloatRange(0.8f, 1.2f);
/// <summary>
/// 最低收割量(确保至少收获这么多)
/// </summary>
public float minimumAmount = 10f;
/// <summary>
/// 是否允许科研点溢出(超过上限)
/// </summary>
public bool allowOverflow = false;
#endregion
#region
/// <summary>
/// 是否可以对倒下的目标使用
/// </summary>
public bool canTargetDowned = false;
/// <summary>
/// 是否要求目标必须有意识来源器官
/// </summary>
public bool requireConsciousnessSource = true;
/// <summary>
/// 在工具提示中显示详细计算
/// </summary>
public bool showDetailedCalculation = true;
#endregion
#region
/// <summary>
/// 部位摧毁伤害倍数(基于部位最大生命值)
/// </summary>
public float partDestructionDamageMult = 2.0f;
/// <summary>
/// 摧毁部位使用的伤害类型
/// </summary>
public DamageDef destroyDamageType = null;
/// <summary>
/// 摧毁部位时的特效
/// </summary>
public FleckDef destroyFleck = null;
/// <summary>
/// 是否显示器官摧毁消息
/// </summary>
public bool showDestroyMessage = true;
/// <summary>
/// 是否可能杀死目标
/// </summary>
public bool canKillTarget = false;
/// <summary>
/// 完全摧毁意识来源器官时的杀死几率
/// </summary>
public float killChanceOnCompleteDestruction = 0.3f;
/// <summary>
/// 杀死目标时使用的伤害类型
/// </summary>
public DamageDef killDamageType = null;
/// <summary>
/// 是否显示杀死消息
/// </summary>
public bool showKillMessage = true;
#endregion
#region
/// <summary>
/// 收割时的特效
/// </summary>
public EffecterDef harvestEffecter = null;
/// <summary>
/// 惩罚效果
/// </summary>
public EffecterDef penaltyEffecter = null;
/// <summary>
/// 意识惩罚Hediff
/// </summary>
public HediffDef consciousnessPenaltyHediff = null;
/// <summary>
/// 失去意识Hediff
/// </summary>
public HediffDef unconsciousnessHediff = null;
/// <summary>
/// 是否显示收割消息
/// </summary>
public bool showHarvestMessage = true;
/// <summary>
/// 自定义收割消息翻译键
/// </summary>
public string customHarvestMessageKey = null;
/// <summary>
/// 是否显示目标受损消息
/// </summary>
public bool showTargetDamageMessage = true;
#endregion
#region
public CompProperties_AbilityPsychicResearchHarvest()
{
compClass = typeof(CompAbilityEffect_PsychicResearchHarvest);
}
#endregion
}
}

View File

@@ -40,12 +40,6 @@
<Compile Include="Abilities\ARA_HediffBlacklist\CompProperties_AbilityHediffBlacklist.cs" /> <Compile Include="Abilities\ARA_HediffBlacklist\CompProperties_AbilityHediffBlacklist.cs" />
<Compile Include="Abilities\ARA_HediffGacha\CompAbilityEffect_HediffGacha.cs" /> <Compile Include="Abilities\ARA_HediffGacha\CompAbilityEffect_HediffGacha.cs" />
<Compile Include="Abilities\ARA_HediffGacha\Window_HediffSelection.cs" /> <Compile Include="Abilities\ARA_HediffGacha\Window_HediffSelection.cs" />
<Compile Include="Abilities\ARA_PsychicLoadCost\CompAbilityEffect_PsychicLoadCost.cs" />
<Compile Include="Abilities\ARA_PsychicLoadCost\CompProperties_AbilityPsychicLoadCost.cs" />
<Compile Include="Abilities\ARA_PsychicLoadDump\CompAbilityEffect_PsychicLoadDump.cs" />
<Compile Include="Abilities\ARA_PsychicLoadDump\CompProperties_AbilityPsychicLoadDump.cs" />
<Compile Include="Abilities\ARA_PsychicResearchHarvest\CompAbilityEffect_PsychicResearchHarvest.cs" />
<Compile Include="Abilities\ARA_PsychicResearchHarvest\CompProperties_AbilityPsychicResearchHarvest.cs" />
<Compile Include="Abilities\ARA_LaunchMultiProjectile\CompAbilityEffect_LaunchMultiProjectile.cs" /> <Compile Include="Abilities\ARA_LaunchMultiProjectile\CompAbilityEffect_LaunchMultiProjectile.cs" />
<Compile Include="Abilities\ARA_LaunchMultiProjectile\CompProperties_AbilityLaunchMultiProjectile.cs" /> <Compile Include="Abilities\ARA_LaunchMultiProjectile\CompProperties_AbilityLaunchMultiProjectile.cs" />
<Compile Include="Abilities\ARA_LaunchMultiProjectile\JobDriver_CastAbilityMaintainMultiProjectile.cs" /> <Compile Include="Abilities\ARA_LaunchMultiProjectile\JobDriver_CastAbilityMaintainMultiProjectile.cs" />
@@ -376,14 +370,8 @@
<Compile Include="Pawn_Comps\ARA_HoneyProduction_Animal\CompHoneyProduction_Animal.cs" /> <Compile Include="Pawn_Comps\ARA_HoneyProduction_Animal\CompHoneyProduction_Animal.cs" />
<Compile Include="Pawn_Comps\ARA_NodeSwarmLifetime\CompNodeSwarmLifetime.cs" /> <Compile Include="Pawn_Comps\ARA_NodeSwarmLifetime\CompNodeSwarmLifetime.cs" />
<Compile Include="Pawn_Comps\ARA_NodeSwarmLifetime\CompProperties_NodeSwarmLifetime.cs" /> <Compile Include="Pawn_Comps\ARA_NodeSwarmLifetime\CompProperties_NodeSwarmLifetime.cs" />
<Compile Include="Pawn_Comps\ARA_PawnResearchBlueprintReader\CompProperties_PawnResearchBlueprintReader.cs" />
<Compile Include="Pawn_Comps\ARA_PawnResearchBlueprintReader\Comp_PawnResearchBlueprintReader.cs" />
<Compile Include="Pawn_Comps\ARA_PawnResearchBlueprintReader\Gizmo_PawnResearchProgress.cs" />
<Compile Include="Pawn_Comps\ARA_SwarmMaintainer\CompProperties_SwarmMaintainer.cs" /> <Compile Include="Pawn_Comps\ARA_SwarmMaintainer\CompProperties_SwarmMaintainer.cs" />
<Compile Include="Pawn_Comps\ARA_SwarmMaintainer\Comp_SwarmMaintainer.cs" /> <Compile Include="Pawn_Comps\ARA_SwarmMaintainer\Comp_SwarmMaintainer.cs" />
<Compile Include="Pawn_Comps\ARA_SwarmSpellHolder\Comp_SwarmSpellHolder.cs" />
<Compile Include="Pawn_Comps\ARA_SwarmSpellHolder\Gizmo_SwarmSpellStatus.cs" />
<Compile Include="Pawn_Comps\ARA_SwarmSpellHolder\SwarmSpellUtility.cs" />
<Compile Include="Pawn_Comps\ARA_TrainingWork\CompAdvancedTraining.cs" /> <Compile Include="Pawn_Comps\ARA_TrainingWork\CompAdvancedTraining.cs" />
<Compile Include="Jobs\JobDriver_Clean\JobGiver_Cleaner.cs" /> <Compile Include="Jobs\JobDriver_Clean\JobGiver_Cleaner.cs" />
<Compile Include="Jobs\JobDriver_Clean\ThinkNode_ConditionalAnimalShouldDoCleaningWork.cs" /> <Compile Include="Jobs\JobDriver_Clean\ThinkNode_ConditionalAnimalShouldDoCleaningWork.cs" />

View File

@@ -50,8 +50,6 @@ namespace ArachnaeSwarm
/// </summary> /// </summary>
public override void CompPostPostRemoved() public override void CompPostPostRemoved()
{ {
base.CompPostPostRemoved();
if (this.Pawn == null || this.Pawn.Map == null || Props.pawnKindDefs.NullOrEmpty()) if (this.Pawn == null || this.Pawn.Map == null || Props.pawnKindDefs.NullOrEmpty())
{ {
ArachnaeLog.Debug("ArachnaeSwarm: HediffComp_SpawnPawnOnRemoved tried to spawn a pawn but required data was missing (Pawn, Map, or pawnKindDefs)."); ArachnaeLog.Debug("ArachnaeSwarm: HediffComp_SpawnPawnOnRemoved tried to spawn a pawn but required data was missing (Pawn, Map, or pawnKindDefs).");
@@ -90,6 +88,8 @@ namespace ArachnaeSwarm
ArachnaeLog.Debug($"ArachnaeSwarm: Failed to generate pawn of kind {selectedPawnKindDef.defName}."); ArachnaeLog.Debug($"ArachnaeSwarm: Failed to generate pawn of kind {selectedPawnKindDef.defName}.");
} }
} }
base.CompPostPostRemoved();
} }
/// <summary> /// <summary>

View File

@@ -1,72 +0,0 @@
using RimWorld;
using Verse;
namespace ArachnaeSwarm
{
public class CompProperties_PawnResearchBlueprintReader : CompProperties
{
/// <summary>
/// 是否允许该Pawn进行科技研究
/// </summary>
public bool canResearch = true;
/// <summary>
/// 研究速度乘数(基于智力等级)
/// </summary>
public float researchSpeedMultiplier = 1.0f;
/// <summary>
/// 是否显示研究进度Gizmo
/// </summary>
public bool showResearchGizmo = true;
/// <summary>
/// 是否自动开始研究(当有未完成科技时)
/// </summary>
public bool autoStartResearch = true;
/// <summary>
/// 所需研究标签页(默认为阿拉克涅科技标签页)
/// </summary>
public ResearchTabDef requiredResearchTab = null; // 将在PostLoad中设置默认值
/// <summary>
/// 每秒研究的点数(基础值,会乘以智力等级)
/// </summary>
public float baseResearchPointsPerSecond = 1.0f;
/// <summary>
/// 是否使用灵能科研点如果为true需要Comp_SwarmSpellHolder
/// </summary>
public bool usePsychicResearchPoints = true;
/// <summary>
/// 研究完成的提示消息翻译键
/// </summary>
public string researchCompletedMessageKey = "ARA_PawnResearch_Completed";
/// <summary>
/// 研究开始的提示消息翻译键
/// </summary>
public string researchStartedMessageKey = "ARA_PawnResearch_Started";
/// <summary>
/// 灵能科研点不足的提示消息翻译键
/// </summary>
public string researchNoPointsMessageKey = "ARA_PawnResearch_NoPoints";
public CompProperties_PawnResearchBlueprintReader()
{
this.compClass = typeof(Comp_PawnResearchBlueprintReader);
}
public void PostLoad()
{
// 如果没有指定研究标签页,使用默认的阿拉克涅科技标签页
if (requiredResearchTab == null)
{
requiredResearchTab = DefDatabase<ResearchTabDef>.GetNamedSilentFail("ARA_ResearchTab");
}
}
}
}

View File

@@ -1,855 +0,0 @@
using RimWorld;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Verse;
namespace ArachnaeSwarm
{
public class Comp_PawnResearchBlueprintReader : ThingComp
{
// === 字段定义 ===
/// <summary>
/// 当前研究的科技项目
/// </summary>
private ResearchProjectDef currentResearch = null;
/// <summary>
/// 是否正在研究中
/// </summary>
private bool isResearching = false;
/// <summary>
/// 灵能术法组件引用
/// </summary>
public Comp_SwarmSpellHolder spellHolder = null;
/// <summary>
/// 上次研究更新的tick数
/// </summary>
private int lastResearchTick = -1;
/// <summary>
/// 研究进度已添加到ResearchManager中的进度
/// </summary>
private float currentProgress = 0f;
/// <summary>
/// 研究开始时间游戏内ticks
/// </summary>
private int researchStartTick = -1;
/// <summary>
/// 临时存储的研究点数(用于累积小数)
/// </summary>
private float accumulatedResearchPoints = 0f;
// === 属性 ===
public CompProperties_PawnResearchBlueprintReader Props =>
(CompProperties_PawnResearchBlueprintReader)this.props;
public Pawn Pawn => this.parent as Pawn;
/// <summary>
/// 当前研究的科技
/// </summary>
public ResearchProjectDef CurrentResearch
{
get => currentResearch;
private set
{
currentResearch = value;
if (currentResearch != null)
{
// 获取当前全局进度
currentProgress = Find.ResearchManager.GetProgress(currentResearch);
}
else
{
currentProgress = 0f;
}
}
}
/// <summary>
/// 当前进度
/// </summary>
public float CurrentProgress => currentProgress;
/// <summary>
/// 是否正在研究中
/// </summary>
public bool IsResearching => isResearching && currentResearch != null && !currentResearch.IsFinished;
/// <summary>
/// 研究速度(每秒点数)
/// </summary>
public float ResearchSpeedPerSecond
{
get
{
if (Pawn == null || Pawn.skills == null)
return Props.baseResearchPointsPerSecond * Props.researchSpeedMultiplier;
// 基础速度乘以智力等级
float intellectualLevel = Pawn.skills.GetSkill(SkillDefOf.Intellectual).Level;
float baseSpeed = Props.baseResearchPointsPerSecond * Props.researchSpeedMultiplier;
// 智力等级影响每级增加10%速度)
float intellectualMultiplier = 1.0f + (intellectualLevel * 0.1f);
return baseSpeed * intellectualMultiplier;
}
}
/// <summary>
/// 每秒消耗的灵能科研点
/// </summary>
public float PsychicResearchPointsPerSecond
{
get
{
// 如果不需要灵能科研点返回0
if (!Props.usePsychicResearchPoints || spellHolder == null)
return 0f;
// 每研究1点需要1点灵能科研点
return ResearchSpeedPerSecond;
}
}
/// <summary>
/// Pawn是否可以进行研究
/// </summary>
public bool CanResearch
{
get
{
if (!Props.canResearch)
return false;
if (Pawn == null || Pawn.Dead || Pawn.Downed || !Pawn.Spawned)
return false;
// 如果需要灵能科研点,检查是否有足够的灵能科研点
if (Props.usePsychicResearchPoints && spellHolder != null)
{
// 检查是否有足够的灵能科研点来支持每秒的研究
if (spellHolder.PsychicResearchPoints < PsychicResearchPointsPerSecond)
return false;
}
return true;
}
}
/// <summary>
/// 研究进度百分比0-1
/// </summary>
public float ResearchProgressPercent
{
get
{
if (currentResearch == null || currentResearch.baseCost <= 0)
return 0f;
return Mathf.Clamp01(currentProgress / currentResearch.baseCost);
}
}
/// <summary>
/// 剩余研究时间(秒)
/// </summary>
public float RemainingResearchTimeSeconds
{
get
{
if (currentResearch == null || currentResearch.baseCost <= 0 || !IsResearching)
return -1f;
float remainingPoints = currentResearch.baseCost - currentProgress;
if (ResearchSpeedPerSecond <= 0)
return -1f;
return remainingPoints / ResearchSpeedPerSecond;
}
}
// === 生命周期方法 ===
public override void PostSpawnSetup(bool respawningAfterLoad)
{
base.PostSpawnSetup(respawningAfterLoad);
// 获取灵能术法组件
spellHolder = Pawn?.GetComp<Comp_SwarmSpellHolder>();
// 如果自动开始研究且当前有研究项目,开始研究
if (Props.autoStartResearch && currentResearch != null && !currentResearch.IsFinished)
{
StartResearching();
}
// 重置上次研究更新时间
lastResearchTick = Find.TickManager.TicksGame;
}
public override void CompTick()
{
base.CompTick();
// 每秒更新一次研究进度
if (Find.TickManager.TicksGame % 60 == 0)
{
UpdateResearch();
}
}
public override void PostExposeData()
{
base.PostExposeData();
Scribe_Defs.Look(ref currentResearch, "currentResearch");
Scribe_Values.Look(ref isResearching, "isResearching", false);
Scribe_Values.Look(ref lastResearchTick, "lastResearchTick", -1);
Scribe_Values.Look(ref currentProgress, "currentProgress", 0f);
Scribe_Values.Look(ref researchStartTick, "researchStartTick", -1);
Scribe_Values.Look(ref accumulatedResearchPoints, "accumulatedResearchPoints", 0f);
if (Scribe.mode == LoadSaveMode.PostLoadInit)
{
// 确保数据一致性
if (currentResearch == null)
{
isResearching = false;
currentProgress = 0f;
researchStartTick = -1;
accumulatedResearchPoints = 0f;
}
}
}
// === 研究逻辑方法 ===
/// <summary>
/// 开始研究新科技
/// </summary>
public void StartNewResearch(ResearchProjectDef project)
{
if (project == null)
{
return;
}
if (project.IsFinished)
{
Messages.Message("ARA_PawnResearch_AlreadyCompleted".Translate(project.LabelCap),
Pawn, MessageTypeDefOf.RejectInput);
return;
}
// 检查前置条件
if (!AreAllPrerequisitesCompleted(project))
{
Messages.Message("ARA_PawnResearch_PrerequisitesNotMet".Translate(project.LabelCap),
Pawn, MessageTypeDefOf.RejectInput);
return;
}
// 检查是否有足够的灵能科研点
if (Props.usePsychicResearchPoints && spellHolder != null)
{
float requiredPoints = project.baseCost - Find.ResearchManager.GetProgress(project);
if (!spellHolder.HasEnoughPsychicResearchPoints(requiredPoints))
{
Messages.Message(Props.researchNoPointsMessageKey.Translate(project.LabelCap),
Pawn, MessageTypeDefOf.RejectInput);
return;
}
}
// 设置当前研究
CurrentResearch = project;
// 开始研究
StartResearching();
// 发送消息
if (!Props.researchStartedMessageKey.NullOrEmpty())
{
Messages.Message(Props.researchStartedMessageKey.Translate(Pawn.LabelShortCap, project.LabelCap),
Pawn, MessageTypeDefOf.PositiveEvent);
}
ArachnaeLog.Debug($"[PawnResearch] {Pawn.LabelShort} 开始研究: {project.defName}");
}
/// <summary>
/// 开始研究(内部方法)
/// </summary>
private void StartResearching()
{
if (currentResearch == null || currentResearch.IsFinished)
{
isResearching = false;
return;
}
isResearching = true;
researchStartTick = Find.TickManager.TicksGame;
lastResearchTick = researchStartTick;
// 获取当前全局进度
currentProgress = Find.ResearchManager.GetProgress(currentResearch);
ArachnaeLog.Debug($"[PawnResearch] {Pawn?.LabelShort} 开始研究中: {currentResearch.defName}");
}
/// <summary>
/// 停止研究
/// </summary>
public void StopResearching()
{
isResearching = false;
researchStartTick = -1;
ArachnaeLog.Debug($"[PawnResearch] {Pawn?.LabelShort} 停止研究");
}
/// <summary>
/// 更新研究进度
/// </summary>
private void UpdateResearch()
{
if (!IsResearching || !CanResearch)
{
// 如果不能研究,停止研究
if (isResearching)
{
StopResearching();
}
return;
}
try
{
// 计算本次应该研究多少点
float researchPoints = ResearchSpeedPerSecond;
accumulatedResearchPoints += researchPoints;
// 如果累积的研究点数大于等于1则添加到全局进度
if (accumulatedResearchPoints >= 1.0f)
{
int pointsToAdd = Mathf.FloorToInt(accumulatedResearchPoints);
// 先检查并消耗灵能科研点,再添加进度(确保原子性)
if (Props.usePsychicResearchPoints && spellHolder != null)
{
// 先检查是否有足够的灵能科研点
if (!spellHolder.HasEnoughPsychicResearchPoints(pointsToAdd))
{
// 灵能科研点不足,停止研究
StopResearching();
Messages.Message(Props.researchNoPointsMessageKey.Translate(currentResearch.LabelCap),
Pawn, MessageTypeDefOf.NegativeEvent);
return;
}
// 消耗灵能科研点
if (!spellHolder.ConsumePsychicResearchPoints(pointsToAdd, $"研究 {currentResearch.LabelCap}"))
{
// 消耗失败(理论上不应该发生,因为已经检查过了)
StopResearching();
return;
}
}
// 添加到全局研究进度(在消耗成功后)
AddResearchProgress(pointsToAdd);
// 减去已添加的点数
accumulatedResearchPoints -= pointsToAdd;
}
// 检查是否完成
CheckResearchCompletion();
// 更新上次研究时间
lastResearchTick = Find.TickManager.TicksGame;
}
catch (Exception ex)
{
ArachnaeLog.Debug($"[PawnResearch] 更新研究进度时出错: {ex.Message}");
StopResearching();
}
}
/// <summary>
/// 添加研究进度到全局
/// </summary>
private void AddResearchProgress(float points)
{
if (currentResearch == null || currentResearch.IsFinished || points <= 0)
return;
// 获取当前全局进度
float globalProgress = Find.ResearchManager.GetProgress(currentResearch);
float remaining = currentResearch.baseCost - globalProgress;
// 确保不会超过总需求
float pointsToAdd = Mathf.Min(points, remaining);
if (pointsToAdd > 0)
{
Find.ResearchManager.AddProgress(currentResearch, pointsToAdd);
// 更新本地进度
currentProgress = Find.ResearchManager.GetProgress(currentResearch);
}
}
/// <summary>
/// 检查研究是否完成
/// </summary>
private void CheckResearchCompletion()
{
if (currentResearch == null)
return;
if (currentResearch.IsFinished)
{
// 研究完成
OnResearchCompleted();
}
}
/// <summary>
/// 研究完成时的处理
/// </summary>
private void OnResearchCompleted()
{
if (currentResearch == null)
return;
// 发送完成消息
if (!Props.researchCompletedMessageKey.NullOrEmpty())
{
Messages.Message(Props.researchCompletedMessageKey.Translate(Pawn.LabelShortCap, currentResearch.LabelCap),
Pawn, MessageTypeDefOf.PositiveEvent);
}
ArachnaeLog.Debug($"[PawnResearch] {Pawn.LabelShort} 完成研究: {currentResearch.defName}");
// 停止研究
StopResearching();
// 清空当前研究
CurrentResearch = null;
currentProgress = 0f;
accumulatedResearchPoints = 0f;
}
/// <summary>
/// 强制完成当前研究(调试用)
/// </summary>
public void ForceCompleteResearch()
{
if (currentResearch == null || currentResearch.IsFinished)
return;
// 消耗剩余的灵能科研点
if (Props.usePsychicResearchPoints && spellHolder != null)
{
float remainingPoints = currentResearch.baseCost - Find.ResearchManager.GetProgress(currentResearch);
spellHolder.ConsumePsychicResearchPoints(remainingPoints, $"强制完成研究 {currentResearch.LabelCap}");
}
// 直接完成研究
Find.ResearchManager.FinishProject(currentResearch, false);
OnResearchCompleted();
}
// === 条件检查方法 ===
/// <summary>
/// 检查所有前置条件是否完成
/// </summary>
private bool AreAllPrerequisitesCompleted(ResearchProjectDef project)
{
if (project == null)
return false;
// 检查普通前置
if (project.prerequisites != null)
{
foreach (var prereq in project.prerequisites)
{
if (!prereq.IsFinished)
return false;
}
}
// 检查隐藏前置
if (project.hiddenPrerequisites != null)
{
foreach (var hiddenPrereq in project.hiddenPrerequisites)
{
if (hiddenPrereq != null && !hiddenPrereq.IsFinished)
return false;
}
}
return true;
}
private bool HasMissingHiddenPrerequisites(ResearchProjectDef project)
{
if (project.hiddenPrerequisites != null)
{
foreach (var hiddenPrereq in project.hiddenPrerequisites)
{
if (hiddenPrereq != null && !hiddenPrereq.IsFinished)
return true;
}
}
return false;
}
// === Gizmo 和 UI 方法 ===
public override IEnumerable<Gizmo> CompGetGizmosExtra()
{
foreach (Gizmo gizmo in base.CompGetGizmosExtra())
{
yield return gizmo;
}
if (!Props.showResearchGizmo || Pawn == null || Pawn.Faction != Faction.OfPlayer)
yield break;
// 研究进度 Gizmo
yield return new Gizmo_PawnResearchProgress(this);
// 选择研究按钮
if (CanResearch || currentResearch == null)
{
var selectCmd = new Command_Action();
selectCmd.defaultLabel = "ARA_PawnResearch_SelectProject".Translate();
selectCmd.defaultDesc = "ARA_PawnResearch_SelectProjectDesc".Translate();
selectCmd.icon = ContentFinder<Texture2D>.Get("ArachnaeSwarm/UI/Abilities/ARA_EggSpew_Techprint", false);
selectCmd.action = delegate
{
ShowResearchMenu();
};
yield return selectCmd;
}
// 开始/停止研究按钮
if (currentResearch != null && !currentResearch.IsFinished)
{
if (IsResearching)
{
var stopCmd = new Command_Action();
stopCmd.defaultLabel = "ARA_PawnResearch_StopResearch".Translate();
stopCmd.defaultDesc = "ARA_PawnResearch_StopResearchDesc".Translate();
stopCmd.icon = ContentFinder<Texture2D>.Get("UI/Commands/Halt", false);
stopCmd.action = delegate
{
StopResearching();
Messages.Message("ARA_PawnResearch_Stopped".Translate(Pawn.LabelShortCap, currentResearch.LabelCap),
Pawn, MessageTypeDefOf.NeutralEvent);
};
yield return stopCmd;
}
else
{
var startCmd = new Command_Action();
startCmd.defaultLabel = "ARA_PawnResearch_StartResearch".Translate();
startCmd.defaultDesc = "ARA_PawnResearch_StartResearchDesc".Translate();
startCmd.icon = ContentFinder<Texture2D>.Get("ArachnaeSwarm/UI/Abilities/ARA_Ability_Morph", false);
startCmd.action = delegate
{
StartResearching();
Messages.Message("ARA_PawnResearch_Started".Translate(Pawn.LabelShortCap, currentResearch.LabelCap),
Pawn, MessageTypeDefOf.PositiveEvent);
};
yield return startCmd;
}
}
// 调试按钮
if (DebugSettings.godMode && currentResearch != null && !currentResearch.IsFinished)
{
var debugCmd = new Command_Action();
debugCmd.defaultLabel = "ARA_PawnResearch_DebugComplete".Translate();
debugCmd.defaultDesc = "ARA_PawnResearch_DebugCompleteDesc".Translate();
debugCmd.icon = ContentFinder<Texture2D>.Get("UI/Designators/Dev", false);
debugCmd.action = delegate
{
ForceCompleteResearch();
};
yield return debugCmd;
}
}
/// <summary>
/// 显示研究选择菜单
/// </summary>
public void ShowResearchMenu()
{
try
{
// 使用指定的研究标签页
ResearchTabDef researchTab = Props.requiredResearchTab;
if (researchTab == null)
{
// 如果没有指定,使用默认的阿拉克涅科技标签页
researchTab = DefDatabase<ResearchTabDef>.GetNamedSilentFail("ARA_ResearchTab");
if (researchTab == null)
{
Messages.Message("ARA_PawnResearch_TabNotFound".Translate(), MessageTypeDefOf.RejectInput);
return;
}
}
// 获取可用的研究项目
var availableProjects = DefDatabase<ResearchProjectDef>.AllDefsListForReading
.Where(p => p.tab == researchTab && p.techprintCount > 0 && !p.IsFinished)
.ToList();
if (availableProjects.Count == 0)
{
Messages.Message("ARA_PawnResearch_NoProjectsAvailable".Translate(), MessageTypeDefOf.NeutralEvent);
return;
}
// 创建菜单选项
List<FloatMenuOption> options = new List<FloatMenuOption>();
var sortedProjects = availableProjects.OrderBy(p => p.defName).ToList();
foreach (var project in sortedProjects)
{
bool allPrerequisitesMet = AreAllPrerequisitesCompleted(project);
// 检查是否有足够的灵能科研点
bool hasEnoughPoints = true;
if (Props.usePsychicResearchPoints && spellHolder != null)
{
float requiredPoints = project.baseCost - Find.ResearchManager.GetProgress(project);
hasEnoughPoints = spellHolder.HasEnoughPsychicResearchPoints(requiredPoints);
}
string label = project.LabelCap.RawText ?? project.defName;
float globalProgress = Find.ResearchManager.GetProgress(project);
// 显示进度
if (globalProgress > 0)
label += $" ({globalProgress:F0}/{project.baseCost:F0})";
// 显示所需灵能科研点
if (Props.usePsychicResearchPoints && spellHolder != null)
{
float requiredPoints = project.baseCost - globalProgress;
label += $" [{requiredPoints:F0}点]";
}
// 根据条件设置颜色
if (!allPrerequisitesMet)
{
bool missingHidden = HasMissingHiddenPrerequisites(project);
if (missingHidden)
label = $"<color=#ff9900>{label} [{"ResearchBlueprintReader_HiddenPrerequisite".Translate()}]</color>";
else
label = $"<color=#999999>{label} [{"ResearchBlueprintReader_PrerequisitesNeeded".Translate()}]</color>";
}
else if (!hasEnoughPoints)
{
label = $"<color=#ff6666>{label} [{"ARA_PawnResearch_InsufficientPoints".Translate()}]</color>";
}
// 创建菜单选项
var option = new FloatMenuOption(label, () => StartNewResearch(project))
{
Disabled = !allPrerequisitesMet || !hasEnoughPoints,
tooltip = GetProjectTooltip(project, allPrerequisitesMet, hasEnoughPoints)
};
options.Add(option);
}
if (options.Count > 0)
{
Find.WindowStack.Add(new FloatMenu(options));
}
}
catch (Exception ex)
{
ArachnaeLog.Debug($"[PawnResearch] Error in ShowResearchMenu: {ex}");
Messages.Message("ARA_PawnResearch_MenuError".Translate(ex.Message),
MessageTypeDefOf.NegativeEvent);
}
}
/// <summary>
/// 获取研究项目的工具提示
/// </summary>
private string GetProjectTooltip(ResearchProjectDef project, bool prerequisitesMet, bool hasEnoughPoints)
{
var builder = new System.Text.StringBuilder();
builder.AppendLine(project.description.StripTags());
builder.AppendLine();
builder.AppendLine("ARA_PawnResearch_ProjectCost".Translate() + $": {project.baseCost}");
float globalProgress = Find.ResearchManager.GetProgress(project);
builder.AppendLine("ResearchBlueprintReader_CurrentProgress".Translate() + $": {globalProgress:F0}/{project.baseCost:F0}");
// 显示所需灵能科研点
if (Props.usePsychicResearchPoints && spellHolder != null)
{
float requiredPoints = project.baseCost - globalProgress;
builder.AppendLine("ARA_PawnResearch_RequiredPoints".Translate() + $": {requiredPoints:F0}");
builder.AppendLine("ARA_PawnResearch_CurrentPoints".Translate() + $": {spellHolder.PsychicResearchPoints:F0}");
}
// 显示研究速度
builder.AppendLine("ARA_PawnResearch_Speed".Translate() + $": {ResearchSpeedPerSecond:F1}/秒");
// 显示研究时间估算
if (prerequisitesMet && hasEnoughPoints)
{
float remainingPoints = project.baseCost - globalProgress;
float timeSeconds = remainingPoints / ResearchSpeedPerSecond;
int ticks = Mathf.RoundToInt(timeSeconds * 60f);
string timeStr = ticks.ToStringTicksToPeriod(allowSeconds: true, shortForm: true);
builder.AppendLine("ARA_PawnResearch_EstimatedTime".Translate() + $": {timeStr}");
}
// 检查未完成的前置
if (!prerequisitesMet)
{
List<ResearchProjectDef> missingPrereqs = new List<ResearchProjectDef>();
// 普通前置
if (project.prerequisites != null)
{
foreach (var prereq in project.prerequisites)
{
if (!prereq.IsFinished)
missingPrereqs.Add(prereq);
}
}
// 隐藏前置
if (project.hiddenPrerequisites != null)
{
foreach (var hiddenPrereq in project.hiddenPrerequisites)
{
if (hiddenPrereq != null && !hiddenPrereq.IsFinished)
missingPrereqs.Add(hiddenPrereq);
}
}
if (missingPrereqs.Count > 0)
{
builder.AppendLine();
builder.AppendLine("<color=#ff9999>" + "ResearchBlueprintReader_PrerequisitesNeeded".Translate() + ":</color>");
foreach (var prereq in missingPrereqs)
{
string label = prereq.LabelCap.RawText ?? prereq.defName;
bool isHidden = project.hiddenPrerequisites != null &&
project.hiddenPrerequisites.Contains(prereq);
if (isHidden)
builder.AppendLine($" • {label} <color=#ffcc00>[{"ResearchBlueprintReader_MissingHiddenPrerequisites".Translate()}]</color>");
else
builder.AppendLine($" • {label}");
}
}
}
// 检查灵能科研点是否足够
if (!hasEnoughPoints && Props.usePsychicResearchPoints && spellHolder != null)
{
float requiredPoints = project.baseCost - globalProgress;
builder.AppendLine();
builder.AppendLine("<color=#ff6666>" + "ARA_PawnResearch_InsufficientPoints".Translate() + ":</color>");
}
return builder.ToString();
}
// === 调试和信息方法 ===
/// <summary>
/// 获取研究状态描述
/// </summary>
public string GetResearchStatus()
{
if (Pawn == null)
return "无Pawn";
if (currentResearch == null)
return $"{Pawn.LabelShort}: 未选择研究项目";
if (currentResearch.IsFinished)
return $"{Pawn.LabelShort}: 已完成研究 {currentResearch.LabelCap}";
string status = IsResearching ? "研究中" : "暂停中";
string progress = $"{currentProgress:F0}/{currentResearch.baseCost:F0} ({ResearchProgressPercent:P0})";
string pointsInfo = "";
if (Props.usePsychicResearchPoints && spellHolder != null)
{
pointsInfo = $", 灵能科研点: {spellHolder.PsychicResearchPoints:F0}";
}
return $"{Pawn.LabelShort}: {status} {currentResearch.LabelCap} ({progress}{pointsInfo})";
}
/// <summary>
/// 获取调试信息
/// </summary>
public string GetDebugInfo()
{
var builder = new System.Text.StringBuilder();
builder.AppendLine("=== Pawn研究蓝图解读器调试信息 ===");
builder.AppendLine($"Pawn: {Pawn?.LabelShort ?? ""}");
builder.AppendLine($"可研究: {CanResearch}");
builder.AppendLine($"正在研究: {IsResearching}");
if (currentResearch != null)
{
builder.AppendLine($"当前研究: {currentResearch.defName}");
builder.AppendLine($"研究进度: {currentProgress:F0}/{currentResearch.baseCost:F0} ({ResearchProgressPercent:P0})");
builder.AppendLine($"研究速度: {ResearchSpeedPerSecond:F1}/秒");
}
else
{
builder.AppendLine("当前研究: 无");
}
if (spellHolder != null)
{
builder.AppendLine($"灵能术法组件: 已连接");
builder.AppendLine($"灵能科研点: {spellHolder.PsychicResearchPoints:F0}");
builder.AppendLine($"每秒消耗: {PsychicResearchPointsPerSecond:F1}");
}
else
{
builder.AppendLine($"灵能术法组件: 未找到");
}
builder.AppendLine($"使用灵能科研点: {Props.usePsychicResearchPoints}");
builder.AppendLine($"自动开始研究: {Props.autoStartResearch}");
builder.AppendLine($"研究标签页: {Props.requiredResearchTab?.defName ?? ""}");
return builder.ToString();
}
}
}

View File

@@ -1,259 +0,0 @@
using RimWorld;
using System.Text;
using UnityEngine;
using Verse;
namespace ArachnaeSwarm
{
[StaticConstructorOnStartup]
public class Gizmo_PawnResearchProgress : Gizmo
{
private readonly Comp_PawnResearchBlueprintReader researchComp;
// 尺寸常量
private const float Width = 180f;
private const float GizmoHeight = 75f;
private const float Padding = 6f;
private const float BarHeight = 18f;
// 材质颜色
private static readonly Texture2D ProgressBarTex = SolidColorMaterials.NewSolidColorTexture(new Color(0.3f, 0.6f, 0.9f, 0.9f));
private static readonly Texture2D CompletedBarTex = SolidColorMaterials.NewSolidColorTexture(new Color(0.2f, 0.8f, 0.3f, 0.9f));
private static readonly Texture2D PausedBarTex = SolidColorMaterials.NewSolidColorTexture(new Color(0.7f, 0.7f, 0.3f, 0.9f));
private static readonly Texture2D NoPowerBarTex = SolidColorMaterials.NewSolidColorTexture(new Color(0.5f, 0.5f, 0.5f, 0.9f));
private static readonly Texture2D EmptyBarTex = SolidColorMaterials.NewSolidColorTexture(new Color(0.15f, 0.15f, 0.15f, 0.8f));
public Gizmo_PawnResearchProgress(Comp_PawnResearchBlueprintReader researchComp)
{
this.researchComp = researchComp;
Order = -130f;
}
public override float GetWidth(float maxWidth)
{
return Mathf.Min(Width, maxWidth);
}
public override GizmoResult GizmoOnGUI(Vector2 topLeft, float maxWidth, GizmoRenderParms parms)
{
Rect rect = new Rect(topLeft.x, topLeft.y, GetWidth(maxWidth), GizmoHeight);
Widgets.DrawWindowBackground(rect);
Rect innerRect = rect.ContractedBy(Padding);
float curY = innerRect.y;
var currentResearch = researchComp.CurrentResearch;
// === 第一行:标题(可点击选择研究) ===
Text.Font = GameFont.Small;
Text.Anchor = TextAnchor.MiddleLeft;
Rect titleRect = new Rect(innerRect.x, curY, innerRect.width, 18f);
string title;
if (currentResearch != null)
{
title = currentResearch.LabelCap.RawText ?? currentResearch.defName;
// 截断过长的标题
title = title.Truncate(innerRect.width - 20f);
}
else
{
title = "ARA_PawnResearch_NoProjectSelected".Translate();
}
// 标题可点击
if (Mouse.IsOver(titleRect))
{
Widgets.DrawHighlight(titleRect);
}
if (Widgets.ButtonInvisible(titleRect) && researchComp.CanResearch)
{
researchComp.ShowResearchMenu();
}
// 显示下拉箭头(如果可以选择研究)
if (researchComp.CanResearch)
{
GUI.color = new Color(0.7f, 0.9f, 1f);
Widgets.Label(titleRect, title + " ▼");
GUI.color = Color.white;
}
else
{
Widgets.Label(titleRect, title);
}
curY += 20f;
// === 第二行:状态信息 ===
Text.Font = GameFont.Tiny;
Text.Anchor = TextAnchor.MiddleLeft;
Rect statusRect = new Rect(innerRect.x, curY, innerRect.width, 14f);
if (currentResearch != null)
{
string statusText;
if (currentResearch.IsFinished)
{
GUI.color = new Color(0.3f, 0.9f, 0.3f);
statusText = "ARA_PawnResearch_Gizmo_Completed".Translate();
}
else if (researchComp.IsResearching && researchComp.CanResearch)
{
GUI.color = new Color(0.5f, 0.8f, 1f);
statusText = "ARA_PawnResearch_Gizmo_Researching".Translate();
}
else if (!researchComp.CanResearch)
{
GUI.color = new Color(0.8f, 0.2f, 0.2f);
statusText = "ARA_PawnResearch_Gizmo_CannotResearch".Translate();
}
else
{
GUI.color = new Color(0.9f, 0.7f, 0.2f);
statusText = "ARA_PawnResearch_Gizmo_Paused".Translate();
}
Widgets.Label(statusRect, statusText);
}
else
{
GUI.color = new Color(0.5f, 0.5f, 0.5f);
Widgets.Label(statusRect, "ARA_PawnResearch_SelectProject".Translate());
}
curY += 15f;
// === 第三行:进度条 ===
GUI.color = Color.white;
Rect barRect = new Rect(innerRect.x, curY, innerRect.width, BarHeight);
// 背景
GUI.DrawTexture(barRect, EmptyBarTex);
if (currentResearch != null)
{
float percentage = researchComp.ResearchProgressPercent;
// 根据状态选择颜色
Texture2D barTex;
if (currentResearch.IsFinished)
barTex = CompletedBarTex;
else if (researchComp.IsResearching && researchComp.CanResearch)
barTex = ProgressBarTex;
else if (!researchComp.CanResearch)
barTex = NoPowerBarTex;
else
barTex = PausedBarTex;
// 填充条
Rect fillRect = new Rect(barRect.x, barRect.y, barRect.width * percentage, barRect.height);
GUI.DrawTexture(fillRect, barTex);
// 百分比文字
Text.Font = GameFont.Tiny;
Text.Anchor = TextAnchor.MiddleCenter;
GUI.color = Color.white;
string progressText;
if (currentResearch.IsFinished)
{
progressText = "ARA_PawnResearch_CompletedShort".Translate();
}
else
{
progressText = $"{researchComp.CurrentProgress:F0} / {currentResearch.baseCost:F0}";
}
Widgets.Label(barRect, progressText);
}
else
{
// 无项目时显示空状态
Text.Font = GameFont.Tiny;
Text.Anchor = TextAnchor.MiddleCenter;
GUI.color = new Color(0.5f, 0.5f, 0.5f);
Widgets.Label(barRect, "---");
}
// === 工具提示 ===
if (Mouse.IsOver(rect))
{
Widgets.DrawHighlight(rect);
TooltipHandler.TipRegion(rect, GetTooltip());
}
GUI.color = Color.white;
Text.Anchor = TextAnchor.UpperLeft;
Text.Font = GameFont.Small;
return new GizmoResult(GizmoState.Clear);
}
private string GetTooltip()
{
var sb = new System.Text.StringBuilder();
var currentResearch = researchComp.CurrentResearch;
sb.AppendLine("ARA_PawnResearch_TooltipTitle".Translate());
sb.AppendLine();
if (currentResearch != null)
{
sb.AppendLine("ARA_PawnResearch_TooltipProject".Translate(currentResearch.LabelCap));
sb.AppendLine("ARA_PawnResearch_TooltipProgress".Translate(
researchComp.CurrentProgress.ToString("F0"),
currentResearch.baseCost.ToString("F0")));
sb.AppendLine();
if (currentResearch.IsFinished)
{
sb.AppendLine("<color=green>" + "ARA_PawnResearch_CompletedShort".Translate() + "</color>");
}
else if (researchComp.IsResearching && researchComp.CanResearch)
{
// 显示研究速度
sb.AppendLine("ARA_PawnResearch_TooltipSpeed".Translate(researchComp.ResearchSpeedPerSecond.ToString("F1")));
// 显示灵能科研点消耗
if (researchComp.Props.usePsychicResearchPoints)
{
sb.AppendLine("ARA_PawnResearch_TooltipPointsConsumption".Translate(researchComp.PsychicResearchPointsPerSecond.ToString("F1")));
}
}
else if (!researchComp.CanResearch)
{
sb.AppendLine("<color=red>" + "ARA_PawnResearch_TooltipCannotResearch".Translate() + "</color>");
// 检查具体原因
if (researchComp.Pawn != null)
{
if (researchComp.Pawn.Dead)
sb.AppendLine(" • " + "Dead".Translate());
if (researchComp.Pawn.Downed)
sb.AppendLine(" • " + "Downed".Translate());
if (!researchComp.Pawn.Spawned)
sb.AppendLine(" • " + "ARA_NotSpawned".Translate());
}
if (researchComp.Props.usePsychicResearchPoints && researchComp.spellHolder != null)
{
if (researchComp.spellHolder.PsychicResearchPoints < researchComp.PsychicResearchPointsPerSecond)
{
sb.AppendLine(" • " + "ARA_PawnResearch_InsufficientPointsShort".Translate());
}
}
}
else
{
sb.AppendLine("<color=yellow>" + "ARA_PawnResearch_TooltipPaused".Translate() + "</color>");
}
}
else
{
sb.AppendLine("ARA_PawnResearch_TooltipNoProject".Translate());
}
return sb.ToString().TrimEndNewlines();
}
}
}

View File

@@ -1,888 +0,0 @@
using RimWorld;
using System.Collections.Generic;
using UnityEngine;
using Verse;
namespace ArachnaeSwarm
{
/// <summary>
/// 虫群术法持有组件 - 每个虫群单位都拥有独立的术法系统
/// </summary>
public class Comp_SwarmSpellHolder : ThingComp
{
#region
private const float MAX_PSYCHIC_RESEARCH = 3000f; // 灵能科研点上限固定为3000
private const float SPELL_LEVEL_MULTIPLIER = 1f; // 术法等级暂定为1
private const float PSYCHIC_LOAD_PER_LEVEL = 10f; // 每级术法等级提供10点灵能负载容量
private const int LOAD_REDUCTION_INTERVAL = 60; // 每60ticks减少1点负载
#endregion
#region
private float psychicResearchPoints = 0f; // 当前灵能科研点
private float psychicLoad = 0f; // 当前灵能负载
private float psychicLoadCapacity = 0f; // 灵能负载容量
private float spellLevel = SPELL_LEVEL_MULTIPLIER; // 术法等级
// 冷却系统
private int cooldownTicksRemaining = 0; // 冷却计数器ticks
private int cooldownDurationTicks = 0; // 冷却总时长ticks
private bool isCooldownActive = false; // 冷却是否激活
// 超载系统
private bool isOverloaded = false; // 是否超载
private bool limitPsychicLoadAmount = true; // 负载限制器(开启时不允许超过容量)
private int overloadCheckTick = -1; // 上次检查超载的时间
private const int OVERLOAD_CHECK_INTERVAL = 30; // 检查超载间隔ticks
// 调试和状态跟踪
private int lastUpdateTick = -1;
private bool systemInitialized = false;
#endregion
#region
public CompProperties_SwarmSpellHolder Props => (CompProperties_SwarmSpellHolder)props;
/// <summary>
/// 当前灵能科研点
/// </summary>
public float PsychicResearchPoints
{
get => psychicResearchPoints;
private set => psychicResearchPoints = Mathf.Clamp(value, 0f, MAX_PSYCHIC_RESEARCH);
}
/// <summary>
/// 灵能科研点上限固定为3000
/// </summary>
public float MaxPsychicResearchPoints => MAX_PSYCHIC_RESEARCH;
/// <summary>
/// 灵能科研点百分比
/// </summary>
public float PsychicResearchPercent => MAX_PSYCHIC_RESEARCH > 0 ? psychicResearchPoints / MAX_PSYCHIC_RESEARCH : 0f;
/// <summary>
/// 当前灵能负载
/// </summary>
public float PsychicLoad
{
get => psychicLoad;
private set
{
float oldValue = psychicLoad;
psychicLoad = Mathf.Max(0f, value);
// 检查是否进入或退出超载状态
bool wasOverloaded = isOverloaded;
isOverloaded = psychicLoad > psychicLoadCapacity;
// 如果超载状态发生变化
if (wasOverloaded != isOverloaded)
{
HandleOverloadStateChange(wasOverloaded, isOverloaded);
}
// 如果负载增加,重置冷却计时器
if (psychicLoad > oldValue)
{
ResetCooldown();
}
}
}
/// <summary>
/// 灵能负载容量10 * 术法等级)
/// </summary>
public float PsychicLoadCapacity
{
get => psychicLoadCapacity;
private set
{
psychicLoadCapacity = Mathf.Max(0f, value);
// 重新检查超载状态
bool wasOverloaded = isOverloaded;
isOverloaded = psychicLoad > psychicLoadCapacity;
if (wasOverloaded != isOverloaded)
{
HandleOverloadStateChange(wasOverloaded, isOverloaded);
}
}
}
/// <summary>
/// 灵能负载百分比用于显示限制在100%以内)
/// </summary>
public float PsychicLoadPercentForDisplay
{
get
{
if (psychicLoadCapacity <= 0) return 0f;
float percent = psychicLoad / psychicLoadCapacity;
return Mathf.Min(1f, percent); // 最大显示为100%
}
}
/// <summary>
/// 实际灵能负载百分比可能超过100%
/// </summary>
public float PsychicLoadPercent => psychicLoadCapacity > 0 ? psychicLoad / psychicLoadCapacity : 0f;
/// <summary>
/// 是否超载(负载超过容量)
/// </summary>
public bool IsOverloaded => isOverloaded;
/// <summary>
/// 超载百分比(超过容量的部分)
/// </summary>
public float OverloadPercent
{
get
{
if (!isOverloaded || psychicLoadCapacity <= 0) return 0f;
return (psychicLoad - psychicLoadCapacity) / psychicLoadCapacity;
}
}
/// <summary>
/// 冷却剩余时间(秒)
/// </summary>
public float CooldownSecondsRemaining => cooldownTicksRemaining / 60f;
/// <summary>
/// 冷却百分比
/// </summary>
public float CooldownPercent
{
get
{
if (cooldownDurationTicks <= 0) return 0f;
return (float)cooldownTicksRemaining / cooldownDurationTicks;
}
}
/// <summary>
/// 术法等级
/// </summary>
public float SpellLevel
{
get => spellLevel;
private set
{
float oldLevel = spellLevel;
spellLevel = parent.GetStatValue(ARA_StatDefOf.ARA_SwarmSpell_Level);
// 更新负载容量
PsychicLoadCapacity = spellLevel * PSYCHIC_LOAD_PER_LEVEL;
}
}
/// <summary>
/// 系统是否已初始化
/// </summary>
public bool IsSystemInitialized => systemInitialized;
/// <summary>
/// Pawn是否可用活着且清醒
/// </summary>
public bool IsPawnAvailable
{
get
{
if (parent is Pawn pawn)
{
return !pawn.Dead && !pawn.Downed && pawn.Spawned;
}
return false;
}
}
/// <summary>
/// 是否冷却中
/// </summary>
public bool IsOnCooldown => isCooldownActive && cooldownTicksRemaining > 0;
/// <summary>
/// 是否启用负载限制器(类似原版心灵熵限制器)
/// </summary>
public bool LimitPsychicLoadAmount
{
get => limitPsychicLoadAmount;
set => limitPsychicLoadAmount = value;
}
#endregion
#region
public override void PostSpawnSetup(bool respawningAfterLoad)
{
base.PostSpawnSetup(respawningAfterLoad);
// 初始化系统
InitializeSystem();
}
public override void CompTick()
{
base.CompTick();
// 每tick都更新冷却
UpdateCooldownTick();
// 每60ticks更新一次系统状态
if (parent.IsHashIntervalTick(60))
{
UpdateSystemTick();
}
// 定期检查超载状态(更频繁的检查)
if (Find.TickManager.TicksGame - overloadCheckTick >= OVERLOAD_CHECK_INTERVAL)
{
CheckOverloadStatus();
overloadCheckTick = Find.TickManager.TicksGame;
}
}
public override void PostExposeData()
{
base.PostExposeData();
// 序列化所有数据
Scribe_Values.Look(ref psychicResearchPoints, "psychicResearchPoints", 0f);
Scribe_Values.Look(ref psychicLoad, "psychicLoad", 0f);
Scribe_Values.Look(ref psychicLoadCapacity, "psychicLoadCapacity", 0f);
Scribe_Values.Look(ref spellLevel, "spellLevel", SPELL_LEVEL_MULTIPLIER);
Scribe_Values.Look(ref systemInitialized, "systemInitialized", false);
Scribe_Values.Look(ref lastUpdateTick, "lastUpdateTick", -1);
// 冷却系统
Scribe_Values.Look(ref cooldownTicksRemaining, "cooldownTicksRemaining", 0);
Scribe_Values.Look(ref cooldownDurationTicks, "cooldownDurationTicks", 0);
Scribe_Values.Look(ref isCooldownActive, "isCooldownActive", false);
// 超载系统
Scribe_Values.Look(ref isOverloaded, "isOverloaded", false);
Scribe_Values.Look(ref overloadCheckTick, "overloadCheckTick", -1);
Scribe_Values.Look(ref limitPsychicLoadAmount, "limitPsychicLoadAmount", true);
if (Scribe.mode == LoadSaveMode.LoadingVars)
{
// 加载后重新计算负载容量以确保一致性
if (psychicLoadCapacity <= 0)
{
psychicLoadCapacity = spellLevel * PSYCHIC_LOAD_PER_LEVEL;
}
// 重新检查超载状态
CheckOverloadStatus();
}
}
#endregion
#region
/// <summary>
/// 初始化术法系统
/// </summary>
private void InitializeSystem()
{
if (systemInitialized)
return;
// 设置初始值
spellLevel = parent.GetStatValue(ARA_StatDefOf.ARA_SwarmSpell_Level);
psychicLoadCapacity = spellLevel * PSYCHIC_LOAD_PER_LEVEL;
psychicLoad = 0f;
psychicResearchPoints = 0f;
// 初始化冷却系统
cooldownTicksRemaining = 0;
cooldownDurationTicks = 0;
isCooldownActive = false;
// 初始化超载系统
isOverloaded = false;
overloadCheckTick = Find.TickManager.TicksGame;
systemInitialized = true;
lastUpdateTick = Find.TickManager.TicksGame;
}
/// <summary>
/// 重置术法系统
/// </summary>
public void ResetSystem()
{
psychicResearchPoints = 0f;
psychicLoad = 0f;
psychicLoadCapacity = spellLevel * PSYCHIC_LOAD_PER_LEVEL;
// 重置冷却
cooldownTicksRemaining = 0;
cooldownDurationTicks = 0;
isCooldownActive = false;
// 重置超载
isOverloaded = false;
RemoveOverdriveHediff();
systemInitialized = false;
}
#endregion
#region
/// <summary>
/// 重置冷却计时器
/// </summary>
private void ResetCooldown()
{
if (!IsSystemInitialized || !IsPawnAvailable)
return;
// 读取ARA_SwarmSpell_Cooldown_Delay stat单位
Pawn pawn = parent as Pawn;
if (pawn == null) return;
float cooldownDelaySeconds = pawn.GetStatValue(ARA_StatDefOf.ARA_SwarmSpell_Cooldown_Delay);
cooldownDurationTicks = Mathf.RoundToInt(cooldownDelaySeconds * 60f); // 转换为ticks
cooldownTicksRemaining = cooldownDurationTicks;
isCooldownActive = true;
}
/// <summary>
/// 每tick更新冷却
/// </summary>
private void UpdateCooldownTick()
{
if (!IsSystemInitialized || !IsPawnAvailable)
return;
// 如果没有冷却中,或者超载状态下冷却锁定,则不处理
if (!isCooldownActive || cooldownTicksRemaining <= 0 || isOverloaded)
return;
// 减少冷却时间
cooldownTicksRemaining--;
// 如果冷却结束
if (cooldownTicksRemaining <= 0)
{
cooldownTicksRemaining = 0;
isCooldownActive = false;
}
}
/// <summary>
/// 自动减少灵能负载(当冷却结束时)
/// </summary>
private void ReduceLoadAfterCooldown()
{
if (!IsSystemInitialized || !IsPawnAvailable || isOverloaded)
return;
// 每60ticks减少1点负载
if (Find.TickManager.TicksGame % LOAD_REDUCTION_INTERVAL == 0)
{
// 只减少1点
if (psychicLoad > 0)
{
PsychicLoad = Mathf.Max(0f, psychicLoad - 1f);
// 如果还有负载,重新激活冷却
if (psychicLoad > 0)
{
ResetCooldown();
}
}
}
}
/// <summary>
/// 获取冷却状态描述
/// </summary>
public string GetCooldownStatus()
{
if (!isCooldownActive || cooldownTicksRemaining <= 0)
return "冷却完成";
return $"冷却中: {CooldownSecondsRemaining:F1}秒 ({CooldownPercent:P0})";
}
#endregion
#region
/// <summary>
/// 处理超载状态变化
/// </summary>
private void HandleOverloadStateChange(bool wasOverloaded, bool nowOverloaded)
{
Pawn pawn = parent as Pawn;
if (pawn == null) return;
if (nowOverloaded && !wasOverloaded)
{
// 进入超载状态
AddOverdriveHediff();
}
else if (!nowOverloaded && wasOverloaded)
{
// 退出超载状态
RemoveOverdriveHediff();
}
}
/// <summary>
/// 添加超载Hediff
/// </summary>
private void AddOverdriveHediff()
{
Pawn pawn = parent as Pawn;
if (pawn == null || pawn.Dead || pawn.Downed) return;
HediffDef overdriveDef = ARA_HediffDefOf.ARA_SwarmSpell_Overdrive;
if (overdriveDef == null)
{
Log.Warning("[虫群术法] 未找到ARA_SwarmSpell_Overdrive Hediff定义");
return;
}
// 添加或更新超载Hediff
Hediff overdriveHediff = pawn.health.hediffSet.GetFirstHediffOfDef(overdriveDef);
if (overdriveHediff == null)
{
overdriveHediff = HediffMaker.MakeHediff(overdriveDef, pawn);
pawn.health.AddHediff(overdriveHediff);
}
}
/// <summary>
/// 移除超载Hediff
/// </summary>
private void RemoveOverdriveHediff()
{
Pawn pawn = parent as Pawn;
if (pawn == null || pawn.Dead || pawn.Downed) return;
HediffDef overdriveDef = ARA_HediffDefOf.ARA_SwarmSpell_Overdrive;
if (overdriveDef == null) return;
Hediff overdriveHediff = pawn.health.hediffSet.GetFirstHediffOfDef(overdriveDef);
if (overdriveHediff != null)
{
pawn.health.RemoveHediff(overdriveHediff);
}
}
/// <summary>
/// 定期检查超载状态
/// </summary>
private void CheckOverloadStatus()
{
if (!IsSystemInitialized || !IsPawnAvailable)
return;
Pawn pawn = parent as Pawn;
if (pawn == null) return;
// 检查当前是否应该处于超载状态
bool shouldBeOverloaded = psychicLoad > psychicLoadCapacity;
// 如果状态不一致,更新
if (shouldBeOverloaded != isOverloaded)
{
isOverloaded = shouldBeOverloaded;
HandleOverloadStateChange(!shouldBeOverloaded, shouldBeOverloaded);
}
// 如果处于超载状态确保Hediff存在
if (isOverloaded)
{
HediffDef overdriveDef = ARA_HediffDefOf.ARA_SwarmSpell_Overdrive;
if (overdriveDef != null)
{
Hediff overdriveHediff = pawn.health.hediffSet.GetFirstHediffOfDef(overdriveDef);
if (overdriveHediff == null)
{
// Hediff丢失重新添加
AddOverdriveHediff();
}
}
}
}
/// <summary>
/// 强制退出超载状态(通过外部方法降低负载时调用)
/// </summary>
public void ForceExitOverload()
{
if (!isOverloaded) return;
// 确保负载不超过容量
if (psychicLoad > psychicLoadCapacity)
{
psychicLoad = psychicLoadCapacity;
}
// 更新状态
isOverloaded = false;
RemoveOverdriveHediff();
}
#endregion
#region -
/// <summary>
/// 获取当前灵能科研点
/// </summary>
public float GetPsychicResearchPoints()
{
return psychicResearchPoints;
}
/// <summary>
/// 设置灵能科研点
/// </summary>
public bool SetPsychicResearchPoints(float value, string reason = "")
{
if (!IsSystemInitialized || value < 0f)
return false;
float oldValue = psychicResearchPoints;
psychicResearchPoints = Mathf.Clamp(value, 0f, MAX_PSYCHIC_RESEARCH);
return true;
}
/// <summary>
/// 增加灵能科研点
/// </summary>
public bool AddPsychicResearchPoints(float amount, string reason = "")
{
if (!IsSystemInitialized || amount <= 0f)
return false;
float oldValue = psychicResearchPoints;
psychicResearchPoints = Mathf.Min(psychicResearchPoints + amount, MAX_PSYCHIC_RESEARCH);
return true;
}
/// <summary>
/// 消耗灵能科研点
/// </summary>
public bool ConsumePsychicResearchPoints(float amount, string reason = "")
{
if (!IsSystemInitialized || amount <= 0f || psychicResearchPoints < amount)
return false;
float oldValue = psychicResearchPoints;
psychicResearchPoints -= amount;
return true;
}
/// <summary>
/// 检查是否有足够的灵能科研点
/// </summary>
public bool HasEnoughPsychicResearchPoints(float requiredAmount)
{
return IsSystemInitialized && psychicResearchPoints >= requiredAmount;
}
/// <summary>
/// 获取剩余科研点容量
/// </summary>
public float GetRemainingResearchCapacity()
{
return MAX_PSYCHIC_RESEARCH - psychicResearchPoints;
}
#endregion
#region -
/// <summary>
/// 获取当前灵能负载
/// </summary>
public float GetPsychicLoad()
{
return psychicLoad;
}
/// <summary>
/// 获取灵能负载容量
/// </summary>
public float GetPsychicLoadCapacity()
{
return psychicLoadCapacity;
}
/// <summary>
/// 设置灵能负载
/// </summary>
public bool SetPsychicLoad(float value, string reason = "")
{
if (!IsSystemInitialized || value < 0f)
return false;
PsychicLoad = value;
return true;
}
/// <summary>
/// 增加灵能负载(可以超过容量)
/// </summary>
public bool AddPsychicLoad(float amount, string reason = "")
{
if (!IsSystemInitialized || amount <= 0f)
return false;
float oldValue = psychicLoad;
PsychicLoad = psychicLoad + amount;
return true;
}
/// <summary>
/// 减少灵能负载
/// </summary>
public bool ReducePsychicLoad(float amount, string reason = "")
{
if (!IsSystemInitialized || amount <= 0f)
return false;
float oldValue = psychicLoad;
PsychicLoad = Mathf.Max(0f, psychicLoad - amount);
// 如果减少负载后退出超载状态,可能需要特殊处理
if (isOverloaded && psychicLoad <= psychicLoadCapacity)
{
ForceExitOverload();
}
return true;
}
/// <summary>
/// 检查是否有足够的灵能负载容量(不考虑超载)
/// </summary>
public bool HasEnoughPsychicLoadCapacity(float requiredAmount)
{
return IsSystemInitialized && (psychicLoadCapacity - psychicLoad) >= requiredAmount;
}
/// <summary>
/// 获取剩余负载容量(不考虑超载)
/// </summary>
public float GetRemainingLoadCapacity()
{
return psychicLoadCapacity - psychicLoad;
}
/// <summary>
/// 获取实际剩余负载容量(考虑当前负载,可以为负数表示超载)
/// </summary>
public float GetActualRemainingLoadCapacity()
{
return psychicLoadCapacity - psychicLoad;
}
#endregion
#region -
/// <summary>
/// 获取当前术法等级
/// </summary>
public float GetSpellLevel()
{
return parent.GetStatValue(ARA_StatDefOf.ARA_SwarmSpell_Level);
}
/// <summary>
/// 设置术法等级
/// </summary>
public bool SetSpellLevel(float level, string reason = "")
{
if (!IsSystemInitialized || level < 0f)
return false;
float oldLevel = spellLevel;
SpellLevel = level;
return true;
}
/// <summary>
/// 增加术法等级
/// </summary>
public bool IncreaseSpellLevel(float amount, string reason = "")
{
if (!IsSystemInitialized || amount <= 0f)
return false;
float oldLevel = spellLevel;
SpellLevel = spellLevel + amount;
return true;
}
/// <summary>
/// 减少术法等级
/// </summary>
public bool DecreaseSpellLevel(float amount, string reason = "")
{
if (!IsSystemInitialized || amount <= 0f)
return false;
float oldLevel = spellLevel;
SpellLevel = Mathf.Max(0f, spellLevel - amount);
return true;
}
#endregion
#region
/// <summary>
/// 定期更新系统
/// </summary>
private void UpdateSystemTick()
{
if (!IsSystemInitialized || !IsPawnAvailable)
return;
lastUpdateTick = Find.TickManager.TicksGame;
// 如果冷却结束且没有超载,自动减少负载
if (!isCooldownActive && !isOverloaded && psychicLoad > 0)
{
ReduceLoadAfterCooldown();
}
// 这里可以添加其他自动恢复逻辑
}
/// <summary>
/// 获取系统状态描述
/// </summary>
public string GetSystemStatus()
{
string status = $"虫群术法系统 - {parent.LabelCap}\n" +
$"术法等级: {spellLevel:F1}\n" +
$"灵能负载: {psychicLoad:F1}/{psychicLoadCapacity:F1} ({PsychicLoadPercent:P1})";
if (isOverloaded)
{
status += $" <color=red>[超载: +{OverloadPercent:P0}]</color>";
}
status += $"\n灵能科研: {psychicResearchPoints:F1}/{MAX_PSYCHIC_RESEARCH:F0} ({PsychicResearchPercent:P1})";
if (IsOnCooldown)
{
status += $"\n冷却: {CooldownSecondsRemaining:F1}秒";
}
return status;
}
/// <summary>
/// 获取调试信息
/// </summary>
public string GetDebugInfo()
{
return $"=== 虫群术法调试信息 ===\n" +
$"单位: {parent.LabelCap}\n" +
$"术法等级: {spellLevel:F1}\n" +
$"负载容量: {psychicLoadCapacity:F1} (10 * 等级)\n" +
$"当前负载: {psychicLoad:F1}\n" +
$"负载百分比: {PsychicLoadPercent:P1}\n" +
$"超载状态: {isOverloaded}\n" +
$"超载百分比: {OverloadPercent:P1}\n" +
$"冷却状态: {IsOnCooldown}\n" +
$"冷却剩余: {cooldownTicksRemaining} ticks ({CooldownSecondsRemaining:F1}秒)\n" +
$"科研点: {psychicResearchPoints:F1}/{MAX_PSYCHIC_RESEARCH:F0}\n" +
$"科研百分比: {PsychicResearchPercent:P1}\n" +
$"最后更新: {lastUpdateTick}\n" +
$"系统初始化: {systemInitialized}\n" +
$"Pawn可用: {IsPawnAvailable}";
}
#endregion
#region Gizmo
/// <summary>
/// 获取术法系统的Gizmo
/// </summary>
public override IEnumerable<Gizmo> CompGetGizmosExtra()
{
foreach (Gizmo gizmo in base.CompGetGizmosExtra())
{
yield return gizmo;
}
// 只有拥有此组件的虫群单位才显示术法Gizmo
if (IsSystemInitialized && parent.Faction == Faction.OfPlayer && parent is Pawn)
{
var spellGizmo = new Gizmo_SwarmSpellStatus(this);
if (spellGizmo.ShouldDisplay())
{
yield return spellGizmo;
}
// 调试Gizmo
if (DebugSettings.godMode)
{
yield return new Command_Action
{
defaultLabel = "[调试] 重置术法系统",
defaultDesc = "重置所有术法数值",
icon = ContentFinder<Texture2D>.Get("UI/Commands/Detonate", false),
action = () => ResetSystem()
};
yield return new Command_Action
{
defaultLabel = "[调试] 增加科研点(100)",
defaultDesc = "增加100灵能科研点",
icon = ContentFinder<Texture2D>.Get("UI/Commands/Research", false),
action = () => AddPsychicResearchPoints(100f, "调试")
};
yield return new Command_Action
{
defaultLabel = "[调试] 增加负载(5)",
defaultDesc = "增加5点灵能负载",
icon = ContentFinder<Texture2D>.Get("UI/Commands/Attack", false),
action = () => AddPsychicLoad(5f, "调试")
};
yield return new Command_Action
{
defaultLabel = "[调试] 减少负载(5)",
defaultDesc = "减少5点灵能负载",
icon = ContentFinder<Texture2D>.Get("UI/Commands/Relax", false),
action = () => ReducePsychicLoad(5f, "调试")
};
yield return new Command_Action
{
defaultLabel = "[调试] 增加术法等级(0.5)",
defaultDesc = "增加0.5级术法等级",
icon = ContentFinder<Texture2D>.Get("UI/Commands/Upgrade", false),
action = () => IncreaseSpellLevel(0.5f, "调试")
};
}
}
}
#endregion
}
/// <summary>
/// CompProperties for Comp_SwarmSpellHolder
/// </summary>
public class CompProperties_SwarmSpellHolder : CompProperties
{
// 可以在这里添加配置选项,例如:
// public float initialSpellLevel = 1f;
// public bool enableAutoRecovery = false;
public CompProperties_SwarmSpellHolder()
{
compClass = typeof(Comp_SwarmSpellHolder);
}
}
}

View File

@@ -1,358 +0,0 @@
using RimWorld;
using System.Text;
using UnityEngine;
using Verse;
using Verse.Sound;
namespace ArachnaeSwarm
{
[StaticConstructorOnStartup]
public class Gizmo_SwarmSpellStatus : Gizmo
{
private readonly Comp_SwarmSpellHolder spellHolder;
private static readonly Texture2D EmptyBarTex = SolidColorMaterials.NewSolidColorTexture(new Color(0.15f, 0.15f, 0.15f));
private static readonly Texture2D LoadBarTex = SolidColorMaterials.NewSolidColorTexture(new Color(0.46f, 0.34f, 0.35f));
private static readonly Texture2D OverLimitBarTex = SolidColorMaterials.NewSolidColorTexture(new Color(0.72f, 0.25f, 0.25f));
private static readonly Texture2D LoadAddPreviewTex = SolidColorMaterials.NewSolidColorTexture(new Color(0.78f, 0.72f, 0.66f));
private static readonly Texture2D ResearchBarTex = SolidColorMaterials.NewSolidColorTexture(new Color(0.32f, 0.47f, 0.7f));
private static readonly Texture2D ThresholdTex = SolidColorMaterials.NewSolidColorTexture(new Color(1f, 1f, 1f, 0.7f));
private static readonly Texture2D LimitedTex = ContentFinder<Texture2D>.Get("UI/Icons/EntropyLimit/Limited", false);
private static readonly Texture2D UnlimitedTex = ContentFinder<Texture2D>.Get("UI/Icons/EntropyLimit/Unlimited", false);
private const float GizmoWidth = 212f;
private const float GizmoHeight = 75f;
private const float Padding = 6f;
public Gizmo_SwarmSpellStatus(Comp_SwarmSpellHolder holder)
{
spellHolder = holder;
Order = -95f;
}
public override float GetWidth(float maxWidth)
{
return GizmoWidth;
}
public override GizmoResult GizmoOnGUI(Vector2 topLeft, float maxWidth, GizmoRenderParms parms)
{
if (spellHolder == null || !spellHolder.IsSystemInitialized)
{
return new GizmoResult(GizmoState.Clear);
}
Rect rect = new Rect(topLeft.x, topLeft.y, GetWidth(maxWidth), GizmoHeight);
Rect contentRect = rect.ContractedBy(Padding);
Widgets.DrawWindowBackground(rect);
Command_Ability hoveredAbility = MapGizmoUtility.LastMouseOverGizmo as Command_Ability;
FloatRange hoveredLoadRange = default;
bool hasHoveredLoad = TryGetHoveredAbilityLoadRange(hoveredAbility, out hoveredLoadRange);
DrawLabels(contentRect);
Rect loadBarRect = new Rect(contentRect.x + 63f, contentRect.y + 6f, 100f, 22f);
Rect researchBarRect = new Rect(contentRect.x + 63f, contentRect.y + 38f, 100f, 22f);
DrawLoadBar(loadBarRect, hasHoveredLoad ? hoveredLoadRange : default, hasHoveredLoad);
DrawResearchBar(researchBarRect);
DrawLimiterToggle(contentRect);
TooltipHandler.TipRegion(loadBarRect, new TipSignal(GenerateLoadTooltip(hasHoveredLoad ? hoveredLoadRange : default, hasHoveredLoad), 43502));
TooltipHandler.TipRegion(researchBarRect, new TipSignal(GenerateResearchTooltip(), 43501));
TooltipHandler.TipRegion(rect, new TipSignal(GenerateBasicTooltip(), 43500));
return new GizmoResult(GizmoState.Clear);
}
private void DrawLabels(Rect contentRect)
{
Text.Font = GameFont.Small;
Text.Anchor = TextAnchor.UpperLeft;
Rect loadLabelRect = new Rect(contentRect.x, contentRect.y + 6f, 62f, Text.LineHeight);
Rect researchLabelRect = new Rect(contentRect.x, contentRect.y + 38f, 62f, Text.LineHeight);
Widgets.Label(loadLabelRect, "ARA_SwarmSpell_Load_Title".Translate());
Widgets.Label(researchLabelRect, "ARA_SwarmSpell_Research_Title".Translate());
}
private void DrawLoadBar(Rect loadBarRect, FloatRange hoveredLoadRange, bool hasHoveredLoad)
{
float loadRelative = LoadToRelativeValue(spellHolder.PsychicLoad, spellHolder.PsychicLoadCapacity);
Widgets.FillableBar(loadBarRect, Mathf.Min(loadRelative, 1f), LoadBarTex, EmptyBarTex, doBorder: true);
if (loadRelative > 1f)
{
Widgets.FillableBar(loadBarRect, Mathf.Min(loadRelative - 1f, 1f), OverLimitBarTex, LoadBarTex, doBorder: true);
}
if (hasHoveredLoad && hoveredLoadRange.max > float.Epsilon)
{
DrawLoadPreview(loadBarRect, loadRelative, hoveredLoadRange.max);
}
if (loadRelative > 1f)
{
float overloadRelative = loadRelative - 1f;
DrawThreshold(loadBarRect, 0.33f, overloadRelative);
DrawThreshold(loadBarRect, 0.66f, overloadRelative);
}
string loadText;
if (spellHolder.IsOverloaded)
{
loadText = "ARA_SwarmSpell_Load_Overload".Translate(
spellHolder.PsychicLoad.ToString("F1"),
spellHolder.PsychicLoadCapacity.ToString("F1"),
spellHolder.OverloadPercent.ToString("P0"));
}
else
{
loadText = "ARA_SwarmSpell_Load".Translate(
spellHolder.PsychicLoad.ToString("F1"),
spellHolder.PsychicLoadCapacity.ToString("F1"));
}
Text.Anchor = TextAnchor.MiddleCenter;
Widgets.Label(loadBarRect, loadText);
Text.Anchor = TextAnchor.UpperLeft;
}
private void DrawLoadPreview(Rect loadBarRect, float currentRelative, float addedLoad)
{
float predictedRelative = LoadToRelativeValue(spellHolder.PsychicLoad + addedLoad, spellHolder.PsychicLoadCapacity);
Rect previewRect = loadBarRect.ContractedBy(3f);
float width = previewRect.width;
float from = currentRelative;
float to = predictedRelative;
if (from > 1f)
{
from -= 1f;
to -= 1f;
}
previewRect.xMin = previewRect.xMin + Mathf.Clamp01(from) * width;
previewRect.width = Mathf.Max(Mathf.Min(to, 1f) - Mathf.Clamp01(from), 0f) * width;
if (previewRect.width > 0f)
{
GUI.color = new Color(1f, 1f, 1f, PulsingAlpha() * 0.7f);
GenUI.DrawTextureWithMaterial(previewRect, LoadAddPreviewTex, null);
GUI.color = Color.white;
}
}
private void DrawThreshold(Rect rect, float percent, float currentOverloadRelative)
{
if (currentOverloadRelative < percent)
{
return;
}
Rect thresholdRect = new Rect(rect.x + rect.width * percent - 1f, rect.y + 2f, 2f, rect.height - 4f);
GUI.DrawTexture(thresholdRect, ThresholdTex);
}
private void DrawResearchBar(Rect researchBarRect)
{
float researchPercent = Mathf.Clamp01(spellHolder.PsychicResearchPercent);
Widgets.FillableBar(researchBarRect, researchPercent, ResearchBarTex, EmptyBarTex, doBorder: true);
Text.Anchor = TextAnchor.MiddleCenter;
Widgets.Label(researchBarRect, "ARA_SwarmSpell_Research".Translate(
spellHolder.PsychicResearchPoints.ToString("F0"),
spellHolder.MaxPsychicResearchPoints.ToString("F0")));
Text.Anchor = TextAnchor.UpperLeft;
}
private void DrawLimiterToggle(Rect contentRect)
{
float buttonSize = 24f;
Rect buttonRect = new Rect(contentRect.x + contentRect.width - buttonSize, contentRect.y + contentRect.height / 2f - buttonSize / 2f, buttonSize, buttonSize);
Texture2D buttonTexture = spellHolder.LimitPsychicLoadAmount ? LimitedTex : UnlimitedTex;
if (buttonTexture == null)
{
buttonTexture = BaseContent.BadTex;
}
if (Widgets.ButtonImage(buttonRect, buttonTexture))
{
spellHolder.LimitPsychicLoadAmount = !spellHolder.LimitPsychicLoadAmount;
if (spellHolder.LimitPsychicLoadAmount)
{
SoundDefOf.Tick_Low.PlayOneShotOnCamera();
}
else
{
SoundDefOf.Tick_High.PlayOneShotOnCamera();
}
}
TooltipHandler.TipRegion(buttonRect, "PawnTooltipPsychicEntropyLimit".Translate());
}
private bool TryGetHoveredAbilityLoadRange(Command_Ability command, out FloatRange loadRange)
{
loadRange = default;
if (command?.Ability == null)
{
return false;
}
if (command.Ability.pawn != spellHolder.parent)
{
return false;
}
float min = 0f;
float max = 0f;
bool foundAny = false;
foreach (CompAbilityEffect_PsychicLoadCost loadComp in command.Ability.CompsOfType<CompAbilityEffect_PsychicLoadCost>())
{
foundAny = true;
if (loadComp.Props.useFixedCost)
{
min += loadComp.Props.fixedLoadCost;
max += loadComp.Props.fixedLoadCost;
}
else
{
min += loadComp.Props.loadCostRange.min;
max += loadComp.Props.loadCostRange.max;
}
}
if (!foundAny)
{
return false;
}
loadRange = new FloatRange(min, max);
return true;
}
private string GenerateResearchTooltip()
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("ARA_SwarmSpell_Research_Title".Translate().Colorize(ColoredText.TipSectionTitleColor));
sb.AppendLine();
sb.AppendLine("ARA_SwarmSpell_Research_Current".Translate(
spellHolder.PsychicResearchPoints.ToString("F0"),
spellHolder.MaxPsychicResearchPoints.ToString("F0")));
sb.AppendLine();
sb.AppendLine("ARA_SwarmSpell_Research_Description".Translate().Colorize(ColoredText.TipSectionTitleColor));
sb.AppendLine("ARA_SwarmSpell_Research_DescriptionText".Translate());
return sb.ToString();
}
private string GenerateLoadTooltip(FloatRange hoveredLoadRange, bool hasHoveredLoad)
{
StringBuilder sb = new StringBuilder();
if (spellHolder.IsOverloaded)
{
sb.AppendLine("ARA_SwarmSpell_Load_Title".Translate().Colorize(Color.red));
}
else
{
sb.AppendLine("ARA_SwarmSpell_Load_Title".Translate().Colorize(ColoredText.TipSectionTitleColor));
}
sb.AppendLine();
sb.AppendLine("ARA_SwarmSpell_Load_Current".Translate(
spellHolder.PsychicLoad.ToString("F1"),
spellHolder.PsychicLoadCapacity.ToString("F1")));
if (hasHoveredLoad && hoveredLoadRange.max > float.Epsilon)
{
sb.AppendLine();
sb.AppendLine("ARA_SwarmSpell_LoadCost".Translate().Colorize(ColoredText.TipSectionTitleColor));
if (Mathf.Abs(hoveredLoadRange.max - hoveredLoadRange.min) < 0.01f)
{
sb.AppendLine("ARA_SwarmSpell_LoadCost_Fixed".Translate(hoveredLoadRange.max.ToString("F1")));
}
else
{
sb.AppendLine("ARA_SwarmSpell_LoadCost_Range".Translate(
hoveredLoadRange.min.ToString("F1"),
hoveredLoadRange.max.ToString("F1")));
}
}
if (spellHolder.IsOverloaded)
{
sb.AppendLine();
sb.AppendLine("ARA_SwarmSpell_Load_OverloadWarning".Translate().Colorize(Color.red));
}
if (spellHolder.IsOnCooldown)
{
sb.AppendLine();
sb.AppendLine("ARA_SwarmSpell_Cooldown_Status".Translate().Colorize(ColoredText.TipSectionTitleColor));
sb.AppendLine("ARA_SwarmSpell_Cooldown_Remaining".Translate(spellHolder.CooldownSecondsRemaining.ToString("F1")));
}
sb.AppendLine();
string limiterState = spellHolder.LimitPsychicLoadAmount ? "On".Translate() : "Off".Translate();
sb.AppendLine($"负载限制器: {limiterState}".Colorize(ColoredText.TipSectionTitleColor));
sb.AppendLine("PawnTooltipPsychicEntropyLimit".Translate());
return sb.ToString();
}
private string GenerateBasicTooltip()
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("ARA_SwarmSpell_Gizmo_Title".Translate().Colorize(ColoredText.TipSectionTitleColor));
sb.AppendLine();
sb.AppendLine("ARA_SwarmSpell_Gizmo_Desc".Translate());
sb.AppendLine();
sb.AppendLine("ARA_SwarmSpell_Gizmo_Hint".Translate());
return sb.ToString();
}
private static float LoadToRelativeValue(float load, float capacity)
{
if (load <= float.Epsilon || capacity <= float.Epsilon)
{
return 0f;
}
if (load <= capacity)
{
return load / capacity;
}
return 1f + (load - capacity) / capacity;
}
private static float PulsingAlpha()
{
float cycle = Mathf.Repeat(Time.time, 0.85f);
if (cycle < 0.1f)
{
return cycle / 0.1f;
}
if (cycle >= 0.25f)
{
return 1f - (cycle - 0.25f) / 0.6f;
}
return 1f;
}
public bool ShouldDisplay()
{
return spellHolder != null &&
spellHolder.IsSystemInitialized &&
spellHolder.parent is Pawn &&
spellHolder.parent.Faction == Faction.OfPlayer;
}
}
}

View File

@@ -1,439 +0,0 @@
using RimWorld;
using System.Collections.Generic;
using System.Linq;
using Verse;
namespace ArachnaeSwarm
{
/// <summary>
/// 虫群术法系统工具类
/// </summary>
public static class SwarmSpellUtility
{
#region
public const float MAX_PSYCHIC_RESEARCH = 3000f;
public const float BASE_SPELL_LEVEL = 1f;
public const float PSYCHIC_LOAD_PER_LEVEL = 10f;
#endregion
#region
/// <summary>
/// 获取Pawn的术法持有组件
/// </summary>
public static Comp_SwarmSpellHolder GetSpellHolder(Pawn pawn)
{
if (pawn == null || pawn.Destroyed)
return null;
return pawn.TryGetComp<Comp_SwarmSpellHolder>();
}
/// <summary>
/// 检查Pawn是否有术法组件
/// </summary>
public static bool HasSpellHolder(Pawn pawn)
{
return GetSpellHolder(pawn) != null;
}
/// <summary>
/// 获取所有拥有术法组件的虫群单位
/// </summary>
public static IEnumerable<Pawn> GetAllSpellHolders()
{
if (Current.Game == null)
yield break;
foreach (Map map in Current.Game.Maps)
{
foreach (Pawn pawn in map.mapPawns.AllPawns)
{
if (HasSpellHolder(pawn))
{
yield return pawn;
}
}
}
}
/// <summary>
/// 获取玩家派系中拥有术法组件的虫群单位数量
/// </summary>
public static int CountPlayerSpellHolders()
{
return GetAllSpellHolders().Count(p => p.Faction == Faction.OfPlayer);
}
#endregion
#region
/// <summary>
/// 获取灵能科研点
/// </summary>
public static float GetPsychicResearchPoints(Pawn pawn)
{
var holder = GetSpellHolder(pawn);
return holder?.GetPsychicResearchPoints() ?? 0f;
}
/// <summary>
/// 设置灵能科研点
/// </summary>
public static bool SetPsychicResearchPoints(Pawn pawn, float value, string reason = "")
{
var holder = GetSpellHolder(pawn);
return holder?.SetPsychicResearchPoints(value, reason) ?? false;
}
/// <summary>
/// 增加灵能科研点
/// </summary>
public static bool AddPsychicResearchPoints(Pawn pawn, float amount, string reason = "")
{
var holder = GetSpellHolder(pawn);
return holder?.AddPsychicResearchPoints(amount, reason) ?? false;
}
/// <summary>
/// 消耗灵能科研点
/// </summary>
public static bool ConsumePsychicResearchPoints(Pawn pawn, float amount, string reason = "")
{
var holder = GetSpellHolder(pawn);
return holder?.ConsumePsychicResearchPoints(amount, reason) ?? false;
}
/// <summary>
/// 检查是否有足够的灵能科研点
/// </summary>
public static bool HasEnoughPsychicResearchPoints(Pawn pawn, float requiredAmount)
{
var holder = GetSpellHolder(pawn);
return holder?.HasEnoughPsychicResearchPoints(requiredAmount) ?? false;
}
/// <summary>
/// 批量增加灵能科研点(给多个单位)
/// </summary>
public static void AddPsychicResearchPointsToAll(IEnumerable<Pawn> pawns, float amount, string reason = "")
{
int successCount = 0;
foreach (var pawn in pawns)
{
if (AddPsychicResearchPoints(pawn, amount, reason))
{
successCount++;
}
}
}
#endregion
#region
/// <summary>
/// 获取灵能负载
/// </summary>
public static float GetPsychicLoad(Pawn pawn)
{
var holder = GetSpellHolder(pawn);
return holder?.GetPsychicLoad() ?? 0f;
}
/// <summary>
/// 获取灵能负载容量
/// </summary>
public static float GetPsychicLoadCapacity(Pawn pawn)
{
var holder = GetSpellHolder(pawn);
return holder?.GetPsychicLoadCapacity() ?? 0f;
}
/// <summary>
/// 设置灵能负载
/// </summary>
public static bool SetPsychicLoad(Pawn pawn, float value, string reason = "")
{
var holder = GetSpellHolder(pawn);
return holder?.SetPsychicLoad(value, reason) ?? false;
}
/// <summary>
/// 增加灵能负载
/// </summary>
public static bool AddPsychicLoad(Pawn pawn, float amount, string reason = "")
{
var holder = GetSpellHolder(pawn);
return holder?.AddPsychicLoad(amount, reason) ?? false;
}
/// <summary>
/// 减少灵能负载
/// </summary>
public static bool ReducePsychicLoad(Pawn pawn, float amount, string reason = "")
{
var holder = GetSpellHolder(pawn);
return holder?.ReducePsychicLoad(amount, reason) ?? false;
}
/// <summary>
/// 检查是否有足够的灵能负载容量
/// </summary>
public static bool HasEnoughPsychicLoadCapacity(Pawn pawn, float requiredAmount)
{
var holder = GetSpellHolder(pawn);
return holder?.HasEnoughPsychicLoadCapacity(requiredAmount) ?? false;
}
/// <summary>
/// 获取负载百分比
/// </summary>
public static float GetPsychicLoadPercent(Pawn pawn)
{
var holder = GetSpellHolder(pawn);
return holder?.PsychicLoadPercent ?? 0f;
}
#endregion
#region
/// <summary>
/// 获取术法等级
/// </summary>
public static float GetSpellLevel(Pawn pawn)
{
var holder = GetSpellHolder(pawn);
return holder?.GetSpellLevel() ?? 0f;
}
/// <summary>
/// 设置术法等级
/// </summary>
public static bool SetSpellLevel(Pawn pawn, float level, string reason = "")
{
var holder = GetSpellHolder(pawn);
return holder?.SetSpellLevel(level, reason) ?? false;
}
/// <summary>
/// 增加术法等级
/// </summary>
public static bool IncreaseSpellLevel(Pawn pawn, float amount, string reason = "")
{
var holder = GetSpellHolder(pawn);
return holder?.IncreaseSpellLevel(amount, reason) ?? false;
}
/// <summary>
/// 减少术法等级
/// </summary>
public static bool DecreaseSpellLevel(Pawn pawn, float amount, string reason = "")
{
var holder = GetSpellHolder(pawn);
return holder?.DecreaseSpellLevel(amount, reason) ?? false;
}
/// <summary>
/// 批量增加术法等级(给多个单位)
/// </summary>
public static void IncreaseSpellLevelForAll(IEnumerable<Pawn> pawns, float amount, string reason = "")
{
int successCount = 0;
foreach (var pawn in pawns)
{
if (IncreaseSpellLevel(pawn, amount, reason))
{
successCount++;
}
}
}
#endregion
#region
/// <summary>
/// 检查术法系统是否可用
/// </summary>
public static bool IsSpellSystemAvailable(Pawn pawn)
{
var holder = GetSpellHolder(pawn);
return holder != null && holder.IsSystemInitialized && holder.IsPawnAvailable;
}
/// <summary>
/// 检查是否过载负载超过80%
/// </summary>
public static bool IsOverloaded(Pawn pawn)
{
float loadPercent = GetPsychicLoadPercent(pawn);
return loadPercent > 0.8f;
}
/// <summary>
/// 检查是否临界过载负载超过95%
/// </summary>
public static bool IsCriticalOverload(Pawn pawn)
{
float loadPercent = GetPsychicLoadPercent(pawn);
return loadPercent > 0.95f;
}
/// <summary>
/// 获取系统状态描述
/// </summary>
public static string GetSystemStatus(Pawn pawn)
{
var holder = GetSpellHolder(pawn);
return holder?.GetSystemStatus() ?? "无术法系统";
}
/// <summary>
/// 获取总科研点(所有虫群单位)
/// </summary>
public static float GetTotalPsychicResearchPoints()
{
float total = 0f;
foreach (var pawn in GetAllSpellHolders())
{
total += GetPsychicResearchPoints(pawn);
}
return total;
}
/// <summary>
/// 获取平均术法等级(所有虫群单位)
/// </summary>
public static float GetAverageSpellLevel()
{
var holders = GetAllSpellHolders().ToList();
if (holders.Count == 0)
return 0f;
float total = 0f;
foreach (var pawn in holders)
{
total += GetSpellLevel(pawn);
}
return total / holders.Count;
}
#endregion
#region
/// <summary>
/// 为所有虫群单位增加科研点
/// </summary>
public static void AddPsychicResearchToAllSwarmPawns(float amount, string reason = "")
{
// 获取所有虫群派系的单位
var swarmPawns = GetAllSwarmPawns();
AddPsychicResearchPointsToAll(swarmPawns, amount, reason);
}
/// <summary>
/// 为所有虫群单位增加术法等级
/// </summary>
public static void IncreaseSpellLevelForAllSwarmPawns(float amount, string reason = "")
{
var swarmPawns = GetAllSwarmPawns();
IncreaseSpellLevelForAll(swarmPawns, amount, reason);
}
/// <summary>
/// 获取所有虫群派系的单位
/// </summary>
public static IEnumerable<Pawn> GetAllSwarmPawns()
{
// 假设虫群派系的defName是"ArachnaeSwarm_Faction"
Faction swarmFaction = Find.FactionManager.FirstFactionOfDef(FactionDef.Named("ArachnaeSwarm_Faction"));
if (swarmFaction == null)
yield break;
foreach (Map map in Current.Game.Maps)
{
foreach (Pawn pawn in map.mapPawns.AllPawns)
{
if (pawn.Faction == swarmFaction && HasSpellHolder(pawn))
{
yield return pawn;
}
}
}
}
#endregion
#region
/// <summary>
/// 初始化术法系统(如果尚未初始化)
/// </summary>
public static bool EnsureSystemInitialized(Pawn pawn)
{
var holder = GetSpellHolder(pawn);
if (holder == null)
return false;
// 组件会在PostSpawnSetup中自动初始化
return holder.IsSystemInitialized;
}
/// <summary>
/// 重置术法系统
/// </summary>
public static bool ResetSpellSystem(Pawn pawn)
{
var holder = GetSpellHolder(pawn);
if (holder == null)
return false;
holder.ResetSystem();
return true;
}
/// <summary>
/// 获取调试信息
/// </summary>
public static string GetDebugInfo(Pawn pawn)
{
var holder = GetSpellHolder(pawn);
return holder?.GetDebugInfo() ?? "无术法组件";
}
/// <summary>
/// 显示术法系统状态消息
/// </summary>
public static void ShowStatusMessage(Pawn pawn)
{
if (!IsSpellSystemAvailable(pawn))
{
Messages.Message("ARA_SwarmSpell_SystemNotAvailable".Translate(pawn.LabelShortCap), MessageTypeDefOf.NeutralEvent);
return;
}
string message = GetSystemStatus(pawn);
Messages.Message(message, pawn, MessageTypeDefOf.NeutralEvent);
}
#endregion
#region
/// <summary>
/// 检查Pawn是否是术法持有者
/// </summary>
public static bool IsSpellHolder(Pawn pawn)
{
return HasSpellHolder(pawn);
}
/// <summary>
/// 获取灵能科研点百分比
/// </summary>
public static float GetPsychicResearchPercent(Pawn pawn)
{
var holder = GetSpellHolder(pawn);
return holder?.PsychicResearchPercent ?? 0f;
}
/// <summary>
/// 获取灵能负载容量百分比
/// </summary>
public static float GetPsychicLoadCapacityPercent(Pawn pawn)
{
var holder = GetSpellHolder(pawn);
return holder?.PsychicLoadPercent ?? 0f;
}
#endregion
}
}