diff --git a/emulator/basic1_emulator b/emulator/basic1_emulator index b168b16..3258335 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 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: