Fork Copy #include #include #include #include #include #include // === Cấu hình Game === const int WINDOW_WIDTH = 800; const int WINDOW_HEIGHT = 600; // === Base Character Class === class Character { protected: std::string name; int hp; int maxHp; int atk; public: Character(std::string name, int hp, int atk) : name(name), hp(hp), maxHp(hp), atk(atk) {} virtual ~Character() {} virtual std::string getName() const { return name; } virtual int getHP() const { return hp; } virtual int getMaxHP() const { return maxHp; } virtual int getATK() const { return atk; } virtual void takeDamage(int dmg) { hp -= dmg; if (hp < 0) hp = 0; } virtual bool isDead() const { return hp <= 0; } virtual void heal(int amount) { hp += amount; if (hp > maxHp) hp = maxHp; } }; // === Player Class === class Player : public Character { public: Player() : Character("Player", 100, 20) {} }; // === Enemy Base Class === class Enemy : public Character { public: Enemy(std::string name, int hp, int atk) : Character(name, hp, atk) {} }; // === Enemy Subclasses === class Slime : public Enemy { public: Slime() : Enemy("Slime", 30, 10) {} }; class Wolf : public Enemy { public: Wolf() : Enemy("Wolf", 50, 15) {} }; class Golem : public Enemy { public: Golem() : Enemy("Golem", 80, 10) {} }; // === Game Class === class Game { private: sf::RenderWindow window; sf::Font font; sf::Text playerStatus, enemyStatus, logText, instructionText, gameOverText; Player player; Enemy* currentEnemy; std::vector log; public: Game() : window(sf::VideoMode(WINDOW_WIDTH, WINDOW_HEIGHT), "Warrior vs Monsters"), currentEnemy(NULL) { srand(static_cast(time(0))); if (!font.loadFromFile("arial.ttf")) { std::cerr << "Font load failed\n"; } playerStatus.setFont(font); playerStatus.setCharacterSize(20); playerStatus.setPosition(20, 20); playerStatus.setFillColor(sf::Color::Green); enemyStatus.setFont(font); enemyStatus.setCharacterSize(20); enemyStatus.setPosition(20, 60); enemyStatus.setFillColor(sf::Color::Red); instructionText.setFont(font); instructionText.setCharacterSize(18); instructionText.setPosition(20, 500); instructionText.setFillColor(sf::Color::White); instructionText.setString("Press A to Attack | D to Defend | H to Heal"); logText.setFont(font); logText.setCharacterSize(16); logText.setPosition(20, 120); logText.setFillColor(sf::Color::White); gameOverText.setFont(font); gameOverText.setCharacterSize(30); gameOverText.setPosition(200, 250); gameOverText.setFillColor(sf::Color::Red); gameOverText.setString("Game Over! Press ESC to Exit"); spawnEnemy(); } void spawnEnemy() { if (currentEnemy != NULL) { delete currentEnemy; currentEnemy = NULL; } int r = rand() % 3; if (r == 0) currentEnemy = new Slime(); else if (r == 1) currentEnemy = new Wolf(); else currentEnemy = new Golem(); log.push_back("A wild " + currentEnemy->getName() + " appears!"); } void updateStatusText() { playerStatus.setString("Player HP: " + intToStr(player.getHP()) + "/" + intToStr(player.getMaxHP())); enemyStatus.setString(currentEnemy->getName() + " HP: " + intToStr(currentEnemy->getHP()) + "/" + intToStr(currentEnemy->getMaxHP())); std::ostringstream oss; int maxLines = 10; int start = log.size() > maxLines ? (log.size() - maxLines) : 0; for (int i = start; i < (int)log.size(); ++i) { oss << log[i] << "\n"; } logText.setString(oss.str()); } std::string intToStr(int x) { std::ostringstream ss; ss << x; return ss.str(); } void playerAttack() { currentEnemy->takeDamage(player.getATK()); log.push_back("Player attacks " + currentEnemy->getName() + " for " + intToStr(player.getATK()) + " damage."); } void enemyAttack() { player.takeDamage(currentEnemy->getATK()); log.push_back(currentEnemy->getName() + " attacks you for " + intToStr(currentEnemy->getATK()) + " damage."); } void run() { while (window.isOpen()) { sf::Event event; while (window.pollEvent(event)) { if (event.type == sf::Event::Closed) window.close(); if (event.type == sf::Event::KeyPressed && !player.isDead()) { if (event.key.code == sf::Keyboard::A) { playerAttack(); if (!currentEnemy->isDead()) enemyAttack(); } else if (event.key.code == sf::Keyboard::H) { player.heal(15); log.push_back("Player heals 15 HP."); enemyAttack(); } else if (event.key.code == sf::Keyboard::D) { int reduced = currentEnemy->getATK() / 2; player.takeDamage(reduced); log.push_back("Player defends. Damage reduced to " + intToStr(reduced)); } } if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape) { window.close(); } } if (currentEnemy->isDead()) { log.push_back(currentEnemy->getName() + " is defeated!"); spawnEnemy(); } updateStatusText(); window.clear(); window.draw(playerStatus); window.draw(enemyStatus); window.draw(instructionText); window.draw(logText); if (player.isDead()) { window.draw(gameOverText); } window.display(); } if (currentEnemy != NULL) { delete currentEnemy; currentEnemy = NULL; } } }; // === main() === int main() { Game game; game.run(); return 0; }