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

SpacecraftWrapper   C

Complexity

Total Complexity 55

Size/Duplication

Total Lines 352
Duplicated Lines 0 %

Test Coverage

Coverage 62.73%

Importance

Changes 0
Metric Value
eloc 150
dl 0
loc 352
ccs 101
cts 161
cp 0.6273
rs 6
c 0
b 0
f 0
wmc 55

28 Methods

Rating   Name   Duplication   Size   Complexity  
A reloadEpsUsage() 0 16 4
A getSpecificShipSystem() 0 8 1
A getRepairCosts() 0 11 1
A getPossibleTorpedoTypes() 0 8 2
A getCrewStyle() 0 11 3
A isOwnedByCurrentUser() 0 4 1
A getShieldSystemData() 0 6 1
A setAlertState() 0 7 1
A getWarpDriveSystemData() 0 6 1
A getReactorUsage() 0 8 2
A getEpsSystemData() 0 6 1
A __construct() 0 13 1
B getReactorWrapper() 0 35 6
A getSpacecraftWrapperFactory() 0 4 1
A getDamagedSystems() 0 24 5
A canFire() 0 13 4
A canBeRepaired() 0 20 5
A lowerEpsUsage() 0 4 1
A getTakeoverTicksLeft() 0 11 2
A getRepairDurationPreview() 0 4 1
A getHullSystemData() 0 13 2
A getEpsUsage() 0 7 2
A getProjectileLauncherSystemData() 0 6 1
A getTractoredShipWrapper() 0 9 2
A getStateIconAndTitle() 0 4 1
A getRepairDuration() 0 4 1
A get() 0 4 1
A getSpacecraftSystemManager() 0 4 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\EpsSystemData;
15
use Stu\Component\Spacecraft\System\Data\FusionCoreSystemData;
16
use Stu\Component\Spacecraft\System\Data\HullSystemData;
17
use Stu\Component\Spacecraft\System\Data\ProjectileLauncherSystemData;
18
use Stu\Component\Spacecraft\System\Data\ShieldSystemData;
19
use Stu\Component\Spacecraft\System\Data\SingularityCoreSystemData;
20
use Stu\Component\Spacecraft\System\Data\WarpCoreSystemData;
21
use Stu\Component\Spacecraft\System\Data\WarpDriveSystemData;
22
use Stu\Component\Spacecraft\System\Exception\SystemNotFoundException;
23
use Stu\Component\Spacecraft\System\SpacecraftSystemManagerInterface;
24
use Stu\Component\Spacecraft\System\SpacecraftSystemTypeEnum;
25
use Stu\Component\Spacecraft\System\SystemDataDeserializerInterface;
26
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...
27
use Stu\Module\Control\GameControllerInterface;
28
use Stu\Module\Spacecraft\Lib\Interaction\ShipTakeoverManagerInterface;
29
use Stu\Module\Spacecraft\Lib\ReactorWrapper;
30
use Stu\Module\Spacecraft\Lib\ReactorWrapperInterface;
31
use Stu\Module\Spacecraft\Lib\ShipRepairCost;
32
use Stu\Module\Spacecraft\Lib\SpacecraftStateChangerInterface;
33
use Stu\Module\Spacecraft\Lib\SpacecraftWrapperFactoryInterface;
34
use Stu\Module\Ship\Lib\ShipWrapperInterface;
35
use Stu\Module\Spacecraft\Lib\Ui\StateIconAndTitle;
36
use Stu\Orm\Entity\SpacecraftSystemInterface;
37
use Stu\Orm\Entity\ShipTakeoverInterface;
38
use Stu\Orm\Entity\SpacecraftInterface;
39
use Stu\Orm\Repository\TorpedoTypeRepositoryInterface;
40
41
//TODO increase coverage
42
/**
43
 * @template T of SpacecraftInterface
44
 */
