Passed
Pull Request — master (#1827)
by Nico
57:37 queued 27:25
created

ShipWrapper::getDamagedSystems()   A

Complexity

Conditions 5
Paths 3

Size

Total Lines 23
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

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