Skip to content

🎮 RPG Class System Project

Mở rộng RPG Character từ Part 1 với InheritancePolymorphism — tạo hệ thống Warrior, Mage, Rogue với abilities riêng biệt.

Project Requirements

Xây dựng hệ thống class cho RPG game:

┌─────────────────────────────────────────────────────────────────┐
│                    RPG CLASS HIERARCHY                          │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│                       ┌─────────────┐                           │
│                       │  Character  │ (Abstract Base)           │
│                       │  ───────────│                           │
│                       │  name_      │                           │
│                       │  health_    │                           │
│                       │  mana_      │                           │
│                       └──────┬──────┘                           │
│                              │                                  │
│         ┌────────────────────┼────────────────────┐             │
│         ▼                    ▼                    ▼             │
│  ┌─────────────┐      ┌─────────────┐      ┌─────────────┐     │
│  │   Warrior   │      │    Mage     │      │    Rogue    │     │
│  │ ─────────── │      │ ─────────── │      │ ─────────── │     │
│  │ rage_       │      │ spellPower_ │      │ stealth_    │     │
│  │ shield_     │      │             │      │ critChance_ │     │
│  └─────────────┘      └─────────────┘      └─────────────┘     │
│        │                                          │             │
│        ▼                                          ▼             │
│  ┌─────────────┐                           ┌─────────────┐     │
│  │  Berserker  │                           │   Assassin  │     │
│  │ (bonus rage)│                           │ (insta-kill)│     │
│  └─────────────┘                           └─────────────┘     │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Bước 1: Abstract Base Class

cpp
#ifndef CHARACTER_HPP
#define CHARACTER_HPP

#include <string>
#include <iostream>
#include <memory>

class Character {
protected:
    std::string name_;
    int health_;
    int maxHealth_;
    int mana_;
    int maxMana_;
    int level_;
    
public:
    Character(const std::string& name, int maxHealth, int maxMana)
        : name_(name),
          health_(maxHealth),
          maxHealth_(maxHealth),
          mana_(maxMana),
          maxMana_(maxMana),
          level_(1) {}
    
    virtual ~Character() = default;
    
    // === Pure Virtual — MUST implement ===
    virtual void attack(Character& target) = 0;
    virtual void specialAbility(Character& target) = 0;
    virtual std::string getClassName() const = 0;
    
    // === Virtual — CAN override ===
    virtual void takeDamage(int damage) {
        health_ = std::max(0, health_ - damage);
        std::cout << name_ << " takes " << damage << " damage! HP: " 
                  << health_ << "/" << maxHealth_ << std::endl;
        
        if (!isAlive()) {
            std::cout << "💀 " << name_ << " has been defeated!" << std::endl;
        }
    }
    
    virtual void heal(int amount) {
        int oldHealth = health_;
        health_ = std::min(maxHealth_, health_ + amount);
        std::cout << name_ << " heals " << (health_ - oldHealth) << " HP!" << std::endl;
    }
    
    virtual void displayStats() const {
        std::cout << "╔══════════════════════════════╗\n";
        std::cout << "" << name_ << " [" << getClassName() << "] Lv." << level_ << "\n";
        std::cout << "║ HP: " << health_ << "/" << maxHealth_ << "\n";
        std::cout << "║ MP: " << mana_ << "/" << maxMana_ << "\n";
        std::cout << "╚══════════════════════════════╝\n";
    }
    
    // === Getters ===
    bool isAlive() const { return health_ > 0; }
    std::string getName() const { return name_; }
    int getHealth() const { return health_; }
    int getMana() const { return mana_; }
    
protected:
    bool useMana(int cost) {
        if (mana_ >= cost) {
            mana_ -= cost;
            return true;
        }
        std::cout << name_ << " not enough mana! (" << mana_ << "/" << cost << ")\n";
        return false;
    }
};

#endif

Bước 2: Warrior Class

cpp
#ifndef WARRIOR_HPP
#define WARRIOR_HPP

#include "Character.hpp"

class Warrior : public Character {
protected:
    int rage_ = 0;
    int armor_;
    int attackPower_;
    
public:
    Warrior(const std::string& name)
        : Character(name, 150, 30),  // High HP, Low Mana
          armor_(10),
          attackPower_(20) {}
    
    std::string getClassName() const override {
        return "Warrior";
    }
    
    void attack(Character& target) override {
        std::cout << "⚔️ " << name_ << " slashes at " << target.getName() << "!\n";
        target.takeDamage(attackPower_);
        rage_ = std::min(100, rage_ + 10);  // Build rage
    }
    
