考察任务

This commit is contained in:
Tourswen
2025-11-24 01:51:11 +08:00
parent 6b68faae33
commit 8607bb5036
69 changed files with 2607 additions and 666 deletions

View File

@@ -0,0 +1,112 @@
using System.Collections.Generic;
using RimWorld;
using Verse;
using Verse.AI;
namespace WulaFallenEmpire
{
public class JobDriver_InspectBuilding : JobDriver
{
private const TargetIndex BuildingIndex = TargetIndex.A;
private int ticksLeft;
// 定义考察效果 - 可以使用现有的效果或创建自定义效果
private static readonly EffecterDef InspectEffect = EffecterDefOf.Research; // 使用研究效果作为临时替代
private static readonly SoundDef InspectSound = SoundDefOf.Interact_CleanFilth; // 使用建造声音作为临时替代
public override bool TryMakePreToilReservations(bool errorOnFailed)
{
// 预订目标建筑
return pawn.Reserve(job.targetA, job, 1, 1, null, errorOnFailed);
}
protected override IEnumerable<Toil> MakeNewToils()
{
// 失败条件
this.FailOnDestroyedOrNull(BuildingIndex);
this.FailOnDespawnedOrNull(BuildingIndex);
this.FailOn(() => !pawn.health.capacities.CapableOf(PawnCapacityDefOf.Moving));
// 第一步:前往目标建筑
yield return Toils_Goto.GotoCell(BuildingIndex, PathEndMode.Touch)
.FailOnSomeonePhysicallyInteracting(BuildingIndex);
// 第二步:进行考察(带动画效果)
Toil inspectToil = CreateInspectionToil();
yield return inspectToil;
}
/// <summary>
/// 创建带动画效果的考察工作
/// </summary>
private Toil CreateInspectionToil()
{
Toil inspectToil = new Toil();
inspectToil.initAction = () =>
{
ticksLeft = job.expiryInterval;
// 记录日志
if (Prefs.DevMode)
{
Log.Message($"[JobDriver_InspectBuilding] {pawn.Name} started inspecting {TargetThingA.Label} for {ticksLeft} ticks");
}
};
inspectToil.tickAction = () =>
{
ticksLeft--;
// 每 tick 检查是否完成
if (ticksLeft <= 0)
{
ReadyForNextToil();
}
};
inspectToil.defaultCompleteMode = ToilCompleteMode.Delay;
inspectToil.defaultDuration = job.expiryInterval;
// 添加动画效果
inspectToil.WithEffect(InspectEffect, BuildingIndex);
// 添加音效
inspectToil.PlaySustainerOrSound(() => InspectSound);
// 添加进度条(可选)
inspectToil.WithProgressBar(BuildingIndex,
() => 1f - ((float)ticksLeft / job.expiryInterval),
interpolateBetweenActorAndTarget: true);
inspectToil.AddFinishAction(() =>
{
// 考察完成后的处理
OnInspectionComplete();
});
return inspectToil;
}
/// <summary>
/// 考察完成时的处理
/// </summary>
private void OnInspectionComplete()
{
// 可以在这里添加考察完成后的效果
// 例如:增加技能经验、触发事件等
if (Prefs.DevMode)
{
Log.Message($"[JobDriver_InspectBuilding] {pawn.Name} completed inspection of {TargetThingA.Label}");
}
}
public override void ExposeData()
{
base.ExposeData();
Scribe_Values.Look(ref ticksLeft, "ticksLeft", 0);
}
}
}

View File

