Passed
Push — dev ( 220852...a22d66 )
by Janko
21:21
created

ShipRepository::getPirateFriends()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 27
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 19
nc 2
nop 1
dl 0
loc 27
ccs 0
cts 20
cp 0
crap 6
rs 9.6333
c 0
b 0
f 0
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 Stu\Component\Anomaly\Type\AnomalyTypeEnum;
10
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...
11
use Stu\Component\Ship\ShipAlertStateEnum;
12
use Stu\Component\Ship\ShipRumpEnum;
13
use Stu\Component\Ship\ShipStateEnum;
14
use Stu\Component\Ship\SpacecraftTypeEnum;
15
use Stu\Component\Ship\System\ShipSystemModeEnum;
16
use Stu\Component\Ship\System\ShipSystemTypeEnum;
17
use Stu\Module\PlayerSetting\Lib\UserEnum;
18
use Stu\Module\Ship\Lib\ShipRumpSpecialAbilityEnum;
19
use Stu\Module\Ship\Lib\TFleetShipItem;
20
use Stu\Module\Ship\Lib\TShipItem;
21
use Stu\Orm\Entity\Anomaly;
22
use Stu\Orm\Entity\Crew;
23
use Stu\Orm\Entity\Fleet;
24
use Stu\Orm\Entity\Map;
25
use Stu\Orm\Entity\MapInterface;
26
use Stu\Orm\Entity\Ship;
27
use Stu\Orm\Entity\ShipBuildplan;
28
use Stu\Orm\Entity\ShipCrew;
29
use Stu\Orm\Entity\ShipInterface;
30
use Stu\Orm\Entity\ShipRump;
31
use Stu\Orm\Entity\ShipRumpSpecial;
32
use Stu\Orm\Entity\ShipSystem;
33
use Stu\Orm\Entity\StarSystemMap;
34
use Stu\Orm\Entity\StarSystemMapInterface;
35
use Stu\Orm\Entity\Storage;
36
use Stu\Orm\Entity\User;
37
use Stu\Orm\Entity\UserInterface;
38
39
/**
40
 * @extends EntityRepository<Ship>
41
 */
