diff --git a/Makefile b/Makefile index ecba416..ae42fa5 100644 --- a/Makefile +++ b/Makefile @@ -12,13 +12,13 @@ Spell.o: lib/Spell.cpp lib/Spell.hpp Character.o: lib/Character.cpp lib/Character.hpp g++ -c lib/Character.cpp -o Character.o -main: Spell.o Character.o UserInterface.o main.cpp - g++ main.cpp -o main Spell.o Character.o UserInterface.o +main: Spell.o Character.o UserInterface.o BattleInterface.o main.cpp + g++ main.cpp -o main Spell.o Character.o UserInterface.o BattleInterface.o -clean: Spell.o Character.o UserInterface.o - rm Spell.o Character.o UserInterface.o +clean: Spell.o Character.o UserInterface.o BattleInterface.o + rm Spell.o Character.o UserInterface.o BattleInterface.o -striped: Spell.o Character.o main.cpp - g++ -s main.cpp -o main Spell.o Character.o UserInterface.o +striped: Spell.o Character.o UserInterface.o BattleInterface.o main.cpp + g++ -s main.cpp -o main Spell.o Character.o UserInterface.o BattleInterface.o all: Spell.o Character.o main diff --git a/lib/BattleInterface.cpp b/lib/BattleInterface.cpp new file mode 100644 index 0000000..591aa82 --- /dev/null +++ b/lib/BattleInterface.cpp @@ -0,0 +1,107 @@ +#include "BattleInterface.hpp" + +const int BattleInterface::maxTurn = 100; + +BattleInterface::BattleInterface() +{ + this->turn = -1; + this->player1 = nullptr; + this->player2 = nullptr; + this->winner = nullptr; + this->loser = nullptr; +} + +BattleInterface::BattleInterface(Character *p1, Character *p2) +{ + this->turn = 1; + this->player1 = p1; + this->player2 = p2; + this->winner = nullptr; + this->loser = nullptr; +} + +void BattleInterface::battleStartAnnounce() +{ + std::cout << " === BATTLE START === \n" + << " " << player1->getName() << "\n" + << " vs \n" + << " " << player2->getName() << "\n" + << " === ============ === \n" + << std::flush; +} + +void BattleInterface::battleEndAnnouce() +{ + std::cout << " === BATTLE END === \n" + << "winner: " << this->winner->getName() << "\n" + << std::flush; +} + +Character *BattleInterface::getLoser() +{ + return this->loser; +} + +Character *BattleInterface::getWinner() +{ + return this->winner; +} + +void BattleInterface::start() +{ + int choiceInt; + char buff[64] = {0}; + + this->battleStartAnnounce(); + while ((player1->isAlive() && player2->isAlive()) && this->turn <= BattleInterface::maxTurn) + { + std::cout << "Turn " << this->turn << " of " << BattleInterface::maxTurn << "\n" + << "Your spell book:\n"; + for (size_t i = 0; i < Spell::Book.size() && i < (player1->getLevel()); i++) + { + std::cout << "[" << i << "] " << Spell::Book.at(i).getName() << "\n"; + } + + choiceInt = UserInterface::getNumber( + "What are you gonna cast this turn?\n>", 0, + player1->getLevel() > Spell::Book.size() ? Spell::Book.size() : player1->getLevel()); + + if (player1->canCastSpell(&Spell::Book.at(choiceInt))) + { + player1->castSpell(&Spell::Book.at(choiceInt), player2); + snprintf(buff, 63, "%s cast %s...\n", player1->getName(), Spell::Book.at(choiceInt).getName().c_str()); + UserInterface::print(buff); + memset(buff, 0, 64); + snprintf(buff, 63, "%s took %d damage...\n", player2->getName(), Spell::Book.at(choiceInt).getAmount()); + UserInterface::print(buff); + memset(buff, 0, 64); + } + else + { + snprintf(buff, 63, "%s failed to cast %s!\n", player1->getName(), Spell::Book.at(choiceInt).getName().c_str()); + UserInterface::print(buff); + } + + choiceInt = (random() % player2->getLevel()); + if (player2->canCastSpell(&Spell::Book.at(choiceInt))) + { + player2->castSpell(&Spell::Book.at(choiceInt), player1); + snprintf(buff, 63, "%s cast %s...\n", player2->getName(), Spell::Book.at(choiceInt).getName().c_str()); + UserInterface::print(buff); + memset(buff, 0, 64); + snprintf(buff, 63, "%s took %d damage...\n", player1->getName(), Spell::Book.at(choiceInt).getAmount()); + UserInterface::print(buff); + memset(buff, 0, 64); + } + else + { + snprintf(buff, 63, "%s failed to cast %s!\n", player2->getName(), Spell::Book.at(choiceInt).getName().c_str()); + UserInterface::print(buff); + } + this->turn++; + } + + winner = (player1->isAlive() && (player1->getCurHP() >= player2->getCurHP())) ? player1 : player2; + loser = (winner == player1) ? player2 : player1; + this->battleEndAnnouce(); +} diff --git a/lib/BattleInterface.hpp b/lib/BattleInterface.hpp index f86da15..dd0034b 100644 --- a/lib/BattleInterface.hpp +++ b/lib/BattleInterface.hpp @@ -12,18 +12,24 @@ class BattleInterface { protected: - static const int maxTurn = 100; - static int turn; + static const int maxTurn; + int turn; Character *player1; Character *player2; + Character *winner; + Character *loser; -public: void battleStartAnnounce(); void battleEndAnnouce(); + void clearBuff(); +public: + BattleInterface(); + BattleInterface(Character *p1, Character *p2); + void start(); - Character *getWinner(); + Character *getLoser(); }; #endif \ No newline at end of file diff --git a/lib/Character.cpp b/lib/Character.cpp index 828474d..9cd3484 100644 --- a/lib/Character.cpp +++ b/lib/Character.cpp @@ -3,7 +3,20 @@ Character::Character(char *n, int l, int e, int h, int m, int a, int d) { + this->level = l; + this->experience = e; + this->maxHP = h; + this->curHP = maxHP; + this->maxMP = m; + this->curMP = maxMP; + this->atk = a; + this->def = d; strcpy(this->name, n); + this->name[15] = 0; +} + +Character::Character(const char *n, int l, int e, int h, int m, int a, int d) +{ this->level = l; this->experience = e; this->maxHP = h; @@ -12,6 +25,7 @@ Character::Character(char *n, int l, int e, int h, int m, int a, int d) this->curMP = maxMP; this->atk = a; this->def = d; + strcpy(this->name, n); } char *Character::getName() @@ -63,13 +77,13 @@ void Character::levelUp() { if ((this->level <= maxLevel) && this->readytoLevelUp()) { + this->experience -= this->toNextLevel(); this->level++; this->maxHP += 10; this->maxMP += 5; + this->curHP = this->maxHP; + this->curMP = this->maxMP; } - - this->curHP = this->maxHP; - this->curMP = this->maxMP; } void Character::restoreHP(int n) @@ -107,12 +121,12 @@ bool Character::isAlive() int Character::toNextLevel() { - return (this->level <= maxLevel) ? ((this->level + 1) * (this->level + 1)) - this->getExperience() : 999999; + return (this->level + 1) * (this->level + 1); } bool Character::readytoLevelUp() { - return (this->toNextLevel() <= 0); + return (this->toNextLevel() <= this->experience); } void Character::increaseExperience(int n) @@ -122,6 +136,10 @@ void Character::increaseExperience(int n) { this->experience = maxEperience; } + while (this->readytoLevelUp()) + { + this->levelUp(); + } } bool Character::canCastSpell(Spell *s) @@ -141,7 +159,15 @@ void Character::rest() this->curMP = this->maxMP; } -void Character::suicide() +void Character::kill() { this->curHP = 0; +} + +void Character::revive() +{ + if (this->curHP <= 0) + { + this->curHP = 1; + } } \ No newline at end of file diff --git a/lib/Character.hpp b/lib/Character.hpp index f68e4b2..0ab2fdb 100644 --- a/lib/Character.hpp +++ b/lib/Character.hpp @@ -27,10 +27,11 @@ protected: void reduceMP(int); public: - Character(char *, int, int, int, int, int, int); + Character(char *name, int level, int exp, int hp, int mp, int atk, int def); + Character(const char *name, int level, int exp, int hp, int mp, int atk, int def); - void castSpell(Spell *, Character *); - bool canCastSpell(Spell *); + void castSpell(Spell *src, Character *dst); + bool canCastSpell(Spell *src); char *getName(); int getLevel(); @@ -46,8 +47,9 @@ public: bool readytoLevelUp(); bool isAlive(); void levelUp(); - void suicide(); + void kill(); void rest(); + void revive(); void increaseExperience(int n); }; diff --git a/lib/Spell.cpp b/lib/Spell.cpp index 4514cb4..f5b8492 100644 --- a/lib/Spell.cpp +++ b/lib/Spell.cpp @@ -28,15 +28,15 @@ std::string Spell::getName() return this->name; } -std::vector Spell::Book = { - new Spell("Napalm Beat", 1, 4, 1), - new Spell("Fire Ball", 1, 6, 2), - new Spell("Cold Bolt", 1, 8, 3), - new Spell("Stone curse", 1, 10, 4), - new Spell("Lightning Bolt", 1, 12, 5), - new Spell("Soul Strike", 1, 14, 6), - new Spell("Fire Wall", 1, 16, 7), - new Spell("Frost Diver", 1, 18, 8), - new Spell("Lighting Storm", 1, 20, 9), - new Spell("Asura's Strike", 1, 50, 10), +std::vector Spell::Book = { + Spell("Napalm Beat", 1, 4, 1), + Spell("Fire Ball", 1, 6, 2), + Spell("Cold Bolt", 1, 8, 3), + Spell("Stone curse", 1, 10, 4), + Spell("Lightning Bolt", 1, 12, 5), + Spell("Soul Strike", 1, 14, 6), + Spell("Fire Wall", 1, 16, 7), + Spell("Frost Diver", 1, 18, 8), + Spell("Lighting Storm", 1, 20, 9), + Spell("Asura's Strike", 1, 50, 10), }; \ No newline at end of file diff --git a/lib/Spell.hpp b/lib/Spell.hpp index ad7b166..618bbaa 100644 --- a/lib/Spell.hpp +++ b/lib/Spell.hpp @@ -16,7 +16,7 @@ protected: public: int TYPE_RESTORATION = 0; int TYPE_DESTRUCTION = 1; - static std::vector Book; + static std::vector Book; Spell(std::string, int, int, int); int getType(); diff --git a/lib/UserInterface.cpp b/lib/UserInterface.cpp index 1dbab09..7ddf831 100644 --- a/lib/UserInterface.cpp +++ b/lib/UserInterface.cpp @@ -1,7 +1,7 @@ #include "UserInterface.hpp" -const int UserInterface::interval = 1000; // in ms -const int UserInterface::miniInterval = 50; // in ms +const int UserInterface::interval = 0; // in ms +const int UserInterface::miniInterval = 0; // in ms const int UserInterface::maxDay = 28; const int UserInterface::maxMonth = 4; const int UserInterface::maxYear = 100; @@ -24,7 +24,7 @@ std::vector UserInterface::dayName = { std::vector UserInterface::monthName = { "Morning Star", - "Sun's Dawn'", + "Sun's Dawn", "First Seed", "Rain's Hand", "Second Seed", @@ -93,9 +93,9 @@ void UserInterface::epilogue() { UserInterface::printByChar( "You are just a young mage in a mage school,\n" - "When you witnessed your arch-mage got killed by your senior...\n" - "The killer was mumbling about some flag inside a scroll,\n" - "he constantly mentioned something about a competition called CTF...\n" + "When you witnessed your arch-mage got defeated by your senior...\n" + "He was mumbling about some flag inside a scroll,\n" + "he also mentioned something about a competition called CTF...\n" "You thought to yourself: \"What the f*ck is a CTF?\"\n" "Nevertheless, you want that scroll, you want that flag!\n" "Defeat the new arch-mage that was once your senior to get a glimpse of that scroll!\n" @@ -144,14 +144,16 @@ std::string UserInterface::dateString() } return std::string( - UserInterface::dayName.at((UserInterface::day % 7) - 1) + ", " + + UserInterface::dayName.at(((UserInterface::day - 1) % 7)) + ", " + date + " of " + UserInterface::monthName.at(UserInterface::month - 1) + ", Year " + std::to_string(UserInterface::year)); } void UserInterface::menu() { - std::cout << UserInterface::dateString() << "\n" + std::cout << "=======================================\n" + << UserInterface::dateString() << "\n" + << "=======================================\n" << "Available action:\n" << "[1] Take a rest\n" << "[2] Practice magic\n" @@ -163,14 +165,27 @@ void UserInterface::menu() << std::flush; } +void UserInterface::enemiesInfo(std::vector &enemies) +{ + for (int i = 0; i < enemies.size(); i++) + { + std::cout << "[" << i << "] " << enemies.at(i)->getName() << " (Lv." << enemies.at(i)->getLevel() << ")\n"; + } + std::cout << std::flush; +} + int UserInterface::getNumber(std::string msg, int min, int max) { int buffer = max + 1; - while (!(buffer <= max && buffer >= min)) + do { std::cout << msg << std::flush; - std::cin >> buffer; - } + if (std::cin.failbit || std::cin.eofbit) + { + std::cin.clear(); + std::cin.ignore(1); + } + } while (std::cin >> buffer && !(buffer <= max && buffer >= min)); return buffer; } @@ -184,11 +199,25 @@ void UserInterface::nextDay() { UserInterface::gameOver = true; } + month = 1; } + day = 1; } } bool UserInterface::isGameOver() { return gameOver; +} + +void UserInterface::characterInfo(Character *c) +{ + std::cout << "++++++++++++++++++++++++" + << "\nName: " << c->getName() + << "\n HP: " << c->getCurHP() << "/" << c->getMaxHP() + << "\n MP: " << c->getCurMP() << "/" << c->getMaxMP() + << "\n Lv: " << c->getLevel() + << "\n Xp: " << c->getExperience() + << "\n next: " << c->toNextLevel() + << "\n++++++++++++++++++++++++" << std::endl; } \ No newline at end of file diff --git a/lib/UserInterface.hpp b/lib/UserInterface.hpp index 21be348..3fb5e60 100644 --- a/lib/UserInterface.hpp +++ b/lib/UserInterface.hpp @@ -40,10 +40,13 @@ public: static void epilogue(); static void menu(); static void nextDay(); + static void characterInfo(Character *); + static void enemiesInfo(std::vector &); static std::string dateString(); static int getNumber(std::string, int, int); static Character *characterCreation(); + static bool isGameOver(); }; diff --git a/main b/main index 9a120ff..723bdf0 100755 Binary files a/main and b/main differ diff --git a/main.cpp b/main.cpp index 0243cdc..af18bd8 100644 --- a/main.cpp +++ b/main.cpp @@ -4,11 +4,12 @@ #include #include #include +#include #include "lib/Character.hpp" #include "lib/Spell.hpp" #include "lib/UserInterface.hpp" -// #include "lib/BattleInterface.hpp" +#include "lib/BattleInterface.hpp" std::string loadFlag() { @@ -19,31 +20,126 @@ int main() { std::string flag; Character *player; + Character *archMage; + Character *enemy; + BattleInterface battle; + std::vector enemies; flag = loadFlag(); srand(time(0)); + alarm(600); UserInterface::welcomeMessage(); UserInterface::epilogue(); player = UserInterface::characterCreation(); + archMage = new Character("Arch-Mage", 100, 0, 100000, 100000, 1, 1); + enemies = { + new Character("Abby", 1, 1, 10, 5, 1, 1), + new Character("John", 2, 3, 20, 10, 1, 1), + new Character("Adam", 3, 7, 40, 25, 1, 1), + new Character("Christo", 4, 11, 80, 40, 1, 1), + new Character("Greg", 5, 16, 160, 80, 1, 1), + }; while (player->isAlive() && !UserInterface::isGameOver()) { - int choice; + int choiceInt = -1; + char choiceChar = -1; UserInterface::menu(); - choice = UserInterface::getNumber( + choiceInt = UserInterface::getNumber( "What are you gonna do today?\n>", - 0, 0); - switch (choice) + 0, 6); + switch (choiceInt) { case 0: UserInterface::print("You commit sudoku...\n"); UserInterface::print("Was it supposed to be seppuku?\n"); UserInterface::print("Nevermind, you killed yourself.\n"); - player->suicide(); + player->kill(); break; - default: + case 1: + UserInterface::print("You spend the whole day sleeping...\n"); + UserInterface::print("HP and MP restored.\n"); + player->rest(); + break; + case 2: + UserInterface::print("You practice your magic in the yard...\n"); + UserInterface::print("You gained some experience.\n"); + player->increaseExperience((random() % player->getLevel()) + 1); + break; + case 3: + UserInterface::print("List of your classmates:\n"); + UserInterface::enemiesInfo(enemies); + choiceInt = UserInterface::getNumber("Who would you prefer to train with?\n>", 0, enemies.size()); + UserInterface::print("You are going to spar with:\n"); + UserInterface::characterInfo(enemies.at(choiceInt)); + UserInterface::print("Are you sure? (y/N)"); + std::cin >> choiceChar; + if (choiceChar == 'y' || choiceChar == 'Y') + { + enemy = new Character(*enemies.at(choiceInt)); + battle = BattleInterface(player, enemy); + battle.start(); + if (battle.getWinner() == player) + { + UserInterface::print("You win, you get more experience...\n"); + player->increaseExperience(enemies.at(choiceInt)->getLevel()); + } + else + { + UserInterface::print("You lose, but you still get some experience...\n"); + player->revive(); + player->increaseExperience(enemies.at(choiceInt)->getLevel() / 2); + } + delete enemy; + } + else + { + UserInterface::print("On second thought, you decide to sleep in your room instead...\n"); + player->rest(); + } + + break; + case 4: + UserInterface::print("You are going to challenge the Arch-Mage...\n"); + UserInterface::print("Are you sure? (y/N)"); + std::cin >> choiceChar; + if (choiceChar == 'y' || choiceChar == 'Y') + { + battle = BattleInterface(player, archMage); + battle.start(); + if (battle.getWinner() == player) + { + UserInterface::print("You win, you get more experience...\n"); + player->increaseExperience(enemies.at(choiceInt)->getLevel()); + UserInterface::print("You win against the Arch-Mage!\n"); + UserInterface::print("He let you take a glimpse to the scroll that you always wanted...\n"); + UserInterface::print("Turns out the content is just some meaningless word...\n"); + UserInterface::print("Here is the content:\n"); + UserInterface::print(flag); + } + else + { + UserInterface::print("You lose, but you still get some experience...\n"); + player->revive(); + player->increaseExperience(enemies.at(choiceInt)->getLevel() / 2); + } + } + else + { + UserInterface::print("On second thought, you decide to sleep in your room instead...\n"); + player->rest(); + } + break; + case 5: + UserInterface::print("You found some info about the arch-mage:\n"); + UserInterface::characterInfo(archMage); + break; + case 6: + UserInterface::print("You meditate and got some insight to your ability:\n"); + UserInterface::characterInfo(player); break; } + UserInterface::nextDay(); } }