fix(lobby): wire OnConfirm to POST /api/game/start, save game_id to PlayerPrefs
- OnConfirm() now calls ApiClient.PostGameStart() with 4-seat config - On success: saves BattleSeats + GameId + WsUrl to PlayerPrefs, loads Battleground scene - On failure: shows inline error label (red), re-enables Confirm button - Added StartGameRequest/Response POCOs matching routes/game.py schema - Added _errorLabel [SerializeField] for user-facing error feedback
This commit is contained in:
1 parent
d8238ec11f
commit
2c67222de1
1 file changed
+100
-2
@@ -12,7 +12,10 @@ namespace CommanderAILab.UI
|
||||
/// <summary>
|
||||
/// Modal shown at Battleground scene start.
|
||||
/// Configures 4 seats (Human vs AI, deck choice, AI style).
|
||||
/// On Confirm writes seat config to PlayerPrefs["BattleSeats"] and starts the game.
|
||||
/// On Confirm:
|
||||
/// 1. POSTs seat config to POST /api/game/start
|
||||
/// 2. Saves game_id + ws_url to PlayerPrefs
|
||||
/// 3. Loads Battleground scene
|
||||
/// </summary>
|
||||
public class LobbySetupModal : MonoBehaviour
|
||||
{
|
||||
@@ -23,6 +26,9 @@ namespace CommanderAILab.UI
|
||||
[SerializeField] private Button confirmButton;
|
||||
[SerializeField] private Button cancelButton;
|
||||
|
||||
[Header("Feedback")]
|
||||
[SerializeField] private TMP_Text errorLabel; // assign in Inspector; hidden by default
|
||||
|
||||
[SerializeField] private ApiClient apiClient;
|
||||
|
||||
private List<string> _deckNames = new();
|
||||
@@ -31,6 +37,8 @@ namespace CommanderAILab.UI
|
||||
public void Open(string savedSeatsJson)
|
||||
{
|
||||
gameObject.SetActive(true);
|
||||
SetError(null);
|
||||
|
||||
confirmButton.onClick.AddListener(OnConfirm);
|
||||
cancelButton.onClick.AddListener(() => SceneManager.LoadScene("MainMenu"));
|
||||
|
||||
@@ -64,19 +72,83 @@ namespace CommanderAILab.UI
|
||||
}
|
||||
}
|
||||
|
||||
// ── Confirm ─────────────────────────────────────────────
|
||||
// ── Confirm → POST /api/game/start ──────────────────────
|
||||
private void OnConfirm()
|
||||
{
|
||||
SetError(null);
|
||||
confirmButton.interactable = false;
|
||||
|
||||
// Build local seat list
|
||||
var configs = new List<SeatConfig>();
|
||||
foreach (var row in seatRows)
|
||||
configs.Add(row?.GetConfig() ?? new SeatConfig());
|
||||
|
||||
// Save locally so Battleground scene can read seat metadata
|
||||
PlayerPrefs.SetString("BattleSeats", JsonConvert.SerializeObject(configs));
|
||||
PlayerPrefs.Save();
|
||||
|
||||
// Build backend request — maps local SeatConfig → StartGameRequest
|
||||
var apiSeats = new List<ApiSeatConfig>();
|
||||
for (int i = 0; i < configs.Count; i++)
|
||||
{
|
||||
var c = configs[i];
|
||||
apiSeats.Add(new ApiSeatConfig
|
||||
{
|
||||
seat_index = i,
|
||||
seat_type = c.isHuman ? "Human" : $"AI {c.aiStyle}",
|
||||
deck_name = c.deckName ?? "",
|
||||
player_name = c.isHuman ? "Player" : $"AI {i}",
|
||||
});
|
||||
}
|
||||
|
||||
var requestJson = JsonConvert.SerializeObject(new StartGameRequest { seats = apiSeats });
|
||||
|
||||
apiClient.PostGameStart(
|
||||
requestJson,
|
||||
onSuccess: responseJson =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var resp = JsonConvert.DeserializeObject<StartGameResponse>(responseJson);
|
||||
if (resp == null || string.IsNullOrEmpty(resp.game_id))
|
||||
{
|
||||
OnStartError("Server returned an invalid response.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Persist game_id and ws_url so GameWebSocketClient can connect
|
||||
PlayerPrefs.SetString("GameId", resp.game_id);
|
||||
PlayerPrefs.SetString("WsUrl", resp.ws_url ?? $"/ws/game/{resp.game_id}");
|
||||
PlayerPrefs.Save();
|
||||
|
||||
gameObject.SetActive(false);
|
||||
SceneManager.LoadScene("Battleground");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnStartError($"Parse error: {ex.Message}");
|
||||
}
|
||||
},
|
||||
onError: err => OnStartError($"Failed to start game: {err}"));
|
||||
}
|
||||
|
||||
private void OnStartError(string message)
|
||||
{
|
||||
Debug.LogError("[LobbySetupModal] " + message);
|
||||
SetError(message);
|
||||
confirmButton.interactable = true;
|
||||
}
|
||||
|
||||
private void SetError(string message)
|
||||
{
|
||||
if (errorLabel == null) return;
|
||||
errorLabel.text = message ?? string.Empty;
|
||||
errorLabel.gameObject.SetActive(!string.IsNullOrEmpty(message));
|
||||
}
|
||||
|
||||
// ── Models ─────────────────────────────────────────────
|
||||
|
||||
/// <summary>Local / PlayerPrefs representation of a seat.</summary>
|
||||
[Serializable]
|
||||
public class SeatConfig
|
||||
{
|
||||
@@ -85,6 +157,32 @@ namespace CommanderAILab.UI
|
||||
public string aiStyle; // Aggro / Control / Combo / Political
|
||||
}
|
||||
|
||||
/// <summary>Matches routes/game.py SeatConfig schema.</summary>
|
||||
[Serializable]
|
||||
private class ApiSeatConfig
|
||||
{
|
||||
public int seat_index;
|
||||
public string seat_type;
|
||||
public string deck_name;
|
||||
public string player_name;
|
||||
}
|
||||
|
||||
/// <summary>Matches routes/game.py StartGameRequest schema.</summary>
|
||||
[Serializable]
|
||||
private class StartGameRequest
|
||||
{
|
||||
public List<ApiSeatConfig> seats;
|
||||
}
|
||||
|
||||
/// <summary>Matches routes/game.py StartGameResponse schema.</summary>
|
||||
[Serializable]
|
||||
private class StartGameResponse
|
||||
{
|
||||
public string game_id;
|
||||
public string status;
|
||||
public string ws_url;
|
||||
}
|
||||
|
||||
[Serializable] private class DecksWrapper { public List<DeckEntry> decks; }
|
||||
[Serializable] private class DeckEntry { public string name; }
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user