Test Failed
Push — dev ( 55bb00...c2c26e )
by Janko
17:14
created

ShipWrapper::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 26
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 1

Importance

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

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