Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

Passed
Pull Request — master (#928)
by Dan
04:27
created

AbstractSmrShip::checkForExcessCargo()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 11
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 9
nc 4
nop 0
dl 0
loc 11
rs 9.9666
c 0
b 0
f 0
1
<?php declare(strict_types=1);
2
3
abstract class AbstractSmrShip {
4
	protected static $CACHE_BASE_SHIPS = array();
5
6
	const SHIP_CLASS_RAIDER = 3;
7
	const SHIP_CLASS_SCOUT = 4;
8
9
	// Player exp gained for each point of damage done
10
	const EXP_PER_DAMAGE_PLAYER = 0.375;
11
	const EXP_PER_DAMAGE_PLANET = 1.0; // note that planet damage is reduced
12
	const EXP_PER_DAMAGE_PORT   = 0.15;
13
	const EXP_PER_DAMAGE_FORCE  = 0.075;
14
15
	const STARTER_SHIPS = [
16
		RACE_NEUTRAL => SHIP_TYPE_GALACTIC_SEMI,
17
		RACE_ALSKANT => SHIP_TYPE_SMALL_TIMER,
18
		RACE_CREONTI => SHIP_TYPE_MEDIUM_CARGO_HULK,
19
		RACE_HUMAN => SHIP_TYPE_LIGHT_FREIGHTER,
20
		RACE_IKTHORNE => SHIP_TYPE_TINY_DELIGHT,
21
		RACE_SALVENE => SHIP_TYPE_HATCHLINGS_DUE,
22
		RACE_THEVIAN => SHIP_TYPE_SWIFT_VENTURE,
23
		RACE_WQHUMAN => SHIP_TYPE_SLIP_FREIGHTER,
24
		RACE_NIJARIN => SHIP_TYPE_REDEEMER,
25
	];
26
27
	protected $player;
28
29
	protected $gameID;
30
	protected $baseShip;
31
32
	protected $hardware;
33
	protected $oldHardware;
34
35
	protected $cargo;
36
37
	protected $weapons = array();
38
39
	protected $illusionShip;
40
41
	protected $hasChangedWeapons = false;
42
	protected $hasChangedCargo = false;
43
	protected $hasChangedHardware = array();
44
45
	public static function getBaseShip($gameTypeID, $shipTypeID, $forceUpdate = false) {
46
		if ($forceUpdate || !isset(self::$CACHE_BASE_SHIPS[$gameTypeID][$shipTypeID])) {
47
			// determine ship
48
			$db = new SmrMySqlDatabase();
49
			$db->query('SELECT * FROM ship_type WHERE ship_type_id = ' . $db->escapeNumber($shipTypeID) . ' LIMIT 1'); //TODO add game type id
50
			if ($db->nextRecord()) {
51
				self::$CACHE_BASE_SHIPS[$gameTypeID][$shipTypeID] = self::buildBaseShip($db);
52
			} else {
53
				self::$CACHE_BASE_SHIPS[$gameTypeID][$shipTypeID] = false;
54
			}
55
		}
56
		return self::$CACHE_BASE_SHIPS[$gameTypeID][$shipTypeID];
57
	}
58
59
	protected static function buildBaseShip(MySqlDatabase $db) {
60
		$ship = array();
61
		$ship['Type'] = 'Ship';
62
		$ship['Name'] = $db->getField('ship_name');
63
		$ship['ShipTypeID'] = $db->getInt('ship_type_id');
64
		$ship['ShipClassID'] = $db->getInt('ship_class_id');
65
		$ship['RaceID'] = $db->getInt('race_id');
66
		$ship['Hardpoint'] = $db->getInt('hardpoint');
67
		$ship['Speed'] = $db->getInt('speed');
68
		$ship['Cost'] = $db->getInt('cost');
69
		$ship['AlignRestriction'] = $db->getInt('buyer_restriction');
70
		$ship['Level'] = $db->getInt('lvl_needed');
71
72
		$maxPower = 0;
73
		switch ($ship['Hardpoint']) {
74
			default:
75
				$maxPower += 1 * $ship['Hardpoint'] - 10;
76
			case 10:
77
				$maxPower += 2;
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment if this fall-through is intended.
Loading history...
78
			case 9:
79
				$maxPower += 2;
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment if this fall-through is intended.
Loading history...
80
			case 8:
81
				$maxPower += 2;
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment if this fall-through is intended.
Loading history...
82
			case 7:
83
				$maxPower += 2;
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment if this fall-through is intended.
Loading history...
84
			case 6:
85
				$maxPower += 3;
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment if this fall-through is intended.
Loading history...
86
			case 5:
87
				$maxPower += 3;
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment if this fall-through is intended.
Loading history...
88
			case 4:
89
				$maxPower += 3;
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment if this fall-through is intended.
Loading history...
90
			case 3:
91
				$maxPower += 4;
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment if this fall-through is intended.
Loading history...
92
			case 2:
93
				$maxPower += 4;
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment if this fall-through is intended.
Loading history...
94
			case 1:
95
				$maxPower += 5;
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment if this fall-through is intended.
Loading history...
96
			case 0:
97
				$maxPower += 0;
98
		}
99
		$ship['MaxPower'] = $maxPower;
100
101
102
		// get supported hardware from db
103
		$db2 = new SmrMySqlDatabase();
104
		$db2->query('SELECT hardware_type_id, max_amount FROM ship_type_support_hardware ' .
105
			'WHERE ship_type_id = ' . $db2->escapeNumber($ship['ShipTypeID']) . ' ORDER BY hardware_type_id');
106
107
		while ($db2->nextRecord()) {
108
			// adding hardware to array
109
			$ship['MaxHardware'][$db2->getInt('hardware_type_id')] = $db2->getInt('max_amount');
110
		}
111
112
		$ship['BaseMR'] = IRound(
113
								700 -
114
								(
115
									(
116
										$ship['MaxHardware'][HARDWARE_SHIELDS]
117
										+$ship['MaxHardware'][HARDWARE_ARMOUR]
118
										+$ship['MaxHardware'][HARDWARE_COMBAT] * 3
119
									) / 25
120
									+(
121
										$ship['MaxHardware'][HARDWARE_CARGO] / 100
122
										-$ship['Speed'] * 5
123
										+$ship['Hardpoint'] * 5
124
										+$ship['MaxHardware'][HARDWARE_COMBAT] / 5
125
									)
126
								)
127
							);
128
		return $ship;
129
	}
130
131
	public static function getAllBaseShips($gameTypeID, $forceUpdate = false) {
0 ignored issues
show
Unused Code introduced by
The parameter $forceUpdate is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

131
	public static function getAllBaseShips($gameTypeID, /** @scrutinizer ignore-unused */ $forceUpdate = false) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
132
		// determine ship
133
		$db = new SmrMySqlDatabase();
134
		$db->query('SELECT * FROM ship_type ORDER BY ship_type_id ASC'); //TODO add game type id
135
		while ($db->nextRecord()) {
136
			if (!isset(self::$CACHE_BASE_SHIPS[$gameTypeID][$db->getInt('ship_type_id')])) {
137
				self::$CACHE_BASE_SHIPS[$gameTypeID][$db->getInt('ship_type_id')] = self::buildBaseShip($db);
138
			}
139
		}
140
		return self::$CACHE_BASE_SHIPS[$gameTypeID];
141
	}
142
143
	protected function __construct(AbstractSmrPlayer $player) {
144
		$this->player = $player;
145
		$this->gameID = $player->getGameID();
146
		$this->regenerateBaseShip();
147
	}
148
149
	protected function regenerateBaseShip() {
150
		$this->baseShip = AbstractSmrShip::getBaseShip(Globals::getGameType($this->gameID), $this->player->getShipTypeID());
151
		$this->checkForExcess();
152
	}
153
154
	public function checkForExcess() {
155
		$this->checkForExcessHardware();
156
		$this->checkForExcessWeapons();
157
		$this->checkForExcessCargo();
158
	}
159
160
	public function checkForExcessWeapons() {
161
		while ($this->hasWeapons() && ($this->getPowerUsed() > $this->getMaxPower() || $this->getNumWeapons() > $this->getHardpoints())) {
162
			//erase the first weapon 1 at a time until we are okay
163
			$this->removeLastWeapon();
164
		}
165
	}
166
167
	public function checkForExcessCargo() {
168
		if ($this->hasCargo()) {
169
			$excess = array_sum($this->getCargo()) - $this->getCargoHolds();
170
			foreach ($this->getCargo() as $goodID => $amount) {
171
				if ($excess > 0) {
172
					$decreaseAmount = min($amount, $excess);
173
					$this->decreaseCargo($goodID, $decreaseAmount);
174
					$excess -= $decreaseAmount;
175
				} else {
176
					// No more excess cargo
177
					break;
178
				}
179
			}
180
		}
181
	}
182
183
	public function checkForExcessHardware() {
184
		//check hardware to see if anything needs to be removed
185
		if (is_array($hardware = $this->getHardware())) {
186
			foreach ($hardware as $hardwareTypeID => $amount) {
187
				if ($amount > ($max = $this->getMaxHardware($hardwareTypeID))) {
188
					$this->setHardware($hardwareTypeID, $max, true);
0 ignored issues
show
Unused Code introduced by
The call to AbstractSmrShip::setHardware() has too many arguments starting with true. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

188
					$this->/** @scrutinizer ignore-call */ 
189
            setHardware($hardwareTypeID, $max, true);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
189
				}
190
			}
191
		}
192
	}
193
194
	/**
195
	 * Set all hardware to its maximum value for this ship.
196
	 */
197
	public function setHardwareToMax() {
198
		foreach ($this->getMaxHardware() as $key => $max) {
199
			$this->setHardware($key, $max);
200
		}
201
		$this->removeUnderAttack();
202
	}
203
204
	public function getPowerUsed() {
205
		$power = 0;
206
		foreach ($this->weapons as $weapon) {
207
			$power += $weapon->getPowerLevel();
208
		}
209
		return $power;
210
	}
211
212
	public function getRemainingPower() {
213
		return $this->getMaxPower() - $this->getPowerUsed();
214
	}
215
216
	/**
217
	 * given power level of new weapon, return whether there is enough power available to install it on this ship
218
	 */
219
	public function checkPowerAvailable($powerLevel) {
220
		return $this->getRemainingPower() >= $powerLevel;
221
	}
222
223
	public function getMaxPower() {
224
		return $this->baseShip['MaxPower'];
225
	}
226
227
	public function hasIllegalGoods() {
228
		return $this->hasCargo(GOODS_SLAVES) || $this->hasCargo(GOODS_WEAPONS) || $this->hasCargo(GOODS_NARCOTICS);
229
	}
230
231
	public function getDisplayAttackRating(AbstractSmrPlayer $player) {
0 ignored issues
show
Unused Code introduced by
The parameter $player is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

231
	public function getDisplayAttackRating(/** @scrutinizer ignore-unused */ AbstractSmrPlayer $player) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
232
		if ($this->hasActiveIllusion()) {
233
			return $this->getIllusionAttack();
234
		} else {
235
			return $this->getAttackRating();
236
		}
237
	}
238
239
	public function getDisplayDefenseRating() {
240
		if ($this->hasActiveIllusion()) {
241
			return $this->getIllusionDefense();
242
		} else {
243
			return $this->getDefenseRating();
244
		}
245
	}
246
247
	public function getAttackRating() : int {
248
		return IRound(($this->getTotalShieldDamage() + $this->getTotalArmourDamage() + $this->getCDs() * 2) / 40);
249
	}
250
251
	public function getAttackRatingWithMaxCDs() : int {
252
		return IRound(($this->getTotalShieldDamage() + $this->getTotalArmourDamage() + $this->getMaxCDs() * .7) / 40);
253
	}
254
255
	public function getDefenseRating() : int {
256
		return IRound((($this->getShields() + $this->getArmour()) / 100) + (($this->getCDs() * 3) / 100));
257
	}
258
259
	public function getMaxDefenseRating() : int {
260
		return IRound((($this->getMaxShields() + $this->getMaxArmour()) / 100) + (($this->getMaxCDs() * 3) / 100));
261
	}
262
263
	public function getShieldLow() : int { return IFloor($this->getShields() / 100) * 100; }
264
	public function getShieldHigh() : int { return $this->getShieldLow() + 100; }
265
	public function getArmourLow() : int { return IFloor($this->getArmour() / 100) * 100; }
266
	public function getArmourHigh() : int { return $this->getArmourLow() + 100; }
267
	public function getCDsLow() : int { return IFloor($this->getCDs() / 100) * 100; }
268
	public function getCDsHigh() : int { return $this->getCDsLow() + 100; }
269
270
271
272
	public function addWeapon(SmrWeapon $weapon) {
273
		if ($this->hasOpenWeaponSlots() && $this->checkPowerAvailable($weapon->getPowerLevel())) {
274
			array_push($this->weapons, $weapon);
275
			$this->hasChangedWeapons = true;
276
			return $weapon;
277
		}
278
		$return = false;
279
		return $return;
280
	}
281
282
	public function moveWeaponUp($orderID) {
283
		$replacement = $orderID - 1;
284
		if ($replacement < 0) {
285
			// Shift everything up by one and put the selected weapon at the bottom
286
			array_push($this->weapons, array_shift($this->weapons));
287
		} else {
288
			// Swap the selected weapon with the one above it
289
			$temp = $this->weapons[$replacement];
290
			$this->weapons[$replacement] = $this->weapons[$orderID];
291
			$this->weapons[$orderID] = $temp;
292
		}
293
		$this->hasChangedWeapons = true;
294
	}
295
296
	public function moveWeaponDown($orderID) {
297
		$replacement = $orderID + 1;
298
		if ($replacement >= count($this->weapons)) {
299
			// Shift everything down by one and put the selected weapon at the top
300
			array_unshift($this->weapons, array_pop($this->weapons));
301
		} else {
302
			// Swap the selected weapon with the one below it
303
			$temp = $this->weapons[$replacement];
304
			$this->weapons[$replacement] = $this->weapons[$orderID];
305
			$this->weapons[$orderID] = $temp;
306
		}
307
		$this->hasChangedWeapons = true;
308
	}
309
310
	public function setWeaponLocations(array $orderArray) {
311
		$weapons = $this->weapons;
312
		foreach ($orderArray as $newOrder => $oldOrder) {
313
			$this->weapons[$newOrder] =& $weapons[$oldOrder];
314
		}
315
		$this->hasChangedWeapons = true;
316
	}
317
318
	public function removeLastWeapon() {
319
		$this->removeWeapon($this->getNumWeapons() - 1);
320
	}
321
322
	public function removeWeapon($orderID) {
323
		// Remove the specified weapon, then reindex the array
324
		unset($this->weapons[$orderID]);
325
		$this->weapons = array_values($this->weapons);
326
		$this->hasChangedWeapons = true;
327
	}
328
329
	public function removeAllWeapons() {
330
		$this->weapons = array();
331
		$this->hasChangedWeapons = true;
332
	}
333
334
	public function removeAllCargo() {
335
		if (is_array($this->cargo)) {
336
			foreach ($this->cargo as $goodID => $amount) {
337
				$this->setCargo($goodID, 0);
338
			}
339
		}
340
	}
341
342
	public function removeAllHardware() {
343
		foreach (array_keys($this->hardware) as $hardwareTypeID) {
344
			$this->setHardware($hardwareTypeID, 0);
345
		}
346
		$this->decloak();
347
		$this->disableIllusion();
348
	}
349
350
	public function getPod($isNewbie = false) {
351
		$this->removeAllWeapons();
352
		$this->removeAllCargo();
353
		$this->removeAllHardware();
354
355
		if ($isNewbie) {
356
			$this->setShields(75, true);
357
			$this->setArmour(150, true);
358
			$this->setCargoHolds(40);
359
			$this->setShipTypeID(SHIP_TYPE_NEWBIE_MERCHANT_VESSEL);
360
		} else {
361
			$this->setShields(50, true);
362
			$this->setArmour(50, true);
363
			$this->setCargoHolds(5);
364
			$this->setShipTypeID(SHIP_TYPE_ESCAPE_POD);
365
		}
366
367
		$this->removeUnderAttack();
368
	}
369
370
	public function giveStarterShip() : void {
371
		if ($this->player->hasNewbieStatus()) {
372
			$shipID = SHIP_TYPE_NEWBIE_MERCHANT_VESSEL;
373
			$amount_shields = 75;
374
			$amount_armour = 150;
375
		} else {
376
			$shipID = self::STARTER_SHIPS[$this->player->getRaceID()];
377
			$amount_shields = 50;
378
			$amount_armour = 50;
379
		}
380
		$this->setShipTypeID($shipID);
381
		$this->setShields($amount_shields, true);
382
		$this->setArmour($amount_armour, true);
383
		$this->setCargoHolds(40);
384
		$this->addWeapon(SmrWeapon::getWeapon(WEAPON_TYPE_LASER));
385
	}
386
387
	public function hasCloak() {
388
		return $this->getHardware(HARDWARE_CLOAK);
389
	}
390
391
	public function canHaveCloak() {
392
		return $this->getMaxHardware(HARDWARE_CLOAK);
393
	}
394
395
396
	public function hasActiveIllusion() {
397
		if (!$this->hasIllusion()) {
398
			return false;
399
		}
400
		return $this->getIllusionShip() !== false;
401
	}
402
403
	public function hasIllusion() {
404
		return $this->getHardware(HARDWARE_ILLUSION);
405
	}
406
407
	public function canHaveIllusion() {
408
		return $this->getMaxHardware(HARDWARE_ILLUSION);
409
	}
410
411
	public function hasJump() {
412
		return $this->getHardware(HARDWARE_JUMP);
413
	}
414
415
	public function canHaveJump() {
416
		return $this->getMaxHardware(HARDWARE_JUMP);
417
	}
418
419
	public function hasDCS() {
420
		return $this->getHardware(HARDWARE_DCS);
421
	}
422
423
	public function canHaveDCS() {
424
		return $this->getMaxHardware(HARDWARE_DCS);
425
	}
426
427
	public function hasScanner() {
428
		return $this->getHardware(HARDWARE_SCANNER);
429
	}
430
431
	public function canHaveScanner() {
432
		return $this->getMaxHardware(HARDWARE_SCANNER);
433
	}
434
435
	abstract public function decloak();
436
437
	abstract public function enableCloak();
438
439
	abstract public function setIllusion($ship_id, $attack, $defense);
440
441
	abstract public function disableIllusion();
442
443
	public function getIllusionShipID() {
444
		$this->getIllusionShip();
445
		return $this->illusionShip['ID'];
446
	}
447
448
	public function getIllusionShipName() {
449
		$this->getIllusionShip();
450
		return $this->illusionShip['Name'];
451
	}
452
453
	abstract public function getIllusionShip();
454
455
	public function getIllusionAttack() {
456
		$this->getIllusionShip();
457
		return $this->illusionShip['Attack'];
458
	}
459
460
	public function getIllusionDefense() {
461
		$this->getIllusionShip();
462
		return $this->illusionShip['Defense'];
463
	}
464
465
	public function getPlayer() {
466
		return $this->player;
467
	}
468
469
	public function getGameID() {
470
		return $this->gameID;
471
	}
472
473
	public function getGame() {
474
		return SmrGame::getGame($this->gameID);
475
	}
476
477
	public function getShipTypeID() {
478
		return $this->baseShip['ShipTypeID'];
479
	}
480
481
	public function getShipClassID() {
482
		return $this->baseShip['ShipClassID'];
483
	}
484
485
	/**
486
	 * Switch to a new ship, updating player turns accordingly.
487
	 */
488
	public function setShipTypeID($shipTypeID) {
489
		$oldSpeed = $this->getSpeed();
490
		$this->getPlayer()->setShipTypeID($shipTypeID);
491
		$this->regenerateBaseShip();
492
		$newSpeed = $this->getSpeed();
493
494
		// Update the player's turns to account for the speed change
495
		$oldTurns = $this->getPlayer()->getTurns();
496
		$this->getPlayer()->setTurns(IRound($oldTurns * $newSpeed / $oldSpeed));
497
	}
498
499
	public function getName() {
500
		return $this->baseShip['Name'];
501
	}
502
503
	public function getCost() {
504
		return $this->baseShip['Cost'];
505
	}
506
507
	public function getCostToUpgrade($upgradeShipID) {
508
		$upgadeBaseShip = AbstractSmrShip::getBaseShip(Globals::getGameType($this->getGameID()), $upgradeShipID);
509
		return $upgadeBaseShip['Cost'] - IFloor($this->getCost() * SHIP_REFUND_PERCENT);
510
	}
511
512
	public function getCostToUpgradeAndUNO($upgradeShipID) {
513
		return $this->getCostToUpgrade($upgradeShipID) + $this->getCostToUNOAgainstShip($upgradeShipID);
514
	}
515
516
	protected function getCostToUNOAgainstShip($shipID) {
517
		$baseShip = AbstractSmrShip::getBaseShip(Globals::getGameType($this->getGameID()), $shipID);
518
		$cost = 0;
519
		$hardwareTypes = array(HARDWARE_SHIELDS, HARDWARE_ARMOUR, HARDWARE_CARGO);
520
		foreach ($hardwareTypes as $hardwareTypeID) {
521
			$cost += max(0, $baseShip['MaxHardware'][$hardwareTypeID] - $this->getHardware($hardwareTypeID)) * Globals::getHardwareCost($hardwareTypeID);
522
		}
523
		return $cost;
524
	}
525
526
	public function getCostToUNO() {
527
		return $this->getCostToUNOAgainstShip($this->getShipTypeID());
528
	}
529
530
	/**
531
	 * Returns the base ship speed (unmodified by the game speed).
532
	 */
533
	public function getSpeed() {
534
		return $this->baseShip['Speed'];
535
	}
536
537
	/**
538
	 * Returns the ship speed modified by the game speed.
539
	 */
540
	public function getRealSpeed() {
541
		return $this->getSpeed() * $this->getGame()->getGameSpeed();
542
	}
543
544
	public function getHardware($hardwareTypeID = false) {
545
		if ($hardwareTypeID === false) {
546
			return $this->hardware;
547
		}
548
		return $this->hardware[$hardwareTypeID] ?? 0;
549
	}
550
551
	public function setHardware($hardwareTypeID, $amount) {
552
		if ($this->getHardware($hardwareTypeID) == $amount) {
553
			return;
554
		}
555
		$this->hardware[$hardwareTypeID] = $amount;
556
		$this->hasChangedHardware[$hardwareTypeID] = true;
557
	}
558
559
	public function increaseHardware($hardwareTypeID, $amount) {
560
		$this->setHardware($hardwareTypeID, $this->getHardware($hardwareTypeID) + $amount);
561
	}
562
563
	public function getOldHardware($hardwareTypeID = false) {
564
		if ($hardwareTypeID === false) {
565
			return $this->oldHardware;
566
		}
567
		return $this->oldHardware[$hardwareTypeID] ?? 0;
568
	}
569
570
	public function setOldHardware($hardwareTypeID, $amount) {
571
		if ($this->getOldHardware($hardwareTypeID) == $amount) {
572
			return;
573
		}
574
		$this->oldHardware[$hardwareTypeID] = $amount;
575
		$this->hasChangedHardware[$hardwareTypeID] = true;
576
	}
577
578
	public function hasMaxHardware($hardwareTypeID) {
579
		return $this->getHardware($hardwareTypeID) == $this->getMaxHardware($hardwareTypeID);
580
	}
581
582
	public function getMaxHardware($hardwareTypeID = false) {
583
		if ($hardwareTypeID === false) {
584
			return $this->baseShip['MaxHardware'];
585
		}
586
		return $this->baseShip['MaxHardware'][$hardwareTypeID];
587
	}
588
589
	public function getShields() {
590
		return $this->getHardware(HARDWARE_SHIELDS);
591
	}
592
593
	public function setShields($amount, $updateOldAmount = false) {
594
		if ($updateOldAmount && !$this->hasLostShields()) {
595
			$this->setOldHardware(HARDWARE_SHIELDS, $amount);
596
		}
597
		$this->setHardware(HARDWARE_SHIELDS, $amount);
598
	}
599
600
	public function decreaseShields($amount) {
601
		$this->setShields($this->getShields() - $amount);
602
	}
603
604
	public function increaseShields($amount) {
605
		$this->setShields($this->getShields() + $amount);
606
	}
607
608
	public function getOldShields() {
609
		return $this->getOldHardware(HARDWARE_SHIELDS);
610
	}
611
612
	public function setOldShields($amount) {
613
		$this->setOldHardware(HARDWARE_SHIELDS, $amount);
614
	}
615
616
	public function hasShields() {
617
		return $this->getShields() > 0;
618
	}
619
620
	public function hasLostShields() {
621
		return $this->getShields() < $this->getOldShields();
622
	}
623
624
	public function hasMaxShields() {
625
		return $this->getShields() == $this->getMaxShields();
626
	}
627
628
	public function getMaxShields() {
629
		return $this->getMaxHardware(HARDWARE_SHIELDS);
630
	}
631
632
	public function getArmour() {
633
		return $this->getHardware(HARDWARE_ARMOUR);
634
	}
635
636
	public function setArmour($amount, $updateOldAmount = false) {
637
		if ($updateOldAmount && !$this->hasLostArmour()) {
638
			$this->setOldHardware(HARDWARE_ARMOUR, $amount);
639
		}
640
		$this->setHardware(HARDWARE_ARMOUR, $amount);
641
	}
642
643
	public function decreaseArmour($amount) {
644
		$this->setArmour($this->getArmour() - $amount);
645
	}
646
647
	public function increaseArmour($amount) {
648
		$this->setArmour($this->getArmour() + $amount);
649
	}
650
651
	public function getOldArmour() {
652
		return $this->getOldHardware(HARDWARE_ARMOUR);
653
	}
654
655
	public function setOldArmour($amount) {
656
		$this->setOldHardware(HARDWARE_ARMOUR, $amount);
657
	}
658
659
	public function hasArmour() {
660
		return $this->getArmour() > 0;
661
	}
662
663
	public function hasLostArmour() {
664
		return $this->getArmour() < $this->getOldArmour();
665
	}
666
667
	public function hasMaxArmour() {
668
		return $this->getArmour() == $this->getMaxArmour();
669
	}
670
671
	public function getMaxArmour() {
672
		return $this->getMaxHardware(HARDWARE_ARMOUR);
673
	}
674
675
	public function isDead() {
676
		return !$this->hasArmour() && !$this->hasShields();
677
	}
678
679
	public function canAcceptCDs() {
680
		return $this->getCDs() < $this->getMaxCDs();
681
	}
682
683
	public function canAcceptSDs() {
684
		return $this->getSDs() < $this->getMaxSDs();
685
	}
686
687
	public function canAcceptMines() {
688
		return $this->getMines() < $this->getMaxMines();
689
	}
690
691
	public function hasCDs() {
692
		return $this->getCDs() > 0;
693
	}
694
695
	public function hasSDs() {
696
		return $this->getSDs() > 0;
697
	}
698
699
	public function hasMines() {
700
		return $this->getMines() > 0;
701
	}
702
703
	public function getCDs() {
704
		return $this->getHardware(HARDWARE_COMBAT);
705
	}
706
707
	public function setCDs($amount, $updateOldAmount = false) {
708
		if ($updateOldAmount && !$this->hasLostCDs()) {
709
			$this->setOldHardware(HARDWARE_COMBAT, $amount);
710
		}
711
		$this->setHardware(HARDWARE_COMBAT, $amount);
712
	}
713
714
	/**
715
	 * Decreases the ship CDs. Use $updateOldAmount=true to prevent
716
	 * this change from triggering `isUnderAttack`.
717
	 */
718
	public function decreaseCDs($amount, $updateOldAmount = false) {
719
		$this->setCDs($this->getCDs() - $amount, $updateOldAmount);
720
	}
721
722
	public function increaseCDs($amount) {
723
		$this->setCDs($this->getCDs() + $amount);
724
	}
725
726
	public function getOldCDs() {
727
		return $this->getOldHardware(HARDWARE_COMBAT);
728
	}
729
730
	public function setOldCDs($amount) {
731
		$this->setOldHardware(HARDWARE_COMBAT, $amount);
732
	}
733
734
	public function hasLostCDs() {
735
		return $this->getCDs() < $this->getOldCDs();
736
	}
737
738
	public function getMaxCDs() {
739
		return $this->getMaxHardware(HARDWARE_COMBAT);
740
	}
741
742
	public function getSDs() {
743
		return $this->getHardware(HARDWARE_SCOUT);
744
	}
745
746
	public function setSDs($amount) {
747
		$this->setHardware(HARDWARE_SCOUT, $amount);
748
	}
749
750
	public function decreaseSDs($amount) {
751
		$this->setSDs($this->getSDs() - $amount);
752
	}
753
754
	public function increaseSDs($amount) {
755
		$this->setSDs($this->getSDs() + $amount);
756
	}
757
758
	public function getMaxSDs() {
759
		return $this->getMaxHardware(HARDWARE_SCOUT);
760
	}
761
762
	public function getMines() {
763
		return $this->getHardware(HARDWARE_MINE);
764
	}
765
766
	public function setMines($amount) {
767
		$this->setHardware(HARDWARE_MINE, $amount);
768
	}
769
770
	public function decreaseMines($amount) {
771
		$this->setMines($this->getMines() - $amount);
772
	}
773
774
	public function increaseMines($amount) {
775
		$this->setMines($this->getMines() + $amount);
776
	}
777
778
	public function getMaxMines() {
779
		return $this->getMaxHardware(HARDWARE_MINE);
780
	}
781
782
	public function getCargoHolds() {
783
		return $this->getHardware(HARDWARE_CARGO);
784
	}
785
786
	public function setCargoHolds($amount) {
787
		$this->setHardware(HARDWARE_CARGO, $amount);
788
	}
789
790
	public function getCargo($goodID = false) {
791
		if ($goodID !== false) {
792
			if (isset($this->cargo[$goodID])) {
793
				return $this->cargo[$goodID];
794
			}
795
			$cargo = 0;
796
			return $cargo;
797
		}
798
		return $this->cargo;
799
	}
800
801
	public function hasCargo($goodID = false) {
802
		if ($goodID !== false) {
803
			return $this->getCargo($goodID) > 0;
804
		}
805
		if (is_array($cargo = $this->getCargo())) {
806
			return array_sum($cargo) > 0;
807
		}
808
		return false;
809
	}
810
811
	public function setCargo($goodID, $amount) {
812
		if ($this->getCargo($goodID) == $amount) {
813
			return;
814
		}
815
		$this->cargo[$goodID] = $amount;
816
		$this->hasChangedCargo = true;
817
		// Sort cargo by goodID to make sure it shows up in the correct order
818
		// before the next page is loaded.
819
		ksort($this->cargo);
820
	}
821
822
	public function decreaseCargo($goodID, $amount) {
823
		if ($amount < 0) {
824
			throw new Exception('Trying to decrease negative cargo.');
825
		}
826
		$this->setCargo($goodID, $this->getCargo($goodID) - $amount);
827
	}
828
829
	public function increaseCargo($goodID, $amount) {
830
		if ($amount < 0) {
831
			throw new Exception('Trying to increase negative cargo.');
832
		}
833
		$this->setCargo($goodID, $this->getCargo($goodID) + $amount);
834
	}
835
836
	public function getEmptyHolds() {
837
		return $this->getCargoHolds() - $this->getUsedHolds();
838
	}
839
840
	public function getUsedHolds() {
841
		return array_sum($this->getCargo());
842
	}
843
844
	public function hasMaxCargoHolds() {
845
		return $this->getCargoHolds() == $this->getMaxCargoHolds();
846
	}
847
848
	public function getMaxCargoHolds() {
849
		return $this->getMaxHardware(HARDWARE_CARGO);
850
	}
851
852
	public function isUnderAttack() {
853
		return $this->hasLostShields() || $this->hasLostArmour() || $this->hasLostCDs();
854
	}
855
856
	public function removeUnderAttack() {
857
		global $var;
858
		$underAttack = $this->isUnderAttack();
859
		$this->setOldShields($this->getShields());
860
		$this->setOldCDs($this->getCDs());
861
		$this->setOldArmour($this->getArmour());
862
		if (isset($var['UnderAttack'])) {
863
			return $var['UnderAttack'];
864
		}
865
		if ($underAttack && !USING_AJAX) {
866
			SmrSession::updateVar('UnderAttack', $underAttack); //Remember we are under attack for AJAX
867
		}
868
		return $underAttack;
869
	}
870
871
	public function hasWeapons() {
872
		return $this->getNumWeapons() > 0;
873
	}
874
875
	public function getWeapons() {
876
		return $this->weapons;
877
	}
878
879
	public function canAttack() {
880
		return $this->hasWeapons() || $this->hasCDs();
881
	}
882
883
	public function getNumWeapons() {
884
		return count($this->getWeapons());
885
	}
886
887
	public function getOpenWeaponSlots() {
888
		return $this->getHardpoints() - $this->getNumWeapons();
889
	}
890
891
	public function hasOpenWeaponSlots() {
892
		return $this->getOpenWeaponSlots() > 0;
893
	}
894
895
	public function getHardpoints() {
896
		return $this->baseShip['Hardpoint'];
897
	}
898
899
	public function getTotalShieldDamage() {
900
		$weapons = $this->getWeapons();
901
		$shieldDamage = 0;
902
		foreach ($weapons as $weapon) {
903
			$shieldDamage += $weapon->getShieldDamage();
904
		}
905
		return $shieldDamage;
906
	}
907
908
	public function getTotalArmourDamage() {
909
		$weapons = $this->getWeapons();
910
		$armourDamage = 0;
911
		foreach ($weapons as $weapon) {
912
			$armourDamage += $weapon->getArmourDamage();
913
		}
914
		return $armourDamage;
915
	}
916
917
	public function isFederal() {
918
		return $this->getShipTypeID() == SHIP_TYPE_FEDERAL_DISCOVERY ||
919
		       $this->getShipTypeID() == SHIP_TYPE_FEDERAL_WARRANT ||
920
		       $this->getShipTypeID() == SHIP_TYPE_FEDERAL_ULTIMATUM;
921
	}
922
923
	public function isUnderground() {
924
		return $this->getShipTypeID() == SHIP_TYPE_THIEF ||
925
		       $this->getShipTypeID() == SHIP_TYPE_ASSASSIN ||
926
		       $this->getShipTypeID() == SHIP_TYPE_DEATH_CRUISER;
927
	}
928
929
	public function &shootPlayer(AbstractSmrPlayer $targetPlayer) {
930
		return $this->shootPlayers(array($targetPlayer));
931
	}
932
933
	public function &shootPlayers(array $targetPlayers) {
934
		$thisPlayer = $this->getPlayer();
935
		$results = array('Player' => $thisPlayer, 'TotalDamage' => 0, 'Weapons' => []);
936
		if ($thisPlayer->isDead()) {
937
			$results['DeadBeforeShot'] = true;
938
			return $results;
939
		}
940
		$results['DeadBeforeShot'] = false;
941
		foreach ($this->weapons as $orderID => $weapon) {
942
			$results['Weapons'][$orderID] =& $weapon->shootPlayer($thisPlayer, $targetPlayers[array_rand($targetPlayers)]);
943
			if ($results['Weapons'][$orderID]['Hit']) {
944
				$results['TotalDamage'] += $results['Weapons'][$orderID]['ActualDamage']['TotalDamage'];
945
			}
946
		}
947
		if ($this->hasCDs()) {
948
			$thisCDs = new SmrCombatDrones($this->getGameID(), $this->getCDs());
949
			$results['Drones'] =& $thisCDs->shootPlayer($thisPlayer, $targetPlayers[array_rand($targetPlayers)]);
950
			$results['TotalDamage'] += $results['Drones']['ActualDamage']['TotalDamage'];
951
		}
952
		$thisPlayer->increaseExperience(IRound($results['TotalDamage'] * self::EXP_PER_DAMAGE_PLAYER));
953
		$thisPlayer->increaseHOF($results['TotalDamage'], array('Combat', 'Player', 'Damage Done'), HOF_PUBLIC);
954
		$thisPlayer->increaseHOF(1, array('Combat', 'Player', 'Shots'), HOF_PUBLIC);
955
		return $results;
956
	}
957
958
	public function &shootForces(SmrForce $forces) {
959
		$thisPlayer = $this->getPlayer();
960
		$results = array('Player' => $thisPlayer, 'TotalDamage' => 0, 'Weapons' => []);
961
		if ($thisPlayer->isDead()) {
962
			$results['DeadBeforeShot'] = true;
963
			return $results;
964
		}
965
		$results['DeadBeforeShot'] = false;
966
		foreach ($this->weapons as $orderID => $weapon) {
967
			$results['Weapons'][$orderID] =& $weapon->shootForces($thisPlayer, $forces);
968
			if ($results['Weapons'][$orderID]['Hit']) {
969
				$results['TotalDamage'] += $results['Weapons'][$orderID]['ActualDamage']['TotalDamage'];
970
				$thisPlayer->increaseHOF($results['Weapons'][$orderID]['ActualDamage']['NumMines'], array('Combat', 'Forces', 'Mines', 'Killed'), HOF_PUBLIC);
971
				$thisPlayer->increaseHOF($results['Weapons'][$orderID]['ActualDamage']['Mines'], array('Combat', 'Forces', 'Mines', 'Damage Done'), HOF_PUBLIC);
972
				$thisPlayer->increaseHOF($results['Weapons'][$orderID]['ActualDamage']['NumCDs'], array('Combat', 'Forces', 'Combat Drones', 'Killed'), HOF_PUBLIC);
973
				$thisPlayer->increaseHOF($results['Weapons'][$orderID]['ActualDamage']['CDs'], array('Combat', 'Forces', 'Combat Drones', 'Damage Done'), HOF_PUBLIC);
974
				$thisPlayer->increaseHOF($results['Weapons'][$orderID]['ActualDamage']['NumSDs'], array('Combat', 'Forces', 'Scout Drones', 'Killed'), HOF_PUBLIC);
975
				$thisPlayer->increaseHOF($results['Weapons'][$orderID]['ActualDamage']['SDs'], array('Combat', 'Forces', 'Scout Drones', 'Damage Done'), HOF_PUBLIC);
976
				$thisPlayer->increaseHOF($results['Weapons'][$orderID]['ActualDamage']['NumMines'] + $results['Weapons'][$orderID]['ActualDamage']['NumCDs'] + $results['Weapons'][$orderID]['ActualDamage']['NumSDs'], array('Combat', 'Forces', 'Killed'), HOF_PUBLIC);
977
			}
978
		}
979
		if ($this->hasCDs()) {
980
			$thisCDs = new SmrCombatDrones($this->getGameID(), $this->getCDs());
981
			$results['Drones'] =& $thisCDs->shootForces($thisPlayer, $forces);
982
			$results['TotalDamage'] += $results['Drones']['ActualDamage']['TotalDamage'];
983
			$thisPlayer->increaseHOF($results['Drones']['ActualDamage']['NumMines'], array('Combat', 'Forces', 'Mines', 'Killed'), HOF_PUBLIC);
984
			$thisPlayer->increaseHOF($results['Drones']['ActualDamage']['Mines'], array('Combat', 'Forces', 'Mines', 'Damage Done'), HOF_PUBLIC);
985
			$thisPlayer->increaseHOF($results['Drones']['ActualDamage']['NumCDs'], array('Combat', 'Forces', 'Combat Drones', 'Killed'), HOF_PUBLIC);
986
			$thisPlayer->increaseHOF($results['Drones']['ActualDamage']['CDs'], array('Combat', 'Forces', 'Combat Drones', 'Damage Done'), HOF_PUBLIC);
987
			$thisPlayer->increaseHOF($results['Drones']['ActualDamage']['NumSDs'], array('Combat', 'Forces', 'Scout Drones', 'Killed'), HOF_PUBLIC);
988
			$thisPlayer->increaseHOF($results['Drones']['ActualDamage']['SDs'], array('Combat', 'Forces', 'Scout Drones', 'Damage Done'), HOF_PUBLIC);
989
			$thisPlayer->increaseHOF($results['Drones']['ActualDamage']['NumMines'] + $results['Drones']['ActualDamage']['NumCDs'] + $results['Drones']['ActualDamage']['NumSDs'], array('Combat', 'Forces', 'Killed'), HOF_PUBLIC);
990
		}
991
		$thisPlayer->increaseExperience(IRound($results['TotalDamage'] * self::EXP_PER_DAMAGE_FORCE));
992
		$thisPlayer->increaseHOF($results['TotalDamage'], array('Combat', 'Forces', 'Damage Done'), HOF_PUBLIC);
993
		$thisPlayer->increaseHOF(1, array('Combat', 'Forces', 'Shots'), HOF_PUBLIC);
994
		return $results;
995
	}
996
997
	public function &shootPort(SmrPort $port) {
998
		$thisPlayer = $this->getPlayer();
999
		$results = array('Player' => $thisPlayer, 'TotalDamage' => 0, 'Weapons' => []);
1000
		if ($thisPlayer->isDead()) {
1001
			$results['DeadBeforeShot'] = true;
1002
			return $results;
1003
		}
1004
		$results['DeadBeforeShot'] = false;
1005
		foreach ($this->weapons as $orderID => $weapon) {
1006
			$results['Weapons'][$orderID] =& $weapon->shootPort($thisPlayer, $port);
1007
			if ($results['Weapons'][$orderID]['Hit']) {
1008
				$results['TotalDamage'] += $results['Weapons'][$orderID]['ActualDamage']['TotalDamage'];
1009
			}
1010
		}
1011
		if ($this->hasCDs()) {
1012
			$thisCDs = new SmrCombatDrones($this->getGameID(), $this->getCDs());
1013
			$results['Drones'] =& $thisCDs->shootPort($thisPlayer, $port);
1014
			$results['TotalDamage'] += $results['Drones']['ActualDamage']['TotalDamage'];
1015
		}
1016
		$thisPlayer->increaseExperience(IRound($results['TotalDamage'] * self::EXP_PER_DAMAGE_PORT));
1017
		$thisPlayer->increaseHOF($results['TotalDamage'], array('Combat', 'Port', 'Damage Done'), HOF_PUBLIC);
1018
//		$thisPlayer->increaseHOF(1,array('Combat','Port','Shots')); //in SmrPortt::attackedBy()
1019
1020
		// Change alignment if we reach a damage threshold.
1021
		// Increase if player and port races are at war; decrease otherwise.
1022
		if ($results['TotalDamage'] >= SmrPort::DAMAGE_NEEDED_FOR_ALIGNMENT_CHANGE) {
1023
			$relations = Globals::getRaceRelations($thisPlayer->getGameID(), $thisPlayer->getRaceID());
1024
			if ($relations[$port->getRaceID()] <= RELATIONS_WAR) {
1025
				$thisPlayer->increaseAlignment(1);
1026
				$thisPlayer->increaseHOF(1, array('Combat', 'Port', 'Alignment', 'Gain'), HOF_PUBLIC);
1027
			} else {
1028
				$thisPlayer->decreaseAlignment(1);
1029
				$thisPlayer->increaseHOF(1, array('Combat', 'Port', 'Alignment', 'Loss'), HOF_PUBLIC);
1030
			}
1031
		}
1032
		return $results;
1033
	}
1034
1035
	public function &shootPlanet(SmrPlanet $planet, $delayed) {
1036
		$thisPlayer = $this->getPlayer();
1037
		$results = array('Player' => $thisPlayer, 'TotalDamage' => 0, 'Weapons' => []);
1038
		if ($thisPlayer->isDead()) {
1039
			$results['DeadBeforeShot'] = true;
1040
			return $results;
1041
		}
1042
		$results['DeadBeforeShot'] = false;
1043
		foreach ($this->weapons as $orderID => $weapon) {
1044
			$results['Weapons'][$orderID] =& $weapon->shootPlanet($thisPlayer, $planet, $delayed);
1045
			if ($results['Weapons'][$orderID]['Hit']) {
1046
				$results['TotalDamage'] += $results['Weapons'][$orderID]['ActualDamage']['TotalDamage'];
1047
			}
1048
		}
1049
		if ($this->hasCDs()) {
1050
			$thisCDs = new SmrCombatDrones($this->getGameID(), $this->getCDs());
1051
			$results['Drones'] =& $thisCDs->shootPlanet($thisPlayer, $planet, $delayed);
1052
			$results['TotalDamage'] += $results['Drones']['ActualDamage']['TotalDamage'];
1053
		}
1054
		$thisPlayer->increaseExperience(IRound($results['TotalDamage'] * self::EXP_PER_DAMAGE_PLANET));
1055
		$thisPlayer->increaseHOF($results['TotalDamage'], array('Combat', 'Planet', 'Damage Done'), HOF_PUBLIC);
1056
//		$thisPlayer->increaseHOF(1,array('Combat','Planet','Shots')); //in SmrPlanet::attackedBy()
1057
		return $results;
1058
	}
1059
1060
	public function &doWeaponDamage(array $damage) {
1061
		$alreadyDead = $this->getPlayer()->isDead();
1062
		$armourDamage = 0;
1063
		$cdDamage = 0;
1064
		$shieldDamage = 0;
1065
		if (!$alreadyDead) {
1066
			$shieldDamage = $this->doShieldDamage(min($damage['MaxDamage'], $damage['Shield']));
1067
			$damage['MaxDamage'] -= $shieldDamage;
1068
			if (!$this->hasShields() && ($shieldDamage == 0 || $damage['Rollover'])) {
1069
				$cdDamage = $this->doCDDamage(min($damage['MaxDamage'], $damage['Armour']));
1070
				$damage['Armour'] -= $cdDamage;
1071
				$damage['MaxDamage'] -= $cdDamage;
1072
				if (!$this->hasCDs() && ($cdDamage == 0 || $damage['Rollover'])) {
1073
					$armourDamage = $this->doArmourDamage(min($damage['MaxDamage'], $damage['Armour']));
1074
				}
1075
			}
1076
		}
1077
		$return = array(
1078
						'KillingShot' => !$alreadyDead && $this->isDead(),
1079
						'TargetAlreadyDead' => $alreadyDead,
1080
						'Shield' => $shieldDamage,
1081
						'CDs' => $cdDamage,
1082
						'NumCDs' => $cdDamage / CD_ARMOUR,
1083
						'Armour' => $armourDamage,
1084
						'HasCDs' => $this->hasCDs(),
1085
						'TotalDamage' => $shieldDamage + $cdDamage + $armourDamage
1086
		);
1087
		return $return;
1088
	}
1089
1090
	public function &doMinesDamage(array $damage) {
1091
		$alreadyDead = $this->getPlayer()->isDead();
1092
		$armourDamage = 0;
1093
		$cdDamage = 0;
1094
		$shieldDamage = 0;
1095
		if (!$alreadyDead) {
1096
			$shieldDamage = $this->doShieldDamage(min($damage['MaxDamage'], $damage['Shield']));
1097
			$damage['MaxDamage'] -= $shieldDamage;
1098
			if (!$this->hasShields() && ($shieldDamage == 0 || $damage['Rollover'])) { //skip CDs if it's mines
1099
				$armourDamage = $this->doArmourDamage(min($damage['MaxDamage'], $damage['Armour']));
1100
			}
1101
		}
1102
		$return = array(
1103
						'KillingShot' => !$alreadyDead && $this->isDead(),
1104
						'TargetAlreadyDead' => $alreadyDead,
1105
						'Shield' => $shieldDamage,
1106
						'CDs' => $cdDamage,
1107
						'NumCDs' => $cdDamage / CD_ARMOUR,
1108
						'Armour' => $armourDamage,
1109
						'HasCDs' => $this->hasCDs(),
1110
						'TotalDamage' => $shieldDamage + $cdDamage + $armourDamage
1111
		);
1112
		return $return;
1113
	}
1114
1115
	protected function doShieldDamage($damage) {
1116
		$actualDamage = min($this->getShields(), $damage);
1117
		$this->decreaseShields($actualDamage);
1118
		return $actualDamage;
1119
	}
1120
1121
	protected function doCDDamage($damage) {
1122
		$actualDamage = min($this->getCDs(), IFloor($damage / CD_ARMOUR));
1123
		$this->decreaseCDs($actualDamage);
1124
		return $actualDamage * CD_ARMOUR;
1125
	}
1126
1127
	protected function doArmourDamage($damage) {
1128
		$actualDamage = min($this->getArmour(), $damage);
1129
		$this->decreaseArmour($actualDamage);
1130
		return $actualDamage;
1131
	}
1132
1133
	/**
1134
	 * Returns the maneuverability rating for this ship.
1135
	 */
1136
	public function getMR() : int {
1137
		//700 - [ (ship hit points / 25) + (ship stat factors) ]
1138
		//Minimum value of 0 because negative values cause issues with calculations calling this routine
1139
		return max(0, IRound(
1140
						700 -
1141
						(
1142
							(
1143
								$this->getShields()
1144
								+$this->getArmour()
1145
								+$this->getCDs() * 3
1146
							) / 25
1147
							+(
1148
								$this->getCargoHolds() / 100
1149
								-$this->getSpeed() * 5
1150
								+($this->getHardpoints()/*+$ship['Increases']['Ship Power']*/) * 5
1151
								/*+(
1152
									$ship['Increases']['Mines']
1153
									+$ship['Increases']['Scout Drones']
1154
								)/12*/
1155
								+$this->getCDs() / 5
1156
							)
1157
						)
1158
					)
1159
					);
1160
	}
1161
1162
}
1163