Skip to content

Repository files navigation

Pokémon Battle Simulator

A quick and passionate project about a battle simulation against a legendary pokemon trainer. You play as Jimmy who has caugt a level 1 Rattata. Now face to face against the almighty legendary pokemon trainer. Can you make sure Jimmy wins?

A C++ battle system implementing the several Programming patterns, while delivering the true OOP.

🎯 Overview

  • State Pattern for turn management
  • Modern C++ features (smart pointers, move semantics)
  • Pimpl Idiom for implementation hiding
  • Polymorphic Move System for dynamic battle effects

🏗️ Architecture

Core Components

Component Purpose Key Features
Battle Manages battle flow State transitions, player/enemy access
BattleState (Base) Interface for all states Enter(), Execute(), HandleInput(), Exit()
PlayerTurnState Handles player moves Move selection UI, damage calculation
EnemyTurnState AI-controlled turns Random move selection, state transition
TurnState Determine which Pokemon to take a turn Takes into consideration if a move had a effect before combat
BattleOverState End-game handling Win/loss detection, battle summary

Key Design Patterns

🔹 State Pattern

  • Encapsulates each battle phase (player turn, enemy turn, etc.)
  • Clean separation of turn logic
  • Easy to extend with new states (e.g., ItemUseState)

🔹 Strategy Pattern (Moves)

  • MoveBase polymorphic interface
  • Concrete moves (Thunderbolt, Psychic) override ApplyEffect()

🔹 Pimpl Idiom

  • Hides Pokémon implementation details
  • Reduces compilation dependencies

Customizing the move set for each individual Pokemon!

Pokemon/Main.cpp

Lines 24 to 42 in b247eb0

std::vector<std::unique_ptr<MoveBase>> mewtwoMoves;
mewtwoMoves.push_back(std::make_unique<Thunderbolt>());
mewtwoMoves.push_back(std::make_unique<IceBeam>());
mewtwoMoves.push_back(std::make_unique<HyperBeam>());
mewtwoMoves.push_back(std::make_unique<Psychic>());
std::unique_ptr<PokemonBase> mewtwo = std::make_unique<Mewtwo>();
mewtwo->LearnMoves(std::move(mewtwoMoves));
std::vector<std::unique_ptr<MoveBase>> rattataMoves;
rattataMoves.push_back(std::make_unique<QuickAttack>());
rattataMoves.push_back(std::make_unique<DoubleTeam>());
rattataMoves.push_back(std::make_unique<Endeavor>());
rattataMoves.push_back(std::make_unique<Hyper_Fang>());
std::unique_ptr<PokemonBase> rattata = std::make_unique<Rattata>();
rattata->LearnMoves(std::move(rattataMoves));
rattata->SetHeldItem("FocusSash");

Pokemon/Main.cpp

Lines 50 to 51 in b247eb0

Battle battle(std::move(rattata), std::move(mewtwo));
battle.Start();

Sorts the turn order if a Pokemon has priority then speed checks

stateDiagram-v2
    direction LR
    [*] --> PlayerTurn: "Battle starts"
    PlayerTurn --> EnemyTurn: "Move selected"
    EnemyTurn --> TurnState: "Move selected"
    
    state TurnState {
        direction LR
        [*] --> DecidingTurnOrder
        DecidingTurnOrder --> PriorityCheck
        PriorityCheck --> SpeedCheck: If equal
    }
    
    
    TurnState --> BattleOccurs
    BattleOccurs --> PlayerTurn : "Everyone survives"
    BattleOccurs --> BattleIsOver : "Someone fainted"
Loading

std::vector<std::pair<std::reference_wrapper<PokemonBase>, std::string>> turnOrder =
{
{player, playerMove},
{opponent, opponentMove}
};
std::sort(turnOrder.begin(), turnOrder.end(), [](const auto& a, const auto& b)
{
if (a.first.get().GetPriority() == b.first.get().GetPriority())
return a.first.get().GetSpeed() > b.first.get().GetSpeed();
if (a.first.get().GetPriority() != b.first.get().GetPriority())
return a.first.get().GetPriority() > b.first.get().GetPriority();
return a.first.get().GetSpeed() > b.first.get().GetSpeed();
});
for (auto& [pokemon, move] : turnOrder)
{
auto& target = (&pokemon.get() == &player) ? opponent : player;
pokemon.get().MoveForCombat(move, target);
if (!target.getIsAlive())
{
battle.SetState(std::make_unique<BattleOverState>());
return;
}
}

Customizable pokemon moves

Context: QuickAttack is a move that priorities to begin first in the turn order, regardless of speed. Endeavor is a move that forces the opponent's health to be the same as the user.

void ApplyEffect(PokemonBase* user, PokemonBase* target) override
{
user->SetPriority(true);
}
int CalculateDamage(PokemonBase* user, PokemonBase* target) override
{
return MoveBase::CalculateDamage(user, target);
}

Pokemon/Move/Endeavor.h

Lines 14 to 27 in b247eb0

int CalculateDamage(PokemonBase* user, PokemonBase* target) override
{
if (target->GetHealth() > user->GetHealth())
{
const int mewtwoHealth = target->GetHealth();
const int rattataHealth = user->GetHealth();
const int healthDiff = mewtwoHealth - rattataHealth;
return target->TakeDamage(healthDiff);
}
else
{
return target->TakeDamage(0);
}

About

A fun simulation Pokemon challenge. Developed with OOP and Game Programming Design Patterns.

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages