Passed
Push — master ( 2f9ca7...02ce50 )
by Nico
23:14 queued 13:11
created

ShowShip   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 321
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 184
dl 0
loc 321
ccs 0
cts 180
cp 0
rs 8.96
c 2
b 0
f 0
wmc 43

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 35 1
C handle() 0 93 11
B doStationStuff() 0 50 11
A addWarpcoreSplitJavascript() 0 21 4
A getColony() 0 7 2
B getAstroState() 0 22 7
B doConstructionStuff() 0 41 7

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