Passed
Push — master ( 77a570...adeb98 )
by Nico
26:43
created

ShipWrapper   F

Complexity

Total Complexity 80

Size/Duplication

Total Lines 485
Duplicated Lines 0 %

Test Coverage

Coverage 13.66%

Importance

Changes 2
Bugs 1 Features 0
Metric Value
eloc 211
dl 0
loc 485
ccs 31
cts 227
cp 0.1366
rs 2
c 2
b 1
f 0
wmc 80

39 Methods

Rating   Name   Duplication   Size   Complexity  
A lowerEpsUsage() 0 4 1
A reloadEpsUsage() 0 16 4
A getReactorUsage() 0 8 2
A getDamagedSystems() 0 24 5
A isOwnedByCurrentUser() 0 4 1
A getShipSystemManager() 0 4 1
A __construct() 0 14 1
A getFleetWrapper() 0 8 2
A getEpsUsage() 0 7 2
A canBeRepaired() 0 20 5
A get() 0 4 1
A setAlertState() 0 7 1
A getShipWrapperFactory() 0 4 1
A canLandOnCurrentColony() 0 22 6
B getReactorWrapper() 0 35 6
A getBussardCollectorSystemData() 0 6 1
A getProjectileLauncherSystemData() 0 6 1
A getRepairDurationPreview() 0 4 1
A canBeRetrofitted() 0 22 6
A getRepairCosts() 0 11 1
A getTakeoverTicksLeft() 0 11 2
A getCrewStyle() 0 11 3
A canFire() 0 13 4
A getSpecificShipSystem() 0 8 1
A getShieldSystemData() 0 6 1
A getHullSystemData() 0 13 2
A getAggregationSystemSystemData() 0 6 1
A getPossibleTorpedoTypes() 0 8 2
A getAstroLaboratorySystemData() 0 6 1
A getTractoredShipWrapper() 0 9 2
A getEpsSystemData() 0 6 1
A getTractoringShipWrapper() 0 9 2
A getWarpDriveSystemData() 0 6 1
A getTrackerSystemData() 0 6 1
A getRepairDuration() 0 4 1
A getDockedToShipWrapper() 0 9 2
A canBeScrapped() 0 6 2
A getStateIconAndTitle() 0 4 1
A getWebEmitterSystemData() 0 6 1

How to fix   Complexity   

Complex Class

