Lines 89.74% 35 / 39
Functions and Methods 57.14% 4 / 7
Classes and Traits 0.00% 0 / 1
Name Lines Functions and Methods CRAP Classes and Traits
LevelUpScript 89.74% 35 / 39 57.14% 4 / 7 15.24 0.00% 0 / 1
 __construct 100.00% 1 / 1 100.00% 1 / 1 1
 title 0.00% 0 / 1 0.00% 0 / 1 2
 rules 89.47% 17 / 19 0.00% 0 / 1 5.03
 levelUpIn 100.00% 9 / 9 100.00% 1 / 1 2
 attackIn 83.33% 5 / 6 0.00% 0 / 1 4.07
 below 100.00% 1 / 1 100.00% 1 / 1 1
 progress 100.00% 2 / 2 100.00% 1 / 1 1
1<?php
2
3declare(strict_types=1);
4
5namespace Lorekeeper\Client\Bot\Script;
6
7use Closure;
8use Generator;
9use Lorekeeper\Client\Bot\Done;
10use Lorekeeper\Client\Command\Combat\AttackMonsterCommand;
11use Lorekeeper\Client\Script\Instruction;
12use Lorekeeper\Client\Script\Progress;
13use Lorekeeper\Client\Script\TimedOut;
14use Lorekeeper\Core\CharacterClass\Skill\SkillSlot;
15use Lorekeeper\Core\Player\PlayerLevel;
16use Lorekeeper\Core\World\Map\Cell;
17use Lorekeeper\Core\World\Map\MapName;
18use Lorekeeper\Core\World\Map\Pad;
19use Lorekeeper\Server\Event\Event;
20use Override;
21use Psl\DateTime\Timestamp;
22use RuntimeException;
23
24final class LevelUpScript extends BotScript
25{
26    private int $from = 1;
27
28    /** @param null|Closure(): Timestamp $clock */
29    public function __construct(
30        private readonly PlayerLevel $target,
31        ?Closure $clock = null,
32    ) {
33        parent::__construct($clock);
34    }
35
36    #[Override]
37    public function title(): string
38    {
39        return "Level up to {$this->target->value}";
40    }
41
42    /** @return Generator<int, Instruction, null|Event|TimedOut, Done> */
43    #[Override]
44    protected function rules(): Generator
45    {
46        $this->from = $this->me->level()->value;
47
48        if ($this->below(10)) {
49            yield from $this->join(new MapName('oaklore'));
50            yield from $this->levelUpIn(new Cell('r3'), Pad::Left, upTo: 10);
51        }
52
53        if ($this->below(20)) {
54            yield from $this->join(new MapName('swordhavenundead'));
55            yield from $this->levelUpIn(new Cell('Gates'), Pad::Left, upTo: 20);
56        }
57
58        if ($this->below(75)) {
59            yield from $this->join(new MapName('icestormarena'));
60            yield from $this->levelUpIn(new Cell('r7'), Pad::Left, upTo: 25);
61            yield from $this->levelUpIn(new Cell('r10'), Pad::Left, upTo: 30);
62            yield from $this->levelUpIn(new Cell('r11'), Pad::Left, upTo: 35);
63            yield from $this->levelUpIn(new Cell('r14'), Pad::Left, upTo: 50);
64            yield from $this->levelUpIn(new Cell('r16'), Pad::Left, upTo: 61);
65            yield from $this->levelUpIn(new Cell('r17'), Pad::Left, upTo: 75);
66        }
67
68        if ($this->below(100)) {
69            yield from $this->join(new MapName('icestormunder'));
70            yield from $this->levelUpIn(new Cell('r2'), Pad::Top, upTo: 100);
71        }
72
73        return new Done("Reached level {$this->me->level()->value}");
74    }
75
76    /** @return Generator<int, Instruction, null|Event|TimedOut, void> */
77    private function levelUpIn(Cell $cell, Pad $pad, int $upTo): Generator
78    {
79        while ($this->below($upTo)) {
80            $next = $this->me->level()->value + 1;
81            yield from $this->kill(
82                $cell,
83                $pad,
84                $this->attackIn($cell),
85                fn(): bool => $this->me->level()->value >= $next,
86            );
87            yield $this->progress();
88        }
89    }
90
91    /** @throws RuntimeException */
92    private function attackIn(Cell $cell): AttackMonsterCommand
93    {
94        foreach ($this->me->map()->cells() as $mapCell) {
95            $spawn = $mapCell->spawns[0] ?? null;
96            if ($mapCell->cell->value !== $cell->value || $spawn === null) {
97                continue;
98            }
99
100            return new AttackMonsterCommand($this->me->position()->area, SkillSlot::AutoAttack, $spawn->id);
101        }
102
103        throw new RuntimeException("There are no monsters in {$cell->value}.");
104    }
105
106    private function below(int $level): bool
107    {
108        return $this->me->level()->value < min($level, $this->target->value);
109    }
110
111    private function progress(): Progress
112    {
113        $total = max(1, $this->target->value - $this->from);
114
115        return new Progress(max(0, min($total, $this->me->level()->value - $this->from)), $total);
116    }
117}