Passed
Pull Request — master (#1777)
by Nico
22:32 queued 18s
created

ShipWrapper::reloadEpsUsage()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 16
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

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