Passed
Push — dev ( d990ab...334d1f )
by Nico
07:26
created

Ship::getCurrentMapFieldLayer()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

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