Passed
Pull Request — master (#1882)
by Janko
28:33
created

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