Passed
Push — master ( 812b31...82efe3 )
by Paweł
02:52
created

Fight   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 117
Duplicated Lines 0 %

Test Coverage

Coverage 78.18%

Importance

Changes 7
Bugs 0 Features 0
Metric Value
wmc 19
eloc 53
c 7
b 0
f 0
dl 0
loc 117
ccs 43
cts 55
cp 0.7818
rs 10

5 Methods

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