Handbuch Scroll-Listen entfernt. Handbuch offen und tabs eingefügt. Upgrades eingefügt. "Garage" eingefügt. Anbindung eines echten Gameplaymanager möglich (theoretisch)

This commit is contained in:
2026-10-15 03:24:48 +02:00
parent dadd1fcad0
commit 5486444dde
132 changed files with 55540 additions and 4807 deletions
@@ -1,150 +1,69 @@
using System.Collections.Generic;
using System.Text;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
namespace DepthsOfGreed.HandbookUI
{
public sealed class HandbookIndexController : MonoBehaviour
{
[SerializeField] private Button[] filterButtons, entryButtons;
[SerializeField] private TMP_Text[] entryNames;
[SerializeField] private Image[] entryIcons;
[SerializeField] private TMP_Text detailName, description, emptyMessage;
[SerializeField] private Image detailIcon;
[SerializeField] private Sprite placeholderIcon;
private sealed class Item { public HandbookTowerEntry tower; public HandbookEnemyEntry enemy; public string Name => tower ? tower.displayName : enemy.displayName; public Sprite Icon => tower ? tower.icon : enemy.icon; }
private readonly List<Item> all = new List<Item>(), filtered = new List<Item>();
public HandbookTowerEntry SelectedEntry { get; private set; }
public int VisibleEntryCount => currentFilter == 2 ? 0 : entries.Count;
public ScrollRect ListScroll => scroll;
private ScrollRect scroll;
private Button[] filters;
private readonly List<HandbookTowerEntry> entries = new List<HandbookTowerEntry>();
private readonly List<Button> rows = new List<Button>();
private TMP_Text detailName, description, emptyMessage;
private Image detailIcon;
private Sprite placeholder;
private Color placeholderColor;
private int currentFilter;
public HandbookEnemyEntry SelectedEnemy { get; private set; }
public int VisibleEntryCount => filtered.Count;
private int filter = -1;
public void Initialize(HandbookTowerCatalog catalog)
{
scroll = transform.Find("IndexLeft/EntryScrollView").GetComponent<ScrollRect>();
filters = new[] {
transform.Find("IndexLeft/FilterBar/Filter_All").GetComponent<Button>(),
transform.Find("IndexLeft/FilterBar/Filter_Towers").GetComponent<Button>(),
transform.Find("IndexLeft/FilterBar/Filter_Enemies").GetComponent<Button>()
};
detailName = transform.Find("IndexRight/DetailNameText").GetComponent<TMP_Text>();
description = transform.Find("IndexRight/DetailDescriptionText").GetComponent<TMP_Text>();
detailIcon = transform.Find("IndexRight/DetailIcon").GetComponent<Image>();
placeholder = detailIcon.sprite;
placeholderColor = detailIcon.color;
var template = scroll.content.GetChild(0).GetComponent<Button>();
if (catalog && catalog.towers != null)
foreach (var entry in catalog.towers)
{
if (!entry) continue;
int index = entries.Count;
entries.Add(entry);
var row = Instantiate(template, scroll.content);
row.name = "IndexEntry_" + entry.id;
row.gameObject.SetActive(true);
row.image.color = Color.white;
var label = row.transform.Find("NameText").GetComponent<TMP_Text>();
label.text = entry.displayName;
label.color = Color.black;
label.raycastTarget = false;
label.enableAutoSizing = true;
label.fontSizeMin = 16;
label.fontSizeMax = 24;
var icon = row.transform.Find("Icon").GetComponent<Image>();
if (entry.icon) { icon.sprite = entry.icon; icon.color = Color.white; }
icon.preserveAspect = true;
icon.raycastTarget = false;
row.onClick.RemoveAllListeners();
row.onClick.AddListener(() => SelectEntry(index));
rows.Add(row);
}
template.gameObject.SetActive(false);
var empty = new GameObject("EmptyListMessage", typeof(RectTransform), typeof(TextMeshProUGUI), typeof(LayoutElement));
empty.transform.SetParent(scroll.content, false);
emptyMessage = empty.GetComponent<TMP_Text>();
emptyMessage.font = description.font;
emptyMessage.fontSize = 22;
emptyMessage.color = new Color(0.3f, 0.3f, 0.3f);
emptyMessage.alignment = TextAlignmentOptions.MidlineLeft;
emptyMessage.raycastTarget = false;
empty.GetComponent<LayoutElement>().preferredHeight = 70;
for (int i = 0; i < filters.Length; i++)
{
int index = i;
filters[i].onClick.AddListener(() => SelectFilter(index));
}
if (catalog.towers != null) foreach (var t in catalog.towers) if (t) all.Add(new Item { tower = t });
if (catalog.enemies != null) foreach (var e in catalog.enemies) if (e) all.Add(new Item { enemy = e });
for (int i = 0; i < filterButtons.Length; i++) { int n = i; filterButtons[i].onClick.AddListener(() => SelectFilter(n)); }
for (int i = 0; i < entryButtons.Length; i++) { int n = i; entryButtons[i].onClick.AddListener(() => SelectEntry(n)); }
ResetForEntry();
}
public void ResetForEntry()
public void ResetForEntry() { SelectFilter(0, true); }
public void SelectFilter(int value, bool force = false)
{
SelectFilter(0, true);
if (value < 0 || value > 4 || (!force && value == filter)) return;
filter = value; filtered.Clear();
foreach (var item in all)
if (value == 0 || (value == 1 && item.tower && (int)item.tower.menuGroup < 2) || (value == 2 && item.tower && item.tower.menuGroup == BuildMenuGroup.Anlagen) || (value == 3 && item.tower && item.tower.menuGroup == BuildMenuGroup.Einsaetze) || (value == 4 && item.enemy)) filtered.Add(item);
ClearSelection(); Render();
for (int i = 0; i < filterButtons.Length; i++) { filterButtons[i].image.color = Color.white; var c = filterButtons[i].colors; c.normalColor = i == value ? new Color(1, 0.76f, 0.35f) : new Color(0.87f, 0.84f, 0.75f); c.selectedColor = c.normalColor; filterButtons[i].colors = c; }
}
public void SelectFilter(int index, bool force = false)
private void Render()
{
if (index < 0 || index > 2 || (!force && currentFilter == index)) return;
currentFilter = index;
foreach (var row in rows) row.gameObject.SetActive(index != 2);
bool empty = index == 2 || rows.Count == 0;
emptyMessage.gameObject.SetActive(empty);
emptyMessage.text = index == 2 ? "Noch keine Gegner-Einträge" : "Noch keine Einträge";
for (int i = 0; i < filters.Length; i++)
if (filtered.Count > entryButtons.Length) Debug.LogError("Index: Anzahl vorbereiteter Auswahlfelder erhöhen.", this);
for (int i = 0; i < entryButtons.Length; i++)
{
filters[i].image.color = Color.white;
var colors = filters[i].colors;
colors.normalColor = i == index ? new Color(1, 0.72f, 0.2f) : new Color(0.82f, 0.82f, 0.82f);
colors.highlightedColor = i == index ? new Color(1, 0.82f, 0.4f) : Color.white;
colors.selectedColor = colors.normalColor;
filters[i].colors = colors;
entryButtons[i].gameObject.SetActive(i < filtered.Count);
if (i >= filtered.Count) continue;
var item = filtered[i]; entryNames[i].text = item.Name; entryIcons[i].sprite = item.Icon ? item.Icon : placeholderIcon; entryIcons[i].color = item.Icon ? Color.white : Color.gray;
bool active = (item.tower && item.tower == SelectedEntry) || (item.enemy && item.enemy == SelectedEnemy);
entryButtons[i].image.color = active ? new Color(0.58f, 0.58f, 0.58f) : new Color(0.9f, 0.88f, 0.81f);
}
ClearSelection();
ResetScroll();
emptyMessage.gameObject.SetActive(filtered.Count == 0);
}
public void SelectEntry(int index)
{
if (currentFilter == 2 || index < 0 || index >= entries.Count) return;
SelectedEntry = entries[index];
detailName.text = SelectedEntry.displayName;
description.text = SelectedEntry.description;
detailIcon.gameObject.SetActive(true);
detailIcon.sprite = SelectedEntry.icon ? SelectedEntry.icon : placeholder;
detailIcon.color = SelectedEntry.icon ? Color.white : placeholderColor;
detailIcon.preserveAspect = true;
RefreshSelectionColors();
}
public void ClearSelection()
{
SelectedEntry = null;
detailIcon.gameObject.SetActive(false);
detailName.text = "";
description.text = "Wähle links einen Eintrag aus.";
RefreshSelectionColors();
}
private void RefreshSelectionColors()
{
for (int i = 0; i < rows.Count; i++)
if (index < 0 || index >= filtered.Count) return;
var item = filtered[index]; SelectedEntry = item.tower; SelectedEnemy = item.enemy;
detailName.text = item.Name; detailIcon.gameObject.SetActive(true); detailIcon.sprite = item.Icon ? item.Icon : placeholderIcon; detailIcon.color = item.Icon ? Color.white : Color.gray;
var text = new StringBuilder(item.tower ? item.tower.description : item.enemy.description);
if (item.tower)
{
bool selected = entries[i] == SelectedEntry;
var colors = rows[i].colors;
colors.normalColor = selected ? new Color(0.58f, 0.58f, 0.58f) : new Color(0.86f, 0.86f, 0.86f);
colors.highlightedColor = selected ? new Color(0.64f, 0.64f, 0.64f) : new Color(0.94f, 0.94f, 0.94f);
colors.selectedColor = colors.normalColor;
rows[i].colors = colors;
var t = item.tower; text.Append("\n\nBaufläche: ").Append(t.region == BuildRegion.WallRing ? "Mauer" : t.region == BuildRegion.Interior ? "Innenfeld" : "Schleuse"); string price = HandbookCostText.Format(t.energyCost, t.entrailsCost); if (price.Length > 0) text.Append("\nKosten: ").Append(price);
if (t.deploymentCooldown > 0) text.Append(" · Cooldown: ").Append(t.deploymentCooldown).Append(" s");
if (t.upgradeable) { text.Append("\n\nVerbesserungen:"); foreach (var u in t.upgrades) text.Append("\n").Append(u.title).Append(": ").Append(u.description).Append(HandbookCostText.Format(u.energyCost, u.entrailsCost) is string upgradePrice && upgradePrice.Length > 0 ? " (" + upgradePrice + ")" : ""); }
}
description.text = text.ToString(); Render();
}
public void ResetScroll()
{
scroll.StopMovement();
Canvas.ForceUpdateCanvases();
LayoutRebuilder.ForceRebuildLayoutImmediate(scroll.content);
scroll.verticalNormalizedPosition = 1f;
}
public void ClearSelection() { SelectedEntry = null; SelectedEnemy = null; detailIcon.gameObject.SetActive(false); detailName.text = ""; description.text = "Wähle links einen Eintrag aus."; }
}
}