// DiceModalGame.h #pragma once #include "../../display/low_level_gui.h" #include "../../display/low_level_render.h" #include "../../lib/game.h" #include "ModalButtonHelper.h" #include "MonopolyBoardRenderer.h" #include "input_manager.h" #include #include class DiceModalGame : public Game { int dice1, dice2; const BoardTile *from_tile, *to_tile; Player *players; int players_count; bool dismissed; int from_pos; int correct_destination; int options[3]; bool option_visible[3]; int selected_choice; bool show_error; 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, 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) { // Find from_pos from_pos = 0; for (int i = 0; i < 40; i++) { if (&MONOPOLY_BOARD[i] == from_tile) { from_pos = i; break; } } correct_destination = (from_pos + dice1 + dice2) % MONOPOLY_BOARD_SIZE; selected_choice = -1; show_error = false; for (int i = 0; i < 3; i++) option_visible[i] = true; // Generate fake options int fake1 = (from_pos + (rand() % 11 + 2)) % MONOPOLY_BOARD_SIZE; if (fake1 == correct_destination) fake1 = (fake1 + 1) % MONOPOLY_BOARD_SIZE; int fake2 = (from_pos + (rand() % 11 + 2)) % MONOPOLY_BOARD_SIZE; while (fake2 == correct_destination || fake2 == fake1) { fake2 = (from_pos + (rand() % 11 + 2)) % MONOPOLY_BOARD_SIZE; } int rand_pos = rand() % 3; if (rand_pos == 0) { options[0] = correct_destination; options[1] = fake1; options[2] = fake2; } else if (rand_pos == 1) { options[0] = fake1; options[1] = correct_destination; options[2] = fake2; } else { options[0] = fake1; options[1] = fake2; options[2] = correct_destination; } } void init() override { dismissed = false; selected_choice = -1; show_error = false; for (int i = 0; i < 3; i++) option_visible[i] = true; ModalButtonHelper::set_monopoly_regions(input_manager, width, height); } Type get_type() const override { return Type::MONOPOLY_DICE; } bool update(const InputEvent &event) override { if (event.type == INPUT_BUTTON_0) { // Select int start_choice = selected_choice; do { selected_choice = (selected_choice + 1) % 3; } while (!option_visible[selected_choice] && selected_choice != start_choice); show_error = false; return true; } if (event.type == INPUT_BUTTON_1) { if (selected_choice == -1 || !option_visible[selected_choice]) return false; if (options[selected_choice] == correct_destination) { dismissed = true; } else { option_visible[selected_choice] = false; show_error = true; selected_choice = -1; } return true; } return false; } void draw() override { renderer->clear_buffer(); // Draw the restricted board perimeter MonopolyBoardRenderer::draw_board_perimeter( renderer, width, height, players, players_count, -1, -1, from_pos, (from_pos + 12) % MONOPOLY_BOARD_SIZE); // --- Inner UI (Center Area) --- int cw = width / 7; int ch = height / 7; int ix = cw + 2; int iy = ch + 2; int iw = width - 2 * cw - 4; int ih = height - 2 * ch - 4; // Window background (White box) renderer->draw_filled_rectangle(ix, iy, iw, ih, false, 0); renderer->draw_rectangle(ix, iy, iw, ih, true, 2); renderer->draw_rectangle(ix + 3, iy + 3, iw - 6, ih - 6, true, 1); // Header renderer->draw_filled_rectangle(ix + 4, iy + 4, iw - 8, 30, true, 1); renderer->set_text_color(false); renderer->draw_string_scaled( ix + (iw - (int)strlen("DICE CHALLENGE") * 12) / 2, iy + 10, "DICE CHALLENGE", 2); renderer->set_text_color(true); // Dice int dice_size = 40; int dice_y = iy + 45; draw_die(ix + 20, dice_y, dice_size, dice1); draw_die(ix + 20 + dice_size + 10, dice_y, dice_size, dice2); // --- Prompt --- int prompt_y = dice_y + dice_size + 10; renderer->draw_string_scaled(ix + 20, prompt_y, "WHERE WILL YOU LAND?", 1); // --- Options --- int opt_y = prompt_y + 20; for (int i = 0; i < 3; i++) { if (!option_visible[i]) { opt_y += 25; continue; } char opt_buf[64]; snprintf(opt_buf, sizeof(opt_buf), "%s %d: %s", (selected_choice == i ? ">" : " "), i + 1, MONOPOLY_BOARD[options[i]].name); if (selected_choice == i) renderer->draw_filled_rectangle(ix + 15, opt_y - 2, iw - 30, 22, true, 1); if (selected_choice == i) renderer->set_text_color(false); // Truncate name if too long if (strlen(opt_buf) > 30) opt_buf[30] = '\0'; renderer->draw_string_scaled(ix + 20, opt_y, opt_buf, 2); renderer->set_text_color(true); opt_y += 25; } if (show_error) { renderer->draw_string_scaled(ix + (iw - 10 * 12) / 2, iy + ih - 30, "TRY AGAIN!", 2); } ModalButtonHelper::draw_virtual_buttons(renderer, input_manager); } bool is_dismissed() const { return dismissed; } };