42
final class ShipRepository extends EntityRepository implements ShipRepositoryInterface
43
{
44
    public function prototype(): ShipInterface
45
    {
46
        return new Ship();
47
    }
48
49
    public function save(ShipInterface $post): void
50
    {
51
        $em = $this->getEntityManager();
52
53
        $em->persist($post);
54
    }
55
56
    public function delete(ShipInterface $post): void
57
    {
58
        $em = $this->getEntityManager();
59
60
        $em->remove($post);
61
    }
62
63
    public function getAmountByUserAndSpecialAbility(
64
        int $userId,
65
        int $specialAbilityId
66
    ): int {
67
        return (int) $this->getEntityManager()->createQuery(
68
            sprintf(
69
                'SELECT COUNT(s)
70
                FROM %s s
71
                JOIN %s bp
72
                WITH s.plans_id = bp.id
73
                WHERE s.user_id = :userId AND s.rumps_id IN (
74
                    SELECT rs.rumps_id FROM %s rs WHERE rs.special = :specialAbilityId
75
                )
76
                %s',
77
                Ship::class,
78
                ShipBuildplan::class,
79
                ShipRumpSpecial::class,
80
                $specialAbilityId === ShipRumpSpecialAbilityEnum::COLONIZE ? 'AND bp.crew = 0' : ''
81
            )
82
        )->setParameters([
83
            'userId' => $userId,
84
            'specialAbilityId' => $specialAbilityId,
85
        ])->getSingleScalarResult();
86
    }
87
88
    public function getAmountByUserAndRump(int $userId, int $shipRumpId): int
89
    {
90
        return $this->count([
91
            'user_id' => $userId,
92
            'rumps_id' => $shipRumpId,
93
        ]);
94
    }
95
96
    public function getByUser(UserInterface $user): iterable
97
    {
98
        return $this->findBy([
99
            'user_id' => $user,
100
        ]);
101
    }
102
103
    public function getByUserAndRump(int $userId, int $rumpId): array
104
    {
105
        return $this->findBy([
106
            'user_id' => $userId,
107
            'rumps_id' => $rumpId
108
        ], [
109
            'map_id' => 'asc',
110
            'starsystem_map_id' => 'asc',
111
            'fleets_id' => 'asc',
112
            'is_fleet_leader' => 'desc'
113
        ]);
114
    }
115
116
    public function getPossibleFleetMembers(ShipInterface $fleetLeader): iterable
117
    {
118
        $isSystem = $fleetLeader->getSystem() !== null;
119
120
        return $this->getEntityManager()->createQuery(
121
            sprintf(
122
                'SELECT s FROM %s s
123
                WHERE s.%s = :mapId
124
                AND s.fleets_id IS NULL
125
                AND s.user_id = :userId
126
                AND s.type = :type
127
                ORDER BY s.rumps_id ASC, s.name ASC',
128
                Ship::class,
129
                $isSystem ? 'starsystem_map_id' : 'map_id'
130
            )
131
        )->setParameters([
132
            'userId' => $fleetLeader->getUser()->getId(),
133
            'type' => SpacecraftTypeEnum::SPACECRAFT_TYPE_SHIP->value,
134
            'mapId' => $isSystem ? $fleetLeader->getStarsystemMap()->getId() : $fleetLeader->getMap()->getId()
135
        ])->getResult();
136
    }
137
138
    public function getByLocationAndUser(?StarSystemMapInterface $starSystemMap, ?MapInterface $map, UserInterface $user): array
139
    {
140
        return $this->findBy([
141
            'user_id' => $user->getId(),
142
            'starsystem_map_id' => $starSystemMap !== null ? $starSystemMap->getId() : null,
143
            'map_id' => $map !== null ? $map->getId() : null
144
        ], [
145
            'fleets_id' => 'desc',
146
            'is_fleet_leader' => 'desc',
147
            'id' => 'desc'
148
        ]);
149
    }
150
151
    public function getByLocation(
152
        ?StarSystemMapInterface $starSystemMap,
153
        ?MapInterface $map
154
    ): array {
155
        return $this->getEntityManager()
156
            ->createQuery(
157
                sprintf(
158
                    'SELECT s FROM %s s
159
                    LEFT JOIN %s f
160
                    WITH s.fleets_id = f.id
161
                    JOIN %s r
162
                    WITH s.rumps_id = r.id
163
                    WHERE s.%s = :mapId
164
                    AND NOT EXISTS (SELECT ss.id
165
                                        FROM %s ss
166
                                        WHERE s.id = ss.ship_id
167
                                        AND ss.system_type = :systemId
168
                                        AND ss.mode > 1)
169
                    ORDER BY s.is_destroyed ASC, f.sort DESC, f.id DESC, s.is_fleet_leader DESC,
170
                    r.category_id ASC, r.role_id ASC, r.id ASC, s.name ASC',
171
                    Ship::class,
172
                    Fleet::class,
173
                    ShipRump::class,
174
                    $starSystemMap === null ? 'map_id' : 'starsystem_map_id',
175
                    ShipSystem::class
176
                )
177
            )
178
            ->setParameters([
179
                'mapId' => $starSystemMap === null ? $map->getId() : $starSystemMap->getId(),
0 ignored issues
show
Bug introduced by
The method getId() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

179
                'mapId' => $starSystemMap === null ? $map->/** @scrutinizer ignore-call */ getId() : $starSystemMap->getId(),

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
180
                'systemId' => ShipSystemTypeEnum::SYSTEM_CLOAK->value
181
            ])
182
            ->getResult();
183
    }
184
185
    public function getForeignStationsInBroadcastRange(ShipInterface $ship): array
186
    {
187
        $systemMap = $ship->getStarsystemMap();
188
        $map = $ship->getMap();
189
190
        return $this->getEntityManager()
191
            ->createQuery(
192
                sprintf(
193
                    'SELECT s FROM %s s
194
                     LEFT JOIN %s m
195
                     WITH s.map_id = m.id
196
                     LEFT JOIN %s sm
197
                     WITH s.starsystem_map_id = sm.id
198
                     WHERE s.user_id NOT IN (:ignoreIds)
199
                     AND s.type = :spacecraftType
200
                     AND (:cx = 0 OR (m.cx BETWEEN (:cx - 1) AND (:cx + 1)
201
                        AND m.cy BETWEEN (:cy - 1) AND (:cy + 1)))
202
                     AND (:systemId = 0 OR (sm.systems_id = :systemId
203
                        AND sm.sx BETWEEN (:sx - 1) AND (:sx + 1)
204
                        AND sm.sy BETWEEN (:sy - 1) AND (:sy + 1)))',
205
                    Ship::class,
206
                    Map::class,
207
                    StarSystemMap::class
208
                )
209
            )
210
            ->setParameters([
211
                'ignoreIds' => [$ship->getUser()->getId(), UserEnum::USER_NOONE],
212
                'spacecraftType' => SpacecraftTypeEnum::SPACECRAFT_TYPE_STATION->value,
213
                'systemId' => $systemMap === null ? 0 : $systemMap->getSystem()->getId(),
214
                'sx' => $systemMap === null ? 0 : $systemMap->getSx(),
215
                'sy' => $systemMap === null ? 0 : $systemMap->getSy(),
216
                'cx' => $map === null ? 0 : $map->getCx(),
217
                'cy' => $map === null ? 0 : $map->getCy()
218
            ])
219
            ->getResult();
220
    }
221
222
    public function getShipsForAlertRed(
223
        ShipInterface $ship
224
    ): iterable {
225
        $isSystem = $ship->getSystem() !== null;
226
227
        return $this->getEntityManager()->createQuery(
228
            sprintf(
229
                'SELECT s FROM %s s
230
                JOIN %s u
231
                WITH s.user_id = u.id
232
                WHERE s.alvl = :alertRed
233
                AND s.user_id != :ignoreId
234
                AND s.%s = :mapId
235
                AND NOT EXISTS (SELECT ss.id
236
                                FROM %s ss
237
                                WHERE s.id = ss.ship_id
238
                                AND ss.system_type = :cloakSystemId
239
                                AND ss.mode > 1)
240
                AND NOT EXISTS (SELECT ss2.id
241
                                FROM %s ss2
242
                                WHERE s.id = ss2.ship_id
243
                                AND ss2.system_type = :warpSystemId
244
                                AND ss2.mode > 1)
245
                AND (u.vac_active = false OR u.vac_request_date > :vacationThreshold)',
246
                Ship::class,
247
                User::class,
248
                $isSystem ? 'starsystem_map_id' : 'map_id',
249
                ShipSystem::class,
250
                ShipSystem::class
251
            )
252
        )->setParameters([
253
            'alertRed' => ShipAlertStateEnum::ALERT_RED,
254
            'mapId' => $isSystem ? $ship->getStarsystemMap()->getId() : $ship->getMap()->getId(),
255
            'ignoreId' => $ship->getUser()->getId(),
256
            'cloakSystemId' => ShipSystemTypeEnum::SYSTEM_CLOAK->value,
257
            'warpSystemId' => ShipSystemTypeEnum::SYSTEM_WARPDRIVE->value,
258
            'vacationThreshold' => time() - UserEnum::VACATION_DELAY_IN_SECONDS
259
        ])->getResult();
260
    }
261
262
    public function getTradePostsWithoutDatabaseEntry(): iterable
263
    {
264
        return $this->getEntityManager()->createQuery(
265
            sprintf(
266
                'SELECT s FROM %s s WHERE s.database_id is null AND s.trade_post_id > 0',
267
                Ship::class
268
            )
269
        )->getResult();
270
    }
271
272
    public function getByUserAndFleetAndType(int $userId, ?int $fleetId, SpacecraftTypeEnum $type): array
273
    {
274
        return $this->findBy(
275
            [
276
                'user_id' => $userId,
277
                'fleets_id' => $fleetId,
278
                'type' => $type->value,
279
            ],
280
            $type === SpacecraftTypeEnum::SPACECRAFT_TYPE_STATION ? ['max_huelle' => 'desc', 'id' => 'asc'] : ['id' => 'asc']
281
        );
282
    }
283
284
    public function getByUplink(int $userId): array
285
    {
286
        return $this->getEntityManager()->createQuery(
287
            sprintf(
288
                'SELECT s FROM %s s
289
                JOIN %s sc
290
                WITH s.id = sc.ship_id
291
                JOIN %s c
292
                WITH sc.crew_id = c.id
293
                JOIN %s ss
294
                WITH ss.ship_id = s.id
295
                JOIN %s u
296
                WITH s.user_id = u.id
297
                WHERE s.user_id != :userId
298
                AND c.user_id = :userId
299
                AND ss.system_type = :systemType
300
                AND ss.mode >= :mode
301
                AND (u.vac_active = false OR u.vac_request_date > :vacationThreshold)',
302
                Ship::class,
303
                ShipCrew::class,
304
                Crew::class,
305
                ShipSystem::class,
306
                User::class
307
            )
308
        )->setParameters([
309
            'userId' => $userId,
310
            'systemType' => ShipSystemTypeEnum::SYSTEM_UPLINK->value,
311
            'mode' => ShipSystemModeEnum::MODE_ON,
312
            'vacationThreshold' => time() - UserEnum::VACATION_DELAY_IN_SECONDS
313
        ])
314
            ->getResult();
315
    }
316
317
    public function getWithTradeLicensePayment(
318
        int $userId,
319
        int $tradePostShipId,
320
        int $commodityId,
321
        int $amount
322
    ): iterable {
323
        return $this->getEntityManager()->createQuery(
324
            sprintf(
325
                'SELECT s FROM %s s WHERE s.user_id = :userId AND s.dock = :tradePostShipId AND s.id IN (
326
                    SELECT st.ship_id FROM %s st WHERE st.commodity_id = :commodityId AND st.count >= :amount
327
                )',
328
                Ship::class,
329
                Storage::class
330
            )
331
        )->setParameters([
332
            'userId' => $userId,
333
            'tradePostShipId' => $tradePostShipId,
334
            'commodityId' => $commodityId,
335
            'amount' => $amount,
336
        ])->getResult();
337
    }
