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

Ship::updateLocation()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 15
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4.016

Importance

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