Passed
Push — dev ( aeeb23...0b9faa )
by Nico
10:57
created

ShipWrapper::canBeRetrofitted()   B

Complexity

Conditions 7
Paths 6

Size

Total Lines 27
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 0
Metric Value
cc 7
eloc 14
c 0
b 0
f 0
nc 6
nop 0
dl 0
loc 27
ccs 0
cts 13
cp 0
crap 56
rs 8.8333
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->getFleet()) {
278
            return false;
279
        }
280
281
        if ($this->ship->getShieldState()) {
282
            return false;
283
        }
284
285
        if ($this->ship->getCloakState()) {
286
            return false;
287
        }
288
289
        if (
290
            $this->ship->getBuildplan() != null
291
            && $this->ship->getBuildplan()->getUser() != $this->game->getUser()
292
        ) {
293
            return false;
294
        }
295
296
        return true;
297
    }
298
299 4
    #[Override]
300
    public function canFire(): bool
301
    {
302 4
        $ship = $this->ship;
303 4
        if (!$ship->getNbs()) {
304 1
            return false;
305
        }
306 3
        if (!$ship->hasActiveWeapon()) {
307 1
            return false;
308
        }
309
310 2
        $epsSystem = $this->getEpsSystemData();
311 2
        return $epsSystem !== null && $epsSystem->getEps() !== 0;
312
    }
313
314
    #[Override]
315
    public function getRepairDuration(): int
316
    {
317
        return $this->repairUtil->getRepairDuration($this);
318
    }
319
320
    #[Override]
321
    public function getRepairDurationPreview(): int
322
    {
323
        return $this->repairUtil->getRepairDurationPreview($this);
324
    }
325
326
    #[Override]
327
    public function getRepairCosts(): array
328
    {
329
        $neededParts = $this->repairUtil->determineSpareParts($this, false);
330
331
        $neededSpareParts = $neededParts[CommodityTypeEnum::COMMODITY_SPARE_PART];
332
        $neededSystemComponents = $neededParts[CommodityTypeEnum::COMMODITY_SYSTEM_COMPONENT];
333
334
        return [
335
            new ShipRepairCost($neededSpareParts, CommodityTypeEnum::COMMODITY_SPARE_PART, CommodityTypeEnum::getDescription(CommodityTypeEnum::COMMODITY_SPARE_PART)),
336
            new ShipRepairCost($neededSystemComponents, CommodityTypeEnum::COMMODITY_SYSTEM_COMPONENT, CommodityTypeEnum::getDescription(CommodityTypeEnum::COMMODITY_SYSTEM_COMPONENT))
337
        ];
338
    }
339
340
    #[Override]
341
    public function getPossibleTorpedoTypes(): array
342
    {
343
        if ($this->ship->hasShipSystem(ShipSystemTypeEnum::SYSTEM_TORPEDO_STORAGE)) {
344
            return $this->torpedoTypeRepository->getAll();
345
        }
346
347
        return $this->torpedoTypeRepository->getByLevel($this->ship->getRump()->getTorpedoLevel());
348
    }
349
350
    #[Override]
351
    public function getTractoredShipWrapper(): ?ShipWrapperInterface
352
    {
353
        $tractoredShip = $this->ship->getTractoredShip();
354
        if ($tractoredShip === null) {
355
            return null;
356
        }
357
358
        return $this->shipWrapperFactory->wrapShip($tractoredShip);
359
    }
360
361
    #[Override]
362
    public function getTractoringShipWrapper(): ?ShipWrapperInterface
363
    {
364
        $tractoringShip = $this->ship->getTractoringShip();
365
        if ($tractoringShip === null) {
366
            return null;
367
        }
368
369
        return $this->shipWrapperFactory->wrapShip($tractoringShip);
370
    }
371
372
    #[Override]
373
    public function getDockedToShipWrapper(): ?ShipWrapperInterface
374
    {
375
        $dockedTo = $this->ship->getDockedTo();
376
        if ($dockedTo === null) {
377
            return null;
378
        }
379
380
        return $this->shipWrapperFactory->wrapShip($dockedTo);
381
    }
382
383
    #[Override]
384
    public function getStateIconAndTitle(): ?array
385
    {
386
        return $this->stateIconAndTitle->getStateIconAndTitle($this);
387
    }
388
389
    #[Override]
390
    public function getTakeoverTicksLeft(?ShipTakeoverInterface $takeover = null): int
391
    {
392
        $takeover ??= $this->ship->getTakeoverActive();
393
        if ($takeover === null) {
394
            throw new RuntimeException('should not call when active takeover is null');
395
        }
396
397
        $currentTurn = $this->game->getCurrentRound()->getTurn();
398
399
        return $takeover->getStartTurn() + ShipTakeoverManagerInterface::TURNS_TO_TAKEOVER - $currentTurn;
400
    }
