Passed
Pull Request — master (#1686)
by Nico
31:24
created

ShipTick::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 24
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 11
nc 1
nop 11
dl 0
loc 24
ccs 0
cts 12
cp 0
crap 2
rs 9.9
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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\UserInterface;
26
use Stu\Orm\Repository\DatabaseUserRepositoryInterface;
27
use Stu\Orm\Repository\ShipRepositoryInterface;
28
29
final class ShipTick implements ShipTickInterface
30
{
31
    private PrivateMessageSenderInterface $privateMessageSender;
32
33
    private ShipRepositoryInterface $shipRepository;
34
35
    private ShipSystemManagerInterface $shipSystemManager;
36
37
    private ShipLeaverInterface $shipLeaver;
38
39
    private GameControllerInterface $game;
40
41
    private AstroEntryLibInterface $astroEntryLib;
42
43
    private DatabaseUserRepositoryInterface $databaseUserRepository;
44
45
    private CreateDatabaseEntryInterface $createDatabaseEntry;
46
47
    private StationUtilityInterface $stationUtility;
48
49
    private RepairUtilInterface $repairUtil;
50
51
    private ShipTakeoverManagerInterface $shipTakeoverManager;
52
53
    /**
54
     * @var array<string>
55
     */
56
    private array $msg = [];
57
58
    public function __construct(
59
        PrivateMessageSenderInterface $privateMessageSender,
60
        ShipRepositoryInterface $shipRepository,
61
        ShipSystemManagerInterface $shipSystemManager,
62
        ShipLeaverInterface $shipLeaver,
63
        GameControllerInterface $game,
64
        AstroEntryLibInterface $astroEntryLib,
65
        DatabaseUserRepositoryInterface $databaseUserRepository,
66
        CreateDatabaseEntryInterface $createDatabaseEntry,
67
        StationUtilityInterface $stationUtility,
68
        RepairUtilInterface $repairUtil,
69
        ShipTakeoverManagerInterface $shipTakeoverManager
70
    ) {
71
        $this->privateMessageSender = $privateMessageSender;
72
        $this->shipRepository = $shipRepository;
73
        $this->shipSystemManager = $shipSystemManager;
74
        $this->shipLeaver = $shipLeaver;
75
        $this->game = $game;
76
        $this->astroEntryLib = $astroEntryLib;
77
        $this->databaseUserRepository = $databaseUserRepository;
78
        $this->createDatabaseEntry = $createDatabaseEntry;
79
        $this->stationUtility = $stationUtility;
80
        $this->repairUtil = $repairUtil;
81
        $this->shipTakeoverManager = $shipTakeoverManager;
82
    }
83
84
    public function work(ShipWrapperInterface $wrapper): void
85
    {
86
        $ship = $wrapper->get();
87
88
        // do construction stuff
89
        if ($this->doConstructionStuff($ship)) {
90
            $this->shipRepository->save($ship);
91
            $this->sendMessages($ship);
92
            return;
93
        }
94
95
        // repair station
96
        if ($ship->isBase() && $ship->getState() === ShipStateEnum::SHIP_STATE_REPAIR_PASSIVE) {
97
            $this->doRepairStation($wrapper);
98
        }
99
100
        // leave ship
101
        if ($ship->getCrewCount() > 0 && !$ship->isSystemHealthy(ShipSystemTypeEnum::SYSTEM_LIFE_SUPPORT)) {
102
            $this->msg[] = _('Die Lebenserhaltung ist ausgefallen:');
103
            $this->msg[] = $this->shipLeaver->evacuate($wrapper);
104
            $this->sendMessages($ship);
105
            return;
106
        }
107
108
        $eps = $wrapper->getEpsSystemData();
109
        $reactor = $wrapper->getReactorWrapper();
110
        if ($eps === null) {
111
            return;
112
        }
113
114
        // not enough crew
115
        $availableEps = $eps->getEps();
116
        if (!$ship->hasEnoughCrew()) {
117
            $this->msg[] = _('Zu wenig Crew an Bord, Schiff ist nicht voll funktionsfähig! Systeme werden deaktiviert!');
118
119
            //deactivate all systems except life support
120
            foreach ($this->shipSystemManager->getActiveSystems($ship) as $system) {
121
                if ($system->getSystemType() != ShipSystemTypeEnum::SYSTEM_LIFE_SUPPORT) {
122
                    $this->shipSystemManager->deactivate($wrapper, $system->getSystemType(), true);
123
                }
124
            }
125
        } elseif ($reactor !== null) {
126
            $availableEps = $eps->getEps() + $reactor->getEpsProduction();
127
        }
128
129
        //try to save energy by reducing alert state
130
        if ($wrapper->getEpsUsage() > $availableEps) {
131
            $malus = $wrapper->getEpsUsage() - $availableEps;
132
            $alertUsage = $ship->getAlertState()->value - 1;
133
134
            if ($alertUsage > 0) {
135
                $preState = $ship->getAlertState();
136
                $reduce = (int)min($malus, $alertUsage);
137
138
                $ship->setAlertState(ShipAlertStateEnum::from($preState->value - $reduce));
139
                $this->msg[] = sprintf(
140
                    _('Wechsel von %s auf %s wegen Energiemangel'),
141
                    $preState->getDescription(),
142
                    $ship->getAlertState()->getDescription()
143
                );
144
            }
145
        }
146
147
        //try to save energy by deactivating systems from low to high priority
148
        if ($wrapper->getEpsUsage() > $availableEps) {
149
            $activeSystems = $this->shipSystemManager->getActiveSystems($ship, true);
150
151
            foreach ($activeSystems as $system) {
152
                $energyConsumption = $this->shipSystemManager->getEnergyConsumption($system->getSystemType());
153
                if ($energyConsumption < 1) {
154
                    continue;
155
                }
156
157
                //echo "- eps: ".$eps." - usage: ".$wrapper->getEpsUsage()."\n";
158
                if ($availableEps - $wrapper->getEpsUsage() - $energyConsumption < 0) {
159
                    //echo "-- hit system: ".$system->getDescription()."\n";
160
161
                    $this->shipSystemManager->deactivate($wrapper, $system->getSystemType(), true);
162
163
                    $wrapper->lowerEpsUsage($energyConsumption);
164
                    $this->msg[] = $system->getSystemType()->getDescription() . ' deaktiviert wegen Energiemangel';
165
166
                    if ($ship->getCrewCount() > 0 && $system->getSystemType() == ShipSystemTypeEnum::SYSTEM_LIFE_SUPPORT) {
167
                        $this->msg[] = _('Die Lebenserhaltung ist ausgefallen:');
168
                        $this->msg[] = $this->shipLeaver->evacuate($wrapper);
169
                        $this->sendMessages($ship);
170
                        return;
171
                    }
172
                }
173
                if ($wrapper->getEpsUsage() <= $availableEps) {
174
                    break;
175
                }
176
            }
177
        }
178
179
        $newEps = $availableEps - $wrapper->getEpsUsage();
180
        $batteryReload = $ship->isBase()
181
            && $eps->reloadBattery()
182
            && $newEps > $eps->getEps()
183
            ? min(
184
                (int) ceil($eps->getMaxBattery() / 10),
185
                $newEps - $eps->getEps(),
186
                $eps->getMaxBattery() - $eps->getBattery()
187
            ) : 0;
188
189
        $newEps -= $batteryReload;
190
        if ($newEps > $eps->getMaxEps()) {
191
            $newEps = $eps->getMaxEps();
192
        }
193
194
        $reactorUsageForWarpdrive = $this->loadWarpdrive($wrapper);
195
        $usedEnergy = $wrapper->getEpsUsage() + $batteryReload + ($newEps - $eps->getEps()) + $reactorUsageForWarpdrive;
196
197
        //echo "--- Generated Id ".$ship->getId()." - eps: ".$eps." - usage: ".$wrapper->getEpsUsage()." - old eps: ".$ship->getEps()." - wk: ".$wkuse."\n";
198
        $eps->setEps($newEps)
199
            ->setBattery($eps->getBattery() + $batteryReload)
200
            ->update();
201
202
        if ($usedEnergy > 0 && $reactor !== null) {
203
            $reactor->changeLoad(-$usedEnergy);
204
        }
205
206
        $this->checkForFinishedTakeover($ship);
207
        $this->checkForFinishedAstroMapping($ship);
208
209
        //update tracker status
210
        $this->doTrackerDeviceStuff($wrapper);
211
212
        $this->shipRepository->save($ship);
213
214
        $this->sendMessages($ship);
215
    }
216
217
    private function loadWarpdrive(ShipWrapperInterface $wrapper): int
218
    {
219
        $reactor = $wrapper->getReactorWrapper();
220
        $warpdrive = $wrapper->getWarpDriveSystemData();
221
        if ($warpdrive === null || $reactor === null) {
222
            return 0;
223
        }
224
225
        $effectiveWarpdriveProduction = $reactor->getEffectiveWarpDriveProduction();
226
        if ($effectiveWarpdriveProduction === 0) {
227
            return 0;
228
        }
229
230
        $currentLoad = $warpdrive->getWarpDrive();
231
232
        $warpdrive->setWarpDrive($currentLoad + $effectiveWarpdriveProduction)->update();
233
234
        return $effectiveWarpdriveProduction * $wrapper->get()->getRump()->getFlightECost();
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
        /** @var null|DatabaseEntryInterface $databaseEntry */
377
        [$message, $databaseEntry] = $this->getDatabaseEntryForShipLocation($ship);
378
379
        if (
380
            $ship->getState() === ShipStateEnum::SHIP_STATE_ASTRO_FINALIZING
381
            && $databaseEntry !== null
382
            && $this->game->getCurrentRound()->getTurn() >= ($ship->getAstroStartTurn() + AstronomicalMappingEnum::TURNS_TO_FINISH)
383
        ) {
384
            $this->astroEntryLib->finish($ship);
385
386
            $this->msg[] = sprintf(
387
                _('Die Kartographierung %s wurde vollendet'),
388
                $message
389
            );
390
391
            $userId = $ship->getUser()->getId();
392
            $databaseEntryId = $databaseEntry->getId();
393
394
            if (!$this->databaseUserRepository->exists($userId, $databaseEntryId)) {
395
                $entry = $this->createDatabaseEntry->createDatabaseEntryForUser($ship->getUser(), $databaseEntryId);
396
397
                if ($entry !== null) {
398
                    $this->msg[] = sprintf(
399
                        _('Neuer Datenbankeintrag: %s (+%d Punkte)'),
400
                        $entry->getDescription(),
401
                        $entry->getCategory()->getPoints()
402
                    );
403
                }
404
            }
405
        }
406
    }
407
408
    /**
409
     * @return array{0: string|null, 1: DatabaseEntryInterface|null}
410
     */
411
    private function getDatabaseEntryForShipLocation(ShipInterface $ship): array
412
    {
413
        $system = $ship->getSystem();
414
        if ($system !== null) {
415
            return [
416
                'des Systems ' . $system->getName(),
417
                $system->getDatabaseEntry()
418
            ];
419
        }
420
421
        $mapRegion = $ship->getMapRegion();
422
        if ($mapRegion !== null) {
423
            return [
424
                'der Region ' . $mapRegion->getDescription(),
425
                $mapRegion->getDatabaseEntry()
426
            ];
427
        }
428
429
        return [null, null];
430
    }
431
432
    private function doTrackerDeviceStuff(ShipWrapperInterface $wrapper): void
433
    {
434
        $ship = $wrapper->get();
435
        $tracker = $wrapper->getTrackerSystemData();
436
437
        if ($tracker === null || $tracker->targetId === null) {
438
            return;
439
        }
440
441
        $targetWrapper = $tracker->getTargetWrapper();
442
        if ($targetWrapper === null) {
443
            throw new RuntimeException('should not happen');
444
        }
445
446
        $target = $targetWrapper->get();
447
        $remainingTicks = $tracker->getRemainingTicks();
448
449
        $reduceByTicks = max(1, (int)ceil((abs($ship->getCx() - $target->getCx()) +  abs($ship->getCy() - $target->getCy())) / 50));
450
451
        //reduce remaining ticks
452
        if ($remainingTicks > $reduceByTicks) {
453
            $tracker->setRemainingTicks($remainingTicks - $reduceByTicks)->update();
454
        } else {
455
            $this->shipSystemManager->deactivate($wrapper, ShipSystemTypeEnum::SYSTEM_TRACKER, true);
456
457
            if ($target->getUser() !== $ship->getUser()) {
458
                //send pm to target owner
459
                $this->privateMessageSender->send(
460
                    UserEnum::USER_NOONE,
461
                    $target->getUser()->getId(),
462
                    sprintf(
463
                        'Die Crew der %s hat einen Transponder gefunden und deaktiviert. %s',
464
                        $target->getName(),
465
                        $this->getTrackerSource($ship->getUser())
466
                    ),
467
                    PrivateMessageFolderSpecialEnum::PM_SPECIAL_SHIP
468
                );
469
470
                //send pm to tracker owner
471
                $this->privateMessageSender->send(
472
                    UserEnum::USER_NOONE,
473
                    $ship->getUser()->getId(),
474
                    sprintf(
475
                        'Die %s hat die Verbindung zum Tracker verloren',
476
                        $ship->getName()
477
                    ),
478
                    PrivateMessageFolderSpecialEnum::PM_SPECIAL_SHIP
479
                );
480
            }
481
        }
482
    }
483
484
    private function getTrackerSource(UserInterface $user): string
485
    {
486
        switch (random_int(0, 2)) {
487
            case 0:
488
                return _('Der Ursprung kann nicht identifiziert werden');
489
            case 1:
490
                return sprintf(_('Der Ursprung lässt auf %s schließen'), $user->getName());
491
            case 2:
492
                return sprintf(_('Der Ursprung lässt darauf schließen, dass er %s-Herkunft ist'), $user->getFaction()->getName());
493
            default:
494
                return '';
495
        }
496
    }
497
498
    private function sendMessages(ShipInterface $ship): void
499
    {
500
        if ($this->msg === []) {
501
            return;
502
        }
503
        $text = "Tickreport der " . $ship->getName() . "\n";
504
        foreach ($this->msg as $msg) {
505
            $text .= $msg . "\n";
506
        }
507
508
        $href = sprintf('ship.php?%s=1&id=%d', ShowShip::VIEW_IDENTIFIER, $ship->getId());
509
510
        $this->privateMessageSender->send(
511
            UserEnum::USER_NOONE,
512
            $ship->getUser()->getId(),
513
            $text,
514
            $ship->isBase() ? PrivateMessageFolderSpecialEnum::PM_SPECIAL_STATION : PrivateMessageFolderSpecialEnum::PM_SPECIAL_SHIP,
515
            $href
516
        );
517
518
        $this->msg = [];
519
    }
520
}
521