monopoly board

This commit is contained in:
Adolfo Reyna
2026-01-31 22:00:46 -05:00
parent 561f7c5951
commit fa4c6f00ca
9 changed files with 464 additions and 135 deletions

View File

@@ -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; }
};

View File

@@ -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; }
};

View File

@@ -0,0 +1,100 @@
#pragma once
#include "../../display/low_level_render.h"
#include "monopoly_board.h"
#include "player.h"
#include <cstring>
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);
}
}
};

View File

@@ -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; }
};

View File

@@ -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},

View File

@@ -19,6 +19,8 @@ extern "C" {
#include <time.h>
#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<DiceModalGame*>(active_modal);
auto prop_modal = dynamic_cast<PropertyModalGame*>(active_modal);
if ((dice_modal && dice_modal->is_dismissed()) || (prop_modal && prop_modal->is_dismissed())) {
auto board_modal = dynamic_cast<BoardModalGame*>(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.
}

View File

@@ -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;