Passed
Push — dev ( 837c0e...fdbfdd )
by Nico
26:17
created

ShipWrapper::canBeRetrofitted()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 20
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

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