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

ShipWrapper::reloadEpsUsage()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 16
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
eloc 8
c 0
b 0
f 0
nc 8
nop 0
dl 0
loc 16
rs 10
ccs 0
cts 8
cp 0
crap 20
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