Initial commit

This commit is contained in:
Nacho
2025-05-24 15:14:47 +02:00
parent 043c4640d0
commit c0e829e4ca
56 changed files with 2611 additions and 1 deletions
+63
View File
@@ -0,0 +1,63 @@
#ifndef ANIMATIONS_H
#define ANIMATIONS_H
#include <TFT_eSPI.h>
const int numFrames = 39;
const int numFramesAngry = 2;
const int numFramesEating = 2;
const int numFramesHappy = 2;
const int numFramesRefuse = 4;
const int numFramesSleepy = 2;
// TODO: Cambiar a una animación mas atractiva
const int animationFrames[numFrames] = {
0, 1, 0, 1, 0, 1, 0, 1, 0, 7, 3, 7,
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 7, 3, 7,
1, 0, 1, 0, 1, 0, 1
};
// Asumiendo que el buffer es 240x240, dejando margen y los sprites son 96x96 (6 de factor)
const int animationPositions[] = {
72, 66, 60, 54, 48, 42, 36, 30, 24, 18, 24, 18,
24, 30, 36, 42, 48, 54, 60, 66, 72, 78, 84, 90, 96, 102, 108, 114, 120, 126, 120, 126,
114, 108, 102, 96, 90, 84, 78
};
const bool animationFlipSprites[numFrames] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0, 0, 0, 0, 0, 0, 0
};
const int angryAnimationFrames[] = {
8, 9
};
const int eatingAnimationFrames[] = {
3, 7
};
const int refuseAnimationFrames[] = {
6, 6, 6, 6
};
const int refuseAnimationFlips[] = {
0, 1, 0, 1
};
const int sleepyAnimationFrames[] = {
4, 5
};
const int happyAnimationFrames[] = {
3, 7
};
void animate_performAnimation(TFT_eSprite &buffer, TFT_eSprite &spr, struct SpriteData* spriteData, uint8_t offsetX);
void animate_performAttentionAnimation(TFT_eSprite &buffer, TFT_eSprite &spr, struct SpriteData* spriteData);
void animate_performEatingAnimation(TFT_eSprite &buffer, TFT_eSprite &spr, struct SpriteData* spriteData);
void animate_performRefuseAnimation(TFT_eSprite &buffer, TFT_eSprite &spr, struct SpriteData* spriteData);
void animate_performSleepyAnimation(TFT_eSprite &buffer, TFT_eSprite &spr, struct SpriteData* spriteData);
void animate_performHappyAnimation(TFT_eSprite &buffer, TFT_eSprite &spr, struct SpriteData* spriteData);
#endif
+95
View File
@@ -0,0 +1,95 @@
#include "animations.h"
#include "draw/draw.h"
#include "display/display.h"
#include "defs/sprite_data.h"
const char* TAG_A = "[ANIMATION]";
const int spriteHeightOnScreen = 72;
int currentAnimationFrame = 0;
void animate_performAnimation(TFT_eSprite &buffer, TFT_eSprite &spr, struct SpriteData* spriteData, uint8_t offsetX) {
draw_drawSprite(
buffer,
spr,
animationPositions[currentAnimationFrame] - offsetX,
spriteHeightOnScreen,
spriteData,
animationFrames[currentAnimationFrame],
6,
animationFlipSprites[currentAnimationFrame]
);
currentAnimationFrame = (currentAnimationFrame + 1) % numFrames;
}
void animate_performAttentionAnimation(TFT_eSprite &buffer, TFT_eSprite &spr, struct SpriteData* spriteData) {
currentAnimationFrame = (currentAnimationFrame + 1) % numFramesAngry;
draw_drawSprite(
buffer,
spr,
spriteHeightOnScreen,
spriteHeightOnScreen,
spriteData,
angryAnimationFrames[currentAnimationFrame],
6,
animationFlipSprites[currentAnimationFrame]
);
}
void animate_performEatingAnimation(TFT_eSprite &buffer, TFT_eSprite &spr, struct SpriteData* spriteData) {
currentAnimationFrame = (currentAnimationFrame + 1) % numFramesEating;
draw_drawSprite(
buffer,
spr,
spriteHeightOnScreen,
spriteHeightOnScreen,
spriteData,
eatingAnimationFrames[currentAnimationFrame],
6,
false
);
}
void animate_performRefuseAnimation(TFT_eSprite &buffer, TFT_eSprite &spr, struct SpriteData* spriteData) {
currentAnimationFrame = (currentAnimationFrame + 1) % numFramesRefuse;
draw_drawSprite(
buffer,
spr,
spriteHeightOnScreen,
spriteHeightOnScreen,
spriteData,
refuseAnimationFrames[currentAnimationFrame],
6,
refuseAnimationFlips[currentAnimationFrame]
);
}
void animate_performSleepyAnimation(TFT_eSprite &buffer, TFT_eSprite &spr, struct SpriteData* spriteData) {
currentAnimationFrame = (currentAnimationFrame + 1) % numFramesSleepy;
draw_drawSprite(
buffer,
spr,
spriteHeightOnScreen,
spriteHeightOnScreen,
spriteData,
sleepyAnimationFrames[currentAnimationFrame],
6,
false
);
}
void animate_performHappyAnimation(TFT_eSprite &buffer, TFT_eSprite &spr, struct SpriteData* spriteData) {
currentAnimationFrame = (currentAnimationFrame + 1) % numFramesHappy;
draw_drawSprite(
buffer,
spr,
spriteHeightOnScreen,
spriteHeightOnScreen,
spriteData,
happyAnimationFrames[currentAnimationFrame],
6,
false
);
}
+56
View File
@@ -0,0 +1,56 @@
#include "defs/defs.h"
#include "buttons.h"
#include <Arduino.h>
uint64_t lastPressedButtonTime = esp_timer_get_time();
bool inactive = false;
bool screenOff = false;
bool k1_prev = HIGH;
bool k2_prev = HIGH;
bool k3_prev = HIGH;
bool k4_prev = HIGH;
void buttons_checkInactivity() {
uint64_t currentTime = esp_timer_get_time();
if (currentTime - lastPressedButtonTime > INACTIVITY_THRESHOLD_TIME_US && !screenOff) {
digitalWrite(BL_PIN, LOW);
screenKey = OFF_SCREEN;
screenOff = true;
} else if (currentTime - lastPressedButtonTime > LAST_PRESSED_BUTTON_THRESHOLD_TIME_US && !inactive) {
screenKey = IDLE_SCREEN;
inactive = true;
}
}
uint8_t buttons_getPressedButtons() {
bool k1_current = digitalRead(K1_PIN);
bool k2_current = digitalRead(K2_PIN);
bool k3_current = digitalRead(K3_PIN);
bool k4_current = digitalRead(K4_PIN);
uint8_t retV = (
(k1_prev == HIGH && k1_current == LOW) << 3 |
(k2_prev == HIGH && k2_current == LOW) << 2 |
(k3_prev == HIGH && k3_current == LOW) << 1 |
(k4_prev == HIGH && k4_current == LOW)
);
if (retV != 0) {
tone(SPK_PIN, BEEP_FREQ_HZ, BEEP_LEN_MS);
lastPressedButtonTime = esp_timer_get_time();
inactive = false;
screenOff = false;
}
k1_prev = k1_current;
k2_prev = k2_current;
k3_prev = k3_current;
k4_prev = k4_current;
delay(15);
return retV;
}
+7
View File
@@ -0,0 +1,7 @@
#ifndef BUTTONS_H
#define BUTTONS_H
uint8_t buttons_getPressedButtons();
void buttons_checkInactivity();
#endif
+21
View File
@@ -0,0 +1,21 @@
#include "debug.h"
#include <freertos/FreeRTOS.h>
void debug_printFreeMemory() {
size_t freeMemory = esp_get_free_heap_size();
printf("[DEBUG] Free memory: %zu bytes\n", freeMemory);
}
void debug_printAllSprites(uint16_t** sprite, uint8_t numSprites, uint8_t width, uint8_t height) {
for (uint8_t i = 0; i < numSprites; i++) {
printf("[DEBUG] Sprite %d:\n", i);
for (uint8_t y = 0; y < height; y++) {
printf("[DEBUG] Row %d: ", y);
for (uint8_t x = 0; x < width; x++) {
printf("%04X ", sprite[i][y * width + x]);
}
printf("\n");
}
}
}
+9
View File
@@ -0,0 +1,9 @@
#ifndef DEBUG_H
#define DEBUG_H
#include <stdint.h>
void debug_printFreeMemory();
void debug_printAllSprites(uint16_t** sprite, uint8_t numSprites, uint8_t width, uint8_t height);
#endif
+13
View File
@@ -0,0 +1,13 @@
#ifndef BACKGROUND_DATA_H
#define BACKGROUND_DATA_H
#include <stdint.h>
struct BackgroundData {
uint8_t backgroundWidth;
uint8_t backgroundHeight;
uint8_t scaleFactor;
uint16_t* backgroundData;
};
#endif
+50
View File
@@ -0,0 +1,50 @@
#ifndef CHARA_DATA_H
#define CHARA_DATA_H
#include <stdint.h>
struct CharacterData {
// Calculated at runtime
uint8_t hunger;
uint8_t strength;
uint8_t effort;
uint8_t careMistakes;
uint8_t weight;
uint8_t age;
uint8_t poopNumber;
int32_t sleepCareMistakeCounter = 0;
int32_t evoLeftTimer;
int16_t hungerCareMistakeTimer;
int16_t strengthCareMistakeTimer;
bool gotLifeYearAdded = false;
bool hungerCareMistakeObtained = false;
bool strengthCareMistakeObtained = false;
bool sleepCareMistakeObtained = false;
bool careMistakeCallLight = false;
bool sleepy = false;
bool asleep = false;
bool injured = false;
bool dead = false;
// Obtained from structure
uint8_t idLine;
uint8_t idChara;
char charaName[40];
uint8_t hp;
uint8_t ap;
uint8_t bp;
uint8_t stage;
uint8_t attribute;
uint32_t sleepTime = 75600;
uint32_t wakeupTime = 25200;
uint32_t evoTime;
uint16_t initialStatsReductionTime = 600;
};
#endif
+162
View File
@@ -0,0 +1,162 @@
#ifndef DEFS_H
#define DEFS_H
#include <ESP32Time.h>
#include <MPU6050.h>
#include "defs/background_data.h"
#include "defs/sprite_data.h"
#define VERSION "Alpha v0.1"
// SCREEN PINOUT
#define SCL_PIN 34
#define SDA_PIN 35
#define RST_PIN 32
#define BL_PIN 25
// BUTTONS PINOUT
#define K1_PIN 33
#define K2_PIN 35
#define K3_PIN 34
#define K4_PIN 32
// SPEAKER PINOUT
#define SPK_PIN 21
// MPU6050 PINOUT
#define MPU_SCL_PIN 4
#define MPU_SDA_PIN 16
#define MPU_INT_PIN 12
// SPECIAL SCREEN THAT OPENS WHEN TIMERS ARE DONE
// RECEIVES AN EXTRA PARAMETER (INTERRUPTKEY)
#define TIMER_FINISHED_SCREEN 999
// ANIMATION FRAMERATE DEFINITION IN µS
#define ANIMATION_THRESHOLD_TIME_US 500000
#define ANIMATION_SLEEPY_THRESHOLD_TIME_US 2500000
// INACTIVITY TIMERS (LAST TIME A BUTTON WAS PRESSED)
#define LAST_PRESSED_BUTTON_THRESHOLD_TIME_US 10000000
#define INACTIVITY_THRESHOLD_TIME_US 30000000
// RTC TIMEOUT WHEN TIME NOT DEFINED
#define RTC_TIMEOUT_THRESHOLD_TIME_MS 100
// STANDARD BEEP WHEN PRESSING BUTTON
#define BEEP_FREQ_HZ 3000
#define BEEP_LEN_MS 50
// MENU ENTRIES
#define STATUS_SCREEN_MENU 0
#define FOOD_SCREEN_MENU 1
#define TRAIN_SCREEN_MENU 2
#define BATTLE_SCREEN_MENU 3
#define POOP_SCREEN_MENU 4
#define MEDICAL_SCREEN_MENU 5
#define SLEEP_SCREEN_MENU 6
#define SETTINGS_SCREEN_MENU 7
// SCREENS THAT OPEN AFTER CLICKING ON A MENU ENTRY
#define STATUS_SCREEN 10
#define FOOD_SCREEN 11
#define TRAIN_SCREEN 12
#define BATTLE_SCREEN 13
#define CLEAR_POOP_SCREEN 14
#define MEDICAL_SCREEN 15
#define SLEEP_SCREEN 16
#define SETTINGS_SCREEN 17
// ICONS FOR EACH MENU ENTRY (MENU.BIN)
#define STATUS_SCREEN_ICON 0
#define FOOD_SCREEN_ICON 1
#define TRAIN_SCREEN_ICON 2
#define BATTLE_SCREEN_ICON 3
#define CLEAR_POOP_ICON 4
#define MEDICAL_SCREEN_ICON 5
#define SLEEP_SCREEN_ICON 6
#define SETTINGS_SCREEN_ICON 7
#define CARE_MISTAKE_CALL_LIGHT 8
#define BED_SPRITE 9
// SCREENS
#define OFF_SCREEN -1
#define TITLE_SCREEN 0
#define CLOCK_EDIT_SCREEN 1
#define CLOCK_SCREEN 2
#define IDLE_SCREEN 3
#define MENU_SCREEN 4
#define FEEDING_SCREEN 20
#define REFUSING_SCREEN 21
#define SLEEPY_SCREEN 22
#define CARE_MISTAKE_SCREEN 23
#define POOPING_SCREEN 24
#define HAPPY_SCREEN 25
// SMALL UI ICONS (UI.BIN)
#define POOP_ICON 0
#define FOOD_ICON 1
#define PILL_ICON 2
#define ZZZ_ICON 3
#define AGE_ICON 4
#define SCALE_ICON 5
#define ARROW_ICON 6
#define FIREWORKS_ICON 7
#define COMPLAIN_ICON 8
#define FULL_HEART_ICON 9
#define EMPTY_HEART_ICON 10
#define CLEANER_ICON 11
// STANDARD VPET PARAMETER (CARE MISTAKES)
#define CARE_MISTAKE_COUNTER_MAX 60
#define SLEEP_CARE_MISTAKE_COUNTER_MAX 60
#define SLEEP_COUNTER_MAX 120
// ATTACK PATTERNS
#define ATTACK_PATTERN_MEDIOCRE 0
#define ATTACK_PATTERN_BAD 1
#define ATTACK_PATTERN_GOOD 2
#define ATTACK_PATTERN_GREAT 3
#define ATTACK_PATTERN_EXCELLENT 4
// BUTTON PRESSES DEFINITIONS
#define K1_PRESSED 8
#define K2_PRESSED 4
#define K3_PRESSED 2
#define K4_PRESSED 1
#define NONE_PRESSED 0
extern int screenKey;
extern int menuKey;
extern int submenuKey;
extern int interruptKey;
extern bool k1_prev;
extern bool k2_prev;
extern bool k3_prev;
extern bool k4_prev;
extern bool inactive;
extern bool screenOff;
extern uint64_t lastPressedButtonTime;
extern uint64_t lastUpdateTime;
extern uint64_t lastBeepTime;
extern struct CharacterData charaData;
extern struct tm timeInfo;
extern uint32_t dayUnixTime;
extern ESP32Time rtc;
extern MPU6050 mpu;
extern hw_timer_t *actionTimerDelta;
extern bool runVpetTasks;
extern uint8_t beepCounter;
extern uint16_t stepCounter;
#endif
+16
View File
@@ -0,0 +1,16 @@
#ifndef EVO_DATA_H
#define EVO_DATA_H
#include <stdint.h>
struct EvoData {
uint8_t charaId;
uint8_t charaNextId;
uint8_t maxCareMistakes;
uint8_t maxSleepInterruptions;
uint8_t maxOverfeed;
uint8_t minWonBattles;
uint8_t minTotalBattles;
};
#endif
+13
View File
@@ -0,0 +1,13 @@
#ifndef SPRITE_DATA_H
#define SPRITE_DATA_H
#include <stdint.h>
struct SpriteData {
uint8_t spriteWidth;
uint8_t spriteHeight;
uint8_t spriteNumber;
uint16_t** spriteData;
};
#endif
+30
View File
@@ -0,0 +1,30 @@
#include "display.h"
void tft_initDisplay(TFT_eSPI &tft, uint16_t color) {
tft.init();
tft.setRotation(0);
tft.fillScreen(TFT_RED);
}
void tft_initScreenBuffer(TFT_eSprite &buffer, uint16_t color) {
buffer.createSprite(SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1);
buffer.fillSprite(color);
buffer.setTextColor(TFT_BLACK);
buffer.setTextSize(4);
buffer.pushSprite(0, 0);
}
void tft_drawBuffer(TFT_eSprite &buffer) {
buffer.pushSprite(0, 0);
}
void tft_clearBuffer(TFT_eSprite &buffer, uint16_t color) {
buffer.fillSprite(color);
}
void tft_drawCenteredText(TFT_eSprite &buffer, const char* text, int factor, int y) {
size_t textWidth = strlen(text) * factor * 6;
int x = (SCREEN_WIDTH - textWidth) / 2;
buffer.setTextSize(factor);
buffer.drawString(text, x, y);
}
+15
View File
@@ -0,0 +1,15 @@
#ifndef DISPLAY_H
#define DISPLAY_H
#include <TFT_eSPI.h>
const int SCREEN_WIDTH = 240;
const int SCREEN_HEIGHT = 240;
void tft_initDisplay(TFT_eSPI &tft, uint16_t color = TFT_WHITE);
void tft_initScreenBuffer(TFT_eSprite &spr, uint16_t color = TFT_WHITE);
void tft_drawBuffer(TFT_eSprite &buffer);
void tft_clearBuffer(TFT_eSprite &buffer, uint16_t color = TFT_WHITE);
void tft_drawCenteredText(TFT_eSprite &buffer, const char* text, int factor, int y);
#endif
+18
View File
@@ -0,0 +1,18 @@
#ifndef DRAW_H
#define DRAW_H
#include <TFT_eSPI.h>
void draw_drawSprite(
TFT_eSprite &buffer, TFT_eSprite &tft,
int x, int y,
struct SpriteData* spriteData, uint8_t spriteNumber,
uint8_t factor, bool flipHorizontal = false
);
void draw_drawBackground(TFT_eSprite &buffer, TFT_eSprite &bg, int spr_w, int spr_h, int factor);
void draw_drawSpriteCentered(
TFT_eSprite &buffer, TFT_eSprite &spr,
struct SpriteData* spriteData, uint8_t spriteNumber, uint8_t factor, bool flipped = false, int y = -1
);
#endif
+19
View File
@@ -0,0 +1,19 @@
#include "draw.h"
const char* TAG_DB = "[DRAW BG]";
void draw_drawBackground(TFT_eSprite &buffer, TFT_eSprite &bg, int spr_w, int spr_h, int factor) {
int scaledWidth = spr_w * factor;
int scaledHeight = spr_h * factor;
for (int sy = 0; sy < scaledHeight; sy++) {
for (int sx = 0; sx < scaledWidth; sx++) {
int srcX = sx / factor;
int srcY = sy / factor;
uint16_t color = bg.readPixel(srcX, srcY);
buffer.drawPixel(sx, sy, color);
}
}
}
+54
View File
@@ -0,0 +1,54 @@
#include "draw.h"
#include "defs/sprite_data.h"
const char* TAG_D = "[DRAW]";
void draw_drawSprite(
TFT_eSprite &buffer, TFT_eSprite &spr, int x, int y,
struct SpriteData* spriteData, uint8_t spriteNumber, uint8_t factor, bool flipHorizontal
) {
int scaledWidth = spriteData->spriteWidth * factor;
int scaledHeight = spriteData->spriteHeight * factor;
spr.createSprite(scaledWidth, scaledHeight);
for (int sy = 0; sy < scaledHeight; sy++) {
for (int sx = 0; sx < scaledWidth; sx++) {
int srcX = sx / factor;
if (flipHorizontal) {
srcX = (spriteData->spriteWidth - 1) - srcX;
}
int srcY = sy / factor;
uint16_t color = spriteData->spriteData
[spriteNumber]
[srcY * spriteData->spriteWidth + srcX];
spr.drawPixel(sx, sy, color);
}
}
spr.pushToSprite(&buffer, x, y, TFT_TRANSPARENT);
//printf("%s: Sprite %d drawn at (%d, %d) %s\n", TAG_D, spriteNumber, x, y, (flipHorizontal ? "flipped" : ""));
}
void draw_drawSpriteCentered(
TFT_eSprite &buffer, TFT_eSprite &spr,
struct SpriteData* spriteData, uint8_t spriteNumber, uint8_t factor, bool flipped, int y
) {
int x = (TFT_WIDTH - (spriteData->spriteWidth * factor)) / 2;
int new_y;
if (y == -1) {
new_y = (TFT_HEIGHT - (spriteData->spriteHeight * factor)) / 2;
} else {
new_y = y;
}
draw_drawSprite(
buffer, spr, x, new_y,
spriteData, spriteNumber, factor, flipped
);
}
+161
View File
@@ -0,0 +1,161 @@
#include <Arduino.h>
#include "display/display.h"
#include "memory/memory.h"
#include "storage/storage.h"
#include "animations/animations.h"
#include "debug/debug.h"
#include "defs/defs.h"
#include "defs/chara_data.h"
#include "menu/menu.h"
#include "buttons/buttons.h"
#include "vpet/vpet.h"
#include "vpet/steps/steps.h"
const char* TAG = "[MAIN]";
TFT_eSPI tft = TFT_eSPI();
TFT_eSprite composite = TFT_eSprite(&tft);
TFT_eSprite sprite = TFT_eSprite(&tft);
TFT_eSprite bg = TFT_eSprite(&tft);
struct BackgroundData backgroundData;
struct SpriteData mainCharacterSprites;
struct SpriteData menuElementsData;
struct SpriteData uiElementsData;
struct CharacterData charaData;
int screenKey = TITLE_SCREEN;
int menuKey = STATUS_SCREEN_MENU;
int submenuKey = -1;
uint32_t dayUnixTime = 0;
uint16_t stepCounter = 0;
struct tm timeInfo;
MPU6050 mpu;
ESP32Time rtc(0);
TaskHandle_t secondLoop = NULL;
void loop2();
void secondCoreTask(void*);
void setup() {
Serial.begin(115200);
delay(100); // Give MPU6050 and ESP32 time to power up
Wire.begin(MPU_SDA_PIN, MPU_SCL_PIN); // I2C init before MPU6050
mpu.initialize();
tft_initDisplay(tft, TFT_BLACK);
tft_initScreenBuffer(composite, TFT_BLACK);
storage_init();
storage_readFile("/sprite.bin", &mainCharacterSprites);
storage_readFile("/menu.bin", &menuElementsData);
storage_readFile("/ui.bin", &uiElementsData);
storage_initBackground("/bg.bin", bg);
debug_printFreeMemory();
pinMode(K1_PIN, INPUT_PULLUP);
pinMode(K2_PIN, INPUT_PULLUP);
pinMode(K3_PIN, INPUT_PULLUP);
pinMode(K4_PIN, INPUT_PULLUP);
charaData.hunger = 4;
charaData.strength = 4;
charaData.effort = 4;
charaData.evoLeftTimer = 60;
charaData.hungerCareMistakeTimer = 60;
charaData.strengthCareMistakeTimer = 60;
xTaskCreatePinnedToCore(secondCoreTask, "VPET_EVAL", 4096, NULL, 0, &secondLoop, 0);
}
void loop() {
switch (screenKey) {
case TITLE_SCREEN:
menu_drawTitle(composite, bg);
break;
case CLOCK_EDIT_SCREEN:
menu_drawClockEdit(composite, bg);
break;
case CLOCK_SCREEN:
menu_drawClock(composite, bg, menuKey);
break;
case IDLE_SCREEN:
menu_drawIdleScreen(composite, bg, sprite, &mainCharacterSprites, &menuElementsData, &uiElementsData);
break;
case MENU_SCREEN:
menu_drawCurrentMenuOption(composite, bg, sprite, &menuElementsData);
break;
case STATUS_SCREEN:
menu_statusScreen(composite, bg, sprite, &uiElementsData, &charaData);
break;
case OFF_SCREEN:
menu_offScreen(composite);
break;
case TIMER_FINISHED_SCREEN:
menu_timerFinishedScreen(composite, bg, sprite, &mainCharacterSprites);
break;
case FOOD_SCREEN:
menu_foodScreen(composite, bg, sprite, &uiElementsData);
break;
case FEEDING_SCREEN:
menu_feedingScreen(composite, bg, sprite, &uiElementsData, &mainCharacterSprites, submenuKey);
break;
case REFUSING_SCREEN:
menu_refuseScreen(composite, bg, sprite, &mainCharacterSprites);
break;
case SLEEPY_SCREEN:
menu_sleepyScreen(composite, bg, sprite, &mainCharacterSprites, &menuElementsData);
break;
case SLEEP_SCREEN:
menu_sleepingScreen(composite, bg, sprite, &mainCharacterSprites, &menuElementsData, &uiElementsData);
break;
case CARE_MISTAKE_SCREEN:
menu_careMistakeScreen(composite, bg, sprite, &mainCharacterSprites, &menuElementsData);
break;
case POOPING_SCREEN:
menu_poopScreen(composite, bg, sprite, &mainCharacterSprites, &uiElementsData, &menuElementsData);
break;
case CLEAR_POOP_SCREEN:
menu_clearPoopScreen(composite, bg, sprite, &mainCharacterSprites, &menuElementsData, &uiElementsData);
break;
case HAPPY_SCREEN:
menu_drawHappyScreen(composite, bg, sprite, &mainCharacterSprites, &uiElementsData);
break;
}
}
void loop2() {
steps_countSteps();
buttons_checkInactivity();
vpet_runVpetTasks();
}
void secondCoreTask(void*) {
for (;;) { loop2(); }
}
+41
View File
@@ -0,0 +1,41 @@
#include "memory.h"
const char* TAG_M = "[MEMORY]";
uint16_t** memory_allocate(uint8_t numSprite, uint8_t width, uint8_t height) {
uint16_t** ptr = (uint16_t**) malloc(numSprite * sizeof(uint16_t*));
if (ptr == NULL) {
printf("%s Memory allocation failed\n", TAG_M);
return NULL;
}
for (uint8_t i = 0; i < numSprite; i++) {
ptr[i] = (uint16_t*) malloc(width * height * sizeof(uint16_t));
if (ptr[i] == NULL) {
printf("%s Memory allocation failed for sprite %d\n", TAG_M, i);
for (uint8_t j = 0; j < i; j++) {
free(ptr[j]);
}
free(ptr);
return NULL;
}
}
printf("%s Allocated %zu bytes at %p\n", TAG_M, numSprite * (width * height * 2), ptr);
return ptr;
}
void memory_free(uint8_t* ptr) {
if (ptr != NULL) {
free(ptr);
ptr = NULL;
} else {
printf("%s Pointer is already NULL\n", TAG_M);
}
}
+18
View File
@@ -0,0 +1,18 @@
#ifndef MEMORY_H
#define MEMORY_H
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
struct spriteData {
uint8_t width;
uint8_t height;
uint8_t number;
uint16_t** data;
};
uint16_t** memory_allocate(uint8_t numSprite, uint8_t width, uint8_t height);
void memory_free(uint8_t* ptr);
#endif
+42
View File
@@ -0,0 +1,42 @@
#include "menu.h"
#include "buttons/buttons.h"
#include "animations/animations.h"
#include "display/display.h"
#include "defs/chara_data.h"
#include "draw/draw.h"
uint64_t lastBeepTime = esp_timer_get_time();
uint8_t beepCounter = 0;
void menu_careMistakeScreen(TFT_eSprite &composite, TFT_eSprite &bg, TFT_eSprite &sprite, struct SpriteData* spriteData, struct SpriteData* uiSpritesBig) {
uint64_t currentTime = esp_timer_get_time();
uint8_t pressedButtons = buttons_getPressedButtons();
if (currentTime - lastBeepTime > ANIMATION_THRESHOLD_TIME_US * 2 && beepCounter < 10) {
tone(SPK_PIN, 2500, 100);
tone(SPK_PIN, 5000, 100);
lastBeepTime = currentTime;
beepCounter++;
}
if (currentTime - lastUpdateTime > ANIMATION_THRESHOLD_TIME_US) {
draw_drawBackground(composite, bg, 90, 90, 3);
tft_clearBuffer(sprite, TFT_TRANSPARENT);
animate_performAttentionAnimation(composite, sprite, spriteData);
tft_clearBuffer(sprite, TFT_TRANSPARENT);
menu_uiOverlay(composite, sprite, uiSpritesBig);
lastUpdateTime = currentTime;
}
if (pressedButtons != 0) {
screenKey = IDLE_SCREEN;
beepCounter = 0;
}
tft_drawBuffer(composite);
}
+77
View File
@@ -0,0 +1,77 @@
#include "menu.h"
#include "defs/defs.h"
#include "draw/draw.h"
#include "display/display.h"
#include "buttons/buttons.h"
#include "vpet/vpet.h"
void menu_drawClock(TFT_eSprite &composite, TFT_eSprite &bg, int menuOption) {
uint8_t pressedButtons = buttons_getPressedButtons();
switch (pressedButtons) {
case 4:
screenKey = IDLE_SCREEN;
break;
default:
break;
}
char hourBuffer[6];
draw_drawBackground(composite, bg, 90, 90, 3);
snprintf(hourBuffer, 6, "%02d:%02d", timeInfo.tm_hour, timeInfo.tm_min);
composite.setTextSize(4);
composite.drawString(hourBuffer, 40, 90);
sniprintf(hourBuffer, 3, "%02d", timeInfo.tm_sec);
composite.setTextSize(2);
composite.drawString(hourBuffer, 170, 104);
tft_drawBuffer(composite);
}
void menu_drawClockEdit(TFT_eSprite &composite, TFT_eSprite &bg) {
char textBuffer[6];
static int clockHourCount = 0;
static int clockMinuteCount = 0;
uint8_t pressedButtons = buttons_getPressedButtons();
lastPressedButtonTime = esp_timer_get_time();
switch (pressedButtons) {
case 8:
clockHourCount = (clockHourCount + 1) % 24;
break;
case 4:
clockMinuteCount = (clockMinuteCount + 1) % 60;
break;
case 2:
rtc.setTime(0, clockMinuteCount, clockHourCount, 1, 11, 2024);
getLocalTime(&timeInfo, 50);
dayUnixTime = mktime(&timeInfo) % 86400;
screenKey = CLOCK_SCREEN;
onActionTimerDelta();
vpet_initTimer();
break;
default:
break;
}
draw_drawBackground(composite, bg, 90, 90, 3);
snprintf(textBuffer, 6, "%02d:%02d", clockHourCount, clockMinuteCount);
composite.setTextSize(4);
composite.drawString(textBuffer, 40, 90);
composite.drawString("SET", 40, 122);
tft_drawBuffer(composite);
}
+49
View File
@@ -0,0 +1,49 @@
#include "menu.h"
#include "animations/animations.h"
#include "buttons/buttons.h"
#include "draw/draw.h"
#include "display/display.h"
void menu_feedingScreen(
TFT_eSprite &composite, TFT_eSprite &bg, TFT_eSprite &charaSprite,
struct SpriteData* uiSpriteData, struct SpriteData* charaSpriteData, uint8_t item
) {
static int currentAnimationFrame = 0;
uint64_t currentTime = esp_timer_get_time();
uint8_t pressedButtons = buttons_getPressedButtons();
switch (pressedButtons) {
case 8:
case 4:
screenKey = FOOD_SCREEN;
currentAnimationFrame = 0;
lastPressedButtonTime = currentTime;
submenuKey = -1;
break;
default:
break;
}
if (currentTime - lastUpdateTime > ANIMATION_THRESHOLD_TIME_US) {
draw_drawBackground(composite, bg, 90, 90, 3);
tft_clearBuffer(charaSprite, TFT_TRANSPARENT);
animate_performEatingAnimation(composite, charaSprite, charaSpriteData);
tft_clearBuffer(charaSprite, TFT_TRANSPARENT);
draw_drawSprite(composite, charaSprite, 24, 120, uiSpriteData, item, 6);
lastUpdateTime = currentTime;
currentAnimationFrame++;
}
if (currentAnimationFrame > 6) {
screenKey = FOOD_SCREEN;
lastPressedButtonTime = currentTime;
currentAnimationFrame = 0;
submenuKey = -1;
}
tft_drawBuffer(composite);
}
+83
View File
@@ -0,0 +1,83 @@
#include "menu.h"
#include "display/display.h"
#include "draw/draw.h"
#include "buttons/buttons.h"
#include "defs/chara_data.h"
#include "vpet/vpet.h"
void menu_foodScreen(TFT_eSprite &composite, TFT_eSprite &bg, TFT_eSprite &mainChara, struct SpriteData* spriteData) {
if (charaData.sleepy) {
tone(SPK_PIN, BEEP_FREQ_HZ, BEEP_LEN_MS);
delay(100);
tone(SPK_PIN, BEEP_FREQ_HZ, BEEP_LEN_MS);
screenKey = MENU_SCREEN;
return;
}
static uint8_t arrowPosition = 0;
uint8_t pressedButtons = buttons_getPressedButtons();
switch (pressedButtons) {
case 8:
arrowPosition = (arrowPosition + 1) % 2;
break;
case 2:
screenKey = MENU_SCREEN;
break;
default:
break;
}
if (pressedButtons == 4) {
switch(arrowPosition) {
case 0:
if (charaData.hunger < 8) {
charaData.weight++;
charaData.hunger++;
screenKey = FEEDING_SCREEN;
submenuKey = FOOD_ICON;
} else {
screenKey = REFUSING_SCREEN;
}
break;
case 1:
if (charaData.strength < 8) {
charaData.strength++;
charaData.weight += 2;
screenKey = FEEDING_SCREEN;
submenuKey = PILL_ICON;
} else {
screenKey = REFUSING_SCREEN;
}
break;
default:
break;
}
vpet_computeCallLight();
}
composite.setTextSize(4);
draw_drawBackground(composite, bg, 90, 90, 3);
menu_foodScreen_drawEntry(composite, mainChara, spriteData, 0, FOOD_ICON, "Meat");
menu_foodScreen_drawEntry(composite, mainChara, spriteData, 1, PILL_ICON, "Pill");
draw_drawSprite(composite, mainChara, 5, (arrowPosition * 34) + 5, spriteData, ARROW_ICON, 4);
tft_drawBuffer(composite);
}
void menu_foodScreen_drawEntry(
TFT_eSprite &composite, TFT_eSprite &mainChara, struct SpriteData* spriteData,
uint8_t entryId, uint8_t spriteNumber, const char* textEntry
) {
tft_clearBuffer(mainChara, TFT_TRANSPARENT);
draw_drawSprite(composite, mainChara, 45, (entryId * 34) + 5, spriteData, spriteNumber, 4);
composite.drawString(textEntry, 80, (entryId * 34) + 5);
}
+46
View File
@@ -0,0 +1,46 @@
#include "menu.h"
#include "defs/defs.h"
#include "defs/sprite_data.h"
#include "display/display.h"
#include "draw/draw.h"
#include "animations/animations.h"
void menu_drawHappyScreen(
TFT_eSprite &composite, TFT_eSprite &bg, TFT_eSprite &sprite,
struct SpriteData* spriteData, struct SpriteData* smallUiElements
) {
uint8_t frameCounter = 0;
while (true) {
uint64_t currentTime = esp_timer_get_time();
if (currentTime - lastUpdateTime > ANIMATION_THRESHOLD_TIME_US) {
if (frameCounter > 3) {
screenKey = IDLE_SCREEN; // TODO: Change for while battling
menuKey = STATUS_SCREEN;
return;
}
draw_drawBackground(composite, bg, 90, 90, 3);
tft_clearBuffer(sprite, TFT_TRANSPARENT);
animate_performHappyAnimation(composite, sprite, spriteData);
if (frameCounter % 2 == 0) {
tone(SPK_PIN, 7500, 50);
tone(SPK_PIN, 5000, 50);
tone(SPK_PIN, 2500, 50);
tone(SPK_PIN, 1000, 50);
tft_clearBuffer(sprite, TFT_TRANSPARENT);
draw_drawSprite(composite, sprite, 18, 72, smallUiElements, FIREWORKS_ICON, 6);
draw_drawSprite(composite, sprite, 174, 72, smallUiElements, FIREWORKS_ICON, 6);
}
frameCounter++;
lastUpdateTime = currentTime;
}
tft_drawBuffer(composite);
}
}
+54
View File
@@ -0,0 +1,54 @@
#include "menu.h"
#include "draw/draw.h"
#include "defs/defs.h"
#include "display/display.h"
#include "buttons/buttons.h"
#include "animations/animations.h"
#include "defs/chara_data.h"
uint64_t lastUpdateTime = esp_timer_get_time();
void menu_drawIdleScreen(TFT_eSprite &composite, TFT_eSprite &bg, TFT_eSprite &sprite, struct SpriteData* spriteData, struct SpriteData* bigUiElements, struct SpriteData* smallUiElements) {
if (charaData.sleepy && !charaData.asleep) {
screenKey = SLEEPY_SCREEN;
return;
} else if ((charaData.sleepy && charaData.asleep) || charaData.asleep) {
screenKey = SLEEP_SCREEN;
return;
}
uint8_t pressedButtons = buttons_getPressedButtons();
switch (pressedButtons) {
case 8:
screenKey = MENU_SCREEN;
menuKey = 0;
break;
case 4:
screenKey = CLOCK_SCREEN;
break;
default:
break;
}
uint64_t currentTime = esp_timer_get_time();
if (currentTime - lastUpdateTime > ANIMATION_THRESHOLD_TIME_US) {
draw_drawBackground(composite, bg, 90, 90, 3);
tft_clearBuffer(sprite, TFT_TRANSPARENT);
uint8_t offsetX = menu_poopOverlay(composite, sprite, smallUiElements);
tft_clearBuffer(sprite, TFT_TRANSPARENT);
animate_performAnimation(composite, sprite, spriteData, offsetX);
tft_clearBuffer(sprite, TFT_TRANSPARENT);
menu_uiOverlay(composite, sprite, bigUiElements);
lastUpdateTime = currentTime;
}
tft_drawBuffer(composite);
}
+44
View File
@@ -0,0 +1,44 @@
#ifndef MENU_H
#define MENU_H
#include <TFT_eSPI.h>
#include "defs/defs.h"
void menu_drawCurrentMenuOption(TFT_eSprite &composite, TFT_eSprite &bg, TFT_eSprite &icon, struct SpriteData* spriteData);
void menu_drawClock(TFT_eSprite &composite, TFT_eSprite &bg, int menuOption);
void menu_drawClockEdit(TFT_eSprite &composite, TFT_eSprite &bg);
void menu_drawTitle(TFT_eSprite &composite, TFT_eSprite &bg);
void menu_drawIdleScreen(TFT_eSprite &composite, TFT_eSprite &bg, TFT_eSprite &sprite, struct SpriteData* spriteData, struct SpriteData* bigUiElements, struct SpriteData* smallUiElements);
void menu_offScreen(TFT_eSprite &buffer);
void menu_statusScreen(TFT_eSprite &composite, TFT_eSprite &bg, TFT_eSprite &sprite, struct SpriteData* spriteData, struct CharacterData* charaData);
void menu_statusScreen_drawStat(TFT_eSprite &composite, TFT_eSprite &sprite, struct SpriteData* spriteData, int x, int y, const char* text, uint8_t statValue);
void menu_timerFinishedScreen(TFT_eSprite &composite, TFT_eSprite &bg, TFT_eSprite &sprite, struct SpriteData* spriteData);
void menu_uiOverlay(TFT_eSprite &composite, TFT_eSprite &charSprite, struct SpriteData* uiElements);
void menu_foodScreen(TFT_eSprite &composite, TFT_eSprite &bg, TFT_eSprite &mainChara, struct SpriteData* spriteData);
void menu_foodScreen_drawEntry(
TFT_eSprite &composite, TFT_eSprite &mainChara, struct SpriteData* spriteData,
uint8_t entryId, uint8_t spriteNumber, const char* textEntry
);
void menu_feedingScreen(
TFT_eSprite &composite, TFT_eSprite &bg, TFT_eSprite &charaSprite,
struct SpriteData* uiSpriteData, struct SpriteData* charaSpriteData, uint8_t item
);
void menu_refuseScreen(TFT_eSprite &composite, TFT_eSprite &bg, TFT_eSprite &mainChara, struct SpriteData* spriteData);
void menu_sleepyScreen(TFT_eSprite &composite, TFT_eSprite &bg, TFT_eSprite &sprite, struct SpriteData* charaSprites, struct SpriteData* uiSprites);
void menu_careMistakeScreen(TFT_eSprite &composite, TFT_eSprite &bg, TFT_eSprite &sprite, struct SpriteData* spriteData, struct SpriteData* uiSpritesBig);
void menu_sleepingScreen(
TFT_eSprite &composite, TFT_eSprite &bg, TFT_eSprite &sprite,
struct SpriteData* mainCharaData, struct SpriteData* bigUiElements, struct SpriteData* smallUIElements
);
void menu_poopScreen(
TFT_eSprite &composite, TFT_eSprite &bg, TFT_eSprite &sprite,
struct SpriteData* spriteData, struct SpriteData* smallUiElements, struct SpriteData* bigUiElements
);
uint8_t menu_poopOverlay(TFT_eSprite &composite, TFT_eSprite &sprite, struct SpriteData* smallUiElements);
void menu_clearPoopScreen(TFT_eSprite &composite, TFT_eSprite &bg, TFT_eSprite &sprite, struct SpriteData* spriteData, struct SpriteData* bigUiElements, struct SpriteData* smallUiElements);
void menu_drawHappyScreen(
TFT_eSprite &composite, TFT_eSprite &bg, TFT_eSprite &sprite,
struct SpriteData* spriteData, struct SpriteData* smallUiElements
);
#endif
+109
View File
@@ -0,0 +1,109 @@
#include "menu.h"
#include "display/display.h"
#include "draw/draw.h"
#include "defs/defs.h"
#include "defs/sprite_data.h"
#include "defs/chara_data.h"
#include "buttons/buttons.h"
#include "vpet/vpet.h"
#include <string.h>
const int textXPos = 10;
const int textYPos = 180;
void menu_drawCurrentMenuOption(TFT_eSprite &composite, TFT_eSprite &bg, TFT_eSprite &icon, struct SpriteData* spriteData) {
draw_drawBackground(composite, bg, 90, 90, 3);
uint8_t pressedButtons = buttons_getPressedButtons();
switch (pressedButtons) {
case 8:
menuKey++;
break;
case 2:
screenKey = IDLE_SCREEN;
menuKey = STATUS_SCREEN_MENU;
return;
break;
default:
break;
}
if (pressedButtons == 4) {
switch (menuKey) {
case STATUS_SCREEN_MENU:
screenKey = STATUS_SCREEN;
break;
case FOOD_SCREEN_MENU:
screenKey = FOOD_SCREEN;
break;
case SLEEP_SCREEN_MENU:
charaData.asleep = true;
vpet_computeCallLight();
menuKey = STATUS_SCREEN;
screenKey = SLEEP_SCREEN;
break;
case POOP_SCREEN_MENU:
menuKey = STATUS_SCREEN;
screenKey = CLEAR_POOP_SCREEN;
return;
break;
default:
break;
}
return;
}
composite.setTextSize(4);
draw_drawSpriteCentered(composite, icon, spriteData, menuKey % 8, 6);
switch(menuKey % 9) {
case STATUS_SCREEN_MENU:
tft_drawCenteredText(composite, "Status", 4, textYPos);
break;
case FOOD_SCREEN_MENU:
tft_drawCenteredText(composite, "Food", 4, textYPos);
break;
case TRAIN_SCREEN_MENU:
tft_drawCenteredText(composite, "Train", 4, textYPos);
break;
case BATTLE_SCREEN_MENU:
tft_drawCenteredText(composite, "Battle", 4, textYPos);
break;
case POOP_SCREEN_MENU:
tft_drawCenteredText(composite, "Cleanup", 4, textYPos);
break;
case MEDICAL_SCREEN_MENU:
tft_drawCenteredText(composite, "Medical", 4, textYPos);
break;
case SLEEP_SCREEN_MENU:
tft_drawCenteredText(composite, "Sleep", 4, textYPos);
break;
case SETTINGS_SCREEN_MENU:
tft_drawCenteredText(composite, "Settings", 4, textYPos);
break;
case 8:
menuKey = 0;
screenKey = IDLE_SCREEN;
return;
break;
}
menu_uiOverlay(composite, icon, spriteData);
tft_drawBuffer(composite);
}
+13
View File
@@ -0,0 +1,13 @@
#include "menu.h"
#include "defs/defs.h"
#include "buttons/buttons.h"
#include "display/display.h"
void menu_offScreen(TFT_eSprite &buffer) {
uint8_t buttons = buttons_getPressedButtons();
if (buttons != 0) {
tft_drawBuffer(buffer);
digitalWrite(BL_PIN, HIGH);
screenKey = IDLE_SCREEN;
}
}
+28
View File
@@ -0,0 +1,28 @@
#include "menu.h"
#include "draw/draw.h"
#include "display/display.h"
#include "defs/defs.h"
#include "defs/chara_data.h"
#include "defs/sprite_data.h"
void menu_clearPoopScreen(
TFT_eSprite &composite, TFT_eSprite &bg, TFT_eSprite &sprite, struct SpriteData* spriteData, struct SpriteData* bigUiElements, struct SpriteData* smallUiElements
) {
int cleanerXPos = 174;
menu_drawIdleScreen(composite, bg, sprite, spriteData, bigUiElements, smallUiElements);
tft_clearBuffer(sprite, TFT_TRANSPARENT);
while (cleanerXPos > 18) {
draw_drawSprite(composite, sprite, cleanerXPos, 72, smallUiElements, CLEANER_ICON, 6);
draw_drawSprite(composite, sprite, cleanerXPos, 120, smallUiElements, CLEANER_ICON, 6);
tft_drawBuffer(composite);
cleanerXPos -= 6;
delay(50);
}
screenKey = HAPPY_SCREEN;
charaData.poopNumber = 0;
return;
}
+23
View File
@@ -0,0 +1,23 @@
#include "menu.h"
#include "draw/draw.h"
#include "defs/defs.h"
#include "defs/sprite_data.h"
#include "defs/chara_data.h"
#include "display/display.h"
uint8_t menu_poopOverlay(TFT_eSprite &composite, TFT_eSprite &sprite, struct SpriteData* smallUiElements) {
static bool poopFlip = false;
const uint8_t poopStartY = 120;
uint8_t poopStartX = 174;
bool poopTop = false;
for (int i = 0; i < charaData.poopNumber; i++) {
draw_drawSprite(composite, sprite, poopStartX, poopStartY - (48 * poopTop), smallUiElements, POOP_ICON, 6, poopFlip);
poopStartX -= (i % 2) * 48;
poopTop = !poopTop;
}
poopFlip = !poopFlip;
return 222 - (poopStartX + ((charaData.poopNumber % 2 == 0) * 48));
}
+81
View File
@@ -0,0 +1,81 @@
#include "menu.h"
#include "draw/draw.h"
#include "animations/animations.h"
#include "display/display.h"
#include "defs/defs.h"
#include "defs/chara_data.h"
#include "defs/sprite_data.h"
void menu_poopScreen(
TFT_eSprite &composite, TFT_eSprite &bg, TFT_eSprite &sprite,
struct SpriteData* spriteData, struct SpriteData* smallUiElements, struct SpriteData* bigUiElements
) {
uint8_t animationFrame = 0;
bool animationPosition = 0;
bool beepedAlready = false;
while (1) {
uint64_t currentTime = esp_timer_get_time();
if (currentTime - lastUpdateTime > ANIMATION_THRESHOLD_TIME_US && animationFrame < 4) {
draw_drawBackground(composite, bg, 90, 90, 3);
tft_clearBuffer(sprite, TFT_TRANSPARENT);
draw_drawSprite(composite, sprite, 72 + (animationPosition * 6), 72, spriteData, 6, 6);
tft_clearBuffer(sprite, TFT_TRANSPARENT);
menu_uiOverlay(composite, sprite, bigUiElements);
animationFrame++;
animationPosition = !animationPosition;
lastUpdateTime = currentTime;
} else if (currentTime - lastUpdateTime > ANIMATION_THRESHOLD_TIME_US && animationFrame < 6) {
if (!beepedAlready) {
tone(SPK_PIN, 2500, 50);
tone(SPK_PIN, 5000, 50);
tone(SPK_PIN, 2500, 50);
tone(SPK_PIN, 5000, 50);
beepedAlready = true;
}
draw_drawBackground(composite, bg, 90, 90, 3);
tft_clearBuffer(sprite, TFT_TRANSPARENT);
draw_drawSprite(composite, sprite, 174, 120, smallUiElements, POOP_ICON, 6);
tft_clearBuffer(sprite, TFT_TRANSPARENT);
menu_uiOverlay(composite, sprite, bigUiElements);
tft_clearBuffer(sprite, TFT_TRANSPARENT);
draw_drawSprite(composite, sprite, 72, 72, spriteData, 7, 6);
animationFrame++;
animationPosition = !animationPosition;
lastUpdateTime = currentTime;
} else if (animationFrame >= 6) {
if (
(charaData.hunger == 0 && !charaData.hungerCareMistakeObtained) ||
(charaData.strength == 0 && !charaData.strengthCareMistakeObtained) ||
(charaData.sleepy && !charaData.asleep && !charaData.sleepCareMistakeObtained)
) {
screenKey = CARE_MISTAKE_SCREEN;
} else {
screenKey = IDLE_SCREEN;
}
menuKey = 0;
animationFrame = 0;
animationPosition = 0;
break;
}
tft_drawBuffer(composite);
}
}
+50
View File
@@ -0,0 +1,50 @@
#include "menu.h"
#include "buttons/buttons.h"
#include "draw/draw.h"
#include "display/display.h"
#include "defs/defs.h"
#include "defs/sprite_data.h"
#include "animations/animations.h"
void menu_refuseScreen(TFT_eSprite &composite, TFT_eSprite &bg, TFT_eSprite &mainChara, struct SpriteData* spriteData) {
static uint8_t currentAnimationFrame = 0;
static bool soundPlayed = false;
uint64_t currentTime = esp_timer_get_time();
uint8_t pressedButtons = buttons_getPressedButtons();
switch (pressedButtons) {
case 8:
case 4:
currentAnimationFrame = 0;
screenKey = FOOD_SCREEN;
soundPlayed = false;
return;
break;
default:
break;
}
if (currentTime - lastUpdateTime > ANIMATION_THRESHOLD_TIME_US) {
draw_drawBackground(composite, bg, 90, 90, 3);
animate_performRefuseAnimation(composite, mainChara, spriteData);
lastUpdateTime = currentTime;
currentAnimationFrame++;
}
if (!soundPlayed) {
tone(SPK_PIN, 3000, 100);
tone(SPK_PIN, 1000, 100);
soundPlayed = true;
}
if (currentAnimationFrame > 4) {
currentAnimationFrame = 0;
soundPlayed = false;
screenKey = IDLE_SCREEN;
}
tft_drawBuffer(composite);
}
+57
View File
@@ -0,0 +1,57 @@
#include "menu.h"
#include "draw/draw.h"
#include "display/display.h"
#include "defs/defs.h"
#include "buttons/buttons.h"
#include "animations/animations.h"
#include "defs/chara_data.h"
void menu_sleepyScreen(TFT_eSprite &composite, TFT_eSprite &bg, TFT_eSprite &sprite, struct SpriteData* charaSprites, struct SpriteData* uiSprites) {
if (!charaData.asleep && !charaData.sleepy) {
screenKey = IDLE_SCREEN;
return;
} else if (charaData.asleep && charaData.sleepy) {
screenKey = SLEEP_SCREEN;
return;
}
uint64_t currentTime = esp_timer_get_time();
uint8_t pressedButtons = buttons_getPressedButtons();
switch (pressedButtons) {
case 8:
screenKey = MENU_SCREEN;
break;
case 4:
screenKey = CLOCK_SCREEN;
break;
default:
break;
}
if (currentTime - lastUpdateTime > ANIMATION_SLEEPY_THRESHOLD_TIME_US) {
draw_drawBackground(composite, bg, 90, 90, 3);
tft_clearBuffer(sprite, TFT_TRANSPARENT);
animate_performSleepyAnimation(composite, sprite, charaSprites);
tft_clearBuffer(sprite, TFT_TRANSPARENT);
menu_uiOverlay(composite, sprite, uiSprites);
tft_clearBuffer(sprite, TFT_TRANSPARENT);
menu_uiOverlay(composite, sprite, uiSprites);
lastUpdateTime = currentTime;
}
if (currentTime - lastBeepTime > ANIMATION_THRESHOLD_TIME_US * 2 && beepCounter < 10) {
tone(SPK_PIN, 2500, 100);
tone(SPK_PIN, 5000, 100);
lastBeepTime = currentTime;
beepCounter++;
}
tft_drawBuffer(composite);
}
+57
View File
@@ -0,0 +1,57 @@
#include "menu.h"
#include "defs/defs.h"
#include "draw/draw.h"
#include "display/display.h"
#include "buttons/buttons.h"
#include "animations/animations.h"
#include "defs/chara_data.h"
void menu_sleepingScreen(
TFT_eSprite &composite, TFT_eSprite &bg, TFT_eSprite &sprite,
struct SpriteData* mainCharaData, struct SpriteData* bigUiElements, struct SpriteData* smallUIElements
) {
if (charaData.sleepy && !charaData.asleep) {
screenKey = SLEEPY_SCREEN;
return;
} else if (!charaData.sleepy && !charaData.asleep) {
screenKey = IDLE_SCREEN;
return;
}
uint64_t currentTime = esp_timer_get_time();
static uint8_t frameCounter = 0;
uint8_t buttonsPressed = buttons_getPressedButtons();
switch (buttonsPressed) {
case 8:
screenKey = MENU_SCREEN;
break;
case 4:
screenKey = CLOCK_SCREEN;
break;
default:
break;
}
if (currentTime - lastUpdateTime > ANIMATION_SLEEPY_THRESHOLD_TIME_US) {
draw_drawBackground(composite, bg, 90, 90, 3);
tft_clearBuffer(sprite, TFT_TRANSPARENT);
animate_performSleepyAnimation(composite, sprite, mainCharaData);
tft_clearBuffer(sprite, TFT_TRANSPARENT);
draw_drawSprite(composite, sprite, 72, 72, bigUiElements, BED_SPRITE, 6);
tft_clearBuffer(sprite, TFT_TRANSPARENT);
draw_drawSprite(composite, sprite, 172, 72, smallUIElements, ZZZ_ICON, 6);
tft_clearBuffer(sprite, TFT_TRANSPARENT);
menu_uiOverlay(composite, sprite, bigUiElements);
lastUpdateTime = currentTime;
}
tft_drawBuffer(composite);
}
+52
View File
@@ -0,0 +1,52 @@
#include "menu.h"
#include "defs/defs.h"
#include "defs/sprite_data.h"
#include "defs/chara_data.h"
#include "buttons/buttons.h"
#include "display/display.h"
#include "draw/draw.h"
void menu_statusScreen(TFT_eSprite &composite, TFT_eSprite &bg, TFT_eSprite &sprite, struct SpriteData* spriteData, struct CharacterData* charaData) {
tft_clearBuffer(sprite, TFT_TRANSPARENT);
uint8_t pressedButtons = buttons_getPressedButtons();
switch (pressedButtons) {
case 2:
screenKey = MENU_SCREEN;
break;
default:
break;
}
draw_drawBackground(composite, bg, 90, 90, 3);
composite.setTextSize(4);
menu_statusScreen_drawStat(composite, sprite, spriteData, 10, 10, "Hunger", charaData->hunger);
menu_statusScreen_drawStat(composite, sprite, spriteData, 10, 80, "Strength", charaData->strength);
menu_statusScreen_drawStat(composite, sprite, spriteData, 10, 150, "Effort", charaData->effort);
tft_drawBuffer(composite);
}
void menu_statusScreen_drawStat(TFT_eSprite &composite, TFT_eSprite &sprite, struct SpriteData* spriteData, int x, int y, const char* text, uint8_t statValue) {
uint8_t icon;
composite.drawString(text, x, y);
for (int i = 0; i < 4; i++) {
if (i < statValue) { icon = FULL_HEART_ICON; }
else { icon = EMPTY_HEART_ICON; }
draw_drawSprite(
composite,
sprite,
15 + (i * 32),
y + 30,
spriteData,
icon,
4,
false
);
}
}
+15
View File
@@ -0,0 +1,15 @@
#include "menu.h"
int interruptKey = -1;
void menu_timerFinishedScreen(TFT_eSprite &composite, TFT_eSprite &bg, TFT_eSprite &sprite, struct SpriteData* spriteData) {
lastPressedButtonTime = esp_timer_get_time();
digitalWrite(BL_PIN, HIGH);
screenOff = false;
inactive = false;
beepCounter = 0;
screenKey = interruptKey;
}
+24
View File
@@ -0,0 +1,24 @@
#include "menu.h"
#include "display/display.h"
#include "draw/draw.h"
#include "defs/defs.h"
#include "buttons/buttons.h"
void menu_drawTitle(TFT_eSprite &composite, TFT_eSprite &bg) {
uint8_t pressedButtons = buttons_getPressedButtons();
lastPressedButtonTime = esp_timer_get_time();
if (pressedButtons == 8 || pressedButtons == 4) {
screenKey = CLOCK_EDIT_SCREEN;
}
draw_drawBackground(composite, bg, 90, 90, 3);
composite.setTextSize(4);
tft_drawCenteredText(composite, "NacaPet", 4, 40);
composite.setTextSize(2);
tft_drawCenteredText(composite, VERSION, 2, 80);
tft_drawBuffer(composite);
}
+28
View File
@@ -0,0 +1,28 @@
#include "menu.h"
#include "draw/draw.h"
#include "display/display.h"
#include "defs/chara_data.h"
void menu_uiOverlay(TFT_eSprite &composite, TFT_eSprite &charSprite, struct SpriteData* uiElements) {
struct tm timeInfo;
char hourBuffer[6];
composite.setTextSize(2);
composite.setTextColor(TFT_WHITE);
composite.fillRect(0, 0, 240, 24, TFT_BLACK);
getLocalTime(&timeInfo, RTC_TIMEOUT_THRESHOLD_TIME_MS);
snprintf(hourBuffer, 6, "%02d:%02d", timeInfo.tm_hour, timeInfo.tm_min);
composite.drawString(hourBuffer, 4, 4);
snprintf(hourBuffer, 6, "%05d", stepCounter);
composite.drawString(hourBuffer, 176, 4);
composite.setTextColor(TFT_BLACK);
if (charaData.careMistakeCallLight) {
tft_clearBuffer(charSprite, TFT_TRANSPARENT);
draw_drawSprite(composite, charSprite, 192, 192, uiElements, CARE_MISTAKE_CALL_LIGHT, 2);
}
}
+106
View File
@@ -0,0 +1,106 @@
#include "storage.h"
#include "memory/memory.h"
#include "defs/sprite_data.h"
const char* TAG_S = "[STORAGE]";
void storage_init() {
if (!SPIFFS.begin(true)) {
printf("%s Failed to mount file system\n", TAG_S);
} else {
printf("%s File system mounted successfully\n", TAG_S);
}
}
void storage_readFile(const char* path, struct SpriteData* spriteData) {
File file = SPIFFS.open(path, "r");
if (!file) {
printf("%s Failed to open file for reading\n", TAG_S);
return;
}
size_t bytesRead = 0;
size_t fileSize = file.size();
bytesRead += file.read(&spriteData->spriteWidth, 1);
bytesRead += file.read(&spriteData->spriteHeight, 1);
bytesRead += file.read(&spriteData->spriteNumber, 1);
printf("%s Read header: width=%d, height=%d, numSprites=%d\n",
TAG_S, spriteData->spriteWidth, spriteData->spriteHeight, spriteData->spriteNumber);
fileSize = (fileSize - 3) / sizeof(uint16_t);
spriteData->spriteData = memory_allocate(spriteData->spriteNumber, spriteData->spriteWidth, spriteData->spriteHeight);
size_t bufferSize = spriteData->spriteNumber * spriteData->spriteWidth * spriteData->spriteHeight;
uint8_t highByte;
uint8_t lowByte;
for (int sprN = 0; sprN < spriteData->spriteNumber; sprN++) {
for (int i = 0; i < spriteData->spriteWidth * spriteData->spriteHeight; i++) {
bytesRead += file.read(&highByte, 1);
bytesRead += file.read(&lowByte, 1);
uint16_t pixel = (highByte << 8) | lowByte;
if (i < bufferSize) {
(spriteData->spriteData)[sprN][i] = pixel;
} else {
printf("%s Buffer overflow, skipping pixel\n", TAG_S);
break;
}
}
}
printf("%s Read %zu bytes from file %s\n", TAG_S, bytesRead, path);
file.close();
}
void storage_initBackground(const char* path, TFT_eSprite& bg) {
File file = SPIFFS.open(path, "r");
if (!file) {
printf("%s Failed to open file for reading\n", TAG_S);
return;
}
size_t bytesRead = 0;
size_t fileSize = file.size();
uint8_t width;
uint8_t height;
bytesRead += file.read(&width, 1);
bytesRead += file.read(&height, 1);
printf("%s Read header: width=%d, height=%d\n", TAG_S, width, height);
fileSize = (fileSize - 2) / sizeof(uint16_t);
bg.createSprite(width, height);
for(int i = 0; i < width * height; i++) {
uint8_t highByte;
uint8_t lowByte;
bytesRead += file.read(&highByte, 1);
bytesRead += file.read(&lowByte, 1);
uint16_t pixel = (highByte << 8) | lowByte;
if (i < fileSize) {
bg.drawPixel(i % width, i / width, pixel);
} else {
printf("%s Buffer overflow, skipping pixel\n", TAG_S);
break;
}
}
printf("%s Read %zu bytes from file %s\n", TAG_S, bytesRead, path);
file.close();
}
+14
View File
@@ -0,0 +1,14 @@
#ifndef STORAGE_H
#define STORAGE_H
#include <SPIFFS.h>
#include <stdio.h>
#include <TFT_eSPI.h>
#include "defs/sprite_data.h"
void storage_init();
void storage_readFile(const char* path, struct SpriteData* spriteData);
void storage_initBackground(const char* path, TFT_eSprite& bg);
#endif
+29
View File
@@ -0,0 +1,29 @@
#include "steps.h"
#include "defs/defs.h"
float gravity = 0.0;
const float alpha = 0.99;
const float thresh = 0.80;
uint64_t lastStepTime = esp_timer_get_time();
void steps_countSteps() {
int16_t ax, ay, az;
mpu.getAcceleration(&ax, &ay, &az);
float axg = ax / 16384.0;
float ayg = ay / 16384.0;
float azg = az / 16384.0;
float mag = sqrt(axg*axg + ayg*ayg + azg*azg);
gravity = alpha * gravity + (1 - alpha) * mag;
float dyn = mag - gravity;
unsigned long now = esp_timer_get_time();
if (dyn > thresh && (now - lastStepTime) > 500000) {
stepCounter++;
lastStepTime = now;
}
}
+6
View File
@@ -0,0 +1,6 @@
#ifndef STEPS_H
#define STEPS_H
void steps_countSteps();
#endif
+11
View File
@@ -0,0 +1,11 @@
#ifndef TRAINING_H
#define TRAINING_H
#include <TFT_eSPI.h>
void training_screenTraining1(
TFT_eSprite &composite, TFT_eSprite &bg, TFT_eSprite &sprite,
struct SpriteData* mainCharaData, struct SpriteData* attackSprites
);
#endif
+36
View File
@@ -0,0 +1,36 @@
#include "training.h"
#include "defs/defs.h"
#include "defs/chara_data.h"
#include "defs/sprite_data.h"
#include "buttons/buttons.h"
void training_screenTraining1(
TFT_eSprite &composite, TFT_eSprite &bg, TFT_eSprite &sprite,
struct SpriteData* mainCharaData, struct SpriteData* attackSprites
) {
uint8_t attackPower = 0;
while (true) {
uint8_t buttonsRead = buttons_getPressedButtons();
switch (buttonsRead)
{
case 8:
attackPower++;
break;
case 2:
screenKey = IDLE_SCREEN;
return;
break;
default:
break;
}
uint64_t currentTime = esp_timer_get_time();
if (currentTime - lastUpdateTime > 10000000) {
// Display attack animation...
// NOT YET SLOW DOWN
}
}
}
+216
View File
@@ -0,0 +1,216 @@
#include "vpet.h"
#include "defs/defs.h"
#include "defs/chara_data.h"
hw_timer_t *actionTimerDelta = NULL;
bool runVpetTasks = false;
void vpet_initTimer() {
printf("[TIMER] Timer Init.\n");
actionTimerDelta = timerBegin(0, 80, true);
timerAttachInterrupt(actionTimerDelta, &onActionTimerDelta, true);
timerAlarmWrite(actionTimerDelta, 1000000, true);
timerAlarmEnable(actionTimerDelta);
}
void vpet_computeCallLight() {
charaData.careMistakeCallLight = (
(charaData.hunger == 0 && !charaData.hungerCareMistakeObtained) ||
(charaData.strength == 0 && !charaData.strengthCareMistakeObtained) ||
(charaData.sleepy && !charaData.asleep && !charaData.sleepCareMistakeObtained)
);
}
bool vpet_evalSleep() {
// Se devuelve true si quieres pausar los otros contadores
// False ejecutara los contadores correspondientes
if (
dayUnixTime < charaData.sleepTime &&
dayUnixTime > charaData.wakeupTime &&
charaData.sleepy
// Esto se ejecuta cuando ya es hora de despertarse
// Resultado el personaje se despierta
) {
charaData.sleepCareMistakeCounter = 0;
charaData.sleepCareMistakeObtained = false;
charaData.gotLifeYearAdded = false;
charaData.sleepy = false;
charaData.asleep = false;
return false;
} else if (
dayUnixTime < charaData.sleepTime &&
dayUnixTime > charaData.wakeupTime &&
charaData.asleep &&
charaData.sleepCareMistakeCounter < 60
// Esto se ejecuta cuando mandamos a dormir al personaje
// durante el dia.
// Resultado, el personaje deberia de dormir una siesta
) {
charaData.sleepCareMistakeCounter++;
return true;
} else if (
dayUnixTime < charaData.sleepTime &&
dayUnixTime > charaData.wakeupTime &&
charaData.asleep &&
charaData.sleepCareMistakeCounter >= 60
// Esto se ejecuta cuando la siesta del personaje acaba
// Resultado, el personaje se despierta
) {
charaData.sleepCareMistakeCounter = 0;
charaData.asleep = false;
return false;
} else if (
(
dayUnixTime > charaData.sleepTime ||
dayUnixTime < charaData.wakeupTime
) &&
!charaData.sleepy
// Esto se ejecuta cuando la hora actual del sistema
// está en el intervalo temporal de las horas el las que el
// personaje duerme
// Resultado: el personaje se duerme, y se llama a la pantalla
// de se acabo el temporizador, ademas activa la call light
) {
charaData.sleepy = true;
charaData.careMistakeCallLight = true;
screenKey = TIMER_FINISHED_SCREEN;
interruptKey = SLEEPY_SCREEN;
return true;
} else if (
charaData.sleepy && !charaData.asleep &&
charaData.sleepCareMistakeCounter < 60 &&
!charaData.sleepCareMistakeObtained
// Esto se ejecuta cuando el personaje debería de estar durmiendo
// pero no se le ha mandado a dormir, empieza a contar para pasar
// un care mistake
) {
charaData.sleepCareMistakeCounter++;
return true;
} else if (
charaData.sleepy && !charaData.asleep &&
charaData.sleepCareMistakeCounter >= 60 &&
!charaData.sleepCareMistakeObtained
// Esto se ejecuta cuando el personaje deberia de estar durmiendo
// pero no se le ha mandado a dormir, y el contador ya ha llegado
// al tiempo maximo
// Resultado: se añade el care mistake y se activa la flag para
// evitar otro care mistake
) {
charaData.sleepCareMistakeObtained = true;
charaData.careMistakes++;
return true;
} else if (
!charaData.gotLifeYearAdded &&
dayUnixTime < 1 // This stinks
// Esto se ejecuta cuando es media noche.
// Resultado: se incrementa la edad por 1
) {
charaData.age++;
charaData.gotLifeYearAdded = true;
return true;
} else if (
charaData.sleepy
) {
return true;
}
return false;
}
void vpet_evalTimers() {
if (charaData.hungerCareMistakeTimer >= 0) {
charaData.hungerCareMistakeTimer -= 1;
}
if (charaData.strengthCareMistakeTimer >= 0) {
charaData.strengthCareMistakeTimer -= 1;
}
if (
charaData.hungerCareMistakeTimer < 0 ||
charaData.strengthCareMistakeTimer < 0
) {
if (charaData.hunger > 0) {
charaData.hunger--;
screenKey = TIMER_FINISHED_SCREEN;
interruptKey = POOPING_SCREEN;
charaData.poopNumber++;
charaData.hungerCareMistakeTimer = 60;
}
if (!charaData.hungerCareMistakeObtained) {
if (
charaData.hunger == 0 &&
charaData.hungerCareMistakeTimer < 0
) {
charaData.careMistakes++;
charaData.hungerCareMistakeObtained = true;
} else if (charaData.hunger == 0) {
interruptKey = POOPING_SCREEN;
screenKey = TIMER_FINISHED_SCREEN;
charaData.poopNumber++;
}
}
if (charaData.strength > 0) {
charaData.strength--;
charaData.strengthCareMistakeTimer = 60;
}
if (!charaData.strengthCareMistakeObtained) {
if (
charaData.strength == 0 &&
charaData.strengthCareMistakeTimer < 0
) {
charaData.careMistakes++;
charaData.strengthCareMistakeObtained = true;
} else if(charaData.strength == 0) {
if (interruptKey != POOPING_SCREEN) {
interruptKey = CARE_MISTAKE_SCREEN;
screenKey = TIMER_FINISHED_SCREEN;
}
}
}
}
}
void IRAM_ATTR onActionTimerDelta() {
runVpetTasks = true;
}
void vpet_runVpetTasks() {
if (runVpetTasks) {
vpet_computeCallLight();
if (!vpet_evalSleep()) {
vpet_evalTimers();
}
printf("[MAIN]: Hunger timer %d, hunger %d\n", charaData.hungerCareMistakeTimer, charaData.hunger);
printf("[MAIN]: Strength timer %d, strength %d\n", charaData.strengthCareMistakeTimer, charaData.strength);
printf("[MAIN]: Evo timer %d\n", charaData.evoLeftTimer);
printf("[MAIN]: RTC time is %d\n", dayUnixTime);
printf("[MAIN]: Sleep counter is %d\n", charaData.sleepCareMistakeCounter);
printf("[MAIN]: Care mistake count is %d\n", charaData.careMistakes);
printf("[MAIN]: Is sleep care mistake tripped? %d\n", charaData.sleepCareMistakeObtained);
runVpetTasks = false;
}
}
+13
View File
@@ -0,0 +1,13 @@
#ifndef VPET_H
#define VPET_H
#include <Arduino.h>
void IRAM_ATTR onActionTimerDelta();
void vpet_initTimer();
void vpet_computeCallLight();
bool vpet_evalSleep();
void vpet_evalTimers();
void vpet_runVpetTasks();
#endif