Fix all Unity C# errors: 4 truncated Editor scripts + 11 controller/model compilation fixes

This commit is contained in:
KoalaTrapLord committed 2026-03-19 05:53:04 +00:00
1 parent 6b50c6ee7d
commit cad2f00ec7
5 files changed
+820 -283

No files matched your search

@@ -159,4 +159,187 @@ public static class CreateCollectionScene
SetAnchors(errorPanel.GetComponent<RectTransform>(), new Vector2(0.2f, 0.02f), new Vector2(0.8f, 0.10f));
var errorText = CreateTMPText(errorPanel.transform, "ErrorText", "", 16, FontStyles.Normal, TextAlignmentOptions.Center);
SetAnchors(errorText.GetComponent<RectTransform>(), new Vector2(0f, 0.2f), new Vector2(0.75f, 1f));
var retryBtn = CreateTMPButton(errorPanel.transform, "RetryButton", "Retry", new Color(0.6f
var retryBtn = CreateTMPButton(errorPanel.transform, "RetryButton", "Retry", new Color(0.6f, 0.25f, 0.25f));
SetAnchors(retryBtn.GetComponent<RectTransform>(), new Vector2(0.78f, 0.2f), new Vector2(0.98f, 0.8f));
errorPanel.SetActive(false);
// -- Wire controller --
var ctrl = canvasGo.AddComponent<CommanderAILab.UI.CollectionController>();
SetField(ctrl, "backButton", backBtn.GetComponent<Button>());
SetField(ctrl, "searchInput", searchInput.GetComponent<TMP_InputField>());
SetField(ctrl, "sortDropdown", sortDd.GetComponent<TMP_Dropdown>());
SetField(ctrl, "searchButton", searchBtn.GetComponent<Button>());
SetField(ctrl, "filterToggleButton", filterToggle.GetComponent<Button>());
SetField(ctrl, "filterPanel", filterPanel);
SetField(ctrl, "colorFilterDropdown", colorDd.GetComponent<TMP_Dropdown>());
SetField(ctrl, "typeFilterDropdown", typeDd.GetComponent<TMP_Dropdown>());
SetField(ctrl, "setFilterDropdown", setDd.GetComponent<TMP_Dropdown>());
SetField(ctrl, "rarityFilterDropdown", rarityDd.GetComponent<TMP_Dropdown>());
SetField(ctrl, "ownedFilterDropdown", ownedDd.GetComponent<TMP_Dropdown>());
SetField(ctrl, "applyFilterButton", applyBtn.GetComponent<Button>());
SetField(ctrl, "clearFilterButton", clearBtn.GetComponent<Button>());
SetField(ctrl, "cardGridParent", gridContent.transform);
SetField(ctrl, "cardPrefab", null); // assign in Inspector
SetField(ctrl, "gridScrollRect", gridScroll.GetComponent<ScrollRect>());
SetField(ctrl, "loadMoreButton", loadMoreBtn.GetComponent<Button>());
SetField(ctrl, "pageInfoText", pageInfo.GetComponent<TMP_Text>());
SetField(ctrl, "loadingSpinner", spinner);
SetField(ctrl, "detailPanel", detailPanel);
SetField(ctrl, "detailCardFront", detailFront.GetComponent<Image>());
SetField(ctrl, "detailCardBack", detailBack.GetComponent<Image>());
SetField(ctrl, "detailCardName", detailName.GetComponent<TMP_Text>());
SetField(ctrl, "detailManaCost", detailMana.GetComponent<TMP_Text>());
SetField(ctrl, "detailType", detailType.GetComponent<TMP_Text>());
SetField(ctrl, "detailRarity", detailRarity.GetComponent<TMP_Text>());
SetField(ctrl, "detailSet", detailSet.GetComponent<TMP_Text>());
SetField(ctrl, "detailOracleText", detailOracle.GetComponent<TMP_Text>());
SetField(ctrl, "detailQuantity", detailQty.GetComponent<TMP_Text>());
SetField(ctrl, "detailAddToDeckButton", addToDeckBtn.GetComponent<Button>());
SetField(ctrl, "detailCloseButton", closeDetailBtn.GetComponent<Button>());
SetField(ctrl, "errorPanel", errorPanel);
SetField(ctrl, "errorText", errorText.GetComponent<TMP_Text>());
SetField(ctrl, "retryButton", retryBtn.GetComponent<Button>());
EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());
Debug.Log("[CreateCollectionScene] Collection scene created.");
}
// -- Helpers --
static void SetField(object target, string fieldName, object value)
{
var field = target.GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
if (field != null) field.SetValue(target, value);
else Debug.LogWarning("Could not find field: " + fieldName);
}
static void SetAnchors(RectTransform rt, Vector2 min, Vector2 max)
{
rt.anchorMin = min;
rt.anchorMax = max;
rt.offsetMin = Vector2.zero;
rt.offsetMax = Vector2.zero;
}
static GameObject CreatePanel(Transform parent, string name, Color color, bool stretch = false)
{
var go = new GameObject(name);
go.transform.SetParent(parent, false);
var img = go.AddComponent<Image>();
img.color = color;
var rt = go.GetComponent<RectTransform>();
if (stretch) SetAnchors(rt, Vector2.zero, Vector2.one);
return go;
}
static GameObject CreateTMPText(Transform parent, string name, string text, int fontSize, FontStyles style, TextAlignmentOptions alignment)
{
var go = new GameObject(name);
go.transform.SetParent(parent, false);
var tmp = go.AddComponent<TextMeshProUGUI>();
tmp.text = text;
tmp.fontSize = fontSize;
tmp.fontStyle = style;
tmp.alignment = alignment;
tmp.color = Color.white;
var rt = go.GetComponent<RectTransform>();
SetAnchors(rt, Vector2.zero, Vector2.one);
return go;
}
static GameObject CreateTMPButton(Transform parent, string name, string label, Color bgColor)
{
var go = new GameObject(name);
go.transform.SetParent(parent, false);
var img = go.AddComponent<Image>();
img.color = bgColor;
var btn = go.AddComponent<Button>();
btn.targetGraphic = img;
var colors = btn.colors;
colors.highlightedColor = bgColor * 1.2f;
colors.pressedColor = bgColor * 0.8f;
btn.colors = colors;
var textGo = new GameObject("Text");
textGo.transform.SetParent(go.transform, false);
var tmp = textGo.AddComponent<TextMeshProUGUI>();
tmp.text = label;
tmp.fontSize = 22;
tmp.alignment = TextAlignmentOptions.Center;
tmp.color = Color.white;
SetAnchors(textGo.GetComponent<RectTransform>(), Vector2.zero, Vector2.one);
var le = go.AddComponent<LayoutElement>();
le.minHeight = 50;
return go;
}
static GameObject CreateTMPDropdown(Transform parent, string name, string[] options)
{
var go = new GameObject(name);
go.transform.SetParent(parent, false);
var img = go.AddComponent<Image>();
img.color = new Color(0.15f, 0.15f, 0.2f);
var labelGo = new GameObject("Label");
labelGo.transform.SetParent(go.transform, false);
var labelTmp = labelGo.AddComponent<TextMeshProUGUI>();
labelTmp.text = options.Length > 0 ? options[0] : "";
labelTmp.fontSize = 16;
labelTmp.alignment = TextAlignmentOptions.Left;
labelTmp.color = Color.white;
SetAnchors(labelGo.GetComponent<RectTransform>(), Vector2.zero, Vector2.one);
var dd = go.AddComponent<TMP_Dropdown>();
dd.captionText = labelTmp;
dd.ClearOptions();
dd.AddOptions(new System.Collections.Generic.List<string>(options));
var le = go.AddComponent<LayoutElement>();
le.minHeight = 40;
return go;
}
static GameObject CreateTMPInputField(Transform parent, string name, string defaultText)
{
var go = new GameObject(name);
go.transform.SetParent(parent, false);
var img = go.AddComponent<Image>();
img.color = new Color(0.15f, 0.15f, 0.2f);
var textArea = new GameObject("Text Area");
textArea.transform.SetParent(go.transform, false);
textArea.AddComponent<RectMask2D>();
SetAnchors(textArea.GetComponent<RectTransform>(), Vector2.zero, Vector2.one);
var placeholder = new GameObject("Placeholder");
placeholder.transform.SetParent(textArea.transform, false);
var phTmp = placeholder.AddComponent<TextMeshProUGUI>();
phTmp.text = defaultText;
phTmp.fontSize = 18;
phTmp.fontStyle = FontStyles.Italic;
phTmp.color = new Color(0.5f, 0.5f, 0.5f, 0.8f);
phTmp.alignment = TextAlignmentOptions.Left;
SetAnchors(placeholder.GetComponent<RectTransform>(), Vector2.zero, Vector2.one);
var inputText = new GameObject("Text");
inputText.transform.SetParent(textArea.transform, false);
var itTmp = inputText.AddComponent<TextMeshProUGUI>();
itTmp.fontSize = 18;
itTmp.color = Color.white;
itTmp.alignment = TextAlignmentOptions.Left;
SetAnchors(inputText.GetComponent<RectTransform>(), Vector2.zero, Vector2.one);
var inputField = go.AddComponent<TMP_InputField>();
inputField.textViewport = textArea.GetComponent<RectTransform>();
inputField.textComponent = itTmp;
inputField.placeholder = phTmp;
inputField.text = defaultText;
inputField.fontAsset = itTmp.font;
return go;
}
}
#endif
@@ -147,4 +147,163 @@ public static class CreateDeckBuilderScene
SetAnchors(colorPieParent.GetComponent<RectTransform>(), new Vector2(0.02f, 0.13f), new Vector2(0.98f, 0.27f));
var pieHlg = colorPieParent.AddComponent<HorizontalLayoutGroup>();
pieHlg.spacing = 6; pieHlg.childForceExpandWidth = true; pieHlg.childForceExpandHeight = false;
Color[] pieColors = { Color.white, new Color(0.3f,0.6f,1f), new Color(0.1f,0.
Color[] pieColors = { Color.white, new Color(0.3f,0.6f,1f), new Color(0.1f,0.1f,0.1f), new Color(0.8f,0.2f,0.2f), new Color(0.2f,0.7f,0.2f) };
string[] pieLabels = { "W", "U", "B", "R", "G" };
for (int i = 0; i < pieColors.Length; i++)
{
var slice = new GameObject("Slice_" + pieLabels[i]);
slice.transform.SetParent(colorPieParent.transform, false);
var sliceImg = slice.AddComponent<Image>();
sliceImg.color = pieColors[i];
var sliceText = CreateTMPText(slice.transform, "Label", pieLabels[i], 14, FontStyles.Bold, TextAlignmentOptions.Center);
}
// -- Card type breakdown --
var typeLabel = CreateTMPText(rightPanel.transform, "TypeBreakdownLabel", "Card Types", 16, FontStyles.Bold, TextAlignmentOptions.Center);
SetAnchors(typeLabel.GetComponent<RectTransform>(), new Vector2(0f, 0.08f), new Vector2(1f, 0.13f));
var typeText = CreateTMPText(rightPanel.transform, "TypeBreakdownText", "", 14, FontStyles.Normal, TextAlignmentOptions.TopLeft);
SetAnchors(typeText.GetComponent<RectTransform>(), new Vector2(0.05f, 0.01f), new Vector2(0.95f, 0.08f));
EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());
Debug.Log("[CreateDeckBuilderScene] DeckBuilder scene created.");
}
// -- Helpers --
static void SetField(object target, string fieldName, object value)
{
var field = target.GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
if (field != null) field.SetValue(target, value);
else Debug.LogWarning("Could not find field: " + fieldName);
}
static void SetAnchors(RectTransform rt, Vector2 min, Vector2 max)
{
rt.anchorMin = min;
rt.anchorMax = max;
rt.offsetMin = Vector2.zero;
rt.offsetMax = Vector2.zero;
}
static GameObject CreatePanel(Transform parent, string name, Color color, bool stretch = false)
{
var go = new GameObject(name);
go.transform.SetParent(parent, false);
var img = go.AddComponent<Image>();
img.color = color;
var rt = go.GetComponent<RectTransform>();
if (stretch) SetAnchors(rt, Vector2.zero, Vector2.one);
return go;
}
static GameObject CreateTMPText(Transform parent, string name, string text, int fontSize, FontStyles style, TextAlignmentOptions alignment)
{
var go = new GameObject(name);
go.transform.SetParent(parent, false);
var tmp = go.AddComponent<TextMeshProUGUI>();
tmp.text = text;
tmp.fontSize = fontSize;
tmp.fontStyle = style;
tmp.alignment = alignment;
tmp.color = Color.white;
var rt = go.GetComponent<RectTransform>();
SetAnchors(rt, Vector2.zero, Vector2.one);
return go;
}
static GameObject CreateTMPButton(Transform parent, string name, string label, Color bgColor)
{
var go = new GameObject(name);
go.transform.SetParent(parent, false);
var img = go.AddComponent<Image>();
img.color = bgColor;
var btn = go.AddComponent<Button>();
btn.targetGraphic = img;
var colors = btn.colors;
colors.highlightedColor = bgColor * 1.2f;
colors.pressedColor = bgColor * 0.8f;
btn.colors = colors;
var textGo = new GameObject("Text");
textGo.transform.SetParent(go.transform, false);
var tmp = textGo.AddComponent<TextMeshProUGUI>();
tmp.text = label;
tmp.fontSize = 22;
tmp.alignment = TextAlignmentOptions.Center;
tmp.color = Color.white;
SetAnchors(textGo.GetComponent<RectTransform>(), Vector2.zero, Vector2.one);
var le = go.AddComponent<LayoutElement>();
le.minHeight = 50;
return go;
}
static GameObject CreateTMPDropdown(Transform parent, string name, string[] options)
{
var go = new GameObject(name);
go.transform.SetParent(parent, false);
var img = go.AddComponent<Image>();
img.color = new Color(0.15f, 0.15f, 0.2f);
var labelGo = new GameObject("Label");
labelGo.transform.SetParent(go.transform, false);
var labelTmp = labelGo.AddComponent<TextMeshProUGUI>();
labelTmp.text = options.Length > 0 ? options[0] : "";
labelTmp.fontSize = 16;
labelTmp.alignment = TextAlignmentOptions.Left;
labelTmp.color = Color.white;
SetAnchors(labelGo.GetComponent<RectTransform>(), Vector2.zero, Vector2.one);
var dd = go.AddComponent<TMP_Dropdown>();
dd.captionText = labelTmp;
dd.ClearOptions();
dd.AddOptions(new System.Collections.Generic.List<string>(options));
var le = go.AddComponent<LayoutElement>();
le.minHeight = 40;
return go;
}
static GameObject CreateTMPInputField(Transform parent, string name, string defaultText)
{
var go = new GameObject(name);
go.transform.SetParent(parent, false);
var img = go.AddComponent<Image>();
img.color = new Color(0.15f, 0.15f, 0.2f);
var textArea = new GameObject("Text Area");
textArea.transform.SetParent(go.transform, false);
textArea.AddComponent<RectMask2D>();
SetAnchors(textArea.GetComponent<RectTransform>(), Vector2.zero, Vector2.one);
var placeholder = new GameObject("Placeholder");
placeholder.transform.SetParent(textArea.transform, false);
var phTmp = placeholder.AddComponent<TextMeshProUGUI>();
phTmp.text = defaultText;
phTmp.fontSize = 18;
phTmp.fontStyle = FontStyles.Italic;
phTmp.color = new Color(0.5f, 0.5f, 0.5f, 0.8f);
phTmp.alignment = TextAlignmentOptions.Left;
SetAnchors(placeholder.GetComponent<RectTransform>(), Vector2.zero, Vector2.one);
var inputText = new GameObject("Text");
inputText.transform.SetParent(textArea.transform, false);
var itTmp = inputText.AddComponent<TextMeshProUGUI>();
itTmp.fontSize = 18;
itTmp.color = Color.white;
itTmp.alignment = TextAlignmentOptions.Left;
SetAnchors(inputText.GetComponent<RectTransform>(), Vector2.zero, Vector2.one);
var inputField = go.AddComponent<TMP_InputField>();
inputField.textViewport = textArea.GetComponent<RectTransform>();
inputField.textComponent = itTmp;
inputField.placeholder = phTmp;
inputField.text = defaultText;
inputField.fontAsset = itTmp.font;
return go;
}
}
#endif
@@ -73,4 +73,86 @@ public static class CreateScannerScene
SetAnchors(resultImg.GetComponent<RectTransform>(), new Vector2(0.03f, 0.55f), new Vector2(0.45f, 0.98f));
var rName = CreateTMPText(resultPanel.transform, "ResultCardName", "", 22, FontStyles.Bold, TextAlignmentOptions.Left);
SetAnchors(r
SetAnchors(rName.GetComponent<RectTransform>(), new Vector2(0.48f, 0.88f), new Vector2(0.97f, 0.98f));
var rType = CreateTMPText(resultPanel.transform, "ResultTypeLine", "", 16, FontStyles.Italic, TextAlignmentOptions.Left);
SetAnchors(rType.GetComponent<RectTransform>(), new Vector2(0.48f, 0.80f), new Vector2(0.97f, 0.88f));
var rOracle = CreateTMPText(resultPanel.transform, "ResultOracleText","", 14, FontStyles.Normal, TextAlignmentOptions.TopLeft);
SetAnchors(rOracle.GetComponent<RectTransform>(), new Vector2(0.48f, 0.55f), new Vector2(0.97f, 0.80f));
var rStatus = CreateTMPText(resultPanel.transform, "ResultStatus", "", 14, FontStyles.Normal, TextAlignmentOptions.Left);
SetAnchors(rStatus.GetComponent<RectTransform>(), new Vector2(0.03f, 0.45f), new Vector2(0.97f, 0.55f));
var addCollBtn = CreateTMPButton(resultPanel.transform, "AddToCollectionButton", "Add to Collection", new Color(0.2f, 0.55f, 0.2f));
SetAnchors(addCollBtn.GetComponent<RectTransform>(), new Vector2(0.03f, 0.03f), new Vector2(0.48f, 0.12f));
var addDeckBtn = CreateTMPButton(resultPanel.transform, "AddToDeckButton", "Add to Deck", new Color(0.2f, 0.3f, 0.6f));
SetAnchors(addDeckBtn.GetComponent<RectTransform>(), new Vector2(0.52f, 0.03f), new Vector2(0.97f, 0.12f));
resultPanel.SetActive(false);
EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());
Debug.Log("[CreateScannerScene] Scanner scene created.");
}
// -- Helpers --
static void SetAnchors(RectTransform rt, Vector2 min, Vector2 max)
{
rt.anchorMin = min;
rt.anchorMax = max;
rt.offsetMin = Vector2.zero;
rt.offsetMax = Vector2.zero;
}
static GameObject CreatePanel(Transform parent, string name, Color color, bool stretch = false)
{
var go = new GameObject(name);
go.transform.SetParent(parent, false);
var img = go.AddComponent<Image>();
img.color = color;
var rt = go.GetComponent<RectTransform>();
if (stretch) SetAnchors(rt, Vector2.zero, Vector2.one);
return go;
}
static GameObject CreateTMPText(Transform parent, string name, string text, int fontSize, FontStyles style, TextAlignmentOptions alignment)
{
var go = new GameObject(name);
go.transform.SetParent(parent, false);
var tmp = go.AddComponent<TextMeshProUGUI>();
tmp.text = text;
tmp.fontSize = fontSize;
tmp.fontStyle = style;
tmp.alignment = alignment;
tmp.color = Color.white;
var rt = go.GetComponent<RectTransform>();
SetAnchors(rt, Vector2.zero, Vector2.one);
return go;
}
static GameObject CreateTMPButton(Transform parent, string name, string label, Color bgColor)
{
var go = new GameObject(name);
go.transform.SetParent(parent, false);
var img = go.AddComponent<Image>();
img.color = bgColor;
var btn = go.AddComponent<Button>();
btn.targetGraphic = img;
var colors = btn.colors;
colors.highlightedColor = bgColor * 1.2f;
colors.pressedColor = bgColor * 0.8f;
btn.colors = colors;
var textGo = new GameObject("Text");
textGo.transform.SetParent(go.transform, false);
var tmp = textGo.AddComponent<TextMeshProUGUI>();
tmp.text = label;
tmp.fontSize = 22;
tmp.alignment = TextAlignmentOptions.Center;
tmp.color = Color.white;
SetAnchors(textGo.GetComponent<RectTransform>(), Vector2.zero, Vector2.one);
var le = go.AddComponent<LayoutElement>();
le.minHeight = 50;
return go;
}
}
#endif
@@ -48,4 +48,144 @@ public static class CreateSimulatorScene
cfgVlg.padding = new RectOffset(16, 16, 16, 16); cfgVlg.spacing = 10;
cfgVlg.childForceExpandWidth = true; cfgVlg.childForceExpandHeight = false;
CreateTMPText(
CreateTMPText(configPanel.transform, "ConfigTitle", "Simulation Config", 20, FontStyles.Bold, TextAlignmentOptions.Center);
// Deck selector
var deckLabel = CreateTMPText(configPanel.transform, "DeckLabel", "Deck:", 16, FontStyles.Normal, TextAlignmentOptions.Left);
var deckInput = CreateTMPInputField(configPanel.transform, "DeckInput", "Deck ID or name...");
// Num games
var gamesLabel = CreateTMPText(configPanel.transform, "GamesLabel", "Games:", 16, FontStyles.Normal, TextAlignmentOptions.Left);
var gamesInput = CreateTMPInputField(configPanel.transform, "GamesInput", "100");
// Start/Stop buttons
var startBtn = CreateTMPButton(configPanel.transform, "StartButton", "Start Simulation", new Color(0.2f, 0.55f, 0.2f));
var stopBtn = CreateTMPButton(configPanel.transform, "StopButton", "Stop", new Color(0.55f, 0.2f, 0.2f));
// Results Panel
var resultsPanel = CreatePanel(canvasGo.transform, "ResultsPanel", new Color(0.1f, 0.1f, 0.16f));
SetAnchors(resultsPanel.GetComponent<RectTransform>(), new Vector2(0.52f, 0.35f), new Vector2(0.95f, 0.93f));
var resultsTitle = CreateTMPText(resultsPanel.transform, "ResultsTitle", "Results", 20, FontStyles.Bold, TextAlignmentOptions.Center);
SetAnchors(resultsTitle.GetComponent<RectTransform>(), new Vector2(0f, 0.90f), new Vector2(1f, 1f));
var statusText = CreateTMPText(resultsPanel.transform, "StatusText", "Ready", 16, FontStyles.Normal, TextAlignmentOptions.TopLeft);
SetAnchors(statusText.GetComponent<RectTransform>(), new Vector2(0.03f, 0.03f), new Vector2(0.97f, 0.90f));
// Progress bar
var progressBg = CreatePanel(canvasGo.transform, "ProgressBar", new Color(0.2f, 0.2f, 0.25f));
SetAnchors(progressBg.GetComponent<RectTransform>(), new Vector2(0.05f, 0.28f), new Vector2(0.95f, 0.33f));
var progressFill = CreatePanel(progressBg.transform, "ProgressFill", new Color(0.2f, 0.6f, 0.3f));
SetAnchors(progressFill.GetComponent<RectTransform>(), new Vector2(0f, 0f), new Vector2(0f, 1f));
var progressText = CreateTMPText(progressBg.transform, "ProgressText", "0%", 14, FontStyles.Normal, TextAlignmentOptions.Center);
SetAnchors(progressText.GetComponent<RectTransform>(), new Vector2(0f, 0f), new Vector2(1f, 1f));
EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());
Debug.Log("[CreateSimulatorScene] Simulator scene created.");
}
// -- Helpers --
static void SetAnchors(RectTransform rt, Vector2 min, Vector2 max)
{
rt.anchorMin = min;
rt.anchorMax = max;
rt.offsetMin = Vector2.zero;
rt.offsetMax = Vector2.zero;
}
static GameObject CreatePanel(Transform parent, string name, Color color, bool stretch = false)
{
var go = new GameObject(name);
go.transform.SetParent(parent, false);
var img = go.AddComponent<Image>();
img.color = color;
var rt = go.GetComponent<RectTransform>();
if (stretch) SetAnchors(rt, Vector2.zero, Vector2.one);
return go;
}
static GameObject CreateTMPText(Transform parent, string name, string text, int fontSize, FontStyles style, TextAlignmentOptions alignment)
{
var go = new GameObject(name);
go.transform.SetParent(parent, false);
var tmp = go.AddComponent<TextMeshProUGUI>();
tmp.text = text;
tmp.fontSize = fontSize;
tmp.fontStyle = style;
tmp.alignment = alignment;
tmp.color = Color.white;
var rt = go.GetComponent<RectTransform>();
SetAnchors(rt, Vector2.zero, Vector2.one);
return go;
}
static GameObject CreateTMPButton(Transform parent, string name, string label, Color bgColor)
{
var go = new GameObject(name);
go.transform.SetParent(parent, false);
var img = go.AddComponent<Image>();
img.color = bgColor;
var btn = go.AddComponent<Button>();
btn.targetGraphic = img;
var colors = btn.colors;
colors.highlightedColor = bgColor * 1.2f;
colors.pressedColor = bgColor * 0.8f;
btn.colors = colors;
var textGo = new GameObject("Text");
textGo.transform.SetParent(go.transform, false);
var tmp = textGo.AddComponent<TextMeshProUGUI>();
tmp.text = label;
tmp.fontSize = 22;
tmp.alignment = TextAlignmentOptions.Center;
tmp.color = Color.white;
SetAnchors(textGo.GetComponent<RectTransform>(), Vector2.zero, Vector2.one);
var le = go.AddComponent<LayoutElement>();
le.minHeight = 50;
return go;
}
static GameObject CreateTMPInputField(Transform parent, string name, string defaultText)
{
var go = new GameObject(name);
go.transform.SetParent(parent, false);
var img = go.AddComponent<Image>();
img.color = new Color(0.15f, 0.15f, 0.2f);
var textArea = new GameObject("Text Area");
textArea.transform.SetParent(go.transform, false);
textArea.AddComponent<RectMask2D>();
SetAnchors(textArea.GetComponent<RectTransform>(), Vector2.zero, Vector2.one);
var placeholder = new GameObject("Placeholder");
placeholder.transform.SetParent(textArea.transform, false);
var phTmp = placeholder.AddComponent<TextMeshProUGUI>();
phTmp.text = defaultText;
phTmp.fontSize = 18;
phTmp.fontStyle = FontStyles.Italic;
phTmp.color = new Color(0.5f, 0.5f, 0.5f, 0.8f);
phTmp.alignment = TextAlignmentOptions.Left;
SetAnchors(placeholder.GetComponent<RectTransform>(), Vector2.zero, Vector2.one);
var inputText = new GameObject("Text");
inputText.transform.SetParent(textArea.transform, false);
var itTmp = inputText.AddComponent<TextMeshProUGUI>();
itTmp.fontSize = 18;
itTmp.color = Color.white;
itTmp.alignment = TextAlignmentOptions.Left;
SetAnchors(inputText.GetComponent<RectTransform>(), Vector2.zero, Vector2.one);
var inputField = go.AddComponent<TMP_InputField>();
inputField.textViewport = textArea.GetComponent<RectTransform>();
inputField.textComponent = itTmp;
inputField.placeholder = phTmp;
inputField.text = defaultText;
inputField.fontAsset = itTmp.font;
return go;
}
}
#endif
@@ -11,289 +11,262 @@ using CommanderAILab.Services;
namespace CommanderAILab.UI
{
/// <summary>
/// DeckGenerator scene — AI deck generation wizard:
/// commander pick, strategy/budget inputs, precon base,
/// streaming progress, result preview, save to DeckBuilder.
/// </summary>
public class DeckGeneratorController : MonoBehaviour
/// <summary>
/// DeckGenerator scene — AI deck generation wizard:
/// commander pick, strategy/budget inputs, precon base,
/// streaming progress, result preview, save to DeckBuilder.
/// </summary>
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<GameObject> _commanderRows = new();
private List<GameObject> _resultRows = new();
private List<string> _preconNames = new();
// ── Lifecycle ─────────────────────────────────────────────────────
private void Start()
{
// ── Navigation ────────────────────────────────────────────────────
[Header("Navigation")]
[SerializeField] private Button backButton;
backButton.onClick.AddListener(() => SceneManager.LoadScene("MainMenu"));
// ── 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;
commanderSearchButton.onClick.AddListener(OnCommanderSearch);
commanderSearchInput.onSubmit.AddListener(_ => OnCommanderSearch());
// ── 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
generateButton.onClick.AddListener(OnGenerate);
saveToDeckBuilderButton.onClick.AddListener(OnSaveToDeckBuilder);
regenerateButton.onClick.AddListener(OnGenerate);
retryButton.onClick.AddListener(OnGenerate);
// ── Step 3: Generate ──────────────────────────────────────────────
[Header("Step 3 - Generate")]
[SerializeField] private Button generateButton;
[SerializeField] private GameObject progressPanel;
[SerializeField] private Slider progressSlider;
[SerializeField] private TMP_Text progressText;
progressPanel.SetActive(false);
resultPanel.SetActive(false);
errorPanel.SetActive(false);
// ── 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<GameObject> _commanderRows = new();
private List<GameObject> _resultRows = new();
private List<string> _preconNames = new();
// ── Helper Models ─────────────────────────────────────────────────
[System.Serializable]
private class PreconListWrapper
{
public List<string> precons;
}
[System.Serializable]
private class CollectionResponse
{
public List<CardModel> cards;
}
// ── 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<PreconListWrapper>(json);
_preconNames = wrapper?.precons ?? new List<string>();
}
catch { _preconNames = new List<string>(); }
var options = new List<TMP_Dropdown.OptionData> { 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";
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<CardModel> cards;
try
{
var resp = JsonConvert.DeserializeObject<CollectionResponse>(json);
cards = resp?.cards ?? new List<CardModel>();
}
catch { return; }
foreach (var card in cards)
{
var row = Instantiate(commanderResultRowPrefab, commanderResultParent);
_commanderRows.Add(row);
var label = row.GetComponentInChildren<TMP_Text>();
if (label) label.text = $"{card.name} — {card.manaCost}";
var btn = row.GetComponentInChildren<Button>();
var captured = card;
if (btn) btn.onClick.AddListener(() => SelectCommander(captured));
}
}
private void SelectCommander(CardModel card)
{
_selectedCommander = card;
selectedCommanderLabel.text = $"Commander: {card.name}";
}
// ── Generate ──────────────────────────────────────────────────────
private void OnGenerate()
{
if (_selectedCommander == null)
{
ShowError("Please select a commander first.");
return;
}
progressPanel.SetActive(true);
resultPanel.SetActive(false);
errorPanel.SetActive(false);
generateButton.interactable = false;
progressSlider.value = 0f;
progressText.text = "Sending request\u2026";
string strategy = strategyDropdown.options[strategyDropdown.value].text;
string budget = budgetDropdown.options[budgetDropdown.value].text;
string synergies = synergiesInput.text.Trim();
bool ownedOnly = ownedOnlyToggle.isOn;
string precon = preconBaseDropdown.value > 0
? preconBaseDropdown.options[preconBaseDropdown.value].text
: "";
var requestObj = new
{
commander = _selectedCommander.name,
strategy = strategy,
budget = budget,
synergies = synergies,
owned_only = ownedOnly,
precon_base = precon
};
string requestJson = JsonConvert.SerializeObject(requestObj);
StartCoroutine(PollGenerate(requestJson));
}
private System.Collections.IEnumerator PollGenerate(string requestJson)
{
float fakeProgress = 0f;
bool done = false;
string resultJson = null;
string errorMsg = null;
ApiClient.Instance.GenerateDeck(requestJson,
json => { resultJson = json; done = true; },
err => { errorMsg = err; done = true; });
while (!done)
{
fakeProgress = Mathf.MoveTowards(fakeProgress, 0.9f, Time.deltaTime * 0.15f);
progressSlider.value = fakeProgress;
progressText.text = $"Generating\u2026 {Mathf.RoundToInt(fakeProgress * 100)}%";
yield return null;
}
progressSlider.value = 1f;
progressText.text = "Complete!";
generateButton.interactable = true;
progressPanel.SetActive(false);
if (errorMsg != null)
{
ShowError($"Generation failed:\n{errorMsg}");
yield break;
}
try
{
_lastResult = JsonConvert.DeserializeObject<DeckGenResponse>(resultJson);
}
catch (Exception e)
{
ShowError($"Parse error: {e.Message}");
yield break;
}
ShowResult(_lastResult);
}
// ── Result ────────────────────────────────────────────────────────
private void ShowResult(DeckGenResponse result)
{
foreach (var go in _resultRows) if (go) Destroy(go);
_resultRows.Clear();
resultPanel.SetActive(true);
int total = result.cards?.Count ?? 0;
resultSummaryText.text = $"{total} cards generated for {_selectedCommander.name}";
int[] buckets = new int[8];
if (result.cards != null)
foreach (var card in result.cards)
{
int cmc = Mathf.Clamp(Mathf.RoundToInt(card.cmc), 0, 7);
buckets[cmc]++;
var row = Instantiate(resultCardRowPrefab, resultCardListParent);
_resultRows.Add(row);
var labels = row.GetComponentsInChildren<TMP_Text>();
if (labels.Length > 0) labels[0].text = card.name;
if (labels.Length > 1) labels[1].text = card.manaCost;
}
var sb = new System.Text.StringBuilder("CMC: ");
for (int i = 0; i < 8; i++)
if (buckets[i] > 0) sb.Append($"{i}{(i==7?"+":"")}:{buckets[i]} ");
resultManaCurveText.text = sb.ToString();
}
// ── Save to DeckBuilder ───────────────────────────────────────────
private void OnSaveToDeckBuilder()
{
if (_lastResult == null) return;
PlayerPrefs.SetString("GeneratedDeck", JsonConvert.SerializeObject(_lastResult));
PlayerPrefs.Save();
SceneManager.LoadScene("DeckBuilder");
}
// ── Error ─────────────────────────────────────────────────────────
private void ShowError(string msg)
{
errorText.text = msg;
errorPanel.SetActive(true);
Debug.LogWarning($"[DeckGeneratorController] {msg}");
}
LoadPrecons();
}
// ── Precon list ───────────────────────────────────────────────────
private void LoadPrecons()
{
ApiClient.Instance.GetPrecons(
json =>
{
try
{
var wrapper = JsonConvert.DeserializeObject<PreconListWrapper>(json);
_preconNames = wrapper?.precons ?? new List<string>();
}
catch { _preconNames = new List<string>(); }
var options = new List<TMP_Dropdown.OptionData> { 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";
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<CardModel> cards;
try
{
var resp = JsonConvert.DeserializeObject<CollectionResponse>(json);
cards = resp?.cards ?? new List<CardModel>();
}
catch { return; }
foreach (var card in cards)
{
var row = Instantiate(commanderResultRowPrefab, commanderResultParent);
_commanderRows.Add(row);
var label = row.GetComponentInChildren<TMP_Text>();
if (label) label.text = $"{card.name} — {card.manaCost}";
var btn = row.GetComponentInChildren<Button>();
var captured = card;
if (btn) btn.onClick.AddListener(() => SelectCommander(captured));
}
}
private void SelectCommander(CardModel card)
{
_selectedCommander = card;
selectedCommanderLabel.text = $"Commander: {card.name}";
}
// ── Generate ──────────────────────────────────────────────────────
private void OnGenerate()
{
if (_selectedCommander == null)
{
ShowError("Please select a commander first.");
return;
}
progressPanel.SetActive(true);
resultPanel.SetActive(false);
errorPanel.SetActive(false);
generateButton.interactable = false;
progressSlider.value = 0f;
progressText.text = "Sending request…";
string strategy = strategyDropdown.options[strategyDropdown.value].text;
string budget = budgetDropdown.options[budgetDropdown.value].text;
string synergies = synergiesInput.text.Trim();
bool ownedOnly = ownedOnlyToggle.isOn;
string precon = preconBaseDropdown.value > 0
? preconBaseDropdown.options[preconBaseDropdown.value].text : "";
var requestObj = new
{
commander = _selectedCommander.name,
strategy = strategy,
budget = budget,
synergies = synergies,
owned_only = ownedOnly,
precon_base = precon
};
string requestJson = JsonConvert.SerializeObject(requestObj);
StartCoroutine(PollGenerate(requestJson));
}
private System.Collections.IEnumerator PollGenerate(string requestJson)
{
// Show incremental progress while waiting for API
float fakeProgress = 0f;
bool done = false;
string resultJson = null;
string errorMsg = null;
ApiClient.Instance.GenerateDeck(requestJson,
json => { resultJson = json; done = true; },
err => { errorMsg = err; done = true; });
while (!done)
{
fakeProgress = Mathf.MoveTowards(fakeProgress, 0.9f, Time.deltaTime * 0.15f);
progressSlider.value = fakeProgress;
progressText.text = $"Generating… {Mathf.RoundToInt(fakeProgress * 100)}%";
yield return null;
}
progressSlider.value = 1f;
progressText.text = "Complete!";
generateButton.interactable = true;
progressPanel.SetActive(false);
if (errorMsg != null) { ShowError($"Generation failed:\n{errorMsg}"); yield break; }
try { _lastResult = JsonConvert.DeserializeObject<DeckGenResponse>(resultJson); }
catch (Exception e) { ShowError($"Parse error: {e.Message}"); yield break; }
ShowResult(_lastResult);
}
// ── Result ────────────────────────────────────────────────────────
private void ShowResult(DeckGenResponse result)
{
foreach (var go in _resultRows) if (go) Destroy(go);
_resultRows.Clear();
resultPanel.SetActive(true);
int total = result.cards?.Count ?? 0;
resultSummaryText.text = $"{total} cards generated for {_selectedCommander.name}";
// Build simple mana curve summary
int[] buckets = new int[8];
if (result.cards != null)
foreach (var card in result.cards)
{
int cmc = Mathf.Clamp(Mathf.RoundToInt(card.cmc), 0, 7);
buckets[cmc]++;
var row = Instantiate(resultCardRowPrefab, resultCardListParent);
_resultRows.Add(row);
var labels = row.GetComponentsInChildren<TMP_Text>();
if (labels.Length > 0) labels[0].text = card.name;
if (labels.Length > 1) labels[1].text = card.manaCost;
}
var sb = new System.Text.StringBuilder("CMC: ");
for (int i = 0; i < 8; i++)
if (buckets[i] > 0) sb.Append($"{i}{(i==7?"+":"")}:{buckets[i]} ");
resultManaCurveText.text = sb.ToString();
}
// ── Save to DeckBuilder ───────────────────────────────────────────
private void OnSaveToDeckBuilder()
{
if (_lastResult == null) return;
PlayerPrefs.SetString("GeneratedDeck", JsonConvert.SerializeObject(_lastResult));
PlayerPrefs.Save();
SceneManager.LoadScene("DeckBuilder");
}
// ── Error ─────────────────────────────────────────────────────────
private void ShowError(string msg)
{
errorText.text = msg;
errorPanel.SetActive(true);
Debug.LogWarning($"[DeckGeneratorController] {msg}");
}
}
}