Passed
Pull Request — master (#2146)
by Nico
36:21 queued 25:53
created

PirateCreation::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 17
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 14
dl 0
loc 17
ccs 2
cts 2
cp 1
crap 1
rs 10
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\Lib\Pirate;
4
5
use Doctrine\ORM\EntityManagerInterface;
6
use Override;
0 ignored issues
show
Bug introduced by
The type Override was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
7
use RuntimeException;
8
use Stu\Component\Map\MapEnum;
0 ignored issues
show
Bug introduced by
The type Stu\Component\Map\MapEnum was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
9
use Stu\Component\Spacecraft\SpacecraftAlertStateEnum;
10
use Stu\Lib\Map\FieldTypeEffectEnum;
11
use Stu\Module\Control\GameControllerInterface;
12
use Stu\Module\Control\StuRandom;
13
use Stu\Module\Logging\LoggerUtilFactoryInterface;
14
use Stu\Module\Logging\PirateLoggerInterface;
15
use Stu\Module\PlayerSetting\Lib\UserEnum;
0 ignored issues
show
Bug introduced by
The type Stu\Module\PlayerSetting\Lib\UserEnum was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
16
use Stu\Module\Ship\Lib\ShipCreatorInterface;
17
use Stu\Orm\Entity\FleetInterface;
18
use Stu\Orm\Entity\MapInterface;
19
use Stu\Orm\Entity\PirateSetupInterface;
20
use Stu\Orm\Entity\PirateRoundInterface;
21
use Stu\Orm\Entity\ShipInterface;
22
use Stu\Orm\Repository\FleetRepositoryInterface;
23
use Stu\Orm\Repository\GameTurnRepositoryInterface;
24
use Stu\Orm\Repository\LayerRepositoryInterface;
25
use Stu\Orm\Repository\MapRepositoryInterface;
26
use Stu\Orm\Repository\NamesRepositoryInterface;
27
use Stu\Orm\Repository\PirateRoundRepositoryInterface;
28
use Stu\Orm\Repository\PirateSetupRepositoryInterface;
29
use Stu\Orm\Repository\ShipRepositoryInterface;
30
use Stu\Orm\Repository\UserRepositoryInterface;
31
32
class PirateCreation implements PirateCreationInterface
33
{
34
    public const MAX_PIRATE_FLEETS = 10;
35
    public const MAX_PIRATE_FLEETS_PER_TICK = 5;
36
    public const MAX_PIRATE_FLEETS_PER_10MIN = 3;
37
38
    public const FORBIDDEN_ADMIN_AREAS = [
39
        MapEnum::ADMIN_REGION_SUPERPOWER_CENTRAL,
40
        MapEnum::ADMIN_REGION_SUPERPOWER_PERIPHERAL
41
    ];
42
43
    private PirateLoggerInterface $logger;
44
45 1
    public function __construct(
46
        private FleetRepositoryInterface $fleetRepository,
47
        private ShipRepositoryInterface $shipRepository,
48
        private UserRepositoryInterface $userRepository,
49
        private PirateSetupRepositoryInterface $pirateSetupRepository,
50
        private PirateRoundRepositoryInterface $pirateRoundRepository,
51
        private GameTurnRepositoryInterface $gameTurnRepository,
52
        private ShipCreatorInterface $shipCreator,
53
        private LayerRepositoryInterface $layerRepository,
54
        private MapRepositoryInterface $mapRepository,
55
        private GameControllerInterface $game,
56
        private StuRandom $stuRandom,
57
        private EntityManagerInterface $entityManager,
58
        private NamesRepositoryInterface $namesRepository,
59
        LoggerUtilFactoryInterface $loggerUtilFactory
60
    ) {
61 1
        $this->logger = $loggerUtilFactory->getPirateLogger();
62
    }
63
64
    #[Override]
65
    public function createPirateFleetsIfNeeded(): array
66
    {
67
        $currentRound = $this->pirateRoundRepository->getCurrentActiveRound();
68
69
        if ($currentRound === null) {
70
            $this->logger->log('    Keine aktive Piratenrunde - keine neuen Flotten');
71
            return [];
72
        }
73
74
        if ($currentRound->getActualPrestige() <= 0) {
75
            $this->logger->log('    Prestige auf 0 - keine neuen Piratenflotten');
76
            return [];
77
        }
78
79
        $gameTurn = $this->game->getCurrentRound();
80
        $pirateFleets = $this->fleetRepository->getByUser(UserEnum::USER_NPC_KAZON);
81
        $currentFleetCount = count($pirateFleets);
82
83
        $dynamicLimits = $this->calculateDynamicLimits($currentRound);
84
85
        $this->logger->logf(
86
            '    Dynamische Limits: Max=%d, PerTick=%d, Per10Min=%d',
87
            $dynamicLimits['maxFleets'],
88
            $dynamicLimits['maxPerTick'],
89
            $dynamicLimits['maxPer10Min']
90
        );
91
92
        $missingFleetAmount = min(
93
            max(0, $dynamicLimits['maxPerTick'] - $gameTurn->getPirateFleets()),
94
            max(0, $dynamicLimits['maxFleets'] - $currentFleetCount)
95
        );
96
97
        if ($missingFleetAmount <= 0) {
98
            $this->logger->logf(
99
                '    Tick-Limit erreicht (%d/%d) oder max. Flotten erreicht (%d/%d)',
100
                $gameTurn->getPirateFleets(),
101
                $dynamicLimits['maxPerTick'],
102
                $currentFleetCount,
103
                $dynamicLimits['maxFleets']
104
            );
105
            return $pirateFleets;
106
        }
107
108
        $spawnProbability = $this->calculateSpawnProbability($currentRound);
109
110
        $this->logger->logf(
111
            '    Spawn-Wahrscheinlichkeit: %.2f%% (Prestige: %d/%d)',
112
            $spawnProbability * 100,
113
            $currentRound->getActualPrestige(),
114
            $currentRound->getMaxPrestige()
115
        );
116
117
        $randomValue = $this->stuRandom->rand(1, 100);
118
        $spawnThreshold = $spawnProbability * 100;
119
120
        if ($randomValue > $spawnThreshold) {
121
            $this->logger->logf(
122
                '    Spawn-Wahrscheinlichkeit nicht erreicht (Random: %d, Threshold: %.2f)',
123
                $randomValue,
124
                $spawnThreshold
125
            );
126
            return $pirateFleets;
127
        }
128
129
130
        /** @var int<0, max> $maxNewFleets */
131
        $maxNewFleets = min($dynamicLimits['maxPer10Min'], $missingFleetAmount);
132
133
        if ($maxNewFleets <= 0) {
134
            return $pirateFleets;
135
        }
136
137
        $fleetsToSpawn = $this->stuRandom->rand(1, $maxNewFleets);
138
139
        $this->logger->logf('    Spawne %d neue Piratenflotten', $fleetsToSpawn);
140
141
        for ($i = 0; $i < $fleetsToSpawn; $i++) {
142
            $this->logger->logf('  Flotte Nr. %d', $i + 1);
143
            $pirateFleets[] = $this->createPirateFleet();
144
            $gameTurn->setPirateFleets($gameTurn->getPirateFleets() + 1);
145
        }
146
147
        $this->gameTurnRepository->save($gameTurn);
148
149
        return $pirateFleets;
150
    }
151
152
    /**
153
     * 
154
     * @return array{maxFleets: int, maxPerTick: int, maxPer10Min: int}
155
     */
156
    private function calculateDynamicLimits(PirateRoundInterface $currentRound): array
157
    {
158
        $maxPrestige = $currentRound->getMaxPrestige();
159
        $actualPrestige = $currentRound->getActualPrestige();
160
161
        if ($maxPrestige <= 0) {
162
            return [
163
                'maxFleets' => 1,
164
                'maxPerTick' => 1,
165
                'maxPer10Min' => 1
166
            ];
167
        }
168
169
        $consumedRatio = 1.0 - ($actualPrestige / $maxPrestige);
170
        $scalingFactor = $this->calculateLimitScalingFactor($consumedRatio);
171
172
        $this->logger->logf(
173
            '    Prestige-Verbrauch: %.1f%%, Skalierungsfaktor: %.2f',
174
            $consumedRatio * 100,
175
            $scalingFactor
176
        );
177
178
        return [
179
            'maxFleets' => max(1, (int) round(self::MAX_PIRATE_FLEETS * $scalingFactor)),
180
            'maxPerTick' => max(1, (int) round(self::MAX_PIRATE_FLEETS_PER_TICK * $scalingFactor)),
181
            'maxPer10Min' => max(1, (int) round(self::MAX_PIRATE_FLEETS_PER_10MIN * $scalingFactor))
182
        ];
183
    }
184
185
    private function calculateLimitScalingFactor(float $consumedRatio): float
186
    {
187
        if ($consumedRatio <= 0.2) {
188
            return 0.1 + (0.9 * ($consumedRatio / 0.2));
189
        } elseif ($consumedRatio <= 0.7) {
190
            return 1.0;
191
        } else {
192
            $remainingRatio = (1.0 - $consumedRatio) / 0.3;
193
            return 0.25 + (0.75 * $remainingRatio);
194
        }
195
    }
196
197
    private function calculateSpawnProbability(PirateRoundInterface $currentRound): float
198
    {
199
        $maxPrestige = $currentRound->getMaxPrestige();
200
        $actualPrestige = $currentRound->getActualPrestige();
201
202
        if ($maxPrestige <= 0) {
203
            return 0.1;
204
        }
205
206
        $consumedRatio = 1.0 - ($actualPrestige / $maxPrestige);
207
208
        if ($consumedRatio <= 0.2) {
209
            return 0.15 + (0.55 * ($consumedRatio / 0.2));
210
        } elseif ($consumedRatio <= 0.7) {
211
            return 0.85;
212
        } else {
213
            $remainingRatio = (1.0 - $consumedRatio) / 0.3;
214
            return 0.25 + (0.6 * $remainingRatio);
215
        }
216
    }
217
218
    #[Override]
219
    public function createPirateFleet(?ShipInterface $supportCaller = null): FleetInterface
220
    {
221
        $pirateUser = $this->userRepository->find(UserEnum::USER_NPC_KAZON);
222
        if ($pirateUser === null) {
223
            throw new RuntimeException('this should not happen');
224
        }
225
226
        $pirateSetup = $this->getRandomPirateSetup();
227
228
        //create ships
229
        $ships = $this->createShips($pirateSetup, $supportCaller);
230
        $this->entityManager->flush();
231
232
        $fleetLeader = $ships[array_rand($ships)];
233
        $fleetLeader->setIsFleetLeader(true);
234
235
        //create fleet
236
        $fleet = $this->fleetRepository->prototype();
237
        $fleet->setUser($pirateUser);
238
        $fleet->setName($pirateSetup->getName());
239
        $fleet->setIsFleetFixed(true);
240
        $fleet->setLeadShip($fleetLeader);
241
242
        $this->logger->log(sprintf('    shipCount: %d', count($ships)));
243
244
        foreach ($ships as $ship) {
245
            $fleet->getShips()->add($ship);
246
            $ship->setFleet($fleet);
247
            $this->shipRepository->save($ship);
248
        }
249
250
        $this->fleetRepository->save($fleet);
251
        $this->entityManager->flush();
252
253
        return $fleet;
254
    }
255
256
    /** @return array<ShipInterface> */
257
    private function createShips(PirateSetupInterface $pirateSetup, ?ShipInterface $supportCaller): array
258
    {
259
        $randomLocation = $supportCaller === null ? $this->getRandomMapLocation() : $supportCaller->getLocation();
260
261
        $randomAlertLevel = SpacecraftAlertStateEnum::getRandomAlertLevel();
262
263
        $this->logger->log(sprintf('    randomAlertLevel: %d', $randomAlertLevel->value));
264
265
        $result = [];
266
267
        foreach ($pirateSetup->getSetupBuildplans() as $setupBuildplan) {
268
269
            $buildplan = $setupBuildplan->getBuildplan();
270
            $rump = $buildplan->getRump();
271
272
            for ($i = 0; $i < $setupBuildplan->getAmount(); $i++) {
273
274
                $mostUnusedNames = $this->namesRepository->mostUnusedNames();
275
                if ($mostUnusedNames !== []) {
276
                    $selectedNameEntry = $mostUnusedNames[array_rand($mostUnusedNames)];
277
                    $shipName = $selectedNameEntry->getName();
278
                    $selectedNameEntry->setCount($selectedNameEntry->getCount() + 1);
279
                    $this->namesRepository->save($selectedNameEntry);
280
                } else {
281
                    $shipName = "Pirate Ship";
282
                }
283
284
285
                $result[] = $this->shipCreator
286
                    ->createBy(
287
                        UserEnum::USER_NPC_KAZON,
288
                        $rump->getId(),
289
                        $buildplan->getId()
290
                    )
291
                    ->setLocation($randomLocation)
292
                    ->maxOutSystems()
293
                    ->createCrew()
294
                    ->setTorpedo()
295
                    ->setSpacecraftName($shipName)
296
                    ->setAlertState($randomAlertLevel)
297
                    ->finishConfiguration()
298
                    ->get();
299
            }
300
        }
301
302
        return $result;
303
    }
304
305
    private function getRandomPirateSetup(): PirateSetupInterface
306
    {
307
        $pirateSetups = $this->pirateSetupRepository->findAll();
308
309
        $pirateProbabilities = array_map(fn(PirateSetupInterface $setup): int => $setup->getProbabilityWeight(), $pirateSetups);
310
311
        return $pirateSetups[$this->stuRandom->randomKeyOfProbabilities($pirateProbabilities)];
312
    }
313
314
    private function getRandomMapLocation(): MapInterface
315
    {
316
        $defaultLayer = $this->layerRepository->getDefaultLayer();
317
318
        do {
319
            $map = $this->mapRepository->getRandomPassableUnoccupiedWithoutDamage($defaultLayer);
320
        } while (
321
            in_array($map->getAdminRegionId(), self::FORBIDDEN_ADMIN_AREAS)
322
            || $map->getFieldType()->hasEffect(FieldTypeEffectEnum::NO_PIRATES)
323
        );
324
325
        $this->logger->log(sprintf('    randomMapLocation: %s', $map->getSectorString()));
326
327
        return $map;
328
    }
329
}
330