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

AttackShip::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 18
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 8
c 0
b 0
f 0
nc 1
nop 8
dl 0
loc 18
ccs 0
cts 9
cp 0
crap 2
rs 10

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\AttackShip;
6
7
use request;
8
use RuntimeException;
9
use Stu\Component\Ship\Nbs\NbsUtilityInterface;
10
use Stu\Exception\SanityCheckException;
11
use Stu\Module\Control\ActionControllerInterface;
12
use Stu\Module\Control\GameControllerInterface;
13
use Stu\Module\Message\Lib\DistributedMessageSenderInterface;
14
use Stu\Module\Message\Lib\PrivateMessageFolderSpecialEnum;
15
use Stu\Module\Ship\Lib\Battle\AlertRedHelperInterface;
16
use Stu\Module\Ship\Lib\Battle\FightLibInterface;
17
use Stu\Module\Ship\Lib\Message\MessageCollectionInterface;
18
use Stu\Module\Ship\Lib\Battle\ShipAttackCycleInterface;
19
use Stu\Module\Ship\Lib\Interaction\InteractionCheckerInterface;
20
use Stu\Module\Ship\Lib\ShipLoaderInterface;
21
use Stu\Module\Ship\Lib\ShipWrapperFactoryInterface;
22
use Stu\Module\Ship\Lib\ShipWrapperInterface;
23
use Stu\Module\Ship\View\ShowShip\ShowShip;
24
use Stu\Orm\Entity\ShipInterface;
25
26
//TODO unit tests and request class
27
final class AttackShip implements ActionControllerInterface
28
{
29
    public const ACTION_IDENTIFIER = 'B_ATTACK_SHIP';
30
31
    private ShipLoaderInterface $shipLoader;
32
33
    private DistributedMessageSenderInterface $distributedMessageSender;
34
35
    private ShipAttackCycleInterface $shipAttackCycle;
36
37
    private InteractionCheckerInterface $interactionChecker;
38
39
    private AlertRedHelperInterface $alertRedHelper;
40
41
    private NbsUtilityInterface $nbsUtility;
42
43
    private FightLibInterface $fightLib;
44
45
    private ShipWrapperFactoryInterface $shipWrapperFactory;
46
47
    public function __construct(
48
        ShipLoaderInterface $shipLoader,
49
        DistributedMessageSenderInterface $distributedMessageSender,
50
        ShipAttackCycleInterface $shipAttackCycle,
51
        InteractionCheckerInterface $interactionChecker,
52
        AlertRedHelperInterface $alertRedHelper,
53
        NbsUtilityInterface $nbsUtility,
54
        FightLibInterface $fightLib,
55
        ShipWrapperFactoryInterface $shipWrapperFactory
56
    ) {
57
        $this->shipLoader = $shipLoader;
58
        $this->distributedMessageSender = $distributedMessageSender;
59
        $this->shipAttackCycle = $shipAttackCycle;
60
        $this->interactionChecker = $interactionChecker;
61
        $this->alertRedHelper = $alertRedHelper;
62
        $this->nbsUtility = $nbsUtility;
63
        $this->fightLib = $fightLib;
64
        $this->shipWrapperFactory = $shipWrapperFactory;
65
    }
66
67
    public function handle(GameControllerInterface $game): void
68
    {
69
        $userId = $game->getUser()->getId();
70
71
        $shipId = request::indInt('id');
72
        $targetId = request::postIntFatal('target');
73
74
        $wrappers = $this->shipLoader->getWrappersBySourceAndUserAndTarget(
75
            $shipId,
76
            $userId,
77
            $targetId
78
        );
79
80
        $wrapper = $wrappers->getSource();
81
        $ship = $wrapper->get();
82
83
        $targetWrapper = $wrappers->getTarget();
84
        if ($targetWrapper === null) {
85
            return;
86
        }
87
        $target = $targetWrapper->get();
88
89
        if ($target->getUser()->isVacationRequestOldEnough()) {
90
            $game->addInformation(_('Aktion nicht möglich, der Spieler befindet sich im Urlaubsmodus!'));
91
            return;
92
        }
93
94
        if (!$ship->hasEnoughCrew($game)) {
95
            return;
96
        }
97
        if (!$this->interactionChecker->checkPosition($target, $ship)) {
98
            throw new SanityCheckException('InteractionChecker->checkPosition failed', self::ACTION_IDENTIFIER);
99
        }
100
101
        if ($this->isTargetDestroyed($target)) {
102
            $game->setView(ShowShip::VIEW_IDENTIFIER);
103
            $game->addInformation(_('Das Ziel ist bereits zerstört'));
104
            return;
105
        }
106
107
        if (!$this->fightLib->canAttackTarget($ship, $target)) {
108
            throw new SanityCheckException('Target cant be attacked', self::ACTION_IDENTIFIER);
109
        }
110
111
        if ($target->getCloakState() && !$this->nbsUtility->isTachyonActive($ship)) {
112
            throw new SanityCheckException('Attacked cloaked ship without active tachyon', self::ACTION_IDENTIFIER);
113
        }
114
115
        $epsSystemData = $wrapper->getEpsSystemData();
116
        if ($epsSystemData === null || $epsSystemData->getEps() === 0) {
117
            $game->addInformation(_('Keine Energie vorhanden'));
118
            return;
119
        }
120
121
        if ($ship->isDisabled()) {
122
            $game->addInformation(_('Das Schiff ist kampfunfähig'));
123
            return;
124
        }
125
        if ($ship->getDockedTo() !== null) {
126
            $ship->setDockedTo(null);
127
        }
128
129
        $isTargetBase = $target->isBase();
130
131
        [$attacker, $defender, $isFleetFight, $isWebSituation] = $this->getAttackersAndDefenders($wrapper, $targetWrapper);
132
133
        $messageCollection = $this->shipAttackCycle->cycle($attacker, $defender, $isWebSituation);
134
135
        $this->sendPms(
136
            $userId,
137
            $ship->getSectorString(),
138
            $messageCollection,
139
            !$isWebSituation && $isTargetBase
140
        );
141
142
        $informations = $messageCollection->getInformationDump();
143
144
        if ($this->isActiveTractorShipWarped($ship, $target)) {
145
            //Alarm-Rot check for ship
146
            if (!$ship->isDestroyed()) {
147
                $informations->addInformationWrapper($this->alertRedHelper->doItAll($ship));
148
            }
149
150
            //Alarm-Rot check for traktor ship
151
            if (!$this->isTargetDestroyed($target)) {
152
                $informations->addInformationWrapper($this->alertRedHelper->doItAll($target));
153
            }
154
        }
155
156
        if ($ship->isDestroyed()) {
157
            $game->addInformationWrapper($informations);
158
            return;
159
        }
160
        $game->setView(ShowShip::VIEW_IDENTIFIER);
161
162
        if ($isFleetFight) {
163
            $game->addInformation(_("Angriff durchgeführt"));
164
            $game->setTemplateVar('FIGHT_RESULTS', $informations->getInformations());
165
        } else {
166
            $game->addInformationWrapper($informations);
167
        }
168
    }
169
170
    private function isTargetDestroyed(ShipInterface $ship): bool
171
    {
172
        return $ship->isDestroyed();
173
    }
174
175
    private function isActiveTractorShipWarped(ShipInterface $ship, ShipInterface $target): bool
176
    {
177
        $tractoringShip = $ship->getTractoringShip();
178
        if ($tractoringShip === null) {
179
            return false;
180
        }
181
182
        if ($tractoringShip !== $target) {
183
            return false;
184
        } else {
185
            return $target->getWarpState();
186
        }
187
    }
188
189
    private function sendPms(
190
        int $userId,
191
        string $sectorString,
192
        MessageCollectionInterface $messageCollection,
193
        bool $isTargetBase
194
    ): void {
195
196
        $header = sprintf(
197
            _("Kampf in Sektor %s"),
198
            $sectorString
199
        );
200
201
        $this->distributedMessageSender->distributeMessageCollection(
202
            $messageCollection,
203
            $userId,
204
            $isTargetBase ? PrivateMessageFolderSpecialEnum::PM_SPECIAL_STATION : PrivateMessageFolderSpecialEnum::PM_SPECIAL_SHIP,
205
            $header
206
        );
207
    }
208
209
    /**
210
     * @return array{0: array<int, ShipWrapperInterface>, 1: array<int, ShipWrapperInterface>, 2: bool, 3: bool}
211
     */
212
    private function getAttackersAndDefenders(ShipWrapperInterface $wrapper, ShipWrapperInterface $targetWrapper): array
213
    {
214
        $ship = $wrapper->get();
215
216
        [$attacker, $defender, $isFleetFight] = $this->fightLib->getAttackersAndDefenders($wrapper, $targetWrapper);
217
218
        $isWebSituation = $this->fightLib->isTargetOutsideFinishedTholianWeb($ship, $targetWrapper->get());
219
220
        //if in tholian web and defenders outside, reflect damage
221
        if ($isWebSituation) {
222
            $holdingWeb = $ship->getHoldingWeb();
223
            if ($holdingWeb === null) {
224
                throw new RuntimeException('this should not happen');
225
            }
226
227
            $defender = $this->shipWrapperFactory->wrapShips($holdingWeb->getCapturedShips()->toArray());
228
        }
229
230
        return [
231
            $attacker,
232
            $defender,
233
            $isFleetFight,
234
            $isWebSituation
235
        ];
236
    }
237
238
    public function performSessionCheck(): bool
239
    {
240
        return true;
241
    }
242
}
243