Menu
Coddy logo textTech

Разработка игрового персонажа

Часть раздела Объектно-ориентированное программирование путешествия по PHP на Coddy — урок 90 из 91.

challenge icon

Задание

Легко

Давайте создадим систему Разработки игровых персонажей, в которой различные классы персонажей сражаются, используя уникальные способности. Вы создадите иерархию персонажей с системой опыта и уровней, демонстрируя, как наследование, интерфейсы, трейты и инкапсуляция работают вместе в игровом контексте.

Вы организуете свой код в шести файлах:

  • Ability.php — Определите интерфейс Ability с двумя методами: getName(): string и use(Character $target): string. Этот контракт гарантирует, что все способности можно использовать единообразно, независимо от их конкретных эффектов.
  • CombatLogger.php — Создайте трейт CombatLogger, который предоставляет метод logAction(string $action): string. Этот метод должен возвращать строку действия с префиксом "[COMBAT] ". Персонажи будут использовать этот трейт для единообразного форматирования своих боевых сообщений.
  • Character.php — Подключите файлы Ability и CombatLogger. Создайте абстрактный класс Character, который использует трейт CombatLogger. Используйте продвижение свойств в конструкторе (constructor promotion) для name (string, public readonly). Добавьте приватные свойства для health (int, начальное значение 100), level (int, начальное значение 1) и experience (int, начальное значение 0). Реализуйте:
    • getHealth(): int и getLevel(): int — геттеры для приватных свойств
    • takeDamage(int $amount): void — уменьшает здоровье, но не ниже 0
    • heal(int $amount): void — увеличивает здоровье, но не выше 100
    • gainExperience(int $xp): string — добавляет XP; если опыт достигает 100 или более, уровень повышается (сбросьте XP до 0, увеличьте уровень, восстановите 20 HP), верните "{$this->name} leveled up to {$this->level}!"; в противном случае верните "{$this->name} gained {$xp} XP"
    • isAlive(): bool — возвращает true, если здоровье больше 0
    • abstract getSpecialAbility(): Ability — каждый тип персонажа предоставляет свою уникальную способность
    • attack(Character $target): string — наносит 10 единиц урона цели, использует logAction() для возврата "[COMBAT] {$this->name} attacks {$target->name} for 10 damage"
  • Warrior.php — Подключите файл Character. Создайте класс Warrior, расширяющий Character. Реализуйте getSpecialAbility(), чтобы возвращать анонимный класс, реализующий Ability:
    • getName() возвращает "Shield Bash"
    • use() наносит 25 единиц урона и возвращает "[COMBAT] Shield Bash hits {$target->name} for 25 damage!" (используйте logAction() трейта)
  • Mage.php — Подключите файл Character. Создайте класс Mage, расширяющий Character. Реализуйте getSpecialAbility(), чтобы возвращать анонимный класс, реализующий Ability:
    • getName() возвращает "Fireball"
    • use() наносит 35 единиц урона и возвращает "[COMBAT] Fireball engulfs {$target->name} for 35 damage!"
  • main.php — Подключите файлы Warrior и Mage. Вы получите два входных значения: данные персонажей (JSON) и последовательность боевых действий (JSON).

    Формат JSON персонажей:

    [{"type": "warrior", "name": "Thorin"}, {"type": "mage", "name": "Gandalf"}]

    Формат JSON действий:

    [{"action": "attack", "attacker": 0, "target": 1}, {"action": "special", "attacker": 1, "target": 0}, {"action": "xp", "character": 0, "amount": 100}, {"action": "status", "character": 0}]

    Создайте всех персонажей и сохраните их в массиве (индекс 0, 1 и т. д.). Обработайте каждое действие:

    • "attack" — Персонаж по индексу атакующего атакует цель; выведите результат
    • "special" — Персонаж использует свою специальную способность на цели; выведите результат
    • "xp" — Персонаж получает опыт; выведите результат
    • "heal" — Персонаж лечится на указанную величину; выведите "{$character->name} healed to {$character->getHealth()} HP"
    • "status" — Выведите "{$character->name} - Level {$character->getLevel()}, HP: {$character->getHealth()}, Alive: {$alive}" (где $alive это 'Yes' или 'No')

    Выводите каждый результат на новой строке.

Эта система демонстрирует, как абстрактные классы определяют общее поведение персонажей, интерфейсы гарантируют, что способности следуют единому контракту, трейты обеспечивают повторно используемую функциональность логирования, а анонимные классы создают уникальные реализации способностей, не загромождая кодовую базу одноразовыми классами.

Попробуйте сами

<?php
require_once 'Warrior.php';
require_once 'Mage.php';

// Чтение входных данных
$charactersJson = trim(fgets(STDIN));
$actionsJson = trim(fgets(STDIN));

// Парсинг JSON-данных
$charactersData = (array)json_decode($charactersJson, true);
$actionsData = (array)json_decode($actionsJson, true);

// TODO: Создать массив для хранения объектов персонажей
$characters = [];

// TODO: Пройти циклом по $charactersData и создать объекты Warrior или Mage
// на основе поля "type", сохранить их в массив $characters

// TODO: Обработать каждое действие в $actionsData
// Обработать следующие типы действий:
// - "attack": Персонаж по индексу атакующего атакует цель; вывести результат
// - "special": Персонаж использует getSpecialAbility()->use() на цель; вывести результат
// - "xp": Персонаж получает опыт; вывести результат
// - "heal": Персонаж лечится; вывести "[name] healed to [health] HP"
// - "status": Вывести "[name] - Level [level], HP: [health], Alive: [Yes/No]"

?>

Все уроки раздела Объектно-ориентированное программирование