Passed
Push — master ( d9de1f...3b5af9 )
by Karthik
13:35 queued 12:28
created

ItemOption::generate()   B

Complexity

Conditions 10
Paths 64

Size

Total Lines 43
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 22
CRAP Score 10.0082

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 10
eloc 24
c 1
b 0
f 0
nc 64
nop 1
dl 0
loc 43
ccs 22
cts 23
cp 0.9565
crap 10.0082
rs 7.6666

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 60
    public function __construct($slots = 0, $crafts = 0)
68
    {
69 60
        if ($slots < self::MIN_SLOTS) {
70 3
            $slots = self::MIN_SLOTS;
71
        }
72 60
        if ($slots > self::MAX_SLOTS) {
73 3
            $slots = self::MAX_SLOTS;
74
        }
75 60
        if ($crafts < self::MIN_CRAFTS) {
76 3
            $crafts = self::MIN_CRAFTS;
77
        }
78 60
        if ($crafts > self::MAX_CRAFTS) {
79 3
            $crafts = self::MAX_CRAFTS;
80
        }
81 60
        if ($slots + $crafts > self::MAX_OPTIONS) {
82 3
            throw new MaxOptionException('Maximum number of options cannot exceed ' . self::MAX_OPTIONS);
83
        }
84 57
        $this->_slots = $slots;
85 57
        $this->_crafts = $crafts;
86 57
    }
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 18
    public function setCraftOption($craftLevel, $option)
159
    {
160 18
        if (!in_array(strval($craftLevel), $this->_availableCraftLevels)) {
161 3
            throw new InvalidCraftLevelException();
162
        }
163 15
        if (!in_array(strval($option), $this->_availableOptions)) {
164 3
            throw new InvalidOptionException();
165
        }
166 12
        if (count($this->_craftOptions) == $this->_crafts) {
167 3
            throw new OptionLimitException('Craft option cannot exceed ' . $this->_crafts);
168
        }
169 12
        $this->_craftOptions[] = [
170 12
            'craftLevel' => (string)$craftLevel,
171 12
            'option' => (string)$option
172
        ];
173 12
        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 9
    public function generate($format = self::OUTPUT_FORMAT_INTEGER)
183
    {
184 9
        $optionString = '';
185 9
        $slotsToFillArray = [];
186
        // Craft options should be at the end of the hex string hence we start prepending all craft options to output
187 9
        foreach ($this->_craftOptions as $craftOption) {
188 6
            $optionString = $craftOption['craftLevel'] . $craftOption['option'] . $optionString;
189
        }
190
        // We calculate slots to fill based upon slot options
191 9
        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 9
        arsort($slotsToFillArray, SORT_NUMERIC);
204
        // Build the output with slot options
205 9
        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 9
        if (strlen($optionString) < 7) {
218
            // Fill the output with 0s if there are empty slots
219 9
            $optionString = str_repeat('0', 7 - strlen($optionString)) . $optionString;
220
        }
221 9
        if ($format != self::OUTPUT_FORMAT_INTEGER) {
222 9
            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 12
    public static function extract($generatedItemOption)
234
    {
235 12
        if (is_int($generatedItemOption)) {
236 3
            $generatedItemOption = dechex($generatedItemOption);
237
        }
238 12
        if (strlen($generatedItemOption) < 8) {
239 3
            $generatedItemOption = str_repeat('0', 8 - strlen($generatedItemOption)) . $generatedItemOption;
240
        }
241 12
        $generatedItemOption = strtoupper($generatedItemOption);
242 12
        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 12
        $craftOptions = [];
252 12
        $slotOptions = [];
253
        // Extract last 3 options
254 12
        for ($i = 1; $i <= 3; $i++) {
255 12
            $currentOption = substr($generatedItemOption, -1 * ($i * 2), 2);
256 12
            if (in_array($currentOption[0], ['9', 'A', 'B', 'C', 'D', 'E', 'F'])) {
257 6
                $craftOptions[] = [
258 6
                    'craftLevel' => $currentOption[0],
259 6
                    'craftOption' => $currentOption[1]
260
                ];
261
            } else {
262 12
                for ($j = 1; $j <= (int)$currentOption[0]; $j++) {
263 12
                    if ($currentOption[1] == '0') {
264
                        continue;
265
                    }
266 12
                    $slotOptions[] = [
267 12
                        'slotOption' => $currentOption[1]
268
                    ];
269
                }
270
            }
271
        }
272 12
        if ($generatedItemOption[1] != '0') {
273
            $slotOptions[] = [
274
                'slotOption' => $generatedItemOption[1]
275
            ];
276
        }
277
        return [
278 12
            'slots' => (int)$generatedItemOption[0],
279 12
            'crafts' => count($craftOptions),
280 12
            'slotOptions' => $slotOptions,
281 12
            '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
     * @throws MaxOptionException
295
     * @throws OptionLimitException
296
     * @throws OptionNotFoundException
297
     */
298 6
    public static function removeSlotOption($inputOptionCode, $optionToRemove, $outputFormat = self::OUTPUT_FORMAT_INTEGER)
299
    {
300
        // Extract input options
301 6
        $extractedOptions = self::extract($inputOptionCode);
302
        // Traverse through slot options to find option index
303 6
        $filledSlotCount = count($extractedOptions['slotOptions']);
304 6
        $optionIndex = null;
305 6
        for ($i = 0; $i < $filledSlotCount; $i++) {
306 6
            if ($extractedOptions['slotOptions'][$i]['slotOption'] == $optionToRemove) {
307 3
                $optionIndex = $i;
308 3
                break;
309
            }
310
        }
311
        // If option index not found in the extracted options then throw exception
312 6
        if ($optionIndex === null) {
313 3
            throw new OptionNotFoundException();
314
        }
315
        // 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 3
        foreach ($extractedOptions['slotOptions'] as $slot) {
320 3
            $generator->setSlotOption($slot['slotOption']);
321
        }
322 3
        foreach ($extractedOptions['craftOptions'] as $craft) {
323
            $generator->setCraftOption($craft['craftLevel'], $craft['craftOption']);
324
        }
325 3
        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 3
    public static function removeAllSlotOptions($inputOptionCode, $outputFormat = self::OUTPUT_FORMAT_INTEGER)
340
    {
341
        // Extract input options
342 3
        $extractedOptions = self::extract($inputOptionCode);
343
        // Regenerate options and return
344 3
        $generator = new self($extractedOptions['slots'], $extractedOptions['crafts']);
345 3
        foreach ($extractedOptions['craftOptions'] as $craft) {
346 3
            $generator->setCraftOption($craft['craftLevel'], $craft['craftOption']);
347
        }
348 3
        return $generator->generate($outputFormat);
349
    }
350
}