Passed
Pull Request — master (#1930)
by Janko
14:58 queued 05:13
created

RepairUtil   D

Complexity

Total Complexity 59

Size/Duplication

Total Lines 369
Duplicated Lines 0 %

Test Coverage

Coverage 11.11%

Importance

Changes 2
Bugs 1 Features 0
Metric Value
eloc 206
dl 0
loc 369
ccs 23
cts 207
cp 0.1111
rs 4.08
c 2
b 1
f 0
wmc 59

18 Methods

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

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