Passed
Push — master ( 784be2...c4713a )
by Nico
58:15 queued 29:26
created

ShowShip   B

Complexity

Total Complexity 52

Size/Duplication

Total Lines 387
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
eloc 218
dl 0
loc 387
ccs 0
cts 214
cp 0
rs 7.44
c 0
b 0
f 0
wmc 52

9 Methods

Rating   Name   Duplication   Size   Complexity  
C handle() 0 104 13
A __construct() 0 37 1
A getDatabaseEntryForShipLocation() 0 18 4
A createUserLayerIfNecessary() 0 22 4
A getColony() 0 7 2
B getAstroState() 0 31 7
B doConstructionStuff() 0 41 7
B doStationStuff() 0 47 11
A addWarpcoreSplitJavascript() 0 22 3

How to fix   Complexity   

Complex Class

Complex classes like ShowShip often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ShowShip, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Stu\Module\Ship\View\ShowShip;
6
7
use NavPanel;
8
use request;
9
use Stu\Component\Game\GameEnum;
10
use Stu\Component\Player\ColonizationCheckerInterface;
11
use Stu\Component\Ship\AstronomicalMappingEnum;
12
use Stu\Component\Ship\Crew\ShipCrewCalculatorInterface;
13
use Stu\Component\Ship\Nbs\NbsUtilityInterface;
14
use Stu\Component\Ship\ShipModuleTypeEnum;
15
use Stu\Component\Ship\ShipRumpEnum;
16
use Stu\Component\Station\StationUtilityInterface;
17
use Stu\Lib\ColonyStorageCommodityWrapper\ColonyStorageCommodityWrapper;
18
use Stu\Lib\SessionInterface;
19
use Stu\Module\Colony\Lib\ColonyLibFactoryInterface;
20
use Stu\Module\Control\GameControllerInterface;
21
use Stu\Module\Control\ViewControllerInterface;
22
use Stu\Module\Database\View\Category\Tal\DatabaseCategoryTalFactoryInterface;
23
use Stu\Module\Logging\LoggerEnum;
24
use Stu\Module\Logging\LoggerUtilFactoryInterface;
25
use Stu\Module\Logging\LoggerUtilInterface;
26
use Stu\Module\Ship\Lib\Battle\FightLibInterface;
27
use Stu\Module\Ship\Lib\ShipLoaderInterface;
28
use Stu\Module\Ship\Lib\ShipRumpSpecialAbilityEnum;
29
use Stu\Module\Ship\Lib\ShipWrapperFactoryInterface;
30
use Stu\Module\Ship\Lib\ShipWrapperInterface;
31
use Stu\Module\Ship\Lib\Ui\ShipUiFactoryInterface;
32
use Stu\Orm\Entity\ColonyInterface;
33
use Stu\Orm\Entity\DatabaseEntryInterface;
34
use Stu\Orm\Entity\ShipInterface;
35
use Stu\Orm\Entity\StationShipRepairInterface;
36
use Stu\Orm\Entity\UserInterface;
37
use Stu\Orm\Repository\AstroEntryRepositoryInterface;
38
use Stu\Orm\Repository\DatabaseUserRepositoryInterface;
39
use Stu\Orm\Repository\ShipyardShipQueueRepositoryInterface;
40
use Stu\Orm\Repository\StationShipRepairRepositoryInterface;
41
use Stu\Orm\Repository\UserLayerRepositoryInterface;
42
43
final class ShowShip implements ViewControllerInterface
44
{
45
    public const VIEW_IDENTIFIER = 'SHOW_SHIP';
46
47
    private SessionInterface $session;
48
49
    private LoggerUtilFactoryInterface $loggerUtilFactory;
50
51
    private LoggerUtilInterface $loggerUtil;
52
53
    private ShipLoaderInterface $shipLoader;
54
55
    private ColonizationCheckerInterface $colonizationChecker;
56
57
    private DatabaseCategoryTalFactoryInterface $databaseCategoryTalFactory;
58
59
    private AstroEntryRepositoryInterface $astroEntryRepository;
60
61
    private DatabaseUserRepositoryInterface $databaseUserRepository;
62
63
    private NbsUtilityInterface $nbsUtility;
64
65
    private StationShipRepairRepositoryInterface $stationShipRepairRepository;
66
67
    private ShipyardShipQueueRepositoryInterface $shipyardShipQueueRepository;
68
69
    private UserLayerRepositoryInterface $userLayerRepository;
70
71
    private StationUtilityInterface $stationUtility;
72
73
    private ShipWrapperFactoryInterface $shipWrapperFactory;
74
75
    private ColonyLibFactoryInterface $colonyLibFactory;
76
77
    private ShipUiFactoryInterface $shipUiFactory;
78
79
    private ShipCrewCalculatorInterface $shipCrewCalculator;
80
81
    private FightLibInterface $fightLib;
82
83
    public function __construct(
84
        SessionInterface $session,
85
        ShipLoaderInterface $shipLoader,
86
        ColonizationCheckerInterface $colonizationChecker,
87
        DatabaseCategoryTalFactoryInterface $databaseCategoryTalFactory,
88
        AstroEntryRepositoryInterface $astroEntryRepository,
89
        DatabaseUserRepositoryInterface $databaseUserRepository,
90
        NbsUtilityInterface $nbsUtility,
91
        StationShipRepairRepositoryInterface $stationShipRepairRepository,
92
        ShipyardShipQueueRepositoryInterface $shipyardShipQueueRepository,
93
        UserLayerRepositoryInterface $userLayerRepository,
94
        StationUtilityInterface $stationUtility,
95
        ShipWrapperFactoryInterface $shipWrapperFactory,
96
        ColonyLibFactoryInterface $colonyLibFactory,
97
        ShipUiFactoryInterface $shipUiFactory,
98
        ShipCrewCalculatorInterface $shipCrewCalculator,
99
        FightLibInterface $fightLib,
100
        LoggerUtilFactoryInterface $loggerUtilFactory
101
    ) {
102
        $this->session = $session;
103
        $this->shipLoader = $shipLoader;
104
        $this->colonizationChecker = $colonizationChecker;
105
        $this->databaseCategoryTalFactory = $databaseCategoryTalFactory;
106
        $this->astroEntryRepository = $astroEntryRepository;
107
        $this->databaseUserRepository = $databaseUserRepository;
108
        $this->nbsUtility = $nbsUtility;
109
        $this->stationShipRepairRepository = $stationShipRepairRepository;
110
        $this->shipyardShipQueueRepository = $shipyardShipQueueRepository;
111
        $this->userLayerRepository = $userLayerRepository;
112
        $this->stationUtility = $stationUtility;
113
        $this->shipWrapperFactory = $shipWrapperFactory;
114
        $this->loggerUtilFactory = $loggerUtilFactory;
115
        $this->loggerUtil = $loggerUtilFactory->getLoggerUtil();
116
        $this->colonyLibFactory = $colonyLibFactory;
117
        $this->shipUiFactory = $shipUiFactory;
118
        $this->shipCrewCalculator = $shipCrewCalculator;
119
        $this->fightLib = $fightLib;
120
    }
121
122
    public function handle(GameControllerInterface $game): void
123
    {
124
        $user = $game->getUser();
125
        $userId = $user->getId();
126
        $ownsCurrentColony = false;
127
128
        $wrapper = $this->shipLoader->getWrapperByIdAndUser(
129
            request::indInt('id'),
130
            $userId,
131
            true
132
        );
133
        $ship = $wrapper->get();
134
135
        $tachyonFresh = $game->getViewContext()['TACHYON_SCAN_JUST_HAPPENED'] ?? false;
136
        $tachyonActive = $tachyonFresh;
137
138
        // check if tachyon scan still active
139
        if (!$tachyonActive) {
140
            $tachyonActive = $this->nbsUtility->isTachyonActive($ship);
141
        }
142
143
        $rump = $ship->getRump();
144
145
        $colony = $this->getColony($ship);
146
        $canColonize = false;
147
        if ($colony !== null) {
148
            if ($rump->hasSpecialAbility(ShipRumpSpecialAbilityEnum::COLONIZE)) {
149
                $canColonize = $this->colonizationChecker->canColonize($user, $colony);
150
            }
151
            $ownsCurrentColony = $colony->getUser() === $user;
152
        }
153
154
        //Forschungseintrag erstellen, damit System-Link optional erstellt werden kann
155
        $starSystem = $ship->getSystem();
156
        if ($starSystem !== null && $starSystem->getDatabaseEntry() !== null) {
157
            $starSystemEntryTal = $this->databaseCategoryTalFactory->createDatabaseCategoryEntryTal($starSystem->getDatabaseEntry(), $user);
158
            $game->setTemplateVar('STARSYSTEM_ENTRY_TAL', $starSystemEntryTal);
159
        }
160
161
        $isBase = $ship->isBase();
162
        $game->appendNavigationPart(
163
            $isBase ? 'station.php' : 'ship.php',
164
            $isBase ? _('Stationen') : _('Schiffe')
165
        );
166
167
        $game->appendNavigationPart(
168
            sprintf('?%s=1&id=%d', static::VIEW_IDENTIFIER, $ship->getId()),
169
            $ship->getName()
170
        );
171
        $game->setPagetitle($ship->getName());
172
173
        $game->setTemplateFile('html/ship.twig', true);
174
175
        $game->setTemplateVar('WRAPPER', $wrapper);
176
177
        if ($ship->getLss()) {
178
179
            $this->createUserLayerIfNecessary($user, $ship);
180
181
            $game->setTemplateVar('VISUAL_NAV_PANEL', $this->shipUiFactory->createVisualNavPanel(
182
                $ship,
183
                $game->getUser(),
184
                $this->loggerUtilFactory->getLoggerUtil(),
185
                $ship->getTachyonState(),
186
                $tachyonFresh
187
            ));
188
        }
189
        $game->setTemplateVar('NAV_PANEL', new NavPanel($ship));
190
191
        $this->doConstructionStuff($ship, $game);
192
        $this->doStationStuff($ship, $game);
193
194
        $this->nbsUtility->setNbsTemplateVars($ship, $game, $this->session, $tachyonActive);
195
196
        $game->setTemplateVar('ASTRO_STATE_SYSTEM', $this->getAstroState($ship, $game, true));
197
        $game->setTemplateVar('ASTRO_STATE_REGION', $this->getAstroState($ship, $game, false));
198
        $game->setTemplateVar('TACHYON_ACTIVE', $tachyonActive);
199
        $game->setTemplateVar('CAN_COLONIZE', $canColonize);
200
        $game->setTemplateVar('OWNS_CURRENT_COLONY', $ownsCurrentColony);
201
        $game->setTemplateVar('CURRENT_COLONY', $colony);
202
        $game->setTemplateVar('FIGHT_LIB', $this->fightLib);
203
204
        $userLayers = $user->getUserLayers();
205
        if ($ship->hasTranswarp()) {
206
            $game->setTemplateVar('USER_LAYERS', $userLayers);
207
        }
208
209
        $layer = $ship->getLayer();
210
        if ($layer !== null && $userLayers->containsKey($layer->getId())) {
211
            $game->setTemplateVar('IS_MAP_BUTTON_VISIBLE', true);
212
        }
213
214
        $crewObj = $this->shipCrewCalculator->getCrewObj($rump);
215
216
        $game->setTemplateVar(
217
            'MAX_CREW_COUNT',
218
            $crewObj === null
219
                ? null
220
                : $this->shipCrewCalculator->getMaxCrewCountByShip($ship)
221
        );
222
223
        $this->addWarpcoreSplitJavascript($wrapper, $game);
224
225
        $this->loggerUtil->log(sprintf('ShowShip.handle-end, timestamp: %F', microtime(true)));
226
    }
227
228
    private function createUserLayerIfNecessary(UserInterface $user, ShipInterface $ship): void
229
    {
230
        $layer = $ship->getLayer();
231
        if ($layer === null) {
232
            return;
233
        }
234
235
        if ($ship->getMap() === null) {
236
            return;
237
        }
238
239
        $hasSeenLayer = $user->hasSeen($layer->getId());
240
        if ($hasSeenLayer) {
241
            return;
242
        }
243
244
        $userLayer = $this->userLayerRepository->prototype();
245
        $userLayer->setLayer($layer);
246
        $userLayer->setUser($user);
247
        $this->userLayerRepository->save($userLayer);
248
249
        $user->getUserLayers()->set($layer->getId(), $userLayer);
250
    }
251
252
    private function addWarpcoreSplitJavascript(ShipWrapperInterface $wrapper, GameControllerInterface $game): void
253
    {
254
        $warpDriveSystem = $wrapper->getWarpDriveSystemData();
255
        $epsSystem = $wrapper->getEpsSystemData();
256
257
        if ($warpDriveSystem !== null && $epsSystem !== null) {
258
            $ship = $wrapper->get();
259
260
            $game->addExecuteJS(sprintf(
261
                'setReactorSplitConstants(%d,%d,%d,%d,%d,%d,%d);',
262
                $ship->getReactorOutputCappedByReactorLoad(),
263
                $wrapper->getEpsUsage(),
264
                $ship->getRump()->getFlightEcost(),
265
                $epsSystem->getEps(),
266
                $epsSystem->getMaxEps(),
267
                $warpDriveSystem->getWarpDrive(),
268
                $warpDriveSystem->getMaxWarpDrive()
269
            ), GameEnum::JS_EXECUTION_AFTER_RENDER);
270
            $game->addExecuteJS(sprintf(
271
                'updateEpsSplitValues(%d);',
272
                $warpDriveSystem->getWarpCoreSplit(),
273
            ), GameEnum::JS_EXECUTION_AFTER_RENDER);
274
        }
275
    }
276
277
    private function getColony(ShipInterface $ship): ?ColonyInterface
278
    {
279
        if ($ship->getStarsystemMap() === null) {
280
            return null;
281
        }
282
283
        return $ship->getStarsystemMap()->getColony();
284
    }
285
286
    private function getAstroState(ShipInterface $ship, GameControllerInterface $game, bool $isSystem): AstroStateWrapper
287
    {
288
        //$this->loggerUtil->init('SS', LoggerEnum::LEVEL_ERROR);
289
290
        $databaseEntry = $this->getDatabaseEntryForShipLocation($ship, $isSystem);
291
292
        $this->loggerUtil->log(sprintf('databaseEntry: %d', $databaseEntry !== null ? $databaseEntry->getId() : 0));
293
294
        $astroEntry = null;
295
296
        if ($databaseEntry === null) {
297
            $state = AstronomicalMappingEnum::NONE;
298
        } elseif ($this->databaseUserRepository->exists($game->getUser()->getId(), $databaseEntry->getId())) {
299
            $state = AstronomicalMappingEnum::DONE;
300
        } else {
301
            $astroEntry = $this->astroEntryRepository->getByShipLocation($ship, $isSystem);
302
303
            $this->loggerUtil->log(sprintf('isSystem: %b, astroEntry?: %b', $isSystem, $astroEntry !== null));
304
305
            $state = $astroEntry === null ? AstronomicalMappingEnum::PLANNABLE : $astroEntry->getState();
306
        }
307
        $turnsLeft = null;
308
        if ($state === AstronomicalMappingEnum::FINISHING && $astroEntry !== null) {
309
            $turnsLeft = AstronomicalMappingEnum::TURNS_TO_FINISH - ($game->getCurrentRound()->getTurn() - $astroEntry->getAstroStartTurn());
310
        }
311
312
        $wrapper = new AstroStateWrapper($state, $turnsLeft, $isSystem);
313
314
        $this->loggerUtil->log(sprintf('type: %s', $wrapper->getType()));
315
316
        return $wrapper;
317
    }
318
319
    private function getDatabaseEntryForShipLocation(ShipInterface $ship, bool $isSystem): ?DatabaseEntryInterface
320
    {
321
        if ($isSystem) {
322
            $system = $ship->getSystem() ?? $ship->isOverSystem();
323
            if ($system !== null) {
324
                return $system->getDatabaseEntry();
325
            }
326
327
            return null;
328
        }
329
330
        $mapRegion = $ship->getMapRegion();
331
        if ($mapRegion !== null) {
332
            $this->loggerUtil->log('mapREgion found');
333
            return $mapRegion->getDatabaseEntry();
334
        }
335
336
        return null;
337
    }
338
339
    private function doConstructionStuff(ShipInterface $ship, GameControllerInterface $game): void
340
    {
341
        if (!$ship->isConstruction() && !$ship->isBase()) {
342
            return;
343
        }
344
345
        $progress =  $this->stationUtility->getConstructionProgress($ship);
346
        if ($progress === null || $progress->getRemainingTicks() === 0) {
347
            $game->setTemplateVar('CONSTRUCTION_PROGRESS_WRAPPER', null);
348
        } else {
349
            $dockedWorkbees = $this->stationUtility->getDockedWorkbeeCount($ship);
350
            $neededWorkbees = $this->stationUtility->getNeededWorkbeeCount($ship, $ship->getRump());
351
352
            $game->setTemplateVar('CONSTRUCTION_PROGRESS_WRAPPER', new ConstructionProgressWrapper(
353
                $progress,
354
                $ship,
355
                $dockedWorkbees,
356
                $neededWorkbees
357
            ));
358
        }
359
360
        if ($progress === null) {
361
            $plans = $this->stationUtility->getStationBuildplansByUser($game->getUser()->getId());
362
            $game->setTemplateVar('POSSIBLE_STATIONS', $plans);
363
364
            $moduleSelectors = [];
365
            foreach ($plans as $plan) {
366
                $ms = $this->colonyLibFactory->createModuleSelectorSpecial(
367
                    ShipModuleTypeEnum::MODULE_TYPE_SPECIAL,
368
                    null,
369
                    $ship,
370
                    $plan->getRump(),
371
                    $game->getUser()->getId()
372
                );
373
374
                $ms->setDummyId($plan->getId());
375
                $moduleSelectors[] = $ms;
376
            }
377
378
            $game->setTemplateVar('MODULE_SELECTORS', $moduleSelectors);
379
            $game->setTemplateVar('HAS_STORAGE', new ColonyStorageCommodityWrapper($ship->getStorage()));
380
        }
381
    }
382
383
    private function doStationStuff(ShipInterface $ship, GameControllerInterface $game): void
384
    {
385
        if ($this->stationUtility->canManageShips($ship)) {
386
            $game->setTemplateVar('CAN_MANAGE', true);
387
        }
388
389
        if ($this->stationUtility->canRepairShips($ship)) {
390
            $game->setTemplateVar('CAN_REPAIR', true);
391
392
            $shipRepairProgress = array_map(
393
                fn (StationShipRepairInterface $repair): ShipWrapperInterface => $this->shipWrapperFactory->wrapShip($repair->getShip()),
394
                $this->stationShipRepairRepository->getByStation(
395
                    $ship->getId()
396
                )
397
            );
398
399
            $game->setTemplateVar('SHIP_REPAIR_PROGRESS', $shipRepairProgress);
400
        }
401
402
        if ($ship->getRump()->getRoleId() === ShipRumpEnum::SHIP_ROLE_SHIPYARD) {
403
            $game->setTemplateVar('SHIP_BUILD_PROGRESS', $this->shipyardShipQueueRepository->getByShipyard($ship->getId()));
404
        }
405
406
        $firstOrbitShip = null;
407
408
        $dockedShips = $ship->getDockedShips();
409
        if (!$dockedShips->isEmpty()) {
410
            // if selected, return the current target
411
            $target = request::postInt('target');
412
413
            if ($target !== 0) {
414
                foreach ($dockedShips as $ship) {
415
                    if ($ship->getId() === $target) {
416
                        $firstOrbitShip = $ship;
417
                    }
418
                }
419
            }
420
            if ($firstOrbitShip === null) {
0 ignored issues
show
introduced by
The condition $firstOrbitShip === null is always true.
Loading history...
421
                $firstOrbitShip = $dockedShips->first();
422
            }
423
        }
424
425
        $game->setTemplateVar('FIRST_MANAGE_SHIP', $firstOrbitShip !== null ? $this->shipWrapperFactory->wrapShip($firstOrbitShip) : null);
426
        $game->setTemplateVar('CAN_UNDOCK', true);
427
428
        if ($ship->getRump()->isShipyard()) {
429
            $game->setTemplateVar('AVAILABLE_BUILDPLANS', $this->stationUtility->getShipyardBuildplansByUser($game->getUser()->getId()));
430
        }
431
    }
432
}
433