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;
+ }
}
}