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 (#870)
by Dan
04:09
created

AbstractSmrShip::getCostToUNOAgainstShip()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 6
dl 0
loc 8
rs 10
c 0
b 0
f 0
cc 2
nc 2
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
		if ($this->getNumWeapons() > 0) {
206
			foreach ($this->weapons as $weapon) {
207
				$power += $weapon->getPowerLevel();
208
			}
209
		}
210
		return $power;
211
	}
212
213
	public function getRemainingPower() {
214
		return $this->getMaxPower() - $this->getPowerUsed();
215
	}
216
217
	public function hasRemainingPower() {
218
		return $this->getRemainingPower() > 0;
219
	}
220
221
	public function getMaxPower() {
222
		return $this->baseShip['MaxPower'];
223
	}
224
225
	public function hasIllegalGoods() {
226
		return $this->hasCargo(GOODS_SLAVES) || $this->hasCargo(GOODS_WEAPONS) || $this->hasCargo(GOODS_NARCOTICS);
227
	}
228
229
	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

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