338
339
    public function getSuitableForShildRegeneration(int $regenerationThreshold): iterable
340
    {
341
        return $this->getEntityManager()->createQuery(
342
            sprintf(
343
                'SELECT s FROM %s s
344
                JOIN %s ss
345
                WITH s.id = ss.ship_id
346
                JOIN %s bp
347
                WITH s.plans_id = bp.id
348
                WHERE ss.system_type = :shieldType
349
                AND ss.mode < :modeOn
350
                AND s.is_destroyed = :destroyedState
351
                AND s.schilde<s.max_schilde
352
                AND s.shield_regeneration_timer <= :regenerationThreshold
353
                AND (SELECT count(sc.id) FROM %s sc WHERE s.id = sc.ship_id) >= bp.crew
354
                AND NOT EXISTS (SELECT a FROM %s a WHERE (a.map_id = s.map_id or a.starsystem_map_id = s.starsystem_map_id) AND a.anomaly_type_id = :anomalyType AND a.remaining_ticks > 0)',
355
                Ship::class,
356
                ShipSystem::class,
357
                ShipBuildplan::class,
358
                ShipCrew::class,
359
                Anomaly::class
360
            )
361
        )->setParameters([
362
            'shieldType' => ShipSystemTypeEnum::SYSTEM_SHIELDS->value,
363
            'modeOn' => ShipSystemModeEnum::MODE_ON,
364
            'regenerationThreshold' => $regenerationThreshold,
365
            'destroyedState' => 0,
366
            'anomalyType' => AnomalyTypeEnum::SUBSPACE_ELLIPSE
367
        ])->getResult();
368
    }
