Skip to content

🎮 RPG Character Project

Áp dụng mọi kiến thức OOP vào một project thực tế: Xây dựng class RPG Character với Health, Mana, và Attack methods.

Yêu cầu Project

Xây dựng class Character với:

AttributeDescription
name_Tên nhân vật
health_HP (0-maxHealth)
maxHealth_HP tối đa
mana_MP (0-maxMana)
maxMana_MP tối đa
attackPower_Sức tấn công
level_Cấp độ
MethodDescription
attack(Character&)Tấn công enemy
heal(int)Hồi HP
castSpell(Character&)Dùng mana tấn công
takeDamage(int)Nhận damage
isAlive()Check còn sống?
levelUp()Tăng cấp
displayStats()In stats

Bước 1: Class Definition

cpp
#ifndef CHARACTER_HPP
#define CHARACTER_HPP

#include <string>
#include <iostream>
#include <algorithm>  // std::max, std::min

class Character {
private:
    // === Member Variables ===
    std::string name_;
    int health_;
    int maxHealth_;
    int mana_;
    int maxMana_;
    int attackPower_;
    int level_;

public:
    // === Constructor ===
    Character(const std::string& name, 
              int maxHealth = 100, 
              int maxMana = 50,
              int attackPower = 10)
        : name_(name),
          health_(maxHealth),      // Start at full health
          maxHealth_(maxHealth),
          mana_(maxMana),          // Start at full mana
          maxMana_(maxMana),
          attackPower_(attackPower),
          level_(1) {}

    // === Getters (const methods) ===
    std::string getName() const { return name_; }
    int getHealth() const { return health_; }
    int getMaxHealth() const { return maxHealth_; }
    int getMana() const { return mana_; }
    int getMaxMana() const { return maxMana_; }
    int getAttackPower() const { return attackPower_; }
    int getLevel() const { return level_; }
    
    bool isAlive() const { return health_ > 0; }

    // === Combat Methods ===
    void takeDamage(int damage);
    void heal(int amount);
    void attack(Character& target);
    bool castSpell(Character& target, int manaCost, int spellPower);
    
    // === Progression ===
    void levelUp();
    
    // === Display ===
    void displayStats() const;
};

#endif

Bước 2: Method Implementations

cpp
// character.cpp
#include "character.hpp"

void Character::takeDamage(int damage) {
    if (damage < 0) return;  // Ignore negative damage
    
    health_ = std::max(0, health_ - damage);
    
    std::cout << name_ << " takes " << damage << " damage! ";
    std::cout << "HP: " << health_ << "/" << maxHealth_ << std::endl;
    
    if (!isAlive()) {
        std::cout << "💀 " << name_ << " has been defeated!" << std::endl;
    }
}

void Character::heal(int amount) {
    if (amount < 0 || !isAlive()) return;
    
    int oldHealth = health_;
    health_ = std::min(maxHealth_, health_ + amount);
    int actualHeal = health_ - oldHealth;
    
    std::cout << name_ << " heals for " << actualHeal << " HP! ";
    std::cout << "HP: " << health_ << "/" << maxHealth_ << std::endl;
}

void Character::attack(Character& target) {
    if (!isAlive()) {
        std::cout << name_ << " cannot attack (defeated)" << std::endl;
        return;
    }
    
    if (!target.isAlive()) {
        std::cout << target.getName() << " is already defeated!" << std::endl;
        return;
    }
    
    std::cout << "⚔️ " << name_ << " attacks " << target.getName() << "!" << std::endl;
    target.takeDamage(attackPower_);
}

bool Character::castSpell(Character& target, int manaCost, int spellPower) {
    if (!isAlive()) {
        std::cout << name_ << " cannot cast spell (defeated)" << std::endl;
        return false;
    }
    
    if (mana_ < manaCost) {
        std::cout << name_ << " doesn't have enough mana! ";
        std::cout << "(" << mana_ << "/" << manaCost << " required)" << std::endl;
        return false;
    }
    
    mana_ -= manaCost;
    std::cout << "" << name_ << " casts a spell on " << target.getName() << "!" << std::endl;
    std::cout << "   Mana: " << mana_ << "/" << maxMana_ << std::endl;
    target.takeDamage(spellPower);
    
    return true;
}