401
402
    #[Override]
403
    public function canBeScrapped(): bool
404
    {
405
        $ship = $this->ship;
406
407
        return $ship->isBase() && $ship->getState() !== ShipStateEnum::SHIP_STATE_UNDER_SCRAPPING;
408
    }
409
410
    #[Override]
411
    public function getCrewStyle(): string
412
    {
413
        $ship = $this->ship;
414
        $excessCrew = $ship->getExcessCrewCount();
415
416
        if ($excessCrew === 0) {
417
            return "";
418
        }
419
420
        return $excessCrew > 0 ? "color: green;" : "color: red;";
421
    }
422
423 1
    #[Override]
424
    public function getHullSystemData(): HullSystemData
425
    {
426 1
        $hullSystemData = $this->getSpecificShipSystem(
427 1
            ShipSystemTypeEnum::SYSTEM_HULL,
428 1
            HullSystemData::class
429 1
        );
430
431 1
        if ($hullSystemData === null) {
432
            throw new SystemNotFoundException('no hull installed?');
433
        }
434
435 1
        return $hullSystemData;
436
    }
437
438
    #[Override]
439
    public function getShieldSystemData(): ?ShieldSystemData
440
    {
441
        return $this->getSpecificShipSystem(
442
            ShipSystemTypeEnum::SYSTEM_SHIELDS,
443
            ShieldSystemData::class
444
        );
445
    }
446
447 2
    #[Override]
448
    public function getEpsSystemData(): ?EpsSystemData
449
    {
450 2
        return $this->getSpecificShipSystem(
451 2
            ShipSystemTypeEnum::SYSTEM_EPS,
452 2
            EpsSystemData::class
453 2
        );
454
    }
455
456
    #[Override]
457
    public function getWarpDriveSystemData(): ?WarpDriveSystemData
458
    {
459
        return $this->getSpecificShipSystem(
460
            ShipSystemTypeEnum::SYSTEM_WARPDRIVE,
461
            WarpDriveSystemData::class
462
        );
463
    }
464
465
    #[Override]
466
    public function getTrackerSystemData(): ?TrackerSystemData
467
    {
468
        return $this->getSpecificShipSystem(
469
            ShipSystemTypeEnum::SYSTEM_TRACKER,
470
            TrackerSystemData::class
471
        );
472
    }
473
474
    #[Override]
475
    public function getAggregationSystemSystemData(): ?AggregationSystemSystemData
476
    {
477
        return $this->getSpecificShipSystem(
478
            ShipSystemTypeEnum::SYSTEM_AGGREGATION_SYSTEM,
479
            AggregationSystemSystemData::class
480
        );
481
    }
482
483
    #[Override]
484
    public function getBussardCollectorSystemData(): ?BussardCollectorSystemData
485
    {
486
        return $this->getSpecificShipSystem(
487
            ShipSystemTypeEnum::SYSTEM_BUSSARD_COLLECTOR,
488
            BussardCollectorSystemData::class
489
        );
490
    }
491
492
    #[Override]
493
    public function getWebEmitterSystemData(): ?WebEmitterSystemData
494
    {
495
        return $this->getSpecificShipSystem(
496
            ShipSystemTypeEnum::SYSTEM_THOLIAN_WEB,
497
            WebEmitterSystemData::class
498
        );
499
    }
500
501
    #[Override]
502
    public function getAstroLaboratorySystemData(): ?AstroLaboratorySystemData
503
    {
504
        return $this->getSpecificShipSystem(
505
            ShipSystemTypeEnum::SYSTEM_ASTRO_LABORATORY,
506
            AstroLaboratorySystemData::class
507
        );
508
    }
509
510
    #[Override]
511
    public function getProjectileLauncherSystemData(): ?ProjectileLauncherSystemData
512
    {
513
        return $this->getSpecificShipSystem(
514
            ShipSystemTypeEnum::SYSTEM_TORPEDO,
515
            ProjectileLauncherSystemData::class
516
        );
517
    }
518
519
    /**
520
     * @template T
521
     * @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...
522
     *
523
     * @return T|null
524
     */
525 3
    private function getSpecificShipSystem(ShipSystemTypeEnum $systemType, string $className)
526
    {
527 3
        return $this->systemDataDeserializer->getSpecificShipSystem(
528 3
            $this->ship,
529 3
            $systemType,
530 3
            $className,
531 3
            $this->shipSystemDataCache,
532 3
            $this->shipWrapperFactory
533 3
        );
534
    }
535
}
536