369
370
    public function getEscapePods(): 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
                WHERE sr.category_id = :categoryId',
378
                Ship::class,
379
                ShipRump::class
380
            )
381
        )->setParameters([
382
            'categoryId' => ShipRumpEnum::SHIP_CATEGORY_ESCAPE_PODS
383
        ])->getResult();
384
    }
385
386
    public function getEscapePodsByCrewOwner(int $userId): iterable
387
    {
388
        return $this->getEntityManager()->createQuery(
389
            sprintf(
390
                'SELECT s FROM %s s
391
                LEFT JOIN %s sr
392
                WITH s.rumps_id = sr.id
393
                LEFT JOIN %s sc
394
                WITH sc.ship_id = s.id
395
                WHERE sr.category_id = :categoryId
396
                AND sc.user_id = :userId',
397
                Ship::class,
398
                ShipRump::class,
399
                ShipCrew::class
400
            )
401
        )->setParameters([
402
            'categoryId' => ShipRumpEnum::SHIP_CATEGORY_ESCAPE_PODS,
403
            'userId' => $userId
404
        ])->getResult();
405
    }
406
407
    public function getDebrisFields(): iterable
408
    {
409
        return $this->findBy([
410
            'is_destroyed' => true,
411
        ]);
412
    }
413
414
    public function getStationConstructions(): iterable
415
    {
416
        return $this->getEntityManager()->createQuery(
417
            sprintf(
418
                'SELECT s FROM %s s
419
                JOIN %s r
420
                WITH s.rumps_id = r.id
421
                WHERE s.user_id > :firstUserId
422
                AND r.category_id = :catId',
423
                Ship::class,
424
                ShipRump::class
425
            )
426
        )->setParameters([
427
            'catId' => ShipRumpEnum::SHIP_CATEGORY_CONSTRUCTION,
428
            'firstUserId' => UserEnum::USER_FIRST_ID
429
        ])
430
            ->getResult();
431
    }
432
433
    public function getPlayerShipsForTick(): iterable
