This commit is contained in:
2026-02-24 12:02:38 +08:00
parent 1af5f0c1d8
commit 96bc1d4c5a
57 changed files with 6595 additions and 1170 deletions

View File

@@ -0,0 +1,446 @@
// 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();
// 每60ticks1秒消耗一次燃料
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<CompMechPilotHolder>();
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.DD_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<Pawn> 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<CompMechPilotHolder>();
return pilotComp != null && pilotComp.HasPilots;
}
public override IEnumerable<Gizmo> 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<Texture2D>.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<Texture2D>.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<Texture2D>.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<Texture2D>.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;
}
}
}

View File

@@ -0,0 +1,56 @@
// CompProperties_MechFuel.cs
using RimWorld;
using System.Collections.Generic;
using UnityEngine;
using Verse;
namespace WulaFallenEmpire
{
public class CompProperties_MechFuel : CompProperties
{
public ThingDef fuelType; // 燃料种类
public float fuelCapacity = 100f; // 燃料容量
public float dailyFuelConsumption = 10f; // 每日燃料消耗量
public float refuelSpeedFactor = 1f; // 加注速度因子
public int refuelDuration = 240; // 基础加注时间ticks
// Gizmo显示设置
public Color fuelBarColor = new Color(0.1f, 0.6f, 0.9f); // 燃料条颜色
public Color emptyBarColor = new Color(0.2f, 0.2f, 0.24f); // 空燃料条颜色
// 自动加注设置
public float autoRefuelThreshold = 0.3f; // 自动加注阈值(低于此值自动加注)
public bool allowAutoRefuel = true; // 是否允许自动加注
// 燃料耗尽效果
public bool shutdownWhenEmpty = true; // 燃料耗尽时关机
public CompProperties_MechFuel()
{
this.compClass = typeof(CompMechFuel);
}
public override IEnumerable<string> ConfigErrors(ThingDef parentDef)
{
foreach (string error in base.ConfigErrors(parentDef))
{
yield return error;
}
if (fuelType == null)
{
yield return $"fuelType is null for {parentDef.defName}";
}
if (fuelCapacity <= 0f)
{
yield return $"fuelCapacity must be positive for {parentDef.defName}";
}
if (dailyFuelConsumption < 0f)
{
yield return $"dailyFuelConsumption cannot be negative for {parentDef.defName}";
}
}
}
}

View File

@@ -0,0 +1,127 @@
// Gizmo_MechFuelStatus.cs
using RimWorld;
using UnityEngine;
using Verse;
namespace WulaFallenEmpire
{
[StaticConstructorOnStartup]
public class Gizmo_MechFuelStatus : Gizmo
{
public CompMechFuel fuelComp;
private static readonly Texture2D FullFuelBarTex = SolidColorMaterials.NewSolidColorTexture(new Color(0.1f, 0.6f, 0.9f));
private static readonly Texture2D EmptyFuelBarTex = SolidColorMaterials.NewSolidColorTexture(new Color(0.2f, 0.2f, 0.24f));
private static readonly Texture2D WarningFuelBarTex = SolidColorMaterials.NewSolidColorTexture(new Color(0.9f, 0.6f, 0.1f));
// 测试模式图标
private static readonly Texture2D DebugIcon = ContentFinder<Texture2D>.Get("UI/Commands/Debug", false);
public Gizmo_MechFuelStatus(CompMechFuel fuelComp)
{
this.fuelComp = fuelComp;
Order = -90f; // 在护盾Gizmo之后显示
}
public override float GetWidth(float maxWidth)
{
return 140f;
}
public override GizmoResult GizmoOnGUI(Vector2 topLeft, float maxWidth, GizmoRenderParms parms)
{
Rect rect = new Rect(topLeft.x, topLeft.y, GetWidth(maxWidth), 75f);
Rect rect2 = rect.ContractedBy(6f);
Widgets.DrawWindowBackground(rect);
// 在 God Mode 下显示调试图标
if (DebugSettings.godMode && DebugIcon != null)
{
Rect debugIconRect = new Rect(rect.x + 5f, rect.y + 5f, 12f, 12f);
GUI.DrawTexture(debugIconRect, DebugIcon);
}
// 标题区域
Rect titleRect = rect2;
titleRect.height = rect.height / 2f;
Text.Font = GameFont.Tiny;
// 在 God Mode 下显示"调试模式"标题
string title = DebugSettings.godMode ?
"DD_MechFuel".Translate().Resolve() + " [DEBUG]" :
"DD_MechFuel".Translate().Resolve();
Widgets.Label(titleRect, title);
// 燃料条区域
Rect barRect = rect2;
barRect.yMin = rect2.y + rect2.height / 2f;
// 选择燃料条颜色(低燃料时用警告色)
Texture2D barTex;
if (fuelComp.FuelPercent < 0.2f)
{
barTex = WarningFuelBarTex;
}
else
{
barTex = FullFuelBarTex;
}
// 绘制燃料条
Widgets.FillableBar(barRect, fuelComp.FuelPercent, barTex, EmptyFuelBarTex, doBorder: false);
// 绘制燃料数值
Text.Font = GameFont.Small;
Text.Anchor = TextAnchor.MiddleCenter;
Widgets.Label(barRect,
fuelComp.Fuel.ToString("F0") + " / " +
fuelComp.Props.fuelCapacity.ToString("F0") +
" (" + (fuelComp.FuelPercent * 100f).ToString("F0") + "%)");
Text.Anchor = TextAnchor.UpperLeft;
// 状态文本
if (fuelComp.IsShutdown)
{
Rect statusRect = new Rect(barRect.x, barRect.y - 15f, barRect.width, 15f);
Text.Font = GameFont.Tiny;
Text.Anchor = TextAnchor.UpperCenter;
GUI.color = Color.red;
Widgets.Label(statusRect, "DD_Shutdown".Translate());
GUI.color = Color.white;
Text.Anchor = TextAnchor.UpperLeft;
}
// 工具提示
string tip = "DD_MechFuelTip".Translate(
fuelComp.FuelPercent.ToStringPercent(),
fuelComp.Props.dailyFuelConsumption,
fuelComp.Props.fuelType.label
);
if (fuelComp.IsShutdown)
{
tip += "\n\n" + "DD_ShutdownTip".Translate();
}
else if (fuelComp.NeedsRefueling)
{
tip += "\n\n" + "DD_NeedsRefueling".Translate();
}
// 在 God Mode 下添加调试信息到工具提示
if (DebugSettings.godMode)
{
tip += "\n\n" + "DD_Debug_Tip".Translate().Colorize(Color.gray) +
"\n" + "DD_Debug_Status".Translate(
fuelComp.IsShutdown ? "DD_Shutdown".Translate() : "DD_Running".Translate(),
fuelComp.HasPilot() ? "DD_HasPilot".Translate() : "DD_NoPilot".Translate()
);
}
TooltipHandler.TipRegion(rect2, tip);
return new GizmoResult(GizmoState.Clear);
}
}
}