diff --git a/1.6/1.6/Assemblies/WulaFallenEmpire.dll b/1.6/1.6/Assemblies/WulaFallenEmpire.dll index 4f393ed2..9177c77e 100644 Binary files a/1.6/1.6/Assemblies/WulaFallenEmpire.dll and b/1.6/1.6/Assemblies/WulaFallenEmpire.dll differ diff --git a/1.6/1.6/Defs/HediffDefs/Hediffs_WULA.xml b/1.6/1.6/Defs/HediffDefs/Hediffs_WULA.xml index 440e47f0..a3b472ab 100644 --- a/1.6/1.6/Defs/HediffDefs/Hediffs_WULA.xml +++ b/1.6/1.6/Defs/HediffDefs/Hediffs_WULA.xml @@ -24,6 +24,12 @@ 0.05 + +
  • + Consciousness + 0.25 +
  • +
  • Flu
  • @@ -38,6 +44,7 @@
  • WULA_Energy
  • +
  • WULA_MaintenanceNeed
  • Food
  • @@ -45,6 +52,11 @@
    + +
  • + 0.0025 +
  • +
    WULA_ChargingHediff @@ -68,73 +80,67 @@ + - WULA_Maintenance_Neglect + WULA_Maintenance_MinorBreakdown - 乌拉帝国的合成人因为设计的过于繁琐,导致需要频繁维护。当她们处于良好维护状态时,各方面的能力都会有所上升,反之如果常年得不到维护或是短时间受到大量伤害,则其将变得难以自主运行甚至直接停机! + 这台乌拉帝国合成人已经在环境恶劣的边缘世界活跃了一段时间,尽管总体状态良好但是已经有了一些小瑕疵。 Hediff_High (0.8, 0.35, 0.35) false - 0 - 0 - 2.0 - - -
  • - 60 - - 0.0166 - - 0.03333 -
  • -
  • - 0.005 -
  • -
    + 0.1 + 0.5 + 1.0
  • - + 0
  • Consciousness - 0.1 -
  • - - -
  • - - 0.5 - -
  • - Consciousness - 0 -
  • - - -
  • - - 0.75 - -
  • - Consciousness - -0.20 + -0.25
  • +
    +
    + + WULA_Maintenance_MajorBreakdown + + 这台乌拉帝国合成人有一段时间没有进行维护了,运行起来相当吃力。 + Hediff_High + (0.8, 0.35, 0.35) + false + 0.1 + 0.5 + 1.0 +
  • - 1.0 + 0
  • Consciousness - 0.5 + -0.25
  • +
    +
    + + WULA_Maintenance_CriticalFailuren + + 这台乌拉帝国合成人几乎无法运作了,需要立刻进行维护,否则就只是一堆废铁。 + Hediff_High + (0.8, 0.35, 0.35) + false + 0.1 + 0.5 + 1.0 +
  • - 2.0 + 0
  • Consciousness @@ -321,9 +327,9 @@ 0.5 1.5 0.1 - 0.01 - 1200 + 0.02 + 600
  • - +
    \ No newline at end of file diff --git a/1.6/1.6/Defs/JobDefs/WULA_JobDefs.xml b/1.6/1.6/Defs/JobDefs/WULA_JobDefs.xml index 57d31346..4c13fce4 100644 --- a/1.6/1.6/Defs/JobDefs/WULA_JobDefs.xml +++ b/1.6/1.6/Defs/JobDefs/WULA_JobDefs.xml @@ -29,5 +29,4 @@ true false - \ No newline at end of file diff --git a/1.6/1.6/Defs/NeedDefs/WULA_Needs.xml b/1.6/1.6/Defs/NeedDefs/WULA_Needs.xml index 01a3f2ae..8f0e420e 100644 --- a/1.6/1.6/Defs/NeedDefs/WULA_Needs.xml +++ b/1.6/1.6/Defs/NeedDefs/WULA_Needs.xml @@ -23,4 +23,28 @@ + + + WULA_MaintenanceNeed + + WulaFallenEmpire.Need_Maintenance + 乌拉帝国的合成人因为设计的过于繁琐,导致需要频繁维护。当她们处于良好维护状态时,各方面的能力都会有所上升,反之如果常年得不到维护或是短时间受到大量伤害,则其将变得难以自主运行甚至直接停机! + true + true + 799 + true + false + false + +
  • + 0.05 + 0.1 + 5 + PatientBedRest + WULA_Maintenance_MinorBreakdown + WULA_Maintenance_MajorBreakdown + WULA_Maintenance_CriticalFailuren +
  • +
    +
    diff --git a/1.6/1.6/Defs/ThingDefs_Buildings/Buildings_Drop.xml b/1.6/1.6/Defs/ThingDefs_Buildings/Buildings_Drop.xml index 2d60658b..07595880 100644 --- a/1.6/1.6/Defs/ThingDefs_Buildings/Buildings_Drop.xml +++ b/1.6/1.6/Defs/ThingDefs_Buildings/Buildings_Drop.xml @@ -147,9 +147,6 @@ BuildingDestroyed_Metal_Small 3.5 - -
  • BuildingsMisc
  • -
  • 6 @@ -230,4 +227,84 @@
  • + + + + WULA_Cube_Productor_BIO + + 一台仿制乌拉帝国科技而建造的塑性构造体,不仅要消耗大量木头用以提供生物能,还只能生产基础的衣物和能源核心用以维持生存——不过它很轻,可以随探险队一起移动。 + WulaFallenEmpire.Building_GlobalWorkTable + MapMeshAndRealTime + + Wula/Building/WULA_Cube_Productor_BIO + Graphic_Multi + (1,1) + + false + + + (0.75, 0.75, 0.5) + + + ConstructMetal + + 50 + + Building + false + 0.5 + True + + 5 + 2000 + 180 + 1.0 + 0.5 + + (1,1) + WULA_Buildings + 2120 + PassThroughOnly + 50 + True + (0,0,-1) + Item + +
  • WULA_Base_Technology
  • +
    + + +
  • Make_WULA_Charge_Cube
  • +
  • Recharge_WULA_Charge_Cube
  • +
  • Wula_Make_Zro
  • +
    + +
  • WulaFallenEmpire.ITab_GlobalBills
  • +
    + +
  • + 300.0 + 150.0 + + +
  • WoodLog
  • + + + true + true + +
  • + CompHeatPusherPowered + 4 +
  • +
    + +
  • PlaceWorker_PreventInteractionSpotOverlap
  • +
    + + + BillsTab + 0.10 + +
    \ No newline at end of file diff --git a/1.6/1.6/Defs/ThingDefs_Buildings/Buildings_WULA.xml b/1.6/1.6/Defs/ThingDefs_Buildings/Buildings_WULA.xml index eb897d06..6f367791 100644 --- a/1.6/1.6/Defs/ThingDefs_Buildings/Buildings_WULA.xml +++ b/1.6/1.6/Defs/ThingDefs_Buildings/Buildings_WULA.xml @@ -807,18 +807,20 @@ true
  • - 30000 - 150000 - 500 - 25 - WULA_Maintenance_Neglect - 2 - 1 - - 0.01 BiosculpterPod_Enter BiosculpterPod_Exit BiosculpterPod_Operating + 30000 + 150000 + 500 + 25 + 2 + 1 + 0.3 + 1.0 + true + true + 5
  • diff --git a/1.6/1.6/Defs/ThingDefs_Races/Races_Wulaspecies.xml b/1.6/1.6/Defs/ThingDefs_Races/Races_Wulaspecies.xml index ed0e4852..edc78c6f 100644 --- a/1.6/1.6/Defs/ThingDefs_Races/Races_Wulaspecies.xml +++ b/1.6/1.6/Defs/ThingDefs_Races/Races_Wulaspecies.xml @@ -664,7 +664,7 @@ Filth_MachineBits - 1 + 1.2 Steel Steel diff --git a/1.6/1.6/Defs/WorkGivers/Wula_WorkGivers.xml b/1.6/1.6/Defs/WorkGivers/Wula_WorkGivers.xml index ccbd74d2..311a64b9 100644 --- a/1.6/1.6/Defs/WorkGivers/Wula_WorkGivers.xml +++ b/1.6/1.6/Defs/WorkGivers/Wula_WorkGivers.xml @@ -110,4 +110,16 @@ + + + WULA_DoMaintenanceWork + PatientBedRest + WulaFallenEmpire.WorkGiver_DoMaintenance + 100 + 接受维护于 + 接受维护于 + false + true + true + \ No newline at end of file diff --git a/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/Misc_Gameplay.xml b/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/Misc_Gameplay.xml index d3531b35..4061e0cb 100644 --- a/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/Misc_Gameplay.xml +++ b/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/Misc_Gameplay.xml @@ -92,4 +92,68 @@ 该空投地点顶部有屋顶,无法进行空投 被厚岩顶阻挡 被屋顶阻挡 + + + 维护舱状态 + 空闲 + 运行中 + + + 进入维护舱 + 选择需要维护的合成人 + 取消当前维护周期 + 没有合成人需要维护 + 需要 {0} 个零部件 + + + 维护水平 + {0} 开始维护周期 + {0} 维护完成 + 修复了 {0} 的 {1} 处损伤 + 修复了 {0} 的缺失部位 + 维护已取消 + 没有可用的搬运者 + 无维护需求 + + + 维护需求 + 状态: {0} (上次维护: {1}天前) + 退化速率: {0}/天 + {0} 的维护已完成 + + + 运行正常 + 轻微故障 + 严重故障 + 完全故障 + + 合成人运行在最佳状态 + 合成人出现轻微故障,工作效率降低 + 合成人出现严重故障,需要立即维护 + 合成人完全故障,无法工作 + + + 合成人维护 + 执行维护 + + + 伤害会影响维护度({0}/点伤害) + 损伤惩罚 + 近期损伤事件 + {0} 维护完成(修复了 {1} 损伤) + 修复了 {0} 的结构损伤 + 距上次维护 + + + 全球生产 + 生产订单 + 添加生产订单 + Resume + Pause + Delete + 等待资源 + Completed + Unknown + 全球存储中的资源不足 + 无可用配方 \ No newline at end of file diff --git a/Source/WulaFallenEmpire/GlobalWorkTable/Building_GlobalWorkTable.cs b/Source/WulaFallenEmpire/GlobalWorkTable/Building_GlobalWorkTable.cs new file mode 100644 index 00000000..1d13f56e --- /dev/null +++ b/Source/WulaFallenEmpire/GlobalWorkTable/Building_GlobalWorkTable.cs @@ -0,0 +1,61 @@ +// Building_GlobalWorkTable.cs (修复版) +using RimWorld; +using System.Collections.Generic; +using Verse; + +namespace WulaFallenEmpire +{ + public class Building_GlobalWorkTable : Building_WorkTable + { + public GlobalProductionOrderStack globalOrderStack; + + private CompPowerTrader powerComp; + private int lastProcessTick = -1; + private const int ProcessInterval = 60; // 每60tick处理一次 + + public Building_GlobalWorkTable() + { + globalOrderStack = new GlobalProductionOrderStack(this); + } + + public override void ExposeData() + { + base.ExposeData(); + Scribe_Deep.Look(ref globalOrderStack, "globalOrderStack", this); + } + + public override void SpawnSetup(Map map, bool respawningAfterLoad) + { + base.SpawnSetup(map, respawningAfterLoad); + powerComp = GetComp(); + } + + public override void Tick() + { + base.Tick(); + + // 每60tick处理一次生产订单 + if (Find.TickManager.TicksGame % ProcessInterval == 0 && + Find.TickManager.TicksGame != lastProcessTick) + { + lastProcessTick = Find.TickManager.TicksGame; + + if (powerComp == null || powerComp.PowerOn) + { + Log.Message($"[DEBUG] Processing orders at tick {Find.TickManager.TicksGame}"); + globalOrderStack.ProcessOrders(); + } + else + { + Log.Message("[DEBUG] No power, skipping order processing"); + } + } + } + + public bool CurrentlyUsableForGlobalBills() + { + return (powerComp == null || powerComp.PowerOn) && + (GetComp() == null || !GetComp().BrokenDown); + } + } +} diff --git a/Source/WulaFallenEmpire/GlobalWorkTable/GlobalProductionOrder.cs b/Source/WulaFallenEmpire/GlobalWorkTable/GlobalProductionOrder.cs new file mode 100644 index 00000000..f66fe8f1 --- /dev/null +++ b/Source/WulaFallenEmpire/GlobalWorkTable/GlobalProductionOrder.cs @@ -0,0 +1,120 @@ +// GlobalProductionOrder.cs (修复版) +using RimWorld; +using System.Collections.Generic; +using Verse; + +namespace WulaFallenEmpire +{ + public class GlobalProductionOrder : IExposable + { + public RecipeDef recipe; + public int targetCount = 1; + public int currentCount = 0; + public bool paused = true; // 初始状态为暂停 + public float progress = 0f; + + // 生产状态 + public ProductionState state = ProductionState.Waiting; + + public enum ProductionState + { + Waiting, // 等待资源 + Producing, // 生产中 + Completed // 完成 + } + + public void ExposeData() + { + Scribe_Defs.Look(ref recipe, "recipe"); + Scribe_Values.Look(ref targetCount, "targetCount", 1); + Scribe_Values.Look(ref currentCount, "currentCount", 0); + Scribe_Values.Look(ref paused, "paused", true); + Scribe_Values.Look(ref progress, "progress", 0f); + Scribe_Values.Look(ref state, "state", ProductionState.Waiting); + } + + public string Label => recipe.LabelCap; + public string Description => $"{currentCount}/{targetCount} {recipe.products[0].thingDef.label}"; + + // 检查是否有足够资源 - 修复逻辑,只检查costList + public bool HasEnoughResources() + { + var globalStorage = Find.World.GetComponent(); + if (globalStorage == null) + { + Log.Warning("GlobalStorageWorldComponent not found"); + return false; + } + + // 只检查costList,不检查ingredients + if (recipe.costList != null && recipe.costList.Count > 0) + { + foreach (var cost in recipe.costList) + { + int required = cost.count; + int available = globalStorage.GetInputStorageCount(cost.thingDef); + + Log.Message($"[DEBUG] Checking {cost.thingDef.defName}: required={required}, available={available}"); + + if (available < required) + { + Log.Message($"[DEBUG] Insufficient {cost.thingDef.defName}"); + return false; + } + } + Log.Message("[DEBUG] All resources available"); + return true; + } + else + { + Log.Warning($"Recipe {recipe.defName} has no costList"); + return false; + } + } + + // 消耗资源 - 修复逻辑,只消耗costList + public bool ConsumeResources() + { + var globalStorage = Find.World.GetComponent(); + if (globalStorage == null) return false; + + // 只消耗costList中的资源 + if (recipe.costList != null) + { + foreach (var cost in recipe.costList) + { + if (!globalStorage.RemoveFromInputStorage(cost.thingDef, cost.count)) + { + Log.Warning($"Failed to consume {cost.count} {cost.thingDef.defName}"); + return false; + } + Log.Message($"[DEBUG] Consumed {cost.count} {cost.thingDef.defName}"); + } + return true; + } + return false; + } + + // 生产一个产品 + public void Produce() + { + var globalStorage = Find.World.GetComponent(); + if (globalStorage == null) return; + + foreach (var product in recipe.products) + { + globalStorage.AddToOutputStorage(product.thingDef, product.count); + Log.Message($"[DEBUG] Produced {product.count} {product.thingDef.defName}"); + } + + currentCount++; + progress = 0f; + + if (currentCount >= targetCount) + { + state = ProductionState.Completed; + Log.Message("[DEBUG] Order completed"); + } + } + } +} diff --git a/Source/WulaFallenEmpire/GlobalWorkTable/GlobalProductionOrderStack.cs b/Source/WulaFallenEmpire/GlobalWorkTable/GlobalProductionOrderStack.cs new file mode 100644 index 00000000..c3b8ac49 --- /dev/null +++ b/Source/WulaFallenEmpire/GlobalWorkTable/GlobalProductionOrderStack.cs @@ -0,0 +1,98 @@ +// GlobalProductionOrderStack.cs (修复版) +using RimWorld; +using System.Collections.Generic; +using Verse; + +namespace WulaFallenEmpire +{ + public class GlobalProductionOrderStack : IExposable + { + public Building_GlobalWorkTable table; + public List orders = new List(); + + public GlobalProductionOrderStack(Building_GlobalWorkTable table) + { + this.table = table; + } + + public void ExposeData() + { + Scribe_References.Look(ref table, "table"); + Scribe_Collections.Look(ref orders, "orders", LookMode.Deep); + } + + public void AddOrder(GlobalProductionOrder order) + { + orders.Add(order); + + // 添加到全局存储中统一管理 + var globalStorage = Find.World.GetComponent(); + if (globalStorage != null) + { + globalStorage.AddProductionOrder(order); + } + } + + public void Delete(GlobalProductionOrder order) + { + orders.Remove(order); + + var globalStorage = Find.World.GetComponent(); + if (globalStorage != null) + { + globalStorage.RemoveProductionOrder(order); + } + } + + public void ProcessOrders() + { + foreach (var order in orders) + { + if (order.paused || order.state == GlobalProductionOrder.ProductionState.Completed) + continue; + + // 检查资源并更新状态 + if (order.state == GlobalProductionOrder.ProductionState.Waiting) + { + if (order.HasEnoughResources()) + { + order.state = GlobalProductionOrder.ProductionState.Producing; + Log.Message($"[DEBUG] Order {order.recipe.defName} started producing"); + } + else + { + continue; + } + } + + // 生产中 + if (order.state == GlobalProductionOrder.ProductionState.Producing) + { + // 更清晰的进度计算 + float progressPerTick = 1f / (order.recipe.workAmount * 5f); // 调整系数以控制生产速度 + order.progress += progressPerTick; + + if (order.progress >= 1f) + { + // 消耗资源并生产 - 在结束时扣除资源 + if (order.ConsumeResources()) + { + order.Produce(); + } + else + { + // 资源被其他订单消耗,回到等待状态 + order.state = GlobalProductionOrder.ProductionState.Waiting; + order.progress = 0f; + Log.Message("[DEBUG] Resources consumed by another order, returning to waiting state"); + } + } + else + { + Log.Message($"[DEBUG] Order {order.recipe.defName} progress: {order.progress:P0}"); + } + } + } + } + } +} diff --git a/Source/WulaFallenEmpire/GlobalWorkTable/GlobalStorageWorldComponent.cs b/Source/WulaFallenEmpire/GlobalWorkTable/GlobalStorageWorldComponent.cs new file mode 100644 index 00000000..64668bce --- /dev/null +++ b/Source/WulaFallenEmpire/GlobalWorkTable/GlobalStorageWorldComponent.cs @@ -0,0 +1,148 @@ +using LudeonTK; +using RimWorld; +using RimWorld.Planet; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityEngine; +using Verse; + +namespace WulaFallenEmpire +{ + public class GlobalStorageWorldComponent : WorldComponent + { + public Dictionary inputStorage = new Dictionary(); + public Dictionary outputStorage = new Dictionary(); + + // 存储生产订单 + public List productionOrders = new List(); + + public GlobalStorageWorldComponent(World world) : base(world) { } + + public override void ExposeData() + { + base.ExposeData(); + + // 序列化输入存储 + Scribe_Collections.Look(ref inputStorage, "inputStorage", LookMode.Def, LookMode.Value); + if (inputStorage == null) inputStorage = new Dictionary(); + + // 序列化输出存储 + Scribe_Collections.Look(ref outputStorage, "outputStorage", LookMode.Def, LookMode.Value); + if (outputStorage == null) outputStorage = new Dictionary(); + + // 序列化生产订单 + Scribe_Collections.Look(ref productionOrders, "productionOrders", LookMode.Deep); + if (productionOrders == null) productionOrders = new List(); + } + + // 输入存储方法 + public void AddToInputStorage(ThingDef thingDef, int count) + { + if (inputStorage.ContainsKey(thingDef)) + inputStorage[thingDef] += count; + else + inputStorage[thingDef] = count; + } + + public bool RemoveFromInputStorage(ThingDef thingDef, int count) + { + if (inputStorage.ContainsKey(thingDef) && inputStorage[thingDef] >= count) + { + inputStorage[thingDef] -= count; + if (inputStorage[thingDef] <= 0) + inputStorage.Remove(thingDef); + return true; + } + return false; + } + + public int GetInputStorageCount(ThingDef thingDef) + { + return inputStorage.ContainsKey(thingDef) ? inputStorage[thingDef] : 0; + } + + // 输出存储方法 + public void AddToOutputStorage(ThingDef thingDef, int count) + { + if (outputStorage.ContainsKey(thingDef)) + outputStorage[thingDef] += count; + else + outputStorage[thingDef] = count; + } + + public bool RemoveFromOutputStorage(ThingDef thingDef, int count) + { + if (outputStorage.ContainsKey(thingDef) && outputStorage[thingDef] >= count) + { + outputStorage[thingDef] -= count; + if (outputStorage[thingDef] <= 0) + outputStorage.Remove(thingDef); + return true; + } + return false; + } + + public int GetOutputStorageCount(ThingDef thingDef) + { + return outputStorage.ContainsKey(thingDef) ? outputStorage[thingDef] : 0; + } + + // 生产订单管理 + public void AddProductionOrder(GlobalProductionOrder order) + { + productionOrders.Add(order); + } + + public void RemoveProductionOrder(GlobalProductionOrder order) + { + productionOrders.Remove(order); + } + + // 添加调试方法 + [DebugAction("WULA", "Add Test Resources", actionType = DebugActionType.Action)] + public static void DebugAddTestResources() + { + var globalStorage = Find.World.GetComponent(); + if (globalStorage != null) + { + globalStorage.AddToInputStorage(ThingDefOf.Steel, 200); + globalStorage.AddToInputStorage(ThingDefOf.ComponentIndustrial, 100); + Log.Message("Added test resources to global storage"); + } + } + [DebugAction("WULA", "Spawn All Products", actionType = DebugActionType.Action)] + public static void DebugSpawnAllProducts() + { + var globalStorage = Find.World.GetComponent(); + if (globalStorage != null) + { + // 查找任意工作台来生成物品 + var workTable = Find.CurrentMap?.listerBuildings?.allBuildingsColonist? + .FirstOrDefault(b => b is Building_GlobalWorkTable) as Building_GlobalWorkTable; + + if (workTable != null && workTable.Spawned) + { + foreach (var kvp in globalStorage.outputStorage.ToList()) // 使用ToList避免修改时枚举 + { + ThingDef thingDef = kvp.Key; + int count = kvp.Value; + + while (count > 0) + { + int stackSize = Mathf.Min(count, thingDef.stackLimit); + Thing thing = ThingMaker.MakeThing(thingDef); + thing.stackCount = stackSize; + + GenPlace.TryPlaceThing(thing, workTable.Position, workTable.Map, ThingPlaceMode.Near); + + globalStorage.RemoveFromOutputStorage(thingDef, stackSize); + count -= stackSize; + } + } + Log.Message("Spawned all output products"); + } + } + } + } +} diff --git a/Source/WulaFallenEmpire/GlobalWorkTable/ITab_GlobalBills.cs b/Source/WulaFallenEmpire/GlobalWorkTable/ITab_GlobalBills.cs new file mode 100644 index 00000000..fc269678 --- /dev/null +++ b/Source/WulaFallenEmpire/GlobalWorkTable/ITab_GlobalBills.cs @@ -0,0 +1,285 @@ +// ITab_GlobalBills.cs (修复版) +using System.Collections.Generic; +using RimWorld; +using UnityEngine; +using Verse; +using Verse.Sound; + +namespace WulaFallenEmpire +{ + public class ITab_GlobalBills : ITab + { + private float viewHeight = 1000f; + private Vector2 scrollPosition; + private GlobalProductionOrder mouseoverOrder; + + private static readonly Vector2 WinSize = new Vector2(420f, 480f); + + protected Building_GlobalWorkTable SelTable => (Building_GlobalWorkTable)base.SelThing; + + public ITab_GlobalBills() + { + size = WinSize; + labelKey = "WULA_GlobalBillsTab"; + tutorTag = "GlobalBills"; + } + + protected override void FillTab() + { + Rect mainRect = new Rect(0f, 0f, WinSize.x, WinSize.y).ContractedBy(10f); + + // 标题 + Text.Font = GameFont.Medium; + Widgets.Label(new Rect(mainRect.x, mainRect.y, mainRect.width, 30f), "WULA_GlobalProduction".Translate()); + Text.Font = GameFont.Small; + + // 开发模式按钮区域 + if (Prefs.DevMode) + { + Rect devButtonRect = new Rect(mainRect.x, mainRect.y + 35f, mainRect.width, 25f); + DoDevButtons(devButtonRect); + } + + // 订单列表区域 - 调整位置 + float ordersRectY = Prefs.DevMode ? mainRect.y + 65f : mainRect.y + 35f; + Rect ordersRect = new Rect(mainRect.x, ordersRectY, mainRect.width, mainRect.height - (Prefs.DevMode ? 110f : 80f)); + mouseoverOrder = DoOrdersListing(ordersRect); + + // 添加订单按钮 + Rect addButtonRect = new Rect(mainRect.x, mainRect.yMax - 35f, mainRect.width, 30f); + if (Widgets.ButtonText(addButtonRect, "WULA_AddProductionOrder".Translate())) + { + Find.WindowStack.Add(new FloatMenu(GenerateRecipeOptions())); + } + } + + private void DoDevButtons(Rect rect) + { + Rect button1Rect = new Rect(rect.x, rect.y, rect.width / 2 - 5f, rect.height); + Rect button2Rect = new Rect(rect.x + rect.width / 2 + 5f, rect.y, rect.width / 2 - 5f, rect.height); + + if (Widgets.ButtonText(button1Rect, "DEV: Add Resources")) + { + AddTestResources(); + } + + if (Widgets.ButtonText(button2Rect, "DEV: Spawn Products")) + { + SpawnOutputProducts(); + } + } + + private void AddTestResources() + { + var globalStorage = Find.World.GetComponent(); + if (globalStorage != null) + { + // 添加200钢铁 + ThingDef steelDef = ThingDefOf.Steel; + globalStorage.AddToInputStorage(steelDef, 200); + + // 添加100零部件 + ThingDef componentDef = ThingDefOf.ComponentIndustrial; + globalStorage.AddToInputStorage(componentDef, 100); + + Messages.Message("Added 200 Steel and 100 Components to global storage", MessageTypeDefOf.PositiveEvent); + Log.Message("[DEBUG] Added test resources"); + } + } + + private void SpawnOutputProducts() + { + var globalStorage = Find.World.GetComponent(); + if (globalStorage != null && SelTable != null && SelTable.Spawned) + { + Map map = SelTable.Map; + IntVec3 spawnCell = SelTable.Position; + int totalSpawned = 0; + + // 复制列表以避免修改时枚举 + var outputCopy = new Dictionary(globalStorage.outputStorage); + + foreach (var kvp in outputCopy) + { + ThingDef thingDef = kvp.Key; + int count = kvp.Value; + + if (count > 0) + { + // 创建物品并放置到地图上 + while (count > 0) + { + int stackSize = Mathf.Min(count, thingDef.stackLimit); + Thing thing = ThingMaker.MakeThing(thingDef); + thing.stackCount = stackSize; + + if (GenPlace.TryPlaceThing(thing, spawnCell, map, ThingPlaceMode.Near)) + { + globalStorage.RemoveFromOutputStorage(thingDef, stackSize); + count -= stackSize; + totalSpawned += stackSize; + } + else + { + break; + } + } + } + } + + Messages.Message($"Spawned {totalSpawned} items at worktable location", MessageTypeDefOf.PositiveEvent); + Log.Message($"[DEBUG] Spawned {totalSpawned} output products"); + } + } + + private GlobalProductionOrder DoOrdersListing(Rect rect) + { + GlobalProductionOrder result = null; + + Widgets.DrawMenuSection(rect); + Rect outRect = rect.ContractedBy(5f); + Rect viewRect = new Rect(0f, 0f, outRect.width - 16f, viewHeight); + + Widgets.BeginScrollView(outRect, ref scrollPosition, viewRect); + + float curY = 0f; + for (int i = 0; i < SelTable.globalOrderStack.orders.Count; i++) + { + var order = SelTable.globalOrderStack.orders[i]; + + // 增加订单行高度 + Rect orderRect = new Rect(0f, curY, viewRect.width, 90f); + + if (DoOrderRow(orderRect, order)) + { + result = order; + } + + curY += 95f; // 增加行间距 + + // 分隔线 + if (i < SelTable.globalOrderStack.orders.Count - 1) + { + Widgets.DrawLineHorizontal(0f, curY - 2f, viewRect.width); + curY += 5f; + } + } + + if (Event.current.type == EventType.Layout) + { + viewHeight = curY; + } + + Widgets.EndScrollView(); + return result; + } + + private bool DoOrderRow(Rect rect, GlobalProductionOrder order) + { + Widgets.DrawHighlightIfMouseover(rect); + + // 增加内边距和行高 + float padding = 8f; + float lineHeight = 20f; + + // 订单信息 + Rect infoRect = new Rect(rect.x + padding, rect.y + padding, rect.width - 100f, lineHeight); + Widgets.Label(infoRect, order.Label); + + Rect descRect = new Rect(rect.x + padding, rect.y + padding + lineHeight + 2f, rect.width - 100f, lineHeight); + Widgets.Label(descRect, order.Description); + + // 状态显示区域 + Rect statusRect = new Rect(rect.x + padding, rect.y + padding + (lineHeight + 2f) * 2, rect.width - 100f, lineHeight); + + if (order.state == GlobalProductionOrder.ProductionState.Producing) + { + // 进度条 + Rect progressRect = new Rect(rect.x + padding, rect.y + padding + (lineHeight + 2f) * 2, rect.width - 100f, 18f); + Widgets.FillableBar(progressRect, order.progress); + + // 进度文本 + Text.Anchor = TextAnchor.MiddleCenter; + Widgets.Label(progressRect, $"{order.progress:P0}"); + Text.Anchor = TextAnchor.UpperLeft; + } + else + { + string statusText = order.state switch + { + GlobalProductionOrder.ProductionState.Waiting => "WULA_WaitingForResources".Translate(), + GlobalProductionOrder.ProductionState.Completed => "WULA_Completed".Translate(), + _ => "WULA_Unknown".Translate() + }; + Widgets.Label(statusRect, statusText); + } + + // 控制按钮 + float buttonY = rect.y + padding; + Rect pauseButtonRect = new Rect(rect.xMax - 90f, buttonY, 40f, 25f); + if (Widgets.ButtonText(pauseButtonRect, order.paused ? "WULA_Resume".Translate() : "WULA_Pause".Translate())) + { + order.paused = !order.paused; + SoundDefOf.Click.PlayOneShotOnCamera(); + } + + Rect deleteButtonRect = new Rect(rect.xMax - 45f, buttonY, 40f, 25f); + if (Widgets.ButtonText(deleteButtonRect, "WULA_Delete".Translate())) + { + SelTable.globalOrderStack.Delete(order); + SoundDefOf.Click.PlayOneShotOnCamera(); + } + + // 资源检查提示 - 修复逻辑 + bool shouldShowRedBorder = (order.state == GlobalProductionOrder.ProductionState.Waiting && !order.HasEnoughResources()); + if (shouldShowRedBorder) + { + TooltipHandler.TipRegion(rect, "WULA_InsufficientResources".Translate()); + GUI.color = Color.red; + Widgets.DrawBox(rect, 2); + GUI.color = Color.white; + } + + return Mouse.IsOver(rect); + } + + private List GenerateRecipeOptions() + { + var options = new List(); + + foreach (var recipe in SelTable.def.AllRecipes) + { + if (recipe.AvailableNow && recipe.AvailableOnNow(SelTable)) + { + string label = recipe.LabelCap; + + options.Add(new FloatMenuOption(label, () => + { + var newOrder = new GlobalProductionOrder + { + recipe = recipe, + targetCount = 1, + paused = true // 初始暂停 + }; + + SelTable.globalOrderStack.AddOrder(newOrder); + SoundDefOf.Click.PlayOneShotOnCamera(); + Log.Message($"[DEBUG] Added order for {recipe.defName}"); + })); + } + } + + if (options.Count == 0) + { + options.Add(new FloatMenuOption("WULA_NoAvailableRecipes".Translate(), null)); + } + + return options; + } + + public override void TabUpdate() + { + base.TabUpdate(); + } + } +} diff --git a/Source/WulaFallenEmpire/HediffComp/HediffCompProperties_NanoRepair.cs b/Source/WulaFallenEmpire/HediffComp/HediffCompProperties_NanoRepair.cs index b16fc4b6..eb4374d2 100644 --- a/Source/WulaFallenEmpire/HediffComp/HediffCompProperties_NanoRepair.cs +++ b/Source/WulaFallenEmpire/HediffComp/HediffCompProperties_NanoRepair.cs @@ -1,3 +1,4 @@ +// HediffCompProperties_NanoRepair.cs using RimWorld; using Verse; using System.Collections.Generic; @@ -12,7 +13,6 @@ namespace WulaFallenEmpire public float minEnergyThreshold = 0.1f; // 最低能量阈值 public float repairCostPerHP = 0.1f; // 每点生命值修复的能量消耗 public int repairCooldownAfterDamage = 300; // 受到伤害后的修复冷却时间 - public float repairTolerance = 0f; // 修复容忍度,低于此值认为已完全修复 public HediffCompProperties_NanoRepair() { @@ -200,7 +200,7 @@ namespace WulaFallenEmpire return true; } - // 使用 GetPartHealth 检查是否有损伤 + // 检查是否有损伤 if (HasDamagedParts()) { if (debugCounter % 10 == 0) @@ -208,14 +208,7 @@ namespace WulaFallenEmpire return true; } - // 检查是否有需要治疗的疾病 - if (Pawn.health.hediffSet.HasTendableNonInjuryNonMissingPartHediff()) - { - if (debugCounter % 10 == 0) - Log.Message($"[NanoRepair] 检测到可治疗疾病"); - return true; - } - + // 不再检查疾病 if (debugCounter % 10 == 0) Log.Message($"[NanoRepair] 没有检测到任何需要修复的损伤"); return false; @@ -235,12 +228,12 @@ namespace WulaFallenEmpire float maxHealth = part.def.GetMaxHealth(Pawn); float currentHealth = Pawn.health.hediffSet.GetPartHealth(part); - // 使用修复容忍度,只有当损伤大于容忍度时才认为需要修复 - if (currentHealth < maxHealth - Props.repairTolerance) + // 不再使用修复容忍度,任何损伤都需要修复 + if (currentHealth < maxHealth) { damagedCount++; if (debugCounter % 10 == 0 && damagedCount == 1) - Log.Message($"[NanoRepair] 部位 {part.def.defName} 有损伤: {currentHealth}/{maxHealth} (容忍度: {Props.repairTolerance})"); + Log.Message($"[NanoRepair] 部位 {part.def.defName} 有损伤: {currentHealth}/{maxHealth}"); } } } @@ -264,8 +257,8 @@ namespace WulaFallenEmpire float maxHealth = part.def.GetMaxHealth(Pawn); float currentHealth = Pawn.health.hediffSet.GetPartHealth(part); - // 使用修复容忍度 - if (currentHealth < maxHealth - Props.repairTolerance) + // 不再使用修复容忍度,任何损伤都需要修复 + if (currentHealth < maxHealth) { damagedParts.Add(part); if (debugCounter % 10 == 0 && damagedParts.Count <= 3) @@ -309,13 +302,7 @@ namespace WulaFallenEmpire return; } - // 最后修复疾病 - if (TryRepairDiseases(energyNeed)) - { - if (debugCounter % 10 == 0) - Log.Message($"[NanoRepair] 已修复疾病"); - return; - } + // 不再修复疾病 if (debugCounter % 10 == 0) Log.Message($"[NanoRepair] 没有执行任何修复"); @@ -450,110 +437,18 @@ namespace WulaFallenEmpire return false; } - private bool TryRepairDiseases(Need energyNeed) - { - var diseases = Pawn.health.hediffSet.GetTendableNonInjuryNonMissingPartHediffs(); - if (diseases == null) - { - if (debugCounter % 10 == 0) - Log.Message($"[NanoRepair] 没有可治疗疾病"); - return false; - } - - int diseaseCount = 0; - foreach (var disease in diseases) - { - if (disease.TendableNow()) - diseaseCount++; - } - - if (diseaseCount == 0) - { - if (debugCounter % 10 == 0) - Log.Message($"[NanoRepair] 没有可治疗的疾病"); - return false; - } - - if (debugCounter % 10 == 0) - Log.Message($"[NanoRepair] 检查修复疾病,共有 {diseaseCount} 个"); - - foreach (var disease in diseases) - { - if (disease.TendableNow()) - { - float repairCost = CalculateRepairCost(disease); - - if (debugCounter % 10 == 0) - Log.Message($"[NanoRepair] 尝试修复疾病 {disease.def.defName}, 严重性: {disease.Severity:F2}, 成本: {repairCost:F2}, 当前能量: {energyNeed.CurLevel:F2}"); - - if (energyNeed.CurLevel >= repairCost) - { - if (RepairDisease(disease, repairCost)) - { - energyNeed.CurLevel -= repairCost; - if (debugCounter % 10 == 0) - Log.Message($"[NanoRepair] 成功修复疾病 {disease.def.defName}, 消耗能量: {repairCost:F2}"); - return true; - } - } - else - { - if (debugCounter % 10 == 0) - Log.Message($"[NanoRepair] 能量不足修复疾病: {energyNeed.CurLevel:F2} < {repairCost:F2}"); - } - } - } - return false; - } - - private float CalculateRepairCost(Hediff hediff) - { - float baseCost = Props.repairCostPerHP; - - // 根据机械族的能量消耗属性调整成本 - var mechEnergyLoss = Pawn.GetStatValue(StatDefOf.MechEnergyLossPerHP); - if (mechEnergyLoss > 0) - { - baseCost *= mechEnergyLoss; - } - - float severityMultiplier = 1.0f; - - if (hediff is Hediff_Injury injury) - { - severityMultiplier = injury.Severity; - } - else if (hediff is Hediff_MissingPart) - { - // 缺失部件修复成本使用正常计算 - severityMultiplier = 1.0f; - } - else - { - severityMultiplier = hediff.Severity * 1.5f; - } - - float finalCost = baseCost * severityMultiplier; - - if (debugCounter % 10 == 0) - Log.Message($"[NanoRepair] 计算修复成本: 基础={Props.repairCostPerHP}, 机械族能耗系数={mechEnergyLoss}, 严重性系数={severityMultiplier}, 最终成本={finalCost:F2}"); - - return finalCost; - } - + // 新的修复逻辑:完美修复所有伤口 private bool RepairDamagedPart(BodyPartRecord part, float repairCost) { try { float maxHealth = part.def.GetMaxHealth(Pawn); float currentHealth = Pawn.health.hediffSet.GetPartHealth(part); - float healthToRepair = Mathf.Min(maxHealth - currentHealth, repairCost / Props.repairCostPerHP); if (debugCounter % 10 == 0) - Log.Message($"[NanoRepair] 开始修复部位 {part.def.defName}, 计划修复量: {healthToRepair:F1}"); + Log.Message($"[NanoRepair] 开始修复部位 {part.def.defName}, 当前健康: {currentHealth:F1}/{maxHealth:F1}"); - // 直接修复部位的健康值,而不是查找特定的hediff - // 找到该部位的所有hediff并尝试修复 + // 获取该部位的所有hediff var hediffsOnPart = new List(); foreach (var hediff in Pawn.health.hediffSet.hediffs) { @@ -575,17 +470,10 @@ namespace WulaFallenEmpire if (debugCounter % 10 == 0) Log.Message($"[NanoRepair] 在部位 {part.def.defName} 上找到 {hediffsOnPart.Count} 个hediff"); - // 按严重性排序,先修复最严重的hediff - hediffsOnPart.Sort((a, b) => b.Severity.CompareTo(a.Severity)); - - float remainingRepair = healthToRepair; bool anyRepairDone = false; foreach (var hediff in hediffsOnPart) { - if (remainingRepair <= 0) - break; - // 检查hediff是否可修复 if (!CanRepairHediff(hediff)) { @@ -593,26 +481,29 @@ namespace WulaFallenEmpire Log.Message($"[NanoRepair] 跳过不可修复的hediff: {hediff.def.defName}"); continue; } - - float healAmount = Mathf.Min(hediff.Severity, remainingRepair); - hediff.Severity -= healAmount; - remainingRepair -= healAmount; - anyRepairDone = true; - if (debugCounter % 10 == 0) - Log.Message($"[NanoRepair] 修复hediff {hediff.def.defName}, 修复量: {healAmount:F1}, 剩余严重性: {hediff.Severity:F1}"); - - // 使用修复容忍度,只有当严重性大于容忍度时才移除hediff - if (hediff.Severity <= Props.repairTolerance) + // 新的修复逻辑:对于小于1的伤口,直接删除 + if (hediff.Severity < 1.0f) { Pawn.health.RemoveHediff(hediff); + anyRepairDone = true; if (debugCounter % 10 == 0) - Log.Message($"[NanoRepair] hediff {hediff.def.defName} 已完全修复并移除"); + Log.Message($"[NanoRepair] 删除严重性小于1的hediff: {hediff.def.defName} (严重性: {hediff.Severity:F2})"); + } + else + { + // 对于大于等于1的伤口,完全修复 + float originalSeverity = hediff.Severity; + hediff.Severity = 0f; + Pawn.health.RemoveHediff(hediff); + anyRepairDone = true; + if (debugCounter % 10 == 0) + Log.Message($"[NanoRepair] 完全修复hediff: {hediff.def.defName} (严重性: {originalSeverity:F2} -> 0)"); } } if (debugCounter % 10 == 0) - Log.Message($"[NanoRepair] 部位 {part.def.defName} 修复完成,总修复量: {healthToRepair - remainingRepair:F1}"); + Log.Message($"[NanoRepair] 部位 {part.def.defName} 修复完成,执行了 {anyRepairDone} 次修复"); return anyRepairDone; } @@ -626,12 +517,16 @@ namespace WulaFallenEmpire // 检查hediff是否可修复 private bool CanRepairHediff(Hediff hediff) { - // 如果是机械族特有的hediff,总是可以修复 - if (IsMechSpecificHediff(hediff)) - return true; + // 跳过疾病 + if (IsDisease(hediff)) + { + if (debugCounter % 10 == 0) + Log.Message($"[NanoRepair] 跳过疾病: {hediff.def.defName}"); + return false; + } - // 如果是可治疗的hediff,可以修复 - if (hediff.TendableNow()) + // 如果是机械族特有的hediff,可以修复 + if (IsMechSpecificHediff(hediff)) return true; // 如果是损伤类型的hediff,可以修复 @@ -642,6 +537,26 @@ namespace WulaFallenEmpire return false; } + // 检查是否是疾病 + private bool IsDisease(Hediff hediff) + { + // 这里可以定义哪些hediff被认为是疾病 + // 常见的疾病类型 + string[] diseaseKeywords = { + "Disease", "Flu", "Plague", "Infection", "Malaria", + "SleepingSickness", "FibrousMechanites", "SensoryMechanites", + "WoundInfection", "FoodPoisoning", "GutWorms", "MuscleParasites" + }; + + foreach (string keyword in diseaseKeywords) + { + if (hediff.def.defName.Contains(keyword)) + return true; + } + + return false; + } + // 将缺失部件转换为指定的hediff private bool ConvertMissingPartToInjury(Hediff_MissingPart missingPart, float repairCost) { @@ -701,33 +616,6 @@ namespace WulaFallenEmpire hediff.def.defName.Contains("Gunshot"); // 包括枪伤 } - private bool RepairDisease(Hediff disease, float repairCost) - { - try - { - // 修复疾病 - float healAmount = Mathf.Min(disease.Severity, repairCost / (Props.repairCostPerHP * 1.5f)); - disease.Severity -= healAmount; - - if (debugCounter % 10 == 0) - Log.Message($"[NanoRepair] 修复疾病 {disease.def.defName}, 修复量: {healAmount:F2}, 剩余严重性: {disease.Severity:F2}"); - - // 使用修复容忍度 - if (disease.Severity <= Props.repairTolerance) - { - Pawn.health.RemoveHediff(disease); - if (debugCounter % 10 == 0) - Log.Message($"[NanoRepair] 疾病 {disease.def.defName} 已完全修复并移除"); - } - return true; - } - catch (System.Exception ex) - { - Log.Error($"[NanoRepair] 修复疾病 {disease.def.defName} 时出错: {ex}"); - return false; - } - } - public override void Notify_PawnPostApplyDamage(DamageInfo dinfo, float totalDamageDealt) { base.Notify_PawnPostApplyDamage(dinfo, totalDamageDealt); diff --git a/Source/WulaFallenEmpire/Pawn/WULA_Maintenance/Building_MaintenancePod.cs b/Source/WulaFallenEmpire/Pawn/WULA_Maintenance/Building_MaintenancePod.cs index ec8209eb..1437c790 100644 --- a/Source/WulaFallenEmpire/Pawn/WULA_Maintenance/Building_MaintenancePod.cs +++ b/Source/WulaFallenEmpire/Pawn/WULA_Maintenance/Building_MaintenancePod.cs @@ -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(); - 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; } } -} \ No newline at end of file +} diff --git a/Source/WulaFallenEmpire/Pawn/WULA_Maintenance/CompMaintenancePod.cs b/Source/WulaFallenEmpire/Pawn/WULA_Maintenance/CompMaintenancePod.cs index dee1f776..ccb1671c 100644 --- a/Source/WulaFallenEmpire/Pawn/WULA_Maintenance/CompMaintenancePod.cs +++ b/Source/WulaFallenEmpire/Pawn/WULA_Maintenance/CompMaintenancePod.cs @@ -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.Get("UI/Designators/Cancel"); private static readonly Texture2D EnterIcon = ContentFinder.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(); + 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(); + 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(this, false, LookMode.Deep); } - public override void PostSpawnSetup(bool respawningAfterLoad) { base.PostSpawnSetup(respawningAfterLoad); powerComp = parent.TryGetComp(); refuelableComp = parent.TryGetComp(); } - 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 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(); + + // 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(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(); + 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 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 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 GetPawnOptions() { - List options = new List(); - // 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(); + 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(); - 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(); + 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 } } \ No newline at end of file diff --git a/Source/WulaFallenEmpire/Pawn/WULA_Maintenance/HediffCompProperties_MaintenanceDamage.cs b/Source/WulaFallenEmpire/Pawn/WULA_Maintenance/HediffCompProperties_MaintenanceDamage.cs new file mode 100644 index 00000000..61eb71ce --- /dev/null +++ b/Source/WulaFallenEmpire/Pawn/WULA_Maintenance/HediffCompProperties_MaintenanceDamage.cs @@ -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(); + if (maintenanceNeed == null) + return; + + // 直接应用伤害惩罚 + maintenanceNeed.ApplyDamagePenalty(totalDamageDealt); + } + + public override string CompTipStringExtra => "WULA_DamageAffectsMaintenance".Translate(Props.damageToMaintenanceFactor.ToStringPercent()); + } +} diff --git a/Source/WulaFallenEmpire/Pawn/WULA_Maintenance/HediffComp_MaintenanceNeed.cs b/Source/WulaFallenEmpire/Pawn/WULA_Maintenance/HediffComp_MaintenanceNeed.cs deleted file mode 100644 index 72cc6722..00000000 --- a/Source/WulaFallenEmpire/Pawn/WULA_Maintenance/HediffComp_MaintenanceNeed.cs +++ /dev/null @@ -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; - } - } - } - } -} \ No newline at end of file diff --git a/Source/WulaFallenEmpire/Pawn/WULA_Maintenance/JobDefOf_WULA.cs b/Source/WulaFallenEmpire/Pawn/WULA_Maintenance/JobDefOf_WULA.cs deleted file mode 100644 index 917ffc15..00000000 --- a/Source/WulaFallenEmpire/Pawn/WULA_Maintenance/JobDefOf_WULA.cs +++ /dev/null @@ -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)); - } - } -} \ No newline at end of file diff --git a/Source/WulaFallenEmpire/Pawn/WULA_Maintenance/JobDriver_EnterMaintenancePod.cs b/Source/WulaFallenEmpire/Pawn/WULA_Maintenance/JobDriver_EnterMaintenancePod.cs index 839f57bd..ee2a2d80 100644 --- a/Source/WulaFallenEmpire/Pawn/WULA_Maintenance/JobDriver_EnterMaintenancePod.cs +++ b/Source/WulaFallenEmpire/Pawn/WULA_Maintenance/JobDriver_EnterMaintenancePod.cs @@ -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(); public override bool TryMakePreToilReservations(bool errorOnFailed) { @@ -19,29 +20,24 @@ namespace WulaFallenEmpire protected override IEnumerable 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(); - 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; } } -} \ No newline at end of file +} diff --git a/Source/WulaFallenEmpire/Pawn/WULA_Maintenance/JobDriver_HaulToMaintenancePod.cs b/Source/WulaFallenEmpire/Pawn/WULA_Maintenance/JobDriver_HaulToMaintenancePod.cs index 8504f1c5..f836377b 100644 --- a/Source/WulaFallenEmpire/Pawn/WULA_Maintenance/JobDriver_HaulToMaintenancePod.cs +++ b/Source/WulaFallenEmpire/Pawn/WULA_Maintenance/JobDriver_HaulToMaintenancePod.cs @@ -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(); + protected CompMaintenancePod PodComp => Pod?.TryGetComp(); 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 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; } } -} \ No newline at end of file +} diff --git a/Source/WulaFallenEmpire/Pawn/WULA_Maintenance/Job_Maintenance.cs b/Source/WulaFallenEmpire/Pawn/WULA_Maintenance/Job_Maintenance.cs deleted file mode 100644 index 46cd988c..00000000 --- a/Source/WulaFallenEmpire/Pawn/WULA_Maintenance/Job_Maintenance.cs +++ /dev/null @@ -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.GetNamed("WULA_EnterMaintenancePod"), t); - } - } -} \ No newline at end of file diff --git a/Source/WulaFallenEmpire/Pawn/WULA_Maintenance/MaintenanceNeedExtension.cs b/Source/WulaFallenEmpire/Pawn/WULA_Maintenance/MaintenanceNeedExtension.cs new file mode 100644 index 00000000..4162105c --- /dev/null +++ b/Source/WulaFallenEmpire/Pawn/WULA_Maintenance/MaintenanceNeedExtension.cs @@ -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; + } +} diff --git a/Source/WulaFallenEmpire/Pawn/WULA_Maintenance/Need_Maintenance.cs b/Source/WulaFallenEmpire/Pawn/WULA_Maintenance/Need_Maintenance.cs new file mode 100644 index 00000000..6f7cb1a7 --- /dev/null +++ b/Source/WulaFallenEmpire/Pawn/WULA_Maintenance/Need_Maintenance.cs @@ -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(); + + // 上次维护的天数 + 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"; + } + } + } +} diff --git a/Source/WulaFallenEmpire/Pawn/WULA_Maintenance/WorkGiver_DoMaintenance.cs b/Source/WulaFallenEmpire/Pawn/WULA_Maintenance/WorkGiver_DoMaintenance.cs new file mode 100644 index 00000000..563cb1d6 --- /dev/null +++ b/Source/WulaFallenEmpire/Pawn/WULA_Maintenance/WorkGiver_DoMaintenance.cs @@ -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(); + 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(); + if (maintenanceNeed == null) + { + // 这个Pawn没有维护需求,不应该使用维护舱 + return false; + } + + // 检查维护水平是否低于阈值 + return maintenanceNeed.CurLevel <= 0.3f; // 需要维护的阈值 + } + } +} diff --git a/Source/WulaFallenEmpire/WulaDefOf.cs b/Source/WulaFallenEmpire/WulaDefOf.cs index 87611687..762c95aa 100644 --- a/Source/WulaFallenEmpire/WulaDefOf.cs +++ b/Source/WulaFallenEmpire/WulaDefOf.cs @@ -15,4 +15,16 @@ namespace WulaFallenEmpire DefOfHelper.EnsureInitializedInCtor(typeof(ThingDefOf_WULA)); } } + [DefOf] + public static class JobDefOf_WULA + { + public static JobDef WULA_EnterMaintenancePod; + + public static JobDef WULA_HaulToMaintenancePod; + + static JobDefOf_WULA() + { + DefOfHelper.EnsureInitializedInCtor(typeof(JobDefOf_WULA)); + } + } } \ No newline at end of file diff --git a/Source/WulaFallenEmpire/WulaFallenEmpire.csproj b/Source/WulaFallenEmpire/WulaFallenEmpire.csproj index 6808b591..ecabd352 100644 --- a/Source/WulaFallenEmpire/WulaFallenEmpire.csproj +++ b/Source/WulaFallenEmpire/WulaFallenEmpire.csproj @@ -106,6 +106,11 @@ + + + + + @@ -142,11 +147,12 @@ - - + - + + +