Login to bookmark this video
Buy Access to Course
04.

Implementing More Actions

|

Share this awesome video!

|

All right! We're ready to add more actions to our game and allow players to choose their actions.

First, we need to create an interface for our commands. To do that, inside the ActionCommand directory, let's create a new PHP file and call it ActionCommandInterface. Inside, we'll add a single method called execute() with no arguments.

// ... lines 1 - 4
interface ActionCommandInterface
{
public function execute(): void;
}

Interface done! Next, let's open AttackCommand and make it implement ActionCommandInterface. The execute() method is already implemented here, so this is ready to go. Nice!

If you downloaded the course code, we can save some time by grabbing the rest of the actions we need in our tutorial directory at the root of our project. Copy the HealCommand and SurrenderCommand files into the ActionCommand directory.

Let's check those out. Inside HealCommand, we can see that it has a constructor that only cares about the player object.

29 lines | src/ActionCommand/HealCommand.php
// ... lines 1 - 4
use App\Character\Character;
// ... lines 6 - 8
class HealCommand implements ActionCommandInterface
{
public function __construct(private readonly Character $player)
{
}
// ... lines 14 - 28
}

And in the execute() method, we have some code that calculates how much damage the player will heal, and then sets the player's health to the new amount (not exceeding their max health). Finally, it prints a message on the screen.

29 lines | src/ActionCommand/HealCommand.php
// ... lines 1 - 14
public function execute(): void
{
$healAmount = Dice::roll(20) + $this->player->getLevel() * 3;
$newAmount = $this->player->getCurrentHealth() + $healAmount;
$newAmount = min($newAmount, $this->player->getMaxHealth());
$this->player->setHealth($newAmount);
$this->player->setStamina(Character::MAX_STAMINA);
GameApplication::$printer->writeln(sprintf(
'You healed %d damage',
$healAmount
));
}

If we take a look at the SurrenderCommand, the constructor here is the same as the one in HealCommand - it only cares about the player object. And in the execute() method, I cheated a little bit because there's no proper way to end a battle, so I just set the player's health to 0. Cool, right?

21 lines | src/ActionCommand/SurrenderCommand.php
// ... lines 1 - 4
use App\Character\Character;
use App\GameApplication;
class SurrenderCommand implements ActionCommandInterface
{
public function __construct(private readonly Character $player)
{
}
public function execute(): void
{
$this->player->setHealth(0);
GameApplication::$printer->block('You\'ve surrendered! Better luck next time!');
}
}

Asking the Player to Choose an Action

All right! It's time to ask the player to choose an action! I'll close a few files first. Okay, head back to the GameApplication... and right before we define $playerAction, write $actionChoice and set it to GameApplication::$printer->choice(), where the question is Your Turn, and the choices are Attack, Heal, and Surrender.

200 lines | src/GameApplication.php
// ... lines 1 - 12
class GameApplication
{
// ... lines 15 - 26
public function play(Character $player, Character $ai, FightResultSet $fightResultSet): void
{
while (true) {
// ... lines 30 - 35
// Player's turn
$actionChoice = GameApplication::$printer->choice('Your turn', [
'Attack',
'Heal',
'Surrender',
]);
// ... lines 42 - 66
}
}
// ... lines 69 - 198
}

Then, we'll replace the AttackCommand instantiation with a match expression, but copy this first, because we'll need it in a moment. Now write match ($actionChoice). Inside, the first case we want to add is Attack, and now... paste. For the second case, write Heal and set it to new HealCommand($player). The third and final case is Surrender, and we'll set that to new SurrenderCommand($player). Perfect!

200 lines | src/GameApplication.php
// ... lines 1 - 26
public function play(Character $player, Character $ai, FightResultSet $fightResultSet): void
{
while (true) {
// ... lines 30 - 42
$playerAction = match ($actionChoice) {
'Attack' => new AttackCommand($player, $ai, $fightResultSet),
'Heal' => new HealCommand($player),
'Surrender' => new SurrenderCommand($player),
};
// ... lines 48 - 66
}
}
// ... lines 69 - 200

Let's give this a try. Spin over to your terminal and run:

php bin/console app:game:play

This time, I'll be a "mage archer", and... look at that! It's asking us what to do! Let's attack first, and... cool! We did 12 points of damage and received 10, so our current health is 40 out of 50. Let's try healing next. And... check it out! We healed 8 points, and the AI did 0 points of damage because we blocked its attack, so our current health is 48. The "heal" action is working as expected! Lastly, let's try to surrender. Choose option 2 and... awesome! We surrendered and lost the match. Giving up isn't something we'd normally celebrate, but in this case, it means our "surrender" action is working like it's supposed to.

High five your rubber duck because we've successfully made our game more interactive! But... wouldn't it be awesome if we could undo our last action if, say... the AI got a little too lucky? That's next!