Passed
Pull Request — master (#1969)
by Janko
22:34 queued 10:03
created

RepairUtil   D

Complexity

Total Complexity 58

Size/Duplication

Total Lines 374
Duplicated Lines 0 %

Test Coverage

Coverage 18.45%

Importance

Changes 0
Metric Value
eloc 203
dl 0
loc 374
ccs 38
cts 206
cp 0.1845
rs 4.5599
c 0
b 0
f 0
wmc 58

18 Methods

Rating   Name   Duplication   Size   Complexity  
A determineSpareParts() 0 11 1
A getRepairDuration() 0 16 3
B calculateNeededSpareParts() 0 37 8
A instantSelfRepair() 0 7 1
A internalSelfRepair() 0 26 4
A calculateNeededSystemComponents() 0 20 5
A selfRepair() 0 9 1
A getRepairTicks() 0 6 1
A __construct() 0 8 1
A determineFreeEngineerCount() 0 19 3
A createRepairTask() 0 12 1
A sendNeededAmountMessage() 0 51 5
A isRepairStationBonus() 0 11 2
A determineRepairOptions() 0 25 4
A determineHealingPercentage() 0 14 4
A getRepairDurationPreview() 0 15 3
A consumeSpareParts() 0 16 4
B enoughSparePartsOnEntity() 0 28 7

How to fix   Complexity   

Complex Class

Complex classes like RepairUtil often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use RepairUtil, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Stu\Component\Spacecraft\Repair;
6
7
use Override;
0 ignored issues
show
Bug introduced by
The type Override was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
8
use RuntimeException;
9
use Stu\Component\Building\BuildingFunctionEnum;
10
use Stu\Component\Colony\ColonyFunctionManagerInterface;
11
use Stu\Component\Crew\CrewEnum;
0 ignored issues
show
Bug introduced by
The type Stu\Component\Crew\CrewEnum was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
12
use Stu\Component\Spacecraft\SpacecraftStateEnum;
13
use Stu\Lib\Transfer\Storage\StorageManagerInterface;
14
use Stu\Component\Spacecraft\System\SpacecraftSystemTypeEnum;
15
use Stu\Module\Commodity\CommodityTypeEnum;
0 ignored issues
show
Bug introduced by
The type Stu\Module\Commodity\CommodityTypeEnum was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
16
use Stu\Module\Message\Lib\PrivateMessageFolderTypeEnum;
17
use Stu\Module\Message\Lib\PrivateMessageSenderInterface;
18
use Stu\Module\PlayerSetting\Lib\UserEnum;
0 ignored issues
show
Bug introduced by
The type Stu\Module\PlayerSetting\Lib\UserEnum was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
19
use Stu\Module\Spacecraft\Lib\SpacecraftWrapperInterface;
20
use Stu\Orm\Entity\ColonyInterface;
21
use Stu\Orm\Entity\RepairTaskInterface;
22
use Stu\Orm\Entity\SpacecraftInterface;
23
use Stu\Orm\Repository\ColonyShipRepairRepositoryInterface;
24
use Stu\Orm\Repository\RepairTaskRepositoryInterface;
25
use Stu\Orm\Repository\SpacecraftSystemRepositoryInterface;
26
27
//TODO unit tests
28
final class RepairUtil implements RepairUtilInterface
29
{
30 9
    public function __construct(
31
        private SpacecraftSystemRepositoryInterface $shipSystemRepository,
32
        private RepairTaskRepositoryInterface $repairTaskRepository,
33
        private ColonyShipRepairRepositoryInterface $colonyShipRepairRepository,
34
        private StorageManagerInterface $storageManager,
35
        private ColonyFunctionManagerInterface $colonyFunctionManager,
36
        private PrivateMessageSenderInterface $privateMessageSender
37 9
    ) {}
38
39
    //REPAIR STUFF
40
    #[Override]
41
    public function determineSpareParts(SpacecraftWrapperInterface $wrapper, bool $tickBased): array
42
    {
43
        $isRepairStationBonus = $this->isRepairStationBonus($wrapper);
44
45
        $neededSpareParts = $this->calculateNeededSpareParts($wrapper, $isRepairStationBonus, $tickBased);
46
        $neededSystemComponents = $this->calculateNeededSystemComponents($wrapper, $isRepairStationBonus, $tickBased);
47
48
        return [
49
            CommodityTypeEnum::COMMODITY_SPARE_PART => $neededSpareParts,
50
            CommodityTypeEnum::COMMODITY_SYSTEM_COMPONENT => $neededSystemComponents
51
        ];
52
    }
53
54
    private function calculateNeededSpareParts(SpacecraftWrapperInterface $wrapper, bool $isRepairStationBonus, bool $tickBased): int
55
    {
56
        $neededSpareParts = 0;
57
        $ship = $wrapper->get();
58
        $hull = $ship->getHull();
59
        $maxHull = $ship->getMaxHull();
60
61
        if ($hull < $maxHull) {
62
            if ($tickBased) {
63
                $neededSpareParts += 1;
64
            } else {
65
                $hullRepairParts = ($maxHull - $hull) / RepairTaskConstants::HULL_HITPOINTS_PER_SPARE_PART;
0 ignored issues
show
Bug introduced by
The type Stu\Component\Spacecraft...air\RepairTaskConstants was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
66
                if ($isRepairStationBonus) {
67
                    $neededSpareParts += (int)ceil($hullRepairParts / 2);
68
                } else {
69
                    $neededSpareParts += (int)ceil($hullRepairParts);
70
                }
71
            }
72
        }
73
74
        $damagedSystems = $wrapper->getDamagedSystems();
75
        $maxSystems = $tickBased ? ($isRepairStationBonus ? 4 : 2) : count($damagedSystems);
76
        $systemCount = min(count($damagedSystems), $maxSystems);
77
78
        for ($i = 0; $i < $systemCount; $i++) {
79
            $system = $damagedSystems[$i];
80
            $systemLvl = $system->determineSystemLevel();
81
            $healingPercentage = (100 - $system->getStatus()) / 100;
82
            $systemRepairParts = $healingPercentage * RepairTaskConstants::SHIPYARD_PARTS_USAGE[$systemLvl][RepairTaskConstants::SPARE_PARTS_ONLY];
83
            if ($isRepairStationBonus) {
84
                $neededSpareParts += (int)ceil($systemRepairParts / 2);
85
            } else {
86
                $neededSpareParts += (int)ceil($systemRepairParts);
87
            }
88
        }
89
90
        return $neededSpareParts;
91
    }
92
93
    private function calculateNeededSystemComponents(SpacecraftWrapperInterface $wrapper, bool $isRepairStationBonus, bool $tickBased): int
94
    {
95
        $neededSystemComponents = 0;
96
        $damagedSystems = $wrapper->getDamagedSystems();
97
        $maxSystems = $tickBased ? ($isRepairStationBonus ? 4 : 2) : count($damagedSystems);
98
        $systemCount = min(count($damagedSystems), $maxSystems);
99
100
        for ($i = 0; $i < $systemCount; $i++) {
101
            $system = $damagedSystems[$i];
102
            $systemLvl = $system->determineSystemLevel();
103
            $healingPercentage = (100 - $system->getStatus()) / 100;
104
            $systemComponents = $healingPercentage * RepairTaskConstants::SHIPYARD_PARTS_USAGE[$systemLvl][RepairTaskConstants::SYSTEM_COMPONENTS_ONLY];
105
            if ($isRepairStationBonus) {
106
                $neededSystemComponents += (int)ceil($systemComponents / 2);
107
            } else {
108
                $neededSystemComponents += (int)ceil($systemComponents);
109
            }
110
        }
111
112
        return $neededSystemComponents;
113
    }
114
115
    #[Override]
116
    public function enoughSparePartsOnEntity(
117
        array $neededParts,
118
        ColonyInterface|SpacecraftInterface $entity,
119
        SpacecraftInterface $spacecraft
120
    ): bool {
121
        $neededSpareParts = $neededParts[CommodityTypeEnum::COMMODITY_SPARE_PART];
122
        $neededSystemComponents = $neededParts[CommodityTypeEnum::COMMODITY_SYSTEM_COMPONENT];
123
124
        if ($neededSpareParts > 0) {
125
            $spareParts = $entity->getStorage()->get(CommodityTypeEnum::COMMODITY_SPARE_PART);
126
127
            if ($spareParts === null || $spareParts->getAmount() < $neededSpareParts) {
128
                $this->sendNeededAmountMessage($neededSpareParts, $neededSystemComponents, $spacecraft, $entity);
129
                return false;
130
            }
131
        }
132
133
        if ($neededSystemComponents > 0) {
134
            $systemComponents = $entity->getStorage()->get(CommodityTypeEnum::COMMODITY_SYSTEM_COMPONENT);
135
136
            if ($systemComponents === null || $systemComponents->getAmount() < $neededSystemComponents) {
137
                $this->sendNeededAmountMessage($neededSpareParts, $neededSystemComponents, $spacecraft, $entity);
138
                return false;
139
            }
140
        }
141
142
        return true;
143
    }
144
145
    private function sendNeededAmountMessage(
146
        int $neededSpareParts,
147
        int $neededSystemComponents,
148
        SpacecraftInterface $spacecraft,
149
        ColonyInterface|SpacecraftInterface $entity
150
    ): void {
151
        $neededPartsString = sprintf(
152
            "%d %s%s",
153
            $neededSpareParts,
154
            CommodityTypeEnum::getDescription(CommodityTypeEnum::COMMODITY_SPARE_PART),
155
            ($neededSystemComponents > 0 ? sprintf(
156
                "\n%d %s",
157
                $neededSystemComponents,
158
                CommodityTypeEnum::getDescription(CommodityTypeEnum::COMMODITY_SYSTEM_COMPONENT)
159
            ) : '')
160
        );
161
162
        $isColony = $entity instanceof ColonyInterface;
163
164
        //PASSIVE REPAIR OF STATION BY WORKBEES
165
        if ($entity === $spacecraft) {
166
            $entityOwnerMessage = sprintf(
167
                "Die Reparatur der %s %s wurde in Sektor %s angehalten.\nEs werden folgende Waren benötigt:\n%s",
168
                $entity->getRump()->getName(),
169
                $spacecraft->getName(),
170
                $spacecraft->getSectorString(),
171
                $neededPartsString
172
            );
173
        } else {
174
            $entityOwnerMessage = $isColony ? sprintf(
175
                "Die Reparatur der %s von Siedler %s wurde in Sektor %s bei der Kolonie %s angehalten.\nEs werden folgende Waren benötigt:\n%s",
176
                $spacecraft->getName(),
177
                $spacecraft->getUser()->getName(),
178
                $spacecraft->getSectorString(),
179
                $entity->getName(),
180
                $neededPartsString
181
            ) : sprintf(
182
                "Die Reparatur der %s von Siedler %s wurde in Sektor %s bei der %s %s angehalten.\nEs werden folgende Waren benötigt:\n%s",
183
                $spacecraft->getName(),
184
                $spacecraft->getUser()->getName(),
185
                $spacecraft->getSectorString(),
186
                $entity->getRump()->getName(),
187
                $entity->getName(),
188
                $neededPartsString
189
            );
190
        }
191
        $this->privateMessageSender->send(
192
            UserEnum::USER_NOONE,
193
            $entity->getUser()->getId(),
194
            $entityOwnerMessage,
195
            $isColony ? PrivateMessageFolderTypeEnum::SPECIAL_COLONY : PrivateMessageFolderTypeEnum::SPECIAL_STATION
196
        );
197
    }
198
199
    #[Override]
200
    public function consumeSpareParts(array $neededParts, ColonyInterface|SpacecraftInterface $entity): void
201
    {
202
        foreach ($neededParts as $commodityKey => $amount) {
203
            //$this->loggerUtil->log(sprintf('consume, cid: %d, amount: %d', $commodityKey, $amount));
204
205
            if ($amount < 1) {
206
                continue;
207
            }
208
209
            $storage = $entity->getStorage()->get($commodityKey);
210
            if ($storage === null) {
211
                throw new RuntimeException('enoughSparePartsOnEntity should be called beforehand!');
212
            }
213
            $commodity = $storage->getCommodity();
214
            $this->storageManager->lowerStorage($entity, $commodity, $amount);
215
        }
216
    }
217
218
219
    //SELFREPAIR STUFF
220
221 1
    #[Override]
222
    public function determineFreeEngineerCount(SpacecraftInterface $ship): int
223
    {
224 1
        $engineerCount = 0;
225
226 1
        $engineerOptions = [];
227 1
        $nextNumber = 1;
228 1
        foreach ($ship->getCrewAssignments() as $shipCrew) {
229
            if (
230 1
                $shipCrew->getSlot() === CrewEnum::CREW_TYPE_TECHNICAL
231
                //&& $shipCrew->getRepairTask() === null
232
            ) {
233
                $engineerOptions[] = $nextNumber;
234
                $nextNumber++;
235
                $engineerCount++;
236
            }
237
        }
238
239 1
        return $engineerCount; //$engineerOptions;
240
    }
241
242 1
    #[Override]
243
    public function determineRepairOptions(SpacecraftWrapperInterface $wrapper): array
244
    {
245 1
        $repairOptions = [];
246
247 1
        $ship = $wrapper->get();
248
249
        //check for hull option
250 1
        $hullPercentage = (int) ($ship->getHull() * 100 / $ship->getMaxHull());
251 1
        if ($hullPercentage < RepairTaskConstants::BOTH_MAX) {
252
            $hullSystem = $this->shipSystemRepository->prototype();
253
            $hullSystem->setSystemType(SpacecraftSystemTypeEnum::SYSTEM_HULL);
254
            $hullSystem->setStatus($hullPercentage);
255
256
            $repairOptions[SpacecraftSystemTypeEnum::SYSTEM_HULL->value] = $hullSystem;
257
        }
258
259
        //check for system options
260 1
        foreach ($wrapper->getDamagedSystems() as $system) {
261
            if ($system->getStatus() < RepairTaskConstants::BOTH_MAX) {
262
                $repairOptions[$system->getSystemType()->value] = $system;
263
            }
264
        }
265
266 1
        return $repairOptions;
267
    }
268
269
    #[Override]
270
    public function createRepairTask(SpacecraftInterface $ship, SpacecraftSystemTypeEnum $systemType, int $repairType, int $finishTime): void
271
    {
272
        $obj = $this->repairTaskRepository->prototype();
273
274
        $obj->setUser($ship->getUser());
275
        $obj->setSpacecraft($ship);
276
        $obj->setSystemType($systemType);
277
        $obj->setHealingPercentage($this->determineHealingPercentage($repairType));
278
        $obj->setFinishTime($finishTime);
279
280
        $this->repairTaskRepository->save($obj);
281
    }
282
283
    #[Override]
284
    public function determineHealingPercentage(int $repairType): int
285
    {
286
        $percentage = 0;
287
288
        if ($repairType === RepairTaskConstants::SPARE_PARTS_ONLY) {
289
            $percentage += random_int(RepairTaskConstants::SPARE_PARTS_ONLY_MIN, RepairTaskConstants::SPARE_PARTS_ONLY_MAX);
290
        } elseif ($repairType === RepairTaskConstants::SYSTEM_COMPONENTS_ONLY) {
291
            $percentage += random_int(RepairTaskConstants::SYSTEM_COMPONENTS_ONLY_MIN, RepairTaskConstants::SYSTEM_COMPONENTS_ONLY_MAX);
292
        } elseif ($repairType === RepairTaskConstants::BOTH) {
293
            $percentage += random_int(RepairTaskConstants::BOTH_MIN, RepairTaskConstants::BOTH_MAX);
294
        }
295
296
        return $percentage;
297
    }
298
299
    #[Override]
300
    public function instantSelfRepair(SpacecraftInterface $spacecraft, SpacecraftSystemTypeEnum $systemType, int $healingPercentage): bool
301
    {
302
        return $this->internalSelfRepair(
303
            $spacecraft,
304
            $systemType,
305
            $healingPercentage
306
        );
307
    }
308
309
    #[Override]
310
    public function selfRepair(SpacecraftInterface $spacecraft, RepairTaskInterface $repairTask): bool
311
    {
312
        $systemType = $repairTask->getSystemType();
313
        $percentage = $repairTask->getHealingPercentage();
314
315
        $this->repairTaskRepository->delete($repairTask);
316
317
        return $this->internalSelfRepair($spacecraft, $systemType, $percentage);
318
    }
319
320
    private function internalSelfRepair(SpacecraftInterface $spacecraft, SpacecraftSystemTypeEnum $systemType, int $percentage): bool
321
    {
322
        $result = true;
323
324
        if ($systemType === SpacecraftSystemTypeEnum::SYSTEM_HULL) {
325
            $hullPercentage = (int) ($spacecraft->getHull() * 100 / $spacecraft->getMaxHull());
326
327
            if ($hullPercentage > $percentage) {
328
                $result = false;
329
            } else {
330
                $spacecraft->setHuell((int)($spacecraft->getMaxHull() * $percentage / 100));
331
            }
332
        } else {
333
            $system = $spacecraft->getShipSystem($systemType);
334
335
            if ($system->getStatus() > $percentage) {
336
                $result = false;
337
            } else {
338
                $system->setStatus($percentage);
339
                $this->shipSystemRepository->save($system);
340
            }
341
        }
342
343
        $spacecraft->setState(SpacecraftStateEnum::SHIP_STATE_NONE);
344
345
        return $result;
346
    }
347
348
    #[Override]
349
    public function isRepairStationBonus(SpacecraftWrapperInterface $wrapper): bool
350
    {
351
        $ship = $wrapper->get();
352
353
        $colony = $ship->isOverColony();
354
        if ($colony === null) {
355
            return false;
356
        }
357
358
        return $this->colonyFunctionManager->hasActiveFunction($colony, BuildingFunctionEnum::BUILDING_FUNCTION_REPAIR_SHIPYARD);
359
    }
360
361 6
    #[Override]
362
    public function getRepairDuration(SpacecraftWrapperInterface $wrapper): int
363
    {
364 6
        $ship = $wrapper->get();
365 6
        $ticks = $this->getRepairTicks($wrapper);
366
367
        //check if repair station is active
368 6
        $colonyRepair = $this->colonyShipRepairRepository->getByShip($ship->getId());
369 6
        if ($colonyRepair !== null) {
370 2
            $isRepairStationBonus = $this->colonyFunctionManager->hasActiveFunction($colonyRepair->getColony(), BuildingFunctionEnum::BUILDING_FUNCTION_REPAIR_SHIPYARD);
371 2
            if ($isRepairStationBonus) {
372 1
                $ticks = (int)ceil($ticks / 2);
373
            }
374
        }
375
376 6
        return $ticks;
377
    }
378
379 3
    #[Override]
380
    public function getRepairDurationPreview(SpacecraftWrapperInterface $wrapper): int
381
    {
382 3
        $ship = $wrapper->get();
383 3
        $ticks = $this->getRepairTicks($wrapper);
384
385 3
        $colony = $ship->isOverColony();
386 3
        if ($colony !== null) {
387 2
            $isRepairStationBonus = $this->colonyFunctionManager->hasActiveFunction($colony, BuildingFunctionEnum::BUILDING_FUNCTION_REPAIR_SHIPYARD);
388 2
            if ($isRepairStationBonus) {
389 1
                $ticks = (int)ceil($ticks / 2);
390
            }
391
        }
392
393 3
        return $ticks;
394
    }
395
396 9
    private function getRepairTicks(SpacecraftWrapperInterface $wrapper): int
397
    {
398 9
        $ship = $wrapper->get();
399 9
        $ticks = (int) ceil(($ship->getMaxHull() - $ship->getHull()) / $ship->getRepairRate());
400
401 9
        return max($ticks, (int) ceil(count($wrapper->getDamagedSystems()) / 2));
402
    }
403
}
404