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