Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c049cf4c5e | ||
|
|
00d43cfba3 | ||
|
|
3df5314d4e | ||
|
|
0982682cc9 | ||
|
|
760ddee0cc | ||
|
|
0207f5abd1 | ||
|
|
62b0ad265e | ||
|
|
b45df3cbfc | ||
|
|
fda704ee2e | ||
|
|
5643623725 | ||
|
|
bafda90d2e | ||
|
|
aab5e225c1 | ||
|
|
4423a16a28 | ||
|
|
b495a2025d |
Binary file not shown.
@@ -31,7 +31,6 @@
|
||||
</li>
|
||||
</comps>
|
||||
</AbilityDef>
|
||||
|
||||
|
||||
<AbilityDef>
|
||||
<defName>ARA_EggSpew</defName>
|
||||
@@ -73,8 +72,8 @@
|
||||
|
||||
<AbilityDef>
|
||||
<defName>ARA_EggSpewBioforgeIncubator</defName>
|
||||
<label>生育培育卵</label>
|
||||
<description>工艺卵</description>
|
||||
<label>生育孵化茧</label>
|
||||
<description>孵化茧</description>
|
||||
<iconPath>UI/Commands/EggSpew</iconPath>
|
||||
<cooldownTicksRange>5000</cooldownTicksRange>
|
||||
<aiCanUse>true</aiCanUse>
|
||||
@@ -99,7 +98,7 @@
|
||||
</li>
|
||||
<li Class="ArachnaeSwarm.CompProperties_AbilityNeedCost">
|
||||
<needDef>Food</needDef>
|
||||
<needCost>0</needCost>
|
||||
<needCost>1</needCost>
|
||||
<failMessage>营养值不足,需要进食</failMessage>
|
||||
</li>
|
||||
</comps>
|
||||
@@ -146,6 +145,49 @@
|
||||
</li>
|
||||
</comps>
|
||||
</AbilityDef>
|
||||
|
||||
<AbilityDef>
|
||||
<defName>ARA_AcidSprayBurst_Myrmecocystus</defName>
|
||||
<label>蜜罐种酸液轰炸</label>
|
||||
<description>阿拉克涅蜜罐种向目标地点喷射大量腐蚀性酸液,虽然不如女皇种所喷射的酸雨那样强劲,但是虫酸的伤害依然是实打实的。</description>
|
||||
<iconPath>UI/Abilities/AcidSpray</iconPath>
|
||||
<cooldownTicksRange>12000</cooldownTicksRange> <!-- 2 hours -->
|
||||
<aiCanUse>true</aiCanUse>
|
||||
<displayOrder>300</displayOrder>
|
||||
<displayGizmoWhileUndrafted>true</displayGizmoWhileUndrafted>
|
||||
<disableGizmoWhileUndrafted>false</disableGizmoWhileUndrafted>
|
||||
<warmupStartSound>AcidSpray_Warmup</warmupStartSound>
|
||||
<verbProperties>
|
||||
<verbClass>Verb_CastAbility</verbClass>
|
||||
<range>15.9</range>
|
||||
<warmupTime>3</warmupTime>
|
||||
<soundCast>AcidSpray_Resolve</soundCast>
|
||||
<targetParams>
|
||||
<canTargetLocations>true</canTargetLocations>
|
||||
</targetParams>
|
||||
</verbProperties>
|
||||
<comps>
|
||||
<li Class="ArachnaeSwarm.CompProperties_AbilitySprayLiquidMulti">
|
||||
<!-- CompProperties_AbilitySprayLiquid 的属性 -->
|
||||
<projectileDef>ARA_Proj_StrongSludgeSpray</projectileDef>
|
||||
<numCellsToHit>6</numCellsToHit>
|
||||
<sprayEffecter>AcidSpray_Directional</sprayEffecter>
|
||||
|
||||
<!-- CompProperties_AbilitySprayLiquidMulti 新增的属性 -->
|
||||
<shotCount>16</shotCount> <!-- 总共发射5次 -->
|
||||
<ticksBetweenShots>3</ticksBetweenShots> <!-- 每次发射间隔12 Ticks (0.2秒) -->
|
||||
</li>
|
||||
<li Class="ArachnaeSwarm.CompProperties_AbilityNeedCost">
|
||||
<needDef>Food</needDef>
|
||||
<needCost>0.5</needCost>
|
||||
<failMessage>营养值不足,需要进食</failMessage>
|
||||
</li>
|
||||
<li Class="ArachnaeSwarm.CompProperties_AbilityBodyPartCheck">
|
||||
<requiredPart>ARA_Acid_sac</requiredPart>
|
||||
<failMessage>酸囊受损或缺失,无法喷射酸液</failMessage>
|
||||
</li>
|
||||
</comps>
|
||||
</AbilityDef>
|
||||
|
||||
<ThingDef>
|
||||
<defName>ARA_Proj_StrongSludgeSpray</defName>
|
||||
@@ -212,11 +254,11 @@
|
||||
</ThingDef>
|
||||
|
||||
<AbilityDef>
|
||||
<defName>ARA_AcidSprayBurst_Myrmecocystus</defName>
|
||||
<label>蜜罐种酸液轰炸</label>
|
||||
<description>阿拉克涅蜜罐种向目标地点喷射大量腐蚀性酸液,虽然不如女皇种所喷射的酸雨那样强劲,但是虫酸的伤害依然是实打实的。</description>
|
||||
<iconPath>UI/Abilities/AcidSpray</iconPath>
|
||||
<cooldownTicksRange>12000</cooldownTicksRange> <!-- 2 hours -->
|
||||
<defName>ARA_BaseRace_Acid_Launcher</defName>
|
||||
<label>酸嗜种投射</label>
|
||||
<description></description>
|
||||
<iconPath>UI/Commands/EggSpew</iconPath>
|
||||
<cooldownTicksRange>5000</cooldownTicksRange>
|
||||
<aiCanUse>true</aiCanUse>
|
||||
<displayOrder>300</displayOrder>
|
||||
<displayGizmoWhileUndrafted>true</displayGizmoWhileUndrafted>
|
||||
@@ -224,33 +266,45 @@
|
||||
<warmupStartSound>AcidSpray_Warmup</warmupStartSound>
|
||||
<verbProperties>
|
||||
<verbClass>Verb_CastAbility</verbClass>
|
||||
<range>15.9</range>
|
||||
<warmupTime>3</warmupTime>
|
||||
<range>24</range>
|
||||
<warmupTime>1</warmupTime>
|
||||
<soundCast>AcidSpray_Resolve</soundCast>
|
||||
<violent>false</violent>
|
||||
<targetable>false</targetable>
|
||||
<targetParams>
|
||||
<canTargetLocations>true</canTargetLocations>
|
||||
<canTargetSelf>false</canTargetSelf>
|
||||
</targetParams>
|
||||
</verbProperties>
|
||||
<comps>
|
||||
<li Class="ArachnaeSwarm.CompProperties_AbilitySprayLiquidMulti">
|
||||
<!-- CompProperties_AbilitySprayLiquid 的属性 -->
|
||||
<projectileDef>ARA_Proj_StrongSludgeSpray</projectileDef>
|
||||
<numCellsToHit>6</numCellsToHit>
|
||||
<sprayEffecter>AcidSpray_Directional</sprayEffecter>
|
||||
|
||||
<!-- CompProperties_AbilitySprayLiquidMulti 新增的属性 -->
|
||||
<shotCount>16</shotCount> <!-- 总共发射5次 -->
|
||||
<ticksBetweenShots>3</ticksBetweenShots> <!-- 每次发射间隔12 Ticks (0.2秒) -->
|
||||
<li Class="CompProperties_AbilityLaunchProjectile">
|
||||
<projectileDef>ArachnaeBase_Race_Acid_Proj</projectileDef>
|
||||
</li>
|
||||
<li Class="ArachnaeSwarm.CompProperties_AbilityNeedCost">
|
||||
<li Class="ArachnaeSwarm.CompProperties_AbilityNeedCost">
|
||||
<needDef>Food</needDef>
|
||||
<needCost>0.5</needCost>
|
||||
<failMessage>营养值不足,需要进食</failMessage>
|
||||
</li>
|
||||
<li Class="ArachnaeSwarm.CompProperties_AbilityBodyPartCheck">
|
||||
<requiredPart>ARA_Acid_sac</requiredPart>
|
||||
<failMessage>酸囊受损或缺失,无法喷射酸液</failMessage>
|
||||
</li>
|
||||
<!-- <li Class="ArachnaeSwarm.CompProperties_AbilityBodyPartCheck">
|
||||
<requiredPart>ARA_Ovary</requiredPart>
|
||||
<failMessage>卵巢受损或缺失,无法生育</failMessage>
|
||||
</li> -->
|
||||
</comps>
|
||||
</AbilityDef>
|
||||
<ThingDef ParentName="BaseGrenadeProjectile">
|
||||
<defName>ArachnaeBase_Race_Acid_Proj</defName>
|
||||
<label>阿拉克涅酸嗜种</label>
|
||||
<thingClass>Projectile_SpawnsPawnZeroAge</thingClass>
|
||||
<uiIconPath>Wula/Things/WULA_Attack_Cat/WULA_Cat_Thin_south</uiIconPath>
|
||||
<graphicData>
|
||||
<texPath>Wula/Things/WULA_Attack_Cat/WULA_Cat_Thin_south</texPath>
|
||||
<graphicClass>Graphic_Single</graphicClass>
|
||||
</graphicData>
|
||||
<projectile>
|
||||
<speed>41</speed>
|
||||
<spawnsPawnKind>ArachnaeBase_Race_Acid</spawnsPawnKind>
|
||||
<tryAdjacentFreeSpaces>true</tryAdjacentFreeSpaces>
|
||||
<damageDef>ARA_AcidBurn</damageDef>
|
||||
<damageAmountBase>10</damageAmountBase>
|
||||
</projectile>
|
||||
</ThingDef>
|
||||
</Defs>
|
||||
27
1.6/1.6/Defs/AbilityDefs/ARA_Possession_Defs.xml
Normal file
27
1.6/1.6/Defs/AbilityDefs/ARA_Possession_Defs.xml
Normal file
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Defs>
|
||||
|
||||
<!-- ==================== Ability Def ==================== -->
|
||||
<AbilityDef>
|
||||
<defName>ARA_Ability_Possess</defName>
|
||||
<label>阿拉克涅寄生</label>
|
||||
<description>将你的意识注入另一个生物的身体,完全占据它。</description>
|
||||
<iconPath>UI/Commands/EggSpew</iconPath>
|
||||
<cooldownTicksRange>600</cooldownTicksRange>
|
||||
<verbProperties>
|
||||
<verbClass>Verb_CastAbility</verbClass>
|
||||
<warmupTime>1.5</warmupTime>
|
||||
<range>5.9</range>
|
||||
<targetParams>
|
||||
<canTargetPawns>true</canTargetPawns>
|
||||
<canTargetBuildings>false</canTargetBuildings>
|
||||
<canTargetSelf>false</canTargetSelf>
|
||||
<canTargetLocations>false</canTargetLocations>
|
||||
</targetParams>
|
||||
</verbProperties>
|
||||
<comps>
|
||||
<li Class="ArachnaeSwarm.CompProperties_AbilityPossess"/>
|
||||
</comps>
|
||||
</AbilityDef>
|
||||
|
||||
</Defs>
|
||||
@@ -85,4 +85,27 @@
|
||||
<li>ArachnaeNode_spawnCategories_ShieldHead</li>
|
||||
</spawnCategories>
|
||||
</AlienRace.AlienBackstoryDef>
|
||||
<AlienRace.AlienBackstoryDef ParentName="ARA_BaseBackStory">
|
||||
<defName>Arachnae_Node_BS_Adult_WeaponSmith</defName>
|
||||
<title>阿拉克涅工艺种</title>
|
||||
<titleShort>工艺种</titleShort>
|
||||
<description>[PAWN_nameDef]是一只阿拉克涅工艺种督虫。能够产出专门用来孵化阿拉克涅武器种的孵化茧,负责阿拉克涅虫巢的基础维护。\n\n[PAWN_nameDef]在战斗中并不是一个值得正视的对手,她没有可以接入武装器官的副肢,脆弱臃肿的特性也决定了她几乎无法躲开任何攻击。</description>
|
||||
<slot>Adulthood</slot>
|
||||
|
||||
<spawnCategories>
|
||||
<li>ArachnaeNode_spawnCategories_WeaponSmith</li>
|
||||
</spawnCategories>
|
||||
</AlienRace.AlienBackstoryDef>
|
||||
|
||||
<AlienRace.AlienBackstoryDef ParentName="ARA_BaseBackStory">
|
||||
<defName>Arachnae_Node_BS_Adult_Facehugger</defName>
|
||||
<title>阿拉克涅原虫种</title>
|
||||
<titleShort>原虫种</titleShort>
|
||||
<description>[PAWN_nameDef]是一只阿拉克涅原虫种督虫。[PAWN_nameDef]通过独特的神经链接管伸入受害者身体来接管受害者的身体。不同于普通阿拉克涅虫族,阿拉克涅原虫种拥有自我意识,不需要女皇种的监管。</description>
|
||||
<slot>Adulthood</slot>
|
||||
|
||||
<spawnCategories>
|
||||
<li>ArachnaeNode_spawnCategories_Facehugger</li>
|
||||
</spawnCategories>
|
||||
</AlienRace.AlienBackstoryDef>
|
||||
</Defs>
|
||||
@@ -14,4 +14,16 @@
|
||||
<hediff>AcidBurn</hediff>
|
||||
<scaleDamageToBuildingsBasedOnFlammability>false</scaleDamageToBuildingsBasedOnFlammability>
|
||||
</DamageDef>
|
||||
<DamageDef ParentName="CutBase">
|
||||
<defName>ARA_AcidCut</defName>
|
||||
<label>酸性撕咬</label>
|
||||
<workerClass>DamageWorker_AddInjury</workerClass>
|
||||
<deathMessage>{0} 被携带酸液的巨颚咬死了。</deathMessage>
|
||||
<additionalHediffs>
|
||||
<li>
|
||||
<hediff>ARA_AcidCoverd</hediff>
|
||||
<severityPerDamageDealt>0.01</severityPerDamageDealt>
|
||||
</li>
|
||||
</additionalHediffs>
|
||||
</DamageDef>
|
||||
</Defs>
|
||||
62
1.6/1.6/Defs/HediffDefs/ARA_Hediffs_Possession.xml
Normal file
62
1.6/1.6/Defs/HediffDefs/ARA_Hediffs_Possession.xml
Normal file
@@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Defs>
|
||||
|
||||
<HediffDef>
|
||||
<defName>ARA_Possession</defName>
|
||||
<label>阿拉克涅原虫</label>
|
||||
<description>这个生物的身体被阿拉克涅原虫所夺取了,这具身体原本的主人已经没有可能再回来了。</description>
|
||||
<hediffClass>ArachnaeSwarm.Hediff_Possession</hediffClass>
|
||||
<defaultLabelColor>(0.6, 0.4, 0.8)</defaultLabelColor>
|
||||
<isBad>false</isBad>
|
||||
<scenarioCanAdd>false</scenarioCanAdd>
|
||||
<maxSeverity>1.0</maxSeverity>
|
||||
<renderNodeProperties>
|
||||
<li>
|
||||
<nodeClass>PawnRenderNode_AttachmentHead</nodeClass>
|
||||
<workerClass>PawnRenderNodeWorker_FlipWhenCrawling</workerClass>
|
||||
<texPath>ArachnaeSwarm/Things/ARA_HiveNode/Heads/Average_Normal</texPath>
|
||||
<colorType>Skin</colorType>
|
||||
<parentTagDef>Head</parentTagDef>
|
||||
<useSkinShader>true</useSkinShader>
|
||||
<useRottenColor>true</useRottenColor>
|
||||
<rotDrawMode>Fresh, Rotting</rotDrawMode>
|
||||
<drawData>
|
||||
<defaultData>
|
||||
<layer>70</layer>
|
||||
</defaultData>
|
||||
</drawData>
|
||||
</li>
|
||||
<li>
|
||||
<nodeClass>PawnRenderNode_AttachmentHead</nodeClass>
|
||||
<workerClass>PawnRenderNodeWorker_FlipWhenCrawling</workerClass>
|
||||
<texPath>ArachnaeSwarm/Things/ARA_HiveNode/Addons/ArachnaeNode_Race_Addons_Base</texPath>
|
||||
<colorType>Skin</colorType>
|
||||
<parentTagDef>Head</parentTagDef>
|
||||
<useSkinShader>true</useSkinShader>
|
||||
<useRottenColor>true</useRottenColor>
|
||||
<rotDrawMode>Fresh, Rotting</rotDrawMode>
|
||||
<drawData>
|
||||
<defaultData>
|
||||
<layer>70</layer>
|
||||
</defaultData>
|
||||
</drawData>
|
||||
</li>
|
||||
</renderNodeProperties>
|
||||
<stages>
|
||||
<li>
|
||||
<minSeverity>0</minSeverity>
|
||||
<disablesNeeds>
|
||||
<li>Mood</li>
|
||||
<li>Joy</li>
|
||||
<li>Beauty</li>
|
||||
<li>Comfort</li>
|
||||
<li>Outdoors</li>
|
||||
<li>Indoors</li>
|
||||
<li>DrugDesire</li>
|
||||
<li>RoomSize</li>
|
||||
</disablesNeeds>
|
||||
</li>
|
||||
</stages>
|
||||
</HediffDef>
|
||||
|
||||
</Defs>
|
||||
@@ -40,6 +40,12 @@
|
||||
</requiredWorkTags>
|
||||
<startingHediffs>
|
||||
</startingHediffs>
|
||||
<moveSpeedFactorByTerrainTag>
|
||||
<li>
|
||||
<key>ARA_Creep</key>
|
||||
<value>8.0</value>
|
||||
</li>
|
||||
</moveSpeedFactorByTerrainTag>
|
||||
</PawnKindDef>
|
||||
<PawnKindDef Name="ArachnaeQueen_Colonist" ParentName="ArachnaeQueenBasePawnKind">
|
||||
<defName>ARA_ArachnaeQueen</defName>
|
||||
@@ -95,6 +101,12 @@
|
||||
</requiredWorkTags>
|
||||
<startingHediffs>
|
||||
</startingHediffs>
|
||||
<moveSpeedFactorByTerrainTag>
|
||||
<li>
|
||||
<key>ARA_Creep</key>
|
||||
<value>8.0</value>
|
||||
</li>
|
||||
</moveSpeedFactorByTerrainTag>
|
||||
</PawnKindDef>
|
||||
<PawnKindDef ParentName="ArachnaeNodeABasePawnKind">
|
||||
<defName>ArachnaeNode_Race_Myrmecocystus</defName>
|
||||
@@ -111,7 +123,7 @@
|
||||
</li>
|
||||
</backstoryFiltersOverride>
|
||||
<abilities>
|
||||
<li>ARA_AcidSprayBurst_Myrmecocystus</li>
|
||||
<li>ARA_AcidSprayBurst_Myrmecocystus</li>
|
||||
</abilities>
|
||||
<apparelTags>
|
||||
</apparelTags>
|
||||
@@ -135,83 +147,128 @@
|
||||
</apparelTags>
|
||||
<apparelMoney>0</apparelMoney>
|
||||
</PawnKindDef>
|
||||
|
||||
<PawnKindDef ParentName="InsectKindBase">
|
||||
<defName>ArachnaeBase_Race_Slavey</defName>
|
||||
<label>阿拉克涅苦役种</label>
|
||||
<race>ArachnaeBase_Race_Slavey</race>
|
||||
<combatPower>75</combatPower>
|
||||
<ecoSystemWeight>0.35</ecoSystemWeight>
|
||||
<forceDeathOnDowned>true</forceDeathOnDowned>
|
||||
<forceNoDeathNotification>true</forceNoDeathNotification>
|
||||
<lifeStages>
|
||||
<li>
|
||||
<bodyGraphicData>
|
||||
<texPath>Things/Pawn/Animal/Spelopede/Spelopede</texPath>
|
||||
<drawSize>1</drawSize>
|
||||
<color>(156,148,125)</color>
|
||||
<shadowData>
|
||||
<volume>(0.4, 0.5, 0.37)</volume>
|
||||
<offset>(0,0,-0.15)</offset>
|
||||
</shadowData>
|
||||
</bodyGraphicData>
|
||||
<dessicatedBodyGraphicData>
|
||||
<texPath>Things/Pawn/Animal/Spelopede/Dessicated_Spelopede</texPath>
|
||||
<drawSize>1</drawSize>
|
||||
</dessicatedBodyGraphicData>
|
||||
</li>
|
||||
</lifeStages>
|
||||
</PawnKindDef>
|
||||
<PawnKindDef ParentName="InsectKindBase">
|
||||
<defName>ArachnaeBase_Race_Scavenger</defName>
|
||||
<label>阿拉克涅食腐种</label>
|
||||
<race>ArachnaeBase_Race_Scavenger</race>
|
||||
<combatPower>75</combatPower>
|
||||
<ecoSystemWeight>0.35</ecoSystemWeight>
|
||||
<forceDeathOnDowned>true</forceDeathOnDowned>
|
||||
<forceNoDeathNotification>true</forceNoDeathNotification>
|
||||
<lifeStages>
|
||||
<li>
|
||||
<bodyGraphicData>
|
||||
<texPath>Things/Pawn/Animal/Spelopede/Spelopede</texPath>
|
||||
<drawSize>1</drawSize>
|
||||
<color>(156,148,125)</color>
|
||||
<shadowData>
|
||||
<volume>(0.4, 0.5, 0.37)</volume>
|
||||
<offset>(0,0,-0.15)</offset>
|
||||
</shadowData>
|
||||
</bodyGraphicData>
|
||||
<dessicatedBodyGraphicData>
|
||||
<texPath>Things/Pawn/Animal/Spelopede/Dessicated_Spelopede</texPath>
|
||||
<drawSize>1</drawSize>
|
||||
</dessicatedBodyGraphicData>
|
||||
</li>
|
||||
</lifeStages>
|
||||
</PawnKindDef>
|
||||
<PawnKindDef ParentName="InsectKindBase">
|
||||
<defName>ArachnaeBase_Race_HardJaw</defName>
|
||||
<label>阿拉克涅坚颚种</label>
|
||||
<race>ArachnaeBase_Race_HardJaw</race>
|
||||
<combatPower>75</combatPower>
|
||||
<ecoSystemWeight>0.35</ecoSystemWeight>
|
||||
<forceDeathOnDowned>true</forceDeathOnDowned>
|
||||
<forceNoDeathNotification>true</forceNoDeathNotification>
|
||||
<lifeStages>
|
||||
<li>
|
||||
<bodyGraphicData>
|
||||
<texPath>Things/Pawn/Animal/Spelopede/Spelopede</texPath>
|
||||
<drawSize>1</drawSize>
|
||||
<color>(156,148,125)</color>
|
||||
<shadowData>
|
||||
<volume>(0.4, 0.5, 0.37)</volume>
|
||||
<offset>(0,0,-0.15)</offset>
|
||||
</shadowData>
|
||||
</bodyGraphicData>
|
||||
<dessicatedBodyGraphicData>
|
||||
<texPath>Things/Pawn/Animal/Spelopede/Dessicated_Spelopede</texPath>
|
||||
<drawSize>1</drawSize>
|
||||
</dessicatedBodyGraphicData>
|
||||
</li>
|
||||
</lifeStages>
|
||||
</PawnKindDef>
|
||||
<PawnKindDef ParentName="ArachnaeNodeABasePawnKind">
|
||||
<defName>ArachnaeNode_Race_WeaponSmith</defName>
|
||||
<label>阿拉克涅工艺种</label>
|
||||
<race>ArachnaeNode_Race_WeaponSmith</race>
|
||||
<defaultFactionType>PlayerColony</defaultFactionType>
|
||||
<invNutrition>0</invNutrition>
|
||||
<backstoryFiltersOverride>
|
||||
<li>
|
||||
<categories>
|
||||
<li>ArachnaeNode_spawnCategoriesA</li>
|
||||
<li>ArachnaeNode_spawnCategories_WeaponSmith</li>
|
||||
</categories>
|
||||
</li>
|
||||
</backstoryFiltersOverride>
|
||||
<abilities>
|
||||
<li>ARA_EggSpewBioforgeIncubator</li>
|
||||
</abilities>
|
||||
<apparelTags>
|
||||
</apparelTags>
|
||||
<apparelMoney>0</apparelMoney>
|
||||
</PawnKindDef>
|
||||
|
||||
<PawnKindDef Name="ARA_InsectKindBase" ParentName="AnimalKindBase" Abstract="True">
|
||||
<defaultFactionType>PlayerColony</defaultFactionType>
|
||||
<canArriveManhunter>false</canArriveManhunter>
|
||||
<forceDeathOnDowned>true</forceDeathOnDowned>
|
||||
<forceNoDeathNotification>true</forceNoDeathNotification>
|
||||
<combatPower>75</combatPower>
|
||||
<ecoSystemWeight>0</ecoSystemWeight>
|
||||
<moveSpeedFactorByTerrainTag>
|
||||
<li>
|
||||
<key>ARA_Creep</key>
|
||||
<value>8.0</value>
|
||||
</li>
|
||||
</moveSpeedFactorByTerrainTag>
|
||||
</PawnKindDef>
|
||||
<PawnKindDef ParentName="ARA_InsectKindBase">
|
||||
<defName>ArachnaeBase_Race_Slavey</defName>
|
||||
<label>阿拉克涅苦役种</label>
|
||||
<race>ArachnaeBase_Race_Slavey</race>
|
||||
<lifeStages>
|
||||
<li>
|
||||
<bodyGraphicData>
|
||||
<texPath>Things/Pawn/Animal/Spelopede/Spelopede</texPath>
|
||||
<drawSize>1</drawSize>
|
||||
<color>(156,148,125)</color>
|
||||
<shadowData>
|
||||
<volume>(0.4, 0.5, 0.37)</volume>
|
||||
<offset>(0,0,-0.15)</offset>
|
||||
</shadowData>
|
||||
</bodyGraphicData>
|
||||
<dessicatedBodyGraphicData>
|
||||
<texPath>Things/Pawn/Animal/Spelopede/Dessicated_Spelopede</texPath>
|
||||
<drawSize>1</drawSize>
|
||||
</dessicatedBodyGraphicData>
|
||||
</li>
|
||||
</lifeStages>
|
||||
</PawnKindDef>
|
||||
<PawnKindDef ParentName="ARA_InsectKindBase">
|
||||
<defName>ArachnaeBase_Race_Scavenger</defName>
|
||||
<label>阿拉克涅食腐种</label>
|
||||
<race>ArachnaeBase_Race_Scavenger</race>
|
||||
<lifeStages>
|
||||
<li>
|
||||
<bodyGraphicData>
|
||||
<texPath>Things/Pawn/Animal/Spelopede/Spelopede</texPath>
|
||||
<drawSize>1</drawSize>
|
||||
<color>(156,148,125)</color>
|
||||
<shadowData>
|
||||
<volume>(0.4, 0.5, 0.37)</volume>
|
||||
<offset>(0,0,-0.15)</offset>
|
||||
</shadowData>
|
||||
</bodyGraphicData>
|
||||
<dessicatedBodyGraphicData>
|
||||
<texPath>Things/Pawn/Animal/Spelopede/Dessicated_Spelopede</texPath>
|
||||
<drawSize>1</drawSize>
|
||||
</dessicatedBodyGraphicData>
|
||||
</li>
|
||||
</lifeStages>
|
||||
</PawnKindDef>
|
||||
<PawnKindDef ParentName="ARA_InsectKindBase">
|
||||
<defName>ArachnaeBase_Race_HardJaw</defName>
|
||||
<label>阿拉克涅坚颚种</label>
|
||||
<race>ArachnaeBase_Race_HardJaw</race>
|
||||
<lifeStages>
|
||||
<li>
|
||||
<bodyGraphicData>
|
||||
<texPath>Things/Pawn/Animal/Spelopede/Spelopede</texPath>
|
||||
<drawSize>1</drawSize>
|
||||
<color>(156,148,125)</color>
|
||||
<shadowData>
|
||||
<volume>(0.4, 0.5, 0.37)</volume>
|
||||
<offset>(0,0,-0.15)</offset>
|
||||
</shadowData>
|
||||
</bodyGraphicData>
|
||||
<dessicatedBodyGraphicData>
|
||||
<texPath>Things/Pawn/Animal/Spelopede/Dessicated_Spelopede</texPath>
|
||||
<drawSize>1</drawSize>
|
||||
</dessicatedBodyGraphicData>
|
||||
</li>
|
||||
</lifeStages>
|
||||
</PawnKindDef>
|
||||
<PawnKindDef ParentName="ARA_InsectKindBase">
|
||||
<defName>ArachnaeBase_Race_Acid</defName>
|
||||
<label>阿拉克涅酸嗜种</label>
|
||||
<race>ArachnaeBase_Race_Acid</race>
|
||||
<lifeStages>
|
||||
<li>
|
||||
<bodyGraphicData>
|
||||
<texPath>Things/Pawn/Animal/Spelopede/Spelopede</texPath>
|
||||
<drawSize>1</drawSize>
|
||||
<color>(156,148,125)</color>
|
||||
<shadowData>
|
||||
<volume>(0.4, 0.5, 0.37)</volume>
|
||||
<offset>(0,0,-0.15)</offset>
|
||||
</shadowData>
|
||||
</bodyGraphicData>
|
||||
<dessicatedBodyGraphicData>
|
||||
<texPath>Things/Pawn/Animal/Spelopede/Dessicated_Spelopede</texPath>
|
||||
<drawSize>1</drawSize>
|
||||
</dessicatedBodyGraphicData>
|
||||
</li>
|
||||
</lifeStages>
|
||||
</PawnKindDef>
|
||||
</Defs>
|
||||
@@ -33,6 +33,7 @@
|
||||
<useMeatFrom>Megaspider</useMeatFrom>
|
||||
<lifeExpectancy>0.1</lifeExpectancy>
|
||||
<trainability>Advanced</trainability>
|
||||
<!-- <hasCorpse>false</hasCorpse> -->
|
||||
<!-- <specialTrainables>
|
||||
<li MayRequire="Ludeon.RimWorld.Odyssey">Dig</li>
|
||||
<li>ARA_Sowing</li>
|
||||
@@ -77,15 +78,13 @@
|
||||
<level>8</level>
|
||||
</li>
|
||||
</skillLevels> -->
|
||||
<instantTrainables>
|
||||
<!-- <li>ARA_Sowing</li>
|
||||
<li>Dig</li> -->
|
||||
<li>Haul</li>
|
||||
<!-- 救援 -->
|
||||
<!-- <li>Rescue</li> -->
|
||||
<!-- 觅食 -->
|
||||
<!-- <li>Forage</li> -->
|
||||
</instantTrainables>
|
||||
<trainables>
|
||||
<li>
|
||||
<trainable>Haul</trainable>
|
||||
<trainInstantly>true</trainInstantly>
|
||||
<setWanted>true</setWanted>
|
||||
</li>
|
||||
</trainables>
|
||||
<disableAllSkillDecay>true</disableAllSkillDecay> <!-- 阻止这个动物的所有技能衰减 -->
|
||||
</li>
|
||||
</comps>
|
||||
@@ -101,9 +100,13 @@
|
||||
</race>
|
||||
<comps>
|
||||
<li Class="ArachnaeSwarm.CompProperties_AdvancedTraining">
|
||||
<instantTrainables>
|
||||
<li>ARA_Sowing</li>
|
||||
</instantTrainables>
|
||||
<trainables>
|
||||
<li>
|
||||
<trainable>ARA_Sowing</trainable>
|
||||
<trainInstantly>true</trainInstantly>
|
||||
<setWanted>true</setWanted>
|
||||
</li>
|
||||
</trainables>
|
||||
<disableAllSkillDecay>true</disableAllSkillDecay> <!-- 阻止这个动物的所有技能衰减 -->
|
||||
</li>
|
||||
</comps>
|
||||
@@ -120,12 +123,64 @@
|
||||
</race>
|
||||
<comps>
|
||||
<li Class="ArachnaeSwarm.CompProperties_AdvancedTraining">
|
||||
<instantTrainables>
|
||||
<li>Dig</li>
|
||||
<li>AttackTarget</li>
|
||||
</instantTrainables>
|
||||
<trainables>
|
||||
<li>
|
||||
<trainable>Dig</trainable>
|
||||
<trainInstantly>true</trainInstantly>
|
||||
<setWanted>true</setWanted>
|
||||
</li>
|
||||
<li>
|
||||
<trainable>AttackTarget</trainable>
|
||||
<trainInstantly>true</trainInstantly>
|
||||
<setWanted>true</setWanted>
|
||||
</li>
|
||||
</trainables>
|
||||
<disableAllSkillDecay>true</disableAllSkillDecay> <!-- 阻止这个动物的所有技能衰减 -->
|
||||
</li>
|
||||
</comps>
|
||||
</ThingDef>
|
||||
<ThingDef ParentName="BaseDrone" MayRequire="Ludeon.RimWorld.Odyssey">
|
||||
<defName>ArachnaeBase_Race_Acid</defName>
|
||||
<label>阿拉克涅酸嗜种</label>
|
||||
<description>阿拉克涅辅虫之一,智力低下,一般被作为活体炮弹打出,击中敌人后若是还没散架,就会继续依靠带酸液的颚撕咬敌军。</description>
|
||||
<race>
|
||||
<thinkTreeConstant>WarUrchinConstant</thinkTreeConstant>
|
||||
<baseBodySize>0.5</baseBodySize>
|
||||
<!-- <hasCorpse>false</hasCorpse> -->
|
||||
<body>BeetleLikeWithClaw</body>
|
||||
<lifeStageAges>
|
||||
<li>
|
||||
<def>EusocialInsectAdult</def>
|
||||
<minAge>0</minAge>
|
||||
<soundWounded>Pawn_Spelopede_Pain</soundWounded>
|
||||
<soundDeath>Pawn_Spelopede_Death</soundDeath>
|
||||
<soundCall>Pawn_Spelopede_Call</soundCall>
|
||||
<soundAngry>Pawn_Spelopede_Angry</soundAngry>
|
||||
</li>
|
||||
</lifeStageAges>
|
||||
</race>
|
||||
<statBases>
|
||||
<MoveSpeed>6</MoveSpeed>
|
||||
</statBases>
|
||||
<comps>
|
||||
<li Class="CompProperties_MechPowerCell">
|
||||
<totalPowerTicks>4400</totalPowerTicks> <!-- 2 hours -->
|
||||
<labelOverride>寿命</labelOverride>
|
||||
<tooltipOverride>这种特殊的阿拉克涅辅虫从出生起就走在死亡的道路上了——它们的寿命就是如此短暂。</tooltipOverride>
|
||||
<showGizmoOnNonPlayerControlled>true</showGizmoOnNonPlayerControlled>
|
||||
</li>
|
||||
</comps>
|
||||
<tools Inherit="False">
|
||||
<li>
|
||||
<label>酸性巨颚</label>
|
||||
<capacities>
|
||||
<li>ARA_AcidCut</li>
|
||||
</capacities>
|
||||
<power>6</power>
|
||||
<cooldownTime>2.6</cooldownTime>
|
||||
<linkedBodyPartsGroup>HeadAttackTool</linkedBodyPartsGroup>
|
||||
<ensureLinkedBodyPartsGroupAlwaysUsable>true</ensureLinkedBodyPartsGroupAlwaysUsable>
|
||||
</li>
|
||||
</tools>
|
||||
</ThingDef>
|
||||
</Defs>
|
||||
@@ -273,9 +273,10 @@
|
||||
</whiteXenotypeList>
|
||||
<onlyUseRaceRestrictedXenotypes>false</onlyUseRaceRestrictedXenotypes>
|
||||
<!-- 食物列表 -->
|
||||
<foodList>
|
||||
</foodList>
|
||||
<onlyEatRaceRestrictedFood>false</onlyEatRaceRestrictedFood>
|
||||
<whiteFoodList>
|
||||
<li>ARA_InsectJelly</li>
|
||||
</whiteFoodList>
|
||||
<onlyEatRaceRestrictedFood>true</onlyEatRaceRestrictedFood>
|
||||
<!-- 可以穿戴的衣服 -->
|
||||
<apparelList>
|
||||
</apparelList>
|
||||
@@ -521,7 +522,7 @@
|
||||
</race>
|
||||
|
||||
<!-- 工具设置(攻击方式) -->
|
||||
<tools>
|
||||
<tools Inherit="False"><!-- 必须写禁止继承不然会把HUMANLIKE的攻击方式继承过来导致红字找不到部件报错 -->
|
||||
<li>
|
||||
<label>头颚</label>
|
||||
<capacities>
|
||||
@@ -593,6 +594,15 @@
|
||||
</bodyAddons>
|
||||
</alienPartGenerator>
|
||||
</generalSettings>
|
||||
<!-- 种族的允许和禁止特化设置 -->
|
||||
<raceRestriction>
|
||||
<!-- 食物列表 -->
|
||||
<whiteFoodList Inherit="False"/>
|
||||
<blackFoodList>
|
||||
<li>ARA_InsectJelly</li>
|
||||
</blackFoodList>
|
||||
<onlyEatRaceRestrictedFood>false</onlyEatRaceRestrictedFood>
|
||||
</raceRestriction>
|
||||
</alienRace>
|
||||
<comps>
|
||||
<li Class="ArachnaeSwarm.CompProperties_MilkableArachnae">
|
||||
@@ -739,7 +749,7 @@
|
||||
<productionQueue>
|
||||
<li>
|
||||
<pawnKind>ArachnaeBase_Race_HardJaw</pawnKind>
|
||||
<count>5</count>
|
||||
<count>2</count>
|
||||
<cooldownTicks>1000</cooldownTicks>
|
||||
</li>
|
||||
</productionQueue>
|
||||
@@ -792,4 +802,126 @@
|
||||
</lifeStageAges>
|
||||
</race>
|
||||
</AlienRace.ThingDef_AlienRace>
|
||||
<AlienRace.ThingDef_AlienRace ParentName="ARA_NodeBase">
|
||||
<defName>ArachnaeNode_Race_WeaponSmith</defName>
|
||||
<label>阿拉克涅工艺种</label>
|
||||
<description>阿拉克涅督虫之一,天生牛马。\n\n作为督虫,她可以繁育并监管若干阿拉克涅坚颚种辅虫,以协助巢穴开采矿脉。</description>
|
||||
<alienRace>
|
||||
<generalSettings>
|
||||
<!-- 各种零件定义 -->
|
||||
<alienPartGenerator>
|
||||
<!-- 额外身体部件 -->
|
||||
<bodyAddons>
|
||||
<li>
|
||||
<path>ArachnaeSwarm/Things/ARA_HiveNode/Addons/ArachnaeNode_Race_Addons_ShieldHead</path>
|
||||
<offsets>
|
||||
<south>
|
||||
<offset>(0,0.32)</offset>
|
||||
</south>
|
||||
<north>
|
||||
<offset>(0,0.32)</offset>
|
||||
</north>
|
||||
<east>
|
||||
<offset>(-0.1,0.32)</offset>
|
||||
</east>
|
||||
<west>
|
||||
<offset>(-0.1,0.32)</offset>
|
||||
</west>
|
||||
</offsets>
|
||||
<inFrontOfBody>true</inFrontOfBody>
|
||||
</li>
|
||||
<li>
|
||||
<path>ArachnaeSwarm/Things/ARA_HiveNode/Addons/ArachnaeNode_Race_Addons_ShieldBody</path>
|
||||
<inFrontOfBody>true</inFrontOfBody>
|
||||
<offsets>
|
||||
<south>
|
||||
<!-- <layerOffset>0.25</layerOffset> -->
|
||||
</south>
|
||||
<north>
|
||||
<layerOffset>-0.6</layerOffset>
|
||||
</north>
|
||||
<east>
|
||||
<!-- same structure repeated -->
|
||||
</east>
|
||||
<west>
|
||||
<!-- optional, mirrors east if omitted -->
|
||||
</west>
|
||||
</offsets>
|
||||
</li>
|
||||
</bodyAddons>
|
||||
</alienPartGenerator>
|
||||
</generalSettings>
|
||||
<raceRestriction>
|
||||
<!-- 食物列表 -->
|
||||
<foodList>
|
||||
<li>ARA_InsectJelly</li>
|
||||
</foodList>
|
||||
<onlyEatRaceRestrictedFood>true</onlyEatRaceRestrictedFood>
|
||||
</raceRestriction>
|
||||
</alienRace>
|
||||
<comps>
|
||||
<li Class="ArachnaeSwarm.CompProperties_AutoMechCarrier">
|
||||
<freeProduction>true</freeProduction>
|
||||
<disableHediff>WULA_MechCarrierSwitchHediff</disableHediff>
|
||||
<fixedIngredient>ARA_InsectJelly</fixedIngredient>
|
||||
<maxIngredientCount>500</maxIngredientCount>
|
||||
<startingIngredientCount>500</startingIngredientCount>
|
||||
<costPerPawn>999</costPerPawn>
|
||||
<cooldownTicks>9999</cooldownTicks>
|
||||
<productionQueue>
|
||||
<li>
|
||||
<pawnKind>ArachnaeBase_Race_Slavey</pawnKind>
|
||||
<count>2</count>
|
||||
<cooldownTicks>1000</cooldownTicks>
|
||||
</li>
|
||||
</productionQueue>
|
||||
<spawnEffecter>CocoonDestroyed</spawnEffecter>
|
||||
</li>
|
||||
</comps>
|
||||
|
||||
<!-- 基础属性设置 -->
|
||||
<statBases>
|
||||
<!-- 移动速度 -->
|
||||
<MoveSpeed>2</MoveSpeed>
|
||||
|
||||
<!-- <RestRateMultiplier>1</RestRateMultiplier> -->
|
||||
<!-- <HungerRateMultiplier>1</HungerRateMultiplier> -->
|
||||
<EatingSpeed>5</EatingSpeed>
|
||||
<MaxNutrition>1</MaxNutrition>
|
||||
|
||||
<CarryingCapacity>100</CarryingCapacity>
|
||||
<MeatAmount>50</MeatAmount>
|
||||
<LeatherAmount>80</LeatherAmount>
|
||||
|
||||
<MeleeDodgeChance>0.75</MeleeDodgeChance>
|
||||
<!-- <MeleeHitChance>1</MeleeHitChance> -->
|
||||
|
||||
<!-- <NegotiationAbility>1</NegotiationAbility> -->
|
||||
<!-- <SellPriceFactor>1</SellPriceFactor> -->
|
||||
<!-- <SocialImpact>1</SocialImpact> -->
|
||||
<!-- <TradePriceImprovement>0.5</TradePriceImprovement> -->
|
||||
|
||||
<!-- 自带的甲壳可以防御外部攻击 -->
|
||||
<ArmorRating_Blunt>1</ArmorRating_Blunt>
|
||||
<ArmorRating_Sharp>1.25</ArmorRating_Sharp>
|
||||
<ArmorRating_Heat>1</ArmorRating_Heat>
|
||||
</statBases>
|
||||
|
||||
<race>
|
||||
<!-- 身体类型 -->
|
||||
<body>ArachnaeMyrmecocystus_Body</body>
|
||||
<!-- 身形大小 -->
|
||||
<baseBodySize>2</baseBodySize>
|
||||
<!-- 血量上限 -->
|
||||
<baseHealthScale>5</baseHealthScale>
|
||||
<!-- 蜜罐虫的寿命很低 -->
|
||||
<lifeExpectancy>2</lifeExpectancy>
|
||||
<lifeStageAges Inherit="False">
|
||||
<li>
|
||||
<def>ArachnaeNode_Myrmecocystus_Adult</def>
|
||||
<minAge>0</minAge>
|
||||
</li>
|
||||
</lifeStageAges>
|
||||
</race>
|
||||
</AlienRace.ThingDef_AlienRace>
|
||||
</Defs>
|
||||
@@ -456,10 +456,17 @@
|
||||
</xenotypeList>
|
||||
<onlyUseRaceRestrictedXenotypes>true</onlyUseRaceRestrictedXenotypes>
|
||||
<!-- 食物列表 -->
|
||||
<foodList>
|
||||
<whiteFoodList>
|
||||
<li>ARA_InsectJelly</li>
|
||||
</foodList>
|
||||
</whiteFoodList>
|
||||
<onlyEatRaceRestrictedFood>true</onlyEatRaceRestrictedFood>
|
||||
<!-- 可以驯服的宠物,主要是防止小虫由别人驯服 -->
|
||||
<petList>
|
||||
<li>ArachnaeBase_Race_Slavey</li>
|
||||
<li>ArachnaeBase_Race_Scavenger</li>
|
||||
<li>ArachnaeBase_Race_HardJaw</li>
|
||||
</petList>
|
||||
<onlyTameRaceRestrictedPets>false</onlyTameRaceRestrictedPets>
|
||||
<!-- 可以穿戴的衣服 -->
|
||||
<apparelList>
|
||||
</apparelList>
|
||||
@@ -732,23 +739,7 @@
|
||||
</recipes>
|
||||
|
||||
<comps>
|
||||
<li Class="ArachnaeSwarm.CompProperties_AutoMechCarrier">
|
||||
<freeProduction>true</freeProduction>
|
||||
<disableHediff>WULA_MechCarrierSwitchHediff</disableHediff>
|
||||
<fixedIngredient>ARA_InsectJelly</fixedIngredient>
|
||||
<maxIngredientCount>500</maxIngredientCount>
|
||||
<startingIngredientCount>500</startingIngredientCount>
|
||||
<costPerPawn>999</costPerPawn>
|
||||
<cooldownTicks>9999</cooldownTicks>
|
||||
<productionQueue>
|
||||
<li>
|
||||
<pawnKind>ArachnaeBase_Race_Slavey</pawnKind>
|
||||
<count>5</count>
|
||||
<cooldownTicks>1000</cooldownTicks>
|
||||
</li>
|
||||
</productionQueue>
|
||||
<spawnEffecter>CocoonDestroyed</spawnEffecter>
|
||||
</li>
|
||||
|
||||
</comps>
|
||||
</AlienRace.ThingDef_AlienRace>
|
||||
</Defs>
|
||||
115
1.6/1.6/Defs/ThingDef_Races/ARA_Race_HuggingFaceSwarm.xml
Normal file
115
1.6/1.6/Defs/ThingDef_Races/ARA_Race_HuggingFaceSwarm.xml
Normal file
@@ -0,0 +1,115 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Defs>
|
||||
|
||||
<!-- ==================== Pawn Kind Def ==================== -->
|
||||
<PawnKindDef ParentName="ArachnaeNodeABasePawnKind">
|
||||
<defName>ArachnaeNode_Race_Facehugger</defName>
|
||||
<label>阿拉克涅原虫种</label>
|
||||
<race>ArachnaeNode_Race_Facehugger</race>
|
||||
<defaultFactionType>PlayerColony</defaultFactionType>
|
||||
<invNutrition>0</invNutrition>
|
||||
<backstoryFiltersOverride>
|
||||
<li>
|
||||
<categories>
|
||||
<li>ArachnaeNode_spawnCategoriesA</li>
|
||||
<li>ArachnaeNode_spawnCategories_Facehugger</li>
|
||||
</categories>
|
||||
</li>
|
||||
</backstoryFiltersOverride>
|
||||
<abilities>
|
||||
<li>ARA_Ability_Possess</li>
|
||||
</abilities>
|
||||
<apparelTags>
|
||||
</apparelTags>
|
||||
<apparelMoney>0</apparelMoney>
|
||||
</PawnKindDef>
|
||||
|
||||
<!-- 定义阿拉克涅节点虫种族 -->
|
||||
<AlienRace.ThingDef_AlienRace ParentName="ARA_NodeBase">
|
||||
<defName>ArachnaeNode_Race_Facehugger</defName>
|
||||
<label>阿拉克涅原虫种</label>
|
||||
<description>阿拉克涅原虫,通过独特的神经链接管伸入受害者身体来接管受害者的身体。不同于普通阿拉克涅虫族,阿拉克涅原虫种拥有自我意识,不需要女皇种的监管。</description>
|
||||
<alienRace>
|
||||
<generalSettings>
|
||||
<!-- 各种零件定义 -->
|
||||
<alienPartGenerator>
|
||||
<!-- 额外身体部件 -->
|
||||
<bodyAddons>
|
||||
<li>
|
||||
<path>ArachnaeSwarm/Things/ARA_HiveNode/Addons/ArachnaeNode_Race_Myrmecocystus_Addons_Stomach</path>
|
||||
<inFrontOfBody>false</inFrontOfBody>
|
||||
<!-- <conditions>
|
||||
<BodyPart>
|
||||
<bodyPart>WULA_Addons_Antenna_Bodypart</bodyPart>
|
||||
</BodyPart>
|
||||
</conditions> -->
|
||||
</li>
|
||||
</bodyAddons>
|
||||
</alienPartGenerator>
|
||||
</generalSettings>
|
||||
<!-- 种族的允许和禁止特化设置 -->
|
||||
<raceRestriction>
|
||||
<!-- 食物列表 -->
|
||||
<whiteFoodList Inherit="False"/>
|
||||
<blackFoodList>
|
||||
<li>ARA_InsectJelly</li>
|
||||
</blackFoodList>
|
||||
<onlyEatRaceRestrictedFood>false</onlyEatRaceRestrictedFood>
|
||||
</raceRestriction>
|
||||
</alienRace>
|
||||
<comps>
|
||||
<li Class="ArachnaeSwarm.CompProperties_MilkableArachnae">
|
||||
<milkDef>ARA_InsectJelly</milkDef>
|
||||
<milkIntervalDays>3</milkIntervalDays>
|
||||
<milkAmount>4</milkAmount>
|
||||
</li>
|
||||
</comps>
|
||||
|
||||
<!-- 基础属性设置 -->
|
||||
<statBases>
|
||||
<!-- 移动速度 -->
|
||||
<MoveSpeed>2</MoveSpeed>
|
||||
|
||||
<!-- <RestRateMultiplier>1</RestRateMultiplier> -->
|
||||
<!-- <HungerRateMultiplier>1</HungerRateMultiplier> -->
|
||||
<EatingSpeed>5</EatingSpeed>
|
||||
<MaxNutrition>1</MaxNutrition>
|
||||
|
||||
<CarryingCapacity>100</CarryingCapacity>
|
||||
<MeatAmount>70</MeatAmount>
|
||||
<LeatherAmount>10</LeatherAmount>
|
||||
|
||||
<MeleeDodgeChance>0.5</MeleeDodgeChance>
|
||||
<!-- <MeleeHitChance>1</MeleeHitChance> -->
|
||||
|
||||
<!-- <NegotiationAbility>1</NegotiationAbility> -->
|
||||
<!-- <SellPriceFactor>1</SellPriceFactor> -->
|
||||
<!-- <SocialImpact>1</SocialImpact> -->
|
||||
<!-- <TradePriceImprovement>0.5</TradePriceImprovement> -->
|
||||
|
||||
<!-- 自带的甲壳可以防御外部攻击 -->
|
||||
<ArmorRating_Blunt>0.18</ArmorRating_Blunt>
|
||||
<ArmorRating_Sharp>0.27</ArmorRating_Sharp>
|
||||
<ArmorRating_Heat>0.2</ArmorRating_Heat>
|
||||
</statBases>
|
||||
|
||||
<race>
|
||||
<foodType>OmnivoreHuman,CarnivoreAnimal,OvivoreAnimal,VegetarianRoughAnimal</foodType>
|
||||
<!-- 身体类型 -->
|
||||
<body>ArachnaeMyrmecocystus_Body</body>
|
||||
<!-- 身形大小 -->
|
||||
<baseBodySize>2.5</baseBodySize>
|
||||
<!-- 血量上限 -->
|
||||
<baseHealthScale>3</baseHealthScale>
|
||||
<!-- 蜜罐虫的寿命很低 -->
|
||||
<lifeExpectancy>1.5</lifeExpectancy>
|
||||
<lifeStageAges Inherit="False">
|
||||
<li>
|
||||
<def>ArachnaeNode_Myrmecocystus_Adult</def>
|
||||
<minAge>0</minAge>
|
||||
</li>
|
||||
</lifeStageAges>
|
||||
</race>
|
||||
</AlienRace.ThingDef_AlienRace>
|
||||
|
||||
</Defs>
|
||||
@@ -3,7 +3,7 @@
|
||||
<ThingDef ParentName="OrganicProductBase">
|
||||
<defName>ARA_InsectJelly</defName>
|
||||
<label>阿拉克涅虫蜜</label>
|
||||
<description>由阿拉克涅虫族储存和用作食物的果冻。它顺滑、浓郁、永不腐烂,能满足食用者的娱乐需求。由于其独特的生物特性,非阿拉克涅虫族也可以食用这种食物,并且不会引起食物中毒。</description>
|
||||
<description>由阿拉克涅虫族储存和用作食物的果冻,是大部分阿拉克涅虫族的唯一食物。它顺滑、浓郁、永不腐烂,能满足食用者的娱乐需求。由于其独特的生物特性,非阿拉克涅虫族也可以食用这种食物,并且不会引起食物中毒。</description>
|
||||
<possessionCount>20</possessionCount>
|
||||
<graphicData>
|
||||
<texPath>ArachnaeSwarm/Item/ARA_InsectJelly</texPath>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<TerrainDef ParentName="FloorBase">
|
||||
<defName>ARA_InsectCreep</defName>
|
||||
<label>阿拉克涅菌毯</label>
|
||||
<description>由阿拉克涅虫族所铺设的由真菌、甲壳素分泌物混合得到的地面,会自动在各种拉克涅虫族建筑附近蔓延。这种奇特的结构质地紧密且暗藏大量营养输送组织,可以支撑重型建筑、种植特殊作物,但是除了虫族以外没人想踩在这坨软乎乎的活体结构上。</description>
|
||||
<description>由阿拉克涅虫族所铺设的由真菌、甲壳素分泌物混合得到的地面,会自动在各种拉克涅虫族建筑附近蔓延。这种奇特的结构质地紧密且暗藏大量营养输送组织,可以支撑重型建筑、种植特殊作物、加速其上的阿拉克涅虫族移动速度,但是除了虫族以外没人想踩在这坨软乎乎的活体结构上。</description>
|
||||
<designationCategory>ARA_Buildings</designationCategory>
|
||||
<texturePath>Terrain/Surfaces/InsectSludge</texturePath>
|
||||
<color>(203, 163, 68)</color>
|
||||
@@ -13,7 +13,7 @@
|
||||
<edgeType>FadeRough</edgeType>
|
||||
<resourcesFractionWhenDeconstructed>0</resourcesFractionWhenDeconstructed>
|
||||
<renderPrecedence>330</renderPrecedence>
|
||||
<pathCost>0</pathCost>
|
||||
<pathCost>2</pathCost>
|
||||
<generatedFilth>Filth_Slime</generatedFilth>
|
||||
<filthAcceptanceMask>
|
||||
<li>Unnatural</li>
|
||||
@@ -206,6 +206,7 @@
|
||||
<tickerType>Normal</tickerType>
|
||||
<designationCategory>ARA_Buildings</designationCategory>
|
||||
<repairEffect>EatVegetarian</repairEffect>
|
||||
<filthLeaving>Filth_Slime</filthLeaving>
|
||||
<!-- <thingCategories>
|
||||
<li>BuildingsSpecial</li>
|
||||
</thingCategories> -->
|
||||
@@ -247,4 +248,114 @@
|
||||
<li>PlaceWorker_GlowRadius</li>
|
||||
</placeWorkers>
|
||||
</ThingDef>
|
||||
<ThingDef ParentName="BuildingNaturalBase">
|
||||
<defName>ARA_Tumor_SunLamp</defName>
|
||||
<label>阿拉克涅巨型菌瘤</label>
|
||||
<description>一个比普通菌瘤更大的菌瘤,内部的发光器官亮的难以想象,可以支持作物的光照条件,但是其寿命比普通菌瘤要短得多。</description>
|
||||
<thingClass>Building_SunLamp</thingClass>
|
||||
<graphicData>
|
||||
<texPath>ArachnaeSwarm/Building/ARA_Tumor</texPath>
|
||||
<graphicClass>Graphic_Single</graphicClass>
|
||||
<shadowData>
|
||||
<volume>(0.3, 0.6, 0.3)</volume>
|
||||
<offset>(0,0,-0.1)</offset>
|
||||
</shadowData>
|
||||
<damageData>
|
||||
<rect>(0.2,0.2,0.6,0.6)</rect>
|
||||
</damageData>
|
||||
</graphicData>
|
||||
<size>(2,2)</size>
|
||||
<blockWind>true</blockWind>
|
||||
<tickerType>Normal</tickerType>
|
||||
<statBases>
|
||||
<WorkToBuild>600</WorkToBuild>
|
||||
<Mass>4.5</Mass>
|
||||
</statBases>
|
||||
<designationCategory>ARA_Buildings</designationCategory>
|
||||
<costList>
|
||||
<ARA_InsectJelly>10</ARA_InsectJelly>
|
||||
</costList>
|
||||
<uiOrder>2995</uiOrder>
|
||||
<comps>
|
||||
<li Class="CompProperties_Glower">
|
||||
<overlightRadius>7.0</overlightRadius>
|
||||
<glowRadius>14</glowRadius>
|
||||
<glowColor>(220,210,171,0)</glowColor>
|
||||
<colorPickerEnabled>true</colorPickerEnabled>
|
||||
</li>
|
||||
<!-- <li Class="CompProperties_Schedule">
|
||||
<startTime>0.25</startTime>
|
||||
<endTime>0.8</endTime>
|
||||
<offMessage>Off for plant resting period</offMessage>
|
||||
</li> -->
|
||||
<!-- <li Class="CompProperties_HeatPusher">
|
||||
<compClass>CompHeatPusherPowered</compClass>
|
||||
<heatPerSecond>3</heatPerSecond>
|
||||
</li> -->
|
||||
</comps>
|
||||
<!-- Determined by trial and error
|
||||
This value isn't perfect because these radii use different algorithms
|
||||
but it matches in this case-->
|
||||
<specialDisplayRadius>5.8</specialDisplayRadius>
|
||||
</ThingDef>
|
||||
|
||||
<ThingDef ParentName="BenchBase">
|
||||
<defName>ARA_ResearchBench</defName>
|
||||
<label>阿拉克涅研究台</label>
|
||||
<description>A simple bench with writing implements and simple measurement devices. Researchers work here to discover new things.</description>
|
||||
<thingClass>Building_ResearchBench</thingClass>
|
||||
<size>(3,2)</size>
|
||||
<stuffCategories>
|
||||
<li>Metallic</li>
|
||||
<li>Woody</li>
|
||||
<li>Stony</li>
|
||||
</stuffCategories>
|
||||
<costStuffCount>75</costStuffCount>
|
||||
<costList>
|
||||
<Steel>25</Steel>
|
||||
</costList>
|
||||
<graphicData>
|
||||
<texPath>Things/Building/Production/ResearchBenchSimple</texPath>
|
||||
<graphicClass>Graphic_Multi</graphicClass>
|
||||
<shaderType>CutoutComplex</shaderType>
|
||||
<drawSize>(5,4)</drawSize>
|
||||
<damageData>
|
||||
<cornerTL>Damage/Corner</cornerTL>
|
||||
<cornerTR>Damage/Corner</cornerTR>
|
||||
<cornerBL>Damage/Corner</cornerBL>
|
||||
<cornerBR>Damage/Corner</cornerBR>
|
||||
</damageData>
|
||||
</graphicData>
|
||||
<castEdgeShadows>true</castEdgeShadows>
|
||||
<staticSunShadowHeight>0.20</staticSunShadowHeight>
|
||||
<altitudeLayer>Building</altitudeLayer>
|
||||
<passability>PassThroughOnly</passability>
|
||||
<pathCost>50</pathCost>
|
||||
<statBases>
|
||||
<MaxHitPoints>250</MaxHitPoints>
|
||||
<WorkToBuild>2800</WorkToBuild>
|
||||
<Flammability>1.0</Flammability>
|
||||
<ResearchSpeedFactor>0.75</ResearchSpeedFactor>
|
||||
</statBases>
|
||||
<placeWorkers>
|
||||
<li>PlaceWorker_PreventInteractionSpotOverlap</li>
|
||||
<li>PlaceWorker_DrawLinesToBookcasesInRoom</li>
|
||||
</placeWorkers>
|
||||
<fillPercent>0.5</fillPercent>
|
||||
<interactionCellOffset>(0,0,-1)</interactionCellOffset>
|
||||
<hasInteractionCell>true</hasInteractionCell>
|
||||
<designationCategory>Production</designationCategory>
|
||||
<uiOrder>2600</uiOrder>
|
||||
<surfaceType>Item</surfaceType>
|
||||
<building>
|
||||
<paintable>true</paintable>
|
||||
<workTableRoomRole>Laboratory</workTableRoomRole>
|
||||
<workTableNotInRoomRoleFactor>0.8</workTableNotInRoomRoleFactor>
|
||||
</building>
|
||||
<comps Inherit="False">
|
||||
<li Class="CompProperties_ReportWorkSpeed">
|
||||
<workSpeedStat>ResearchSpeedFactor</workSpeedStat>
|
||||
</li>
|
||||
</comps>
|
||||
</ThingDef>
|
||||
</Defs>
|
||||
@@ -64,6 +64,10 @@
|
||||
<pawnKind>ArachnaeNode_Race_ShieldHead</pawnKind>
|
||||
<delay>300</delay>
|
||||
</li>
|
||||
<li>
|
||||
<pawnKind>ArachnaeNode_Race_WeaponSmith</pawnKind>
|
||||
<delay>300</delay>
|
||||
</li>
|
||||
</pawnKindDelays>
|
||||
<destroyOnSpawn>true</destroyOnSpawn>
|
||||
</li>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<ThingDef ParentName="BuildingBase">
|
||||
<defName>ARA_BioforgeIncubator</defName>
|
||||
<label>阿拉克涅孵化茧</label>
|
||||
<description>一个脆弱、易燃、黏滑的囊状物,是阿拉克涅工艺种所诞之卵,内含哺育阿拉克涅武器种虫族所需的营养和遗传物质,可以通过阿拉克涅工艺种的交互完成激活进程。</description>
|
||||
<description>一个脆弱、易燃、黏滑的囊状物,是阿拉克涅工艺种所诞之卵,内含哺育阿拉克涅武器种虫族所需的营养和遗传物质,可以通过阿拉克涅工艺种的交互完成激活进程。孵化茧对温度极度敏感,遭受极端温度将会受损需要小心保护。</description>
|
||||
<thingClass>Building</thingClass>
|
||||
<graphicData>
|
||||
<texPath>ArachnaeSwarm/Building/ARA_EggSac</texPath>
|
||||
@@ -27,7 +27,15 @@
|
||||
</building>
|
||||
|
||||
<comps>
|
||||
<li Class="CompProperties_Flickable"/>
|
||||
<li Class="CompProperties_SpawnEffecterOnDestroy">
|
||||
<effect>CocoonDestroyed</effect>
|
||||
</li>
|
||||
<li Class="CompProperties_SpawnerFilth">
|
||||
<filthDef>Filth_Slime</filthDef>
|
||||
<spawnCountOnSpawn>10</spawnCountOnSpawn>
|
||||
<spawnMtbHours>4</spawnMtbHours>
|
||||
<spawnRadius>5</spawnRadius>
|
||||
</li>
|
||||
|
||||
<!-- The new, GrowthVat-style fuel component -->
|
||||
<li Class="ArachnaeSwarm.CompProperties_RefuelableNutrition">
|
||||
@@ -38,7 +46,7 @@
|
||||
<li>Foods</li>
|
||||
</categories>
|
||||
</fuelFilter>
|
||||
<fuelConsumptionRate>0</fuelConsumptionRate> <!-- IMPORTANT: Disable base class consumption -->
|
||||
<fuelConsumptionRate>0</fuelConsumptionRate> <!-- IMPORTANT: Disable base class consumption -->
|
||||
</li>
|
||||
|
||||
<!-- The refactored producer component -->
|
||||
@@ -57,7 +65,7 @@
|
||||
</processes>
|
||||
|
||||
<whitelist>
|
||||
<li>ARA_ArachnaeQueen</li>
|
||||
<li>ArachnaeNode_Race_WeaponSmith</li>
|
||||
</whitelist>
|
||||
<spawnCount>1</spawnCount>
|
||||
<destroyOnSpawn>True</destroyOnSpawn>
|
||||
@@ -165,7 +173,7 @@
|
||||
<recoveryRate>0.001</recoveryRate>
|
||||
</li>
|
||||
</comps>
|
||||
|
||||
|
||||
</ThingDef>
|
||||
|
||||
</Defs>
|
||||
|
||||
@@ -4,4 +4,11 @@
|
||||
<ARA_Incubate>孵化 {0}</ARA_Incubate>
|
||||
<ARA_NeedsInteraction>未孵化,需要阿拉克涅女皇种交互</ARA_NeedsInteraction>
|
||||
|
||||
<!-- CompAbilityEffect_BindDrone -->
|
||||
<ARA_BindDrone_Success>{0} 已成功绑定到 {1} 的蜂巢思维。</ARA_BindDrone_Success>
|
||||
<ARA_BindDrone_Failure>无法将 {0} 绑定到 {1} 的蜂巢思维。</ARA_BindDrone_Failure>
|
||||
<ARA_BindDrone_NoDroneHediff>目标 {0} 没有“ARA_HiveMindDrone”的 hediff。</ARA_BindDrone_NoDroneHediff>
|
||||
<ARA_BindDrone_AlreadyBound>目标 {0} 已绑定到 {1}。</ARA_BindDrone_AlreadyBound>
|
||||
<ARA_BindDrone_NoMasterHediff>施法者 {0} 没有“ARA_HiveMindMaster”的 hediff。</ARA_BindDrone_NoMasterHediff>
|
||||
|
||||
</LanguageData>
|
||||
@@ -20,11 +20,11 @@ namespace ArachnaeSwarm
|
||||
{
|
||||
if (masterHediff.TryBindDrone(dronePawn))
|
||||
{
|
||||
Messages.Message($"Successfully bound {dronePawn.LabelShort} to {masterPawn.LabelShort}'s hive mind.", MessageTypeDefOf.PositiveEvent, historical: false);
|
||||
Messages.Message("ARA_BindDrone_Success".Translate(dronePawn.LabelShort, masterPawn.LabelShort), MessageTypeDefOf.PositiveEvent, historical: false);
|
||||
}
|
||||
else
|
||||
{
|
||||
Messages.Message($"Failed to bind {dronePawn.LabelShort} to {masterPawn.LabelShort}'s hive mind. Check logs for details.", MessageTypeDefOf.NegativeEvent, historical: false);
|
||||
Messages.Message("ARA_BindDrone_Failure".Translate(dronePawn.LabelShort, masterPawn.LabelShort), MessageTypeDefOf.NegativeEvent, historical: false);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -70,7 +70,7 @@ namespace ArachnaeSwarm
|
||||
{
|
||||
if (throwMessages)
|
||||
{
|
||||
Messages.Message($"Target {dronePawn.LabelShort} does not have the 'ARA_HiveMindDrone' hediff.", MessageTypeDefOf.RejectInput, historical: false);
|
||||
Messages.Message("ARA_BindDrone_NoDroneHediff".Translate(dronePawn.LabelShort), MessageTypeDefOf.RejectInput, historical: false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -80,7 +80,7 @@ namespace ArachnaeSwarm
|
||||
{
|
||||
if (throwMessages)
|
||||
{
|
||||
Messages.Message($"Target {dronePawn.LabelShort} is already bound to {droneHediff.target.LabelShort}.", MessageTypeDefOf.RejectInput, historical: false);
|
||||
Messages.Message("ARA_BindDrone_AlreadyBound".Translate(dronePawn.LabelShort, droneHediff.target.LabelShort), MessageTypeDefOf.RejectInput, historical: false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -91,7 +91,7 @@ namespace ArachnaeSwarm
|
||||
{
|
||||
if (throwMessages)
|
||||
{
|
||||
Messages.Message($"Caster {masterPawn.LabelShort} does not have the 'ARA_HiveMindMaster' hediff.", MessageTypeDefOf.RejectInput, historical: false);
|
||||
Messages.Message("ARA_BindDrone_NoMasterHediff".Translate(masterPawn.LabelShort), MessageTypeDefOf.RejectInput, historical: false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -7,13 +7,13 @@ namespace ArachnaeSwarm
|
||||
public class CompProperties_AdvancedTraining : CompProperties
|
||||
{
|
||||
// 1. 用于设置固定技能等级
|
||||
public List<SkillLevelEntry> skillLevels = new List<SkillLevelEntry>();
|
||||
|
||||
// 2. 用于指定生成时立即完成的训练
|
||||
public List<TrainableDef> instantTrainables = new List<TrainableDef>();
|
||||
public List<SkillLevelEntry> skillLevels = new List<SkillLevelEntry>();
|
||||
|
||||
// 2. 用于配置训练项目
|
||||
public List<TrainableEntry> trainables = new List<TrainableEntry>();
|
||||
|
||||
// 3. 全局开关:是否阻止所有技能衰减
|
||||
public bool disableAllSkillDecay = false;
|
||||
public bool disableAllSkillDecay = false;
|
||||
|
||||
public CompProperties_AdvancedTraining()
|
||||
{
|
||||
@@ -25,10 +25,15 @@ namespace ArachnaeSwarm
|
||||
{
|
||||
public SkillDef skill;
|
||||
public int level = 0;
|
||||
// 这里的 disableDecay 字段现在是冗余的,因为我们有全局的 disableAllSkillDecay
|
||||
// 但为了兼容性或未来可能的需求,可以保留。
|
||||
// 在当前方案中,它的值将被忽略。
|
||||
public bool disableDecay = true;
|
||||
public bool disableDecay = true;
|
||||
}
|
||||
|
||||
// 新增:用于定义训练项目的条目
|
||||
public class TrainableEntry
|
||||
{
|
||||
public TrainableDef trainable;
|
||||
public bool trainInstantly = false; // 是否立即完成训练
|
||||
public bool setWanted = false; // 是否默认启用
|
||||
}
|
||||
|
||||
public class CompAdvancedTraining : ThingComp
|
||||
@@ -53,22 +58,28 @@ namespace ArachnaeSwarm
|
||||
if (skillRecord != null)
|
||||
{
|
||||
skillRecord.Level = entry.level;
|
||||
// 注意: 激情 (passion) 影响学习速度,不直接阻止衰减。
|
||||
// 实际的衰减阻止逻辑在 TrainingSystem_Patcher.cs 中处理。
|
||||
// 默认情况下,我们不改变 passion,除非有特殊需求。
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- 2. 执行瞬间训练 (只在初次生成时) ---
|
||||
if (!respawningAfterLoad && pawn.training != null && !Props.instantTrainables.NullOrEmpty())
|
||||
// --- 2. 处理训练项目 (只在初次生成时) ---
|
||||
if (!respawningAfterLoad && pawn.training != null && !Props.trainables.NullOrEmpty())
|
||||
{
|
||||
foreach (var trainable in Props.instantTrainables)
|
||||
foreach (var entry in Props.trainables)
|
||||
{
|
||||
if (trainable != null && !pawn.training.HasLearned(trainable))
|
||||
if (entry.trainable == null) continue;
|
||||
|
||||
// 2a. 立即完成训练
|
||||
if (entry.trainInstantly && !pawn.training.HasLearned(entry.trainable))
|
||||
{
|
||||
pawn.training.Train(trainable, null, complete: true);
|
||||
pawn.training.Train(entry.trainable, null, complete: true);
|
||||
}
|
||||
|
||||
// 2b. 设置为默认启用
|
||||
if (entry.setWanted)
|
||||
{
|
||||
pawn.training.SetWantedRecursive(entry.trainable, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,6 +112,12 @@
|
||||
<Compile Include="DataContracts.cs" />
|
||||
<Compile Include="CompTemperatureRuinableDamage.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Possession\Hediff_Possession.cs" />
|
||||
<Compile Include="Possession\PawnDataUtility.cs" />
|
||||
<Compile Include="Possession\CompAbilityEffect_Possess.cs" />
|
||||
<Compile Include="Possession\CompProperties_AbilityPossess.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- 自定义清理任务,删除obj文件夹中的临时文件 -->
|
||||
<Target Name="CleanDebugFiles" AfterTargets="Build">
|
||||
|
||||
44
Source/ArachnaeSwarm/Possession/CompAbilityEffect_Possess.cs
Normal file
44
Source/ArachnaeSwarm/Possession/CompAbilityEffect_Possess.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
public class CompAbilityEffect_Possess : CompAbilityEffect
|
||||
{
|
||||
public override void Apply(LocalTargetInfo target, LocalTargetInfo dest)
|
||||
{
|
||||
base.Apply(target, dest);
|
||||
|
||||
Pawn caster = this.parent.pawn;
|
||||
Pawn targetPawn = target.Pawn;
|
||||
|
||||
if (targetPawn == null || caster == null) return;
|
||||
|
||||
Log.Message($"[夺舍] 开始执行。施法者: {caster.LabelShort}, 目标: {targetPawn.LabelShort}");
|
||||
|
||||
// 步骤 1: 创建Hediff实例
|
||||
Hediff_Possession hediff = (Hediff_Possession)HediffMaker.MakeHediff(HediffDef.Named("ARA_Possession"), targetPawn);
|
||||
|
||||
// 步骤 2: 使用SplitOff(1)将施法者的一个安全、独立的副本存入容器。
|
||||
// 这是从地图上移除Pawn并将其存入容器的标准、原子性操作。
|
||||
if (hediff.GetDirectlyHeldThings().TryAdd(caster.SplitOff(1), true))
|
||||
{
|
||||
Log.Message($"[夺舍] 成功将 {caster.LabelShort} 的副本存入Hediff。");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error($"[夺舍] 无法将 {caster.LabelShort} 的副本存入Hediff。中止操作。");
|
||||
return;
|
||||
}
|
||||
|
||||
// 步骤 3: 使用原始施法者的数据覆盖目标Pawn。
|
||||
// 即使caster的stackCount变为0,其数据在当前Tick中依然可读。
|
||||
PawnDataUtility.TransferSoul(caster, targetPawn);
|
||||
|
||||
// 步骤 4: 将准备好的Hediff添加到目标身上。
|
||||
targetPawn.health.AddHediff(hediff);
|
||||
|
||||
Log.Message($"[夺舍] {targetPawn.LabelShort} (原 {caster.LabelShort}) 夺舍完成。");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using RimWorld;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
public class CompProperties_AbilityPossess : CompProperties_AbilityEffect
|
||||
{
|
||||
public CompProperties_AbilityPossess()
|
||||
{
|
||||
this.compClass = typeof(CompAbilityEffect_Possess);
|
||||
}
|
||||
}
|
||||
}
|
||||
80
Source/ArachnaeSwarm/Possession/Hediff_Possession.cs
Normal file
80
Source/ArachnaeSwarm/Possession/Hediff_Possession.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
using System.Collections.Generic;
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
public class Hediff_Possession : HediffWithComps, IThingHolder
|
||||
{
|
||||
private ThingOwner innerContainer;
|
||||
|
||||
public Hediff_Possession()
|
||||
{
|
||||
this.innerContainer = new ThingOwner<Thing>(this, false, LookMode.Deep);
|
||||
}
|
||||
|
||||
public Pawn StoredCasterPawn => innerContainer.Count > 0 ? innerContainer[0] as Pawn : null;
|
||||
|
||||
public IThingHolder ParentHolder => this.pawn;
|
||||
|
||||
public void GetChildHolders(List<IThingHolder> outChildren)
|
||||
{
|
||||
ThingOwnerUtility.AppendThingHoldersFromThings(outChildren, this.GetDirectlyHeldThings());
|
||||
}
|
||||
|
||||
public ThingOwner GetDirectlyHeldThings()
|
||||
{
|
||||
return innerContainer;
|
||||
}
|
||||
|
||||
// PostAdd现在只在游戏加载时起作用,我们不需要在这里做任何特殊操作。
|
||||
// 所有的夺舍逻辑都在CompAbilityEffect_Possess中处理了。
|
||||
|
||||
public override void Notify_PawnDied(DamageInfo? dinfo, Hediff culprit = null)
|
||||
{
|
||||
base.Notify_PawnDied(dinfo, culprit);
|
||||
|
||||
Pawn deadBody = this.pawn;
|
||||
Pawn storedCaster = this.StoredCasterPawn;
|
||||
|
||||
if (storedCaster == null)
|
||||
{
|
||||
Log.Error("Possessed pawn died, but no caster soul was found inside.");
|
||||
return;
|
||||
}
|
||||
|
||||
Log.Message($"Host {deadBody.LabelShort} died. Transferring experience back to {storedCaster.LabelShort} and ejecting.");
|
||||
PawnDataUtility.TransferSoul(deadBody, storedCaster);
|
||||
this.EjectContents();
|
||||
}
|
||||
|
||||
|
||||
public void EjectContents()
|
||||
{
|
||||
if (StoredCasterPawn == null) return;
|
||||
|
||||
// 采用更稳健的方式获取地图和位置,防止因宿主死亡导致Map为null
|
||||
Map map = this.pawn.MapHeld ?? Find.AnyPlayerHomeMap;
|
||||
if (map == null)
|
||||
{
|
||||
Log.Error("[夺舍] 无法找到一个有效的地图来重生抱脸虫。");
|
||||
return;
|
||||
}
|
||||
|
||||
IntVec3 cell = this.pawn.PositionHeld;
|
||||
if (!cell.IsValid)
|
||||
{
|
||||
cell = map.Center;
|
||||
}
|
||||
|
||||
Log.Message($"[夺舍] 准备在地图 {map.ToString()} 的位置 {cell.ToString()} 处重生 {StoredCasterPawn.LabelShort}。");
|
||||
this.innerContainer.TryDropAll(cell, map, ThingPlaceMode.Near);
|
||||
}
|
||||
|
||||
public override void ExposeData()
|
||||
{
|
||||
base.ExposeData();
|
||||
Scribe_Deep.Look(ref innerContainer, "innerContainer", this);
|
||||
}
|
||||
}
|
||||
}
|
||||
161
Source/ArachnaeSwarm/Possession/PawnDataUtility.cs
Normal file
161
Source/ArachnaeSwarm/Possession/PawnDataUtility.cs
Normal file
@@ -0,0 +1,161 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
public static class PawnDataUtility
|
||||
{
|
||||
public static void TransferSoul(Pawn soulSource, Pawn bodyTarget)
|
||||
{
|
||||
if (soulSource == null || bodyTarget == null)
|
||||
{
|
||||
Log.Error("Cannot transfer soul: source or target is null.");
|
||||
return;
|
||||
}
|
||||
|
||||
Log.Message($"Beginning soul transfer from {soulSource.LabelShort} to {bodyTarget.LabelShort}.");
|
||||
|
||||
// --- 1. Core Identity ---
|
||||
bodyTarget.Name = soulSource.Name;
|
||||
bodyTarget.story.Childhood = soulSource.story.Childhood;
|
||||
bodyTarget.story.Adulthood = soulSource.story.Adulthood;
|
||||
|
||||
if (bodyTarget.story.traits != null) bodyTarget.story.traits.allTraits.Clear();
|
||||
if (soulSource.story.traits != null)
|
||||
{
|
||||
foreach (Trait trait in soulSource.story.traits.allTraits)
|
||||
{
|
||||
bodyTarget.story.traits.GainTrait(trait);
|
||||
}
|
||||
}
|
||||
|
||||
if (bodyTarget.Faction != soulSource.Faction)
|
||||
{
|
||||
bodyTarget.SetFaction(soulSource.Faction, soulSource);
|
||||
}
|
||||
|
||||
// --- 2. Growth & Experience ---
|
||||
if (bodyTarget.skills != null) bodyTarget.skills.skills.Clear();
|
||||
if (soulSource.skills != null)
|
||||
{
|
||||
foreach (SkillRecord skill in soulSource.skills.skills)
|
||||
{
|
||||
SkillRecord newSkill = new SkillRecord(bodyTarget, skill.def)
|
||||
{
|
||||
levelInt = skill.levelInt,
|
||||
xpSinceLastLevel = skill.xpSinceLastLevel,
|
||||
passion = skill.passion
|
||||
};
|
||||
bodyTarget.skills.skills.Add(newSkill);
|
||||
}
|
||||
}
|
||||
|
||||
if (bodyTarget.records != null && soulSource.records != null)
|
||||
{
|
||||
foreach (RecordDef recordDef in DefDatabase<RecordDef>.AllDefs)
|
||||
{
|
||||
// 根据您的指示,我们不再处理时间类型的记录,以避免警告
|
||||
if (recordDef.type == RecordType.Time)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
float sourceValue = soulSource.records.GetValue(recordDef);
|
||||
bodyTarget.records.AddTo(recordDef, sourceValue - bodyTarget.records.GetValue(recordDef));
|
||||
}
|
||||
}
|
||||
|
||||
// --- 3. Mind & Settings ---
|
||||
if (bodyTarget.needs?.mood?.thoughts?.memories != null)
|
||||
{
|
||||
bodyTarget.needs.mood.thoughts.memories.Memories.Clear();
|
||||
}
|
||||
if (soulSource.needs?.mood?.thoughts?.memories != null)
|
||||
{
|
||||
foreach (Thought_Memory memory in soulSource.needs.mood.thoughts.memories.Memories)
|
||||
{
|
||||
bodyTarget.needs.mood.thoughts.memories.TryGainMemory(memory);
|
||||
}
|
||||
}
|
||||
|
||||
if (soulSource.workSettings != null && bodyTarget.workSettings != null)
|
||||
{
|
||||
bodyTarget.workSettings.EnableAndInitialize();
|
||||
foreach (WorkTypeDef workDef in DefDatabase<WorkTypeDef>.AllDefs)
|
||||
{
|
||||
// 在设置优先级之前,检查目标Pawn是否禁用了该工作类型
|
||||
if (!bodyTarget.WorkTypeIsDisabled(workDef))
|
||||
{
|
||||
bodyTarget.workSettings.SetPriority(workDef, soulSource.workSettings.GetPriority(workDef));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (soulSource.timetable != null && bodyTarget.timetable != null)
|
||||
{
|
||||
bodyTarget.timetable.times = new List<TimeAssignmentDef>(soulSource.timetable.times);
|
||||
}
|
||||
|
||||
if (soulSource.playerSettings != null && bodyTarget.playerSettings != null)
|
||||
{
|
||||
bodyTarget.playerSettings.hostilityResponse = soulSource.playerSettings.hostilityResponse;
|
||||
bodyTarget.playerSettings.medCare = soulSource.playerSettings.medCare;
|
||||
bodyTarget.playerSettings.selfTend = soulSource.playerSettings.selfTend;
|
||||
}
|
||||
|
||||
if (soulSource.outfits != null && bodyTarget.outfits != null) bodyTarget.outfits.CurrentApparelPolicy = soulSource.outfits.CurrentApparelPolicy;
|
||||
if (soulSource.drugs != null && bodyTarget.drugs != null) bodyTarget.drugs.CurrentPolicy = soulSource.drugs.CurrentPolicy;
|
||||
if (soulSource.foodRestriction != null && bodyTarget.foodRestriction != null) bodyTarget.foodRestriction.CurrentFoodPolicy = soulSource.foodRestriction.CurrentFoodPolicy;
|
||||
// Ownership is claimed on the Building, not the pawn. We can't directly transfer this.
|
||||
// if (soulSource.ownership != null && bodyTarget.ownership != null)
|
||||
// {
|
||||
// // This requires finding the bed and calling bed.SetOwner(pawn)
|
||||
// }
|
||||
|
||||
|
||||
// --- 4. DLC & Social ---
|
||||
if (ModsConfig.IdeologyActive && soulSource.ideo != null && bodyTarget.ideo != null)
|
||||
{
|
||||
bodyTarget.ideo.SetIdeo(soulSource.ideo.Ideo);
|
||||
// Can't set certainty directly, but setting the ideo resets it.
|
||||
}
|
||||
|
||||
if (ModsConfig.RoyaltyActive && soulSource.royalty != null && bodyTarget.royalty != null)
|
||||
{
|
||||
// Clear existing royalty status from the target body
|
||||
bodyTarget.royalty.AllTitlesForReading.Clear();
|
||||
|
||||
// Transfer titles
|
||||
foreach(var title in soulSource.royalty.AllTitlesForReading)
|
||||
{
|
||||
bodyTarget.royalty.SetTitle(title.faction, title.def, true, false, false);
|
||||
}
|
||||
// Transfer permits
|
||||
if(soulSource.royalty.AllFactionPermits != null)
|
||||
{
|
||||
foreach (var permit in soulSource.royalty.AllFactionPermits)
|
||||
{
|
||||
bodyTarget.royalty.AddPermit(permit.Permit, permit.Faction);
|
||||
}
|
||||
}
|
||||
// Abilities are handled by the titles and should update automatically.
|
||||
bodyTarget.royalty.UpdateAvailableAbilities();
|
||||
}
|
||||
|
||||
if (soulSource.relations != null && bodyTarget.relations != null)
|
||||
{
|
||||
bodyTarget.relations.ClearAllRelations();
|
||||
foreach (DirectPawnRelation relation in soulSource.relations.DirectRelations.Where(r => !r.def.familyByBloodRelation).ToList())
|
||||
{
|
||||
bodyTarget.relations.AddDirectRelation(relation.def, relation.otherPawn);
|
||||
}
|
||||
}
|
||||
|
||||
// --- 5. Finalization ---
|
||||
bodyTarget.Drawer.renderer.SetAllGraphicsDirty();
|
||||
|
||||
Log.Message("Soul transfer complete.");
|
||||
}
|
||||
}
|
||||
}
|
||||
787
Source/Documents/Human.xml
Normal file
787
Source/Documents/Human.xml
Normal file
@@ -0,0 +1,787 @@
|
||||
<thing Class="Pawn">
|
||||
<def>Human</def>
|
||||
<tickDelta>2</tickDelta>
|
||||
<id>Human846</id>
|
||||
<map>0</map>
|
||||
<pos>(146, 0, 131)</pos>
|
||||
<rot>1</rot>
|
||||
<faction>Faction_18</faction>
|
||||
<questTags IsNull="True" />
|
||||
<spawnedTick>0</spawnedTick>
|
||||
<despawnedTick>-1</despawnedTick>
|
||||
<beenRevealed>True</beenRevealed>
|
||||
<targetHolder>null</targetHolder>
|
||||
<lastStudiedTick>-9999999</lastStudiedTick>
|
||||
<AlienRaces_AlienComp>
|
||||
<addonVariants />
|
||||
<addonColors />
|
||||
<colorChannels>
|
||||
<keys>
|
||||
<li>base</li>
|
||||
<li>hair</li>
|
||||
<li>skin</li>
|
||||
<li>skinBase</li>
|
||||
<li>tattoo</li>
|
||||
<li>favorite</li>
|
||||
<li>ideo</li>
|
||||
<li>mech</li>
|
||||
</keys>
|
||||
<values>
|
||||
<li>
|
||||
<first>RGBA(1.000, 1.000, 1.000, 1.000)</first>
|
||||
<second>RGBA(1.000, 1.000, 1.000, 1.000)</second>
|
||||
</li>
|
||||
<li>
|
||||
<first>RGBA(0.343, 0.310, 0.288, 1.000)</first>
|
||||
</li>
|
||||
<li>
|
||||
<first>RGBA(1.000, 0.937, 0.788, 1.000)</first>
|
||||
</li>
|
||||
<li>
|
||||
<first>RGBA(1.000, 0.937, 0.788, 1.000)</first>
|
||||
</li>
|
||||
<li>
|
||||
<first>RGBA(1.000, 0.937, 0.788, 0.800)</first>
|
||||
</li>
|
||||
<li>
|
||||
<first>RGBA(0.890, 0.451, 1.000, 1.000)</first>
|
||||
<second>RGBA(0.890, 0.451, 1.000, 1.000)</second>
|
||||
</li>
|
||||
<li>
|
||||
<first>RGBA(0.600, 0.500, 0.900, 1.000)</first>
|
||||
<second>RGBA(0.549, 0.458, 0.824, 1.000)</second>
|
||||
</li>
|
||||
<li>
|
||||
<first>RGBA(0.000, 0.737, 0.847, 1.000)</first>
|
||||
</li>
|
||||
</values>
|
||||
</colorChannels>
|
||||
<colorChannelLinks>
|
||||
<keys />
|
||||
<values />
|
||||
</colorChannelLinks>
|
||||
<headVariant>0</headVariant>
|
||||
<bodyVariant>0</bodyVariant>
|
||||
<headMaskVariant>0</headMaskVariant>
|
||||
<bodyMaskVariant>0</bodyMaskVariant>
|
||||
</AlienRaces_AlienComp>
|
||||
<kindDef>Colonist</kindDef>
|
||||
<gender>Female</gender>
|
||||
<name Class="NameTriple">
|
||||
<first>Yue</first>
|
||||
<nick>Moon</nick>
|
||||
<last>Ren</last>
|
||||
</name>
|
||||
<deadlifeDustFaction>null</deadlifeDustFaction>
|
||||
<mindState>
|
||||
<meleeThreat>null</meleeThreat>
|
||||
<enemyTarget>null</enemyTarget>
|
||||
<knownExploder>null</knownExploder>
|
||||
<lastMannedThing>null</lastMannedThing>
|
||||
<droppedWeapon>null</droppedWeapon>
|
||||
<lastAttackedTarget>(0, 0, 0)</lastAttackedTarget>
|
||||
<thinkData>
|
||||
<keys />
|
||||
<values />
|
||||
</thinkData>
|
||||
<lastJobTag>Idle</lastJobTag>
|
||||
<nextApparelOptimizeTick>6673</nextApparelOptimizeTick>
|
||||
<lastEngageTargetTick>-99999</lastEngageTargetTick>
|
||||
<lastAttackTargetTick>-99999</lastAttackTargetTick>
|
||||
<canFleeIndividual>True</canFleeIndividual>
|
||||
<lastMeleeThreatHarmTick>-99999</lastMeleeThreatHarmTick>
|
||||
<duty IsNull="True" />
|
||||
<mentalStateHandler>
|
||||
<curState IsNull="True" />
|
||||
</mentalStateHandler>
|
||||
<mentalBreaker />
|
||||
<mentalFitGenerator />
|
||||
<inspirationHandler>
|
||||
<curState IsNull="True" />
|
||||
</inspirationHandler>
|
||||
<priorityWork>
|
||||
<prioritizedCell>(-1000, -1000, -1000)</prioritizedCell>
|
||||
</priorityWork>
|
||||
<lastSelfTendTick>-99999</lastSelfTendTick>
|
||||
<breachingTarget IsNull="True" />
|
||||
<babyAutoBreastfeedMoms>
|
||||
<keys />
|
||||
<values />
|
||||
</babyAutoBreastfeedMoms>
|
||||
<babyCaravanBreastfeed>
|
||||
<keys />
|
||||
<values />
|
||||
</babyCaravanBreastfeed>
|
||||
<resurrectTarget IsNull="True" />
|
||||
<lastRangedHarmTick>0</lastRangedHarmTick>
|
||||
<lastDayInteractionTick>16777</lastDayInteractionTick>
|
||||
</mindState>
|
||||
<jobs>
|
||||
<curJob>
|
||||
<commTarget>null</commTarget>
|
||||
<verbToUse>null</verbToUse>
|
||||
<bill>null</bill>
|
||||
<lord>null</lord>
|
||||
<quest>null</quest>
|
||||
<def>GotoWander</def>
|
||||
<loadID>221</loadID>
|
||||
<targetA>(156, 0, 116)</targetA>
|
||||
<targetQueueA IsNull="True" />
|
||||
<targetQueueB IsNull="True" />
|
||||
<countQueue IsNull="True" />
|
||||
<startTick>1576</startTick>
|
||||
<checkOverrideOnExpire>True</checkOverrideOnExpire>
|
||||
<placedThings IsNull="True" />
|
||||
<locomotionUrgency>Walk</locomotionUrgency>
|
||||
<jobGiverThinkTree>Humanlike</jobGiverThinkTree>
|
||||
<psyfocusTargetLast>-1</psyfocusTargetLast>
|
||||
<ability>null</ability>
|
||||
<source>null</source>
|
||||
<interactableIndex>-1</interactableIndex>
|
||||
<lastJobGiverKey>-1672709817</lastJobGiverKey>
|
||||
</curJob>
|
||||
<curDriver Class="JobDriver_Goto">
|
||||
<curToilIndex>0</curToilIndex>
|
||||
<ticksLeftThisToil>-203</ticksLeftThisToil>
|
||||
<startTick>1576</startTick>
|
||||
<locomotionUrgencySameAs>null</locomotionUrgencySameAs>
|
||||
</curDriver>
|
||||
<jobQueue>
|
||||
<jobs />
|
||||
</jobQueue>
|
||||
<formingCaravanTick>-1</formingCaravanTick>
|
||||
</jobs>
|
||||
<stances>
|
||||
<stunner>
|
||||
<showStunMote>True</showStunMote>
|
||||
<adaptationTicksLeft>
|
||||
<keys />
|
||||
<values />
|
||||
</adaptationTicksLeft>
|
||||
</stunner>
|
||||
<stagger />
|
||||
<curStance Class="Stance_Mobile" />
|
||||
</stances>
|
||||
<infectionVectors>
|
||||
<givenPrearrival>True</givenPrearrival>
|
||||
<pathways>
|
||||
<keys />
|
||||
<values />
|
||||
</pathways>
|
||||
</infectionVectors>
|
||||
<verbTracker>
|
||||
<verbs>
|
||||
<li Class="Verb_MeleeAttackDamage">
|
||||
<loadID>Thing_Human846_0_Smash</loadID>
|
||||
<currentTarget>(0, 0, 0)</currentTarget>
|
||||
<currentDestination>(0, 0, 0)</currentDestination>
|
||||
<lastShotTick>-999999</lastShotTick>
|
||||
<canHitNonTargetPawnsNow>True</canHitNonTargetPawnsNow>
|
||||
</li>
|
||||
<li Class="Verb_MeleeAttackDamage">
|
||||
<loadID>Thing_Human846_1_Smash</loadID>
|
||||
<currentTarget>(0, 0, 0)</currentTarget>
|
||||
<currentDestination>(0, 0, 0)</currentDestination>
|
||||
<lastShotTick>-999999</lastShotTick>
|
||||
<canHitNonTargetPawnsNow>True</canHitNonTargetPawnsNow>
|
||||
</li>
|
||||
<li Class="Verb_MeleeAttackDamage">
|
||||
<loadID>Thing_Human846_2_Bite</loadID>
|
||||
<currentTarget>(0, 0, 0)</currentTarget>
|
||||
<currentDestination>(0, 0, 0)</currentDestination>
|
||||
<lastShotTick>-999999</lastShotTick>
|
||||
<canHitNonTargetPawnsNow>True</canHitNonTargetPawnsNow>
|
||||
</li>
|
||||
<li Class="Verb_MeleeAttackDamage">
|
||||
<loadID>Thing_Human846_3_Smash</loadID>
|
||||
<currentTarget>(0, 0, 0)</currentTarget>
|
||||
<currentDestination>(0, 0, 0)</currentDestination>
|
||||
<lastShotTick>-999999</lastShotTick>
|
||||
<canHitNonTargetPawnsNow>True</canHitNonTargetPawnsNow>
|
||||
</li>
|
||||
</verbs>
|
||||
</verbTracker>
|
||||
<natives>
|
||||
<verbTracker>
|
||||
<verbs IsNull="True" />
|
||||
</verbTracker>
|
||||
</natives>
|
||||
<meleeVerbs>
|
||||
<curMeleeVerb>null</curMeleeVerb>
|
||||
<terrainVerbs IsNull="True" />
|
||||
</meleeVerbs>
|
||||
<rotationTracker />
|
||||
<pather>
|
||||
<nextCell>(147, 0, 130)</nextCell>
|
||||
<nextCellCostLeft>48</nextCellCostLeft>
|
||||
<nextCellCostInitial>50</nextCellCostInitial>
|
||||
<peMode>OnCell</peMode>
|
||||
<cellsUntilClamor>4</cellsUntilClamor>
|
||||
<lastEnteredCellTick>1777</lastEnteredCellTick>
|
||||
<lastMovedTick>1779</lastMovedTick>
|
||||
<destination>(156, 0, 116)</destination>
|
||||
</pather>
|
||||
<carryTracker>
|
||||
<innerContainer>
|
||||
<maxStacks>1</maxStacks>
|
||||
<innerList />
|
||||
</innerContainer>
|
||||
</carryTracker>
|
||||
<apparel>
|
||||
<wornApparel>
|
||||
<innerList>
|
||||
<li>
|
||||
<def>Apparel_Pants</def>
|
||||
<id>Apparel_Pants847</id>
|
||||
<health>130</health>
|
||||
<stackCount>1</stackCount>
|
||||
<stuff>Synthread</stuff>
|
||||
<questTags IsNull="True" />
|
||||
<despawnedTick>-1</despawnedTick>
|
||||
<quality>Normal</quality>
|
||||
<sourcePrecept>null</sourcePrecept>
|
||||
<everSeenByPlayer>True</everSeenByPlayer>
|
||||
<abilities />
|
||||
</li>
|
||||
<li>
|
||||
<def>Apparel_Parka</def>
|
||||
<id>Apparel_Parka848</id>
|
||||
<health>235</health>
|
||||
<stackCount>1</stackCount>
|
||||
<stuff>Synthread</stuff>
|
||||
<questTags IsNull="True" />
|
||||
<despawnedTick>-1</despawnedTick>
|
||||
<quality>Normal</quality>
|
||||
<sourcePrecept>null</sourcePrecept>
|
||||
<everSeenByPlayer>True</everSeenByPlayer>
|
||||
<abilities />
|
||||
</li>
|
||||
</innerList>
|
||||
</wornApparel>
|
||||
<lockedApparel IsNull="True" />
|
||||
<lastApparelWearoutTick>111</lastApparelWearoutTick>
|
||||
</apparel>
|
||||
<story>
|
||||
<bodyType>Fat</bodyType>
|
||||
<hairDef>Elisabeth</hairDef>
|
||||
<hairColor>RGBA(0.343, 0.310, 0.288, 1.000)</hairColor>
|
||||
<traits>
|
||||
<allTraits>
|
||||
<li>
|
||||
<def>SpeedOffset</def>
|
||||
<sourceGene>null</sourceGene>
|
||||
<degree>-1</degree>
|
||||
<suppressedBy>null</suppressedBy>
|
||||
</li>
|
||||
<li>
|
||||
<def>Jealous</def>
|
||||
<sourceGene>null</sourceGene>
|
||||
<suppressedBy>null</suppressedBy>
|
||||
</li>
|
||||
</allTraits>
|
||||
</traits>
|
||||
<birthLastName>Ren</birthLastName>
|
||||
<favoriteColorDef>LightPurple</favoriteColorDef>
|
||||
<headType>Female_AveragePointy</headType>
|
||||
<childhood>WarRefugee51</childhood>
|
||||
<adulthood>MedievalMinstrel95</adulthood>
|
||||
</story>
|
||||
<equipment>
|
||||
<equipment>
|
||||
<innerList />
|
||||
</equipment>
|
||||
<bondedWeapon>null</bondedWeapon>
|
||||
</equipment>
|
||||
<drafter>
|
||||
<autoUndrafter />
|
||||
</drafter>
|
||||
<ageTracker>
|
||||
<ageBiologicalTicks>139350193</ageBiologicalTicks>
|
||||
<birthAbsTicks>-297730916</birthAbsTicks>
|
||||
<growth>1</growth>
|
||||
<nextGrowthCheckTick>9223372036854775807</nextGrowthCheckTick>
|
||||
<ageReversalDemandedAtAgeTicks>141208416</ageReversalDemandedAtAgeTicks>
|
||||
</ageTracker>
|
||||
<healthTracker>
|
||||
<hediffSet>
|
||||
<hediffs />
|
||||
</hediffSet>
|
||||
<surgeryBills>
|
||||
<bills />
|
||||
</surgeryBills>
|
||||
<immunity>
|
||||
<imList />
|
||||
</immunity>
|
||||
</healthTracker>
|
||||
<records>
|
||||
<records>
|
||||
<vals>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>1840</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
</vals>
|
||||
</records>
|
||||
<battleActive>null</battleActive>
|
||||
</records>
|
||||
<inventory>
|
||||
<itemsNotForSale />
|
||||
<unpackedCaravanItems />
|
||||
<innerContainer>
|
||||
<innerList />
|
||||
</innerContainer>
|
||||
</inventory>
|
||||
<filth>
|
||||
<lastTerrainFilthDef>Filth_Sand</lastTerrainFilthDef>
|
||||
<carriedFilth>
|
||||
<li>
|
||||
<def>Filth_Sand</def>
|
||||
<id>Filth_Sand14506</id>
|
||||
<questTags IsNull="True" />
|
||||
<despawnedTick>-1</despawnedTick>
|
||||
</li>
|
||||
</carriedFilth>
|
||||
</filth>
|
||||
<roping>
|
||||
<hitchingPostInt>null</hitchingPostInt>
|
||||
<ropees />
|
||||
</roping>
|
||||
<needs>
|
||||
<needs>
|
||||
<li Class="Need_Mood">
|
||||
<def>Mood</def>
|
||||
<curLevel>0.586400032</curLevel>
|
||||
<thoughts>
|
||||
<memories>
|
||||
<memories>
|
||||
<li Class="Thought_MemorySocial">
|
||||
<def>CrashedTogether</def>
|
||||
<sourcePrecept>null</sourcePrecept>
|
||||
<otherPawn>Thing_Human849</otherPawn>
|
||||
<age>1800</age>
|
||||
<opinionOffset>25</opinionOffset>
|
||||
</li>
|
||||
<li Class="Thought_MemorySocial">
|
||||
<def>CrashedTogether</def>
|
||||
<sourcePrecept>null</sourcePrecept>
|
||||
<otherPawn>Thing_Human852</otherPawn>
|
||||
<age>1800</age>
|
||||
<opinionOffset>25</opinionOffset>
|
||||
</li>
|
||||
<li>
|
||||
<def>NewColonyOptimism</def>
|
||||
<sourcePrecept>null</sourcePrecept>
|
||||
<otherPawn>null</otherPawn>
|
||||
<age>1800</age>
|
||||
</li>
|
||||
<li Class="Thought_MemorySocialCumulative">
|
||||
<def>Chitchat</def>
|
||||
<sourcePrecept>null</sourcePrecept>
|
||||
<otherPawn>Thing_Human852</otherPawn>
|
||||
<moodPowerFactor>0.773389459</moodPowerFactor>
|
||||
<age>1650</age>
|
||||
<opinionOffset>0.510437071</opinionOffset>
|
||||
</li>
|
||||
</memories>
|
||||
</memories>
|
||||
</thoughts>
|
||||
<recentMemory>
|
||||
<lastLightTick>1711</lastLightTick>
|
||||
<lastOutdoorTick>1711</lastOutdoorTick>
|
||||
</recentMemory>
|
||||
</li>
|
||||
<li Class="Need_Food">
|
||||
<def>Food</def>
|
||||
<curLevel>0.751999915</curLevel>
|
||||
<lastNonStarvingTick>1711</lastNonStarvingTick>
|
||||
</li>
|
||||
<li Class="Need_Rest">
|
||||
<def>Rest</def>
|
||||
<curLevel>0.879273593</curLevel>
|
||||
</li>
|
||||
<li Class="Need_Joy">
|
||||
<def>Joy</def>
|
||||
<curLevel>0.513921499</curLevel>
|
||||
<tolerances>
|
||||
<vals>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
</vals>
|
||||
</tolerances>
|
||||
<bored>
|
||||
<vals>
|
||||
<li>False</li>
|
||||
<li>False</li>
|
||||
<li>False</li>
|
||||
<li>False</li>
|
||||
<li>False</li>
|
||||
<li>False</li>
|
||||
<li>False</li>
|
||||
<li>False</li>
|
||||
<li>False</li>
|
||||
<li>False</li>
|
||||
</vals>
|
||||
</bored>
|
||||
</li>
|
||||
<li Class="Need_Beauty">
|
||||
<def>Beauty</def>
|
||||
<curLevel>0.442400098</curLevel>
|
||||
</li>
|
||||
<li Class="Need_Comfort">
|
||||
<def>Comfort</def>
|
||||
<curLevel>0.47119987</curLevel>
|
||||
</li>
|
||||
<li Class="Need_Outdoors">
|
||||
<def>Outdoors</def>
|
||||
<curLevel>1</curLevel>
|
||||
</li>
|
||||
<li Class="Need_Chemical_Any">
|
||||
<def>DrugDesire</def>
|
||||
<curLevel>0.5</curLevel>
|
||||
</li>
|
||||
<li Class="Need_RoomSize">
|
||||
<def>RoomSize</def>
|
||||
<curLevel>1</curLevel>
|
||||
</li>
|
||||
</needs>
|
||||
</needs>
|
||||
<guest>
|
||||
<hostFaction>null</hostFaction>
|
||||
<slaveFaction>null</slaveFaction>
|
||||
<joinStatus>JoinAsColonist</joinStatus>
|
||||
<interactionMode>MaintainOnly</interactionMode>
|
||||
<slaveInteractionMode>NoInteraction</slaveInteractionMode>
|
||||
<spotToWaitInsteadOfEscaping>(-1000, -1000, -1000)</spotToWaitInsteadOfEscaping>
|
||||
<lastPrisonBreakTicks>-1</lastPrisonBreakTicks>
|
||||
<ideoForConversion>null</ideoForConversion>
|
||||
<recruitable>False</recruitable>
|
||||
<enabledNonExclusiveInteractions />
|
||||
<lastResistanceInteractionData IsNull="True" />
|
||||
<finalResistanceInteractionData IsNull="True" />
|
||||
</guest>
|
||||
<guilt />
|
||||
<royalty>
|
||||
<titles />
|
||||
<favor>
|
||||
<keys />
|
||||
<values />
|
||||
</favor>
|
||||
<highestTitles>
|
||||
<keys />
|
||||
<values />
|
||||
</highestTitles>
|
||||
<heirs>
|
||||
<keys />
|
||||
<values />
|
||||
</heirs>
|
||||
<permits />
|
||||
<abilities />
|
||||
</royalty>
|
||||
<social>
|
||||
<directRelations>
|
||||
<li>
|
||||
<def>ExSpouse</def>
|
||||
<otherPawn>Thing_Human849</otherPawn>
|
||||
</li>
|
||||
<li>
|
||||
<def>Parent</def>
|
||||
<otherPawn>Thing_Human853</otherPawn>
|
||||
</li>
|
||||
<li>
|
||||
<def>Parent</def>
|
||||
<otherPawn>Thing_Human857</otherPawn>
|
||||
</li>
|
||||
</directRelations>
|
||||
<virtualRelations />
|
||||
<relativeInvolvedInRescueQuest>null</relativeInvolvedInRescueQuest>
|
||||
<pregnancyApproaches>
|
||||
<keys />
|
||||
<values />
|
||||
</pregnancyApproaches>
|
||||
<romanceEnableTick>-1</romanceEnableTick>
|
||||
</social>
|
||||
<psychicEntropy>
|
||||
<limitEntropyAmount>True</limitEntropyAmount>
|
||||
</psychicEntropy>
|
||||
<shambler IsNull="True" />
|
||||
<ownership>
|
||||
<ownedBed>null</ownedBed>
|
||||
<assignedMeditationSpot>null</assignedMeditationSpot>
|
||||
<assignedGrave>null</assignedGrave>
|
||||
<assignedThrone>null</assignedThrone>
|
||||
<assignedDeathrestCasket>null</assignedDeathrestCasket>
|
||||
</ownership>
|
||||
<interactions>
|
||||
<lastInteraction>Chitchat</lastInteraction>
|
||||
<lastInteractionTime>121</lastInteractionTime>
|
||||
<lastInteractionDef>Chitchat</lastInteractionDef>
|
||||
</interactions>
|
||||
<skills>
|
||||
<skills>
|
||||
<li>
|
||||
<def>Shooting</def>
|
||||
<level>3</level>
|
||||
</li>
|
||||
<li>
|
||||
<def>Melee</def>
|
||||
<level>5</level>
|
||||
</li>
|
||||
<li>
|
||||
<def>Construction</def>
|
||||
<level>2</level>
|
||||
</li>
|
||||
<li>
|
||||
<def>Mining</def>
|
||||
<level>2</level>
|
||||
</li>
|
||||
<li>
|
||||
<def>Cooking</def>
|
||||
<level>7</level>
|
||||
</li>
|
||||
<li>
|
||||
<def>Plants</def>
|
||||
<level>1</level>
|
||||
</li>
|
||||
<li>
|
||||
<def>Animals</def>
|
||||
<level>3</level>
|
||||
<passion>Minor</passion>
|
||||
</li>
|
||||
<li>
|
||||
<def>Crafting</def>
|
||||
<level>3</level>
|
||||
</li>
|
||||
<li>
|
||||
<def>Artistic</def>
|
||||
<level>5</level>
|
||||
<passion>Minor</passion>
|
||||
</li>
|
||||
<li>
|
||||
<def>Medicine</def>
|
||||
<level>2</level>
|
||||
<passion>Minor</passion>
|
||||
</li>
|
||||
<li>
|
||||
<def>Social</def>
|
||||
<level>7</level>
|
||||
<passion>Major</passion>
|
||||
</li>
|
||||
<li>
|
||||
<def>Intellectual</def>
|
||||
</li>
|
||||
</skills>
|
||||
<lastXpSinceMidnightResetTimestamp>-1</lastXpSinceMidnightResetTimestamp>
|
||||
</skills>
|
||||
<abilities>
|
||||
<abilities />
|
||||
</abilities>
|
||||
<ideo>
|
||||
<ideo>Ideo_10</ideo>
|
||||
<previousIdeos />
|
||||
<certainty>0.605378687</certainty>
|
||||
<babyIdeoExposure IsNull="True" />
|
||||
</ideo>
|
||||
<workSettings>
|
||||
<priorities>
|
||||
<vals>
|
||||
<li>3</li>
|
||||
<li>3</li>
|
||||
<li>0</li>
|
||||
<li>3</li>
|
||||
<li>3</li>
|
||||
<li>3</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
<li>3</li>
|
||||
<li>0</li>
|
||||
<li>3</li>
|
||||
<li>0</li>
|
||||
<li>0</li>
|
||||
</vals>
|
||||
</priorities>
|
||||
</workSettings>
|
||||
<trader IsNull="True" />
|
||||
<outfits>
|
||||
<curOutfit>ApparelPolicy_任意_1</curOutfit>
|
||||
<overrideHandler>
|
||||
<forcedAps />
|
||||
</overrideHandler>
|
||||
</outfits>
|
||||
<drugs>
|
||||
<curAssignedDrugs>DrugPolicy_社交成瘾品_1</curAssignedDrugs>
|
||||
<drugTakeRecords />
|
||||
</drugs>
|
||||
<foodRestriction>
|
||||
<curRestriction>null</curRestriction>
|
||||
<allowedBabyFoodTypes IsNull="True" />
|
||||
</foodRestriction>
|
||||
<timetable>
|
||||
<times>
|
||||
<li>Sleep</li>
|
||||
<li>Sleep</li>
|
||||
<li>Sleep</li>
|
||||
<li>Sleep</li>
|
||||
<li>Sleep</li>
|
||||
<li>Sleep</li>
|
||||
<li>Anything</li>
|
||||
<li>Anything</li>
|
||||
<li>Anything</li>
|
||||
<li>Anything</li>
|
||||
<li>Anything</li>
|
||||
<li>Anything</li>
|
||||
<li>Anything</li>
|
||||
<li>Anything</li>
|
||||
<li>Anything</li>
|
||||
<li>Anything</li>
|
||||
<li>Anything</li>
|
||||
<li>Anything</li>
|
||||
<li>Anything</li>
|
||||
<li>Anything</li>
|
||||
<li>Anything</li>
|
||||
<li>Anything</li>
|
||||
<li>Sleep</li>
|
||||
<li>Sleep</li>
|
||||
</times>
|
||||
</timetable>
|
||||
<playerSettings>
|
||||
<medCare>Best</medCare>
|
||||
<allowedAreas>
|
||||
<keys />
|
||||
<values />
|
||||
</allowedAreas>
|
||||
<master>null</master>
|
||||
<displayOrder>1</displayOrder>
|
||||
</playerSettings>
|
||||
<training IsNull="True" />
|
||||
<style>
|
||||
<beardDef>NoBeard</beardDef>
|
||||
<faceTattoo>NoTattoo_Face</faceTattoo>
|
||||
<bodyTattoo>NoTattoo_Body</bodyTattoo>
|
||||
</style>
|
||||
<styleObserver />
|
||||
<connections>
|
||||
<connectedThings />
|
||||
</connections>
|
||||
<inventoryStock>
|
||||
<stockEntries>
|
||||
<keys />
|
||||
<values />
|
||||
</stockEntries>
|
||||
</inventoryStock>
|
||||
<treeSightings>
|
||||
<miniTreeSightings />
|
||||
<fullTreeSightings />
|
||||
<superTreeSightings />
|
||||
</treeSightings>
|
||||
<thinker />
|
||||
<mechanitor IsNull="True" />
|
||||
<genes>
|
||||
<xenogenes />
|
||||
<endogenes>
|
||||
<li>
|
||||
<def>Skin_Melanin3</def>
|
||||
<pawn>Thing_Human846</pawn>
|
||||
<overriddenByGene>null</overriddenByGene>
|
||||
<loadID>132</loadID>
|
||||
</li>
|
||||
<li>
|
||||
<def>Hair_MidBlack</def>
|
||||
<pawn>Thing_Human846</pawn>
|
||||
<overriddenByGene>null</overriddenByGene>
|
||||
<loadID>133</loadID>
|
||||
</li>
|
||||
</endogenes>
|
||||
<xenotype>Baseliner</xenotype>
|
||||
</genes>
|
||||
<learning IsNull="True" />
|
||||
<reading>
|
||||
<curAssignment>null</curAssignment>
|
||||
</reading>
|
||||
<creepjoiner IsNull="True" />
|
||||
<duplicate />
|
||||
<flight />
|
||||
</thing>
|
||||
188
Source/Documents/PawnStorageInHediff_Analysis.md
Normal file
188
Source/Documents/PawnStorageInHediff_Analysis.md
Normal file
@@ -0,0 +1,188 @@
|
||||
# RimWorld Modding: 利用Hediff存储Pawn的深度解析
|
||||
|
||||
在RimWorld的Mod开发中,有时需要将一个`Pawn`(人物、动物等)从游戏世界中临时移除,并将其数据完整保存起来,之后再释放回游戏中。一个非常精妙且强大的实现方式就是让`Hediff`(健康效果)扮演一个“容器”的角色。
|
||||
|
||||
本文档将以`HediffAbility_PaintedSkin`为例,深入剖析其实现`Pawn`存储的核心机制。
|
||||
|
||||
## 核心概念
|
||||
|
||||
该功能主要依赖于RimWorld框架中的两个核心组件:
|
||||
|
||||
1. **`IThingHolder`接口**: 一个对象(如建筑、Hediff、Pawn的装备栏等)如果实现了这个接口,就等于向游戏声明:“我是一个可以容纳其他物品(`Thing`)的容器”。
|
||||
2. **`ThingOwner`类**: 这是实现存储功能的“袋子”。它是一个专门用于管理一组`Thing`对象的集合,并负责处理这些物品的保存、加载和所有权关系。
|
||||
|
||||
## 案例分析: `HediffAbility_PaintedSkin`
|
||||
|
||||
以下是`HediffAbility_PaintedSkin`的完整源代码,它完美地展示了如何利用`Hediff`来存储一个`Pawn`。
|
||||
|
||||
```csharp
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
using Verse.AI;
|
||||
using Verse.Sound;
|
||||
|
||||
namespace RigorMortis
|
||||
{
|
||||
public class HediffAbility_PaintedSkin : HediffWithComps, IHediffAbility, IThingHolder
|
||||
{
|
||||
// 1. 核心存储容器
|
||||
protected ThingOwner innerContainer;
|
||||
|
||||
private CompYinAndMalevolent compYin;
|
||||
public Pawn victim;
|
||||
|
||||
// 构造函数:初始化容器
|
||||
public HediffAbility_PaintedSkin()
|
||||
{
|
||||
// 'this'表示容器的所有者是当前Hediff实例
|
||||
// 'LookMode.Deep'是关键,确保能完整保存Pawn的所有数据
|
||||
this.innerContainer = new ThingOwner<Thing>(this, false, LookMode.Deep, true);
|
||||
}
|
||||
|
||||
// --- IThingHolder 接口实现 ---
|
||||
|
||||
public IThingHolder ParentHolder
|
||||
{
|
||||
get
|
||||
{
|
||||
// 对于Hediff来说,它的父容器就是持有它的Pawn
|
||||
return this.pawn;
|
||||
}
|
||||
}
|
||||
|
||||
public void GetChildHolders(List<IThingHolder> outChildren)
|
||||
{
|
||||
ThingOwnerUtility.AppendThingHoldersFromThings(outChildren, this.GetDirectlyHeldThings());
|
||||
}
|
||||
|
||||
|
||||
|
||||
public ThingOwner GetDirectlyHeldThings()
|
||||
{
|
||||
return this.innerContainer;
|
||||
}
|
||||
|
||||
// --- 容器内容访问 ---
|
||||
|
||||
public Thing ContainedThing
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.innerContainer.Count > 0 ? this.innerContainer[0] : null;
|
||||
}
|
||||
}
|
||||
|
||||
public Pawn Zombie
|
||||
{
|
||||
get
|
||||
{
|
||||
// 提供一个便捷的属性来访问被存储的Pawn
|
||||
return this.ContainedThing as Pawn;
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasAnyContents
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.innerContainer.Count > 0;
|
||||
}
|
||||
}
|
||||
|
||||
// --- 存入/取出逻辑 ---
|
||||
|
||||
public virtual bool Accepts(Thing thing)
|
||||
{
|
||||
return this.innerContainer.CanAcceptAnyOf(thing, true);
|
||||
}
|
||||
|
||||
public virtual bool TryAcceptThing(Thing thing, bool allowSpecialEffects = true)
|
||||
{
|
||||
if (!this.Accepts(thing))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool flag;
|
||||
if (thing.holdingOwner != null)
|
||||
{
|
||||
// 将Pawn从当前持有者(通常是地图)转移到我们的容器中
|
||||
thing.holdingOwner.TryTransferToContainer(thing, this.innerContainer, thing.stackCount, true);
|
||||
flag = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果Pawn没有持有者(例如是新生成的),直接添加
|
||||
flag = this.innerContainer.TryAdd(thing, true);
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
public virtual void EjectContents()
|
||||
{
|
||||
// 决定在何处释放Pawn
|
||||
Map map = this.pawn.MapHeld ?? Find.AnyPlayerHomeMap;
|
||||
IntVec3 cell = (this.pawn.Spawned || (this.pawn.Corpse != null && this.pawn.Corpse.Spawned)) ? this.pawn.PositionHeld : ((this.pawn.CarriedBy != null) ? this.pawn.CarriedBy.PositionHeld : map.Center);
|
||||
|
||||
// 将容器内的所有东西(即被存储的Pawn)扔到地图上
|
||||
this.innerContainer.TryDropAll(cell, map, ThingPlaceMode.Direct, null, null, true);
|
||||
}
|
||||
|
||||
// --- 存档/读档 ---
|
||||
|
||||
public override void ExposeData()
|
||||
{
|
||||
base.ExposeData();
|
||||
Scribe_References.Look<Pawn>(ref this.victim, "victim", false);
|
||||
|
||||
// 2. 深度保存容器内容
|
||||
// 'Scribe_Deep.Look' 会序列化容器内的Pawn的所有数据
|
||||
Scribe_Deep.Look<ThingOwner>(ref this.innerContainer, "innerContainer", new object[]
|
||||
{
|
||||
this
|
||||
});
|
||||
|
||||
// 兼容性处理:确保旧存档在加载后也能正确初始化容器
|
||||
if (Scribe.mode == LoadSaveMode.PostLoadInit)
|
||||
{
|
||||
if (this.innerContainer == null)
|
||||
{
|
||||
this.innerContainer = new ThingOwner<Thing>(this, false, LookMode.Deep, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- 其他逻辑 ---
|
||||
// (为了简洁,此处省略了PostTick, End, AbsolutelyKill等与存储机制非直接相关的代码)
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 机制剖析
|
||||
|
||||
### 1. 声明容器身份 (`IThingHolder`)
|
||||
|
||||
通过在类声明中加入 `IThingHolder`,`HediffAbility_PaintedSkin` 就获得了“容器”的资格。这要求它必须实现接口定义的属性和方法,如 `ParentHolder` 和 `GetDirectlyHeldThings()`。`GetDirectlyHeldThings()` 方法必须返回真正的存储实例,也就是我们的 `innerContainer`。
|
||||
|
||||
### 2. 初始化存储核心 (`ThingOwner`)
|
||||
|
||||
在构造函数中,我们创建了一个 `ThingOwner` 实例。这里的关键在于 `LookMode.Deep` 参数。
|
||||
|
||||
* `LookMode.Value`: 只保存简单值类型(如int, float, string)。
|
||||
* `LookMode.Reference`: 只保存一个对物体的引用ID。加载时,游戏会尝试在世界中找到这个ID对应的物体。如果物体已被销毁,引用会丢失。**这不适用于存储Pawn**,因为Pawn在被存入容器时已经从世界中移除了。
|
||||
* **`LookMode.Deep`**: 这才是我们的选择。它告诉序列化系统:“请将这个物体(`Pawn`)的所有数据——健康、技能、装备、Hediff、人际关系、思想等等——完完整整地打包保存起来。” 当游戏加载时,它会用这些数据重建一个一模一样的`Pawn`实例。
|
||||
|
||||
### 3. 序列化 (`ExposeData`)
|
||||
|
||||
`ExposeData` 方法是RimWorld存档机制的核心。
|
||||
|
||||
* `Scribe_Deep.Look<ThingOwner>(ref this.innerContainer, ...)`: 这行代码是魔法发生的地方。当游戏保存时,`Scribe_Deep` 会深入到 `innerContainer` 内部,并因为我们之前设置了 `LookMode.Deep`,它会对容器里的每一个 `Pawn` 进行递归式的深度保存。
|
||||
* 当游戏加载时,`Scribe_Deep` 会读取存档中的数据,重建 `innerContainer`,并利用深度保存的数据重建一个与存入时状态完全一致的 `Pawn`。
|
||||
|
||||
## 总结
|
||||
|
||||
通过实现 `IThingHolder` 接口并利用一个配置为 `LookMode.Deep` 的 `ThingOwner` 容器,我们可以将一个 `Hediff` 转变为一个功能强大的、能够随宿主移动的“Pawn胶囊”。这个“胶囊”可以安全地携带一个`Pawn`穿越存档的海洋,确保其数据的完整性和一致性。
|
||||
|
||||
这项技术是实现诸如吞噬、俘获、传送、特殊休眠仓等高级Mod功能的基石。
|
||||
618
Source/Documents/Possession_Implementation_Guide.md
Normal file
618
Source/Documents/Possession_Implementation_Guide.md
Normal file
@@ -0,0 +1,618 @@
|
||||
# “抱脸虫夺舍”技能实现说明书 (V3 - 最终版)
|
||||
|
||||
## 1. 功能概述
|
||||
|
||||
本功能实现了一个名为“阿拉克涅寄生”的特殊技能,允许一个特定的“阿拉克涅原虫”(抱脸虫)Pawn将自己的意识(灵魂)注入另一个生物的身体,从而完全占据并控制它。
|
||||
|
||||
**核心玩法循环:**
|
||||
1. **夺舍**: 抱脸虫使用技能后,其物理实体消失,其“灵魂”(名字、背景、技能、特性等)将完全覆盖目标的身体。
|
||||
2. **成长**: 玩家将控制这个新的身体进行游戏,所有获得的经验、技能和记忆都将积累在这个身体上。
|
||||
3. **重生**: 当被夺舍的身体死亡时,抱脸虫的灵魂会带着所有新的成长,从尸体中“重生”,变回一个独立的、更强大的阿拉克涅原虫Pawn,准备寻找下一个宿主。
|
||||
|
||||
这是一个高风险、高回报的玩法,允许玩家以一种独特的方式延续一个核心角色的“生命”和成长。
|
||||
|
||||
## 2. 实现流程
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[抱脸虫] -- 使用“阿拉克涅寄生”技能 --> B(目标Pawn);
|
||||
B -- 添加 Hediff_Possession --> C{执行灵魂覆盖};
|
||||
C -- 1. 存储抱脸虫Pawn的完整数据 --> D[Hediff容器];
|
||||
C -- 2. 将抱脸虫的“灵魂”数据覆盖到目标身上 --> E[被夺舍的身体];
|
||||
D -- 3. 抱脸虫物理实体消失 --> F([Vanish]);
|
||||
E -- 玩家控制,积累经验和记忆 --> E;
|
||||
E -- 受到致命伤害 --> G{身体死亡};
|
||||
G -- 触发Hediff.Notify_PawnDied --> H{反向同步成长};
|
||||
D -- 获取存储的抱脸虫数据 --> H;
|
||||
E -- 获取身体上的成长数据 --> H;
|
||||
H -- 将成长更新到抱脸虫数据上 --> I[更新后的抱脸虫];
|
||||
I -- 从Hediff中释放 --> J(更强大的抱脸虫重生);
|
||||
J -- 等待下一次夺舍 --> A;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 代码详解
|
||||
|
||||
### 3.1 `CompAbilityEffect_Possess.cs` - 技能效果的起点
|
||||
|
||||
这是技能被使用时第一个被调用的C#文件。它的职责是创建`Hediff_Possession`并将其附加到目标身上,从而启动整个夺舍流程。
|
||||
## 3. 最终数据迁移规范 (Final Data Transfer Specification)
|
||||
|
||||
通过对真实存档文件 (`Human.xml`) 的深度分析,我们最终确定了“灵魂”与“肉体”的数据边界。`PawnDataUtility.TransferSoul` 方法将严格遵循以下规范进行数据迁移:
|
||||
|
||||
### 3.1 必须复制的“灵魂”数据
|
||||
|
||||
这些数据定义了Pawn的身份、经历、思想和核心能力,将**完全从抱脸虫(源)复制到宿主(目标)**。
|
||||
|
||||
- **核心身份 (`Name`, `Story`, `Faction`)**:
|
||||
- `Name`: 姓名与昵称。
|
||||
- `Story`: 童年和成年背景 (`Childhood`, `Adulthood`)。
|
||||
- `Traits`: 所有特性。
|
||||
- `Faction`: 所属阵营。
|
||||
- **成长与经历 (`Skills`, `Records`)**:
|
||||
- `Skills`: 所有技能的等级、经验和热情。
|
||||
- `Records`: 全部生平记录 (如击杀数、建造数等)。
|
||||
- **思想与设定 (`Needs`, `WorkSettings`, etc.)**:
|
||||
- `Needs`: 主要是指`thoughts.memories` (思想和记忆)。
|
||||
- `WorkSettings`: 工作优先级。
|
||||
- `Timetable`: 时间表。
|
||||
- `PlayerSettings`: 玩家设定 (如医疗策略)。
|
||||
- `Ownership`: 对床、王座等的所有权。
|
||||
- `Outfits` & `Drugs`: 穿着和药物策略。
|
||||
- `FoodRestriction`: 食物策略。
|
||||
- **DLC核心数据 (`Ideo`, `Royalty`)**:
|
||||
- `Ideo`: 完整的信仰体系。
|
||||
- `Royalty`: 完整的贵族系统,包括头衔、恩惠、许可、灵能和相关技能 (`abilities`)。
|
||||
- **社交 (`Relations`)**:
|
||||
- 将采用**简化处理**:清空目标的旧关系,然后只复制源的**非亲属**直接关系 (如朋友、对手、爱人)。这可以避免破坏家族树。
|
||||
|
||||
### 3.2 必须保留的“肉体”数据
|
||||
|
||||
这些数据属于物理身体的范畴,在夺舍过程中将**完全保留宿主原有的数据**,不进行任何复制。
|
||||
|
||||
- **健康与生理 (`Health`, `Age`)**:
|
||||
- `Health`: 所有伤口、疤痕、疾病和植入物。
|
||||
- `Age`: 生物年龄和时间年龄。
|
||||
- **外观与基因 (`Style`, `Genes`, `BodyType`)**:
|
||||
- `Style`: 发型、胡须、纹身。
|
||||
- `Genes`: 所有内生和异种基因。
|
||||
- `BodyType`, `HeadType`, `HairColor`: 身体类型、头型和发色。
|
||||
- **装备与物品 (`Apparel`, `Equipment`, `Inventory`)**:
|
||||
- `Apparel`: 身上穿着的衣物。
|
||||
- `Equipment`: 手中持有的装备。
|
||||
- `Inventory`: 物品栏中的物品。
|
||||
- **物理状态 (`Position`, `Stances`, `Pather`)**:
|
||||
- Pawn在世界中的位置、姿态和寻路信息。
|
||||
|
||||
---
|
||||
|
||||
```csharp
|
||||
// 路径: Source/ArachnaeSwarm/Possession/CompAbilityEffect_Possess.cs
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
|
||||
namespace ArachnaeSwarm.Possession
|
||||
{
|
||||
// 继承自CompAbilityEffect,这是所有技能效果组件的基类
|
||||
public class CompAbilityEffect_Possess : CompAbilityEffect
|
||||
{
|
||||
// 当技能成功施放时,游戏会调用这个Apply方法
|
||||
public override void Apply(LocalTargetInfo target, LocalTargetInfo dest)
|
||||
{
|
||||
base.Apply(target, dest); // 调用基类方法,确保标准流程执行
|
||||
|
||||
// 获取施法者 (我们的抱脸虫)
|
||||
Pawn caster = this.parent.pawn;
|
||||
// 获取目标Pawn
|
||||
Pawn targetPawn = target.Pawn;
|
||||
|
||||
// 安全检查:如果目标不是一个Pawn,则直接返回
|
||||
if (targetPawn == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: 在此可以添加更多的限制条件,例如:
|
||||
// 1. 不能夺舍机械体
|
||||
// if (targetPawn.RaceProps.IsMechanoid) { ... }
|
||||
// 2. 不能夺舍已经被夺舍的目标
|
||||
// if (targetPawn.health.hediffSet.HasHediff(HediffDef.Named("ARA_Possession"))) { ... }
|
||||
|
||||
// 步骤1: 创建Hediff实例
|
||||
// HediffMaker.MakeHediff会根据XML定义创建一个新的Hediff对象
|
||||
Hediff_Possession hediff = (Hediff_Possession)HediffMaker.MakeHediff(HediffDef.Named("ARA_Possession"), targetPawn);
|
||||
|
||||
// 步骤2: 注入施法者灵魂
|
||||
// 在Hediff被正式添加到目标身上之前,将施法者的引用传递进去。
|
||||
// 这是关键一步,确保Hediff在执行PostAdd逻辑时能知道谁是施法者。
|
||||
hediff.SetCaster(caster);
|
||||
|
||||
// 步骤3: 将Hediff添加到目标身上
|
||||
// 这会触发Hediff_Possession类中的PostAdd方法,从而启动真正的夺舍逻辑。
|
||||
targetPawn.health.AddHediff(hediff);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 `Hediff_Possession.cs` - 夺舍与重生的核心
|
||||
|
||||
这个Hediff是整个功能的核心。它作为“灵魂容器”,负责存储抱脸虫的本体,并在恰当的时机执行“夺舍”和“重生”的逻辑。
|
||||
|
||||
```csharp
|
||||
// 路径: Source/ArachnaeSwarm/Possession/Hediff_Possession.cs
|
||||
using System.Collections.Generic;
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
|
||||
namespace ArachnaeSwarm.Possession
|
||||
{
|
||||
// 继承HediffWithComps以支持组件,并实现IThingHolder接口来表明自己是容器
|
||||
public class Hediff_Possession : HediffWithComps, IThingHolder
|
||||
{
|
||||
// --- 核心字段 ---
|
||||
private ThingOwner innerContainer; // 实际存储灵魂(抱脸虫Pawn)的容器
|
||||
private Pawn originalCaster; // 临时保存对原始施法者的引用
|
||||
|
||||
// --- 构造与属性 ---
|
||||
public Hediff_Possession()
|
||||
{
|
||||
// 初始化容器。LookMode.Deep是关键,它能确保Pawn的所有数据都被完整保存。
|
||||
this.innerContainer = new ThingOwner<Thing>(this, false, LookMode.Deep);
|
||||
}
|
||||
|
||||
// 提供一个方便的只读属性来获取容器中存储的Pawn
|
||||
public Pawn StoredCasterPawn => innerContainer.Count > 0 ? innerContainer[0] as Pawn : null;
|
||||
|
||||
// --- IThingHolder 接口实现 ---
|
||||
public IThingHolder ParentHolder => this.pawn; // 容器的父级就是持有该Hediff的Pawn
|
||||
|
||||
public void GetChildHolders(List<IThingHolder> outChildren)
|
||||
{
|
||||
ThingOwnerUtility.AppendThingHoldersFromThings(outChildren, this.GetDirectlyHeldThings());
|
||||
}
|
||||
|
||||
public ThingOwner GetDirectlyHeldThings()
|
||||
{
|
||||
return innerContainer;
|
||||
}
|
||||
|
||||
// --- 核心逻辑 ---
|
||||
|
||||
// 当Hediff被成功添加到目标身上后,此方法被自动调用
|
||||
public override void PostAdd(DamageInfo? dinfo)
|
||||
{
|
||||
base.PostAdd(dinfo);
|
||||
|
||||
if (this.originalCaster == null)
|
||||
{
|
||||
Log.Error("Hediff_Possession was added without an original caster.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. 存储灵魂:将施法者的Pawn对象完整地存入容器
|
||||
this.innerContainer.TryAdd(this.originalCaster);
|
||||
|
||||
// 2. 灵魂覆盖:调用工具类,执行数据迁移
|
||||
PawnDataUtility.TransferSoul(this.originalCaster, this.pawn);
|
||||
|
||||
// 3. 销毁施法者的物理实体,因为它现在“活”在目标的身体里了
|
||||
if (!this.originalCaster.Destroyed)
|
||||
{
|
||||
this.originalCaster.Destroy(DestroyMode.Vanish);
|
||||
}
|
||||
|
||||
Log.Message($"{this.pawn.LabelShort} has been possessed by {StoredCasterPawn.LabelShort}!");
|
||||
}
|
||||
|
||||
// 当持有此Hediff的Pawn(即宿主)死亡时,此方法被自动调用
|
||||
public override void Notify_PawnDied(DamageInfo? dinfo, Hediff culprit = null)
|
||||
{
|
||||
base.Notify_PawnDied(dinfo, culprit);
|
||||
|
||||
Pawn deadBody = this.pawn;
|
||||
Pawn storedCaster = this.StoredCasterPawn;
|
||||
|
||||
if (storedCaster == null)
|
||||
{
|
||||
Log.Error("Possessed pawn died, but no caster soul was found inside.");
|
||||
return;
|
||||
}
|
||||
|
||||
Log.Message($"Host {deadBody.LabelShort} died. Transferring experience back to {storedCaster.LabelShort} and ejecting.");
|
||||
|
||||
// 1. 灵魂更新:反向调用工具类,将宿主身体上的成长同步回抱脸虫的灵魂
|
||||
PawnDataUtility.TransferSoul(deadBody, storedCaster);
|
||||
|
||||
// 2. 重生:将更新后的抱脸虫灵魂从容器中释放到地图上
|
||||
this.EjectContents();
|
||||
}
|
||||
|
||||
// --- 公共方法 ---
|
||||
|
||||
// 由CompAbilityEffect调用,用于在添加Hediff前设置施法者
|
||||
public void SetCaster(Pawn caster)
|
||||
{
|
||||
this.originalCaster = caster;
|
||||
}
|
||||
|
||||
// 将容器内的东西(抱脸虫)扔到地图上
|
||||
public void EjectContents()
|
||||
{
|
||||
if (StoredCasterPawn != null)
|
||||
{
|
||||
this.innerContainer.TryDropAll(this.pawn.Position, this.pawn.Map, ThingPlaceMode.Near);
|
||||
}
|
||||
}
|
||||
|
||||
// --- 存档/读档 ---
|
||||
public override void ExposeData()
|
||||
{
|
||||
base.ExposeData();
|
||||
// Scribe_Deep是关键,确保容器内的Pawn被深度保存
|
||||
Scribe_Deep.Look(ref innerContainer, "innerContainer", this);
|
||||
// 保存对原始施法者的引用(虽然它很快会被销毁,但以防万一)
|
||||
Scribe_References.Look(ref originalCaster, "originalCaster");
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 `PawnDataUtility.cs` - “灵魂”数据迁移的执行者
|
||||
|
||||
这是一个静态工具类,集中处理所有与Pawn数据复制相关的复杂逻辑,使得其他部分的代码更整洁。
|
||||
|
||||
```csharp
|
||||
// 路径: Source/ArachnaeSwarm/Possession/PawnDataUtility.cs
|
||||
using System.Collections.Generic;
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
|
||||
namespace ArachnaeSwarm.Possession
|
||||
{
|
||||
public static class PawnDataUtility
|
||||
{
|
||||
// 核心方法:将soulSource的“灵魂”数据转移到bodyTarget上
|
||||
public static void TransferSoul(Pawn soulSource, Pawn bodyTarget)
|
||||
{
|
||||
if (soulSource == null || bodyTarget == null)
|
||||
{
|
||||
Log.Error("Cannot transfer soul: source or target is null.");
|
||||
return;
|
||||
}
|
||||
|
||||
Log.Message($"Beginning soul transfer from {soulSource.LabelShort} to {bodyTarget.LabelShort}.");
|
||||
|
||||
// --- 1. 核心身份数据 ---
|
||||
|
||||
// 姓名
|
||||
bodyTarget.Name = soulSource.Name;
|
||||
|
||||
// 故事 (背景和特性)
|
||||
bodyTarget.story.Childhood = soulSource.story.Childhood;
|
||||
bodyTarget.story.Adulthood = soulSource.story.Adulthood;
|
||||
// 先清空目标的所有特性,再逐一添加源的特性
|
||||
bodyTarget.story.traits.allTraits.Clear();
|
||||
foreach (Trait trait in soulSource.story.traits.allTraits)
|
||||
{
|
||||
bodyTarget.story.traits.GainTrait(trait);
|
||||
}
|
||||
|
||||
// 技能
|
||||
// 同样地,清空后逐一添加
|
||||
bodyTarget.skills.skills.Clear();
|
||||
foreach (SkillRecord skill in soulSource.skills.skills)
|
||||
{
|
||||
SkillRecord newSkill = new SkillRecord(bodyTarget, skill.def)
|
||||
{
|
||||
levelInt = skill.levelInt,
|
||||
xpSinceLastLevel = skill.xpSinceLastLevel,
|
||||
passion = skill.passion
|
||||
};
|
||||
bodyTarget.skills.skills.Add(newSkill);
|
||||
}
|
||||
|
||||
// 阵营
|
||||
if (bodyTarget.Faction != soulSource.Faction)
|
||||
{
|
||||
bodyTarget.SetFaction(soulSource.Faction, soulSource);
|
||||
}
|
||||
|
||||
// --- 2. 思想、社交和设定 ---
|
||||
|
||||
// 思想和记忆
|
||||
// 清空目标的记忆,然后复制源的记忆
|
||||
if (bodyTarget.needs.mood?.thoughts?.memories != null)
|
||||
{
|
||||
bodyTarget.needs.mood.thoughts.memories.Memories.Clear();
|
||||
}
|
||||
if (soulSource.needs.mood?.thoughts?.memories != null)
|
||||
{
|
||||
foreach (Thought_Memory memory in soulSource.needs.mood.thoughts.memories.Memories)
|
||||
{
|
||||
bodyTarget.needs.mood.thoughts.memories.TryGainMemory(memory);
|
||||
}
|
||||
}
|
||||
|
||||
// 工作设置
|
||||
if (soulSource.workSettings != null && bodyTarget.workSettings != null)
|
||||
{
|
||||
bodyTarget.workSettings.EnableAndInitialize();
|
||||
foreach (WorkTypeDef workDef in DefDatabase<WorkTypeDef>.AllDefs)
|
||||
{
|
||||
bodyTarget.workSettings.SetPriority(workDef, soulSource.workSettings.GetPriority(workDef));
|
||||
}
|
||||
}
|
||||
|
||||
// 时间表
|
||||
if (soulSource.timetable != null && bodyTarget.timetable != null)
|
||||
{
|
||||
bodyTarget.timetable.times = new List<TimeAssignmentDef>(soulSource.timetable.times);
|
||||
}
|
||||
|
||||
// 社交关系 (简化处理)
|
||||
// 警告: 直接复制关系可能很危险,这里采用清空再添加直接关系的方式
|
||||
if (soulSource.relations != null && bodyTarget.relations != null)
|
||||
{
|
||||
bodyTarget.relations.ClearAllRelations();
|
||||
foreach (DirectPawnRelation relation in soulSource.relations.DirectRelations)
|
||||
{
|
||||
bodyTarget.relations.AddDirectRelation(relation.def, relation.otherPawn);
|
||||
}
|
||||
}
|
||||
|
||||
// 访客/囚犯状态
|
||||
if (soulSource.guest != null && bodyTarget.guest != null)
|
||||
{
|
||||
// 使用游戏提供的标准方法来设置,而不是直接赋值
|
||||
bodyTarget.guest.SetGuestStatus(soulSource.guest.HostFaction, soulSource.guest.GuestStatus);
|
||||
if (soulSource.guest.IsPrisoner)
|
||||
{
|
||||
bodyTarget.guest.SetExclusiveInteraction(soulSource.guest.ExclusiveInteractionMode);
|
||||
}
|
||||
bodyTarget.guest.joinStatus = soulSource.guest.joinStatus;
|
||||
}
|
||||
|
||||
// --- 3. 收尾工作 ---
|
||||
|
||||
// 强制刷新Pawn的渲染缓存,确保外观(如名字)能立刻更新
|
||||
bodyTarget.Drawer.renderer.SetAllGraphicsDirty();
|
||||
|
||||
Log.Message("Soul transfer complete.");
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.4 `CompProperties_AbilityPossess.cs` - 技能属性类
|
||||
|
||||
这是一个简单的属性类,用于将我们的技能效果组件(`CompAbilityEffect_Possess`)连接到XML定义上。
|
||||
|
||||
```csharp
|
||||
// 路径: Source/ArachnaeSwarm/Possession/CompProperties_AbilityPossess.cs
|
||||
using RimWorld;
|
||||
|
||||
namespace ArachnaeSwarm.Possession
|
||||
{
|
||||
// CompProperties类用于在XML中配置Comp组件的参数
|
||||
public class CompProperties_AbilityPossess : CompProperties_AbilityEffect
|
||||
{
|
||||
public CompProperties_AbilityPossess()
|
||||
{
|
||||
// 将这个属性类与我们的技能效果实现类关联起来
|
||||
this.compClass = typeof(CompAbilityEffect_Possess);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. XML 定义详解
|
||||
|
||||
### 4.1 `ARA_Hediffs_Possession.xml` - 定义Hediff
|
||||
|
||||
```xml
|
||||
<!-- 路径: 1.6/1.6/Defs/HediffDefs/ARA_Hediffs_Possession.xml -->
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Defs>
|
||||
|
||||
<HediffDef>
|
||||
<!-- 内部唯一的定义名 -->
|
||||
<defName>ARA_Possession</defName>
|
||||
<!-- 游戏中显示的标签 -->
|
||||
<label>阿拉克涅原虫寄生</label>
|
||||
<!-- 鼠标悬浮时的描述 -->
|
||||
<description>这个生物的身体正被另一个实体所控制。</description>
|
||||
<!-- 关键:将这个XML定义与我们的C#实现类关联起来 -->
|
||||
<hediffClass>ArachnaeSwarm.Possession.Hediff_Possession</hediffClass>
|
||||
<!-- 其他标准Hediff属性 -->
|
||||
<isBad>false</isBad>
|
||||
<scenarioCanAdd>false</scenarioCanAdd>
|
||||
<maxSeverity>1.0</maxSeverity>
|
||||
<stages>
|
||||
<li>
|
||||
<label>被寄生</label>
|
||||
</li>
|
||||
</stages>
|
||||
</HediffDef>
|
||||
|
||||
</Defs>
|
||||
```
|
||||
|
||||
### 4.2 `ARA_Possession_Defs.xml` - 定义技能、种族和身体
|
||||
|
||||
这个文件定义了技能本身,以及我们的“阿拉克涅原虫”作为一个完整的生物所需的一切。
|
||||
|
||||
```xml
|
||||
<!-- 路径: 1.6/1.6/Defs/Misc/ARA_Possession_Defs.xml -->
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Defs>
|
||||
|
||||
<!-- ==================== 技能定义 ==================== -->
|
||||
<AbilityDef>
|
||||
<defName>ARA_Ability_Possess</defName>
|
||||
<label>阿拉克涅寄生</label>
|
||||
<description>将你的意识注入另一个生物的身体,完全占据它。</description>
|
||||
<iconPath>UI/Abilities/Possess</iconPath> <!-- TODO: 需要一张图标 -->
|
||||
<cooldownTicks>600</cooldownTicks>
|
||||
<verbProperties>
|
||||
<verbClass>Verb_CastAbility</verbClass>
|
||||
<warmupTime>1.5</warmupTime>
|
||||
<range>5.9</range>
|
||||
<targetParams>
|
||||
<canTargetPawns>true</canTargetPawns>
|
||||
<canTargetBuildings>false</canTargetBuildings>
|
||||
<canTargetSelf>false</canTargetSelf>
|
||||
<canTargetLocations>false</canTargetLocations>
|
||||
</targetParams>
|
||||
</verbProperties>
|
||||
<comps>
|
||||
<!-- 关键:将这个技能与我们的C#属性类关联起来 -->
|
||||
<li Class="ArachnaeSwarm.Possession.CompProperties_AbilityPossess"/>
|
||||
</comps>
|
||||
</AbilityDef>
|
||||
|
||||
<!-- ==================== 生物AI定义 (可选) ==================== -->
|
||||
<ThinkTreeDef>
|
||||
<defName>ARA_Facehugger</defName>
|
||||
<insertTag>Humanlike_PostMentalState</insertTag>
|
||||
<insertPriority>100</insertPriority>
|
||||
<thinkRoot Class="ThinkNode_Priority">
|
||||
<subNodes>
|
||||
<!-- 在这里可以为抱脸虫添加自定义AI,例如当它空闲时自动寻找宿主 -->
|
||||
</subNodes>
|
||||
</thinkRoot>
|
||||
</ThinkTreeDef>
|
||||
|
||||
<!-- ==================== 生物类型定义 ==================== -->
|
||||
<PawnKindDef>
|
||||
<defName>ARA_Facehugger</defName>
|
||||
<label>阿拉克涅原虫</label>
|
||||
<!-- 关联下面的种族定义 -->
|
||||
<race>ARA_FacehuggerRace</race>
|
||||
<combatPower>25</combatPower>
|
||||
<lifeStages>
|
||||
<li>
|
||||
<bodyGraphicData>
|
||||
<texPath>Things/Pawn/Animal/ARA_Facehugger</texPath> <!-- TODO: 需要贴图 -->
|
||||
<drawSize>0.8</drawSize>
|
||||
</bodyGraphicData>
|
||||
<dessicatedBodyGraphicData>
|
||||
<texPath>Things/Pawn/Animal/Dessicated/CritterDessicated</texPath>
|
||||
<drawSize>0.8</drawSize>
|
||||
</dessicatedBodyGraphicData>
|
||||
</li>
|
||||
</lifeStages>
|
||||
<aiThinkTree>ARA_Facehugger</aiThinkTree>
|
||||
<abilities>
|
||||
<!-- 赋予该生物我们的夺舍技能 -->
|
||||
<li>ARA_Ability_Possess</li>
|
||||
</abilities>
|
||||
</PawnKindDef>
|
||||
|
||||
<!-- ==================== 种族定义 ==================== -->
|
||||
<ThingDef ParentName="AnimalThingBase">
|
||||
<defName>ARA_FacehuggerRace</defName>
|
||||
<label>阿拉克涅原虫</label>
|
||||
<description>一种小型的、脆弱的寄生生物,其唯一的生存目的就是寻找并占据一个更强大的宿主。它通过将自己的意识注入目标来完成这一过程。</description>
|
||||
<statBases>
|
||||
<MoveSpeed>4.0</MoveSpeed>
|
||||
<MarketValue>50</MarketValue>
|
||||
<ComfyTemperatureMin>-10</ComfyTemperatureMin>
|
||||
<ComfyTemperatureMax>50</ComfyTemperatureMax>
|
||||
</statBases>
|
||||
<tools>
|
||||
<li>
|
||||
<label>tiny claws</label>
|
||||
<capacities>
|
||||
<li>Scratch</li>
|
||||
</capacities>
|
||||
<power>2</power>
|
||||
<cooldownTime>1.5</cooldownTime>
|
||||
</li>
|
||||
</tools>
|
||||
<race>
|
||||
<thinkTreeMain>Animal</thinkTreeMain>
|
||||
<!-- 关联下面的身体定义 -->
|
||||
<body>ARA_FacehuggerBody</body>
|
||||
<baseBodySize>0.2</baseBodySize>
|
||||
<baseHealthScale>0.3</baseHealthScale>
|
||||
<baseHungerRate>0.1</baseHungerRate>
|
||||
<lifeStageAges>
|
||||
<li>
|
||||
<def>AnimalAdult</def>
|
||||
<minAge>0</minAge>
|
||||
</li>
|
||||
</lifeStageAges>
|
||||
</race>
|
||||
</ThingDef>
|
||||
|
||||
<!-- ==================== 身体结构定义 ==================== -->
|
||||
<BodyDef>
|
||||
<defName>ARA_FacehuggerBody</defName>
|
||||
<label>facehugger</label>
|
||||
<corePart>
|
||||
<def>Body</def>
|
||||
<height>20</height>
|
||||
<depth>20</depth>
|
||||
<parts>
|
||||
<li>
|
||||
<def>Head</def>
|
||||
<coverage>0.3</coverage>
|
||||
<parts>
|
||||
<li>
|
||||
<def>Skull</def>
|
||||
<coverage>0.2</coverage>
|
||||
<depth>Inside</depth>
|
||||
<parts>
|
||||
<li>
|
||||
<def>Brain</def>
|
||||
<coverage>0.1</coverage>
|
||||
<depth>Inside</depth>
|
||||
</li>
|
||||
</parts>
|
||||
</li>
|
||||
<li>
|
||||
<def>Eye</def>
|
||||
<customLabel>left eye</customLabel>
|
||||
<coverage>0.07</coverage>
|
||||
</li>
|
||||
<li>
|
||||
<def>Eye</def>
|
||||
<customLabel>right eye</customLabel>
|
||||
<coverage>0.07</coverage>
|
||||
</li>
|
||||
</parts>
|
||||
</li>
|
||||
<li>
|
||||
<def>Leg</def>
|
||||
<customLabel>front left leg</customLabel>
|
||||
<coverage>0.1</coverage>
|
||||
</li>
|
||||
<li>
|
||||
<def>Leg</def>
|
||||
<customLabel>front right leg</customLabel>
|
||||
<coverage>0.1</coverage>
|
||||
</li>
|
||||
<li>
|
||||
<def>Leg</def>
|
||||
<customLabel>rear left leg</customLabel>
|
||||
<coverage>0.1</coverage>
|
||||
</li>
|
||||
<li>
|
||||
<def>Leg</def>
|
||||
<customLabel>rear right leg</customLabel>
|
||||
<coverage>0.1</coverage>
|
||||
</li>
|
||||
</parts>
|
||||
</corePart>
|
||||
</BodyDef>
|
||||
|
||||
</Defs>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
这份详尽的文档现在包含了我们所有的最终代码和XML,并附有详细的注释,解释了每一步的作用和它们之间的关联。
|
||||
Reference in New Issue
Block a user