Passed
Push — master ( 288b46...98b0e3 )
by Nico
31:30 queued 08:00
created

ShipRemover   A

Complexity

Total Complexity 35

Size/Duplication

Total Lines 319
Duplicated Lines 0 %

Test Coverage

Coverage 12.33%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 155
c 1
b 0
f 0
dl 0
loc 319
ccs 18
cts 146
cp 0.1233
rs 9.6
wmc 35

7 Methods

Rating   Name   Duplication   Size   Complexity  
A orphanizeStorage() 0 5 2
A destroyTradepost() 0 31 4
B leaveSomeIntactModules() 0 31 7
B destroy() 0 103 9
A resetTrackerDevices() 0 6 2
B remove() 0 55 10
A __construct() 0 36 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Stu\Module\Ship\Lib;
6
7
use RuntimeException;
8
use Stu\Component\Ship\ShipRumpEnum;
9
use Stu\Component\Ship\ShipStateEnum;
10
use Stu\Component\Ship\SpacecraftTypeEnum;
11
use Stu\Component\Ship\Storage\ShipStorageManagerInterface;
12
use Stu\Component\Ship\System\ShipSystemManagerInterface;
13
use Stu\Component\Ship\System\ShipSystemTypeEnum;
14
use Stu\Module\Message\Lib\PrivateMessageFolderSpecialEnum;
15
use Stu\Module\Message\Lib\PrivateMessageSenderInterface;
16
use Stu\Module\PlayerSetting\Lib\UserEnum;
17
use Stu\Module\Ship\Lib\Crew\ShipLeaverInterface;
18
use Stu\Module\Ship\Lib\Fleet\LeaveFleetInterface;
19
use Stu\Module\Ship\Lib\Torpedo\ClearTorpedoInterface;
20
use Stu\Module\Ship\View\ShowShip\ShowShip;
21
use Stu\Orm\Entity\ShipInterface;
22
use Stu\Orm\Entity\TradePostInterface;
23
use Stu\Orm\Repository\CrewRepositoryInterface;
24
use Stu\Orm\Repository\ShipCrewRepositoryInterface;
25
use Stu\Orm\Repository\ShipRepositoryInterface;
26
use Stu\Orm\Repository\ShipRumpRepositoryInterface;
27
use Stu\Orm\Repository\ShipSystemRepositoryInterface;
28
use Stu\Orm\Repository\StorageRepositoryInterface;
29
use Stu\Orm\Repository\TradePostRepositoryInterface;
30
use Stu\Orm\Repository\UserRepositoryInterface;
31
32
//TODO unit tests
33
final class ShipRemover implements ShipRemoverInterface
34
{
35
    private ShipSystemRepositoryInterface $shipSystemRepository;
36
37
    private StorageRepositoryInterface $storageRepository;
38
39
    private ShipStorageManagerInterface $shipStorageManager;
40
41
    private CrewRepositoryInterface $crewRepository;
42
43
    private ShipCrewRepositoryInterface $shipCrewRepository;
44
45
    private ShipRepositoryInterface $shipRepository;
46
47
    private UserRepositoryInterface $userRepository;
48
49
    private ShipRumpRepositoryInterface $shipRumpRepository;
50
51
    private ShipSystemManagerInterface $shipSystemManager;
52
53
    private ShipLeaverInterface $shipLeaver;
54
55
    private AstroEntryLibInterface $astroEntryLib;
56
57
    private ClearTorpedoInterface $clearTorpedo;
58
59
    private TradePostRepositoryInterface $tradePostRepository;
60
61
    private ShipStateChangerInterface $shipStateChanger;
62
63
    private ShipWrapperFactoryInterface $shipWrapperFactory;
64
65
    private LeaveFleetInterface $leaveFleet;
66
67
    private PrivateMessageSenderInterface $privateMessageSender;
68
69 1
    public function __construct(
70
        ShipSystemRepositoryInterface $shipSystemRepository,
71
        StorageRepositoryInterface $storageRepository,
72
        ShipStorageManagerInterface $shipStorageManager,
73
        CrewRepositoryInterface $crewRepository,
74
        ShipCrewRepositoryInterface $shipCrewRepository,
75
        ShipRepositoryInterface $shipRepository,
76
        UserRepositoryInterface $userRepository,
77
        ShipRumpRepositoryInterface $shipRumpRepository,
78
        ShipSystemManagerInterface $shipSystemManager,
79
        ShipLeaverInterface $shipLeaver,
80
        AstroEntryLibInterface $astroEntryLib,
81
        ClearTorpedoInterface $clearTorpedo,
82
        TradePostRepositoryInterface $tradePostRepository,
83
        ShipStateChangerInterface $shipStateChanger,
84
        ShipWrapperFactoryInterface $shipWrapperFactory,
85
        LeaveFleetInterface $leaveFleet,
86
        PrivateMessageSenderInterface $privateMessageSender
87
    ) {
88 1
        $this->shipSystemRepository = $shipSystemRepository;
89 1
        $this->storageRepository = $storageRepository;
90 1
        $this->shipStorageManager = $shipStorageManager;
91 1
        $this->crewRepository = $crewRepository;
92 1
        $this->shipCrewRepository = $shipCrewRepository;
93 1
        $this->shipRepository = $shipRepository;
94 1
        $this->userRepository = $userRepository;
95 1
        $this->shipRumpRepository = $shipRumpRepository;
96 1
        $this->shipSystemManager = $shipSystemManager;
97 1
        $this->shipLeaver = $shipLeaver;
98 1
        $this->astroEntryLib = $astroEntryLib;
99 1
        $this->clearTorpedo = $clearTorpedo;
100 1
        $this->tradePostRepository = $tradePostRepository;
101 1
        $this->shipStateChanger = $shipStateChanger;
102 1
        $this->shipWrapperFactory = $shipWrapperFactory;
103 1
        $this->leaveFleet = $leaveFleet;
104 1
        $this->privateMessageSender = $privateMessageSender;
105
    }
106
107
    public function destroy(ShipWrapperInterface $wrapper): ?string
108
    {
109
        $trumfieldRump = $this->shipRumpRepository->find(ShipRumpEnum::SHIP_CATEGORY_TRUMFIELD);
110
        if ($trumfieldRump === null) {
111
            throw new RuntimeException('trumfield rump missing');
112
        }
113
114
        $msg = null;
115
116
        $ship = $wrapper->get();
117
        $this->shipSystemManager->deactivateAll($wrapper);
118
        $user = $ship->getUser();
119
120
        $this->leaveFleet->leaveFleet($ship);
121
122
        if ($ship->getState() === ShipStateEnum::SHIP_STATE_ASTRO_FINALIZING) {
123
            $this->astroEntryLib->cancelAstroFinalizing($ship);
124
        }
125
126
        //leave ship if there is crew
127
        if ($ship->getCrewCount() > 0) {
128
            $msg = $this->shipLeaver->evacuate($wrapper);
129
        }
130
131
        /**
132
         * this is buggy :(
133
         * throws ORMInvalidArgumentException
134
         *
135
         if ($ship->getRump()->isEscapePods())
136
         {
137
             $this->remove($ship);
138
             return $msg;
139
            }
140
         */
141
142
        $this->leaveSomeIntactModules($ship);
143
144
        $ship->setFormerRumpId($ship->getRump()->getId());
145
        $ship->setRump($trumfieldRump);
146
        $ship->setHuell((int) ceil($ship->getMaxHull() / 20));
147
        $ship->setUser($this->userRepository->getFallbackUser());
148
        $ship->setBuildplan(null);
149
        $ship->setSpacecraftType(SpacecraftTypeEnum::SPACECRAFT_TYPE_OTHER);
150
        $ship->setShield(0);
151
        $ship->setAlertStateGreen();
152
        $ship->setInfluenceArea(null);
153
        $ship->setDockedTo(null);
154
        $oldName = $ship->getName();
155
        $ship->setName(_('Trümmer'));
156
        $ship->setIsDestroyed(true);
157
        $this->shipStateChanger->changeShipState($wrapper, ShipStateEnum::SHIP_STATE_DESTROYED);
158
159
        // delete ship systems
160
        $this->shipSystemRepository->truncateByShip($ship->getId());
161
        $ship->getSystems()->clear();
162
163
        if ($user->getState() === UserEnum::USER_STATE_COLONIZATION_SHIP) {
164
            $user->setState(UserEnum::USER_STATE_UNCOLONIZED);
165
            $this->userRepository->save($user);
166
        }
167
168
        // delete torpedo storage
169
        $this->clearTorpedo->clearTorpedoStorage($wrapper);
170
171
        // delete trade post stuff
172
        if ($ship->getTradePost() !== null) {
173
            $this->destroyTradepost($ship->getTradePost());
174
            $ship->setTradePost(null);
175
        }
176
177
        // change storage owner
178
        $this->orphanizeStorage($ship);
179
180
        $this->shipRepository->save($ship);
181
182
        // undock docked ships
183
        foreach ($ship->getDockedShips() as $dockedShip) {
184
            $dockedShip->setDockedTo(null);
185
            $this->shipRepository->save($dockedShip);
186
        }
187
188
        // clear tractor status
189
190
        $tractoringShipWrapper = $wrapper->getTractoringShipWrapper();
191
        if ($tractoringShipWrapper !== null) {
192
            $tractoringShip = $tractoringShipWrapper->get();
193
            $this->shipSystemManager->deactivate($tractoringShipWrapper, ShipSystemTypeEnum::SYSTEM_TRACTOR_BEAM, true);
194
195
            $href = sprintf('ship.php?%s=1&id=%d', ShowShip::VIEW_IDENTIFIER, $tractoringShip->getId());
196
197
            $this->privateMessageSender->send(
198
                UserEnum::USER_NOONE,
199
                $tractoringShip->getUser()->getId(),
200
                sprintf('Die im Traktorstrahl der %s befindliche %s wurde zerstört', $tractoringShip->getName(), $oldName),
201
                $tractoringShip->isBase() ? PrivateMessageFolderSpecialEnum::PM_SPECIAL_STATION : PrivateMessageFolderSpecialEnum::PM_SPECIAL_SHIP,
202
                $href
203
            );
204
        }
205
206
        // reset tracker devices
207
        $this->resetTrackerDevices($ship->getId());
208
209
        return $msg;
210
    }
211
212
    private function resetTrackerDevices(int $shipId): void
213
    {
214
        foreach ($this->shipSystemRepository->getTrackingShipSystems($shipId) as $system) {
215
            $wrapper = $this->shipWrapperFactory->wrapShip($system->getShip());
216
217
            $this->shipSystemManager->deactivate($wrapper, ShipSystemTypeEnum::SYSTEM_TRACKER, true);
218
        }
219
    }
220
221
    private function leaveSomeIntactModules(ShipInterface $ship): void
222
    {
223
        if ($ship->isShuttle()) {
224
            return;
225
        }
226
227
        $intactModules = [];
228
229
        foreach ($ship->getSystems() as $system) {
230
            if (
231
                $system->getModule() !== null
232
                && $system->getStatus() == 100
233
            ) {
234
                $module = $system->getModule();
235
236
                if (!array_key_exists($module->getId(), $intactModules)) {
237
                    $intactModules[$module->getId()] = $module;
238
                }
239
            }
240
        }
241
242
        //leave 50% of all intact modules
243
        $leaveCount = (int) ceil(count($intactModules) / 2);
244
        for ($i = 1; $i <= $leaveCount; $i++) {
245
            $module = $intactModules[array_rand($intactModules)];
246
            unset($intactModules[$module->getId()]);
247
248
            $this->shipStorageManager->upperStorage(
249
                $ship,
250
                $module->getCommodity(),
251
                1
252
            );
253
        }
254
    }
255
256
    private function orphanizeStorage(ShipInterface $ship): void
257
    {
258
        foreach ($ship->getStorage() as $storage) {
259
            $storage->setUser($this->userRepository->getFallbackUser());
260
            $this->storageRepository->save($storage);
261
        }
262
    }
263
264
    private function destroyTradepost(TradePostInterface $tradePost): void
265
    {
266
        //salvage offers and storage
267
        $storages = $this->storageRepository->getByTradePost($tradePost->getId());
268
        foreach ($storages as $storage) {
269
            //only 50% off all storages
270
            if (random_int(0, 1) === 0) {
271
                $this->storageRepository->delete($storage);
272
                continue;
273
            }
274
275
            //only 0 to 50% of the specific amount
276
            $amount = (int)ceil($storage->getAmount() / 100 * random_int(0, 50));
277
278
            if ($amount === 0) {
279
                $this->storageRepository->delete($storage);
280
                continue;
281
            }
282
283
            //add to trumfield storage
284
            $this->shipStorageManager->upperStorage(
285
                $tradePost->getShip(),
286
                $storage->getCommodity(),
287
                $amount
288
            );
289
290
            $this->storageRepository->delete($storage);
291
        }
292
293
        //remove tradepost and cascading stuff
294
        $this->tradePostRepository->delete($tradePost);
295
    }
296
297
    public function remove(ShipInterface $ship, ?bool $truncateCrew = false): void
298
    {
299
        $this->leaveFleet->leaveFleet($ship);
300
301
        if ($ship->getState() === ShipStateEnum::SHIP_STATE_ASTRO_FINALIZING) {
302
            $this->astroEntryLib->cancelAstroFinalizing($ship);
303
        }
304
305
        $wrapper = $this->shipWrapperFactory->wrapShip($ship);
306
307
        //both sides have to be cleared, foreign key violation
308
        if ($ship->isTractoring()) {
309
            $this->shipSystemManager->deactivate($wrapper, ShipSystemTypeEnum::SYSTEM_TRACTOR_BEAM, true);
310
        } else {
311
            $tractoringShipWrapper = $wrapper->getTractoringShipWrapper();
312
            if ($tractoringShipWrapper !== null) {
313
                $this->shipSystemManager->deactivate($tractoringShipWrapper, ShipSystemTypeEnum::SYSTEM_TRACTOR_BEAM, true);
314
            }
315
        }
316
317
        foreach ($ship->getStorage() as $item) {
318
            $this->storageRepository->delete($item);
319
        }
320
321
        foreach ($ship->getDockedShips() as $dockedShip) {
322
            $dockedShip->setDockedTo(null);
323
            $this->shipRepository->save($dockedShip);
324
        }
325
326
        // delete torpedo storage
327
        $this->clearTorpedo->clearTorpedoStorage($wrapper);
328
329
        if ($truncateCrew) {
330
            $crewArray = [];
331
            foreach ($ship->getCrewlist() as $shipCrew) {
332
                $crewArray[] = $shipCrew->getCrew();
333
            }
334
335
            $this->shipCrewRepository->truncateByShip($ship->getId());
336
337
            foreach ($crewArray as $crew) {
338
                $this->crewRepository->delete($crew);
339
            }
340
341
            $ship->getCrewlist()->clear();
342
        }
343
344
        // reset tracker devices
345
        $this->resetTrackerDevices($ship->getId());
346
347
        foreach ($ship->getSystems() as $shipSystem) {
348
            $this->shipSystemRepository->delete($shipSystem);
349
        }
350
351
        $this->shipRepository->delete($ship);
352
    }
353
}
354