Passed
Pull Request — master (#1686)
by Nico
31:24
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
        $neededSpareParts = 0;
299
        $neededSystemComponents = 0;
300
301
        $hull = $this->get()->getHull();
302
        $maxHull = $this->get()->getMaxHull();
303
304
        if ($hull < $maxHull) {
305
            $ticks = (int) ceil(($this->get()->getMaxHull() - $this->get()->getHull()) / $this->get()->getRepairRate());
306
            $neededSpareParts += ((int)($this->get()->getRepairRate() / RepairTaskEnum::HULL_HITPOINTS_PER_SPARE_PART)) * $ticks;
307
        }
308
309
        $damagedSystems = $this->getDamagedSystems();
310
        foreach ($damagedSystems as $system) {
311
            $systemLvl = $system->determineSystemLevel();
312
            $healingPercentage = (100 - $system->getStatus()) / 100;
313
314
            $neededSpareParts += (int)ceil($healingPercentage * RepairTaskEnum::SHIPYARD_PARTS_USAGE[$systemLvl][RepairTaskEnum::SPARE_PARTS_ONLY]);
315
            $neededSystemComponents += (int)ceil($healingPercentage * RepairTaskEnum::SHIPYARD_PARTS_USAGE[$systemLvl][RepairTaskEnum::SYSTEM_COMPONENTS_ONLY]);
316
        }
317
318
        return [
319
            new ShipRepairCost($neededSpareParts, CommodityTypeEnum::COMMODITY_SPARE_PART, CommodityTypeEnum::getDescription(CommodityTypeEnum::COMMODITY_SPARE_PART)),
320
            new ShipRepairCost($neededSystemComponents, CommodityTypeEnum::COMMODITY_SYSTEM_COMPONENT, CommodityTypeEnum::getDescription(CommodityTypeEnum::COMMODITY_SYSTEM_COMPONENT))
321
        ];
322
    }
323
324
    public function getPossibleTorpedoTypes(): array
325
    {
326
        if ($this->ship->hasShipSystem(ShipSystemTypeEnum::SYSTEM_TORPEDO_STORAGE)) {
327
            return $this->torpedoTypeRepository->getAll();
328
        }
329
330
        return $this->torpedoTypeRepository->getByLevel($this->ship->getRump()->getTorpedoLevel());
331
    }
332
333
    public function getTractoredShipWrapper(): ?ShipWrapperInterface
334
    {
335
        $tractoredShip = $this->get()->getTractoredShip();
336
        if ($tractoredShip === null) {
337
            return null;
338
        }
339
340
        return $this->shipWrapperFactory->wrapShip($tractoredShip);
341
    }
342
343
    public function getTractoringShipWrapper(): ?ShipWrapperInterface
344
    {
345
        $tractoringShip = $this->get()->getTractoringShip();
346
        if ($tractoringShip === null) {
347
            return null;
348
        }
349
350
        return $this->shipWrapperFactory->wrapShip($tractoringShip);
351
    }
352
353
    public function getDockedToShipWrapper(): ?ShipWrapperInterface
354
    {
355
        $dockedTo = $this->get()->getDockedTo();
356
        if ($dockedTo === null) {
357
            return null;
358
        }
359
360
        return $this->shipWrapperFactory->wrapShip($dockedTo);
361
    }
362
363 7
    public function getStateIconAndTitle(): ?array
364
    {
365 7
        $ship = $this->get();
366
367 7
        $state = $ship->getState();
368
369 7
        if ($state === ShipStateEnum::SHIP_STATE_REPAIR_ACTIVE) {
370 2
            $isBase = $ship->isBase();
371 2
            return ['rep2', sprintf('%s repariert die Station', $isBase ? 'Stationscrew' : 'Schiffscrew')];
372
        }
373
374 5
        if ($state === ShipStateEnum::SHIP_STATE_REPAIR_PASSIVE) {
375 2
            $isBase = $ship->isBase();
376 2
            $repairDuration = $this->getRepairDuration();
377 2
            return ['rep2', sprintf('%s wird repariert (noch %d Runden)', $isBase ? 'Station' : 'Schiff', $repairDuration)];
378
        }
379
380 3
        $currentTurn = $this->game->getCurrentRound()->getTurn();
381 3
        if ($state === ShipStateEnum::SHIP_STATE_ASTRO_FINALIZING) {
382 1
            return ['map1', sprintf(
383 1
                'Schiff kartographiert (noch %d Runden)',
384 1
                $ship->getAstroStartTurn() + AstronomicalMappingEnum::TURNS_TO_FINISH - $currentTurn
385 1
            )];
386
        }
387
388 2
        $takeover = $ship->getTakeoverActive();
389
        if (
390 2
            $state === ShipStateEnum::SHIP_STATE_ACTIVE_TAKEOVER
391 2
            && $takeover !== null
392
        ) {
393 1
            $targetNamePlainText = $this->bbCodeParser->parse($takeover->getTargetShip()->getName())->getAsText();
394 1
            return ['take2', sprintf(
395 1
                'Schiff übernimmt die "%s" (noch %d Runden)',
396 1
                $targetNamePlainText,
397 1
                $this->getTakeoverTicksLeft($takeover)
398 1
            )];
399
        }
400
401 1
        $takeover = $ship->getTakeoverPassive();
402 1
        if ($takeover !== null) {
403 1
            $sourceUserNamePlainText = $this->bbCodeParser->parse($takeover->getSourceShip()->getUser()->getName())->getAsText();
404 1
            return ['untake2', sprintf(
405 1
                'Schiff wird von Spieler "%s" übernommen (noch %d Runden)',
406 1
                $sourceUserNamePlainText,
407 1
                $this->getTakeoverTicksLeft($takeover)
408 1
            )];
409
        }
410
411
        return null;
412
    }
