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

ShipWrapper::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 24
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 11
nc 1
nop 11
dl 0
loc 24
ccs 12
cts 12
cp 1
crap 1
rs 9.9
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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
}