Passed
Push — master ( 784be2...c4713a )
by Nico
58:15 queued 29:26
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\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