Started learning C++ a few weeks ago, tried to accurately simulate a Pokemon battle. Not too optimistic how it turned out, please tell me how I could improve it.
Only the bare minimum(damage, accuracy, type effectiveness) are implemented, items weather etc are not.
typechart.h:
#pragma once
constexpr float typeChart[18][18]
{
{1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.5f, 0.0f, 1.0f, 1.0f, 0.5f, 1.0f}, //normal
{1.0f, 0.5f, 0.5f, 1.0f, 2.0f, 2.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 2.0f, 0.5f, 1.0f, 0.5f, 1.0f, 2.0f, 1.0f}, //fire
{1.0f, 2.0f, 0.5f, 1.0f, 0.5f, 1.0f, 1.0f, 1.0f, 2.0f, 1.0f, 1.0f, 1.0f, 2.0f, 1.0f, 0.5f, 1.0f, 1.0f, 1.0f}, //water
{1.0f, 1.0f, 2.0f, 0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 2.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.5f, 1.0f, 1.0f, 1.0f}, //elec
{1.0f, 0.5f, 2.0f, 1.0f, 0.5f, 1.0f, 1.0f, 0.5f, 2.0f, 0.5f, 1.0f, 0.5f, 2.0f, 1.0f, 0.5f, 1.0f, 0.5f, 1.0f}, //grass
{1.0f, 0.5f, 0.5f, 1.0f, 2.0f, 0.5f, 1.0f, 1.0f, 2.0f, 2.0f, 1.0f, 1.0f, 1.0f, 1.0f, 2.0f, 1.0f, 0.5f, 1.0f}, //ice
{2.0f, 1.0f, 1.0f, 1.0f, 1.0f, 2.0f, 1.0f, 0.5f, 1.0f, 0.5f, 0.5f, 0.5f, 2.0f, 0.0f, 1.0f, 2.0f, 2.0f, 0.5f}, //fight
{1.0f, 1.0f, 1.0f, 1.0f, 2.0f, 1.0f, 1.0f, 0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 0.5f, 0.5f, 1.0f, 1.0f, 0.0f, 2.0f}, //poison
{1.0f, 2.0f, 1.0f, 2.0f, 0.5f, 1.0f, 1.0f, 2.0f, 1.0f, 0.0f, 1.0f, 0.5f, 2.0f, 1.0f, 1.0f, 1.0f, 2.0f, 1.0f}, //ground
{1.0f, 1.0f, 1.0f, 0.5f, 2.0f, 1.0f, 2.0f, 1.0f, 1.0f, 1.0f, 1.0f, 2.0f, 0.5f, 1.0f, 1.0f, 1.0f, 0.5f, 1.0f}, //flying
{1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 2.0f, 2.0f, 1.0f, 1.0f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.5f, 1.0f}, //psychic
{1.0f, 0.5f, 1.0f, 1.0f, 2.0f, 1.0f, 0.5f, 0.5f, 1.0f, 0.5f, 2.0f, 1.0f, 1.0f, 0.5f, 1.0f, 2.0f, 0.5f, 0.5f}, //bug
{1.0f, 2.0f, 1.0f, 1.0f, 1.0f, 2.0f, 0.5f, 1.0f, 0.5f, 2.0f, 1.0f, 2.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.5f, 1.0f}, //rock
{0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 2.0f, 1.0f, 1.0f, 2.0f, 1.0f, 0.5f, 1.0f, 1.0f}, //ghost
{1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 2.0f, 1.0f, 0.5f, 0.0f}, //dragon
{1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.5f, 1.0f, 1.0f, 1.0f, 2.0f, 1.0f, 1.0f, 2.0f, 1.0f, 0.5f, 1.0f, 0.5f}, //dark
{1.0f, 0.5f, 0.5f, 0.5f, 1.0f, 2.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 2.0f, 1.0f, 1.0f, 1.0f, 0.5f, 2.0f}, //steel
{1.0f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 2.0f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 2.0f, 2.0f, 0.5f, 1.0f} //fairy
};
pokemon.h:
#pragma once
#include <string>
#include <iostream>
#include <random>
struct Move
{
std::string name;
int basePower{};
int accuracy{};
int type{};
bool dmgType{}; //true for physical, false for special
};
class Pokemon
{
std::string name;
int level{};
int hp{};
int hpMax{};
int attack{};
int defense{};
int spAttack{};
int spDefense{};
int speed{};
int typePrimary{};
int typeSecondary{};
Move slotOne;
Move slotTwo;
Move slotThree;
Move slotFour;
public:
std::string getName();
void printBattleText();
void printFainted();
void setName(std::string iname);
void setStats(int ilevel, int ihpMax, int iattack, int idefense, int ispAttack, int ispDefense, int ispeed);
void setType(int one, int two);
void setMoves(Move one, Move two, Move three, Move four);
int getPhysAtkValue(Pokemon pokemon, Move move);
int getSpAtkValue(Pokemon pokemon, Move move);
bool isFasterThan(Pokemon pokemon);
void displayMoves();
Move moveSelect(int input);
void takeDamage(int damage);
double stabCheck(Move move);
float getTypeEff(Move move);
bool hasFainted();
};
pokemon.cpp:
#include "pokemon.h"
#include "typechart.h"
std::string Pokemon::getName()
{
return name;
}
void Pokemon::printBattleText()
{
std::cout << name << '\n' << "Level " << level << '\n' << hp << "/" << hpMax << "\n \n";
}
void Pokemon::printFainted()
{
std::cout << name << " fainted!\n";
}
void Pokemon::setName(std::string iname)
{
name = iname;
}
void Pokemon::setStats(int ilevel, int ihpMax, int iattack, int idefense, int ispAttack, int ispDefense, int ispeed)
{
level = ilevel;
hp = ihpMax;
hpMax = ihpMax;
attack = iattack;
defense = idefense;
spAttack = ispAttack;
spDefense = ispDefense;
speed = ispeed;
}
void Pokemon::setType(int one, int two)
{
typePrimary = one;
typeSecondary = two;
}
void Pokemon::setMoves(Move one, Move two, Move three, Move four)
{
slotOne = one;
slotTwo = two;
slotThree = three;
slotFour = four;
}
int Pokemon::getPhysAtkValue(Pokemon pokemon, Move move)
{
int levelCalc{ (2 * level / 5) + 2 };
int finalCalc{ levelCalc * attack * move.basePower / pokemon.defense };
return finalCalc;
}
int Pokemon::getSpAtkValue(Pokemon pokemon, Move move)
{
int levelCalc{ (2 * level / 5) + 2 };
int finalCalc{ levelCalc * spAttack * move.basePower / pokemon.spDefense };
return finalCalc;
}
bool Pokemon::isFasterThan(Pokemon pokemon)
{
return (speed > pokemon.speed);
}
void Pokemon::displayMoves()
{
std::cout << "1. " << slotOne.name << std::endl;
std::cout << "2. " << slotTwo.name << std::endl;
std::cout << "3. " << slotThree.name << std::endl;
std::cout << "4. " << slotFour.name << std::endl;
}
Move Pokemon::moveSelect(int input)
{
switch (input)
{
case 1:
return slotOne;
case 2:
return slotTwo;
case 3:
return slotThree;
case 4:
return slotFour;
default:
return slotOne;
}
}
void Pokemon::takeDamage(int damage)
{
if (damage == -1)
std::cout << name << " avoided the attack!\n";
else if (damage >= hp)
{
std::cout << name << " took " << damage - (damage - hp) << " damage!\n";
hp = 0;
}
else
{
hp = hp - damage;
std::cout << name << " took " << damage << " damage!\n \n";
}
}
double Pokemon::stabCheck(Move move)
{
if (typePrimary == move.type || typeSecondary == move.type)
return 1.5;
else
return 1.0;
}
float Pokemon::getTypeEff(Move move)
{
if (typeSecondary == -1)
return typeChart[move.type][typePrimary];
else
return typeChart[move.type][typePrimary] * typeChart[move.type][typeSecondary];
}
bool Pokemon::hasFainted()
{
return (hp <= 0);
}
battlefunctions.cpp:
#include "pokemon.h"
#include <array>
std::random_device rd{};
std::mt19937 mersenne{ rd() };
std::uniform_int_distribution hit{ 1, 100 };
std::uniform_int_distribution dmgRoll{ 85, 100 };
void battleStart(std::array<Pokemon, 2>&, std::array<Pokemon, 2>&);
int getInput(int lower, int upper)
{
int input{};
std::cin >> input;
while (input < lower || input > upper)
{
std::cout << "Select a valid option:\n";
std::cin >> input;
}
return input;
}
bool moveHit(int accuracy)
{
int x = hit(mersenne);
if (x <= accuracy)
return true;
else
return false;
}
int getDamage(Pokemon& attacker, Pokemon& defender, Move move)
{
int damageOne{}, damageFinal{};
std::cout << attacker.getName() << " used " << move.name << "!\n";
if (moveHit(move.accuracy))
{
if (move.dmgType)
{
damageOne = attacker.getPhysAtkValue(defender, move);
damageFinal = (damageOne / 50) + 2;
}
else
{
damageOne = attacker.getSpAtkValue(defender, move);
damageFinal = (damageOne / 50) + 2;
}
damageFinal = static_cast<int>(damageFinal * attacker.stabCheck(move) * defender.getTypeEff(move));
damageFinal = damageFinal * dmgRoll(mersenne) / 100;
if (defender.getTypeEff(move) >= 2)
std::cout << "It's super effective!\n";
else if (defender.getTypeEff(move) == 0)
std::cout << "It doesn't affect " << defender.getName() << "...\n";
else if (defender.getTypeEff(move) <= 0.5)
std::cout << "It's not very effective...\n";
return damageFinal;
}
else
{
return -1; //attack missed
}
}
int switchPokemon(std::array <Pokemon, 2>& party)
{
std::cout << "Who will you switch to?\n";
int partySize{ 2 }, count{}, input{ 0 };
while (input <= 1 || input > partySize)
{
count = 1;
for (auto& Pokemon : party)
{
std::cout << count << ". " << Pokemon.getName() << std::endl;
count++;
}
std::cin >> input;
if (input <= 1 || input > partySize)
std::cout << "You can't select that!\n";
else if (party[input - 1].hasFainted())
std::cout << party[input - 1].getName() << "has fainted! You can't send them out!\n";
}
int result = input;
return result;
}
bool loseBattle(std::array<Pokemon, 2>& party)
{
for (auto& pokemon : party)
{
if (pokemon.hasFainted() == false)
return false;
}
return true;
}
void twoFaint(std::array<Pokemon, 2> two, std::array<Pokemon, 2> one)
{
two[0].printFainted();
if (loseBattle(two))
{
std::cout << "Player Two has no Pokemon left!\n";
throw "Player One wins!";
}
else
{
int switchOut{ switchPokemon(two) };
std::swap(two[0], two[switchOut - 1]);
battleStart(one, two);
}
}
void oneFaint(std::array<Pokemon, 2> one, std::array<Pokemon, 2> two)
{
one[0].printFainted();
if (loseBattle(one))
{
std::cout << "Player One has no Pokemon left!\n";
throw "Player Two wins!";
}
else
{
int switchOut{ switchPokemon(one) };
std::swap(one[0], one[switchOut - 1]);
battleStart(one, two);
}
}
movedata.h:
#pragma once
#include "pokemon.h"
namespace Moves
{
Move flamethrower{ "Flamethrower", 90, 100, 1, false };
Move airSlash{ "Air Slash", 75, 95, 9, false };
Move dragonClaw{ "Dragon Claw", 80, 100, 14, true };
Move slash{ "Slash", 70, 100, 0, true };
Move psychic{ "Psychic", 90, 100, 10, false };
Move thunderPunch{ "Thunder Punch", 75, 100, 3, true };
Move icePunch{ "Ice Punch", 75, 100, 5, true };
Move energyBall{ "Energy Ball", 90, 100, 4, false };
Move bubbleBeam{ "Bubblebeam", 65, 100, 2, false };
Move shadowBall{ "Shadow Ball", 80, 100, 13, false };
Move iceBeam{ "Ice Beam", 90, 100, 5, false };
Move dazzlingGleam{ "Dazzling Gleam", 80, 100, 17, false };
Move dragonRush{ "Dragon Rush", 100, 75, 14, true };
Move earthquake{ "Earthquake", 100, 100, 8, true };
Move brickBreak{ "Brick Break", 75, 100, 6, true };
Move poisonJab{ "Poison Jab", 80, 100, 7, true };
}
main.cpp:
#include "pokemon.h"
#include "movedata.h"
#include <array>
int getInput(int, int);
bool moveHit(int);
int getDamage(Pokemon&, Pokemon&, Move);
int switchPokemon(std::array <Pokemon, 2>&);
bool loseBattle(std::array<Pokemon, 2>&);
void twoFaint(std::array<Pokemon, 2>, std::array<Pokemon, 2>);
void oneFaint(std::array<Pokemon, 2>, std::array<Pokemon, 2>);
void battleStart(std::array<Pokemon, 2>& one, std::array<Pokemon, 2>& two)
{
one[0].printBattleText();
two[0].printBattleText();
std::cout << "Player One, what will you do?\n1.Fight\n2.Pokemon\n";
int playerOneInput{ getInput(1, 2) };
int playerOneInputTwo{};
switch (playerOneInput)
{
case 2:
playerOneInputTwo = switchPokemon(one);
break;
default:
one[0].displayMoves();
playerOneInputTwo = getInput(1, 4);
}
std::cout << "Player Two, what will you do?\n1. Fight\n2. Pokemon\n";
int playerTwoInput{ getInput(1, 2) };
int playerTwoInputTwo{};
switch (playerTwoInput)
{
case 2:
playerTwoInputTwo = switchPokemon(two);
break;
default:
two[0].displayMoves();
playerTwoInputTwo = getInput(1, 4);
}
if (playerOneInput == 1 && playerTwoInput == 1)
{
if (one[0].isFasterThan(two[0]))
{
int damage{ getDamage(one[0], two[0], one[0].moveSelect(playerOneInputTwo)) };
two[0].takeDamage(damage);
if (two[0].hasFainted())
twoFaint(two, one);
int damageSecondRound{ getDamage(two[0], one[0], two[0].moveSelect(playerTwoInputTwo)) };
one[0].takeDamage(damageSecondRound);
if (one[0].hasFainted())
oneFaint(one, two);
battleStart(one, two);
}
else
{
int damage{ getDamage(two[0], one[0], two[0].moveSelect(playerTwoInputTwo)) };
one[0].takeDamage(damage);
if (one[0].hasFainted())
oneFaint(one, two);
int damageSecondRound{ getDamage(one[0], two[0], one[0].moveSelect(playerOneInputTwo)) };
two[0].takeDamage(damageSecondRound);
if (two[0].hasFainted())
twoFaint(two, one);
battleStart(one, two);
}
}
else if (playerOneInput == 1 && playerTwoInput == 2)
{
std::cout << "Player Two withdrew " << two[0].getName() << "! They sent out " << two[playerTwoInputTwo - 1].getName() << "!\n";
std::swap(two[0], two[playerTwoInputTwo - 1]);
int damage{ getDamage(one[0], two[0], one[0].moveSelect(playerOneInputTwo)) };
two[0].takeDamage(damage);
if (two[0].hasFainted())
twoFaint(two, one);
battleStart(one, two);
}
else if (playerOneInput == 2 && playerTwoInput == 1)
{
std::cout << "Player One withdrew " << one[0].getName() << "! They sent out " << one[playerOneInputTwo - 1].getName() << "!\n";
std::swap(one[0], one[playerOneInputTwo - 1]);
int damage{ getDamage(two[0], one[0], two[0].moveSelect(playerTwoInputTwo)) };
one[0].takeDamage(damage);
if (one[0].hasFainted())
oneFaint(one, two);
battleStart(one, two);
}
else
{
std::cout << "Player One withdrew " << one[0].getName() << "! They sent out " << one[playerOneInputTwo - 1].getName() << "!\n";
std::swap(one[0], one[playerOneInputTwo - 1]);
std::cout << "Player Two withdrew " << two[0].getName() << "! They sent out " << two[playerTwoInputTwo - 1].getName() << "!\n";
std::swap(two[0], two[playerTwoInputTwo - 1]);
battleStart(one, two);
}
}
int main()
{
Pokemon Charizard;
Charizard.setName("Charizard");
Charizard.setStats(50, 150, 90, 130, 177, 140, 155);
Charizard.setType(1, 9); //fire, flying
Charizard.setMoves(Moves::flamethrower, Moves::airSlash, Moves::dragonClaw, Moves::slash);
Pokemon Mesprit;
Mesprit.setName("Mesprit");
Mesprit.setStats(50, 155, 157, 157, 157, 157, 140);
Mesprit.setType(10, -1); //psychic
Mesprit.setMoves(Moves::psychic, Moves::icePunch, Moves::thunderPunch, Moves::energyBall);
Pokemon Manaphy;
Manaphy.setName("Manaphy");
Manaphy.setStats(50, 195, 150, 150, 150, 150, 150);
Manaphy.setType(2, -1); //water
Manaphy.setMoves(Moves::bubbleBeam, Moves::dazzlingGleam, Moves::iceBeam, Moves::shadowBall);
Pokemon Garchomp;
Garchomp.setName("Garchomp");
Garchomp.setStats(50, 180, 180, 145, 85, 110, 160);
Garchomp.setType(14, 8); //dragon, ground
Garchomp.setMoves(Moves::brickBreak, Moves::dragonRush, Moves::earthquake, Moves::poisonJab);
std::array<Pokemon, 2> partyOne{ Charizard, Manaphy };
std::array<Pokemon, 2> partyTwo{ Mesprit, Garchomp };
try
{
battleStart(partyOne, partyTwo);
}
catch (const char* winText)
{
std::cout << winText << std::endl;
return EXIT_SUCCESS;
}
}