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