Passed
Pull Request — master (#2043)
by Janko
20:39 queued 09: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 5
CRAP Score 12.4085

Importance

Changes 0
Metric Value
cc 5
eloc 13
c 0
b 0
f 0
nc 8
nop 3
dl 0
loc 24
ccs 5
cts 15
cp 0.3333
crap 12.4085
rs 9.5222
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\PreFlightConditionsCheckInterface;
16
use Stu\Module\Spacecraft\Lib\Movement\Route\FlightRouteInterface;
17
use Stu\Module\Spacecraft\Lib\SpacecraftWrapperInterface;
18
use Stu\Orm\Entity\ShipInterface;
19
use Stu\Orm\Entity\SpacecraftInterface;
20
use Stu\Orm\Repository\SpacecraftRepositoryInterface;
21
22
//TODO unit tests
23
final class ShipMover implements ShipMoverInterface
24
{
25 3
    public function __construct(
26
        private SpacecraftRepositoryInterface $spacecraftRepository,
27
        private ShipMovementInformationAdderInterface $shipMovementInformationAdder,
28
        private PreFlightConditionsCheckInterface $preFlightConditionsCheck,
29
        private LeaveFleetInterface $leaveFleet,
30
        private AlertReactionFacadeInterface $alertReactionFacade,
31
        private MessageFactoryInterface $messageFactory
32 3
    ) {}
33
34 2
    #[Override]
35
    public function checkAndMove(
36
        SpacecraftWrapperInterface $leadWrapper,
37
        FlightRouteInterface $flightRoute
38
    ): MessageCollectionInterface {
39
40 2
        $messages = $this->messageFactory->createMessageCollection();
41
42 2
        $leadSpacecraft = $leadWrapper->get();
43 2
        $leadSpacecraftName = $leadSpacecraft->getName();
44 2
        $isFleetMode = $leadSpacecraft instanceof ShipInterface ? $leadSpacecraft->isFleetLeader() : false;
45
46 2
        $wrappers = $this->initWrappers($leadWrapper, $isFleetMode);
47 2
        $initialTractoredShips = $this->initTractoredShips($wrappers);
48
49
        // fly until destination arrived
50 2
        $hasTravelled = $this->travelFlightRoute(
51 2
            $leadWrapper,
52 2
            $wrappers,
53 2
            $isFleetMode,
54 2
            $flightRoute,
55 2
            $messages
56 2
        );
57
58
        //skip save and log info if flight did not happen
59 2
        if (!$hasTravelled) {
60 1
            return $messages;
61
        }
62
63
        // save all ships
64 1
        $this->saveShips($wrappers, $initialTractoredShips);
65
66
        // add post flight informations
67 1
        $this->postFlightInformations(
68 1
            $leadWrapper,
69 1
            $leadSpacecraftName,
70 1
            $wrappers,
71 1
            $flightRoute,
72 1
            $isFleetMode,
73 1
            $messages
74 1
        );
75
76 1
        return $messages;
77
    }
78
79
    /** @return Collection<int, covariant SpacecraftWrapperInterface> */
80 2
    private function initWrappers(SpacecraftWrapperInterface $leadWrapper, bool $isFleetMode): Collection
81
    {
82 2
        $fleetWrapper = $leadWrapper->getFleetWrapper();
83
84 2
        return
85 2
            $isFleetMode && $fleetWrapper !== null
86 1
            ? $fleetWrapper->getShipWrappers()
87 2
            : new ArrayCollection([$leadWrapper->get()->getId() => $leadWrapper]);
88
    }
89
90
    /** @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...
91 2
    private function travelFlightRoute(
92
        SpacecraftWrapperInterface $leadWrapper,
93
        Collection $wrappers,
94
        bool $isFleetMode,
95
        FlightRouteInterface $flightRoute,
96
        MessageCollectionInterface $messages
97
    ): bool {
98
99 2
        $hasTravelled = false;
100 2
        $leadSpacecraft = $leadWrapper->get();
101 2
        $fleetWrapper = $leadWrapper->getFleetWrapper();
102
103 2
        $isFixedFleetMode = $isFleetMode
104 2
            && $fleetWrapper !== null
105 2
            && $fleetWrapper->get()->isFleetFixed();
106
107 2
        $activeWrappers = new ArrayCollection($wrappers->toArray());
108
109 2
        while (!$flightRoute->isDestinationArrived()) {
110 2
            $nextWaypoint = $flightRoute->getNextWaypoint();
111
112
            // nächstes Feld nicht passierbar
113 2
            if (!$nextWaypoint->getFieldType()->getPassable()) {
114
                $flightRoute->abortFlight();
115
                $messages->addInformation('Das nächste Feld kann nicht passiert werden');
116
                break;
117
            }
118
119 2
            $activeWrappers = $activeWrappers->filter(fn(SpacecraftWrapperInterface $wrapper): bool => !$wrapper->get()->isDestroyed());
120
121
            // check all flight pre conditions
122 2
            $conditionCheckResult = $this->preFlightConditionsCheck->checkPreconditions(
123 2
                $leadWrapper,
124 2
                $activeWrappers->toArray(),
125 2
                $flightRoute,
126 2
                $isFixedFleetMode
127 2
            );
128
129 2
            if (!$conditionCheckResult->isFlightPossible()) {
130 1
                $flightRoute->abortFlight();
131 1
                $messages->addInformation('Der Weiterflug wurde aus folgenden Gründen abgebrochen:');
132 1
                $this->addInformationMerge($conditionCheckResult->getInformations(), $messages);
133 1
                break;
134
            }
135
136 1
            foreach ($conditionCheckResult->getBlockedIds() as $spacecraftId) {
137 1
                $activeWrappers->remove($spacecraftId);
138
            }
139
140 1
            $hasToLeaveFleet = $leadWrapper->getFleetWrapper() !== null && !$isFleetMode;
141 1
            if ($hasToLeaveFleet) {
142
                $this->leaveFleet($leadSpacecraft, $messages);
143
            }
144
145 1
            $this->addInformationMerge($conditionCheckResult->getInformations(), $messages);
146
147
            /** @var array<array{0: SpacecraftInterface, 1: ShipWrapperInterface}> */
148 1
            $movedTractoredShipWrappers = [];
149
150
            // move every possible ship by one field
151 1
            $this->moveShipsByOneField(
152 1
                $activeWrappers,
153 1
                $flightRoute,
154 1
                $movedTractoredShipWrappers,
155 1
                $messages
156 1
            );
157 1
            $hasTravelled = true;
158
159
            // alert reaction check
160 1
            $this->alertReactionCheck(
161 1
                $leadWrapper,
162 1
                $movedTractoredShipWrappers,
163 1
                $messages
164 1
            );
165
166 1
            if ($this->areAllShipsDestroyed($activeWrappers)) {
167
                $flightRoute->abortFlight();
168
                $messages->addInformation('Es wurden alle Schiffe zerstört');
169
            }
170
        }
171
172 2
        return $hasTravelled;
173
    }
