Passed
Pull Request — master (#1825)
by Nico
59:43 queued 25:38
created

ShipRepository::getStationConstructions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 17
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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