Passed
Push — master ( e68e96...040eb8 )
by Paweł
03:01
created

Fight::askForAction()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 26
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 14
c 3
b 0
f 0
dl 0
loc 26
ccs 0
cts 15
cp 0
rs 9.4888
cc 5
nc 4
nop 1
crap 30
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
16
final class Fight
17
{
18
    private IntegerValue $round;
19
20
    public function __construct(
21
        private Fighter $player,
22
        private Fighter $opponent,
23
        private PlayerAction $playerAction,
24
    ) {
25
        $this->round = new IntegerValue(1);
26
    }
27
28
    public function __invoke(): void
29
    {
30
        $playerInitialHealth = new Health($this->player->getHealth()->get());
31
32
        while (true) {
33
            $this->playerAction->newRound("Round {$this->round}:");
34
            $actionOrder = ActionOrder::resolve($this->player, $this->opponent);
35
36
            try {
37
                foreach ($actionOrder as $attacker) {
38
                    if ($attacker instanceof Player) {
39
                        // todo: ask for choice
40
                        $this->action($attacker, $this->opponent);
41
                        continue;
42
                    }
43
44
                    $this->action($attacker, $this->player);
45
                }
46
            } catch (IntegerValueException $exception) {
47
                if ($this->player->getHealth()->isLowerThan(new Health(1))) {
48
                    throw PlayerException::death();
49
                }
50
51
                break;
52
            }
53
54
            $this->playerAction->tell([
55
                '',
56
                "{$this->player->getName()} health: {$this->player->getHealth()}",
57
                "{$this->opponent->getName()} health: {$this->opponent->getHealth()}",
58
            ]);
59
60
            $this->round->increment();
61
        }
62
63
        $this->player->getHealth()->replaceWith($playerInitialHealth);
64
    }
65
66
    /**
67
     * @throws IntegerValueException if target's health hits 0
68
     */
69
    private function action(Fighter $attacker, Fighter $target): void
70
    {
71
        if ($attacker instanceof Player) {
72
            $action = $this->askForAction($attacker);
73
            if ($action instanceof Usable) {
74
                $action->use($attacker, $this->playerAction);
75
                return;
76
            }
77
        } else {
78
            $action = $attacker->getTalents()->filterAttacks()->getIterator()->current(); // todo
79
        }
80
81
        $damage = match (true) {
82
            $action instanceof MeleeAttack =>
83
                $action->getDamage($attacker->getWeapon() ?? throw FightException::weaponRequired())
0 ignored issues
show
Bug introduced by
The method getDamage() does not exist on AardsGerds\Game\Fight\Attack. Since it exists in all sub-types, consider adding an abstract or default implementation to AardsGerds\Game\Fight\Attack. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

83
                $action->/** @scrutinizer ignore-call */ 
84
                         getDamage($attacker->getWeapon() ?? throw FightException::weaponRequired())
Loading history...
84
                    ->increaseBy($attacker->getStrength()),
85
            $action instanceof EtherumAttack => $action->getDamage(),
86
            default => new Damage(0),
87
        };
88
89
        try {
90
            $target->getHealth()->decreaseBy($damage);
91
        } catch (IntegerValueException $exception) {
92
            $this->playerAction->tell("{$attacker->getName()} uses {$action} and deals {$damage} damage, which brings opponent to their knees!");
93
            throw $exception;
94
        }
95
96
        $this->playerAction->tell("{$attacker->getName()} uses {$action} and deals {$damage} damage.");
97
    }
98
99
    private function askForAction(Player $player): Attack|Usable
100
    {
101
        $action = $this->playerAction->askForChoice(
102
            'Select action',
103
            array_merge($player->getTalents()->filterAttacks()->getItems(), ['Go to inventory']),
104
        );
105
106
        if ($action === 'Go to inventory') {
107
            $selectedItem = $this->playerAction->askForChoice(
108
                'Select item to use',
109
                array_merge($player->getInventory()->filterUsable()->getItems(), ['Back']),
110
            );
111
112
            if ($selectedItem === 'Back') {
113
                return $this->askForAction($player);
114
            }
115
116
            return $selectedItem;
117
        }
118
119
        if ($action instanceof MeleeAttack && $player->getWeapon() === null) {
120
            $this->playerAction->note('This attack requires weapon equipped.');
121
            return $this->askForAction($player);
122
        }
123
124
        return $action;
125
    }
126
}
127