Passed
Pull Request — master (#1685)
by Nico
30:06
created

ShipTick::doRepairStation()   B

Complexity

Conditions 9
Paths 30

Size

Total Lines 70
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 90

Importance

Changes 0
Metric Value
cc 9
eloc 39
nc 30
nop 1
dl 0
loc 70
ccs 0
cts 43
cp 0
crap 90
rs 7.7404
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Stu\Module\Tick\Ship;
4
5
use RuntimeException;
6
use Stu\Component\Ship\AstronomicalMappingEnum;
7
use Stu\Component\Ship\Repair\RepairUtilInterface;
8
use Stu\Component\Ship\ShipAlertStateEnum;
9
use Stu\Component\Ship\ShipStateEnum;
10
use Stu\Component\Ship\System\ShipSystemManagerInterface;
11
use Stu\Component\Ship\System\ShipSystemTypeEnum;
12
use Stu\Component\Station\StationUtilityInterface;
13
use Stu\Module\Control\GameControllerInterface;
14
use Stu\Module\Database\Lib\CreateDatabaseEntryInterface;
15
use Stu\Module\Message\Lib\PrivateMessageFolderSpecialEnum;
16
use Stu\Module\Message\Lib\PrivateMessageSenderInterface;
17
use Stu\Module\PlayerSetting\Lib\UserEnum;
18
use Stu\Module\Ship\Lib\AstroEntryLibInterface;
19
use Stu\Module\Ship\Lib\Crew\ShipLeaverInterface;
20
use Stu\Module\Ship\Lib\Interaction\ShipTakeoverManagerInterface;
21
use Stu\Module\Ship\Lib\ShipWrapperInterface;
22
use Stu\Module\Ship\View\ShowShip\ShowShip;
23
use Stu\Orm\Entity\DatabaseEntryInterface;
24
use Stu\Orm\Entity\ShipInterface;
25
use Stu\Orm\Entity\ShipSystemInterface;
26
use Stu\Orm\Entity\UserInterface;
27
use Stu\Orm\Repository\DatabaseUserRepositoryInterface;
28
use Stu\Orm\Repository\ShipRepositoryInterface;
29
30
final class ShipTick implements ShipTickInterface
31
{
32
    private PrivateMessageSenderInterface $privateMessageSender;
33
34
    private ShipRepositoryInterface $shipRepository;
35
36
    private ShipSystemManagerInterface $shipSystemManager;
37
38
    private ShipLeaverInterface $shipLeaver;
39
40
    private GameControllerInterface $game;
41
42
    private AstroEntryLibInterface $astroEntryLib;
43
44
    private DatabaseUserRepositoryInterface $databaseUserRepository;
45
46
    private CreateDatabaseEntryInterface $createDatabaseEntry;
47
48
    private StationUtilityInterface $stationUtility;
49
50
    private RepairUtilInterface $repairUtil;
51
52
    private ShipTakeoverManagerInterface $shipTakeoverManager;
53
54
    /**
55
     * @var array<string>
56
     */
57
    private array $msg = [];
58
59
    public function __construct(
60
        PrivateMessageSenderInterface $privateMessageSender,
61
        ShipRepositoryInterface $shipRepository,
62
        ShipSystemManagerInterface $shipSystemManager,
63
        ShipLeaverInterface $shipLeaver,
64
        GameControllerInterface $game,
65
        AstroEntryLibInterface $astroEntryLib,
66
        DatabaseUserRepositoryInterface $databaseUserRepository,
67
        CreateDatabaseEntryInterface $createDatabaseEntry,
68
        StationUtilityInterface $stationUtility,
69
        RepairUtilInterface $repairUtil,
70
        ShipTakeoverManagerInterface $shipTakeoverManager
71
    ) {
72
        $this->privateMessageSender = $privateMessageSender;
73
        $this->shipRepository = $shipRepository;
74
        $this->shipSystemManager = $shipSystemManager;
75
        $this->shipLeaver = $shipLeaver;
76
        $this->game = $game;
77
        $this->astroEntryLib = $astroEntryLib;
78
        $this->databaseUserRepository = $databaseUserRepository;
79
        $this->createDatabaseEntry = $createDatabaseEntry;
80
        $this->stationUtility = $stationUtility;
81
        $this->repairUtil = $repairUtil;
82
        $this->shipTakeoverManager = $shipTakeoverManager;
83
    }
84
85
    public function work(ShipWrapperInterface $wrapper): void
86
    {
87
        $ship = $wrapper->get();
88
89
        // do construction stuff
90
        if ($this->doConstructionStuff($ship)) {
91
            $this->shipRepository->save($ship);
92
            $this->sendMessages($ship);
93
            return;
94
        }
95
96
        // repair station
97
        if ($ship->isBase() && $ship->getState() === ShipStateEnum::SHIP_STATE_REPAIR_PASSIVE) {
98
            $this->doRepairStation($wrapper);
99
        }
100
101
        // leave ship
102
        if ($ship->getCrewCount() > 0 && !$ship->isSystemHealthy(ShipSystemTypeEnum::SYSTEM_LIFE_SUPPORT)) {
103
            $this->msg[] = _('Die Lebenserhaltung ist ausgefallen:');
104
            $this->msg[] = $this->shipLeaver->evacuate($wrapper);
105
            $this->sendMessages($ship);
106
            return;
107
        }
108
109
        $eps = $wrapper->getEpsSystemData();
110
        $warpdrive = $wrapper->getWarpDriveSystemData();
111
        if ($eps === null) {
112
            return;
113
        }
114
115
        // not enough crew
116
        $availableWarpDrive = null;
117
        if (!$ship->hasEnoughCrew()) {
118
            $this->msg[] = _('Zu wenig Crew an Bord, Schiff ist nicht voll funktionsfähig! Systeme werden deaktiviert!');
119
120
            //deactivate all systems except life support
121
            foreach ($this->shipSystemManager->getActiveSystems($ship) as $system) {
122
                if ($system->getSystemType() != ShipSystemTypeEnum::SYSTEM_LIFE_SUPPORT) {
123
                    $this->shipSystemManager->deactivate($wrapper, $system->getSystemType(), true);
124
                }
125
            }
126
127
128
129
            $availableEps = $eps->getEps();
130
        } else {
131
            if ($warpdrive != null) {
132
                $availableEps = (int)($eps->getEps() +  ($ship->getReactorOutputCappedByReactorLoad() * ($warpdrive->getWarpCoreSplit() / 100)));
133
            } else {
134
                $availableEps = $eps->getEps() + $ship->getReactorOutputCappedByReactorLoad();
135
            }
136
            if ($warpdrive !== null) {
137
                $availableWarpDrive = $warpdrive->getWarpDrive() + $wrapper->getEffectiveWarpDriveProduction();
138
            }
139
        }
140
141
        //try to save energy by reducing alert state
142
        if ($wrapper->getEpsUsage() > $availableEps) {
143
            $malus = $wrapper->getEpsUsage() - $availableEps;
144
            $alertUsage = $ship->getAlertState()->value - 1;
145
146
            if ($alertUsage > 0) {
147
                $preState = $ship->getAlertState();
148
                $reduce = (int)min($malus, $alertUsage);
149
150
                $ship->setAlertState(ShipAlertStateEnum::from($preState->value - $reduce));
151
                $this->msg[] = sprintf(
152
                    _('Wechsel von %s auf %s wegen Energiemangel'),
153
                    $preState->getDescription(),
154
                    $ship->getAlertState()->getDescription()
155
                );
156
            }
157
        }
158
159
        //try to save energy by deactivating systems from low to high priority
160
        if ($wrapper->getEpsUsage() > $availableEps) {
161
            $activeSystems = $this->shipSystemManager->getActiveSystems($ship, true);
162
163
            foreach ($activeSystems as $system) {
164
                $energyConsumption = $this->shipSystemManager->getEnergyConsumption($system->getSystemType());
165
                if ($energyConsumption < 1) {
166
                    continue;
167
                }
168
169
                //echo "- eps: ".$eps." - usage: ".$wrapper->getEpsUsage()."\n";
170
                if ($availableEps - $wrapper->getEpsUsage() - $energyConsumption < 0) {
171
                    //echo "-- hit system: ".$system->getDescription()."\n";
172
173
                    $this->shipSystemManager->deactivate($wrapper, $system->getSystemType(), true);
174
175
                    $wrapper->lowerEpsUsage($energyConsumption);
176
                    $this->msg[] = $system->getSystemType()->getDescription() . ' deaktiviert wegen Energiemangel';
177
178
                    if ($ship->getCrewCount() > 0 && $system->getSystemType() == ShipSystemTypeEnum::SYSTEM_LIFE_SUPPORT) {
179
                        $this->msg[] = _('Die Lebenserhaltung ist ausgefallen:');
180
                        $this->msg[] = $this->shipLeaver->evacuate($wrapper);
181
                        $this->sendMessages($ship);
182
                        return;
183
                    }
184
                }
185
                if ($wrapper->getEpsUsage() <= $availableEps) {
186
                    break;
187
                }
188
            }
189
        }
190
191
        $newEps = $availableEps - $wrapper->getEpsUsage();
192
        $batteryReload = $ship->isBase()
193
            && $eps->reloadBattery()
194
            && $newEps > $eps->getEps()
195
            ? min(
196
                (int) ceil($eps->getMaxBattery() / 10),
197
                $newEps - $eps->getEps(),
198
                $eps->getMaxBattery() - $eps->getBattery()
199
            ) : 0;
200
201
        $newEps -= $batteryReload;
202
        if ($newEps > $eps->getMaxEps()) {
203
            $newEps = $eps->getMaxEps();
204
        }
205
        $usedwarpdrive = 0;
206
        if ($warpdrive !== null && $availableWarpDrive !== null) {
207
            if ($availableWarpDrive > $warpdrive->getMaxWarpDrive()) {
208
                $availableWarpDrive = $warpdrive->getMaxWarpDrive();
209
                $usedwarpdrive = ($warpdrive->getMaxWarpDrive() - $warpdrive->getWarpDrive()) * $ship->getRump()->getFlightECost();
210
            } else {
211
                $usedwarpdrive = $wrapper->getEffectiveWarpDriveProduction() * $ship->getRump()->getFlightECost();
212
            }
213
            $warpdrive->setWarpDrive($availableWarpDrive)->update();
214
        }
215
216
        $usedEnergy = $wrapper->getEpsUsage() + $batteryReload + ($newEps - $eps->getEps()) + $usedwarpdrive;
217
        //echo "--- Generated Id ".$ship->getId()." - eps: ".$eps." - usage: ".$wrapper->getEpsUsage()." - old eps: ".$ship->getEps()." - wk: ".$wkuse."\n";
218
        $eps->setEps($newEps)
219
            ->setBattery($eps->getBattery() + $batteryReload)
220
            ->update();
221
222
223
        //core OR fusion
224
        $ship->setReactorLoad($ship->getReactorLoad() - $usedEnergy);
225
226
        $this->checkForFinishedTakeover($ship);
227
        $this->checkForFinishedAstroMapping($ship);
228
229
        //update tracker status
230
        $this->doTrackerDeviceStuff($wrapper);
231
232
        $this->shipRepository->save($ship);
233
234
        $this->sendMessages($ship);
235
    }
236
237
    private function doConstructionStuff(ShipInterface $ship): bool
238
    {
239
        $progress =  $this->stationUtility->getConstructionProgress($ship);
240
241
        if ($progress === null) {
242
            return false;
243
        }
244
245
        if ($progress->getRemainingTicks() === 0) {
246
            return false;
247
        }
248
249
        $isUnderConstruction = $ship->getState() === ShipStateEnum::SHIP_STATE_UNDER_CONSTRUCTION;
250
251
        if (!$this->stationUtility->hasEnoughDockedWorkbees($ship, $ship->getRump())) {
252
            $neededWorkbees = $isUnderConstruction ? $ship->getRump()->getNeededWorkbees() :
253
                (int)ceil($ship->getRump()->getNeededWorkbees() / 2);
254
255
            $this->msg[] = sprintf(
256
                _('Nicht genügend Workbees (%d/%d) angedockt um %s weiterführen zu können'),
257
                $this->stationUtility->getDockedWorkbeeCount($ship),
258
                $neededWorkbees,
259
                $isUnderConstruction ? 'den Bau' : 'die Demontage'
260
            );
261
            return true;
262
        }
263
264
        if ($progress->getRemainingTicks() === 1) {
265
            if ($isUnderConstruction) {
266
                $this->stationUtility->finishStation($ship, $progress);
267
            } else {
268
                $this->stationUtility->finishScrapping($ship, $progress);
269
            }
270
271
            $this->msg[] = sprintf(
272
                _('%s: %s bei %s fertiggestellt'),
273
                $ship->getRump()->getName(),
274
                $isUnderConstruction ? 'Bau' : 'Demontage',
275
                $ship->getSectorString()
276
            );
277
        } else {
278
            $this->stationUtility->reduceRemainingTicks($progress);
279
280
            if ($isUnderConstruction) {
281
                // raise hull
282
                $increase = intdiv($ship->getMaxHull(), 2 * $ship->getRump()->getBuildtime());
283
                $ship->setHuell($ship->getHull() + $increase);
284
            }
285
        }
286
287
        return true;
288
    }
289
290
    private function doRepairStation(ShipWrapperInterface $wrapper): void
291
    {
292
        $station = $wrapper->get();
293
294
        if (!$this->stationUtility->hasEnoughDockedWorkbees($station, $station->getRump())) {
295
            $neededWorkbees = (int)ceil($station->getRump()->getNeededWorkbees() / 5);
296
297
            $this->msg[] = sprintf(
298
                _('Nicht genügend Workbees (%d/%d) angedockt um die Reparatur weiterführen zu können'),
299
                $this->stationUtility->getDockedWorkbeeCount($station),
300
                $neededWorkbees
301
            );
302
            return;
303
        }
304
305
        $neededParts = $this->repairUtil->determineSpareParts($wrapper);
306
307
        // parts stored?
308
        if (!$this->repairUtil->enoughSparePartsOnEntity($neededParts, $station, false, $station)) {
309
            return;
310
        }
311
312
        //repair hull
313
        $station->setHuell($station->getHull() + $station->getRepairRate());
314
        if ($station->getHull() > $station->getMaxHull()) {
315
            $station->setHuell($station->getMaxHull());
316
        }
317
318
        //repair station systems
319
        $damagedSystems = $wrapper->getDamagedSystems();
320
        if (!empty($damagedSystems)) {
321
            $firstSystem = $damagedSystems[0];
322
            $firstSystem->setStatus(100);
323
324
            if ($station->getCrewCount() > 0) {
325
                $firstSystem->setMode($this->shipSystemManager->lookupSystem($firstSystem->getSystemType())->getDefaultMode());
326
            }
327
328
            // maximum of two systems get repaired
329
            if (count($damagedSystems) > 1) {
330
                $secondSystem = $damagedSystems[1];
331
                $secondSystem->setStatus(100);
332
333
                if ($station->getCrewCount() > 0) {
334
                    $secondSystem->setMode($this->shipSystemManager->lookupSystem($secondSystem->getSystemType())->getDefaultMode());
335
                }
336
            }
337
        }
338
339
        // consume spare parts
340
        $this->repairUtil->consumeSpareParts($neededParts, $station, false);
341
342
        if (!$wrapper->canBeRepaired()) {
343
            $station->setHuell($station->getMaxHull());
344
            $station->setState(ShipStateEnum::SHIP_STATE_NONE);
345
346
            $shipOwnerMessage = sprintf(
347
                "Die Reparatur der %s wurde in Sektor %s fertiggestellt",
348
                $station->getName(),
349
                $station->getSectorString()
350
            );
351
352
            $this->privateMessageSender->send(
353
                UserEnum::USER_NOONE,
354
                $station->getUser()->getId(),
355
                $shipOwnerMessage,
356
                PrivateMessageFolderSpecialEnum::PM_SPECIAL_STATION
357
            );
358
        }
359
        $this->shipRepository->save($station);
360
    }
361
362
    private function checkForFinishedTakeover(ShipInterface $ship): void
363
    {
364
        $takeover = $ship->getTakeoverActive();
365
        if ($takeover === null) {
366
            return;
367
        }
368
369
        if ($this->shipTakeoverManager->isTakeoverReady($takeover)) {
370
            $this->shipTakeoverManager->finishTakeover($takeover);
371
        }
372
    }
373
374
    private function checkForFinishedAstroMapping(ShipInterface $ship): void
375
    {
376
        [$message, $databaseEntry] = $this->getDatabaseEntryForShipLocation($ship);
377
378
        if (
379
            $ship->getState() === ShipStateEnum::SHIP_STATE_ASTRO_FINALIZING
380
            && $databaseEntry !== null
381
            && $this->game->getCurrentRound()->getTurn() >= ($ship->getAstroStartTurn() + AstronomicalMappingEnum::TURNS_TO_FINISH)
382
        ) {
383
            $this->astroEntryLib->finish($ship);
384
385
            $this->msg[] = sprintf(
386
                _('Die Kartographierung %s wurde vollendet'),
387
                $message
388
            );
389
390
            $userId = $ship->getUser()->getId();
391
            $databaseEntryId = $databaseEntry->getId();
392
393
            if (!$this->databaseUserRepository->exists($userId, $databaseEntryId)) {
394
                $entry = $this->createDatabaseEntry->createDatabaseEntryForUser($ship->getUser(), $databaseEntryId);
395
396
                if ($entry !== null) {
397
                    $this->msg[] = sprintf(
398
                        _('Neuer Datenbankeintrag: %s (+%d Punkte)'),
399
                        $entry->getDescription(),
400
                        $entry->getCategory()->getPoints()
401
                    );
402
                }
403
            }
404
        }
405
    }
406
407
    /**
408
     * @return array{0: string|null, 1: DatabaseEntryInterface|null}
409
     */
410
    private function getDatabaseEntryForShipLocation(ShipInterface $ship): array
411
    {
412
        $system = $ship->getSystem();
413
        if ($system !== null) {
414
            return [
415
                'des Systems ' . $system->getName(),
416
                $system->getDatabaseEntry()
417
            ];
418
        }
419
420
        $mapRegion = $ship->getMapRegion();
421
        if ($mapRegion !== null) {
422
            return [
423
                'der Region ' . $mapRegion->getDescription(),
424
                $mapRegion->getDatabaseEntry()
425
            ];
426
        }
427
428
        return [null, null];
429
    }
430
431
    private function doTrackerDeviceStuff(ShipWrapperInterface $wrapper): void
432
    {
433
        $ship = $wrapper->get();
434
        $tracker = $wrapper->getTrackerSystemData();
435
436
        if ($tracker === null || $tracker->targetId === null) {
437
            return;
438
        }
439
440
        $targetWrapper = $tracker->getTargetWrapper();
441
        if ($targetWrapper === null) {
442
            throw new RuntimeException('should not happen');
443
        }
444
445
        $target = $targetWrapper->get();
446
        $remainingTicks = $tracker->getRemainingTicks();
447
448
        $reduceByTicks = max(1, (int)ceil((abs($ship->getCx() - $target->getCx()) +  abs($ship->getCy() - $target->getCy())) / 50));
449
450
        //reduce remaining ticks
451
        if ($remainingTicks > $reduceByTicks) {
452
            $tracker->setRemainingTicks($remainingTicks - $reduceByTicks)->update();
453
        } else {
454
            $this->shipSystemManager->deactivate($wrapper, ShipSystemTypeEnum::SYSTEM_TRACKER, true);
455
456
            if ($target->getUser() !== $ship->getUser()) {
457
                //send pm to target owner
458
                $this->privateMessageSender->send(
459
                    UserEnum::USER_NOONE,
460
                    $target->getUser()->getId(),
461
                    sprintf(
462
                        'Die Crew der %s hat einen Transponder gefunden und deaktiviert. %s',
463
                        $target->getName(),
464
                        $this->getTrackerSource($ship->getUser())
465
                    ),
466
                    PrivateMessageFolderSpecialEnum::PM_SPECIAL_SHIP
467
                );
468
469
                //send pm to tracker owner
470
                $this->privateMessageSender->send(
471
                    UserEnum::USER_NOONE,
472
                    $ship->getUser()->getId(),
473
                    sprintf(
474
                        'Die %s hat die Verbindung zum Tracker verloren',
475
                        $ship->getName()
476
                    ),
477
                    PrivateMessageFolderSpecialEnum::PM_SPECIAL_SHIP
478
                );
479
            }
480
        }
481
    }
482
483
    private function getTrackerSource(UserInterface $user): string
484
    {
485
        switch (random_int(0, 2)) {
486
            case 0:
487
                return _('Der Ursprung kann nicht identifiziert werden');
488
            case 1:
489
                return sprintf(_('Der Ursprung lässt auf %s schließen'), $user->getName());
490
            case 2:
491
                return sprintf(_('Der Ursprung lässt darauf schließen, dass er %s-Herkunft ist'), $user->getFaction()->getName());
492
            default:
493
                return '';
494
        }
495
    }
496
497
    private function sendMessages(ShipInterface $ship): void
498
    {
499
        if ($this->msg === []) {
500
            return;
501
        }
502
        $text = "Tickreport der " . $ship->getName() . "\n";
503
        foreach ($this->msg as $msg) {
504
            $text .= $msg . "\n";
505
        }
506
507
        $href = sprintf('ship.php?%s=1&id=%d', ShowShip::VIEW_IDENTIFIER, $ship->getId());
508
509
        $this->privateMessageSender->send(
510
            UserEnum::USER_NOONE,
511
            $ship->getUser()->getId(),
512
            $text,
513
            $ship->isBase() ? PrivateMessageFolderSpecialEnum::PM_SPECIAL_STATION : PrivateMessageFolderSpecialEnum::PM_SPECIAL_SHIP,
514
            $href
515
        );
516
517
        $this->msg = [];
518
    }
519
}
520