51
\$\begingroup\$

I started to make a C++ RPG game just for fun. It does have quests but it isn't just one long adventure that lasts like 30 minutes. I would like to know what I did well on and things on which I can do better on. This is my normal coding so I would like to know things I could work on.

// Warscape
// (c) 2017 by, Handge
#include <iostream>
#include <string>
#include <cstdlib>
#include <algorithm>
#include <ctime>
#include <tuple>

using namespace std;

// Clears the screen
void clear() {
    cout << string(50, '\n');
}

// Draw the ascii art
void draw(string type) {
    if (type == "Lion") {
        cout << R"(
======================================================================
                                                ,w.
                                              ,YWMMw  ,M  ,
                         _.---.._   __..---._.'MMMMMw,wMWmW,
                    _.-""        """           YP"WMMMMMMMMMb,
                 .-' __.'                   .'     MMMMW^WMMMM;
     _,        .'.-'"; `,       /`     .--""      :MMM[==MWMW^;
  ,mM^"     ,-'.'   /   ;      ;      /   ,       MMMMb_wMW"  @\
 ,MM:.    .'.-'   .'     ;     `\    ;     `,     MMMMMMMW `"=./`-,
 WMMm__,-'.'     /      _.\      F"""-+,,   ;_,_.dMMMMMMMM[,_ / `=_}
 "^MP__.-'    ,-' _.--""   `-,   ;       \  ; ;MMMMMMMMMMW^``; __|
            /   .'            ; ;         )  )`{  \ `"^W^`,   \  :
           /  .'             /  (       .'  /     Ww._     `.  `"
          /  Y,              `,  `-,=,_{   ;      MMMP`""-,  `-._.-,
         (--, )                `,_ / `) \/"")      ^"      `-, -;"\:
          `"""                    `"""   `"'                  `---" 
======================================================================
        )" << endl;
    } else if (type == "Boar") {
        cout << R"(
============================================
              _,-""""-..__
         |`,-'_. `  ` ``  `--'""".
         ;  ,'  | ``  ` `  ` ```  `.
       ,-'   ..-' ` ` `` `  `` `  ` |==.
     ,'    ^    `  `    `` `  ` `.  ;   \
    `}_,-^-   _ .  ` \ `  ` __ `   ;    #
       `"---"' `-`. ` \---""`.`.  `;
                  \\` ;       ; `. `,
                   ||`;      / / | |
                  //_;`    ,_;' ,_;"
============================================
        )" << endl;
    } else if (type == "Spider") {
        cout << R"(
================================
        /\  .-"""-.  /\
       //\\/  ,,,  \//\\
       |/\| ,;;;;;, |/\|
       //\\\;-"""-;///\\
      //  \/   .   \/  \\
     (| ,-_| \ | / |_-, |)
       //`__\.-.-./__`\\
      // /.-(() ())-.\ \\
     (\ |)   '---'   (| /)
      ` (|           |) `
        \)           (/
================================
        )" << endl;
    } else if (type == "Wolf") {
        cout << R"(
=====================================
                          ,     ,
                          |\---/|
                         /  , , |
                    __.-'|  / \ /
           __ ___.-'        ._O|
        .-'  '        :      _/
       / ,    .        .     |
      :  ;    :        :   _/
      |  |   .'     __:   /
      |  :   /'----'| \  |
      \  |\  |      | /| |
       '.'| /       || \ |
       | /|.'       '.l \\_
       || ||             '-'
       '-''-'
=====================================
        )" << endl;
    } else if (type == "Elephant") {
        cout << R"(
=====================================================
              ___.-~"~-._   __....__
            .'    `    \ ~"~        ``-.
           /` _      )  `\              `\
          /`  a)    /     |               `\
         :`        /      |                 \
    <`-._|`  .-.  (      /   .            `;\\
     `-. `--'_.'-.;\___/'   .      .       | \\
  _     /:--`     |        /     /        .'  \\
 ("\   /`/        |       '     '         /    :`;
 `\'\_/`/         .\     /`~`=-.:        /     ``
   `._.'          /`\    |      `\      /(
                 /  /\   |        `Y   /  \
                J  /  Y  |         |  /`\  \
               /  |   |  |         |  |  |  |
              "---"  /___|        /___|  /__|
=====================================================
        )" << endl;
    } else if (type == "Slime") {
        cout << R"(
==================================
         , - ~ ~ ~ - ,
     , '               ' ,
   ,    !           !      ,
  ,                         ,
 ,               @@     @@   ,
 , !             @@     @@   ,
 ,        !                  ,
  ,                         ,
   ,                    !  ,
     ,       !          , '
       ' - , _ _ _ ,  '
==================================
        )" << endl;
    }
}

// Displays the abilities
pair<string, int> getability(string pclass, int mana, int inte, int level) {
    string input;
    string ability;
    int cost;
    cout << "Choose an ability" << endl;
    if (pclass == "Champion") { // Champion Section
        cost = inte*(level);
        cout << "[1] Cleaving Strike [" << cost << " mana]" << endl;
        cout << "[2] Melting Thrust [" << cost << " mana]" << endl;
        cout << "[3] Critical Bash [" << cost << " mana]" << endl;
        cost = inte*(level+1);
        cout << "[4] Purify [" << cost << " mana]" << endl;
        cost = inte*(level);
        cin >> input;
        if (input == "1") {
            ability = "cleaving strike";
            if (mana >= cost) {
                mana = mana - cost;
                return {ability, mana};
            } else {
                ability = "none";
                return {ability, mana};
            }
        } else if (input == "2") {
            ability = "melting thrust";
            if (mana >= cost) {
                mana = mana - cost;
                return {ability, mana};
            } else {
                ability = "none";
                return {ability, mana};
            }
        } else if (input == "3") {
            ability = "critical bash";
            if (mana >= cost) {
                mana = mana - cost;
                return {ability, mana};
            } else {
                ability = "none";
                return {ability, mana};
            }
        } else if (input == "4") {
            ability = "purify";
            if (mana >= cost) {
                cost = inte*(level+1);
                mana = mana - cost;
                return {ability, mana};
            } else {
                ability = "none";
                return {ability, mana};
            }
        } else {
            ability = "none";
            return {ability, mana};
        }
    } else if (pclass == "Necromancer") { // Necromancer Section
        cost = inte*(level);
        cout << "[1] Shadow Strike [" << cost << " mana]" << endl;
        cout << "[2] Cripple [" << cost << " mana]" << endl;
        cout << "[3] Mutilate [" << cost << " mana]" << endl;
        cost = inte*(level+2);
        cout << "[4] Life Tap [" << cost << " mana]" << endl;
        cost = inte*(level);
        cin >> input;
        if (input == "1") {
            ability = "shadow strike";
            if (mana >= cost) {
                mana = mana - cost;
                return {ability, mana};
            } else {
                ability = "none";
                return {ability, mana};
            }
        } else if (input == "2") {
            ability = "cripple";
            if (mana >= cost) {
                mana = mana - cost;
                return {ability, mana};
            } else {
                ability = "none";
                return {ability, mana};
            }
        } else if (input == "3") {
            ability = "mutilate";
            if (mana >= cost) {
                mana = mana - cost;
                return {ability, mana};
            } else {
                ability = "none";
                return {ability, mana};
            }
        } else if (input == "4") {
            ability = "life tap";
            if (mana >= cost) {
                cost = inte*(level+2);
                mana = mana - cost;
                return {ability, mana};
            } else {
                ability = "none";
                return {ability, mana};
            }
        } else {
            ability = "none";
            return {ability, mana};
        }
    } else if (pclass == "Assassin") { // Assassin Section
        cost = inte*(level);
        cout << "[1] Back Stab [" << cost << " mana]" << endl;
        cout << "[2] Headcrack [" << cost << " mana]" << endl;
        cout << "[3] Poison [" << cost << " mana]" << endl;
        cost = inte*10;
        cout << "[4] Assassinate [" << cost << " mana]" << endl;
        cost = inte*(level);
        cin >> input;
        if (input == "1") {
            ability = "back stab";
            if (mana >= cost) {
                mana = mana - cost;
                return {ability, mana};
            } else {
                ability = "none";
                return {ability, mana};
            }
        } else if (input == "2") {
            ability = "headcrack";
            if (mana >= cost) {
                mana = mana - cost;
                return {ability, mana};
            } else {
                ability = "none";
                return {ability, mana};
            }
        } else if (input == "3") {
            ability = "poison";
            if (mana >= cost) {
                mana = mana - cost;
                return {ability, mana};
            } else {
                ability = "none";
                return {ability, mana};
            }
        } else if (input == "4") {
            ability = "assassinate";
            if (mana >= cost) {
                cost = inte*10;
                mana = mana - cost;
                return {ability, mana};
            } else {
                ability = "none";
                return {ability, mana};
            }
        } else {
            ability = "none";
            return {ability, mana};
        }
    }else if (pclass == "Cleric") { // Cleric Section
        cost = inte*(level);
        cout << "[1] Smite [" << cost << " mana]" << endl;
        cout << "[2] Enflame [" << cost << " mana]" << endl;
        cout << "[3] Atonement [" << cost << " mana]" << endl;
        cout << "[4] Flash Heal [" << cost << " mana]" << endl;
        cin >> input;
        if (input == "1") {
            ability = "smite";
            if (mana >= cost) {
                mana = mana - cost;
                return {ability, mana};
            } else {
                ability = "none";
                return {ability, mana};
            }
        } else if (input == "2") {
            ability = "enflame";
            if (mana >= cost) {
                mana = mana - cost;
                return {ability, mana};
            } else {
                ability = "none";
                return {ability, mana};
            }
        } else if (input == "3") {
            ability = "atonement";
            if (mana >= cost) {
                mana = mana - cost;
                return {ability, mana};
            } else {
                ability = "none";
                return {ability, mana};
            }
        } else if (input == "4") {
            ability = "flash heal";
            if (mana >= cost) {
                mana = mana - cost;
                return {ability, mana};
            } else {
                ability = "none";
                return {ability, mana};
            }
        } else {
            ability = "none";
            return {ability, mana};
        }
    } else {
        ability = "none";
        return {ability, mana};
    }

    }


int main()
{

    srand((unsigned)time(0));

    // Strings
    string code; // Game save code
    string name; // Player name
    string etype; // Enemy Type (spider, giant)
    string pclass; // Player class
    string action; // Player ability / action
    // Dungeons
    string status = "arena"; // Status if in Dungeon or just Arena
    int ckills; // Current amount of kills in Dungeon
    int rkills; // Required amount of kills to complete a Dungeon
    // Armor / Weapons
    int bhp = 0; // Bonus hp
    double bphp = 0; // Bonus precantage hp
    double goldb = 0; // Bonus Gold
    int slimychestplate = 0; // Slimy Chestplate (TRUE or FALSE)
    int slimyhelmet = 0; // Slimy Helmet (TRUE or FALSE)
    int oozinglegplates = 0; // Oozing Legplates
    int oozingboots = 0; // Oozing Boots
    string head = "Open"; // Whats on the head
    string chest = "Open"; // Whats on the chest
    string legs = "Open"; // Whats on the legs
    string feet = "Open"; // Whats on the feet
    // Player Variables
    int piclass = 0;
    int level = 1; // Level
    int xp = 0; // Current XP
    int rxp; // Reward XP
    int xpb = 50; // Increases each level, adds to max hp to increase it
    int xpl = 25; // XP Required till next level
    int stre = 5; // Strength
    int inte = 5; // Intelligence
    int dext = 5; // Dexterity
    int skill = 0; // Skillpoint points (used to increase Strength, Intelligence, Dexterity)
    int dmg; // Damage
    int hp = stre * 20; // Current hp
    int mhp = stre * 20; // Max hp
    int shp = stre * 20; // hp used to set enemy hp
    int mana = inte * 10; // Mana
    int mmana = inte * 10; // Max Mana
    // Inventory / Shop
    int hpotion = 1; // Healing Potions
    int price; // Price of item(s)
    // Resources
    int gold = 25; // Gold
    int rgold; // Reward Gold
    // Saving / Loading Strings
    int x = 0;
    int y = 0;
    string sslimychestplate = std::to_string(slimychestplate);
    string sslimyhelmet = std::to_string(slimyhelmet);
    string soozinglegplates = std::to_string(oozinglegplates);
    string soozingboots = std::to_string(oozingboots);
    string spiclass = std::to_string(piclass);
    string slevel = std::to_string(level);
    string sxp = std::to_string(xp);
    string sxpb = std::to_string(xpb);
    string sxpl = std::to_string(xpl);
    string sstre = std::to_string(stre);
    string sinte = std::to_string(inte);
    string sdext = std::to_string(dext);
    string shpotion = std::to_string(hpotion);
    string sgold = std::to_string(gold);
    string sskill = std::to_string(skill);
    // Questhall
    int quest = 0; // The quest
    int questr = 0; // The quest requirements (6 boars slain)
    int questc = 0; // The current position on the requirements (3/6 boars slain)
    int questreward = 0; // The quest's reward
    string questmob; // Enemy that must be slain to complete the quest
    string questdisplay; // Displays the quest
    int qxpr; // Quest xp reward
    int qgoldr; // Quest gold reward
    // Monster Variables
    int elevel; // Enemy level
    int ehp; // Enemy hp
    int mehp; // Max Enemy hp
    int edmg; // Enemy dmg
    string eability; // Enemy Ability
    // Other Variables
    int random; // Random Integer #1
    int random2; // Random Integer #2
    // Weapons
    // Inputs
    string input; // Standard Input

    // Setup
    neworload:
    clear();
    cout << "Hello there please choose an option" << endl;
    cout << "1- New Game" << endl;
    cout << "2- Load Game" << endl;
    cin >> input;
    if (input == "1") {
        goto setup;
    } else if (input == "2") {
        goto loadgame;
    } else {
        goto neworload;
    }

    setup:
    clear();
    cout << "Hello there, what is your name?" << endl;
    cin >> input;
    name = input;
    cout << "Ahh, is it " << input << "? [y/n]" << endl;
    cin >> input;
    if (input == "n") {
        clear();
        goto setup;
    }

    // Class Setup
    csetup:
    cout << endl;
    cout << "Choose your Class" << endl;
    cout << "=================" << endl;
    cout << "[1] Champion" << endl;
    cout << "[2] Necromancer" << endl;
    cout << "[3] Assassin" << endl;
    cout << "[4] Cleric" << endl;
    cin >> input;

    if (input == "1") {
        pclass = "Champion";
        piclass = 1;
    } else if (input == "2") {
        pclass = "Necromancer";
        piclass = 2;
    } else if (input == "3") {
        pclass = "Assassin";
        piclass = 3;
    } else if (input == "4") {
        pclass = "Cleric";
        piclass =4;
    }

    goto menue;

    // Main Menue
    menue:
    clear();

    if (pclass == "Assassin") {
        goldb = 1.25;
    } else {
        goldb = 1;
    }

    cout << R"(
___  ___ _____ _   _ _   _ 
|  \/  ||  ___| \ | | | | |
| .  . || |__ |  \| | | | |
| |\/| ||  __|| . ` | | | |
| |  | || |___| |\  | |_| |
\_|  |_/\____/\_| \_/\___/
    )" << endl;

    cout << pclass << " " << name << endl;
    cout << "[1] Traveller's Encounter" << endl;
    cout << "[2] Inventory" << endl;
    cout << "[3] Rest (Returns you to full health/mana)" << endl;
    cout << "[4] Assign Skillpoints [" << skill << " available]" << endl;
    cout << "[5] Shop" << endl;
    cout << "[6] Questhall" << endl;
    cout << "[7] Dungeons" << endl;
    cout << "[98] Save Game" << endl;
    cout << "[99] Exit" << endl;
    cin >> input;
    if (input == "1") {
        goto sarena;
    } else if (input == "99") {
        goto leave;
    } else if (input == "2") {
        goto inventory;
    } else if (input == "3") {
        mhp = stre * 20;
        mmana = inte * 10;
        hp = mhp;
        hp = hp * (bphp+1);
        hp = hp + bhp;
        mana = mmana;
        goto menue;
    } else if (input == "4") {
        goto askill;
    } else if (input == "5") {
        goto shop;
    } else if (input == "6") {
        goto questhall;
    } else if (input == "7") {
        goto dungeonmenue;
    } else if (input == "98") {
        goto savegame;
    }

    goto menue;

    // Setting Up Enemys
    sarena:

    mmana = inte * 10;
    shp = stre * 20;
    mhp = stre * 20;
    random = rand()%5+1;
    switch (random) {
        case 1:
            etype = "Lion";
            random2 = rand()%2+1;
            eability = "gnaw";
            if (random2 == 1) {
                ehp = shp + rand()%(level*5);
            } else {
                ehp = shp - rand()%(level*5);
            }
            mehp = ehp;
        break;

        case 2:
            etype = "Boar";
            random2 = rand()%2+1;
            eability = "goar";
            if (random2 == 1) {
                ehp = shp + rand()%(level*3);
            } else {
                ehp = shp - rand()%(level*3);
            }
            mehp = ehp;
        break;

        case 3:
            etype = "Spider";
            random2 = rand()%2+1;
            eability = "webspin";
            elevel = rand()%(level+3)+1;
            if (random2 == 1) {
                ehp = shp + rand()%(level*2);
            } else {
                ehp = shp - rand()%(level*2);
            }
            mehp = ehp;
        break;

        case 4:
            etype = "Wolf";
            random2 = rand()%2+1;
            eability = "growl";
            elevel = rand()%(level+3)+1;
            if (random2 == 1) {
                ehp = shp + rand()%(level*4);
            } else {
                ehp = shp - rand()%(level*4);
            }
            mehp = ehp;
        break;

        case 5:
            etype = "Elephant";
            random2 = rand()%2+1;
            eability = "stomp";
            if (random2 == 1) {
                ehp = shp + rand()%(level*6);
            } else {
                ehp = shp - rand()%(level*3);
            }
            mehp = ehp;
        break;

    }

    elevel = rand()%(level+3)+1;
    if ((elevel < (level-3)) || (elevel > (level+3))) {
        elevel = level;
    }

    goto aarena;

    // Arena Main
    aarena:

    clear();
    draw(etype);
    cout << "[" << etype << " " << elevel << "] >>> " << ehp << "/" << mehp << endl;
    cout << "[" << name << " " << level << "] >>> " << hp << "/" << mhp << endl;
    cout << "-Mana- >>> " << mana << "/" << mmana << endl;
    cout << "[1] Attack" << endl;
    cout << "[2] Do ability" << endl;
    cout << "[3] Inventory" << endl;
    cout << "[99] Flee" << endl;
    cin >> input;

    if (input == "1") {
        dmg = (stre+inte) + rand()%(dext*2);
        ehp = ehp - dmg;
        // If enemy ehp < 0 then
        if (ehp <= 0) {
            clear();
            cout << "[*] You killed the " << etype << endl;
            cout << "Type [1] to continue" << endl;
            cin >> input;
            goto rarena;
        }
        random2 = rand()%2+1;
        edmg = dmg;
        // Setting up enemy damage
        if (random2 == 1) {
                edmg = edmg + rand()%(dext*2);
            } else {
                edmg = edmg - rand()%(dext*2);
            }
        hp = hp - edmg;
        // If player hp < 0 then
        if (hp <= 0) {
            clear();
            cout << "[*] You Died" << endl;
            cout << "Type [1] to continue" << endl;
            cin >> input;
            if (status != "arena") {
                status = "arena";
            }
            goto menue;
        }
        goto aarena;
    } else if (input == "99") {
        goto menue;
    } else if (input == "2") {
        goto darena;
    } else if (input == "3") {
        goto iarena;
    } else {
        goto aarena;
    }

    // Using items from Inventory
    iarena:
    clear();

    cout << "Inventory" << endl;
    cout << "[1] Health Potion, " << hpotion << " (Heals you to max health)" << endl;
    cout << "[99] Exit" << endl;
    cin >> input;
    if (input == "1") {
        if (hpotion > 0) {
            hpotion = hpotion - 1;
            hp = mhp;
        }
    } else if (input == "99") {
        goto aarena;
    }
    goto iarena;

    // Picking spells and doing damage
    darena:

    clear();
    tie(action, mana) = getability(pclass, mana, inte, level);
    // Champion spells
    if (action == "cleaving strike") {
        dmg = (stre+inte+dext) + rand()%(dext*2);
    } else if (action == "melting thrust") {
        dmg = (stre+inte+dext) + rand()%(stre*2);
    } else if (action == "critical bash") {
        dmg = (stre+inte+dext) + rand()%(inte*2);
    } else if (action == "purify") {
        hp = hp + inte*(level)+(rand()%inte);
        if (hp > mhp) {
            hp = mhp;
        }
        goto aarena;
    } else if (action == "none") {
        goto aarena;
    } else if (action == "shadow strike") {
        dmg = (stre+inte+dext) + rand()%(dext*2);
    } else if (action == "cripple") {
        dmg = (stre+inte+dext) + rand()%(stre*2);
    } else if (action == "mutilate") {
        dmg = (stre+inte+dext) + rand()%(inte*2);
    } else if (action == "life tap") {
        dmg = (stre+inte) + rand()%(inte);
        hp = hp + dmg;
        if (hp > mhp) {
            hp = mhp;
        }
        ehp = ehp - dmg;
        // If enemy ehp < 0 then
        if (ehp <= 0) {
            clear();
            cout << "[*] You killed the " << etype << endl;
            cout << "Type [1] to continue" << endl;
            cin >> input;
            goto rarena;
        }
        goto aarena;
    }  else if (action == "back stab") {
        dmg = (stre+inte+dext) + rand()%(dext*2);
    } else if (action == "headcrack") {
        dmg = (stre+inte+dext) + rand()%(stre*2);
    } else if (action == "poison") {
        dmg = (stre+inte+dext) + rand()%(inte*2);
    } else if (action == "assassinate") {
        dmg = (stre+inte+dext) + rand()%((inte+stre)*3);
        ehp = ehp - dmg;
        // If enemy ehp < 0 then
        if (ehp <= 0) {
            clear();
            cout << "[*] You killed the " << etype << endl;
            cout << "Type [1] to continue" << endl;
            cin >> input;
            goto rarena;
        }
        goto aarena;
    } else if (action == "smite") {
        dmg = (stre+inte+dext) + rand()%(dext);
    } else if (action == "enflame") {
        dmg = (stre+inte+dext) + rand()%(stre);
    } else if (action == "atonement") {
        dmg = (stre+inte+dext) + rand()%(inte);
    } else if (action == "flash heal") {
        hp = hp + ((stre * level) + (rand()%(inte*2)));
        if (hp > mhp) {
            hp = mhp;
        }
        goto aarena;
    }

    // Doing the damage (Enemy)
    ehp = ehp - dmg;
    // If enemy ehp < 0 then
    if (ehp <= 0) {
        clear();
        cout << "[*] You killed the " << etype << endl;
        cout << "Type [1] to continue" << endl;
        cin >> input;
        goto rarena;
    }
    random2 = rand()%2+1;
    dmg = (stre+inte) + rand()%(dext*2);
    edmg = dmg;
    // Setting up enemy damage
    if (random2 == 1) {
        edmg = edmg + rand()%(dext*2);
    } else {
        edmg = edmg - rand()%(dext*2);
    }
    hp = hp - edmg;
    // If player hp < 0 then
    if (hp <= 0) {
        clear();
        cout << "[*] You Died" << endl;
        cout << "Type [1] to continue" << endl;
        cin >> input;
        if (status != "arena") {
            status = "arena";
        }
        goto menue;
    }
    goto aarena;

    // Here you get arena awards
    rarena:
    clear();
    rxp = rand()%(level*2)+5;
    rgold = rand()%(level*5);
    rgold = rgold * goldb;

    cout << R"(
*******************************************************************************
          |                   |                  |                     |       
 _________|________________.=""_;=.______________|_____________________|_______
|                   |  ,-"_,=""     `"=.|                  |                   
|___________________|__"=._o`"-._        `"=.______________|___________________
          |                `"=._o`"=._      _`"=._                     |       
 _________|_____________________:=._o "=._."_.-="'"=.__________________|_______
|                   |    __.--" , ; `"=._o." ,-"""-._ ".   |                   
|___________________|_._"  ,. .` ` `` ,  `"-._"-._   ". '__|___________________
          |           |o`"=._` , "` `; .". ,  "-._"-._; ;              |       
 _________|___________| ;`-.o`"=._; ." ` '`."\` . "-._ /_______________|_______
|                   | |o;    `"-.o`"=._``  '` " ,__.--o;   |                   
|___________________|_| ;     (#) `-.o `"=.`_.--"_o.-; ;___|___________________
____/______/______/___|o;._    "      `".o|o_.--"    ;o;____/______/______/____
/______/______/______/_"=._o--._        ; | ;        ; ;/______/______/______/_
____/______/______/______/__"=._o--._   ;o|o;     _._;o;____/______/______/____
/______/______/______/______/____"=._o._; | ;_.--"o.--"_/______/______/______/_
____/______/______/______/______/_____"=.o|o_.--""___/______/______/______/____
/______/______/______/______/______/______/______/______/______/______/______/_
*******************************************************************************
    )" << endl; // That chest looks amazing

    if (status == "oozeworks") {
        goto oozeworksr;
    } else {
        goto rrarena;
    }

    rrarena:
    cout << "You recieved " << rxp << " xp" << endl;
    cout << "You recieved " << rgold << " gold" << endl;
    xp = xp + rxp;
    gold = gold + rgold;
    if (xp >= xpl) {
        level = level + 1;
        xpl = xpl + xpb;
        xpb = xpb + 50;
        skill = skill + 5;
        cout << "You have leveled up to level " << level << endl;
    }
    cout << "Type [1] to continue to the menue" << endl;

    if (etype == questmob) {
        questc = questc + 1;
    }

    cin >> input;
    goto menue;

    // Inventory
    inventory:
    clear();

    cout << R"(
       ___
       )_(                                            _
       | |                                           [_ ]
     .-'-'-.       _                               .-'. '-.
    /-::_..-\    _[_]_                            /:;/ _.-'\
    )_     _(   /_   _\      [-]                  |:._   .-|
    |;::    |   )_``'_(    .-'-'-.       (-)      |:._     |
    |;::    |   |;:   |    :-...-:     .-'-'-.    |:._     |
    |;::    |   |;:   |    |;:   |     |-...-|    |:._     |
    |;::-.._|   |;:.._|    |;:.._|     |;:.._|    |:._     |
    `-.._..-'   `-...-'    `-...-'     `-...-'    `-.____.-'    
    )" << endl;

    levelalert:
    if (xp >= xpl) {
        level = level + 1;
        xpl = xpl + xpb;
        xpb = xpb + 50;
        skill = skill + 5;
        goto levelalert;
    } else {
        goto inventory2;
    }

    inventory2:
    cout << "[Level] >>> " << level << endl;
    cout << "[XP] >>> " << xp << "/" << xpl << endl;
    cout << "[Gold] >>> " << gold << endl;
    cout << "[Healing Potions] >>> " << hpotion << endl;
    cout << "==================================" << endl;
    cout << "Head- " << head << endl;
    cout << "Chest- " << chest << endl;
    cout << "Legs- " << legs << endl;
    cout << "Feet- " << feet << endl;
    cout << "==================================" << endl;
    cout << "1- Armory" << endl;
    cout << "99- Exit" << endl;
    cin >> input;
    if (input == "99") {
        goto menue;
    } else if (input == "1") {
        goto armory;
    }
    goto inventory;

    armory:
    clear();

    cout << R"(
   ,   A           {}
  / \, | ,        .--.
 |    =|= >      /.--.\
  \ /` | `       |====|
   `   |         |`::`|  
       |     .-;`\..../`;_.-^-._
      /\\/  /  |...::..|`   :   `|
      |:'\ |   /'''::''|   .:.   |
       \ /\;-,/\   ::  |..:::::..|
       |\ <` >  >._::_.| ':::::' |
       | `""`  /   ^^  |   ':'   |
    )" << endl;

    cout << "1- Slimy Chestplate >>> [10% HP Increase] ";
    if (slimychestplate == 1) {
        cout << ">>> [Owned] ";
        if (chest == "slimychestplate") {
            cout << ">>> [Equiped]" << endl;
        } else {
            cout << endl;
        }
    } else {
        cout << endl;
    }

    cout << "2- Slimy Helmet >>> [5% HP Increase] ";
    if (slimyhelmet == 1) {
        cout << ">>> [Owned] ";
        if (head == "slimyhelmet") {
            cout << ">>> [Equiped]" << endl;
        } else {
            cout << endl;
        }
    } else {
        cout << endl;
    }

    cout << "3- Oozing Legplates >>> [15% HP Increase] ";
    if (oozinglegplates == 1) {
        cout << ">>> [Owned] ";
        if (legs == "oozinglegplates") {
            cout << ">>> [Equiped]" << endl;
        } else {
            cout << endl;
        }
    } else {
        cout << endl;
    }

    cout << "4- Oozing Boots >>> [10% HP Increase] ";
    if (oozingboots == 1) {
        cout << ">>> [Owned] ";
        if (feet == "oozingboots") {
            cout << ">>> [Equiped]" << endl;
        } else {
            cout << endl;
        }
    } else {
        cout << endl;
    }

    cout << "99- Back" << endl;
    cin >> input;
    if (input == "99") {
        goto armor2;
    } else if (input == "1") {
        if (chest == "slimychestplate") {
            chest = "Open";
            goto armory;
        } else if (slimychestplate == 1) {
            chest = "slimychestplate";
            goto armory;
        } else {
            goto armory;
        }
    } else if (input == "2") {
        if (head == "slimyhelmet") {
            head = "Open";
            goto armory;
        } else if (slimyhelmet == 1) {
            head = "slimyhelmet";
            goto armory;
        } else {
            goto armory;
        }
    } else if (input == "3") {
        if (legs == "oozinglegplates") {
            legs = "Open";
            goto armory;
        } else if (oozinglegplates == 1) {
            legs = "oozinglegplates";
            goto armory;
        } else {
            goto armory;
        }
    } else if (input == "4") {
        if (feet == "oozingboots") {
            feet = "Open";
            goto armory;
        } else if (oozingboots == 1) {
            feet = "oozingboots";
            goto armory;
        } else {
            goto armory;
        }
    }

    armor2:
    bhp = 0;
    bphp = 0;
    // Head
    if (head == "slimyhelmet") {
        bphp = bphp + .10;
    }
    // Chest
    if (chest == "slimychestplate") {
        bphp = bphp + .05;
    }
    // Legs
    if (legs == "oozinglegplates") {
        bphp = bphp + .15;
    }
    // Feet
    if (feet == "oozingboots") {
        bphp = bphp + .10;
    }
    goto inventory;

    askill:
    clear();

    cout << R"(
              .__=\__                  .__==__,
            jf'      ~~=\,         _=/~'      `\,
        ._jZ'            `\q,   /=~             `\__
       j5(/                 `\./                  V\\,
     .Z))' _____              |             .____, \)/\
    j5(K=~~     ~~~~\=_,      |      _/=~~~~'    `~~+K\\,
  .Z)\/                `~=L   |  _=/~                 t\ZL
 j5(_/.__/===========\__   ~q |j/   .__============___/\J(N,
4L#XXXL_________________XGm, \P  .mXL_________________JXXXW8L
~~~~~~~~~~~~~~~~~~~~~~~~~YKWmmWmmW@~~~~~~~~~~~~~~~~~~~~~~~~~~
    )" << endl; // Bedtime stories?

    cout << "Available Skillpoints [" << skill << "]" << endl;
    cout << "1- Strength [" << stre << "]" << endl;
    cout << "2- Intelligence [" << inte << "]" << endl;
    cout << "3- Dexterity [" << dext << "]" << endl;
    cout << "99- Exit" << endl;
    cin >> input;
    if (skill > 0) {
        if (input == "1") {
            stre = stre + 1;
            skill = skill - 1;
        } else if (input == "2") {
            inte = inte + 1;
            skill = skill - 1;
        } else if (input == "3") {
            dext = dext + 1;
            skill = skill - 1;
        } else if (input == "99") {
            goto menue;
        }
    } else {
        goto menue;
    }
    goto askill;

    shop:
    clear();

    cout << R"(
                               ____
                  _           |---||            _
                  ||__________|---||___________||
                 /_ _ _ _ _ _ |:._|'_ _ _ _ _ _ _\`.
                /_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _\:`.
               /_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _\::`.
              /:.___________________________________\:::`-._
          _.-'_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _`::::::`-.._
      _.-' _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ `:::::::::`-._
    ,'_:._________________________________________________`:_.::::-';`
     `.'/ || |:::::`.'/::::::::`.'/::::::::`.'/::::::|.`.'/.|     :|
      ||  || |::::::||::::::::::||::::::::::||:::::::|..||..|     ||
      ||  || |  __  || ::  ___  || ::  __   || ::    |..||;||     ||
      ||  || | |::| || :: |:::| || :: |::|  || ::    |.|||:||_____||__
      ||  || | |::| || :: |:::| || :: |::|  || ::    |.|||:||_|_|_||,(
      ||_.|| | |::| || :: |:::| || :: |::|  || ::    |.'||..|    _||,|
   .-'::_.:'.:-.--.-::--.-:.--:-::--.--.--.-::--.--.-:.-::,'.--.'_|| |
    );||_|__||_|__|_||__|_||::|_||__|__|__|_||__|__|_|;-'|__|_(,' || '-
    ||||  || |. . . ||. . . . . ||. . . . . ||. . . .|::||;''||   ||:'
    ||||.;  _|._._._||._._._._._||._._._._._||._._._.|:'||,, ||,,
    '''''           ''-         ''-         ''-         '''  '''
    )" << endl;

    price = level*5;
    cout << "[Gold] >>> " << gold << endl;
    cout << "1- Health Potion (" << price << " gold)" << endl;
    cout << "99- Exit" << endl;
    cin >> input;
    if (input == "1") {
        price = level*5;
        if (gold >= price) {
            hpotion = hpotion + 1;
            gold = gold - price;
            goto shop;
        } else {
            goto menue;
        }
    } else if (input == "99") {
        goto menue;
    }
    goto shop;

    // Receive / "Turn" in quests
    questhall:
    clear();

    cout << R"(
         _______________
    ()==(              (@==()
         '______________'|
           |             |
           |             |
         __)_____________|
    ()==(               (@==()
         '--------------'
    )" << endl;

    cout << "Current Quest >>> ";

    if (quest == 0) {
        quest = rand()%5+1;
        switch (quest) {
            case 1:
                questc = 0;
                questr = 6;
                questmob = "Boar";
                questreward = rand()%4+1;
                questdisplay = "Slay Boars";
            break;

            case 2:
                questc = 0;
                questr = 6;
                questmob = "Lion";
                questreward = rand()%4+1;
                questdisplay = "Slay Lions";
            break;

            case 3:
                questc = 0;
                questr = 6;
                questmob = "Elephant";
                questreward = rand()%4+1;
                questdisplay = "Slay Elephants";
            break;

            case 4:
                questc = 0;
                questr = 6;
                questmob = "Wolf";
                questreward = rand()%4+1;
                questdisplay = "Slay Wolves";
            break;

            case 5:
                questc = 0;
                questr = 6;
                questmob = "Spider";
                questreward = rand()%4+1;
                questdisplay = "Slay Spiders";
            break;

        }
    }

    cout << questdisplay << " [" << questc << "/" << questr << "]" << endl;
    cout << "Reward >>> ";

    switch (questreward) {
        case 1:
            qgoldr = rand()%(level*10)+(level*10);
            cout << qgoldr << " Gold" << endl;
        break;

        case 2:
            qxpr = rand()%(level*5)+(level*5);
            cout << qxpr << " XP" << endl;
        break;

        case 3:
            cout << "Slimy Chestplate >>> [10% HP Increase]" << endl;
        break;

        case 4:
            cout << "Slimy Helmet >>> [5% HP Increase]" << endl;
        break;
    }

    if (questc >= questr) {
        quest = 0;
        switch (questreward) {
            case 1:
                gold = gold + qgoldr;
                goto questhall;
            break;

            case 2:
                xp = xp + qxpr;
                goto questhall;
            break;

            case 3:
                slimychestplate = 1;
                goto questhall;
            break;

            case 4:
                slimyhelmet = 1;
                goto questhall;
            break;
        }
    }

    cout << "99- Exit" << endl;
    cin >> input;
    goto menue;

    dungeonmenue:
    clear();
    cout << R"(
   _________________________________________________________
  |                            ,                            |
  |      .-'````````'.        '(`        .-'```````'-.      |
  |    .` |           `.      `)'      .` |           `.    |          
  |   /   |   ()        \      U      /   |    ()       \   |
  |  |    |    ;         | o   T   o |    |    ;         |  |
  |  |    |     ;        |  .  |  .  |    |    ;         |  |
  |  |    |     ;        |   . | .   |    |    ;         |  |
  |  |    |     ;        |    .|.    |    |    ;         |  |
  |  |    |____;_________|     |     |    |____;_________|  |  
  |  |   /  __ ;   -     |     !     |   /     `'() _ -  |  |
  |  |  / __  ()        -|        -  |  /  __--      -   |  |
  |  | /        __-- _   |   _- _ -  | /        __--_    |  |
  |__|/__________________|___________|/__________________|__|
 /                                             _ -        lc \
/   -_- _ -             _- _---                       -_-  -_ \

    )" << endl;

    cout << "Available Dungeons" << endl;
    cout << "[1] Oozing Oozeworks >>> [150 Gold, Oozing Gear]" << endl;
    cout << "[99] Return to Menue" << endl;
    cin >> input;
    if (input == "99") {
        goto menue;
    } else if (input == "1") {
        if (gold >= 150) {
            status = "oozeworks";
            gold = gold - 150;
            ckills = 0;
            rkills = 6;
            goto oozeworks;
        } else {
            goto menue;
        }
    }
    goto dungeonmenue;

    oozeworks:
    clear();
    cout << R"(
 _____          _               _____                              _        
|  _  |        (_)             |  _  |                            | |       
| | | | ___ _____ _ __   __ _  | | | | ___ ______      _____  _ __| | _____ 
| | | |/ _ \_  / | '_ \ / _` | | | | |/ _ \_  /\ \ /\ / / _ \| '__| |/ / __|
\ \_/ / (_) / /| | | | | (_| | \ \_/ / (_) / /  \ V  V / (_) | |  |   <\__ \
 \___/ \___/___|_|_| |_|\__, |  \___/ \___/___|  \_/\_/ \___/|_|  |_|\_\___/
                         __/ |                                              
                        |___/                                               
    )" << endl;

    if (ckills >= rkills) {
        goto oozeworksc;
    }

    cout << "[1] Continue [" << ckills << "/" << rkills << " slimes killed]" << endl;
    cout << "[2] Adrenalin (Heals all missing hp/mana)" << endl;
    cout << "[99] Exit" << endl;
    cin >> input;
    if (input == "1") {
        mmana = inte * 10;
        shp = stre * 20;
        mhp = stre * 20;
        etype = "Slime";
        random2 = rand()%2+1;
        eability = "ooze";
        if (random2 == 1) {
            ehp = shp + rand()%(level*8);
        } else {
            ehp = shp + rand()%(level*6);
        }
        mehp = ehp;
        elevel = rand()%(level+3)+1;
        if ((elevel < (level-3)) || (elevel > (level+3))) {
            elevel = level;
        }
        goto aarena;
    } else if (input == "99") {
        goto menue;
    } else if (input == "2") {
        mhp = stre * 20;
        mmana = inte * 10;
        hp = mhp;
        hp = hp * (bphp+1);
        hp = hp + bhp;
        mana = mmana;
    }
    goto oozeworks;

    oozeworksc:
    status = "arena";
    cout << "You have completed the Oozing Oozworks" << endl;
    rxp = rand()%(level*6)+5;
    rgold = rand()%(level*9);
    cout << "You recieved " << rxp << " xp" << endl;
    cout << "You recieved " << rgold << " gold" << endl;
    xp = xp + rxp;
    gold = gold + rgold;
    random = rand()%2+1;
    switch (random) {
        case 1:
            cout << "New legplates unlocked! >>> [Oozing Legplates, increases hp by 15%]" << endl;
            oozinglegplates = 1;
        break;

        case 2:
            cout << "New boots unclocked! >>> [Oozing Boots, increases hp by 10%]" << endl;
            oozingboots = 1;
        break;
    }
    cout << "Type [1] to return to the menue" << endl;
    cin >> input;
    goto menue;

    oozeworksr:
    ckills = ckills + 1;
    rxp = rand()%(level*4)+5;
    rgold = rand()%(level*7);
    rgold = rgold * goldb;
    cout << "You recieved " << rxp << " xp" << endl;
    cout << "You recieved " << rgold << " gold" << endl;
    xp = xp + rxp;
    gold = gold + rgold;
    if (xp >= xpl) {
        level = level + 1;
        xpl = xpl + xpb;
        xpb = xpb + 50;
        skill = skill + 5;
        cout << "You have leveled up to level " << level << endl;
    }
    cout << "Type [1] to continue your adventure" << endl;
    cin >> input;
    goto oozeworks;

    savegame:
    clear();

    // Saving / Loading Strings
    sslimychestplate = std::to_string(slimychestplate);
    sslimyhelmet = std::to_string(slimyhelmet);
    soozinglegplates = std::to_string(oozinglegplates);
    soozingboots = std::to_string(oozingboots);
    spiclass = std::to_string(piclass);
    slevel = std::to_string(level);
    sxp = std::to_string(xp);
    sxpb = std::to_string(xpb);
    sxpl = std::to_string(xpl);
    sstre = std::to_string(stre);
    sinte = std::to_string(inte);
    sdext = std::to_string(dext);
    shpotion = std::to_string(hpotion);
    sgold = std::to_string(gold);
    sskill = std::to_string(skill);

    code = "";
    code = code + sgold + ":" + sstre + ":" + sinte + ":" + sdext + ":" + sskill + ":" + sxp + ":" + sxpl + ":" + sxpb + ":" + slevel + ":";
    code = code + shpotion + ":";
    code = code + spiclass + sslimyhelmet + sslimychestplate + soozinglegplates + soozingboots;
    cout << code << endl;
    cout << "Type [1] to return to the menue" << endl;
    cin >> input;
    goto menue;

    // Loading the game code they input
    loadgame:
    clear();

    sslimychestplate = "";
    sslimyhelmet = "";
    soozinglegplates = "";
    soozingboots = "";
    spiclass = "";
    slevel = "";
    sxp = "";
    sxpb = "";
    sxpl = "";
    sstre = "";
    sinte = "";
    sdext = "";
    shpotion = "";
    sgold = "";
    sskill = "";

    cout << "Please input your game code EXACTLY (if you don't this may corrupt your game save)" << endl;
    cin >> input;
    code = input;
    goto compile;
    cout << code << endl;

    // Loading Game Save FILE
    compile:
    x = 0;
    goldr:
    cout << sgold << endl;
    if (code[x] == ':') { // If the character is a pipe
        x=x+1; // Go to next variable
        goto strer;
    } else {
        sgold = sgold + code[x]; // If not add the variables
        x=x+1; // Go to next charcter
        goto goldr;
    }
    strer:
    if (code[x] == ':') { // If the character is a pipe
        x=x+1; // Go to next variable
        goto inter;
    } else {
        sstre = sstre + code[x]; // If not add the variables
        x=x+1; // Go to next charcter
        goto strer;
    }
    inter:
    if (code[x] == ':') { // If the character is a pipe
        x=x+1; // Go to next variable
        goto dextr;
    } else {
        sinte = sinte + code[x]; // If not add the variables
        x=x+1; // Go to next charcter
        goto inter;
    }
    dextr:
    if (code[x] == ':') { // If the character is a pipe
        x=x+1; // Go to next variable
        goto skillr;
    } else {
        sdext = sdext + code[x]; // If not add the variables
        x=x+1; // Go to next charcter
        goto dextr;
    }
    skillr:
    if (code[x] == ':') { // If the character is a pipe
        x=x+1; // Go to next variable
        goto xpr;
    } else {
        sskill = sskill + code[x]; // If not add the variables
        x=x+1; // Go to next charcter
        goto skillr;
    }
    xpr:
    if (code[x] == ':') { // If the character is a pipe
        x=x+1; // Go to next variable
        goto xplr;
    } else {
        sxp += code[x]; // If not add the variables
        x=x+1; // Go to next charcter
        goto xpr;
    }
    xplr:
    if (code[x] == ':') { // If the character is a pipe
        x=x+1; // Go to next variable
        goto xpbr;
    } else {
        sxpl = sxpl + code[x]; // If not add the variables
        x=x+1; // Go to next charcter
        goto xplr;
    }
    xpbr:
    if (code[x] == ':') { // If the character is a pipe
        x=x+1; // Go to next variable
        goto levelr;
    } else {
        sxpb = sxpb + code[x]; // If not add the variables
        x=x+1; // Go to next charcter
        goto xpbr;
    }
    levelr:
    if (code[x] == ':') { // If the character is a pipe
        x=x+1; // Go to next variable
        goto hpotionr;
    } else {
        slevel = slevel + code[x]; // If not add the variables
        x=x+1; // Go to next charcter
        goto levelr;
    }
    hpotionr:
    if (code[x] == ':') { // If the character is a pipe
        x=x+1; // Go to next variable
        goto items;
    } else {
        shpotion = shpotion + code[x]; // If not add the variables
        x=x+1; // Go to next charcter
        goto hpotionr;
    }
    items:
    spiclass += code[x];
    x=x+1;
    sslimyhelmet += code[x];
    x=x+1;
    sslimychestplate += code[x];
    x=x+1;
    soozinglegplates += code[x];
    x=x+1;
    soozingboots += code[x];
    goto settingvaribles;

    settingvaribles:

    gold = atoi(sgold.c_str()); // Gold
    stre = atoi(sstre.c_str()); // Strength
    inte = atoi(sinte.c_str()); // Intelligence
    dext = atoi(sdext.c_str()); // Dexterity
    skill = atoi(sskill.c_str()); // Skillpoints
    xp = atoi(sxp.c_str()); // Xp
    xpl = atoi(sxpl.c_str()); // Xp till level
    xpb = atoi(sxpb.c_str()); // Increases xpl
    level = atoi(slevel.c_str()); // Level
    hpotion = atoi(shpotion.c_str()); // HP potions
    piclass = atoi(spiclass.c_str()); // Class
    slimyhelmet = atoi(sslimyhelmet.c_str()); // Slimy Helmet
    slimychestplate = atoi(sslimychestplate.c_str()); // Slimy Chestplate
    oozinglegplates = atoi(soozinglegplates.c_str()); // Oozing Legplates
    oozingboots = atoi(soozingboots.c_str()); // Oozing Boots

    if (piclass == 1) {
        pclass = "Champion";
    } else if (piclass == 2) {
        pclass = "Necromancer";
    } else if (piclass == 3) {
        pclass = "Assassin";
    } else if (piclass == 4) {
        pclass = "Cleric";
    }

    /*
    gold = arr[1];
    stre = arr[2];
    inte = arr[3];
    dext = arr[4];
    skill = arr[5];
    xp = arr[6];
    xpl = arr[7];
    xpb = arr[8];
    level = arr[9];
    hpotion = arr[10];
    piclass = arr[11];
    slimyhelmet = arr[12];
    slimychestplate = arr[13];
    oozinglegplates = arr[14];
    oozingboots = arr[15];
    */

    goto compilend;

    compilend:
    clear();

    loadingload:
    cout << R"(
db       .d88b.   .d8b.  d8888b. d888888b d8b   db  d888b  
88      .8P  Y8. d8' `8b 88  `8D   `88'   888o  88 88' Y8b 
88      88    88 88ooo88 88   88    88    88V8o 88 88      
88      88    88 88~~~88 88   88    88    88 V8o88 88  ooo 
88booo. `8b  d8' 88   88 88  .8D   .88.   88  V888 88. ~8~ 
Y88888P  `Y88P'  YP   YP Y8888D' Y888888P VP   V8P  Y888P  
    )" << endl;
    y = 0;

    loadingload2:
    if (y < 101) {
        y = y + 1;
        goto loadingload2;
    }

    clear();
    cout << "-------~ Process Complete ~-------" << endl;
    cout << "Please re-input your name" << endl;
    cin >> input;
    name = input;
    goto menue;

    leave:
    clear();
    cout << "See you later!" << endl;

    return 0;
}
\$\endgroup\$
  • 45
    \$\begingroup\$ Some very impressive ascii art. \$\endgroup\$ – Martin York Aug 5 '17 at 16:07
  • 2
    \$\begingroup\$ Thx, also known problem "using namespace std" didn't know it was bad until I was half way done, currently I am still working on this project. \$\endgroup\$ – Handge Aug 5 '17 at 16:18
  • 3
    \$\begingroup\$ Can you talk a little about why you wanted to do this in C++ rather than, say, some scripting language? \$\endgroup\$ – einpoklum - reinstate Monica Aug 5 '17 at 19:40
  • 1
    \$\begingroup\$ Well, reason why I did this in C++, was because: a- One of the only languages I really know. b- I think C++ is really nice and easy and I don't really want to learn another language currently \$\endgroup\$ – Handge Aug 5 '17 at 20:38
  • 3
    \$\begingroup\$ I highly doubt that he did the ascii art, since this page has some of it with different names attached to it... ascii.co.uk/art \$\endgroup\$ – Florian Schaetz Aug 7 '17 at 14:04
46
\$\begingroup\$

First of all, let me join others in pointing out what sweet ASCII art you've done. This definitely has the beginnings of a much nicer text game than most.

I'd start by defining some structures to hold data about specific things in the game. For a couple of examples:

struct Ability {
    std::string name;
    int level_adder;

    int cost(int level, int inte) { 
        return (level + level_adder) * inte;
    }

    bool can_afford(int level, int inte, int manna) { 
        return cost(level, inte) <= manna;
    }

    void show(int level, int inte) { 
        std::cout << name << "[" << cost() << " manna]\n";
    }       
};

class PlayerClass {
    std::string name;
    std::vector<Ability> abilities;
    size_t ability_count() { return abilities.size(); }

    void show(int level, int inte) {
        for (int i=0; i<abilities.size(); i++)
            std::cout << "[" << i << "] ";
            abilities[i].show(level, inte);
    }

    Ability const &operator[](size_t index) const { 
        return abilities.at(index);
    }
};

With these, we can define all the data for the Player classes something like this:

PlayerClass Champion{
    "Champion",
    { "Cleaving Strike", 0},
    { "Melting Thrust", 0},
    {"Critical Bash", 0},
    {"Purify", 1}
};

PlayerClass Necromancer{
    "Necromancer",
    { "Shadow Strike", 0},
    { "cripple", 0},
    { "Mutilate", 0},
    { "Life Tap", 2}
};

...and so on for the other player classes. For only one example, this makes it much easier to add more player classes in the future--for example, I can sit down and decide I want to add a "thief" class:

PlayerClass Thief { 
    "Thief",
    { "Pick Pocket", 0},
    { "Grab Purse", 0},
    { "Rob Business", 1},
    { "Rob Bank", 4}
};

...and most of the rest of the game can work with this new player class without any modification at all. Likewise, I can add a new ability to an existing player class by simply deciding on a name and a relative cost for using that ability--I don't have to modify all the ability-related logic to take the newly added ability into account.

Then we can define a player to hold (for example) a reference to a PlayerClass object:

class Player { 
    PlayerClass &pclass;
    // ...
};

With this, getability obviously returns (possibly a pointer or reference to) an Ability object, and looks something like this:

player.pclass.show();
cin >> input;

// This logic isn't complete--we need to add a call to `can_afford` to see 
// whether the player can afford to use an ability.
if (input > player.pclass.ability_count()
    ability = None;
else { 
    ability = player.pclass[input];
    player.manna -= ability.cost();
}

Note how this has eliminated huge amounts of repetition in the code, with essentially identical logic repeated once for every ability of every player class.

Prevent mistakes

I'd also consider checking whether the player can afford to use a particular ability before displaying that ability. This way they only choose from the abilities they can use, rather than trying to choose an ability they can't actually afford, then finding out too late that they made a bad choice and nothing happens.

Naming

Some of the names you've used are shortened to the point that I'm not sure what they're intended to mean. Just for a couple of examples, inte and stre--I'd de-abbreviate these to the point that somebody reading the code can easily understand what they're supposed to really mean.

\$\endgroup\$
  • 2
    \$\begingroup\$ Funny how your players are not instances of their player class :-) \$\endgroup\$ – einpoklum - reinstate Monica Aug 5 '17 at 19:39
  • \$\begingroup\$ @einpoklum: It is somewhat--and tends to suggest that the names I used still aren't exactly ideal... \$\endgroup\$ – Jerry Coffin Aug 5 '17 at 19:48
  • 6
    \$\begingroup\$ Well, you can't call it "PlayerClassButNotInTheCppSense"... \$\endgroup\$ – einpoklum - reinstate Monica Aug 5 '17 at 21:00
  • 12
    \$\begingroup\$ If you're looking for an alternative to 'class' in the RPG sense, sometimes 'job' or 'profession' are preferred. \$\endgroup\$ – Pharap Aug 6 '17 at 2:41
29
\$\begingroup\$

There's good news and bad news about this code. The bad news is that it's not very good code. The good news is that it's not going to be too hard to improve it greatly, and the ASCII art is awesome! Here are some observations that may help you improve your code.

Don't abuse using namespace std

Putting using namespace std at the top of every program is a bad habit that you'd do well to avoid.

Consider separating I/O from the algorithm

Right now, everything is done in main. Better practice is to separate things into functions. In particular, I'd recommend separating the input/output routines into separate functions. Right now the control flow is too difficult to follow and it's hard to tell what's happening in the code.

Avoid using goto

Having a proliferation of goto statements is usually a sign of bad design. Better would be to eliminate them entirely -- it makes the code easier to follow and less error-prone. In this code, it's probable that you could use a state machine and switch statement instead which would make the code much easier to follow and much less prone to error.

Use objects

Because you're writing in C++, it would make sense to use objects. For example, you might have a class Player which contains all of the variables currently only labelled with a comment saying "player variables" and a game state class to simplify loading and saving the game state.

Consider using a better random number generator

You are currently using

random = rand()%5+1;

There is a problem with this approach: the low order bits of the random number generator are not particularly random, so neither will random be. On my machine, there's a slight but measurable bias toward 0 with that. A better solution, if your compiler and library supports it, would be to use the C++11 `std::uniform_int_distribution. It looks complex, but it's actually pretty easy to use.

class Die {
public:
    Die(int min, int max) : min{min}, max{max} {}
    Die(int max=6) : min{1}, max{max} {}
    int operator()() { 
        std::uniform_int_distribution<> dist(min,max);
        return dist(eng);
    }

private:
    int min; 
    int max;
    static std::mt19937 eng;
};

std::mt19937 Die::eng{std::random_device{}()};

When you want, as in this example, some number from 1 to 6, just call it like this:

int main() {
    Die die{6};   // create conventional 6-sided die
    for (int i=20; i; --i) {
        std::cout << die();
    }
}

Eliminate "magic numbers"

There are numbers sprinkled throughout the code, such as 5 and 6 that have a specific meaning in their particular context. By using named constants the program becomes easier to read and maintain. For cases in which the constant only has sense with respect to a particular object, consider making that constant part of the object.

Omit return 0

When a C or C++ program reaches the end of main the compiler will automatically generate code to return 0, so there is no need to put return 0; explicitly at the end of main.

\$\endgroup\$
  • 20
    \$\begingroup\$ With due respect - I think it's a waste of advise space to talk about return 0. It really doesn't matter this way or the other, it has no effect on the design of anything. Suggest you just drop that section of a long (and useful) answer. \$\endgroup\$ – einpoklum - reinstate Monica Aug 5 '17 at 19:36
  • \$\begingroup\$ I've trimmed it. \$\endgroup\$ – Edward Aug 5 '17 at 21:43
  • 4
    \$\begingroup\$ A matter of personal coding style, but I learned to write return EXIT_SUCCESS; and usually still do. Any other function in C or C++ declared as returning int needs a return statement, and magic numbers are bad style. \$\endgroup\$ – Davislor Aug 7 '17 at 8:31
  • \$\begingroup\$ Int-to-bool implicit casting makes i; a valid expression in the condition statement, but is this common practice for C++? I'd think i != 0 or i > 0 is better. \$\endgroup\$ – person27 Aug 7 '17 at 20:13
  • \$\begingroup\$ @Anon234_4521: yes, it's common practice and I think you'll find that on most architectures and compilers, it produces identical code if it's instead written i != 0. \$\endgroup\$ – Edward Aug 7 '17 at 21:02
16
\$\begingroup\$

Control Flow

I want to endorse and flesh out a bit Edward’s advice on removing the goto statements. A better approach here is some kind of state machine. You’ve got some kind of game state that tracks what mode you’re in, such as whether you’re selecting an ability, moving around, starting an encounter, and so on.

You draw whatever screen is appropriate to the current state. (Say, a map in overland mode, ASCII art of a monster in battle mode, maybe a box-drawn first-person dungeon if you’re paying homage to Wizardry or Ultima?) The options displayed depend on the game mode, what abilities you have, and whatever else is appropriate (such as what items are in your inventory, maybe, or how much energy you have, or a cooldown period). Selecting any option then updates the game state. For example, winning a battle might send you to the loot screen. The program logic is a loop something like this:

while ( !state.hasQuit() ) {
  state.displayScreen();
  state.getActionAndUpdate();
}

A lot simpler and easier to follow than a bunch of goto statements!

Then displayScreen() could, say, clear the screen, print a status bar, print the ASCII art, look up what abilities your character has, print those, and give you a menu prompt.

If the state becomes big and bulky enough, you would no longer want to keep the entire game state in a single “God object” that everything needs to muck around with. Player stats might go into one object, world maps in another, enemy stats in others. Separate subsystems however it makes sense. But that kind of loop is a nice, simple pattern for tracking which game mode you’re currently in and acting appropriately.

Using OO to Clean Up the Code

A variant is to make the last line something like state = state->getNewState(), where state is a pointer whose type is the base class of all states. That lets you split up the abstract concept of a game mode into concrete instances, like overland mode or combat mode. These can be daughter classes whose member functions implement the correct behavior for that one mode. Each of those member functions would then be shorter and clearer than one big function that contains all the code paths for every mode. You select between them by returning a reference to another state object, no if, switch or goto involved.

If you have only a finite number of static game modes, you can create a single object for each and return const references to them. The specific code for each mode would live inside a member function of one derived class.

If you need to create new state objects, such as to store different information for each battle, you want to manage them with smart pointers like std::unique_ptr. This does all the memory management for you, which is otherwise one of the trickiest things to get right.

If you do have any remaining sections of code that fit the pattern of a series of tests to select a different code branch for each of many cases, the classic solution is to define an enum with a constant for every possible case and then write a switch statement. Your compiler might even have a prayer of warning you if you forget or mistype one of the cases.

Avoid Global Variables

These make your program very hard to debug, because any part of the code could have changed them. A better solution is to have the player state store internally things like how many health potions you have. Then, only the code to use one decrements the count, and only the code to check inventory looks it up and displays it.

You also would want to store numeric data as a number, rather than as a string that you repeatedly convert back into a number.

Use Data Structures

In general, I would advise moving your special cases into data structures, not code branches. Jerry Coffin had good suggestions about how to do this for player abilities.

But, for example, your phenomenal ASCII art and other monster data could be stored as values in a hash table (std::unordered_map in the STL) with the names you’re using to select them as the keys. This would have several advantages. One of the most important is that, if you add another monster, you just need to add its entry to the table in one place.

Treating every new ability and every new monster as a special case that needs special handling in every piece of code that deals with them becomes a real nightmare: you’ve always got to check that you remembered them all everywhere and dealt with them consistently. If you put all the data in one place, and you write code in another place that can handle any piece of the data, adding stuff becomes so much easier.

Some Technicalities

In this case, you might consider storing your ASCII art as vectors of rows. (Since two-dimensional array can mean a few different things, a good unambiguous name for this is a rectangular array.) This would make it possible to, for example, display a picture of the monster in a box and wrap an infodump around it, or show its stats on the left or right of the picture, or overlay a caption like "-999 HP" on the frame.

I want to repeat and emphasize the advice to separate the data from the display code. You want code orthogonal to your game data. You shouldn’t be rewriting the display code for every kind of monster, because that makes it a nightmare to ever change. Don’t repeat yourself! That also makes it possible for you to do a lot more things with your data or run your code on arbitrary new data.

If you make the art arrays wide characters, then use std::wcout, you can use Unicode art, and not just ASCII art. You might not necessarily want to. Your art is spectacular. The classic games you’re hearkening back to used the box-drawing and geometric shape characters now in Unicode. But the option is there. Note that Windows needs a bit of non-standard initialization code inside an #ifdef block for this to work, but I can share it if you want.

On many systems even today, you can also insert ANSI color codes (16 colors, foreground and background, and some special features like bold and underline, like in the late 8-bit or early 16-bit era, and exactly like classic Unix terminals over telnet) or xterm color (more than 200). Can’t get more authentically retro than that. The Linux console supports those codes natively, and I think the OSX terminal does too.

\$\endgroup\$
12
\$\begingroup\$

You might want to load the ascii art from separate text files so you can edit those without messing with the functional code.

\$\endgroup\$
  • \$\begingroup\$ This seems to be more like a comment, but I also notice you didn't have enough reputation to post a comment. +1 anyway. \$\endgroup\$ – Oskar Skog Aug 7 '17 at 20:02
5
\$\begingroup\$

Separate the code in short and simple functions

Your current functions are way too long: for example, main() has 1282 lines. That's much more than acceptable.

While the exact maximal number of lines is a subjective thing, there are some good rules of thumb. For example, this question mentions 100 to 200 lines as the upper bound. Ideally, every function should do just one thing and do it well - this called SRP, single responsibility principle.

In many programs main() is somewhat an exception of this, as it usually does both initialization (by calling an init() or similar function) and the main loop. Still, the main loop should consist mainly of calls to other functions and/or state machine logic. I believe the other answers give more details on structuring it.

\$\endgroup\$
3
\$\begingroup\$

Of all the answers, the one thing that I would emphasize the most is use objects. This was already answered, but again, I wanted to emphasize it in detail. If you only learn that, this is worth more than the rest of the answers combined.

You seem to think in procedures and algorithms. This is nice if you do C, but if you want to do C++, learn to think in objects. Objects have clear responsibilities in which you trust blindly (as long as their unit tests work) and of which you don't need to know HOW they do stuff. Objects work as autonomous entities. I'd recommend that you try to use no functions at all, best is only when you need to extend the functionality of a standard library type like std::vector.

In the end, your main should look like this:

int main() {
    Game game;
    game.run();
    return 0;
}

The Game class (or however you want to call it) then creates other classes which in turn might create some more, in some sort of hierarchy. The methods of such a class call methods of other classes and do not much more than to make the child objects work together. Actual algorithms are as short as possible, in methods of most basic classes.

Anything that is content - like ASCII art, RPG character class names, names of places et cetera - should not be in the code itself.

Focus on the concept of readability. Your code should have a very clear outline. If a stranger wants to find some specific functionality, it should be evident where to look. Right now, one has to browse through one big file. If you have several classes with header and source files, one knows where to look.

\$\endgroup\$
  • \$\begingroup\$ OOP is one of the approaches, but not the only one or necessarily the best one for all of things. C++ is a multi paradigm language. \$\endgroup\$ – kfx Aug 8 '17 at 9:52
  • 1
    \$\begingroup\$ @kfx I'd say that the cases in which you use other approaches are comparatively rare. Basically when it comes down to actual algorithms that solve some very mathematically modeled problem. anyway, thing is, a beginner should learn the one approach that will be the best one in most situations he will ever encounter, which is OOP. We can argue in which cases other approaches are better, but if we have a beginner... And the actual case we have at hand is certainly best solved by OOP. \$\endgroup\$ – Aziuth Aug 8 '17 at 15:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.