434
    {
435
        return $this->getEntityManager()->createQuery(
436
            sprintf(
437
                'SELECT s
438
                FROM %s s
439
                JOIN %s p
440
                WITH s.plans_id = p.id
441
                JOIN %s u
442
                WITH s.user_id = u.id
443
                WHERE s.user_id > :firstUserId
444
                AND (   ((SELECT count(sc.id)
445
                        FROM %s sc
446
                        WHERE sc.ship_id = s.id) > 0)
447
                    OR
448
                        (s.state IN (:scrapping, :underConstruction))
449
                    OR
450
                        (p.crew = 0))
451
                AND (u.vac_active = false OR u.vac_request_date > :vacationThreshold)',
452
                Ship::class,
453
                ShipBuildplan::class,
454
                User::class,
455
                ShipCrew::class
456
            )
457
        )->setParameters([
458
            'underConstruction' => ShipStateEnum::SHIP_STATE_UNDER_CONSTRUCTION,
459
            'scrapping' => ShipStateEnum::SHIP_STATE_UNDER_SCRAPPING,
460
            'vacationThreshold' => time() - UserEnum::VACATION_DELAY_IN_SECONDS,
461
            'firstUserId' => UserEnum::USER_FIRST_ID
462
        ])->toIterable();
463
    }
464
465
    public function getNpcShipsForTick(): iterable
466
    {
467
        return $this->getEntityManager()->createQuery(
468
            sprintf(
469
                'SELECT s FROM %s s WHERE s.user_id BETWEEN 2 AND (:firstUserId - 1)',
470
                Ship::class
471
            )
472
        )->setParameter('firstUserId', UserEnum::USER_FIRST_ID)->getResult();
473
    }
474
475
    public function getFleetShipsScannerResults(
476
        ShipInterface $ship,
477
        bool $showCloaked = false,
478
        int $mapId = null,
479
        int $sysMapId = null
480
    ): array {
481
        $isSystem = $sysMapId !== null || ($mapId === null && $ship->getSystem() !== null);
482
483
        $rsm = new ResultSetMapping();
484
        $rsm->addEntityResult(TFleetShipItem::class, 's');
485
        $rsm->addFieldResult('s', 'fleetname', 'fleet_name');
486
        $rsm->addFieldResult('s', 'isdefending', 'is_defending');
487
        $rsm->addFieldResult('s', 'isblocking', 'is_blocking');
488
        $this->addTShipItemFields($rsm);
489
490
        return $this->getEntityManager()->createNativeQuery(
491
            sprintf(
492
                'SELECT f.id as fleetid, f.name as fleetname, f.defended_colony_id is not null as isdefending,
493
                    f.blocked_colony_id is not null as isblocking, s.id as shipid, s.rumps_id as rumpid, s.former_rumps_id as formerrumpid,
494
                    ss.mode as warpstate, COALESCE(ss2.mode,0) as cloakstate, ss3.mode as shieldstate, COALESCE(ss4.status,0) as uplinkstate, s.is_destroyed as isdestroyed,
495
                    s.type as spacecrafttype, s.name as shipname, s.huelle as hull, s.max_huelle as maxhull, s.schilde as shield, s.holding_web_id as webid, tw.finished_time as webfinishtime,
496
                    u.id as userid, u.username, r.category_id as rumpcategoryid, r.name as rumpname, r.role_id as rumproleid,
497
                    (SELECT count(*) > 0 FROM stu_ship_log sl WHERE sl.ship_id = s.id AND sl.is_private = false) as haslogbook,
498
                    (SELECT count(*) > 0 FROM stu_crew_assign ca WHERE ca.ship_id = s.id) as hascrew
499
                FROM stu_ships s
500
                LEFT JOIN stu_ship_system ss
501
                ON s.id = ss.ship_id
502
                AND ss.system_type = :warpdriveType
503
                LEFT JOIN stu_ship_system ss2
504
                ON s.id = ss2.ship_id
505
                AND ss2.system_type = :cloakType
506
                LEFT JOIN stu_ship_system ss3
507
                ON s.id = ss3.ship_id
508
                AND ss3.system_type = :shieldType
509
                LEFT JOIN stu_ship_system ss4
510
                ON s.id = ss4.ship_id
511
                AND ss4.system_type = :uplinkType
512
                JOIN stu_rumps r
513
                ON s.rumps_id = r.id
514
                JOIN stu_fleets f
515
                ON s.fleets_id = f.id
516
                LEFT OUTER JOIN stu_tholian_web tw
517
                ON s.holding_web_id = tw.id
518
                JOIN stu_user u
519
                ON s.user_id = u.id
520
                WHERE s.%s = :fieldId
521
                AND s.id != :ignoreId
522
                %s
523
                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',
524
                $isSystem ? 'starsystem_map_id' : 'map_id',
525
                $showCloaked ? '' : sprintf(' AND (s.user_id = %d OR COALESCE(ss2.mode,0) < %d) ', $ship->getUser()->getId(), ShipSystemModeEnum::MODE_ON)
526
            ),
527
            $rsm
528
        )->setParameters([
529
            'fieldId' => $mapId ?? $sysMapId ?? ($isSystem ? $ship->getStarsystemMap()->getId() : $ship->getMap()->getId()),
530
            'ignoreId' => $ship->getId(),
531
            'cloakType' => ShipSystemTypeEnum::SYSTEM_CLOAK->value,
532
            'warpdriveType' => ShipSystemTypeEnum::SYSTEM_WARPDRIVE->value,
533
            'shieldType' => ShipSystemTypeEnum::SYSTEM_SHIELDS->value,
534
            'uplinkType' => ShipSystemTypeEnum::SYSTEM_UPLINK->value
535
        ])->getResult();
536
    }
