Completed
Branch develop (070f2a)
by Benjamin
02:52
created

RuleConfigBuilder::prepareInducementTable()   A

Complexity

Conditions 6
Paths 12

Size

Total Lines 25
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 18
c 1
b 0
f 0
nc 12
nop 2
dl 0
loc 25
rs 9.0444
1
<?php
2
3
namespace Obblm\Core\Helper\Rule;
4
5
use Doctrine\Common\Collections\ArrayCollection;
6
use Doctrine\Common\Collections\Criteria;
7
use Doctrine\Common\Collections\Expr\CompositeExpression;
8
use Obblm\Core\Contracts\InducementInterface;
9
use Obblm\Core\Contracts\RosterInterface;
10
use Obblm\Core\Contracts\Rule\RuleBuilderInterface;
11
use Obblm\Core\Entity\Team;
12
use Obblm\Core\Exception\NotFoundRuleKeyExcepion;
13
use Obblm\Core\Helper\CoreTranslation;
14
use Obblm\Core\Helper\Rule\Config\ConfigResolver;
15
use Obblm\Core\Helper\Rule\Config\RuleConfigResolver;
16
use Obblm\Core\Helper\Rule\Inducement\Inducement;
17
use Obblm\Core\Helper\Rule\Inducement\InducementType;
18
use Obblm\Core\Helper\Rule\Inducement\MultipleStarPlayer;
19
use Obblm\Core\Helper\Rule\Inducement\StarPlayer;
20
use Obblm\Core\Helper\Rule\Roster\Roster;
21
use Obblm\Core\Helper\Rule\Skill\Skill;
22
23
class RuleConfigBuilder extends RuleConfigResolver implements RuleBuilderInterface
24
{
25
    /** @var ArrayCollection */
26
    private $injuries;
27
    /** @var ArrayCollection|Skill[] */
28
    private $skills;
29
    /** @var ArrayCollection|InducementType[] */
30
    private $inducementTypes;
31
    /** @var ArrayCollection */
32
    private $sppLevels;
33
    /** @var ArrayCollection|InducementInterface[] */
34
    private $inducements;
35
    /** @var ArrayCollection|RosterInterface[] */
36
    private $rosters;
37
38
    protected function build(string $ruleKey, array $rule)
39
    {
40
        $treeResolver = new ConfigResolver($this);
41
        $rule = $treeResolver->resolve($rule);
42
        $this->prepareInjuriesTable($ruleKey, $rule);
43
        $this->prepareSppTable($ruleKey, $rule);
44
        $this->prepareSkillsTable($ruleKey, $rule);
45
        $this->prepareInducementTypes($ruleKey, $rule);
46
        $this->prepareInducementTable($ruleKey, $rule);
47
        $this->prepareRosterTable($ruleKey, $rule);
48
    }
49
50
    public function getInjuries():ArrayCollection
51
    {
52
        return $this->injuries;
53
    }
54
55
    public function setInjuries(ArrayCollection $injuries):self
56
    {
57
        $this->injuries = $injuries;
58
        return $this;
59
    }
60
61
    public function getInducementTypes():ArrayCollection
62
    {
63
        return $this->inducementTypes;
64
    }
65
66
    public function setInducementTypes(ArrayCollection $inducementTypes):self
67
    {
68
        $this->inducementTypes = $inducementTypes;
69
        return $this;
70
    }
71
72
    public function getInducementType(string $type):InducementType
73
    {
74
        return $this->getInducementTypes()->get($type);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getInducementTypes()->get($type) could return the type null which is incompatible with the type-hinted return Obblm\Core\Helper\Rule\Inducement\InducementType. Consider adding an additional type-check to rule them out.
Loading history...
75
    }
76
77
    public function getSppLevels():ArrayCollection
78
    {
79
        return $this->sppLevels;
80
    }
81
82
    public function setSppLevels(ArrayCollection $sppLevels):self
83
    {
84
        $this->sppLevels = $sppLevels;
85
        return $this;
86
    }
87
88
    public function getInducementTable():ArrayCollection
89
    {
90
        return $this->inducements;
91
    }
92
93
    public function setInducementTable(ArrayCollection $inducements):self
94
    {
95
        $this->inducements = $inducements;
96
        return $this;
97
    }
98
99
    public function getRosters():ArrayCollection
100
    {
101
        return $this->rosters;
102
    }
103
104
    public function setRosters(ArrayCollection $rosters):self
105
    {
106
        $this->rosters = $rosters;
107
        return $this;
108
    }
109
110
    public function getSkills():ArrayCollection
111
    {
112
        return $this->skills;
113
    }
114
115
    public function setSkills(ArrayCollection $skills):self
116
    {
117
        $this->skills = $skills;
118
        return $this;
119
    }
120
121
    private function prepareInjuriesTable(string $ruleKey, array $rule)
122
    {
123
        $this->injuries = new ArrayCollection();
124
        foreach ($rule['injuries'] as $key => $injury) {
125
            $label = CoreTranslation::getInjuryKey($ruleKey, $key);
126
            $effectLabel = CoreTranslation::getInjuryEffect($ruleKey, $key);
127
            if (isset($injury['to'])) {
128
                for ($i = $injury['from']; $i <= $injury['to']; $i++) {
129
                    $this->injuries->set(
130
                        $i,
131
                        (object) ['value' => $i, 'label' => $label, 'effect_label' => $effectLabel, 'effects' => $injury['effects']]
132
                    );
133
                }
134
            } else {
135
                $this->injuries->set(
136
                    $injury['from'],
137
                    (object) ['value' => $injury['from'], 'label' => $label, 'effect_label' => $effectLabel, 'effects' => $injury['effects']]
138
                );
139
            }
140
        }
141
    }
142
143
    private function prepareInducementTypes(string $ruleKey, array $rule)
144
    {
145
        $this->inducementTypes = new ArrayCollection();
146
        $this->inducementTypes->set('star_players', new InducementType([
147
            'key' => 'star_players',
148
            'translation_key' => CoreTranslation::getStarPlayerTitle($ruleKey),
149
            'translation_domain' => $ruleKey,
150
        ]));
151
        $this->inducementTypes->set('inducements', new InducementType([
152
            'key' => 'inducements',
153
            'translation_key' => CoreTranslation::getInducementTitle($ruleKey),
154
            'translation_domain' => $ruleKey,
155
        ]));
156
        $this->inducementTypes->set('mercenary', new InducementType([
157
            'key' => 'mercenary',
158
            'translation_key' => CoreTranslation::getMercenaryTitle($ruleKey),
159
            'translation_domain' => $ruleKey,
160
        ]));
161
    }
162
163
    private function prepareSppTable(string $ruleKey, array $rule)
164
    {
165
        $spps = new ArrayCollection($rule['spp_levels']);
166
        foreach ($spps as $from => $level) {
167
            $to = $spps->next();
168
            if ($to) {
169
                for ($i = $from; $i < $spps->indexOf($to); $i++) {
170
                    if (!isset($spps[$i])) {
171
                        $spps[$i] = $level;
172
                    }
173
                }
174
            }
175
        }
176
        $spps = $spps->toArray();
177
        ksort($spps);
178
        $this->sppLevels = new ArrayCollection($spps);
179
    }
180
181
    private function prepareSkillsTable(string $ruleKey, array $rule)
182
    {
183
        $this->skills = new ArrayCollection();
184
        foreach ($rule['skills'] as $type => $skills) {
185
            foreach ($skills as $skill) {
186
                $key = join(CoreTranslation::TRANSLATION_GLUE, [$type, $skill]);
187
                $this->skills->set(
188
                    $key,
189
                    new Skill([
190
                        'key' => $skill,
191
                        'type' => $type,
192
                        'translation_key' => CoreTranslation::getSkillNameKey($ruleKey, $skill),
193
                        'type_translation_key' => CoreTranslation::getSkillType($ruleKey, $type),
194
                        'translation_domain' => $ruleKey,
195
                    ])
196
                );
197
            }
198
        }
199
    }
200
201
    private function prepareInducementTable(string $ruleKey, array $rule)
202
    {
203
        $this->inducements = new ArrayCollection();
204
205
        foreach ($rule['inducements'] as $key => $value) {
206
            if ($key !== 'star_players') {
207
                $inducement = new Inducement([
208
                    'type' => $this->inducementTypes['inducements'],
209
                    'key' => join(CoreTranslation::TRANSLATION_GLUE, [$ruleKey, 'inducements', $key]),
210
                    'translation_domain' => $ruleKey,
211
                    'translation_key' => CoreTranslation::getInducementName($ruleKey, $key),
212
                    'max' => $value['max'] ?? 0,
213
                    'rosters' => $value['rosters'] ?? null,
214
                    'value' => $value['cost'],
215
                    'discount_cost' => $value['discount_cost'] ?? null,
216
                ]);
217
                if (!$this->inducements->contains($inducement)) {
218
                    $this->inducements->add($inducement);
219
                }
220
            }
221
        }
222
        foreach ($rule['star_players'] as $key => $starPlayer) {
223
            $inducement = $this->createStarPlayerInducement($ruleKey, $key, $starPlayer);
224
            if (!$this->inducements->contains($inducement)) {
225
                $this->inducements->add($inducement);
226
            }
227
        }
228
    }
229
230
    private function prepareRosterTable(string $ruleKey, array $rule)
231
    {
232
        $this->rosters = new ArrayCollection();
233
        foreach ($rule['rosters'] as $key => $roster) {
234
            $this->rosters->set(
235
                $key,
236
                new Roster([
237
                    'key' => $key,
238
                    'translation_key' => CoreTranslation::getRosterKey($ruleKey, $key),
239
                    'translation_domain' => $ruleKey,
240
                    'player_types' => $roster['players'],
241
                    'reroll_cost' => $roster['reroll_cost'] ?? 0,
242
                    'can_have_apothecary' => $roster['options']['can_have_apothecary'] ?? true,
243
                    'inducement_options' => $roster['options']['inducements'] ?? [],
244
                ])
245
            );
246
        }
247
    }
248
249
    private function createStarPlayerInducement(string $ruleKey, string $key, array $starPlayer):InducementInterface
250
    {
251
        $options = [
252
            'type' => $this->inducementTypes['star_players'],
253
            'key' => join(CoreTranslation::TRANSLATION_GLUE, [$ruleKey, 'star_players', $key]),
254
            'value' => $starPlayer['cost'],
255
            'discount_cost' => $starPlayer['discount_cost'] ?? null,
256
            'characteristics' => $starPlayer['characteristics'] ?? null,
257
            'skills' => $starPlayer['skills'] ?? null,
258
            'rosters' => $starPlayer['rosters'] ?? null,
259
            'translation_domain' => $ruleKey,
260
            'translation_key' => CoreTranslation::getStarPlayerName($ruleKey, $key),
261
            'max' => $starPlayer['max'] ?? 1,
262
        ];
263
        if (isset($starPlayer['multi_parts']) && $starPlayer['multi_parts']) {
264
            $options['parts'] = [];
265
            $first = true;
266
            foreach ($starPlayer['multi_parts'] as $key => $part) {
267
                $part['cost'] = $first ? $starPlayer['cost'] : 0;
268
                $options['parts'][] = $this->createStarPlayerInducement($ruleKey, $key, $part);
269
                $first = false;
270
            }
271
            $inducement = new MultipleStarPlayer($options);
272
        } else {
273
            $inducement = new StarPlayer($options);
274
        }
275
        return $inducement;
276
    }
277
278
    protected function getInducementExpression($options = ['type' => 'inducements']):CompositeExpression
279
    {
280
        $subExpressions = [];
281
        if (isset($options['type'])) {
282
            // Criteria by inducement type
283
            if (is_array($options['type'])) {
284
                $types = [];
285
                foreach ($options['type'] as $type) {
286
                    if (is_string($type)) {
287
                        $inducementType = $this->getInducementType($type);
288
                        $types[] = Criteria::expr()->eq('type', $inducementType);
289
                    }
290
                }
291
                $subExpressions['type'] = new CompositeExpression(CompositeExpression::TYPE_OR, $types);
292
                ;
293
            } elseif (is_string($options['type'])) {
294
                $inducementType = $this->getInducementType($options['type']);
295
                $subExpressions['type'] = Criteria::expr()->eq('type', $inducementType);
296
            }
297
        }
298
        if (isset($options['cost_limit'])) {
299
            // Criteria by cost limit
300
            if (is_int($options['cost_limit'])) {
301
                $subExpressions['cost'] = Criteria::expr()->lte('value', $options['cost_limit']);
302
            }
303
        }
304
        if (isset($options['roster'])) {
305
            // Criteria by roster
306
            if (is_string($options['roster'])) {
307
                $subExpressions['roster'] = Criteria::expr()->orX(
308
                    Criteria::expr()->memberOf('rosters', $options['roster']),
309
                    Criteria::expr()->eq('rosters', [])
310
                );
311
            }
312
        }
313
        return new CompositeExpression(CompositeExpression::TYPE_AND, $subExpressions);
314
    }
315
316
    public function getInducementsFor(Team $team, ?int $budget = null):array
317
    {
318
        $criteria = Criteria::create();
319
        $expr = [
320
            'type' => [
321
                'inducements',
322
                'star_players'
323
            ],
324
            'roster' => $team->getRoster()
325
        ];
326
        if ($budget !== null) {
327
            $expr['cost_limit'] = $budget;
328
        }
329
        $criteria->where(Criteria::expr()->andX(
330
            $this->getInducementExpression($expr)
331
        ));
332
        $availableInducements = $this->getInducementTable()->matching($criteria);
333
        return $availableInducements->toArray();
334
    }
335
336
    public function getAllStarPlayers():ArrayCollection
337
    {
338
        $criteria = Criteria::create();
339
340
        $criteria->where(Criteria::expr()->andX(
341
            $this->getInducementExpression([
342
                'type' => 'star_players'
343
            ])
344
        ));
345
        return $this->getInducementTable()->matching($criteria);
346
    }
347
}
348