Passed
Pull Request — master (#1696)
by Nico
49:43 queued 22:34
created

ShipMover   A

Complexity

Total Complexity 36

Size/Duplication

Total Lines 252
Duplicated Lines 0 %

Test Coverage

Coverage 39.68%

Importance

Changes 5
Bugs 2 Features 2
Metric Value
eloc 121
c 5
b 2
f 2
dl 0
loc 252
ccs 50
cts 126
cp 0.3968
rs 9.52
wmc 36

7 Methods

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