Compare commits
10 Commits
mechworkpr
...
animalwork
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a98b4d2612 | ||
|
|
9a8b90be82 | ||
|
|
2d4d171eb9 | ||
|
|
aca5fa287d | ||
|
|
ed9ac9d389 | ||
|
|
254a621c55 | ||
|
|
b3be9897df | ||
|
|
f74c2b844b | ||
|
|
b21de9de91 | ||
|
|
9acd5aac1e |
Binary file not shown.
@@ -14,6 +14,7 @@
|
||||
<Wildness>0.3</Wildness>
|
||||
</statBases>
|
||||
<uiIconScale>1.1</uiIconScale>
|
||||
|
||||
<tools>
|
||||
<li>
|
||||
<label>head claw</label>
|
||||
@@ -39,6 +40,7 @@
|
||||
<race>
|
||||
<thinkTreeMain>ARA_InsectWorker</thinkTreeMain>
|
||||
<body>BeetleLikeWithClaw</body>
|
||||
<thinkTreeMain>ARA_Insect_WithPlanting</thinkTreeMain>
|
||||
<baseHungerRate>0.25</baseHungerRate>
|
||||
<baseBodySize>0.8</baseBodySize>
|
||||
<baseHealthScale>1.7</baseHealthScale>
|
||||
@@ -47,6 +49,7 @@
|
||||
<trainability>Advanced</trainability>
|
||||
<specialTrainables>
|
||||
<li MayRequire="Ludeon.RimWorld.Odyssey">Dig</li>
|
||||
<li>ARA_Sowing</li>
|
||||
</specialTrainables>
|
||||
<lifeStageAges>
|
||||
<li>
|
||||
@@ -70,26 +73,21 @@
|
||||
<deathAction Class="DeathActionProperties_Vanish">
|
||||
<workerClass>DeathActionWorker_Vanish</workerClass>
|
||||
</deathAction>
|
||||
<mechEnabledWorkTypes Inherit="False">
|
||||
<li>Hauling</li>
|
||||
<li>Mining</li>
|
||||
<li>Construction</li>
|
||||
<li>Crafting</li>
|
||||
<li>Smithing</li>
|
||||
<li>Tailoring</li>
|
||||
<li>Cooking</li>
|
||||
<li>Research</li>
|
||||
<li>PlantCutting</li>
|
||||
<li>Growing</li>
|
||||
<li>Cleaning</li>
|
||||
<li>Doctor</li>
|
||||
<li>Firefighter</li>
|
||||
</mechEnabledWorkTypes>
|
||||
<mechFixedSkillLevel>10</mechFixedSkillLevel>
|
||||
</race>
|
||||
<comps>
|
||||
<li Class="ArachnaeSwarm.CompProperties_WorkForNonMechs"/>
|
||||
</comps>
|
||||
<li Class="ArachnaeSwarm.CompProperties_AdvancedTraining">
|
||||
<skillLevels>
|
||||
<li>
|
||||
<skill>Plants</skill>
|
||||
<level>10</level> <!-- 动物生成时,种植技能固定为8级 -->
|
||||
</li>
|
||||
</skillLevels>
|
||||
<instantTrainables>
|
||||
<li>ARA_Sowing</li> <!-- 动物生成时,立即完成播种训练 -->
|
||||
</instantTrainables>
|
||||
<disableAllSkillDecay>true</disableAllSkillDecay> <!-- 阻止这个动物的所有技能衰减 -->
|
||||
</li>
|
||||
</comps>
|
||||
<tradeTags>
|
||||
<li>AnimalInsect</li>
|
||||
</tradeTags>
|
||||
|
||||
@@ -508,7 +508,7 @@
|
||||
<cooldownTicks>9999</cooldownTicks>
|
||||
<productionQueue>
|
||||
<li>
|
||||
<pawnKind>Spelopede</pawnKind>
|
||||
<pawnKind>ArachnaeBase_Race_Scavenger</pawnKind>
|
||||
<count>3</count>
|
||||
<cooldownTicks>6000</cooldownTicks>
|
||||
</li>
|
||||
|
||||
@@ -1,7 +1,455 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Defs>
|
||||
|
||||
<ThinkTreeDef>
|
||||
<defName>ARA_Humanlike</defName> <!-- 更改defName以避免与原版Humanlike冲突 -->
|
||||
<defName>ARA_Insect_WithPlanting</defName>
|
||||
<thinkRoot Class="ThinkNode_Priority">
|
||||
<subNodes>
|
||||
<!-- Keep lying down if we have to -->
|
||||
<li Class="ThinkNode_ConditionalMustKeepLyingDown">
|
||||
<subNodes>
|
||||
<!-- Do a queued job if possible -->
|
||||
<li Class="ThinkNode_QueuedJob">
|
||||
<inBedOnly>true</inBedOnly>
|
||||
</li>
|
||||
|
||||
<!-- Keep lying down -->
|
||||
<li Class="JobGiver_KeepLyingDown"/>
|
||||
</subNodes>
|
||||
</li>
|
||||
|
||||
<li Class="ThinkNode_Subtree">
|
||||
<treeDef>Downed</treeDef>
|
||||
</li>
|
||||
<li Class="ThinkNode_Subtree">
|
||||
<treeDef>BurningResponse</treeDef>
|
||||
</li>
|
||||
<li Class="ThinkNode_Subtree">
|
||||
<treeDef>MentalStateCritical</treeDef>
|
||||
</li>
|
||||
|
||||
<!-- React to close melee threat -->
|
||||
<li Class="JobGiver_ReactToCloseMeleeThreat"/>
|
||||
|
||||
<!-- Do a queued job -->
|
||||
<li Class="ThinkNode_QueuedJob"/>
|
||||
|
||||
<!-- Wild insects dig out if no path to map edge and starving -->
|
||||
<li Class="ThinkNode_ConditionalHasFaction">
|
||||
<invert>true</invert>
|
||||
<subNodes>
|
||||
<li Class="ThinkNode_ConditionalStarving">
|
||||
<subNodes>
|
||||
<li Class="ThinkNode_ConditionalBodySize">
|
||||
<min>0.7</min>
|
||||
<subNodes>
|
||||
<li Class="ThinkNode_Subtree">
|
||||
<treeDef>DigOutIfCannotReachMapEdge</treeDef>
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
|
||||
<!-- Leave if timed out -->
|
||||
<li Class="ThinkNode_ConditionalExitTimedOut">
|
||||
<subNodes>
|
||||
<li Class="ThinkNode_Tagger">
|
||||
<tagToGive>Misc</tagToGive>
|
||||
<subNodes>
|
||||
<li Class="JobGiver_ExitMapRandom">
|
||||
<defaultLocomotion>Walk</defaultLocomotion>
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
|
||||
<!-- Mental state non critical -->
|
||||
<li Class="ThinkNode_Subtree">
|
||||
<treeDef>MentalStateNonCritical</treeDef>
|
||||
</li>
|
||||
|
||||
<!-- Forced goto -->
|
||||
<li Class="ThinkNode_ConditionalForcedGoto">
|
||||
<subNodes>
|
||||
<li Class="ThinkNode_Tagger">
|
||||
<tagToGive>Misc</tagToGive>
|
||||
<subNodes>
|
||||
<li Class="JobGiver_ForcedGoto"/>
|
||||
</subNodes>
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
|
||||
<!-- Behavior when roped -->
|
||||
<li Class="ThinkNode_Subtree">
|
||||
<treeDef>RopedPawn</treeDef>
|
||||
</li>
|
||||
|
||||
<!-- Lord directives -->
|
||||
<li Class="ThinkNode_Subtree">
|
||||
<treeDef>LordDuty</treeDef>
|
||||
</li>
|
||||
|
||||
<!-- Insertion hook for modders -->
|
||||
<li Class="ThinkNode_SubtreesByTag">
|
||||
<insertTag>Animal_PreMain</insertTag>
|
||||
</li>
|
||||
<li Class="ThinkNode_SubtreesByTag">
|
||||
<insertTag>Insect_PreMain</insertTag>
|
||||
</li>
|
||||
|
||||
<li Class="ThinkNode_ConditionalHasFaction">
|
||||
<invert>true</invert>
|
||||
<subNodes>
|
||||
<!-- Wild insects with no lord will fight nearby enemies -->
|
||||
<li Class="JobGiver_AIFightEnemies">
|
||||
<targetAcquireRadius>30</targetAcquireRadius> <!-- Same as DefendAndExpandHive -->
|
||||
<targetKeepRadius>35</targetKeepRadius>
|
||||
</li>
|
||||
|
||||
<!-- Wild insects leave map in some conditions -->
|
||||
<li Class="ThinkNode_Subtree">
|
||||
<treeDef>LeaveIfWrongSeason</treeDef>
|
||||
</li>
|
||||
<li Class="ThinkNode_Subtree">
|
||||
<treeDef>LeaveIfStarving</treeDef>
|
||||
</li>
|
||||
|
||||
<!-- Wild insects randomly leave map -->
|
||||
<li Class="ThinkNode_ChancePerHour_Constant">
|
||||
<mtbDays>60</mtbDays>
|
||||
<subNodes>
|
||||
<li Class="ThinkNode_Tagger">
|
||||
<tagToGive>Misc</tagToGive>
|
||||
<subNodes>
|
||||
<li Class="JobGiver_ExitMapRandom">
|
||||
<defaultLocomotion>Walk</defaultLocomotion>
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
|
||||
<!-- Insects of a faction that's not the players without a lord leave randomly -->
|
||||
<li Class="ThinkNode_ConditionalOfPlayerFaction">
|
||||
<invert>true</invert>
|
||||
<subNodes>
|
||||
<li Class="ThinkNode_ConditionalHasFaction">
|
||||
<subNodes>
|
||||
<li Class="ThinkNode_ConditionalNoLord">
|
||||
<subNodes>
|
||||
<li Class="ThinkNode_ChancePerHour_Constant">
|
||||
<mtbDays>60</mtbDays>
|
||||
<subNodes>
|
||||
<li Class="ThinkNode_Tagger">
|
||||
<tagToGive>Misc</tagToGive>
|
||||
<subNodes>
|
||||
<li Class="JobGiver_ExitMapRandom">
|
||||
<defaultLocomotion>Walk</defaultLocomotion>
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
|
||||
<!-- Tame insects -->
|
||||
<li Class="ThinkNode_ConditionalOfPlayerFaction">
|
||||
<subNodes>
|
||||
<li Class="ThinkNode_Tagger">
|
||||
<tagToGive>TrainedAnimalBehavior</tagToGive>
|
||||
<subNodes>
|
||||
<!-- Trained behavior: obedience: Follow and defend master -->
|
||||
<li Class="ThinkNode_ConditionalTrainableCompleted">
|
||||
<trainable>Obedience</trainable>
|
||||
<subNodes>
|
||||
<li Class="ThinkNode_ConditionalShouldFollowMaster">
|
||||
<subNodes>
|
||||
<li Class="JobGiver_AIDefendMaster">
|
||||
<attackMeleeThreatEvenIfNotHostile>true</attackMeleeThreatEvenIfNotHostile>
|
||||
</li>
|
||||
<li Class="JobGiver_AIFollowMaster"/>
|
||||
<li Class="JobGiver_WanderNearMaster"/>
|
||||
</subNodes>
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
|
||||
<!-- Trained behavior: Rescue-->
|
||||
<li Class="ThinkNode_ConditionalTrainableCompleted">
|
||||
<trainable>Rescue</trainable>
|
||||
<subNodes>
|
||||
<li Class="JobGiver_RescueNearby">
|
||||
<radius>75</radius>
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
|
||||
<!-- Take care of critical needs (below rescue - so heroic!)-->
|
||||
|
||||
<li Class="ThinkNode_Tagger">
|
||||
<tagToGive>RestingForMedicalReasons</tagToGive>
|
||||
<subNodes>
|
||||
<li Class="JobGiver_PatientGoToBed"/>
|
||||
</subNodes>
|
||||
</li>
|
||||
|
||||
<li Class="JobGiver_SeekAllowedArea"/>
|
||||
<li Class="JobGiver_SeekSafeTemperature"/>
|
||||
|
||||
</subNodes>
|
||||
</li>
|
||||
|
||||
<!-- Eat random things out of curiosity -->
|
||||
<li Class="ThinkNode_ChancePerHour_Constant">
|
||||
<mtbDays>60</mtbDays>
|
||||
<subNodes>
|
||||
<li Class="ThinkNode_Tagger">
|
||||
<tagToGive>SatisfyingNeeds</tagToGive>
|
||||
<subNodes>
|
||||
<li Class="JobGiver_EatRandom"/>
|
||||
</subNodes>
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
|
||||
<!-- Satisfy basic needs -->
|
||||
<li Class="ThinkNode_Subtree">
|
||||
<treeDef>SatisfyBasicNeeds</treeDef>
|
||||
</li>
|
||||
|
||||
<!-- Tame insect: do useful things for the colony-->
|
||||
<li Class="ThinkNode_ConditionalHasFaction">
|
||||
<subNodes>
|
||||
|
||||
<!-- Try to mate -->
|
||||
<li Class="ThinkNode_ChancePerHour_Mate">
|
||||
<subNodes>
|
||||
<li Class="ThinkNode_Tagger">
|
||||
<tagToGive>SatisfyingNeeds</tagToGive>
|
||||
<subNodes>
|
||||
<li Class="JobGiver_Mate"/>
|
||||
</subNodes>
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
|
||||
<!-- Nuzzle randoms -->
|
||||
<li Class="ThinkNode_ChancePerHour_Nuzzle">
|
||||
<subNodes>
|
||||
<li Class="ThinkNode_Tagger">
|
||||
<tagToGive>Misc</tagToGive>
|
||||
<subNodes>
|
||||
<li Class="JobGiver_Nuzzle"/>
|
||||
</subNodes>
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
|
||||
<!-- Roamers gonna roam -->
|
||||
<li Class="ThinkNode_ChancePerDay_Roam">
|
||||
<subNodes>
|
||||
<li Class="ThinkNode_Tagger">
|
||||
<tagToGive>Misc</tagToGive>
|
||||
<subNodes>
|
||||
<li Class="JobGiver_StartRoaming"/>
|
||||
</subNodes>
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
|
||||
<!-- Trained behavior: Haul-->
|
||||
<li Class="ThinkNode_ChancePerHour_Constant">
|
||||
<mtbHours>1.5</mtbHours>
|
||||
<subNodes>
|
||||
<li Class="ThinkNode_ConditionalRequireCapacities">
|
||||
<requiredCapacities>
|
||||
<li>Manipulation</li>
|
||||
</requiredCapacities>
|
||||
<subNodes>
|
||||
<li Class="ThinkNode_ConditionalTrainableCompleted">
|
||||
<trainable>Haul</trainable>
|
||||
<subNodes>
|
||||
<li Class="ThinkNode_Tagger">
|
||||
<tagToGive>TrainedAnimalBehavior</tagToGive>
|
||||
<subNodes>
|
||||
<li Class="JobGiver_Haul"/>
|
||||
</subNodes>
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
|
||||
<!-- Trained behavior: Forage -->
|
||||
<li Class="ThinkNode_ConditionalTrainableCompleted" MayRequire="Ludeon.RimWorld.Odyssey">
|
||||
<trainable>Forage</trainable>
|
||||
<subNodes>
|
||||
<li Class="ThinkNode_ChancePerHour_Forage">
|
||||
<subNodes>
|
||||
<li Class="ThinkNode_ConditionalAnimalShouldForage">
|
||||
<subNodes>
|
||||
<li Class="JobGiver_Forage" />
|
||||
</subNodes>
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
|
||||
<!-- Trained behavior: Mine -->
|
||||
<li Class="ThinkNode_ConditionalTrainableCompleted" MayRequire="Ludeon.RimWorld.Odyssey">
|
||||
<trainable>Dig</trainable>
|
||||
<subNodes>
|
||||
<li Class="ThinkNode_ConditionalAnimalShouldDig">
|
||||
<subNodes>
|
||||
<li Class="JobGiver_Mine" />
|
||||
</subNodes>
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
|
||||
<!-- OUR CUSTOM LOGIC INJECTION FOR PLANT HARVESTING -->
|
||||
<li Class="ThinkNode_ConditionalTrainableCompleted">
|
||||
<trainable>ARA_Sowing</trainable> <!-- Harvesting is part of Sowing skill -->
|
||||
<subNodes>
|
||||
<!-- 统一的农业工作节点 -->
|
||||
<li Class="ArachnaeSwarm.ThinkNode_ConditionalAnimalShouldDoGrowingWork">
|
||||
<subNodes>
|
||||
<!-- 统一的、智能的农业 JobGiver -->
|
||||
<li Class="ArachnaeSwarm.JobGiver_Grower" />
|
||||
</subNodes>
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
|
||||
</subNodes>
|
||||
</li>
|
||||
|
||||
<!-- Insertion hook for modders -->
|
||||
<li Class="ThinkNode_SubtreesByTag">
|
||||
<insertTag>Animal_PreWander</insertTag>
|
||||
</li>
|
||||
<li Class="ThinkNode_SubtreesByTag">
|
||||
<insertTag>Insect_PreWander</insertTag>
|
||||
</li>
|
||||
|
||||
<!-- Tame insect: wander near colony if possible -->
|
||||
<li Class="ThinkNode_ConditionalOfPlayerFaction">
|
||||
<subNodes>
|
||||
<li Class="ThinkNode_Tagger">
|
||||
<tagToGive>Idle</tagToGive>
|
||||
<subNodes>
|
||||
<!-- Wander near your current position if in hostile map -->
|
||||
<li Class="ThinkNode_ConditionalAnyEnemyInHostileMap">
|
||||
<subNodes>
|
||||
<li Class="JobGiver_WanderAnywhere">
|
||||
<maxDanger>None</maxDanger>
|
||||
<ticksBetweenWandersRange>120~240</ticksBetweenWandersRange>
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
|
||||
<li Class="ThinkNode_ConditionalRoamer">
|
||||
<subNodes>
|
||||
<li Class="JobGiver_WanderInRoofedCellsInPen">
|
||||
<maxDanger>None</maxDanger>
|
||||
<ticksBetweenWandersRange>120~240</ticksBetweenWandersRange>
|
||||
<expiryInterval>500</expiryInterval>
|
||||
</li>
|
||||
|
||||
<!-- tame roamers should not wander too far, and if unenclosed wander near a suitable pen marker 10% of the time -->
|
||||
<li Class="ThinkNode_ConditionalRandom">
|
||||
<chance>0.1</chance>
|
||||
<subNodes>
|
||||
<li Class="JobGiver_WanderInPen">
|
||||
<maxDanger>None</maxDanger>
|
||||
<ticksBetweenWandersRange>120~240</ticksBetweenWandersRange>
|
||||
<expiryInterval>500</expiryInterval>
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
|
||||
<li Class="JobGiver_WanderAnywhere">
|
||||
<maxDanger>None</maxDanger>
|
||||
<ticksBetweenWandersRange>120~240</ticksBetweenWandersRange>
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
|
||||
<!-- Wander near colony -->
|
||||
<li Class="JobGiver_WanderColony">
|
||||
<maxDanger>None</maxDanger>
|
||||
<ticksBetweenWandersRange>120~240</ticksBetweenWandersRange>
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
|
||||
<!-- Of neutral faction: rest and then exit the map -->
|
||||
<li Class="ThinkNode_ConditionalNonPlayerNonHostileFaction">
|
||||
<subNodes>
|
||||
<li Class="ThinkNode_Tagger">
|
||||
<tagToGive>RestingForMedicalReasons</tagToGive>
|
||||
<subNodes>
|
||||
<li Class="JobGiver_PatientGoToBed"/>
|
||||
</subNodes>
|
||||
</li>
|
||||
|
||||
<li Class="ThinkNode_Tagger">
|
||||
<tagToGive>Misc</tagToGive>
|
||||
<subNodes>
|
||||
<li Class="JobGiver_ExitMapBest">
|
||||
<defaultLocomotion>Walk</defaultLocomotion>
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
|
||||
<!-- Wander -->
|
||||
<li Class="ThinkNode_Tagger">
|
||||
<tagToGive>Idle</tagToGive>
|
||||
<subNodes>
|
||||
<li Class="ThinkNode_ConditionalHerdAnimal">
|
||||
<subNodes>
|
||||
<li Class="JobGiver_WanderHerd">
|
||||
<maxDanger>Deadly</maxDanger>
|
||||
<ticksBetweenWandersRange>120~240</ticksBetweenWandersRange>
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
|
||||
<li Class="JobGiver_WanderAnywhere">
|
||||
<maxDanger>Deadly</maxDanger>
|
||||
<ticksBetweenWandersRange>120~240</ticksBetweenWandersRange>
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
|
||||
<li Class="JobGiver_IdleError"/>
|
||||
</subNodes>
|
||||
</thinkRoot>
|
||||
</ThinkTreeDef>
|
||||
|
||||
<ThinkTreeDef>
|
||||
<defName>ARA_Humanlike</defName> <!-- 更改defName以避免与原版Humanlike冲突 -->
|
||||
<thinkRoot Class="ThinkNode_Priority">
|
||||
<subNodes>
|
||||
|
||||
@@ -20,7 +468,7 @@
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
|
||||
|
||||
<!-- If we HAVE to keep lying down... -->
|
||||
<li Class="ThinkNode_ConditionalMustKeepLyingDown">
|
||||
<subNodes>
|
||||
@@ -45,12 +493,12 @@
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
|
||||
|
||||
<!-- Keep lying down -->
|
||||
<li Class="JobGiver_KeepLyingDown" />
|
||||
</subNodes>
|
||||
</li>
|
||||
|
||||
|
||||
<li Class="ThinkNode_Subtree">
|
||||
<treeDef>Downed</treeDef>
|
||||
</li>
|
||||
@@ -65,7 +513,7 @@
|
||||
<li Class="ThinkNode_Subtree" MayRequire="Ludeon.RimWorld.Biotech">
|
||||
<treeDef>Abilities_Escape</treeDef>
|
||||
</li>
|
||||
|
||||
|
||||
<!-- React to close melee threat -->
|
||||
<li Class="JobGiver_ReactToCloseMeleeThreat" />
|
||||
|
||||
@@ -83,7 +531,7 @@
|
||||
<li Class="ThinkNode_SubtreesByTag">
|
||||
<insertTag>Humanlike_PostMentalState</insertTag>
|
||||
</li>
|
||||
|
||||
|
||||
<!-- Do a queued job -->
|
||||
<li Class="ThinkNode_QueuedJob" />
|
||||
|
||||
@@ -121,21 +569,21 @@
|
||||
<li Class="ThinkNode_SubtreesByTag">
|
||||
<insertTag>Humanlike_PostDuty</insertTag>
|
||||
</li>
|
||||
|
||||
|
||||
<!-- JobGiver_MaintainBuildings start -->
|
||||
<li Class="ThinkNode_ChancePerHour_Constant">
|
||||
<mtbHours>2.5</mtbHours>
|
||||
<subNodes>
|
||||
<li Class="ArachnaeSwarm.JobGiver_MaintainBuildings">
|
||||
<maintainableThingDefs>
|
||||
<li>ARA_InteractiveEggSac</li> <!-- 默认维护Hive -->
|
||||
<li>ARA_InteractiveEggSac</li> <!-- 默认维护Hive -->
|
||||
</maintainableThingDefs>
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
<li Class="ArachnaeSwarm.JobGiver_MaintainBuildings">
|
||||
<maintainableThingDefs>
|
||||
<li>ARA_InteractiveEggSac</li> <!-- 默认维护Hive -->
|
||||
<li>ARA_InteractiveEggSac</li> <!-- 默认维护Hive -->
|
||||
</maintainableThingDefs>
|
||||
<onlyIfDamagingState>true</onlyIfDamagingState>
|
||||
</li>
|
||||
@@ -158,7 +606,7 @@
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
|
||||
|
||||
<!-- Escape -->
|
||||
<li Class="ThinkNode_Tagger">
|
||||
<tagToGive>Escaping</tagToGive>
|
||||
@@ -168,7 +616,7 @@
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
|
||||
|
||||
<!-- Exit map if released -->
|
||||
<li Class="ThinkNode_ConditionalReleased">
|
||||
<subNodes>
|
||||
@@ -189,14 +637,14 @@
|
||||
<li Class="JobGiver_PatientGoToBed" />
|
||||
</subNodes>
|
||||
</li>
|
||||
|
||||
|
||||
<li Class="ThinkNode_Tagger">
|
||||
<tagToGive>ChangingApparel</tagToGive>
|
||||
<subNodes>
|
||||
<li Class="JobGiver_PrisonerGetDressed" />
|
||||
</subNodes>
|
||||
</li>
|
||||
|
||||
|
||||
<li Class="ThinkNode_Tagger">
|
||||
<tagToGive>SatisfyingNeeds</tagToGive>
|
||||
<subNodes>
|
||||
@@ -220,7 +668,7 @@
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
|
||||
|
||||
<!-- If in non-PlayerHomeMap -->
|
||||
<li Class="ThinkNode_ConditionalInNonPlayerHomeMap">
|
||||
<subNodes>
|
||||
@@ -247,7 +695,7 @@
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
|
||||
|
||||
<li Class="ThinkNode_Tagger">
|
||||
<tagToGive>Idle</tagToGive>
|
||||
<subNodes>
|
||||
@@ -256,7 +704,7 @@
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
|
||||
|
||||
<li Class="JobGiver_IdleError" />
|
||||
</subNodes>
|
||||
</li>
|
||||
@@ -279,7 +727,7 @@
|
||||
<leaveJoinableLordIfIssuesJob>true</leaveJoinableLordIfIssuesJob>
|
||||
<emergency>true</emergency>
|
||||
</li>
|
||||
|
||||
|
||||
<!-- Get food (only if starving) -->
|
||||
<li Class="ThinkNode_ConditionalStarving">
|
||||
<subNodes>
|
||||
@@ -375,7 +823,7 @@
|
||||
<li Class="ThinkNode_SubtreesByTag">
|
||||
<insertTag>Humanlike_PreMain</insertTag>
|
||||
</li>
|
||||
|
||||
|
||||
<!-- Main colonist behavior core -->
|
||||
<li Class="ThinkNode_ConditionalColonist">
|
||||
<subNodes>
|
||||
@@ -416,7 +864,7 @@
|
||||
<li Class="JobGiver_IdleJoy" />
|
||||
</subNodes>
|
||||
</li>
|
||||
|
||||
|
||||
<!-- Wander -->
|
||||
<li Class="JobGiver_WanderColony">
|
||||
<maxDanger>None</maxDanger>
|
||||
@@ -454,7 +902,7 @@
|
||||
<li Class="JobGiver_PatientGoToBed" />
|
||||
</subNodes>
|
||||
</li>
|
||||
|
||||
|
||||
<li Class="ThinkNode_Tagger">
|
||||
<tagToGive>Misc</tagToGive>
|
||||
<subNodes>
|
||||
@@ -498,4 +946,5 @@
|
||||
</subNodes>
|
||||
</thinkRoot>
|
||||
</ThinkTreeDef>
|
||||
|
||||
</Defs>
|
||||
23
1.6/1.6/Defs/TrainableDefs/ARA_Sowing.xml
Normal file
23
1.6/1.6/Defs/TrainableDefs/ARA_Sowing.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Defs>
|
||||
|
||||
<TrainableDef>
|
||||
<defName>ARA_Sowing</defName>
|
||||
<label>种植</label>
|
||||
<description>允许该生物执行种植任务。</description>
|
||||
|
||||
<!-- 标记为特殊训练,这样它就会被 PawnColumnWorker_Trainable_Special 统一管理 -->
|
||||
<specialTrainable>true</specialTrainable>
|
||||
|
||||
<!-- 训练难度和所需智力 -->
|
||||
<difficulty>5</difficulty>
|
||||
<requiredTrainability>Advanced</requiredTrainability>
|
||||
|
||||
<!-- 训练所需步骤 -->
|
||||
<steps>3</steps>
|
||||
|
||||
<!-- 在UI中的排序 -->
|
||||
<listPriority>100</listPriority>
|
||||
</TrainableDef>
|
||||
|
||||
</Defs>
|
||||
@@ -1,75 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using HarmonyLib;
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
[StaticConstructorOnStartup]
|
||||
public static class AnimalWorkSystemPatcher
|
||||
{
|
||||
static AnimalWorkSystemPatcher()
|
||||
{
|
||||
var harmony = new Harmony("com.yourname.animalworksystem");
|
||||
harmony.PatchAll();
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(Pawn_WorkSettings), "EnableAndInitialize")]
|
||||
public static class Patch_Pawn_WorkSettings_EnableAndInitialize
|
||||
{
|
||||
public static void Postfix(Pawn_WorkSettings __instance, Pawn ___pawn)
|
||||
{
|
||||
// 检查是否是我们想要启用工作系统的动物,并且它不是机械体
|
||||
// 因为原版的 EnableAndInitialize 已经处理了机械体的工作设置
|
||||
if (___pawn.Faction != null && ___pawn.Faction.IsPlayer &&
|
||||
!___pawn.RaceProps.IsMechanoid &&
|
||||
ShouldEnableWorkSystem(___pawn))
|
||||
{
|
||||
// 获取 CompProperties_WorkForNonMechs
|
||||
CompProperties_WorkForNonMechs compProps = null;
|
||||
if (___pawn.def.comps != null)
|
||||
{
|
||||
foreach (var comp in ___pawn.def.comps)
|
||||
{
|
||||
if (comp is CompProperties_WorkForNonMechs props)
|
||||
{
|
||||
compProps = props;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (compProps != null && compProps.workTypes != null)
|
||||
{
|
||||
// 设置 CompProperties_WorkForNonMechs 中定义的工作类型优先级
|
||||
foreach (var workType in compProps.workTypes)
|
||||
{
|
||||
if (!__instance.WorkIsActive(workType) && !___pawn.WorkTypeIsDisabled(workType))
|
||||
{
|
||||
__instance.SetPriority(workType, 3); // 默认优先级
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ShouldEnableWorkSystem(Pawn pawn)
|
||||
{
|
||||
// 检查 ThingDef 中是否有 CompProperties_WorkForNonMechs 配置
|
||||
if (pawn.def.comps != null)
|
||||
{
|
||||
foreach (var compProperties in pawn.def.comps)
|
||||
{
|
||||
if (compProperties is CompProperties_WorkForNonMechs)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -86,10 +86,12 @@
|
||||
<Compile Include="JobGiver_MaintainBuildings.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="CompWorkForNonMechs.cs" />
|
||||
<Compile Include="AnimalWorkSystemPatcher.cs" />
|
||||
<Compile Include="Patch_WorkGivers_Growing.cs" />
|
||||
<Compile Include="Patch_QualityUtility.cs" />
|
||||
<Compile Include="MainHarmony.cs" />
|
||||
<Compile Include="ThinkNode_ConditionalAnimalShouldDoGrowingWork.cs" />
|
||||
<Compile Include="CompAdvancedTraining.cs" />
|
||||
<Compile Include="JobGiver_Grower.cs" />
|
||||
<Compile Include="WorkGiver_ArachnaeSow.cs" />
|
||||
<Compile Include="TrainingSystem_Patcher.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="CompProperties_DelayedTerrainSpawn.cs" />
|
||||
|
||||
77
Source/ArachnaeSwarm/CompAdvancedTraining.cs
Normal file
77
Source/ArachnaeSwarm/CompAdvancedTraining.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
using System.Collections.Generic;
|
||||
using Verse;
|
||||
using RimWorld;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
public class CompProperties_AdvancedTraining : CompProperties
|
||||
{
|
||||
// 1. 用于设置固定技能等级
|
||||
public List<SkillLevelEntry> skillLevels = new List<SkillLevelEntry>();
|
||||
|
||||
// 2. 用于指定生成时立即完成的训练
|
||||
public List<TrainableDef> instantTrainables = new List<TrainableDef>();
|
||||
|
||||
// 3. 全局开关:是否阻止所有技能衰减
|
||||
public bool disableAllSkillDecay = false;
|
||||
|
||||
public CompProperties_AdvancedTraining()
|
||||
{
|
||||
this.compClass = typeof(CompAdvancedTraining);
|
||||
}
|
||||
}
|
||||
|
||||
public class SkillLevelEntry
|
||||
{
|
||||
public SkillDef skill;
|
||||
public int level = 0;
|
||||
// 这里的 disableDecay 字段现在是冗余的,因为我们有全局的 disableAllSkillDecay
|
||||
// 但为了兼容性或未来可能的需求,可以保留。
|
||||
// 在当前方案中,它的值将被忽略。
|
||||
public bool disableDecay = true;
|
||||
}
|
||||
|
||||
public class CompAdvancedTraining : ThingComp
|
||||
{
|
||||
public CompProperties_AdvancedTraining Props => (CompProperties_AdvancedTraining)this.props;
|
||||
|
||||
public override void PostSpawnSetup(bool respawningAfterLoad)
|
||||
{
|
||||
base.PostSpawnSetup(respawningAfterLoad);
|
||||
|
||||
Pawn pawn = this.parent as Pawn;
|
||||
if (pawn == null) return;
|
||||
|
||||
// --- 1. 设置固定技能等级 ---
|
||||
if (pawn.skills != null && !Props.skillLevels.NullOrEmpty())
|
||||
{
|
||||
foreach (var entry in Props.skillLevels)
|
||||
{
|
||||
if (entry.skill != null)
|
||||
{
|
||||
var skillRecord = pawn.skills.GetSkill(entry.skill);
|
||||
if (skillRecord != null)
|
||||
{
|
||||
skillRecord.Level = entry.level;
|
||||
// 注意: 激情 (passion) 影响学习速度,不直接阻止衰减。
|
||||
// 实际的衰减阻止逻辑在 TrainingSystem_Patcher.cs 中处理。
|
||||
// 默认情况下,我们不改变 passion,除非有特殊需求。
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- 2. 执行瞬间训练 (只在初次生成时) ---
|
||||
if (!respawningAfterLoad && pawn.training != null && !Props.instantTrainables.NullOrEmpty())
|
||||
{
|
||||
foreach (var trainable in Props.instantTrainables)
|
||||
{
|
||||
if (trainable != null && !pawn.training.HasLearned(trainable))
|
||||
{
|
||||
pawn.training.Train(trainable, null, complete: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
public class CompProperties_WorkForNonMechs : CompProperties
|
||||
{
|
||||
public List<WorkTypeDef> workTypes;
|
||||
|
||||
public CompProperties_WorkForNonMechs()
|
||||
{
|
||||
compClass = typeof(CompWorkForNonMechs);
|
||||
}
|
||||
}
|
||||
|
||||
public class CompWorkForNonMechs : ThingComp
|
||||
{
|
||||
public CompProperties_WorkForNonMechs Props => (CompProperties_WorkForNonMechs)props;
|
||||
|
||||
public override void PostSpawnSetup(bool respawningAfterLoad)
|
||||
{
|
||||
base.PostSpawnSetup(respawningAfterLoad);
|
||||
|
||||
var pawn = parent as Pawn;
|
||||
if (pawn == null || pawn.Faction == null || !pawn.Faction.IsPlayer) return;
|
||||
|
||||
// 确保 workSettings 实例存在
|
||||
if (pawn.workSettings == null)
|
||||
{
|
||||
pawn.workSettings = new Pawn_WorkSettings(pawn);
|
||||
}
|
||||
|
||||
pawn.workSettings.EnableAndInitialize();
|
||||
}
|
||||
}
|
||||
}
|
||||
158
Source/ArachnaeSwarm/JobGiver_Grower.cs
Normal file
158
Source/ArachnaeSwarm/JobGiver_Grower.cs
Normal file
@@ -0,0 +1,158 @@
|
||||
using System.Collections.Generic;
|
||||
using Verse;
|
||||
using Verse.AI;
|
||||
using RimWorld;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
public class JobGiver_Grower : ThinkNode_JobGiver
|
||||
{
|
||||
private WorkGiver_GrowerHarvest _workGiverHarvest;
|
||||
private WorkGiver_Scanner _workGiverPlantsCut; // 通用扫描器类型
|
||||
private WorkGiver_ArachnaeSow _workGiverArachnaeSow;
|
||||
|
||||
protected override Job TryGiveJob(Pawn pawn)
|
||||
{
|
||||
// 懒加载 WorkGiver 实例,确保 DefOf 已被初始化
|
||||
if (_workGiverHarvest == null)
|
||||
{
|
||||
_workGiverHarvest = DefDatabase<WorkGiverDef>.GetNamed("GrowerHarvest").Worker as WorkGiver_GrowerHarvest;
|
||||
_workGiverPlantsCut = DefDatabase<WorkGiverDef>.GetNamed("PlantsCut").Worker as WorkGiver_Scanner;
|
||||
_workGiverArachnaeSow = new WorkGiver_ArachnaeSow();
|
||||
|
||||
if (_workGiverHarvest == null || _workGiverPlantsCut == null || _workGiverArachnaeSow == null)
|
||||
{
|
||||
Log.ErrorOnce("JobGiver_Grower: Failed to get a required WorkGiver. DefOfs might not be initialized.", 123458);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 1. 优先收获(自动)
|
||||
IntVec3 bestHarvestCell = FindClosestHarvestableCell(pawn);
|
||||
if (bestHarvestCell.IsValid)
|
||||
{
|
||||
Job harvestJob = _workGiverHarvest.JobOnCell(pawn, bestHarvestCell);
|
||||
if (harvestJob != null)
|
||||
{
|
||||
return harvestJob;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 其次处理手动指定的砍伐/收获任务
|
||||
Thing bestCuttable = FindClosestWorkableThing(pawn, _workGiverPlantsCut);
|
||||
if (bestCuttable != null)
|
||||
{
|
||||
Job cutJob = _workGiverPlantsCut.JobOnThing(pawn, bestCuttable);
|
||||
if (cutJob != null)
|
||||
{
|
||||
return cutJob;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 最后处理播种或清理障碍(由 WorkGiver_ArachnaeSow 处理)
|
||||
Job sowOrClearJob = FindClosestSowableOrClearJob(pawn, _workGiverArachnaeSow);
|
||||
if (sowOrClearJob != null)
|
||||
{
|
||||
return sowOrClearJob;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Thing FindClosestWorkableThing(Pawn pawn, WorkGiver_Scanner scanner)
|
||||
{
|
||||
return GenClosest.ClosestThing_Global(
|
||||
pawn.Position,
|
||||
scanner.PotentialWorkThingsGlobal(pawn),
|
||||
maxDistance: 9999f,
|
||||
validator: t => t != null && !t.IsForbidden(pawn) && scanner.HasJobOnThing(pawn, t) && pawn.CanReach(t, PathEndMode.Touch, Danger.Deadly)
|
||||
);
|
||||
}
|
||||
|
||||
private IntVec3 FindClosestHarvestableCell(Pawn pawn)
|
||||
{
|
||||
IntVec3 bestCell = IntVec3.Invalid;
|
||||
float bestDistSq = float.MaxValue;
|
||||
|
||||
foreach (Zone zone in pawn.Map.zoneManager.AllZones)
|
||||
{
|
||||
if (zone is Zone_Growing growingZone)
|
||||
{
|
||||
foreach (IntVec3 cell in growingZone.Cells)
|
||||
{
|
||||
float distSq = pawn.Position.DistanceToSquared(cell);
|
||||
if (distSq < bestDistSq && pawn.CanReach(cell, PathEndMode.ClosestTouch, Danger.Deadly))
|
||||
{
|
||||
if (_workGiverHarvest.HasJobOnCell(pawn, cell))
|
||||
{
|
||||
bestDistSq = distSq;
|
||||
bestCell = cell;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return bestCell;
|
||||
}
|
||||
|
||||
// 修改后的方法:寻找最近的播种或清理 Job
|
||||
private Job FindClosestSowableOrClearJob(Pawn pawn, WorkGiver_ArachnaeSow scanner)
|
||||
{
|
||||
IntVec3 bestClearCell = IntVec3.Invalid;
|
||||
Job bestClearJob = null;
|
||||
float bestClearDistSq = float.MaxValue;
|
||||
|
||||
IntVec3 bestSowCell = IntVec3.Invalid;
|
||||
Job bestSowJob = null;
|
||||
float bestSowDistSq = float.MaxValue;
|
||||
|
||||
foreach (Zone zone in pawn.Map.zoneManager.AllZones)
|
||||
{
|
||||
if (zone is Zone_Growing growingZone)
|
||||
{
|
||||
ThingDef wantedPlant = growingZone.GetPlantDefToGrow();
|
||||
if (wantedPlant == null) continue;
|
||||
|
||||
foreach (IntVec3 cell in growingZone.Cells)
|
||||
{
|
||||
float distSq = pawn.Position.DistanceToSquared(cell);
|
||||
if (pawn.CanReach(cell, PathEndMode.ClosestTouch, Danger.Deadly))
|
||||
{
|
||||
Job potentialJob = scanner.JobOnCell(pawn, cell);
|
||||
if (potentialJob != null)
|
||||
{
|
||||
if (potentialJob.def == JobDefOf.CutPlant || potentialJob.def == JobDefOf.HaulToContainer || potentialJob.def == JobDefOf.HaulToCell)
|
||||
{
|
||||
if (distSq < bestClearDistSq)
|
||||
{
|
||||
bestClearDistSq = distSq;
|
||||
bestClearJob = potentialJob;
|
||||
}
|
||||
}
|
||||
else if (potentialJob.def == JobDefOf.Sow)
|
||||
{
|
||||
if (distSq < bestSowDistSq)
|
||||
{
|
||||
bestSowDistSq = distSq;
|
||||
bestSowJob = potentialJob;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 优先返回清理 Job
|
||||
if (bestClearJob != null)
|
||||
{
|
||||
return bestClearJob;
|
||||
}
|
||||
// 其次返回播种 Job
|
||||
if (bestSowJob != null)
|
||||
{
|
||||
return bestSowJob;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
21
Source/ArachnaeSwarm/MainHarmony.cs
Normal file
21
Source/ArachnaeSwarm/MainHarmony.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using Verse;
|
||||
using HarmonyLib;
|
||||
using System.Reflection;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
// [StaticConstructorOnStartup] 属性确保这个类的静态构造函数在游戏启动时被调用
|
||||
[StaticConstructorOnStartup]
|
||||
public static class MainHarmony
|
||||
{
|
||||
static MainHarmony()
|
||||
{
|
||||
// 创建一个 Harmony 实例。ID 应该是唯一的,通常使用 "作者.Mod名称" 的格式。
|
||||
var harmony = new Harmony("com.kalospacer.arachnaeswarm");
|
||||
|
||||
// Harmony 会自动扫描当前整个程序集(我们的 .dll 文件),
|
||||
// 寻找所有带有 [HarmonyPatch] 属性的类,并应用它们。
|
||||
harmony.PatchAll(Assembly.GetExecutingAssembly());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
using HarmonyLib;
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
using System.Reflection; // For MethodInfo
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
[StaticConstructorOnStartup]
|
||||
public static class QualityUtilityPatch
|
||||
{
|
||||
static QualityUtilityPatch()
|
||||
{
|
||||
var harmony = new Harmony("com.yourname.qualityutilitypatch");
|
||||
harmony.Patch(
|
||||
original: AccessTools.Method(typeof(QualityUtility), nameof(QualityUtility.GenerateQualityCreatedByPawn), new[] { typeof(Pawn), typeof(SkillDef), typeof(bool) }),
|
||||
prefix: new HarmonyMethod(typeof(QualityUtilityPatch), nameof(GenerateQualityCreatedByPawn_Prefix))
|
||||
);
|
||||
}
|
||||
|
||||
public static bool GenerateQualityCreatedByPawn_Prefix(Pawn pawn, SkillDef relevantSkill, bool consumeInspiration, ref QualityCategory __result)
|
||||
{
|
||||
// 检查当前 Pawn 是否是我们的自定义动物(通过检查其 ThingDef 是否拥有 CompProperties_WorkForNonMechs)
|
||||
if (pawn != null && pawn.def.comps != null && ShouldEnableWorkSystem(pawn))
|
||||
{
|
||||
// 如果是,强制使用 mechFixedSkillLevel
|
||||
int relevantSkillLevel = pawn.RaceProps.mechFixedSkillLevel;
|
||||
bool inspired = consumeInspiration && pawn.InspirationDef == InspirationDefOf.Inspired_Creativity;
|
||||
|
||||
// 调用 QualityUtility.GenerateQualityCreatedByPawn 的 int 重载
|
||||
__result = QualityUtility.GenerateQualityCreatedByPawn(relevantSkillLevel, inspired);
|
||||
|
||||
// 消耗灵感(如果适用)
|
||||
if (inspired)
|
||||
{
|
||||
pawn.mindState.inspirationHandler.EndInspiration(InspirationDefOf.Inspired_Creativity);
|
||||
}
|
||||
|
||||
// 返回 false,跳过原版方法执行
|
||||
return false;
|
||||
}
|
||||
|
||||
// 返回 true,执行原版方法
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool ShouldEnableWorkSystem(Pawn pawn)
|
||||
{
|
||||
// 检查 ThingDef 中是否有 CompProperties_WorkForNonMechs 配置
|
||||
if (pawn.def.comps != null)
|
||||
{
|
||||
foreach (var compProperties in pawn.def.comps)
|
||||
{
|
||||
if (compProperties is CompProperties_WorkForNonMechs)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
using HarmonyLib;
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
using Verse.AI;
|
||||
using System.Reflection;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
[StaticConstructorOnStartup]
|
||||
public static class Patch_WorkGivers_Growing
|
||||
{
|
||||
static Patch_WorkGivers_Growing()
|
||||
{
|
||||
var harmony = new Harmony("com.yourname.workgiversgrowingpatch");
|
||||
|
||||
// Patch WorkGiver_GrowerSow.JobOnCell
|
||||
harmony.Patch(
|
||||
original: AccessTools.Method(typeof(WorkGiver_GrowerSow), nameof(WorkGiver_GrowerSow.JobOnCell)),
|
||||
prefix: new HarmonyMethod(typeof(Patch_WorkGivers_Growing), nameof(JobOnCell_GrowerSow_Prefix))
|
||||
);
|
||||
|
||||
// Patch JobDriver_Deconstruct.TickActionInterval
|
||||
harmony.Patch(
|
||||
original: AccessTools.Method(typeof(JobDriver_Deconstruct), "TickActionInterval"),
|
||||
prefix: new HarmonyMethod(typeof(Patch_WorkGivers_Growing), nameof(TickActionInterval_Deconstruct_Prefix))
|
||||
);
|
||||
}
|
||||
|
||||
public static bool JobOnCell_GrowerSow_Prefix(Pawn pawn, IntVec3 c, ref Job __result, WorkGiver_GrowerSow __instance)
|
||||
{
|
||||
// 检查是否是我们的自定义动物,并且它不是真正的机械体 (因为真正的机械体原版会处理)
|
||||
if (ShouldEnableWorkSystem(pawn) && !pawn.RaceProps.IsMechanoid)
|
||||
{
|
||||
// 使用反射获取 WorkGiver_GrowerSow 实例的 wantedPlantDef 字段
|
||||
ThingDef wantedPlantDef = (ThingDef)AccessTools.Field(typeof(WorkGiver_Grower), "wantedPlantDef").GetValue(__instance);
|
||||
|
||||
if (wantedPlantDef == null)
|
||||
{
|
||||
__result = null;
|
||||
return false; // 跳过原版方法
|
||||
}
|
||||
|
||||
// 强制使用 mechFixedSkillLevel 作为相关技能等级
|
||||
int relevantSkillLevel = pawn.RaceProps.mechFixedSkillLevel;
|
||||
|
||||
// 然后进行原始的 sowMinSkill 检查
|
||||
if (wantedPlantDef.plant.sowMinSkill > relevantSkillLevel)
|
||||
{
|
||||
__result = null; // 技能不足,不生成 Job
|
||||
return false; // 跳过原版方法
|
||||
}
|
||||
|
||||
// 如果技能足够,让原版方法继续执行,处理其他复杂的检查
|
||||
// 注意:这里我们只处理了技能检查部分,其他逻辑仍然依赖原版方法。
|
||||
// 如果原版方法在其他地方再次访问 pawn.skills,仍然可能出错。
|
||||
// 但这是最直接的修复方法,避免了完全复制整个原始方法。
|
||||
}
|
||||
|
||||
return true; // 执行原版方法
|
||||
}
|
||||
|
||||
public static bool TickActionInterval_Deconstruct_Prefix(JobDriver_Deconstruct __instance, Pawn ___pawn, int delta)
|
||||
{
|
||||
// 检查是否是我们的自定义动物,并且它不是真正的机械体
|
||||
if (ShouldEnableWorkSystem(___pawn) && !___pawn.RaceProps.IsMechanoid)
|
||||
{
|
||||
// 模拟技能学习,避免访问 pawn.skills 导致 NullReferenceException
|
||||
// 这里我们不实际增加经验值,只是模拟原版方法的行为
|
||||
// 避免了对 pawn.skills 的访问
|
||||
if (__instance.Building.def.CostListAdjusted(__instance.Building.Stuff).Count > 0)
|
||||
{
|
||||
// 可以选择在这里添加一些日志,以便调试
|
||||
// Log.Message($"Animal {___pawn.LabelShort} is deconstructing, simulating skill gain.");
|
||||
}
|
||||
return false; // 跳过原版方法
|
||||
}
|
||||
return true; // 执行原版方法
|
||||
}
|
||||
|
||||
private static bool ShouldEnableWorkSystem(Pawn pawn)
|
||||
{
|
||||
// 检查 ThingDef 中是否有 CompProperties_WorkForNonMechs 配置
|
||||
if (pawn.def.comps != null)
|
||||
{
|
||||
foreach (var compProperties in pawn.def.comps)
|
||||
{
|
||||
if (compProperties is CompProperties_WorkForNonMechs)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using Verse;
|
||||
using Verse.AI;
|
||||
using RimWorld;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
// 将 DefOf 类放在这里,以便在命名空间内共享
|
||||
[DefOf]
|
||||
public static class ARA_TrainableDefOf
|
||||
{
|
||||
public static TrainableDef ARA_Sowing;
|
||||
|
||||
static ARA_TrainableDefOf()
|
||||
{
|
||||
DefOfHelper.EnsureInitializedInCtor(typeof(ARA_TrainableDefOf));
|
||||
}
|
||||
}
|
||||
|
||||
// 这个新的条件节点将检查动物是否应该执行任何农业工作(播种或切割/收获)
|
||||
public class ThinkNode_ConditionalAnimalShouldDoGrowingWork : ThinkNode_Conditional
|
||||
{
|
||||
protected override bool Satisfied(Pawn pawn)
|
||||
{
|
||||
// 首先,进行安全检查,确保 pawn.training 存在
|
||||
if (pawn.training == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查动物是否学会并被允许执行“播种”工作
|
||||
bool canSow = pawn.training.HasLearned(ARA_TrainableDefOf.ARA_Sowing) &&
|
||||
pawn.training.GetWanted(ARA_TrainableDefOf.ARA_Sowing);
|
||||
|
||||
// 现在只需要检查播种技能,因为切割功能已合并
|
||||
return canSow;
|
||||
}
|
||||
}
|
||||
}
|
||||
48
Source/ArachnaeSwarm/TrainingSystem_Patcher.cs
Normal file
48
Source/ArachnaeSwarm/TrainingSystem_Patcher.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using HarmonyLib;
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
// Patcher 1: 阻止训练退化
|
||||
[HarmonyPatch(typeof(Pawn_TrainingTracker), "TrainingTrackerTickRare")]
|
||||
public static class Patch_TrainingTracker_TickRare
|
||||
{
|
||||
public static bool Prefix(Pawn_TrainingTracker __instance)
|
||||
{
|
||||
Pawn pawn = Traverse.Create(__instance).Field("pawn").GetValue<Pawn>();
|
||||
if (pawn == null) return true;
|
||||
|
||||
var comp = pawn.GetComp<CompAdvancedTraining>();
|
||||
if (comp != null && comp.Props.disableAllSkillDecay)
|
||||
{
|
||||
return false; // 阻止原版方法运行
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Patcher 2: 阻止特定技能的衰减
|
||||
[HarmonyPatch(typeof(SkillRecord), "Interval")]
|
||||
public static class Patch_SkillRecord_Interval
|
||||
{
|
||||
// 使用 __instance 来获取 SkillRecord 对象, __pawn 为 SkillRecord 内部的私有字段
|
||||
public static bool Prefix(SkillRecord __instance, Pawn ___pawn)
|
||||
{
|
||||
if (___pawn == null) return true;
|
||||
|
||||
var comp = ___pawn.GetComp<CompAdvancedTraining>();
|
||||
if (comp == null || comp.Props.skillLevels.NullOrEmpty())
|
||||
{
|
||||
return true; // 没有组件或配置,正常执行原版衰减
|
||||
}
|
||||
|
||||
// 检查全局开关:如果设置了 disableAllSkillDecay 为 true,则阻止衰减
|
||||
if (comp.Props.disableAllSkillDecay)
|
||||
{
|
||||
return false; // 阻止原版 Interval 方法的执行
|
||||
}
|
||||
return true; // 正常执行原版衰减
|
||||
}
|
||||
}
|
||||
}
|
||||
227
Source/ArachnaeSwarm/WorkGiver_ArachnaeSow.cs
Normal file
227
Source/ArachnaeSwarm/WorkGiver_ArachnaeSow.cs
Normal file
@@ -0,0 +1,227 @@
|
||||
using System.Collections.Generic;
|
||||
using Verse;
|
||||
using Verse.AI;
|
||||
using RimWorld;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
public class WorkGiver_ArachnaeSow : WorkGiver_Grower
|
||||
{
|
||||
protected static string CantSowCavePlantBecauseOfLightTrans;
|
||||
protected static string CantSowCavePlantBecauseUnroofedTrans;
|
||||
|
||||
public override PathEndMode PathEndMode => PathEndMode.ClosestTouch;
|
||||
|
||||
public static void ResetStaticData()
|
||||
{
|
||||
CantSowCavePlantBecauseOfLightTrans = "CantSowCavePlantBecauseOfLight".Translate();
|
||||
CantSowCavePlantBecauseUnroofedTrans = "CantSowCavePlantBecauseUnroofed".Translate();
|
||||
}
|
||||
|
||||
protected override bool ExtraRequirements(IPlantToGrowSettable settable, Pawn pawn)
|
||||
{
|
||||
if (!settable.CanAcceptSowNow())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
IntVec3 c;
|
||||
if (settable is Zone_Growing zone_Growing)
|
||||
{
|
||||
if (!zone_Growing.allowSow)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
c = zone_Growing.Cells[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
c = ((Thing)settable).Position;
|
||||
}
|
||||
ThingDef wantedPlantDef = WorkGiver_Grower.CalculateWantedPlantDef(c, pawn.Map);
|
||||
if (wantedPlantDef == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public override Job JobOnCell(Pawn pawn, IntVec3 c, bool forced = false)
|
||||
{
|
||||
Map map = pawn.Map;
|
||||
if (c.GetVacuum(pawn.Map) >= 0.5f)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// 直接计算 wantedPlantDef
|
||||
ThingDef wantedPlantDefLocal = WorkGiver_Grower.CalculateWantedPlantDef(c, map);
|
||||
if (wantedPlantDefLocal == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!PlantUtility.GrowthSeasonNow(c, map, wantedPlantDefLocal))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
List<Thing> thingList = c.GetThingList(map);
|
||||
Zone_Growing zone_Growing = c.GetZone(map) as Zone_Growing;
|
||||
bool flag = false;
|
||||
for (int i = 0; i < thingList.Count; i++)
|
||||
{
|
||||
Thing thing = thingList[i];
|
||||
if (thing.def == wantedPlantDef)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if ((thing is Blueprint || thing is Frame) && thing.Faction == pawn.Faction)
|
||||
{
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
if (flag)
|
||||
{
|
||||
Thing edifice = c.GetEdifice(map);
|
||||
if (edifice == null || edifice.def.fertility < 0f)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (wantedPlantDefLocal.plant.diesToLight)
|
||||
{
|
||||
if (!c.Roofed(map) && !map.GameConditionManager.IsAlwaysDarkOutside)
|
||||
{
|
||||
JobFailReason.Is(CantSowCavePlantBecauseUnroofedTrans);
|
||||
return null;
|
||||
}
|
||||
if (map.glowGrid.GroundGlowAt(c, ignoreCavePlants: true) > 0f)
|
||||
{
|
||||
JobFailReason.Is(CantSowCavePlantBecauseOfLightTrans);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (wantedPlantDefLocal.plant.interferesWithRoof && c.Roofed(pawn.Map))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
Plant plant = c.GetPlant(map);
|
||||
if (plant != null) // 只要地块上有植物
|
||||
{
|
||||
// 如果地块上的植物不是我们想要种植的植物,就割除
|
||||
if (plant.def != wantedPlantDefLocal)
|
||||
{
|
||||
if (!pawn.CanReserve(plant, 1, -1, null, forced) || plant.IsForbidden(pawn))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (zone_Growing != null && !zone_Growing.allowCut)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (!forced && plant.TryGetComp<CompPlantPreventCutting>(out var comp) && comp.PreventCutting)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return JobMaker.MakeJob(JobDefOf.CutPlant, plant);
|
||||
}
|
||||
// 如果地块上的植物是我们想要种植的植物,并且它阻碍了相邻播种,则不割除
|
||||
// 因为它已经是我们想要种植的植物了
|
||||
if (plant.def.plant.blockAdjacentSow)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Thing thing2 = PlantUtility.AdjacentSowBlocker(wantedPlantDefLocal, c, map);
|
||||
if (thing2 != null)
|
||||
{
|
||||
if (thing2 is Plant plant2)
|
||||
{
|
||||
// 如果阻碍播种的是植物,并且不是我们想要种植的植物,就割除
|
||||
if (plant2.def != wantedPlantDefLocal)
|
||||
{
|
||||
if (pawn.CanReserveAndReach(plant2, PathEndMode.Touch, Danger.Deadly, 1, -1, null, forced) && !plant2.IsForbidden(pawn))
|
||||
{
|
||||
IPlantToGrowSettable plantToGrowSettable = plant2.Position.GetPlantToGrowSettable(plant2.Map);
|
||||
if (plantToGrowSettable == null || plantToGrowSettable.GetPlantDefToGrow() != plant2.def)
|
||||
{
|
||||
Zone_Growing zone_Growing2 = c.GetZone(map) as Zone_Growing;
|
||||
Zone_Growing zone_Growing3 = c.GetZone(map) as Zone_Growing;
|
||||
if ((zone_Growing2 != null && !zone_Growing2.allowCut) || (zone_Growing3 != null && !zone_Growing3.allowCut && plant2.def == zone_Growing3.GetPlantDefToGrow()))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (!forced && thing2.TryGetComp(out CompPlantPreventCutting comp2) && comp2.PreventCutting)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (PlantUtility.TreeMarkedForExtraction(plant2))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return JobMaker.MakeJob(JobDefOf.CutPlant, plant2);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (thing2.def.EverHaulable)
|
||||
{
|
||||
return HaulAIUtility.HaulAsideJobFor(pawn, thing2);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (wantedPlantDefLocal.plant.sowMinSkill > 0 && ((pawn.skills != null && pawn.skills.GetSkill(SkillDefOf.Plants).Level < wantedPlantDefLocal.plant.sowMinSkill) || (pawn.IsColonyMech && pawn.RaceProps.mechFixedSkillLevel < wantedPlantDefLocal.plant.sowMinSkill)))
|
||||
{
|
||||
JobFailReason.Is("UnderAllowedSkill".Translate(wantedPlantDefLocal.plant.sowMinSkill), def.label);
|
||||
return null;
|
||||
}
|
||||
for (int j = 0; j < thingList.Count; j++)
|
||||
{
|
||||
Thing thing3 = thingList[j];
|
||||
if (!thing3.def.BlocksPlanting())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!pawn.CanReserve(thing3, 1, -1, null, forced))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (thing3.def.category == ThingCategory.Plant)
|
||||
{
|
||||
// 如果阻碍播种的是植物,并且不是我们想要种植的植物,就割除
|
||||
if (thing3.def != wantedPlantDefLocal)
|
||||
{
|
||||
if (thing3.IsForbidden(pawn))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (zone_Growing != null && !zone_Growing.allowCut)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (!forced && thing3.TryGetComp<CompPlantPreventCutting>(out var comp3) && comp3.PreventCutting)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (PlantUtility.TreeMarkedForExtraction(thing3))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return JobMaker.MakeJob(JobDefOf.CutPlant, thing3);
|
||||
}
|
||||
}
|
||||
else if (thing3.def.EverHaulable)
|
||||
{
|
||||
return HaulAIUtility.HaulAsideJobFor(pawn, thing3);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (!wantedPlantDefLocal.CanNowPlantAt(c, map) || !PlantUtility.GrowthSeasonNow(c, map, wantedPlantDefLocal) || !pawn.CanReserve(c, 1, -1, null, forced))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
Job job = JobMaker.MakeJob(JobDefOf.Sow, c);
|
||||
job.plantDefToSow = wantedPlantDefLocal;
|
||||
return job;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
// <autogenerated />
|
||||
using System;
|
||||
using System.Reflection;
|
||||
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
70a1ccfa141c7c82eb05a9fac71c32df86f6b34332995c92bb7aac69bc46394b
|
||||
@@ -1,2 +0,0 @@
|
||||
C:\Steam\steamapps\common\RimWorld\Mods\ArachnaeSwarm\Source\ArachnaeSwarm\obj\Debug\ArachnaeSwarm.csproj.AssemblyReference.cache
|
||||
C:\Steam\steamapps\common\RimWorld\Mods\ArachnaeSwarm\Source\ArachnaeSwarm\obj\Debug\ArachnaeSwarm.csproj.CoreCompileInputs.cache
|
||||
Reference in New Issue
Block a user