This commit is contained in:
2026-02-25 17:30:59 +08:00
parent 0509f26c3c
commit fff40b0edb
70 changed files with 3951 additions and 1219 deletions

View File

@@ -0,0 +1,182 @@
// File: FloatMenuOptionProvider_ForceEjectPilot.cs
using RimWorld;
using System.Collections.Generic;
using System.Linq;
using Verse;
using Verse.AI;
namespace WulaFallenEmpire
{
public class FloatMenuOptionProvider_ForceEjectPilot : FloatMenuOptionProvider
{
// 征召状态下不能执行此工作
protected override bool Drafted => true;
// 非征召状态下可以执行
protected override bool Undrafted => true;
// 不支持多选
protected override bool Multiselect => false;
// 需要操纵能力
protected override bool RequiresManipulation => true;
// 检查Thing是否为机甲
private bool IsMech(Pawn thing)
{
return thing is Wulamechunit || thing?.GetType()?.IsSubclassOf(typeof(Wulamechunit)) == true;
}
// 检查是否适用于当前上下文
protected override bool AppliesInt(FloatMenuContext context)
{
// 必须有选中的殖民者
if (context.FirstSelectedPawn == null)
return false;
// 检查点击的单元格中是否有机甲
var ClickedPawns = context.ClickedPawns;
if (ClickedPawns == null || ClickedPawns.Count == 0)
return false;
// 查找第一个机甲
Pawn mech = null;
foreach (var thing in ClickedPawns)
{
if (IsMech(thing))
{
mech = thing;
break;
}
}
if (mech == null)
return false;
// 检查机甲是否有驾驶员组件
var comp = mech.TryGetComp<CompMechPilotHolder>();
if (comp == null)
return false;
// 检查机甲是否属于非玩家派系且Downed但未死亡并且有驾驶员
if (mech.Faction == Faction.OfPlayer || !mech.Downed || mech.Dead || !comp.HasPilots)
return false;
return true;
}
// 获取单个选项
protected override FloatMenuOption GetSingleOptionFor(Pawn clickedPawn, FloatMenuContext context)
{
if (clickedPawn == null || context.FirstSelectedPawn == null)
return null;
// 如果不是机甲返回null
if (!IsMech(clickedPawn))
return null;
// 获取机甲和组件
var mech = clickedPawn as Wulamechunit;
var comp = mech?.TryGetComp<CompMechPilotHolder>();
if (mech == null || comp == null)
return null;
// 检查机甲是否属于非玩家派系且Downed但未死亡并且有驾驶员
if (mech.Faction == Faction.OfPlayer || !mech.Downed || mech.Dead || !comp.HasPilots)
return null;
// 检查殖民者是否能够执行此工作
return CreateForceEjectOption(mech, context.FirstSelectedPawn, comp);
}
// 创建强制拉出驾驶员的菜单选项
private FloatMenuOption CreateForceEjectOption(Wulamechunit mech, Pawn pawn, CompMechPilotHolder comp)
{
string label = "WULA_ForceEjectPilot".Translate(mech.LabelShort);
string disabledReason = "";
// 检查条件是否允许执行强制拉出
bool canForceEject = CanForceEject(mech, pawn, comp, ref disabledReason);
if (canForceEject)
{
return new FloatMenuOption(label, () =>
{
// 创建强制拉出驾驶员的工作
Job job = JobMaker.MakeJob(Wula_JobDefOf.WULA_ForceEjectPilot, mech);
pawn.jobs.TryTakeOrderedJob(job, JobTag.Misc);
}, MenuOptionPriority.High);
}
else
{
// 创建禁用的选项,显示原因
return new FloatMenuOption(
"WULA_ForceEjectPilot".Translate(mech.LabelShort) + ": " + disabledReason,
null,
MenuOptionPriority.DisabledOption);
}
}
// 检查殖民者是否可以执行强制拉出
private bool CanForceEject(Wulamechunit mech, Pawn pawn, CompMechPilotHolder comp, ref string disabledReason)
{
// 检查殖民者是否能够到达机甲
if (!pawn.CanReach(mech, PathEndMode.Touch, Danger.Some))
{
disabledReason = "NoPath".Translate();
return false;
}
// 检查殖民者状态
if (pawn.Downed)
{
disabledReason = "Downed".Translate();
return false;
}
if (pawn.Dead)
{
disabledReason = "Dead".Translate();
return false;
}
// 检查是否为囚犯
if (pawn.IsPrisoner)
{
disabledReason = "Prisoner".Translate();
return false;
}
// 检查是否为奴隶
if (pawn.IsSlave)
{
disabledReason = "Slave".Translate();
return false;
}
// 检查机甲是否已经被玩家派系控制
if (mech.Faction == Faction.OfPlayer)
{
disabledReason = "WULA_AlreadyPlayerMech".Translate();
return false;
}
// 检查机甲是否Downed且未死亡
if (!mech.Downed || mech.Dead)
{
disabledReason = "WULA_MechNotDowned".Translate();
return false;
}
// 检查是否有驾驶员
if (!comp.HasPilots)
{
disabledReason = "WULA_NoPilot".Translate();
return false;
}
return true;
}
}
}

