diff --git a/unity-client/Assets/Scripts/UI/DeckGeneratorController.cs b/unity-client/Assets/Scripts/UI/DeckGeneratorController.cs new file mode 100644 index 0000000..78049ab --- /dev/null +++ b/unity-client/Assets/Scripts/UI/DeckGeneratorController.cs @@ -0,0 +1,271 @@ +using System; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; +using UnityEngine.SceneManagement; +using TMPro; +using Newtonsoft.Json; +using CommanderAILab.Models; +using CommanderAILab.Services; + +namespace CommanderAILab.UI +{ + /// + /// DeckGenerator scene — AI deck generation wizard: + /// commander pick, strategy/budget inputs, precon base, + /// streaming progress, result preview, save to DeckBuilder. + /// + public class DeckGeneratorController : MonoBehaviour + { + // ── Navigation ──────────────────────────────────────────────────── + [Header("Navigation")] + [SerializeField] private Button backButton; + + // ── Step 1: Commander Selection ─────────────────────────────────── + [Header("Step 1 - Commander")] + [SerializeField] private TMP_InputField commanderSearchInput; + [SerializeField] private Button commanderSearchButton; + [SerializeField] private Transform commanderResultParent; + [SerializeField] private GameObject commanderResultRowPrefab; + [SerializeField] private TMP_Text selectedCommanderLabel; + + // ── Step 2: Strategy & Budget ───────────────────────────────────── + [Header("Step 2 - Strategy & Budget")] + [SerializeField] private TMP_Dropdown strategyDropdown; // Aggro/Control/Combo/Midrange/Stax/Chaos + [SerializeField] private TMP_Dropdown budgetDropdown; // Pauper/$50/$100/$250/No limit + [SerializeField] private TMP_InputField synergiesInput; // free-text themes + [SerializeField] private TMP_Dropdown preconBaseDropdown; // optional precon starting point + [SerializeField] private Toggle ownedOnlyToggle; // restrict to owned cards + + // ── Step 3: Generate ────────────────────────────────────────────── + [Header("Step 3 - Generate")] + [SerializeField] private Button generateButton; + [SerializeField] private GameObject progressPanel; + [SerializeField] private Slider progressSlider; + [SerializeField] private TMP_Text progressText; + + // ── Step 4: Result Preview ──────────────────────────────────────── + [Header("Step 4 - Result")] + [SerializeField] private GameObject resultPanel; + [SerializeField] private Transform resultCardListParent; + [SerializeField] private GameObject resultCardRowPrefab; + [SerializeField] private TMP_Text resultSummaryText; + [SerializeField] private TMP_Text resultManaCurveText; + [SerializeField] private Button saveToDeckBuilderButton; + [SerializeField] private Button regenerateButton; + + // ── Error ───────────────────────────────────────────────────────── + [Header("Error")] + [SerializeField] private GameObject errorPanel; + [SerializeField] private TMP_Text errorText; + [SerializeField] private Button retryButton; + + // ── State ───────────────────────────────────────────────────────── + private CardModel _selectedCommander; + private DeckGenResponse _lastResult; + private List _commanderRows = new(); + private List _resultRows = new(); + private List _preconNames = new(); + + // ── Lifecycle ───────────────────────────────────────────────────── + private void Start() + { + backButton.onClick.AddListener(() => SceneManager.LoadScene("MainMenu")); + + commanderSearchButton.onClick.AddListener(OnCommanderSearch); + commanderSearchInput.onSubmit.AddListener(_ => OnCommanderSearch()); + + generateButton.onClick.AddListener(OnGenerate); + saveToDeckBuilderButton.onClick.AddListener(OnSaveToDeckBuilder); + regenerateButton.onClick.AddListener(OnGenerate); + retryButton.onClick.AddListener(OnGenerate); + + progressPanel.SetActive(false); + resultPanel.SetActive(false); + errorPanel.SetActive(false); + + LoadPrecons(); + } + + // ── Precon list ─────────────────────────────────────────────────── + private void LoadPrecons() + { + ApiClient.Instance.GetPrecons( + json => + { + try + { + var wrapper = JsonConvert.DeserializeObject(json); + _preconNames = wrapper?.precons ?? new List(); + } + catch { _preconNames = new List(); } + + var options = new List { new("None (from scratch)") }; + foreach (var p in _preconNames) options.Add(new TMP_Dropdown.OptionData(p)); + preconBaseDropdown.options = options; + preconBaseDropdown.value = 0; + }, + err => Debug.LogWarning($"[DeckGen] Could not load precons: {err}")); + } + + // ── Commander Search ────────────────────────────────────────────── + private void OnCommanderSearch() + { + string q = commanderSearchInput.text.Trim(); + if (string.IsNullOrEmpty(q)) return; + string path = $"/api/collection?search={UnityWebRequest.EscapeURL(q)}&type=Legendary+Creature&per_page=20"; + StartCoroutine(ApiClient.Instance.GetRaw(path, + json => PopulateCommanderResults(json), + err => ShowError($"Commander search failed:\n{err}"))); + } + + private void PopulateCommanderResults(string json) + { + foreach (var go in _commanderRows) if (go) Destroy(go); + _commanderRows.Clear(); + + List cards; + try + { + var resp = JsonConvert.DeserializeObject(json); + cards = resp?.cards ?? new List(); + } + catch { return; } + + foreach (var card in cards) + { + var row = Instantiate(commanderResultRowPrefab, commanderResultParent); + _commanderRows.Add(row); + var label = row.GetComponentInChildren(); + if (label) label.text = $"{card.name} — {card.manaCost}"; + var btn = row.GetComponentInChildren