Passed
Push — master ( 49c69a...8b031e )
by Paweł
04:46
created

Fight::findOpponent()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
eloc 5
c 0
b 0
f 0
dl 0
loc 9
ccs 0
cts 6
cp 0
rs 10
cc 3
nc 3
nop 1
crap 12
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\Usable;
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
    private IntegerValue $round;
20
21
    public function __construct(
22
        private Player $player,
23
        private FighterCollection $opponents,
24
        private PlayerAction $playerAction,
25
    ) {
26
        $this->round = new IntegerValue(1);
27
    }
28
29
    public function __invoke(): void
30
    {
31
        while ($this->opponents->count() > 0) {
32
            $fighters = new FighterCollection(
33
                array_merge([$this->player], $this->opponents->getItems()),
34
            );
35
36
            $this->playerAction->newRound("Round {$this->round}:");
37
            $this->playerAction->list(map(
38
                static fn(Fighter $fighter) => [$fighter->getName() => "{$fighter->getHealth()} health"],
39
                $fighters,
40
            ));
41
42
            try {
43
                foreach ($fighters->order() as $fighter) {
44
                    $this->action($fighter);
45
                }
46
            } catch (IntegerValueException $exception) {
47
                $isDead = static fn(Fighter $fighter): bool => $fighter->getHealth()->isLowerThan(new Health(1));
48
49
                if ($isDead($this->player)) {
50
                    throw PlayerException::death();
51
                }
52
53
                $this->opponents = $this->opponents->filter(not($isDead));
54
            }
55
56
            $this->round->increment();
57
        }
58
    }
59
60
    private function action(Fighter $fighter): void
61
    {
62
        if ($fighter instanceof Player) {
63
            $action = $this->askForAction();
64
65
            if ($action instanceof Usable) {
66
                $action->use($fighter, $this->playerAction);
67
                return;
68
            }
69
        } else {
70
            $action = $fighter->getTalents()->filterAttacks()->getIterator()->current();
71
        }
72
73
        if ($action instanceof EtherumAttack) {
74
            $fighter->getEtherum()->decreaseBy($action::getEtherumCost());
75
        }
76
77
        AttackAction::invoke($fighter, $this->findOpponent($fighter), $action, $this->playerAction);
78
    }
79
80
    private function askForAction(): Attack|Usable
81
    {
82
        $action = $this->playerAction->askForChoice(
83
            'Select action',
84
            array_merge($this->player->getTalents()->filterAttacks()->getItems(), ['Go to inventory']),
85
        );
86
87
        if ($action === 'Go to inventory') {
88
            $action = $this->playerAction->askForChoice(
89
                'Select item to use',
90
                array_merge($this->player->getInventory()->filterUsable()->getItems(), ['Back']),
91
            );
92
93
            if ($action === 'Back') {
94
                return $this->askForAction();
95
            }
96
        }
97
98
        return $this->validateAction($action);
99
    }
100
101
    private function validateAction(Attack|Usable $action): Attack|Usable
102
    {
103
        if ($action instanceof MeleeAttack && $this->player->getWeapon() === null) {
104
            $this->playerAction->note('This attack requires weapon equipped.');
105
            return $this->askForAction();
106
        }
107
108
        if ($action instanceof EtherumAttack && $this->player->getEtherum()->isLowerThan($action::getEtherumCost())) {
109
            $this->playerAction->note('You do not posses enough Etherum.');
110
            return $this->askForAction();
111
        }
112
113
        return $action;
114
    }
115
116
    private function findOpponent(Fighter $fighter): Fighter
117
    {
118
        if ($fighter instanceof Player) {
119
            return $this->opponents->count() > 1
120
                ? $this->playerAction->askForChoice('Select opponent to attack', $this->opponents->getItems())
121
                : $this->opponents->getIterator()->current();
122
        }
123
124
        return $this->player;
125
    }
126
}
127