55
\$\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;
}
Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Aug 5, 2017 at 14:58
\$\endgroup\$
10
  • 47
    \$\begingroup\$ Some very impressive ascii art. \$\endgroup\$ Commented Aug 5, 2017 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\$ Commented Aug 5, 2017 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\$ Commented Aug 5, 2017 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\$ Commented Aug 5, 2017 at 20:38
  • 4
    \$\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\$ Commented Aug 7, 2017 at 14:04

6 Answers 6

47
\$\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.

answered Aug 5, 2017 at 17:09
\$\endgroup\$
4
  • 2
    \$\begingroup\$ Funny how your players are not instances of their player class :-) \$\endgroup\$ Commented Aug 5, 2017 at 19:39
  • \$\begingroup\$ @einpoklum: It is somewhat--and tends to suggest that the names I used still aren't exactly ideal... \$\endgroup\$ Commented Aug 5, 2017 at 19:48
  • 6
    \$\begingroup\$ Well, you can't call it "PlayerClassButNotInTheCppSense"... \$\endgroup\$ Commented Aug 5, 2017 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\$ Commented Aug 6, 2017 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.

answered Aug 5, 2017 at 16:54
\$\endgroup\$
5
  • 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\$ Commented Aug 5, 2017 at 19:36
  • \$\begingroup\$ I've trimmed it. \$\endgroup\$ Commented Aug 5, 2017 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\$ Commented Aug 7, 2017 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\$ Commented Aug 7, 2017 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\$ Commented Aug 7, 2017 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. ETA: And so does Windows, through a non-standard API.

answered Aug 6, 2017 at 1:02
\$\endgroup\$
0
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.

answered Aug 7, 2017 at 11:14
\$\endgroup\$
1
  • \$\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\$ Commented Aug 7, 2017 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.

Stephen Rauch
4,31412 gold badges24 silver badges36 bronze badges
answered Aug 7, 2017 at 14:16
\$\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.

kfx
3061 silver badge7 bronze badges
answered Aug 7, 2017 at 15:09
\$\endgroup\$
2
  • \$\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\$ Commented Aug 8, 2017 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\$ Commented Aug 8, 2017 at 15:00

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.