    // Special: Shield Bash (costs rage, not mana)
    void specialAbility(Character& target) override {
        if (rage_ >= 30) {
            rage_ -= 30;
            int damage = attackPower_ + 15;
            std::cout << "🛡️ " << name_ << " SHIELD BASH!\n";
            target.takeDamage(damage);
        } else {
            std::cout << name_ << " not enough rage! (" << rage_ << "/30)\n";
        }
    }
    
    // Override takeDamage — armor reduction
    void takeDamage(int damage) override {
        int reducedDamage = std::max(1, damage - armor_);
        Character::takeDamage(reducedDamage);
        rage_ = std::min(100, rage_ + 5);  // Gain rage when hit
    }
    
    void displayStats() const override {
        Character::displayStats();
        std::cout << "║ Rage: " << rage_ << "/100 | Armor: " << armor_ << "\n";
        std::cout << "╚══════════════════════════════╝\n";
    }
};

#endif

Bước 3: Mage Class

cpp
#ifndef MAGE_HPP
#define MAGE_HPP

#include "Character.hpp"

class Mage : public Character {
private:
    int spellPower_;
    
public:
    Mage(const std::string& name)
        : Character(name, 80, 150),  // Low HP, High Mana
          spellPower_(30) {}
    
    std::string getClassName() const override {
        return "Mage";
    }
    
    void attack(Character& target) override {
        std::cout << "" << name_ << " casts Magic Missile at " 
                  << target.getName() << "!\n";
        if (useMana(10)) {
            target.takeDamage(spellPower_);
        }
    }
    
    // Special: Fireball (high damage, high cost)
    void specialAbility(Character& target) override {
        const int cost = 40;
        if (useMana(cost)) {
            int damage = spellPower_ * 2;
            std::cout << "🔥 " << name_ << " casts FIREBALL!\n";
            target.takeDamage(damage);
        }
    }
    
    // Mage-specific spell
    void blizzard(std::vector<Character*>& targets) {
        const int cost = 60;
        if (useMana(cost)) {
            std::cout << "❄️ " << name_ << " casts BLIZZARD!\n";
            for (auto* target : targets) {
                if (target->isAlive() && target != this) {
                    target->takeDamage(spellPower_);
                }
            }
        }
    }
    
    void displayStats() const override {
        Character::displayStats();
        std::cout << "║ Spell Power: " << spellPower_ << "\n";
        std::cout << "╚══════════════════════════════╝\n";
    }
};

#endif

Bước 4: Rogue Class

cpp
#ifndef ROGUE_HPP
#define ROGUE_HPP

#include "Character.hpp"
#include <random>

class Rogue : public Character {
private:
    int attackPower_;
    int critChance_;  // Percentage (0-100)
    bool stealthed_ = false;
    
    bool rollCrit() const {
        static std::random_device rd;
        static std::mt19937 gen(rd());
        std::uniform_int_distribution<> dis(1, 100);
        return dis(gen) <= critChance_;
    }
    
public:
    Rogue(const std::string& name)
        : Character(name, 100, 80),
          attackPower_(25),
          critChance_(30) {}
    
    std::string getClassName() const override {
        return "Rogue";
    }
    
    void attack(Character& target) override {
        int damage = attackPower_;
        
        if (stealthed_) {
            damage *= 2;  // Stealth bonus
            stealthed_ = false;
            std::cout << "🗡️ " << name_ << " BACKSTAB from stealth!\n";
        } else if (rollCrit()) {
            damage = static_cast<int>(damage * 1.5);
            std::cout << "💥 CRITICAL HIT! " << name_ << " strikes " 
                      << target.getName() << "!\n";
        } else {
            std::cout << "🗡️ " << name_ << " stabs " << target.getName() << "!\n";
        }
        
        target.takeDamage(damage);
    }
    
    // Special: Vanish (enter stealth)
    void specialAbility(Character& target) override {
        if (useMana(30)) {
            stealthed_ = true;
            std::cout << "💨 " << name_ << " vanishes into the shadows...\n";
        }
    }
    
    void displayStats() const override {
        Character::displayStats();
        std::cout << "║ Crit Chance: " << critChance_ << "% | Stealth: " 
                  << (stealthed_ ? "Yes" : "No") << "\n";
        std::cout << "╚══════════════════════════════╝\n";
    }
};

#endif

Bước 5: Main Game Loop

cpp
#include <iostream>
#include <vector>
#include <memory>
#include "Warrior.hpp"
#include "Mage.hpp"
#include "Rogue.hpp"

