From fa4c6f00caca7a283de2a3aa764373abfbdef305 Mon Sep 17 00:00:00 2001 From: Adolfo Reyna Date: Sat, 31 Jan 2026 22:00:46 -0500 Subject: [PATCH] monopoly board --- emulator/basic1_emulator | Bin 143696 -> 145664 bytes games/monopoly/BoardModalGame.h | 59 ++++++++ games/monopoly/DiceModalGame.h | 99 ++++++++++-- games/monopoly/MonopolyBoardRenderer.h | 100 +++++++++++++ games/monopoly/PropertyModalGame.cpp | 0 games/monopoly/PropertyModalGame.h | 128 +++++++++++++--- games/monopoly/monopoly_board.h | 10 +- games/monopoly/monopoly_game.cpp | 199 +++++++++++++------------ games/monopoly/monopoly_game.h | 4 +- 9 files changed, 464 insertions(+), 135 deletions(-) create mode 100644 games/monopoly/BoardModalGame.h create mode 100644 games/monopoly/MonopolyBoardRenderer.h delete mode 100644 games/monopoly/PropertyModalGame.cpp diff --git a/emulator/basic1_emulator b/emulator/basic1_emulator index ded0e14ae02b2fc1ddb2fa6a13bed173bbd58964..76f221d0e701067eb989f8011ff6763794f9b627 100755 GIT binary patch delta 32321 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 delta 23780 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 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;