Complex classes like ShipWrapper 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 ShipWrapper, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Stu\Module\Ship\Lib;
6
7
use Doctrine\Common\Collections\ArrayCollection;
8
use Doctrine\Common\Collections\Collection;
9
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...
10
use RuntimeException;
11
use Stu\Component\Ship\Repair\RepairUtilInterface;
12
use Stu\Component\Ship\ShipAlertStateEnum;
13
use Stu\Component\Ship\ShipStateEnum;
14
use Stu\Component\Ship\System\Data\AbstractSystemData;
15
use Stu\Component\Ship\System\Data\AstroLaboratorySystemData;
16
use Stu\Component\Ship\System\Data\AggregationSystemSystemData;
17
use Stu\Component\Ship\System\Data\BussardCollectorSystemData;
18
use Stu\Component\Ship\System\Data\EpsSystemData;
19
use Stu\Component\Ship\System\Data\FusionCoreSystemData;
20
use Stu\Component\Ship\System\Data\HullSystemData;
21
use Stu\Component\Ship\System\Data\ProjectileLauncherSystemData;
22
use Stu\Component\Ship\System\Data\ShieldSystemData;
23
use Stu\Component\Ship\System\Data\SingularityCoreSystemData;
24
use Stu\Component\Ship\System\Data\TrackerSystemData;
25
use Stu\Component\Ship\System\Data\WarpCoreSystemData;
26
use Stu\Component\Ship\System\Data\WarpDriveSystemData;
27
use Stu\Component\Ship\System\Data\WebEmitterSystemData;
28
use Stu\Component\Ship\System\Exception\SystemNotFoundException;
29
use Stu\Component\Ship\System\ShipSystemManagerInterface;
30
use Stu\Component\Ship\System\ShipSystemTypeEnum;
31
use Stu\Component\Ship\System\SystemDataDeserializerInterface;
32
use Stu\Module\Colony\Lib\ColonyLibFactoryInterface;
33
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...
34
use Stu\Module\Control\GameControllerInterface;
35
use Stu\Module\Ship\Lib\Interaction\ShipTakeoverManagerInterface;
36
use Stu\Module\Ship\Lib\Ui\StateIconAndTitle;
37
use Stu\Orm\Entity\ShipInterface;
38
use Stu\Orm\Entity\ShipSystemInterface;
39
use Stu\Orm\Entity\ShipTakeoverInterface;
40
use Stu\Orm\Repository\TorpedoTypeRepositoryInterface;
41
42
//TODO increase coverage
43
final class ShipWrapper implements ShipWrapperInterface
44
{
45
    /**
46
     * @var Collection<int, AbstractSystemData>
47
     */
48
    private Collection $shipSystemDataCache;
49
50
    private ?ReactorWrapperInterface $reactorWrapper = null;
51
52
    private ?int $epsUsage = null;
53
54 8
    public function __construct(
55
        private ShipInterface $ship,
56
        private ShipSystemManagerInterface $shipSystemManager,
57
        private SystemDataDeserializerInterface $systemDataDeserializer,
58
        private ColonyLibFactoryInterface $colonyLibFactory,
59
        private TorpedoTypeRepositoryInterface $torpedoTypeRepository,
60
        private GameControllerInterface $game,
61
        private ShipWrapperFactoryInterface $shipWrapperFactory,
62
        private ShipStateChangerInterface $shipStateChanger,
63
        private RepairUtilInterface $repairUtil,
64
        private StateIconAndTitle $stateIconAndTitle,
65
    ) {
66
67 8
        $this->shipSystemDataCache = new ArrayCollection();
68
    }
69
70 3
    #[Override]
71
    public function get(): ShipInterface
72
    {
73 3
        return $this->ship;
74
    }
75
76
    #[Override]
77
    public function getShipWrapperFactory(): ShipWrapperFactoryInterface
78
    {
79
        return $this->shipWrapperFactory;
80
    }
81
82
    #[Override]
83
    public function getShipSystemManager(): ShipSystemManagerInterface
84
    {
85
        return $this->shipSystemManager;
86
    }
87
88
    #[Override]
89
    public function getFleetWrapper(): ?FleetWrapperInterface
90
    {
91
        if ($this->ship->getFleet() === null) {
92
            return null;
93
        }
94
95
        return $this->shipWrapperFactory->wrapFleet($this->ship->getFleet());
96
    }
97
98
    #[Override]
99
    public function getEpsUsage(): int
100
    {
101
        if ($this->epsUsage === null) {
102
            $this->epsUsage = $this->reloadEpsUsage();
103
        }
104
        return $this->epsUsage;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->epsUsage could return the type null which is incompatible with the type-hinted return integer. Consider adding an additional type-check to rule them out.
Loading history...
105
    }
106
107
    #[Override]
108
    public function lowerEpsUsage(int $value): void
109
    {
110
        $this->epsUsage -= $value;
111
    }
112
113
    private function reloadEpsUsage(): int
114
    {
115
        $result = 0;
116
117
        foreach ($this->shipSystemManager->getActiveSystems($this->ship) as $shipSystem) {
118
            $result += $this->shipSystemManager->getEnergyConsumption($shipSystem->getSystemType());
119
        }
120
121
        if ($this->ship->getAlertState() == ShipAlertStateEnum::ALERT_YELLOW) {
122
            $result += ShipStateChangerInterface::ALERT_YELLOW_EPS_USAGE;
123
        }
124
        if ($this->ship->getAlertState() == ShipAlertStateEnum::ALERT_RED) {
125
            $result += ShipStateChangerInterface::ALERT_RED_EPS_USAGE;
126
        }
127
128
        return $result;
129
    }
130
131
    public function getReactorUsage(): int
132
    {
133
        $reactor = $this->reactorWrapper;
134
        if ($reactor === null) {
135
            throw new RuntimeException('this should not happen');
136
        }
137
138
        return $this->getEpsUsage() + $reactor->getUsage();
139
    }
140
141
    #[Override]
142
    public function getReactorWrapper(): ?ReactorWrapperInterface
143
    {
144
        if ($this->reactorWrapper === null) {
145
            $ship = $this->ship;
146
            $reactorSystemData = null;
147
148
149
            if ($ship->hasShipSystem(ShipSystemTypeEnum::SYSTEM_WARPCORE)) {
150
                $reactorSystemData = $this->getSpecificShipSystem(
151
                    ShipSystemTypeEnum::SYSTEM_WARPCORE,
152
                    WarpCoreSystemData::class
153
                );
154
            }
155
            if ($ship->hasShipSystem(ShipSystemTypeEnum::SYSTEM_SINGULARITY_REACTOR)) {
156
                $reactorSystemData = $this->getSpecificShipSystem(
157
                    ShipSystemTypeEnum::SYSTEM_SINGULARITY_REACTOR,
158
                    SingularityCoreSystemData::class
159
                );
160
            }
161
            if ($ship->hasShipSystem(ShipSystemTypeEnum::SYSTEM_FUSION_REACTOR)) {
162
                $reactorSystemData = $this->getSpecificShipSystem(
163
                    ShipSystemTypeEnum::SYSTEM_FUSION_REACTOR,
164
                    FusionCoreSystemData::class
165
                );
166
            }
167
168
            if ($reactorSystemData === null) {
169
                return null;
170
            }
171
172
            $this->reactorWrapper = new ReactorWrapper($this, $reactorSystemData);
173
        }
174
175
        return $this->reactorWrapper;
176
    }
177
178
    #[Override]
179
    public function setAlertState(ShipAlertStateEnum $alertState): ?string
180
    {
181
        $msg = $this->shipStateChanger->changeAlertState($this, $alertState);
182
        $this->epsUsage = $this->reloadEpsUsage();
183
184
        return $msg;
185
    }
186
187
    /**
188
     * highest damage first, then prio
189
     *
190
     * @return ShipSystemInterface[]
191
     */
192
    #[Override]
193
    public function getDamagedSystems(): array
194
    {
195
        $damagedSystems = [];
196
        $prioArray = [];
197
        foreach ($this->ship->getSystems() as $system) {
198
            if ($system->getStatus() < 100) {
199
                $damagedSystems[] = $system;
200
                $prioArray[$system->getSystemType()->value] = $this->shipSystemManager->lookupSystem($system->getSystemType())->getPriority();
201
            }
202
        }
203
204
        // sort by damage and priority
205
        usort(
206
            $damagedSystems,
207
            function (ShipSystemInterface $a, ShipSystemInterface $b) use ($prioArray): int {
208
                if ($a->getStatus() === $b->getStatus()) {
209
                    return $prioArray[$b->getSystemType()->value] <=> $prioArray[$a->getSystemType()->value];
210
                }
211
                return ($a->getStatus() < $b->getStatus()) ? -1 : 1;
212
            }
213
        );
214
215
        return $damagedSystems;
216
    }
217
218
    #[Override]
219
    public function isOwnedByCurrentUser(): bool
220
    {
221
        return $this->game->getUser() === $this->ship->getUser();
222
    }
223
224
    #[Override]
225
    public function canLandOnCurrentColony(): bool
226
    {
227
        if ($this->ship->getRump()->getCommodity() === null) {
228
            return false;
229
        }
230
        if ($this->ship->isShuttle()) {
231
            return false;
232
        }
233
234
        $currentColony = $this->ship->getStarsystemMap() !== null ? $this->ship->getStarsystemMap()->getColony() : null;
235
236
        if ($currentColony === null) {
237
            return false;
238
        }
239
        if ($currentColony->getUser() !== $this->ship->getUser()) {
240
            return false;
241
        }
242
243
        return $this->colonyLibFactory
244
            ->createColonySurface($currentColony)
245
            ->hasAirfield();
246
    }
247
248
    #[Override]
249
    public function canBeRepaired(): bool
250
    {
251
        if ($this->ship->getAlertState() !== ShipAlertStateEnum::ALERT_GREEN) {
252
            return false;
253
        }
254
255
        if ($this->ship->getShieldState()) {
256
            return false;
257
        }
258
259
        if ($this->ship->getCloakState()) {
260
            return false;
261
        }
262
263
        if ($this->getDamagedSystems() !== []) {
264
            return true;
265
        }
266
267
        return $this->ship->getHull() < $this->ship->getMaxHull();
268
    }
269
270
    #[Override]
271
    public function canBeRetrofitted(): bool
272
    {
273
        if ($this->ship->getAlertState() !== ShipAlertStateEnum::ALERT_GREEN) {
274
            return false;
275
        }
276
277
        if ($this->ship->getShieldState()) {
278
            return false;
279
        }
280
281
        if ($this->ship->getCloakState()) {
282
            return false;
283
        }
284
285
        if ($this->ship->getBuildplan() != null) {
286
            if ($this->ship->getBuildplan()->getUser() != $this->game->getUser()) {
287
                return false;
288
            }
289
        }
290
291
        return true;
292
    }
293
294 4
    #[Override]
295
    public function canFire(): bool
296
    {
297 4
        $ship = $this->ship;
298 4
        if (!$ship->getNbs()) {
299 1
            return false;
300
        }
301 3
        if (!$ship->hasActiveWeapon()) {
302 1
            return false;
303
        }
304
305 2
        $epsSystem = $this->getEpsSystemData();
306 2
        return $epsSystem !== null && $epsSystem->getEps() !== 0;
307
    }
308
309
    #[Override]
310
    public function getRepairDuration(): int
311
    {
312
        return $this->repairUtil->getRepairDuration($this);
313
    }
314
315
    #[Override]
316
    public function getRepairDurationPreview(): int
317
    {
318
        return $this->repairUtil->getRepairDurationPreview($this);
319
    }
320
321
    #[Override]
322
    public function getRepairCosts(): array
323
    {
324
        $neededParts = $this->repairUtil->determineSpareParts($this, false);
325
326
        $neededSpareParts = $neededParts[CommodityTypeEnum::COMMODITY_SPARE_PART];
327
        $neededSystemComponents = $neededParts[CommodityTypeEnum::COMMODITY_SYSTEM_COMPONENT];
328
329
        return [
330
            new ShipRepairCost($neededSpareParts, CommodityTypeEnum::COMMODITY_SPARE_PART, CommodityTypeEnum::getDescription(CommodityTypeEnum::COMMODITY_SPARE_PART)),
331
            new ShipRepairCost($neededSystemComponents, CommodityTypeEnum::COMMODITY_SYSTEM_COMPONENT, CommodityTypeEnum::getDescription(CommodityTypeEnum::COMMODITY_SYSTEM_COMPONENT))
332
        ];
333
    }
334
335
    #[Override]
336
    public function getPossibleTorpedoTypes(): array
337
    {
338
        if ($this->ship->hasShipSystem(ShipSystemTypeEnum::SYSTEM_TORPEDO_STORAGE)) {
339
            return $this->torpedoTypeRepository->getAll();
340
        }
341
342
        return $this->torpedoTypeRepository->getByLevel($this->ship->getRump()->getTorpedoLevel());
343
    }
344
345
    #[Override]
346
    public function getTractoredShipWrapper(): ?ShipWrapperInterface
347
    {
348
        $tractoredShip = $this->ship->getTractoredShip();
349
        if ($tractoredShip === null) {
350
            return null;
351
        }
352
353
        return $this->shipWrapperFactory->wrapShip($tractoredShip);
354
    }
355
356
    #[Override]
357
    public function getTractoringShipWrapper(): ?ShipWrapperInterface
358
    {
359
        $tractoringShip = $this->ship->getTractoringShip();
360
        if ($tractoringShip === null) {
361
            return null;
362
        }
363
364
        return $this->shipWrapperFactory->wrapShip($tractoringShip);
365
    }
366
367
    #[Override]
368
    public function getDockedToShipWrapper(): ?ShipWrapperInterface
369
    {
370
        $dockedTo = $this->ship->getDockedTo();
371
        if ($dockedTo === null) {
372
            return null;
373
        }
374
375
        return $this->shipWrapperFactory->wrapShip($dockedTo);
376
    }
377
378
    #[Override]
379
    public function getStateIconAndTitle(): ?array
380
    {
381
        return $this->stateIconAndTitle->getStateIconAndTitle($this);
382
    }
383
384
    #[Override]
385
    public function getTakeoverTicksLeft(?ShipTakeoverInterface $takeover = null): int
386
    {
387
        $takeover ??= $this->ship->getTakeoverActive();
388
        if ($takeover === null) {
389
            throw new RuntimeException('should not call when active takeover is null');
390
        }
391
392
        $currentTurn = $this->game->getCurrentRound()->getTurn();
393
394
        return $takeover->getStartTurn() + ShipTakeoverManagerInterface::TURNS_TO_TAKEOVER - $currentTurn;
395
    }
396
397
    #[Override]
398
    public function canBeScrapped(): bool
399
    {
400
        $ship = $this->ship;
401
402
        return $ship->isBase() && $ship->getState() !== ShipStateEnum::SHIP_STATE_UNDER_SCRAPPING;
403
    }
404
405
    #[Override]
406
    public function getCrewStyle(): string
407
    {
408
        $ship = $this->ship;
409
        $excessCrew = $ship->getExcessCrewCount();
410
411
        if ($excessCrew === 0) {
412
            return "";
413
        }
414
415
        return $excessCrew > 0 ? "color: green;" : "color: red;";
416
    }
417
418 1
    #[Override]
419
    public function getHullSystemData(): HullSystemData
420
    {
421 1
        $hullSystemData = $this->getSpecificShipSystem(
422 1
            ShipSystemTypeEnum::SYSTEM_HULL,
423 1
            HullSystemData::class
424 1
        );
425
426 1
        if ($hullSystemData === null) {
427
            throw new SystemNotFoundException('no hull installed?');
428
        }
429
430 1
        return $hullSystemData;
431
    }
432
433
    #[Override]
434
    public function getShieldSystemData(): ?ShieldSystemData
435
    {
436
        return $this->getSpecificShipSystem(
437
            ShipSystemTypeEnum::SYSTEM_SHIELDS,
438
            ShieldSystemData::class
439
        );
440
    }
441
442 2
    #[Override]
443
    public function getEpsSystemData(): ?EpsSystemData
444
    {
445 2
        return $this->getSpecificShipSystem(
446 2
            ShipSystemTypeEnum::SYSTEM_EPS,
447 2
            EpsSystemData::class
448 2
        );
449
    }
450
451
    #[Override]
452
    public function getWarpDriveSystemData(): ?WarpDriveSystemData
453
    {
454
        return $this->getSpecificShipSystem(
455
            ShipSystemTypeEnum::SYSTEM_WARPDRIVE,
456
            WarpDriveSystemData::class
457
        );
458
    }
459
460
    #[Override]
461
    public function getTrackerSystemData(): ?TrackerSystemData
462
    {
463
        return $this->getSpecificShipSystem(
464
            ShipSystemTypeEnum::SYSTEM_TRACKER,
465
            TrackerSystemData::class
466
        );
467
    }
468
469
    #[Override]
470
    public function getAggregationSystemSystemData(): ?AggregationSystemSystemData
471
    {
472
        return $this->getSpecificShipSystem(
473
            ShipSystemTypeEnum::SYSTEM_AGGREGATION_SYSTEM,
474
            AggregationSystemSystemData::class
475
        );
476
    }
477
478
    #[Override]
479
    public function getBussardCollectorSystemData(): ?BussardCollectorSystemData
480
    {
481
        return $this->getSpecificShipSystem(
482
            ShipSystemTypeEnum::SYSTEM_BUSSARD_COLLECTOR,
483
            BussardCollectorSystemData::class
484
        );
485
    }
486
487
    #[Override]
488
    public function getWebEmitterSystemData(): ?WebEmitterSystemData
489
    {
490
        return $this->getSpecificShipSystem(
491
            ShipSystemTypeEnum::SYSTEM_THOLIAN_WEB,
492
            WebEmitterSystemData::class
493
        );
494
    }
495
496
    #[Override]
497
    public function getAstroLaboratorySystemData(): ?AstroLaboratorySystemData
498
    {
499
        return $this->getSpecificShipSystem(
500
            ShipSystemTypeEnum::SYSTEM_ASTRO_LABORATORY,
501
            AstroLaboratorySystemData::class
502
        );
503
    }
504
505
    #[Override]
506
    public function getProjectileLauncherSystemData(): ?ProjectileLauncherSystemData
507
    {
508
        return $this->getSpecificShipSystem(
509
            ShipSystemTypeEnum::SYSTEM_TORPEDO,
510
            ProjectileLauncherSystemData::class
511
        );
512
    }
513
514
    /**
515
     * @template T
516
     * @param class-string<T> $className
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<T> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<T>.
Loading history...
517
     *
518
     * @return T|null
519
     */
520 3
    private function getSpecificShipSystem(ShipSystemTypeEnum $systemType, string $className)
521
    {
522 3
        return $this->systemDataDeserializer->getSpecificShipSystem(
523 3
            $this->ship,
524 3
            $systemType,
525 3
            $className,
526 3
            $this->shipSystemDataCache,
527 3
            $this->shipWrapperFactory
528 3
        );
529
    }
530
}