int main() {
    std::cout << "=== RPG Class System Demo ===\n\n";
    
    // Create party (polymorphic!)
    std::vector<std::unique_ptr<Character>> party;
    party.push_back(std::make_unique<Warrior>("Conan"));
    party.push_back(std::make_unique<Mage>("Gandalf"));
    party.push_back(std::make_unique<Rogue>("Ezio"));
    
    // Create enemies
    std::vector<std::unique_ptr<Character>> enemies;
    enemies.push_back(std::make_unique<Warrior>("Orc Grunt"));
    enemies.push_back(std::make_unique<Mage>("Dark Wizard"));
    
    // Display all stats
    std::cout << ">>> YOUR PARTY <<<\n";
    for (const auto& member : party) {
        member->displayStats();
    }
    
    std::cout << "\n>>> ENEMIES <<<\n";
    for (const auto& enemy : enemies) {
        enemy->displayStats();
    }
    
    std::cout << "\n>>> BATTLE BEGINS! <<<\n\n";
    
    // Battle simulation
    party[0]->attack(*enemies[0]);           // Warrior attacks
    party[1]->attack(*enemies[1]);           // Mage attacks
    party[2]->specialAbility(*enemies[0]);   // Rogue goes stealth
    party[2]->attack(*enemies[0]);           // Rogue backstabs!
    
    enemies[0]->attack(*party[0]);           // Enemy attacks (reduced by armor!)
    
    party[0]->specialAbility(*enemies[0]);   // Warrior shield bash (need rage)
    party[1]->specialAbility(*enemies[0]);   // Mage fireball!
    
    std::cout << "\n>>> BATTLE RESULTS <<<\n";
    for (const auto& enemy : enemies) {
        enemy->displayStats();
    }
    
    return 0;
}

Sample Output

=== RPG Class System Demo ===

>>> YOUR PARTY <<<
╔══════════════════════════════╗
║ Conan [Warrior] Lv.1
║ HP: 150/150
║ MP: 30/30
║ Rage: 0/100 | Armor: 10
╚══════════════════════════════╝
...

>>> BATTLE BEGINS! <<<

⚔️ Conan slashes at Orc Grunt!
Orc Grunt takes 20 damage! HP: 130/150
✨ Gandalf casts Magic Missile at Dark Wizard!
Dark Wizard takes 30 damage! HP: 50/80
💨 Ezio vanishes into the shadows...
🗡️ Ezio BACKSTAB from stealth!
Orc Grunt takes 50 damage! HP: 80/150
⚔️ Orc Grunt slashes at Conan!
Conan takes 10 damage! HP: 140/150  ← Armor reduced 20 → 10!
Conan not enough rage! (10/30)
🔥 Gandalf casts FIREBALL!
Orc Grunt takes 60 damage! HP: 20/150

Mở rộng (Bài tập)

💡 Thử thách 1: Thêm Berserker (subclass of Warrior)
cpp
class Berserker : public Warrior {
private:
    bool enraged_ = false;
    
public:
    Berserker(const std::string& name) : Warrior(name) {}
    
    std::string getClassName() const override {
        return "Berserker";
    }
    
    void attack(Character& target) override {
        int damage = attackPower_;
        if (health_ < maxHealth_ / 2) {
            damage *= 2;  // Double damage when low HP!
            std::cout << "😡 " << name_ << " is ENRAGED!\n";
        }
        target.takeDamage(damage);
    }
};
💡 Thử thách 2: Implement Equipment System
cpp
class IEquipment {
public:
    virtual int getAttackBonus() const = 0;
    virtual int getDefenseBonus() const = 0;
    virtual ~IEquipment() = default;
};

class Sword : public IEquipment {
    int damage_;
public:
    explicit Sword(int dmg) : damage_(dmg) {}
    int getAttackBonus() const override { return damage_; }
    int getDefenseBonus() const override { return 0; }
};

📚 Tổng kết

Trong project này, chúng ta đã sử dụng:

OOP ConceptApplication
Abstract classCharacter base class
Pure virtualattack(), specialAbility(), getClassName()
Virtual overridetakeDamage(), displayStats()
Polymorphismstd::vector<unique_ptr<Character>>
InheritanceWarrior, Mage, Rogue → Character
Protected membersname_, health_, useMana()

🎉 Hoàn thành Part 3: OOP!

Bạn đã nắm vững:

  • ✅ Part 1: Encapsulation (Classes, Access Modifiers, this, Member Init)
  • ✅ Part 2: Inheritance (Virtual, Polymorphism, Abstract Classes)
  • ✅ Practical Projects (RPG Character, RPG Class System)

Tiếp theo: Module 4 — Modern C++ (Smart Pointers, Move Semantics, Lambdas)