Passed
Pull Request — master (#1819)
by Nico
52:14 queued 25:19
created

ShipWrapper::getDockedToShipWrapper()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 0
dl 0
loc 8
ccs 0
cts 5
cp 0
crap 6
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Stu\Module\Ship\Lib;
6
7
use Doctrine\Common\Collections\ArrayCollection;
8
use Doctrine\Common\Collections\Collection;
9
use JBBCode\Parser;
10
use RuntimeException;
11
use Stu\Component\Ship\AstronomicalMappingEnum;
12
use Stu\Component\Ship\Repair\RepairUtilInterface;
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\SingularityCoreSystemData;
21
use Stu\Component\Ship\System\Data\TrackerSystemData;
22
use Stu\Component\Ship\System\Data\WarpCoreSystemData;
23
use Stu\Component\Ship\System\Data\WarpDriveSystemData;
24
use Stu\Component\Ship\System\Data\WebEmitterSystemData;
25
use Stu\Component\Ship\System\Exception\SystemNotFoundException;
26
use Stu\Component\Ship\System\ShipSystemManagerInterface;
27
use Stu\Component\Ship\System\ShipSystemTypeEnum;
28
use Stu\Component\Ship\System\SystemDataDeserializerInterface;
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
    /**
42
     * @var Collection<int, AbstractSystemData>
43
     */
44
    private Collection $shipSystemDataCache;
45
46
    private ?ReactorWrapperInterface $reactorWrapper = null;
47
48
    private ?int $epsUsage = null;
49
50 15
    public function __construct(
51
        private ShipInterface $ship,
52
        private ShipSystemManagerInterface $shipSystemManager,
53
        private SystemDataDeserializerInterface $systemDataDeserializer,
54
        private ColonyLibFactoryInterface $colonyLibFactory,
55
        private TorpedoTypeRepositoryInterface $torpedoTypeRepository,
56
        private GameControllerInterface $game,
57
        private ShipWrapperFactoryInterface $shipWrapperFactory,
58
        private ShipStateChangerInterface $shipStateChanger,
59
        private RepairUtilInterface $repairUtil,
60
        private Parser $bbCodeParser
61
    ) {
62
63 15
        $this->shipSystemDataCache = new ArrayCollection();
64
    }
65
66 15
    public function get(): ShipInterface
67
    {
68 15
        return $this->ship;
69
    }
70
71
    public function getShipWrapperFactory(): ShipWrapperFactoryInterface
72
    {
73
        return $this->shipWrapperFactory;
74
    }
75
76
    public function getShipSystemManager(): ShipSystemManagerInterface
77
    {
78
        return $this->shipSystemManager;
79
    }
80
81
    public function getFleetWrapper(): ?FleetWrapperInterface
82
    {
83
        if ($this->get()->getFleet() === null) {
84
            return null;
85
        }
86
87
        return $this->shipWrapperFactory->wrapFleet($this->get()->getFleet());
88
    }
89
90
    public function getEpsUsage(): int
91
    {
92
        if ($this->epsUsage === null) {
93
            $this->epsUsage = $this->reloadEpsUsage();
94
        }
95
        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...
96
    }
97
98
    public function lowerEpsUsage(int $value): void
99
    {
100
        $this->epsUsage -= $value;
101
    }
102
103
    private function reloadEpsUsage(): int
104
    {
105
        $result = 0;
106
107
        foreach ($this->shipSystemManager->getActiveSystems($this->get()) as $shipSystem) {
108
            $result += $this->shipSystemManager->getEnergyConsumption($shipSystem->getSystemType());
109
        }
110
111
        if ($this->get()->getAlertState() == ShipAlertStateEnum::ALERT_YELLOW) {
112
            $result += ShipStateChangerInterface::ALERT_YELLOW_EPS_USAGE;
113
        }
114
        if ($this->get()->getAlertState() == ShipAlertStateEnum::ALERT_RED) {
115
            $result += ShipStateChangerInterface::ALERT_RED_EPS_USAGE;
116
        }
117
118
        return $result;
119
    }
120
121
    public function getReactorUsage(): int
122
    {
123
        $reactor = $this->reactorWrapper;
124
        if ($reactor === null) {
125
            throw new RuntimeException('this should not happen');
126
        }
127
128
        return $this->getEpsUsage() + $reactor->getUsage();
129
    }
130
131
    public function getReactorWrapper(): ?ReactorWrapperInterface
132
    {
133
        if ($this->reactorWrapper === null) {
134
            $ship = $this->get();
135
            $reactorSystemData = null;
136
137
138
            if ($ship->hasShipSystem(ShipSystemTypeEnum::SYSTEM_WARPCORE)) {
139
                $reactorSystemData = $this->getSpecificShipSystem(
140
                    ShipSystemTypeEnum::SYSTEM_WARPCORE,
141
                    WarpCoreSystemData::class
142
                );
143
            }
144
            if ($ship->hasShipSystem(ShipSystemTypeEnum::SYSTEM_SINGULARITY_REACTOR)) {
145
                $reactorSystemData = $this->getSpecificShipSystem(
146
                    ShipSystemTypeEnum::SYSTEM_SINGULARITY_REACTOR,
147
                    SingularityCoreSystemData::class
148
                );
149
            }
150
            if ($ship->hasShipSystem(ShipSystemTypeEnum::SYSTEM_FUSION_REACTOR)) {
151
                $reactorSystemData = $this->getSpecificShipSystem(
152
                    ShipSystemTypeEnum::SYSTEM_FUSION_REACTOR,
153
                    FusionCoreSystemData::class
154
                );
155
            }
156
157
            if ($reactorSystemData === null) {
158
                return null;
159
            }
160
161
            $this->reactorWrapper = new ReactorWrapper($this, $reactorSystemData);
162
        }
163
164
        return $this->reactorWrapper;
165
    }
166
167
    public function setAlertState(ShipAlertStateEnum $alertState): ?string
168
    {
169
        $msg = $this->shipStateChanger->changeAlertState($this, $alertState);
170
        $this->epsUsage = $this->reloadEpsUsage();
171
172
        return $msg;
173
    }
174
175
    /**
176
     * highest damage first, then prio
177
     *
178
     * @return ShipSystemInterface[]
179
     */
180
    public function getDamagedSystems(): array
181
    {
182
        $damagedSystems = [];
183
        $prioArray = [];
184
        foreach ($this->get()->getSystems() as $system) {
185
            if ($system->getStatus() < 100) {
186
                $damagedSystems[] = $system;
187
                $prioArray[$system->getSystemType()->value] = $this->shipSystemManager->lookupSystem($system->getSystemType())->getPriority();
188
            }
189
        }
190
191
        // sort by damage and priority
192
        usort(
193
            $damagedSystems,
194
            function (ShipSystemInterface $a, ShipSystemInterface $b) use ($prioArray): int {
195
                if ($a->getStatus() === $b->getStatus()) {
196
                    return $prioArray[$b->getSystemType()->value] <=> $prioArray[$a->getSystemType()->value];
197
                }
198
                return ($a->getStatus() < $b->getStatus()) ? -1 : 1;
199
            }
200
        );
201
202
        return $damagedSystems;
203
    }
204
205
    public function isOwnedByCurrentUser(): bool
206
    {
207
        return $this->game->getUser() === $this->get()->getUser();
208
    }
209
210
    public function canLandOnCurrentColony(): bool
211
    {
212
        if ($this->get()->getRump()->getCommodity() === null) {
213
            return false;
214
        }
215
        if ($this->get()->isShuttle()) {
216
            return false;
217
        }
218
219
        $currentColony = $this->get()->getStarsystemMap() !== null ? $this->get()->getStarsystemMap()->getColony() : null;
220
221
        if ($currentColony === null) {
222
            return false;
223
        }
224
        if ($currentColony->getUser() !== $this->get()->getUser()) {
225
            return false;
226
        }
227
228
        return $this->colonyLibFactory
229
            ->createColonySurface($currentColony)
230
            ->hasAirfield();
231
    }
232
233
    public function canBeRepaired(): bool
234
    {
235
        if ($this->get()->getAlertState() !== ShipAlertStateEnum::ALERT_GREEN) {
236
            return false;
237
        }
238
239
        if ($this->get()->getShieldState()) {
240
            return false;
241
        }
242
243
        if ($this->get()->getCloakState()) {
244
            return false;
245
        }
246
247
        if (!empty($this->getDamagedSystems())) {
248
            return true;
249
        }
250
251
        return $this->get()->getHull() < $this->get()->getMaxHull();
252
    }
253
254 4
    public function canFire(): bool
255
    {
256 4
        $ship = $this->get();
257 4
        if (!$ship->getNbs()) {
258 1
            return false;
259
        }
260 3
        if (!$ship->hasActiveWeapon()) {
261 1
            return false;
262
        }
263
264 2
        $epsSystem = $this->getEpsSystemData();
265 2
        return $epsSystem !== null && $epsSystem->getEps() !== 0;
266
    }
267
268 2
    public function getRepairDuration(): int
269
    {
270 2
        return $this->repairUtil->getRepairDuration($this);
271
    }
272
273
    public function getRepairDurationPreview(): int
274
    {
275
        return $this->repairUtil->getRepairDurationPreview($this);
276
    }
277
278
    public function getRepairCosts(): array
279
    {
280
        $neededParts = $this->repairUtil->determineSpareParts($this, false);
281
282
        $neededSpareParts = $neededParts[CommodityTypeEnum::COMMODITY_SPARE_PART];
283
        $neededSystemComponents = $neededParts[CommodityTypeEnum::COMMODITY_SYSTEM_COMPONENT];
284
285
        return [
286
            new ShipRepairCost($neededSpareParts, CommodityTypeEnum::COMMODITY_SPARE_PART, CommodityTypeEnum::getDescription(CommodityTypeEnum::COMMODITY_SPARE_PART)),
287
            new ShipRepairCost($neededSystemComponents, CommodityTypeEnum::COMMODITY_SYSTEM_COMPONENT, CommodityTypeEnum::getDescription(CommodityTypeEnum::COMMODITY_SYSTEM_COMPONENT))
288
        ];
289
    }
290
291
    public function getPossibleTorpedoTypes(): array
292
    {
293
        if ($this->ship->hasShipSystem(ShipSystemTypeEnum::SYSTEM_TORPEDO_STORAGE)) {
294
            return $this->torpedoTypeRepository->getAll();
295
        }
296
297
        return $this->torpedoTypeRepository->getByLevel($this->ship->getRump()->getTorpedoLevel());
298
    }
299
300
    public function getTractoredShipWrapper(): ?ShipWrapperInterface
301
    {
302
        $tractoredShip = $this->get()->getTractoredShip();
303
        if ($tractoredShip === null) {
304
            return null;
305
        }
306
307
        return $this->shipWrapperFactory->wrapShip($tractoredShip);
308
    }
309
310
    public function getTractoringShipWrapper(): ?ShipWrapperInterface
311
    {
312
        $tractoringShip = $this->get()->getTractoringShip();
313
        if ($tractoringShip === null) {
314
            return null;
315
        }
316
317
        return $this->shipWrapperFactory->wrapShip($tractoringShip);
318
    }
319
320
    public function getDockedToShipWrapper(): ?ShipWrapperInterface
321
    {
322
        $dockedTo = $this->get()->getDockedTo();
323
        if ($dockedTo === null) {
324
            return null;
325
        }
326
327
        return $this->shipWrapperFactory->wrapShip($dockedTo);
328
    }
329
330 7
    public function getStateIconAndTitle(): ?array
331
    {
332 7
        $ship = $this->get();
333
334 7
        $state = $ship->getState();
335
336 7
        if ($state === ShipStateEnum::SHIP_STATE_REPAIR_ACTIVE) {
337 2
            $isBase = $ship->isBase();
338 2
            return ['rep2', sprintf('%s repariert die Station', $isBase ? 'Stationscrew' : 'Schiffscrew')];
339
        }
340
341 5
        if ($state === ShipStateEnum::SHIP_STATE_REPAIR_PASSIVE) {
342 2
            $isBase = $ship->isBase();
343 2
            $repairDuration = $this->getRepairDuration();
344 2
            return ['rep2', sprintf('%s wird repariert (noch %d Runden)', $isBase ? 'Station' : 'Schiff', $repairDuration)];
345
        }
346
347 3
        $currentTurn = $this->game->getCurrentRound()->getTurn();
348 3
        if ($state === ShipStateEnum::SHIP_STATE_ASTRO_FINALIZING) {
349 1
            return ['map1', sprintf(
350 1
                'Schiff kartographiert (noch %d Runden)',
351 1
                $ship->getAstroStartTurn() + AstronomicalMappingEnum::TURNS_TO_FINISH - $currentTurn
352 1
            )];
353
        }
354
355 2
        $takeover = $ship->getTakeoverActive();
356
        if (
357 2
            $state === ShipStateEnum::SHIP_STATE_ACTIVE_TAKEOVER
358 2
            && $takeover !== null
359
        ) {
360 1
            $targetNamePlainText = $this->bbCodeParser->parse($takeover->getTargetShip()->getName())->getAsText();
361 1
            return ['take2', sprintf(
362 1
                'Schiff übernimmt die "%s" (noch %d Runden)',
363 1
                $targetNamePlainText,
364 1
                $this->getTakeoverTicksLeft($takeover)
365 1
            )];
366
        }
367
368 1
        $takeover = $ship->getTakeoverPassive();
369 1
        if ($takeover !== null) {
370 1
            $sourceUserNamePlainText = $this->bbCodeParser->parse($takeover->getSourceShip()->getUser()->getName())->getAsText();
371 1
            return ['untake2', sprintf(
372 1
                'Schiff wird von Spieler "%s" übernommen (noch %d Runden)',
373 1
                $sourceUserNamePlainText,
374 1
                $this->getTakeoverTicksLeft($takeover)
375 1
            )];
376
        }
377
378
        return null;
379
    }
380
381 2
    public function getTakeoverTicksLeft(ShipTakeoverInterface $takeover = null): int
382
    {
383 2
        $takeover = $takeover ?? $this->get()->getTakeoverActive();
384 2
        if ($takeover === null) {
385
            throw new RuntimeException('should not call when active takeover is null');
386
        }
387
388 2
        $currentTurn = $this->game->getCurrentRound()->getTurn();
389
390 2
        return $takeover->getStartTurn() + ShipTakeoverManagerInterface::TURNS_TO_TAKEOVER - $currentTurn;
391
    }
392
393
    public function canBeScrapped(): bool
394
    {
395
        $ship = $this->get();
396
397
        return $ship->isBase() && $ship->getState() !== ShipStateEnum::SHIP_STATE_UNDER_SCRAPPING;
398
    }
399
400
    public function getCrewStyle(): string
401
    {
402
        $ship = $this->get();
403
        $excessCrew = $ship->getExcessCrewCount();
404
405
        if ($excessCrew === 0) {
406
            return "";
407
        }
408
409
        return $excessCrew > 0 ? "color: green;" : "color: red;";
410
    }
411
412 1
    public function getHullSystemData(): HullSystemData
413
    {
414 1
        $hullSystemData = $this->getSpecificShipSystem(
415 1
            ShipSystemTypeEnum::SYSTEM_HULL,
416 1
            HullSystemData::class
417 1
        );
418
419 1
        if ($hullSystemData === null) {
420
            throw new SystemNotFoundException('no hull installed?');
421
        }
422
423 1
        return $hullSystemData;
424
    }
425
426
    public function getShieldSystemData(): ?ShieldSystemData
427
    {
428
        return $this->getSpecificShipSystem(
429
            ShipSystemTypeEnum::SYSTEM_SHIELDS,
430
            ShieldSystemData::class
431
        );
432
    }
433
434 2
    public function getEpsSystemData(): ?EpsSystemData
435
    {
436 2
        return $this->getSpecificShipSystem(
437 2
            ShipSystemTypeEnum::SYSTEM_EPS,
438 2
            EpsSystemData::class
439 2
        );
440
    }
441
442
    public function getWarpDriveSystemData(): ?WarpDriveSystemData
443
    {
444
        return $this->getSpecificShipSystem(
445
            ShipSystemTypeEnum::SYSTEM_WARPDRIVE,
446
            WarpDriveSystemData::class
447
        );
448
    }
449
450
    public function getTrackerSystemData(): ?TrackerSystemData
451
    {
452
        return $this->getSpecificShipSystem(
453
            ShipSystemTypeEnum::SYSTEM_TRACKER,
454
            TrackerSystemData::class
455
        );
456
    }
457
458
    public function getWebEmitterSystemData(): ?WebEmitterSystemData
459
    {
460
        return $this->getSpecificShipSystem(
461
            ShipSystemTypeEnum::SYSTEM_THOLIAN_WEB,
462
            WebEmitterSystemData::class
463
        );
464
    }
465
466
    /**
467
     * @template T
468
     * @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...
469
     *
470
     * @return T|null
471
     */
472 3
    private function getSpecificShipSystem(ShipSystemTypeEnum $systemType, string $className)
473
    {
474 3
        return $this->systemDataDeserializer->getSpecificShipSystem(
475 3
            $this->get(),
476 3
            $systemType,
477 3
            $className,
478 3
            $this->shipSystemDataCache,
479 3
            $this->shipWrapperFactory
480 3
        );
481
    }
482
}
483