Passed
Pull Request — master (#1830)
by Nico
30:56
created

Ship   F

Complexity

Total Complexity 291

Size/Duplication

Total Lines 1597
Duplicated Lines 0 %

Test Coverage

Coverage 4.03%

Importance

Changes 3
Bugs 1 Features 0
Metric Value
eloc 552
c 3
b 1
f 0
dl 0
loc 1597
ccs 25
cts 621
cp 0.0403
rs 2
wmc 291

205 Methods

Rating   Name   Duplication   Size   Complexity  
A getTorpedoCount() 0 7 2
A isFleetLeader() 0 3 1
A getWebState() 0 3 1
A setShield() 0 4 1
A getConstructionHubState() 0 3 1
A getShieldState() 0 3 1
A setAlertState() 0 4 1
A getTractorPayload() 0 8 2
A getTakeoverPassive() 0 3 1
A setMaxHuell() 0 4 1
A getImpulseState() 0 3 1
A getWarpDriveState() 0 3 1
A isOverSystem() 0 7 2
A setFleetId() 0 4 1
A getCrewCount() 0 3 1
A setDisabled() 0 4 1
A isTrumfield() 0 3 1
A isConstruction() 0 3 1
A getSystem() 0 3 2
A setTorpedoStorage() 0 4 1
A isLSSModeBorder() 0 3 1
A isSystemHealthy() 0 7 2
A getSystemState() 0 8 3
A isUnderRepair() 0 4 2
A isTractored() 0 3 1
A setTakeoverPassive() 0 5 1
A getMapRegion() 0 13 3
A getSy() 0 3 1
A getPosY() 0 9 2
A getLayer() 0 3 1
A isTractoring() 0 3 1
A isShuttleRampHealthy() 0 3 1
A getName() 0 3 1
A setHitChance() 0 4 1
A getSpacecraftType() 0 3 1
A setName() 0 4 1
A getSystemsId() 0 3 2
A getLSSmode() 0 3 1
A getShieldRegenerationTimer() 0 3 1
A setEvadeChance() 0 4 1
A getHull() 0 3 1
A isOverWormhole() 0 3 2
A __construct() 0 8 1
A getTakeoverActive() 0 3 1
A getEvadeChance() 0 8 2
A getPhaserState() 0 3 1
A getLss() 0 3 1
A isMatrixScannerHealthy() 0 3 1
A isDestroyed() 0 3 1
A getSx() 0 3 1
A getMaxStorage() 0 3 1
A getExcessCrewCount() 0 3 1
A setUser() 0 4 1
A setSpacecraftType() 0 4 1
A setDatabaseId() 0 4 1
A getMap() 0 3 1
A getUser() 0 3 1
A getIsFleetLeader() 0 3 2
A setAlertStateGreen() 0 3 1
A setFormerRumpId() 0 4 1
A setFlightDirection() 0 4 1
A isOverColony() 0 3 2
A getShield() 0 3 1
A getBaseDamage() 0 8 2
A setFleet() 0 4 1
A setBaseDamage() 0 4 1
A getUserId() 0 3 1
A getUserName() 0 3 1
A getCloakState() 0 3 1
A getId() 0 7 2
A setMaxShield() 0 4 1
A getFleet() 0 3 1
A isShuttle() 0 3 1
B getModules() 0 27 8
A setIsFleetLeader() 0 4 1
A setShieldRegenerationTimer() 0 4 1
A getAstroStartTurn() 0 3 1
A getHitChance() 0 8 2
A getFormerRumpId() 0 3 1
A setHuell() 0 4 1
A getSubspaceState() 0 3 1
A getAstroState() 0 3 1
A getTachyonState() 0 3 1
A getFleetId() 0 3 1
A getRPGModuleState() 0 3 1
A setIsInEmergency() 0 4 1
A getTorpedoState() 0 3 1
A setTradePost() 0 5 1
A getMaxShield() 0 8 3
A setLSSMode() 0 4 1
A getHealthPercentage() 0 4 1
A getState() 0 3 1
A getBeamableStorage() 0 5 1
A getLogbook() 0 3 1
A getPosX() 0 9 2
A isAlertGreen() 0 3 1
A getFlightDirection() 0 3 1
A setTakeoverActive() 0 5 1
A getTorpedo() 0 7 2
A isLSSModeNormal() 0 3 1
A getMaxHull() 0 3 1
A getStorageSum() 0 6 1
A isWarped() 0 9 2
A setIsDestroyed() 0 4 1
A getDatabaseId() 0 3 1
A hasEnoughCrew() 0 22 6
A isDisabled() 0 3 1
A isWarpPossible() 0 3 2
A setState() 0 4 1
A getTorpedoStorage() 0 3 1
A getCrewAssignments() 0 3 1
A isDeflectorHealthy() 0 3 1
A isTorpedoStorageHealthy() 0 3 1
A getAlertState() 0 3 1
A getNbs() 0 3 1
A getStorage() 0 3 1
A isWarpAble() 0 3 1
A isWebEmitterHealthy() 0 3 1
A isTroopQuartersHealthy() 0 3 1
A getTradePost() 0 3 1
A getNeededCrewCount() 0 8 2
A setSensorRange() 0 4 1
A getSensorRange() 0 8 2
A getIsInEmergency() 0 3 1
A isBase() 0 3 1
A setTractoredShipId() 0 4 1
A hasRPGModule() 0 3 1
A getCurrentMapField() 0 3 1
A hasCloak() 0 3 1
A hasTranswarp() 0 3 1
A getDockedWorkbeeCount() 0 11 3
A getBuildplan() 0 3 1
A getColorStyle() 0 24 5
A getLocation() 0 3 1
A canMove() 0 4 2
A getMapCX() 0 3 1
A getSystems() 0 3 1
A hasTachyonScanner() 0 3 1
A getShipSystem() 0 8 2
A getTractoredShip() 0 3 1
A hasUplink() 0 3 1
A getBeamFactor() 0 3 1
A getDockPrivileges() 0 3 1
A getHullColorStyle() 0 3 1
A canMan() 0 7 3
A hasFreeDockingSlots() 0 3 1
A getTractoringShip() 0 3 1
A getHoldingWeb() 0 3 1
A setDockedTo() 0 4 1
A canBuildConstruction() 0 3 1
A getHoldingWebBackgroundStyle() 0 15 4
A setLocation() 0 13 3
A getDockingSlotCount() 0 5 3
A hasReactor() 0 5 3
A getStoredShuttleCount() 0 11 3
A setTractoredShip() 0 4 1
A getShieldRegenerationRate() 0 3 1
A setBuildplan() 0 4 1
A setStarsystemMap() 0 5 1
A hasNbsLss() 0 3 1
A getTranswarpCooldown() 0 5 2
A getDockedShips() 0 3 1
A setRump() 0 4 1
A setInfluenceArea() 0 4 1
A hasEscapePods() 0 3 2
A hasCrewmanOfUser() 0 9 3
A hasStoredBuoy() 0 3 1
A hasActiveWeapon() 0 3 2
A getHoldingWebImageStyle() 0 15 4
A setDockedToId() 0 4 1
A isBoardingPossible() 0 3 1
A hasTorpedo() 0 3 1
A hasWarpdrive() 0 3 1
A dockedOnTradePost() 0 3 2
A hasShipSystem() 0 3 1
A getSectorId() 0 18 3
A getHealthySystems() 0 9 3
A getRumpName() 0 3 1
A hasFreeShuttleSpace() 0 8 3
A getMapCY() 0 3 1
A getDockedTo() 0 3 1
A getRepairRate() 0 4 1
A hasAstroLaboratory() 0 3 1
A getShieldRegenerationPercentage() 0 3 2
A getMaxTorpedos() 0 5 2
A hasPhaser() 0 3 1
A setMap() 0 5 1
A isInterceptable() 0 4 1
A hasSubspaceScanner() 0 3 1
A isTractorbeamPossible() 0 3 1
A getSectorString() 0 3 1
A getRumpId() 0 3 1
A getInfluenceArea() 0 3 1
A canIntercept() 0 4 3
A hasShuttleRamp() 0 3 1
A getDockedShipCount() 0 3 1
A getFreeDockingSlotCount() 0 3 1
A getRump() 0 3 1
A getStoredShuttles() 0 11 3
A getStarsystemMap() 0 3 1
A setTractoringShip() 0 4 1
A __toString() 0 7 2
A getStoredBuoy() 0 11 3
A setHoldingWeb() 0 9 2
A displayNbsActions() 0 4 2

How to fix   Complexity   

Complex Class

Complex classes like Ship often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Ship, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Stu\Orm\Entity;
6
7
use Doctrine\Common\Collections\ArrayCollection;
8
use Doctrine\Common\Collections\Collection;
9
use Doctrine\ORM\Mapping\Column;
10
use Doctrine\ORM\Mapping\Entity;
11
use Doctrine\ORM\Mapping\GeneratedValue;
12
use Doctrine\ORM\Mapping\Id;
13
use Doctrine\ORM\Mapping\Index;
14
use Doctrine\ORM\Mapping\JoinColumn;
15
use Doctrine\ORM\Mapping\ManyToOne;
16
use Doctrine\ORM\Mapping\OneToMany;
17
use Doctrine\ORM\Mapping\OneToOne;
18
use Doctrine\ORM\Mapping\OrderBy;
19
use Doctrine\ORM\Mapping\Table;
20
use RuntimeException;
21
use Stu\Component\Game\TimeConstants;
0 ignored issues
show
Bug introduced by
The type Stu\Component\Game\TimeConstants was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

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