diff --git a/1.6/1.6/Assemblies/ArachnaeSwarm.dll b/1.6/1.6/Assemblies/ArachnaeSwarm.dll new file mode 100644 index 0000000..9449026 Binary files /dev/null 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 f8e3c6a..fbb66f2 100644 --- a/1.6/1.6/Defs/ThingDef_Races/ARA_RaceBaseSwarm.xml +++ b/1.6/1.6/Defs/ThingDef_Races/ARA_RaceBaseSwarm.xml @@ -79,7 +79,6 @@
  • Dig
  • ARA_Sowing
  • -
  • ARA_PlantCutting
  • @@ -103,25 +102,20 @@ DeathActionWorker_Vanish - -
  • Hauling
  • -
  • Mining
  • -
  • Construction
  • -
  • Crafting
  • -
  • Smithing
  • -
  • Tailoring
  • -
  • Cooking
  • -
  • Research
  • -
  • PlantCutting
  • -
  • Growing
  • -
  • Cleaning
  • -
  • Doctor
  • -
  • Firefighter
  • - - 10 -
  • +
  • + +
  • + Plants + 10 +
  • + + +
  • ARA_Sowing
  • +
    + true +
  • AnimalInsect
  • diff --git a/1.6/1.6/Defs/ThinkTreeDefs/ARA_ThinkTrees.xml b/1.6/1.6/Defs/ThinkTreeDefs/ARA_ThinkTrees.xml index fd0e1ae..a5101a2 100644 --- a/1.6/1.6/Defs/ThinkTreeDefs/ARA_ThinkTrees.xml +++ b/1.6/1.6/Defs/ThinkTreeDefs/ARA_ThinkTrees.xml @@ -324,44 +324,19 @@ - -
  • - ARA_Sowing - -
  • + +
  • + ARA_Sowing - -
  • + +
  • + + +
  • + +
  • - - - - -
  • - ARA_Sowing - -
  • - - -
  • - -
  • - - - - -
  • - ARA_PlantCutting - -
  • - - -
  • - -
  • - - diff --git a/1.6/1.6/Defs/TrainableDefs/ARA_PlantCutting.xml b/1.6/1.6/Defs/TrainableDefs/ARA_PlantCutting.xml deleted file mode 100644 index 9dcdf64..0000000 --- a/1.6/1.6/Defs/TrainableDefs/ARA_PlantCutting.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - ARA_PlantCutting - - 允许该生物执行植物割除任务。 - - true - - 3 - Advanced - - 1 - - 99 - - - \ No newline at end of file diff --git a/1.6/1.6/Defs/TrainableDefs/ARA_Sowing.xml b/1.6/1.6/Defs/TrainableDefs/ARA_Sowing.xml index 9c3fcde..ca09fab 100644 --- a/1.6/1.6/Defs/TrainableDefs/ARA_Sowing.xml +++ b/1.6/1.6/Defs/TrainableDefs/ARA_Sowing.xml @@ -3,8 +3,8 @@ ARA_Sowing - - 允许该生物执行播种任务。 + + 允许该生物执行种植任务。 true @@ -14,7 +14,7 @@ Advanced - 1 + 3 100 diff --git a/Source/ArachnaeSwarm/AnimalWorkSystemPatcher.cs b/Source/ArachnaeSwarm/AnimalWorkSystemPatcher.cs deleted file mode 100644 index 6efbef3..0000000 --- a/Source/ArachnaeSwarm/AnimalWorkSystemPatcher.cs +++ /dev/null @@ -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; - } - } -} \ No newline at end of file diff --git a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj index 40d3856..fa0e51d 100644 --- a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj +++ b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj @@ -86,26 +86,19 @@ - - - - + - - - - - - - - - + + + + + diff --git a/Source/ArachnaeSwarm/CompAdvancedTraining.cs b/Source/ArachnaeSwarm/CompAdvancedTraining.cs new file mode 100644 index 0000000..9a0b8c9 --- /dev/null +++ b/Source/ArachnaeSwarm/CompAdvancedTraining.cs @@ -0,0 +1,77 @@ +using System.Collections.Generic; +using Verse; +using RimWorld; + +namespace ArachnaeSwarm +{ + public class CompProperties_AdvancedTraining : CompProperties + { + // 1. 用于设置固定技能等级 + public List skillLevels = new List(); + + // 2. 用于指定生成时立即完成的训练 + public List instantTrainables = new List(); + + // 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); + } + } + } + } + } +} \ No newline at end of file diff --git a/Source/ArachnaeSwarm/CompAnimalWorkSettings.cs b/Source/ArachnaeSwarm/CompAnimalWorkSettings.cs deleted file mode 100644 index 6c462cc..0000000 --- a/Source/ArachnaeSwarm/CompAnimalWorkSettings.cs +++ /dev/null @@ -1,97 +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 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; - - public override void PostSpawnSetup(bool respawningAfterLoad) - { - base.PostSpawnSetup(respawningAfterLoad); - - Pawn pawn = this.parent as Pawn; - if (pawn == null) return; - - // 1. 确保 workSettings 存在 - if (pawn.workSettings == null) - { - pawn.workSettings = new Pawn_WorkSettings(pawn); - pawn.workSettings.EnableAndInitializeIfNotAlreadyInitialized(); - } - - // 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) - { - if (entry.trainable != null && entry.workType != null) - { - // 为相关工作类型设置默认优先级 - // 实际是否执行由 ThinkNode_Conditional 的 GetWanted 控制 - pawn.workSettings.SetPriority(entry.workType, 3); - } - } - } - } - } -} diff --git a/Source/ArachnaeSwarm/CompInstantTrain.cs b/Source/ArachnaeSwarm/CompInstantTrain.cs deleted file mode 100644 index cfb504d..0000000 --- a/Source/ArachnaeSwarm/CompInstantTrain.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.Collections.Generic; -using Verse; -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(); - - public CompProperties_InstantTrain() - { - this.compClass = typeof(CompInstantTrain); - } - } -} diff --git a/Source/ArachnaeSwarm/CompNoTrainingDecay.cs b/Source/ArachnaeSwarm/CompNoTrainingDecay.cs deleted file mode 100644 index f612d2a..0000000 --- a/Source/ArachnaeSwarm/CompNoTrainingDecay.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Verse; - -namespace ArachnaeSwarm -{ - // 这是一个“标记”组件。它的唯一目的就是在 XML 中被添加到 ThingDef, - // 以便我们的 Harmony 补丁可以识别哪些 Pawn 的训练不应该衰减。 - // 它本身不需要任何逻辑。 - public class CompProperties_NoTrainingDecay : CompProperties - { - public CompProperties_NoTrainingDecay() - { - this.compClass = typeof(ThingComp); // 我们可以使用一个通用的、空的 ThingComp - } - } -} \ No newline at end of file diff --git a/Source/ArachnaeSwarm/CompWorkForNonMechs.cs b/Source/ArachnaeSwarm/CompWorkForNonMechs.cs deleted file mode 100644 index 3f5b471..0000000 --- a/Source/ArachnaeSwarm/CompWorkForNonMechs.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Collections.Generic; -using RimWorld; -using Verse; - -namespace ArachnaeSwarm -{ - public class CompProperties_WorkForNonMechs : CompProperties - { - public List 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(); - } - } -} \ No newline at end of file diff --git a/Source/ArachnaeSwarm/JobGiver_Grower.cs b/Source/ArachnaeSwarm/JobGiver_Grower.cs new file mode 100644 index 0000000..2c10f81 --- /dev/null +++ b/Source/ArachnaeSwarm/JobGiver_Grower.cs @@ -0,0 +1,99 @@ +using System.Collections.Generic; +using Verse; +using Verse.AI; +using RimWorld; + +namespace ArachnaeSwarm +{ + // 确保 WorkGiverDefOf 被正确初始化 + [DefOf] + public static class WorkGiverDefOf + { + public static WorkGiverDef Harvest; + public static WorkGiverDef GrowerSow; + + static WorkGiverDefOf() + { + DefOfHelper.EnsureInitializedInCtor(typeof(WorkGiverDefOf)); + } + } + + public class JobGiver_Grower : ThinkNode_JobGiver + { + private static WorkGiver_GrowerHarvest _workGiverHarvest; + private static WorkGiver_GrowerSow _workGiverSow; + + static JobGiver_Grower() + { + // 确保在访问 WorkGiverDefOf 之前,它已经被初始化 + // 尽管 [DefOf] 会自动处理,但显式调用可以避免某些加载时序问题 + DefOfHelper.EnsureInitializedInCtor(typeof(WorkGiverDefOf)); + _workGiverHarvest = WorkGiverDefOf.Harvest.Worker as WorkGiver_GrowerHarvest; + _workGiverSow = WorkGiverDefOf.GrowerSow.Worker as WorkGiver_GrowerSow; + } + + protected override Job TryGiveJob(Pawn pawn) + { + if (_workGiverHarvest == null || _workGiverSow == null) + { + Log.ErrorOnce("JobGiver_Grower could not find vanilla Grower WorkGivers.", 123457); + return null; + } + + // 1. 优先收获 + Thing bestHarvestable = FindClosestThing(pawn, _workGiverHarvest); + if (bestHarvestable != null) + { + return _workGiverHarvest.JobOnThing(pawn, bestHarvestable); + } + + // 2. 其次播种 + IntVec3 bestSowCell = FindClosestSowableCell(pawn, _workGiverSow); + if (bestSowCell.IsValid) + { + return _workGiverSow.JobOnCell(pawn, bestSowCell); + } + + return null; + } + + private Thing FindClosestThing(Pawn pawn, WorkGiver_Scanner scanner) + { + return GenClosest.ClosestThingReachable( + pawn.Position, + pawn.Map, + scanner.PotentialWorkThingRequest, + PathEndMode.Touch, + TraverseParms.For(pawn), + 9999f, + t => scanner.HasJobOnThing(pawn, t) + ); + } + + private IntVec3 FindClosestSowableCell(Pawn pawn, WorkGiver_Scanner scanner) + { + 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 (scanner.HasJobOnCell(pawn, cell)) + { + bestDistSq = distSq; + bestCell = cell; + } + } + } + } + } + return bestCell; + } + } +} \ No newline at end of file diff --git a/Source/ArachnaeSwarm/JobGiver_PlantCutting.cs b/Source/ArachnaeSwarm/JobGiver_PlantCutting.cs deleted file mode 100644 index 4ea44c1..0000000 --- a/Source/ArachnaeSwarm/JobGiver_PlantCutting.cs +++ /dev/null @@ -1,56 +0,0 @@ -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 deleted file mode 100644 index 13563fe..0000000 --- a/Source/ArachnaeSwarm/JobGiver_PlantHarvest.cs +++ /dev/null @@ -1,63 +0,0 @@ -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 deleted file mode 100644 index 5994d2b..0000000 --- a/Source/ArachnaeSwarm/JobGiver_PlantSow.cs +++ /dev/null @@ -1,41 +0,0 @@ -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; - } - } -} diff --git a/Source/ArachnaeSwarm/Patch_QualityUtility.cs b/Source/ArachnaeSwarm/Patch_QualityUtility.cs deleted file mode 100644 index dfa742e..0000000 --- a/Source/ArachnaeSwarm/Patch_QualityUtility.cs +++ /dev/null @@ -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; - } - } -} \ No newline at end of file diff --git a/Source/ArachnaeSwarm/Patch_TrainingTracker_TickRare.cs b/Source/ArachnaeSwarm/Patch_TrainingTracker_TickRare.cs deleted file mode 100644 index e4fa9ac..0000000 --- a/Source/ArachnaeSwarm/Patch_TrainingTracker_TickRare.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Verse; -using HarmonyLib; -using RimWorld; - -namespace ArachnaeSwarm -{ - [HarmonyPatch(typeof(Pawn_TrainingTracker), "TrainingTrackerTickRare")] - public static class Patch_TrainingTracker_TickRare - { - // [HarmonyPrefix] 表示这是一个“前缀”补丁,在原方法执行前运行 - // 它返回一个 bool 值: - // - return true: 继续执行原方法 (TrainingTrackerTickRare) - // - return false: 阻止执行原方法,直接跳过 - [HarmonyPrefix] - public static bool PreventDecayForSpecialAnimals(Pawn_TrainingTracker __instance) - { - // __instance 是原方法的实例对象,我们可以通过它访问 pawn - Pawn pawn = __instance.pawn; - - // 检查 Pawn 的 ThingDef 是否有我们的“标记”组件 - if (pawn.def.HasComp(typeof(CompProperties_NoTrainingDecay))) - { - // 如果有,则这是一个不应衰减训练度的特殊动物,返回 false 阻止原方法执行 - return false; - } - - // 如果没有,则这是一个普通动物,返回 true 让原版的衰减逻辑正常执行 - return true; - } - } -} \ No newline at end of file diff --git a/Source/ArachnaeSwarm/Patch_WorkGivers_Growing.cs b/Source/ArachnaeSwarm/Patch_WorkGivers_Growing.cs deleted file mode 100644 index 4a416ee..0000000 --- a/Source/ArachnaeSwarm/Patch_WorkGivers_Growing.cs +++ /dev/null @@ -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; - } - } -} \ No newline at end of file diff --git a/Source/ArachnaeSwarm/ThinkNode_ConditionalAnimalShouldDoGrowingWork.cs b/Source/ArachnaeSwarm/ThinkNode_ConditionalAnimalShouldDoGrowingWork.cs new file mode 100644 index 0000000..3170615 --- /dev/null +++ b/Source/ArachnaeSwarm/ThinkNode_ConditionalAnimalShouldDoGrowingWork.cs @@ -0,0 +1,43 @@ +using Verse; +using Verse.AI; +using RimWorld; + +namespace ArachnaeSwarm +{ + // 将 DefOf 类放在这里,以便在命名空间内共享 + [DefOf] + public static class ARA_TrainableDefOf + { + public static TrainableDef ARA_Sowing; + public static TrainableDef ARA_PlantCutting; + + 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); + + // 检查动物是否学会并被允许执行“植物切割”工作 + bool canCut = pawn.training.HasLearned(ARA_TrainableDefOf.ARA_PlantCutting) && + pawn.training.GetWanted(ARA_TrainableDefOf.ARA_PlantCutting); + + // 只要满足其中任何一个条件,就返回 true + return canSow || canCut; + } + } +} \ No newline at end of file diff --git a/Source/ArachnaeSwarm/ThinkNode_ConditionalAnimalShouldPlantCut.cs b/Source/ArachnaeSwarm/ThinkNode_ConditionalAnimalShouldPlantCut.cs deleted file mode 100644 index 4489b91..0000000 --- a/Source/ArachnaeSwarm/ThinkNode_ConditionalAnimalShouldPlantCut.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Verse; -using Verse.AI; -using RimWorld; - -namespace ArachnaeSwarm -{ - public class ThinkNode_ConditionalAnimalShouldPlantCut : ThinkNode_Conditional - { - protected override bool Satisfied(Pawn pawn) - { - if (pawn.training == null) - { - return false; - } - - // 使用我们之前创建的静态 DefOf 类来安全地引用 Def - return pawn.training.HasLearned(ARA_TrainableDefOf.ARA_PlantCutting) && - pawn.training.GetWanted(ARA_TrainableDefOf.ARA_PlantCutting); - } - } -} \ No newline at end of file diff --git a/Source/ArachnaeSwarm/ThinkNode_ConditionalAnimalShouldSow.cs b/Source/ArachnaeSwarm/ThinkNode_ConditionalAnimalShouldSow.cs deleted file mode 100644 index da6319c..0000000 --- a/Source/ArachnaeSwarm/ThinkNode_ConditionalAnimalShouldSow.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Verse; -using Verse.AI; -using RimWorld; - -namespace ArachnaeSwarm -{ - // 使用 [DefOf] 属性,让游戏在启动时自动为我们填充这些字段 - [DefOf] - public static class ARA_TrainableDefOf - { - // 确保这些字段名与你在 XML 中定义的 defName 完全一致 - public static TrainableDef ARA_Sowing; - public static TrainableDef ARA_PlantCutting; - - // 静态构造函数,确保 DefOf 被初始化 - static ARA_TrainableDefOf() - { - DefOfHelper.EnsureInitializedInCtor(typeof(ARA_TrainableDefOf)); - } - } - - public class ThinkNode_ConditionalAnimalShouldSow : ThinkNode_Conditional - { - protected override bool Satisfied(Pawn pawn) - { - // MCP 已证实:对于野生动物等情况,pawn.training 可能为 null,此检查是必要的。 - if (pawn.training == null) - { - return false; - } - - // 使用静态缓存的 Def,检查动物是否学会了该技能,并且玩家是否在“动物”标签页中勾选了允许 - return pawn.training.HasLearned(ARA_TrainableDefOf.ARA_Sowing) && - pawn.training.GetWanted(ARA_TrainableDefOf.ARA_Sowing); - } - } -} \ No newline at end of file diff --git a/Source/ArachnaeSwarm/TrainingSystem_Patcher.cs b/Source/ArachnaeSwarm/TrainingSystem_Patcher.cs new file mode 100644 index 0000000..6383318 --- /dev/null +++ b/Source/ArachnaeSwarm/TrainingSystem_Patcher.cs @@ -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(); + if (pawn == null) return true; + + var comp = pawn.GetComp(); + 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(); + if (comp == null || comp.Props.skillLevels.NullOrEmpty()) + { + return true; // 没有组件或配置,正常执行原版衰减 + } + + // 检查全局开关:如果设置了 disableAllSkillDecay 为 true,则阻止衰减 + if (comp.Props.disableAllSkillDecay) + { + return false; // 阻止原版 Interval 方法的执行 + } + return true; // 正常执行原版衰减 + } + } +} \ No newline at end of file diff --git a/Source/ArachnaeSwarm/obj/Debug/.NETFramework,Version=v4.8.AssemblyAttributes.cs b/Source/ArachnaeSwarm/obj/Debug/.NETFramework,Version=v4.8.AssemblyAttributes.cs deleted file mode 100644 index 15efebf..0000000 --- a/Source/ArachnaeSwarm/obj/Debug/.NETFramework,Version=v4.8.AssemblyAttributes.cs +++ /dev/null @@ -1,4 +0,0 @@ -// -using System; -using System.Reflection; -[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] diff --git a/Source/ArachnaeSwarm/obj/Debug/ArachnaeSwarm.csproj.AssemblyReference.cache b/Source/ArachnaeSwarm/obj/Debug/ArachnaeSwarm.csproj.AssemblyReference.cache deleted file mode 100644 index 33b62db..0000000 Binary files a/Source/ArachnaeSwarm/obj/Debug/ArachnaeSwarm.csproj.AssemblyReference.cache and /dev/null differ diff --git a/Source/ArachnaeSwarm/obj/Debug/ArachnaeSwarm.csproj.CoreCompileInputs.cache b/Source/ArachnaeSwarm/obj/Debug/ArachnaeSwarm.csproj.CoreCompileInputs.cache deleted file mode 100644 index a6b0f7a..0000000 --- a/Source/ArachnaeSwarm/obj/Debug/ArachnaeSwarm.csproj.CoreCompileInputs.cache +++ /dev/null @@ -1 +0,0 @@ -70a1ccfa141c7c82eb05a9fac71c32df86f6b34332995c92bb7aac69bc46394b diff --git a/Source/ArachnaeSwarm/obj/Debug/ArachnaeSwarm.csproj.FileListAbsolute.txt b/Source/ArachnaeSwarm/obj/Debug/ArachnaeSwarm.csproj.FileListAbsolute.txt deleted file mode 100644 index c17b74f..0000000 --- a/Source/ArachnaeSwarm/obj/Debug/ArachnaeSwarm.csproj.FileListAbsolute.txt +++ /dev/null @@ -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