Add virtual touch buttons for Monopoly game and centralize configuration in ModalButtonHelper
This commit is contained in:
@@ -16,9 +16,9 @@
|
||||
// ============================================================================
|
||||
|
||||
// ---- SELECT YOUR BOARD HERE ----
|
||||
// #define BOARD_FEATHER_TFT // Feather RP2350 + 4.0" TFT ST7796
|
||||
#define BOARD_FEATHER_TFT // Feather RP2350 + 4.0" TFT ST7796
|
||||
// #define BOARD_PICO2_TFT // Pico 2 + 4.0" TFT ST7796
|
||||
#define BOARD_PICO2_EINK // Pico 2 + E-Ink Display
|
||||
// #define BOARD_PICO2_EINK // Pico 2 + E-Ink Display
|
||||
// --------------------------------
|
||||
|
||||
// ============================================================================
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "monopoly_board.h"
|
||||
#include "player.h"
|
||||
#include "MonopolyBoardRenderer.h"
|
||||
#include "ModalButtonHelper.h"
|
||||
|
||||
class BoardModalGame : public Game {
|
||||
bool dismissed;
|
||||
@@ -18,7 +19,10 @@ public:
|
||||
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; }
|
||||
void init() override {
|
||||
dismissed = false;
|
||||
ModalButtonHelper::set_monopoly_regions(input_manager, width, height);
|
||||
}
|
||||
Type get_type() const override { return Type::MONOPOLY_BOARD; }
|
||||
|
||||
bool update(const InputEvent& event) override {
|
||||
@@ -119,6 +123,8 @@ public:
|
||||
}
|
||||
|
||||
renderer->draw_string_scaled(ix + (iw - 12 * 6) / 2, iy + ih - 15, "PRESS BUTTON", 1);
|
||||
|
||||
ModalButtonHelper::draw_virtual_buttons(renderer, input_manager);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "MonopolyBoardRenderer.h"
|
||||
#include "sprites.h"
|
||||
#include "ModalButtonHelper.h"
|
||||
|
||||
class ChanceModalGame : public Game {
|
||||
const ChanceCard* card;
|
||||
@@ -21,7 +22,10 @@ public:
|
||||
ChanceModalGame(uint16_t width, uint16_t height, LowLevelRenderer* renderer, LowLevelGUI* gui, InputManager* input_manager, const ChanceCard* c, Player* p_list = nullptr, int p_count = 0, int p_pos = -1)
|
||||
: Game(width, height, renderer, gui, input_manager), card(c), dismissed(false), players(p_list), players_count(p_count), player_pos(p_pos) {}
|
||||
|
||||
void init() override { dismissed = false; }
|
||||
void init() override {
|
||||
dismissed = false;
|
||||
ModalButtonHelper::set_monopoly_regions(input_manager, width, height);
|
||||
}
|
||||
Type get_type() const override { return Type::MONOPOLY_CHANCE; }
|
||||
|
||||
bool update(const InputEvent& event) override {
|
||||
@@ -89,6 +93,8 @@ public:
|
||||
|
||||
// Prompt
|
||||
renderer->draw_string_scaled(ix + (iw - 12 * 12) / 2, iy + ih - 20, "Press BUTTON", 2);
|
||||
|
||||
ModalButtonHelper::draw_virtual_buttons(renderer, input_manager);
|
||||
}
|
||||
|
||||
bool is_dismissed() const { return dismissed; }
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "player.h"
|
||||
#include "MonopolyBoardRenderer.h"
|
||||
#include "sprites.h"
|
||||
#include "ModalButtonHelper.h"
|
||||
|
||||
class CommunityChestModalGame : public Game {
|
||||
const CommunityCard* card;
|
||||
@@ -20,7 +21,10 @@ public:
|
||||
CommunityChestModalGame(uint16_t width, uint16_t height, LowLevelRenderer* renderer, LowLevelGUI* gui, InputManager* input_manager, const CommunityCard* c, Player* p_list = nullptr, int p_count = 0, int p_pos = -1)
|
||||
: Game(width, height, renderer, gui, input_manager), card(c), dismissed(false), players(p_list), players_count(p_count), player_pos(p_pos) {}
|
||||
|
||||
void init() override { dismissed = false; }
|
||||
void init() override {
|
||||
dismissed = false;
|
||||
ModalButtonHelper::set_monopoly_regions(input_manager, width, height);
|
||||
}
|
||||
Type get_type() const override { return Type::MONOPOLY_COMMUNITY_CHEST; }
|
||||
|
||||
bool update(const InputEvent& event) override {
|
||||
@@ -86,6 +90,8 @@ public:
|
||||
|
||||
// Prompt
|
||||
renderer->draw_string_scaled(ix + (iw - 12 * 12) / 2, iy + ih - 20, "Press BUTTON", 2);
|
||||
|
||||
ModalButtonHelper::draw_virtual_buttons(renderer, input_manager);
|
||||
}
|
||||
|
||||
bool is_dismissed() const { return dismissed; }
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "../../display/low_level_gui.h"
|
||||
#include "input_manager.h"
|
||||
#include "MonopolyBoardRenderer.h"
|
||||
#include "ModalButtonHelper.h"
|
||||
|
||||
class DiceModalGame : public Game {
|
||||
int dice1, dice2;
|
||||
@@ -49,7 +50,10 @@ class DiceModalGame : public Game {
|
||||
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) {}
|
||||
void init() override { dismissed = false; }
|
||||
void init() override {
|
||||
dismissed = false;
|
||||
ModalButtonHelper::set_monopoly_regions(input_manager, width, height);
|
||||
}
|
||||
Type get_type() const override { return Type::MONOPOLY_DICE; }
|
||||
bool update(const InputEvent& event) override {
|
||||
// Only B dismisses now, so A can still be used for "change action" (even if it does nothing here)
|
||||
@@ -114,6 +118,8 @@ public:
|
||||
renderer->set_text_color(false);
|
||||
renderer->draw_string_scaled(btn_x + 5, btn_y + 5, "> CONTINUE", 2);
|
||||
renderer->set_text_color(true);
|
||||
|
||||
ModalButtonHelper::draw_virtual_buttons(renderer, input_manager);
|
||||
}
|
||||
bool is_dismissed() const { return dismissed; }
|
||||
};
|
||||
|
||||
60
games/monopoly/ModalButtonHelper.h
Normal file
60
games/monopoly/ModalButtonHelper.h
Normal file
@@ -0,0 +1,60 @@
|
||||
// ModalButtonHelper.h
|
||||
#pragma once
|
||||
#include "../../lib/input_manager.h"
|
||||
#include "../../display/low_level_render.h"
|
||||
|
||||
class ModalButtonHelper {
|
||||
public:
|
||||
/**
|
||||
* @brief Sets the standard Monopoly virtual button regions
|
||||
*/
|
||||
static void set_monopoly_regions(InputManager* input_manager, uint16_t width, uint16_t height) {
|
||||
if (!input_manager) return;
|
||||
|
||||
// --- BUTTON CONFIGURATION ---
|
||||
// Adjust these variables to move the virtual buttons easily
|
||||
int btn_w = 60;
|
||||
int btn_h = 60;
|
||||
int margin_right = 135; // Positioned for the inner board area (W - 135 = 345)
|
||||
int start_y = 80; // Vertical start position
|
||||
int spacing = 30; // Gap between buttons
|
||||
|
||||
int btn_x = width - margin_right;
|
||||
int btn_a_y = start_y;
|
||||
int btn_b_y = btn_a_y + btn_h + spacing;
|
||||
|
||||
// Apply regions to input manager
|
||||
input_manager->set_virtual_button_regions(
|
||||
btn_x, btn_a_y, btn_w, btn_h, // Button A (Top)
|
||||
btn_x, btn_b_y, btn_w, btn_h // Button B (Bottom)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Draws virtual A and B buttons at consistent screen locations
|
||||
*/
|
||||
static void draw_virtual_buttons(LowLevelRenderer* renderer, InputManager* input_manager) {
|
||||
if (!renderer || !input_manager) return;
|
||||
|
||||
int a[4], b[4];
|
||||
input_manager->get_virtual_button_regions(a, b);
|
||||
|
||||
// Save current color
|
||||
bool original_color = renderer->get_current_text_color();
|
||||
|
||||
// Draw Button A (Top)
|
||||
renderer->draw_filled_rectangle(a[0], a[1], a[2], a[3], false, 0); // White back
|
||||
renderer->draw_rectangle(a[0], a[1], a[2], a[3], true, 2); // Black border
|
||||
renderer->set_text_color(true);
|
||||
renderer->draw_string_scaled(a[0] + (a[2] - 12) / 2, a[1] + (a[3] - 16) / 2, "A", 2);
|
||||
|
||||
// Draw Button B (Under A)
|
||||
renderer->draw_filled_rectangle(b[0], b[1], b[2], b[3], false, 0); // White back
|
||||
renderer->draw_rectangle(b[0], b[1], b[2], b[3], true, 2); // Black border
|
||||
renderer->set_text_color(true);
|
||||
renderer->draw_string_scaled(b[0] + (b[2] - 12) / 2, b[1] + (b[3] - 16) / 2, "B", 2);
|
||||
|
||||
// Restore color
|
||||
renderer->set_text_color(original_color);
|
||||
}
|
||||
};
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "player.h"
|
||||
#include "MonopolyBoardRenderer.h"
|
||||
#include "sprites.h"
|
||||
#include "ModalButtonHelper.h"
|
||||
|
||||
class PropertyModalGame : public Game {
|
||||
const BoardTile* property;
|
||||
@@ -28,7 +29,10 @@ public:
|
||||
: Game(width, height, renderer, gui, input_manager), property(prop), dismissed(false), is_owned(owned), owner_name(owner), owner_id(o_id), can_afford(affordable), selected_choice(0), buy_requested(false), rent_requested(false), players(p_list), players_count(p_count), observer_idx(obs_idx) {
|
||||
if (is_owned || !can_afford) selected_choice = 1;
|
||||
}
|
||||
void init() override { dismissed = false; buy_requested = false; rent_requested = false; selected_choice = 0; }
|
||||
void init() override {
|
||||
dismissed = false; buy_requested = false; rent_requested = false; selected_choice = 0;
|
||||
ModalButtonHelper::set_monopoly_regions(input_manager, width, height);
|
||||
}
|
||||
Type get_type() const override { return Type::MONOPOLY_PROPERTY; }
|
||||
bool update(const InputEvent& event) override {
|
||||
// If rent is owed, any button pays it (it's forced)
|
||||
@@ -189,6 +193,8 @@ public:
|
||||
renderer->draw_string_scaled(win_x + (win_w - 7 * 12) / 2, btn_y + 10, ">PASS", 2);
|
||||
renderer->set_text_color(true);
|
||||
}
|
||||
|
||||
ModalButtonHelper::draw_virtual_buttons(renderer, input_manager);
|
||||
}
|
||||
bool is_dismissed() const { return dismissed; }
|
||||
bool wants_to_buy() const { return buy_requested; }
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "../../display/low_level_gui.h"
|
||||
#include "input_manager.h"
|
||||
#include "player.h"
|
||||
#include "ModalButtonHelper.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
@@ -16,7 +17,10 @@ public:
|
||||
TurnModalGame(uint16_t width, uint16_t height, LowLevelRenderer* renderer, LowLevelGUI* gui, InputManager* input_manager, Player* next)
|
||||
: Game(width, height, renderer, gui, input_manager), next_player(next), dismissed(false) {}
|
||||
|
||||
void init() override { dismissed = false; }
|
||||
void init() override {
|
||||
dismissed = false;
|
||||
ModalButtonHelper::set_monopoly_regions(input_manager, width, height);
|
||||
}
|
||||
Type get_type() const override { return Type::MONOPOLY_TURN; }
|
||||
|
||||
bool update(const InputEvent& event) override {
|
||||
@@ -61,6 +65,8 @@ public:
|
||||
// Prompt
|
||||
const char* prompt = "Press B to start";
|
||||
renderer->draw_string_scaled(ix + (iw - (int)strlen(prompt) * 6) / 2, iy + ih - 30, prompt, 1);
|
||||
|
||||
ModalButtonHelper::draw_virtual_buttons(renderer, input_manager);
|
||||
}
|
||||
|
||||
bool is_dismissed() const { return dismissed; }
|
||||
|
||||
@@ -23,6 +23,7 @@ extern "C" {
|
||||
#include "ChanceModalGame.h"
|
||||
#include "CommunityChestModalGame.h"
|
||||
#include "TurnModalGame.h"
|
||||
#include "ModalButtonHelper.h"
|
||||
#include "sprites.h"
|
||||
#include "MonopolyBoardRenderer.h"
|
||||
|
||||
@@ -39,6 +40,9 @@ MonopolyGame::MonopolyGame(uint16_t width, uint16_t height, LowLevelRenderer* re
|
||||
|
||||
// --- Initialize game state ---
|
||||
void MonopolyGame::init() {
|
||||
// Initialize regions for touch buttons
|
||||
ModalButtonHelper::set_monopoly_regions(input_manager, width, height);
|
||||
|
||||
// Hardcoded 2 players for minimal version
|
||||
init_player(&players[0], 0, "Elias", "Top Hat");
|
||||
init_player(&players[1], 1, "Adolfo", "Racecar");
|
||||
@@ -114,6 +118,7 @@ bool MonopolyGame::update(const InputEvent& event) {
|
||||
delete active_modal;
|
||||
active_modal = nullptr;
|
||||
needs_redraw = true;
|
||||
ModalButtonHelper::set_monopoly_regions(input_manager, width, height);
|
||||
|
||||
// Immediately check if we need to show a property modal
|
||||
if (modal_property_index >= 0) {
|
||||
@@ -141,10 +146,14 @@ bool MonopolyGame::update(const InputEvent& event) {
|
||||
active_modal = new CommunityChestModalGame(width, height, renderer, gui, input_manager, &COMMUNITY_DECK[last_drawn_community_idx], players, players_count, p->position);
|
||||
// We'll apply the effect when CommunityChestModal is dismissed
|
||||
}
|
||||
if (active_modal) active_modal->init();
|
||||
return needs_redraw;
|
||||
} else if (chance_modal && chance_modal->is_dismissed()) {
|
||||
const ChanceCard* card = &CHANCE_DECK[last_drawn_chance_idx];
|
||||
last_drawn_chance_idx = -1;
|
||||
delete active_modal;
|
||||
active_modal = nullptr;
|
||||
ModalButtonHelper::set_monopoly_regions(input_manager, width, height);
|
||||
|
||||
// Apply card effects
|
||||
bool position_changed = false;
|
||||
@@ -204,6 +213,7 @@ bool MonopolyGame::update(const InputEvent& event) {
|
||||
delete active_modal;
|
||||
active_modal = nullptr;
|
||||
needs_redraw = true;
|
||||
ModalButtonHelper::set_monopoly_regions(input_manager, width, height);
|
||||
|
||||
if (position_changed) {
|
||||
// If we moved, check if we landed on a property
|
||||
@@ -269,6 +279,7 @@ bool MonopolyGame::update(const InputEvent& event) {
|
||||
delete active_modal;
|
||||
active_modal = nullptr;
|
||||
needs_redraw = true;
|
||||
ModalButtonHelper::set_monopoly_regions(input_manager, width, height);
|
||||
|
||||
if (position_changed) {
|
||||
const BoardTile* landed = &MONOPOLY_BOARD[p->position];
|
||||
@@ -360,14 +371,17 @@ bool MonopolyGame::update(const InputEvent& event) {
|
||||
delete active_modal;
|
||||
active_modal = nullptr;
|
||||
needs_redraw = true;
|
||||
ModalButtonHelper::set_monopoly_regions(input_manager, width, height);
|
||||
} else if (board_modal && board_modal->is_dismissed()) {
|
||||
delete active_modal;
|
||||
active_modal = nullptr;
|
||||
needs_redraw = true;
|
||||
ModalButtonHelper::set_monopoly_regions(input_manager, width, height);
|
||||
} else if (turn_modal && turn_modal->is_dismissed()) {
|
||||
delete active_modal;
|
||||
active_modal = nullptr;
|
||||
needs_redraw = true;
|
||||
ModalButtonHelper::set_monopoly_regions(input_manager, width, height);
|
||||
}
|
||||
return needs_redraw;
|
||||
}
|
||||
@@ -544,4 +558,6 @@ void MonopolyGame::draw() {
|
||||
renderer->draw_string_scaled(ix + 15, content_y, buf, 2);
|
||||
content_y += 25;
|
||||
}
|
||||
|
||||
ModalButtonHelper::draw_virtual_buttons(renderer, input_manager);
|
||||
}
|
||||
|
||||
@@ -93,6 +93,14 @@ InputEvent InputManager::process_touch_input(uint32_t* last_time) {
|
||||
// Determine event type
|
||||
if (*last_time == 0) {
|
||||
event.type = INPUT_TOUCH_DOWN;
|
||||
|
||||
// Check for virtual buttons
|
||||
InputType virtual_type;
|
||||
if (check_virtual_buttons(event.x, event.y, virtual_type)) {
|
||||
event.type = virtual_type;
|
||||
event.button_id = (virtual_type == INPUT_BUTTON_0) ? 0 : 1;
|
||||
printf("Virtual button %d pressed via touch\n", event.button_id);
|
||||
}
|
||||
} else {
|
||||
event.type = INPUT_TOUCH_MOVE;
|
||||
}
|
||||
@@ -164,3 +172,38 @@ const char* InputManager::get_gesture_name(uint8_t gesture_code) {
|
||||
default: return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
void InputManager::get_virtual_button_regions(int* a_rect, int* b_rect) const {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
a_rect[i] = v_button_a[i];
|
||||
b_rect[i] = v_button_b[i];
|
||||
}
|
||||
}
|
||||
|
||||
void InputManager::set_virtual_button_regions(int ax, int ay, int aw, int ah, int bx, int by, int bw, int bh) {
|
||||
v_button_a[0] = ax; v_button_a[1] = ay; v_button_a[2] = aw; v_button_a[3] = ah;
|
||||
v_button_b[0] = bx; v_button_b[1] = by; v_button_b[2] = bw; v_button_b[3] = bh;
|
||||
v_buttons_active = true;
|
||||
}
|
||||
|
||||
void InputManager::clear_virtual_button_regions() {
|
||||
v_buttons_active = false;
|
||||
}
|
||||
|
||||
bool InputManager::check_virtual_buttons(int16_t x, int16_t y, InputType& out_type) const {
|
||||
if (!v_buttons_active) return false;
|
||||
|
||||
if (x >= v_button_a[0] && x <= v_button_a[0] + v_button_a[2] &&
|
||||
y >= v_button_a[1] && y <= v_button_a[1] + v_button_a[3]) {
|
||||
out_type = INPUT_BUTTON_0;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (x >= v_button_b[0] && x <= v_button_b[0] + v_button_b[2] &&
|
||||
y >= v_button_b[1] && y <= v_button_b[1] + v_button_b[3]) {
|
||||
out_type = INPUT_BUTTON_1;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -49,6 +49,32 @@ public:
|
||||
*/
|
||||
InputEvent process_button_input();
|
||||
|
||||
/**
|
||||
* @brief Get virtual button regions for drawing
|
||||
* @param a_rect Pointer to rectangle for Button A [x, y, w, h]
|
||||
* @param b_rect Pointer to rectangle for Button B [x, y, w, h]
|
||||
*/
|
||||
void get_virtual_button_regions(int* a_rect, int* b_rect) const;
|
||||
|
||||
/**
|
||||
* @brief Set virtual button regions
|
||||
*/
|
||||
void set_virtual_button_regions(int ax, int ay, int aw, int ah, int bx, int by, int bw, int bh);
|
||||
|
||||
/**
|
||||
* @brief Clear virtual button regions (disables detection)
|
||||
*/
|
||||
void clear_virtual_button_regions();
|
||||
|
||||
/**
|
||||
* @brief Check if a touch event hits a virtual button
|
||||
* @param x Touch X coordinate
|
||||
* @param y Touch Y coordinate
|
||||
* @param out_type Output parameter for the button type if hit
|
||||
* @return true if a virtual button was hit
|
||||
*/
|
||||
bool check_virtual_buttons(int16_t x, int16_t y, InputType& out_type) const;
|
||||
|
||||
/**
|
||||
* @brief Get human-readable gesture name
|
||||
* @param gesture_code Gesture code from touch controller
|
||||
@@ -71,6 +97,11 @@ public:
|
||||
private:
|
||||
LowLevelTouch* touch;
|
||||
const GameConfig* config;
|
||||
|
||||
// Virtual button regions
|
||||
int v_button_a[4] = {0, 0, 0, 0}; // [x, y, w, h]
|
||||
int v_button_b[4] = {0, 0, 0, 0};
|
||||
bool v_buttons_active = false;
|
||||
};
|
||||
|
||||
#endif // INPUT_MANAGER_H
|
||||
|
||||
Reference in New Issue
Block a user