diff --git a/emulator/basic1_emulator b/emulator/basic1_emulator index ded0e14..76f221d 100755 Binary files a/emulator/basic1_emulator and b/emulator/basic1_emulator differ diff --git a/games/monopoly/BoardModalGame.h b/games/monopoly/BoardModalGame.h new file mode 100644 index 0000000..bba11e1 --- /dev/null +++ b/games/monopoly/BoardModalGame.h @@ -0,0 +1,59 @@ +// BoardModalGame.h +#pragma once +#include "../../lib/game.h" +#include "../../display/low_level_render.h" +#include "../../display/low_level_gui.h" +#include "input_manager.h" +#include "monopoly_board.h" +#include "player.h" +#include "MonopolyBoardRenderer.h" + +class BoardModalGame : public Game { + bool dismissed; + Player* players; + int players_count; + +public: + BoardModalGame(uint16_t width, uint16_t height, LowLevelRenderer* renderer, LowLevelGUI* gui, InputManager* input_manager, Player* p, int count) + : Game(width, height, renderer, gui, input_manager), dismissed(false), players(p), players_count(count) {} + + void init() override { dismissed = false; } + + bool update(const InputEvent& event) override { + // Any button dismisses the board view + if (event.type == INPUT_BUTTON_0 || event.type == INPUT_BUTTON_1) { + dismissed = true; + return true; + } + return false; + } + + void draw() override { + renderer->clear_buffer(); + + MonopolyBoardRenderer::draw_board_perimeter(renderer, width, height, players, players_count); + + // --- Inner UI --- + int cw = width / 7; + int ch = height / 7; + int ix = cw + 5, iy = ch + 5; + int iw = width - 2 * cw - 10, ih = height - 2 * ch - 10; + + // Title + renderer->draw_string_scaled(ix + (iw - 144) / 2, iy + 10, "== BOARD ==", 2); + + // Legend for players + int ly = iy + 40; + for (int i = 0; i < players_count; ++i) { + char buf[64]; + snprintf(buf, sizeof(buf), "%c:%s($%d)", (players[i].token ? players[i].token[0] : 'P'), players[i].name, players[i].balance); + renderer->draw_string_scaled(ix + 10, ly, buf, 1); + ly += 12; + } + + renderer->draw_string_scaled(ix + (iw - 120) / 2, iy + ih - 15, "PRESS B TO EXIT", 1); + } + +public: + bool is_dismissed() const { return dismissed; } +}; diff --git a/games/monopoly/DiceModalGame.h b/games/monopoly/DiceModalGame.h index 74e7a51..9bf0922 100644 --- a/games/monopoly/DiceModalGame.h +++ b/games/monopoly/DiceModalGame.h @@ -4,13 +4,51 @@ #include "../../display/low_level_render.h" #include "../../display/low_level_gui.h" #include "input_manager.h" +#include "MonopolyBoardRenderer.h" class DiceModalGame : public Game { int dice1, dice2; + const BoardTile *from_tile, *to_tile; + Player* players; + int players_count; bool dismissed; + + void draw_die(int x, int y, int size, int value) { + // Die base + renderer->draw_rounded_rectangle(x, y, size, size, 4, true, false); + // Shadow (offset 2,2) + renderer->draw_line(x + size, y + 2, x + size, y + size, true); + renderer->draw_line(x + 2, y + size, x + size, y + size, true); + + int dot_size = size / 6; + int m = size / 2; + int l = size / 4; + int r = 3 * size / 4; + int t = size / 4; + int b = 3 * size / 4; + + auto draw_dot = [&](int dx, int dy) { + renderer->draw_filled_rectangle(x + dx - dot_size / 2, y + dy - dot_size / 2, dot_size, dot_size, true, 1); + }; + + if (value % 2 == 1) draw_dot(m, m); // Center dot for 1, 3, 5 + if (value > 1) { + draw_dot(l, t); + draw_dot(r, b); + } + if (value > 3) { + draw_dot(r, t); + draw_dot(l, b); + } + if (value == 6) { + draw_dot(l, m); + draw_dot(r, m); + } + } + public: - DiceModalGame(uint16_t width, uint16_t height, LowLevelRenderer* renderer, LowLevelGUI* gui, InputManager* input_manager, int d1, int d2) - : Game(width, height, renderer, gui, input_manager), dice1(d1), dice2(d2), dismissed(false) {} + DiceModalGame(uint16_t width, uint16_t height, LowLevelRenderer* renderer, LowLevelGUI* gui, InputManager* input_manager, int d1, int d2, const BoardTile* from, const BoardTile* to, Player* p, int count) + : Game(width, height, renderer, gui, input_manager), dice1(d1), dice2(d2), from_tile(from), to_tile(to), players(p), players_count(count), dismissed(false) {} void init() override { dismissed = false; } bool update(const InputEvent& event) override { if (event.type == INPUT_BUTTON_0 || event.type == INPUT_BUTTON_1) { @@ -20,13 +58,56 @@ public: return false; } void draw() override { - int win_w = 220, win_h = 120; - int win_x = (width - win_w) / 2, win_y = (height - win_h) / 2; - char buf[64]; - gui->draw_new_window(win_x, win_y, win_w, win_h, "Dice Roll"); - snprintf(buf, sizeof(buf), "You rolled: %d + %d", dice1, dice2); - renderer->draw_string_scaled(win_x + 30, win_y + 40, buf, 2); - renderer->draw_string_scaled(10, height - 20, "Press any button...", 2); + renderer->clear_buffer(); + + MonopolyBoardRenderer::draw_board_perimeter(renderer, width, height, players, players_count); + + // --- Inner UI (Center Area) --- + int cw = width / 7; + int ch = height / 7; + int ix = cw + 2, iy = ch + 2; + int iw = width - 2 * cw - 4, ih = height - 2 * ch - 4; + + // Window background (White box) + renderer->draw_filled_rectangle(ix, iy, iw, ih, false, 0); // Clear central area + renderer->draw_rectangle(ix, iy, iw, ih, true, 1); // Border + + // Header + renderer->draw_filled_rectangle(ix + 2, iy + 2, iw - 4, 30, true, 1); + renderer->set_text_color(false); + renderer->draw_string_scaled(ix + (iw - (int)strlen("==DICE ROLL==") * 12) / 2, iy + 10, "==DICE ROLL==", 2); + renderer->set_text_color(true); + + // Dice + int dice_size = 50; + int dice_y = iy + 45; + draw_die(ix + iw / 2 - dice_size - 10, dice_y, dice_size, dice1); + draw_die(ix + iw / 2 + 10, dice_y, dice_size, dice2); + + // Total + char buf[32]; + snprintf(buf, sizeof(buf), "TOTAL: %d", dice1 + dice2); + renderer->draw_string_scaled(ix + (iw - 100) / 2, dice_y + dice_size + 10, buf, 2); + + // Movement info + int info_y = dice_y + dice_size + 30; + char move_buf[64]; + if (from_tile && to_tile) { + snprintf(move_buf, sizeof(move_buf), "FROM: %s", from_tile->name); + renderer->draw_string_scaled(ix + 10, info_y, move_buf, 1); + info_y += 12; + snprintf(move_buf, sizeof(move_buf), "TO: %s", to_tile->name); + renderer->draw_string_scaled(ix + 10, info_y, move_buf, 1); + } + + // Button + int btn_w = 120, btn_h = 25; + int btn_x = ix + (iw - btn_w) / 2; + int btn_y = iy + ih - 35; + renderer->draw_filled_rectangle(btn_x, btn_y, btn_w, btn_h, true, 1); + renderer->set_text_color(false); + renderer->draw_string_scaled(btn_x + 5, btn_y + 5, ">A CONTINUE", 2); + renderer->set_text_color(true); } bool is_dismissed() const { return dismissed; } }; diff --git a/games/monopoly/MonopolyBoardRenderer.h b/games/monopoly/MonopolyBoardRenderer.h new file mode 100644 index 0000000..839ebc2 --- /dev/null +++ b/games/monopoly/MonopolyBoardRenderer.h @@ -0,0 +1,100 @@ +#pragma once +#include "../../display/low_level_render.h" +#include "monopoly_board.h" +#include "player.h" +#include + +class MonopolyBoardRenderer { +public: + static void draw_tile(LowLevelRenderer* renderer, int x, int y, int w, int h, int index, bool is_corner, Player* players, int players_count, int orientation = 0) { + if (index < 0 || index >= BOARD_SIZE) return; + + renderer->draw_rectangle(x, y, w, h, true, 1); + + const BoardTile& tile = MONOPOLY_BOARD[index]; + int content_x = x, content_y = y, content_w = w, content_h = h; + + if (!is_corner && tile.type == TILE_PROPERTY) { + int bar_size = 10; + int bx = x, by = y, bw = w, bh = h; + + if (orientation == 0) { // Bottom row (Bar on top) + bh = bar_size; + content_y += bar_size; content_h -= bar_size; + } else if (orientation == 1) { // Left column (Bar on right) + bx = x + w - bar_size; bw = bar_size; + content_w -= bar_size; + } else if (orientation == 2) { // Top row (Bar on bottom) + by = y + h - bar_size; bh = bar_size; + content_h -= bar_size; + } else if (orientation == 3) { // Right column (Bar on left) + bw = bar_size; + content_x += bar_size; content_w -= bar_size; + } + + renderer->draw_filled_rectangle(bx, by, bw, bh, true, 1); + + // Group number + renderer->set_text_color(false); // Black text on white bar + char gbuf[2] = { (char)('0' + tile.group[0]), '\0' }; + renderer->draw_string_scaled(bx + (bw - 6) / 2, by + (bh - 8) / 2, gbuf, 1); + renderer->set_text_color(true); + } + + char short_name[5] = {0}; + const char* full_name = tile.name; + + if (is_corner) { + strncpy(short_name, full_name, 4); + } else { + short_name[0] = full_name[0]; + const char* space = strchr(full_name, ' '); + if (space && space[1] != '\0') short_name[1] = space[1]; + } + + for (int i = 0; short_name[i]; i++) if(short_name[i] >= 'a' && short_name[i] <= 'z') short_name[i] -= 32; + + renderer->draw_string_scaled(content_x + (content_w - (int)strlen(short_name) * 6) / 2, content_y + (content_h - 8) / 2, short_name, 1); + + // Draw player markers + int p_count = 0; + for (int i = 0; i < players_count; ++i) { + if (players[i].position == index) { + char mark[2] = { (players[i].token ? players[i].token[0] : 'P'), '\0' }; + renderer->draw_string_scaled(content_x + 2 + (p_count * 8), content_y + 2, mark, 1); + p_count++; + } + } + } + + static void draw_board_perimeter(LowLevelRenderer* renderer, int width, int height, Player* players, int players_count) { + int cw = width / 7; // Corner width + int ch = height / 7; // Corner height + int rw = (width - 2 * cw) / 9; // Regular tile width + int rh = (height - 2 * ch) / 9; // Regular tile height + + // --- Bottom Row: 0 to 10 (Right to Left) --- + draw_tile(renderer, width - cw, height - ch, cw, ch, 0, true, players, players_count, 0); // GO + for (int i = 1; i < 10; ++i) { + draw_tile(renderer, width - cw - i * rw, height - ch, rw, ch, i, false, players, players_count, 0); + } + draw_tile(renderer, 0, height - ch, cw, ch, 10, true, players, players_count, 1); // JAIL + + // --- Left Column: 11 to 19 (Bottom to Top) --- + for (int i = 11; i < 20; ++i) { + draw_tile(renderer, 0, height - ch - (i - 10) * rh, cw, rh, i, false, players, players_count, 1); + } + + // --- Top Row: 20 to 30 (Left to Right) --- + draw_tile(renderer, 0, 0, cw, ch, 20, true, players, players_count, 2); // FREE PARKING + for (int i = 21; i < 30; ++i) { + draw_tile(renderer, cw + (i - 21) * rw, 0, rw, ch, i, false, players, players_count, 2); + } + draw_tile(renderer, width - cw, 0, cw, ch, 30, true, players, players_count, 3); // GO TO JAIL + + // --- Right Column: 31 to 39 (Top to Bottom) --- + for (int i = 31; i < 40; ++i) { + draw_tile(renderer, width - cw, ch + (i - 31) * rh, cw, rh, i, false, players, players_count, 3); + } + } +}; diff --git a/games/monopoly/PropertyModalGame.cpp b/games/monopoly/PropertyModalGame.cpp deleted file mode 100644 index e69de29..0000000 diff --git a/games/monopoly/PropertyModalGame.h b/games/monopoly/PropertyModalGame.h index e4ce039..04ca6cf 100644 --- a/games/monopoly/PropertyModalGame.h +++ b/games/monopoly/PropertyModalGame.h @@ -5,43 +5,125 @@ #include "../../display/low_level_gui.h" #include "input_manager.h" #include "monopoly_board.h" +#include "player.h" +#include "MonopolyBoardRenderer.h" class PropertyModalGame : public Game { const BoardTile* property; bool dismissed; + bool is_owned; + const char* owner_name; + bool can_afford; + int selected_choice; // 0: Buy, 1: Cancel + bool buy_requested; + Player* players; + int players_count; + public: - PropertyModalGame(uint16_t width, uint16_t height, LowLevelRenderer* renderer, LowLevelGUI* gui, InputManager* input_manager, const BoardTile* prop) - : Game(width, height, renderer, gui, input_manager), property(prop), dismissed(false) {} - void init() override { dismissed = false; } + PropertyModalGame(uint16_t width, uint16_t height, LowLevelRenderer* renderer, LowLevelGUI* gui, InputManager* input_manager, const BoardTile* prop, bool owned, const char* owner, bool affordable, Player* p_list = nullptr, int p_count = 0) + : Game(width, height, renderer, gui, input_manager), property(prop), dismissed(false), is_owned(owned), owner_name(owner), can_afford(affordable), selected_choice(0), buy_requested(false), players(p_list), players_count(p_count) { + if (is_owned || !can_afford) selected_choice = 1; + } + void init() override { dismissed = false; buy_requested = false; } bool update(const InputEvent& event) override { - if (event.type == INPUT_BUTTON_0 || event.type == INPUT_BUTTON_1) { + if (event.type == INPUT_BUTTON_0) { // BUTTON A -> BUY + if (!is_owned && can_afford) { + buy_requested = true; + dismissed = true; + return true; + } else if (is_owned || !can_afford) { + // If it's just the "OK" state, either button works? + // Image shows >B CONTINUE in my code, so maybe Button 1. + } + } + if (event.type == INPUT_BUTTON_1) { // BUTTON B -> AUCTION / DISMISS dismissed = true; return true; } return false; } void draw() override { - int win_w = 320, win_h = 180; - int win_x = (width - win_w) / 2, win_y = (height - win_h) / 2; + renderer->clear_buffer(); + int win_w = 160; + int win_h = 160; + int win_x = (width - win_w) / 2; + int win_y = (height - win_h) / 2; char buf[128]; - snprintf(buf, sizeof(buf), "Property: %s", property->name); - gui->draw_new_window(win_x, win_y, win_w, win_h, buf); - int py = win_y + 30; - char pbuf[128]; - if (property->type == TILE_PROPERTY) { - snprintf(pbuf, sizeof(pbuf), "Cost: $%d", property->cost); - renderer->draw_string_scaled(win_x + 20, py, pbuf, 2); - py += 25; - snprintf(pbuf, sizeof(pbuf), "Rent: $%d", property->rent[0]); - renderer->draw_string_scaled(win_x + 20, py, pbuf, 2); - py += 25; - snprintf(pbuf, sizeof(pbuf), "House Cost: $%d", property->house_cost); - renderer->draw_string_scaled(win_x + 20, py, pbuf, 2); - } else if (property->type == TILE_RAILROAD || property->type == TILE_UTILITY) { - snprintf(pbuf, sizeof(pbuf), "Cost: $%d", property->cost); - renderer->draw_string_scaled(win_x + 20, py, pbuf, 2); + + if (players && players_count > 0) { + MonopolyBoardRenderer::draw_board_perimeter(renderer, width, height, players, players_count); + } + + // Window background (White box) + renderer->draw_filled_rectangle(win_x, win_y, win_w, win_h, false, 0); // Clear background + renderer->draw_rectangle(win_x, win_y, win_w, win_h, true, 2); + renderer->draw_rectangle(win_x + 3, win_y + 3, win_w - 6, win_h - 6, true, 1); + + // Header Title Bar + renderer->draw_filled_rectangle(win_x + 4, win_y + 4, win_w - 8, 30, true, 1); + renderer->set_text_color(false); // White text + snprintf(buf, sizeof(buf), "%s", property->name); + renderer->draw_string_scaled(win_x + (win_w - (int)strlen(buf) * 6) / 2, win_y + 8, buf, 1); + renderer->set_text_color(true); + + // Subtitle (Type) + const char* type_str = "PROPERTY"; + if (property->type == TILE_RAILROAD) type_str = "RAILROAD"; + else if (property->type == TILE_UTILITY) type_str = "UTILITY"; + snprintf(buf, sizeof(buf), "%s", type_str); + renderer->draw_string_scaled(win_x + (win_w - (int)strlen(buf) * 6) / 2, win_y + 40, buf, 1); + + // Info box center + int info_y = win_y + 60; + // Price + snprintf(buf, sizeof(buf), "PRICE: $%d", property->cost); + renderer->draw_string_scaled(win_x + 15, info_y, buf, 1); + info_y += 15; + + // Rent + if (property->type == TILE_PROPERTY) { + snprintf(buf, sizeof(buf), "RENT: $%d", property->rent[0]); + } else if (property->type == TILE_UTILITY) { + snprintf(buf, sizeof(buf), "RENT: 4x DICE"); + } else if (property->type == TILE_RAILROAD) { + snprintf(buf, sizeof(buf), "RENT: $25"); + } + renderer->draw_string_scaled(win_x + 15, info_y, buf, 1); + info_y += 15; + + // Owner + if (is_owned && owner_name) { + snprintf(buf, sizeof(buf), "OWNER: %s", owner_name); + } else { + snprintf(buf, sizeof(buf), "OWNER: %s", is_owned ? "PLAYER" : "BANK"); + } + renderer->draw_string_scaled(win_x + 15, info_y, buf, 1); + + // Action Buttons + int btn_y = win_y + win_h - 60; + int btn_w = win_w - 30; + int btn_h = 25; + + if (is_owned || !can_afford) { + // Only one option: CONTINUE (B) + renderer->draw_filled_rectangle(win_x + 15, btn_y, btn_w, btn_h, true, 1); + renderer->set_text_color(false); + renderer->draw_string_scaled(win_x + 25, btn_y + 8, ">B CONTINUE", 1); + renderer->set_text_color(true); + } else { + // Choice: Buy (A) or Auction (B) + // Buy Button + renderer->draw_filled_rectangle(win_x + 15, btn_y, btn_w, btn_h, true, 1); + renderer->set_text_color(false); + snprintf(buf, sizeof(buf), ">A BUY ($%d)", property->cost); + renderer->draw_string_scaled(win_x + 20, btn_y + 8, buf, 1); + renderer->set_text_color(true); + + btn_y += 30; + // Auction Button + renderer->draw_string_scaled(win_x + 20, btn_y + 8, ">B AUCTION", 1); } - renderer->draw_string_scaled(10, height - 20, "Press any button...", 2); } bool is_dismissed() const { return dismissed; } + bool wants_to_buy() const { return buy_requested; } }; diff --git a/games/monopoly/monopoly_board.h b/games/monopoly/monopoly_board.h index ed26ad5..5a59213 100644 --- a/games/monopoly/monopoly_board.h +++ b/games/monopoly/monopoly_board.h @@ -37,7 +37,7 @@ static const BoardTile MONOPOLY_BOARD[BOARD_SIZE] = { {"Baltic Avenue", TILE_PROPERTY, false, 60, "#955438", {4, 20, 60, 180, 320, 450}, {1, 2, 2}, 50}, {"Income Tax", TILE_TAX, false, 200, NULL, {0}, {0}, 0}, {"Reading Railroad", TILE_RAILROAD, false, 200, NULL, {0}, {9, 1, 4}, 0}, - {"Oriental Avenue", TILE_PROPERTY, false, 100, "#aae0fa", {6, 30, 90, 270, 400, 550}, {2, 1, 3}, 50}, + {"Rhode Island Avenue", TILE_PROPERTY, false, 100, "#aae0fa", {6, 30, 90, 270, 400, 550}, {2, 1, 3}, 50}, {"Chance", TILE_CHANCE, false, 0, NULL, {0}, {0}, 0}, {"Vermont Avenue", TILE_PROPERTY, false, 100, "#aae0fa", {6, 30, 90, 270, 400, 550}, {2, 2, 3}, 50}, {"Connecticut Avenue", TILE_PROPERTY, false, 120, "#aae0fa", {8, 40, 100, 300, 450, 600}, {2, 3, 3}, 50}, @@ -55,10 +55,10 @@ static const BoardTile MONOPOLY_BOARD[BOARD_SIZE] = { {"Kentucky Avenue", TILE_PROPERTY, false, 220, "#ed1b24", {18, 90, 250, 700, 875, 1050}, {5, 1, 3}, 150}, {"Chance", TILE_CHANCE, false, 0, NULL, {0}, {0}, 0}, {"Indiana Avenue", TILE_PROPERTY, false, 220, "#ed1b24", {18, 90, 250, 700, 875, 1050}, {5, 2, 3}, 150}, - {"Illnois Avenue", TILE_PROPERTY, false, 240, "#ed1b24", {20, 100, 300, 750, 925, 1100}, {5, 3, 3}, 150}, + {"Illinois Avenue", TILE_PROPERTY, false, 240, "#ed1b24", {20, 100, 300, 750, 925, 1100}, {5, 3, 3}, 150}, {"B. & O. Railroad", TILE_RAILROAD, false, 200, NULL, {0}, {9, 3, 4}, 0}, - {"Atlatic Avenue", TILE_PROPERTY, false, 260, "#fef200", {22, 110, 330, 800, 975, 1150}, {6, 1, 3}, 150}, - {"Ventura Avenue", TILE_PROPERTY, false, 260, "#fef200", {22, 110, 330, 800, 975, 1150}, {6, 2, 3}, 150}, + {"Atlantic Avenue", TILE_PROPERTY, false, 260, "#fef200", {22, 110, 330, 800, 975, 1150}, {6, 1, 3}, 150}, + {"Ventnor Avenue", TILE_PROPERTY, false, 260, "#fef200", {22, 110, 330, 800, 975, 1150}, {6, 2, 3}, 150}, {"Water Works", TILE_UTILITY, false, 150, NULL, {0}, {10, 2, 2}, 0}, {"Marvin Gardens", TILE_PROPERTY, false, 280, "#fef200", {24, 120, 360, 850, 1025, 1200}, {6, 3, 3}, 150}, {"Go To Jail", TILE_GO_TO_JAIL, true, 0, NULL, {0}, {0}, 0}, @@ -66,7 +66,7 @@ static const BoardTile MONOPOLY_BOARD[BOARD_SIZE] = { {"North Carolina Avenue", TILE_PROPERTY, false, 300, "#1fb25a", {26, 130, 390, 900, 1100, 1275}, {7, 2, 3}, 200}, {"Community Chest", TILE_COMMUNITY_CHEST, false, 0, NULL, {0}, {0}, 0}, {"Pennsylvania Avenue", TILE_PROPERTY, false, 320, "#1fb25a", {28, 150, 450, 1000, 1200, 1400}, {7, 3, 3}, 200}, - {"Shortline", TILE_RAILROAD, false, 200, NULL, {0}, {9, 4, 4}, 0}, + {"Short Line", TILE_RAILROAD, false, 200, NULL, {0}, {9, 4, 4}, 0}, {"Chance", TILE_CHANCE, false, 0, NULL, {0}, {0}, 0}, {"Park Place", TILE_PROPERTY, false, 350, "#0072bb", {35, 175, 500, 1100, 1300, 1500}, {8, 1, 2}, 200}, {"Luxury Tax", TILE_TAX, false, 100, NULL, {0}, {0}, 0}, diff --git a/games/monopoly/monopoly_game.cpp b/games/monopoly/monopoly_game.cpp index 94a1c09..01ba5e5 100644 --- a/games/monopoly/monopoly_game.cpp +++ b/games/monopoly/monopoly_game.cpp @@ -19,6 +19,8 @@ extern "C" { #include #include "DiceModalGame.h" #include "PropertyModalGame.h" +#include "BoardModalGame.h" +#include "MonopolyBoardRenderer.h" // --- Constructor --- @@ -56,11 +58,26 @@ bool MonopolyGame::update(const InputEvent& event) { if (active_modal) { bool modal_redraw = active_modal->update(event); if (modal_redraw) needs_redraw = true; - // If modal is dismissed, delete and return control - // Use dynamic_cast to check for modal type and dismissal + + // Check for specific modal types to handle their results auto dice_modal = dynamic_cast(active_modal); auto prop_modal = dynamic_cast(active_modal); - if ((dice_modal && dice_modal->is_dismissed()) || (prop_modal && prop_modal->is_dismissed())) { + auto board_modal = dynamic_cast(active_modal); + + if (dice_modal && dice_modal->is_dismissed()) { + delete active_modal; + active_modal = nullptr; + needs_redraw = true; + } else if (prop_modal && prop_modal->is_dismissed()) { + if (prop_modal->wants_to_buy()) { + const BoardTile* tile = &MONOPOLY_BOARD[p->position]; + p->balance -= tile->cost; + p->properties_owned[p->property_count++] = p->position; + } + delete active_modal; + active_modal = nullptr; + needs_redraw = true; + } else if (board_modal && board_modal->is_dismissed()) { delete active_modal; active_modal = nullptr; needs_redraw = true; @@ -75,56 +92,44 @@ bool MonopolyGame::update(const InputEvent& event) { break; case INPUT_BUTTON_1: // Select option switch (selected_action) { - case 0: // Roll - if (!has_rolled && !p->is_in_jail) { - int dice1 = (rand() % 6) + 1; - int dice2 = (rand() % 6) + 1; - int total = dice1 + dice2; - int old_pos = p->position; - p->position = (p->position + total) % BOARD_SIZE; - if (p->position < old_pos) p->balance += 200; - has_rolled = true; - needs_redraw = true; - // Store dice values and show dice modal - last_dice1 = dice1; - last_dice2 = dice2; - if (active_modal) delete active_modal; - active_modal = new DiceModalGame(width, height, renderer, gui, input_manager, dice1, dice2); - // Show property modal if landed on property/railroad/utility - const BoardTile* landed = &MONOPOLY_BOARD[p->position]; - if (landed->type == TILE_PROPERTY || landed->type == TILE_RAILROAD || landed->type == TILE_UTILITY) { - modal_property_index = p->position; - // Queue property modal after dice modal is dismissed - } - // TODO: Handle doubles, jail, landing effects - } - break; - case 1: // Buy - if (has_rolled) { - const BoardTile* tile = &MONOPOLY_BOARD[p->position]; - if ((tile->type == TILE_PROPERTY || tile->type == TILE_RAILROAD || tile->type == TILE_UTILITY) && p->balance >= tile->cost) { - bool owned = false; - for (int i = 0; i < p->property_count; ++i) { - if (p->properties_owned[i] == p->position) owned = true; - } - if (!owned) { - p->balance -= tile->cost; - p->properties_owned[p->property_count++] = p->position; - needs_redraw = true; + case 0: // Context Action + if (!has_rolled) { + // Roll Dice + if (!p->is_in_jail) { + int dice1 = (rand() % 6) + 1; + int dice2 = (rand() % 6) + 1; + int total = dice1 + dice2; + int old_pos = p->position; + p->position = (p->position + total) % BOARD_SIZE; + if (p->position < old_pos) p->balance += 200; + has_rolled = true; + needs_redraw = true; + // Store dice values and show dice modal + last_dice1 = dice1; + last_dice2 = dice2; + if (active_modal) delete active_modal; + active_modal = new DiceModalGame(width, height, renderer, gui, input_manager, dice1, dice2, &MONOPOLY_BOARD[old_pos], &MONOPOLY_BOARD[p->position], players, players_count); + // Show property modal if landed on property/railroad/utility + const BoardTile* landed = &MONOPOLY_BOARD[p->position]; + if (landed->type == TILE_PROPERTY || landed->type == TILE_RAILROAD || landed->type == TILE_UTILITY) { + modal_property_index = p->position; } } - // TODO: Check for ownership by other players - } - break; - case 2: // End Turn - if (has_rolled) { + } else { + // End Turn current_player_idx = (current_player_idx + 1) % players_count; has_rolled = false; double_rolls = 0; just_sent_to_jail = false; + selected_action = 0; // Reset selection for next player needs_redraw = true; } break; + case 1: // View Board + if (active_modal) delete active_modal; + active_modal = new BoardModalGame(width, height, renderer, gui, input_manager, players, players_count); + needs_redraw = true; + break; } break; default: @@ -132,7 +137,22 @@ bool MonopolyGame::update(const InputEvent& event) { } // If dice modal was just dismissed and a property modal is queued, show it if (!active_modal && modal_property_index >= 0) { - active_modal = new PropertyModalGame(width, height, renderer, gui, input_manager, &MONOPOLY_BOARD[modal_property_index]); + // Evaluate ownership and affordability + bool is_owned = false; + const char* owner_name = nullptr; + for (int i = 0; i < players_count; ++i) { + for (int j = 0; j < players[i].property_count; ++j) { + if (players[i].properties_owned[j] == modal_property_index) { + is_owned = true; + owner_name = players[i].name; + break; + } + } + if (is_owned) break; + } + bool can_afford = (p->balance >= MONOPOLY_BOARD[modal_property_index].cost); + + active_modal = new PropertyModalGame(width, height, renderer, gui, input_manager, &MONOPOLY_BOARD[modal_property_index], is_owned, owner_name, can_afford, players, players_count); modal_property_index = -1; needs_redraw = true; } @@ -146,63 +166,50 @@ void MonopolyGame::draw() { active_modal->draw(); return; } + + renderer->clear_buffer(); + + // --- Draw Board Perimeter --- + MonopolyBoardRenderer::draw_board_perimeter(renderer, width, height, players, players_count); + + // --- Inner Dashboard (Center Area) --- + int cw = width / 7; + int ch = height / 7; + int ix = cw + 2, iy = ch + 2; + int iw = width - 2 * cw - 4, ih = height - 2 * ch - 4; + Player* p = &players[current_player_idx]; const BoardTile* tile = &MONOPOLY_BOARD[p->position]; - // Title - renderer->draw_string_scaled(10, 10, "Monopoly", 3); - - // --- Player Stats (Right Side) --- - int stats_x = width - 200; - int y = 40 + 15 * current_player_idx; + // Stats Window in center char buf[128]; - // make a window showing player stats - LowLevelWindow* stats_win = gui->draw_new_window(stats_x - 10, y - 10 , 190, 120, p->name); - y += 20; - renderer->draw_string_scaled(stats_x, y, "Location:", 1); - renderer->draw_string_scaled(stats_x, y + 10, tile->name, 2); - y += 30; - // Money - snprintf(buf, sizeof(buf), "$%d", p->balance); - renderer->draw_string_scaled(stats_x, y, "Money:", 1); - renderer->draw_string_scaled(stats_x, y + 10, buf, 2); - y += 30; - // Properties - int prop_count = 0; - for (int i = 0; i < p->property_count; ++i) { - int prop_idx = p->properties_owned[i]; - if (prop_idx >= 0 && MONOPOLY_BOARD[prop_idx].type == TILE_PROPERTY) prop_count++; - } - snprintf(buf, sizeof(buf), "Properties: %d", prop_count); - renderer->draw_string_scaled(stats_x, y, buf, 1); - y += 10; - // Monopoly count - int monopoly_count = 0; - // For each group, check if player owns all properties in group - for (int group = 1; group <= 8; ++group) { - int group_total = 0, group_owned = 0; - for (int i = 0; i < BOARD_SIZE; ++i) { - if (MONOPOLY_BOARD[i].type == TILE_PROPERTY && MONOPOLY_BOARD[i].group[0] == group) { - group_total++; - for (int j = 0; j < p->property_count; ++j) { - if (p->properties_owned[j] == i) group_owned++; - } - } - } - if (group_total > 0 && group_total == group_owned) monopoly_count++; - } - snprintf(buf, sizeof(buf), "Monopolies: %d", monopoly_count); - renderer->draw_string_scaled(stats_x, y, buf, 1); + renderer->draw_string_scaled(ix + 5, iy + 5, "Monopoly", 2); + + int content_y = iy + 25; + snprintf(buf, sizeof(buf), "TURN: %s", p->name); + renderer->draw_string_scaled(ix + 5, content_y, buf, 1); + content_y += 12; + + snprintf(buf, sizeof(buf), "BAL: $%d", p->balance); + renderer->draw_string_scaled(ix + 5, content_y, buf, 1); + content_y += 12; + + snprintf(buf, sizeof(buf), "POS: %s", tile->name); + renderer->draw_string_scaled(ix + 5, content_y, buf, 1); + content_y += 15; + + // Draw action menu + const char* actions[ACTION_COUNT]; + if (!has_rolled) { + actions[0] = "Roll Dice"; + } else { + actions[0] = "End Turn"; + } + actions[1] = "View Board"; - // Draw action menu (highlight selected) - const char* actions[ACTION_COUNT] = {"Roll Dice", "Buy Property", "End Turn"}; for (int i = 0; i < ACTION_COUNT; ++i) { - int y = height - 80 + i * 20; snprintf(buf, sizeof(buf), "%s%s", (i == selected_action) ? "> " : " ", actions[i]); - renderer->draw_string_scaled(10, y, buf, 2); + renderer->draw_string_scaled(ix + 5, content_y, buf, 1); + content_y += 12; } - - // TODO: Draw board, all players, property ownership, jail, chance, etc. - // TODO: Add win/lose/game over conditions - // TODO: Add touch support, more UI, etc. } diff --git a/games/monopoly/monopoly_game.h b/games/monopoly/monopoly_game.h index 08a829b..0b87773 100644 --- a/games/monopoly/monopoly_game.h +++ b/games/monopoly/monopoly_game.h @@ -37,8 +37,8 @@ private: bool just_sent_to_jail; // UI selection state - int selected_action; // 0: Roll, 1: Buy, 2: End Turn - static constexpr int ACTION_COUNT = 3; + int selected_action; // 0: Context action (Roll or End Turn), 1: View Board + static constexpr int ACTION_COUNT = 2; // Modal games Game* active_modal = nullptr;