Passed
Push — master ( 55cfd1...cb4d54 )
by Nico
22:41
created

ShipRepository::isCloakedShipAtShipLocation()   A

Complexity

Conditions 1
Paths 1

Size

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

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