Passed
Pull Request — master (#2053)
by Janko
22:12 queued 10:55
created

ShipMover::addInformationMerge()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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