From 0f807a1867ddbbad28334f387c3b4fdfdce28dee Mon Sep 17 00:00:00 2001 From: Zvonimir Rudinski Date: Wed, 29 Apr 2026 00:11:35 +0200 Subject: [PATCH] feat: add proof-of-concept --- include/game.hpp | 42 +++++++++++++ include/menu.hpp | 15 ++--- src/display.cpp | 6 +- src/game.cpp | 151 +++++++++++++++++++++++++++++++++++++++++++++++ src/main.cpp | 39 ++---------- src/menu.cpp | 21 +++---- 6 files changed, 216 insertions(+), 58 deletions(-) create mode 100644 include/game.hpp create mode 100644 src/game.cpp diff --git a/include/game.hpp b/include/game.hpp new file mode 100644 index 0000000..300c7f4 --- /dev/null +++ b/include/game.hpp @@ -0,0 +1,42 @@ +#pragma once +#include +#include "joystick.hpp" +#include "display.hpp" +#include "menu.hpp" +#define ACTION_INTERVAL 60000 // 1 minute +#define MAXIMUM_STAT 100 + +typedef struct { + bool dead; + bool menuOpen; + + int hunger; + int joy; + int energy; + int cleanliness; + + uint64_t lastActionTime; + bool shouldClear; +} GameState; + +class Game { + public: + Game(); + void begin(); + void update(); + void render(); + + protected: + void forceUpdate(String reason); + void feed(); + void play(); + void sleep(); + void clean(); + + private: + GameState state; + Joystick joystick; + Display display; + String* items; + Menu menu; +}; diff --git a/include/menu.hpp b/include/menu.hpp index 0f048a5..4fcfe32 100644 --- a/include/menu.hpp +++ b/include/menu.hpp @@ -3,20 +3,15 @@ #include #include "joystick.hpp" -typedef struct { - String name; - void (*action)(); -} Item; - class Menu { public: - Menu(Item* items); - void updateCurrentItem(JoystickDirection& direction); - void execute(); + Menu(); + void setItems(String* items); + bool updateCurrentItem(JoystickDirection& direction); - Item& getItemAt(size_t index); + String& getItemAt(size_t index); size_t getCurrentItemIndex() const; private: - Item items[MENU_ITEM_COUNT]; + String items[MENU_ITEM_COUNT]; int currentItem; }; diff --git a/src/display.cpp b/src/display.cpp index d42b550..a385ee2 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -17,12 +17,12 @@ void Display::drawMenu(Menu &menu) { size_t currentItemIndex = menu.getCurrentItemIndex(); for (size_t i = 0; i < MENU_ITEM_COUNT; i++) { lcd.setCursor(0, i); - Item& item = menu.getItemAt(i); + String item = menu.getItemAt(i); if (i == currentItemIndex) { - lcd.print("> " + item.name); + lcd.print("> " + item); } else { - lcd.print(" " + item.name); + lcd.print(" " + item); } } } diff --git a/src/game.cpp b/src/game.cpp new file mode 100644 index 0000000..7ea8663 --- /dev/null +++ b/src/game.cpp @@ -0,0 +1,151 @@ +#include "game.hpp" + +Game::Game() : joystick(), display(), menu() { + state = (GameState) { + .dead = false, + .menuOpen = false, + .hunger = 0, + .joy = 100, + .energy = 100, + .cleanliness = 100, + .lastActionTime = 0, + .shouldClear = false, + }; + + String items[] = { + "Feed", + "Play", + "Sleep", + "Clean" + }; + + menu.setItems(items); +} + +void Game::begin() { + Serial.begin(9600); + while (!Serial) { + delay(10); + } + display.begin(); +} + +void Game::update() { + // If the pet is dead, we don't need to update its state + if (state.dead) { + return; + } + + bool isPressed = joystick.isPressed(); + + // If the menu is open, we don't need to update the pet's state + if (state.menuOpen) { + JoystickDirection direction = joystick.getDirection(); + if (menu.updateCurrentItem(direction)) { + forceUpdate("Menu navigation"); + } + + // If the joystick is pressed, execute the current menu item + if (isPressed) { + state.menuOpen = false; + // Based on the current menu item, perform the corresponding action + switch (menu.getCurrentItemIndex()) { + case 0: + feed(); + break; + case 1: + play(); + break; + case 2: + sleep(); + break; + case 3: + clean(); + break; + } + forceUpdate("Menu action"); + } + return; + } + + // If the joystick is pressed and the menu is not open, open the menu + if (isPressed) { + state.menuOpen = true; + forceUpdate("Opening menu"); + return; + } + + // Update the pet's stats based on the time elapsed since the last action + uint64_t currentTime = millis(); + if (currentTime - state.lastActionTime >= ACTION_INTERVAL) { + state.hunger += 10; + state.joy -= 5; + state.energy -= 5; + state.cleanliness -= 5; + state.lastActionTime = currentTime; + + forceUpdate("Time-based stat update"); + } + + // Check if the pet has died from any of the conditions + bool starvedToDeath = state.hunger >= MAXIMUM_STAT; + bool diedOfBoredom = state.joy <= -MAXIMUM_STAT; + bool diedOfExhaustion = state.energy <= -MAXIMUM_STAT; + bool diedOfFilth = state.cleanliness <= -MAXIMUM_STAT; + + state.dead = starvedToDeath || diedOfBoredom || diedOfExhaustion || diedOfFilth; +} + +void Game::render() { + if (state.shouldClear) { + display.clear(); + state.shouldClear = false; + + if (state.menuOpen) { + display.drawMenu(menu); + Serial.println("Rendering menu"); + return; + } + } + + if (state.menuOpen) { + return; + } + + if (state.dead) { + display.getLCD().print("Your pet has died."); + return; + } + + display.getLCD().setCursor(0, 0); + display.getLCD().print("H: " + String(state.hunger)); + display.getLCD().setCursor(0, 1); + display.getLCD().print("J: " + String(state.joy)); + display.getLCD().setCursor(0, 2); + display.getLCD().print("E: " + String(state.energy)); + display.getLCD().setCursor(0, 3); + display.getLCD().print("C: " + String(state.cleanliness)); +} + +void Game::forceUpdate(String reason) { + if (!state.shouldClear) { + Serial.println("Forcing update: " + reason); + state.shouldClear = true; + } +} + +void Game::feed() { + state.hunger += 20; +} + +void Game::play() { + state.joy += 20; +} + +void Game::sleep() { + state.energy += 20; +} + +void Game::clean() { + state.cleanliness += 20; +} diff --git a/src/main.cpp b/src/main.cpp index 77ccc86..79fb710 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,40 +1,13 @@ -#include -#include -#include "joystick.hpp" -#include "display.hpp" +#include "game.hpp" -Joystick joystick; -Display display; - -Item items[MENU_ITEM_COUNT] = { - {"Feed", []() { Serial.println("Feeding..."); }}, - {"Play", []() { Serial.println("Playing..."); }}, - {"Sleep", []() { Serial.println("Sleeping..."); }}, - {"Clean", []() { Serial.println("Cleaning..."); }} -}; -Menu menu(items); +Game game; void setup() { - Serial.begin(9600); - while (!Serial) { - delay(10); - } - - display.begin(); - pinMode(LED_BUILTIN, OUTPUT); - display.drawMenu(menu); + game.begin(); } void loop() { - JoystickDirection direction = joystick.getDirection(); - if (direction != JoystickDirection::CENTER) { - menu.updateCurrentItem(direction); - display.drawMenu(menu); - } - - if (joystick.isPressed()) { - menu.execute(); - } - - delay(250); + game.update(); + delay(100); // Small delay to prevent overwhelming the CPU + game.render(); } diff --git a/src/menu.cpp b/src/menu.cpp index 8525f3a..93ac132 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -1,31 +1,28 @@ #include "menu.hpp" -Menu::Menu(Item* items) : currentItem(0) { +Menu::Menu() : currentItem(0) {} + +void Menu::setItems(String* items) { + this->currentItem = 0; for (int i = 0; i < MENU_ITEM_COUNT; i++) { this->items[i] = items[i]; } } -void Menu::updateCurrentItem(JoystickDirection &direction) { +bool Menu::updateCurrentItem(JoystickDirection &direction) { switch (direction) { case JoystickDirection::UP: currentItem = (currentItem - 1 + MENU_ITEM_COUNT) % MENU_ITEM_COUNT; - break; + return true; case JoystickDirection::DOWN: currentItem = (currentItem + 1) % MENU_ITEM_COUNT; - break; + return true; default: - break; + return false; } } -void Menu::execute() { - if (items[currentItem].action) { - items[currentItem].action(); - } -} - -Item& Menu::getItemAt(size_t index) { +String& Menu::getItemAt(size_t index) { return items[index]; }