Passed
Push — master ( f80fed...0867c9 )
by Nico
11:47
created

ShipMover::alertReactionCheck()   A

Complexity

Conditions 5
Paths 8

Size

Total Lines 24
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
cc 5
eloc 13
nc 8
nop 3
dl 0
loc 24
ccs 0
cts 15
cp 0
crap 30
rs 9.5222
c 0
b 0
f 0
1
<?php
2
3
namespace Stu\Module\Spacecraft\Lib\Movement;
4
5
use Doctrine\Common\Collections\ArrayCollection;
6
use Doctrine\Common\Collections\Collection;
7
use Override;
0 ignored issues
show
Bug introduced by
The type Override was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
8
use Stu\Lib\Information\InformationWrapper;
9
use Stu\Module\PlayerSetting\Lib\UserEnum;
0 ignored issues
show
Bug introduced by
The type Stu\Module\PlayerSetting\Lib\UserEnum was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
10
use Stu\Module\Spacecraft\Lib\Battle\AlertDetection\AlertReactionFacadeInterface;
11
use Stu\Module\Ship\Lib\Fleet\LeaveFleetInterface;
12
use Stu\Module\Ship\Lib\ShipWrapperInterface;
13
use Stu\Module\Spacecraft\Lib\Message\MessageCollectionInterface;
14
use Stu\Module\Spacecraft\Lib\Message\MessageFactoryInterface;
15
use Stu\Module\Spacecraft\Lib\Movement\Component\PreFlight\ConditionCheckResult;
16
use Stu\Module\Spacecraft\Lib\Movement\Component\PreFlight\PreFlightConditionsCheckInterface;
17
use Stu\Module\Spacecraft\Lib\Movement\Route\FlightRouteInterface;
18
use Stu\Module\Spacecraft\Lib\SpacecraftWrapperInterface;
19
use Stu\Orm\Entity\ShipInterface;
20
use Stu\Orm\Entity\SpacecraftInterface;
21
use Stu\Orm\Repository\SpacecraftRepositoryInterface;
22
23
//TODO unit tests
24
final class ShipMover implements ShipMoverInterface
25
{
26 2
    public function __construct(
27
        private SpacecraftRepositoryInterface $spacecraftRepository,
28
        private ShipMovementInformationAdderInterface $shipMovementInformationAdder,
29
        private PreFlightConditionsCheckInterface $preFlightConditionsCheck,
30
        private LeaveFleetInterface $leaveFleet,
31
        private AlertReactionFacadeInterface $alertReactionFacade,
32
        private MessageFactoryInterface $messageFactory
33 2
    ) {}
34
35 1
    #[Override]
36
    public function checkAndMove(
37
        SpacecraftWrapperInterface $leadWrapper,
38
        FlightRouteInterface $flightRoute
39
    ): MessageCollectionInterface {
40
41 1
        $messages = $this->messageFactory->createMessageCollection();
42
43 1
        $leadSpacecraft = $leadWrapper->get();
44 1
        $leadSpacecraftName = $leadSpacecraft->getName();
45 1
        $isFleetMode = $leadSpacecraft instanceof ShipInterface ? $leadSpacecraft->isFleetLeader() : false;
46
47 1
        $wrappers = $this->initWrappers($leadWrapper, $isFleetMode);
48 1
        $initialTractoredShips = $this->initTractoredShips($wrappers);
49
50
        // fly until destination arrived
51 1
        $hasTravelled = $this->travelFlightRoute(
52 1
            $leadWrapper,
53 1
            $wrappers,
54 1
            $isFleetMode,
55 1
            $flightRoute,
56 1
            $messages
57 1
        );
58
59
        //skip save and log info if flight did not happen
60 1
        if (!$hasTravelled) {
61 1
            return $messages;
62
        }
63
64
        // save all ships
65
        $this->saveShips($wrappers, $initialTractoredShips);
66
67
        // add post flight informations
68
        $this->postFlightInformations(
69
            $leadWrapper,
70
            $leadSpacecraftName,
71
            $wrappers,
72
            $flightRoute,
73
            $isFleetMode,
74
            $messages
75
        );
76
77
        return $messages;
78
    }
79
80
    /** @return Collection<int, covariant SpacecraftWrapperInterface> */
81 1
    private function initWrappers(SpacecraftWrapperInterface $leadWrapper, bool $isFleetMode): Collection
82
    {
83 1
        $fleetWrapper = $leadWrapper->getFleetWrapper();
84
85 1
        return
86 1
            $isFleetMode && $fleetWrapper !== null
87
            ? $fleetWrapper->getShipWrappers()
88 1
            : new ArrayCollection([$leadWrapper->get()->getId() => $leadWrapper]);
89
    }
90
91
    /** @param Collection<int, covariant SpacecraftWrapperInterface> $wrappers */
0 ignored issues
show
Bug introduced by
The type Stu\Module\Spacecraft\Lib\Movement\covariant was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
92 1
    private function travelFlightRoute(
93
        SpacecraftWrapperInterface $leadWrapper,
94
        Collection $wrappers,
95
        bool $isFleetMode,
96
        FlightRouteInterface $flightRoute,
97
        MessageCollectionInterface $messages
98
    ): bool {
99
100 1
        $hasTravelled = false;
101 1
        $leadSpacecraft = $leadWrapper->get();
102 1
        $fleetWrapper = $leadWrapper->getFleetWrapper();
103
104 1
        $isFixedFleetMode = $isFleetMode
105 1
            && $fleetWrapper !== null
106 1
            && $fleetWrapper->get()->isFleetFixed();
107
108 1
        while (!$flightRoute->isDestinationArrived()) {
109 1
            $nextWaypoint = $flightRoute->getNextWaypoint();
110
111
            // nächstes Feld nicht passierbar
112 1
            if (!$nextWaypoint->getFieldType()->getPassable()) {
113
                $flightRoute->abortFlight();
114
                $messages->addInformation('Das nächste Feld kann nicht passiert werden');
115
                break;
116
            }
117
118 1
            $activeWrappers = $wrappers->filter(fn(SpacecraftWrapperInterface $wrapper): bool => !$wrapper->get()->isDestroyed());
119
120
            // check all flight pre conditions
121 1
            $conditionCheckResult = $this->preFlightConditionsCheck->checkPreconditions(
122 1
                $leadWrapper,
123 1
                $activeWrappers->toArray(),
124 1
                $flightRoute,
125 1
                $isFixedFleetMode
126 1
            );
127
128 1
            if (!$conditionCheckResult->isFlightPossible()) {
129 1
                $flightRoute->abortFlight();
130 1
                $messages->addInformation('Der Weiterflug wurde aus folgenden Gründen abgebrochen:');
131 1
                $this->addInformationMerge($conditionCheckResult->getInformations(), $messages);
132 1
                break;
133
            }
134
135
            foreach ($conditionCheckResult->getBlockedIds() as $spacecraftId) {
136
                $wrappers->remove($spacecraftId);
137
            }
138
139
            $hasToLeaveFleet = $leadWrapper->getFleetWrapper() !== null && !$isFleetMode;
140
            if ($hasToLeaveFleet) {
141
                $this->leaveFleet($leadSpacecraft, $messages);
142
            }
143
144
            $this->addInformationMerge($conditionCheckResult->getInformations(), $messages);
145
146
            /** @var array<array{0: SpacecraftInterface, 1: ShipWrapperInterface}> */
147
            $movedTractoredShipWrappers = [];
148
149
            // move every possible ship by one field
150
            $this->moveShipsByOneField(
151
                $activeWrappers,
152
                $flightRoute,
153
                $movedTractoredShipWrappers,
154
                $messages
155
            );
156
            $hasTravelled = true;
157
158
            // alert reaction check
159
            $this->alertReactionCheck(
160
                $leadWrapper,
161
                $movedTractoredShipWrappers,
162
                $messages
163
            );
164
165
            if ($this->areAllShipsDestroyed($activeWrappers)) {
166
                $flightRoute->abortFlight();
167
                $messages->addInformation('Es wurden alle Schiffe zerstört');
168
            }
169
        }
170
171 1
        return $hasTravelled;
172
    }
173
174
    /**
175
     * @param Collection<int, SpacecraftWrapperInterface> $activeWrappers
176
     * @param array<array{0: SpacecraftInterface, 1: ShipWrapperInterface}> $movedTractoredShipWrappers
177
     */
178
    private function moveShipsByOneField(
179
        Collection $activeWrappers,
180
        FlightRouteInterface $flightRoute,
181
        array &$movedTractoredShipWrappers,
182
        MessageCollectionInterface $messages
183
    ): void {
184
185
        $flightRoute->enterNextWaypoint(
186
            $activeWrappers,
187
            $messages
188
        );
189
190
        foreach ($activeWrappers as $wrapper) {
191
192
            $tractoredShipWrapper = $wrapper->getTractoredShipWrapper();
193
            if ($tractoredShipWrapper !== null) {
194
                $movedTractoredShipWrappers[] = [$wrapper->get(), $tractoredShipWrapper];
195
            }
196
        }
197
    }
198
199
    /** @param array<array{0: SpacecraftInterface, 1: ShipWrapperInterface}> $movedTractoredShipWrappers */
200
    private function alertReactionCheck(
201
        SpacecraftWrapperInterface $leadWrapper,
202
        array $movedTractoredShipWrappers,
203
        MessageCollectionInterface $messages
204
    ): void {
205
        $alertRedInformations = new InformationWrapper();
206
        $this->alertReactionFacade->doItAll($leadWrapper, $alertRedInformations);
207
208
        if (!$alertRedInformations->isEmpty()) {
209
            $this->addInformationMerge($alertRedInformations->getInformations(), $messages);
210
        }
211
212
        // alert red check for tractored ships
213
        foreach ($movedTractoredShipWrappers as [$tractoringSpacecraft, $tractoredShipWrapper]) {
214
            if (!$tractoringSpacecraft->isDestroyed()) {
215
                $alertRedInformations = new InformationWrapper();
216
                $this->alertReactionFacade->doItAll(
217
                    $tractoredShipWrapper,
218
                    $alertRedInformations,
219
                    $tractoringSpacecraft
220
                );
221
222
                if (!$alertRedInformations->isEmpty()) {
223
                    $this->addInformationMerge($alertRedInformations->getInformations(), $messages);
224
                }
225
            }
226
        }
227
    }
228
229
    /**
230
     * @param Collection<int, covariant SpacecraftWrapperInterface> $wrappers
231
     *
232
     * @return array<ShipInterface>
233
     */
234 1
    private function initTractoredShips(Collection $wrappers): array
235
    {
236 1
        $tractoredShips = [];
237
238 1
        foreach ($wrappers as $fleetShipWrapper) {
239 1
            $fleetShip = $fleetShipWrapper->get();
240
241 1
            $tractoredShip = $fleetShip->getTractoredShip();
242
            if (
243 1
                $tractoredShip !== null
244
            ) {
245
                $tractoredShips[] = $tractoredShip;
246
            }
247
        }
248
249 1
        return $tractoredShips;
250
    }
251
252
    private function leaveFleet(SpacecraftInterface $ship, MessageCollectionInterface $messages): void
253
    {
254
        if ($ship instanceof ShipInterface) {
255
            if ($this->leaveFleet->leaveFleet($ship)) {
256
                $messages->addInformation(sprintf('Die %s hat die Flotte verlassen', $ship->getName()));
257
            }
258
        }
259
    }
260
261
    /**
262
     * @param Collection<int, covariant SpacecraftWrapperInterface> $wrappers
263
     * @param array<ShipInterface> $initialTractoredShips
264
     */
265
    private function saveShips(Collection $wrappers, array $initialTractoredShips): void
266
    {
267
        foreach ($wrappers as $wrapper) {
268
            $ship = $wrapper->get();
269
            if (!$ship->isDestroyed()) {
270
                $this->spacecraftRepository->save($ship);
271
            }
272
        }
273
274
        foreach ($initialTractoredShips as $tractoredShip) {
275
            $this->spacecraftRepository->save($tractoredShip);
276
        }
277
    }
278
279
    /**
280
     * @param Collection<int, covariant SpacecraftWrapperInterface> $wrappers
281
     */
282
    private function postFlightInformations(
283
        SpacecraftWrapperInterface $leadWrapper,
284
        string $leadSpacecraftName,
285
        Collection $wrappers,
286
        FlightRouteInterface $flightRoute,
287
        bool $isFleetMode,
288
        MessageCollectionInterface $messages
289
    ): void {
290
291
        //add tractor info
292
        foreach ($wrappers as $wrapper) {
293
            $ship = $wrapper->get();
294
295
            $tractoredShip = $ship->getTractoredShip();
296
            if ($tractoredShip !== null) {
297
                $this->shipMovementInformationAdder->pulledTractoredShip(
298
                    $ship,
299
                    $tractoredShip,
300
                    $flightRoute->getRouteMode(),
301
                    $messages
302
                );
303
            }
304
        }
305
306
        $leadSpacecraft = $leadWrapper->get();
307
308
        //add destination info
309
        if ($this->areAllShipsDestroyed($wrappers)) {
310
            $this->shipMovementInformationAdder->reachedDestinationDestroyed(
311
                $leadSpacecraft,
312
                $leadSpacecraftName,
313
                $isFleetMode,
314
                $flightRoute->getRouteMode(),
315
                $messages
316
            );
317
        } else {
318
            $this->shipMovementInformationAdder->reachedDestination(
319
                $leadSpacecraft,
320
                $isFleetMode,
321
                $flightRoute->getRouteMode(),
322
                $messages
323
            );
324
        }
325
326
        //add info about anomalies
327
        foreach ($leadWrapper->get()->getLocation()->getAnomalies() as $anomaly) {
328
            $messages->addInformation(sprintf(
329
                '[b][color=yellow]In diesem Sektor befindet sich eine %s-Anomalie[/color][/b]',
330
                $anomaly->getAnomalyType()->getName()
331
            ));
332
        }
333
        // add info about buyos
334
        foreach ($leadWrapper->get()->getLocation()->getBuoys() as $buoy) {
335
            $messages->addInformation(sprintf('[b][color=yellow]Boje entdeckt: [/color][/b]%s', $buoy->getText()));
336
        }
337
    }
338
339
    /**
340
     * @param Collection<int, covariant SpacecraftWrapperInterface> $wrappers
341
     */
342
    private function areAllShipsDestroyed(Collection $wrappers): bool
343
    {
344
        return !$wrappers->exists(fn(int $key, SpacecraftWrapperInterface $wrapper): bool => !$wrapper->get()->isDestroyed());
345
    }
346
347
    /**
348
     * @param array<string> $value
349
     */
350 1
    private function addInformationMerge(array $value, MessageCollectionInterface $messages): void
351
    {
352 1
        $messages->add($this->messageFactory->createMessage(UserEnum::USER_NOONE, null, $value));
353
    }
354
}
355