Passed
Push — dev ( bec44a...bd896c )
by Nico
19:27
created

Ship::getHoldingWeb()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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