Giao diện
🎮 RPG Class System Project
Mở rộng RPG Character từ Part 1 với Inheritance và Polymorphism — 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;
}
};
#endifBướ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";
}
};
#endifBướ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";
}
};
#endifBướ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";
}
};
#endifBướ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/150Mở 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 Concept | Application |
|---|---|
| Abstract class | Character base class |
| Pure virtual | attack(), specialAbility(), getClassName() |
| Virtual override | takeDamage(), displayStats() |
| Polymorphism | std::vector<unique_ptr<Character>> |
| Inheritance | Warrior, Mage, Rogue → Character |
| Protected members | name_, 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)