Passed
Push — dev ( 996fbb...25004f )
by Janko
16:40
created

SpacecraftWrapper   F

Complexity

Total Complexity 69

Size/Duplication

Total Lines 435
Duplicated Lines 0 %

Test Coverage

Coverage 66.99%

Importance

Changes 0
Metric Value
eloc 191
dl 0
loc 435
ccs 138
cts 206
cp 0.6699
rs 2.88
c 0
b 0
f 0
wmc 69

36 Methods

Rating   Name   Duplication   Size   Complexity  
A reloadEpsUsage() 0 11 3
A isUnalerted() 0 5 2
A setAlertState() 0 7 1
A getReactorUsage() 0 8 2
B getReactorWrapper() 0 35 6
A __construct() 0 13 1
A getSpacecraftWrapperFactory() 0 4 1
A lowerEpsUsage() 0 4 1
A getEpsUsage() 0 7 2
A getAlertState() 0 4 1
A get() 0 4 1
A getSpacecraftSystemManager() 0 4 1
A canMan() 0 8 3
A getRepairCosts() 0 11 1
A getSpecificShipSystem() 0 8 1
A getPossibleTorpedoTypes() 0 8 2
A getCrewStyle() 0 11 3
A getComputerSystemDataMandatory() 0 12 2
A __toString() 0 12 1
A getShieldSystemData() 0 6 1
A getWarpDriveSystemData() 0 6 1
A getEpsSystemData() 0 6 1
A isSelectable() 0 5 2
A getDamagedSystems() 0 24 5
A getLssSystemData() 0 6 1
A canFire() 0 13 4
A canBeRepaired() 0 20 5
A getTakeoverTicksLeft() 0 11 2
A getHullSystemData() 0 13 2
A getRepairDurationPreview() 0 4 1
A getProjectileLauncherSystemData() 0 6 1
A getTractoredShipWrapper() 0 9 2
A getStateIconAndTitle() 0 4 1
A getRepairDuration() 0 4 1
A getShieldRegenerationRate() 0 15 3
A getEnergyWeaponSystemData() 0 6 1

How to fix   Complexity   

Complex Class

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

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Stu\Module\Spacecraft\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\Spacecraft\Repair\RepairUtilInterface;
12
use Stu\Component\Spacecraft\SpacecraftAlertStateEnum;
13
use Stu\Component\Spacecraft\System\Data\AbstractSystemData;
14
use Stu\Component\Spacecraft\System\Data\ComputerSystemData;
15
use Stu\Component\Spacecraft\System\Data\EnergyWeaponSystemData;
16
use Stu\Component\Spacecraft\System\Data\EpsSystemData;
17
use Stu\Component\Spacecraft\System\Data\FusionCoreSystemData;
18
use Stu\Component\Spacecraft\System\Data\HullSystemData;
19
use Stu\Component\Spacecraft\System\Data\LssSystemData;
20
use Stu\Component\Spacecraft\System\Data\ProjectileLauncherSystemData;
21
use Stu\Component\Spacecraft\System\Data\ShieldSystemData;
22
use Stu\Component\Spacecraft\System\Data\SingularityCoreSystemData;
23
use Stu\Component\Spacecraft\System\Data\WarpCoreSystemData;
24
use Stu\Component\Spacecraft\System\Data\WarpDriveSystemData;
25
use Stu\Component\Spacecraft\System\Exception\SystemNotFoundException;
26
use Stu\Component\Spacecraft\System\SpacecraftSystemManagerInterface;
27
use Stu\Component\Spacecraft\System\SpacecraftSystemTypeEnum;
28
use Stu\Component\Spacecraft\System\SystemDataDeserializerInterface;
29
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...
30
use Stu\Module\Control\GameControllerInterface;
31
use Stu\Module\Spacecraft\Lib\Interaction\ShipTakeoverManagerInterface;
32
use Stu\Module\Spacecraft\Lib\ReactorWrapper;
33
use Stu\Module\Spacecraft\Lib\ReactorWrapperInterface;
34
use Stu\Module\Spacecraft\Lib\ShipRepairCost;
35
use Stu\Module\Spacecraft\Lib\SpacecraftStateChangerInterface;
36
use Stu\Module\Spacecraft\Lib\SpacecraftWrapperFactoryInterface;
37
use Stu\Module\Ship\Lib\ShipWrapperInterface;
38
use Stu\Module\Spacecraft\Lib\Ui\StateIconAndTitle;
39
use Stu\Orm\Entity\SpacecraftSystemInterface;
40
use Stu\Orm\Entity\ShipTakeoverInterface;
41
use Stu\Orm\Entity\SpacecraftInterface;
42
use Stu\Orm\Repository\TorpedoTypeRepositoryInterface;
43
44
//TODO increase coverage
45
/**
46
 * @template T of SpacecraftInterface
47
 */