174
175
    /**
176
     * @param Collection<int, SpacecraftWrapperInterface> $activeWrappers
177
     * @param array<array{0: SpacecraftInterface, 1: ShipWrapperInterface}> $movedTractoredShipWrappers
178
     */
179 1
    private function moveShipsByOneField(
180
        Collection $activeWrappers,
181
        FlightRouteInterface $flightRoute,
182
        array &$movedTractoredShipWrappers,
183
        MessageCollectionInterface $messages
184
    ): void {
185
186 1
        $flightRoute->enterNextWaypoint(
187 1
            $activeWrappers,
188 1
            $messages
189 1
        );
190
191 1
        foreach ($activeWrappers as $wrapper) {
192
193 1
            $tractoredShipWrapper = $wrapper->getTractoredShipWrapper();
194 1
            if ($tractoredShipWrapper !== null) {
195
                $movedTractoredShipWrappers[] = [$wrapper->get(), $tractoredShipWrapper];
196
            }
197
        }
198
    }
199
200
    /** @param array<array{0: SpacecraftInterface, 1: ShipWrapperInterface}> $movedTractoredShipWrappers */
201 1
    private function alertReactionCheck(
202
        SpacecraftWrapperInterface $leadWrapper,
203
        array $movedTractoredShipWrappers,
204
        MessageCollectionInterface $messages
205
    ): void {
206 1
        $alertRedInformations = new InformationWrapper();
207 1
        $this->alertReactionFacade->doItAll($leadWrapper, $alertRedInformations);
208
209 1
        if (!$alertRedInformations->isEmpty()) {
210
            $this->addInformationMerge($alertRedInformations->getInformations(), $messages);
211
        }
212
213
        // alert red check for tractored ships
214 1
        foreach ($movedTractoredShipWrappers as [$tractoringSpacecraft, $tractoredShipWrapper]) {
215
            if (!$tractoredShipWrapper->get()->isDestroyed()) {
216
                $alertRedInformations = new InformationWrapper();
217
                $this->alertReactionFacade->doItAll(
218
                    $tractoredShipWrapper,
219
                    $alertRedInformations,
220
                    $tractoringSpacecraft
221
                );
222
223
                if (!$alertRedInformations->isEmpty()) {
224
                    $this->addInformationMerge($alertRedInformations->getInformations(), $messages);
225
                }
226
            }
227
        }
228
    }
