1
|
|
|
<?php declare(strict_types=1); |
2
|
|
|
|
3
|
|
|
use Smr\ShipClass; |
4
|
|
|
|
5
|
|
|
/** |
6
|
|
|
* Properties and methods for a ship instance. |
7
|
|
|
* Does not include the database layer (see SmrShip). |
8
|
|
|
*/ |
9
|
|
|
class AbstractSmrShip { |
10
|
|
|
|
11
|
|
|
// Player exp gained for each point of damage done |
12
|
|
|
protected const EXP_PER_DAMAGE_PLAYER = 0.375; |
13
|
|
|
protected const EXP_PER_DAMAGE_PLANET = 1.0; // note that planet damage is reduced |
14
|
|
|
protected const EXP_PER_DAMAGE_PORT = 0.15; |
15
|
|
|
protected const EXP_PER_DAMAGE_FORCE = 0.075; |
16
|
|
|
|
17
|
|
|
protected const STARTER_SHIPS = [ |
18
|
|
|
RACE_NEUTRAL => SHIP_TYPE_GALACTIC_SEMI, |
19
|
|
|
RACE_ALSKANT => SHIP_TYPE_SMALL_TIMER, |
20
|
|
|
RACE_CREONTI => SHIP_TYPE_MEDIUM_CARGO_HULK, |
21
|
|
|
RACE_HUMAN => SHIP_TYPE_LIGHT_FREIGHTER, |
22
|
|
|
RACE_IKTHORNE => SHIP_TYPE_TINY_DELIGHT, |
23
|
|
|
RACE_SALVENE => SHIP_TYPE_HATCHLINGS_DUE, |
24
|
|
|
RACE_THEVIAN => SHIP_TYPE_SWIFT_VENTURE, |
25
|
|
|
RACE_WQHUMAN => SHIP_TYPE_SLIP_FREIGHTER, |
26
|
|
|
RACE_NIJARIN => SHIP_TYPE_REDEEMER, |
27
|
|
|
]; |
28
|
|
|
|
29
|
|
|
protected AbstractSmrPlayer $player; |
30
|
|
|
|
31
|
|
|
protected int $gameID; |
32
|
|
|
protected SmrShipType $shipType; |
33
|
|
|
|
34
|
|
|
/** @var array<int, SmrWeapon> */ |
35
|
|
|
protected array $weapons = []; |
36
|
|
|
/** @var array<int, int> */ |
37
|
|
|
protected array $cargo = []; |
38
|
|
|
/** @var array<int, int> */ |
39
|
|
|
protected array $hardware = []; |
40
|
|
|
protected bool $isCloaked = false; |
41
|
|
|
/** @var array<string, int>|false */ |
42
|
|
|
protected array|false $illusionShip = false; |
43
|
|
|
|
44
|
|
|
protected bool $hasChangedWeapons = false; |
45
|
|
|
protected bool $hasChangedCargo = false; |
46
|
|
|
/** @var array<int, bool> */ |
47
|
|
|
protected array $hasChangedHardware = []; |
48
|
|
|
protected bool $hasChangedCloak = false; |
49
|
|
|
protected bool $hasChangedIllusion = false; |
50
|
|
|
|
51
|
|
|
public function __construct(AbstractSmrPlayer $player) { |
52
|
|
|
$this->player = $player; |
53
|
|
|
$this->gameID = $player->getGameID(); |
54
|
|
|
$this->regenerateShipType(); |
55
|
|
|
} |
56
|
|
|
|
57
|
|
|
protected function regenerateShipType(): void { |
58
|
|
|
$this->shipType = SmrShipType::get($this->player->getShipTypeID()); |
59
|
|
|
} |
60
|
|
|
|
61
|
|
|
public function checkForExcess(): void { |
62
|
|
|
$this->checkForExcessHardware(); |
63
|
|
|
$this->checkForExcessWeapons(); |
64
|
|
|
$this->checkForExcessCargo(); |
65
|
|
|
} |
66
|
|
|
|
67
|
|
|
public function checkForExcessWeapons(): void { |
68
|
|
|
while ($this->hasWeapons() && ($this->getPowerUsed() > $this->getType()->getMaxPower() || $this->getNumWeapons() > $this->getHardpoints())) { |
69
|
|
|
//erase the first weapon 1 at a time until we are okay |
70
|
|
|
$this->removeLastWeapon(); |
71
|
|
|
} |
72
|
|
|
} |
73
|
|
|
|
74
|
|
|
public function checkForExcessCargo(): void { |
75
|
|
|
if ($this->hasCargo()) { |
76
|
|
|
$excess = array_sum($this->getCargo()) - $this->getCargoHolds(); |
77
|
|
|
foreach ($this->getCargo() as $goodID => $amount) { |
78
|
|
|
if ($excess > 0) { |
79
|
|
|
$decreaseAmount = min($amount, $excess); |
80
|
|
|
$this->decreaseCargo($goodID, $decreaseAmount); |
81
|
|
|
$excess -= $decreaseAmount; |
82
|
|
|
} else { |
83
|
|
|
// No more excess cargo |
84
|
|
|
break; |
85
|
|
|
} |
86
|
|
|
} |
87
|
|
|
} |
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
public function checkForExcessHardware(): void { |
91
|
|
|
//check hardware to see if anything needs to be removed |
92
|
|
|
foreach ($this->getHardware() as $hardwareTypeID => $amount) { |
93
|
|
|
$max = $this->shipType->getMaxHardware($hardwareTypeID); |
94
|
|
|
$this->setHardware($hardwareTypeID, min($amount, $max)); |
95
|
|
|
} |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
/** |
99
|
|
|
* Set all hardware to its maximum value for this ship. |
100
|
|
|
*/ |
101
|
|
|
public function setHardwareToMax(): void { |
102
|
|
|
foreach ($this->shipType->getAllMaxHardware() as $hardwareTypeID => $max) { |
103
|
|
|
$this->setHardware($hardwareTypeID, $max); |
104
|
|
|
} |
105
|
|
|
} |
106
|
|
|
|
107
|
|
|
public function getPowerUsed(): int { |
108
|
|
|
$power = 0; |
109
|
|
|
foreach ($this->weapons as $weapon) { |
110
|
|
|
$power += $weapon->getPowerLevel(); |
111
|
|
|
} |
112
|
|
|
return $power; |
113
|
|
|
} |
114
|
|
|
|
115
|
|
|
public function getRemainingPower(): int { |
116
|
|
|
return $this->getType()->getMaxPower() - $this->getPowerUsed(); |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
/** |
120
|
|
|
* given power level of new weapon, return whether there is enough power available to install it on this ship |
121
|
|
|
*/ |
122
|
|
|
public function checkPowerAvailable(int $powerLevel): bool { |
123
|
|
|
return $this->getRemainingPower() >= $powerLevel; |
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
public function hasIllegalGoods(): bool { |
127
|
|
|
return $this->hasCargo(GOODS_SLAVES) || $this->hasCargo(GOODS_WEAPONS) || $this->hasCargo(GOODS_NARCOTICS); |
128
|
|
|
} |
129
|
|
|
|
130
|
|
|
public function getDisplayAttackRating(): int { |
131
|
|
|
if ($this->hasActiveIllusion()) { |
132
|
|
|
return $this->getIllusionAttack(); |
133
|
|
|
} |
134
|
|
|
return $this->getAttackRating(); |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
public function getDisplayDefenseRating(): int { |
138
|
|
|
if ($this->hasActiveIllusion()) { |
139
|
|
|
return $this->getIllusionDefense(); |
140
|
|
|
} |
141
|
|
|
return $this->getDefenseRating(); |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
public function getDisplayName(): string { |
145
|
|
|
if ($this->hasActiveIllusion()) { |
146
|
|
|
return $this->getIllusionShipName(); |
147
|
|
|
} |
148
|
|
|
return $this->getName(); |
149
|
|
|
} |
150
|
|
|
|
151
|
|
|
public function getAttackRating(): int { |
152
|
|
|
return IRound(($this->getTotalShieldDamage() + $this->getTotalArmourDamage() + $this->getCDs() * 2) / 40); |
|
|
|
|
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
public function getAttackRatingWithMaxCDs(): int { |
156
|
|
|
return IRound(($this->getTotalShieldDamage() + $this->getTotalArmourDamage() + $this->getMaxCDs() * 2) / 40); |
|
|
|
|
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
public function getDefenseRating(): int { |
160
|
|
|
return IRound(($this->getShields() + $this->getArmour() + $this->getCDs() * CD_ARMOUR) / 100); |
|
|
|
|
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
public function getMaxDefenseRating(): int { |
164
|
|
|
return IRound(($this->getMaxShields() + $this->getMaxArmour() + $this->getMaxCDs() * CD_ARMOUR) / 100); |
|
|
|
|
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
public function getShieldLow(): int { return IFloor($this->getShields() / 100) * 100; } |
|
|
|
|
168
|
|
|
public function getShieldHigh(): int { return $this->getShieldLow() + 100; } |
169
|
|
|
public function getArmourLow(): int { return IFloor($this->getArmour() / 100) * 100; } |
|
|
|
|
170
|
|
|
public function getArmourHigh(): int { return $this->getArmourLow() + 100; } |
171
|
|
|
public function getCDsLow(): int { return IFloor($this->getCDs() / 100) * 100; } |
|
|
|
|
172
|
|
|
public function getCDsHigh(): int { return $this->getCDsLow() + 100; } |
173
|
|
|
|
174
|
|
|
|
175
|
|
|
|
176
|
|
|
public function addWeapon(SmrWeapon $weapon): SmrWeapon|false { |
177
|
|
|
if ($this->hasOpenWeaponSlots() && $this->checkPowerAvailable($weapon->getPowerLevel())) { |
178
|
|
|
$this->weapons[] = $weapon; |
179
|
|
|
$this->hasChangedWeapons = true; |
180
|
|
|
return $weapon; |
181
|
|
|
} |
182
|
|
|
return false; |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
public function moveWeaponUp(int $orderID): void { |
186
|
|
|
$replacement = $orderID - 1; |
187
|
|
|
if ($replacement < 0) { |
188
|
|
|
// Shift everything up by one and put the selected weapon at the bottom |
189
|
|
|
$this->weapons[] = array_shift($this->weapons); |
190
|
|
|
} else { |
191
|
|
|
// Swap the selected weapon with the one above it |
192
|
|
|
$temp = $this->weapons[$replacement]; |
193
|
|
|
$this->weapons[$replacement] = $this->weapons[$orderID]; |
194
|
|
|
$this->weapons[$orderID] = $temp; |
195
|
|
|
} |
196
|
|
|
$this->hasChangedWeapons = true; |
197
|
|
|
} |
198
|
|
|
|
199
|
|
|
public function moveWeaponDown(int $orderID): void { |
200
|
|
|
$replacement = $orderID + 1; |
201
|
|
|
if ($replacement >= count($this->weapons)) { |
202
|
|
|
// Shift everything down by one and put the selected weapon at the top |
203
|
|
|
array_unshift($this->weapons, array_pop($this->weapons)); |
204
|
|
|
} else { |
205
|
|
|
// Swap the selected weapon with the one below it |
206
|
|
|
$temp = $this->weapons[$replacement]; |
207
|
|
|
$this->weapons[$replacement] = $this->weapons[$orderID]; |
208
|
|
|
$this->weapons[$orderID] = $temp; |
209
|
|
|
} |
210
|
|
|
$this->hasChangedWeapons = true; |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
/** |
214
|
|
|
* @param array<int, int> $orderArray |
215
|
|
|
*/ |
216
|
|
|
public function setWeaponLocations(array $orderArray): void { |
217
|
|
|
$weapons = $this->weapons; |
218
|
|
|
foreach ($orderArray as $newOrder => $oldOrder) { |
219
|
|
|
$this->weapons[$newOrder] = $weapons[$oldOrder]; |
220
|
|
|
} |
221
|
|
|
$this->hasChangedWeapons = true; |
222
|
|
|
} |
223
|
|
|
|
224
|
|
|
public function removeLastWeapon(): void { |
225
|
|
|
$this->removeWeapon($this->getNumWeapons() - 1); |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
public function removeWeapon(int $orderID): void { |
229
|
|
|
// Remove the specified weapon, then reindex the array |
230
|
|
|
unset($this->weapons[$orderID]); |
231
|
|
|
$this->weapons = array_values($this->weapons); |
232
|
|
|
$this->hasChangedWeapons = true; |
233
|
|
|
} |
234
|
|
|
|
235
|
|
|
public function removeAllWeapons(): void { |
236
|
|
|
$this->weapons = []; |
237
|
|
|
$this->hasChangedWeapons = true; |
238
|
|
|
} |
239
|
|
|
|
240
|
|
|
public function removeAllCargo(): void { |
241
|
|
|
foreach ($this->cargo as $goodID => $amount) { |
242
|
|
|
$this->setCargo($goodID, 0); |
243
|
|
|
} |
244
|
|
|
} |
245
|
|
|
|
246
|
|
|
public function removeAllHardware(): void { |
247
|
|
|
foreach (array_keys($this->hardware) as $hardwareTypeID) { |
248
|
|
|
$this->hasChangedHardware[$hardwareTypeID] = true; |
249
|
|
|
} |
250
|
|
|
$this->hardware = []; |
251
|
|
|
$this->decloak(); |
252
|
|
|
$this->disableIllusion(); |
253
|
|
|
} |
254
|
|
|
|
255
|
|
|
public function getPod(bool $isNewbie = false): void { |
256
|
|
|
$this->removeAllWeapons(); |
257
|
|
|
$this->removeAllCargo(); |
258
|
|
|
$this->removeAllHardware(); |
259
|
|
|
|
260
|
|
|
if ($isNewbie) { |
261
|
|
|
$this->setShields(75); |
262
|
|
|
$this->setArmour(150); |
263
|
|
|
$this->setCargoHolds(40); |
264
|
|
|
$this->setTypeID(SHIP_TYPE_NEWBIE_MERCHANT_VESSEL); |
265
|
|
|
} else { |
266
|
|
|
$this->setShields(50); |
267
|
|
|
$this->setArmour(50); |
268
|
|
|
$this->setCargoHolds(5); |
269
|
|
|
$this->setTypeID(SHIP_TYPE_ESCAPE_POD); |
270
|
|
|
} |
271
|
|
|
} |
272
|
|
|
|
273
|
|
|
public function giveStarterShip(): void { |
274
|
|
|
if ($this->player->hasNewbieStatus()) { |
275
|
|
|
$shipID = SHIP_TYPE_NEWBIE_MERCHANT_VESSEL; |
276
|
|
|
$amount_shields = 75; |
277
|
|
|
$amount_armour = 150; |
278
|
|
|
} else { |
279
|
|
|
$shipID = self::STARTER_SHIPS[$this->player->getRaceID()]; |
280
|
|
|
$amount_shields = 50; |
281
|
|
|
$amount_armour = 50; |
282
|
|
|
} |
283
|
|
|
$this->setTypeID($shipID); |
284
|
|
|
$this->setShields($amount_shields); |
285
|
|
|
$this->setArmour($amount_armour); |
286
|
|
|
$this->setCargoHolds(40); |
287
|
|
|
$this->addWeapon(SmrWeapon::getWeapon(WEAPON_TYPE_LASER)); |
288
|
|
|
} |
289
|
|
|
|
290
|
|
|
public function hasJump(): bool { |
291
|
|
|
return $this->getHardware(HARDWARE_JUMP) > 0; |
292
|
|
|
} |
293
|
|
|
|
294
|
|
|
public function hasDCS(): bool { |
295
|
|
|
return $this->getHardware(HARDWARE_DCS) > 0; |
296
|
|
|
} |
297
|
|
|
|
298
|
|
|
public function hasScanner(): bool { |
299
|
|
|
return $this->getHardware(HARDWARE_SCANNER) > 0; |
300
|
|
|
} |
301
|
|
|
|
302
|
|
|
public function hasCloak(): bool { |
303
|
|
|
return $this->getHardware(HARDWARE_CLOAK) > 0; |
304
|
|
|
} |
305
|
|
|
|
306
|
|
|
public function isCloaked(): bool { |
307
|
|
|
return $this->isCloaked; |
308
|
|
|
} |
309
|
|
|
|
310
|
|
|
public function decloak(): void { |
311
|
|
|
if ($this->isCloaked === false) { |
312
|
|
|
return; |
313
|
|
|
} |
314
|
|
|
$this->isCloaked = false; |
315
|
|
|
$this->hasChangedCloak = true; |
316
|
|
|
} |
317
|
|
|
|
318
|
|
|
public function enableCloak(): void { |
319
|
|
|
if ($this->hasCloak() === false) { |
320
|
|
|
throw new Exception('Ship does not have the supported hardware!'); |
321
|
|
|
} |
322
|
|
|
if ($this->isCloaked === true) { |
323
|
|
|
return; |
324
|
|
|
} |
325
|
|
|
$this->isCloaked = true; |
326
|
|
|
$this->hasChangedCloak = true; |
327
|
|
|
} |
328
|
|
|
|
329
|
|
|
public function hasIllusion(): bool { |
330
|
|
|
return $this->getHardware(HARDWARE_ILLUSION) > 0; |
331
|
|
|
} |
332
|
|
|
|
333
|
|
|
/** |
334
|
|
|
* @return array<string, int>|false |
335
|
|
|
*/ |
336
|
|
|
public function getIllusionShip(): array|false { |
337
|
|
|
return $this->illusionShip; |
338
|
|
|
} |
339
|
|
|
|
340
|
|
|
public function hasActiveIllusion(): bool { |
341
|
|
|
return $this->getIllusionShip() !== false; |
342
|
|
|
} |
343
|
|
|
|
344
|
|
|
public function setIllusion(int $shipTypeID, int $attack, int $defense): void { |
345
|
|
|
if ($this->hasIllusion() === false) { |
346
|
|
|
throw new Exception('Ship does not have the supported hardware!'); |
347
|
|
|
} |
348
|
|
|
$newIllusionShip = [ |
349
|
|
|
'ID' => $shipTypeID, |
350
|
|
|
'Attack' => $attack, |
351
|
|
|
'Defense' => $defense, |
352
|
|
|
]; |
353
|
|
|
if ($this->getIllusionShip() === $newIllusionShip) { |
354
|
|
|
return; |
355
|
|
|
} |
356
|
|
|
$this->illusionShip = $newIllusionShip; |
357
|
|
|
$this->hasChangedIllusion = true; |
358
|
|
|
} |
359
|
|
|
|
360
|
|
|
public function disableIllusion(): void { |
361
|
|
|
if ($this->getIllusionShip() === false) { |
362
|
|
|
return; |
363
|
|
|
} |
364
|
|
|
$this->illusionShip = false; |
365
|
|
|
$this->hasChangedIllusion = true; |
366
|
|
|
} |
367
|
|
|
|
368
|
|
|
public function getIllusionShipID(): int { |
369
|
|
|
return $this->getIllusionShip()['ID']; |
370
|
|
|
} |
371
|
|
|
|
372
|
|
|
public function getIllusionShipName(): string { |
373
|
|
|
return SmrShipType::get($this->getIllusionShip()['ID'])->getName(); |
374
|
|
|
} |
375
|
|
|
|
376
|
|
|
public function getIllusionAttack(): int { |
377
|
|
|
return $this->getIllusionShip()['Attack']; |
378
|
|
|
} |
379
|
|
|
|
380
|
|
|
public function getIllusionDefense(): int { |
381
|
|
|
return $this->getIllusionShip()['Defense']; |
382
|
|
|
} |
383
|
|
|
|
384
|
|
|
public function getPlayer(): AbstractSmrPlayer { |
385
|
|
|
return $this->player; |
386
|
|
|
} |
387
|
|
|
|
388
|
|
|
public function getAccountID(): int { |
389
|
|
|
return $this->getPlayer()->getAccountID(); |
390
|
|
|
} |
391
|
|
|
|
392
|
|
|
public function getGameID(): int { |
393
|
|
|
return $this->gameID; |
394
|
|
|
} |
395
|
|
|
|
396
|
|
|
public function getGame(): SmrGame { |
397
|
|
|
return SmrGame::getGame($this->gameID); |
398
|
|
|
} |
399
|
|
|
|
400
|
|
|
/** |
401
|
|
|
* Switch to a new ship, updating player turns accordingly. |
402
|
|
|
*/ |
403
|
|
|
public function setTypeID(int $shipTypeID): void { |
404
|
|
|
$oldSpeed = $this->shipType->getSpeed(); |
405
|
|
|
$this->getPlayer()->setShipTypeID($shipTypeID); |
406
|
|
|
$this->regenerateShipType(); |
407
|
|
|
$this->checkForExcess(); |
408
|
|
|
|
409
|
|
|
// Update the player's turns to account for the speed change |
410
|
|
|
$newSpeed = $this->shipType->getSpeed(); |
411
|
|
|
$oldTurns = $this->getPlayer()->getTurns(); |
412
|
|
|
$this->getPlayer()->setTurns(IRound($oldTurns * $newSpeed / $oldSpeed)); |
|
|
|
|
413
|
|
|
} |
414
|
|
|
|
415
|
|
|
public function getType(): SmrShipType { |
416
|
|
|
return $this->shipType; |
417
|
|
|
} |
418
|
|
|
|
419
|
|
|
public function getTypeID(): int { |
420
|
|
|
return $this->shipType->getTypeID(); |
421
|
|
|
} |
422
|
|
|
|
423
|
|
|
public function getClass(): ShipClass { |
424
|
|
|
return $this->shipType->getClass(); |
425
|
|
|
} |
426
|
|
|
|
427
|
|
|
public function getName(): string { |
428
|
|
|
return $this->shipType->getName(); |
429
|
|
|
} |
430
|
|
|
|
431
|
|
|
public function getCost(): int { |
432
|
|
|
return $this->shipType->getCost(); |
433
|
|
|
} |
434
|
|
|
|
435
|
|
|
public function getHardpoints(): int { |
436
|
|
|
return $this->shipType->getHardpoints(); |
437
|
|
|
} |
438
|
|
|
|
439
|
|
|
/** |
440
|
|
|
* Trade-in value of the ship |
441
|
|
|
*/ |
442
|
|
|
public function getRefundValue(): int { |
443
|
|
|
return IFloor($this->getCost() * SHIP_REFUND_PERCENT); |
|
|
|
|
444
|
|
|
} |
445
|
|
|
|
446
|
|
|
public function getCostToUpgrade(int $otherShipTypeID): int { |
447
|
|
|
$otherShipType = SmrShipType::get($otherShipTypeID); |
448
|
|
|
return $otherShipType->getCost() - $this->getRefundValue(); |
449
|
|
|
} |
450
|
|
|
|
451
|
|
|
public function getCostToUpgradeAndUNO(int $otherShipTypeID): int { |
452
|
|
|
return $this->getCostToUpgrade($otherShipTypeID) + $this->getCostToUNOAgainstShip($otherShipTypeID); |
453
|
|
|
} |
454
|
|
|
|
455
|
|
|
protected function getCostToUNOAgainstShip(int $otherShipTypeID): int { |
456
|
|
|
$otherShipType = SmrShipType::get($otherShipTypeID); |
457
|
|
|
$cost = 0; |
458
|
|
|
$hardwareTypes = [HARDWARE_SHIELDS, HARDWARE_ARMOUR, HARDWARE_CARGO]; |
459
|
|
|
foreach ($hardwareTypes as $hardwareTypeID) { |
460
|
|
|
$cost += max(0, $otherShipType->getMaxHardware($hardwareTypeID) - $this->getHardware($hardwareTypeID)) * Globals::getHardwareCost($hardwareTypeID); |
461
|
|
|
} |
462
|
|
|
return $cost; |
463
|
|
|
} |
464
|
|
|
|
465
|
|
|
public function getCostToUNO(): int { |
466
|
|
|
return $this->getCostToUNOAgainstShip($this->getTypeID()); |
467
|
|
|
} |
468
|
|
|
|
469
|
|
|
/** |
470
|
|
|
* Returns the ship speed modified by the game speed. |
471
|
|
|
*/ |
472
|
|
|
public function getRealSpeed(): float { |
473
|
|
|
return $this->shipType->getSpeed() * $this->getGame()->getGameSpeed(); |
474
|
|
|
} |
475
|
|
|
|
476
|
|
|
/** |
477
|
|
|
* @return ($hardwareTypeID is null ? array<int, int> : int) |
|
|
|
|
478
|
|
|
*/ |
479
|
|
|
public function getHardware(int $hardwareTypeID = null): array|int { |
480
|
|
|
if ($hardwareTypeID === null) { |
481
|
|
|
return $this->hardware; |
482
|
|
|
} |
483
|
|
|
return $this->hardware[$hardwareTypeID] ?? 0; |
484
|
|
|
} |
485
|
|
|
|
486
|
|
|
public function setHardware(int $hardwareTypeID, int $amount): void { |
487
|
|
|
if ($this->getHardware($hardwareTypeID) === $amount) { |
488
|
|
|
return; |
489
|
|
|
} |
490
|
|
|
$this->hardware[$hardwareTypeID] = $amount; |
491
|
|
|
$this->hasChangedHardware[$hardwareTypeID] = true; |
492
|
|
|
} |
493
|
|
|
|
494
|
|
|
public function increaseHardware(int $hardwareTypeID, int $amount): void { |
495
|
|
|
$this->setHardware($hardwareTypeID, $this->getHardware($hardwareTypeID) + $amount); |
496
|
|
|
} |
497
|
|
|
|
498
|
|
|
public function hasMaxHardware(int $hardwareTypeID): bool { |
499
|
|
|
return $this->getHardware($hardwareTypeID) == $this->shipType->getMaxHardware($hardwareTypeID); |
500
|
|
|
} |
501
|
|
|
|
502
|
|
|
public function getShields(): int { |
503
|
|
|
return $this->getHardware(HARDWARE_SHIELDS); |
504
|
|
|
} |
505
|
|
|
|
506
|
|
|
public function setShields(int $amount): void { |
507
|
|
|
$this->setHardware(HARDWARE_SHIELDS, $amount); |
508
|
|
|
} |
509
|
|
|
|
510
|
|
|
public function decreaseShields(int $amount): void { |
511
|
|
|
$this->setShields($this->getShields() - $amount); |
512
|
|
|
} |
513
|
|
|
|
514
|
|
|
public function increaseShields(int $amount): void { |
515
|
|
|
$this->setShields($this->getShields() + $amount); |
516
|
|
|
} |
517
|
|
|
|
518
|
|
|
public function hasShields(): bool { |
519
|
|
|
return $this->getShields() > 0; |
520
|
|
|
} |
521
|
|
|
|
522
|
|
|
public function hasMaxShields(): bool { |
523
|
|
|
return $this->hasMaxHardware(HARDWARE_SHIELDS); |
524
|
|
|
} |
525
|
|
|
|
526
|
|
|
public function getMaxShields(): int { |
527
|
|
|
return $this->shipType->getMaxHardware(HARDWARE_SHIELDS); |
528
|
|
|
} |
529
|
|
|
|
530
|
|
|
public function getArmour(): int { |
531
|
|
|
return $this->getHardware(HARDWARE_ARMOUR); |
532
|
|
|
} |
533
|
|
|
|
534
|
|
|
public function setArmour(int $amount): void { |
535
|
|
|
$this->setHardware(HARDWARE_ARMOUR, $amount); |
536
|
|
|
} |
537
|
|
|
|
538
|
|
|
public function decreaseArmour(int $amount): void { |
539
|
|
|
$this->setArmour($this->getArmour() - $amount); |
540
|
|
|
} |
541
|
|
|
|
542
|
|
|
public function increaseArmour(int $amount): void { |
543
|
|
|
$this->setArmour($this->getArmour() + $amount); |
544
|
|
|
} |
545
|
|
|
|
546
|
|
|
public function hasArmour(): bool { |
547
|
|
|
return $this->getArmour() > 0; |
548
|
|
|
} |
549
|
|
|
|
550
|
|
|
public function hasMaxArmour(): bool { |
551
|
|
|
return $this->hasMaxHardware(HARDWARE_ARMOUR); |
552
|
|
|
} |
553
|
|
|
|
554
|
|
|
public function getMaxArmour(): int { |
555
|
|
|
return $this->shipType->getMaxHardware(HARDWARE_ARMOUR); |
556
|
|
|
} |
557
|
|
|
|
558
|
|
|
public function isDead(): bool { |
559
|
|
|
return !$this->hasArmour() && !$this->hasShields(); |
560
|
|
|
} |
561
|
|
|
|
562
|
|
|
public function hasMaxCDs(): bool { |
563
|
|
|
return $this->hasMaxHardware(HARDWARE_COMBAT); |
564
|
|
|
} |
565
|
|
|
|
566
|
|
|
public function hasMaxSDs(): bool { |
567
|
|
|
return $this->hasMaxHardware(HARDWARE_SCOUT); |
568
|
|
|
} |
569
|
|
|
|
570
|
|
|
public function hasMaxMines(): bool { |
571
|
|
|
return $this->hasMaxHardware(HARDWARE_MINE); |
572
|
|
|
} |
573
|
|
|
|
574
|
|
|
public function hasCDs(): bool { |
575
|
|
|
return $this->getCDs() > 0; |
576
|
|
|
} |
577
|
|
|
|
578
|
|
|
public function hasSDs(): bool { |
579
|
|
|
return $this->getSDs() > 0; |
580
|
|
|
} |
581
|
|
|
|
582
|
|
|
public function hasMines(): bool { |
583
|
|
|
return $this->getMines() > 0; |
584
|
|
|
} |
585
|
|
|
|
586
|
|
|
public function getCDs(): int { |
587
|
|
|
return $this->getHardware(HARDWARE_COMBAT); |
588
|
|
|
} |
589
|
|
|
|
590
|
|
|
public function setCDs(int $amount): void { |
591
|
|
|
$this->setHardware(HARDWARE_COMBAT, $amount); |
592
|
|
|
} |
593
|
|
|
|
594
|
|
|
public function decreaseCDs(int $amount): void { |
595
|
|
|
$this->setCDs($this->getCDs() - $amount); |
596
|
|
|
} |
597
|
|
|
|
598
|
|
|
public function increaseCDs(int $amount): void { |
599
|
|
|
$this->setCDs($this->getCDs() + $amount); |
600
|
|
|
} |
601
|
|
|
|
602
|
|
|
public function getMaxCDs(): int { |
603
|
|
|
return $this->shipType->getMaxHardware(HARDWARE_COMBAT); |
604
|
|
|
} |
605
|
|
|
|
606
|
|
|
public function getSDs(): int { |
607
|
|
|
return $this->getHardware(HARDWARE_SCOUT); |
608
|
|
|
} |
609
|
|
|
|
610
|
|
|
public function setSDs(int $amount): void { |
611
|
|
|
$this->setHardware(HARDWARE_SCOUT, $amount); |
612
|
|
|
} |
613
|
|
|
|
614
|
|
|
public function decreaseSDs(int $amount): void { |
615
|
|
|
$this->setSDs($this->getSDs() - $amount); |
616
|
|
|
} |
617
|
|
|
|
618
|
|
|
public function increaseSDs(int $amount): void { |
619
|
|
|
$this->setSDs($this->getSDs() + $amount); |
620
|
|
|
} |
621
|
|
|
|
622
|
|
|
public function getMaxSDs(): int { |
623
|
|
|
return $this->shipType->getMaxHardware(HARDWARE_SCOUT); |
624
|
|
|
} |
625
|
|
|
|
626
|
|
|
public function getMines(): int { |
627
|
|
|
return $this->getHardware(HARDWARE_MINE); |
628
|
|
|
} |
629
|
|
|
|
630
|
|
|
public function setMines(int $amount): void { |
631
|
|
|
$this->setHardware(HARDWARE_MINE, $amount); |
632
|
|
|
} |
633
|
|
|
|
634
|
|
|
public function decreaseMines(int $amount): void { |
635
|
|
|
$this->setMines($this->getMines() - $amount); |
636
|
|
|
} |
637
|
|
|
|
638
|
|
|
public function increaseMines(int $amount): void { |
639
|
|
|
$this->setMines($this->getMines() + $amount); |
640
|
|
|
} |
641
|
|
|
|
642
|
|
|
public function getMaxMines(): int { |
643
|
|
|
return $this->shipType->getMaxHardware(HARDWARE_MINE); |
644
|
|
|
} |
645
|
|
|
|
646
|
|
|
public function getCargoHolds(): int { |
647
|
|
|
return $this->getHardware(HARDWARE_CARGO); |
648
|
|
|
} |
649
|
|
|
|
650
|
|
|
public function setCargoHolds(int $amount): void { |
651
|
|
|
$this->setHardware(HARDWARE_CARGO, $amount); |
652
|
|
|
} |
653
|
|
|
|
654
|
|
|
/** |
655
|
|
|
* @return ($goodID is null ? array<int, int> : int) |
|
|
|
|
656
|
|
|
*/ |
657
|
|
|
public function getCargo(int $goodID = null): int|array { |
658
|
|
|
if ($goodID === null) { |
659
|
|
|
return $this->cargo; |
660
|
|
|
} |
661
|
|
|
return $this->cargo[$goodID] ?? 0; |
662
|
|
|
} |
663
|
|
|
|
664
|
|
|
public function hasCargo(int $goodID = null): bool { |
665
|
|
|
if ($goodID === null) { |
666
|
|
|
return $this->getUsedHolds() > 0; |
667
|
|
|
} |
668
|
|
|
return $this->getCargo($goodID) > 0; |
669
|
|
|
} |
670
|
|
|
|
671
|
|
|
public function setCargo(int $goodID, int $amount): void { |
672
|
|
|
if ($this->getCargo($goodID) === $amount) { |
673
|
|
|
return; |
674
|
|
|
} |
675
|
|
|
$this->cargo[$goodID] = $amount; |
676
|
|
|
$this->hasChangedCargo = true; |
677
|
|
|
// Sort cargo by goodID to make sure it shows up in the correct order |
678
|
|
|
// before the next page is loaded. |
679
|
|
|
ksort($this->cargo); |
680
|
|
|
} |
681
|
|
|
|
682
|
|
|
public function decreaseCargo(int $goodID, int $amount): void { |
683
|
|
|
if ($amount < 0) { |
684
|
|
|
throw new Exception('Trying to decrease negative cargo.'); |
685
|
|
|
} |
686
|
|
|
$this->setCargo($goodID, $this->getCargo($goodID) - $amount); |
687
|
|
|
} |
688
|
|
|
|
689
|
|
|
public function increaseCargo(int $goodID, int $amount): void { |
690
|
|
|
if ($amount < 0) { |
691
|
|
|
throw new Exception('Trying to increase negative cargo.'); |
692
|
|
|
} |
693
|
|
|
$this->setCargo($goodID, $this->getCargo($goodID) + $amount); |
694
|
|
|
} |
695
|
|
|
|
696
|
|
|
public function getEmptyHolds(): int { |
697
|
|
|
return $this->getCargoHolds() - $this->getUsedHolds(); |
698
|
|
|
} |
699
|
|
|
|
700
|
|
|
public function getUsedHolds(): int { |
701
|
|
|
return array_sum($this->getCargo()); |
702
|
|
|
} |
703
|
|
|
|
704
|
|
|
public function hasMaxCargoHolds(): bool { |
705
|
|
|
return $this->hasMaxHardware(HARDWARE_CARGO); |
706
|
|
|
} |
707
|
|
|
|
708
|
|
|
public function getMaxCargoHolds(): int { |
709
|
|
|
return $this->shipType->getMaxHardware(HARDWARE_CARGO); |
710
|
|
|
} |
711
|
|
|
|
712
|
|
|
public function hasWeapons(): bool { |
713
|
|
|
return $this->getNumWeapons() > 0; |
714
|
|
|
} |
715
|
|
|
|
716
|
|
|
/** |
717
|
|
|
* @return array<int, SmrWeapon> |
718
|
|
|
*/ |
719
|
|
|
public function getWeapons(): array { |
720
|
|
|
return $this->weapons; |
721
|
|
|
} |
722
|
|
|
|
723
|
|
|
public function canAttack(): bool { |
724
|
|
|
return $this->hasWeapons() || $this->hasCDs(); |
725
|
|
|
} |
726
|
|
|
|
727
|
|
|
public function getNumWeapons(): int { |
728
|
|
|
return count($this->getWeapons()); |
729
|
|
|
} |
730
|
|
|
|
731
|
|
|
public function getOpenWeaponSlots(): int { |
732
|
|
|
return $this->getHardpoints() - $this->getNumWeapons(); |
733
|
|
|
} |
734
|
|
|
|
735
|
|
|
public function hasOpenWeaponSlots(): bool { |
736
|
|
|
return $this->getOpenWeaponSlots() > 0; |
737
|
|
|
} |
738
|
|
|
|
739
|
|
|
public function getTotalShieldDamage(): int { |
740
|
|
|
$shieldDamage = 0; |
741
|
|
|
foreach ($this->getWeapons() as $weapon) { |
742
|
|
|
$shieldDamage += $weapon->getShieldDamage(); |
743
|
|
|
} |
744
|
|
|
return $shieldDamage; |
745
|
|
|
} |
746
|
|
|
|
747
|
|
|
public function getTotalArmourDamage(): int { |
748
|
|
|
$armourDamage = 0; |
749
|
|
|
foreach ($this->getWeapons() as $weapon) { |
750
|
|
|
$armourDamage += $weapon->getArmourDamage(); |
751
|
|
|
} |
752
|
|
|
return $armourDamage; |
753
|
|
|
} |
754
|
|
|
|
755
|
|
|
public function isFederal(): bool { |
756
|
|
|
return $this->getTypeID() === SHIP_TYPE_FEDERAL_DISCOVERY || |
757
|
|
|
$this->getTypeID() === SHIP_TYPE_FEDERAL_WARRANT || |
758
|
|
|
$this->getTypeID() === SHIP_TYPE_FEDERAL_ULTIMATUM; |
759
|
|
|
} |
760
|
|
|
|
761
|
|
|
public function isUnderground(): bool { |
762
|
|
|
return $this->getTypeID() === SHIP_TYPE_THIEF || |
763
|
|
|
$this->getTypeID() === SHIP_TYPE_ASSASSIN || |
764
|
|
|
$this->getTypeID() === SHIP_TYPE_DEATH_CRUISER; |
765
|
|
|
} |
766
|
|
|
|
767
|
|
|
/** |
768
|
|
|
* @param array<AbstractSmrPlayer> $targetPlayers |
769
|
|
|
* @return array<string, mixed> |
770
|
|
|
*/ |
771
|
|
|
public function shootPlayers(array $targetPlayers): array { |
772
|
|
|
$thisPlayer = $this->getPlayer(); |
773
|
|
|
$results = ['Player' => $thisPlayer, 'TotalDamage' => 0, 'Weapons' => []]; |
774
|
|
|
foreach ($targetPlayers as $targetPlayer) { |
775
|
|
|
$results['TotalDamagePerTargetPlayer'][$targetPlayer->getAccountID()] = 0; |
776
|
|
|
} |
777
|
|
|
if ($thisPlayer->isDead()) { |
778
|
|
|
$results['DeadBeforeShot'] = true; |
779
|
|
|
return $results; |
780
|
|
|
} |
781
|
|
|
$results['DeadBeforeShot'] = false; |
782
|
|
|
foreach ($this->weapons as $orderID => $weapon) { |
783
|
|
|
$results['Weapons'][$orderID] = $weapon->shootPlayer($thisPlayer, array_rand_value($targetPlayers)); |
|
|
|
|
784
|
|
|
if ($results['Weapons'][$orderID]['Hit']) { |
785
|
|
|
$results['TotalDamage'] += $results['Weapons'][$orderID]['ActualDamage']['TotalDamage']; |
786
|
|
|
$results['TotalDamagePerTargetPlayer'][$results['Weapons'][$orderID]['TargetPlayer']->getAccountID()] += $results['Weapons'][$orderID]['ActualDamage']['TotalDamage']; |
787
|
|
|
} |
788
|
|
|
} |
789
|
|
|
if ($this->hasCDs()) { |
790
|
|
|
$thisCDs = new SmrCombatDrones($this->getCDs()); |
791
|
|
|
$results['Drones'] = $thisCDs->shootPlayer($thisPlayer, array_rand_value($targetPlayers)); |
792
|
|
|
$results['TotalDamage'] += $results['Drones']['ActualDamage']['TotalDamage']; |
793
|
|
|
$results['TotalDamagePerTargetPlayer'][$results['Drones']['TargetPlayer']->getAccountID()] += $results['Drones']['ActualDamage']['TotalDamage']; |
794
|
|
|
} |
795
|
|
|
$thisPlayer->increaseExperience(IRound($results['TotalDamage'] * self::EXP_PER_DAMAGE_PLAYER)); |
|
|
|
|
796
|
|
|
$thisPlayer->increaseHOF($results['TotalDamage'], ['Combat', 'Player', 'Damage Done'], HOF_PUBLIC); |
797
|
|
|
$thisPlayer->increaseHOF(1, ['Combat', 'Player', 'Shots'], HOF_PUBLIC); |
798
|
|
|
return $results; |
799
|
|
|
} |
800
|
|
|
|
801
|
|
|
/** |
802
|
|
|
* @return array<string, mixed> |
803
|
|
|
*/ |
804
|
|
|
public function shootForces(SmrForce $forces): array { |
805
|
|
|
$thisPlayer = $this->getPlayer(); |
806
|
|
|
$results = ['Player' => $thisPlayer, 'TotalDamage' => 0, 'Weapons' => []]; |
807
|
|
|
if ($thisPlayer->isDead()) { |
808
|
|
|
$results['DeadBeforeShot'] = true; |
809
|
|
|
return $results; |
810
|
|
|
} |
811
|
|
|
$results['DeadBeforeShot'] = false; |
812
|
|
|
foreach ($this->weapons as $orderID => $weapon) { |
813
|
|
|
$results['Weapons'][$orderID] = $weapon->shootForces($thisPlayer, $forces); |
814
|
|
|
if ($results['Weapons'][$orderID]['Hit']) { |
815
|
|
|
$results['TotalDamage'] += $results['Weapons'][$orderID]['ActualDamage']['TotalDamage']; |
816
|
|
|
$thisPlayer->increaseHOF($results['Weapons'][$orderID]['ActualDamage']['NumMines'], ['Combat', 'Forces', 'Mines', 'Killed'], HOF_PUBLIC); |
817
|
|
|
$thisPlayer->increaseHOF($results['Weapons'][$orderID]['ActualDamage']['Mines'], ['Combat', 'Forces', 'Mines', 'Damage Done'], HOF_PUBLIC); |
818
|
|
|
$thisPlayer->increaseHOF($results['Weapons'][$orderID]['ActualDamage']['NumCDs'], ['Combat', 'Forces', 'Combat Drones', 'Killed'], HOF_PUBLIC); |
819
|
|
|
$thisPlayer->increaseHOF($results['Weapons'][$orderID]['ActualDamage']['CDs'], ['Combat', 'Forces', 'Combat Drones', 'Damage Done'], HOF_PUBLIC); |
820
|
|
|
$thisPlayer->increaseHOF($results['Weapons'][$orderID]['ActualDamage']['NumSDs'], ['Combat', 'Forces', 'Scout Drones', 'Killed'], HOF_PUBLIC); |
821
|
|
|
$thisPlayer->increaseHOF($results['Weapons'][$orderID]['ActualDamage']['SDs'], ['Combat', 'Forces', 'Scout Drones', 'Damage Done'], HOF_PUBLIC); |
822
|
|
|
$thisPlayer->increaseHOF($results['Weapons'][$orderID]['ActualDamage']['NumMines'] + $results['Weapons'][$orderID]['ActualDamage']['NumCDs'] + $results['Weapons'][$orderID]['ActualDamage']['NumSDs'], ['Combat', 'Forces', 'Killed'], HOF_PUBLIC); |
823
|
|
|
} |
824
|
|
|
} |
825
|
|
|
if ($this->hasCDs()) { |
826
|
|
|
$thisCDs = new SmrCombatDrones($this->getCDs()); |
827
|
|
|
$results['Drones'] = $thisCDs->shootForces($thisPlayer, $forces); |
828
|
|
|
$results['TotalDamage'] += $results['Drones']['ActualDamage']['TotalDamage']; |
829
|
|
|
$thisPlayer->increaseHOF($results['Drones']['ActualDamage']['NumMines'], ['Combat', 'Forces', 'Mines', 'Killed'], HOF_PUBLIC); |
830
|
|
|
$thisPlayer->increaseHOF($results['Drones']['ActualDamage']['Mines'], ['Combat', 'Forces', 'Mines', 'Damage Done'], HOF_PUBLIC); |
831
|
|
|
$thisPlayer->increaseHOF($results['Drones']['ActualDamage']['NumCDs'], ['Combat', 'Forces', 'Combat Drones', 'Killed'], HOF_PUBLIC); |
832
|
|
|
$thisPlayer->increaseHOF($results['Drones']['ActualDamage']['CDs'], ['Combat', 'Forces', 'Combat Drones', 'Damage Done'], HOF_PUBLIC); |
833
|
|
|
$thisPlayer->increaseHOF($results['Drones']['ActualDamage']['NumSDs'], ['Combat', 'Forces', 'Scout Drones', 'Killed'], HOF_PUBLIC); |
834
|
|
|
$thisPlayer->increaseHOF($results['Drones']['ActualDamage']['SDs'], ['Combat', 'Forces', 'Scout Drones', 'Damage Done'], HOF_PUBLIC); |
835
|
|
|
$thisPlayer->increaseHOF($results['Drones']['ActualDamage']['NumMines'] + $results['Drones']['ActualDamage']['NumCDs'] + $results['Drones']['ActualDamage']['NumSDs'], ['Combat', 'Forces', 'Killed'], HOF_PUBLIC); |
836
|
|
|
} |
837
|
|
|
$thisPlayer->increaseExperience(IRound($results['TotalDamage'] * self::EXP_PER_DAMAGE_FORCE)); |
|
|
|
|
838
|
|
|
$thisPlayer->increaseHOF($results['TotalDamage'], ['Combat', 'Forces', 'Damage Done'], HOF_PUBLIC); |
839
|
|
|
$thisPlayer->increaseHOF(1, ['Combat', 'Forces', 'Shots'], HOF_PUBLIC); |
840
|
|
|
return $results; |
841
|
|
|
} |
842
|
|
|
|
843
|
|
|
/** |
844
|
|
|
* @return array<string, mixed> |
845
|
|
|
*/ |
846
|
|
|
public function shootPort(SmrPort $port): array { |
847
|
|
|
$thisPlayer = $this->getPlayer(); |
848
|
|
|
$results = ['Player' => $thisPlayer, 'TotalDamage' => 0, 'Weapons' => []]; |
849
|
|
|
if ($thisPlayer->isDead()) { |
850
|
|
|
$results['DeadBeforeShot'] = true; |
851
|
|
|
return $results; |
852
|
|
|
} |
853
|
|
|
$results['DeadBeforeShot'] = false; |
854
|
|
|
foreach ($this->weapons as $orderID => $weapon) { |
855
|
|
|
$results['Weapons'][$orderID] = $weapon->shootPort($thisPlayer, $port); |
856
|
|
|
if ($results['Weapons'][$orderID]['Hit']) { |
857
|
|
|
$results['TotalDamage'] += $results['Weapons'][$orderID]['ActualDamage']['TotalDamage']; |
858
|
|
|
} |
859
|
|
|
} |
860
|
|
|
if ($this->hasCDs()) { |
861
|
|
|
$thisCDs = new SmrCombatDrones($this->getCDs()); |
862
|
|
|
$results['Drones'] = $thisCDs->shootPort($thisPlayer, $port); |
863
|
|
|
$results['TotalDamage'] += $results['Drones']['ActualDamage']['TotalDamage']; |
864
|
|
|
} |
865
|
|
|
$thisPlayer->increaseExperience(IRound($results['TotalDamage'] * self::EXP_PER_DAMAGE_PORT)); |
|
|
|
|
866
|
|
|
$thisPlayer->increaseHOF($results['TotalDamage'], ['Combat', 'Port', 'Damage Done'], HOF_PUBLIC); |
867
|
|
|
//$thisPlayer->increaseHOF(1,array('Combat','Port','Shots')); //in SmrPort::attackedBy() |
868
|
|
|
|
869
|
|
|
// Change alignment if we reach a damage threshold. |
870
|
|
|
// Increase if player and port races are at war; decrease otherwise. |
871
|
|
|
if ($results['TotalDamage'] >= SmrPort::DAMAGE_NEEDED_FOR_ALIGNMENT_CHANGE) { |
872
|
|
|
$relations = Globals::getRaceRelations($thisPlayer->getGameID(), $thisPlayer->getRaceID()); |
873
|
|
|
if ($relations[$port->getRaceID()] <= RELATIONS_WAR) { |
874
|
|
|
$thisPlayer->increaseAlignment(1); |
875
|
|
|
$thisPlayer->increaseHOF(1, ['Combat', 'Port', 'Alignment', 'Gain'], HOF_PUBLIC); |
876
|
|
|
} else { |
877
|
|
|
$thisPlayer->decreaseAlignment(1); |
878
|
|
|
$thisPlayer->increaseHOF(1, ['Combat', 'Port', 'Alignment', 'Loss'], HOF_PUBLIC); |
879
|
|
|
} |
880
|
|
|
} |
881
|
|
|
return $results; |
882
|
|
|
} |
883
|
|
|
|
884
|
|
|
/** |
885
|
|
|
* @return array<string, mixed> |
886
|
|
|
*/ |
887
|
|
|
public function shootPlanet(SmrPlanet $planet): array { |
888
|
|
|
$thisPlayer = $this->getPlayer(); |
889
|
|
|
$results = ['Player' => $thisPlayer, 'TotalDamage' => 0, 'Weapons' => []]; |
890
|
|
|
if ($thisPlayer->isDead()) { |
891
|
|
|
$results['DeadBeforeShot'] = true; |
892
|
|
|
return $results; |
893
|
|
|
} |
894
|
|
|
$results['DeadBeforeShot'] = false; |
895
|
|
|
foreach ($this->weapons as $orderID => $weapon) { |
896
|
|
|
$results['Weapons'][$orderID] = $weapon->shootPlanet($thisPlayer, $planet); |
897
|
|
|
if ($results['Weapons'][$orderID]['Hit']) { |
898
|
|
|
$results['TotalDamage'] += $results['Weapons'][$orderID]['ActualDamage']['TotalDamage']; |
899
|
|
|
} |
900
|
|
|
} |
901
|
|
|
if ($this->hasCDs()) { |
902
|
|
|
$thisCDs = new SmrCombatDrones($this->getCDs()); |
903
|
|
|
$results['Drones'] = $thisCDs->shootPlanet($thisPlayer, $planet); |
904
|
|
|
$results['TotalDamage'] += $results['Drones']['ActualDamage']['TotalDamage']; |
905
|
|
|
} |
906
|
|
|
$thisPlayer->increaseExperience(IRound($results['TotalDamage'] * self::EXP_PER_DAMAGE_PLANET)); |
|
|
|
|
907
|
|
|
$thisPlayer->increaseHOF($results['TotalDamage'], ['Combat', 'Planet', 'Damage Done'], HOF_PUBLIC); |
908
|
|
|
//$thisPlayer->increaseHOF(1,array('Combat','Planet','Shots')); //in SmrPlanet::attackedBy() |
909
|
|
|
return $results; |
910
|
|
|
} |
911
|
|
|
|
912
|
|
|
/** |
913
|
|
|
* @param array<string, int|bool> $damage |
914
|
|
|
* @return array<string, int|bool> |
915
|
|
|
*/ |
916
|
|
|
public function takeDamage(array $damage): array { |
917
|
|
|
$alreadyDead = $this->getPlayer()->isDead(); |
918
|
|
|
$armourDamage = 0; |
919
|
|
|
$cdDamage = 0; |
920
|
|
|
$shieldDamage = 0; |
921
|
|
|
if (!$alreadyDead) { |
922
|
|
|
// Even if the weapon doesn't do any damage, it was fired at the |
923
|
|
|
// player, so alert them that they're under attack. |
924
|
|
|
$this->getPlayer()->setUnderAttack(true); |
925
|
|
|
|
926
|
|
|
$shieldDamage = $this->takeDamageToShields($damage['Shield']); |
|
|
|
|
927
|
|
|
if (!$this->hasShields() && ($shieldDamage == 0 || $damage['Rollover'])) { |
928
|
|
|
$cdMaxDamage = $damage['Armour'] - $shieldDamage; |
929
|
|
|
$cdDamage = $this->takeDamageToCDs($cdMaxDamage); |
930
|
|
|
if (!$this->hasCDs() && ($cdDamage == 0 || $damage['Rollover'])) { |
931
|
|
|
$armourMaxDamage = $damage['Armour'] - $shieldDamage - $cdDamage; |
932
|
|
|
$armourDamage = $this->takeDamageToArmour($armourMaxDamage); |
933
|
|
|
} |
934
|
|
|
} |
935
|
|
|
} |
936
|
|
|
return [ |
937
|
|
|
'KillingShot' => !$alreadyDead && $this->isDead(), |
938
|
|
|
'TargetAlreadyDead' => $alreadyDead, |
939
|
|
|
'Shield' => $shieldDamage, |
940
|
|
|
'CDs' => $cdDamage, |
941
|
|
|
'NumCDs' => $cdDamage / CD_ARMOUR, |
942
|
|
|
'HasCDs' => $this->hasCDs(), |
943
|
|
|
'Armour' => $armourDamage, |
944
|
|
|
'TotalDamage' => $shieldDamage + $cdDamage + $armourDamage, |
945
|
|
|
]; |
946
|
|
|
} |
947
|
|
|
|
948
|
|
|
/** |
949
|
|
|
* @param array<string, int|bool> $damage |
950
|
|
|
* @return array<string, int|bool> |
951
|
|
|
*/ |
952
|
|
|
public function takeDamageFromMines(array $damage): array { |
953
|
|
|
$alreadyDead = $this->getPlayer()->isDead(); |
954
|
|
|
$armourDamage = 0; |
955
|
|
|
$cdDamage = 0; |
956
|
|
|
$shieldDamage = 0; |
957
|
|
|
if (!$alreadyDead) { |
958
|
|
|
$shieldDamage = $this->takeDamageToShields($damage['Shield']); |
|
|
|
|
959
|
|
|
if (!$this->hasShields() && ($shieldDamage == 0 || $damage['Rollover'])) { //skip CDs if it's mines |
960
|
|
|
$armourMaxDamage = $damage['Armour'] - $shieldDamage; |
961
|
|
|
$armourDamage = $this->takeDamageToArmour($armourMaxDamage); |
962
|
|
|
} |
963
|
|
|
} |
964
|
|
|
return [ |
965
|
|
|
'KillingShot' => !$alreadyDead && $this->isDead(), |
966
|
|
|
'TargetAlreadyDead' => $alreadyDead, |
967
|
|
|
'Shield' => $shieldDamage, |
968
|
|
|
'CDs' => $cdDamage, |
969
|
|
|
'NumCDs' => $cdDamage / CD_ARMOUR, |
970
|
|
|
'HasCDs' => $this->hasCDs(), |
971
|
|
|
'Armour' => $armourDamage, |
972
|
|
|
'TotalDamage' => $shieldDamage + $cdDamage + $armourDamage, |
973
|
|
|
]; |
974
|
|
|
} |
975
|
|
|
|
976
|
|
|
protected function takeDamageToShields(int $damage): int { |
977
|
|
|
$actualDamage = min($this->getShields(), $damage); |
978
|
|
|
$this->decreaseShields($actualDamage); |
979
|
|
|
return $actualDamage; |
980
|
|
|
} |
981
|
|
|
|
982
|
|
|
protected function takeDamageToCDs(int $damage): int { |
983
|
|
|
$actualDamage = min($this->getCDs(), IFloor($damage / CD_ARMOUR)); |
|
|
|
|
984
|
|
|
$this->decreaseCDs($actualDamage); |
985
|
|
|
return $actualDamage * CD_ARMOUR; |
986
|
|
|
} |
987
|
|
|
|
988
|
|
|
protected function takeDamageToArmour(int $damage): int { |
989
|
|
|
$actualDamage = min($this->getArmour(), $damage); |
990
|
|
|
$this->decreaseArmour($actualDamage); |
991
|
|
|
return $actualDamage; |
992
|
|
|
} |
993
|
|
|
|
994
|
|
|
/** |
995
|
|
|
* Returns the maneuverability rating for this ship. |
996
|
|
|
*/ |
997
|
|
|
public function getMR(): int { |
998
|
|
|
return $this->shipType->getBaseManeuverability(); |
999
|
|
|
} |
1000
|
|
|
|
1001
|
|
|
public function update(): void { |
1002
|
|
|
throw new Exception('Can only call update on SmrShip objects'); |
1003
|
|
|
} |
1004
|
|
|
|
1005
|
|
|
} |
1006
|
|
|
|