#include <LiquidCrystal.h>
#include <stdlib.h>
#include <EEPROM.h>
LiquidCrystal lcd(12, 11, 10, 9, 8, 7);
// Button pins
const int BTN_RED = 6;
const int BTN_BLUE = 5;
const int BTN_GREEN = 4;
const int BTN_YELLOW = 3;
// Game states
enum GameState {
TITLE_SCREEN,
CHARACTER_SELECT,
EXPLORATION,
BATTLE,
INVENTORY,
GAME_OVER,
VICTORY,
GAME_INTRO // New state for game introduction
};
GameState currentState = GAME_INTRO; // Start with game intro
// Character stats
struct Character {
char name[10];
int maxHp;
int hp;
int attack;
int defense;
int exp;
int level;
int gold;
byte items[3]; // Potion count, weapon upgrade, armor upgrade
};
Character player = {"Hero", 20, 20, 5, 3, 0, 1, 0, {3, 0, 0}};
// Enemy types
struct Enemy {
char name[10];
int hp;
int attack;
int defense;
int expReward;
int goldReward;
};
Enemy enemies[] = {
{"Slime", 10, 3, 1, 2, 2},
{"Goblin", 15, 4, 2, 3, 3},
{"Skeleton", 18, 5, 3, 4, 4},
{"Orc", 25, 6, 4, 6, 6},
{"Dragon", 50, 10, 8, 20, 30}
};
Enemy currentEnemy;
int currentLocation = 0;
const char* locations[] = {"Forest", "Cave", "Ruins", "Castle", "Dungeon"};
const char* locationIntros[] = {
"Forest of Whispers.\nEnemies weak but\nforest path unclear.\nCollect GOLD!\n(Green button moves)", // Forest intro - Area switch, Gold
"The Dark Cave...\nCaves are dangerous!\nUpgrade ARMOR in\nInventory(BLUE).\n(Gold buys upgrades)", // Cave intro - Armor, Inventory, Gold
"Ruins of Eldoria.\nDefeat foes, gain EXP\nto LEVEL UP!\nStronger hero awaits!", // Ruins intro - Level up, EXP
"Castle Brugbor!\nFinal locations\nahead! Prepare for\nultimate battle!", // Castle intro - Weapon, Final area
"Brugbor's Dungeon.\nThe Dragon's Lair!\nVICTORY or GAME\nOVER awaits!" // Dungeon intro - Final boss, Victory/GameOver
};
// Game Intro Text Pages
const char* gameIntroText[] = {
"Long ago, in a realm\nof pixelated beauty,\npeace reigned...",
"But then, from the\nmountains of despair,\nrose Brugbor!",
"AShadow Dragon of immense\nmalevolence, Brugbor\nplunged land in dark.",
"Monsters, goblins, and\nundead roamed free,\nterrorizing villages.",
"You, a brave hero,\nare the last hope.\nDestiny calls you!",
"Your quest: defeat\nBrugbor and restore\npeace to the land!",
"Use RED button to\ncontinue to the\nadventure...",
"" // Empty string to signal end of intro
};
int introPage = 0; // Keep track of intro page
int menuPosition = 0;
bool buttonReleased = true;
unsigned long lastButtonPress = 0;
const int debounceTime = 200;
void setup() {
lcd.begin(16, 2); // Assuming a 16x2 LCD
// Set button pins as inputs with pull-up resistors
pinMode(BTN_RED, INPUT_PULLUP);
pinMode(BTN_BLUE, INPUT_PULLUP);
pinMode(BTN_GREEN, INPUT_PULLUP);
pinMode(BTN_YELLOW, INPUT_PULLUP);
randomSeed(analogRead(A0)); // Initialize random seed
displayGameIntro(); // Start with game intro
}
void loop() {
// Check buttons with debounce
if (millis() - lastButtonPress > debounceTime) {
if (!digitalRead(BTN_RED) && buttonReleased) {
handleRedButton();
buttonReleased = false;
lastButtonPress = millis();
} else if (!digitalRead(BTN_BLUE) && buttonReleased) {
handleBlueButton();
buttonReleased = false;
lastButtonPress = millis();
} else if (!digitalRead(BTN_GREEN) && buttonReleased) {
handleGreenButton();
buttonReleased = false;
lastButtonPress = millis();
} else if (!digitalRead(BTN_YELLOW) && buttonReleased) {
handleYellowButton();
buttonReleased = false;
lastButtonPress = millis();
} else if (digitalRead(BTN_RED) && digitalRead(BTN_BLUE) && digitalRead(BTN_GREEN) && digitalRead(BTN_YELLOW)) {
buttonReleased = true;
}
}
}
void handleRedButton() {
switch (currentState) {
case GAME_INTRO:
introPage++;
if (gameIntroText[introPage][0] == '\0') { // Check for empty string to end intro
currentState = TITLE_SCREEN;
displayTitleScreen();
introPage = 0; // Reset for next game start if needed
} else {
displayGameIntro(); // Show next page of intro
}
break;
case TITLE_SCREEN:
currentState = CHARACTER_SELECT;
displayCharacterSelect();
break;
case CHARACTER_SELECT:
currentState = EXPLORATION;
displayExploration();
break;
case EXPLORATION:
// Encounter enemy
encounterEnemy();
break;
case BATTLE:
// Attack enemy
attackEnemy();
break;
case INVENTORY:
useItem(menuPosition);
currentState = EXPLORATION;
displayExploration();
break;
case GAME_OVER:
case VICTORY:
resetGame();
break;
}
}
void handleBlueButton() {
switch (currentState) {
case EXPLORATION:
currentState = INVENTORY;
menuPosition = 0;
displayInventory();
break;
case BATTLE:
// Use potion in battle if available
if (player.items[0] > 0) {
player.items[0]--;
int healAmount = 10;
player.hp = min(player.hp + healAmount, player.maxHp);
lcd.clear();
lcd.print("Used potion!");
lcd.setCursor(0, 1);
lcd.print("HP: ");
lcd.print(player.hp);
lcd.print("/");
lcd.print(player.maxHp);
delay(1000);
displayBattle();
} else {
lcd.clear();
lcd.print("No potions left!");
delay(1000);
displayBattle();
}
break;
}
}
void handleGreenButton() {
switch (currentState) {
case EXPLORATION:
// Move to next location
currentLocation = (currentLocation + 1) % 5;
displayExploration();
break;
case INVENTORY:
menuPosition = (menuPosition + 1) % 3;
displayInventory();
break;
}
}
void handleYellowButton() {
switch (currentState) {
case BATTLE:
// Try to run from battle
if (random(10) > 3) {
currentState = EXPLORATION;
displayExploration();
} else {
// Failed to run, enemy attacks
enemyAttack();
}
break;
case INVENTORY:
currentState = EXPLORATION;
displayExploration();
break;
}
}
void displayGameIntro() {
lcd.clear();
lcd.print(gameIntroText[introPage]);
lcd.setCursor(0, 1);
delay(1500); // Short delay for reading
}
void displayTitleScreen() {
lcd.clear();
lcd.print("Dungeon Pixel");
lcd.setCursor(0, 1);
lcd.print("Press RED start and scroll");
}
void displayCharacterSelect() {
lcd.clear();
lcd.print("Your hero:");
lcd.setCursor(0, 1);
lcd.print(player.name);
lcd.print(" Lv.");
lcd.print(player.level);
}
void displayExploration() {
lcd.clear();
lcd.print(locationIntros[currentLocation]); // Display location intro
delay(3000); // Longer pause for intro to be read
lcd.clear();
lcd.print(locations[currentLocation]);
lcd.setCursor(0, 1);
lcd.print("HP:");
lcd.print(player.hp);
lcd.print(" G:");
lcd.print(player.gold);
}
void displayBattle() {
lcd.clear();
lcd.print(currentEnemy.name);
lcd.print(" HP:");
lcd.print(currentEnemy.hp);
lcd.setCursor(0, 1);
lcd.print("HP:");
lcd.print(player.hp);
lcd.print(" R:Atk B:Pot");
}
void displayInventory() {
lcd.clear();
switch (menuPosition) {
case 0:
lcd.print("> Potions: ");
lcd.print(player.items[0]);
break;
case 1:
lcd.print("> Weapon Lv.");
lcd.print(player.items[1]);
break;
case 2:
lcd.print("> Armor Lv.");
lcd.print(player.items[2]);
break;
}
lcd.setCursor(0, 1);
lcd.print("G:");
lcd.print(player.gold);
lcd.print(" RED:Use");
}
void encounterEnemy() {
// Choose an enemy based on location and some randomness
int enemyIndex = min(currentLocation + random(2), 4);
currentEnemy = enemies[enemyIndex];
// Copy the enemy to ensure we don't modify the original
if (currentLocation == 4 && random(10) == 0) {
// Chance of dragon in the dungeon
currentEnemy = enemies[4];
}
currentState = BATTLE;
displayBattle();
}
void attackEnemy() {
// Calculate damage with some randomness
int damage = max(1, player.attack - currentEnemy.defense + random(-2, 3));
currentEnemy.hp -= damage;
lcd.clear();
lcd.print("You hit for ");
lcd.print(damage);
lcd.setCursor(0, 1);
lcd.print(currentEnemy.name);
lcd.print(" HP:");
lcd.print(currentEnemy.hp);
delay(1000);
if (currentEnemy.hp <= 0) {
defeatEnemy();
} else {
enemyAttack();
}
}
void enemyAttack() {
// Enemy attacks player
int damage = max(1, currentEnemy.attack - player.defense + random(-1, 2));
player.hp -= damage;
lcd.clear();
lcd.print(currentEnemy.name);
lcd.print(" hits ");
lcd.print(damage);
lcd.setCursor(0, 1);
lcd.print("Your HP: ");
lcd.print(player.hp);
delay(1000);
if (player.hp <= 0) {
gameOver();
} else {
displayBattle();
}
}
void defeatEnemy() {
// Gain rewards
player.exp += currentEnemy.expReward;
player.gold += currentEnemy.goldReward;
lcd.clear();
lcd.print("You won! EXP+");
lcd.print(currentEnemy.expReward);
lcd.setCursor(0, 1);
lcd.print("Gold+");
lcd.print(currentEnemy.goldReward);
delay(1500);
// Check for level up
if (player.exp >= player.level * 10) {
levelUp();
} else {
// Check for game victory
if (currentEnemy.name[0] == 'D' && currentLocation == 4) { // If we defeated Dragon in Dungeon
victory();
} else {
currentState = EXPLORATION;
displayExploration();
}
}
}
void levelUp() {
player.level++;
player.maxHp += 5;
player.hp = player.maxHp;
player.attack += 2;
player.defense += 1;
player.exp = 0;
lcd.clear();
lcd.print("LEVEL UP!");
lcd.setCursor(0, 1);
lcd.print("Now level ");
lcd.print(player.level);
delay(2000);
// Check for game victory after Dragon
if (currentEnemy.name[0] == 'D' && currentLocation == 4) {
victory();
} else {
currentState = EXPLORATION;
displayExploration();
}
}
void useItem(int itemIndex) {
switch (itemIndex) {
case 0: // Use potion
if (player.items[0] > 0 && player.hp < player.maxHp) {
player.items[0]--;
int healAmount = 10;
player.hp = min(player.hp + healAmount, player.maxHp);
lcd.clear();
lcd.print("Used potion!");
lcd.setCursor(0, 1);
lcd.print("HP: ");
lcd.print(player.hp);
lcd.print("/");
lcd.print(player.maxHp);
delay(1000);
}
break;
case 1: // Upgrade weapon
if (player.gold >= (player.items[1] + 1) * 10) {
player.gold -= (player.items[1] + 1) * 10;
player.items[1]++;
player.attack += 2;
lcd.clear();
lcd.print("Weapon upgraded!");
lcd.setCursor(0, 1);
lcd.print("ATK now: ");
lcd.print(player.attack);
delay(1000);
}
break;
case 2: // Upgrade armor
if (player.gold >= (player.items[2] + 1) * 8) {
player.gold -= (player.items[2] + 1) * 8;
player.items[2]++;
player.defense += 1;
lcd.clear();
lcd.print("Armor upgraded!");
lcd.setCursor(0, 1);
lcd.print("DEF now: ");
lcd.print(player.defense);
delay(1000);
}
break;
}
}
void gameOver() {
lcd.clear();
lcd.print("GAME OVER");
lcd.setCursor(0, 1);
lcd.print("Press RED restart");
currentState = GAME_OVER;
}
void victory() {
lcd.clear();
lcd.print("YOU WON!");
lcd.setCursor(0, 1);
lcd.print("Hero Level: ");
lcd.print(player.level);
currentState = VICTORY;
}
void resetGame() {
// Reset player stats
player.hp = player.maxHp = 20;
player.attack = 5;
player.defense = 3;
player.exp = 0;
player.level = 1;
player.gold = 0;
player.items[0] = 3; // Potions
player.items[1] = 0; // Weapon level
player.items[2] = 0; // Armor level
currentLocation = 0;
currentState = GAME_INTRO; // Go back to game intro on reset
introPage = 0; // Reset intro page
displayGameIntro();
}