Files
WulaFallenEmpireRW/Source/WulaFallenEmpire/GlobalWorkTable/Dialog_GlobalStorageTransfer.cs
2025-12-14 13:10:25 +08:00

449 lines
16 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 GlobalStorageWorldComponent storage;
private readonly QuickSearchWidget quickSearchWidget = new QuickSearchWidget();
private Vector2 scrollPosition;
private float viewHeight;
private List<Tradeable> tradeables = new List<Tradeable>();
public override Vector2 InitialSize => new Vector2(1024f, UI.screenHeight);
public Dialog_GlobalStorageTransfer(Building_GlobalWorkTable table, Pawn negotiator)
{
this.table = table;
storage = Find.World.GetComponent<GlobalStorageWorldComponent>();
doCloseX = true;
closeOnAccept = false;
closeOnClickedOutside = true;
absorbInputAroundWindow = true;
}
public override void PostOpen()
{
base.PostOpen();
RebuildTradeables();
}
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);
float curY = 0f;
int drawnIndex = 0;
Widgets.BeginScrollView(outRect, ref scrollPosition, viewRect);
try
{
for (int i = 0; i < tradeables.Count; i++)
{
Tradeable trad = tradeables[i];
if (trad == null) continue;
PruneTradeableThingLists(trad);
if (!TryGetAnyThing(trad, out Thing anyThing)) continue;
ThingDef def = anyThing?.def;
if (def == null) continue;
if (!quickSearchWidget.filter.Matches(def))
continue;
Rect rowRect = new Rect(0f, curY, viewRect.width, RowHeight);
DrawStorageTransferRow(rowRect, trad, drawnIndex);
curY += RowHeight;
drawnIndex++;
}
if (Event.current.type == EventType.Layout)
{
viewHeight = Mathf.Max(curY, outRect.height);
}
}
finally
{
GenUI.ResetLabelAlign();
Widgets.EndScrollView();
}
}
private static bool TryGetAnyThing(Tradeable trad, out Thing anyThing)
{
anyThing = null;
if (trad == null) return false;
if (TryGetAnyThingFromList(trad.thingsColony, out anyThing)) return true;
if (TryGetAnyThingFromList(trad.thingsTrader, out anyThing)) return true;
return false;
}
private static bool TryGetAnyThingFromList(List<Thing> things, out Thing anyThing)
{
anyThing = null;
if (things == null || things.Count == 0) return false;
for (int i = 0; i < things.Count; i++)
{
Thing t = things[i];
if (t == null || t.Destroyed) continue;
anyThing = t.GetInnerIfMinified();
if (anyThing != null && !anyThing.Destroyed) return true;
}
anyThing = null;
return false;
}
private static void DrawStorageTransferRow(Rect rect, Tradeable trad, int index)
{
if (index % 2 == 1)
{
Widgets.DrawLightHighlight(rect);
}
Text.Font = GameFont.Small;
Widgets.BeginGroup(rect);
try
{
float width = rect.width;
int globalCount = SafeCountHeldBy(trad, Transactor.Trader);
if (globalCount != 0 && trad.IsThing)
{
Rect countRect = new Rect(width - TradeUI.CountColumnWidth, 0f, TradeUI.CountColumnWidth, rect.height);
Widgets.DrawHighlightIfMouseover(countRect);
Text.Anchor = TextAnchor.MiddleRight;
Rect labelRect = countRect.ContractedBy(5f, 0f);
Widgets.Label(labelRect, globalCount.ToStringCached());
TooltipHandler.TipRegionByKey(countRect, "TraderCount");
}
width -= TradeUI.CountColumnWidth + TradeUI.PriceColumnWidth;
Rect adjustRect = new Rect(width - TradeUI.AdjustColumnWidth, 0f, TradeUI.AdjustColumnWidth, rect.height);
int min = -SafeCountHeldBy(trad, Transactor.Colony);
int max = SafeCountHeldBy(trad, Transactor.Trader);
TransferableUIUtility.DoCountAdjustInterface(adjustRect, trad, index, min, max, flash: false);
width -= TradeUI.AdjustColumnWidth;
int beaconCount = SafeCountHeldBy(trad, Transactor.Colony);
if (beaconCount != 0)
{
Rect countRect = new Rect(width - TradeUI.CountColumnWidth, 0f, TradeUI.CountColumnWidth, rect.height);
Widgets.DrawHighlightIfMouseover(countRect);
Text.Anchor = TextAnchor.MiddleLeft;
Rect labelRect = countRect.ContractedBy(5f, 0f);
Widgets.Label(labelRect, beaconCount.ToStringCached());
TooltipHandler.TipRegionByKey(countRect, "ColonyCount");
}
width -= TradeUI.CountColumnWidth + TradeUI.PriceColumnWidth;
Rect infoRect = new Rect(0f, 0f, width, rect.height);
TransferableUIUtility.DrawTransferableInfo(trad, infoRect, Color.white);
}
finally
{
GenUI.ResetLabelAlign();
Widgets.EndGroup();
}
}
private static int SafeCountHeldBy(Tradeable trad, Transactor transactor)
{
if (trad == null) return 0;
List<Thing> list = (transactor == Transactor.Colony) ? trad.thingsColony : trad.thingsTrader;
if (list == null || list.Count == 0) return 0;
int count = 0;
for (int i = 0; i < list.Count; i++)
{
Thing t = list[i];
if (t == null || t.Destroyed) continue;
count += t.stackCount;
}
return count;
}
private static void PruneTradeableThingLists(Tradeable trad)
{
if (trad == null) return;
trad.thingsColony?.RemoveAll(t => t == null || t.Destroyed);
trad.thingsTrader?.RemoveAll(t => t == null || t.Destroyed);
}
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()
{
if (storage == null || table?.Map == null)
return;
bool changed = false;
Map map = table.Map;
IntVec3 dropSpot = DropCellFinder.TradeDropSpot(map);
for (int i = 0; i < tradeables.Count; i++)
{
Tradeable trad = tradeables[i];
if (trad == null) continue;
if (trad.CountToTransfer == 0) continue;
PruneTradeableThingLists(trad);
if (!trad.HasAnyThing) continue;
int storeCount = trad.CountToTransferToDestination; // 信标 -> 全局CountToTransfer<0
int takeCount = trad.CountToTransferToSource; // 全局 -> 信标CountToTransfer>0
if (storeCount > 0)
{
changed |= TransferToGlobalStorage(trad, storeCount);
}
else if (takeCount > 0)
{
changed |= TransferFromGlobalStorage(trad, takeCount, map, dropSpot);
}
trad.ForceTo(0);
}
if (!changed)
{
SoundDefOf.Tick_Low.PlayOneShotOnCamera();
return;
}
SoundDefOf.ExecuteTrade.PlayOneShotOnCamera();
RebuildTradeables();
}
private bool TransferToGlobalStorage(Tradeable trad, int count)
{
if (trad == null || count <= 0 || storage == null) return false;
bool changed = false;
TransferableUtility.TransferNoSplit(trad.thingsColony, count, (Thing thing, int countToTransfer) =>
{
if (thing == null || thing.Destroyed || countToTransfer <= 0) return;
Thing split = thing.SplitOff(countToTransfer);
if (split == null) return;
if (ShouldGoToOutputStorage(split))
{
storage.AddToOutputStorage(split);
}
else
{
storage.AddToInputStorage(split);
}
changed = true;
});
return changed;
}
private bool TransferFromGlobalStorage(Tradeable trad, int count, Map map, IntVec3 dropSpot)
{
if (trad == null || count <= 0 || storage == null || map == null) return false;
bool changed = false;
TransferableUtility.TransferNoSplit(trad.thingsTrader, count, (Thing thing, int countToTransfer) =>
{
if (thing == null || thing.Destroyed || countToTransfer <= 0) return;
Thing split = thing.SplitOff(countToTransfer);
if (split == null) return;
if (split.holdingOwner != null)
{
split.holdingOwner.Remove(split);
}
if (split.Spawned)
{
split.DeSpawn();
}
TradeUtility.SpawnDropPod(dropSpot, map, split);
changed = true;
});
return changed;
}
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 void RebuildTradeables()
{
tradeables.Clear();
void AddThingToTradeables(Thing thing, Transactor transactor)
{
if (thing == null) return;
Tradeable trad = TransferableUtility.TradeableMatching(thing, tradeables);
if (trad == null)
{
trad = thing is Pawn ? new Tradeable_StorageTransferPawn() : new Tradeable_StorageTransfer();
tradeables.Add(trad);
}
trad.AddThing(thing, transactor);
}
foreach (Thing t in GetThingsInPoweredTradeBeaconRange(table.Map))
{
if (t?.def == null) continue;
if (t is Corpse) continue;
if (t.def.category != ThingCategory.Item) continue;
AddThingToTradeables(t, Transactor.Colony);
}
if (storage?.inputContainer != null)
{
foreach (Thing t in storage.inputContainer)
{
if (t == null) continue;
AddThingToTradeables(t, Transactor.Trader);
}
}
if (storage?.outputContainer != null)
{
foreach (Thing t in storage.outputContainer)
{
if (t == null) continue;
AddThingToTradeables(t, Transactor.Trader);
}
}
tradeables = tradeables
.Where(t => t != null && TryGetAnyThing(t, out _))
.OrderBy(t =>
{
TryGetAnyThing(t, out Thing anyThing);
return anyThing?.def?.label ?? "";
})
.ToList();
}
private static IEnumerable<Thing> GetThingsInPoweredTradeBeaconRange(Map map)
{
if (map == null) yield break;
HashSet<Thing> yielded = new HashSet<Thing>();
foreach (var beacon in Building_OrbitalTradeBeacon.AllPowered(map))
{
foreach (var cell in beacon.TradeableCells)
{
List<Thing> 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 class Tradeable_StorageTransfer : Tradeable
{
public override bool TraderWillTrade => true;
public override bool IsCurrency => false;
public override bool Interactive => true;
public override TransferablePositiveCountDirection PositiveCountDirection => TransferablePositiveCountDirection.Source;
}
private class Tradeable_StorageTransferPawn : Tradeable_Pawn
{
public override bool TraderWillTrade => true;
public override bool IsCurrency => false;
public override bool Interactive => true;
public override TransferablePositiveCountDirection PositiveCountDirection => TransferablePositiveCountDirection.Source;
}
}
}