diff --git a/1.6/1.6/Assemblies/ArachnaeSwarm.dll b/1.6/1.6/Assemblies/ArachnaeSwarm.dll index 9aac5af..5c8cda1 100644 Binary files a/1.6/1.6/Assemblies/ArachnaeSwarm.dll and b/1.6/1.6/Assemblies/ArachnaeSwarm.dll differ diff --git a/1.6/1.6/Defs/ThingDef_Races/ARA_RaceBaseSwarm.xml b/1.6/1.6/Defs/ThingDef_Races/ARA_RaceBaseSwarm.xml index 1859908..8e78c82 100644 --- a/1.6/1.6/Defs/ThingDef_Races/ARA_RaceBaseSwarm.xml +++ b/1.6/1.6/Defs/ThingDef_Races/ARA_RaceBaseSwarm.xml @@ -22,6 +22,15 @@
  • + + +
  • + Plants + 10 + true +
  • + +
  • ARA_Sowing diff --git a/1.6/1.6/Defs/ThinkTreeDefs/ARA_ThinkTrees.xml b/1.6/1.6/Defs/ThinkTreeDefs/ARA_ThinkTrees.xml index 8f310ad..fd0e1ae 100644 --- a/1.6/1.6/Defs/ThinkTreeDefs/ARA_ThinkTrees.xml +++ b/1.6/1.6/Defs/ThinkTreeDefs/ARA_ThinkTrees.xml @@ -107,7 +107,7 @@
  • - 30 + 30 35
  • @@ -324,6 +324,45 @@ + +
  • + ARA_Sowing + +
  • + + +
  • + +
  • + + + + +
  • + ARA_Sowing + +
  • + + +
  • + +
  • + + + + +
  • + ARA_PlantCutting + +
  • + + +
  • + +
  • + + + @@ -335,31 +374,6 @@ Insect_PreWander - -
  • - -
  • - TrainedAnimalBehavior - -
  • - -
  • - - - - - -
  • - -
  • - TrainedAnimalBehavior - -
  • - -
  • - - -
  • diff --git a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj index 7fc9efb..8f83075 100644 --- a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj +++ b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj @@ -96,8 +96,10 @@ - + + + diff --git a/Source/ArachnaeSwarm/CompAnimalWorkSettings.cs b/Source/ArachnaeSwarm/CompAnimalWorkSettings.cs index 758e74c..6c462cc 100644 --- a/Source/ArachnaeSwarm/CompAnimalWorkSettings.cs +++ b/Source/ArachnaeSwarm/CompAnimalWorkSettings.cs @@ -4,6 +4,35 @@ using RimWorld; namespace ArachnaeSwarm { + public class CompProperties_AnimalWorkSettings : CompProperties + { + // 使用列表存储键值对,因为RimWorld的XML解析器对字典有特殊要求 + public List workTypeMap = new List(); + // 定义动物的技能等级 + public List skillLevels = new List(); + + 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); } } } } } -} \ No newline at end of file +} diff --git a/Source/ArachnaeSwarm/CompInstantTrain.cs b/Source/ArachnaeSwarm/CompInstantTrain.cs index c7c95e8..cfb504d 100644 --- a/Source/ArachnaeSwarm/CompInstantTrain.cs +++ b/Source/ArachnaeSwarm/CompInstantTrain.cs @@ -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 trainables = new List(); @@ -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); - } - } - } - } - } -} \ No newline at end of file +} diff --git a/Source/ArachnaeSwarm/CompProperties_AnimalWorkSettings.cs b/Source/ArachnaeSwarm/CompProperties_AnimalWorkSettings.cs deleted file mode 100644 index a53c0ea..0000000 --- a/Source/ArachnaeSwarm/CompProperties_AnimalWorkSettings.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Collections.Generic; -using Verse; -using RimWorld; - -namespace ArachnaeSwarm -{ - public class CompProperties_AnimalWorkSettings : CompProperties - { - // 使用列表存储键值对,因为RimWorld的XML解析器对字典有特殊要求 - public List workTypeMap = new List(); - - // 可以在这里添加一些配置选项,比如默认优先级等,但现在简单起见,可以留空或只做基本初始化 - public CompProperties_AnimalWorkSettings() - { - this.compClass = typeof(CompAnimalWorkSettings); - } - } - - // 定义键值对的结构 - public class WorkTypeMapEntry - { - public TrainableDef trainable; - public WorkTypeDef workType; - } -} \ No newline at end of file diff --git a/Source/ArachnaeSwarm/JobGiver_PlantCutting.cs b/Source/ArachnaeSwarm/JobGiver_PlantCutting.cs new file mode 100644 index 0000000..4ea44c1 --- /dev/null +++ b/Source/ArachnaeSwarm/JobGiver_PlantCutting.cs @@ -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.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 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; // 使用传入的实例 + } + } +} diff --git a/Source/ArachnaeSwarm/JobGiver_PlantHarvest.cs b/Source/ArachnaeSwarm/JobGiver_PlantHarvest.cs new file mode 100644 index 0000000..13563fe --- /dev/null +++ b/Source/ArachnaeSwarm/JobGiver_PlantHarvest.cs @@ -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.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 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; + } + } +} diff --git a/Source/ArachnaeSwarm/JobGiver_PlantSow.cs b/Source/ArachnaeSwarm/JobGiver_PlantSow.cs new file mode 100644 index 0000000..5994d2b --- /dev/null +++ b/Source/ArachnaeSwarm/JobGiver_PlantSow.cs @@ -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.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; + } + } +}