From d5a80235b4b1f230a0c0a6595464e346fbbe62ff Mon Sep 17 00:00:00 2001 From: Adolfo Reyna Date: Sat, 31 Jan 2026 23:02:25 -0500 Subject: [PATCH] improve board view with visual cues of the properties owned --- emulator/basic1_emulator | Bin 164672 -> 164672 bytes games/monopoly/BoardModalGame.h | 34 +++++++++---- games/monopoly/MonopolyBoardRenderer.h | 64 +++++++++++++++--------- games/monopoly/PropertyModalGame.h | 65 +++++++++++-------------- games/monopoly/monopoly_game.cpp | 2 +- 5 files changed, 95 insertions(+), 70 deletions(-) diff --git a/emulator/basic1_emulator b/emulator/basic1_emulator index b168b168790f2a66ff55ec888b5c14030849666d..32583355dc96b12071e3651a56551687f839ddcd 100755 GIT binary patch delta 11483 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 Yl;v@w9J_}`jPp3Z6TGEi3x!+%3ui65ApigX diff --git a/games/monopoly/BoardModalGame.h b/games/monopoly/BoardModalGame.h index 9d23d68..5ca6afc 100644 --- a/games/monopoly/BoardModalGame.h +++ b/games/monopoly/BoardModalGame.h @@ -12,10 +12,11 @@ class BoardModalGame : public Game { bool dismissed; Player* players; int players_count; + int observer_idx; 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) {} + BoardModalGame(uint16_t width, uint16_t height, LowLevelRenderer* renderer, LowLevelGUI* gui, InputManager* input_manager, Player* p, int count, int current_idx) + : Game(width, height, renderer, gui, input_manager), dismissed(false), players(p), players_count(count), observer_idx(current_idx) {} void init() override { dismissed = false; } Type get_type() const override { return Type::MONOPOLY_BOARD; } @@ -32,9 +33,7 @@ public: void draw() override { renderer->clear_buffer(); - MonopolyBoardRenderer::draw_board_perimeter(renderer, width, height, players, players_count, players[0].position /* Just for example, or -1 */); - // We'll pass -1 for the general board view to keep it clean - MonopolyBoardRenderer::draw_board_perimeter(renderer, width, height, players, players_count, -1); + MonopolyBoardRenderer::draw_board_perimeter(renderer, width, height, players, players_count, -1, observer_idx); // --- Inner UI --- int cw = width / 7; @@ -42,15 +41,30 @@ public: int ix = cw + 5, iy = ch + 5; int iw = width - 2 * cw - 10, ih = height - 2 * ch - 10; + // Window Background + renderer->draw_filled_rectangle(ix, iy, iw, ih, false, 0); + renderer->draw_rectangle(ix, iy, iw, ih, true, 1); + // Title - renderer->draw_string_scaled(ix + (iw - 144) / 2, iy + 10, "== BOARD ==", 2); + renderer->draw_string_scaled(ix + (iw - 144) / 2, iy + 5, "== BOARD ==", 2); + // Stats for current player + Player& curr = players[observer_idx]; + int total_wealth = curr.balance; + for (int i = 0; i < curr.property_count; ++i) { + total_wealth += MONOPOLY_BOARD[curr.properties_owned[i]].cost; + } + + char wealth_buf[64]; + snprintf(wealth_buf, sizeof(wealth_buf), "%s: Bal:$%d Wealth:$%d", curr.name, curr.balance, total_wealth); + renderer->draw_string_scaled(ix + 10, iy + 30, wealth_buf, 1); + // Legend for players - int ly = iy + 40; + int ly = iy + 45; 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); + char buf[128]; + 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 + 5, ly, buf, 1); ly += 12; } diff --git a/games/monopoly/MonopolyBoardRenderer.h b/games/monopoly/MonopolyBoardRenderer.h index 3d57ea2..11e310b 100644 --- a/games/monopoly/MonopolyBoardRenderer.h +++ b/games/monopoly/MonopolyBoardRenderer.h @@ -6,10 +6,22 @@ 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, int currentPlayerPos = -1) { + 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, int currentPlayerPos = -1, int observer_idx = -1) { if (index < 0 || index >= BOARD_SIZE) return; - bool isInverted = (index == currentPlayerPos); + // Find owner + int owner_id = -1; + for (int i = 0; i < players_count; i++) { + for (int j = 0; j < players[i].property_count; j++) { + if (players[i].properties_owned[j] == index) { + owner_id = i; + break; + } + } + if (owner_id != -1) break; + } + + bool isInverted = (index == currentPlayerPos) || (observer_idx != -1 && owner_id == observer_idx); if (isInverted) { renderer->draw_filled_rectangle(x, y, w, h, true, 1); renderer->set_text_color(false); // Black text on white background @@ -39,31 +51,39 @@ public: } if (isInverted) { - // In inverted mode, the bar should be black (transparent/inverted) - renderer->draw_filled_rectangle(bx, by, bw, bh, false, 0); // Clear the bar area back to black - renderer->set_text_color(true); // Groups on the bar are usually white text on black bar in this mode + // Background is white, so bar is black + renderer->draw_filled_rectangle(bx, by, bw, bh, false, 0); + renderer->set_text_color(true); // White text } else { renderer->draw_filled_rectangle(bx, by, bw, bh, true, 1); - renderer->set_text_color(false); // Black text on white bar + renderer->set_text_color(false); // Black text } // Group number char gbuf[2] = { (char)('0' + tile.group[0]), '\0' }; renderer->draw_string_scaled(bx + (bw - 6) / 2, by + (bh - 8) / 2, gbuf, 1); - if (isInverted) renderer->set_text_color(false); // Back to black text for the rest of the tile + if (isInverted) renderer->set_text_color(false); else renderer->set_text_color(true); } - char short_name[5] = {0}; - const char* full_name = tile.name; + char short_name[8] = {0}; + int s_ptr = 0; + // Add * if owned by someone else + if (owner_id != -1 && observer_idx != -1 && owner_id != observer_idx) { + short_name[s_ptr++] = '*'; + } + + const char* full_name = tile.name; if (is_corner) { - strncpy(short_name, full_name, 4); + int len = strlen(full_name); + if (len > 3) len = 3; + for(int i=0; i= 'a' && short_name[i] <= 'z') short_name[i] -= 32; @@ -81,38 +101,38 @@ public: } if (isInverted) { - renderer->set_text_color(true); // Reset for next tiles + renderer->set_text_color(true); } } - static void draw_board_perimeter(LowLevelRenderer* renderer, int width, int height, Player* players, int players_count, int currentPlayerPos = -1) { + static void draw_board_perimeter(LowLevelRenderer* renderer, int width, int height, Player* players, int players_count, int currentPlayerPos = -1, int observer_idx = -1) { 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, currentPlayerPos); // GO + draw_tile(renderer, width - cw, height - ch, cw, ch, 0, true, players, players_count, 0, currentPlayerPos, observer_idx); // 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, currentPlayerPos); + draw_tile(renderer, width - cw - i * rw, height - ch, rw, ch, i, false, players, players_count, 0, currentPlayerPos, observer_idx); } - draw_tile(renderer, 0, height - ch, cw, ch, 10, true, players, players_count, 1, currentPlayerPos); // JAIL + draw_tile(renderer, 0, height - ch, cw, ch, 10, true, players, players_count, 1, currentPlayerPos, observer_idx); // 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, currentPlayerPos); + draw_tile(renderer, 0, height - ch - (i - 10) * rh, cw, rh, i, false, players, players_count, 1, currentPlayerPos, observer_idx); } // --- Top Row: 20 to 30 (Left to Right) --- - draw_tile(renderer, 0, 0, cw, ch, 20, true, players, players_count, 2, currentPlayerPos); // FREE PARKING + draw_tile(renderer, 0, 0, cw, ch, 20, true, players, players_count, 2, currentPlayerPos, observer_idx); // 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, currentPlayerPos); + draw_tile(renderer, cw + (i - 21) * rw, 0, rw, ch, i, false, players, players_count, 2, currentPlayerPos, observer_idx); } - draw_tile(renderer, width - cw, 0, cw, ch, 30, true, players, players_count, 3, currentPlayerPos); // GO TO JAIL + draw_tile(renderer, width - cw, 0, cw, ch, 30, true, players, players_count, 3, currentPlayerPos, observer_idx); // 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, currentPlayerPos); + draw_tile(renderer, width - cw, ch + (i - 31) * rh, cw, rh, i, false, players, players_count, 3, currentPlayerPos, observer_idx); } } }; diff --git a/games/monopoly/PropertyModalGame.h b/games/monopoly/PropertyModalGame.h index c58ecdf..83602e4 100644 --- a/games/monopoly/PropertyModalGame.h +++ b/games/monopoly/PropertyModalGame.h @@ -61,16 +61,13 @@ public: } void draw() override { 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; + int win_w = width - 2 * (width / 7) - 4; + int win_h = height - 2 * (height / 7) - 4; + int win_x = width / 7 + 2; + int win_y = height / 7 + 2; char buf[128]; if (players && players_count > 0) { - // Find current player position (who is interacting with the modal) - // In MonopolyGame, p is players[current_player_idx] - // We don't have the idx here directly, but we can highlight the property itself int property_idx = -1; for(int i=0; i<40; i++) if(&MONOPOLY_BOARD[i] == property) property_idx = i; MonopolyBoardRenderer::draw_board_perimeter(renderer, width, height, players, players_count, property_idx); @@ -82,10 +79,10 @@ public: 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->draw_filled_rectangle(win_x + 4, win_y + 4, win_w - 8, 35, 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->draw_string_scaled(win_x + (win_w - (int)strlen(buf) * 12) / 2, win_y + 10, buf, 2); renderer->set_text_color(true); // Subtitle (Type) @@ -93,14 +90,14 @@ public: 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); + renderer->draw_string_scaled(win_x + (win_w - (int)strlen(buf) * 12) / 2, win_y + 45, buf, 2); // Info box center - int info_y = win_y + 60; + int info_y = win_y + 75; // Price snprintf(buf, sizeof(buf), "PRICE: $%d", property->cost); - renderer->draw_string_scaled(win_x + 15, info_y, buf, 1); - info_y += 15; + renderer->draw_string_scaled(win_x + 20, info_y, buf, 2); + info_y += 25; // Rent if (property->type == TILE_PROPERTY) { @@ -110,8 +107,8 @@ public: } 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; + renderer->draw_string_scaled(win_x + 20, info_y, buf, 2); + info_y += 25; // Owner if (is_owned && owner_name) { @@ -119,26 +116,24 @@ public: } else { snprintf(buf, sizeof(buf), "OWNER: %s", is_owned ? "PLAYER" : "BANK"); } - renderer->draw_string_scaled(win_x + 15, info_y, buf, 1); + renderer->draw_string_scaled(win_x + 20, info_y, buf, 2); // Action Buttons - int btn_y = win_y + win_h - 60; - int btn_w = win_w - 30; - int btn_h = 25; + int btn_y = win_y + win_h - 85; + int btn_w = win_w - 40; + int btn_h = 35; if (is_owned && owner_id != -1) { // Option: Pay Rent (A or B) - renderer->draw_filled_rectangle(win_x + 15, btn_y, btn_w, btn_h, true, 1); + renderer->draw_filled_rectangle(win_x + 20, btn_y, btn_w, btn_h, true, 1); renderer->set_text_color(false); int rent = 0; if (property->type == TILE_PROPERTY) { rent = property->rent[0]; } else if (property->type == TILE_RAILROAD) { - // Calculate Railroad rent based on owner's count int rr_count = 0; if (owner_id != -1 && owner_id < players_count) { for (int i = 0; i < players[owner_id].property_count; ++i) { - // Find the property in the global board to check type (or just trust the index) int prop_idx = players[owner_id].properties_owned[i]; if (MONOPOLY_BOARD[prop_idx].type == TILE_RAILROAD) { rr_count++; @@ -149,42 +144,38 @@ public: else if (rr_count == 2) rent = 50; else if (rr_count == 3) rent = 100; else if (rr_count == 4) rent = 200; - else rent = 25; // Fallback + else rent = 25; } else if (property->type == TILE_UTILITY) { - // Simplified 40 or use a more complex check rent = 40; } snprintf(buf, sizeof(buf), ">PAY RENT ($%d)", rent); - renderer->draw_string_scaled(win_x + (win_w - (int)strlen(buf) * 6) / 2, btn_y + 8, buf, 1); + renderer->draw_string_scaled(win_x + (win_w - (int)strlen(buf) * 12) / 2, btn_y + 10, buf, 2); renderer->set_text_color(true); } else if (!is_owned && can_afford) { - // Choice: Buy (A) or Pass (B). Changed to: A cycles, B selects. - // Buy Button - if (selected_choice == 0) renderer->draw_filled_rectangle(win_x + 15, btn_y, btn_w, btn_h, true, 1); - else renderer->draw_rectangle(win_x + 15, btn_y, btn_w, btn_h, true, 1); + if (selected_choice == 0) renderer->draw_filled_rectangle(win_x + 20, btn_y, btn_w, btn_h, true, 1); + else renderer->draw_rectangle(win_x + 20, btn_y, btn_w, btn_h, true, 1); if (selected_choice == 0) renderer->set_text_color(false); snprintf(buf, sizeof(buf), "%sBUY ($%d)", (selected_choice == 0 ? "> " : " "), property->cost); - renderer->draw_string_scaled(win_x + 20, btn_y + 8, buf, 1); + renderer->draw_string_scaled(win_x + 25, btn_y + 10, buf, 2); renderer->set_text_color(true); - btn_y += 30; + btn_y += 40; // Pass Button - if (selected_choice == 1) renderer->draw_filled_rectangle(win_x + 15, btn_y, btn_w, btn_h, true, 1); + if (selected_choice == 1) renderer->draw_filled_rectangle(win_x + 20, btn_y, btn_w, btn_h, true, 1); if (selected_choice == 1) renderer->set_text_color(false); snprintf(buf, sizeof(buf), "%sPASS", (selected_choice == 1 ? "> " : " ")); - renderer->draw_string_scaled(win_x + 20, btn_y + 8, buf, 1); + renderer->draw_string_scaled(win_x + 25, btn_y + 10, buf, 2); renderer->set_text_color(true); // Helpful hint - renderer->draw_string_scaled(win_x + 15, win_y + win_h - 15, "A:Next B:Sel", 1); + renderer->draw_string_scaled(win_x + 20, win_y + win_h - 20, "A:Next B:Sel", 1); } else { - // Only one option: PASS (B) (e.g. if owned by self or can't afford) - renderer->draw_filled_rectangle(win_x + 15, btn_y, btn_w, btn_h, true, 1); + renderer->draw_filled_rectangle(win_x + 20, btn_y, btn_w, btn_h, true, 1); renderer->set_text_color(false); - renderer->draw_string_scaled(win_x + 25, btn_y + 8, ">B PASS", 1); + renderer->draw_string_scaled(win_x + (win_w - 7 * 12) / 2, btn_y + 10, ">B PASS", 2); renderer->set_text_color(true); } } diff --git a/games/monopoly/monopoly_game.cpp b/games/monopoly/monopoly_game.cpp index 97a3d43..5319b08 100644 --- a/games/monopoly/monopoly_game.cpp +++ b/games/monopoly/monopoly_game.cpp @@ -380,7 +380,7 @@ bool MonopolyGame::update(const InputEvent& event) { } if (active_modal) delete active_modal; if (selected_action == (menu_count - 1)) { - active_modal = new BoardModalGame(width, height, renderer, gui, input_manager, players, players_count); + active_modal = new BoardModalGame(width, height, renderer, gui, input_manager, players, players_count, current_player_idx); needs_redraw = true; } else if (!has_rolled) { roll_dice_logic: