Passed
Push — master ( 48bfaa...170129 )
by Nico
25:44
created

Ship::getIsFleetLeader()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 1
nc 2
nop 0
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 6
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Stu\Orm\Entity;
6
7
use Doctrine\Common\Collections\ArrayCollection;
8
use Doctrine\Common\Collections\Collection;
9
use Doctrine\ORM\Mapping\Column;
10
use Doctrine\ORM\Mapping\Entity;
11
use Doctrine\ORM\Mapping\GeneratedValue;
12
use Doctrine\ORM\Mapping\Id;
13
use Doctrine\ORM\Mapping\Index;
14
use Doctrine\ORM\Mapping\JoinColumn;
15
use Doctrine\ORM\Mapping\ManyToOne;
16
use Doctrine\ORM\Mapping\OneToMany;
17
use Doctrine\ORM\Mapping\OneToOne;
18
use Doctrine\ORM\Mapping\OrderBy;
19
use Doctrine\ORM\Mapping\Table;
20
use RuntimeException;
21
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...
22
use Stu\Component\Map\MapEnum;
23
use Stu\Component\Ship\ShipAlertStateEnum;
24
use Stu\Component\Ship\ShipLSSModeEnum;
25
use Stu\Component\Ship\ShipModuleTypeEnum;
26
use Stu\Component\Ship\ShipRumpEnum;
27
use Stu\Component\Ship\ShipStateEnum;
28
use Stu\Component\Ship\SpacecraftTypeEnum;
29
use Stu\Component\Ship\System\ShipSystemModeEnum;
30
use Stu\Component\Ship\System\ShipSystemTypeEnum;
31
use Stu\Component\Ship\System\Type\TorpedoStorageShipSystem;
32
use Stu\Component\Ship\System\Type\TractorBeamShipSystem;
33
use Stu\Component\Station\StationUtility;
34
use Stu\Lib\Map\Location;
35
use Stu\Module\Control\GameControllerInterface;
36
use Stu\Module\Logging\LoggerUtilInterface;
37
use Stu\Module\Ship\Lib\Battle\FightLib;
38
39
#[Table(name: 'stu_ships')]
40
#[Index(name: 'ship_fleet_idx', columns: ['fleets_id'])]
41
#[Index(name: 'ship_map_idx', columns: ['map_id'])]
42
#[Index(name: 'ship_starsystem_map_idx', columns: ['starsystem_map_id'])]
43
#[Index(name: 'outer_system_location_idx', columns: ['cx', 'cy'])]
44
#[Index(name: 'ship_rump_idx', columns: ['rumps_id'])]
45
#[Index(name: 'ship_web_idx', columns: ['holding_web_id'])]
46
#[Index(name: 'ship_user_idx', columns: ['user_id'])]
47
#[Index(name: 'ship_tractored_idx', columns: ['tractored_ship_id'])]
48
#[Index(name: 'ship_influence_area_idx', columns: ['influence_area_id'])]
49
#[Entity(repositoryClass: 'Stu\Orm\Repository\ShipRepository')]
50
class Ship implements ShipInterface
51
{
52
    #[Id]
53
    #[Column(type: 'integer')]
54
    #[GeneratedValue(strategy: 'IDENTITY')]
55
    private ?int $id = null;
56
57
    #[Column(type: 'integer')]
58
    private int $user_id = 0;
59
60
    #[Column(type: 'integer')]
61
    private int $rumps_id = 0;
0 ignored issues
show
introduced by
The private property $rumps_id is not used, and could be removed.
Loading history...
62
63
    #[Column(type: 'integer', nullable: true)]
64
    private ?int $plans_id = null;
0 ignored issues
show
introduced by
The private property $plans_id is not used, and could be removed.
Loading history...
65
66
    #[Column(type: 'integer', nullable: true)]
67
    private ?int $fleets_id = null;
68
69
    #[Column(type: 'integer', length: 5)]
70
    private int $layer_id = MapEnum::DEFAULT_LAYER;
71
72
    #[Column(type: 'integer', length: 5)]
73
    private int $cx = 0;
74
75
    #[Column(type: 'integer', length: 5)]
76
    private int $cy = 0;
77
78
    #[Column(type: 'smallint', length: 1)]
79
    private int $direction = 0;
80
81
    #[Column(type: 'string')]
82
    private string $name = '';
83
84
    #[Column(type: 'smallint', length: 1, enumType: ShipAlertStateEnum::class)]
85
    private ShipAlertStateEnum $alvl = ShipAlertStateEnum::ALERT_GREEN;
86
87
    #[Column(type: 'smallint', length: 1)]
88
    private int $lss_mode = ShipLSSModeEnum::LSS_NORMAL;
89
90
    #[Column(type: 'integer', length: 6)]
91
    private int $huelle = 0;
92
93
    #[Column(type: 'integer', length: 6)]
94
    private int $max_huelle = 0;
95
96
    #[Column(type: 'integer', length: 6)]
97
    private int $schilde = 0;
98
99
    #[Column(type: 'integer', length: 6)]
100
    private int $max_schilde = 0;
101
102
    #[Column(type: 'integer', nullable: true)]
103
    private ?int $tractored_ship_id = null;
104
105
    #[Column(type: 'integer', nullable: true)]
106
    private ?int $holding_web_id = null;
107
108
    #[Column(type: 'integer', nullable: true)]
109
    private ?int $dock = null;
110
111
    #[Column(type: 'integer')]
112
    private int $former_rumps_id = 0;
113
114
    #[Column(type: 'smallint', length: 1, enumType: SpacecraftTypeEnum::class)]
115
    private SpacecraftTypeEnum $type = SpacecraftTypeEnum::SPACECRAFT_TYPE_SHIP;
116
117
    #[Column(type: 'integer')]
118
    private int $database_id = 0;
119
120
    #[Column(type: 'boolean')]
121
    private bool $is_destroyed = false;
122
123
    #[Column(type: 'boolean')]
124
    private bool $disabled = false;
125
126
    #[Column(type: 'smallint', length: 3)]
127
    private int $hit_chance = 0;
128
129
    #[Column(type: 'smallint', length: 3)]
130
    private int $evade_chance = 0;
131
132
    #[Column(type: 'smallint', length: 4)]
133
    private int $base_damage = 0;
134
135
    #[Column(type: 'smallint', length: 3)]
136
    private int $sensor_range = 0;
137
138
    #[Column(type: 'integer')]
139
    private int $shield_regeneration_timer = 0;
140
141
    #[Column(type: 'smallint', enumType: ShipStateEnum::class)]
142
    private ShipStateEnum $state = ShipStateEnum::SHIP_STATE_NONE;
143
144
    #[Column(type: 'integer', nullable: true)]
145
    private ?int $astro_start_turn = null;
146
147
    #[Column(type: 'boolean')]
148
    private bool $is_fleet_leader = false;
149
150
    #[Column(type: 'integer', nullable: true)]
151
    private ?int $map_id = null;
0 ignored issues
show
introduced by
The private property $map_id is not used, and could be removed.
Loading history...
152
153
    #[Column(type: 'integer', nullable: true)]
154
    private ?int $starsystem_map_id = null;
0 ignored issues
show
introduced by
The private property $starsystem_map_id is not used, and could be removed.
Loading history...
155
156
    #[Column(type: 'integer', nullable: true)]
157
    private ?int $influence_area_id = null;
0 ignored issues
show
introduced by
The private property $influence_area_id is not used, and could be removed.
Loading history...
158
159
    #[Column(type: 'boolean')]
160
    private bool $in_emergency = false;
161
162
    #[ManyToOne(targetEntity: 'Fleet', inversedBy: 'ships')]
163
    #[JoinColumn(name: 'fleets_id', referencedColumnName: 'id')]
164
    private ?FleetInterface $fleet = null;
165
166
    #[OneToOne(targetEntity: 'TradePost', mappedBy: 'ship')]
167
    private ?TradePostInterface $tradePost = null;
168
169
    #[ManyToOne(targetEntity: 'Ship', inversedBy: 'dockedShips')]
170
    #[JoinColumn(name: 'dock', referencedColumnName: 'id')]
171
    private ?ShipInterface $dockedTo = null;
172
173
    /**
174
     * @var ArrayCollection<int, ShipInterface>
175
     */
176
    #[OneToMany(targetEntity: 'Ship', mappedBy: 'dockedTo', indexBy: 'id')]
177
    #[OrderBy(['fleets_id' => 'DESC', 'is_fleet_leader' => 'DESC'])]
178
    private Collection $dockedShips;
179
180
    /**
181
     * @var ArrayCollection<int, DockingPrivilegeInterface>
182
     */
183
    #[OneToMany(targetEntity: 'DockingPrivilege', mappedBy: 'ship')]
184
    private Collection $dockingPrivileges;
185
186
    #[OneToOne(targetEntity: 'Ship')]
187
    #[JoinColumn(name: 'tractored_ship_id', referencedColumnName: 'id')]
188
    private ?ShipInterface $tractoredShip = null;
189
190
    #[OneToOne(targetEntity: 'Ship', mappedBy: 'tractoredShip')]
191
    private ?ShipInterface $tractoringShip = null;
192
193
    #[ManyToOne(targetEntity: 'TholianWeb')]
194
    #[JoinColumn(name: 'holding_web_id', referencedColumnName: 'id', onDelete: 'CASCADE')]
195
    private ?TholianWebInterface $holdingWeb = null;
196
197
    #[ManyToOne(targetEntity: 'User')]
198
    #[JoinColumn(name: 'user_id', referencedColumnName: 'id', onDelete: 'CASCADE')]
199
    private UserInterface $user;
200
201
    /**
202
     * @var ArrayCollection<int, ShipCrewInterface>
203
     */
204
    #[OneToMany(targetEntity: 'ShipCrew', mappedBy: 'ship', indexBy: 'id')]
205
    #[OrderBy(['id' => 'ASC'])]
206
    private Collection $crew;
207
208
    #[OneToOne(targetEntity: 'TorpedoStorage', mappedBy: 'ship')]
209
    private ?TorpedoStorageInterface $torpedoStorage = null;
210
211
    /**
212
     * @var ArrayCollection<int, ShipSystemInterface>
213
     */
214
    #[OneToMany(targetEntity: 'ShipSystem', mappedBy: 'ship', indexBy: 'system_type')]
215
    #[OrderBy(['system_type' => 'ASC'])]
216
    private Collection $systems;
217
218
    #[ManyToOne(targetEntity: 'ShipRump')]
219
    #[JoinColumn(name: 'rumps_id', referencedColumnName: 'id')]
220
    private ShipRumpInterface $rump;
221
222
    #[ManyToOne(targetEntity: 'ShipBuildplan')]
223
    #[JoinColumn(name: 'plans_id', referencedColumnName: 'id', onDelete: 'CASCADE')]
224
    private ?ShipBuildplanInterface $buildplan = null;
225
226
    /**
227
     * @var ArrayCollection<int, StorageInterface>
228
     */
229
    #[OneToMany(targetEntity: 'Storage', mappedBy: 'ship', indexBy: 'commodity_id')]
230
    #[OrderBy(['commodity_id' => 'ASC'])]
231
    private Collection $storage;
232
233
    #[ManyToOne(targetEntity: 'Map')]
234
    #[JoinColumn(name: 'map_id', referencedColumnName: 'id')]
235
    private ?MapInterface $map = null;
236
237
    #[ManyToOne(targetEntity: 'StarSystemMap')]
238
    #[JoinColumn(name: 'starsystem_map_id', referencedColumnName: 'id')]
239
    private ?StarSystemMapInterface $starsystem_map = null;
240
241
    #[ManyToOne(targetEntity: 'StarSystem')]
242
    #[JoinColumn(name: 'influence_area_id', referencedColumnName: 'id')]
243
    private ?StarSystemInterface $influenceArea = null;
244
245
    /**
246
     * @var ArrayCollection<int, ShipLogInterface>
247
     */
248
    #[OneToMany(targetEntity: 'ShipLog', mappedBy: 'ship', fetch: 'EXTRA_LAZY')]
249
    #[OrderBy(['id' => 'DESC'])]
250
    private Collection $logbook;
251
252
    #[OneToOne(targetEntity: 'ShipTakeover', mappedBy: 'source')]
253
    private ?ShipTakeoverInterface $takeoverActive = null;
254
255
    #[OneToOne(targetEntity: 'ShipTakeover', mappedBy: 'target')]
256
    private ?ShipTakeoverInterface $takeoverPassive = null;
257
258
    public function __construct()
259
    {
260
        $this->dockedShips = new ArrayCollection();
261
        $this->dockingPrivileges = new ArrayCollection();
262
        $this->crew = new ArrayCollection();
263
        $this->systems = new ArrayCollection();
264
        $this->storage = new ArrayCollection();
265
        $this->logbook = new ArrayCollection();
266
    }
267
268
    public function getId(): int
269
    {
270
        if ($this->id === null) {
271
            throw new RuntimeException('entity not yet persisted');
272
        }
273
274
        return $this->id;
275
    }
276
277
    public function getUserId(): int
278
    {
279
        return $this->user_id;
280
    }
281
282
    public function getUserName(): string
283
    {
284
        return $this->getUser()->getName();
285
    }
286
287
    public function getFleetId(): ?int
288
    {
289
        return $this->fleets_id;
290
    }
291
292
    public function setFleetId(?int $fleetId): ShipInterface
293
    {
294
        $this->fleets_id = $fleetId;
295
        return $this;
296
    }
297
298
    public function getSystemsId(): ?int
299
    {
300
        return $this->getSystem() !== null ? $this->getSystem()->getId() : null;
301
    }
302
303
    public function getLayer(): ?LayerInterface
304
    {
305
        return $this->getLocation()->getLayer();
306
    }
307
308
    public function getLayerId(): int
309
    {
310
        return $this->layer_id;
311
    }
312
313
    public function setLayerId(int $layerId): ShipInterface
314
    {
315
        $this->layer_id = $layerId;
316
        return $this;
317
    }
318
319
    public function getCx(): int
320
    {
321
        return $this->cx;
322
    }
323
324
    public function setCx(int $cx): ShipInterface
325
    {
326
        $this->cx = $cx;
327
        return $this;
328
    }
329
330
    public function getCy(): int
331
    {
332
        return $this->cy;
333
    }
334
335
    public function setCy(int $cy): ShipInterface
336
    {
337
        $this->cy = $cy;
338
        return $this;
339
    }
340
341
    public function getSx(): int
342
    {
343
        return $this->getStarsystemMap()->getSx();
344
    }
345
346
    public function getSy(): int
347
    {
348
        return $this->getStarsystemMap()->getSy();
349
    }
350
351
    public function getFlightDirection(): int
352
    {
353
        return $this->direction;
354
    }
355
356
    public function setFlightDirection(int $direction): ShipInterface
357
    {
358
        $this->direction = $direction;
359
        return $this;
360
    }
361
362
    public function getName(): string
363
    {
364
        return $this->name;
365
    }
366
367
    public function setName(string $name): ShipInterface
368
    {
369
        $this->name = $name;
370
        return $this;
371
    }
372
373
    public function getLSSmode(): int
374
    {
375
        return $this->lss_mode;
376
    }
377
378
    public function isLSSModeNormal(): bool
379
    {
380
        return $this->getLSSMode() === ShipLSSModeEnum::LSS_NORMAL;
381
    }
382
383
    public function isLSSModeBorder(): bool
384
    {
385
        return $this->getLSSMode() === ShipLSSModeEnum::LSS_BORDER;
386
    }
387
388
    public function setLSSMode(int $lssMode): ShipInterface
389
    {
390
        $this->lss_mode = $lssMode;
391
        return $this;
392
    }
393
394
    public function getAlertState(): ShipAlertStateEnum
395
    {
396
        return $this->alvl;
397
    }
398
399
    public function setAlertState(ShipAlertStateEnum $state): ShipInterface
400
    {
401
        $this->alvl = $state;
402
        return $this;
403
    }
404
405
    public function setAlertStateGreen(): ShipInterface
406
    {
407
        return $this->setAlertState(ShipAlertStateEnum::ALERT_GREEN);
408
    }
409
410
    public function isSystemHealthy(ShipSystemTypeEnum $type): bool
411
    {
412
        if (!$this->hasShipSystem($type)) {
413
            return false;
414
        }
415
416
        return $this->getShipSystem($type)->getStatus() > 0;
417
    }
418
419
    public function getSystemState(ShipSystemTypeEnum $type): bool
420
    {
421
        if (!$this->hasShipSystem($type)) {
422
            return false;
423
        }
424
425
        return $this->getShipSystem($type)->getMode() === ShipSystemModeEnum::MODE_ON
426
            || $this->getShipSystem($type)->getMode() === ShipSystemModeEnum::MODE_ALWAYS_ON;
427
    }
428
429
    public function getImpulseState(): bool
430
    {
431
        return $this->getSystemState(ShipSystemTypeEnum::SYSTEM_IMPULSEDRIVE);
432
    }
433
434
    public function getWarpDriveState(): bool
435
    {
436
        return $this->getSystemState(ShipSystemTypeEnum::SYSTEM_WARPDRIVE);
437
    }
438
439
    public function isWarped(): bool
440
    {
441
        $tractoringShip = $this->getTractoringShip();
442
443
        if ($tractoringShip !== null) {
444
            return $tractoringShip->getWarpDriveState();
445
        }
446
447
        return $this->getWarpDriveState();
448
    }
449
450
    public function getWebState(): bool
451
    {
452
        return $this->getHoldingWeb() !== null;
453
    }
454
455
    public function getCloakState(): bool
456
    {
457
        return $this->getSystemState(ShipSystemTypeEnum::SYSTEM_CLOAK);
458
    }
459
460
    public function getTachyonState(): bool
461
    {
462
        return $this->getSystemState(ShipSystemTypeEnum::SYSTEM_TACHYON_SCANNER);
463
    }
464
465
    public function getSubspaceState(): bool
466
    {
467
        return $this->getSystemState(ShipSystemTypeEnum::SYSTEM_SUBSPACE_SCANNER);
468
    }
469
470
    public function getAstroState(): bool
471
    {
472
        return $this->getSystemState(ShipSystemTypeEnum::SYSTEM_ASTRO_LABORATORY);
473
    }
474
475
    public function getRPGModuleState(): bool
476
    {
477
        return $this->getSystemState(ShipSystemTypeEnum::SYSTEM_RPG_MODULE);
478
    }
479
480
    public function getConstructionHubState(): bool
481
    {
482
        return $this->getSystemState(ShipSystemTypeEnum::SYSTEM_CONSTRUCTION_HUB);
483
    }
484
485
    public function getHull(): int
486
    {
487
        return $this->huelle;
488
    }
489
490
    public function setHuell(int $hull): ShipInterface
491
    {
492
        $this->huelle = $hull;
493
        return $this;
494
    }
495
496
    public function getMaxHull(): int
497
    {
498
        return $this->max_huelle;
499
    }
500
501
    public function setMaxHuell(int $maxHull): ShipInterface
502
    {
503
        $this->max_huelle = $maxHull;
504
        return $this;
505
    }
506
507
    public function getShield(): int
508
    {
509
        return $this->schilde;
510
    }
511
512
    public function setShield(int $schilde): ShipInterface
513
    {
514
        $this->schilde = $schilde;
515
        return $this;
516
    }
517
518
    /**
519
     * proportional to shield system status
520
     */
521
    public function getMaxShield(bool $isTheoretical = false): int
522
    {
523
        if ($isTheoretical || !$this->hasShipSystem(ShipSystemTypeEnum::SYSTEM_SHIELDS)) {
524
            return $this->max_schilde;
525
        }
526
527
        return (int) (ceil($this->max_schilde
528
            * $this->getShipSystem(ShipSystemTypeEnum::SYSTEM_SHIELDS)->getStatus() / 100));
529
    }
530
531
    public function setMaxShield(int $maxShields): ShipInterface
532
    {
533
        $this->max_schilde = $maxShields;
534
        return $this;
535
    }
536
537
    public function getHealthPercentage(): float
538
    {
539
        return ($this->getHull() + $this->getShield())
540
            / ($this->getMaxHull() + $this->getMaxShield(true)) * 100;
541
    }
542
543
    public function getShieldState(): bool
544
    {
545
        return $this->getSystemState(ShipSystemTypeEnum::SYSTEM_SHIELDS);
546
    }
547
548
    public function getNbs(): bool
549
    {
550
        return $this->getSystemState(ShipSystemTypeEnum::SYSTEM_NBS);
551
    }
552
553
    public function getLss(): bool
554
    {
555
        return $this->getSystemState(ShipSystemTypeEnum::SYSTEM_LSS);
556
    }
557
558
    public function getPhaserState(): bool
559
    {
560
        return $this->getSystemState(ShipSystemTypeEnum::SYSTEM_PHASER);
561
    }
562
563
    public function isAlertGreen(): bool
564
    {
565
        return $this->getAlertState() === ShipAlertStateEnum::ALERT_GREEN;
566
    }
567
568
    public function getTorpedoState(): bool
569
    {
570
        return $this->getSystemState(ShipSystemTypeEnum::SYSTEM_TORPEDO);
571
    }
572
573
    public function getFormerRumpId(): int
574
    {
575
        return $this->former_rumps_id;
576
    }
577
578
    public function setFormerRumpId(int $formerShipRumpId): ShipInterface
579
    {
580
        $this->former_rumps_id = $formerShipRumpId;
581
        return $this;
582
    }
583
584
    public function getTorpedoCount(): int
585
    {
586
        if ($this->getTorpedoStorage() === null) {
587
            return 0;
588
        }
589
590
        return $this->getTorpedoStorage()->getStorage()->getAmount();
591
    }
592
593
    public function isBase(): bool
594
    {
595
        return $this->type === SpacecraftTypeEnum::SPACECRAFT_TYPE_STATION;
596
    }
597
598
    public function isTrumfield(): bool
599
    {
600
        return $this->getRump()->isTrumfield();
601
    }
602
603
    public function isShuttle(): bool
604
    {
605
        return $this->getRump()->getCategoryId() === ShipRumpEnum::SHIP_CATEGORY_SHUTTLE;
606
    }
607
608
    public function isConstruction(): bool
609
    {
610
        return $this->getRump()->getCategoryId() === ShipRumpEnum::SHIP_CATEGORY_CONSTRUCTION;
611
    }
612
613
    public function getSpacecraftType(): SpacecraftTypeEnum
614
    {
615
        return $this->type;
616
    }
617
618
    public function setSpacecraftType(SpacecraftTypeEnum $type): ShipInterface
619
    {
620
        $this->type = $type;
621
        return $this;
622
    }
623
624
    public function getDatabaseId(): int
625
    {
626
        return $this->database_id;
627
    }
628
629
    public function setDatabaseId(int $databaseEntryId): ShipInterface
630
    {
631
        $this->database_id = $databaseEntryId;
632
        return $this;
633
    }
634
635
    public function isDestroyed(): bool
636
    {
637
        return $this->is_destroyed;
638
    }
639
640
    public function setIsDestroyed(bool $isDestroyed): ShipInterface
641
    {
642
        $this->is_destroyed = $isDestroyed;
643
        return $this;
644
    }
645
646
    public function isDisabled(): bool
647
    {
648
        return $this->disabled;
649
    }
650
651
    public function setDisabled(bool $isDisabled): ShipInterface
652
    {
653
        $this->disabled = $isDisabled;
654
        return $this;
655
    }
656
657
658
659
    /**
660
     * proportional to computer system status
661
     */
662
    public function getHitChance(): int
663
    {
664
        if (!$this->hasShipSystem(ShipSystemTypeEnum::SYSTEM_COMPUTER)) {
665
            return $this->hit_chance;
666
        }
667
668
        return (int) (ceil($this->hit_chance
669
            * $this->getShipSystem(ShipSystemTypeEnum::SYSTEM_COMPUTER)->getStatus() / 100));
670
    }
671
672
    public function setHitChance(int $hitChance): ShipInterface
673
    {
674
        $this->hit_chance = $hitChance;
675
        return $this;
676
    }
677
678
    /**
679
     * proportional to impulsedrive system status
680
     */
681
    public function getEvadeChance(): int
682
    {
683
        if (!$this->hasShipSystem(ShipSystemTypeEnum::SYSTEM_IMPULSEDRIVE)) {
684
            return $this->evade_chance;
685
        }
686
687
        return (int) (ceil($this->evade_chance
688
            * $this->getShipSystem(ShipSystemTypeEnum::SYSTEM_IMPULSEDRIVE)->getStatus() / 100));
689
    }
690
691
    public function setEvadeChance(int $evadeChance): ShipInterface
692
    {
693
        $this->evade_chance = $evadeChance;
694
        return $this;
695
    }
696
697
    /**
698
     * proportional to energy weapon system status
699
     */
700
    public function getBaseDamage(): int
701
    {
702
        if (!$this->hasShipSystem(ShipSystemTypeEnum::SYSTEM_PHASER)) {
703
            return $this->base_damage;
704
        }
705
706
        return (int) (ceil($this->base_damage
707
            * $this->getShipSystem(ShipSystemTypeEnum::SYSTEM_PHASER)->getStatus() / 100));
708
    }
709
710
    public function setBaseDamage(int $baseDamage): ShipInterface
711
    {
712
        $this->base_damage = $baseDamage;
713
        return $this;
714
    }
715
716
    /**
717
     * proportional to sensor system status
718
     */
719
    public function getSensorRange(): int
720
    {
721
        if (!$this->hasShipSystem(ShipSystemTypeEnum::SYSTEM_LSS)) {
722
            return $this->sensor_range;
723
        }
724
725
        return (int) (ceil($this->sensor_range
726
            * $this->getShipSystem(ShipSystemTypeEnum::SYSTEM_LSS)->getStatus() / 100));
727
    }
728
729
    public function setSensorRange(int $sensorRange): ShipInterface
730
    {
731
        $this->sensor_range = $sensorRange;
732
        return $this;
733
    }
734
735
    /**
736
     * proportional to tractor beam system status
737
     */
738
    public function getTractorPayload(): int
739
    {
740
        if (!$this->hasShipSystem(ShipSystemTypeEnum::SYSTEM_TRACTOR_BEAM)) {
741
            return 0;
742
        }
743
744
        return (int) (ceil($this->getRump()->getTractorPayload()
745
            * $this->getShipSystem(ShipSystemTypeEnum::SYSTEM_TRACTOR_BEAM)->getStatus() / 100));
746
    }
747
748
    public function getShieldRegenerationTimer(): int
749
    {
750
        return $this->shield_regeneration_timer;
751
    }
752
753
    public function setShieldRegenerationTimer(int $shieldRegenerationTimer): ShipInterface
754
    {
755
        $this->shield_regeneration_timer = $shieldRegenerationTimer;
756
        return $this;
757
    }
758
759
    public function getState(): ShipStateEnum
760
    {
761
        return $this->state;
762
    }
763
764
    public function setState(ShipStateEnum $state): ShipInterface
765
    {
766
        $this->state = $state;
767
        return $this;
768
    }
769
770
    public function getIsInEmergency(): bool
771
    {
772
        return $this->in_emergency;
773
    }
774
775
    public function setIsInEmergency(bool $inEmergency): ShipInterface
776
    {
777
        $this->in_emergency = $inEmergency;
778
        return $this;
779
    }
780
781
    public function isUnderRepair(): bool
782
    {
783
        return $this->getState() === ShipStateEnum::SHIP_STATE_REPAIR_ACTIVE
784
            || $this->getState() === ShipStateEnum::SHIP_STATE_REPAIR_PASSIVE;
785
    }
786
787
    public function getAstroStartTurn(): ?int
788
    {
789
        return $this->astro_start_turn;
790
    }
791
792
    public function setAstroStartTurn(?int $turn): ShipInterface
793
    {
794
        $this->astro_start_turn = $turn;
795
        return $this;
796
    }
797
798
    public function getIsFleetLeader(): bool
799
    {
800
        return $this->getFleet() !== null && $this->is_fleet_leader;
801
    }
802
803
    public function setIsFleetLeader(bool $isFleetLeader): ShipInterface
804
    {
805
        $this->is_fleet_leader = $isFleetLeader;
806
        return $this;
807
    }
808
809
    public function getCrewAssignments(): Collection
810
    {
811
        return $this->crew;
812
    }
813
814
    public function getPosX(): int
815
    {
816
        if ($this->getSystem() !== null) {
817
            return $this->getSX();
818
        }
819
        return $this->getCX();
820
    }
821
822
    public function getPosY(): int
823
    {
824
        if ($this->getSystem() !== null) {
825
            return $this->getSY();
826
        }
827
        return $this->getCY();
828
    }
829
830
    public function getCrewCount(): int
831
    {
832
        return $this->getCrewAssignments()->count();
833
    }
834
835
    public function getNeededCrewCount(): int
836
    {
837
        $buildplan = $this->getBuildplan();
838
        if ($buildplan === null) {
839
            return 0;
840
        }
841
842
        return $buildplan->getCrew();
843
    }
844
845
    public function getExcessCrewCount(): int
846
    {
847
        return $this->getCrewCount() - $this->getNeededCrewCount();
848
    }
849
850
    public function hasEnoughCrew(?GameControllerInterface $game = null): bool
851
    {
852
        $buildplan = $this->getBuildplan();
853
854
        if ($buildplan === null) {
855
            if ($game !== null) {
856
                $game->addInformation(_("Keine Crew vorhanden"));
857
            }
858
            return false;
859
        }
860
861
        $result = $buildplan->getCrew() <= 0
862
            || $this->getCrewCount() >= $buildplan->getCrew();
863
864
        if (!$result && $game !== null) {
865
            $game->addInformationf(
866
                _("Es werden %d Crewmitglieder benötigt"),
867
                $buildplan->getCrew()
868
            );
869
        }
870
871
        return $result;
872
    }
873
874
    public function getFleet(): ?FleetInterface
875
    {
876
        return $this->fleet;
877
    }
878
879
    public function setFleet(?FleetInterface $fleet): ShipInterface
880
    {
881
        $this->fleet = $fleet;
882
        return $this;
883
    }
884
885
    public function isFleetLeader(): bool
886
    {
887
        return $this->getIsFleetLeader();
888
    }
889
890
    public function getUser(): UserInterface
891
    {
892
        return $this->user;
893
    }
894
895
    public function setUser(UserInterface $user): ShipInterface
896
    {
897
        $this->user = $user;
898
        return $this;
899
    }
900
901
    public function getSystem(): ?StarSystemInterface
902
    {
903
        return $this->getStarsystemMap() !== null ? $this->getStarsystemMap()->getSystem() : null;
904
    }
905
906
    public function getModules(): array
907
    {
908
        $modules = [];
909
910
        $buildplan = $this->getBuildplan();
911
        if ($buildplan === null) {
912
            return $modules;
913
        }
914
915
        foreach ($buildplan->getModules() as $obj) {
916
            $module = $obj->getModule();
917
            $index = $module->getType() === ShipModuleTypeEnum::SPECIAL ? $module->getId() : $module->getType()->value;
918
            $modules[$index] = $module;
919
        }
920
921
        if ($this->isBase()) {
922
            foreach ($this->getSystems() as $system) {
923
                $module = $system->getModule();
924
925
                if ($module !== null) {
926
                    $index = $module->getType() === ShipModuleTypeEnum::SPECIAL ? $module->getId() : $module->getType()->value;
927
                    $modules[$index] = $module;
928
                }
929
            }
930
        }
931
932
        return $modules;
933
    }
934
935
    public function isDeflectorHealthy(): bool
936
    {
937
        return $this->isSystemHealthy(ShipSystemTypeEnum::SYSTEM_DEFLECTOR);
938
    }
939
940
    public function isTroopQuartersHealthy(): bool
941
    {
942
        return $this->isSystemHealthy(ShipSystemTypeEnum::SYSTEM_TROOP_QUARTERS);
943
    }
944
945
    public function isMatrixScannerHealthy(): bool
946
    {
947
        return $this->isSystemHealthy(ShipSystemTypeEnum::SYSTEM_MATRIX_SCANNER);
948
    }
949
950
    public function isTorpedoStorageHealthy(): bool
951
    {
952
        return $this->isSystemHealthy(ShipSystemTypeEnum::SYSTEM_TORPEDO_STORAGE);
953
    }
954
955
    public function isShuttleRampHealthy(): bool
956
    {
957
        return $this->isSystemHealthy(ShipSystemTypeEnum::SYSTEM_SHUTTLE_RAMP);
958
    }
959
960
    public function isWebEmitterHealthy(): bool
961
    {
962
        return $this->isSystemHealthy(ShipSystemTypeEnum::SYSTEM_THOLIAN_WEB);
963
    }
964
965
    public function isWarpAble(): bool
966
    {
967
        return $this->isSystemHealthy(ShipSystemTypeEnum::SYSTEM_WARPDRIVE);
968
    }
969
970
    public function isTractoring(): bool
971
    {
972
        return $this->getTractoredShip() !== null;
973
    }
974
975
    public function isTractored(): bool
976
    {
977
        return $this->getTractoringShip() !== null;
978
    }
979
980
    public function isOverColony(): ?ColonyInterface
981
    {
982
        return $this->getStarsystemMap() !== null ? $this->getStarsystemMap()->getColony() : null;
983
    }
984
985
    public function isOverSystem(): ?StarSystemInterface
986
    {
987
        if ($this->getSystem() !== null) {
988
            return null;
989
        }
990
991
        return $this->getMap()->getSystem();
992
    }
993
994
    public function isOverWormhole(): bool
995
    {
996
        return $this->getMap() !== null && $this->getMap()->getRandomWormholeEntry() !== null;
997
    }
998
999
    public function isWarpPossible(): bool
1000
    {
1001
        return $this->hasShipSystem(ShipSystemTypeEnum::SYSTEM_WARPDRIVE) && $this->getSystem() === null;
1002
    }
1003
1004
    public function getTorpedo(): ?TorpedoTypeInterface
1005
    {
1006
        if ($this->getTorpedoStorage() === null) {
1007
            return null;
1008
        }
1009
1010
        return $this->getTorpedoStorage()->getTorpedo();
1011
    }
1012
1013
    public function getTorpedoStorage(): ?TorpedoStorageInterface
1014
    {
1015
        return $this->torpedoStorage;
1016
    }
1017
1018
    public function setTorpedoStorage(?TorpedoStorageInterface $torpedoStorage): ShipInterface
1019
    {
1020
        $this->torpedoStorage = $torpedoStorage;
1021
        return $this;
1022
    }
1023
1024
    public function getStorage(): Collection
1025
    {
1026
        return $this->storage;
1027
    }
1028
1029
    public function getLogbook(): Collection
1030
    {
1031
        return $this->logbook;
1032
    }
1033
1034
    public function getTakeoverActive(): ?ShipTakeoverInterface
1035
    {
1036
        return $this->takeoverActive;
1037
    }
1038
1039
    public function setTakeoverActive(?ShipTakeoverInterface $takeover): ShipInterface
1040
    {
1041
        $this->takeoverActive = $takeover;
1042
1043
        return $this;
1044
    }
1045
1046
    public function getTakeoverPassive(): ?ShipTakeoverInterface
1047
    {
1048
        return $this->takeoverPassive;
1049
    }
1050
1051
    public function setTakeoverPassive(?ShipTakeoverInterface $takeover): ShipInterface
1052
    {
1053
        $this->takeoverPassive = $takeover;
1054
1055
        return $this;
1056
    }
1057
1058
    public function getStorageSum(): int
1059
    {
1060
        return array_reduce(
1061
            $this->getStorage()->getValues(),
1062
            fn (int $sum, StorageInterface $storage): int => $sum + $storage->getAmount(),
1063
            0
1064
        );
1065
    }
1066
1067
    public function getMaxStorage(): int
1068
    {
1069
        return $this->getRump()->getStorage();
1070
    }
1071
1072
    public function getBeamableStorage(): array
1073
    {
1074
        return array_filter(
1075
            $this->getStorage()->getValues(),
1076
            fn (StorageInterface $storage): bool => $storage->getCommodity()->isBeamable() === true
1077
        );
1078
    }
1079
1080
    public function getTradePost(): ?TradePostInterface
1081
    {
1082
        return $this->tradePost;
1083
    }
1084
1085
    public function setTradePost(?TradePostInterface $tradePost): ShipInterface
1086
    {
1087
        $this->tradePost = $tradePost;
1088
1089
        return $this;
1090
    }
1091
1092
    public function getMap(): ?MapInterface
1093
    {
1094
        return $this->map;
1095
    }
1096
1097
    public function getMapRegion(): ?MapRegionInterface
1098
    {
1099
        $map = $this->getMap();
1100
        if ($map === null) {
1101
            return null;
1102
        }
1103
1104
        return $map->getMapRegion();
1105
    }
1106
1107
    public function updateLocation(MapInterface|StarSystemMapInterface|Location $location): ShipInterface
1108
    {
1109
        if ($location instanceof MapInterface) {
1110
            $this->setMap($location);
1111
            $this->setStarsystemMap(null);
1112
        } elseif ($location instanceof StarSystemMapInterface) {
1113
            $this->setMap(null);
1114
            $this->setStarsystemMap($location);
1115
        } else {
1116
            $this->updateLocation($location->get());
1117
        }
1118
1119
        return $this;
1120
    }
1121
1122
    public function setMap(?MapInterface $map): ShipInterface
1123
    {
1124
        $this->map = $map;
1125
1126
        if ($map !== null) {
1127
            $this->setLayerId($map->getLayer()->getId());
1128
            $this->setCx($map->getCx());
1129
            $this->setCy($map->getCy());
1130
        }
1131
1132
        return $this;
1133
    }
1134
1135
    public function getStarsystemMap(): ?StarSystemMapInterface
1136
    {
1137
        return $this->starsystem_map;
1138
    }
1139
1140
    public function setStarsystemMap(?StarSystemMapInterface $systemMap): ShipInterface
1141
    {
1142
        $this->starsystem_map = $systemMap;
1143
1144
        if ($systemMap !== null) {
1145
            $system = $systemMap->getSystem();
1146
            $layer = $system->getLayer();
1147
            if ($layer === null) {
1148
                $this->setLayerId(MapEnum::LAYER_ID_WORMHOLES);
1149
            } else {
1150
                $this->setLayerId($layer->getId());
1151
            }
1152
            $this->setCx($systemMap->getSystem()->getCx());
1153
            $this->setCy($systemMap->getSystem()->getCy());
1154
        }
1155
1156
        return $this;
1157
    }
1158
1159
    public function getLocation(): Location
1160
    {
1161
        return new Location($this->getMap(), $this->getStarsystemMap());
1162
    }
1163
1164
    public function getInfluenceArea(): ?StarSystemInterface
1165
    {
1166
        return $this->influenceArea;
1167
    }
1168
1169
    public function setInfluenceArea(?StarSystemInterface $influenceArea): ShipInterface
1170
    {
1171
        $this->influenceArea = $influenceArea;
1172
        return $this;
1173
    }
1174
1175
    public function getBeamFactor(): int
1176
    {
1177
        return $this->getRump()->getBeamFactor();
1178
    }
1179
1180
    public function getSectorString(): string
1181
    {
1182
        if ($this->getStarsystemMap() !== null) {
1183
            return $this->getStarsystemMap()->getSectorString();
1184
        } else {
1185
            return $this->getMap()->getSectorString();
1186
        }
1187
    }
1188
1189
    public function getSectorId(): ?int
1190
    {
1191
        $layer = $this->getLayer();
1192
        if ($layer === null) {
1193
            return null;
1194
        }
1195
1196
        return $layer->getSectorId($this->getMapCX(), $this->getMapCY());
1197
    }
1198
1199
    public function getBuildplan(): ?ShipBuildplanInterface
1200
    {
1201
        return $this->buildplan;
1202
    }
1203
1204
    public function setBuildplan(?ShipBuildplanInterface $shipBuildplan): ShipInterface
1205
    {
1206
        $this->buildplan = $shipBuildplan;
1207
        return $this;
1208
    }
1209
1210
    public function getSystems(): Collection
1211
    {
1212
        return $this->systems;
1213
    }
1214
1215
    public function hasShipSystem(ShipSystemTypeEnum $type): bool
1216
    {
1217
        return $this->getSystems()->containsKey($type->value);
1218
    }
1219
1220
    public function getShipSystem(ShipSystemTypeEnum $type): ShipSystemInterface
1221
    {
1222
        $system = $this->getSystems()->get($type->value);
1223
        if ($system === null) {
1224
            throw new RuntimeException(sprintf('system type %d does not exist on ship', $type->value));
1225
        }
1226
1227
        return $system;
1228
    }
1229
1230
    public function getHealthySystems(): array
1231
    {
1232
        $healthySystems = [];
1233
        foreach ($this->getSystems() as $system) {
1234
            if ($system->getStatus() > 0) {
1235
                $healthySystems[] = $system;
1236
            }
1237
        }
1238
        return $healthySystems;
1239
    }
1240
1241
    public function displayNbsActions(): bool
1242
    {
1243
        return !$this->getCloakState()
1244
            && !$this->isWarped();
1245
    }
1246
1247
    public function isTractorbeamPossible(): bool
1248
    {
1249
        return TractorBeamShipSystem::isTractorBeamPossible($this);
1250
    }
1251
1252
    public function isBoardingPossible(): bool
1253
    {
1254
        return FightLib::isBoardingPossible($this);
1255
    }
1256
1257
    public function isInterceptable(): bool
1258
    {
1259
        //TODO can tractored ships be intercepted?!
1260
        return $this->getWarpDriveState();
1261
    }
1262
1263
    public function getMapCX(): int
1264
    {
1265
        return (int) ceil($this->getCX() / MapEnum::FIELDS_PER_SECTION);
1266
    }
1267
1268
    public function getMapCY(): int
1269
    {
1270
        return (int) ceil($this->getCY() / MapEnum::FIELDS_PER_SECTION);
1271
    }
1272
1273
    public function dockedOnTradePost(): bool
1274
    {
1275
        return $this->getDockedTo() && $this->getDockedTo()->getTradePost() !== null;
1276
    }
1277
1278
    public function getDockPrivileges(): Collection
1279
    {
1280
        return $this->dockingPrivileges;
1281
    }
1282
1283
    public function getDockingSlotCount(): int
1284
    {
1285
        return ($this->getState() === ShipStateEnum::SHIP_STATE_UNDER_CONSTRUCTION)
1286
            || ($this->getState() === ShipStateEnum::SHIP_STATE_UNDER_SCRAPPING)
1287
            ? 50 : $this->getRump()->getDockingSlots();
1288
    }
1289
1290
    public function hasFreeDockingSlots(): bool
1291
    {
1292
        return $this->getDockingSlotCount() > $this->getDockedShipCount();
1293
    }
1294
1295
    public function getFreeDockingSlotCount(): int
1296
    {
1297
        return $this->getDockingSlotCount() - $this->getDockedShipCount();
1298
    }
1299
1300
    public function getDockedShipCount(): int
1301
    {
1302
        return $this->dockedShips->count();
1303
    }
1304
1305
    public function getTractoredShip(): ?ShipInterface
1306
    {
1307
        return $this->tractoredShip;
1308
    }
1309
1310
    public function setTractoredShip(?ShipInterface $ship): ShipInterface
1311
    {
1312
        $this->tractoredShip = $ship;
1313
        return $this;
1314
    }
1315
1316
    public function setTractoredShipId(?int $shipId): ShipInterface
1317
    {
1318
        $this->tractored_ship_id = $shipId;
1319
        return $this;
1320
    }
1321
1322
    public function getTractoringShip(): ?ShipInterface
1323
    {
1324
        return $this->tractoringShip;
1325
    }
1326
1327
    public function setTractoringShip(?ShipInterface $ship): ShipInterface
1328
    {
1329
        $this->tractoringShip = $ship;
1330
        return $this;
1331
    }
1332
1333
    public function getHoldingWeb(): ?TholianWebInterface
1334
    {
1335
        return $this->holdingWeb;
1336
    }
1337
1338
    public function setHoldingWeb(?TholianWebInterface $web): ShipInterface
1339
    {
1340
        $this->holdingWeb = $web;
1341
1342
        if ($web === null) {
1343
            $this->holding_web_id = null;
1344
        }
1345
1346
        return $this;
1347
    }
1348
1349
    public function getHoldingWebBackgroundStyle(): string
1350
    {
1351
        if ($this->getHoldingWeb() === null) {
1352
            return '';
1353
        }
1354
1355
        if ($this->getHoldingWeb()->isFinished()) {
1356
            $icon =  'web.png';
1357
        } else {
1358
            $closeTofinish = $this->getHoldingWeb()->getFinishedTime() - time() < TimeConstants::ONE_HOUR_IN_SECONDS;
1359
1360
            $icon = $closeTofinish ? 'web_u.png' : 'web_u2.png';
1361
        }
1362
1363
        return sprintf('src="assets/buttons/%s"; class="indexedGraphics" style="z-index: 5;"', $icon);
1364
    }
1365
1366
    public function getHoldingWebImageStyle(): string
1367
    {
1368
        if ($this->getHoldingWeb() === null) {
1369
            return '';
1370
        }
1371
1372
        if ($this->getHoldingWeb()->isFinished()) {
1373
            $icon =  'webfill.png';
1374
        } else {
1375
            $closeTofinish = $this->getHoldingWeb()->getFinishedTime() - time() < TimeConstants::ONE_HOUR_IN_SECONDS;
1376
1377
            $icon = $closeTofinish ? 'web_ufill.png' : 'web_ufill2.png';
1378
        }
1379
1380
        return $icon;
1381
    }
1382
1383
    public function getCurrentMapField(): StarSystemMapInterface|MapInterface
1384
    {
1385
        return $this->getStarsystemMap() ?? $this->getMap();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getStarsys...ap() ?? $this->getMap() could return the type null which is incompatible with the type-hinted return Stu\Orm\Entity\MapInterf...\StarSystemMapInterface. Consider adding an additional type-check to rule them out.
Loading history...
1386
    }
1387
1388
    private function getShieldRegenerationPercentage(): int
1389
    {
1390
        return $this->isSystemHealthy(ShipSystemTypeEnum::SYSTEM_SHIELDS) ? 10 : 0;
1391
    }
1392
1393
    public function getShieldRegenerationRate(): int
1394
    {
1395
        return (int) ceil(($this->getMaxShield() / 100) * $this->getShieldRegenerationPercentage());
1396
    }
1397
1398
    public function canIntercept(): bool
1399
    {
1400
        return $this->isSystemHealthy(ShipSystemTypeEnum::SYSTEM_WARPDRIVE)
1401
            && !$this->isTractored() && !$this->isTractoring();
1402
    }
1403
1404
    public function canMove(): bool
1405
    {
1406
        return $this->hasShipSystem(ShipSystemTypeEnum::SYSTEM_WARPDRIVE)
1407
            || $this->hasShipSystem(ShipSystemTypeEnum::SYSTEM_IMPULSEDRIVE);
1408
    }
1409
1410
    public function hasActiveWeapon(): bool
1411
    {
1412
        return $this->getPhaserState() || $this->getTorpedoState();
1413
    }
1414
1415
    public function hasEscapePods(): bool
1416
    {
1417
        return $this->getRump()->isEscapePods() && $this->getCrewCount() > 0;
1418
    }
1419
1420
    public function getRepairRate(): int
1421
    {
1422
        // @todo
1423
        return 100;
1424
    }
1425
1426
    public function getRump(): ShipRumpInterface
1427
    {
1428
        return $this->rump;
1429
    }
1430
1431
    public function getRumpId(): int
1432
    {
1433
        return $this->getRump()->getId();
1434
    }
1435
1436
    public function getRumpName(): string
1437
    {
1438
        return $this->getRump()->getName();
1439
    }
1440
1441
    public function setRump(ShipRumpInterface $shipRump): ShipInterface
1442
    {
1443
        $this->rump = $shipRump;
1444
        return $this;
1445
    }
1446
1447
    public function hasPhaser(): bool
1448
    {
1449
        return $this->hasShipSystem(ShipSystemTypeEnum::SYSTEM_PHASER);
1450
    }
1451
1452
    public function hasTorpedo(): bool
1453
    {
1454
        return $this->hasShipSystem(ShipSystemTypeEnum::SYSTEM_TORPEDO);
1455
    }
1456
1457
    public function hasCloak(): bool
1458
    {
1459
        return $this->hasShipSystem(ShipSystemTypeEnum::SYSTEM_CLOAK);
1460
    }
1461
1462
    public function hasTachyonScanner(): bool
1463
    {
1464
        return $this->hasShipSystem(ShipSystemTypeEnum::SYSTEM_TACHYON_SCANNER);
1465
    }
1466
1467
    public function hasShuttleRamp(): bool
1468
    {
1469
        return $this->hasShipSystem(ShipSystemTypeEnum::SYSTEM_SHUTTLE_RAMP);
1470
    }
1471
1472
    public function hasSubspaceScanner(): bool
1473
    {
1474
        return $this->hasShipSystem(ShipSystemTypeEnum::SYSTEM_SUBSPACE_SCANNER);
1475
    }
1476
1477
    public function hasAstroLaboratory(): bool
1478
    {
1479
        return $this->hasShipSystem(ShipSystemTypeEnum::SYSTEM_ASTRO_LABORATORY);
1480
    }
1481
1482
    public function hasWarpdrive(): bool
1483
    {
1484
        return $this->hasShipSystem(ShipSystemTypeEnum::SYSTEM_WARPDRIVE);
1485
    }
1486
1487
    public function hasReactor(): bool
1488
    {
1489
        return $this->hasShipSystem(ShipSystemTypeEnum::SYSTEM_WARPCORE) ||
1490
            $this->hasShipSystem(ShipSystemTypeEnum::SYSTEM_FUSION_REACTOR) ||
1491
            $this->hasShipSystem(ShipSystemTypeEnum::SYSTEM_SINGULARITY_REACTOR);
1492
    }
1493
1494
    public function hasRPGModule(): bool
1495
    {
1496
        return $this->hasShipSystem(ShipSystemTypeEnum::SYSTEM_RPG_MODULE);
1497
    }
1498
1499
    public function hasNbsLss(): bool
1500
    {
1501
        return $this->hasShipSystem(ShipSystemTypeEnum::SYSTEM_LSS);
1502
    }
1503
1504
    public function hasUplink(): bool
1505
    {
1506
        return $this->hasShipSystem(ShipSystemTypeEnum::SYSTEM_UPLINK);
1507
    }
1508
1509
    public function hasTranswarp(): bool
1510
    {
1511
        return $this->hasShipSystem(ShipSystemTypeEnum::SYSTEM_TRANSWARP_COIL);
1512
    }
1513
1514
    public function getTranswarpCooldown(): ?int
1515
    {
1516
        $cooldown = $this->getShipSystem(ShipSystemTypeEnum::SYSTEM_TRANSWARP_COIL)->getCooldown();
1517
1518
        return $cooldown > time() ? $cooldown : null;
1519
    }
1520
1521
    public function getMaxTorpedos(): int
1522
    {
1523
        return $this->getRump()->getBaseTorpedoStorage()
1524
            + ($this->isSystemHealthy(ShipSystemTypeEnum::SYSTEM_TORPEDO_STORAGE)
1525
                ? TorpedoStorageShipSystem::TORPEDO_CAPACITY : 0);
1526
    }
1527
1528
    public function getDockedShips(): Collection
1529
    {
1530
        return $this->dockedShips;
1531
    }
1532
1533
    public function getDockedTo(): ?ShipInterface
1534
    {
1535
        return $this->dockedTo;
1536
    }
1537
1538
    public function setDockedTo(?ShipInterface $dockedTo): ShipInterface
1539
    {
1540
        $this->dockedTo = $dockedTo;
1541
        return $this;
1542
    }
1543
1544
    public function setDockedToId(?int $dockedToId): ShipInterface
1545
    {
1546
        $this->dock = $dockedToId;
1547
        return $this;
1548
    }
1549
1550
    public function hasFreeShuttleSpace(?LoggerUtilInterface $loggerUtil = null): bool
1551
    {
1552
        if ($loggerUtil !== null) {
1553
            $loggerUtil->log(sprintf('rumpShuttleSlots: %d', $this->getRump()->getShuttleSlots()));
1554
            $loggerUtil->log(sprintf('storedShuttleCount: %d', $this->getStoredShuttleCount()));
1555
        }
1556
        return $this->hasShipSystem(ShipSystemTypeEnum::SYSTEM_SHUTTLE_RAMP)
1557
            && $this->getRump()->getShuttleSlots() - $this->getStoredShuttleCount() > 0;
1558
    }
1559
1560
    public function getStoredShuttles(): array
1561
    {
1562
        $shuttles = [];
1563
1564
        foreach ($this->getStorage() as $stor) {
1565
            if ($stor->getCommodity()->isShuttle()) {
1566
                $shuttles[] = $stor->getCommodity();
1567
            }
1568
        }
1569
1570
        return $shuttles;
1571
    }
1572
1573
    public function getStoredShuttleCount(): int
1574
    {
1575
        $count = 0;
1576
1577
        foreach ($this->getStorage() as $stor) {
1578
            if ($stor->getCommodity()->isShuttle()) {
1579
                $count += $stor->getAmount();
1580
            }
1581
        }
1582
1583
        return $count;
1584
    }
1585
1586
    /**
1587
     * @return CommodityInterface[]
1588
     */
1589
    public function getStoredBuoy(): array
1590
    {
1591
        $buoy = [];
1592
1593
        foreach ($this->getStorage() as $stor) {
1594
            if ($stor->getCommodity()->isBouy()) {
1595
                $buoy[] = $stor->getCommodity();
1596
            }
1597
        }
1598
1599
        return $buoy;
1600
    }
1601
1602
1603
    public function hasStoredBuoy(): bool
1604
    {
1605
        return !empty($this->getStoredBuoy());
1606
    }
1607
1608
1609
    public function getDockedWorkbeeCount(): int
1610
    {
1611
        $count = 0;
1612
1613
        foreach ($this->getDockedShips() as $ships) {
1614
            if ($ships->getRump()->getCategoryId() === ShipRumpEnum::SHIP_CATEGORY_SHUTTLE) {
1615
                $count += 1;
1616
            }
1617
        }
1618
1619
        return $count;
1620
    }
1621
1622
    public function canMan(): bool
1623
    {
1624
        $buildplan = $this->getBuildplan();
1625
1626
        return $buildplan !== null
1627
            && $buildplan->getCrew() > 0
1628
            && $this->hasShipSystem(ShipSystemTypeEnum::SYSTEM_LIFE_SUPPORT);
1629
    }
1630
1631
    public function canBuildConstruction(): bool
1632
    {
1633
        return StationUtility::canShipBuildConstruction($this);
1634
    }
1635
1636
    public function hasCrewmanOfUser(int $userId): bool
1637
    {
1638
        foreach ($this->getCrewAssignments() as $shipCrew) {
1639
            if ($shipCrew->getCrew()->getUser()->getId() === $userId) {
1640
                return true;
1641
            }
1642
        }
1643
1644
        return false;
1645
    }
1646
1647
    public function __toString(): string
1648
    {
1649
        if ($this->id !== null) {
1650
            return sprintf('id: %d, name: %s', $this->getId(), $this->getName());
1651
        }
1652
1653
        return $this->getName();
1654
    }
1655
1656
    public function getHullColorStyle(): string
1657
    {
1658
        return $this->getColorStyle($this->getHull(), $this->getMaxHull());
1659
    }
1660
1661
    private function getColorStyle(int $actual, int $max): string
1662
    {
1663
        // full
1664
        if ($actual === $max) {
1665
            return '';
1666
        }
1667
1668
        // less than 100% - green
1669
        if ($actual / $max > 0.75) {
1670
            return 'color: #19c100;';
1671
        }
1672
1673
        // less than 75% - yellow
1674
        if ($actual / $max > 0.50) {
1675
            return 'color: #f4e932;';
1676
        }
1677
1678
        // less than 50% - orange
1679
        if ($actual / $max > 0.25) {
1680
            return 'color: #f48b28;';
1681
        }
1682
1683
        // less than 25% - red
1684
        return 'color: #ff3c3c;';
1685
    }
1686
}
1687