Passed
Pull Request — master (#1809)
by Nico
49:28 queued 23:38
created

RepairUtil::getRepairDurationPreview()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 8
c 0
b 0
f 0
nc 3
nop 1
dl 0
loc 14
ccs 9
cts 9
cp 1
crap 3
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Stu\Component\Ship\Repair;
6
7
use RuntimeException;
8
use Stu\Component\Building\BuildingEnum;
9
use Stu\Component\Colony\ColonyFunctionManagerInterface;
10
use Stu\Component\Colony\Storage\ColonyStorageManagerInterface;
11
use Stu\Component\Crew\CrewEnum;
12
use Stu\Component\Ship\RepairTaskEnum;
13
use Stu\Component\Ship\ShipStateEnum;
14
use Stu\Component\Ship\Storage\ShipStorageManagerInterface;
15
use Stu\Component\Ship\System\ShipSystemTypeEnum;
16
use Stu\Module\Commodity\CommodityTypeEnum;
17
use Stu\Module\Message\Lib\PrivateMessageFolderSpecialEnum;
18
use Stu\Module\Message\Lib\PrivateMessageSenderInterface;
19
use Stu\Module\PlayerSetting\Lib\UserEnum;
20
use Stu\Module\Ship\Lib\ShipWrapperInterface;
21
use Stu\Orm\Entity\ColonyInterface;
22
use Stu\Orm\Entity\RepairTaskInterface;
23
use Stu\Orm\Entity\ShipInterface;
24
use Stu\Orm\Repository\ColonyShipRepairRepositoryInterface;
25
use Stu\Orm\Repository\RepairTaskRepositoryInterface;
26
use Stu\Orm\Repository\ShipSystemRepositoryInterface;
27
28
//TODO unit tests
29
final class RepairUtil implements RepairUtilInterface
30
{
31
    private ShipSystemRepositoryInterface $shipSystemRepository;
32
33
    private RepairTaskRepositoryInterface $repairTaskRepository;
34
35
    private ColonyShipRepairRepositoryInterface $colonyShipRepairRepository;
36
37
    private ShipStorageManagerInterface $shipStorageManager;
38
39
    private ColonyStorageManagerInterface $colonyStorageManager;
40
41
    private ColonyFunctionManagerInterface $colonyFunctionManager;
42
43
    private PrivateMessageSenderInterface $privateMessageSender;
44
45 10
    public function __construct(
46
        ShipSystemRepositoryInterface $shipSystemRepository,
47
        RepairTaskRepositoryInterface $repairTaskRepository,
48
        ColonyShipRepairRepositoryInterface $colonyShipRepairRepository,
49
        ShipStorageManagerInterface $shipStorageManager,
50
        ColonyStorageManagerInterface $colonyStorageManager,
51
        ColonyFunctionManagerInterface $colonyFunctionManager,
52
        PrivateMessageSenderInterface $privateMessageSender
53
    ) {
54 10
        $this->shipSystemRepository = $shipSystemRepository;
55 10
        $this->repairTaskRepository = $repairTaskRepository;
56 10
        $this->colonyShipRepairRepository = $colonyShipRepairRepository;
57 10
        $this->shipStorageManager = $shipStorageManager;
58 10
        $this->colonyStorageManager = $colonyStorageManager;
59 10
        $this->colonyFunctionManager = $colonyFunctionManager;
60 10
        $this->privateMessageSender = $privateMessageSender;
61
    }
62
63
    //REPAIR STUFF
64
    public function determineSpareParts(ShipWrapperInterface $wrapper, bool $tickBased): array
65
    {
66
        $isRepairStationBonus = $this->isRepairStationBonus($wrapper);
67
68
        $neededSpareParts = $this->calculateNeededSpareParts($wrapper, $isRepairStationBonus, $tickBased);
69
        $neededSystemComponents = $this->calculateNeededSystemComponents($wrapper, $isRepairStationBonus, $tickBased);
70
71
        return [
72
            CommodityTypeEnum::COMMODITY_SPARE_PART => $neededSpareParts,
73
            CommodityTypeEnum::COMMODITY_SYSTEM_COMPONENT => $neededSystemComponents
74
        ];
75
    }
76
77
    private function calculateNeededSpareParts(ShipWrapperInterface $wrapper, bool $isRepairStationBonus, bool $tickBased): int
78
    {
79
        $neededSpareParts = 0;
80
        $ship = $wrapper->get();
81
        $hull = $ship->getHull();
82
        $maxHull = $ship->getMaxHull();
83
84
        if ($hull < $maxHull) {
85
            if ($tickBased) {
86
                $neededSpareParts += 1;
87
            } else {
88
                $hullRepairParts = ($maxHull - $hull) / RepairTaskEnum::HULL_HITPOINTS_PER_SPARE_PART;
89
                if ($isRepairStationBonus) {
90
                    $neededSpareParts += (int)ceil($hullRepairParts / 2);
91
                } else {
92
                    $neededSpareParts += (int)ceil($hullRepairParts);
93
                }
94
            }
95
        }
96
97
        $damagedSystems = $wrapper->getDamagedSystems();
98
        $maxSystems = $tickBased ? ($isRepairStationBonus ? 4 : 2) : count($damagedSystems);
99
        $systemCount = min(count($damagedSystems), $maxSystems);
100
101
        for ($i = 0; $i < $systemCount; $i++) {
102
            $system = $damagedSystems[$i];
103
            $systemLvl = $system->determineSystemLevel();
104
            $healingPercentage = (100 - $system->getStatus()) / 100;
105
            $systemRepairParts = $healingPercentage * RepairTaskEnum::SHIPYARD_PARTS_USAGE[$systemLvl][RepairTaskEnum::SPARE_PARTS_ONLY];
106
            if ($isRepairStationBonus) {
107
                $neededSpareParts += (int)ceil($systemRepairParts / 2);
108
            } else {
109
                $neededSpareParts += (int)ceil($systemRepairParts);
110
            }
111
        }
112
113
        return $neededSpareParts;
114
    }
115
116
    private function calculateNeededSystemComponents(ShipWrapperInterface $wrapper, bool $isRepairStationBonus, bool $tickBased): int
117
    {
118
        $neededSystemComponents = 0;
119
        $damagedSystems = $wrapper->getDamagedSystems();
120
        $maxSystems = $tickBased ? ($isRepairStationBonus ? 4 : 2) : count($damagedSystems);
121
        $systemCount = min(count($damagedSystems), $maxSystems);
122
123
        for ($i = 0; $i < $systemCount; $i++) {
124
            $system = $damagedSystems[$i];
125
            $systemLvl = $system->determineSystemLevel();
126
            $healingPercentage = (100 - $system->getStatus()) / 100;
127
            $systemComponents = $healingPercentage * RepairTaskEnum::SHIPYARD_PARTS_USAGE[$systemLvl][RepairTaskEnum::SYSTEM_COMPONENTS_ONLY];
128
            if ($isRepairStationBonus) {
129
                $neededSystemComponents += (int)ceil($systemComponents / 2);
130
            } else {
131
                $neededSystemComponents += (int)ceil($systemComponents);
132
            }
133
        }
134
135
        return $neededSystemComponents;
136
    }
137
138
    public function enoughSparePartsOnEntity(array $neededParts, ColonyInterface|ShipInterface $entity, ShipInterface $ship): bool
139
    {
140
        $neededSpareParts = $neededParts[CommodityTypeEnum::COMMODITY_SPARE_PART];
141
        $neededSystemComponents = $neededParts[CommodityTypeEnum::COMMODITY_SYSTEM_COMPONENT];
142
143
        if ($neededSpareParts > 0) {
144
            $spareParts = $entity->getStorage()->get(CommodityTypeEnum::COMMODITY_SPARE_PART);
145
146
            if ($spareParts === null || $spareParts->getAmount() < $neededSpareParts) {
147
                $this->sendNeededAmountMessage($neededSpareParts, $neededSystemComponents, $ship, $entity);
148
                return false;
149
            }
150
        }
151
152
        if ($neededSystemComponents > 0) {
153
            $systemComponents = $entity->getStorage()->get(CommodityTypeEnum::COMMODITY_SYSTEM_COMPONENT);
154
155
            if ($systemComponents === null || $systemComponents->getAmount() < $neededSystemComponents) {
156
                $this->sendNeededAmountMessage($neededSpareParts, $neededSystemComponents, $ship, $entity);
157
                return false;
158
            }
159
        }
160
161
        return true;
162
    }
163
164
    private function sendNeededAmountMessage(
165
        int $neededSpareParts,
166
        int $neededSystemComponents,
167
        ShipInterface $ship,
168
        ColonyInterface|ShipInterface $entity
169
    ): void {
170
        $neededPartsString = sprintf(
171
            "%d %s%s",
172
            $neededSpareParts,
173
            CommodityTypeEnum::getDescription(CommodityTypeEnum::COMMODITY_SPARE_PART),
174
            ($neededSystemComponents > 0 ? sprintf(
175
                "\n%d %s",
176
                $neededSystemComponents,
177
                CommodityTypeEnum::getDescription(CommodityTypeEnum::COMMODITY_SYSTEM_COMPONENT)
178
            ) : '')
179
        );
180
181
        $isColony = $entity instanceof ColonyInterface;
182
183
        //PASSIVE REPAIR OF STATION BY WORKBEES
184
        if ($entity === $ship) {
185
            $entityOwnerMessage = sprintf(
186
                "Die Reparatur der %s %s wurde in Sektor %s angehalten.\nEs werden folgende Waren benötigt:\n%s",
187
                $entity->getRump()->getName(),
188
                $ship->getName(),
189
                $ship->getSectorString(),
190
                $neededPartsString
191
            );
192
        } else {
193
            $entityOwnerMessage = $isColony ? sprintf(
194
                "Die Reparatur der %s von Siedler %s wurde in Sektor %s bei der Kolonie %s angehalten.\nEs werden folgende Waren benötigt:\n%s",
195
                $ship->getName(),
196
                $ship->getUser()->getName(),
197
                $ship->getSectorString(),
198
                $entity->getName(),
199
                $neededPartsString
200
            ) : sprintf(
201
                "Die Reparatur der %s von Siedler %s wurde in Sektor %s bei der %s %s angehalten.\nEs werden folgende Waren benötigt:\n%s",
202
                $ship->getName(),
203
                $ship->getUser()->getName(),
204
                $ship->getSectorString(),
205
                $entity->getRump()->getName(),
206
                $entity->getName(),
207
                $neededPartsString
208
            );
209
        }
210
        $this->privateMessageSender->send(
211
            UserEnum::USER_NOONE,
212
            $entity->getUser()->getId(),
213
            $entityOwnerMessage,
214
            $isColony ? PrivateMessageFolderSpecialEnum::PM_SPECIAL_COLONY : PrivateMessageFolderSpecialEnum::PM_SPECIAL_STATION
215
        );
216
    }
217
218
    public function consumeSpareParts(array $neededParts, ColonyInterface|ShipInterface $entity): void
219
    {
220
        foreach ($neededParts as $commodityKey => $amount) {
221
            //$this->loggerUtil->log(sprintf('consume, cid: %d, amount: %d', $commodityKey, $amount));
222
223
            if ($amount < 1) {
224
                continue;
225
            }
226
227
            $storage = $entity->getStorage()->get($commodityKey);
228
            if ($storage === null) {
229
                throw new RuntimeException('enoughSparePartsOnEntity should be called beforehand!');
230
            }
231
            $commodity = $storage->getCommodity();
232
233
            if ($entity instanceof ColonyInterface) {
234
                $this->colonyStorageManager->lowerStorage($entity, $commodity, $amount);
235
            } else {
236
                $this->shipStorageManager->lowerStorage($entity, $commodity, $amount);
237
            }
238
        }
239
    }
240
241
242
    //SELFREPAIR STUFF
243
244
    public function determineFreeEngineerCount(ShipInterface $ship): int
245
    {
246
        $engineerCount = 0;
247
248
        $engineerOptions = [];
249
        $nextNumber = 1;
250
        foreach ($ship->getCrewAssignments() as $shipCrew) {
251
            if (
252
                $shipCrew->getSlot() === CrewEnum::CREW_TYPE_TECHNICAL
253
                //&& $shipCrew->getRepairTask() === null
254
            ) {
255
                $engineerOptions[] = $nextNumber;
256
                $nextNumber++;
257
                $engineerCount++;
258
            }
259
        }
260
261
        return $engineerCount; //$engineerOptions;
262
    }
263
264
    public function determineRepairOptions(ShipWrapperInterface $wrapper): array
265
    {
266
        $repairOptions = [];
267
268
        $ship = $wrapper->get();
269
270
        //check for hull option
271
        $hullPercentage = (int) ($ship->getHull() * 100 / $ship->getMaxHull());
272
        if ($hullPercentage < RepairTaskEnum::BOTH_MAX) {
273
            $hullSystem = $this->shipSystemRepository->prototype();
274
            $hullSystem->setSystemType(ShipSystemTypeEnum::SYSTEM_HULL);
275
            $hullSystem->setStatus($hullPercentage);
276
277
            $repairOptions[ShipSystemTypeEnum::SYSTEM_HULL->value] = $hullSystem;
278
        }
279
280
        //check for system options
281
        foreach ($wrapper->getDamagedSystems() as $system) {
282
            if ($system->getStatus() < RepairTaskEnum::BOTH_MAX) {
283
                $repairOptions[$system->getSystemType()->value] = $system;
284
            }
285
        }
286
287
        return $repairOptions;
288
    }
289
290
    public function createRepairTask(ShipInterface $ship, ShipSystemTypeEnum $systemType, int $repairType, int $finishTime): void
291
    {
292
        $obj = $this->repairTaskRepository->prototype();
293
294
        $obj->setUser($ship->getUser());
295
        $obj->setShip($ship);
296
        $obj->setSystemType($systemType);
297
        $obj->setHealingPercentage($this->determineHealingPercentage($repairType));
298
        $obj->setFinishTime($finishTime);
299
300
        $this->repairTaskRepository->save($obj);
301
    }
302
303
    public function determineHealingPercentage(int $repairType): int
304
    {
305
        $percentage = 0;
306
307
        if ($repairType === RepairTaskEnum::SPARE_PARTS_ONLY) {
308
            $percentage += random_int(RepairTaskEnum::SPARE_PARTS_ONLY_MIN, RepairTaskEnum::SPARE_PARTS_ONLY_MAX);
309
        } elseif ($repairType === RepairTaskEnum::SYSTEM_COMPONENTS_ONLY) {
310
            $percentage += random_int(RepairTaskEnum::SYSTEM_COMPONENTS_ONLY_MIN, RepairTaskEnum::SYSTEM_COMPONENTS_ONLY_MAX);
311
        } elseif ($repairType === RepairTaskEnum::BOTH) {
312
            $percentage += random_int(RepairTaskEnum::BOTH_MIN, RepairTaskEnum::BOTH_MAX);
313
        }
314
315
        return $percentage;
316
    }
317
318
    public function instantSelfRepair(ShipInterface $ship, ShipSystemTypeEnum $systemType, int $healingPercentage): bool
319
    {
320
        return $this->internalSelfRepair(
321
            $ship,
322
            $systemType,
323
            $healingPercentage
324
        );
325
    }
326
327
    public function selfRepair(ShipInterface $ship, RepairTaskInterface $repairTask): bool
328
    {
329
        $systemType = $repairTask->getSystemType();
330
        $percentage = $repairTask->getHealingPercentage();
331
332
        $this->repairTaskRepository->delete($repairTask);
333
334
        return $this->internalSelfRepair($ship, $systemType, $percentage);
335
    }
336
337
    private function internalSelfRepair(ShipInterface $ship, ShipSystemTypeEnum $systemType, int $percentage): bool
338
    {
339
        $result = true;
340
341
        if ($systemType === ShipSystemTypeEnum::SYSTEM_HULL) {
342
            $hullPercentage = (int) ($ship->getHull() * 100 / $ship->getMaxHull());
343
344
            if ($hullPercentage > $percentage) {
345
                $result = false;
346
            } else {
347
                $ship->setHuell((int)($ship->getMaxHull() * $percentage / 100));
348
            }
349
        } else {
350
            $system = $ship->getShipSystem($systemType);
351
352
            if ($system->getStatus() > $percentage) {
353
                $result = false;
354
            } else {
355
                $system->setStatus($percentage);
356
                $this->shipSystemRepository->save($system);
357
            }
358
        }
359
360
        $ship->setState(ShipStateEnum::SHIP_STATE_NONE);
361
362
        return $result;
363
    }
364
365
    public function isRepairStationBonus(ShipWrapperInterface $wrapper): bool
366
    {
367
        $ship = $wrapper->get();
368
369
        $colony = $ship->isOverColony();
370
        if ($colony === null) {
371
            return false;
372
        }
373
374
        return $this->colonyFunctionManager->hasActiveFunction($colony, BuildingEnum::BUILDING_FUNCTION_REPAIR_SHIPYARD);
375
    }
376
377 5
    public function getRepairDuration(ShipWrapperInterface $wrapper): int
378
    {
379 5
        $ship = $wrapper->get();
380 5
        $ticks = $this->getRepairTicks($wrapper);
381
382
        //check if repair station is active
383 5
        $colonyRepair = $this->colonyShipRepairRepository->getByShip($ship->getId());
384 5
        if ($colonyRepair !== null) {
385 2
            $isRepairStationBonus = $this->colonyFunctionManager->hasActiveFunction($colonyRepair->getColony(), BuildingEnum::BUILDING_FUNCTION_REPAIR_SHIPYARD);
386 2
            if ($isRepairStationBonus) {
387 1
                $ticks = (int)ceil($ticks / 2);
388
            }
389
        }
390
391 5
        return $ticks;
392
    }
393
394 3
    public function getRepairDurationPreview(ShipWrapperInterface $wrapper): int
395
    {
396 3
        $ship = $wrapper->get();
397 3
        $ticks = $this->getRepairTicks($wrapper);
398
399 3
        $colony = $ship->isOverColony();
400 3
        if ($colony !== null) {
401 2
            $isRepairStationBonus = $this->colonyFunctionManager->hasActiveFunction($colony, BuildingEnum::BUILDING_FUNCTION_REPAIR_SHIPYARD);
402 2
            if ($isRepairStationBonus) {
403 1
                $ticks = (int)ceil($ticks / 2);
404
            }
405
        }
406
407 3
        return $ticks;
408
    }
409
410 8
    private function getRepairTicks(ShipWrapperInterface $wrapper): int
411
    {
412 8
        $ship = $wrapper->get();
413 8
        $ticks = (int) ceil(($ship->getMaxHull() - $ship->getHull()) / $ship->getRepairRate());
414
415 8
        return max($ticks, (int) ceil(count($wrapper->getDamagedSystems()) / 2));
416
    }
417
}
418