537
538
    public function getSingleShipScannerResults(
539
        ShipInterface $ship,
540
        array $types,
541
        bool $showCloaked = false,
542
        int $mapId = null,
543
        int $sysMapId = null
544
    ): array {
545
        $isSystem = $sysMapId !== null || ($mapId === null && $ship->getSystem() !== null);
546
547
        $rsm = new ResultSetMapping();
548
        $rsm->addEntityResult(TShipItem::class, 's');
549
        $this->addTShipItemFields($rsm);
550
551
        return $this->getEntityManager()->createNativeQuery(
552
            sprintf(
553
                '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, COALESCE(ss2.mode,0) as cloakstate,
554
                    ss3.mode as shieldstate, COALESCE(ss4.status,0) as uplinkstate, s.is_destroyed as isdestroyed, s.type as spacecrafttype, s.name as shipname,
555
                    s.huelle as hull, s.max_huelle as maxhull, s.schilde as shield, s.holding_web_id as webid, tw.finished_time as webfinishtime, u.id as userid, u.username,
556
                    r.category_id as rumpcategoryid, r.name as rumpname, r.role_id as rumproleid,
557
                    (SELECT count(*) > 0 FROM stu_ship_log sl WHERE sl.ship_id = s.id AND sl.is_private = false) as haslogbook,
558
                    (SELECT count(*) > 0 FROM stu_crew_assign ca WHERE ca.ship_id = s.id) as hascrew
559
                FROM stu_ships s
560
                LEFT JOIN stu_ship_system ss
561
                ON s.id = ss.ship_id
562
                AND ss.system_type = :warpdriveType
563
                LEFT JOIN stu_ship_system ss2
564
                ON s.id = ss2.ship_id
565
                AND ss2.system_type = :cloakType
566
                LEFT JOIN stu_ship_system ss3
567
                ON s.id = ss3.ship_id
568
                AND ss3.system_type = :shieldType
569
                LEFT JOIN stu_ship_system ss4
570
                ON s.id = ss4.ship_id
571
                AND ss4.system_type = :uplinkType
572
                JOIN stu_rumps r
573
                ON s.rumps_id = r.id
574
                LEFT OUTER JOIN stu_tholian_web tw
575
                ON s.holding_web_id = tw.id
576
                JOIN stu_user u
577
                ON s.user_id = u.id
578
                WHERE s.%s = :fieldId
579
                AND s.id != :ignoreId
580
                AND s.fleets_id IS NULL
581
                AND s.type IN (:types)
582
                %s
583
                ORDER BY r.category_id ASC, r.role_id ASC, r.id ASC, s.name ASC',
584
                $isSystem ? 'starsystem_map_id' : 'map_id',
585
                $showCloaked ? '' : sprintf(' AND (s.user_id = %d OR COALESCE(ss2.mode,0) < %d) ', $ship->getUser()->getId(), ShipSystemModeEnum::MODE_ON)
586
            ),
587
            $rsm
588
        )->setParameters([
589
            'fieldId' => $mapId ?? $sysMapId ?? ($isSystem ? $ship->getStarsystemMap()->getId() : $ship->getMap()->getId()),
590
            'ignoreId' => $ship->getId(),
591
            'types' => $types,
592
            'cloakType' => ShipSystemTypeEnum::SYSTEM_CLOAK->value,
593
            'warpdriveType' => ShipSystemTypeEnum::SYSTEM_WARPDRIVE->value,
594
            'shieldType' => ShipSystemTypeEnum::SYSTEM_SHIELDS->value,
595
            'uplinkType' => ShipSystemTypeEnum::SYSTEM_UPLINK->value
596
        ])->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', 'cloakstate', 'cloak_state');
607
        $rsm->addFieldResult('s', 'shieldstate', 'shield_state');
608
        $rsm->addFieldResult('s', 'uplinkstate', 'uplink_state');
609
        $rsm->addFieldResult('s', 'isdestroyed', 'is_destroyed');
610
        $rsm->addFieldResult('s', 'spacecrafttype', 'spacecraft_type');
611
        $rsm->addFieldResult('s', 'shipname', 'ship_name');
612
        $rsm->addFieldResult('s', 'hull', 'hull');
613
        $rsm->addFieldResult('s', 'maxhull', 'max_hull');
614
        $rsm->addFieldResult('s', 'shield', 'shield');
615
        $rsm->addFieldResult('s', 'webid', 'web_id');
616
        $rsm->addFieldResult('s', 'webfinishtime', 'web_finish_time');
617
        $rsm->addFieldResult('s', 'userid', 'user_id');
618
        $rsm->addFieldResult('s', 'username', 'user_name');
619
        $rsm->addFieldResult('s', 'rumpcategoryid', 'rump_category_id');
620
        $rsm->addFieldResult('s', 'rumpname', 'rump_name');
621
        $rsm->addFieldResult('s', 'rumproleid', 'rump_role_id');
622
        $rsm->addFieldResult('s', 'haslogbook', 'has_logbook');
623
        $rsm->addFieldResult('s', 'hascrew', 'has_crew');
624
    }
