Passed
Pull Request — master (#1801)
by Nico
23:50
created

AlertRedHelper::informAboutTrojanHorse()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 1
dl 0
loc 7
ccs 0
cts 6
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\Battle;
6
7
use Stu\Component\Player\PlayerRelationDeterminatorInterface;
8
use Stu\Lib\Information\InformationWrapper;
9
use Stu\Module\Control\GameControllerInterface;
10
use Stu\Module\Logging\LoggerUtilFactoryInterface;
11
use Stu\Module\Logging\LoggerUtilInterface;
12
use Stu\Module\Message\Lib\PrivateMessageFolderSpecialEnum;
13
use Stu\Module\Message\Lib\PrivateMessageSenderInterface;
14
use Stu\Module\PlayerSetting\Lib\UserEnum;
15
use Stu\Module\Ship\Lib\ShipWrapperFactoryInterface;
16
use Stu\Module\Ship\Lib\ShipWrapperInterface;
17
use Stu\Orm\Entity\ShipInterface;
18
use Stu\Orm\Repository\ShipRepositoryInterface;
19
20
//TODO create unit tests
21
final class AlertRedHelper implements AlertRedHelperInterface
22
{
23
    private ShipRepositoryInterface $shipRepository;
24
25
    private ShipAttackCycleInterface $shipAttackCycle;
26
27
    private ShipWrapperFactoryInterface $shipWrapperFactory;
28
29
    private PrivateMessageSenderInterface $privateMessageSender;
30
31
    private LoggerUtilInterface $loggerUtil;
32
33
    private PlayerRelationDeterminatorInterface $playerRelationDeterminator;
34
35
    public function __construct(
36
        PrivateMessageSenderInterface $privateMessageSender,
37
        ShipRepositoryInterface $shipRepository,
38
        ShipAttackCycleInterface $shipAttackCycle,
39
        ShipWrapperFactoryInterface $shipWrapperFactory,
40
        PlayerRelationDeterminatorInterface $playerRelationDeterminator,
41
        LoggerUtilFactoryInterface $loggerUtilFactory
42
    ) {
43
        $this->privateMessageSender = $privateMessageSender;
44
        $this->shipRepository = $shipRepository;
45
        $this->shipAttackCycle = $shipAttackCycle;
46
        $this->shipWrapperFactory = $shipWrapperFactory;
47
        $this->loggerUtil = $loggerUtilFactory->getLoggerUtil();
48
        $this->playerRelationDeterminator = $playerRelationDeterminator;
49
    }
50
51
    public function doItAll(
52
        ShipInterface $ship,
53
        ?GameControllerInterface $game = null,
54
        ?ShipInterface $tractoringShip = null
55
    ): ?InformationWrapper {
56
        //$this->loggerUtil->init('ARH', LoggerEnum::LEVEL_ERROR);
57
58
        $informations = new InformationWrapper();
59
60
        $shipsToShuffle = $this->checkForAlertRedShips($ship, $informations, $tractoringShip);
61
        shuffle($shipsToShuffle);
62
63
        $ships = $this->getShips($ship);
64
65
        foreach ($shipsToShuffle as $alertShip) {
66
            $leader = $this->getLeader($ships);
67
            if ($leader !== null) {
68
                $this->loggerUtil->log(sprintf('leaderId: %d', $leader->getId()));
69
            } else {
70
                $this->loggerUtil->log('leader is null');
71
            }
72
73
            if ($leader === null) {
74
                break;
75
            }
76
77
            $this->performAttackCycle($alertShip, $leader, $informations);
78
        }
79
80
        if ($game !== null) {
81
            $game->addInformationMergeDown($informations->getInformations());
82
            return null;
83
        } else {
84
            return $informations;
85
        }
86
    }
87
88
    /**
89
     * @param ShipWrapperInterface[] $wrappers
90
     */
91
    private function getLeader(array $wrappers): ?ShipInterface
92
    {
93
        $nonDestroyedShips = [];
94
95
        foreach ($wrappers as $wrapper) {
96
            $ship = $wrapper->get();
97
98
            if (!$ship->isDestroyed()) {
99
                if ($ship->isFleetLeader()) {
100
                    return $ship;
101
                }
102
                $nonDestroyedShips[] = $ship;
103
            }
104
        }
105
106
        if ($nonDestroyedShips !== []) {
107
            return current($nonDestroyedShips);
108
        }
109
110
        return null;
111
    }
112
113
    /**
114
     * @return ShipWrapperInterface[]
115
     */
116
    private function getShips(ShipInterface $leadShip): array
117
    {
118
        if ($leadShip->getFleet() !== null) {
119
            return $this->shipWrapperFactory->wrapShips($leadShip->getFleet()->getShips()->toArray());
120
        } else {
121
            return $this->shipWrapperFactory->wrapShips([$leadShip]);
122
        }
123
    }
124
125
    public function checkForAlertRedShips(
126
        ShipInterface $leadShip,
127
        InformationWrapper $informations,
128
        ?ShipInterface $tractoringShip = null
129
    ): array {
130
        $leadShipUser = $leadShip->getUser();
131
132
        if ($leadShipUser->getId() === UserEnum::USER_NOONE) {
133
            return [];
134
        }
135
        if ($this->allFleetShipsWarped($leadShip)) {
136
            return [];
137
        }
138
        if ($this->allFleetShipsCloaked($leadShip)) {
139
            return [];
140
        }
141
142
        $shipsToShuffle = [];
143
144
        // get ships inside or outside systems
145
        $shipsOnLocation = $this->shipRepository->getShipsForAlertRed($leadShip);
146
147
        $fleetIds = [];
148
        $fleetCount = 0;
149
        $singleShipCount = 0;
150
        $usersToInformAboutTrojanHorse = [];
151
152
        foreach ($shipsOnLocation as $shipOnLocation) {
153
            $user = $shipOnLocation->getUser();
154
155
            //ships of friends from tractoring ship dont attack
156
            if ($tractoringShip !== null && $this->playerRelationDeterminator->isFriend($user, $tractoringShip->getUser())) {
157
                $userId = $user->getId();
158
159
                if (
160
                    !array_key_exists($userId, $usersToInformAboutTrojanHorse)
161
                    && $user !== $leadShipUser
162
                    && !$this->playerRelationDeterminator->isFriend($user, $leadShipUser)
163
                ) {
164
                    $txt = sprintf(
165
                        _('Die %s von Spieler %s ist in Sektor %s eingeflogen und hat dabei die %s von Spieler %s gezogen'),
166
                        $tractoringShip->getName(),
167
                        $tractoringShip->getUser()->getName(),
168
                        $tractoringShip->getSectorString(),
169
                        $leadShip->getName(),
170
                        $leadShipUser->getName()
171
                    );
172
                    $usersToInformAboutTrojanHorse[$userId] = $txt;
173
                }
174
                continue;
175
            }
176
177
            //ships of friends dont attack
178
            if ($this->playerRelationDeterminator->isFriend($user, $leadShipUser)) {
179
                continue;
180
            }
181
182
            //ships in finished tholian web dont attack
183
            if ($shipOnLocation->getHoldingWeb() !== null && $shipOnLocation->getHoldingWeb()->isFinished()) {
184
                continue;
185
            }
186
187
            //pirates don't attack if user is protected
188
            $pirateWrath = $leadShipUser->getPirateWrath();
189
            if (
190
                $shipOnLocation->getUserId() === UserEnum::USER_NPC_KAZON
191
                && $pirateWrath !== null
192
                && $pirateWrath->getProtectionTimeout() > time()
193
            ) {
194
                continue;
195
            }
196
197
            //players don't attack pirates if protection is active
198
            $pirateWrath = $user->getPirateWrath();
199
            if (
200
                $leadShipUser->getId() === UserEnum::USER_NPC_KAZON
201
                && $pirateWrath !== null
202
                && $pirateWrath->getProtectionTimeout() > time()
203
            ) {
204
                continue;
205
            }
206
207
            $fleet = $shipOnLocation->getFleet();
208
209
            if ($fleet === null) {
210
                $singleShipCount++;
211
                $shipsToShuffle[$shipOnLocation->getId()] = $shipOnLocation;
212
            } else {
213
                $fleetIdEintrag = $fleetIds[$fleet->getId()] ?? null;
214
                if ($fleetIdEintrag === null) {
215
                    $fleetCount++;
216
                    $shipsToShuffle[$fleet->getLeadShip()->getId()] = $fleet->getLeadShip();
217
                    $fleetIds[$fleet->getId()] = [];
218
                }
219
            }
220
        }
221
222
        $this->informAboutTrojanHorse($usersToInformAboutTrojanHorse);
223
224
        if ($fleetCount == 1) {
225
            $informations->addInformation(sprintf(
226
                _('In Sektor %d|%d befindet sich 1 Flotte auf [b][color=red]Alarm-Rot![/color][/b]') . "\n",
227
                $leadShip->getPosX(),
228
                $leadShip->getPosY()
229
            ));
230
        }
231
        if ($fleetCount > 1) {
232
            $informations->addInformation(sprintf(
233
                _('In Sektor %d|%d befinden sich %d Flotte auf [b][color=red]Alarm-Rot![/color][/b]') . "\n",
234
                $leadShip->getPosX(),
235
                $leadShip->getPosY(),
236
                $fleetCount
237
            ));
238
        }
239
        if ($singleShipCount == 1) {
240
            $informations->addInformation(sprintf(
241
                _('In Sektor %d|%d befindet sich 1 Einzelschiff auf [b][color=red]Alarm-Rot![/color][/b]') . "\n",
242
                $leadShip->getPosX(),
243
                $leadShip->getPosY()
244
            ));
245
        }
246
        if ($singleShipCount > 1) {
247
            $informations->addInformation(sprintf(
248
                _('In Sektor %d|%d befinden sich %d Einzelschiffe auf [b][color=red]Alarm-Rot![/color][/b]') . "\n",
249
                $leadShip->getPosX(),
250
                $leadShip->getPosY(),
251
                $singleShipCount
252
            ));
253
        }
254
255
        return $shipsToShuffle;
256
    }
257
258
    /**
259
     * @param array<int, string> $users
260
     */
261
    private function informAboutTrojanHorse(array $users): void
262
    {
263
        foreach ($users as $userId => $txt) {
264
            $this->privateMessageSender->send(
265
                UserEnum::USER_NOONE,
266
                $userId,
267
                $txt
268
            );
269
        }
270
    }
271
272
    private function allFleetShipsWarped(ShipInterface $leadShip): bool
273
    {
274
        if ($leadShip->getFleet() !== null) {
275
            foreach ($leadShip->getFleet()->getShips() as $fleetShip) {
276
                if (!$fleetShip->getWarpState()) {
277
                    return false;
278
                }
279
            }
280
        } elseif (!$leadShip->getWarpState()) {
281
            return false;
282
        }
283
284
        return true;
285
    }
286
287
    private function allFleetShipsCloaked(ShipInterface $leadShip): bool
288
    {
289
        if ($leadShip->getFleet() !== null) {
290
            foreach ($leadShip->getFleet()->getShips() as $fleetShip) {
291
                if (!$fleetShip->getCloakState()) {
292
                    return false;
293
                }
294
            }
295
        } elseif (!$leadShip->getCloakState()) {
296
            return false;
297
        }
298
299
        return true;
300
    }
301
302
    public function performAttackCycle(
303
        ShipInterface $alertShip,
304
        ShipInterface $leadShip,
305
        InformationWrapper $informations,
306
        bool $isColonyDefense = false
307
    ): void {
308
        $alert_user_id = $alertShip->getUser()->getId();
309
        $lead_user_id = $leadShip->getUser()->getId();
310
        $isAlertShipBase = $alertShip->isBase();
311
312
        if ($alertShip->getFleet() !== null) {
313
            $attacker = [];
314
315
            // only uncloaked and unwarped ships enter fight
316
            foreach ($alertShip->getFleet()->getShips()->toArray() as $fleetShip) {
317
                if (!$fleetShip->getCloakState() && !$fleetShip->getWarpState()) {
318
                    $attacker[$fleetShip->getId()] = $fleetShip;
319
                }
320
            }
321
        } else {
322
            $attacker = [$alertShip->getId() => $alertShip];
323
        }
324
        if ($leadShip->isFleetLeader()) {
325
            $this->loggerUtil->log('leadShip is FleetLeader');
326
            $defender = [];
327
328
            // only uncloaked ships enter fight
329
            foreach ($leadShip->getFleet()->getShips()->toArray() as $defShip) {
330
                if (!$defShip->getCloakState()) {
331
                    $defender[$defShip->getId()] = $defShip;
332
                }
333
            }
334
            // if whole flying fleet cloaked, nothing happens
335
            if ($defender === []) {
336
                return;
337
            }
338
        } else {
339
            // if flying ship is cloaked, nothing happens
340
            if ($leadShip->getCloakState()) {
341
                return;
342
            }
343
344
            $defender = [$leadShip->getId() => $leadShip];
345
        }
346
347
        //$this->loggerUtil->log(sprintf('before_shipAttackCycle, attackerCount: %d, defenderCount: %d', count($attacker), count($defender)));
348
349
        $messageCollection = $this->shipAttackCycle->cycle(
350
            $this->shipWrapperFactory->wrapShips($attacker),
351
            $this->shipWrapperFactory->wrapShips($defender),
352
            false,
353
            true
354
        );
355
356
        $fightInformations = $messageCollection->getInformationDump();
357
358
        if (empty($fightInformations->getInformations())) {
359
            //$this->loggerUtil->init('ARH', LoggerEnum::LEVEL_ERROR);
360
            //$this->loggerUtil->log(sprintf('attackerCount: %d, defenderCount: %d', count($attacker), count($defender)));
361
        }
362
363
        $pm = sprintf(
364
            _("Eigene Schiffe auf [b][color=red]%s[/color][/b], Kampf in Sektor %s\n%s"),
365
            $isColonyDefense ? 'Kolonie-Verteidigung' : 'Alarm-Rot',
366
            $leadShip->getSectorString(),
367
            $fightInformations->getInformationsAsString()
368
        );
369
        $href = sprintf(_('ship.php?SHOW_SHIP=1&id=%d'), $alertShip->getId());
370
        $this->privateMessageSender->send(
371
            $lead_user_id,
372
            $alert_user_id,
373
            $pm,
374
            $isAlertShipBase ? PrivateMessageFolderSpecialEnum::PM_SPECIAL_STATION : PrivateMessageFolderSpecialEnum::PM_SPECIAL_SHIP,
375
            $alertShip->isDestroyed() ? null : $href
376
        );
377
        $pm = sprintf(
378
            _("Fremde Schiffe auf [b][color=red]%s[/color][/b], Kampf in Sektor %s\n%s"),
379
            $isColonyDefense ? 'Kolonie-Verteidigung' : 'Alarm-Rot',
380
            $leadShip->getSectorString(),
381
            $fightInformations->getInformationsAsString()
382
        );
383
        $this->privateMessageSender->send(
384
            $alert_user_id,
385
            $lead_user_id,
386
            $pm,
387
            PrivateMessageFolderSpecialEnum::PM_SPECIAL_SHIP
388
        );
389
390
        if ($leadShip->isDestroyed()) {
391
            $informations->addInformationWrapper($fightInformations);
392
            return;
393
        }
394
395
        $informations->addInformation(sprintf(
396
            _('[b][color=red]%s[/color][/b] fremder Schiffe auf Feld %d|%d, Angriff durchgeführt') . "\n",
397
            $isColonyDefense ? 'Kolonie-Verteidigung' : 'Alarm-Rot',
398
            $leadShip->getPosX(),
399
            $leadShip->getPosY()
400
        ));
401
        $informations->addInformationWrapper($fightInformations);
402
    }
403
}
404