This course is still being released! Check back later for more chapters.

Get Notified About this Course!

We will send you messages regarding this course only
and nothing else, we promise.
You can unsubscribe anytime by emailing us at:
privacy@symfonycasts.com
Login to bookmark this video
Buy Access to Course
03.

Añadir acciones al juego

|

Share this awesome video!

|

Nuestros inversores nos han pedido una nueva característica: "hacer el juego más interactivo". Esos interesados son tan graciosos... De acuerdo, en lugar de que las batallas se desarrollen automáticamente, quieren que el jugador pueda elegir qué acción realizar al principio de cada turno. Ahora mismo, nuestro juego sólo admite la acción Atacar, así que, para hacerlo más real, también añadiremos un par de acciones más: Curar y Rendirse.

¡Esta es una gran oportunidad para utilizar el patrón de mando!

Aplicar el patrón de comandos

El primer paso es identificar el código que hay que cambiar y encapsularlo en su propia clase comando. Abre GameApplication y busca el método play(). Dentro del bucle while hay un comentario que nos dice dónde empieza el turno del jugador. Selecciona el código justo antes de comprobar si el jugador ha ganado y córtalo, y ya que estamos aquí escribamos el código que queremos. Sabemos que queremos instanciar un objeto AttackCommand y llamar a execute() sobre él, así que vamos a hacerlo. Crea una variable llamada $playerAction y establécela en new AttackCommand(), esta clase aún no existe. A continuación, escribe $playerAction->execute().

Lo siguiente es manejar los argumentos del comando. Para poder atacar necesitamos tanto los objetos carácter como FightResultSet, pero ¿dónde debemos establecerlos, en el constructor o en el método execute? La respuesta puede depender de las necesidades de tu aplicación, pero pasarlos al constructor suele ser mejor idea porque te permite desacoplar la instanciación de la ejecución. Puedes crear tus objetos comando en algún momento, y más tarde, si se cumplen las condiciones, ejecutarlos sin preocuparte de sus argumentos.

187 lines | src/GameApplication.php
// ... lines 1 - 10
class GameApplication
{
// ... lines 13 - 24
public function play(Character $player, Character $ai, FightResultSet $fightResultSet): void
{
while (true) {
// ... lines 28 - 33
// Player's turn
$attackCommand = new AttackCommand($player, $ai, $fightResultSet);
$attackCommand->execute();
// ... lines 37 - 53
}
}
// ... lines 56 - 185
}

¡Muy bien! Vamos a crear esta clase, pulsaré "Opción + Intro", luego seleccionaré Create class. La pondré en el espacio de nombres App\ActionCommand en lugar de simplemente Command porque no queremos confundirlos con los comandos de Symfony. Pulsa intro y, ¡voilá! Ahí tienes nuestra clase AttackCommand. Elimina las anotaciones encima del constructor porque son redundantes. A continuación, dividiré los argumentos en varias líneas y acortaré los espacios de nombres. Mientras lo hago, añadiré private readonly para aprovechar la promoción de propiedades del constructor, y cambiaré el nombre de la variable $ai por $opponent porque tiene más sentido en este contexto.

21 lines | src/ActionCommand/AttackCommand.php
// ... lines 1 - 4
use App\Character\Character;
use App\FightResultSet;
class AttackCommand implements ActionCommandInterface
{
public function __construct(
private readonly Character $player,
private readonly Character $opponent,
private readonly FightResultSet $fightResultSet,
) {
}
// ... lines 16 - 19
}

A continuación, tenemos que implementar el método execute. Escribe public function execute(), ¡y pega! Di que sí para añadir la sentencia import GameApplication, y ahora sólo tenemos que refactorizar las variables locales con $this. Ah, y no olvides cambiar aipor opponent.

36 lines | src/ActionCommand/AttackCommand.php
// ... lines 1 - 17
public function execute(): void
{
$damage = $this->player->attack();
if ($damage === 0) {
GameApplication::$printer->printFor($this->player)->exhaustedMessage();
$this->fightResultSet->of($this->player)->addExhaustedTurn();
return;
}
$damageDealt = $this->opponent->receiveAttack($damage);
$this->fightResultSet->of($this->player)->addDamageDealt($damageDealt);
GameApplication::$printer->printFor($this->player)->attackMessage($damageDealt);
GameApplication::$printer->writeln('');
usleep(300000);
}
// ... lines 35 - 36

¡Perfecto! Nuestro AttackCommand está listo. Ahora, volvamos a GameApplication y haremos lo mismo para el turno de la IA. Desplázate un poco hacia abajo hasta que veas el comentario "Turno de la IA", selecciona todo ese código y sustitúyelo por $aiAction = new AttackCommand() donde el argumento del jugador es $ai, el del adversario es $player y $fightResultSet al final. A continuación, escribe $aiAction->execute().

187 lines | src/GameApplication.php
// ... lines 1 - 10
class GameApplication
{
// ... lines 13 - 24
public function play(Character $player, Character $ai, FightResultSet $fightResultSet): void
{
while (true) {
// ... lines 28 - 42
// AI's turn
$aiAttackCommand = new AttackCommand($ai, $player, $fightResultSet);
$aiAttackCommand->execute();
// ... lines 46 - 53
}
}
// ... lines 56 - 185
}

Uf, ¡por fin! Estamos listos para probarlo. Gira hasta tu terminal y ejecuta:

php bin/console app:game:play

¡Ejecuta! Y... ¡sí! ¡Hemos ganado! ¡Esto es genial! Bueno, no ha cambiado nada, pero está utilizando nuestros comandos bajo el capó.

Estamos listos para añadir más comandos y preguntar al jugador qué acción debe realizar. ¡Eso a continuación!