Passed
Push — master ( b679aa...39d8dd )
by Karthik
02:26
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 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
     * Removea 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 OptionNotFoundException
293
     */
294 6
    public static function removeSlotOption($inputOptionCode, $optionToRemove, $outputFormat = self::OUTPUT_FORMAT_INTEGER)
295
    {
296
        // Extract input options
297 6
        $extractedOptions = self::extract($inputOptionCode);
298
        // Traverse through slot options to find option index
299 6
        $filledSlotCount = count($extractedOptions['slotOptions']);
300 6
        $optionIndex = null;
301 6
        for ($i = 0; $i < $filledSlotCount; $i++) {
302 6
            if ($extractedOptions['slotOptions'][$i]['slotOption'] == $optionToRemove) {
303 3
                $optionIndex = $i;
304 3
                break;
305
            }
306
        }
307
        // If option index not found in the extracted options then throw exception
308 6
        if ($optionIndex === null) {
309 3
            throw new OptionNotFoundException();
310
        }
311
        // Remove the option
312 3
        unset($extractedOptions['slotOptions'][$optionIndex]);
313
        // Return regenerated item option code
314 3
        $generator = new self($extractedOptions['slots'], $extractedOptions['crafts']);
315 3
        foreach ($extractedOptions['slotOptions'] as $slot) {
316 3
            $generator->setSlotOption($slot['slotOption']);
317
        }
318 3
        foreach ($extractedOptions['craftOptions'] as $craft) {
319
            $generator->setCraftOption($craft['craftLevel'], $craft['craftOption']);
320
        }
321 3
        return $generator->generate($outputFormat);
322
    }
323
}