Passed
Push — master ( cba0a9...d9de1f )
by Karthik
13:36
created

ItemOption::getCraftOptions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
1
<?php
2
3
namespace cyberinferno\Cabal\Helpers;
4
5
use cyberinferno\Cabal\Helpers\Exceptions\InvalidCraftLevelException;
6
use cyberinferno\Cabal\Helpers\Exceptions\InvalidOptionException;
7
use cyberinferno\Cabal\Helpers\Exceptions\MaxOptionException;
8
use cyberinferno\Cabal\Helpers\Exceptions\OptionLimitException;
9
use cyberinferno\Cabal\Helpers\Exceptions\OptionNotFoundException;
10
11
/**
12
 * Class ItemOption
13
 *
14
 * Contains methods helpful to generate item option
15
 *
16
 * @package cyberinferno\Cabal\Helpers
17
 * @author Karthik P
18
 */
19
class ItemOption
20
{
21
    const MAX_OPTIONS = 4;
22
23
    const MIN_SLOTS = 0;
24
    const MAX_SLOTS = 4;
25
26
    const MIN_CRAFTS = 0;
27
    const MAX_CRAFTS = 3;
28
29
    const OUTPUT_FORMAT_INTEGER = 'int';
30
    const OUTPUT_FORMAT_HEXADECIMAL = 'hex';
31
32
    /**
33
     * @var int Max number of slots the generated item option will have
34
     */
35
    protected $_slots;
36
    /**
37
     * @var array
38
     */
39
    protected $_slotOptions = [];
40
    /**
41
     * @var int Max number of crafts the generated item option will have
42
     */
43
    protected $_crafts;
44
    /**
45
     * @var array
46
     */
47
    protected $_craftOptions = [];
48
    /**
49
     * @var array
50
     */
51
    protected $_availableCraftLevels = ['9', 'A', 'B', 'C', 'D', 'E', 'F'];
52
    /**
53
     * @var array
54
     */
55
    protected $_availableOptions = [
56
        '1', '2', '3', '4', '5',
57
        '6', '7', '8', '9', 'A',
58
        'B', 'C', 'D', 'E', 'F'
59
    ];
60
61
    /**
62
     * ItemOption constructor.
63
     * @param int $slots
64
     * @param int $crafts
65
     * @throws \cyberinferno\Cabal\Helpers\Exceptions\MaxOptionException
66
     */
67 57
    public function __construct($slots = 0, $crafts = 0)
68
    {
69 57
        if ($slots < self::MIN_SLOTS) {
70 3
            $slots = self::MIN_SLOTS;
71
        }
72 57
        if ($slots > self::MAX_SLOTS) {
73 3
            $slots = self::MAX_SLOTS;
74
        }
75 57
        if ($crafts < self::MIN_CRAFTS) {
76 3
            $crafts = self::MIN_CRAFTS;
77
        }
78 57
        if ($crafts > self::MAX_CRAFTS) {
79 3
            $crafts = self::MAX_CRAFTS;
80
        }
81 57
        if ($slots + $crafts > self::MAX_OPTIONS) {
82 3
            throw new MaxOptionException('Maximum number of options cannot exceed ' . self::MAX_OPTIONS);
83
        }
84 54
        $this->_slots = $slots;
85 54
        $this->_crafts = $crafts;
86 54
    }
87
88
    /**
89
     * Returns the number of slots in the option
90
     *
91
     * @return int
92
     */
93 9
    public function getSlots()
94
    {
95 9
        return $this->_slots;
96
    }
97
98
    /**
99
     * Returns the number of crafts in the option
100
     *
101
     * @return int
102
     */
103 9
    public function getCrafts()
104
    {
105 9
        return $this->_crafts;
106
    }
107
108
    /**
109
     * Returns slot options
110
     *
111
     * @return array
112
     */
113 6
    public function getSlotOptions()
114
    {
115 6
        return $this->_slotOptions;
116
    }
117
118
    /**
119
     * Sets a slot option
120
     *
121
     * @param string $option
122
     * @return ItemOption
123
     * @throws InvalidOptionException
124
     * @throws OptionLimitException
125
     */
126 15
    public function setSlotOption($option)
127
    {
128 15
        if (!in_array(strval($option), $this->_availableOptions)) {
129 3
            throw new InvalidOptionException();
130
        }
131 12
        if (count($this->_slotOptions) == $this->_slots) {
132 3
            throw new OptionLimitException('Slot option cannot exceed ' . $this->_slots);
133
        }
134 12
        $this->_slotOptions[] = ['option' => (string)$option];
135 12
        return $this;
136
    }
137
138
    /**
139
     * Returns craft options
140
     *
141
     * @return array
142
     */
143 6
    public function getCraftOptions()
144
    {
145 6
        return $this->_craftOptions;
146
    }
147
148
    /**
149
     * Sets a craft option
150
     *
151
     * @param string $craftLevel
152
     * @param string $option
153
     * @return ItemOption
154
     * @throws InvalidCraftLevelException
155
     * @throws InvalidOptionException
156
     * @throws OptionLimitException
157
     */
158 15
    public function setCraftOption($craftLevel, $option)
159
    {
160 15
        if (!in_array(strval($craftLevel), $this->_availableCraftLevels)) {
161 3
            throw new InvalidCraftLevelException();
162
        }
163 12
        if (!in_array(strval($option), $this->_availableOptions)) {
164 3
            throw new InvalidOptionException();
165
        }
166 9
        if (count($this->_craftOptions) == $this->_crafts) {
167 3
            throw new OptionLimitException('Craft option cannot exceed ' . $this->_crafts);
168
        }
169 9
        $this->_craftOptions[] = [
170 9
            'craftLevel' => (string)$craftLevel,
171 9
            'option' => (string)$option
172
        ];
173 9
        return $this;
174
    }
175
176
    /**
177
     * Returns the generated item option based upon the criteria set
178
     *
179
     * @param string $format
180
     * @return string|int
181
     */
182 6
    public function generate($format = self::OUTPUT_FORMAT_INTEGER)
183
    {
184 6
        $optionString = '';
185 6
        $slotsToFillArray = [];
186
        // Craft options should be at the end of the hex string hence we start prepending all craft options to output
187 6
        foreach ($this->_craftOptions as $craftOption) {
188 3
            $optionString = $craftOption['craftLevel'] . $craftOption['option'] . $optionString;
189
        }
190
        // We calculate slots to fill based upon slot options
191 6
        foreach ($this->_slotOptions as $slotOption) {
192 6
            if (isset($slotsToFillArray[$slotOption['option']])) {
193
                // Item cannot have 4 set of same options
194 6
                if ($slotsToFillArray[$slotOption['option']] == 3) {
195 3
                    continue;
196
                }
197 6
                $slotsToFillArray[$slotOption['option']]++;
198
            } else {
199 6
                $slotsToFillArray[$slotOption['option']] = 1;
200
            }
201
        }
202
        // Sorting slots to fill array to make sure multiple slot options stay in the end
203 6
        arsort($slotsToFillArray, SORT_NUMERIC);
204
        // Build the output with slot options
205 6
        foreach ($slotsToFillArray as $option => $fill) {
206 6
            if (strlen($optionString) == 7) {
207
                // Means we prematurely reached the end due to wrong options being set hence go out of the loop
208
                break;
209
            }
210 6
            if (strlen($optionString) == 6) {
211
                // Ignore slots to fill value
212 3
                $optionString = $option . $optionString;
213
            } else {
214 6
                $optionString = $fill . $option . $optionString;
215
            }
216
        }
217 6
        if (strlen($optionString) < 7) {
218
            // Fill the output with 0s if there are empty slots
219 6
            $optionString = str_repeat('0', 7 - strlen($optionString)) . $optionString;
220
        }
221 6
        if ($format != self::OUTPUT_FORMAT_INTEGER) {
222 6
            return $this->_slots . $optionString;
223
        }
224 3
        return hexdec($this->_slots . $optionString);
225
    }
226
227
    /**
228
     * Extracts item options details from generated item option
229
     *
230
     * @param int|string $generatedItemOption
231
     * @return array
232
     */
233 9
    public static function extract($generatedItemOption)
234
    {
235 9
        if (is_int($generatedItemOption)) {
236 3
            $generatedItemOption = dechex($generatedItemOption);
237
        }
238 9
        if (strlen($generatedItemOption) < 8) {
239 3
            $generatedItemOption = str_repeat('0', 8 - strlen($generatedItemOption)) . $generatedItemOption;
240
        }
241 9
        $generatedItemOption = strtoupper($generatedItemOption);
242 9
        if (hexdec($generatedItemOption) > hexdec('4FFFFFFF')) {
243
            // Invalid generated option hence return default values
244
            return [
245
                'slots' => 0,
246
                'crafts' => 0,
247
                'slotOptions' => [],
248
                'craftOptions' => []
249
            ];
250
        }
251 9
        $craftOptions = [];
252 9
        $slotOptions = [];
253
        // Extract last 3 options
254 9
        for ($i = 1; $i <= 3; $i++) {
255 9
            $currentOption = substr($generatedItemOption, -1 * ($i * 2), 2);
256 9
            if (in_array($currentOption[0], ['9', 'A', 'B', 'C', 'D', 'E', 'F'])) {
257 3
                $craftOptions[] = [
258 3
                    'craftLevel' => $currentOption[0],
259 3
                    'craftOption' => $currentOption[1]
260
                ];
261
            } else {
262 9
                for ($j = 1; $j <= (int)$currentOption[0]; $j++) {
263 9
                    if ($currentOption[1] == '0') {
264
                        continue;
265
                    }
266 9
                    $slotOptions[] = [
267 9
                        'slotOption' => $currentOption[1]
268
                    ];
269
                }
270
            }
271
        }
272 9
        if ($generatedItemOption[1] != '0') {
273
            $slotOptions[] = [
274
                'slotOption' => $generatedItemOption[1]
275
            ];
276
        }
277
        return [
278 9
            'slots' => (int)$generatedItemOption[0],
279 9
            'crafts' => count($craftOptions),
280 9
            'slotOptions' => $slotOptions,
281 9
            'craftOptions' => $craftOptions
282
        ];
283
    }
284
285
    /**
286
     * Removes a particular option from item option code
287
     *
288
     * @param string $inputOptionCode
289
     * @param string $optionToRemove
290
     * @param string $outputFormat
291
     * @return string|int
292
     * @throws InvalidCraftLevelException
293
     * @throws InvalidOptionException
294 6
     * @throws MaxOptionException
295
     * @throws OptionLimitException
296
     * @throws OptionNotFoundException
297 6
     */
298
    public static function removeSlotOption($inputOptionCode, $optionToRemove, $outputFormat = self::OUTPUT_FORMAT_INTEGER)
299 6
    {
300 6
        // Extract input options
301 6
        $extractedOptions = self::extract($inputOptionCode);
302 6
        // Traverse through slot options to find option index
303 3
        $filledSlotCount = count($extractedOptions['slotOptions']);
304 3
        $optionIndex = null;
305
        for ($i = 0; $i < $filledSlotCount; $i++) {
306
            if ($extractedOptions['slotOptions'][$i]['slotOption'] == $optionToRemove) {
307
                $optionIndex = $i;
308 6
                break;
309 3
            }
310
        }
311
        // If option index not found in the extracted options then throw exception
312 3
        if ($optionIndex === null) {
313
            throw new OptionNotFoundException();
314 3
        }
315 3
        // Remove the option
316 3
        unset($extractedOptions['slotOptions'][$optionIndex]);
317
        // Return regenerated item option code
318 3
        $generator = new self($extractedOptions['slots'], $extractedOptions['crafts']);
319
        foreach ($extractedOptions['slotOptions'] as $slot) {
320
            $generator->setSlotOption($slot['slotOption']);
321 3
        }
322
        foreach ($extractedOptions['craftOptions'] as $craft) {
323
            $generator->setCraftOption($craft['craftLevel'], $craft['craftOption']);
324
        }
325
        return $generator->generate($outputFormat);
326
    }
327
328
    /**
329
     * Removes all slot options from item option code
330
     *
331
     * @param $inputOptionCode
332
     * @param string $outputFormat
333
     * @return int|string
334
     * @throws InvalidCraftLevelException
335
     * @throws InvalidOptionException
336
     * @throws MaxOptionException
337
     * @throws OptionLimitException
338
     */
339
    public static function removeAllSlotOptions($inputOptionCode, $outputFormat = self::OUTPUT_FORMAT_INTEGER)
340
    {
341
        // Extract input options
342
        $extractedOptions = self::extract($inputOptionCode);
343
        // Regenerate options and return
344
        $generator = new self($extractedOptions['slots'], $extractedOptions['crafts']);
345
        foreach ($extractedOptions['craftOptions'] as $craft) {
346
            $generator->setCraftOption($craft['craftLevel'], $craft['craftOption']);
347
        }
348
        return $generator->generate($outputFormat);
349
    }
350
}