void Character::levelUp() {
    ++level_;
    
    // Tăng stats khi level up
    int healthBonus = 20;
    int manaBonus = 10;
    int attackBonus = 5;
    
    maxHealth_ += healthBonus;
    health_ = maxHealth_;  // Full heal on level up
    maxMana_ += manaBonus;
    mana_ = maxMana_;      // Full mana restore
    attackPower_ += attackBonus;
    
    std::cout << "🎉 " << name_ << " leveled up to Level " << level_ << "!" << std::endl;
    std::cout << "   Max HP +" << healthBonus << "" << maxHealth_ << std::endl;
    std::cout << "   Max MP +" << manaBonus << "" << maxMana_ << std::endl;
    std::cout << "   Attack +" << attackBonus << "" << attackPower_ << std::endl;
}

void Character::displayStats() const {
    std::cout << "╔════════════════════════════════╗" << std::endl;
    std::cout << "" << name_;
    // Padding
    for (size_t i = name_.length(); i < 24; ++i) std::cout << " ";
    std::cout << "Lv." << level_ << "" << std::endl;
    std::cout << "╠════════════════════════════════╣" << std::endl;
    std::cout << "║ HP: " << health_ << "/" << maxHealth_;
    std::cout << "  MP: " << mana_ << "/" << maxMana_;
    std::cout << "" << std::endl;
    std::cout << "║ Attack Power: " << attackPower_;
    std::cout << "" << std::endl;
    std::cout << "║ Status: " << (isAlive() ? "✅ Alive" : "💀 Dead");
    std::cout << "" << std::endl;
    std::cout << "╚════════════════════════════════╝" << std::endl;
}

Bước 3: Main Program — Game Simulation

cpp
// main.cpp
#include "character.hpp"
#include <iostream>

int main() {
    std::cout << "=== RPG Character Demo ===" << std::endl << std::endl;
    
    // Tạo characters
    Character hero("Aragorn", 120, 60, 15);
    Character enemy("Goblin", 80, 20, 8);
    
    // Display initial stats
    std::cout << ">>> Initial Stats <<<" << std::endl;
    hero.displayStats();
    enemy.displayStats();
    
    std::cout << std::endl << ">>> Battle Begins! <<<" << std::endl << std::endl;
    
    // Combat simulation
    hero.attack(enemy);               // Basic attack
    enemy.attack(hero);               // Enemy attacks back
    
    hero.castSpell(enemy, 20, 25);    // Spell attack (cost 20 mana, 25 damage)
    hero.castSpell(enemy, 20, 25);    // Another spell
    
    enemy.attack(hero);
    
    hero.heal(30);                    // Hero heals
    
    hero.attack(enemy);               // Finish enemy
    hero.attack(enemy);               // Already dead check
    
    std::cout << std::endl << ">>> Battle Results <<<" << std::endl;
    hero.displayStats();
    enemy.displayStats();
    
    // Level up
    std::cout << std::endl << ">>> Victory Rewards <<<" << std::endl;
    hero.levelUp();
    hero.displayStats();
    
    return 0;
}

Sample Output

=== RPG Character Demo ===

>>> Initial Stats <<<
╔════════════════════════════════╗
║ Aragorn                 Lv.1 ║
╠════════════════════════════════╣
║ HP: 120/120  MP: 60/60        ║
║ Attack Power: 15               ║
║ Status: ✅ Alive                ║
╚════════════════════════════════╝
╔════════════════════════════════╗
║ Goblin                  Lv.1 ║
╠════════════════════════════════╣
║ HP: 80/80  MP: 20/20          ║
║ Attack Power: 8                ║
║ Status: ✅ Alive                ║
╚════════════════════════════════╝

