// ============================================================================ // MONOPOLY GAME IMPLEMENTATION (for custom console) // ============================================================================ // Refactored from console version to use Game interface and rendering/input system #include "monopoly_game.h" #ifdef __cplusplus extern "C" { #endif #include "player.h" #ifdef __cplusplus } #endif #include #include #include #include #include "DiceModalGame.h" #include "PropertyModalGame.h" #include "BoardModalGame.h" #include "MonopolyBoardRenderer.h" // --- Constructor --- MonopolyGame::MonopolyGame(uint16_t width, uint16_t height, LowLevelRenderer* renderer, LowLevelGUI* gui, InputManager* input_manager) : Game(width, height, renderer, gui, input_manager) { players_count = 2; current_player_idx = 0; has_rolled = false; double_rolls = 0; just_sent_to_jail = false; } // --- Initialize game state --- void MonopolyGame::init() { // Hardcoded 2 players for minimal version init_player(&players[0], 0, "Elias", "Top Hat"); init_player(&players[1], 1, "Adolfo", "Racecar"); players_count = 2; current_player_idx = 0; has_rolled = false; double_rolls = 0; just_sent_to_jail = false; selected_action = 0; srand(time(NULL)); if (active_modal) { delete active_modal; active_modal = nullptr; } // TODO: Reset all board state, property ownership, etc. } // --- Handle input events (minimal: roll, buy, end turn) --- bool MonopolyGame::update(const InputEvent& event) { Player* p = &players[current_player_idx]; bool needs_redraw = false; // If a modal is active, delegate input and check for dismissal if (active_modal) { bool modal_redraw = active_modal->update(event); if (modal_redraw) needs_redraw = true; // Check for specific modal types to handle their results using custom get_type() DiceModalGame* dice_modal = (active_modal->get_type() == Game::Type::MONOPOLY_DICE) ? static_cast(active_modal) : nullptr; PropertyModalGame* prop_modal = (active_modal->get_type() == Game::Type::MONOPOLY_PROPERTY) ? static_cast(active_modal) : nullptr; BoardModalGame* board_modal = (active_modal->get_type() == Game::Type::MONOPOLY_BOARD) ? static_cast(active_modal) : nullptr; if (dice_modal && dice_modal->is_dismissed()) { delete active_modal; active_modal = nullptr; needs_redraw = true; // Immediately check if we need to show a property modal if (modal_property_index >= 0) { bool is_owned = false; const char* owner_name = nullptr; 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] == modal_property_index) { is_owned = true; owner_name = players[i].name; owner_id = i; 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, owner_id, can_afford, players, players_count); modal_property_index = -1; } return needs_redraw; } else if (prop_modal && prop_modal->is_dismissed()) { if (prop_modal->wants_to_buy()) { const BoardTile* tile = &MONOPOLY_BOARD[p->position]; if (p->balance >= tile->cost) { p->balance -= tile->cost; p->properties_owned[p->property_count++] = p->position; } } else if (prop_modal->wants_to_pay_rent()) { const BoardTile* tile = &MONOPOLY_BOARD[p->position]; int rent = 0; if (tile->type == TILE_PROPERTY) { // Logic for rent: If owner has all properties of group, rent is doubled (base only) // For now, let's just use rent[0] as requested, but we should probably eventually // check for houses. Let's stick to rent[0] to match the modal's display. rent = tile->rent[0]; } else if (tile->type == TILE_RAILROAD) { // Utility logic for Railroads: 1:25, 2:50, 3:100, 4:200 int owner_id = prop_modal->get_owner_id(); int rr_count = 0; if (owner_id != -1) { for (int i = 0; i < players[owner_id].property_count; ++i) { if (MONOPOLY_BOARD[players[owner_id].properties_owned[i]].type == TILE_RAILROAD) { rr_count++; } } } if (rr_count == 1) rent = 25; 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 if (tile->type == TILE_UTILITY) { // Utility: 4x dice if 1 owned, 10x if both. // Since we don't have the dice roll here, let's use a fixed 40 for now // or calculate it from last_dice1 + last_dice2. int total_dice = last_dice1 + last_dice2; int owner_id = prop_modal->get_owner_id(); int utility_count = 0; if (owner_id != -1) { for (int i = 0; i < players[owner_id].property_count; ++i) { if (MONOPOLY_BOARD[players[owner_id].properties_owned[i]].type == TILE_UTILITY) { utility_count++; } } } rent = (utility_count == 2) ? (total_dice * 10) : (total_dice * 4); } int o_id = prop_modal->get_owner_id(); if (o_id != -1 && (int)current_player_idx != o_id) { p->balance -= rent; players[o_id].balance += rent; } } 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; } return needs_redraw; } switch (event.type) { case INPUT_BUTTON_0: // Cycle options selected_action = (selected_action + 1) % ACTION_COUNT; needs_redraw = true; break; case INPUT_BUTTON_1: // Select option switch (selected_action) { 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; } } } 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: break; } return needs_redraw; } // --- Draw game state (minimal: player info, current tile, actions) --- void MonopolyGame::draw() { // If a modal is active, draw it and return if (active_modal) { active_modal->draw(); return; } renderer->clear_buffer(); Player* p = &players[current_player_idx]; const BoardTile* tile = &MONOPOLY_BOARD[p->position]; // --- Draw Board Perimeter --- MonopolyBoardRenderer::draw_board_perimeter(renderer, width, height, players, players_count, p->position); // --- 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; // Stats Window in center char buf[128]; renderer->draw_string_scaled(ix + 5, iy + 5, "Monopoly", 2); int content_y = iy + 25; snprintf(buf, sizeof(buf), "%s", p->name); renderer->draw_string_scaled(ix + 5, content_y, buf, 2); content_y += 20; 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"; for (int i = 0; i < ACTION_COUNT; ++i) { snprintf(buf, sizeof(buf), "%s%s", (i == selected_action) ? "> " : " ", actions[i]); renderer->draw_string_scaled(ix + 5, content_y, buf, 1); content_y += 12; } }