625
626
    public function isCloakedShipAtShipLocation(
627
        ShipInterface $ship
628
    ): bool {
629
        return $this->isCloakedShipAtLocation(
630
            $ship->getStarsystemMap()->getId(),
631
            $ship->getMap()->getId(),
632
            $ship->getUser()->getId()
633
        );
634
    }
635
636
    public function isCloakedShipAtLocation(
637
        ?int $sysMapId,
638
        ?int $mapId,
639
        int $ignoreId
640
    ): bool {
641
        $cloakSql = sprintf(
642
            ' AND EXISTS (SELECT ss.id
643
                            FROM %s ss
644
                            WHERE s.id = ss.ship_id
645
                            AND ss.system_type = %d
646
                            AND ss.mode > 1) ',
647
            ShipSystem::class,
648
            ShipSystemTypeEnum::SYSTEM_CLOAK->value
649
        );
650
651
        $result = $this->getEntityManager()->createQuery(
652
            sprintf(
653
                'SELECT COUNT(s.id) FROM %s s
654
                    WHERE s.%s = :fieldId
655
                    %s
656
                    AND s.user_id != :ignoreId',
657
                Ship::class,
658
                $sysMapId !== null ? 'starsystem_map_id' : 'map_id',
659
                $cloakSql
660
            )
661
        )->setParameters([
662
            'fieldId' => $mapId ?? $sysMapId,
663
            'ignoreId' => $ignoreId
664
        ])->getSingleScalarResult();
665
666
        return $result > 0;
667
    }
668
669
    public function getRandomShipIdWithCrewByUser(int $userId): ?int
670
    {
671
        $rsm = new ResultSetMapping();
672
        $rsm->addScalarResult('id', 'id', 'integer');
673
674
        $result = $this->getEntityManager()
675
            ->createNativeQuery(
676
                'SELECT s.id as id FROM stu_ships s
677
                WHERE s.user_id = :userId
678
                AND EXISTS (SELECT sc.id
679
                            FROM stu_crew_assign sc
680
                            WHERE s.id = sc.ship_id)
681
                ORDER BY RANDOM()
682
                LIMIT 1',
683
                $rsm
684
            )
685
            ->setParameters([
686
                'userId' => $userId
687
            ])
688
            ->getOneOrNullResult();
689
690
        return $result != null ? $result['id'] : null;
691
    }
692
693
    public function isBaseOnLocation(ShipInterface $ship): bool
694
    {
695
        $isSystem = $ship->getSystem() !== null;
696
697
        $query = $this->getEntityManager()->createQuery(
698
            sprintf(
699
                'SELECT COUNT(s.id) FROM %s s
700
                WHERE s.%s = :mapId
701
                AND s.type = :type',
702
                Ship::class,
703
                $isSystem ? 'starsystem_map_id' : 'map_id',
704
            )
705
        )->setParameters([
706
            'mapId' => $isSystem ? $ship->getStarsystemMap()->getId() : $ship->getMap()->getId(),
707
            'type' => SpacecraftTypeEnum::SPACECRAFT_TYPE_STATION->value
708
        ]);
709
710
        return $query->getSingleScalarResult() > 0;
711
    }