45
abstract class SpacecraftWrapper implements SpacecraftWrapperInterface
46
{
47
    /** @var Collection<int, AbstractSystemData> */
48
    private Collection $shipSystemDataCache;
49
50
    private ?ReactorWrapperInterface $reactorWrapper = null;
51
52
    private ?int $epsUsage = null;
53
54
    /**
55
     * @param T $spacecraft
56
     */
57 59
    public function __construct(
58
        protected SpacecraftInterface $spacecraft,
59
        private SpacecraftSystemManagerInterface $spacecraftSystemManager,
60
        private SystemDataDeserializerInterface $systemDataDeserializer,
61
        private TorpedoTypeRepositoryInterface $torpedoTypeRepository,
62
        protected GameControllerInterface $game,
63
        protected SpacecraftWrapperFactoryInterface $spacecraftWrapperFactory,
64
        private SpacecraftStateChangerInterface $spacecraftStateChanger,
65
        private RepairUtilInterface $repairUtil,
66
        private StateIconAndTitle $stateIconAndTitle
67
    ) {
68
69 59
        $this->shipSystemDataCache = new ArrayCollection();
70
    }
71
72
    #[Override]
73
    public function get(): SpacecraftInterface
74
    {
75
        return $this->spacecraft;
76
    }
77
78
    #[Override]
79
    public function getSpacecraftWrapperFactory(): SpacecraftWrapperFactoryInterface
80
    {
81
        return $this->spacecraftWrapperFactory;
82
    }
83
84
    #[Override]
85
    public function getSpacecraftSystemManager(): SpacecraftSystemManagerInterface
86
    {
87
        return $this->spacecraftSystemManager;
88
    }
89
90 3
    #[Override]
91
    public function getEpsUsage(): int
92
    {
93 3
        if ($this->epsUsage === null) {
94 3
            $this->epsUsage = $this->reloadEpsUsage();
95
        }
96 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...
97
    }
98
99
    #[Override]
100
    public function lowerEpsUsage(int $value): void
101
    {
102
        $this->epsUsage -= $value;
103
    }
104
105 3
    private function reloadEpsUsage(): int
106
    {
107 3
        $result = 0;
108
109 3
        foreach ($this->spacecraftSystemManager->getActiveSystems($this->spacecraft) as $shipSystem) {
110 3
            $result += $this->spacecraftSystemManager->getEnergyConsumption($shipSystem->getSystemType());
111
        }
112
113 3
        if ($this->spacecraft->getAlertState() == SpacecraftAlertStateEnum::ALERT_YELLOW) {
114
            $result += SpacecraftStateChangerInterface::ALERT_YELLOW_EPS_USAGE;
115
        }
116 3
        if ($this->spacecraft->getAlertState() == SpacecraftAlertStateEnum::ALERT_RED) {
117
            $result += SpacecraftStateChangerInterface::ALERT_RED_EPS_USAGE;
118
        }
119
120 3
        return $result;
121
    }
122
123 2
    public function getReactorUsage(): int
124
    {
125 2
        $reactor = $this->reactorWrapper;
126 2
        if ($reactor === null) {
127
            throw new RuntimeException('this should not happen');
128
        }
129
130 2
        return $this->getEpsUsage() + $reactor->getUsage();
131
    }
132
133 9
    #[Override]
134
    public function getReactorWrapper(): ?ReactorWrapperInterface
135
    {
136 9
        if ($this->reactorWrapper === null) {
137 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...
138 9
            $reactorSystemData = null;
139
140
141 9
            if ($ship->hasShipSystem(SpacecraftSystemTypeEnum::SYSTEM_WARPCORE)) {
142 5
                $reactorSystemData = $this->getSpecificShipSystem(
143 5
                    SpacecraftSystemTypeEnum::SYSTEM_WARPCORE,
144 5
                    WarpCoreSystemData::class
145 5
                );
146
            }
147 9
            if ($ship->hasShipSystem(SpacecraftSystemTypeEnum::SYSTEM_SINGULARITY_REACTOR)) {
148
                $reactorSystemData = $this->getSpecificShipSystem(
149
                    SpacecraftSystemTypeEnum::SYSTEM_SINGULARITY_REACTOR,
150
                    SingularityCoreSystemData::class
151
                );
152
            }
153 9
            if ($ship->hasShipSystem(SpacecraftSystemTypeEnum::SYSTEM_FUSION_REACTOR)) {
154
                $reactorSystemData = $this->getSpecificShipSystem(
155
                    SpacecraftSystemTypeEnum::SYSTEM_FUSION_REACTOR,
156
                    FusionCoreSystemData::class
157
                );
158
            }
159
160 9
            if ($reactorSystemData === null) {
161 6
                return null;
162
            }
163
164 5
            $this->reactorWrapper = new ReactorWrapper($this, $reactorSystemData);
165
        }
166
167 5
        return $this->reactorWrapper;
168
    }
169
170
    #[Override]
171
    public function setAlertState(SpacecraftAlertStateEnum $alertState): ?string
172
    {
173
        $msg = $this->spacecraftStateChanger->changeAlertState($this, $alertState);
174
        $this->epsUsage = $this->reloadEpsUsage();
175
176
        return $msg;
177
    }
178
179
    /**
180
     * highest damage first, then prio
181
     *
182
     * @return SpacecraftSystemInterface[]
183
     */
184 3
    #[Override]
185
    public function getDamagedSystems(): array
186
    {
187 3
        $damagedSystems = [];
188 3
        $prioArray = [];
189 3
        foreach ($this->spacecraft->getSystems() as $system) {
190 2
            if ($system->getStatus() < 100) {
191
                $damagedSystems[] = $system;
192
                $prioArray[$system->getSystemType()->value] = $this->spacecraftSystemManager->lookupSystem($system->getSystemType())->getPriority();
193
            }
194
        }
195
196
        // sort by damage and priority
197 3
        usort(
198 3
            $damagedSystems,
199 3
            function (SpacecraftSystemInterface $a, SpacecraftSystemInterface $b) use ($prioArray): int {
200
                if ($a->getStatus() === $b->getStatus()) {
201
                    return $prioArray[$b->getSystemType()->value] <=> $prioArray[$a->getSystemType()->value];
202
                }
203
                return ($a->getStatus() < $b->getStatus()) ? -1 : 1;
204 3
            }
205 3
        );
206
207 3
        return $damagedSystems;
208
    }
209
210 9
    #[Override]
211
    public function isOwnedByCurrentUser(): bool
212
    {
213 9
        return $this->game->getUser() === $this->spacecraft->getUser();
214
    }
215
216 2
    #[Override]
217
    public function canBeRepaired(): bool
218
    {
219 2
        if ($this->spacecraft->getAlertState() !== SpacecraftAlertStateEnum::ALERT_GREEN) {
220
            return false;
221
        }
222
223 2
        if ($this->spacecraft->getShieldState()) {
224
            return false;
225
        }
226
227 2
        if ($this->spacecraft->getCloakState()) {
228
            return false;
229
        }
230
231 2
        if ($this->getDamagedSystems() !== []) {
232
            return true;
233
        }
234
235 2
        return $this->spacecraft->getHull() < $this->spacecraft->getMaxHull();
236
    }
237
238 4
    #[Override]
239
    public function canFire(): bool
240
    {
241 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...
242 4
        if (!$ship->getNbs()) {
243 1
            return false;
244
        }
245 3
        if (!$ship->hasActiveWeapon()) {
246 1
            return false;
247
        }
248
249 2
        $epsSystem = $this->getEpsSystemData();
250 2
        return $epsSystem !== null && $epsSystem->getEps() !== 0;
251
    }
252
253
    #[Override]
254
    public function getRepairDuration(): int
255
    {
256
        return $this->repairUtil->getRepairDuration($this);
257
    }
258
259
    #[Override]
260
    public function getRepairDurationPreview(): int
261
    {
262
        return $this->repairUtil->getRepairDurationPreview($this);
263
    }
264
265
    #[Override]
266
    public function getRepairCosts(): array
267
    {
268
        $neededParts = $this->repairUtil->determineSpareParts($this, false);
269
270
        $neededSpareParts = $neededParts[CommodityTypeEnum::COMMODITY_SPARE_PART];
271
        $neededSystemComponents = $neededParts[CommodityTypeEnum::COMMODITY_SYSTEM_COMPONENT];
272
273
        return [
274
            new ShipRepairCost($neededSpareParts, CommodityTypeEnum::COMMODITY_SPARE_PART, CommodityTypeEnum::getDescription(CommodityTypeEnum::COMMODITY_SPARE_PART)),
275
            new ShipRepairCost($neededSystemComponents, CommodityTypeEnum::COMMODITY_SYSTEM_COMPONENT, CommodityTypeEnum::getDescription(CommodityTypeEnum::COMMODITY_SYSTEM_COMPONENT))
276
        ];
277
    }
278
279
    #[Override]
280
    public function getPossibleTorpedoTypes(): array
281
    {
282
        if ($this->spacecraft->hasShipSystem(SpacecraftSystemTypeEnum::SYSTEM_TORPEDO_STORAGE)) {
283
            return $this->torpedoTypeRepository->getAll();
284
        }
285
286
        return $this->torpedoTypeRepository->getByLevel($this->spacecraft->getRump()->getTorpedoLevel());
287
    }
288
289
    #[Override]
290
    public function getTractoredShipWrapper(): ?ShipWrapperInterface
291
    {
292
        $tractoredShip = $this->spacecraft->getTractoredShip();
293
        if ($tractoredShip === null) {
294
            return null;
295
        }
296
297
        return $this->spacecraftWrapperFactory->wrapShip($tractoredShip);
298
    }
299
300 4
    #[Override]
301
    public function getStateIconAndTitle(): ?array
302
    {
303 4
        return $this->stateIconAndTitle->getStateIconAndTitle($this);
304
    }
305
306
    #[Override]
307
    public function getTakeoverTicksLeft(?ShipTakeoverInterface $takeover = null): int
308
    {
309
        $takeover ??= $this->spacecraft->getTakeoverActive();
310
        if ($takeover === null) {
311
            throw new RuntimeException('should not call when active takeover is null');
312
        }
313
314
        $currentTurn = $this->game->getCurrentRound()->getTurn();
315
316
        return $takeover->getStartTurn() + ShipTakeoverManagerInterface::TURNS_TO_TAKEOVER - $currentTurn;
317
    }
318
319 6
    #[Override]
320
    public function getCrewStyle(): string
321
    {
322 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...
323 6
        $excessCrew = $ship->getExcessCrewCount();
324
325 6
        if ($excessCrew === 0) {
326 5
            return "";
327
        }
328
329 3
        return $excessCrew > 0 ? "color: green;" : "color: red;";
330
    }
331
332 9
    #[Override]
333
    public function getHullSystemData(): HullSystemData
334
    {
335 9
        $hullSystemData = $this->getSpecificShipSystem(
336 9
            SpacecraftSystemTypeEnum::SYSTEM_HULL,
337 9
            HullSystemData::class
338 9
        );
339
340 9
        if ($hullSystemData === null) {
341
            throw new SystemNotFoundException('no hull installed?');
342
        }
343
344 9
        return $hullSystemData;
345
    }
346
347 8
    #[Override]
348
    public function getShieldSystemData(): ?ShieldSystemData
349
    {
350 8
        return $this->getSpecificShipSystem(
351 8
            SpacecraftSystemTypeEnum::SYSTEM_SHIELDS,
352 8
            ShieldSystemData::class
353 8
        );
354
    }
355
356 18
    #[Override]
357
    public function getEpsSystemData(): ?EpsSystemData
358
    {
359 18
        return $this->getSpecificShipSystem(
360 18
            SpacecraftSystemTypeEnum::SYSTEM_EPS,
361 18
            EpsSystemData::class
362 18
        );
363
    }
364
365 4
    #[Override]
366
    public function getWarpDriveSystemData(): ?WarpDriveSystemData
367
    {
368 4
        return $this->getSpecificShipSystem(
369 4
            SpacecraftSystemTypeEnum::SYSTEM_WARPDRIVE,
370 4
            WarpDriveSystemData::class
371 4
        );
372
    }
373
374 1
    #[Override]
375
    public function getProjectileLauncherSystemData(): ?ProjectileLauncherSystemData
376
    {
377 1
        return $this->getSpecificShipSystem(
378 1
            SpacecraftSystemTypeEnum::SYSTEM_TORPEDO,
379 1
            ProjectileLauncherSystemData::class
380 1
        );
381
    }
382
383
    /**
384
     * @template T2
385
     * @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...
386
     *
387
     * @return T2|null
388
     */
389 19
    protected function getSpecificShipSystem(SpacecraftSystemTypeEnum $systemType, string $className)
390
    {
391 19
        return $this->systemDataDeserializer->getSpecificShipSystem(
392 19
            $this->spacecraft,
393 19
            $systemType,
394 19
            $className,
395 19
            $this->shipSystemDataCache,
396 19
            $this->spacecraftWrapperFactory
397 19
        );
398
    }
399
}
400