48
abstract class SpacecraftWrapper implements SpacecraftWrapperInterface
49
{
50
    /** @var Collection<int, AbstractSystemData> */
51
    private Collection $shipSystemDataCache;
52
53
    private ?ReactorWrapperInterface $reactorWrapper = null;
54
55
    private ?int $epsUsage = null;
56
57
    /**
58
     * @param T $spacecraft
59
     */
60 66
    public function __construct(
61
        protected SpacecraftInterface $spacecraft,
62
        private SpacecraftSystemManagerInterface $spacecraftSystemManager,
63
        private SystemDataDeserializerInterface $systemDataDeserializer,
64
        private TorpedoTypeRepositoryInterface $torpedoTypeRepository,
65
        protected GameControllerInterface $game,
66
        protected SpacecraftWrapperFactoryInterface $spacecraftWrapperFactory,
67
        private SpacecraftStateChangerInterface $spacecraftStateChanger,
68
        private RepairUtilInterface $repairUtil,
69
        private StateIconAndTitle $stateIconAndTitle
70
    ) {
71
72 66
        $this->shipSystemDataCache = new ArrayCollection();
73
    }
74
75
    #[Override]
76
    public function get(): SpacecraftInterface
77
    {
78
        return $this->spacecraft;
79
    }
80
81
    #[Override]
82
    public function getSpacecraftWrapperFactory(): SpacecraftWrapperFactoryInterface
83
    {
84
        return $this->spacecraftWrapperFactory;
85
    }
86
87
    #[Override]
88
    public function getSpacecraftSystemManager(): SpacecraftSystemManagerInterface
89
    {
90
        return $this->spacecraftSystemManager;
91
    }
92
93 3
    #[Override]
94
    public function getEpsUsage(): int
95
    {
96 3
        if ($this->epsUsage === null) {
97 3
            $this->epsUsage = $this->reloadEpsUsage();
98
        }
99 3
        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...
100
    }
101
102
    #[Override]
103
    public function lowerEpsUsage(int $value): void
104
    {
105
        $this->epsUsage -= $value;
106
    }
107
108 3
    private function reloadEpsUsage(): int
109
    {
110 3
        $result = 0;
111
112 3
        foreach ($this->spacecraftSystemManager->getActiveSystems($this->spacecraft) as $shipSystem) {
113 3
            $result += $this->spacecraftSystemManager->getEnergyConsumption($shipSystem->getSystemType());
114
        }
115
116 3
        return $this->get()->hasComputer()
117 3
            ? $result + $this->getComputerSystemDataMandatory()->getAlertState()->getEpsUsage()
118 3
            : $result;
119
    }
120
121 2
    public function getReactorUsage(): int
122
    {
123 2
        $reactor = $this->reactorWrapper;
124 2
        if ($reactor === null) {
125
            throw new RuntimeException('this should not happen');
126
        }
127
128 2
        return $this->getEpsUsage() + $reactor->getUsage();
129
    }
130
131 9
    #[Override]
132
    public function getReactorWrapper(): ?ReactorWrapperInterface
133
    {
134 9
        if ($this->reactorWrapper === null) {
135 9
            $ship = $this->spacecraft;
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->spacecraft of type Stu\Orm\Entity\SpacecraftInterface is incompatible with the declared type Stu\Module\Spacecraft\Lib\T of property $spacecraft.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
136 9
            $reactorSystemData = null;
137
138
139 9
            if ($ship->hasSpacecraftSystem(SpacecraftSystemTypeEnum::WARPCORE)) {
140 5
                $reactorSystemData = $this->getSpecificShipSystem(
141 5
                    SpacecraftSystemTypeEnum::WARPCORE,
142 5
                    WarpCoreSystemData::class
143 5
                );
144
            }
145 9
            if ($ship->hasSpacecraftSystem(SpacecraftSystemTypeEnum::SINGULARITY_REACTOR)) {
146
                $reactorSystemData = $this->getSpecificShipSystem(
147
                    SpacecraftSystemTypeEnum::SINGULARITY_REACTOR,
148
                    SingularityCoreSystemData::class
149
                );
150
            }
151 9
            if ($ship->hasSpacecraftSystem(SpacecraftSystemTypeEnum::FUSION_REACTOR)) {
152
                $reactorSystemData = $this->getSpecificShipSystem(
153
                    SpacecraftSystemTypeEnum::FUSION_REACTOR,
154
                    FusionCoreSystemData::class
155
                );
156
            }
157
158 9
            if ($reactorSystemData === null) {
159 6
                return null;
160
            }
161
162 5
            $this->reactorWrapper = new ReactorWrapper($this, $reactorSystemData);
163
        }
164
165 5
        return $this->reactorWrapper;
166
    }
167
168 3
    #[Override]
169
    public function getAlertState(): SpacecraftAlertStateEnum
170
    {
171 3
        return $this->getComputerSystemDataMandatory()->getAlertState();
172
    }
173
174
    #[Override]
175
    public function setAlertState(SpacecraftAlertStateEnum $alertState): ?string
176
    {
177
        $msg = $this->spacecraftStateChanger->changeAlertState($this, $alertState);
178
        $this->epsUsage = $this->reloadEpsUsage();
179
180
        return $msg;
181
    }
182
183 2
    #[Override]
184
    public function isUnalerted(): bool
185
    {
186 2
        return !$this->spacecraft->hasSpacecraftSystem(SpacecraftSystemTypeEnum::COMPUTER)
187 2
            || $this->getComputerSystemDataMandatory()->isAlertGreen();
188
    }
189
190 3
    #[Override]
191
    public function getShieldRegenerationRate(): int
192
    {
193 3
        $regenerationPercentage = $this->get()->isSystemHealthy(SpacecraftSystemTypeEnum::SHIELDS) ? 10 : 0;
194
195 3
        $shield = $this->get()->getCondition()->getShield();
196 3
        $maxshield = $this->get()->getMaxShield();
197
198 3
        $result = (int) ceil(($maxshield / 100) * $regenerationPercentage);
199
200 3
        if ($result > $maxshield - $shield) {
201 1
            $result = $maxshield - $shield;
202
        }
203
204 3
        return $result;
205
    }
206
207
    /**
208
     * highest damage first, then prio
209
     *
210
     * @return SpacecraftSystemInterface[]
211
     */
212 3
    #[Override]
213
    public function getDamagedSystems(): array
214
    {
215 3
        $damagedSystems = [];
216 3
        $prioArray = [];
217 3
        foreach ($this->spacecraft->getSystems() as $system) {
218 2
            if ($system->getStatus() < 100) {
219
                $damagedSystems[] = $system;
220
                $prioArray[$system->getSystemType()->value] = $this->spacecraftSystemManager->lookupSystem($system->getSystemType())->getPriority();
221
            }
222
        }
223
224
        // sort by damage and priority
225 3
        usort(
226 3
            $damagedSystems,
227 3
            function (SpacecraftSystemInterface $a, SpacecraftSystemInterface $b) use ($prioArray): int {
228
                if ($a->getStatus() === $b->getStatus()) {
229
                    return $prioArray[$b->getSystemType()->value] <=> $prioArray[$a->getSystemType()->value];
230
                }
231
                return ($a->getStatus() < $b->getStatus()) ? -1 : 1;
232 3
            }
233 3
        );
234
235 3
        return $damagedSystems;
236
    }
237
238 10
    #[Override]
239
    public function isSelectable(): bool
240
    {
241 10
        return $this->game->getUser() === $this->spacecraft->getUser()
242 10
            && $this->spacecraft->getType()->getModuleView() !== null;
243
    }
244
245 2
    #[Override]
246
    public function canBeRepaired(): bool
247
    {
248 2
        if (!$this->isUnalerted()) {
249
            return false;
250
        }
251
252 2
        if ($this->spacecraft->isShielded()) {
253
            return false;
254
        }
255
256 2
        if ($this->spacecraft->isCloaked()) {
257
            return false;
258
        }
259
260 2
        if ($this->getDamagedSystems() !== []) {
261
            return true;
262
        }
263
264 2
        return $this->spacecraft->getCondition()->getHull() < $this->spacecraft->getMaxHull();
265
    }
266
267 4
    #[Override]
268
    public function canFire(): bool
269
    {
270 4
        $ship = $this->spacecraft;
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->spacecraft of type Stu\Orm\Entity\SpacecraftInterface is incompatible with the declared type Stu\Module\Spacecraft\Lib\T of property $spacecraft.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
271 4
        if (!$ship->getNbs()) {
272 1
            return false;
273
        }
274 3
        if (!$ship->hasActiveWeapon()) {
275 1
            return false;
276
        }
277
278 2
        $epsSystem = $this->getEpsSystemData();
279 2
        return $epsSystem !== null && $epsSystem->getEps() !== 0;
280
    }
281
282 2
    #[Override]
283
    public function canMan(): bool
284
    {
285 2
        $buildplan = $this->spacecraft->getBuildplan();
286
287 2
        return $buildplan !== null
288 2
            && $buildplan->getCrew() > 0
289 2
            && $this->spacecraft->hasSpacecraftSystem(SpacecraftSystemTypeEnum::LIFE_SUPPORT);
290
    }
291
292
    #[Override]
293
    public function getRepairDuration(): int
294
    {
295
        return $this->repairUtil->getRepairDuration($this);
296
    }
297
298
    #[Override]
299
    public function getRepairDurationPreview(): int
300
    {
301
        return $this->repairUtil->getRepairDurationPreview($this);
302
    }
303
304
    #[Override]
305
    public function getRepairCosts(): array
306
    {
307
        $neededParts = $this->repairUtil->determineSpareParts($this, false);
308
309
        $neededSpareParts = $neededParts[CommodityTypeEnum::COMMODITY_SPARE_PART];
310
        $neededSystemComponents = $neededParts[CommodityTypeEnum::COMMODITY_SYSTEM_COMPONENT];
311
312
        return [
313
            new ShipRepairCost($neededSpareParts, CommodityTypeEnum::COMMODITY_SPARE_PART, CommodityTypeEnum::getDescription(CommodityTypeEnum::COMMODITY_SPARE_PART)),
314
            new ShipRepairCost($neededSystemComponents, CommodityTypeEnum::COMMODITY_SYSTEM_COMPONENT, CommodityTypeEnum::getDescription(CommodityTypeEnum::COMMODITY_SYSTEM_COMPONENT))
315
        ];
316
    }
317
318
    #[Override]
319
    public function getPossibleTorpedoTypes(): array
320
    {
321
        if ($this->spacecraft->hasSpacecraftSystem(SpacecraftSystemTypeEnum::TORPEDO_STORAGE)) {
322
            return $this->torpedoTypeRepository->getAll();
323
        }
324
325
        return $this->torpedoTypeRepository->getByLevel($this->spacecraft->getRump()->getTorpedoLevel());
326
    }
327
328
    #[Override]
329
    public function getTractoredShipWrapper(): ?ShipWrapperInterface
330
    {
331
        $tractoredShip = $this->spacecraft->getTractoredShip();
332
        if ($tractoredShip === null) {
333
            return null;
334
        }
335
336
        return $this->spacecraftWrapperFactory->wrapShip($tractoredShip);
337
    }
338
339 4
    #[Override]
340
    public function getStateIconAndTitle(): ?array
341
    {
342 4
        return $this->stateIconAndTitle->getStateIconAndTitle($this);
343
    }
344
345
    #[Override]
346
    public function getTakeoverTicksLeft(?ShipTakeoverInterface $takeover = null): int
347
    {
348
        $takeover ??= $this->spacecraft->getTakeoverActive();
349
        if ($takeover === null) {
350
            throw new RuntimeException('should not call when active takeover is null');
351
        }
352
353
        $currentTurn = $this->game->getCurrentRound()->getTurn();
354
355
        return $takeover->getStartTurn() + ShipTakeoverManagerInterface::TURNS_TO_TAKEOVER - $currentTurn;
356
    }
357
358 6
    #[Override]
359
    public function getCrewStyle(): string
360
    {
361 6
        $ship = $this->spacecraft;
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->spacecraft of type Stu\Orm\Entity\SpacecraftInterface is incompatible with the declared type Stu\Module\Spacecraft\Lib\T of property $spacecraft.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
362 6
        $excessCrew = $ship->getExcessCrewCount();
363
364 6
        if ($excessCrew === 0) {
365 5
            return "";
366
        }
367
368 3
        return $excessCrew > 0 ? "color: green;" : "color: red;";
369
    }
370
371 10
    #[Override]
372
    public function getHullSystemData(): HullSystemData
373
    {
374 10
        $hullSystemData = $this->getSpecificShipSystem(
375 10
            SpacecraftSystemTypeEnum::HULL,
376 10
            HullSystemData::class
377 10
        );
378
379 10
        if ($hullSystemData === null) {
380
            throw new SystemNotFoundException('no hull installed?');
381
        }
382
383 10
        return $hullSystemData;
384
    }
385
386 9
    #[Override]
387
    public function getShieldSystemData(): ?ShieldSystemData
388
    {
389 9
        return $this->getSpecificShipSystem(
390 9
            SpacecraftSystemTypeEnum::SHIELDS,
391 9
            ShieldSystemData::class
392 9
        );
393
    }
394
395 20
    #[Override]
396
    public function getEpsSystemData(): ?EpsSystemData
397
    {
398 20
        return $this->getSpecificShipSystem(
399 20
            SpacecraftSystemTypeEnum::EPS,
400 20
            EpsSystemData::class
401 20
        );
402
    }
403
404 5
    #[Override]
405
    public function getComputerSystemDataMandatory(): ComputerSystemData
406
    {
407 5
        $computer = $this->getSpecificShipSystem(
408 5
            SpacecraftSystemTypeEnum::COMPUTER,
409 5
            ComputerSystemData::class
410 5
        );
411 5
        if ($computer === null) {
412
            throw new SystemNotFoundException('no computer installed?');
413
        }
414
415 5
        return $computer;
416
    }
417
418 3
    #[Override]
419
    public function getLssSystemData(): ?LssSystemData
420
    {
421 3
        return $this->getSpecificShipSystem(
422 3
            SpacecraftSystemTypeEnum::LSS,
423 3
            LssSystemData::class
424 3
        );
425
    }
426
427 1
    #[Override]
428
    public function getEnergyWeaponSystemData(): ?EnergyWeaponSystemData
429
    {
430 1
        return $this->getSpecificShipSystem(
431 1
            SpacecraftSystemTypeEnum::PHASER,
432 1
            EnergyWeaponSystemData::class
433 1
        );
434
    }
435
436 10
    #[Override]
437
    public function getWarpDriveSystemData(): ?WarpDriveSystemData
438
    {
439 10
        return $this->getSpecificShipSystem(
440 10
            SpacecraftSystemTypeEnum::WARPDRIVE,
441 10
            WarpDriveSystemData::class
442 10
        );
443
    }
444
445 1
    #[Override]
446
    public function getProjectileLauncherSystemData(): ?ProjectileLauncherSystemData
447
    {
448 1
        return $this->getSpecificShipSystem(
449 1
            SpacecraftSystemTypeEnum::TORPEDO,
450 1
            ProjectileLauncherSystemData::class
451 1
        );
452
    }
453
454
    /**
455
     * @template T2
456
     * @param class-string<T2> $className
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<T2> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<T2>.
Loading history...
457
     *
458
     * @return T2|null
459
     */
460 26
    protected function getSpecificShipSystem(SpacecraftSystemTypeEnum $systemType, string $className)
461
    {
462 26
        return $this->systemDataDeserializer->getSpecificShipSystem(
463 26
            $this->spacecraft,
464 26
            $systemType,
465 26
            $className,
466 26
            $this->shipSystemDataCache,
467 26
            $this->spacecraftWrapperFactory
468 26
        );
469
    }
470
471
    #[Override]
472
    public function __toString(): string
473
    {
474
        $systems = implode(",\n", $this->spacecraft->getSystems()
475
            ->filter(fn($system): bool => $system->getData() !== null)
476
            ->map(fn($system): string => $system->__toString())
477
            ->toArray());
478
479
        return sprintf(
480
            "spacecraft: {%s,\n  systems: [\n%s\n}\n]",
481
            $this->spacecraft->__toString(),
482
            $systems
483
        );
484
    }
485
}
486