Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added api/public/cards/datacenter.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions api/resources/cards_list.php

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

57 changes: 57 additions & 0 deletions api/src/Game/Card/Character/DataCenterCard.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

declare(strict_types=1);

namespace App\Game\Card\Character;

use App\Game\Card\CardHelper;
use App\Game\Card\Interface\TurnAwareInterface;
use App\Game\Card\Trait\BaseOnTurnTrait;
use App\Game\GameContext;
use App\Game\GameUtils;

final class DataCenterCard extends AbstractCharacterCard implements TurnAwareInterface
{
use BaseOnTurnTrait;

private const TURN_DELAY = 1;
private const SPAWN_QUANTITY = 1;

private const SPAWNED_CARD_ID = 'DataMiner';

public function getId(): string
{
return 'DataCenter';
}

public function getHealthPoints(): int
{
return 150;
}

public function getTurnDelay(): int
{
return $this->getValue(self::TURN_DELAY, true);
}

public function getDescription(): string
{
return GameUtils::formatDescription(parent::getDescription(), [
'card' => self::SPAWNED_CARD_ID,
'value' => $this->getValue(self::SPAWN_QUANTITY, true),
'value2' => $this->getTurnDelay(),
]);
}

private function onTurnAction(GameContext $gameContext): void
{
$ownerId = $this->getOwnerId();
if (null === $ownerId) {
return;
}

for ($i = 1; $i <= $this->getValue(self::SPAWN_QUANTITY, true); $i++) {
CardHelper::generatedAndPlay($gameContext, $ownerId, self::SPAWNED_CARD_ID, true);
}
}
}
81 changes: 81 additions & 0 deletions api/src/Game/Card/Monster/DataMinerCard.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?php

namespace App\Game\Card\Monster;

use App\Enum\GameEventTypeEnum;
use App\Game\Card\CardState;
use App\Game\Card\Interface\TurnAwareInterface;
use App\Game\Card\Trait\TurnAwareTrait;
use App\Game\GameContext;
use App\Game\GameUtils;
use App\Game\State\GameEvent;
use Override;

final class DataMinerCard extends AbstractMonsterCard implements TurnAwareInterface
{
use TurnAwareTrait;

private const HEALTH_POINTS = 10;
private const ATTACK = 5;

private int $currentCoins = 0;

public function getId(): string
{
return 'DataMiner';
}

public function getDescription(): string
{
return GameUtils::formatDescription(parent::getDescription(), [
'value' => $this->currentCoins,
'const' => 1,
]);
}

public function getImage(): string
{
return 'https://thumbs.dreamstime.com/z/server-computer-component-database-big-data-storage-cartoon-eyes-mascot-cute-funny-smile-tech-object-vector-drawing-66747778.jpg';
}

public function getBaseAttack(): int
{
return self::ATTACK;
}

public function getHealPoints(): int
{
return self::HEALTH_POINTS;
}

public function onTurnStart(GameEvent $event, GameContext $gameContext): void
{
if (!$this->isOwnerTurn($event)) {
return;
}

$gameContext->addCoins($this->getValue($this->currentCoins, true), $this->getOwnerId());
}

public function onTurnEnd(GameEvent $event, GameContext $gameContext): void
{
if (!$this->isOwnerTurn($event)) {
return;
}

$gameContext->pushGameEvent(GameEventTypeEnum::CARD_STATE_UPDATED, [
'cardId' => $this->getInstanceId(),
'playerId' => $this->getOwnerId(),
'stateToUpdate' => [
'currentCoins' => ++$this->currentCoins,
],
]);
}

public function setState(CardState $state): void
{
parent::setState($state);

$this->currentCoins = (int) ($state->values['currentCoins'] ?? 0);
}
}
125 changes: 125 additions & 0 deletions api/tests/Unit/Game/Card/Character/DataCenterCardTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
<?php

declare(strict_types=1);

namespace App\Tests\Unit\Game\Card\Character;

