Passed
Pull Request — master (#1777)
by Nico
22:32 queued 18s
created

RepairUtil::instantSelfRepair()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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