From 4782a6a4591522b9e81c106c5c17ca9bc9f8df43 Mon Sep 17 00:00:00 2001 From: "ProjectKoi-Kalo\\Kalo" Date: Sun, 14 Dec 2025 12:15:45 +0800 Subject: [PATCH] zc --- .../Keyed/WULA_Keyed.xml | 11 +- .../Building_GlobalWorkTable.cs | 164 +++++++ .../Dialog_GlobalStorageTransfer.cs | 416 ++++++++++++++++++ .../GlobalWorkTable/ITab_GlobalBills.cs | 2 +- .../JobDriver_GlobalWorkTable.cs | 63 +-- .../WorkGiver_GlobalWorkTable.cs | 48 +- 6 files changed, 636 insertions(+), 68 deletions(-) create mode 100644 Source/WulaFallenEmpire/GlobalWorkTable/Dialog_GlobalStorageTransfer.cs diff --git a/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/WULA_Keyed.xml b/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/WULA_Keyed.xml index fa335434..f93e6a24 100644 --- a/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/WULA_Keyed.xml +++ b/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/WULA_Keyed.xml @@ -50,6 +50,15 @@ 存储统计 种输入物品 种输出物品 + + 存取全局存储 + 在轨道贸易信标范围和全局存储之间存取物品 + 没有已通电的轨道贸易信标 + 没有可用的殖民者 + 全局存储存取 + 负数=存(信标->全局),正数=取(全局->信标空投) + 清零 + 执行存取 空投成品 @@ -157,4 +166,4 @@ 错误:连接丢失。“军团”保持沉默。 {FACTION_name}已经在附近投下了一些资源。 - \ No newline at end of file + diff --git a/Source/WulaFallenEmpire/GlobalWorkTable/Building_GlobalWorkTable.cs b/Source/WulaFallenEmpire/GlobalWorkTable/Building_GlobalWorkTable.cs index dfc1ef9e..3c3da336 100644 --- a/Source/WulaFallenEmpire/GlobalWorkTable/Building_GlobalWorkTable.cs +++ b/Source/WulaFallenEmpire/GlobalWorkTable/Building_GlobalWorkTable.cs @@ -56,11 +56,145 @@ namespace WulaFallenEmpire if (CurrentlyUsableForGlobalBills()) { + TryAutoGatherFromBeaconsAndContainer(); globalOrderStack.ProcessOrders(); } } } + internal void TryAutoGatherFromBeaconsAndContainer() + { + var order = globalOrderStack?.orders?.FirstOrDefault(o => + o != null && + !o.paused && + o.state == GlobalProductionOrder.ProductionState.Gathering); + if (order == null) return; + + var storage = Find.World.GetComponent(); + if (storage == null) return; + + Dictionary required = GetRequiredMaterialsForOrder(order); + if (required.Count == 0) return; + + bool changed = false; + foreach (var kvp in required) + { + ThingDef thingDef = kvp.Key; + int need = kvp.Value; + if (need <= 0) continue; + + int inCloud = storage.GetInputStorageCount(thingDef); + int missing = need - inCloud; + if (missing <= 0) continue; + + int uploadedFromBeacons = UploadFromPoweredTradeBeacons(storage, thingDef, missing); + if (uploadedFromBeacons > 0) + { + changed = true; + missing -= uploadedFromBeacons; + } + + if (missing <= 0) continue; + + int uploadedFromContainer = UploadFromInnerContainer(storage, thingDef, missing); + if (uploadedFromContainer > 0) + { + changed = true; + } + } + + if (changed) + { + order.UpdateState(); + } + } + + internal Dictionary GetRequiredMaterialsForOrder(GlobalProductionOrder order) + { + var required = order.GetProductCostList(); + if (required.Count > 0) return required; + + required = new Dictionary(); + if (order.recipe?.ingredients == null) return required; + + foreach (var ingredient in order.recipe.ingredients) + { + ThingDef def = ingredient.filter?.AllowedThingDefs?.FirstOrDefault(); + if (def == null) continue; + + int count = ingredient.CountRequiredOfFor(def, order.recipe); + if (count <= 0) continue; + + if (required.ContainsKey(def)) required[def] += count; + else required[def] = count; + } + + return required; + } + + internal int UploadFromInnerContainer(GlobalStorageWorldComponent storage, ThingDef def, int count) + { + if (count <= 0) return 0; + + int remaining = count; + int uploaded = 0; + + while (remaining > 0) + { + Thing thing = innerContainer?.FirstOrDefault(t => t.def == def); + if (thing == null) break; + + int take = Mathf.Min(thing.stackCount, remaining); + Thing split = thing.SplitOff(take); + split.Destroy(DestroyMode.Vanish); + + storage.AddToInputStorage(def, take); + + uploaded += take; + remaining -= take; + } + + return uploaded; + } + + internal int UploadFromPoweredTradeBeacons(GlobalStorageWorldComponent storage, ThingDef def, int count) + { + if (count <= 0) return 0; + if (Map == null) return 0; + + int remaining = count; + int uploaded = 0; + + foreach (var beacon in Building_OrbitalTradeBeacon.AllPowered(Map)) + { + foreach (var cell in beacon.TradeableCells) + { + if (remaining <= 0) break; + + List things = cell.GetThingList(Map); + for (int i = things.Count - 1; i >= 0; i--) + { + if (remaining <= 0) break; + + Thing t = things[i]; + if (t?.def != def) continue; + + int take = Mathf.Min(t.stackCount, remaining); + Thing split = t.SplitOff(take); + split.Destroy(DestroyMode.Vanish); + + storage.AddToInputStorage(def, take); + uploaded += take; + remaining -= take; + } + } + + if (remaining <= 0) break; + } + + return uploaded; + } + public bool CurrentlyUsableForGlobalBills() { if (powerComp != null && !powerComp.PowerOn) @@ -83,6 +217,15 @@ namespace WulaFallenEmpire { yield return g; } + + yield return new Command_Action + { + action = OpenGlobalStorageTransferDialog, + defaultLabel = "WULA_AccessGlobalStorage".Translate(), + defaultDesc = "WULA_AccessGlobalStorageDesc".Translate(), + icon = ContentFinder.Get("UI/Commands/Trade", true), + }; + // 白银转移按钮 - 检查输入端是否有白银 var globalStorage = Find.World.GetComponent(); int silverAmount = globalStorage?.GetInputStorageCount(ThingDefOf.Silver) ?? 0; @@ -122,6 +265,27 @@ namespace WulaFallenEmpire } } + private void OpenGlobalStorageTransferDialog() + { + if (Map == null) + return; + + if (!Building_OrbitalTradeBeacon.AllPowered(Map).Any()) + { + Messages.Message("WULA_NoPoweredTradeBeacon".Translate(), this, MessageTypeDefOf.RejectInput); + return; + } + + Pawn negotiator = Map.mapPawns?.FreeColonistsSpawned?.FirstOrDefault(); + if (negotiator == null) + { + Messages.Message("WULA_NoNegotiator".Translate(), this, MessageTypeDefOf.RejectInput); + return; + } + + Find.WindowStack.Add(new Dialog_GlobalStorageTransfer(this, negotiator)); + } + // 新增:将输入端白银转移到输出端的方法 private void TransferSilverToOutput() { diff --git a/Source/WulaFallenEmpire/GlobalWorkTable/Dialog_GlobalStorageTransfer.cs b/Source/WulaFallenEmpire/GlobalWorkTable/Dialog_GlobalStorageTransfer.cs new file mode 100644 index 00000000..2ac3aa5a --- /dev/null +++ b/Source/WulaFallenEmpire/GlobalWorkTable/Dialog_GlobalStorageTransfer.cs @@ -0,0 +1,416 @@ +using RimWorld; +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using Verse; +using Verse.Sound; + +namespace WulaFallenEmpire +{ + public class Dialog_GlobalStorageTransfer : Window + { + private const float RowHeight = 30f; + private const float TitleHeight = 45f; + private const float TopAreaHeight = 58f; + + private readonly Building_GlobalWorkTable table; + private readonly Pawn negotiator; + private readonly GlobalStorageWorldComponent storage; + private readonly GlobalStorageTransferTrader trader; + + private readonly QuickSearchWidget quickSearchWidget = new QuickSearchWidget(); + private Vector2 scrollPosition; + private float viewHeight; + + private List tradeables = new List(); + + private ITrader prevTrader; + private Pawn prevNegotiator; + private TradeDeal prevDeal; + private bool prevGiftMode; + + public override Vector2 InitialSize => new Vector2(1024f, UI.screenHeight); + + public Dialog_GlobalStorageTransfer(Building_GlobalWorkTable table, Pawn negotiator) + { + this.table = table; + this.negotiator = negotiator; + storage = Find.World.GetComponent(); + trader = new GlobalStorageTransferTrader(table?.Map, storage); + + doCloseX = true; + closeOnAccept = false; + closeOnClickedOutside = true; + absorbInputAroundWindow = true; + } + + public override void PostOpen() + { + base.PostOpen(); + + prevTrader = TradeSession.trader; + prevNegotiator = TradeSession.playerNegotiator; + prevDeal = TradeSession.deal; + prevGiftMode = TradeSession.giftMode; + + TradeSession.trader = trader; + TradeSession.playerNegotiator = negotiator; + TradeSession.deal = null; + TradeSession.giftMode = false; + + RebuildTradeables(); + } + + public override void PostClose() + { + base.PostClose(); + + TradeSession.trader = prevTrader; + TradeSession.playerNegotiator = prevNegotiator; + TradeSession.deal = prevDeal; + TradeSession.giftMode = prevGiftMode; + } + + public override void DoWindowContents(Rect inRect) + { + if (table == null || table.DestroyedOrNull() || table.Map == null || storage == null) + { + Close(); + return; + } + + Text.Font = GameFont.Medium; + Widgets.Label(new Rect(0f, 0f, inRect.width, TitleHeight), "WULA_GlobalStorageTransferTitle".Translate()); + Text.Font = GameFont.Small; + + Rect topRect = new Rect(0f, TitleHeight, inRect.width, TopAreaHeight); + DrawTopArea(topRect); + + float bottomAreaHeight = 45f; + Rect listRect = new Rect(0f, topRect.yMax + 6f, inRect.width, inRect.height - topRect.yMax - bottomAreaHeight - 8f); + DrawTradeablesList(listRect); + + Rect bottomRect = new Rect(0f, inRect.height - bottomAreaHeight, inRect.width, bottomAreaHeight); + DrawBottomButtons(bottomRect); + } + + private void DrawTopArea(Rect rect) + { + Rect searchRect = new Rect(rect.xMax - 260f, rect.y + 2f, 260f, 24f); + quickSearchWidget.OnGUI(searchRect, onFilterChange: () => { }, onClear: () => { }); + + Rect hintRect = new Rect(rect.x, rect.y, rect.width - 270f, rect.height); + Widgets.Label(hintRect, + "WULA_GlobalStorageTransferHint".Translate()); + } + + private void DrawTradeablesList(Rect rect) + { + 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; + int drawnIndex = 0; + + for (int i = 0; i < tradeables.Count; i++) + { + Tradeable_StorageTransfer trad = tradeables[i]; + if (trad == null || trad.ThingDef == null) continue; + + if (!quickSearchWidget.filter.Matches(trad.ThingDef)) + continue; + + Rect rowRect = new Rect(0f, curY, viewRect.width, RowHeight); + TradeUI.DrawTradeableRow(rowRect, trad, drawnIndex); + curY += RowHeight; + drawnIndex++; + } + + if (Event.current.type == EventType.Layout) + { + viewHeight = Mathf.Max(curY, outRect.height); + } + + Widgets.EndScrollView(); + } + + private void DrawBottomButtons(Rect rect) + { + float buttonWidth = 160f; + Rect executeRect = new Rect(rect.xMax - buttonWidth, rect.y + 2f, buttonWidth, rect.height - 4f); + Rect resetRect = new Rect(executeRect.x - buttonWidth - 10f, executeRect.y, buttonWidth, executeRect.height); + + if (Widgets.ButtonText(resetRect, "WULA_ResetTransfer".Translate())) + { + foreach (var t in tradeables) + { + t?.ForceTo(0); + } + SoundDefOf.Tick_Low.PlayOneShotOnCamera(); + } + + if (Widgets.ButtonText(executeRect, "WULA_ExecuteTransfer".Translate())) + { + ExecuteTransfers(); + } + } + + private void ExecuteTransfers() + { + bool changed = false; + + foreach (var trad in tradeables) + { + if (trad == null) continue; + if (trad.CountToTransfer == 0) continue; + + changed = true; + trad.ResolveTrade(); + trad.ForceTo(0); + } + + if (changed) + { + SoundDefOf.ExecuteTrade.PlayOneShotOnCamera(); + RebuildTradeables(); + } + else + { + SoundDefOf.Tick_Low.PlayOneShotOnCamera(); + } + } + + private void RebuildTradeables() + { + tradeables.Clear(); + + var byDef = new Dictionary(); + + foreach (Thing t in GetThingsInPoweredTradeBeaconRange(table.Map)) + { + if (t?.def == null) continue; + if (t.def.category != ThingCategory.Item) continue; + + if (!byDef.TryGetValue(t.def, out var trad)) + { + trad = new Tradeable_StorageTransfer(); + byDef[t.def] = trad; + } + trad.AddThing(t, Transactor.Colony); + } + + foreach (var kvp in GetGlobalStorageCounts(storage)) + { + ThingDef def = kvp.Key; + int count = kvp.Value; + if (def == null || count <= 0) continue; + + if (!byDef.TryGetValue(def, out var trad)) + { + trad = new Tradeable_StorageTransfer(); + byDef[def] = trad; + } + + foreach (Thing dummy in MakeDummyStacks(def, count)) + { + trad.AddThing(dummy, Transactor.Trader); + } + } + + tradeables = byDef.Values + .Where(t => t != null && t.ThingDef != null) + .OrderBy(t => t.ThingDef.label) + .ToList(); + } + + private static IEnumerable GetThingsInPoweredTradeBeaconRange(Map map) + { + if (map == null) yield break; + + HashSet yielded = new HashSet(); + foreach (var beacon in Building_OrbitalTradeBeacon.AllPowered(map)) + { + foreach (var cell in beacon.TradeableCells) + { + List things = cell.GetThingList(map); + for (int i = 0; i < things.Count; i++) + { + Thing t = things[i]; + if (t == null) continue; + if (!yielded.Add(t)) continue; + yield return t; + } + } + } + } + + private static Dictionary GetGlobalStorageCounts(GlobalStorageWorldComponent storage) + { + var counts = new Dictionary(); + if (storage == null) return counts; + + foreach (var kvp in storage.outputStorage) + { + if (kvp.Key == null || kvp.Value <= 0) continue; + counts[kvp.Key] = counts.TryGetValue(kvp.Key, out var v) ? v + kvp.Value : kvp.Value; + } + + foreach (var kvp in storage.inputStorage) + { + if (kvp.Key == null || kvp.Value <= 0) continue; + counts[kvp.Key] = counts.TryGetValue(kvp.Key, out var v) ? v + kvp.Value : kvp.Value; + } + + return counts; + } + + private static IEnumerable MakeDummyStacks(ThingDef def, int count) + { + int remaining = count; + int stackLimit = Mathf.Max(1, def.stackLimit); + + while (remaining > 0) + { + int stackCount = Mathf.Min(remaining, stackLimit); + Thing thing = MakeThingForDef(def, stackCount); + if (thing == null) yield break; + + yield return thing; + remaining -= stackCount; + } + } + + private static Thing MakeThingForDef(ThingDef def, int stackCount) + { + if (def == null) return null; + + Thing thing; + if (def.MadeFromStuff) + { + ThingDef stuff = GenStuff.DefaultStuffFor(def) ?? GenStuff.AllowedStuffsFor(def).FirstOrDefault(); + if (stuff == null) return null; + thing = ThingMaker.MakeThing(def, stuff); + } + else + { + thing = ThingMaker.MakeThing(def); + } + + thing.stackCount = stackCount; + return thing; + } + + private class Tradeable_StorageTransfer : Tradeable + { + public override bool TraderWillTrade => true; + public override bool IsCurrency => false; + } + + private class GlobalStorageTransferTrader : ITrader + { + private readonly Map map; + private readonly GlobalStorageWorldComponent storage; + private readonly TraderKindDef traderKind; + + public GlobalStorageTransferTrader(Map map, GlobalStorageWorldComponent storage) + { + this.map = map; + this.storage = storage; + + traderKind = + DefDatabase.GetNamedSilentFail("Orbital_ExoticGoods") ?? + DefDatabase.GetNamedSilentFail("Orbital_BulkGoods") ?? + DefDatabase.AllDefs.FirstOrDefault(); + } + + public TraderKindDef TraderKind => traderKind; + public IEnumerable Goods => Enumerable.Empty(); + public int RandomPriceFactorSeed => 0; + public string TraderName => "WULA_GlobalStorageTransferTitle".Translate(); + public bool CanTradeNow => true; + public float TradePriceImprovementOffsetForPlayer => 0f; + public Faction Faction => Faction.OfPlayer; + public TradeCurrency TradeCurrency => TradeCurrency.Silver; + + public IEnumerable ColonyThingsWillingToBuy(Pawn playerNegotiator) => Enumerable.Empty(); + + public void GiveSoldThingToTrader(Thing toGive, int countToGive, Pawn playerNegotiator) + { + if (storage == null) return; + if (toGive == null || countToGive <= 0) return; + + Thing thing = toGive.SplitOff(countToGive); + thing.PreTraded(TradeAction.PlayerSells, playerNegotiator, this); + + if (ShouldGoToOutputStorage(thing)) + { + storage.AddToOutputStorage(thing.def, thing.stackCount); + } + else + { + storage.AddToInputStorage(thing.def, thing.stackCount); + } + + thing.Destroy(DestroyMode.Vanish); + } + + public void GiveSoldThingToPlayer(Thing toGive, int countToGive, Pawn playerNegotiator) + { + if (storage == null) return; + if (map == null) return; + if (toGive == null || countToGive <= 0) return; + + if (!TryRemoveFromAnyStorage(toGive.def, countToGive)) + { + Log.Warning($"[WULA] Global storage changed while transfer dialog open; could not remove {countToGive}x {toGive.def?.defName}."); + return; + } + + Thing thing = toGive.SplitOff(countToGive); + thing.PreTraded(TradeAction.PlayerBuys, playerNegotiator, this); + + IntVec3 dropSpot = DropCellFinder.TradeDropSpot(map); + TradeUtility.SpawnDropPod(dropSpot, map, thing); + } + + private static bool ShouldGoToOutputStorage(Thing thing) + { + ThingDef def = thing?.def; + if (def == null) return false; + if (def.IsWeapon) return true; + if (def.IsApparel) return true; + return false; + } + + private bool TryRemoveFromAnyStorage(ThingDef def, int count) + { + if (def == null || count <= 0) return false; + + int available = storage.GetInputStorageCount(def) + storage.GetOutputStorageCount(def); + if (available < count) return false; + + int remaining = count; + int fromOutput = Mathf.Min(remaining, storage.GetOutputStorageCount(def)); + if (fromOutput > 0) + { + if (!storage.RemoveFromOutputStorage(def, fromOutput)) return false; + remaining -= fromOutput; + } + + if (remaining > 0) + { + if (!storage.RemoveFromInputStorage(def, remaining)) + { + if (fromOutput > 0) storage.AddToOutputStorage(def, fromOutput); + return false; + } + } + + return true; + } + } + } +} diff --git a/Source/WulaFallenEmpire/GlobalWorkTable/ITab_GlobalBills.cs b/Source/WulaFallenEmpire/GlobalWorkTable/ITab_GlobalBills.cs index be669e51..bebdfacd 100644 --- a/Source/WulaFallenEmpire/GlobalWorkTable/ITab_GlobalBills.cs +++ b/Source/WulaFallenEmpire/GlobalWorkTable/ITab_GlobalBills.cs @@ -193,7 +193,7 @@ namespace WulaFallenEmpire { recipe = recipe, targetCount = 1, - paused = true + paused = false }; SelTable.globalOrderStack.AddOrder(newOrder); SoundDefOf.Click.PlayOneShotOnCamera(); diff --git a/Source/WulaFallenEmpire/GlobalWorkTable/JobDriver_GlobalWorkTable.cs b/Source/WulaFallenEmpire/GlobalWorkTable/JobDriver_GlobalWorkTable.cs index acc604dc..042c96aa 100644 --- a/Source/WulaFallenEmpire/GlobalWorkTable/JobDriver_GlobalWorkTable.cs +++ b/Source/WulaFallenEmpire/GlobalWorkTable/JobDriver_GlobalWorkTable.cs @@ -52,68 +52,19 @@ namespace WulaFallenEmpire private void CheckAndUpload() { var table = Table; - var globalStorage = Find.World.GetComponent(); - + // 找到当前正在进行的订单 var order = table.globalOrderStack.orders.FirstOrDefault(o => o.state == GlobalProductionOrder.ProductionState.Gathering && !o.paused); if (order == null) return; - // 检查是否满足需求 - var costList = order.GetProductCostList(); - bool allSatisfied = true; - - foreach (var kvp in costList) - { - int needed = kvp.Value; - int inCloud = globalStorage.GetInputStorageCount(kvp.Key); - int inContainer = table.innerContainer.TotalStackCountOfDef(kvp.Key); - - if (inCloud + inContainer < needed) - { - allSatisfied = false; - break; - } - } + var beforeState = order.state; + table.TryAutoGatherFromBeaconsAndContainer(); - if (allSatisfied) + if (beforeState == GlobalProductionOrder.ProductionState.Gathering && + order.state == GlobalProductionOrder.ProductionState.Producing) { - // 1. 消耗容器中的材料并上传到云端 - foreach (var kvp in costList) - { - int needed = kvp.Value; - int inCloud = globalStorage.GetInputStorageCount(kvp.Key); - int missingInCloud = needed - inCloud; - - if (missingInCloud > 0) - { - int toTake = missingInCloud; - while (toTake > 0) - { - Thing t = table.innerContainer.FirstOrDefault(x => x.def == kvp.Key); - if (t == null) break; - - int num = UnityEngine.Mathf.Min(t.stackCount, toTake); - t.SplitOff(num).Destroy(); // 销毁实体 - globalStorage.AddToInputStorage(kvp.Key, num); // 添加虚拟库存 - toTake -= num; - } - } - } - - // 2. 立即尝试扣除资源并开始生产 - // 这会从云端扣除刚刚上传的资源,防止其他订单抢占 - if (order.TryDeductResources()) - { - order.state = GlobalProductionOrder.ProductionState.Producing; - order.progress = 0f; - Messages.Message("WULA_OrderStarted".Translate(order.Label), table, MessageTypeDefOf.PositiveEvent); - } - else - { - // 理论上不应该发生,因为前面检查了 allSatisfied - Log.Error($"[WULA] Failed to deduct resources for {order.Label} immediately after upload."); - } + Messages.Message("WULA_OrderStarted".Translate(order.Label), table, MessageTypeDefOf.PositiveEvent); } } } -} \ No newline at end of file +} diff --git a/Source/WulaFallenEmpire/GlobalWorkTable/WorkGiver_GlobalWorkTable.cs b/Source/WulaFallenEmpire/GlobalWorkTable/WorkGiver_GlobalWorkTable.cs index 26ee6e6a..3a8b4d84 100644 --- a/Source/WulaFallenEmpire/GlobalWorkTable/WorkGiver_GlobalWorkTable.cs +++ b/Source/WulaFallenEmpire/GlobalWorkTable/WorkGiver_GlobalWorkTable.cs @@ -41,14 +41,16 @@ namespace WulaFallenEmpire } // 检查是否已经有足够的材料在容器中或云端 - if (order.HasEnoughResources()) + var globalStorage = Find.World.GetComponent(); + var neededMaterials = GetNeededMaterials(order, table, globalStorage); + if (neededMaterials.Count == 0) { if (forced) Log.Message($"[WULA_DEBUG] HasJobOnThing: Order has enough resources."); return false; } // 查找所需材料 - var ingredients = FindBestIngredients(pawn, table, order); + var ingredients = FindBestIngredients(pawn, table, neededMaterials); if (ingredients == null) { if (forced) Log.Message($"[WULA_DEBUG] HasJobOnThing: Could not find ingredients for {order.Label}."); @@ -67,7 +69,11 @@ namespace WulaFallenEmpire if (order == null) return null; - var ingredients = FindBestIngredients(pawn, table, order); + var globalStorage = Find.World.GetComponent(); + var neededMaterials = GetNeededMaterials(order, table, globalStorage); + if (neededMaterials.Count == 0) return null; + + var ingredients = FindBestIngredients(pawn, table, neededMaterials); if (ingredients == null) return null; @@ -77,15 +83,11 @@ namespace WulaFallenEmpire return job; } - private List> FindBestIngredients(Pawn pawn, Building_GlobalWorkTable table, GlobalProductionOrder order) + private List> FindBestIngredients(Pawn pawn, Building_GlobalWorkTable table, Dictionary neededMaterials) { var result = new List>(); - var globalStorage = Find.World.GetComponent(); - - // 获取所需材料清单 - var neededMaterials = GetNeededMaterials(order, table, globalStorage); - - Log.Message($"[WULA_DEBUG] Needed materials for {order.Label}: {string.Join(", ", neededMaterials.Select(k => $"{k.Key.defName} x{k.Value}"))}"); + + Log.Message($"[WULA_DEBUG] Needed materials: {string.Join(", ", neededMaterials.Select(k => $"{k.Key.defName} x{k.Value}"))}"); foreach (var kvp in neededMaterials) { @@ -155,6 +157,9 @@ namespace WulaFallenEmpire int containerCount = table.innerContainer.TotalStackCountOfDef(kvp.Key); remaining -= containerCount; + // 4. 减去轨道贸易信标(已通电)的范围内已有的 + remaining -= CountInPoweredTradeBeaconRange(table.Map, kvp.Key); + if (remaining > 0) { needed[kvp.Key] = remaining; @@ -163,5 +168,28 @@ namespace WulaFallenEmpire return needed; } + + private int CountInPoweredTradeBeaconRange(Map map, ThingDef def) + { + if (map == null || def == null) return 0; + + int count = 0; + foreach (var beacon in Building_OrbitalTradeBeacon.AllPowered(map)) + { + foreach (var cell in beacon.TradeableCells) + { + List things = cell.GetThingList(map); + for (int i = 0; i < things.Count; i++) + { + Thing t = things[i]; + if (t != null && t.def == def) + { + count += t.stackCount; + } + } + } + } + return count; + } } }