Симулятор битвы покемонов

Начал изучать C ++ несколько недель назад, пытался точно смоделировать битву покемонов. Не слишком оптимистично, как это получилось, скажите, пожалуйста, как я могу это улучшить.

Реализован только самый минимум (урон, точность, эффективность типа), погодные условия и т. Д. — нет.

types.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);
    }
}

moveata.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.Fightn2.Pokemonn";
    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. Fightn2. Pokemonn";
    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;
    }
    
}

1 ответ
1

Здравствуйте, я думаю, вам следует читать данные из файла, а не помещать их в код, чтобы вы могли настроить баланс без перекомпиляции, если вы опасаетесь недостаточной читабельности кода, просто добавьте один комментарий, где это уместно.

Вы также можете использовать Enum для типа покемонов. Меня немного смутил Move, так как я думал, что это движение (на самом деле это атака или способность).

То же самое для одного и двух, почему бы не только игрокам[i]?

некоторое упрощение:

    void Pokemon::takeDamage(int damage)
{
    if (damage == -1)
        std::cout << name << " avoided the attack!n";
    
    else
    {
        hp -= damage;
        std::cout << name << " took " << damage << " damage!n n";
        if (hp<0) {hp = 0;}
    }
}

Вам, вероятно, следует создать класс игрока с собственным покемоном (и объектом?)

И последнее:

try … catch существует для обработки исключений, не совсем для идеального завершения программы.

Я не тестировал вашу программу, запустив ее!

  • 1

    {} после того, как переменная инициализируется по умолчанию. Сегодня в C ++ считается золотым стандартом инициализация все переменные или — что еще лучше — присвоить им в объявлении.

    — Аганджу

  • Я годами не проверял код на C ++, я впервые это вижу, использовался для var = 0 / NULL

    — джолраэль

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *