暂存
This commit is contained in:
@@ -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,26 +86,19 @@
|
||||
<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="TrainingSystem_Patcher.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="CompProperties_DelayedTerrainSpawn.cs" />
|
||||
<Compile Include="CompDelayedTerrainSpawn.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="CompInstantTrain.cs" />
|
||||
<Compile Include="MainHarmony.cs" />
|
||||
<Compile Include="Patch_TrainingTracker_TickRare.cs" />
|
||||
<Compile Include="CompNoTrainingDecay.cs" />
|
||||
<Compile Include="ThinkNode_ConditionalAnimalShouldSow.cs" />
|
||||
<Compile Include="ThinkNode_ConditionalAnimalShouldPlantCut.cs" />
|
||||
<Compile Include="CompAnimalWorkSettings.cs" />
|
||||
<Compile Include="JobGiver_PlantCutting.cs" />
|
||||
<Compile Include="JobGiver_PlantHarvest.cs" />
|
||||
<Compile Include="JobGiver_PlantSow.cs" />
|
||||
<Compile Include="ThinkNode_ConditionalAnimalShouldDoGrowingWork.cs" />
|
||||
<Compile Include="CompAdvancedTraining.cs" />
|
||||
<Compile Include="JobGiver_Grower.cs" />
|
||||
<!-- AnimalWorkSystemPatcher.cs is now in its own group -->
|
||||
<!-- TrainingSystem_Patcher.cs is now in its own group -->
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="WULA_AutoMechCarrier\CompAutoMechCarrier.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,97 +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 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;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<TrainableDef> trainables = new List<TrainableDef>();
|
||||
|
||||
public CompProperties_InstantTrain()
|
||||
{
|
||||
this.compClass = typeof(CompInstantTrain);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
99
Source/ArachnaeSwarm/JobGiver_Grower.cs
Normal file
99
Source/ArachnaeSwarm/JobGiver_Grower.cs
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<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; // 使用传入的实例
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
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; // 正常执行原版衰减
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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