Passed
Pull Request — master (#1883)
by Janko
55:13 queued 27:46
created

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