1
This commit is contained in:
Binary file not shown.
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Binary file not shown.
@@ -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"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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" />
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user