Lines 92.15% 47 / 51
Methods 66.66% 4 / 6
Classes 0.00% 0 / 1
Name Lines Methods CRAP
 __construct 100.00% 1 / 1 100.00% 1 / 1 1
 from 90.00% 27 / 30 0.00% 0 / 1 7.05
 slot 87.50% 7 / 8 0.00% 0 / 1 8.12
 target 100.00% 5 / 5 100.00% 1 / 1 5
 summary 100.00% 1 / 1 100.00% 1 / 1 1
 [Lorekeeper\Server\Event\NamedAfterClass] name 100.00% 6 / 6 100.00% 1 / 1 1
22final readonly class SkillsLoadedEvent implements Event
23{
24    use NamedAfterClass;
25
26    /**
27     * @param list<Skill> $skills
28     * @param list<SkillSlot> $usable
29     */
30    public function __construct(
31        public array $skills,
32        public array $usable,
33    ) {}
34
35    /** @return Option\Option<static> */
36    #[Override]
37    public static function from(Message $message): Option\Option
38    {
39        if (!$message instanceof JsonMessage || ($message->data['cmd'] ?? null) !== 'sAct') {
40            return Option\none();
41        }
42
43        try {
44            $active = Type\shape([
45                'actions' => Type\shape([
46                    'active' => Type\vec(Type\shape([
47                        'ref' => Type\non_empty_string(),
48                        'tgt' => Type\string(),
49                        'mp' => Type\uint(),
50                        'cd' => Type\uint(),
51                        'isOK' => Type\optional(Type\bool()),
52                    ], allowUnknownFields: true)),
53                ], allowUnknownFields: true),
54            ], allowUnknownFields: true)->coerce($message->data)['actions']['active'];
55        } catch (CoercionException $exception) {
56            throw new MalformedEventException(static::class, 'skills are missing or invalid', $exception);
57        }
58
59        $skills = [];
60        $usable = [];
61        foreach ($active as $skill) {
62            $slot = self::slot($skill['ref']);
63            if ($slot === null) {
64                continue;
65            }
66
67            $skills[] = new Skill(
68                $slot,
69                self::target($skill['tgt']),
70                new Mana($skill['mp']),
71                Duration::milliseconds($skill['cd']),
72            );
73            if ($skill['isOK'] ?? false) {
74                $usable[] = $slot;
75            }
76        }
77
78        return Option\some(new static($skills, $usable));
79    }
80
81    private static function slot(string $ref): ?SkillSlot
82    {
83        return match ($ref) {
84            'aa' => SkillSlot::AutoAttack,
85            'a1' => SkillSlot::First,
86            'a2' => SkillSlot::Second,
87            'a3' => SkillSlot::Third,
88            'a4' => SkillSlot::Fourth,
89            'i1' => SkillSlot::Potion,
90            default => null,
91        };
92    }
93
94    /** @throws MalformedEventException */
95    private static function target(string $target): SkillTarget
96    {
97        return match ($target) {
98            'h' => SkillTarget::Enemy,
99            's' => SkillTarget::Self,
100            'f' => SkillTarget::Ally,
101            default => throw new MalformedEventException(static::class, "unknown skill target '{$target}'"),
102        };
103    }
104
105    #[Override]
106    public function summary(): string
107    {
108        return count($this->skills) . ' skills, ' . count($this->usable) . ' unlocked';
109    }
110}

From Lorekeeper\Server\Event\NamedAfterClass

7trait NamedAfterClass
8{
9    public function name(): string
10    {
11        $class = static::class;
12
13        return (string) preg_replace(
14            '/Event$/',
15            replacement: '',
16            subject: substr($class, (int) strrpos($class, needle: '\\') + 1),
17        );
18    }
19}