207 lines
11 KiB
C#
207 lines
11 KiB
C#
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using TMPro;
|
|
using UnityEngine;
|
|
using UnityEngine.EventSystems;
|
|
using UnityEngine.UI;
|
|
|
|
namespace DepthsOfGreed.HandbookUI
|
|
{
|
|
// Presentation prototype. Connection intent is separate from real ammunition accounting.
|
|
public sealed partial class SupplyBasementController : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler
|
|
{
|
|
public bool IsInBasement { get; private set; }
|
|
public bool IsTravelling { get; private set; }
|
|
public int Priority { get; private set; } = -1;
|
|
public event System.Action<long, bool> ConnectionChanged;
|
|
public event System.Action<int> PriorityChanged;
|
|
[Header("Authored scene references")]
|
|
public GameObject basementView;
|
|
public GameObject handbookRoot;
|
|
public CanvasGroup handbookVisibility;
|
|
public HandbookController handbook;
|
|
public TestPlacementController placement;
|
|
public Button liftButton;
|
|
public RectTransform liftSign;
|
|
public TMP_Text liftLabel;
|
|
public CanvasGroup doorGroup;
|
|
public RectTransform leftDoor, rightDoor;
|
|
public Button[] connectionButtons;
|
|
public TMP_Text[] connectionNames, connectionStates;
|
|
public Image[] connectionIcons, cables;
|
|
public Button[] priorityButtons;
|
|
public TMP_Text[] priorityLabels;
|
|
public RectTransform truck;
|
|
public TMP_Text truckStatus;
|
|
[Header("Lift timing")]
|
|
[Min(0.05f)] public float doorDuration = 0.3f;
|
|
[Min(0)] public float travelDuration = 0.35f;
|
|
[Min(0.05f)] public float signHalfFlipDuration = 0.1f;
|
|
[Header("Truck display test (no ammunition is consumed)")]
|
|
public bool animateTruckPreview = true;
|
|
public float truckSpeed = 100f;
|
|
public float[] loadingPositions = { -530f, 0f, 530f };
|
|
public float unloadingPosition = 710f;
|
|
public float loadingPause = 1.2f;
|
|
public Color physicalColor = new Color(.35f,.35f,.35f);
|
|
public Color cellColor = new Color(.48f,.48f,.48f);
|
|
public Color specialColor = new Color(.60f,.60f,.60f);
|
|
private readonly long[] slotIds = new long[8];
|
|
private readonly Dictionary<long, bool> connections = new Dictionary<long, bool>();
|
|
private bool hovered, labelBack;
|
|
private Coroutine flipRoutine;
|
|
private float nextRefresh, truckPauseUntil;
|
|
private int previewKind;
|
|
private bool unloading;
|
|
private Vector3 signScale;
|
|
private Vector3 truckScale;
|
|
private readonly string[] kinds = { "Physisch", "Energiezellen", "Sondermunition" };
|
|
|
|
private void Awake()
|
|
{
|
|
signScale = liftSign.localScale;
|
|
truckScale = truck.localScale;
|
|
basementView.SetActive(false);
|
|
doorGroup.gameObject.SetActive(false);
|
|
liftButton.onClick.AddListener(UseLift);
|
|
for (int i = 0; i < connectionButtons.Length; i++)
|
|
{ int slot = i; connectionButtons[i].onClick.AddListener(() => ToggleConnection(slot)); }
|
|
for (int i = 0; i < priorityButtons.Length; i++)
|
|
{ int kind = i; priorityButtons[i].onClick.AddListener(() => SetPriority(kind)); }
|
|
if (!HasSupplyBackend) SetPriority(-1);
|
|
else if (supplyBackend && !(supplyBackend is ISupplyGameplay)) Debug.LogError("Versorgung: ISupplyGameplay fehlt.", this);
|
|
RefreshSign();
|
|
}
|
|
public void OnPointerEnter(PointerEventData e) { hovered = true; FlipSign(); }
|
|
public void OnPointerExit(PointerEventData e) { hovered = false; FlipSign(); }
|
|
private void FlipSign()
|
|
{
|
|
if (flipRoutine != null) StopCoroutine(flipRoutine);
|
|
flipRoutine = StartCoroutine(Flip());
|
|
}
|
|
private IEnumerator Flip()
|
|
{
|
|
float start = liftSign.localScale.x;
|
|
for (float t = 0; t < signHalfFlipDuration; t += Time.unscaledDeltaTime)
|
|
{ liftSign.localScale = new Vector3(Mathf.Lerp(start, 0.02f, t / signHalfFlipDuration), signScale.y, signScale.z); yield return null; }
|
|
labelBack = hovered; RefreshSign();
|
|
for (float t = 0; t < signHalfFlipDuration; t += Time.unscaledDeltaTime)
|
|
{ liftSign.localScale = new Vector3(Mathf.Lerp(0.02f, signScale.x, t / signHalfFlipDuration), signScale.y, signScale.z); yield return null; }
|
|
liftSign.localScale = signScale; flipRoutine = null;
|
|
}
|
|
private void RefreshSign() => liftLabel.text = labelBack
|
|
? (IsInBasement ? "ZUR\nTURMEBENE" : "ZUM\nVERSORGUNGS-\nKELLER")
|
|
: (IsInBasement ? "▲\nAUFZUG" : "▼\nAUFZUG");
|
|
public void UseLift()
|
|
{
|
|
if (IsTravelling) return;
|
|
if (placement && (placement.IsDragging || placement.IsWaitingForReturn)) return;
|
|
StartCoroutine(Travel());
|
|
}
|
|
private IEnumerator Travel()
|
|
{
|
|
IsTravelling = true; liftButton.interactable = false;
|
|
doorGroup.gameObject.SetActive(true); doorGroup.blocksRaycasts = true;
|
|
yield return Doors(true);
|
|
if (handbook) handbook.Close();
|
|
IsInBasement = !IsInBasement;
|
|
basementView.SetActive(IsInBasement);
|
|
// Hide only the presentation. The backend on HandbookRoot must keep updating.
|
|
handbookVisibility.alpha = IsInBasement ? 0f : 1f;
|
|
handbookVisibility.interactable = !IsInBasement;
|
|
handbookVisibility.blocksRaycasts = !IsInBasement;
|
|
hovered = false; labelBack = false; RefreshSign(); RefreshConnections();
|
|
yield return new WaitForSecondsRealtime(travelDuration);
|
|
yield return Doors(false);
|
|
doorGroup.blocksRaycasts = false; doorGroup.gameObject.SetActive(false);
|
|
liftButton.interactable = true; IsTravelling = false;
|
|
}
|
|
private IEnumerator Doors(bool close)
|
|
{
|
|
float width = ((RectTransform)leftDoor.parent).rect.width * 0.5f;
|
|
for (float t = 0; t < doorDuration; t += Time.unscaledDeltaTime)
|
|
{
|
|
float p = Mathf.SmoothStep(0, 1, t / doorDuration); if (!close) p = 1 - p;
|
|
leftDoor.anchoredPosition = new Vector2(-width * (1 - p), 0);
|
|
rightDoor.anchoredPosition = new Vector2(width * (1 - p), 0);
|
|
yield return null;
|
|
}
|
|
leftDoor.anchoredPosition = new Vector2(close ? 0 : -width, 0);
|
|
rightDoor.anchoredPosition = new Vector2(close ? 0 : width, 0);
|
|
}
|
|
public bool IsConnectionEnabled(long instanceId) => HasSupplyBackend ? FindTower(instanceId)?.connected == true : !connections.TryGetValue(instanceId, out bool enabled) || enabled;
|
|
public void ToggleConnection(int slot)
|
|
{
|
|
if (slot < 0 || slot >= slotIds.Length || slotIds[slot] == 0) return;
|
|
long id = slotIds[slot];
|
|
if (TrySetBackendConnection(id, !IsConnectionEnabled(id))) return;
|
|
connections[id] = !IsConnectionEnabled(id);
|
|
ConnectionChanged?.Invoke(id, connections[id]); RefreshConnections();
|
|
}
|
|
public void SetPriority(int kind)
|
|
{
|
|
if (kind < -1 || kind > 2) return;
|
|
if (TrySetBackendPriority(kind)) return;
|
|
Priority = Priority == kind ? -1 : kind;
|
|
PriorityChanged?.Invoke(Priority);
|
|
for (int i = 0; i < priorityButtons.Length; i++)
|
|
{ priorityButtons[i].GetComponent<Image>().color = Priority == i ? ColorFor(i) : new Color(.72f,.72f,.72f); priorityLabels[i].color = new Color(.18f,.18f,.18f); priorityLabels[i].text = Priority == i ? "Vorrang aktiv" : "Vorrang geben"; }
|
|
}
|
|
private Color ColorFor(int kind) => kind == 0 ? physicalColor : kind == 1 ? cellColor : specialColor;
|
|
private static bool UsesSupply(HandbookTowerEntry t) => t && t.use == BuildUse.Structure && t.supply != null && t.supply.kind != SupplyKind.None;
|
|
private void RefreshConnections()
|
|
{
|
|
var list = GetSupplyObjects();
|
|
for (int i = 0; i < slotIds.Length; i++)
|
|
{
|
|
bool exists = false;
|
|
if (list != null) foreach (var obj in list) if (obj.instanceId == slotIds[i] && UsesSupply(obj.type)) { exists = true; break; }
|
|
if (!exists) { connections.Remove(slotIds[i]); slotIds[i] = 0; }
|
|
}
|
|
if (list != null) foreach (var obj in list)
|
|
{
|
|
if (!UsesSupply(obj.type) || System.Array.IndexOf(slotIds, obj.instanceId) >= 0) continue;
|
|
int free = System.Array.IndexOf(slotIds, 0L); if (free >= 0) slotIds[free] = obj.instanceId;
|
|
}
|
|
for (int i = 0; i < connectionButtons.Length; i++)
|
|
{
|
|
BuiltObjectSnapshot found = null;
|
|
if (list != null) foreach (var obj in list) if (obj.instanceId == slotIds[i]) { found = obj; break; }
|
|
RenderMagazine(i, found == null ? 0 : found.instanceId);
|
|
connectionButtons[i].interactable = found != null;
|
|
connectionIcons[i].gameObject.SetActive(found != null && found.type.icon);
|
|
if (found == null) { connectionNames[i].text = ""; connectionStates[i].text = "Frei"; cables[i].color = new Color(.48f,.48f,.48f); continue; }
|
|
bool enabled = IsConnectionEnabled(found.instanceId);
|
|
int kind = Mathf.Clamp((int)found.type.supply.kind, 0, 2);
|
|
connectionIcons[i].sprite = found.type.icon;
|
|
connectionNames[i].text = found.type.displayName + " #" + found.instanceId;
|
|
connectionStates[i].text = (enabled ? "Verbunden" : "Getrennt") + "\n" + kinds[kind];
|
|
cables[i].color = enabled ? ColorFor(kind) : new Color(.48f,.48f,.48f);
|
|
}
|
|
}
|
|
private void Update()
|
|
{
|
|
if (!IsInBasement) return;
|
|
if (Time.unscaledTime > nextRefresh) { nextRefresh = Time.unscaledTime + 0.25f; RefreshConnections(); }
|
|
if (HasSupplyBackend) { RenderBackendTruck(); return; }
|
|
if (!animateTruckPreview) { truckStatus.text = "LKW bereit"; return; }
|
|
if (Time.unscaledTime < truckPauseUntil) return;
|
|
float destination = unloading ? unloadingPosition : loadingPositions[previewKind];
|
|
var p = truck.anchoredPosition;
|
|
float direction = destination - p.x;
|
|
if (Mathf.Abs(direction) > 0.1f)
|
|
truck.localScale = new Vector3(Mathf.Abs(truckScale.x) * Mathf.Sign(direction), truckScale.y, truckScale.z);
|
|
p.x = Mathf.MoveTowards(p.x, destination, Mathf.Max(0, truckSpeed) * Time.unscaledDeltaTime); truck.anchoredPosition = p;
|
|
truckStatus.text = (unloading ? "Zur Verteilerstation" : "Zum Behälter: " + kinds[previewKind]);
|
|
if (Mathf.Abs(p.x - destination) < 0.1f)
|
|
{
|
|
truckStatus.text = (unloading ? "Abladen" : "Beladen: " + kinds[previewKind]);
|
|
truckPauseUntil = Time.unscaledTime + loadingPause;
|
|
if (unloading) previewKind = Priority >= 0 ? Priority : (previewKind + 1) % 3;
|
|
unloading = !unloading;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|