Passed
Pull Request — master (#1833)
by Nico
34:40
created

ShipWrapper::getHullSystemData()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2.0078

Importance

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