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 (#926)
by Dan
03:43
created

AbstractSmrShip::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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

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

187
					$this->/** @scrutinizer ignore-call */ 
188
            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...
188
				}
189
			}
190
		}
191
	}
192
193
	/**
194
	 * Set all hardware to its maximum value for this ship.
195
	 */
196
	public function setHardwareToMax() {
197
		foreach ($this->getMaxHardware() as $key => $max) {
198
			$this->setHardware($key, $max);
199
		}
200
		$this->removeUnderAttack();
201
	}
202
203
	public function getPowerUsed() {
204
		$power = 0;
205
		foreach ($this->weapons as $weapon) {
206
			$power += $weapon->getPowerLevel();
207
		}
208
		return $power;
209
	}
210
211
	public function getRemainingPower() {
212
		return $this->getMaxPower() - $this->getPowerUsed();
213
	}
214
215
	/**
216
	 * given power level of new weapon, return whether there is enough power available to install it on this ship
217
	 */
218
	public function checkPowerAvailable($powerLevel) {
219
		return $this->getRemainingPower() >= $powerLevel;
220
	}
221
222
	public function getMaxPower() {
223
		return $this->baseShip['MaxPower'];
224
	}
225
226
	public function hasIllegalGoods() {
227
		return $this->hasCargo(GOODS_SLAVES) || $this->hasCargo(GOODS_WEAPONS) || $this->hasCargo(GOODS_NARCOTICS);
228
	}
229
230
	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

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