712
713
    public function getStationsByUser(int $userId): array
714
    {
715
        return $this->getEntityManager()
716
            ->createQuery(
717
                sprintf(
718
                    'SELECT s
719
                    FROM %s s
720
                    JOIN %s r
721
                    WITH s.rumps_id = r.id
722
                    WHERE s.user_id = :userId
723
                    AND r.category_id = :categoryId',
724
                    Ship::class,
725
                    ShipRump::class
726
                )
727
            )
728
            ->setParameters([
729
                'userId' => $userId,
730
                'categoryId' => ShipRumpEnum::SHIP_CATEGORY_STATION
731
            ])
732
            ->getResult();
733
    }
734
735
    public function getAllDockedShips(): array
736
    {
737
        return $this->getEntityManager()->createQuery(
738
            sprintf(
739
                'SELECT s FROM %s s
740
                WHERE s.dock IS NOT NULL',
741
                Ship::class
742
            )
743
        )->getResult();
744
    }
745
746
    public function getAllTractoringShips(): array
747
    {
748
        return $this->getEntityManager()->createQuery(
749
            sprintf(
750
                'SELECT s FROM %s s
751
                WHERE s.tractored_ship_id IS NOT NULL',
752
                Ship::class
753
            )
754
        )->getResult();
755
    }
756
757
    public function getPirateTargets(ShipInterface $ship): array
758
    {
759
        $layer = $ship->getLayer();
760
        if ($layer === null) {
761
            return [];
762
        }
763
764
        $range = $ship->getSensorRange() * 2;
765
766
        return $this->getEntityManager()->createQuery(
767
            sprintf(
768
                'SELECT s FROM %s s
769
                JOIN %s u
770
                WITH s.user_id = u.id
771
                WHERE s.cx BETWEEN :minX AND :maxX
772
                AND s.cy BETWEEN :minY AND :maxY
773
                AND s.layer_id = :layerId
774
                AND s.type = :shipType
775
                AND (s.fleets_id IS NULL OR s.is_fleet_leader = true)
776
                AND u.id >= :firstUserId
777
                AND u.state >= :stateActive
778
                AND u.creation < :eightWeeksEarlier
779
                AND (u.vac_active = false OR u.vac_request_date > :vacationThreshold)',
780
                Ship::class,
781
                User::class
782
            )
783
        )
784
            ->setParameters([
785
                'minX' => $ship->getCx() - $range,
786
                'maxX' => $ship->getCx() + $range,
787
                'minY' => $ship->getCY() - $range,
788
                'maxY' => $ship->getCY() + $range,
789
                'layerId' => $layer->getId(),
790
                'shipType' => SpacecraftTypeEnum::SPACECRAFT_TYPE_SHIP->value,
791
                'firstUserId' => UserEnum::USER_FIRST_ID,
792
                'stateActive' => UserEnum::USER_STATE_ACTIVE,
793
                'eightWeeksEarlier' => time() - TimeConstants::EIGHT_WEEKS_IN_SECONDS,
794
                'vacationThreshold' => time() - UserEnum::VACATION_DELAY_IN_SECONDS
795
            ])
796
            ->getResult();
797
    }
798
799
    public function getPirateFriends(ShipInterface $ship): array
800
    {
801
        $layer = $ship->getLayer();
802
        if ($layer === null) {
803
            return [];
804
        }
805
806
        $range = $ship->getSensorRange() * 3;
807
808
        return $this->getEntityManager()->createQuery(
809
            sprintf(
810
                'SELECT s FROM %s s
811
                WHERE s.cx BETWEEN :minX AND :maxX
812
                AND s.cy BETWEEN :minY AND :maxY
813
                AND s.layer_id = :layerId
814
                AND s.is_fleet_leader = true',
815
                Ship::class
816
            )
817
        )
818
            ->setParameters([
819
                'minX' => $ship->getCx() - $range,
820
                'maxX' => $ship->getCx() + $range,
821
                'minY' => $ship->getCY() - $range,
822
                'maxY' => $ship->getCY() + $range,
823
                'layerId' => $layer->getId()
824
            ])
825
            ->getResult();
826
    }
827
828
    public function truncateAllShips(): void
829
    {
830
        $this->getEntityManager()->createQuery(
831
            sprintf(
832
                'DELETE FROM %s s',
833
                Ship::class
834
            )
835
        )->execute();
836
    }
837
}
838