>>> Battle Begins! <<<

⚔️ Aragorn attacks Goblin!
Goblin takes 15 damage! HP: 65/80
⚔️ Goblin attacks Aragorn!
Aragorn takes 8 damage! HP: 112/120
✨ Aragorn casts a spell on Goblin!
   Mana: 40/60
Goblin takes 25 damage! HP: 40/80
✨ Aragorn casts a spell on Goblin!
   Mana: 20/60
Goblin takes 25 damage! HP: 15/80
⚔️ Goblin attacks Aragorn!
Aragorn takes 8 damage! HP: 104/120
Aragorn heals for 16 HP! HP: 120/120
⚔️ Aragorn attacks Goblin!
Goblin takes 15 damage! HP: 0/80
💀 Goblin has been defeated!
Goblin is already defeated!

>>> Battle Results <<<
...

>>> Victory Rewards <<<
🎉 Aragorn leveled up to Level 2!
   Max HP +20 → 140
   Max MP +10 → 70
   Attack +5 → 20
...

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

💡 Thử thách 1: Thêm Equipment System
cpp
class Weapon {
private:
    std::string name_;
    int bonusDamage_;
public:
    Weapon(const std::string& name, int damage)
        : name_(name), bonusDamage_(damage) {}
    int getDamage() const { return bonusDamage_; }
};

class Character {
private:
    Weapon* equippedWeapon_ = nullptr;  // Optional weapon
    
public:
    void equip(Weapon* weapon) { equippedWeapon_ = weapon; }
    
    int getTotalAttack() const {
        return attackPower_ + (equippedWeapon_ ? equippedWeapon_->getDamage() : 0);
    }
};
💡 Thử thách 2: Thêm Character Types (Warrior, Mage)
cpp
enum class CharacterClass { Warrior, Mage, Rogue };

class Character {
private:
    CharacterClass charClass_;
    
public:
    Character(const std::string& name, CharacterClass cls)
        : name_(name), charClass_(cls) {
        switch (cls) {
            case CharacterClass::Warrior:
                maxHealth_ = 150; attackPower_ = 20; maxMana_ = 30;
                break;
            case CharacterClass::Mage:
                maxHealth_ = 80; attackPower_ = 8; maxMana_ = 100;
                break;
            case CharacterClass::Rogue:
                maxHealth_ = 100; attackPower_ = 15; maxMana_ = 50;
                break;
        }
        health_ = maxHealth_;
        mana_ = maxMana_;
    }
};
💡 Thử thách 3: Save/Load System
cpp
#include <fstream>

class Character {
public:
    void saveToFile(const std::string& filename) const {
        std::ofstream file(filename);
        file << name_ << "\n"
             << level_ << "\n"
             << health_ << " " << maxHealth_ << "\n"
             << mana_ << " " << maxMana_ << "\n"
             << attackPower_;
    }
    
    static Character loadFromFile(const std::string& filename) {
        std::ifstream file(filename);
        std::string name;
        int level, hp, maxHp, mp, maxMp, atk;
        file >> name >> level >> hp >> maxHp >> mp >> maxMp >> atk;
        // ... construct character from data
    }
};

📚 Tổng kết

Trong project này, chúng ta đã áp dụng:

ConceptApplication
EncapsulationPrivate members, public interface
Data HidingStats ẩn, chỉ access qua methods
ConstructorsInit với default parameters
Member init listEfficient initialization
const methodsgetters, displayStats
this pointer(implicit trong các methods)
ValidationKiểm tra bounds cho HP/MP

🎉 Hoàn thành Module 2: Part 1!

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

  • ✅ Tại sao OOP
  • ✅ Class Anatomy
  • ✅ Access Modifiers
  • ✅ this Pointer
  • ✅ Member Initialization
  • ✅ Practical Project

Tiếp theo: Module 2 Part 2 — Inheritance & Polymorphism →