Passed
Push — master ( f4068b...77f637 )
by Nico
40:27 queued 14:09
created

BoardShip::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 32
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 1
eloc 15
c 1
b 0
f 1
nc 1
nop 15
dl 0
loc 32
ccs 0
cts 16
cp 0
crap 2
rs 9.7666

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\Action\BoardShip;
6
7
use request;
8
use Stu\Component\Ship\Nbs\NbsUtilityInterface;
9
use Stu\Component\Ship\ShipStateEnum;
10
use Stu\Exception\SanityCheckException;
11
use Stu\Module\Control\ActionControllerInterface;
12
use Stu\Module\Control\GameControllerInterface;
13
use Stu\Module\Control\StuRandom;
14
use Stu\Module\Message\Lib\DistributedMessageSenderInterface;
15
use Stu\Module\Message\Lib\PrivateMessageFolderSpecialEnum;
16
use Stu\Module\Prestige\Lib\CreatePrestigeLogInterface;
17
use Stu\Module\Ship\Lib\Auxiliary\ShipShutdownInterface;
18
use Stu\Module\Ship\Lib\Battle\FightLibInterface;
19
use Stu\Module\Ship\Lib\CloseCombat\CloseCombatUtilInterface;
20
use Stu\Module\Ship\Lib\Interaction\ThreatReactionInterface;
21
use Stu\Module\Ship\Lib\Interaction\InteractionCheckerInterface;
22
use Stu\Module\Ship\Lib\Interaction\ShipTakeoverManagerInterface;
23
use Stu\Module\Ship\Lib\Message\Message;
24
use Stu\Module\Ship\Lib\Message\MessageCollection;
25
use Stu\Module\Ship\Lib\Message\MessageCollectionInterface;
26
use Stu\Module\Ship\Lib\ShipLoaderInterface;
27
use Stu\Module\Ship\Lib\ShipStateChangerInterface;
28
use Stu\Module\Ship\Lib\ShipWrapperInterface;
29
use Stu\Module\Ship\View\Overview\Overview;
30
use Stu\Module\Ship\View\ShowShip\ShowShip;
31
use Stu\Orm\Entity\ShipCrewInterface;
32
use Stu\Orm\Repository\CrewRepositoryInterface;
33
use Stu\Orm\Repository\ShipCrewRepositoryInterface;
34
use Stu\Orm\Repository\UserRepositoryInterface;
35
36
final class BoardShip implements ActionControllerInterface
37
{
38
    public const ACTION_IDENTIFIER = 'B_BOARD_SHIP';
39
40
    private CrewRepositoryInterface $crewRepository;
41
42
    private ShipCrewRepositoryInterface $shipCrewRepository;
43
44
    private UserRepositoryInterface $userRepository;
45
46
    private ShipLoaderInterface $shipLoader;
47
48
    private InteractionCheckerInterface $interactionChecker;
49
50
    private NbsUtilityInterface $nbsUtility;
51
52
    private CloseCombatUtilInterface $closeCombatUtil;
53
54
    private ThreatReactionInterface $threatReaction;
55
56
    private FightLibInterface $fightLib;
57
58
    private ShipStateChangerInterface $shipStateChanger;
59
60
    private ShipShutdownInterface $shipShutdown;
61
62
    private ShipTakeoverManagerInterface $shipTakeoverManager;
63
64
    private CreatePrestigeLogInterface $createPrestigeLog;
65
66
    private DistributedMessageSenderInterface $distributedMessageSender;
67
68
    private StuRandom $stuRandom;
69
70
    public function __construct(
71
        CrewRepositoryInterface $crewRepository,
72
        ShipCrewRepositoryInterface $shipCrewRepository,
73
        UserRepositoryInterface $userRepository,
74
        ShipLoaderInterface $shipLoader,
75
        InteractionCheckerInterface $interactionChecker,
76
        NbsUtilityInterface $nbsUtility,
77
        CloseCombatUtilInterface $closeCombatUtil,
78
        ThreatReactionInterface $threatReaction,
79
        FightLibInterface $fightLib,
80
        ShipStateChangerInterface $shipStateChanger,
81
        ShipShutdownInterface $shipShutdown,
82
        ShipTakeoverManagerInterface $shipTakeoverManager,
83
        CreatePrestigeLogInterface $createPrestigeLog,
84
        DistributedMessageSenderInterface $distributedMessageSender,
85
        StuRandom $stuRandom
86
    ) {
87
        $this->crewRepository = $crewRepository;
88
        $this->shipCrewRepository = $shipCrewRepository;
89
        $this->userRepository = $userRepository;
90
        $this->shipLoader = $shipLoader;
91
        $this->interactionChecker = $interactionChecker;
92
        $this->nbsUtility = $nbsUtility;
93
        $this->closeCombatUtil = $closeCombatUtil;
94
        $this->threatReaction = $threatReaction;
95
        $this->fightLib = $fightLib;
96
        $this->shipStateChanger = $shipStateChanger;
97
        $this->shipShutdown = $shipShutdown;
98
        $this->shipTakeoverManager = $shipTakeoverManager;
99
        $this->createPrestigeLog = $createPrestigeLog;
100
        $this->distributedMessageSender = $distributedMessageSender;
101
        $this->stuRandom = $stuRandom;
102
    }
103
104
    public function handle(GameControllerInterface $game): void
105
    {
106
        $game->setView(ShowShip::VIEW_IDENTIFIER);
107
108
        $user = $game->getUser();
109
        $userId = $user->getId();
110
111
        $shipId = request::getIntFatal('id');
112
        $targetId = request::getIntFatal('target');
113
114
        $wrappers = $this->shipLoader->getWrappersBySourceAndUserAndTarget(
115
            $shipId,
116
            $userId,
117
            $targetId
118
        );
119
120
        $wrapper = $wrappers->getSource();
121
        $ship = $wrapper->get();
122
123
        $targetWrapper = $wrappers->getTarget();
124
        if ($targetWrapper === null) {
125
            return;
126
        }
127
        $target = $targetWrapper->get();
128
129
        if (!$target->isBoardingPossible()) {
130
            return;
131
        }
132
133
        if ($target->getUser() === $user) {
134
            return;
135
        }
136
137
        if ($target->getCrewCount() === 0) {
138
            return;
139
        }
140
141
        if ($targetWrapper->get()->getShieldState()) {
142
            return;
143
        }
144
145
        if ($target->getUser()->isVacationRequestOldEnough()) {
146
            $game->addInformation(_('Aktion nicht möglich, der Spieler befindet sich im Urlaubsmodus!'));
147
            return;
148
        }
149
150
        if (!$this->interactionChecker->checkPosition($target, $ship)) {
151
            throw new SanityCheckException('InteractionChecker->checkPosition failed', self::ACTION_IDENTIFIER);
152
        }
153
154
        if (!$this->fightLib->canAttackTarget($ship, $target, false)) {
155
            throw new SanityCheckException('Target cant be attacked', self::ACTION_IDENTIFIER);
156
        }
157
158
        if ($target->getCloakState() && !$this->nbsUtility->isTachyonActive($ship)) {
159
            throw new SanityCheckException('Attacked cloaked ship without active tachyon', self::ACTION_IDENTIFIER);
160
        }
161
162
        if ($ship->getCrewCount() === 0) {
163
            $game->addInformation(_('Aktion nicht möglich, keine Crew vorhanden!'));
164
            return;
165
        }
166
167
        $lastTakeover = $user->getLastBoarding();
168
        if (
169
            $lastTakeover !== null
170
            && time() < $lastTakeover +  ShipTakeoverManagerInterface::BOARDING_COOLDOWN_IN_SECONDS
171
        ) {
172
            $game->addInformation(sprintf(
173
                'Enterkommando kann erst wieder um %s entsendet werden',
174
                date('H:i', $lastTakeover +  ShipTakeoverManagerInterface::BOARDING_COOLDOWN_IN_SECONDS)
175
            ));
176
            return;
177
        }
178
179
        $epsSystemData = $wrapper->getEpsSystemData();
180
        if ($epsSystemData === null || $epsSystemData->getEps() === 0) {
181
            $game->addInformation(_('Keine Energie vorhanden'));
182
            return;
183
        }
184
185
        if ($ship->isDisabled()) {
186
            $game->addInformation(_('Das Schiff ist kampfunfähig'));
187
            return;
188
        }
189
190
        if ($this->fightLib->isTargetOutsideFinishedTholianWeb($ship, $target)) {
191
            $game->addInformation(_('Das Ziel ist nicht mit im Energienetz gefangen'));
192
            return;
193
        }
194
195
        if ($ship->getShieldState()) {
196
            $game->addInformation(_("Die Schilde sind aktiviert"));
197
            return;
198
        }
199
200
        $neededPrestige = $this->shipTakeoverManager->getPrestigeForBoardingAttempt($target);
201
        if ($user->getPrestige() < $neededPrestige) {
202
            $game->addInformation(sprintf(
203
                'Nicht genügend Prestige vorhanden, benötigt wird: %d',
204
                $neededPrestige
205
            ));
206
            return;
207
        }
208
209
        $shipName = $ship->getName();
210
        $targetName = $target->getName();
211
        $targetUserId = $target->getUser()->getId();
212
213
        $this->threatReaction->reactToThreat(
214
            $wrapper,
215
            $targetWrapper,
216
            sprintf(
217
                "Die %s versucht die %s in Sektor %s zu entern.",
218
                $shipName,
219
                $targetName,
220
                $ship->getSectorString()
221
            )
222
        );
223
224
        if ($ship->isDestroyed()) {
225
            $game->setView(Overview::VIEW_IDENTIFIER);
226
            return;
227
        }
228
229
        if ($target->getShieldState()) {
230
            $game->addInformationf("Die %s hat die Schilde aktiviert. Enterkommando kann nicht entsendet werden.", $targetName);
231
            return;
232
        }
233
234
        $combatGroupAttacker = $this->closeCombatUtil->getCombatGroup($ship);
235
        $combatGroupDefender = $this->closeCombatUtil->getCombatGroup($target);
236
237
        $messages = new MessageCollection();
238
        $message = new Message($userId, $targetUserId, [sprintf(
239
            'Die %s entsendet ein Enterkommando auf die %s',
240
            $ship->getName(),
241
            $target->getName()
242
        )]);
243
244
        $messages->add($message);
245
246
        $this->shipStateChanger->changeShipState($targetWrapper, ShipStateEnum::SHIP_STATE_NONE);
247
248
        $this->createPrestigeLog->createLog(
249
            -$neededPrestige,
250
            sprintf(
251
                '-%d Prestige erhalten für einen Enterversuch auf die %s von Spieler %s',
252
                $neededPrestige,
253
                $target->getName(),
254
                $target->getUser()->getName()
255
            ),
256
            $user,
257
            time()
258
        );
259
260
        while (!empty($combatGroupAttacker) && !empty($combatGroupDefender)) {
261
            $this->cycleKillRound(
262
                $combatGroupAttacker,
263
                $combatGroupDefender,
264
                $wrapper,
265
                $targetWrapper,
266
                $messages
267
            );
268
        }
269
270
        $message = new Message($userId, $targetUserId);
271
        $messages->add($message);
272
273
        if (empty($combatGroupAttacker)) {
274
            $message->add('Der Enterversuch ist gescheitert');
275
        } else if ($target->getCrewAssignments()->isEmpty()) {
276
            $message->add(sprintf(
277
                'Die Crew der %s wurde getötet. Übernahme kann nun erfolgen.',
278
                $target->getName()
279
            ));
280
        } else {
281
            $message->add(sprintf(
282
                'Es leben noch %d Crewman auf der %s.',
283
                $target->getCrewCount(),
284
                $target->getName()
285
            ));
286
        }
287
288
        $user->setLastBoarding(time());
289
        $this->userRepository->save($user);
290
291
        $this->sendPms(
292
            $userId,
293
            $ship->getSectorString(),
294
            $messages,
295
            $target->isBase()
296
        );
297
298
        $informations = $messages->getInformationDump();
299
300
        $game->addInformationWrapper($informations);
301
    }
302
303
    /**
304
     * @param array<ShipCrewInterface> $attackers
305
     * @param array<ShipCrewInterface> $defenders
306
     */
307
    private function cycleKillRound(
308
        array &$attackers,
309
        array &$defenders,
310
        ShipWrapperInterface $wrapper,
311
        ShipWrapperInterface $targetWrapper,
312
        MessageCollectionInterface $messages
313
    ): void {
314
315
        $ship = $wrapper->get();
316
        $target = $targetWrapper->get();
317
318
        $combatValueAttacker = $this->closeCombatUtil->getCombatValue($attackers, $ship->getUser()->getFaction());
319
        $combatValueDefender = $this->closeCombatUtil->getCombatValue($defenders, $target->getUser()->getFaction());
320
321
        $rand = $this->stuRandom->rand(
322
            0,
323
            $combatValueAttacker + $combatValueDefender
324
        );
325
326
        $isDeathOnDefenderSide = $rand <= $combatValueAttacker;
327
        if ($isDeathOnDefenderSide) {
328
            $killedShipCrew = $this->getKilledCrew($defenders, $targetWrapper);
329
        } else {
330
            $killedShipCrew = $this->getKilledCrew($attackers, $wrapper);
331
        }
332
333
        $message = new Message();
334
        $message->add(sprintf(
335
            '%s %s von der %s wurde im Kampf getötet',
336
            $killedShipCrew->getCrew()->getTypeDescription(),
337
            $killedShipCrew->getCrew()->getName(),
338
            $isDeathOnDefenderSide ? $target->getName() : $ship->getName()
339
        ));
340
        $messages->add($message);
341
    }
342
343
    /**
344
     * @param array<int, ShipCrewInterface> &$combatGroup
345
     */
346
    private function getKilledCrew(array &$combatGroup, ShipWrapperInterface $wrapper): ShipCrewInterface
347
    {
348
        $keys = array_keys($combatGroup);
349
        shuffle($keys);
350
351
        $randomKey = current($keys);
352
353
        $killedShipCrew = $combatGroup[$randomKey];
354
        unset($combatGroup[$randomKey]);
355
356
        $ship = $wrapper->get();
357
        $ship->getCrewAssignments()->removeElement($killedShipCrew);
358
        $this->crewRepository->delete($killedShipCrew->getCrew());
359
        $this->shipCrewRepository->delete($killedShipCrew);
360
361
        if ($ship->getCrewAssignments()->isEmpty()) {
362
            $this->shipShutdown->shutdown($wrapper);
363
        }
364
365
        return $killedShipCrew;
366
    }
367
368
    private function sendPms(
369
        int $userId,
370
        string $sectorString,
371
        MessageCollectionInterface $messageCollection,
372
        bool $isTargetBase
373
    ): void {
374
375
        $header = sprintf(
376
            _("Enterversuch in Sektor %s"),
377
            $sectorString
378
        );
379
380
        $this->distributedMessageSender->distributeMessageCollection(
381
            $messageCollection,
382
            $userId,
383
            $isTargetBase ? PrivateMessageFolderSpecialEnum::PM_SPECIAL_STATION : PrivateMessageFolderSpecialEnum::PM_SPECIAL_SHIP,
384
            $header
385
        );
386
    }
387
388
    public function performSessionCheck(): bool
389
    {
390
        return true;
391
    }
392
}
393