失败
This commit is contained in:
Binary file not shown.
@@ -22,6 +22,15 @@
|
||||
</trainables>
|
||||
</li>
|
||||
<li Class="ArachnaeSwarm.CompProperties_AnimalWorkSettings">
|
||||
<!-- 定义技能等级 -->
|
||||
<skillLevels>
|
||||
<li>
|
||||
<skill>Plants</skill>
|
||||
<level>10</level>
|
||||
<disableDecay>true</disableDecay>
|
||||
</li>
|
||||
</skillLevels>
|
||||
<!-- 定义训练项与工作类型的映射 -->
|
||||
<workTypeMap>
|
||||
<li>
|
||||
<trainable>ARA_Sowing</trainable>
|
||||
|
||||
@@ -107,7 +107,7 @@
|
||||
<subNodes>
|
||||
<!-- Wild insects with no lord will fight nearby enemies -->
|
||||
<li Class="JobGiver_AIFightEnemies">
|
||||
<targetAcquireRadius>30</targetAcquireRadius> <!-- Same as DefendAndExpandHive -->
|
||||
<targetAcquireRadius>30</targetAcquireRadius> <!-- Same as DefendAndExpandHive -->
|
||||
<targetKeepRadius>35</targetKeepRadius>
|
||||
</li>
|
||||
|
||||
@@ -324,6 +324,45 @@
|
||||
</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_ConditionalAnimalShouldSow">
|
||||
<subNodes>
|
||||
<!-- 优先执行收割 -->
|
||||
<li Class="ArachnaeSwarm.JobGiver_PlantHarvest" />
|
||||
</subNodes>
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
|
||||
<!-- OUR CUSTOM LOGIC INJECTION FOR PLANT SOWING -->
|
||||
<li Class="ThinkNode_ConditionalTrainableCompleted">
|
||||
<trainable>ARA_Sowing</trainable>
|
||||
<subNodes>
|
||||
<li Class="ArachnaeSwarm.ThinkNode_ConditionalAnimalShouldSow">
|
||||
<subNodes>
|
||||
<!-- 然后执行播种 -->
|
||||
<li Class="ArachnaeSwarm.JobGiver_PlantSow" />
|
||||
</subNodes>
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
|
||||
<!-- OUR CUSTOM LOGIC INJECTION FOR PLANT CUTTING -->
|
||||
<li Class="ThinkNode_ConditionalTrainableCompleted">
|
||||
<trainable>ARA_PlantCutting</trainable>
|
||||
<subNodes>
|
||||
<li Class="ArachnaeSwarm.ThinkNode_ConditionalAnimalShouldPlantCut">
|
||||
<subNodes>
|
||||
<!-- 最后执行割除 -->
|
||||
<li Class="ArachnaeSwarm.JobGiver_PlantCutting" />
|
||||
</subNodes>
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
|
||||
</subNodes>
|
||||
</li>
|
||||
|
||||
@@ -335,31 +374,6 @@
|
||||
<insertTag>Insect_PreWander</insertTag>
|
||||
</li>
|
||||
|
||||
<!-- OUR CUSTOM LOGIC INJECTION -->
|
||||
<li Class="ArachnaeSwarm.ThinkNode_ConditionalAnimalShouldSow">
|
||||
<subNodes>
|
||||
<li Class="ThinkNode_Tagger">
|
||||
<tagToGive>TrainedAnimalBehavior</tagToGive>
|
||||
<subNodes>
|
||||
<li Class="RimWorld.JobGiver_Work" />
|
||||
</subNodes>
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
<!-- END OF CUSTOM LOGIC -->
|
||||
|
||||
<!-- OUR CUSTOM LOGIC INJECTION FOR PLANT CUTTING -->
|
||||
<li Class="ArachnaeSwarm.ThinkNode_ConditionalAnimalShouldPlantCut">
|
||||
<subNodes>
|
||||
<li Class="ThinkNode_Tagger">
|
||||
<tagToGive>TrainedAnimalBehavior</tagToGive>
|
||||
<subNodes>
|
||||
<li Class="RimWorld.JobGiver_Work" />
|
||||
</subNodes>
|
||||
</li>
|
||||
</subNodes>
|
||||
</li>
|
||||
|
||||
<!-- Tame insect: wander near colony if possible -->
|
||||
<li Class="ThinkNode_ConditionalOfPlayerFaction">
|
||||
<subNodes>
|
||||
|
||||
@@ -96,8 +96,10 @@
|
||||
<Compile Include="CompNoTrainingDecay.cs" />
|
||||
<Compile Include="ThinkNode_ConditionalAnimalShouldSow.cs" />
|
||||
<Compile Include="ThinkNode_ConditionalAnimalShouldPlantCut.cs" />
|
||||
<Compile Include="CompProperties_AnimalWorkSettings.cs" />
|
||||
<Compile Include="CompAnimalWorkSettings.cs" />
|
||||
<Compile Include="JobGiver_PlantCutting.cs" />
|
||||
<Compile Include="JobGiver_PlantHarvest.cs" />
|
||||
<Compile Include="JobGiver_PlantSow.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="WULA_AutoMechCarrier\CompAutoMechCarrier.cs" />
|
||||
|
||||
@@ -4,6 +4,35 @@ using RimWorld;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
public class CompProperties_AnimalWorkSettings : CompProperties
|
||||
{
|
||||
// 使用列表存储键值对,因为RimWorld的XML解析器对字典有特殊要求
|
||||
public List<WorkTypeMapEntry> workTypeMap = new List<WorkTypeMapEntry>();
|
||||
// 定义动物的技能等级
|
||||
public List<SkillLevelEntry> skillLevels = new List<SkillLevelEntry>();
|
||||
|
||||
public CompProperties_AnimalWorkSettings()
|
||||
{
|
||||
this.compClass = typeof(CompAnimalWorkSettings);
|
||||
}
|
||||
}
|
||||
|
||||
// 定义键值对的结构
|
||||
public class WorkTypeMapEntry
|
||||
{
|
||||
public TrainableDef trainable;
|
||||
public WorkTypeDef workType;
|
||||
}
|
||||
|
||||
// 定义技能和等级的结构
|
||||
public class SkillLevelEntry
|
||||
{
|
||||
public SkillDef skill;
|
||||
public int level = 0; // 默认等级为0
|
||||
// 可选:是否阻止技能衰减
|
||||
public bool disableDecay = true;
|
||||
}
|
||||
|
||||
public class CompAnimalWorkSettings : ThingComp
|
||||
{
|
||||
public CompProperties_AnimalWorkSettings Props => (CompProperties_AnimalWorkSettings)this.props;
|
||||
@@ -15,35 +44,54 @@ namespace ArachnaeSwarm
|
||||
Pawn pawn = this.parent as Pawn;
|
||||
if (pawn == null) return;
|
||||
|
||||
// 关键:如果 pawn 没有 workSettings,则为其创建一个
|
||||
// 1. 确保 workSettings 存在
|
||||
if (pawn.workSettings == null)
|
||||
{
|
||||
// Log.Message($"Initializing Pawn_WorkSettings for animal: {pawn.LabelShort}");
|
||||
pawn.workSettings = new Pawn_WorkSettings(pawn);
|
||||
pawn.workSettings.EnableAndInitializeIfNotAlreadyInitialized();
|
||||
|
||||
// 注意:Pawn_WorkSettings 的构造函数和 EnableAndInitializeIfNotAlreadyInitialized()
|
||||
// 通常会处理所有可用的 WorkTypeDef,并将它们的优先级初始化为 0。
|
||||
// 这正是我们想要的初始状态。具体的优先级将由其他逻辑(如你的 CompInstantTrain 或 ThinkNode)来设置。
|
||||
}
|
||||
|
||||
// 设置工作优先级
|
||||
if (pawn.workSettings != null)
|
||||
// 2. 设置技能等级 (如果定义了)
|
||||
if (pawn.skills != null && Props.skillLevels != null)
|
||||
{
|
||||
foreach (var entry in Props.skillLevels)
|
||||
{
|
||||
if (entry.skill != null)
|
||||
{
|
||||
var skillRecord = pawn.skills.GetSkill(entry.skill);
|
||||
if (skillRecord != null)
|
||||
{
|
||||
// 设置技能等级
|
||||
skillRecord.Level = entry.level;
|
||||
|
||||
// 可选:阻止技能衰减
|
||||
// 在新版本中,将激情设置为 None 可以阻止自然增长和衰减
|
||||
if (entry.disableDecay)
|
||||
{
|
||||
skillRecord.passion = Passion.None;
|
||||
// 注意:仅设置 passion=None 可能不足以完全阻止衰减
|
||||
// 如果仍有问题,可能需要在 ThinkNode 或其他地方定期重置技能等级
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 设置工作优先级 (如果定义了映射关系)
|
||||
// 这里我们不检查 pawn.training.HasLearned,因为这个组件只负责提供"能力"
|
||||
// 具体是否执行工作由 ThinkNode_Conditional 控制
|
||||
if (pawn.workSettings != null && Props.workTypeMap != null)
|
||||
{
|
||||
foreach (var entry in Props.workTypeMap)
|
||||
{
|
||||
TrainableDef trainable = entry.trainable;
|
||||
WorkTypeDef workType = entry.workType;
|
||||
|
||||
// 检查动物是否学会了对应的 Trainable
|
||||
if (pawn.training != null && pawn.training.HasLearned(trainable))
|
||||
if (entry.trainable != null && entry.workType != null)
|
||||
{
|
||||
// 设置一个默认的非零优先级,例如 3 (Medium)
|
||||
// 真正的"开关"由 ThinkNode_Conditional 的 GetWanted 控制
|
||||
pawn.workSettings.SetPriority(workType, 3);
|
||||
// 为相关工作类型设置默认优先级
|
||||
// 实际是否执行由 ThinkNode_Conditional 的 GetWanted 控制
|
||||
pawn.workSettings.SetPriority(entry.workType, 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,36 @@ using RimWorld;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
public class CompInstantTrain : ThingComp
|
||||
{
|
||||
public CompProperties_InstantTrain Props => (CompProperties_InstantTrain)this.props;
|
||||
|
||||
public override void PostSpawnSetup(bool respawningAfterLoad)
|
||||
{
|
||||
base.PostSpawnSetup(respawningAfterLoad);
|
||||
|
||||
// 只在初次生成时执行,加载存档时不需要重新训练
|
||||
if (respawningAfterLoad) return;
|
||||
|
||||
Pawn pawn = this.parent as Pawn;
|
||||
if (pawn?.training == null) return;
|
||||
|
||||
// 瞬间训练所有在 Props 中指定的技能
|
||||
foreach (var trainable in Props.trainables)
|
||||
{
|
||||
if (trainable != null && !pawn.training.HasLearned(trainable))
|
||||
{
|
||||
// 注意:直接调用 Train 方法,它会内部处理 SetWanted 的逻辑
|
||||
// 原版的 SetWanted 方法可能已被移除或变为内部方法
|
||||
pawn.training.Train(trainable, null, true); // true表示瞬间完成
|
||||
}
|
||||
}
|
||||
|
||||
// CompInstantTrain 的职责到此结束
|
||||
// 工作优先级设置由 CompAnimalWorkSettings 负责
|
||||
}
|
||||
}
|
||||
|
||||
public class CompProperties_InstantTrain : CompProperties
|
||||
{
|
||||
public List<TrainableDef> trainables = new List<TrainableDef>();
|
||||
@@ -13,32 +43,4 @@ namespace ArachnaeSwarm
|
||||
this.compClass = typeof(CompInstantTrain);
|
||||
}
|
||||
}
|
||||
|
||||
public class CompInstantTrain : ThingComp
|
||||
{
|
||||
public CompProperties_InstantTrain Props => (CompProperties_InstantTrain)this.props;
|
||||
|
||||
public override void PostSpawnSetup(bool respawningAfterLoad)
|
||||
{
|
||||
base.PostSpawnSetup(respawningAfterLoad);
|
||||
|
||||
if (!respawningAfterLoad) // 只在初次生成时执行
|
||||
{
|
||||
Pawn pawn = this.parent as Pawn;
|
||||
if (pawn == null || pawn.training == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 瞬间训练技能
|
||||
foreach (TrainableDef trainableDef in Props.trainables)
|
||||
{
|
||||
if (!pawn.training.HasLearned(trainableDef))
|
||||
{
|
||||
pawn.training.Train(trainableDef, null, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Verse;
|
||||
using RimWorld;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
public class CompProperties_AnimalWorkSettings : CompProperties
|
||||
{
|
||||
// 使用列表存储键值对,因为RimWorld的XML解析器对字典有特殊要求
|
||||
public List<WorkTypeMapEntry> workTypeMap = new List<WorkTypeMapEntry>();
|
||||
|
||||
// 可以在这里添加一些配置选项,比如默认优先级等,但现在简单起见,可以留空或只做基本初始化
|
||||
public CompProperties_AnimalWorkSettings()
|
||||
{
|
||||
this.compClass = typeof(CompAnimalWorkSettings);
|
||||
}
|
||||
}
|
||||
|
||||
// 定义键值对的结构
|
||||
public class WorkTypeMapEntry
|
||||
{
|
||||
public TrainableDef trainable;
|
||||
public WorkTypeDef workType;
|
||||
}
|
||||
}
|
||||
56
Source/ArachnaeSwarm/JobGiver_PlantCutting.cs
Normal file
56
Source/ArachnaeSwarm/JobGiver_PlantCutting.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using System.Collections.Generic;
|
||||
using Verse;
|
||||
using Verse.AI;
|
||||
using RimWorld;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
public class JobGiver_PlantCutting : ThinkNode_JobGiver
|
||||
{
|
||||
private WorkGiverDef cutWorkGiverDef = null;
|
||||
private bool triedToLoad = false;
|
||||
|
||||
protected override Job TryGiveJob(Pawn pawn)
|
||||
{
|
||||
if (!triedToLoad || cutWorkGiverDef == null)
|
||||
{
|
||||
triedToLoad = true;
|
||||
cutWorkGiverDef = DefDatabase<WorkGiverDef>.GetNamed("PlantsCut", false);
|
||||
if (cutWorkGiverDef == null)
|
||||
{
|
||||
Log.ErrorOnce("[ArachnaeSwarm] Could not find WorkGiverDef named 'PlantsCut'. Plant Cutting will not work.", 847595);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (cutWorkGiverDef?.Worker is WorkGiver_Scanner workGiver)
|
||||
{
|
||||
// ... (你的逻辑,调用 workGiver 的方法) ...
|
||||
IEnumerable<Thing> potentialPlants = workGiver.PotentialWorkThingsGlobal(pawn);
|
||||
// ... (后续逻辑,确保都用 workGiver 实例) ...
|
||||
if (!workGiver.ShouldSkip(pawn) && workGiver.MissingRequiredCapacity(pawn) == null)
|
||||
{
|
||||
return workGiver.NonScanJob(pawn);
|
||||
}
|
||||
// ...
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Warning($"[ArachnaeSwarm] WorkGiverDef 'PlantsCut' Worker is not a WorkGiver_Scanner for pawn {pawn?.LabelShort ?? "NULL"}.");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// ... (Validator 方法也需要修改) ...
|
||||
private static bool Validator(Pawn pawn, WorkGiver_Scanner workGiver, Thing t)
|
||||
{
|
||||
if (t.IsForbidden(pawn))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// if (workGiver.ShouldSkip(pawn)) { return false; } // 这个检查可以保留或去掉
|
||||
return workGiver.JobOnThing(pawn, t) != null; // 使用传入的实例
|
||||
}
|
||||
}
|
||||
}
|
||||
63
Source/ArachnaeSwarm/JobGiver_PlantHarvest.cs
Normal file
63
Source/ArachnaeSwarm/JobGiver_PlantHarvest.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using System.Collections.Generic;
|
||||
using Verse;
|
||||
using Verse.AI;
|
||||
using RimWorld;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
public class JobGiver_PlantHarvest : ThinkNode_JobGiver
|
||||
{
|
||||
// 声明变量,但不立即初始化
|
||||
private WorkGiverDef harvestWorkGiverDef = null;
|
||||
// 用一个标志位确保只尝试加载一次
|
||||
private bool triedToLoad = false;
|
||||
|
||||
protected override Job TryGiveJob(Pawn pawn)
|
||||
{
|
||||
// 如果还没尝试加载,或者之前加载失败了
|
||||
if (!triedToLoad || harvestWorkGiverDef == null)
|
||||
{
|
||||
triedToLoad = true; // 标记为已尝试
|
||||
// 在需要时才查找 Def
|
||||
harvestWorkGiverDef = DefDatabase<WorkGiverDef>.GetNamed("GrowerHarvest", false);
|
||||
if (harvestWorkGiverDef == null)
|
||||
{
|
||||
// 这个 Log 可以保留,方便调试
|
||||
Log.ErrorOnce("[ArachnaeSwarm] Could not find WorkGiverDef named 'GrowerHarvest'. Harvesting will not work.", 847593);
|
||||
return null; // 找不到就直接返回
|
||||
}
|
||||
}
|
||||
|
||||
// 现在可以安全地使用 harvestWorkGiverDef 了
|
||||
if (harvestWorkGiverDef?.Worker is WorkGiver_Scanner workGiver)
|
||||
{
|
||||
// ... (你原来的逻辑) ...
|
||||
IEnumerable<Thing> potentialPlants = workGiver.PotentialWorkThingsGlobal(pawn);
|
||||
// ... (后续逻辑) ...
|
||||
// 确保调用的是 workGiver (已解析的实例)
|
||||
if (!workGiver.ShouldSkip(pawn) && workGiver.MissingRequiredCapacity(pawn) == null)
|
||||
{
|
||||
return workGiver.NonScanJob(pawn);
|
||||
}
|
||||
// ... 其他逻辑 ...
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Warning($"[ArachnaeSwarm] WorkGiverDef 'GrowerHarvest' Worker is not a WorkGiver_Scanner for pawn {pawn?.LabelShort ?? "NULL"}.");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// ... (Validator 方法也需要确保使用传入的 workGiver 实例) ...
|
||||
private static bool HarvestValidator(Pawn pawn, WorkGiver_Scanner workGiver, Thing t)
|
||||
{
|
||||
if (t.IsForbidden(pawn))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// 使用传入的已解析实例
|
||||
return workGiver.JobOnThing(pawn, t) != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
41
Source/ArachnaeSwarm/JobGiver_PlantSow.cs
Normal file
41
Source/ArachnaeSwarm/JobGiver_PlantSow.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using Verse;
|
||||
using Verse.AI;
|
||||
using RimWorld;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
public class JobGiver_PlantSow : ThinkNode_JobGiver
|
||||
{
|
||||
private WorkGiverDef sowWorkGiverDef = null;
|
||||
private bool triedToLoad = false;
|
||||
|
||||
protected override Job TryGiveJob(Pawn pawn)
|
||||
{
|
||||
if (!triedToLoad || sowWorkGiverDef == null)
|
||||
{
|
||||
triedToLoad = true;
|
||||
sowWorkGiverDef = DefDatabase<WorkGiverDef>.GetNamed("GrowerSow", false);
|
||||
if (sowWorkGiverDef == null)
|
||||
{
|
||||
Log.ErrorOnce("[ArachnaeSwarm] Could not find WorkGiverDef named 'GrowerSow'. Sowing will not work.", 847594);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (sowWorkGiverDef?.Worker is WorkGiver_Scanner sowerScanner) // 直接用 Scanner 接口
|
||||
{
|
||||
// ... (你的逻辑,调用 sowerScanner 的方法) ...
|
||||
if (!sowerScanner.ShouldSkip(pawn) && sowerScanner.MissingRequiredCapacity(pawn) == null)
|
||||
{
|
||||
return sowerScanner.NonScanJob(pawn); // 使用 Scanner 接口
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Warning($"[ArachnaeSwarm] WorkGiverDef 'GrowerSow' Worker is not a WorkGiver_Scanner for pawn {pawn?.LabelShort ?? "NULL"}.");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user