use App\Enum\GameEventTypeEnum;
use App\Game\Card\CardState;
use App\Game\Card\Character\DataCenterCard;
use App\Service\Game\CardFactoryInterface;
use App\Service\Game\CardIdGeneratorInterface;
use App\Service\Game\CardRegistryInterface;
use App\Service\Game\Helper\CardHelper;
use App\Tests\Unit\Game\Card\CardTestCase;
use App\Tests\Unit\Game\Card\GameUtilsContainerTrait;
use Psr\Container\ContainerInterface;

final class DataCenterCardTest extends CardTestCase
{
use GameUtilsContainerTrait;

protected function setUp(): void
{
parent::setUp();

$spawnedMonster = $this->createStub(\App\Game\Card\Monster\AbstractMonsterCard::class);
$spawnedMonster->method('getHealPoints')->willReturn(42);

$cardFactory = $this->createStub(CardFactoryInterface::class);
$cardFactory->method('create')->willReturn($spawnedMonster);

$cardHelper = new CardHelper($this->createStub(CardRegistryInterface::class), $this->createStub(CardIdGeneratorInterface::class), $cardFactory);

$this->setGameUtilsContainer(new class($cardHelper) implements ContainerInterface {
public function __construct(
private CardHelper $cardHelper,
) {}

public function get(string $id): mixed
{
return 'cards' === $id ? $this->cardHelper : throw new \RuntimeException("Unexpected service \"{$id}\"");
}

public function has(string $id): bool
{
return 'cards' === $id;
}
});
}

protected function tearDown(): void
{
$this->restoreGameUtilsContainer();
parent::tearDown();
}

public function getCardFQCN(): string
{
return DataCenterCard::class;
}

public function testOnTurnStartDoesNothingWhenNotOwnerTurn(): void
{
$card = $this->buildCard(1);
$ctx = $this->createGameContext();

$card->onTurnStart($this->createTurnStartedEvent('2'), $ctx);
$events = $ctx->flushEvents();

self::assertCount(0, $events);
}

public function testOnTurnStartOnlyDecrementsWhenDelayNotReached(): void
{
$card = $this->buildCard(2);
$ctx = $this->createGameContext();

$card->onTurnStart($this->createTurnStartedEvent('1'), $ctx);
$events = $ctx->flushEvents();

self::assertCount(1, $events);
self::assertSame(GameEventTypeEnum::CARD_STATE_UPDATED, $events[0]->type);
self::assertSame(1, $events[0]->data['stateToUpdate']['turnRemainingBeforeAction']);
}

public function testOnTurnStartSpawnsMonsterWhenDelayIsReached(): void
{
$card = $this->buildCard(1);
$ctx = $this->createGameContext();

$card->onTurnStart($this->createTurnStartedEvent('1'), $ctx);
$events = $ctx->flushEvents();

self::assertCount(3, $events);

self::assertSame(GameEventTypeEnum::CARD_GENERATED, $events[0]->type);
self::assertSame('1', $events[0]->data['playerId']);
self::assertSame('DataMiner', $events[0]->data['cardTemplateId']);

self::assertSame(GameEventTypeEnum::CARD_PLACED_IN_MONSTER_AREA, $events[1]->type);
self::assertSame('1', $events[1]->data['playerId']);
self::assertSame(42, $events[1]->data['cardHealthPoints']);

self::assertSame(GameEventTypeEnum::CARD_STATE_UPDATED, $events[2]->type);
self::assertSame(1, $events[2]->data['stateToUpdate']['turnRemainingBeforeAction']);
}

private function buildCard(int $turnRemainingBeforeAction): DataCenterCard
{
$card = new DataCenterCard();
$card->setState(
new CardState(
'test_card',
$card->getId(),
'1',
[],
[
'turnRemainingBeforeAction' => $turnRemainingBeforeAction,
],
),
);

return $card;
}
}
Loading
Loading