Menu
Coddy logo textTech

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

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

challenge icon

Задание

Легко

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

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

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

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

    • "attack": Character с индексом attacker атакует target; выведите результат
    • "special": Character использует свою специальную способность на target; выведите результат
    • "xp": Character получает experience; выведите результат
    • "heal": Character восстанавливает здоровье на указанное количество; выведите "[name] healed to [health] HP"
    • "status": Выведите "[name] - Level [level], HP: [health], Alive: [Yes/No]"

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

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

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

<?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]"

?>

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

Потренируйтесь самостоятельно: Онлайн-компилятор PHP