View File

@@ -0,0 +1,177 @@
// Jobs/JobDriver_ForceEjectPilot.cs
using RimWorld;
using System.Collections.Generic;
using Verse;
using Verse.AI;
using System.Linq;
namespace WulaFallenEmpire
{
public class JobDriver_ForceEjectPilot : JobDriver
{
private const int WorkDurationTicks = 600; // 10秒60帧/秒)
// 目标机甲
private Pawn TargetMech => job.targetA.Thing as Pawn;
// 工作进度属性
private float WorkProgress
{
get
{
if (TargetMech == null) return 0f;
return (float)ticksLeftThisToil / WorkDurationTicks;
}
}
public override bool TryMakePreToilReservations(bool errorOnFailed)
{
// 为殖民者预留机甲的位置
return pawn.Reserve(TargetMech, job, 1, -1, null, errorOnFailed);
}
protected override IEnumerable<Toil> MakeNewToils()
{
// 目标验证
this.FailOnDespawnedNullOrForbidden(TargetIndex.A);
this.FailOn(() => !CanForceEject(TargetMech));
// Toil 1移动到机甲位置
yield return Toils_Goto.GotoThing(TargetIndex.A, PathEndMode.Touch)
.FailOnDespawnedOrNull(TargetIndex.A);
// Toil 2执行强制拉出工作
var workToil = new Toil();
workToil.initAction = () =>
{
pawn.rotationTracker.FaceCell(TargetMech.Position);
pawn.jobs.posture = PawnPosture.Standing;
};
workToil.tickAction = () =>
{
// 每帧工作进度
pawn.skills?.Learn(SkillDefOf.Melee, 0.1f);
// 显示工作条
if (pawn.IsColonistPlayerControlled)
{
TargetMech.Map.overlayDrawer.DrawOverlay(TargetMech, OverlayTypes.QuestionMark);
}
};
workToil.defaultCompleteMode = ToilCompleteMode.Delay;
workToil.WithEffect(EffecterDefOf.MechRepairing, TargetIndex.A);
workToil.defaultDuration = WorkDurationTicks;
workToil.WithProgressBar(TargetIndex.A, () => 1f - WorkProgress);
workToil.handlingFacing = true;
workToil.activeSkill = () => SkillDefOf.Construction;
yield return workToil;
// Toil 3完成工作
yield return new Toil
{
initAction = () =>
{
CompleteForceEject();
},
defaultCompleteMode = ToilCompleteMode.Instant
};
}
// 检查是否可以强制拉出
private bool CanForceEject(Pawn mech)
{
if (mech == null || mech.Dead)
return false;
// 必须是机甲
if (!(mech is Wulamechunit))
return false;
// 必须是玩家派系的目标
if (mech.Faction == Faction.OfPlayer)
return false;
// 必须失去行动能力但未死亡
if (!mech.Downed)
return false;
// 必须有驾驶员
var pilotComp = mech.TryGetComp<CompMechPilotHolder>();
if (pilotComp == null || !pilotComp.HasPilots)
return false;
// 殖民者必须能够接触机甲
if (!pawn.CanReach(mech, PathEndMode.Touch, Danger.Some))
return false;
// 殖民者不能是囚犯或已失去行动能力
if (pawn.Downed || pawn.Dead || pawn.IsPrisoner)
return false;
return true;
}
// 完成强制拉出
private void CompleteForceEject()
{
var mech = TargetMech;
if (mech == null) return;
var pilotComp = mech.TryGetComp<CompMechPilotHolder>();
if (pilotComp == null) return;
try
{
// 1. 弹出所有驾驶员
var ejectedPilots = pilotComp.GetPilots().ToList();
pilotComp.RemoveAllPilots();
// 2. 转换派系为玩家
mech.SetFaction(Faction.OfPlayer);
// 4. 发送消息
SendCompletionMessages(mech, ejectedPilots);
}
catch (System.Exception ex)
{
Log.Error($"[WULA] Error in ForceEjectPilot: {ex}");
}
}
// 发送完成消息
private void SendCompletionMessages(Pawn mech, List<Pawn> ejectedPilots)
{
string message;
if (ejectedPilots.Count > 0)
{
message = "WULA_ForceEjectComplete_WithPilots".Translate(
pawn.LabelShortCap,
mech.LabelShortCap,
ejectedPilots.Count
);
}
else
{
message = "WULA_ForceEjectComplete".Translate(
pawn.LabelShortCap,
mech.LabelShortCap
);
}
Messages.Message(message, MessageTypeDefOf.PositiveEvent);
// 如果弹出的是敌对派系驾驶员,添加额外消息
foreach (var pilot in ejectedPilots)
{
if (pilot.Faction != null && pilot.Faction.HostileTo(Faction.OfPlayer))
{
Messages.Message("WULA_HostilePilotEjected".Translate(
pilot.LabelShortCap,
pilot.Faction.Name
), MessageTypeDefOf.NeutralEvent);
}
}
}
}
}