Passed
Push — dev ( ff3b26...5458f8 )
by Nico
20:18
created

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