// CompMechFuel.cs using RimWorld; using System.Collections.Generic; using UnityEngine; using Verse; using Verse.AI; using System.Linq; namespace WulaFallenEmpire { public class CompMechFuel : ThingComp { public CompProperties_MechFuel Props => (CompProperties_MechFuel)props; private float fuel; private bool isShutdown = false; private int lastFuelTick = -1; public float Fuel => fuel; public float FuelPercent => fuel / Props.fuelCapacity; public bool HasFuel => fuel > 0f; public bool IsFull => FuelPercent >= 0.999f; public bool NeedsRefueling => FuelPercent < Props.autoRefuelThreshold && Props.allowAutoRefuel; public bool IsShutdown => isShutdown; public ThingDef FuelType => Props.fuelType; // 停机状态 Hediff private HediffDef ShutdownHediffDef => HediffDef.Named("DD_MechShutdown"); public override void PostSpawnSetup(bool respawningAfterLoad) { base.PostSpawnSetup(respawningAfterLoad); lastFuelTick = Find.TickManager.TicksGame; // 如果是新生成的机甲,自动加满燃料 if (!respawningAfterLoad && fuel <= 0f) { Refuel(Props.fuelCapacity); } // 确保停机状态和 Hediff 同步 SyncShutdownHediff(); } public override void CompTick() { base.CompTick(); // 每60ticks(1秒)消耗一次燃料 if (Find.TickManager.TicksGame % 60 == 0) { ConsumeFuelOverTime(); } // 检查是否需要关机 CheckShutdown(); // 确保停机状态和 Hediff 同步 SyncShutdownHediff(); } private void SyncShutdownHediff() { var mech = parent as Pawn; if (mech == null || mech.health == null || ShutdownHediffDef == null) return; bool hasShutdownHediff = mech.health.hediffSet.HasHediff(ShutdownHediffDef); // 如果处于停机状态但没有 Hediff,添加 Hediff if (isShutdown && !hasShutdownHediff) { mech.health.AddHediff(ShutdownHediffDef); } // 如果不处于停机状态但有 Hediff,移除 Hediff else if (!isShutdown && hasShutdownHediff) { var hediff = mech.health.hediffSet.GetFirstHediffOfDef(ShutdownHediffDef); if (hediff != null) { mech.health.RemoveHediff(hediff); } } } private void ConsumeFuelOverTime() { if (fuel <= 0f || !parent.Spawned) return; // 检查是否有驾驶员 - 没有驾驶员时不消耗燃料 var pilotComp = parent.TryGetComp(); bool hasPilot = pilotComp != null && pilotComp.HasPilots; if (!hasPilot) return; // 没有驾驶员,不消耗燃料 // 获取当前时间 int currentTick = Find.TickManager.TicksGame; // 计算经过的游戏时间(以天为单位) float daysPassed = (currentTick - lastFuelTick) / 60000f; // 60000 ticks = 1天 // 计算基础燃料消耗 float consumption = Props.dailyFuelConsumption * daysPassed; // 如果机甲正在活动(移动、战斗等),消耗更多燃料 var mech = parent as Pawn; if (mech != null) { // 增加活动消耗 if (mech.pather.Moving) { consumption *= 2f; // 移动时消耗加倍 } // 战斗状态消耗更多 if (mech.CurJob != null && mech.CurJob.def == JobDefOf.AttackStatic) { consumption *= 1.5f; } } // 消耗燃料 fuel = Mathf.Max(0f, fuel - consumption); // 更新最后消耗时间 lastFuelTick = currentTick; } private void CheckShutdown() { if (!Props.shutdownWhenEmpty || isShutdown) return; // 燃料耗尽时关机 if (fuel <= 0f && parent.Spawned) { Shutdown(); } } private void Shutdown() { if (isShutdown) return; isShutdown = true; var mech = parent as Pawn; if (mech != null) { // 取消所有工作 mech.jobs.StopAll(); mech.drafter.Drafted = false; // 添加停机 Hediff if (ShutdownHediffDef != null) { mech.health.AddHediff(ShutdownHediffDef); } // 播放关机效果 MoteMaker.ThrowText(mech.DrawPos, mech.Map, "DD_Shutdown".Translate(), Color.gray, 3.5f); } } public void Startup() { if (!isShutdown || fuel <= 0f) return; isShutdown = false; var mech = parent as Pawn; if (mech != null) { // 移除停机 Hediff if (ShutdownHediffDef != null) { var hediff = mech.health.hediffSet.GetFirstHediffOfDef(ShutdownHediffDef); if (hediff != null) { mech.health.RemoveHediff(hediff); } } MoteMaker.ThrowText(mech.DrawPos, mech.Map, "DD_Startup".Translate(), Color.green, 3.5f); } } public bool TryConsumeFuel(float amount) { if (fuel >= amount) { fuel -= amount; return true; } return false; } public bool Refuel(float amount) { float oldFuel = fuel; fuel = Mathf.Min(Props.fuelCapacity, fuel + amount); // 如果之前关机了且现在有燃料了,启动 if (isShutdown && fuel > 0f) { Startup(); } return fuel > oldFuel; } // 设置燃料到特定值(测试用) public void SetFuel(float amount) { float oldFuel = fuel; fuel = Mathf.Clamp(amount, 0f, Props.fuelCapacity); // 检查是否需要关机或启动 if (fuel <= 0f) { if (!isShutdown && Props.shutdownWhenEmpty) { Shutdown(); } } else if (isShutdown && fuel > 0f) { Startup(); } // 发送调试消息 if (DebugSettings.godMode) { Messages.Message($"DD_Debug_FuelSet".Translate( parent.LabelShort, fuel.ToString("F1"), Props.fuelCapacity.ToString("F1"), (fuel / Props.fuelCapacity * 100f).ToString("F0") + "%" ), parent, MessageTypeDefOf.PositiveEvent); } } public float FuelSpaceRemaining => Mathf.Max(0f, Props.fuelCapacity - fuel); public int GetFuelCountToFullyRefuel() { if (FuelType == null) return 0; float fuelNeeded = FuelSpaceRemaining; return Mathf.CeilToInt(fuelNeeded); } // 立刻加注 - 现在触发最近殖民者来加注 public void RefuelNow() { if (IsFull || parent.Map == null || FuelType == null) return; // 寻找最近的可用殖民者 Pawn bestColonist = FindBestColonistForRefuel(); if (bestColonist == null) { Messages.Message("DD_NoColonistAvailable".Translate(), parent, MessageTypeDefOf.RejectInput); return; } // 寻找燃料 Thing fuel = FindFuelForRefuel(bestColonist); if (fuel == null) { Messages.Message("DD_NoFuelAvailable".Translate(FuelType), parent, MessageTypeDefOf.RejectInput); return; } // 为殖民者创建强制加注工作 Job job = JobMaker.MakeJob(Wula_JobDefOf.WULA_RefuelMech, parent, fuel); job.count = GetFuelCountToFullyRefuel(); job.playerForced = true; bestColonist.jobs.StartJob(job, JobCondition.InterruptForced, null, resumeCurJobAfterwards: true); // 显示消息 Messages.Message("DD_OrderedRefuel".Translate(bestColonist.LabelShort, parent.LabelShort), parent, MessageTypeDefOf.PositiveEvent); } private Pawn FindBestColonistForRefuel() { Map map = parent.Map; if (map == null) return null; // 寻找所有可用的殖民者 List colonists = map.mapPawns.FreeColonists.ToList(); // 过滤掉无法工作或无法到达机甲的殖民者 colonists = colonists.Where(colonist => colonist.health.capacities.CapableOf(PawnCapacityDefOf.Manipulation) && colonist.health.capacities.CapableOf(PawnCapacityDefOf.Moving) && !colonist.Downed && !colonist.Dead && colonist.CanReserveAndReach(parent, PathEndMode.Touch, Danger.Some) ).ToList(); if (!colonists.Any()) return null; // 按照距离排序,选择最近的殖民者 return colonists.OrderBy(colonist => colonist.Position.DistanceTo(parent.Position)).FirstOrDefault(); } private Thing FindFuelForRefuel(Pawn colonist) { if (FuelType == null) return null; // 先在殖民者库存中寻找 if (colonist.inventory != null) { Thing fuelInInventory = colonist.inventory.innerContainer.FirstOrDefault(t => t.def == FuelType); if (fuelInInventory != null) return fuelInInventory; } // 在地图上寻找可用的燃料 return GenClosest.ClosestThingReachable( colonist.Position, colonist.Map, ThingRequest.ForDef(FuelType), PathEndMode.ClosestTouch, TraverseParms.For(colonist), 9999f, validator: thing => !thing.IsForbidden(colonist) && colonist.CanReserve(thing) ); } public bool CanRefuelNow() { if (IsFull || parent.Map == null || FuelType == null) return false; // 检查是否有可用殖民者 if (FindBestColonistForRefuel() == null) return false; return true; } // 检查机甲是否有驾驶员 public bool HasPilot() { var pilotComp = parent.TryGetComp(); return pilotComp != null && pilotComp.HasPilots; } public override IEnumerable CompGetGizmosExtra() { // 添加燃料状态Gizmo yield return new Gizmo_MechFuelStatus(this); // 添加立刻加注按钮 if (!IsFull && parent.Faction == Faction.OfPlayer) { Command_Action refuelNow = new Command_Action { defaultLabel = "DD_RefuelNow".Translate(), defaultDesc = "DD_RefuelNowDesc".Translate(), icon = ContentFinder.Get("WulaFallenEmpire/UI/Commands/DD_Refuel_Mech"), action = () => RefuelNow() }; // 检查是否可以立刻加注 if (!CanRefuelNow()) { refuelNow.Disable("DD_CannotRefuelNow".Translate()); } yield return refuelNow; } // 在 God Mode 下显示测试按钮 if (DebugSettings.godMode && parent.Faction == Faction.OfPlayer) { // 设置燃料为空 Command_Action setEmpty = new Command_Action { defaultLabel = "DD_Debug_SetEmpty".Translate(), defaultDesc = "DD_Debug_SetEmptyDesc".Translate(), icon = ContentFinder.Get("UI/Commands/SetEmpty", false) ?? BaseContent.BadTex, action = () => SetFuel(0f) }; yield return setEmpty; // 设置燃料为50% Command_Action setHalf = new Command_Action { defaultLabel = "DD_Debug_SetHalf".Translate(), defaultDesc = "DD_Debug_SetHalfDesc".Translate(), icon = ContentFinder.Get("UI/Commands/SetHalf", false) ?? BaseContent.BadTex, action = () => SetFuel(Props.fuelCapacity * 0.5f) }; yield return setHalf; // 设置燃料为满 Command_Action setFull = new Command_Action { defaultLabel = "DD_Debug_SetFull".Translate(), defaultDesc = "DD_Debug_SetFullDesc".Translate(), icon = ContentFinder.Get("UI/Commands/SetFull", false) ?? BaseContent.BadTex, action = () => SetFuel(Props.fuelCapacity) }; yield return setFull; } } public override void PostExposeData() { base.PostExposeData(); Scribe_Values.Look(ref fuel, "fuel", Props.fuelCapacity); Scribe_Values.Look(ref isShutdown, "isShutdown", false); Scribe_Values.Look(ref lastFuelTick, "lastFuelTick", -1); } public override string CompInspectStringExtra() { string baseString = base.CompInspectStringExtra(); string fuelString = "DD_Fuel".Translate(FuelType) + ": " + fuel.ToString("F1") + " / " + Props.fuelCapacity.ToString("F1") + " (" + (FuelPercent * 100f).ToString("F0") + "%)"; if (!baseString.NullOrEmpty()) return baseString + "\n" + fuelString; else return fuelString; } } }