Match::letTheCombatBegin()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 19
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 11
c 0
b 0
f 0
nc 2
nop 2
dl 0
loc 19
rs 9.9
1
<?php declare(strict_types=1);
2
3
namespace Stratadox\CardGame\Match;
4
5
use DateTimeInterface;
6
use Stratadox\CardGame\DomainEventRecorder;
7
use Stratadox\CardGame\DomainEventRecording;
8
use Stratadox\CardGame\Match\Event\StartedMatchForProposal;
9
use Stratadox\CardGame\Proposal\ProposalId;
10
11
final class Match implements DomainEventRecorder
12
{
13
    use DomainEventRecording;
14
15
    private $id;
16
    private $turn;
17
    private $players;
18
19
    private function __construct(
20
        MatchId $id,
21
        Turn $turn,
22
        Players $players,
23
        MatchEvent ...$events
24
    ) {
25
        $this->id = $id;
26
        $this->turn = $turn;
27
        $this->players = $players;
28
        $this->happened(...$events);
29
    }
30
31
    public static function fromProposal(
32
        MatchId $id,
33
        ProposalId $proposal,
34
        Decks $decks,
35
        DateTimeInterface $startTime
36
    ): self {
37
        return Match::begin(
38
            $id,
39
            new StartedMatchForProposal($id, $proposal),
40
            new Players(
41
                Player::from(0, $decks[0]->cards()),
42
                Player::from(1, $decks[1]->cards())
43
            ),
44
            $startTime
45
        );
46
    }
47
48
    private static function begin(
49
        MatchId $id,
50
        MatchEvent $creationEvent,
51
        Players $players,
52
        DateTimeInterface $startTime
53
    ): Match {
54
        $whoBegins = $players->pickRandom();
55
        $turn = Turn::first($whoBegins, $startTime, $id);
56
        return new Match($id, $turn, $players, $creationEvent, ...$turn->events());
57
    }
58
59
    public function id(): MatchId
60
    {
61
        return $this->id;
62
    }
63
64
    /**
65
     * @throws NotEnoughMana
66
     * @throws NotYourTurn
67
     */
68
    public function playTheCard(
69
        int $cardNumber,
70
        int $player,
71
        DateTimeInterface $when
72
    ): void {
73
        $this->turn->mustAllowCardPlaying($player, $when);
74
75
        $this->players[$player]->playTheCard($cardNumber, $this->id);
76
77
        $this->happened(...$this->players[$player]->domainEvents());
78
        $this->players[$player]->eraseEvents();
79
    }
80
81
    /**
82
     * @throws NoSuchCard
83
     * @throws NotYourTurn
84
     */
85
    public function attackWithCard(
86
        int $cardNumber,
87
        int $playerNumber,
88
        DateTimeInterface $when
89
    ): void {
90
        $this->turn->mustAllowAttacking($playerNumber, $when);
91
92
        $this->players[$playerNumber]->attackWith($cardNumber, $this->id);
93
94
        $this->happened(...$this->players[$playerNumber]->domainEvents());
95
        $this->players[$playerNumber]->eraseEvents();
96
    }
97
98
    /**
99
     * @throws NoSuchCard
100
     * @throws NotYourTurn
101
     */
102
    public function defendAgainst(
103
        int $attacker,
104
        int $defender,
105
        int $defendingPlayer,
106
        DateTimeInterface $when
107
    ): void {
108
        $this->turn->mustAllowDefending($defendingPlayer, $when);
109
110
        $this->players[$defendingPlayer]->defendAgainst($attacker, $defender, $this->id);
111
112
        $this->happened(...$this->players[$defendingPlayer]->domainEvents());
113
        $this->players[$defendingPlayer]->eraseEvents();
114
    }
115
116
    public function drawOpeningHands(): void
117
    {
118
        $this->players->drawOpeningHands($this->id);
119
120
        foreach ($this->players as $player) {
121
            $this->happened(...$player->domainEvents());
122
            $player->eraseEvents();
123
        }
124
    }
125
126
    /** @throws NotYourTurn */
127
    public function endCardPlayingPhaseFor(
128
        int $playerNumber,
129
        DateTimeInterface $when
130
    ): void {
131
        $this->turn = $this->turn->endCardPlayingPhaseFor(
132
            $playerNumber,
133
            $when,
134
            $this->id
135
        );
136
        $this->happened(...$this->turn->events());
137
    }
138
139
    /** @throws NotYourTurn */
140
    public function endTurnOf(int $playerNumber, DateTimeInterface $when): void
141
    {
142
        if ($this->turn->hasNotHadCombatYet()) {
143
            $this->letTheCombatBegin($playerNumber, $when);
144
        }
145
        $this->turn = $this->turn->beginTheTurnOf(
146
            $this->players->after($playerNumber),
147
            $when,
148
            $playerNumber,
149
            $this->players[$playerNumber]->hasAttackingUnits(),
150
            $this->id
151
        );
152
        $this->happened(...$this->turn->events());
153
    }
154
155
    /** @throws NotYourTurn */
156
    public function letTheCombatBegin(int $defender, DateTimeInterface $when): void
157
    {
158
        $this->turn->mustAllowStartingCombat($defender);
159
160
        $this->players[$defender]->counterTheAttackersOf(
161
            $this->players->after($defender),
162
            $this->id,
163
            $this->players[$this->players->after($defender)]->attackers()
164
        );
165
166
        // @todo let non-countered units damage defender
167
168
        foreach ($this->players as $player) {
169
            $player->regroupSurvivingUnits($this->id);
170
            $this->happened(...$player->domainEvents());
171
            $player->eraseEvents();
172
        }
173
        $this->turn = $this->turn->startPlayPhase($when, $this->id);
174
        $this->happened(...$this->turn->events());
175
    }
176
177
    public function endExpiredTurnOrPhase(DateTimeInterface $already): void
178
    {
179
        if ($this->turn->hasExpired($already)) {
180
            try {
181
                $this->turn = $this->turn->endExpiredPhase($already, $this->id);
182
            } catch (NeedCombatFirst $cannotJustSwitch) {
183
                $this->letTheCombatBegin($this->turn->currentPlayer(), $already);
184
            } catch (NoNextPhase $available) {
185
                $this->endTurnOf($this->turn->currentPlayer(), $already);
186
            }
187
            $this->happened(...$this->turn->events());
188
        }
189
    }
190
}
191