@@ -0,0 +1,307 @@
using RimWorld;
using RimWorld.Planet;
using System.Collections.Generic;
using System.Linq;
using Verse;
using Verse.AI;
namespace WulaFallenEmpire
{
public class JobGiver_InspectBuilding : ThinkNode_JobGiver
{
// 检查间隔ticks
private const int CheckInterval = 120; // 2秒检查一次
// 最大考察距离
private const float MaxDistance = 20f;
// 默认最小间隔ticks- 5分钟
private const int DefaultMinIntervalTicks = 300 * 60; // 5分钟 * 60秒/分钟 * 60ticks/秒
// 存储每个 Pawn 的最后考察时间
private static Dictionary<Pawn, int> lastInspectionTicks = new Dictionary<Pawn, int>();
protected override Job TryGiveJob(Pawn pawn)
{
// 检查 Pawn 是否有效
if (pawn == null || pawn.Destroyed || !pawn.Spawned || pawn.Map == null)
return null;
// 检查 Pawn 是否能够工作
if (pawn.Downed || pawn.InMentalState || !pawn.health.capacities.CanBeAwake)
return null;
// 检查 Pawn 是否能够移动
if (!pawn.health.capacities.CapableOf(PawnCapacityDefOf.Moving))
return null;
// 检查背景故事是否为军团背景
if (!HasLegionBackstory(pawn))
return null;
// 检查是否已经有工作
if (pawn.CurJob != null && pawn.CurJob.def == JobDefOf_WULA.WULA_InspectBuilding)
return null;
// 检查冷却时间
if (!CanInspectNow(pawn))
return null;
// 寻找合适的考察目标
Thing inspectionTarget = FindRandomInspectionTarget(pawn);
if (inspectionTarget == null)
return null;
// 创建考察工作
Job job = JobMaker.MakeJob(JobDefOf_WULA.WULA_InspectBuilding, inspectionTarget);
job.expiryInterval = Rand.Range(300, 600); // 5-10秒的随机时间
job.checkOverrideOnExpire = true;
// 记录开始考察时间
RecordInspectionStart(pawn);
// 记录调试信息
if (Prefs.DevMode)
{
Log.Message($"[JobGiver_InspectBuilding] Assigned inspection job to {pawn.Name} at {inspectionTarget.Label}");
}
return job;
}
/// <summary>
/// 检查 Pawn 是否具有军团背景故事
/// </summary>
private bool HasLegionBackstory(Pawn pawn)
{
if (pawn.story == null)
return false;
// 检查成年背景故事是否为军团背景
if (pawn.story.Adulthood != null && pawn.story.Adulthood.identifier == "WULA_Adult_Backstory_Legion")
return true;
return false;
}
/// <summary>
/// 检查 Pawn 是否可以开始新的考察(冷却时间检查)
/// </summary>
private bool CanInspectNow(Pawn pawn)
{
// 获取设置的最小间隔时间
int minIntervalTicks = GetMinInspectionIntervalTicks();
// 如果 Pawn 没有记录,说明可以立即开始
if (!lastInspectionTicks.ContainsKey(pawn))
return true;
int lastTick = lastInspectionTicks[pawn];
int currentTick = Find.TickManager.TicksGame;
int elapsedTicks = currentTick - lastTick;
// 检查是否已经过了最小间隔时间
bool canInspect = elapsedTicks >= minIntervalTicks;
if (Prefs.DevMode && !canInspect)
{
int remainingTicks = minIntervalTicks - elapsedTicks;
float remainingSeconds = remainingTicks / 60f;
Log.Message($"[JobGiver_InspectBuilding] {pawn.Name} must wait {remainingSeconds:F1} seconds before next inspection");
}
return canInspect;
}
/// <summary>
/// 记录 Pawn 开始考察的时间
/// </summary>
private void RecordInspectionStart(Pawn pawn)
{
lastInspectionTicks[pawn] = Find.TickManager.TicksGame;
if (Prefs.DevMode)
{
Log.Message($"[JobGiver_InspectBuilding] Recorded inspection start for {pawn.Name} at tick {lastInspectionTicks[pawn]}");
}
}
/// <summary>
/// 获取最小考察间隔时间ticks
/// </summary>
private int GetMinInspectionIntervalTicks()
{
// 这里可以从 Mod 设置中获取值
// 暂时返回默认值,您可以根据需要修改
return DefaultMinIntervalTicks;
}
/// <summary>
/// 随机寻找合适的考察目标
/// </summary>
private Thing FindRandomInspectionTarget(Pawn pawn)
{
// 获取地图上所有符合条件的建筑
List<Thing> validBuildings = new List<Thing>();
// 遍历地图上的所有建筑
foreach (Thing thing in pawn.Map.listerThings.ThingsInGroup(ThingRequestGroup.BuildingArtificial))
{
if (IsValidInspectionTarget(thing, pawn))
{
validBuildings.Add(thing);
}
}
// 如果没有找到合适的建筑返回null
if (validBuildings.Count == 0)
{
if (Prefs.DevMode)
{
Log.Message($"[JobGiver_InspectBuilding] No valid inspection targets found for {pawn.Name}");
}
return null;
}
// 随机选择一个建筑
Thing selectedBuilding = validBuildings.RandomElement();
if (Prefs.DevMode)
{
Log.Message($"[JobGiver_InspectBuilding] Randomly selected {selectedBuilding.Label} from {validBuildings.Count} valid targets");
}
return selectedBuilding;
}
/// <summary>
/// 检查是否有效的考察目标
/// </summary>
private bool IsValidInspectionTarget(Thing thing, Pawn pawn)
{
// 基本检查
if (thing == null || thing.Destroyed)
return false;
// 检查是否玩家拥有
if (thing.Faction != Faction.OfPlayer)
return false;
// 检查是否可到达
if (!pawn.CanReach(thing, PathEndMode.Touch, Danger.None))
return false;
// 排除一些不适合的建筑类型
if (thing.def.IsFrame || thing.def.IsBlueprint)
return false;
// 确保建筑是完整的
if (thing is Building building && (building.IsBurning() || building.IsBrokenDown()))
return false;
// 确保不是禁止进入的区域
if (thing.IsForbidden(pawn))
return false;
// 确保没有其他 Pawn 正在考察这个建筑
if (IsBeingInspectedByOther(thing, pawn))
return false;
// 排除墙壁建筑
if (IsWall(thing))
return false;
// 距离检查(可选,但为了性能考虑可以保留)
if (pawn.Position.DistanceTo(thing.Position) > MaxDistance)
return false;
return true;
}
/// <summary>
/// 检查是否为墙壁
/// </summary>
private bool IsWall(Thing thing)
{
// 检查建筑的 def 中是否有 isWall 标签
if (thing.def?.building != null && thing.def.building.isWall)
{
if (Prefs.DevMode)
{
Log.Message($"[JobGiver_InspectBuilding] Excluding wall: {thing.Label}");
}
return true;
}
// 额外的检查:通过 defName 或标签判断
if (thing.def?.defName == "Wall" ||
(thing.def?.thingCategories?.Any(c => c.defName == "Walls") ?? false))
{
return true;
}
return false;
}
/// <summary>
/// 检查是否有其他 Pawn 正在考察这个建筑
/// </summary>
private bool IsBeingInspectedByOther(Thing thing, Pawn currentPawn)
{
foreach (Pawn otherPawn in thing.Map.mapPawns.AllPawnsSpawned)
{
if (otherPawn != currentPawn &&
otherPawn.CurJob != null &&
otherPawn.CurJob.def == JobDefOf_WULA.WULA_InspectBuilding &&
otherPawn.CurJob.targetA.Thing == thing)
{
return true;
}
}
return false;
}
/// <summary>
/// 清理不再存在的 Pawn 的记录
/// </summary>
public static void CleanupInspectionRecords()
{
List<Pawn> toRemove = new List<Pawn>();
foreach (var pair in lastInspectionTicks)
{
if (pair.Key.Destroyed || !pair.Key.Spawned)
{
toRemove.Add(pair.Key);
}
}
foreach (Pawn pawn in toRemove)
{
lastInspectionTicks.Remove(pawn);
}
if (Prefs.DevMode && toRemove.Count > 0)
{
Log.Message($"[JobGiver_InspectBuilding] Cleaned up {toRemove.Count} inspection records");
}
}
public class InspectionCleanupComponent : WorldComponent
{
private int lastCleanupTick = 0;
private const int CleanupIntervalTicks = 6000; // 每100秒清理一次
public InspectionCleanupComponent(World world) : base(world) { }
public override void WorldComponentTick()
{
base.WorldComponentTick();
int currentTick = Find.TickManager.TicksGame;
if (currentTick - lastCleanupTick > CleanupIntervalTicks)
{
JobGiver_InspectBuilding.CleanupInspectionRecords();
lastCleanupTick = currentTick;
}
}
}
}
}