Passed
Push — master ( 0577a9...ac39e5 )
by Paweł
03:28
created

Fight   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 110
Duplicated Lines 0 %

Test Coverage

Coverage 89.47%

Importance

Changes 7
Bugs 0 Features 0
Metric Value
wmc 20
eloc 53
c 7
b 0
f 0
dl 0
loc 110
ccs 51
cts 57
cp 0.8947
rs 10

6 Methods

Rating   Name   Duplication   Size   Complexity  
A findOpponent() 0 7 2
A __invoke() 0 30 5
A validatePlayerAction() 0 15 5
A action() 0 22 4
A askForPlayerAction() 0 19 3
A __construct() 0 4 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace AardsGerds\Game\Fight;
6
7
use AardsGerds\Game\Build\Attribute\Health;
8
use AardsGerds\Game\Inventory\Consumable;
9
use AardsGerds\Game\Player\Player;
10
use AardsGerds\Game\Player\PlayerAction;
11
use AardsGerds\Game\Player\PlayerException;
12
use AardsGerds\Game\Shared\IntegerValue;
13
use AardsGerds\Game\Shared\IntegerValueException;
14
use function Lambdish\Phunctional\map;
15
use function Lambdish\Phunctional\not;
16
17
final class Fight
18
{
19 5
    public function __construct(
20
        private Player $player,
21
        private FighterCollection $opponents,
22 5
    ) {}
23
24 5
    public function __invoke(PlayerAction $playerAction): void
25
    {
26 5
        $round = new IntegerValue(1);
27
28 5
        while ($this->opponents->count() > 0) {
29 5
            $fighters = new FighterCollection(
30 5
                array_merge([$this->player], $this->opponents->getItems()),
31
            );
32
33 5
            $playerAction->section("Round {$round}:");
34 5
            $playerAction->list(map(
35 5
                static fn(Fighter $fighter) => [$fighter->getName() => "{$fighter->getHealth()} health"],
36 5
                $fighters,
37
            ));
38
39
            try {
40 5
                foreach ($fighters->orderByInitiative() as $fighter) {
41 5
                    $this->action($fighter, $playerAction);
42
                }
43 5
            } catch (IntegerValueException) {
44 5
                $isDead = static fn(Fighter $fighter): bool => $fighter->getHealth()->isLowerThan(new Health(1));
45
46 5
                if ($isDead($this->player)) {
47 1
                    throw PlayerException::death();
48
                }
49
50 4
                $this->opponents = $this->opponents->filter(not($isDead));
51
            }
52
53 5
            $round->increment();
54
        }
55 4
    }
56
57 5
    private function action(Fighter $fighter, PlayerAction $playerAction): void
58
    {
59 5
        if ($fighter instanceof Player) {
60 5
            $action = $this->askForPlayerAction($playerAction);
61
62 5
            if ($action instanceof Consumable) {
63 1
                $action->consume($fighter, $playerAction);
64 5
                return;
65
            }
66
        } else {
67 4
            $action = $fighter->getTalents()->randomAttack(); // careful with etherum attacks
68
        }
69
70 5
        if ($action instanceof EtherumAttack) {
71
            $fighter->getEtherum()->decreaseBy($action::getEtherumCost());
72
        }
73
74 5
        Clash::invoke(
75 5
            $fighter,
76 5
            $this->findOpponent($fighter, $playerAction),
77
            $action,
78
            $playerAction,
79
        );
80 4
    }
81
82 5
    private function askForPlayerAction(PlayerAction $playerAction): Attack|Consumable
83
    {
84 5
        $action = $playerAction->askForChoice(
85 5
            'Select action',
86 5
            array_merge($this->player->getTalents()->filterAttacks()->getItems(), ['Go to inventory']),
87
        );
88
89 5
        if ($action === 'Go to inventory') {
90 1
            $action = $playerAction->askForChoice(
91 1
                'Select item to use',
92 1
                array_merge($this->player->getInventory()->filterConsumable()->getItems(), ['Back']),
93
            );
94
95 1
            if ($action === 'Back') {
96
                return $this->askForPlayerAction($playerAction);
97
            }
98
        }
99
100 5
        return $this->validatePlayerAction($action, $playerAction);
101
    }
102
103 5
    private function validatePlayerAction(
104
        Attack|Consumable $action,
105
        PlayerAction $playerAction,
106
    ): Attack|Consumable {
107 5
        if ($action instanceof MeleeAttack && $this->player->getWeapon() === null) {
108
            $playerAction->note('This attack requires weapon equipped.');
109
            return $this->askForPlayerAction($playerAction);
110
        }
111
112 5
        if ($action instanceof EtherumAttack && $this->player->getEtherum()->isLowerThan($action::getEtherumCost())) {
113
            $playerAction->note('You do not posses enough Etherum.');
114
            return $this->askForPlayerAction($playerAction);
115
        }
116
117 5
        return $action;
118
    }
119
120 5
    private function findOpponent(Fighter $fighter, PlayerAction $playerAction): Fighter
121
    {
122
        return match (true) {
123 5
            $fighter instanceof Player && $this->opponents->count() > 1
124 1
                => $playerAction->askForChoice('Select opponent to attack', $this->opponents->getItems()),
125 5
            $fighter instanceof Player => $this->opponents->first(),
126 5
            default => $this->player, // @todo: support player parties
127
        };
128
    }
129
}
130