229
230
    /**
231
     * @param Collection<int, covariant SpacecraftWrapperInterface> $wrappers
232
     *
233
     * @return array<ShipInterface>
234
     */
235 2
    private function initTractoredShips(Collection $wrappers): array
236
    {
237 2
        $tractoredShips = [];
238
239 2
        foreach ($wrappers as $fleetShipWrapper) {
240 2
            $fleetShip = $fleetShipWrapper->get();
241
242 2
            $tractoredShip = $fleetShip->getTractoredShip();
243
            if (
244 2
                $tractoredShip !== null
245
            ) {
246
                $tractoredShips[] = $tractoredShip;
247
            }
248
        }
249
250 2
        return $tractoredShips;
251
    }
252
253
    private function leaveFleet(SpacecraftInterface $ship, MessageCollectionInterface $messages): void
254
    {
255
        if ($ship instanceof ShipInterface) {
256
            if ($this->leaveFleet->leaveFleet($ship)) {
257
                $messages->addInformation(sprintf('Die %s hat die Flotte verlassen', $ship->getName()));
258
            }
259
        }
260
    }
261
262
    /**
263
     * @param Collection<int, covariant SpacecraftWrapperInterface> $wrappers
264
     * @param array<ShipInterface> $initialTractoredShips
265
     */
266 1
    private function saveShips(Collection $wrappers, array $initialTractoredShips): void
267
    {
268 1
        foreach ($wrappers as $wrapper) {
269 1
            $ship = $wrapper->get();
270 1
            if (!$ship->isDestroyed()) {
271 1
                $this->spacecraftRepository->save($ship);
272
            }
273
        }
274
275 1
        foreach ($initialTractoredShips as $tractoredShip) {
276
            $this->spacecraftRepository->save($tractoredShip);
277
        }
278
    }
279
280
    /**
281
     * @param Collection<int, covariant SpacecraftWrapperInterface> $wrappers
282
     */
283 1
    private function postFlightInformations(
284
        SpacecraftWrapperInterface $leadWrapper,
285
        string $leadSpacecraftName,
286
        Collection $wrappers,
287
        FlightRouteInterface $flightRoute,
288
        bool $isFleetMode,
289
        MessageCollectionInterface $messages
290
    ): void {
291
292
        //add tractor info
293 1
        foreach ($wrappers as $wrapper) {
294 1
            $ship = $wrapper->get();
295
296 1
            $tractoredShip = $ship->getTractoredShip();
297 1
            if ($tractoredShip !== null) {
298
                $this->shipMovementInformationAdder->pulledTractoredShip(
299
                    $ship,
300
                    $tractoredShip,
301
                    $flightRoute->getRouteMode(),
302
                    $messages
303
                );
304
            }
305
        }
306
307 1
        $leadSpacecraft = $leadWrapper->get();
308
309
        //add destination info
310 1
        if ($this->areAllShipsDestroyed($wrappers)) {
311
            $this->shipMovementInformationAdder->reachedDestinationDestroyed(
312
                $leadSpacecraft,
313
                $leadSpacecraftName,
314
                $isFleetMode,
315
                $flightRoute->getRouteMode(),
316
                $messages
317
            );
318
        } else {
319 1
            $this->shipMovementInformationAdder->reachedDestination(
320 1
                $leadSpacecraft,
321 1
                $isFleetMode,
322 1
                $flightRoute->getRouteMode(),
323 1
                $messages
324 1
            );
325
        }
326
327 1
        $finalDestination = $leadWrapper->get()->getLocation();
328
329
        //add info about anomalies
330 1
        foreach ($finalDestination->getAnomalies() as $anomaly) {
331
            $messages->addInformation(sprintf(
332
                '[b][color=yellow]In diesem Sektor befindet sich eine %s-Anomalie[/color][/b]',
333
                $anomaly->getAnomalyType()->getName()
334
            ));
335
        }
336
        // add info about buyos
337 1
        foreach ($finalDestination->getBuoys() as $buoy) {
338
            $messages->addInformation(sprintf('[b][color=yellow]Boje entdeckt: [/color][/b]%s', $buoy->getText()));
339
        }
340
    }
341
342
    /**
343
     * @param Collection<int, covariant SpacecraftWrapperInterface> $wrappers
344
     */
345 1
    private function areAllShipsDestroyed(Collection $wrappers): bool
346
    {
347 1
        return !$wrappers->exists(fn(int $key, SpacecraftWrapperInterface $wrapper): bool => !$wrapper->get()->isDestroyed());
348
    }
349
350
    /**
351
     * @param array<string> $value
352
     */
353 2
    private function addInformationMerge(array $value, MessageCollectionInterface $messages): void
354
    {
355 2
        $messages->add($this->messageFactory->createMessage(UserEnum::USER_NOONE, null, $value));
356
    }
357
}
358