Passed
Pull Request — master (#1821)
by Nico
52:02 queued 25:23
created

ShipMover::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
nc 1
nop 5
dl 0
loc 8
ccs 2
cts 2
cp 1
crap 1
rs 10
c 1
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 Stu\Lib\Information\InformationWrapper;
8
use Stu\Module\PlayerSetting\Lib\UserEnum;
9
use Stu\Module\Ship\Lib\Battle\AlertDetection\AlertReactionFacadeInterface;
10
use Stu\Module\Ship\Lib\Fleet\LeaveFleetInterface;
11
use Stu\Module\Ship\Lib\Message\Message;
12
use Stu\Module\Ship\Lib\Message\MessageCollection;
13
use Stu\Module\Ship\Lib\Message\MessageCollectionInterface;
14
use Stu\Module\Ship\Lib\Movement\Component\PreFlight\PreFlightConditionsCheckInterface;
15
use Stu\Module\Ship\Lib\Movement\Route\FlightRouteInterface;
16
use Stu\Module\Ship\Lib\ShipWrapperInterface;
17
use Stu\Orm\Entity\ShipInterface;
18
use Stu\Orm\Repository\ShipRepositoryInterface;
19
20
//TODO unit tests
21
final class ShipMover implements ShipMoverInterface
22
{
23
    private MessageCollectionInterface $messages;
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
    ) {
32 1
        $this->messages = new MessageCollection();
33
    }
34
35 1
    private function addInformation(string $value): void
36
    {
37 1
        $this->messages->add(new Message(UserEnum::USER_NOONE, null, [$value]));
38
    }
39
40
    /**
41
     * @param array<string> $value
42
     */
43 1
    private function addInformationMerge(array $value): void
44
    {
45 1
        $this->messages->add(new Message(UserEnum::USER_NOONE, null, $value));
46
    }
47
48 1
    public function checkAndMove(
49
        ShipWrapperInterface $leadShipWrapper,
50
        FlightRouteInterface $flightRoute
51
    ): MessageCollectionInterface {
52
53 1
        $leadShip = $leadShipWrapper->get();
54 1
        $leadShipName = $leadShip->getName();
55 1
        $fleetWrapper = $leadShipWrapper->getFleetWrapper();
56
57 1
        $isFleetMode = $leadShip->isFleetLeader();
58 1
        $hasToLeaveFleet = $fleetWrapper !== null && !$isFleetMode;
59
60 1
        $wrappers = $isFleetMode && $fleetWrapper !== null
61
            ? $fleetWrapper->getShipWrappers()
62 1
            : new ArrayCollection([$leadShipWrapper->get()->getId() => $leadShipWrapper]);
63
64 1
        $isFixedFleetMode = $isFleetMode
65 1
            && $fleetWrapper !== null
66 1
            && $fleetWrapper->get()->isFleetFixed();
67
68 1
        $initialTractoredShips = $this->initTractoredShips($wrappers);
69
70 1
        $hasTravelled = false;
71
72
        // fly until destination arrived
73 1
        while (!$flightRoute->isDestinationArrived()) {
74 1
            $nextWaypoint = $flightRoute->getNextWaypoint();
75
76
            // nächstes Feld nicht passierbar
77 1
            if (!$nextWaypoint->getFieldType()->getPassable()) {
78
                $flightRoute->abortFlight();
79
                $this->addInformation(_('Das nächste Feld kann nicht passiert werden'));
80
                break;
81
            }
82
83 1
            $activeWrappers = $wrappers->filter(fn (ShipWrapperInterface $wrapper) => !$wrapper->get()->isDestroyed());
84
85
            // check all flight pre conditions
86 1
            $conditionCheckResult = $this->preFlightConditionsCheck->checkPreconditions(
87 1
                $leadShipWrapper,
88 1
                $activeWrappers->toArray(),
89 1
                $flightRoute,
90 1
                $isFixedFleetMode
91 1
            );
92
93 1
            if (!$conditionCheckResult->isFlightPossible()) {
94 1
                $flightRoute->abortFlight();
95 1
                $this->addInformation(_('Der Weiterflug wurde aus folgenden Gründen abgebrochen:'));
96 1
                $this->addInformationMerge($conditionCheckResult->getInformations());
97 1
                break;
98
            }
99
100
            $this->addInformationMerge($conditionCheckResult->getInformations());
101
102
            $movedTractoredShipWrappers = [];
103
104
            // move every ship by one field
105
            foreach ($activeWrappers as $wrapper) {
106
107
                $ship = $wrapper->get();
108
109
                if ($conditionCheckResult->isNotBlocked($ship)) {
110
111
                    $this->leaveFleetIfNotFleetLeader($ship, $hasToLeaveFleet);
112
113
                    $flightRoute->enterNextWaypoint(
114
                        $wrapper,
115
                        $this->messages
116
                    );
117
118
                    $tractoredShipWrapper = $wrapper->getTractoredShipWrapper();
119
                    if ($tractoredShipWrapper !== null) {
120
                        $flightRoute->enterNextWaypoint(
121
                            $tractoredShipWrapper,
122
                            $this->messages
123
                        );
124
125
                        $movedTractoredShipWrappers[] = [$wrapper->get(), $tractoredShipWrapper];
126
                    }
127
128
                    $hasTravelled = true;
129
                }
130
            }
131
132
            $flightRoute->stepForward();
133
134
            // alert red check
135
            $alertRedInformations = new InformationWrapper();
136
            $this->alertReactionFacade->doItAll($leadShipWrapper, $alertRedInformations);
137
138
            if (!$alertRedInformations->isEmpty()) {
139
                $this->addInformationMerge($alertRedInformations->getInformations());
140
            }
141
142
            // alert red check for tractored ships
143
            foreach ($movedTractoredShipWrappers as [$tractoringShip, $tractoredShipWrapper]) {
144
                if (!$tractoringShip->isDestroyed()) {
145
                    $alertRedInformations = new InformationWrapper();
146
                    $this->alertReactionFacade->doItAll(
147
                        $tractoredShipWrapper,
148
                        $alertRedInformations,
149
                        $tractoringShip
150
                    );
151
152
                    if (!$alertRedInformations->isEmpty()) {
153
                        $this->addInformationMerge($alertRedInformations->getInformations());
154
                    }
155
                }
156
            }
157
158
            if ($this->areAllShipsDestroyed($activeWrappers)) {
159
                $flightRoute->abortFlight();
160
                $this->addInformation(_('Es wurden alle Schiffe zerstört'));
161
            }
162
        }
163
164
        //skip save and log info if flight did not happen
165 1
        if (!$hasTravelled) {
166 1
            return $this->messages;
167
        }
168
169
        // save all ships
170
        foreach ($wrappers as $wrapper) {
171
            $ship = $wrapper->get();
172
            if (!$ship->isDestroyed()) {
173
                $this->shipRepository->save($ship);
174
            }
175
176
            $tractoredShip = $ship->getTractoredShip();
177
            if ($tractoredShip !== null) {
178
                $this->shipMovementInformationAdder->pulledTractoredShip(
179
                    $ship,
180
                    $tractoredShip,
181
                    $flightRoute->getRouteMode(),
182
                    $this->messages
183
                );
184
            }
185
        }
186
187
188
        foreach ($initialTractoredShips as $tractoredShip) {
189
            $this->shipRepository->save($tractoredShip);
190
        }
191
192
        if ($this->areAllShipsDestroyed($wrappers)) {
193
            $this->shipMovementInformationAdder->reachedDestinationDestroyed(
194
                $leadShip,
195
                $leadShipName,
196
                $isFleetMode,
197
                $flightRoute->getRouteMode(),
198
                $this->messages
199
            );
200
        } else {
201
            $this->shipMovementInformationAdder->reachedDestination(
202
                $leadShip,
203
                $isFleetMode,
204
                $flightRoute->getRouteMode(),
205
                $this->messages
206
            );
207
        }
208
209
        //add info about anomalies
210
        foreach ($leadShipWrapper->get()->getLocation()->getAnomalies() as $anomaly) {
211
            $this->addInformation(sprintf('[b][color=yellow]In diesem Sektor befindet sich eine %s[/color][/b]', $anomaly->getAnomalyType()->getName()));
212
        }
213
        // add info about buyos
214
        foreach ($leadShipWrapper->get()->getCurrentMapField()->getBuoys() as $buoy) {
215
            $this->addInformation(sprintf('[b][color=yellow]Boje entdeckt: [/color][/b]%s', $buoy->getText()));
216
        }
217
218
        return $this->messages;
219
    }
220
221
    /**
222
     * @param Collection<int, ShipWrapperInterface> $wrappers
223
     * 
224
     * @return array<ShipInterface>
225
     */
226 1
    private function initTractoredShips(Collection $wrappers): array
227
    {
228 1
        $tractoredShips = [];
229
230 1
        foreach ($wrappers as $fleetShipWrapper) {
231 1
            $fleetShip = $fleetShipWrapper->get();
232
233 1
            $tractoredShip = $fleetShip->getTractoredShip();
234
            if (
235 1
                $tractoredShip !== null
236
            ) {
237
                $tractoredShips[] = $tractoredShip;
238
            }
239
        }
240
241 1
        return $tractoredShips;
242
    }
243
244
    private function leaveFleetIfNotFleetLeader(ShipInterface $ship, bool $hasToLeaveFleet): void
245
    {
246
        if ($hasToLeaveFleet && $ship->getFleet() !== null) {
247
            $this->leaveFleet->leaveFleet($ship);
248
            $this->addInformation(sprintf('Die %s hat die Flotte verlassen', $ship->getName()));
249
        }
250
    }
251
252
    /**
253
     * @param Collection<int, ShipWrapperInterface> $wrappers
254
     */
255
    private function areAllShipsDestroyed(Collection $wrappers): bool
256
    {
257
        return !$wrappers->exists(fn (int $key, ShipWrapperInterface $wrapper) => !$wrapper->get()->isDestroyed());
258
    }
259
}
260