Compare commits
26 Commits
8798a2d012
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
3d75b05bf8
|
|||
|
708a7d53bb
|
|||
|
296b48eb9f
|
|||
|
ad1c76106a
|
|||
|
28af5da4f3
|
|||
|
c6923c209c
|
|||
|
6fffa3e879
|
|||
|
ea3632326b
|
|||
|
5b763a33bc
|
|||
|
6c349c2ada
|
|||
|
0f807a1867
|
|||
|
d9b72aa8b5
|
|||
|
075ecdf1e2
|
|||
|
f2fbb9f205
|
|||
|
2d2051e6c2
|
|||
|
6be5a881e7
|
|||
|
511beae681
|
|||
|
b63c610665
|
|||
|
bcb2047f60
|
|||
|
5c9a089781
|
|||
|
1a8047d5e0
|
|||
|
48a1cd5ee6
|
|||
|
85714b4520
|
|||
|
924b2806fb
|
|||
|
fb7fbfe6c4
|
|||
|
dd643c1c10
|
@@ -5,6 +5,7 @@ An open-source implementation of the Tamagotchi virtual pet game, designed to ru
|
|||||||
## Bill of Materials
|
## Bill of Materials
|
||||||
- Arduino Nano (ATmega328P)
|
- Arduino Nano (ATmega328P)
|
||||||
- KY-023 Joystick Module
|
- KY-023 Joystick Module
|
||||||
|
- HD44780 Character LCD (20x4 or 16x2, can be changed in the code)
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
After cloning the repository, navigate to the project directory and run `make build` to compile the code. To upload the compiled firmware to your Arduino Nano, use `make upload`.
|
After cloning the repository, navigate to the project directory and run `make build` to compile the code. To upload the compiled firmware to your Arduino Nano, use `make upload`.
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#pragma once
|
||||||
|
/*
|
||||||
|
* A header file to store all the constants used in the project.
|
||||||
|
*/
|
||||||
|
#include <Arduino.h>
|
||||||
|
#define LCD_I2C_ADDRESS 0x27
|
||||||
|
#define LCD_COLS 20
|
||||||
|
#define LCD_ROWS 4
|
||||||
|
|
||||||
|
#define ACTION_INTERVAL 60000 // 1 minute
|
||||||
|
|
||||||
|
#define MAXIMUM_STAT 100
|
||||||
|
#define ANIMATION_FRAME_INTERVAL 1500 // Time in milliseconds between animation frames
|
||||||
|
|
||||||
|
#define DEBOUNCE_DELAY 50 // Debounce delay in milliseconds for the joystick button
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <LiquidCrystal_I2C.h>
|
||||||
|
#include "constants.hpp"
|
||||||
|
#include "menu.hpp"
|
||||||
|
#include "pet.hpp"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A helper class to facilitate drawing on a HD44780 LCD display.
|
||||||
|
*/
|
||||||
|
class Display {
|
||||||
|
public:
|
||||||
|
Display();
|
||||||
|
|
||||||
|
void begin();
|
||||||
|
void clear();
|
||||||
|
void drawPetStats(Pet& pet);
|
||||||
|
void drawPet(Pet& pet);
|
||||||
|
void drawBuffer(String buffer[]);
|
||||||
|
void drawMenu(Menu& menu);
|
||||||
|
|
||||||
|
LiquidCrystal_I2C& getLCD();
|
||||||
|
private:
|
||||||
|
LiquidCrystal_I2C lcd;
|
||||||
|
};
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "joystick.hpp"
|
||||||
|
#include "display.hpp"
|
||||||
|
#include "menu.hpp"
|
||||||
|
#include "pet.hpp"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Pet pet;
|
||||||
|
|
||||||
|
uint64_t lastActionTime;
|
||||||
|
|
||||||
|
bool isMenuOpen;
|
||||||
|
bool shouldClearDisplay;
|
||||||
|
} 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;
|
||||||
|
};
|
||||||
+29
-5
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
#include "constants.hpp"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A helper class to read values from the KY-023 joystick module.
|
* A helper class to read values from the KY-023 joystick module.
|
||||||
@@ -11,18 +12,41 @@
|
|||||||
*
|
*
|
||||||
* Note: The button is active LOW and requires a pull-up resistor, which can either be external (10kΩ)
|
* Note: The button is active LOW and requires a pull-up resistor, which can either be external (10kΩ)
|
||||||
* or the internal pull-up resistor of the microcontroller (default).
|
* or the internal pull-up resistor of the microcontroller (default).
|
||||||
*
|
|
||||||
* TODO: Add "deadzone" handling to prevent small joystick movements from being registered as input.
|
|
||||||
*/
|
*/
|
||||||
|
enum JoystickDirection {
|
||||||
|
CENTER,
|
||||||
|
UP,
|
||||||
|
DOWN,
|
||||||
|
LEFT,
|
||||||
|
RIGHT
|
||||||
|
};
|
||||||
|
|
||||||
class Joystick {
|
class Joystick {
|
||||||
public:
|
public:
|
||||||
Joystick();
|
Joystick();
|
||||||
|
|
||||||
double getX() const;
|
double getX() const;
|
||||||
double getY() const;
|
double getY() const;
|
||||||
bool isPressed() const;
|
bool isPressed();
|
||||||
|
|
||||||
|
JoystickDirection getDirection() const;
|
||||||
private:
|
private:
|
||||||
|
class Switch {
|
||||||
|
public:
|
||||||
|
Switch(uint8_t pin);
|
||||||
|
bool isPressed();
|
||||||
|
private:
|
||||||
|
uint8_t pin;
|
||||||
|
bool lastKnownState;
|
||||||
|
bool lastStableState;
|
||||||
|
uint64_t lastDebounceTime;
|
||||||
|
};
|
||||||
|
|
||||||
uint8_t vrx;
|
uint8_t vrx;
|
||||||
uint8_t vry;
|
uint8_t vry;
|
||||||
uint8_t sw;
|
Switch sw;
|
||||||
|
|
||||||
|
uint8_t xOffset;
|
||||||
|
uint8_t yOffset;
|
||||||
|
|
||||||
|
const uint8_t deadzone = 15; // Deadzone threshold to prevent jitter around the center position
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "constants.hpp"
|
||||||
|
#include "joystick.hpp"
|
||||||
|
|
||||||
|
class Menu {
|
||||||
|
public:
|
||||||
|
Menu();
|
||||||
|
void setItems(String* items);
|
||||||
|
bool updateCurrentItem(JoystickDirection& direction);
|
||||||
|
|
||||||
|
String& getItemAt(size_t index);
|
||||||
|
size_t getCurrentItemIndex() const;
|
||||||
|
private:
|
||||||
|
String items[LCD_ROWS];
|
||||||
|
int currentItem;
|
||||||
|
};
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "constants.hpp"
|
||||||
|
|
||||||
|
class Pet {
|
||||||
|
public:
|
||||||
|
Pet();
|
||||||
|
bool isAlive;
|
||||||
|
|
||||||
|
void updateHunger(int8_t delta);
|
||||||
|
void updateJoy(int8_t delta);
|
||||||
|
void updateEnergy(int8_t delta);
|
||||||
|
void updateCleanliness(int8_t delta);
|
||||||
|
|
||||||
|
int8_t getHunger() const;
|
||||||
|
int8_t getJoy() const;
|
||||||
|
int8_t getEnergy() const;
|
||||||
|
int8_t getCleanliness() const;
|
||||||
|
String getReasonForDeath() const;
|
||||||
|
|
||||||
|
byte getAnimationFrame();
|
||||||
|
private:
|
||||||
|
int8_t hunger;
|
||||||
|
int8_t joy;
|
||||||
|
int8_t energy;
|
||||||
|
int8_t cleanliness;
|
||||||
|
String reasonForDeath;
|
||||||
|
|
||||||
|
byte lastAnimationFrame;
|
||||||
|
uint64_t lastAnimationFrameTime;
|
||||||
|
};
|
||||||
+1
-1
@@ -18,4 +18,4 @@ monitor_speed = 9600
|
|||||||
|
|
||||||
; External libraries
|
; External libraries
|
||||||
lib_deps =
|
lib_deps =
|
||||||
embeddedartistry/LibPrintf@1.2.13
|
marcoschwartz/LiquidCrystal_I2C@1.1.4
|
||||||
|
|||||||
@@ -0,0 +1,83 @@
|
|||||||
|
#include "display.hpp"
|
||||||
|
|
||||||
|
// Initialize the LCD display with the specified number of columns and rows, using the I2C address.
|
||||||
|
Display::Display() : lcd(LCD_I2C_ADDRESS, LCD_COLS, LCD_ROWS) {}
|
||||||
|
|
||||||
|
void Display::begin() {
|
||||||
|
byte CUSTOM_CHAR_PET1[] = {
|
||||||
|
B00100,
|
||||||
|
B01110,
|
||||||
|
B11111,
|
||||||
|
B10101,
|
||||||
|
B11111,
|
||||||
|
B11111,
|
||||||
|
B10001,
|
||||||
|
B11111
|
||||||
|
};
|
||||||
|
byte CUSTOM_CHAR_PET2[] = {
|
||||||
|
B00100,
|
||||||
|
B01110,
|
||||||
|
B11111,
|
||||||
|
B10101,
|
||||||
|
B11111,
|
||||||
|
B10001,
|
||||||
|
B11011,
|
||||||
|
B11111
|
||||||
|
};
|
||||||
|
|
||||||
|
lcd.init();
|
||||||
|
lcd.backlight();
|
||||||
|
|
||||||
|
// Create a custom character for the pet
|
||||||
|
lcd.createChar(0, CUSTOM_CHAR_PET1);
|
||||||
|
lcd.createChar(1, CUSTOM_CHAR_PET2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Display::clear() {
|
||||||
|
lcd.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Display::drawBuffer(String buffer[]) {
|
||||||
|
clear();
|
||||||
|
for (size_t i = 0; i < LCD_ROWS; i++) {
|
||||||
|
lcd.setCursor(0, i);
|
||||||
|
lcd.print(buffer[i].substring(0, LCD_COLS)); // Ensure we only print up to the number of columns
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Display::drawPetStats(Pet &pet) {
|
||||||
|
clear();
|
||||||
|
|
||||||
|
lcd.setCursor(0, 0);
|
||||||
|
lcd.print("Hunger: " + String(pet.getHunger()));
|
||||||
|
lcd.setCursor(0, 1);
|
||||||
|
lcd.print("Joy: " + String(pet.getJoy()));
|
||||||
|
lcd.setCursor(0, 2);
|
||||||
|
lcd.print("Energy: " + String(pet.getEnergy()));
|
||||||
|
lcd.setCursor(0, 3);
|
||||||
|
lcd.print("Cleanliness: " + String(pet.getCleanliness()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Display::drawPet(Pet &pet) {
|
||||||
|
lcd.setCursor(LCD_COLS - 2, LCD_ROWS / 2 - 1); // Position the pet
|
||||||
|
lcd.write(pet.getAnimationFrame()); // Draw the custom pet character
|
||||||
|
}
|
||||||
|
|
||||||
|
void Display::drawMenu(Menu &menu) {
|
||||||
|
clear();
|
||||||
|
size_t currentItemIndex = menu.getCurrentItemIndex();
|
||||||
|
for (size_t i = 0; i < LCD_ROWS; i++) {
|
||||||
|
lcd.setCursor(0, i);
|
||||||
|
String item = menu.getItemAt(i);
|
||||||
|
|
||||||
|
if (i == currentItemIndex) {
|
||||||
|
lcd.print(("> " + item).substring(0, LCD_COLS)); // Add a ">" to indicate the current item
|
||||||
|
} else {
|
||||||
|
lcd.print((" " + item).substring(0, LCD_COLS)); // Indent non-selected items
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LiquidCrystal_I2C& Display::getLCD() {
|
||||||
|
return lcd;
|
||||||
|
}
|
||||||
+154
@@ -0,0 +1,154 @@
|
|||||||
|
#include "game.hpp"
|
||||||
|
|
||||||
|
Game::Game() : joystick(), display(), menu() {
|
||||||
|
state = (GameState) {
|
||||||
|
.pet = Pet(),
|
||||||
|
.lastActionTime = 0,
|
||||||
|
.isMenuOpen = false,
|
||||||
|
.shouldClearDisplay = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
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.pet.isAlive) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isPressed = joystick.isPressed();
|
||||||
|
|
||||||
|
// If the menu is open, we don't need to update the pet's state
|
||||||
|
if (state.isMenuOpen) {
|
||||||
|
JoystickDirection direction = joystick.getDirection();
|
||||||
|
if (menu.updateCurrentItem(direction)) {
|
||||||
|
forceUpdate("Menu navigation");
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the joystick is pressed, execute the current menu item
|
||||||
|
if (isPressed) {
|
||||||
|
state.isMenuOpen = 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.isMenuOpen = 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.pet.updateHunger(10);
|
||||||
|
state.pet.updateJoy(-5);
|
||||||
|
state.pet.updateEnergy(-5);
|
||||||
|
state.pet.updateCleanliness(-5);
|
||||||
|
|
||||||
|
state.lastActionTime = currentTime;
|
||||||
|
|
||||||
|
forceUpdate("Time-based stat update");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Game::render() {
|
||||||
|
// If the display doesn't need to be cleared, we can skip rendering
|
||||||
|
if (!state.shouldClearDisplay) {
|
||||||
|
// However, we still need to draw the pet if it's alive and
|
||||||
|
// we're not in the menu
|
||||||
|
if (state.pet.isAlive && !state.isMenuOpen) {
|
||||||
|
display.drawPet(state.pet);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.shouldClearDisplay = false;
|
||||||
|
|
||||||
|
if (state.isMenuOpen) {
|
||||||
|
display.drawMenu(menu);
|
||||||
|
Serial.println("Rendering menu");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the pet is dead, display a message and return
|
||||||
|
if (!state.pet.isAlive) {
|
||||||
|
String buffer[LCD_ROWS] = {
|
||||||
|
"Your pet has died of",
|
||||||
|
state.pet.getReasonForDeath() + ".",
|
||||||
|
"Reset the device",
|
||||||
|
"to start over."
|
||||||
|
};
|
||||||
|
|
||||||
|
display.drawBuffer(buffer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render the pet's stats on the display
|
||||||
|
display.drawPetStats(state.pet);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Game::forceUpdate(String reason) {
|
||||||
|
if (!state.shouldClearDisplay) {
|
||||||
|
Serial.println("Forcing update: " + reason);
|
||||||
|
state.shouldClearDisplay = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Game::feed() {
|
||||||
|
if (state.pet.isAlive) {
|
||||||
|
state.pet.updateHunger(-20);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Game::play() {
|
||||||
|
if (state.pet.isAlive) {
|
||||||
|
state.pet.updateJoy(20);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Game::sleep() {
|
||||||
|
if (state.pet.isAlive) {
|
||||||
|
state.pet.updateEnergy(20);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Game::clean() {
|
||||||
|
if (state.pet.isAlive) {
|
||||||
|
state.pet.updateCleanliness(20);
|
||||||
|
}
|
||||||
|
}
|
||||||
+52
-6
@@ -1,22 +1,68 @@
|
|||||||
#include "joystick.hpp"
|
#include "joystick.hpp"
|
||||||
|
|
||||||
|
Joystick::Switch::Switch(uint8_t pin) : pin(pin), lastKnownState(HIGH), lastStableState(HIGH), lastDebounceTime(0) {
|
||||||
|
pinMode(pin, INPUT_PULLUP); // Use internal pull-up resistor
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Joystick::Switch::isPressed() {
|
||||||
|
bool state = digitalRead(pin);
|
||||||
|
|
||||||
|
// Check for state change and debounce
|
||||||
|
if (state != lastKnownState) {
|
||||||
|
lastDebounceTime = millis(); // Reset debounce timer
|
||||||
|
lastKnownState = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the state has been stable for longer than the debounce delay, update the stable state
|
||||||
|
if ((millis() - lastDebounceTime) > DEBOUNCE_DELAY) {
|
||||||
|
if (lastStableState != state) {
|
||||||
|
lastStableState = state; // Update stable state
|
||||||
|
return state == LOW; // Return true if the button is pressed (active LOW)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false; // No change in button state
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize the joystick pins in the constructor
|
// Initialize the joystick pins in the constructor
|
||||||
Joystick::Joystick() : vrx(A0), vry(A1), sw(4) {
|
Joystick::Joystick() : vrx(A0), vry(A1), sw(Switch(4)) {
|
||||||
pinMode(vrx, INPUT);
|
pinMode(vrx, INPUT);
|
||||||
pinMode(vry, INPUT);
|
pinMode(vry, INPUT);
|
||||||
pinMode(sw, INPUT_PULLUP);
|
|
||||||
|
// TODO: Calibrate the joystick by reading the center position and storing it as an offset
|
||||||
|
xOffset = 10;
|
||||||
|
yOffset = 13;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map the analog readings from the joystick to a range of -100 to 100 for both X and Y axes
|
// Map the analog readings from the joystick to a range of -100 to 100 for both X and Y axes
|
||||||
double Joystick::getX() const {
|
double Joystick::getX() const {
|
||||||
return map(analogRead(vrx), 0, 1023, -100, 100);
|
return map(analogRead(vrx), 0, 1023, -100, 100) + xOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
double Joystick::getY() const {
|
double Joystick::getY() const {
|
||||||
return map(analogRead(vry), 0, 1023, -100, 100);
|
return map(analogRead(vry), 0, 1023, -100, 100) + yOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the joystick button is pressed (active LOW)
|
// Check if the joystick button is pressed (active LOW)
|
||||||
bool Joystick::isPressed() const {
|
bool Joystick::isPressed() {
|
||||||
return digitalRead(sw) == LOW;
|
return sw.isPressed();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine the direction of the joystick based on the X and Y values, considering a deadzone to prevent jitter
|
||||||
|
JoystickDirection Joystick::getDirection() const {
|
||||||
|
double x = getX();
|
||||||
|
double y = getY();
|
||||||
|
|
||||||
|
if (abs(x) < deadzone && abs(y) < deadzone) {
|
||||||
|
return CENTER;
|
||||||
|
} else if (y > deadzone) {
|
||||||
|
return UP;
|
||||||
|
} else if (y < -deadzone) {
|
||||||
|
return DOWN;
|
||||||
|
} else if (x > deadzone) {
|
||||||
|
return RIGHT;
|
||||||
|
} else if (x < -deadzone) {
|
||||||
|
return LEFT;
|
||||||
|
}
|
||||||
|
return CENTER; // Default to CENTER if no direction is detected
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-20
@@ -1,26 +1,13 @@
|
|||||||
#include <Arduino.h>
|
#include "game.hpp"
|
||||||
#include <LibPrintf.h>
|
|
||||||
#include "joystick.hpp"
|
|
||||||
|
|
||||||
Joystick joystick;
|
Game game;
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
Serial.begin(9600);
|
game.begin();
|
||||||
while (!Serial) {
|
|
||||||
delay(10);
|
|
||||||
}
|
|
||||||
|
|
||||||
pinMode(LED_BUILTIN, OUTPUT);
|
|
||||||
Serial.println("configured");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
double x = joystick.getX();
|
game.update();
|
||||||
double y = joystick.getY();
|
delay(100); // Small delay to prevent overwhelming the CPU
|
||||||
bool pressed = joystick.isPressed();
|
game.render();
|
||||||
|
}
|
||||||
printf("X: %.2f | Y: %.2f | Pressed: %s\n", x, y, pressed ? "Yes" : "No");
|
|
||||||
|
|
||||||
digitalWrite(LED_BUILTIN, pressed ? HIGH : LOW);
|
|
||||||
|
|
||||||
delay(500);}
|
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
#include "menu.hpp"
|
||||||
|
|
||||||
|
Menu::Menu() : currentItem(0) {}
|
||||||
|
|
||||||
|
void Menu::setItems(String* items) {
|
||||||
|
this->currentItem = 0;
|
||||||
|
for (int i = 0; i < LCD_ROWS; i++) {
|
||||||
|
this->items[i] = items[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Menu::updateCurrentItem(JoystickDirection &direction) {
|
||||||
|
switch (direction) {
|
||||||
|
case JoystickDirection::UP:
|
||||||
|
currentItem = (currentItem - 1 + LCD_ROWS) % LCD_ROWS;
|
||||||
|
return true;
|
||||||
|
case JoystickDirection::DOWN:
|
||||||
|
currentItem = (currentItem + 1) % LCD_ROWS;
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String& Menu::getItemAt(size_t index) {
|
||||||
|
return items[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Menu::getCurrentItemIndex() const {
|
||||||
|
return currentItem;
|
||||||
|
}
|
||||||
+103
@@ -0,0 +1,103 @@
|
|||||||
|
#include "pet.hpp"
|
||||||
|
|
||||||
|
Pet::Pet() :
|
||||||
|
isAlive(true),
|
||||||
|
hunger(0),
|
||||||
|
joy(100),
|
||||||
|
energy(100),
|
||||||
|
cleanliness(100),
|
||||||
|
reasonForDeath(""),
|
||||||
|
lastAnimationFrame(0),
|
||||||
|
lastAnimationFrameTime(0)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void Pet::updateHunger(int8_t delta) {
|
||||||
|
hunger += delta;
|
||||||
|
|
||||||
|
if (hunger < 0) {
|
||||||
|
isAlive = false;
|
||||||
|
reasonForDeath = "overfeeding";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hunger > MAXIMUM_STAT) {
|
||||||
|
isAlive = false;
|
||||||
|
reasonForDeath = "starvation";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pet::updateJoy(int8_t delta) {
|
||||||
|
joy += delta;
|
||||||
|
|
||||||
|
if (joy < 0) {
|
||||||
|
isAlive = false;
|
||||||
|
reasonForDeath = "depression";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (joy > MAXIMUM_STAT) {
|
||||||
|
isAlive = false;
|
||||||
|
reasonForDeath = "overstimulation";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pet::updateEnergy(int8_t delta) {
|
||||||
|
energy += delta;
|
||||||
|
|
||||||
|
if (energy < 0) {
|
||||||
|
isAlive = false;
|
||||||
|
reasonForDeath = "exhaustion";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (energy > MAXIMUM_STAT) {
|
||||||
|
isAlive = false;
|
||||||
|
reasonForDeath = "overresting";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pet::updateCleanliness(int8_t delta) {
|
||||||
|
cleanliness += delta;
|
||||||
|
|
||||||
|
if (cleanliness < 0) {
|
||||||
|
isAlive = false;
|
||||||
|
reasonForDeath = "disease";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cleanliness > MAXIMUM_STAT) {
|
||||||
|
isAlive = false;
|
||||||
|
reasonForDeath = "overcleaning";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int8_t Pet::getHunger() const {
|
||||||
|
return hunger;
|
||||||
|
}
|
||||||
|
|
||||||
|
int8_t Pet::getJoy() const {
|
||||||
|
return joy;
|
||||||
|
}
|
||||||
|
|
||||||
|
int8_t Pet::getEnergy() const {
|
||||||
|
return energy;
|
||||||
|
}
|
||||||
|
|
||||||
|
int8_t Pet::getCleanliness() const {
|
||||||
|
return cleanliness;
|
||||||
|
}
|
||||||
|
|
||||||
|
String Pet::getReasonForDeath() const {
|
||||||
|
return reasonForDeath;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte Pet::getAnimationFrame() {
|
||||||
|
uint64_t currentTime = millis();
|
||||||
|
|
||||||
|
if (currentTime - lastAnimationFrameTime >= ANIMATION_FRAME_INTERVAL) {
|
||||||
|
lastAnimationFrameTime = currentTime;
|
||||||
|
lastAnimationFrame = (lastAnimationFrame + 1) % 2; // Alternate between frame 0 and 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return lastAnimationFrame;
|
||||||
|
}
|
||||||
-11
@@ -1,11 +0,0 @@
|
|||||||
|
|
||||||
This directory is intended for PlatformIO Test Runner and project tests.
|
|
||||||
|
|
||||||
Unit Testing is a software testing method by which individual units of
|
|
||||||
source code, sets of one or more MCU program modules together with associated
|
|
||||||
control data, usage procedures, and operating procedures, are tested to
|
|
||||||
determine whether they are fit for use. Unit testing finds problems early
|
|
||||||
in the development cycle.
|
|
||||||
|
|
||||||
More information about PlatformIO Unit Testing:
|
|
||||||
- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html
|
|
||||||
Reference in New Issue
Block a user