Passed
Push — dev ( ef4569...47421a )
by Nico
06:55
created

Ship::setFormerRumpId()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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