monopoly board
This commit is contained in:
Binary file not shown.
59
games/monopoly/BoardModalGame.h
Normal file
59
games/monopoly/BoardModalGame.h
Normal 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; }
|
||||||
|
};
|
||||||
@@ -4,13 +4,51 @@
|
|||||||
#include "../../display/low_level_render.h"
|
#include "../../display/low_level_render.h"
|
||||||
#include "../../display/low_level_gui.h"
|
#include "../../display/low_level_gui.h"
|
||||||
#include "input_manager.h"
|
#include "input_manager.h"
|
||||||
|
#include "MonopolyBoardRenderer.h"
|
||||||
|
|
||||||
class DiceModalGame : public Game {
|
class DiceModalGame : public Game {
|
||||||
int dice1, dice2;
|
int dice1, dice2;
|
||||||
|
const BoardTile *from_tile, *to_tile;
|
||||||
|
Player* players;
|
||||||
|
int players_count;
|
||||||
bool dismissed;
|
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:
|
public:
|
||||||
DiceModalGame(uint16_t width, uint16_t height, LowLevelRenderer* renderer, LowLevelGUI* gui, InputManager* input_manager, int d1, int d2)
|
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), dismissed(false) {}
|
: 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; }
|
void init() override { dismissed = false; }
|
||||||
bool update(const InputEvent& event) override {
|
bool update(const InputEvent& event) override {
|
||||||
if (event.type == INPUT_BUTTON_0 || event.type == INPUT_BUTTON_1) {
|
if (event.type == INPUT_BUTTON_0 || event.type == INPUT_BUTTON_1) {
|
||||||
@@ -20,13 +58,56 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
void draw() override {
|
void draw() override {
|
||||||
int win_w = 220, win_h = 120;
|
renderer->clear_buffer();
|
||||||
int win_x = (width - win_w) / 2, win_y = (height - win_h) / 2;
|
|
||||||
char buf[64];
|
MonopolyBoardRenderer::draw_board_perimeter(renderer, width, height, players, players_count);
|
||||||
gui->draw_new_window(win_x, win_y, win_w, win_h, "Dice Roll");
|
|
||||||
snprintf(buf, sizeof(buf), "You rolled: %d + %d", dice1, dice2);
|
// --- Inner UI (Center Area) ---
|
||||||
renderer->draw_string_scaled(win_x + 30, win_y + 40, buf, 2);
|
int cw = width / 7;
|
||||||
renderer->draw_string_scaled(10, height - 20, "Press any button...", 2);
|
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; }
|
bool is_dismissed() const { return dismissed; }
|
||||||
};
|
};
|
||||||
|
|||||||
100
games/monopoly/MonopolyBoardRenderer.h
Normal file
100
games/monopoly/MonopolyBoardRenderer.h
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -5,43 +5,125 @@
|
|||||||
#include "../../display/low_level_gui.h"
|
#include "../../display/low_level_gui.h"
|
||||||
#include "input_manager.h"
|
#include "input_manager.h"
|
||||||
#include "monopoly_board.h"
|
#include "monopoly_board.h"
|
||||||
|
#include "player.h"
|
||||||
|
#include "MonopolyBoardRenderer.h"
|
||||||
|
|
||||||
class PropertyModalGame : public Game {
|
class PropertyModalGame : public Game {
|
||||||
const BoardTile* property;
|
const BoardTile* property;
|
||||||
bool dismissed;
|
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:
|
public:
|
||||||
PropertyModalGame(uint16_t width, uint16_t height, LowLevelRenderer* renderer, LowLevelGUI* gui, InputManager* input_manager, const BoardTile* prop)
|
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) {}
|
: 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) {
|
||||||
void init() override { dismissed = false; }
|
if (is_owned || !can_afford) selected_choice = 1;
|
||||||
|
}
|
||||||
|
void init() override { dismissed = false; buy_requested = false; }
|
||||||
bool update(const InputEvent& event) override {
|
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;
|
dismissed = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
void draw() override {
|
void draw() override {
|
||||||
int win_w = 320, win_h = 180;
|
renderer->clear_buffer();
|
||||||
int win_x = (width - win_w) / 2, win_y = (height - win_h) / 2;
|
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];
|
char buf[128];
|
||||||
snprintf(buf, sizeof(buf), "Property: %s", property->name);
|
|
||||||
gui->draw_new_window(win_x, win_y, win_w, win_h, buf);
|
if (players && players_count > 0) {
|
||||||
int py = win_y + 30;
|
MonopolyBoardRenderer::draw_board_perimeter(renderer, width, height, players, players_count);
|
||||||
char pbuf[128];
|
}
|
||||||
if (property->type == TILE_PROPERTY) {
|
|
||||||
snprintf(pbuf, sizeof(pbuf), "Cost: $%d", property->cost);
|
// Window background (White box)
|
||||||
renderer->draw_string_scaled(win_x + 20, py, pbuf, 2);
|
renderer->draw_filled_rectangle(win_x, win_y, win_w, win_h, false, 0); // Clear background
|
||||||
py += 25;
|
renderer->draw_rectangle(win_x, win_y, win_w, win_h, true, 2);
|
||||||
snprintf(pbuf, sizeof(pbuf), "Rent: $%d", property->rent[0]);
|
renderer->draw_rectangle(win_x + 3, win_y + 3, win_w - 6, win_h - 6, true, 1);
|
||||||
renderer->draw_string_scaled(win_x + 20, py, pbuf, 2);
|
|
||||||
py += 25;
|
// Header Title Bar
|
||||||
snprintf(pbuf, sizeof(pbuf), "House Cost: $%d", property->house_cost);
|
renderer->draw_filled_rectangle(win_x + 4, win_y + 4, win_w - 8, 30, true, 1);
|
||||||
renderer->draw_string_scaled(win_x + 20, py, pbuf, 2);
|
renderer->set_text_color(false); // White text
|
||||||
} else if (property->type == TILE_RAILROAD || property->type == TILE_UTILITY) {
|
snprintf(buf, sizeof(buf), "%s", property->name);
|
||||||
snprintf(pbuf, sizeof(pbuf), "Cost: $%d", property->cost);
|
renderer->draw_string_scaled(win_x + (win_w - (int)strlen(buf) * 6) / 2, win_y + 8, buf, 1);
|
||||||
renderer->draw_string_scaled(win_x + 20, py, pbuf, 2);
|
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 is_dismissed() const { return dismissed; }
|
||||||
|
bool wants_to_buy() const { return buy_requested; }
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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},
|
{"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},
|
{"Income Tax", TILE_TAX, false, 200, NULL, {0}, {0}, 0},
|
||||||
{"Reading Railroad", TILE_RAILROAD, false, 200, NULL, {0}, {9, 1, 4}, 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},
|
{"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},
|
{"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},
|
{"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},
|
{"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},
|
{"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},
|
{"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},
|
{"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},
|
{"Atlantic 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},
|
{"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},
|
{"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},
|
{"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},
|
{"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},
|
{"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},
|
{"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},
|
{"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},
|
{"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},
|
{"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},
|
{"Luxury Tax", TILE_TAX, false, 100, NULL, {0}, {0}, 0},
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ extern "C" {
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include "DiceModalGame.h"
|
#include "DiceModalGame.h"
|
||||||
#include "PropertyModalGame.h"
|
#include "PropertyModalGame.h"
|
||||||
|
#include "BoardModalGame.h"
|
||||||
|
#include "MonopolyBoardRenderer.h"
|
||||||
|
|
||||||
|
|
||||||
// --- Constructor ---
|
// --- Constructor ---
|
||||||
@@ -56,11 +58,26 @@ bool MonopolyGame::update(const InputEvent& event) {
|
|||||||
if (active_modal) {
|
if (active_modal) {
|
||||||
bool modal_redraw = active_modal->update(event);
|
bool modal_redraw = active_modal->update(event);
|
||||||
if (modal_redraw) needs_redraw = true;
|
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 dice_modal = dynamic_cast<DiceModalGame*>(active_modal);
|
||||||
auto prop_modal = dynamic_cast<PropertyModalGame*>(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;
|
delete active_modal;
|
||||||
active_modal = nullptr;
|
active_modal = nullptr;
|
||||||
needs_redraw = true;
|
needs_redraw = true;
|
||||||
@@ -75,8 +92,10 @@ bool MonopolyGame::update(const InputEvent& event) {
|
|||||||
break;
|
break;
|
||||||
case INPUT_BUTTON_1: // Select option
|
case INPUT_BUTTON_1: // Select option
|
||||||
switch (selected_action) {
|
switch (selected_action) {
|
||||||
case 0: // Roll
|
case 0: // Context Action
|
||||||
if (!has_rolled && !p->is_in_jail) {
|
if (!has_rolled) {
|
||||||
|
// Roll Dice
|
||||||
|
if (!p->is_in_jail) {
|
||||||
int dice1 = (rand() % 6) + 1;
|
int dice1 = (rand() % 6) + 1;
|
||||||
int dice2 = (rand() % 6) + 1;
|
int dice2 = (rand() % 6) + 1;
|
||||||
int total = dice1 + dice2;
|
int total = dice1 + dice2;
|
||||||
@@ -89,42 +108,28 @@ bool MonopolyGame::update(const InputEvent& event) {
|
|||||||
last_dice1 = dice1;
|
last_dice1 = dice1;
|
||||||
last_dice2 = dice2;
|
last_dice2 = dice2;
|
||||||
if (active_modal) delete active_modal;
|
if (active_modal) delete active_modal;
|
||||||
active_modal = new DiceModalGame(width, height, renderer, gui, input_manager, dice1, dice2);
|
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
|
// Show property modal if landed on property/railroad/utility
|
||||||
const BoardTile* landed = &MONOPOLY_BOARD[p->position];
|
const BoardTile* landed = &MONOPOLY_BOARD[p->position];
|
||||||
if (landed->type == TILE_PROPERTY || landed->type == TILE_RAILROAD || landed->type == TILE_UTILITY) {
|
if (landed->type == TILE_PROPERTY || landed->type == TILE_RAILROAD || landed->type == TILE_UTILITY) {
|
||||||
modal_property_index = p->position;
|
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO: Check for ownership by other players
|
} else {
|
||||||
}
|
// End Turn
|
||||||
break;
|
|
||||||
case 2: // End Turn
|
|
||||||
if (has_rolled) {
|
|
||||||
current_player_idx = (current_player_idx + 1) % players_count;
|
current_player_idx = (current_player_idx + 1) % players_count;
|
||||||
has_rolled = false;
|
has_rolled = false;
|
||||||
double_rolls = 0;
|
double_rolls = 0;
|
||||||
just_sent_to_jail = false;
|
just_sent_to_jail = false;
|
||||||
|
selected_action = 0; // Reset selection for next player
|
||||||
needs_redraw = true;
|
needs_redraw = true;
|
||||||
}
|
}
|
||||||
break;
|
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;
|
break;
|
||||||
default:
|
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 dice modal was just dismissed and a property modal is queued, show it
|
||||||
if (!active_modal && modal_property_index >= 0) {
|
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;
|
modal_property_index = -1;
|
||||||
needs_redraw = true;
|
needs_redraw = true;
|
||||||
}
|
}
|
||||||
@@ -146,63 +166,50 @@ void MonopolyGame::draw() {
|
|||||||
active_modal->draw();
|
active_modal->draw();
|
||||||
return;
|
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];
|
Player* p = &players[current_player_idx];
|
||||||
const BoardTile* tile = &MONOPOLY_BOARD[p->position];
|
const BoardTile* tile = &MONOPOLY_BOARD[p->position];
|
||||||
|
|
||||||
// Title
|
// Stats Window in center
|
||||||
renderer->draw_string_scaled(10, 10, "Monopoly", 3);
|
|
||||||
|
|
||||||
// --- Player Stats (Right Side) ---
|
|
||||||
int stats_x = width - 200;
|
|
||||||
int y = 40 + 15 * current_player_idx;
|
|
||||||
char buf[128];
|
char buf[128];
|
||||||
// make a window showing player stats
|
renderer->draw_string_scaled(ix + 5, iy + 5, "Monopoly", 2);
|
||||||
LowLevelWindow* stats_win = gui->draw_new_window(stats_x - 10, y - 10 , 190, 120, p->name);
|
|
||||||
y += 20;
|
int content_y = iy + 25;
|
||||||
renderer->draw_string_scaled(stats_x, y, "Location:", 1);
|
snprintf(buf, sizeof(buf), "TURN: %s", p->name);
|
||||||
renderer->draw_string_scaled(stats_x, y + 10, tile->name, 2);
|
renderer->draw_string_scaled(ix + 5, content_y, buf, 1);
|
||||||
y += 30;
|
content_y += 12;
|
||||||
// Money
|
|
||||||
snprintf(buf, sizeof(buf), "$%d", p->balance);
|
snprintf(buf, sizeof(buf), "BAL: $%d", p->balance);
|
||||||
renderer->draw_string_scaled(stats_x, y, "Money:", 1);
|
renderer->draw_string_scaled(ix + 5, content_y, buf, 1);
|
||||||
renderer->draw_string_scaled(stats_x, y + 10, buf, 2);
|
content_y += 12;
|
||||||
y += 30;
|
|
||||||
// Properties
|
snprintf(buf, sizeof(buf), "POS: %s", tile->name);
|
||||||
int prop_count = 0;
|
renderer->draw_string_scaled(ix + 5, content_y, buf, 1);
|
||||||
for (int i = 0; i < p->property_count; ++i) {
|
content_y += 15;
|
||||||
int prop_idx = p->properties_owned[i];
|
|
||||||
if (prop_idx >= 0 && MONOPOLY_BOARD[prop_idx].type == TILE_PROPERTY) prop_count++;
|
// Draw action menu
|
||||||
}
|
const char* actions[ACTION_COUNT];
|
||||||
snprintf(buf, sizeof(buf), "Properties: %d", prop_count);
|
if (!has_rolled) {
|
||||||
renderer->draw_string_scaled(stats_x, y, buf, 1);
|
actions[0] = "Roll Dice";
|
||||||
y += 10;
|
} else {
|
||||||
// Monopoly count
|
actions[0] = "End Turn";
|
||||||
int monopoly_count = 0;
|
}
|
||||||
// For each group, check if player owns all properties in group
|
actions[1] = "View Board";
|
||||||
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);
|
|
||||||
|
|
||||||
// Draw action menu (highlight selected)
|
|
||||||
const char* actions[ACTION_COUNT] = {"Roll Dice", "Buy Property", "End Turn"};
|
|
||||||
for (int i = 0; i < ACTION_COUNT; ++i) {
|
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]);
|
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.
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ private:
|
|||||||
bool just_sent_to_jail;
|
bool just_sent_to_jail;
|
||||||
|
|
||||||
// UI selection state
|
// UI selection state
|
||||||
int selected_action; // 0: Roll, 1: Buy, 2: End Turn
|
int selected_action; // 0: Context action (Roll or End Turn), 1: View Board
|
||||||
static constexpr int ACTION_COUNT = 3;
|
static constexpr int ACTION_COUNT = 2;
|
||||||
|
|
||||||
// Modal games
|
// Modal games
|
||||||
Game* active_modal = nullptr;
|
Game* active_modal = nullptr;
|
||||||
|
|||||||
Reference in New Issue
Block a user