Passed
Push — master ( 82efe3...775557 )
by Paweł
03:11
created

Fight::action()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 26
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 4.0092

Importance

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