1
This commit is contained in:
@@ -1,35 +1,32 @@
|
||||
// Building_MaintenancePod.cs
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
public class CompProperties_MaintenanceCycle : CompProperties_BiosculpterPod_BaseCycle
|
||||
public class Building_MaintenancePod : Building
|
||||
{
|
||||
public HediffDef hediffToRemove;
|
||||
public CompMaintenancePod MaintenanceComp => GetComp<CompMaintenancePod>();
|
||||
|
||||
public CompProperties_MaintenanceCycle()
|
||||
protected override void Tick()
|
||||
{
|
||||
compClass = typeof(CompMaintenanceCycle);
|
||||
base.Tick();
|
||||
// 建筑级别的特殊逻辑可以在这里添加
|
||||
}
|
||||
}
|
||||
|
||||
public class CompMaintenanceCycle : CompBiosculpterPod_Cycle
|
||||
{
|
||||
public new CompProperties_MaintenanceCycle Props => (CompProperties_MaintenanceCycle)props;
|
||||
|
||||
public override void CycleCompleted(Pawn pawn)
|
||||
public override string GetInspectString()
|
||||
{
|
||||
if (pawn == null)
|
||||
string baseString = base.GetInspectString();
|
||||
string maintenanceString = MaintenanceComp?.CompInspectStringExtra();
|
||||
|
||||
if (!string.IsNullOrEmpty(maintenanceString))
|
||||
{
|
||||
return;
|
||||
if (!string.IsNullOrEmpty(baseString))
|
||||
return baseString + "\n" + maintenanceString;
|
||||
return maintenanceString;
|
||||
}
|
||||
|
||||
Hediff hediff = pawn.health.hediffSet.GetFirstHediffOfDef(Props.hediffToRemove);
|
||||
if (hediff != null)
|
||||
{
|
||||
hediff.Severity = 0f;
|
||||
Messages.Message("WULA_MaintenanceCycleComplete".Translate(pawn.Named("PAWN")), pawn, MessageTypeDefOf.PositiveEvent);
|
||||
}
|
||||
return baseString;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,16 +15,25 @@ namespace WulaFallenEmpire
|
||||
public SoundDef enterSound;
|
||||
public SoundDef exitSound;
|
||||
public EffecterDef operatingEffecter;
|
||||
public int baseDurationTicks = 60000;
|
||||
public float ticksPerSeverity = 0f;
|
||||
|
||||
// 时间相关
|
||||
public int baseDurationTicks = 60000; // 基础维护时间(1天)
|
||||
public float ticksPerNeedLevel = 120000f; // 每点需求降低需要的时间
|
||||
|
||||
// 电力消耗
|
||||
public float powerConsumptionRunning = 250f;
|
||||
public float powerConsumptionIdle = 50f;
|
||||
public HediffDef hediffToRemove;
|
||||
public float componentCostPerSeverity = 1f;
|
||||
public int baseComponentCost = 0;
|
||||
public float minSeverityToMaintain = 0.75f;
|
||||
public float hediffSeverityAfterCycle = 0.01f;
|
||||
|
||||
// 组件消耗
|
||||
public float componentCostPerNeedLevel = 2f;
|
||||
public int baseComponentCost = 1;
|
||||
|
||||
// 维护效果
|
||||
public float minNeedLevelToMaintain = 0.3f; // 低于此值才需要维护
|
||||
public float needLevelAfterCycle = 1.0f; // 维护后的需求水平
|
||||
public bool healInjuries = true; // 是否治疗损伤
|
||||
public bool healMissingParts = true; // 是否修复缺失部位
|
||||
public int maxInjuriesHealedPerCycle = 5; // 每次维护最多治疗的损伤数量
|
||||
public CompProperties_MaintenancePod()
|
||||
{
|
||||
compClass = typeof(CompMaintenancePod);
|
||||
@@ -40,45 +49,55 @@ namespace WulaFallenEmpire
|
||||
private CompRefuelable refuelableComp;
|
||||
private int ticksRemaining;
|
||||
private MaintenancePodState state = MaintenancePodState.Idle;
|
||||
|
||||
private Effecter operatingEffecter;
|
||||
private static readonly Texture2D CancelIcon = ContentFinder<Texture2D>.Get("UI/Designators/Cancel");
|
||||
private static readonly Texture2D EnterIcon = ContentFinder<Texture2D>.Get("UI/Commands/PodEject");
|
||||
|
||||
// ===================== Properties =====================
|
||||
public CompProperties_MaintenancePod Props => (CompProperties_MaintenancePod)props;
|
||||
public MaintenancePodState State => state;
|
||||
public Pawn Occupant => innerContainer.FirstOrDefault() as Pawn;
|
||||
public bool PowerOn => powerComp != null && powerComp.PowerOn;
|
||||
|
||||
public float RequiredComponents(Pawn pawn)
|
||||
public float RequiredComponents
|
||||
{
|
||||
if (pawn == null || Props.hediffToRemove == null) return Props.baseComponentCost;
|
||||
Hediff hediff = pawn.health.hediffSet.GetFirstHediffOfDef(Props.hediffToRemove);
|
||||
if (hediff == null) return Props.baseComponentCost;
|
||||
return Props.baseComponentCost + (int)(hediff.Severity * Props.componentCostPerSeverity);
|
||||
}
|
||||
get
|
||||
{
|
||||
var occupant = Occupant;
|
||||
if (occupant == null) return Props.baseComponentCost;
|
||||
|
||||
public int RequiredDuration(Pawn pawn)
|
||||
var maintenanceNeed = occupant.needs?.TryGetNeed<Need_Maintenance>();
|
||||
if (maintenanceNeed == null) return Props.baseComponentCost;
|
||||
|
||||
// 计算基于当前需求水平的组件需求
|
||||
float needDeficit = 1.0f - maintenanceNeed.CurLevel;
|
||||
return Props.baseComponentCost + (needDeficit * Props.componentCostPerNeedLevel);
|
||||
}
|
||||
}
|
||||
public int RequiredDuration
|
||||
{
|
||||
if (pawn == null || Props.hediffToRemove == null) return Props.baseDurationTicks;
|
||||
Hediff hediff = pawn.health.hediffSet.GetFirstHediffOfDef(Props.hediffToRemove);
|
||||
if (hediff == null) return Props.baseDurationTicks;
|
||||
return Props.baseDurationTicks + (int)(hediff.Severity * Props.ticksPerSeverity);
|
||||
}
|
||||
get
|
||||
{
|
||||
var occupant = Occupant;
|
||||
if (occupant == null) return Props.baseDurationTicks;
|
||||
|
||||
var maintenanceNeed = occupant.needs?.TryGetNeed<Need_Maintenance>();
|
||||
if (maintenanceNeed == null) return Props.baseDurationTicks;
|
||||
|
||||
// 计算基于当前需求水平的维护时间
|
||||
float needDeficit = 1.0f - maintenanceNeed.CurLevel;
|
||||
return Props.baseDurationTicks + (int)(needDeficit * Props.ticksPerNeedLevel);
|
||||
}
|
||||
}
|
||||
// ===================== Setup =====================
|
||||
public CompMaintenancePod()
|
||||
{
|
||||
innerContainer = new ThingOwner<Thing>(this, false, LookMode.Deep);
|
||||
}
|
||||
|
||||
public override void PostSpawnSetup(bool respawningAfterLoad)
|
||||
{
|
||||
base.PostSpawnSetup(respawningAfterLoad);
|
||||
powerComp = parent.TryGetComp<CompPowerTrader>();
|
||||
refuelableComp = parent.TryGetComp<CompRefuelable>();
|
||||
}
|
||||
|
||||
public override void PostExposeData()
|
||||
{
|
||||
base.PostExposeData();
|
||||
@@ -86,361 +105,335 @@ namespace WulaFallenEmpire
|
||||
Scribe_Values.Look(ref ticksRemaining, "ticksRemaining", 0);
|
||||
Scribe_Deep.Look(ref innerContainer, "innerContainer", this);
|
||||
}
|
||||
|
||||
public override void PostDestroy(DestroyMode mode, Map previousMap)
|
||||
{
|
||||
base.PostDestroy(mode, previousMap);
|
||||
// If the pod is deconstructed or destroyed, eject the occupant to prevent deletion.
|
||||
if (mode == DestroyMode.Deconstruct || mode == DestroyMode.KillFinalize)
|
||||
{
|
||||
Log.Warning($"[WulaPodDebug] Pod destroyed (mode: {mode}). Ejecting pawn.");
|
||||
EjectPawn();
|
||||
}
|
||||
}
|
||||
|
||||
// ===================== IThingHolder Implementation =====================
|
||||
public void GetChildHolders(List<IThingHolder> outChildren)
|
||||
{
|
||||
ThingOwnerUtility.AppendThingHoldersFromThings(outChildren, GetDirectlyHeldThings());
|
||||
}
|
||||
|
||||
public ThingOwner GetDirectlyHeldThings()
|
||||
{
|
||||
return innerContainer;
|
||||
}
|
||||
|
||||
// ===================== Core Logic =====================
|
||||
public override void CompTick()
|
||||
{
|
||||
base.CompTick();
|
||||
if (!parent.Spawned) return;
|
||||
|
||||
if (state == MaintenancePodState.Running)
|
||||
{
|
||||
if (PowerOn)
|
||||
{
|
||||
ticksRemaining--;
|
||||
if (ticksRemaining <= 0)
|
||||
{
|
||||
CycleFinished();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 更新电力消耗
|
||||
if (powerComp != null)
|
||||
{
|
||||
powerComp.PowerOutput = -(state == MaintenancePodState.Running ? Props.powerConsumptionRunning : Props.powerConsumptionIdle);
|
||||
}
|
||||
}
|
||||
// 运行维护周期
|
||||
if (state == MaintenancePodState.Running && PowerOn)
|
||||
{
|
||||
ticksRemaining--;
|
||||
|
||||
// 更新效果器
|
||||
if (Props.operatingEffecter != null)
|
||||
{
|
||||
if (operatingEffecter == null)
|
||||
{
|
||||
operatingEffecter = Props.operatingEffecter.Spawn();
|
||||
}
|
||||
operatingEffecter.EffectTick(parent, Occupant);
|
||||
}
|
||||
if (ticksRemaining <= 0)
|
||||
{
|
||||
CycleFinished();
|
||||
}
|
||||
}
|
||||
else if (operatingEffecter != null)
|
||||
{
|
||||
operatingEffecter.Cleanup();
|
||||
operatingEffecter = null;
|
||||
}
|
||||
}
|
||||
public void StartCycle(Pawn pawn)
|
||||
{
|
||||
Log.Warning($"[WulaPodDebug] StartCycle called for pawn: {pawn.LabelShortCap}");
|
||||
float required = RequiredComponents(pawn);
|
||||
if (refuelableComp.Fuel < required)
|
||||
if (pawn == null) return;
|
||||
// 检查组件是否足够
|
||||
float requiredComponents = RequiredComponents;
|
||||
if (refuelableComp.Fuel < requiredComponents)
|
||||
{
|
||||
Log.Error($"[WulaPodDebug] ERROR: Tried to start cycle for {pawn.LabelShort} without enough components.");
|
||||
Messages.Message("WULA_MaintenancePod_NotEnoughComponents".Translate(requiredComponents.ToString("F0")), MessageTypeDefOf.RejectInput);
|
||||
return;
|
||||
}
|
||||
|
||||
if (required > 0)
|
||||
// 消耗组件
|
||||
if (requiredComponents > 0)
|
||||
{
|
||||
refuelableComp.ConsumeFuel(required);
|
||||
refuelableComp.ConsumeFuel(requiredComponents);
|
||||
}
|
||||
|
||||
Log.Warning($"[WulaPodDebug] Pawn state before action: holdingOwner is {(pawn.holdingOwner == null ? "NULL" : "NOT NULL")}, Spawned is {pawn.Spawned}");
|
||||
|
||||
// THE ACTUAL FIX: A pawn, whether held or not, must be despawned before being put in a container.
|
||||
// 将 pawn 放入容器
|
||||
if (pawn.Spawned)
|
||||
{
|
||||
Log.Warning($"[WulaPodDebug] Pawn is spawned. Despawning...");
|
||||
pawn.DeSpawn(DestroyMode.Vanish);
|
||||
}
|
||||
Log.Warning($"[WulaPodDebug] Attempting to add/transfer pawn to container.");
|
||||
innerContainer.TryAddOrTransfer(pawn);
|
||||
|
||||
|
||||
// 开始维护周期
|
||||
state = MaintenancePodState.Running;
|
||||
ticksRemaining = RequiredDuration(pawn);
|
||||
Log.Warning($"[WulaPodDebug] Cycle started. Ticks remaining: {ticksRemaining}");
|
||||
}
|
||||
ticksRemaining = RequiredDuration;
|
||||
|
||||
// 播放进入音效
|
||||
if (Props.enterSound != null)
|
||||
{
|
||||
Props.enterSound.PlayOneShot(new TargetInfo(parent.Position, parent.Map));
|
||||
}
|
||||
Messages.Message("WULA_MaintenanceCycleStarted".Translate(pawn.LabelShortCap), MessageTypeDefOf.PositiveEvent);
|
||||
}
|
||||
private void CycleFinished()
|
||||
{
|
||||
Pawn occupant = Occupant;
|
||||
Log.Warning($"[WulaPodDebug] CycleFinished. Occupant: {(occupant == null ? "NULL" : occupant.LabelShortCap)}");
|
||||
var occupant = Occupant;
|
||||
if (occupant == null)
|
||||
{
|
||||
Log.Error("[WulaPodDebug] ERROR: Maintenance cycle finished, but no one was inside.");
|
||||
state = MaintenancePodState.Idle;
|
||||
return;
|
||||
}
|
||||
// 执行维护效果
|
||||
PerformMaintenanceEffects(occupant);
|
||||
|
||||
// 1. Fix the maintenance hediff
|
||||
bool maintenanceDone = false;
|
||||
if (Props.hediffToRemove != null)
|
||||
// 弹出 pawn
|
||||
EjectPawn();
|
||||
|
||||
Messages.Message("WULA_MaintenanceCycleComplete".Translate(occupant.LabelShortCap), MessageTypeDefOf.PositiveEvent);
|
||||
}
|
||||
private void PerformMaintenanceEffects(Pawn pawn)
|
||||
{
|
||||
var maintenanceNeed = pawn.needs?.TryGetNeed<Need_Maintenance>();
|
||||
|
||||
// 1. 恢复维护需求
|
||||
if (maintenanceNeed != null)
|
||||
{
|
||||
Hediff hediff = occupant.health.hediffSet.GetFirstHediffOfDef(Props.hediffToRemove);
|
||||
if (hediff != null)
|
||||
{
|
||||
hediff.Severity = Props.hediffSeverityAfterCycle;
|
||||
Messages.Message("WULA_MaintenanceComplete".Translate(occupant.Named("PAWN")), occupant, MessageTypeDefOf.PositiveEvent);
|
||||
maintenanceDone = true;
|
||||
}
|
||||
maintenanceNeed.PerformMaintenance(Props.needLevelAfterCycle);
|
||||
}
|
||||
|
||||
// 2. Heal all other injuries and missing parts
|
||||
// 2. 治疗损伤(如果启用)
|
||||
if (Props.healInjuries)
|
||||
{
|
||||
HealInjuries(pawn);
|
||||
}
|
||||
// 3. 修复缺失部位(如果启用)
|
||||
if (Props.healMissingParts)
|
||||
{
|
||||
HealMissingParts(pawn);
|
||||
}
|
||||
}
|
||||
private void HealInjuries(Pawn pawn)
|
||||
{
|
||||
int injuriesHealed = 0;
|
||||
// Loop until no more health conditions can be fixed
|
||||
while (HealthUtility.TryGetWorstHealthCondition(occupant, out var hediffToFix, out var _))
|
||||
var injuries = pawn.health.hediffSet.hediffs
|
||||
.Where(h => h.def.isBad && h.Visible && h.def != HediffDefOf.BloodLoss)
|
||||
.ToList();
|
||||
foreach (var injury in injuries)
|
||||
{
|
||||
// Ensure we don't try to "heal" the maintenance hediff itself, as it's handled separately.
|
||||
if (hediffToFix != null && hediffToFix.def == Props.hediffToRemove)
|
||||
{
|
||||
if (injuriesHealed >= Props.maxInjuriesHealedPerCycle)
|
||||
break;
|
||||
}
|
||||
|
||||
// Store the state before attempting to fix
|
||||
int initialHediffCount = occupant.health.hediffSet.hediffs.Count;
|
||||
var hediffsBefore = new HashSet<Hediff>(occupant.health.hediffSet.hediffs);
|
||||
|
||||
// Attempt to fix the worst condition
|
||||
HealthUtility.FixWorstHealthCondition(occupant);
|
||||
|
||||
// Check if a change actually occurred
|
||||
bool conditionFixed = initialHediffCount > occupant.health.hediffSet.hediffs.Count ||
|
||||
!hediffsBefore.SetEquals(occupant.health.hediffSet.hediffs);
|
||||
|
||||
if (conditionFixed)
|
||||
{
|
||||
injuriesHealed++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If FixWorstHealthCondition did nothing, it means it can't handle
|
||||
// the current worst condition. We must break to avoid an infinite loop.
|
||||
Log.Warning($"[WulaPodDebug] Halting healing loop. FixWorstHealthCondition did not resolve: {hediffToFix?.LabelCap ?? "a missing part"}.");
|
||||
break;
|
||||
}
|
||||
pawn.health.RemoveHediff(injury);
|
||||
injuriesHealed++;
|
||||
}
|
||||
|
||||
if (injuriesHealed > 0)
|
||||
{
|
||||
Messages.Message("WULA_MaintenanceHealedAllWounds".Translate(occupant.Named("PAWN")), occupant, MessageTypeDefOf.PositiveEvent);
|
||||
Messages.Message("WULA_MaintenanceHealedInjuries".Translate(pawn.LabelShortCap, injuriesHealed), MessageTypeDefOf.PositiveEvent);
|
||||
}
|
||||
}
|
||||
private void HealMissingParts(Pawn pawn)
|
||||
{
|
||||
var missingParts = pawn.health.hediffSet.GetMissingPartsCommonAncestors();
|
||||
int partsHealed = 0;
|
||||
foreach (var missingPart in missingParts)
|
||||
{
|
||||
if (partsHealed >= 1) // 每次最多修复一个缺失部位
|
||||
break;
|
||||
pawn.health.RemoveHediff(missingPart);
|
||||
partsHealed++;
|
||||
}
|
||||
if (partsHealed > 0)
|
||||
{
|
||||
Messages.Message("WULA_MaintenanceHealedParts".Translate(pawn.LabelShortCap), MessageTypeDefOf.PositiveEvent);
|
||||
}
|
||||
else if (!maintenanceDone)
|
||||
{
|
||||
// If nothing was done at all, give a neutral message
|
||||
Messages.Message("WULA_MaintenanceNoEffect".Translate(occupant.Named("PAWN")), occupant, MessageTypeDefOf.NeutralEvent);
|
||||
}
|
||||
|
||||
EjectPawn();
|
||||
}
|
||||
|
||||
public void EjectPawn(bool interrupted = false)
|
||||
{
|
||||
Pawn occupant = Occupant;
|
||||
Log.Warning($"[WulaPodDebug] EjectPawn. Occupant: {(occupant == null ? "NULL" : occupant.LabelShortCap)}");
|
||||
var occupant = Occupant;
|
||||
if (occupant != null)
|
||||
{
|
||||
Map mapToUse = parent.Map ?? Find.CurrentMap;
|
||||
if (mapToUse == null)
|
||||
// 弹出到交互单元格
|
||||
innerContainer.TryDropAll(parent.InteractionCell, parent.Map, ThingPlaceMode.Near);
|
||||
|
||||
// 播放退出音效
|
||||
if (Props.exitSound != null)
|
||||
{
|
||||
// Try to find the map from nearby things
|
||||
mapToUse = GenClosest.ClosestThing_Global(occupant.Position, Gen.YieldSingle(parent), 99999f, (thing) => thing.Map != null)?.Map;
|
||||
Props.exitSound.PlayOneShot(new TargetInfo(parent.Position, parent.Map));
|
||||
}
|
||||
|
||||
if (mapToUse != null)
|
||||
{
|
||||
innerContainer.TryDropAll(parent.InteractionCell, mapToUse, ThingPlaceMode.Near);
|
||||
if (Props.exitSound != null)
|
||||
{
|
||||
SoundStarter.PlayOneShot(Props.exitSound, new TargetInfo(parent.Position, mapToUse));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error($"[WulaPodDebug] EjectPawn FAILED: No valid map found to eject {occupant.LabelShortCap}. The pawn will be lost.");
|
||||
}
|
||||
|
||||
// Additional logic to handle occupant if needed
|
||||
// 如果被中断,应用负面效果
|
||||
if (interrupted)
|
||||
{
|
||||
occupant.needs?.mood.thoughts.memories.TryGainMemory(ThoughtDefOf.SoakingWet);
|
||||
occupant.health?.AddHediff(HediffDefOf.BiosculptingSickness);
|
||||
occupant.needs?.mood?.thoughts?.memories?.TryGainMemory(ThoughtDefOf.SoakingWet);
|
||||
}
|
||||
}
|
||||
innerContainer.Clear();
|
||||
state = MaintenancePodState.Idle;
|
||||
Log.Warning($"[WulaPodDebug] EjectPawn finished. State set to Idle.");
|
||||
}
|
||||
|
||||
// 清理效果器
|
||||
if (operatingEffecter != null)
|
||||
{
|
||||
operatingEffecter.Cleanup();
|
||||
operatingEffecter = null;
|
||||
}
|
||||
}
|
||||
// ===================== UI & Gizmos =====================
|
||||
public override string CompInspectStringExtra()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendLine("WULA_MaintenancePod_Status".Translate() + ": " + $"WULA_MaintenancePod_State_{state}".Translate());
|
||||
|
||||
if (state == MaintenancePodState.Running)
|
||||
if (state == MaintenancePodState.Running && Occupant != null)
|
||||
{
|
||||
if (Occupant != null)
|
||||
{
|
||||
sb.AppendLine("Contains".Translate() + ": " + Occupant.NameShortColored.Resolve());
|
||||
}
|
||||
sb.AppendLine("Contains".Translate() + ": " + Occupant.NameShortColored.Resolve());
|
||||
sb.AppendLine("TimeLeft".Translate() + ": " + ticksRemaining.ToStringTicksToPeriod());
|
||||
var maintenanceNeed = Occupant.needs?.TryGetNeed<Need_Maintenance>();
|
||||
if (maintenanceNeed != null)
|
||||
{
|
||||
// 直接显示 CurLevel,确保与 Need 显示一致
|
||||
sb.AppendLine("WULA_MaintenanceLevel".Translate() + ": " + maintenanceNeed.CurLevel.ToStringPercent());
|
||||
}
|
||||
}
|
||||
|
||||
if (!PowerOn)
|
||||
{
|
||||
sb.AppendLine("NoPower".Translate().Colorize(Color.red));
|
||||
}
|
||||
|
||||
return sb.ToString().TrimEnd();
|
||||
}
|
||||
|
||||
public override IEnumerable<Gizmo> CompGetGizmosExtra()
|
||||
{
|
||||
foreach (var gizmo in base.CompGetGizmosExtra())
|
||||
{
|
||||
yield return gizmo;
|
||||
}
|
||||
|
||||
// 进入维护舱的按钮
|
||||
if (state == MaintenancePodState.Idle && PowerOn)
|
||||
{
|
||||
var enterCommand = new Command_Action
|
||||
yield return new Command_Action
|
||||
{
|
||||
defaultLabel = "WULA_MaintenancePod_Enter".Translate(),
|
||||
defaultDesc = "WULA_MaintenancePod_EnterDesc".Translate(),
|
||||
icon = EnterIcon,
|
||||
action = () =>
|
||||
{
|
||||
List<FloatMenuOption> options = GetPawnOptions();
|
||||
if (options.Any())
|
||||
{
|
||||
Find.WindowStack.Add(new FloatMenu(options));
|
||||
}
|
||||
else
|
||||
{
|
||||
Messages.Message("WULA_MaintenancePod_NoOneNeeds".Translate(), MessageTypeDefOf.RejectInput);
|
||||
}
|
||||
}
|
||||
action = () => ShowPawnSelectionMenu()
|
||||
};
|
||||
yield return enterCommand;
|
||||
}
|
||||
|
||||
// 取消维护的按钮
|
||||
if (state == MaintenancePodState.Running)
|
||||
{
|
||||
var cancelCommand = new Command_Action
|
||||
yield return new Command_Action
|
||||
{
|
||||
defaultLabel = "CommandCancelConstructionLabel".Translate(),
|
||||
defaultDesc = "WULA_MaintenancePod_CancelDesc".Translate(),
|
||||
icon = CancelIcon,
|
||||
action = () =>
|
||||
{
|
||||
EjectPawn();
|
||||
EjectPawn(true);
|
||||
Messages.Message("WULA_MaintenanceCanceled".Translate(), MessageTypeDefOf.NegativeEvent);
|
||||
}
|
||||
};
|
||||
yield return cancelCommand;
|
||||
}
|
||||
|
||||
// DEV GIZMO
|
||||
if (DebugSettings.godMode && state == MaintenancePodState.Running)
|
||||
{
|
||||
var finishCommand = new Command_Action
|
||||
{
|
||||
defaultLabel = "DEV: Finish Cycle",
|
||||
action = () =>
|
||||
{
|
||||
Log.Warning("[WulaPodDebug] DEV: Force finishing cycle.");
|
||||
CycleFinished();
|
||||
}
|
||||
};
|
||||
yield return finishCommand;
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowPawnSelectionMenu()
|
||||
{
|
||||
var options = GetPawnOptions();
|
||||
if (options.Any())
|
||||
{
|
||||
Find.WindowStack.Add(new FloatMenu(options));
|
||||
}
|
||||
else
|
||||
{
|
||||
Messages.Message("WULA_MaintenancePod_NoOneNeeds".Translate(), MessageTypeDefOf.RejectInput);
|
||||
}
|
||||
}
|
||||
private List<FloatMenuOption> GetPawnOptions()
|
||||
{
|
||||
List<FloatMenuOption> options = new List<FloatMenuOption>();
|
||||
// Now iterates over all pawns on the map, not just colonists.
|
||||
foreach (Pawn p in parent.Map.mapPawns.AllPawns.Where(pawn => pawn.def.defName == "WulaSpecies" || pawn.def.defName == "WulaSpeciesReal"))
|
||||
{
|
||||
if (p.health.hediffSet.HasHediff(Props.hediffToRemove))
|
||||
{
|
||||
// If the pawn is downed or not a free colonist, they need to be brought to the pod.
|
||||
if (p.Downed || !p.IsFreeColonist)
|
||||
{
|
||||
float required = RequiredComponents(p);
|
||||
if (refuelableComp.Fuel < required)
|
||||
{
|
||||
options.Add(new FloatMenuOption(p.LabelShortCap + " (" + p.KindLabel + ", " + "WULA_MaintenancePod_NotEnoughComponents".Translate(required.ToString("F0")) + ")", null));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Find colonists who can haul the pawn.
|
||||
var potentialHaulers = parent.Map.mapPawns.FreeColonistsSpawned.Where(colonist =>
|
||||
!colonist.Downed && colonist.CanReserveAndReach(p, PathEndMode.OnCell, Danger.Deadly) && colonist.CanReserveAndReach(parent, PathEndMode.InteractionCell, Danger.Deadly));
|
||||
var options = new List<FloatMenuOption>();
|
||||
var map = parent.Map;
|
||||
|
||||
if (!potentialHaulers.Any())
|
||||
{
|
||||
// If no one can haul, then it's unreachable.
|
||||
options.Add(new FloatMenuOption(p.LabelShortCap + " (" + p.KindLabel + ", " + "CannotReach".Translate() + ")", null));
|
||||
}
|
||||
else
|
||||
{
|
||||
Action action = delegate
|
||||
{
|
||||
// Create a menu to select which colonist should do the hauling.
|
||||
var haulerOptions = new List<FloatMenuOption>();
|
||||
foreach (var hauler in potentialHaulers)
|
||||
{
|
||||
haulerOptions.Add(new FloatMenuOption(hauler.LabelCap, delegate
|
||||
{
|
||||
var haulJob = JobMaker.MakeJob(JobDefOf_WULA.WULA_HaulToMaintenancePod, p, parent);
|
||||
haulJob.count = 1;
|
||||
hauler.jobs.TryTakeOrderedJob(haulJob, JobTag.Misc);
|
||||
}));
|
||||
}
|
||||
Find.WindowStack.Add(new FloatMenu(haulerOptions));
|
||||
};
|
||||
options.Add(new FloatMenuOption(p.LabelShortCap + " (" + p.KindLabel + ")", action));
|
||||
}
|
||||
}
|
||||
}
|
||||
// If the pawn is a free colonist and can walk, they can go on their own.
|
||||
else
|
||||
{
|
||||
if (!p.CanReach(parent, PathEndMode.InteractionCell, Danger.Deadly))
|
||||
{
|
||||
options.Add(new FloatMenuOption(p.LabelShortCap + " (" + "CannotReach".Translate() + ")", null));
|
||||
}
|
||||
else
|
||||
{
|
||||
float required = RequiredComponents(p);
|
||||
if (refuelableComp.Fuel >= required)
|
||||
{
|
||||
options.Add(new FloatMenuOption(p.LabelShortCap, () =>
|
||||
{
|
||||
Job job = JobMaker.MakeJob(JobDefOf_WULA.WULA_EnterMaintenancePod, parent);
|
||||
p.jobs.TryTakeOrderedJob(job, JobTag.Misc);
|
||||
}));
|
||||
}
|
||||
else
|
||||
{
|
||||
options.Add(new FloatMenuOption(p.LabelShortCap + " (" + "WULA_MaintenancePod_NotEnoughComponents".Translate(required.ToString("F0")) + ")", null));
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (var pawn in map.mapPawns.AllPawnsSpawned)
|
||||
{
|
||||
// 首先检查是否有维护需求
|
||||
var maintenanceNeed = pawn.needs?.TryGetNeed<Need_Maintenance>();
|
||||
if (maintenanceNeed == null)
|
||||
{
|
||||
// 这个Pawn没有维护需求,跳过
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检查是否真的需要维护
|
||||
if (maintenanceNeed.CurLevel > Props.minNeedLevelToMaintain && !DebugSettings.godMode)
|
||||
continue;
|
||||
|
||||
// 创建选项
|
||||
var option = CreatePawnOption(pawn, maintenanceNeed);
|
||||
if (option != null)
|
||||
options.Add(option);
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
}
|
||||
|
||||
private FloatMenuOption CreatePawnOption(Pawn pawn, Need_Maintenance need)
|
||||
{
|
||||
string label = $"{pawn.LabelShortCap} ({need.CurLevel.ToStringPercent()})";
|
||||
float requiredComponents = RequiredComponents;
|
||||
// 检查组件是否足够
|
||||
if (refuelableComp.Fuel < requiredComponents)
|
||||
{
|
||||
return new FloatMenuOption(label + " (" + "WULA_MaintenancePod_NotEnoughComponents".Translate(requiredComponents.ToString("F0")) + ")", null);
|
||||
}
|
||||
// 检查是否可以到达
|
||||
if (!pawn.CanReach(parent, PathEndMode.InteractionCell, Danger.Deadly))
|
||||
{
|
||||
return new FloatMenuOption(label + " (" + "CannotReach".Translate() + ")", null);
|
||||
}
|
||||
return new FloatMenuOption(label, () =>
|
||||
{
|
||||
if (pawn.Downed || !pawn.IsFreeColonist)
|
||||
{
|
||||
// 需要搬运
|
||||
var haulJob = JobMaker.MakeJob(JobDefOf_WULA.WULA_HaulToMaintenancePod, pawn, parent);
|
||||
var hauler = FindBestHauler(pawn);
|
||||
if (hauler != null)
|
||||
{
|
||||
hauler.jobs.TryTakeOrderedJob(haulJob);
|
||||
}
|
||||
else
|
||||
{
|
||||
Messages.Message("WULA_NoHaulerAvailable".Translate(), MessageTypeDefOf.RejectInput);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 自己进入
|
||||
var enterJob = JobMaker.MakeJob(JobDefOf_WULA.WULA_EnterMaintenancePod, parent);
|
||||
pawn.jobs.TryTakeOrderedJob(enterJob);
|
||||
}
|
||||
});
|
||||
}
|
||||
private Pawn FindBestHauler(Pawn target)
|
||||
{
|
||||
return parent.Map.mapPawns.FreeColonistsSpawned
|
||||
.Where(colonist => !colonist.Downed &&
|
||||
colonist.CanReserveAndReach(target, PathEndMode.OnCell, Danger.Deadly) &&
|
||||
colonist.CanReserveAndReach(parent, PathEndMode.InteractionCell, Danger.Deadly))
|
||||
.OrderBy(colonist => colonist.Position.DistanceTo(target.Position))
|
||||
.FirstOrDefault();
|
||||
}
|
||||
}
|
||||
public enum MaintenancePodState
|
||||
{
|
||||
Idle,
|
||||
Running,
|
||||
Running
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// HediffComp_MaintenanceDamage.cs
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
public class HediffCompProperties_MaintenanceDamage : HediffCompProperties
|
||||
{
|
||||
public float damageToMaintenanceFactor = 0.01f; // 每点伤害扣除的维护度比例
|
||||
|
||||
public HediffCompProperties_MaintenanceDamage()
|
||||
{
|
||||
compClass = typeof(HediffComp_MaintenanceDamage);
|
||||
}
|
||||
}
|
||||
|
||||
public class HediffComp_MaintenanceDamage : HediffComp
|
||||
{
|
||||
private HediffCompProperties_MaintenanceDamage Props => (HediffCompProperties_MaintenanceDamage)props;
|
||||
|
||||
public override void Notify_PawnPostApplyDamage(DamageInfo dinfo, float totalDamageDealt)
|
||||
{
|
||||
base.Notify_PawnPostApplyDamage(dinfo, totalDamageDealt);
|
||||
|
||||
// 获取维护需求
|
||||
var maintenanceNeed = Pawn.needs?.TryGetNeed<Need_Maintenance>();
|
||||
if (maintenanceNeed == null)
|
||||
return;
|
||||
|
||||
// 直接应用伤害惩罚
|
||||
maintenanceNeed.ApplyDamagePenalty(totalDamageDealt);
|
||||
}
|
||||
|
||||
public override string CompTipStringExtra => "WULA_DamageAffectsMaintenance".Translate(Props.damageToMaintenanceFactor.ToStringPercent());
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
using Verse;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
public class HediffCompProperties_MaintenanceNeed : HediffCompProperties
|
||||
{
|
||||
public float severityPerDayBeforeThreshold = 0.0f;
|
||||
public float severityPerDayAfterThreshold = 0.0f;
|
||||
public float thresholdDays = 0.0f;
|
||||
|
||||
public HediffCompProperties_MaintenanceNeed()
|
||||
{
|
||||
compClass = typeof(HediffComp_MaintenanceNeed);
|
||||
}
|
||||
}
|
||||
|
||||
public class HediffComp_MaintenanceNeed : HediffComp
|
||||
{
|
||||
private HediffCompProperties_MaintenanceNeed Props => (HediffCompProperties_MaintenanceNeed)props;
|
||||
|
||||
public override void CompPostTick(ref float severityAdjustment)
|
||||
{
|
||||
base.CompPostTick(ref severityAdjustment);
|
||||
|
||||
// We adjust severity once per game day (60000 ticks)
|
||||
if (parent.ageTicks % 60000 == 0)
|
||||
{
|
||||
float ageInDays = (float)parent.ageTicks / 60000f;
|
||||
if (ageInDays < Props.thresholdDays)
|
||||
{
|
||||
severityAdjustment += Props.severityPerDayBeforeThreshold;
|
||||
}
|
||||
else
|
||||
{
|
||||
severityAdjustment += Props.severityPerDayAfterThreshold;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
[DefOf]
|
||||
public static class JobDefOf_WULA
|
||||
{
|
||||
public static JobDef WULA_EnterMaintenancePod;
|
||||
|
||||
public static JobDef WULA_HaulToMaintenancePod;
|
||||
|
||||
static JobDefOf_WULA()
|
||||
{
|
||||
DefOfHelper.EnsureInitializedInCtor(typeof(JobDefOf_WULA));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// JobDriver_EnterMaintenancePod.cs (更新版)
|
||||
using RimWorld;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Verse;
|
||||
using Verse.AI;
|
||||
@@ -11,6 +11,7 @@ namespace WulaFallenEmpire
|
||||
private const TargetIndex PodIndex = TargetIndex.A;
|
||||
|
||||
protected Thing Pod => job.GetTarget(PodIndex).Thing;
|
||||
protected CompMaintenancePod PodComp => Pod?.TryGetComp<CompMaintenancePod>();
|
||||
|
||||
public override bool TryMakePreToilReservations(bool errorOnFailed)
|
||||
{
|
||||
@@ -19,29 +20,24 @@ namespace WulaFallenEmpire
|
||||
|
||||
protected override IEnumerable<Toil> MakeNewToils()
|
||||
{
|
||||
Log.Warning($"[WulaPodDebug] JobDriver_EnterMaintenancePod started for pawn: {pawn.LabelShortCap}");
|
||||
this.FailOnDespawnedNullOrForbidden(PodIndex);
|
||||
this.FailOnBurningImmobile(PodIndex);
|
||||
this.FailOn(() => PodComp == null || PodComp.State != MaintenancePodState.Idle || !PodComp.PowerOn);
|
||||
|
||||
var podComp = Pod.TryGetComp<CompMaintenancePod>();
|
||||
this.FailOn(() => podComp == null || podComp.State != MaintenancePodState.Idle || !podComp.PowerOn);
|
||||
// 移动到维护舱
|
||||
yield return Toils_Goto.GotoThing(PodIndex, PathEndMode.InteractionCell);
|
||||
|
||||
// Go to the pod's interaction cell
|
||||
Toil goToPod = Toils_Goto.GotoThing(PodIndex, PathEndMode.InteractionCell);
|
||||
goToPod.AddPreInitAction(() => Log.Warning($"[WulaPodDebug] EnterJob: Pawn {pawn.LabelShortCap} is going to the pod."));
|
||||
yield return goToPod;
|
||||
|
||||
// Enter the pod
|
||||
Toil enterToil = new Toil
|
||||
// 进入维护舱
|
||||
yield return new Toil
|
||||
{
|
||||
initAction = () =>
|
||||
{
|
||||
Log.Warning($"[WulaPodDebug] EnterJob: Pawn {pawn.LabelShortCap} has arrived and is entering the pod.");
|
||||
podComp.StartCycle(pawn);
|
||||
if (PodComp != null)
|
||||
{
|
||||
PodComp.StartCycle(pawn);
|
||||
}
|
||||
},
|
||||
defaultCompleteMode = ToilCompleteMode.Instant
|
||||
};
|
||||
yield return enterToil;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// JobDriver_HaulToMaintenancePod.cs (修复版)
|
||||
using System.Collections.Generic;
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
@@ -12,52 +13,70 @@ namespace WulaFallenEmpire
|
||||
|
||||
protected Pawn Takee => (Pawn)job.GetTarget(TakeeIndex).Thing;
|
||||
protected Building Pod => (Building)job.GetTarget(PodIndex).Thing;
|
||||
protected CompMaintenancePod PodComp => Pod.TryGetComp<CompMaintenancePod>();
|
||||
protected CompMaintenancePod PodComp => Pod?.TryGetComp<CompMaintenancePod>();
|
||||
|
||||
public override bool TryMakePreToilReservations(bool errorOnFailed)
|
||||
{
|
||||
// 修复:明确指定计数为1
|
||||
return pawn.Reserve(Takee, job, 1, -1, null, errorOnFailed)
|
||||
&& pawn.Reserve(Pod, job, 1, -1, null, errorOnFailed);
|
||||
}
|
||||
|
||||
protected override IEnumerable<Toil> MakeNewToils()
|
||||
{
|
||||
Log.Warning($"[WulaPodDebug] JobDriver_HaulToMaintenancePod started. Hauler: {pawn.LabelShortCap}, Takee: {Takee.LabelShortCap}");
|
||||
// Standard failure conditions
|
||||
this.FailOnDestroyedOrNull(TakeeIndex);
|
||||
this.FailOnDestroyedOrNull(PodIndex);
|
||||
this.FailOnAggroMentalStateAndHostile(TakeeIndex);
|
||||
this.FailOn(() => PodComp == null);
|
||||
this.FailOn(() => !pawn.CanReach(Pod, PathEndMode.InteractionCell, Danger.Deadly));
|
||||
this.FailOn(() => !Takee.Downed);
|
||||
this.FailOn(() => PodComp == null || PodComp.State != MaintenancePodState.Idle);
|
||||
|
||||
// Go to the pawn to be rescued
|
||||
Toil goToTakee = Toils_Goto.GotoThing(TakeeIndex, PathEndMode.ClosestTouch)
|
||||
.FailOnDespawnedNullOrForbidden(TakeeIndex)
|
||||
.FailOnDespawnedNullOrForbidden(PodIndex)
|
||||
.FailOnSomeonePhysicallyInteracting(TakeeIndex);
|
||||
goToTakee.AddPreInitAction(() => Log.Warning($"[WulaPodDebug] HaulJob: {pawn.LabelShortCap} is going to pick up {Takee.LabelShortCap}."));
|
||||
yield return goToTakee;
|
||||
// 前往目标 pawn
|
||||
yield return Toils_Goto.GotoThing(TakeeIndex, PathEndMode.ClosestTouch);
|
||||
|
||||
// Start carrying the pawn
|
||||
Toil startCarrying = Toils_Haul.StartCarryThing(TakeeIndex, false, true, false);
|
||||
startCarrying.AddPreInitAction(() => Log.Warning($"[WulaPodDebug] HaulJob: {pawn.LabelShortCap} is now carrying {Takee.LabelShortCap}."));
|
||||
yield return startCarrying;
|
||||
|
||||
// Go to the maintenance pod
|
||||
Toil goToPod = Toils_Goto.GotoThing(PodIndex, PathEndMode.InteractionCell);
|
||||
goToPod.AddPreInitAction(() => Log.Warning($"[WulaPodDebug] HaulJob: {pawn.LabelShortCap} is hauling {Takee.LabelShortCap} to the pod."));
|
||||
yield return goToPod;
|
||||
|
||||
// Place the pawn inside the pod
|
||||
Toil placeInPod = ToilMaker.MakeToil("PlaceInPod");
|
||||
placeInPod.initAction = delegate
|
||||
// 开始搬运 - 修复计数问题
|
||||
yield return new Toil
|
||||
{
|
||||
Log.Warning($"[WulaPodDebug] HaulJob: {pawn.LabelShortCap} has arrived and is placing {Takee.LabelShortCap} in the pod.");
|
||||
PodComp.StartCycle(Takee);
|
||||
initAction = () =>
|
||||
{
|
||||
// 明确设置搬运数量为1
|
||||
if (pawn.carryTracker.CarriedThing == null)
|
||||
{
|
||||
if (Takee == null || Takee.Destroyed)
|
||||
{
|
||||
Log.Error("试图搬运不存在的Pawn");
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用TryStartCarryThing并明确指定数量
|
||||
if (pawn.carryTracker.TryStartCarry(Takee, 1) <= 0)
|
||||
{
|
||||
Log.Error($"无法搬运Pawn: {Takee.Label}");
|
||||
EndJobWith(JobCondition.Incompletable);
|
||||
}
|
||||
}
|
||||
},
|
||||
defaultCompleteMode = ToilCompleteMode.Instant
|
||||
};
|
||||
|
||||
// 前往维护舱
|
||||
yield return Toils_Goto.GotoThing(PodIndex, PathEndMode.InteractionCell);
|
||||
|
||||
// 放入维护舱
|
||||
yield return new Toil
|
||||
{
|
||||
initAction = () =>
|
||||
{
|
||||
if (PodComp != null && Takee != null)
|
||||
{
|
||||
// 确保Pawn被放下
|
||||
if (pawn.carryTracker.CarriedThing == Takee)
|
||||
{
|
||||
pawn.carryTracker.TryDropCarriedThing(pawn.Position, ThingPlaceMode.Near, out _);
|
||||
}
|
||||
|
||||
PodComp.StartCycle(Takee);
|
||||
}
|
||||
},
|
||||
defaultCompleteMode = ToilCompleteMode.Instant
|
||||
};
|
||||
placeInPod.defaultCompleteMode = ToilCompleteMode.Instant;
|
||||
yield return placeInPod;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
using Verse.AI;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
public class WorkGiver_DoMaintenance : WorkGiver_Scanner
|
||||
{
|
||||
public override ThingRequest PotentialWorkThingRequest => ThingRequest.ForDef(ThingDef.Named("WULA_MaintenancePod"));
|
||||
|
||||
public override bool HasJobOnThing(Pawn pawn, Thing t, bool forced = false)
|
||||
{
|
||||
return pawn.CanReserve(t, 1, -1, null, forced);
|
||||
}
|
||||
|
||||
public override Job JobOnThing(Pawn pawn, Thing t, bool forced = false)
|
||||
{
|
||||
return JobMaker.MakeJob(DefDatabase<JobDef>.GetNamed("WULA_EnterMaintenancePod"), t);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// MaintenanceNeedExtension.cs
|
||||
using Verse;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
public class MaintenanceNeedExtension : DefModExtension
|
||||
{
|
||||
// 基础退化设置
|
||||
public float severityPerDayBeforeThreshold = 0.05f;
|
||||
public float severityPerDayAfterThreshold = 0.1f;
|
||||
public float thresholdDays = 5f;
|
||||
|
||||
// 状态阈值
|
||||
public float minorBreakdownThreshold = 0.3f;
|
||||
public float majorBreakdownThreshold = 0.1f;
|
||||
public float criticalFailureThreshold = 0.01f;
|
||||
|
||||
// 伤害相关设置 - 简化
|
||||
public float damageToMaintenanceFactor = 0.01f; // 每点伤害扣除的维护度
|
||||
|
||||
// 维护效果相关的 HediffDefs
|
||||
public HediffDef minorBreakdownHediff = null;
|
||||
public HediffDef majorBreakdownHediff = null;
|
||||
public HediffDef criticalFailureHediff = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
// Need_Maintenance.cs
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
using System.Linq;
|
||||
using System;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
public class Need_Maintenance : Need
|
||||
{
|
||||
private MaintenanceNeedExtension Extension => def.GetModExtension<MaintenanceNeedExtension>();
|
||||
|
||||
// 上次维护的天数
|
||||
private float daysSinceLastMaintenance = 0f;
|
||||
|
||||
// 当前维护状态
|
||||
public MaintenanceStatus Status
|
||||
{
|
||||
get
|
||||
{
|
||||
if (CurLevel <= Extension?.criticalFailureThreshold) return MaintenanceStatus.CriticalFailure;
|
||||
if (CurLevel <= Extension?.majorBreakdownThreshold) return MaintenanceStatus.MajorBreakdown;
|
||||
if (CurLevel <= Extension?.minorBreakdownThreshold) return MaintenanceStatus.MinorBreakdown;
|
||||
return MaintenanceStatus.Operational;
|
||||
}
|
||||
}
|
||||
|
||||
public float DaysSinceLastMaintenance => daysSinceLastMaintenance;
|
||||
|
||||
public Need_Maintenance(Pawn pawn) : base(pawn)
|
||||
{
|
||||
}
|
||||
|
||||
public override void SetInitialLevel()
|
||||
{
|
||||
CurLevel = 1.0f;
|
||||
daysSinceLastMaintenance = 0f;
|
||||
}
|
||||
|
||||
public override void NeedInterval()
|
||||
{
|
||||
if (pawn.Dead || !pawn.Spawned)
|
||||
return;
|
||||
|
||||
// 每150 ticks 更新一次(Need 的标准间隔)
|
||||
if (IsFrozen)
|
||||
return;
|
||||
|
||||
// 增加天数计数
|
||||
daysSinceLastMaintenance += 150f / 60000f; // 150 ticks 占一天的比例
|
||||
|
||||
// 计算退化速率
|
||||
float degradationRate = CalculateDegradationRate();
|
||||
|
||||
// 应用退化
|
||||
CurLevel -= degradationRate * (150f / 60000f); // 转换为每天的比例
|
||||
|
||||
// 确保数值在有效范围内
|
||||
CurLevel = ClampNeedLevel(CurLevel);
|
||||
|
||||
// 检查状态变化
|
||||
CheckStatusChanges();
|
||||
}
|
||||
|
||||
private float CalculateDegradationRate()
|
||||
{
|
||||
if (Extension == null)
|
||||
return 0f;
|
||||
|
||||
if (daysSinceLastMaintenance < Extension.thresholdDays)
|
||||
{
|
||||
return Extension.severityPerDayBeforeThreshold;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Extension.severityPerDayAfterThreshold;
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckStatusChanges()
|
||||
{
|
||||
if (Extension == null)
|
||||
return;
|
||||
|
||||
// 检查是否需要应用故障效果
|
||||
var currentStatus = Status;
|
||||
|
||||
// 移除旧的维护相关 Hediff
|
||||
RemoveMaintenanceHediffs();
|
||||
|
||||
// 根据状态添加相应的 Hediff
|
||||
switch (currentStatus)
|
||||
{
|
||||
case MaintenanceStatus.MinorBreakdown:
|
||||
if (Extension.minorBreakdownHediff != null)
|
||||
pawn.health.AddHediff(Extension.minorBreakdownHediff);
|
||||
break;
|
||||
|
||||
case MaintenanceStatus.MajorBreakdown:
|
||||
if (Extension.majorBreakdownHediff != null)
|
||||
pawn.health.AddHediff(Extension.majorBreakdownHediff);
|
||||
break;
|
||||
|
||||
case MaintenanceStatus.CriticalFailure:
|
||||
if (Extension.criticalFailureHediff != null)
|
||||
pawn.health.AddHediff(Extension.criticalFailureHediff);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveMaintenanceHediffs()
|
||||
{
|
||||
if (Extension == null)
|
||||
return;
|
||||
|
||||
// 移除所有维护相关的 Hediff
|
||||
var hediffsToRemove = pawn.health.hediffSet.hediffs.FindAll(h =>
|
||||
h.def == Extension.minorBreakdownHediff ||
|
||||
h.def == Extension.majorBreakdownHediff ||
|
||||
h.def == Extension.criticalFailureHediff);
|
||||
|
||||
foreach (var hediff in hediffsToRemove)
|
||||
{
|
||||
pawn.health.RemoveHediff(hediff);
|
||||
}
|
||||
}
|
||||
|
||||
// 执行维护操作
|
||||
public void PerformMaintenance(float maintenanceAmount = 1.0f)
|
||||
{
|
||||
CurLevel += maintenanceAmount;
|
||||
CurLevel = ClampNeedLevel(CurLevel);
|
||||
daysSinceLastMaintenance = 0f;
|
||||
|
||||
// 移除所有维护相关的负面效果
|
||||
RemoveMaintenanceHediffs();
|
||||
|
||||
// 触发维护完成的效果
|
||||
OnMaintenancePerformed(maintenanceAmount);
|
||||
}
|
||||
|
||||
// 应用伤害惩罚 - 简单的线性减少
|
||||
public void ApplyDamagePenalty(float damageAmount)
|
||||
{
|
||||
if (Extension == null) return;
|
||||
|
||||
// 直接线性减少维护度
|
||||
float reduction = damageAmount * Extension.damageToMaintenanceFactor;
|
||||
CurLevel = Math.Max(0f, CurLevel - reduction);
|
||||
|
||||
// 立即检查状态变化
|
||||
CheckStatusChanges();
|
||||
|
||||
if (pawn.IsColonistPlayerControlled && reduction > 0.01f)
|
||||
{
|
||||
Messages.Message("WULA_MaintenanceReducedDueToDamage".Translate(pawn.LabelShort, reduction.ToStringPercent()),
|
||||
pawn, MessageTypeDefOf.NegativeEvent);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMaintenancePerformed(float amount)
|
||||
{
|
||||
// 这里可以添加维护完成时的特殊效果
|
||||
if (pawn.IsColonistPlayerControlled)
|
||||
{
|
||||
Messages.Message("WULA_MaintenanceCompleted".Translate(pawn.LabelShort), pawn, MessageTypeDefOf.PositiveEvent);
|
||||
}
|
||||
}
|
||||
|
||||
private float ClampNeedLevel(float level)
|
||||
{
|
||||
return level < 0f ? 0f : (level > 1f ? 1f : level);
|
||||
}
|
||||
|
||||
public override string GetTipString()
|
||||
{
|
||||
string baseTip = base.GetTipString();
|
||||
|
||||
string statusText = "WULA_MaintenanceStatus".Translate(Status.GetLabel(), daysSinceLastMaintenance.ToString("F1"));
|
||||
string degradationText = "WULA_DegradationRate".Translate(CalculateDegradationRate().ToString("F3"));
|
||||
|
||||
return $"{baseTip}\n\n{statusText}\n{degradationText}";
|
||||
}
|
||||
|
||||
public override void ExposeData()
|
||||
{
|
||||
base.ExposeData();
|
||||
Scribe_Values.Look(ref daysSinceLastMaintenance, "daysSinceLastMaintenance", 0f);
|
||||
}
|
||||
}
|
||||
|
||||
// 维护状态枚举
|
||||
public enum MaintenanceStatus
|
||||
{
|
||||
Operational,
|
||||
MinorBreakdown,
|
||||
MajorBreakdown,
|
||||
CriticalFailure
|
||||
}
|
||||
|
||||
public static class MaintenanceStatusExtensions
|
||||
{
|
||||
public static string GetLabel(this MaintenanceStatus status)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case MaintenanceStatus.Operational:
|
||||
return "WULA_Operational".Translate();
|
||||
case MaintenanceStatus.MinorBreakdown:
|
||||
return "WULA_MinorBreakdown".Translate();
|
||||
case MaintenanceStatus.MajorBreakdown:
|
||||
return "WULA_MajorBreakdown".Translate();
|
||||
case MaintenanceStatus.CriticalFailure:
|
||||
return "WULA_CriticalFailure".Translate();
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetDescription(this MaintenanceStatus status)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case MaintenanceStatus.Operational:
|
||||
return "WULA_OperationalDesc".Translate();
|
||||
case MaintenanceStatus.MinorBreakdown:
|
||||
return "WULA_MinorBreakdownDesc".Translate();
|
||||
case MaintenanceStatus.MajorBreakdown:
|
||||
return "WULA_MajorBreakdownDesc".Translate();
|
||||
case MaintenanceStatus.CriticalFailure:
|
||||
return "WULA_CriticalFailureDesc".Translate();
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// WorkGiver_DoMaintenance.cs (修复版)
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
using Verse.AI;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
public class WorkGiver_DoMaintenance : WorkGiver_Scanner
|
||||
{
|
||||
public override ThingRequest PotentialWorkThingRequest =>
|
||||
ThingRequest.ForDef(ThingDef.Named("WULA_MaintenancePod"));
|
||||
|
||||
public override PathEndMode PathEndMode => PathEndMode.Touch;
|
||||
|
||||
public override bool HasJobOnThing(Pawn pawn, Thing t, bool forced = false)
|
||||
{
|
||||
// 检查维护舱是否可用
|
||||
if (!pawn.CanReserve(t, 1, -1, null, forced))
|
||||
return false;
|
||||
|
||||
var podComp = t.TryGetComp<CompMaintenancePod>();
|
||||
if (podComp == null || podComp.State != MaintenancePodState.Idle || !podComp.PowerOn)
|
||||
return false;
|
||||
|
||||
// 检查当前pawn是否有维护需求且需要维护
|
||||
return PawnNeedsMaintenance(pawn);
|
||||
}
|
||||
|
||||
public override Job JobOnThing(Pawn pawn, Thing t, bool forced = false)
|
||||
{
|
||||
return JobMaker.MakeJob(JobDefOf_WULA.WULA_EnterMaintenancePod, t);
|
||||
}
|
||||
|
||||
// 检查单个Pawn是否需要维护
|
||||
private bool PawnNeedsMaintenance(Pawn pawn)
|
||||
{
|
||||
// 检查是否有维护需求组件
|
||||
var maintenanceNeed = pawn.needs?.TryGetNeed<Need_Maintenance>();
|
||||
if (maintenanceNeed == null)
|
||||
{
|
||||
// 这个Pawn没有维护需求,不应该使用维护舱
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查维护水平是否低于阈值
|
||||
return maintenanceNeed.CurLevel <= 0.3f; // 需要维护的阈值
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user