413
414 2
    public function getTakeoverTicksLeft(ShipTakeoverInterface $takeover = null): int
415
    {
416 2
        $takeover = $takeover ?? $this->get()->getTakeoverActive();
417 2
        if ($takeover === null) {
418
            throw new RuntimeException('should not call when active takeover is null');
419
        }
420
421 2
        $currentTurn = $this->game->getCurrentRound()->getTurn();
422
423 2
        return $takeover->getStartTurn() + ShipTakeoverManagerInterface::TURNS_TO_TAKEOVER - $currentTurn;
424
    }
425
426
    public function canBeScrapped(): bool
427
    {
428
        $ship = $this->get();
429
430
        return $ship->isBase() && $ship->getState() !== ShipStateEnum::SHIP_STATE_UNDER_SCRAPPING;
431
    }
432
433 1
    public function getHullSystemData(): HullSystemData
434
    {
435 1
        $hullSystemData = $this->getSpecificShipSystem(
436 1
            ShipSystemTypeEnum::SYSTEM_HULL,
437 1
            HullSystemData::class
438 1
        );
439
440 1
        if ($hullSystemData === null) {
441
            throw new SystemNotFoundException('no hull installed?');
442
        }
443
444 1
        return $hullSystemData;
445
    }
446
447
    public function getShieldSystemData(): ?ShieldSystemData
448
    {
449
        return $this->getSpecificShipSystem(
450
            ShipSystemTypeEnum::SYSTEM_SHIELDS,
451
            ShieldSystemData::class
452
        );
453
    }
454
455 3
    public function getEpsSystemData(): ?EpsSystemData
456
    {
457 3
        return $this->getSpecificShipSystem(
458 3
            ShipSystemTypeEnum::SYSTEM_EPS,
459 3
            EpsSystemData::class
460 3
        );
461
    }
462
463
    public function getWarpDriveSystemData(): ?WarpDriveSystemData
464
    {
465
        return $this->getSpecificShipSystem(
466
            ShipSystemTypeEnum::SYSTEM_WARPDRIVE,
467
            WarpDriveSystemData::class
468
        );
469
    }
470
471
    public function getTrackerSystemData(): ?TrackerSystemData
472
    {
473
        return $this->getSpecificShipSystem(
474
            ShipSystemTypeEnum::SYSTEM_TRACKER,
475
            TrackerSystemData::class
476
        );
477
    }
478
479
    public function getWebEmitterSystemData(): ?WebEmitterSystemData
480
    {
481
        return $this->getSpecificShipSystem(
482
            ShipSystemTypeEnum::SYSTEM_THOLIAN_WEB,
483
            WebEmitterSystemData::class
484
        );
485
    }
486
487
    /**
488
     * @template T
489
     * @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...
490
     *
491
     * @return T|null
492
     */
493 4
    private function getSpecificShipSystem(ShipSystemTypeEnum $systemType, string $className)
494
    {
495
        if (
496 4
            $systemType !== ShipSystemTypeEnum::SYSTEM_HULL
497 4
            && !$this->get()->hasShipSystem($systemType)
498
        ) {
499 1
            return null;
500
        }
501
502
        //add system to cache if not already deserialized
503 3
        if (!array_key_exists($systemType->value, $this->shipSystemDataCache)) {
504 3
            $systemData = $this->shipSystemDataFactory->createSystemData($systemType, $this->shipWrapperFactory);
505 3
            $systemData->setShip($this->get());
506
507 3
            $data = $systemType === ShipSystemTypeEnum::SYSTEM_HULL ? null : $this->get()->getShipSystem($systemType)->getData();
508
509 3
            if ($data === null) {
510 2
                $this->shipSystemDataCache[$systemType->value] = $systemData;
511
            } else {
512 1
                $this->shipSystemDataCache[$systemType->value] =
513 1
                    $this->jsonMapper->mapObjectFromString(
514 1
                        $data,
515 1
                        $systemData
516 1
                    );
517
            }
518
        }
519
520
        //load deserialized system from cache
521 3
        $cacheItem = $this->shipSystemDataCache[$systemType->value];
522 3
        if (!$cacheItem instanceof $className) {
523
            throw new RuntimeException('this should not happen');
524
        }
525
526 3
        return $cacheItem;
527
    }
528
}
529