Passed
Pull Request — master (#1846)
by Nico
32:16
created

ShipRepository::getShipsForAlertRed()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 39
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 34
nc 1
nop 1
dl 0
loc 39
ccs 0
cts 19
cp 0
crap 2
rs 9.376
c 0
b 0
f 0

1 Method

Rating   Name   Duplication   Size   Complexity  
A ShipRepository::getByUserAndFleetAndType() 0 10 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Stu\Orm\Repository;
6
7
use Doctrine\ORM\EntityRepository;
8
use Doctrine\ORM\Query\ResultSetMapping;
9
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...
10
use Stu\Component\Anomaly\Type\AnomalyTypeEnum;
11
use Stu\Component\Game\TimeConstants;
0 ignored issues
show
Bug introduced by
The type Stu\Component\Game\TimeConstants 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...
12
use Stu\Component\Ship\ShipAlertStateEnum;
13
use Stu\Component\Ship\ShipRumpEnum;
0 ignored issues
show
Bug introduced by
The type Stu\Component\Ship\ShipRumpEnum 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...
14
use Stu\Component\Ship\ShipStateEnum;
15
use Stu\Component\Ship\SpacecraftTypeEnum;
16
use Stu\Component\Ship\System\ShipSystemModeEnum;
0 ignored issues
show
Bug introduced by
The type Stu\Component\Ship\System\ShipSystemModeEnum 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...
17
use Stu\Component\Ship\System\ShipSystemTypeEnum;
18
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...
19
use Stu\Module\Ship\Lib\ShipRumpSpecialAbilityEnum;
0 ignored issues
show
Bug introduced by
The type Stu\Module\Ship\Lib\ShipRumpSpecialAbilityEnum 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...
20
use Stu\Module\Ship\Lib\TFleetShipItem;
21
use Stu\Module\Ship\Lib\TShipItem;
22
use Stu\Orm\Entity\Anomaly;
23
use Stu\Orm\Entity\Crew;
24
use Stu\Orm\Entity\Fleet;
25
use Stu\Orm\Entity\Location;
26
use Stu\Orm\Entity\LocationInterface;
27
use Stu\Orm\Entity\Map;
28
use Stu\Orm\Entity\MapInterface;
29
use Stu\Orm\Entity\PirateWrath;
30
use Stu\Orm\Entity\Ship;
31
use Stu\Orm\Entity\ShipBuildplan;
32
use Stu\Orm\Entity\ShipCrew;
33
use Stu\Orm\Entity\ShipInterface;
34
use Stu\Orm\Entity\ShipRump;
35
use Stu\Orm\Entity\ShipRumpSpecial;
36
use Stu\Orm\Entity\ShipSystem;
37
use Stu\Orm\Entity\StarSystemMap;
38
use Stu\Orm\Entity\StarSystemMapInterface;
39
use Stu\Orm\Entity\Storage;
40
use Stu\Orm\Entity\User;
41
use Stu\Orm\Entity\UserInterface;
42
43
/**
44
 * @extends EntityRepository<Ship>
45
 */
46
final class ShipRepository extends EntityRepository implements ShipRepositoryInterface
47
{
48
    #[Override]
49
    public function prototype(): ShipInterface
50
    {
51
        return new Ship();
52
    }
53
54
    #[Override]
55
    public function save(ShipInterface $post): void
56
    {
57
        $em = $this->getEntityManager();
58
59
        $em->persist($post);
60
    }
61
62
    #[Override]
63
    public function delete(ShipInterface $post): void
64
    {
65
        $em = $this->getEntityManager();
66
67
        $em->remove($post);
68
    }
69
70
    #[Override]
71
    public function getAmountByUserAndSpecialAbility(
72
        int $userId,
73
        int $specialAbilityId
74
    ): int {
75
        return (int) $this->getEntityManager()->createQuery(
76
            sprintf(
77
                'SELECT COUNT(s)
78
                FROM %s s
79
                JOIN %s bp
80
                WITH s.plans_id = bp.id
81
                WHERE s.user_id = :userId AND s.rumps_id IN (
82
                    SELECT rs.rumps_id FROM %s rs WHERE rs.special = :specialAbilityId
83
                )
84
                %s',
85
                Ship::class,
86
                ShipBuildplan::class,
87
                ShipRumpSpecial::class,
88
                $specialAbilityId === ShipRumpSpecialAbilityEnum::COLONIZE ? 'AND bp.crew = 0' : ''
89
            )
90
        )->setParameters([
91
            'userId' => $userId,
92
            'specialAbilityId' => $specialAbilityId,
93
        ])->getSingleScalarResult();
94
    }
95
96
    #[Override]
97
    public function getAmountByUserAndRump(int $userId, int $shipRumpId): int
98
    {
99
        return $this->count([
100
            'user_id' => $userId,
101
            'rumps_id' => $shipRumpId,
102
        ]);
103
    }
104
105
    #[Override]
106
    public function getByUser(UserInterface $user): iterable
107
    {
108
        return $this->findBy([
109
            'user_id' => $user,
110
        ]);
111
    }
112
113
    #[Override]
114
    public function getByUserAndRump(int $userId, int $rumpId): array
115
    {
116
        return $this->findBy([
117
            'user_id' => $userId,
118
            'rumps_id' => $rumpId
119
        ], [
120
            'location_id' => 'asc',
121
            'fleets_id' => 'asc',
122
            'is_fleet_leader' => 'desc'
123
        ]);
124
    }
125
126
    #[Override]
127
    public function getPossibleFleetMembers(ShipInterface $fleetLeader): iterable
128
    {
129
        return $this->getEntityManager()->createQuery(
130
            sprintf(
131
                'SELECT s FROM %s s
132
                WHERE s.location = :location
133
                AND s.fleets_id IS NULL
134
                AND s.user_id = :userId
135
                AND s.type = :type
136
                ORDER BY s.rumps_id ASC, s.name ASC',
137
                Ship::class
138
            )
139
        )->setParameters([
140
            'userId' => $fleetLeader->getUser()->getId(),
141
            'type' => SpacecraftTypeEnum::SPACECRAFT_TYPE_SHIP->value,
142
            'location' => $fleetLeader->getLocation(),
143
        ])->getResult();
144
    }
145
146
    #[Override]
147
    public function getByLocationAndUser(LocationInterface $location, UserInterface $user): array
148
    {
149
        return $this->findBy([
150
            'type' => SpacecraftTypeEnum::SPACECRAFT_TYPE_SHIP,
151
            'user' => $user,
152
            'location' => $location,
153
        ], [
154
            'fleets_id' => 'desc',
155
            'is_fleet_leader' => 'desc',
156
            'id' => 'desc'
157
        ]);
158
    }
159
160
    #[Override]
161
    public function getByLocation(LocationInterface $location): array
162
    {
163
        return $this->getEntityManager()
164
            ->createQuery(
165
                sprintf(
166
                    'SELECT s FROM %s s
167
                    LEFT JOIN %s f
168
                    WITH s.fleets_id = f.id
169
                    JOIN %s r
170
                    WITH s.rumps_id = r.id
171
                    WHERE s.location = :location
172
                    AND NOT EXISTS (SELECT ss.id
173
                                        FROM %s ss
174
                                        WHERE s.id = ss.ship_id
175
                                        AND ss.system_type = :systemId
176
                                        AND ss.mode > 1)
177
                    ORDER BY s.is_destroyed ASC, f.sort DESC, f.id DESC, s.is_fleet_leader DESC,
178
                    r.category_id ASC, r.role_id ASC, r.id ASC, s.name ASC',
179
                    Ship::class,
180
                    Fleet::class,
181
                    ShipRump::class,
182
                    ShipSystem::class
183
                )
184
            )
185
            ->setParameters([
186
                'location' => $location,
187
                'systemId' => ShipSystemTypeEnum::SYSTEM_CLOAK->value
188
            ])
189
            ->getResult();
190
    }
191
192
    #[Override]
193
    public function getForeignStationsInBroadcastRange(ShipInterface $ship): array
194
    {
195
        $layer = $ship->getLayer();
196
        $systemMap = $ship->getStarsystemMap();
197
        $map = $ship->getMap();
198
199
        return $this->getEntityManager()
200
            ->createQuery(
201
                sprintf(
202
                    'SELECT s FROM %s s
203
                     LEFT JOIN %s m
204
                     WITH s.location_id = m.id
205
                     LEFT JOIN %s l
206
                     WITH m.id = l.id
207
                     LEFT JOIN %s sm
208
                     WITH s.location_id = sm.id
209
                     WHERE s.user_id NOT IN (:ignoreIds)
210
                     AND s.type = :spacecraftType
211
                     AND (:layerId = 0 OR (l.layer_id = :layerId
212
                        AND l.cx BETWEEN (:cx - 1) AND (:cx + 1)
213
                        AND l.cy BETWEEN (:cy - 1) AND (:cy + 1)))
214
                     AND (:systemId = 0 OR (sm.systems_id = :systemId
215
                        AND sm.sx BETWEEN (:sx - 1) AND (:sx + 1)
216
                        AND sm.sy BETWEEN (:sy - 1) AND (:sy + 1)))',
217
                    Ship::class,
218
                    Map::class,
219
                    Location::class,
220
                    StarSystemMap::class
221
                )
222
            )
223
            ->setParameters([
224
                'ignoreIds' => [$ship->getUser()->getId(), UserEnum::USER_NOONE],
225
                'spacecraftType' => SpacecraftTypeEnum::SPACECRAFT_TYPE_STATION->value,
226
                'systemId' => $systemMap === null ? 0 : $systemMap->getSystem()->getId(),
227
                'sx' => $systemMap === null ? 0 : $systemMap->getSx(),
228
                'sy' => $systemMap === null ? 0 : $systemMap->getSy(),
229
                'layerId' => ($systemMap !== null || $layer === null) ? 0 : $layer->getId(),
230
                'cx' => ($systemMap !== null || $map === null) ? 0 : $map->getCx(),
231
                'cy' => ($systemMap !== null || $map === null) ? 0 : $map->getCy()
232
            ])
233
            ->getResult();
234
    }
235
236
    #[Override]
237
    public function getTradePostsWithoutDatabaseEntry(): iterable
238
    {
239
        return $this->getEntityManager()->createQuery(
240
            sprintf(
241
                'SELECT s FROM %s s WHERE s.database_id is null AND s.trade_post_id > 0',
242
                Ship::class
243
            )
244
        )->getResult();
245
    }
246
247
    #[Override]
248
    public function getByUserAndFleetAndType(int $userId, ?int $fleetId, SpacecraftTypeEnum $type): array
249
    {
250
        return $this->findBy(
251
            [
252
                'user_id' => $userId,
253
                'fleets_id' => $fleetId,
254
                'type' => $type->value,
255
            ],
256
            $type === SpacecraftTypeEnum::SPACECRAFT_TYPE_STATION ? ['max_huelle' => 'desc', 'id' => 'asc'] : ['id' => 'asc']
257
        );
258
    }
259
260
    #[Override]
261
    public function getByUplink(int $userId): array
262
    {
263
        return $this->getEntityManager()->createQuery(
264
            sprintf(
265
                'SELECT s FROM %s s
266
                JOIN %s sc
267
                WITH s.id = sc.ship_id
268
                JOIN %s c
269
                WITH sc.crew_id = c.id
270
                JOIN %s ss
271
                WITH ss.ship_id = s.id
272
                JOIN %s u
273
                WITH s.user_id = u.id
274
                WHERE s.user_id != :userId
275
                AND c.user_id = :userId
276
                AND ss.system_type = :systemType
277
                AND ss.mode >= :mode
278
                AND (u.vac_active = false OR u.vac_request_date > :vacationThreshold)',
279
                Ship::class,
280
                ShipCrew::class,
281
                Crew::class,
282
                ShipSystem::class,
283
                User::class
284
            )
285
        )->setParameters([
286
            'userId' => $userId,
287
            'systemType' => ShipSystemTypeEnum::SYSTEM_UPLINK->value,
288
            'mode' => ShipSystemModeEnum::MODE_ON,
289
            'vacationThreshold' => time() - UserEnum::VACATION_DELAY_IN_SECONDS
290
        ])
291
            ->getResult();
292
    }
293
294
    #[Override]
295
    public function getWithTradeLicensePayment(
296
        int $userId,
297
        int $tradePostShipId,
298
        int $commodityId,
299
        int $amount
300
    ): iterable {
301
        return $this->getEntityManager()->createQuery(
302
            sprintf(
303
                'SELECT s FROM %s s WHERE s.user_id = :userId AND s.dock = :tradePostShipId AND s.id IN (
304
                    SELECT st.ship_id FROM %s st WHERE st.commodity_id = :commodityId AND st.count >= :amount
305
                )',
306
                Ship::class,
307
                Storage::class
308
            )
309
        )->setParameters([
310
            'userId' => $userId,
311
            'tradePostShipId' => $tradePostShipId,
312
            'commodityId' => $commodityId,
313
            'amount' => $amount,
314
        ])->getResult();
315
    }
316
317
    #[Override]
318
    public function getSuitableForShildRegeneration(int $regenerationThreshold): iterable
319
    {
320
        return $this->getEntityManager()->createQuery(
321
            sprintf(
322
                'SELECT s FROM %s s
323
                JOIN %s ss
324
                WITH s.id = ss.ship_id
325
                JOIN %s bp
326
                WITH s.plans_id = bp.id
327
                WHERE ss.system_type = :shieldType
328
                AND ss.mode < :modeOn
329
                AND s.is_destroyed = :destroyedState
330
                AND s.schilde<s.max_schilde
331
                AND s.shield_regeneration_timer <= :regenerationThreshold
332
                AND (SELECT count(sc.id) FROM %s sc WHERE s.id = sc.ship_id) >= bp.crew
333
                AND NOT EXISTS (SELECT a FROM %s a
334
                                WHERE a.location_id = s.location_id
335
                                AND a.anomaly_type_id = :anomalyType
336
                                AND a.remaining_ticks > 0)',
337
                Ship::class,
338
                ShipSystem::class,
339
                ShipBuildplan::class,
340
                ShipCrew::class,
341
                Anomaly::class
342
            )
343
        )->setParameters([
344
            'shieldType' => ShipSystemTypeEnum::SYSTEM_SHIELDS->value,
345
            'modeOn' => ShipSystemModeEnum::MODE_ON,
346
            'regenerationThreshold' => $regenerationThreshold,
347
            'destroyedState' => 0,
348
            'anomalyType' => AnomalyTypeEnum::SUBSPACE_ELLIPSE
349
        ])->getResult();
350
    }
351
352
    #[Override]
353
    public function getEscapePods(): iterable
354
    {
355
        return $this->getEntityManager()->createQuery(
356
            sprintf(
357
                'SELECT s FROM %s s
358
                LEFT JOIN %s sr
359
                WITH s.rumps_id = sr.id
360
                WHERE sr.category_id = :categoryId',
361
                Ship::class,
362
                ShipRump::class
363
            )
364
        )->setParameters([
365
            'categoryId' => ShipRumpEnum::SHIP_CATEGORY_ESCAPE_PODS
366
        ])->getResult();
367
    }
368
369
    #[Override]
370
    public function getEscapePodsByCrewOwner(int $userId): iterable
371
    {
372
        return $this->getEntityManager()->createQuery(
373
            sprintf(
374
                'SELECT s FROM %s s
375
                LEFT JOIN %s sr
376
                WITH s.rumps_id = sr.id
377
                LEFT JOIN %s sc
378
                WITH sc.ship_id = s.id
379
                WHERE sr.category_id = :categoryId
380
                AND sc.user_id = :userId',
381
                Ship::class,
382
                ShipRump::class,
383
                ShipCrew::class
384
            )
385
        )->setParameters([
386
            'categoryId' => ShipRumpEnum::SHIP_CATEGORY_ESCAPE_PODS,
387
            'userId' => $userId
388
        ])->getResult();
389
    }
390
391
    #[Override]
392
    public function getDebrisFields(): iterable
393
    {
394
        return $this->findBy([
395
            'is_destroyed' => true,
396
        ]);
397
    }
398
399
    #[Override]
400
    public function getStationConstructions(): iterable
401
    {
402
        return $this->getEntityManager()->createQuery(
403
            sprintf(
404
                'SELECT s FROM %s s
405
                JOIN %s r
406
                WITH s.rumps_id = r.id
407
                WHERE s.user_id > :firstUserId
408
                AND r.category_id = :catId',
409
                Ship::class,
410
                ShipRump::class
411
            )
412
        )->setParameters([
413
            'catId' => ShipRumpEnum::SHIP_CATEGORY_CONSTRUCTION,
414
            'firstUserId' => UserEnum::USER_FIRST_ID
415
        ])
416
            ->getResult();
417
    }
418
419
    #[Override]
420
    public function getPlayerShipsForTick(): iterable
421
    {
422
        return $this->getEntityManager()->createQuery(
423
            sprintf(
424
                'SELECT s
425
                FROM %s s
426
                JOIN %s p
427
                WITH s.plans_id = p.id
428
                JOIN %s u
429
                WITH s.user_id = u.id
430
                WHERE s.user_id > :firstUserId
431
                AND (   ((SELECT count(sc.id)
432
                        FROM %s sc
433
                        WHERE sc.ship_id = s.id) > 0)
434
                    OR
435
                        (s.state IN (:scrapping, :underConstruction))
436
                    OR
437
                        (p.crew = 0))
438
                AND (u.vac_active = false OR u.vac_request_date > :vacationThreshold)',
439
                Ship::class,
440
                ShipBuildplan::class,
441
                User::class,
442
                ShipCrew::class
443
            )
444
        )->setParameters([
445
            'underConstruction' => ShipStateEnum::SHIP_STATE_UNDER_CONSTRUCTION,
446
            'scrapping' => ShipStateEnum::SHIP_STATE_UNDER_SCRAPPING,
447
            'vacationThreshold' => time() - UserEnum::VACATION_DELAY_IN_SECONDS,
448
            'firstUserId' => UserEnum::USER_FIRST_ID
449
        ])->toIterable();
450
    }
451
452
    #[Override]
453
    public function getNpcShipsForTick(): iterable
454
    {
455
        return $this->getEntityManager()->createQuery(
456
            sprintf(
457
                'SELECT s FROM %s s WHERE s.user_id BETWEEN 2 AND (:firstUserId - 1)',
458
                Ship::class
459
            )
460
        )->setParameter('firstUserId', UserEnum::USER_FIRST_ID)->getResult();
461
    }
462
463
    #[Override]
464
    public function getFleetShipsScannerResults(
465
        ShipInterface $ship,
466
        bool $showCloaked = false,
467
        MapInterface|StarSystemMapInterface|null $field = null
468
    ): array {
469
470
        $rsm = new ResultSetMapping();
471
        $rsm->addEntityResult(TFleetShipItem::class, 's');
472
        $rsm->addFieldResult('s', 'fleetname', 'fleet_name');
473
        $rsm->addFieldResult('s', 'isdefending', 'is_defending');
474
        $rsm->addFieldResult('s', 'isblocking', 'is_blocking');
475
        $this->addTShipItemFields($rsm);
476
477
        $query = $this->getEntityManager()->createNativeQuery(
478
            sprintf(
479
                'SELECT f.id as fleetid, f.name as fleetname, f.defended_colony_id is not null as isdefending,
480
                    f.blocked_colony_id is not null as isblocking, s.id as shipid, s.rumps_id as rumpid, s.former_rumps_id as formerrumpid,
481
                    ss.mode as warpstate, twd.mode as tractorwarpstate, COALESCE(ss2.mode,0) as cloakstate, ss3.mode as shieldstate,
482
                    COALESCE(ss4.status,0) as uplinkstate, s.is_destroyed as isdestroyed, s.type as spacecrafttype, s.name as shipname,
483
                    s.huelle as hull, s.max_huelle as maxhull, s.schilde as shield, s.holding_web_id as webid, tw.finished_time as webfinishtime,
484
                    u.id as userid, u.username, r.category_id as rumpcategoryid, r.name as rumpname, r.role_id as rumproleid,
485
                    (SELECT count(*) > 0 FROM stu_ship_log sl WHERE sl.ship_id = s.id AND sl.is_private = false) as haslogbook,
486
                    (SELECT count(*) > 0 FROM stu_crew_assign ca WHERE ca.ship_id = s.id) as hascrew
487
                FROM stu_ships s
488
                LEFT JOIN stu_ship_system ss
489
                ON s.id = ss.ship_id
490
                AND ss.system_type = :warpdriveType
491
                LEFT JOIN stu_ships tractor
492
                ON tractor.tractored_ship_id = s.id
493
                LEFT JOIN stu_ship_system twd
494
                ON tractor.id = twd.ship_id
495
                AND twd.system_type = :warpdriveType
496
                LEFT JOIN stu_ship_system ss2
497
                ON s.id = ss2.ship_id
498
                AND ss2.system_type = :cloakType
499
                LEFT JOIN stu_ship_system ss3
500
                ON s.id = ss3.ship_id
501
                AND ss3.system_type = :shieldType
502
                LEFT JOIN stu_ship_system ss4
503
                ON s.id = ss4.ship_id
504
                AND ss4.system_type = :uplinkType
505
                JOIN stu_rumps r
506
                ON s.rumps_id = r.id
507
                JOIN stu_fleets f
508
                ON s.fleets_id = f.id
509
                LEFT OUTER JOIN stu_tholian_web tw
510
                ON s.holding_web_id = tw.id
511
                JOIN stu_user u
512
                ON s.user_id = u.id
513
                WHERE s.location_id = :locationId
514
                AND s.id != :ignoreId
515
                %s
516
                ORDER BY f.sort DESC, f.id DESC, (CASE WHEN s.is_fleet_leader THEN 0 ELSE 1 END), r.category_id ASC, r.role_id ASC, r.id ASC, s.name ASC',
517
                $showCloaked ? '' : sprintf(' AND (s.user_id = %d OR COALESCE(ss2.mode,0) < %d) ', $ship->getUser()->getId(), ShipSystemModeEnum::MODE_ON)
518
            ),
519
            $rsm
520
        )->setParameters([
521
            'locationId' => $ship->getLocation()->getId(),
522
            'ignoreId' => $ship->getId(),
523
            'cloakType' => ShipSystemTypeEnum::SYSTEM_CLOAK->value,
524
            'warpdriveType' => ShipSystemTypeEnum::SYSTEM_WARPDRIVE->value,
525
            'shieldType' => ShipSystemTypeEnum::SYSTEM_SHIELDS->value,
526
            'uplinkType' => ShipSystemTypeEnum::SYSTEM_UPLINK->value
527
        ]);
528
529
        return $query->getResult();
530
    }
531
532
    #[Override]
533
    public function getSingleShipScannerResults(
534
        ShipInterface $ship,
535
        array $types,
536
        bool $showCloaked = false,
537
        MapInterface|StarSystemMapInterface|null $field = null
538
    ): array {
539
540
        $rsm = new ResultSetMapping();
541
        $rsm->addEntityResult(TShipItem::class, 's');
542
        $this->addTShipItemFields($rsm);
543
544
        $query = $this->getEntityManager()->createNativeQuery(
545
            sprintf(
546
                'SELECT s.id as shipid, s.fleets_id as fleetid, s.rumps_id as rumpid , s.former_rumps_id as formerrumpid, ss.mode as warpstate,
547
                    twd.mode as tractorwarpstate, COALESCE(ss2.mode,0) as cloakstate, ss3.mode as shieldstate, COALESCE(ss4.status,0) as uplinkstate,
548
                    s.is_destroyed as isdestroyed, s.type as spacecrafttype, s.name as shipname, s.huelle as hull, s.max_huelle as maxhull,
549
                    s.schilde as shield, s.holding_web_id as webid, tw.finished_time as webfinishtime, u.id as userid, u.username,
550
                    r.category_id as rumpcategoryid, r.name as rumpname, r.role_id as rumproleid,
551
                    (SELECT count(*) > 0 FROM stu_ship_log sl WHERE sl.ship_id = s.id AND sl.is_private = false) as haslogbook,
552
                    (SELECT count(*) > 0 FROM stu_crew_assign ca WHERE ca.ship_id = s.id) as hascrew
553
                FROM stu_ships s
554
                LEFT JOIN stu_ship_system ss
555
                ON s.id = ss.ship_id
556
                AND ss.system_type = :warpdriveType
557
                LEFT JOIN stu_ships tractor
558
                ON tractor.tractored_ship_id = s.id
559
                LEFT JOIN stu_ship_system twd
560
                ON tractor.id = twd.ship_id
561
                AND twd.system_type = :warpdriveType
562
                LEFT JOIN stu_ship_system ss2
563
                ON s.id = ss2.ship_id
564
                AND ss2.system_type = :cloakType
565
                LEFT JOIN stu_ship_system ss3
566
                ON s.id = ss3.ship_id
567
                AND ss3.system_type = :shieldType
568
                LEFT JOIN stu_ship_system ss4
569
                ON s.id = ss4.ship_id
570
                AND ss4.system_type = :uplinkType
571
                JOIN stu_rumps r
572
                ON s.rumps_id = r.id
573
                LEFT OUTER JOIN stu_tholian_web tw
574
                ON s.holding_web_id = tw.id
575
                JOIN stu_user u
576
                ON s.user_id = u.id
577
                WHERE s.location_id = :locationId
578
                AND s.id != :ignoreId
579
                AND s.fleets_id IS NULL
580
                AND s.type IN (:types)
581
                %s
582
                ORDER BY r.category_id ASC, r.role_id ASC, r.id ASC, s.name ASC',
583
                $showCloaked ? '' : sprintf(' AND (s.user_id = %d OR COALESCE(ss2.mode,0) < %d) ', $ship->getUser()->getId(), ShipSystemModeEnum::MODE_ON)
584
            ),
585
            $rsm
586
        )->setParameters([
587
            'locationId' => $ship->getLocation()->getId(),
588
            'ignoreId' => $ship->getId(),
589
            'types' => $types,
590
            'cloakType' => ShipSystemTypeEnum::SYSTEM_CLOAK->value,
591
            'warpdriveType' => ShipSystemTypeEnum::SYSTEM_WARPDRIVE->value,
592
            'shieldType' => ShipSystemTypeEnum::SYSTEM_SHIELDS->value,
593
            'uplinkType' => ShipSystemTypeEnum::SYSTEM_UPLINK->value
594
        ]);
595
596
        return $query->getResult();
597
    }
598
599
    private function addTShipItemFields(ResultSetMapping $rsm): void
600
    {
601
        $rsm->addFieldResult('s', 'shipid', 'ship_id');
602
        $rsm->addFieldResult('s', 'fleetid', 'fleet_id');
603
        $rsm->addFieldResult('s', 'rumpid', 'rump_id');
604
        $rsm->addFieldResult('s', 'formerrumpid', 'former_rump_id');
605
        $rsm->addFieldResult('s', 'warpstate', 'warp_state');
606
        $rsm->addFieldResult('s', 'tractorwarpstate', 'tractor_warp_state');
607
        $rsm->addFieldResult('s', 'cloakstate', 'cloak_state');
608
        $rsm->addFieldResult('s', 'shieldstate', 'shield_state');
609
        $rsm->addFieldResult('s', 'uplinkstate', 'uplink_state');
610
        $rsm->addFieldResult('s', 'isdestroyed', 'is_destroyed');
611
        $rsm->addFieldResult('s', 'spacecrafttype', 'spacecraft_type');
612
        $rsm->addFieldResult('s', 'shipname', 'ship_name');
613
        $rsm->addFieldResult('s', 'hull', 'hull');
614
        $rsm->addFieldResult('s', 'maxhull', 'max_hull');
615
        $rsm->addFieldResult('s', 'shield', 'shield');
616
        $rsm->addFieldResult('s', 'webid', 'web_id');
617
        $rsm->addFieldResult('s', 'webfinishtime', 'web_finish_time');
618
        $rsm->addFieldResult('s', 'userid', 'user_id');
619
        $rsm->addFieldResult('s', 'username', 'user_name');
620
        $rsm->addFieldResult('s', 'rumpcategoryid', 'rump_category_id');
621
        $rsm->addFieldResult('s', 'rumpname', 'rump_name');
622
        $rsm->addFieldResult('s', 'rumproleid', 'rump_role_id');
623
        $rsm->addFieldResult('s', 'haslogbook', 'has_logbook');
624
        $rsm->addFieldResult('s', 'hascrew', 'has_crew');
625
    }
626
627
    #[Override]
628
    public function isCloakedShipAtShipLocation(
629
        ShipInterface $ship
630
    ): bool {
631
632
        $result = $this->getEntityManager()->createQuery(
633
            sprintf(
634
                'SELECT COUNT(s.id) FROM %s s
635
                    WHERE s.location = :location
636
                    AND EXISTS (SELECT ss.id
637
                            FROM %s ss
638
                            WHERE s = ss.ship
639
                            AND ss.system_type = %d
640
                            AND ss.mode > 1)
641
                    AND s.user != :ignoreUser',
642
                Ship::class,
643
                ShipSystem::class,
644
                ShipSystemTypeEnum::SYSTEM_CLOAK->value
645
            )
646
        )->setParameters([
647
            'location' => $ship->getLocation(),
648
            'ignoreUser' => $ship->getUser()
649
        ])->getSingleScalarResult();
650
651
        return $result > 0;
652
    }
653
654
    #[Override]
655
    public function getRandomShipIdWithCrewByUser(int $userId): ?int
656
    {
657
        $rsm = new ResultSetMapping();
658
        $rsm->addScalarResult('id', 'id', 'integer');
659
660
        $result = $this->getEntityManager()
661
            ->createNativeQuery(
662
                'SELECT s.id as id FROM stu_ships s
663
                WHERE s.user_id = :userId
664
                AND EXISTS (SELECT sc.id
665
                            FROM stu_crew_assign sc
666
                            WHERE s.id = sc.ship_id)
667
                ORDER BY RANDOM()
668
                LIMIT 1',
669
                $rsm
670
            )
671
            ->setParameters([
672
                'userId' => $userId
673
            ])
674
            ->getOneOrNullResult();
675
676
        return $result != null ? $result['id'] : null;
677
    }
678
679
    #[Override]
680
    public function isBaseOnLocation(ShipInterface $ship): bool
681
    {
682
        $query = $this->getEntityManager()->createQuery(
683
            sprintf(
684
                'SELECT COUNT(s.id) FROM %s s
685
                WHERE s.location = :location
686
                AND s.type = :type',
687
                Ship::class
688
            )
689
        )->setParameters([
690
            'location' => $ship->getLocation(),
691
            'type' => SpacecraftTypeEnum::SPACECRAFT_TYPE_STATION->value
692
        ]);
693
694
        return $query->getSingleScalarResult() > 0;
695
    }
696
697
    #[Override]
698
    public function getStationsByUser(int $userId): array
699
    {
700
        return $this->getEntityManager()
701
            ->createQuery(
702
                sprintf(
703
                    'SELECT s
704
                    FROM %s s
705
                    JOIN %s r
706
                    WITH s.rumps_id = r.id
707
                    WHERE s.user_id = :userId
708
                    AND r.category_id = :categoryId',
709
                    Ship::class,
710
                    ShipRump::class
711
                )
712
            )
713
            ->setParameters([
714
                'userId' => $userId,
715
                'categoryId' => ShipRumpEnum::SHIP_CATEGORY_STATION
716
            ])
717
            ->getResult();
718
    }
719
720
    #[Override]
721
    public function getAllDockedShips(): array
722
    {
723
        return $this->getEntityManager()->createQuery(
724
            sprintf(
725
                'SELECT s FROM %s s
726
                WHERE s.dock IS NOT NULL',
727
                Ship::class
728
            )
729
        )->getResult();
730
    }
731
732
    #[Override]
733
    public function getAllTractoringShips(): array
734
    {
735
        return $this->getEntityManager()->createQuery(
736
            sprintf(
737
                'SELECT s FROM %s s
738
                WHERE s.tractored_ship_id IS NOT NULL',
739
                Ship::class
740
            )
741
        )->getResult();
742
    }
743
744
    #[Override]
745
    public function getPirateTargets(ShipInterface $ship): array
746
    {
747
        $layer = $ship->getLayer();
748
        if ($layer === null) {
749
            return [];
750
        }
751
752
        $location = $ship->getLocation();
753
        $range = $ship->getSensorRange() * 2;
754
755
        return $this->getEntityManager()->createQuery(
756
            sprintf(
757
                'SELECT s FROM %s s
758
                JOIN %s l
759
                WITH s.location = l.id
760
                JOIN %s u
761
                WITH s.user_id = u.id
762
                LEFT JOIN %s w
763
                WITH u.id = w.user_id
764
                WHERE l.layer_id = :layerId
765
                AND l.cx BETWEEN :minX AND :maxX
766
                AND l.cy BETWEEN :minY AND :maxY
767
                AND s.type = :shipType
768
                AND (s.fleets_id IS NULL OR s.is_fleet_leader = true)
769
                AND u.id >= :firstUserId
770
                AND u.state >= :stateActive
771
                AND u.creation < :eightWeeksEarlier
772
                AND (u.vac_active = false OR u.vac_request_date > :vacationThreshold)
773
                AND COALESCE(w.protection_timeout, 0) < :currentTime',
774
                Ship::class,
775
                Location::class,
776
                User::class,
777
                PirateWrath::class
778
            )
779
        )
780
            ->setParameters([
781
                'minX' => $location->getCx() - $range,
782
                'maxX' => $location->getCx() + $range,
783
                'minY' => $location->getCy() - $range,
784
                'maxY' => $location->getCy() + $range,
785
                'layerId' => $layer->getId(),
786
                'shipType' => SpacecraftTypeEnum::SPACECRAFT_TYPE_SHIP->value,
787
                'firstUserId' => UserEnum::USER_FIRST_ID,
788
                'stateActive' => UserEnum::USER_STATE_ACTIVE,
789
                'eightWeeksEarlier' => time() - TimeConstants::EIGHT_WEEKS_IN_SECONDS,
790
                'vacationThreshold' => time() - UserEnum::VACATION_DELAY_IN_SECONDS,
791
                'currentTime' => time()
792
            ])
793
            ->getResult();
794
    }
795
796
    #[Override]
797
    public function getPirateFriends(ShipInterface $ship): array
798
    {
799
        $layer = $ship->getLayer();
800
        if ($layer === null) {
801
            return [];
802
        }
803
804
        $location = $ship->getLocation();
805
        $range = $ship->getSensorRange() * 3;
806
807
        return $this->getEntityManager()->createQuery(
808
            sprintf(
809
                'SELECT s FROM %s s
810
                JOIN %s l
811
                WITH s.location_id = l.id
812
                WHERE l.layer_id = :layerId
813
                AND l.cx BETWEEN :minX AND :maxX
814
                AND l.cy BETWEEN :minY AND :maxY
815
                AND s.id != :shipId
816
                AND s.user_id = :kazonUserId',
817
                Ship::class,
818
                Location::class
819
            )
820
        )
821
            ->setParameters([
822
                'minX' => $location->getCx() - $range,
823
                'maxX' => $location->getCx() + $range,
824
                'minY' => $location->getCy() - $range,
825
                'maxY' => $location->getCy() + $range,
826
                'layerId' => $layer->getId(),
827
                'shipId' => $ship->getId(),
828
                'kazonUserId' => UserEnum::USER_NPC_KAZON
829
            ])
830
            ->getResult();
831
    }
832
833
    #[Override]
834
    public function truncateAllShips(): void
835
    {
836
        $this->getEntityManager()->createQuery(
837
            sprintf(
838
                'DELETE FROM %s s',
839
                Ship::class
840
            )
841
        )->execute();
842
    }
843
}
844