Passed
Pull Request — master (#2043)
by Janko
20:39 queued 09:47
created

ShipMover::travelFlightRoute()   B

Complexity

Conditions 10
Paths 9

Size

Total Lines 82
Code Lines 45

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 43
CRAP Score 10.1832

Importance

Changes 0
Metric Value
cc 10
eloc 45
c 0
b 0
f 0
nc 9
nop 5
dl 0
loc 82
ccs 43
cts 49
cp 0.8776
crap 10.1832
rs 7.3333

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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