Menu
Coddy logo textTech

Desarrollo de personajes de videojuegos

Parte de la sección Programación Orientada a Objetos del Journey de PHP de Coddy — lección 90 de 91.

challenge icon

Desafío

Fácil

Vamos a construir un sistema de Desarrollo de Personajes de Juego donde diferentes clases de personajes luchan usando habilidades únicas. Crearás una jerarquía de personajes con un sistema de experiencia y niveles, demostrando cómo la herencia, las interfaces, los traits y el encapsulamiento trabajan juntos en el contexto de un juego.

Organizarás tu código en seis archivos:

  • Ability.php — Define una interfaz Ability con dos métodos: getName(): string y use(Character $target): string. Este contrato asegura que todas las habilidades puedan usarse de manera consistente independientemente de sus efectos específicos.
  • CombatLogger.php — Crea un trait CombatLogger que proporcione un método logAction(string $action): string. Este método debe devolver la cadena de acción con el prefijo "[COMBAT] ". Los personajes usarán este trait para formatear sus mensajes de combate de manera consistente.
  • Character.php — Incluye los archivos Ability y CombatLogger. Crea una clase abstracta Character que use el trait CombatLogger. Usa la promoción de constructor para name (string, public readonly). Incluye propiedades privadas para health (int, comenzando en 100), level (int, comenzando en 1) y experience (int, comenzando en 0). Implementa:
    • getHealth(): int y getLevel(): int — getters para las propiedades privadas
    • takeDamage(int $amount): void — reduce la salud pero nunca por debajo de 0
    • heal(int $amount): void — aumenta la salud pero nunca por encima de 100
    • gainExperience(int $xp): string — añade XP; si la experiencia llega a 100 o más, sube de nivel (reinicia XP a 0, aumenta el nivel, cura 20 HP), devuelve "{$this->name} leveled up to {$this->level}!"; de lo contrario, devuelve "{$this->name} gained {$xp} XP"
    • isAlive(): bool — devuelve true si la salud es mayor que 0
    • abstract getSpecialAbility(): Ability — cada tipo de personaje proporciona su habilidad única
    • attack(Character $target): string — inflige 10 de daño al objetivo, usa logAction() para devolver "[COMBAT] {$this->name} attacks {$target->name} for 10 damage"
  • Warrior.php — Incluye el archivo Character. Crea una clase Warrior que extienda de Character. Implementa getSpecialAbility() para devolver una clase anónima que implemente Ability:
    • getName() devuelve "Shield Bash"
    • use() inflige 25 de daño y devuelve "[COMBAT] Shield Bash hits {$target->name} for 25 damage!" (usa el logAction() del trait)
  • Mage.php — Incluye el archivo Character. Crea una clase Mage que extienda de Character. Implementa getSpecialAbility() para devolver una clase anónima que implemente Ability:
    • getName() devuelve "Fireball"
    • use() inflige 35 de daño y devuelve "[COMBAT] Fireball engulfs {$target->name} for 35 damage!"
  • main.php — Incluye los archivos Warrior y Mage. Recibirás dos entradas: datos de personajes (JSON) y una secuencia de acciones de combate (JSON).

    Formato JSON de personajes:

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

    Formato JSON de acciones:

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

    Crea todos los personajes y almacénalos en un array (índice 0, 1, etc.). Procesa cada acción:

    • "attack" — El personaje en el índice atacante ataca al objetivo; imprime el resultado
    • "special" — El personaje usa su habilidad especial sobre el objetivo; imprime el resultado
    • "xp" — El personaje gana experiencia; imprime el resultado
    • "heal" — El personaje se cura por la cantidad dada; imprime "{$character->name} healed to {$character->getHealth()} HP"
    • "status" — Imprime "{$character->name} - Level {$character->getLevel()}, HP: {$character->getHealth()}, Alive: {$alive}" (donde $alive es 'Yes' o 'No')

    Imprime cada resultado en una línea nueva.

Este sistema demuestra cómo las clases abstractas definen el comportamiento compartido de los personajes, las interfaces aseguran que las habilidades sigan un contrato consistente, los traits proporcionan funcionalidad de registro reutilizable y las clases anónimas crean implementaciones de habilidades únicas sin saturar tu base de código con clases de un solo uso.

Pruébalo tú mismo

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

// Leer la entrada
$charactersJson = trim(fgets(STDIN));
$actionsJson = trim(fgets(STDIN));

// Analizar las entradas JSON
$charactersData = (array)json_decode($charactersJson, true);
$actionsData = (array)json_decode($actionsJson, true);

// TODO: Crear un array para almacenar los objetos de los personajes
$characters = [];

// TODO: Recorrer $charactersData y crear objetos Warrior o Mage
// basándose en el campo "type", almacenarlos en el array $characters

// TODO: Procesar cada acción en $actionsData
// Manejar estos tipos de acciones:
// - "attack": El personaje en el índice del atacante ataca al objetivo; imprimir resultado
// - "special": El personaje usa getSpecialAbility()->use() en el objetivo; imprimir resultado
// - "xp": El personaje gana experiencia; imprimir resultado
// - "heal": El personaje se cura; imprimir "[name] healed to [health] HP"
// - "status": Imprimir "[name] - Level [level], HP: [health], Alive: [Yes/No]"

?>

Todas las lecciones de Programación Orientada a Objetos