Passed
Push — master ( 992f4b...49c69a )
by Paweł
03:39
created

Fight::askForAction()   A

Complexity

Conditions 6
Paths 5

Size

Total Lines 26
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

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