Passed
Pull Request — master (#1676)
by Nico
24:49
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 JsonMapper\JsonMapperInterface;
8
use RuntimeException;
9
use Stu\Component\Building\BuildingEnum;
10
use Stu\Component\Colony\ColonyFunctionManagerInterface;
11
use Stu\Component\Ship\RepairTaskEnum;
12
use Stu\Component\Ship\ShipAlertStateEnum;
13
use Stu\Component\Ship\ShipStateEnum;
14
use Stu\Component\Ship\System\Data\AbstractSystemData;
15
use Stu\Component\Ship\System\Data\EpsSystemData;
16
use Stu\Component\Ship\System\Data\HullSystemData;
17
use Stu\Component\Ship\System\Data\ShieldSystemData;
18
use Stu\Component\Ship\System\Data\ShipSystemDataFactoryInterface;
19
use Stu\Component\Ship\System\Data\TrackerSystemData;
20
use Stu\Component\Ship\System\Data\WarpCoreSystemData;
21
use Stu\Component\Ship\System\Data\WarpDriveSystemData;
22
use Stu\Component\Ship\System\Data\WebEmitterSystemData;
23
use Stu\Component\Ship\System\Exception\SystemNotFoundException;
24
use Stu\Component\Ship\System\ShipSystemManagerInterface;
25
use Stu\Component\Ship\System\ShipSystemTypeEnum;
26
use Stu\Module\Colony\Lib\ColonyLibFactoryInterface;
27
use Stu\Module\Commodity\CommodityTypeEnum;
28
use Stu\Module\Control\GameControllerInterface;
29
use Stu\Orm\Entity\ShipInterface;
30
use Stu\Orm\Entity\ShipSystemInterface;
31
use Stu\Orm\Repository\ColonyShipRepairRepositoryInterface;
32
use Stu\Orm\Repository\TorpedoTypeRepositoryInterface;
33
34
//TODO increase coverage
35
final class ShipWrapper implements ShipWrapperInterface
36
{
37
    private ShipInterface $ship;
38
39
    private ShipSystemManagerInterface $shipSystemManager;
40
41
    private ColonyLibFactoryInterface $colonyLibFactory;
42
43
    private ColonyShipRepairRepositoryInterface $colonyShipRepairRepository;
44
45
    private TorpedoTypeRepositoryInterface $torpedoTypeRepository;
46
47
    private GameControllerInterface $game;
48
49
    private JsonMapperInterface $jsonMapper;
50
51
    private ShipWrapperFactoryInterface $shipWrapperFactory;
52
53
    private ShipSystemDataFactoryInterface $shipSystemDataFactory;
54
55
    private ColonyFunctionManagerInterface $colonyFunctionManager;
56
57
    private ShipStateChangerInterface $shipStateChanger;
58
59
    /**
60
     * @var array<int, AbstractSystemData>
61
     */
62
    private array $shipSystemDataCache = [];
63
64
    private ?int $epsUsage = null;
65
66
    private ?int $effectiveEpsProduction = null;
67
68
    private ?int $effectiveWarpDriveProduction = null;
69
70 15
    public function __construct(
71
        ColonyFunctionManagerInterface $colonyFunctionManager,
72
        ShipInterface $ship,
73
        ShipSystemManagerInterface $shipSystemManager,
74
        ColonyShipRepairRepositoryInterface $colonyShipRepairRepository,
75
        ColonyLibFactoryInterface $colonyLibFactory,
76
        TorpedoTypeRepositoryInterface $torpedoTypeRepository,
77
        GameControllerInterface $game,
78
        JsonMapperInterface $jsonMapper,
79
        ShipWrapperFactoryInterface $shipWrapperFactory,
80
        ShipSystemDataFactoryInterface $shipSystemDataFactory,
81
        ShipStateChangerInterface $shipStateChanger
82
    ) {
83 15
        $this->ship = $ship;
84 15
        $this->shipSystemManager = $shipSystemManager;
85 15
        $this->colonyLibFactory = $colonyLibFactory;
86 15
        $this->colonyShipRepairRepository = $colonyShipRepairRepository;
87 15
        $this->torpedoTypeRepository = $torpedoTypeRepository;
88 15
        $this->game = $game;
89 15
        $this->jsonMapper = $jsonMapper;
90 15
        $this->shipWrapperFactory = $shipWrapperFactory;
91 15
        $this->shipSystemDataFactory = $shipSystemDataFactory;
92 15
        $this->colonyFunctionManager = $colonyFunctionManager;
93 15
        $this->shipStateChanger = $shipStateChanger;
94
    }
95
96 15
    public function get(): ShipInterface
97
    {
98 15
        return $this->ship;
99
    }
100
101
    public function getShipWrapperFactory(): ShipWrapperFactoryInterface
102
    {
103
        return $this->shipWrapperFactory;
104
    }
105
106
    public function getShipSystemManager(): ShipSystemManagerInterface
107
    {
108
        return $this->shipSystemManager;
109
    }
110
111
    public function getFleetWrapper(): ?FleetWrapperInterface
112
    {
113
        if ($this->get()->getFleet() === null) {
114
            return null;
115
        }
116
117
        return $this->shipWrapperFactory->wrapFleet($this->get()->getFleet());
118
    }
119
120
    public function getEpsUsage(): int
121
    {
122
        if ($this->epsUsage === null) {
123
            $this->epsUsage = $this->reloadEpsUsage();
124
        }
125
        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...
126
    }
127
128
    public function lowerEpsUsage(int $value): void
129
    {
130
        $this->epsUsage -= $value;
131
    }
132
133
    private function reloadEpsUsage(): int
134
    {
135
        $result = 0;
136
137
        foreach ($this->shipSystemManager->getActiveSystems($this->get()) as $shipSystem) {
138
            $result += $this->shipSystemManager->getEnergyConsumption($shipSystem->getSystemType());
139
        }
140
141
        if ($this->get()->getAlertState() == ShipAlertStateEnum::ALERT_YELLOW) {
142
            $result += ShipAlertStateEnum::ALERT_YELLOW_EPS_USAGE;
143
        }
144
        if ($this->get()->getAlertState() == ShipAlertStateEnum::ALERT_RED) {
145
            $result += ShipAlertStateEnum::ALERT_RED_EPS_USAGE;
146
        }
147
148
        return $result;
149
    }
150
151
    public function getEffectiveEpsProduction(): int
152
    {
153
        if ($this->effectiveEpsProduction === null) {
154
            $warpcore = $this->getWarpCoreSystemData();
155
            if ($warpcore === null) {
156
                $prod = $this->get()->getReactorOutputCappedByReactorLoad() - $this->getEpsUsage();
157
            } else {
158
                $prod = round(($this->get()->getReactorOutputCappedByReactorLoad() - $this->getEpsUsage()) * ($warpcore->getWarpCoreSplit() / 100));
159
            }
160
            if ($prod <= 0) {
161
                return (int) $prod;
162
            }
163
164
            $eps = $this->getEpsSystemData();
165
            if (
166
                $eps !== null
167
                && $eps->getEps() + $prod > $eps->getMaxEps()
168
            ) {
169
                return $eps->getMaxEps() - $eps->getEps();
170
            }
171
            $this->effectiveEpsProduction = (int) $prod;
172
        }
173
        return $this->effectiveEpsProduction;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->effectiveEpsProduction 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...
174
    }
175
176
    public function getEffectiveWarpDriveProduction(): int
177
    {
178
179
        if ($this->ship->getRump()->getFlightEcost() === 0 || $this->ship->getRump()->getFlightEcost() === null) {
180
            $flightcost = 1;
181
        } else {
182
            $flightcost = $this->ship->getRump()->getFlightEcost();
183
        }
184
        if ($this->effectiveWarpDriveProduction === null) {
185
            $warpcore = $this->getWarpCoreSystemData();
186
            if ($warpcore === null) {
187
                $prod = ($this->get()->getReactorOutputCappedByReactorLoad() - $this->getEpsUsage()) / $flightcost;
188
            } else {
189
                $prod = (($this->get()->getReactorOutputCappedByReactorLoad() - $this->getEpsUsage()) * (1 - ($warpcore->getWarpCoreSplit() / 100))) / $flightcost;
190
            }
191
            if ($prod <= 0) {
192
                return (int) $prod;
193
            }
194
195
            $warpdrive = $this->getWarpDriveSystemData();
196
            if (
197
                $warpdrive !== null
198
                && $warpdrive->getWarpDrive() + $prod > $warpdrive->getMaxWarpDrive()
199
            ) {
200
                return $warpdrive->getMaxWarpDrive() - $warpdrive->getWarpDrive();
201
            }
202
            $this->effectiveWarpDriveProduction = (int) round($prod);
203
        }
204
        return $this->effectiveWarpDriveProduction;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->effectiveWarpDriveProduction 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...
205
    }
206
207
208
    public function getWarpcoreUsage(): int
209
    {
210
        return $this->getEffectiveEpsProduction() + $this->getEpsUsage() + $this->getEffectiveWarpDriveProduction();
211
    }
212
213
    public function setAlertState(int $alertState): ?string
214
    {
215
        $msg = $this->shipStateChanger->changeAlertState($this, $alertState);
216
        $this->epsUsage = $this->reloadEpsUsage();
217
218
        return $msg;
219
    }
220
221
    /**
222
     * highest damage first, then prio
223
     *
224
     * @return ShipSystemInterface[]
225
     */
226 8
    public function getDamagedSystems(): array
227
    {
228 8
        $damagedSystems = [];
229 8
        $prioArray = [];
230 8
        foreach ($this->get()->getSystems() as $system) {
231 2
            if ($system->getStatus() < 100) {
232 2
                $damagedSystems[] = $system;
233 2
                $prioArray[$system->getSystemType()] = $this->shipSystemManager->lookupSystem($system->getSystemType())->getPriority();
234
            }
235
        }
236
237
        // sort by damage and priority
238 8
        usort(
239 8
            $damagedSystems,
240 8
            function (ShipSystemInterface $a, ShipSystemInterface $b) use ($prioArray): int {
241 2
                if ($a->getStatus() === $b->getStatus()) {
242
                    return $prioArray[$b->getSystemType()] <=> $prioArray[$a->getSystemType()];
243
                }
244 2
                return ($a->getStatus() < $b->getStatus()) ? -1 : 1;
245 8
            }
246 8
        );
247
248 8
        return $damagedSystems;
249
    }
250
251
    public function isOwnedByCurrentUser(): bool
252
    {
253
        return $this->game->getUser() === $this->get()->getUser();
254
    }
255
256
    public function canLandOnCurrentColony(): bool
257
    {
258
        if ($this->get()->getRump()->getCommodity() === null) {
259
            return false;
260
        }
261
        if ($this->get()->isShuttle()) {
262
            return false;
263
        }
264
265
        $currentColony = $this->get()->getStarsystemMap() !== null ? $this->get()->getStarsystemMap()->getColony() : null;
266
267
        if ($currentColony === null) {
268
            return false;
269
        }
270
        if ($currentColony->getUser() !== $this->get()->getUser()) {
271
            return false;
272
        }
273
274
        return $this->colonyLibFactory
275
            ->createColonySurface($currentColony)
276
            ->hasAirfield();
277
    }
278
279
    public function canBeRepaired(): bool
280
    {
281
        if ($this->get()->getAlertState() !== ShipAlertStateEnum::ALERT_GREEN) {
282
            return false;
283
        }
284
285
        if ($this->get()->getShieldState()) {
286
            return false;
287
        }
288
289
        if ($this->get()->getCloakState()) {
290
            return false;
291
        }
292
293
        if (!empty($this->getDamagedSystems())) {
294
            return true;
295
        }
296
297
        return $this->get()->getHull() < $this->get()->getMaxHull();
298
    }
299
300 5
    public function getRepairDuration(): int
301
    {
302 5
        $ship = $this->get();
303
304 5
        $ticks = $this->getRepairTicks($ship);
305
306
        //check if repair station is active
307 5
        $colonyRepair = $this->colonyShipRepairRepository->getByShip($ship->getId());
308 5
        if ($colonyRepair !== null) {
309 2
            $isRepairStationBonus = $this->colonyFunctionManager->hasActiveFunction($colonyRepair->getColony(), BuildingEnum::BUILDING_FUNCTION_REPAIR_SHIPYARD);
310 2
            if ($isRepairStationBonus) {
311 1
                $ticks = (int)ceil($ticks / 2);
312
            }
313
        }
314
315 5
        return $ticks;
316
    }
317
318 3
    public function getRepairDurationPreview(): int
319
    {
320 3
        $ship = $this->get();
321
322 3
        $ticks = $this->getRepairTicks($ship);
323
324 3
        $colony = $ship->isOverColony();
325 3
        if ($colony !== null) {
326 2
            $isRepairStationBonus = $this->colonyFunctionManager->hasActiveFunction($colony, BuildingEnum::BUILDING_FUNCTION_REPAIR_SHIPYARD);
327 2
            if ($isRepairStationBonus) {
328 1
                $ticks = (int)ceil($ticks / 2);
329
            }
330
        }
331
332 3
        return $ticks;
333
    }
334
335 8
    private function getRepairTicks(ShipInterface $ship): int
336
    {
337 8
        $ticks = (int) ceil(($ship->getMaxHull() - $ship->getHull()) / $this->get()->getRepairRate());
338 8
        return max($ticks, (int) ceil(count($this->getDamagedSystems()) / 2));
339
    }
340
341
    public function getRepairCosts(): array
342
    {
343
        $neededSpareParts = 0;
344
        $neededSystemComponents = 0;
345
346
        $hull = $this->get()->getHull();
347
        $maxHull = $this->get()->getMaxHull();
348
349
        if ($hull < $maxHull) {
350
            $ticks = (int) ceil(($this->get()->getMaxHull() - $this->get()->getHull()) / $this->get()->getRepairRate());
351
            $neededSpareParts += ((int)($this->get()->getRepairRate() / RepairTaskEnum::HULL_HITPOINTS_PER_SPARE_PART)) * $ticks;
352
        }
353
354
        $damagedSystems = $this->getDamagedSystems();
355
        foreach ($damagedSystems as $system) {
356
            $systemLvl = $system->determineSystemLevel();
357
            $healingPercentage = (100 - $system->getStatus()) / 100;
358
359
            $neededSpareParts += (int)ceil($healingPercentage * RepairTaskEnum::SHIPYARD_PARTS_USAGE[$systemLvl][RepairTaskEnum::SPARE_PARTS_ONLY]);
360
            $neededSystemComponents += (int)ceil($healingPercentage * RepairTaskEnum::SHIPYARD_PARTS_USAGE[$systemLvl][RepairTaskEnum::SYSTEM_COMPONENTS_ONLY]);
361
        }
362
363
        return [
364
            new ShipRepairCost($neededSpareParts, CommodityTypeEnum::COMMODITY_SPARE_PART, CommodityTypeEnum::getDescription(CommodityTypeEnum::COMMODITY_SPARE_PART)),
365
            new ShipRepairCost($neededSystemComponents, CommodityTypeEnum::COMMODITY_SYSTEM_COMPONENT, CommodityTypeEnum::getDescription(CommodityTypeEnum::COMMODITY_SYSTEM_COMPONENT))
366
        ];
367
    }
368
369
    public function getPossibleTorpedoTypes(): array
370
    {
371
        if ($this->ship->hasShipSystem(ShipSystemTypeEnum::SYSTEM_TORPEDO_STORAGE)) {
372
            return $this->torpedoTypeRepository->getAll();
373
        }
374
375
        return $this->torpedoTypeRepository->getByLevel($this->ship->getRump()->getTorpedoLevel());
376
    }
377
378
    public function getTractoredShipWrapper(): ?ShipWrapperInterface
379
    {
380
        $tractoredShip = $this->get()->getTractoredShip();
381
        if ($tractoredShip === null) {
382
            return null;
383
        }
384
385
        return $this->shipWrapperFactory->wrapShip($tractoredShip);
386
    }
387
388
    public function getTractoringShipWrapper(): ?ShipWrapperInterface
389
    {
390
        $tractoringShip = $this->get()->getTractoringShip();
391
        if ($tractoringShip === null) {
392
            return null;
393
        }
394
395
        return $this->shipWrapperFactory->wrapShip($tractoringShip);
396
    }
397
398
    public function getDockedToShipWrapper(): ?ShipWrapperInterface
399
    {
400
        $dockedTo = $this->get()->getDockedTo();
401
        if ($dockedTo === null) {
402
            return null;
403
        }
404
405
        return $this->shipWrapperFactory->wrapShip($dockedTo);
406
    }
407
408
    public function getStateIconAndTitle(): ?array
409
    {
410
        $state = $this->get()->getState();
411
        $isBase = $this->get()->isBase();
412
        $repairDuration = $this->getRepairDuration();
413
414
        if ($state === ShipStateEnum::SHIP_STATE_REPAIR_PASSIVE) {
415
            return ['rep2', sprintf('%s wird repariert (noch %s Runden)', $isBase ? 'Station' : 'Schiff', $repairDuration)];
416
        }
417
        if ($state === ShipStateEnum::SHIP_STATE_REPAIR_ACTIVE) {
418
            return ['rep2', sprintf('%s repariert die Station', $isBase ? 'Stationscrew' : 'Schiffscrew')];
419
        }
420
        if ($state === ShipStateEnum::SHIP_STATE_ASTRO_FINALIZING) {
421
            return ['map1', 'Schiff kartographiert'];
422
        }
423
424
        return null;
425
    }
426
427
    public function canBeScrapped(): bool
428
    {
429
        $ship = $this->get();
430
431
        return $ship->isBase() && $ship->getState() !== ShipStateEnum::SHIP_STATE_UNDER_SCRAPPING;
432
    }
433
434 1
    public function getHullSystemData(): HullSystemData
435
    {
436 1
        $hullSystemData = $this->getSpecificShipSystem(
437 1
            ShipSystemTypeEnum::SYSTEM_HULL,
438 1
            HullSystemData::class
439 1
        );
440
441 1
        if ($hullSystemData === null) {
442
            throw new SystemNotFoundException('no hull installed?');
443
        }
444
445 1
        return $hullSystemData;
446
    }
447
448
    public function getShieldSystemData(): ?ShieldSystemData
449
    {
450
        return $this->getSpecificShipSystem(
451
            ShipSystemTypeEnum::SYSTEM_SHIELDS,
452
            ShieldSystemData::class
453
        );
454
    }
455
456 3
    public function getEpsSystemData(): ?EpsSystemData
457
    {
458 3
        return $this->getSpecificShipSystem(
459 3
            ShipSystemTypeEnum::SYSTEM_EPS,
460 3
            EpsSystemData::class
461 3
        );
462
    }
463
464
    public function getWarpCoreSystemData(): ?WarpCoreSystemData
465
    {
466
        return $this->getSpecificShipSystem(
467
            ShipSystemTypeEnum::SYSTEM_WARPCORE,
468
            WarpCoreSystemData::class
469
        );
470
    }
471
472
    public function getWarpDriveSystemData(): ?WarpDriveSystemData
473
    {
474
        return $this->getSpecificShipSystem(
475
            ShipSystemTypeEnum::SYSTEM_WARPDRIVE,
476
            WarpDriveSystemData::class
477
        );
478
    }
479
480
    public function getTrackerSystemData(): ?TrackerSystemData
481
    {
482
        return $this->getSpecificShipSystem(
483
            ShipSystemTypeEnum::SYSTEM_TRACKER,
484
            TrackerSystemData::class
485
        );
486
    }
487
488
    public function getWebEmitterSystemData(): ?WebEmitterSystemData
489
    {
490
        return $this->getSpecificShipSystem(
491
            ShipSystemTypeEnum::SYSTEM_THOLIAN_WEB,
492
            WebEmitterSystemData::class
493
        );
494
    }
495
496
    /**
497
     * @template T
498
     * @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...
499
     *
500
     * @return T|null
501
     */
502 4
    private function getSpecificShipSystem(int $systemType, string $className)
503
    {
504
        if (
505 4
            $systemType !== ShipSystemTypeEnum::SYSTEM_HULL
506 4
            && !$this->get()->hasShipSystem($systemType)
507
        ) {
508 1
            return null;
509
        }
510
511
        //add system to cache if not already deserialized
512 3
        if (!array_key_exists($systemType, $this->shipSystemDataCache)) {
513 3
            $systemData = $this->shipSystemDataFactory->createSystemData($systemType, $this->shipWrapperFactory);
514 3
            $systemData->setShip($this->get());
515
516 3
            $data = $systemType === ShipSystemTypeEnum::SYSTEM_HULL ? null : $this->get()->getShipSystem($systemType)->getData();
517
518 3
            if ($data === null) {
519 2
                $this->shipSystemDataCache[$systemType] = $systemData;
520
            } else {
521 1
                $this->shipSystemDataCache[$systemType] =
522 1
                    $this->jsonMapper->mapObjectFromString(
523 1
                        $data,
524 1
                        $systemData
525 1
                    );
526
            }
527
        }
528
529
        //load deserialized system from cache
530 3
        $cacheItem = $this->shipSystemDataCache[$systemType];
531 3
        if (!$cacheItem instanceof $className) {
532
            throw new RuntimeException('this should not happen');
533
        }
534
535 3
        return $cacheItem;
536
    }
537
}
538