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

SmrPlanet::shootPlayer()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 2
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php declare(strict_types=1);
2
3
// This file defines more than just one class, which is not handled by
4
// the autoloader. So we must include it explicitly.
5
require_once('SmrPlanetType.class.php');
6
7
class SmrPlanet {
8
	protected static $CACHE_PLANETS = array();
9
10
	const DAMAGE_NEEDED_FOR_DOWNGRADE_CHANCE = 100;
11
	const CHANCE_TO_DOWNGRADE = 15; // percent
12
	const TIME_TO_CREDIT_BUST = 10800; // 3 hours
13
	const TIME_ATTACK_NEWS_COOLDOWN = 3600; // 1 hour
14
	const MAX_STOCKPILE = 600;
15
16
	protected $db;
17
	protected $SQL;
18
19
	protected $sectorID;
20
	protected $gameID;
21
	protected $planetName;
22
	protected $ownerID;
23
	protected $password;
24
	protected $shields;
25
	protected $armour;
26
	protected $drones;
27
	protected $credits;
28
	protected $bonds;
29
	protected $maturity;
30
	protected $stockpile;
31
	protected $buildings;
32
	protected $inhabitableTime;
33
	protected $currentlyBuilding;
34
	protected $mountedWeapons;
35
	protected $typeID;
36
	protected $typeInfo;
37
38
	protected $hasChanged = false;
39
	protected $hasChangedFinancial = false; // for credits, bond, maturity
40
	protected $hasChangedStockpile = false;
41
	protected $hasChangedWeapons = array();
42
	protected $hasChangedBuildings = array();
43
	protected $hasStoppedBuilding = array();
44
	protected $isNew = false;
45
46
	protected $delayedShieldsDelta = 0;
47
	protected $delayedCDsDelta = 0;
48
	protected $delayedArmourDelta = 0;
49
50
	public static function refreshCache() {
51
		foreach (self::$CACHE_PLANETS as $gameID => &$gamePlanets) {
52
			foreach ($gamePlanets as $sectorID => &$planet) {
53
				$planet = self::getPlanet($gameID, $sectorID, true);
54
			}
55
		}
56
	}
57
58
	public static function clearCache() {
59
		self::$CACHE_PLANETS = array();
60
	}
61
62
	public static function savePlanets() {
63
		foreach (self::$CACHE_PLANETS as $gamePlanets) {
64
			foreach ($gamePlanets as $planet) {
65
				$planet->update();
66
			}
67
		}
68
	}
69
70
	public static function getGalaxyPlanets($gameID, $galaxyID, $forceUpdate = false) {
71
		$db = MySqlDatabase::getInstance();
72
		$db->query('SELECT planet.*, sector_id FROM sector LEFT JOIN planet USING (game_id, sector_id) WHERE game_id = ' . $db->escapeNumber($gameID) . ' AND galaxy_id = ' . $db->escapeNumber($galaxyID));
73
		$galaxyPlanets = [];
74
		while ($db->nextRecord()) {
75
			$sectorID = $db->getInt('sector_id');
76
			$planet = self::getPlanet($gameID, $sectorID, $forceUpdate, $db);
77
			if ($planet->exists()) {
78
				$galaxyPlanets[$sectorID] = $planet;
79
			}
80
		}
81
		return $galaxyPlanets;
82
	}
83
84
	public static function getPlanet($gameID, $sectorID, $forceUpdate = false, $db = null) {
85
		if ($forceUpdate || !isset(self::$CACHE_PLANETS[$gameID][$sectorID])) {
86
			self::$CACHE_PLANETS[$gameID][$sectorID] = new SmrPlanet($gameID, $sectorID, $db);
87
		}
88
		return self::$CACHE_PLANETS[$gameID][$sectorID];
89
	}
90
91
	public static function createPlanet($gameID, $sectorID, $type = 1) {
92
		if (!self::getPlanet($gameID, $sectorID)->exists()) {
93
			$minTime = SmrGame::getGame($gameID)->getStartTime();
94
			$inhabitableTime = $minTime + pow(rand(45, 85), 3);
95
96
			// insert planet into db
97
			$db = MySqlDatabase::getInstance();
98
			$db->query('INSERT INTO planet (game_id, sector_id, inhabitable_time, planet_type_id)
99
				VALUES (' . $db->escapeNumber($gameID) . ', ' . $db->escapeNumber($sectorID) . ', ' . $db->escapeNumber($inhabitableTime) . ', ' . $db->escapeNumber($type) . ')');
100
		}
101
		return self::getPlanet($gameID, $sectorID, true);
102
	}
103
104
	public static function removePlanet($gameID, $sectorID) {
105
		$db = MySqlDatabase::getInstance();
106
		$SQL = 'game_id = ' . $db->escapeNumber($gameID) . ' AND sector_id = ' . $db->escapeNumber($sectorID);
107
		$db->query('DELETE FROM planet WHERE ' . $SQL);
108
		$db->query('DELETE FROM planet_has_weapon WHERE ' . $SQL);
109
		$db->query('DELETE FROM planet_has_cargo WHERE ' . $SQL);
110
		$db->query('DELETE FROM planet_has_building WHERE ' . $SQL);
111
		$db->query('DELETE FROM planet_is_building WHERE ' . $SQL);
112
		//kick everyone from planet
113
		$db->query('UPDATE player SET land_on_planet = \'FALSE\' WHERE ' . $SQL);
114
115
		self::$CACHE_PLANETS[$gameID][$sectorID] = null;
116
		unset(self::$CACHE_PLANETS[$gameID][$sectorID]);
117
	}
118
119
	protected function __construct($gameID, $sectorID, $db = null) {
120
		$this->db = MySqlDatabase::getInstance();
121
		$this->SQL = 'game_id = ' . $this->db->escapeNumber($gameID) . ' AND sector_id = ' . $this->db->escapeNumber($sectorID);
122
123
		if (isset($db)) {
124
			$planetExists = $db->hasField('planet_type_id');
125
		} else {
126
			$db = $this->db;
127
			$db->query('SELECT * FROM planet WHERE ' . $this->SQL);
128
			$planetExists = $db->nextRecord();
129
		}
130
131
		if ($planetExists) {
132
			$this->gameID = (int)$gameID;
133
			$this->sectorID = (int)$sectorID;
134
			$this->planetName = stripslashes($db->getField('planet_name'));
135
			$this->ownerID = $db->getInt('owner_id');
136
			$this->password = $db->getField('password');
137
			$this->shields = $db->getInt('shields');
138
			$this->armour = $db->getInt('armour');
139
			$this->drones = $db->getInt('drones');
140
			$this->credits = $db->getInt('credits');
141
			$this->bonds = $db->getInt('bonds');
142
			$this->maturity = $db->getInt('maturity');
143
			$this->inhabitableTime = $db->getInt('inhabitable_time');
144
			$this->typeID = $db->getInt('planet_type_id');
145
146
			$this->typeInfo = SmrPlanetType::getTypeInfo($this->getTypeID());
147
			$this->checkBondMaturity();
148
		}
149
	}
150
151
	public function getInterestRate() {
152
		$level = $this->getLevel();
153
		if ($level < 9) {
154
			return .0404;
155
		} elseif ($level < 19) {
156
			return .0609;
157
		} elseif ($level < 29) {
158
			return .1236;
159
		} elseif ($level < 39) {
160
			return .050625;
161
		} elseif ($level < 49) {
162
			return .0404;
163
		} elseif ($level < 59) {
164
			return .030225;
165
		} elseif ($level < 69) {
166
			return .0201;
167
		} else {
168
			return .018081;
169
		}
170
	}
171
172
	public function checkBondMaturity($partial = false) {
173
		if ($this->getMaturity() > 0 && ($partial === true || $this->getMaturity() < SmrSession::getTime())) {
174
			// calc the interest for the time
175
			$interest = $this->getBonds() * $this->getInterestRate();
176
177
			if ($partial === true && $this->getMaturity() > SmrSession::getTime()) {
178
				// Adjust interest based upon how much of the bond duration has passed.
179
				$interest -= ($interest / $this->getBondTime()) * ($this->getMaturity() - SmrSession::getTime());
180
			}
181
182
			// transfer money to free avail cash
183
			$this->increaseCredits($this->getBonds() + IFloor($interest));
184
185
			// reset bonds
186
			$this->setBonds(0);
187
188
			// reset maturity
189
			$this->setMaturity(0);
190
		}
191
	}
192
193
	public function getBondTime() : int {
194
		return IRound(BOND_TIME / $this->getGame()->getGameSpeed());
195
	}
196
197
	public function bond() {
198
		$this->checkBondMaturity(true);
199
200
		// add it to bond
201
		$this->increaseBonds($this->getCredits());
202
203
		// set free cash to 0
204
		$this->setCredits(0);
205
206
		// initialize time
207
		$this->setMaturity(SmrSession::getTime() + $this->getBondTime());
208
	}
209
210
	public function getGameID() {
211
		return $this->gameID;
212
	}
213
214
	public function getGame() {
215
		return SmrGame::getGame($this->gameID);
216
	}
217
218
	public function getSectorID() {
219
		return $this->sectorID;
220
	}
221
222
	public function getGalaxy() {
223
		return SmrGalaxy::getGalaxyContaining($this->getGameID(), $this->getSectorID());
224
	}
225
226
	public function getOwnerID() {
227
		return $this->ownerID;
228
	}
229
230
	public function hasOwner() {
231
		return $this->ownerID != 0;
232
	}
233
234
	public function removeOwner() {
235
		$this->setOwnerID(0);
236
	}
237
238
	public function setOwnerID($claimerID) {
239
		if ($this->ownerID == $claimerID) {
240
			return;
241
		}
242
		$this->ownerID = $claimerID;
243
		$this->hasChanged = true;
244
	}
245
246
	public function getOwner() {
247
		return SmrPlayer::getPlayer($this->getOwnerID(), $this->getGameID());
248
	}
249
250
	public function getPassword() {
251
		return $this->password;
252
	}
253
254
	public function setPassword($password) {
255
		if ($this->password == $password) {
256
			return;
257
		}
258
		$this->password = $password;
259
		$this->hasChanged = true;
260
	}
261
262
	public function removePassword() {
263
		$this->setPassword('');
264
	}
265
266
	public function getCredits() {
267
		return $this->credits;
268
	}
269
270
	public function setCredits(int $num) : void {
271
		if ($this->credits == $num) {
272
			return;
273
		}
274
		if ($num < 0) {
275
			throw new Exception('You cannot set negative credits.');
276
		}
277
		if ($num > MAX_MONEY) {
278
			throw new Exception('You cannot set more than the max credits.');
279
		}
280
		$this->credits = $num;
281
		$this->hasChangedFinancial = true;
282
	}
283
284
	/**
285
	 * Increases planet credits up to the maximum allowed credits.
286
	 * Returns the amount that was actually added to handle overflow.
287
	 */
288
	public function increaseCredits(int $num) : int {
289
		if ($num == 0) {
290
			return 0;
291
		}
292
		$newTotal = min($this->credits + $num, MAX_MONEY);
293
		$actualAdded = $newTotal - $this->credits;
294
		$this->setCredits($newTotal);
295
		return $actualAdded;
296
	}
297
298
	public function decreaseCredits(int $num) : void {
299
		if ($num == 0) {
300
			return;
301
		}
302
		$newTotal = $this->credits - $num;
303
		$this->setCredits($newTotal);
304
	}
305
306
	public function getMaturity() {
307
		return $this->maturity;
308
	}
309
310
	public function setMaturity($num) {
311
		if ($this->maturity == $num) {
312
			return;
313
		}
314
		if ($num < 0) {
315
			throw new Exception('You cannot set negative maturity.');
316
		}
317
		$this->maturity = $num;
318
		$this->hasChangedFinancial = true;
319
	}
320
321
	public function getBonds() {
322
		return $this->bonds;
323
	}
324
325
	public function setBonds($num) {
326
		if ($this->bonds == $num) {
327
			return;
328
		}
329
		if ($num < 0) {
330
			throw new Exception('You cannot set negative bonds.');
331
		}
332
		$this->bonds = $num;
333
		$this->hasChangedFinancial = true;
334
	}
335
336
	public function increaseBonds($num) {
337
		if ($num == 0) {
338
			return;
339
		}
340
		$this->setBonds($this->getBonds() + $num);
341
	}
342
343
	public function decreaseBonds($num) {
344
		if ($num == 0) {
345
			return;
346
		}
347
		$this->setBonds($this->getBonds() - $num);
348
	}
349
350
	public function checkForExcessDefense() {
351
		if ($this->getShields() > $this->getMaxShields()) {
352
			$this->setShields($this->getMaxShields());
353
		}
354
		if ($this->getCDs() > $this->getMaxCDs()) {
355
			$this->setCDs($this->getMaxCDs());
356
		}
357
		if ($this->getArmour() > $this->getMaxArmour()) {
358
				$this->setArmour($this->getMaxArmour());
359
		}
360
		// Remove a random (0-indexed) mounted weapon, if over max mount slots
361
		while ($this->getMountedWeapons() && max(array_keys($this->getMountedWeapons())) >= $this->getMaxMountedWeapons()) {
362
			$removeID = array_rand($this->getMountedWeapons());
363
			$this->removeMountedWeapon($removeID);
364
			foreach ($this->getMountedWeapons() as $orderID => $weapon) {
365
				if ($orderID > $removeID) {
366
					$this->moveMountedWeaponUp($orderID);
367
				}
368
			}
369
		}
370
	}
371
372
	public function getShields($delayed = false) {
373
		return $this->shields + ($delayed ? $this->delayedShieldsDelta : 0);
374
	}
375
376
	public function hasShields($delayed = false) {
377
		return $this->getShields($delayed) > 0;
378
	}
379
380
	public function setShields($shields) {
381
		$shields = max(0, min($shields, $this->getMaxShields()));
382
		if ($this->shields == $shields) {
383
			return;
384
		}
385
		$this->shields = $shields;
386
		$this->hasChanged = true;
387
	}
388
389
	public function decreaseShields($number, $delayed = false) {
390
		if ($number == 0) {
391
			return;
392
		}
393
		if ($delayed === false) {
394
			$this->setShields($this->getShields() - $number);
395
		} else {
396
			$this->delayedShieldsDelta -= $number;
397
		}
398
	}
399
400
	public function increaseShields($number, $delayed = false) {
401
		if ($number == 0) {
402
			return;
403
		}
404
		if ($delayed === false) {
405
			$this->setShields($this->getShields() + $number);
406
		} else {
407
			$this->delayedShieldsDelta += $number;
408
		}
409
	}
410
411
	public function getMaxShields() {
412
		return $this->getBuilding(PLANET_GENERATOR) * PLANET_GENERATOR_SHIELDS;
413
	}
414
415
	public function getArmour($delayed = false) {
416
		return $this->armour + ($delayed ? $this->delayedArmourDelta : 0);
417
	}
418
419
	public function hasArmour($delayed = false) {
420
		return $this->getArmour($delayed) > 0;
421
	}
422
423
	public function setArmour($armour) {
424
		$armour = max(0, min($armour, $this->getMaxArmour()));
425
		if ($this->armour == $armour) {
426
			return;
427
		}
428
		$this->armour = $armour;
429
		$this->hasChanged = true;
430
	}
431
432
	public function decreaseArmour($number, $delayed = false) {
433
		if ($number == 0) {
434
			return;
435
		}
436
		if ($delayed === false) {
437
			$this->setArmour($this->getArmour() - $number);
438
		} else {
439
			$this->delayedArmourDelta -= $number;
440
		}
441
	}
442
443
	public function increaseArmour($number, $delayed = false) {
444
		if ($number == 0) {
445
			return;
446
		}
447
		if ($delayed === false) {
448
			$this->setArmour($this->getArmour() + $number);
449
		} else {
450
			$this->delayedArmourDelta += $number;
451
		}
452
	}
453
454
	public function getMaxArmour() {
455
		return $this->getBuilding(PLANET_BUNKER) * PLANET_BUNKER_ARMOUR;
456
	}
457
458
	public function getCDs($delayed = false) {
459
		return $this->drones + ($delayed ? $this->delayedCDsDelta : 0);
460
	}
461
462
	public function hasCDs($delayed = false) {
463
		return $this->getCDs($delayed) > 0;
464
	}
465
466
	public function setCDs($combatDrones) {
467
		$combatDrones = max(0, min($combatDrones, $this->getMaxCDs()));
468
		if ($this->drones == $combatDrones) {
469
			return;
470
		}
471
		$this->drones = $combatDrones;
472
		$this->hasChanged = true;
473
	}
474
475
	public function decreaseCDs($number, $delayed = false) {
476
		if ($number == 0) {
477
			return;
478
		}
479
		if ($delayed === false) {
480
			$this->setCDs($this->getCDs() - $number);
481
		} else {
482
			$this->delayedCDsDelta -= $number;
483
		}
484
	}
485
486
	public function increaseCDs($number, $delayed = false) {
487
		if ($number == 0) {
488
			return;
489
		}
490
		if ($delayed === false) {
491
			$this->setCDs($this->getCDs() + $number);
492
		} else {
493
			$this->delayedCDsDelta += $number;
494
		}
495
	}
496
497
	public function getMaxCDs() {
498
		return $this->getBuilding(PLANET_HANGAR) * PLANET_HANGAR_DRONES;
499
	}
500
501
	public function getMaxMountedWeapons() {
502
		return $this->getBuilding(PLANET_WEAPON_MOUNT);
503
	}
504
505
	public function getMountedWeapons() {
506
		if (!isset($this->mountedWeapons)) {
507
			$this->mountedWeapons = [];
508
			if ($this->hasBuilding(PLANET_WEAPON_MOUNT)) {
509
				$this->db->query('SELECT * FROM planet_has_weapon JOIN weapon_type USING (weapon_type_id) WHERE ' . $this->SQL);
510
				while ($this->db->nextRecord()) {
511
					$weaponTypeID = $this->db->getInt('weapon_type_id');
512
					$orderID = $this->db->getInt('order_id');
513
					$weapon = SmrWeapon::getWeapon($weaponTypeID, $this->db);
514
					$weapon->setBonusAccuracy($this->db->getBoolean('bonus_accuracy'));
515
					$weapon->setBonusDamage($this->db->getBoolean('bonus_damage'));
516
					$this->mountedWeapons[$orderID] = $weapon;
517
				}
518
			}
519
		}
520
		return $this->mountedWeapons;
521
	}
522
523
	public function hasMountedWeapon($orderID) {
524
		$this->getMountedWeapons(); // Make sure array is initialized
525
		return isset($this->mountedWeapons[$orderID]);
526
	}
527
528
	public function addMountedWeapon(SmrWeapon $weapon, $orderID) {
529
		$this->getMountedWeapons(); // Make sure array is initialized
530
		$this->mountedWeapons[$orderID] = $weapon;
531
		$this->hasChangedWeapons[$orderID] = true;
532
	}
533
534
	public function removeMountedWeapon($orderID) {
535
		$this->getMountedWeapons(); // Make sure array is initialized
536
		unset($this->mountedWeapons[$orderID]);
537
		$this->hasChangedWeapons[$orderID] = true;
538
	}
539
540
	private function swapMountedWeapons($orderID1, $orderID2) {
541
		$this->getMountedWeapons(); // Make sure array is initialized
542
		if (isset($this->mountedWeapons[$orderID1])) {
543
			$saveWeapon = $this->mountedWeapons[$orderID1];
544
		}
545
		if (isset($this->mountedWeapons[$orderID2])) {
546
			$this->mountedWeapons[$orderID1] = $this->mountedWeapons[$orderID2];
547
		} else {
548
			unset($this->mountedWeapons[$orderID1]);
549
		}
550
		if (isset($saveWeapon)) {
551
			$this->mountedWeapons[$orderID2] = $saveWeapon;
552
		} else {
553
			unset($this->mountedWeapons[$orderID2]);
554
		}
555
		$this->hasChangedWeapons[$orderID1] = true;
556
		$this->hasChangedWeapons[$orderID2] = true;
557
	}
558
559
	public function moveMountedWeaponUp($orderID) {
560
		if ($orderID == 0) {
561
			throw new Exception('Cannot move this weapon up!');
562
		}
563
		$this->swapMountedWeapons($orderID - 1, $orderID);
564
	}
565
566
	public function moveMountedWeaponDown($orderID) {
567
		if ($orderID == $this->getMaxMountedWeapons() - 1) {
568
			throw new Exception('Cannot move this weapon down!');
569
		}
570
		$this->swapMountedWeapons($orderID + 1, $orderID);
571
	}
572
573
574
	public function isDestroyed($delayed = false) {
575
		return !$this->hasCDs($delayed) && !$this->hasShields($delayed) && !$this->hasArmour($delayed);
576
	}
577
578
	public function exists() {
579
		return $this->getGameID() != null && $this->getSectorID() !== null;
580
	}
581
582
	public function getStockpile($goodID = false) {
583
		if (!isset($this->stockpile)) {
584
			// initialize cargo array
585
			$this->stockpile = array();
586
			// get supplies from db
587
			$this->db->query('SELECT good_id, amount FROM planet_has_cargo WHERE ' . $this->SQL);
588
			// adding cargo and amount to array
589
			while ($this->db->nextRecord()) {
590
				$this->stockpile[$this->db->getInt('good_id')] = $this->db->getInt('amount');
591
			}
592
		}
593
		if ($goodID === false) {
594
			return $this->stockpile;
595
		}
596
		if (isset($this->stockpile[$goodID])) {
597
			return $this->stockpile[$goodID];
598
		}
599
		return 0;
600
	}
601
602
	public function hasStockpile($goodID = false) {
603
		if ($goodID === false) {
604
			$stockpile = $this->getStockpile($goodID);
605
			return count($stockpile) > 0 && max($stockpile) > 0;
0 ignored issues
show
Bug introduced by
It seems like $stockpile can also be of type integer; however, parameter $value of count() does only seem to accept Countable|array, maybe add an additional type check? ( Ignorable by Annotation )

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

605
			return count(/** @scrutinizer ignore-type */ $stockpile) > 0 && max($stockpile) > 0;
Loading history...
606
		} else {
607
			return $this->getStockpile($goodID) > 0;
608
		}
609
	}
610
611
	public function setStockpile($goodID, $amount) {
612
		if ($amount < 0) {
613
			throw new Exception('Trying to set negative stockpile.');
614
		}
615
		if ($this->getStockpile($goodID) == $amount) {
616
			return;
617
		}
618
		$this->stockpile[$goodID] = $amount;
619
		$this->hasChangedStockpile = true;
620
	}
621
622
	public function decreaseStockpile($goodID, $amount) {
623
		if ($amount < 0) {
624
			throw new Exception('Trying to decrease negative stockpile.');
625
		}
626
		$this->setStockpile($goodID, $this->getStockpile($goodID) - $amount);
627
	}
628
629
	public function increaseStockpile($goodID, $amount) {
630
		if ($amount < 0) {
631
			throw new Exception('Trying to increase negative stockpile.');
632
		}
633
		$this->setStockpile($goodID, $this->getStockpile($goodID) + $amount);
634
	}
635
636
	public function getBuildings() {
637
		if (!isset($this->buildings)) {
638
			$this->buildings = array();
639
640
			// get buildingss from db
641
			$this->db->query('SELECT construction_id, amount FROM planet_has_building WHERE ' . $this->SQL);
642
			// adding building and amount to array
643
			while ($this->db->nextRecord()) {
644
				$this->buildings[$this->db->getInt('construction_id')] = $this->db->getInt('amount');
645
			}
646
647
			// Update building counts if construction has finished
648
			$this->getCurrentlyBuilding();
649
		}
650
		return $this->buildings;
651
	}
652
653
	public function getBuilding($buildingTypeID) {
654
		$buildings = $this->getBuildings();
655
		if (isset($buildings[$buildingTypeID])) {
656
			return $buildings[$buildingTypeID];
657
		}
658
		return 0;
659
	}
660
661
	public function hasBuilding($buildingTypeID) {
662
		return $this->getBuilding($buildingTypeID) > 0;
663
	}
664
665
	public function setBuilding($buildingTypeID, $number) {
666
		if ($this->getBuilding($buildingTypeID) == $number) {
667
			return;
668
		}
669
670
		$this->buildings[$buildingTypeID] = $number;
671
		$this->hasChangedBuildings[$buildingTypeID] = true;
672
	}
673
674
	public function increaseBuilding($buildingTypeID, $number) {
675
		$this->setBuilding($buildingTypeID, $this->getBuilding($buildingTypeID) + $number);
676
	}
677
678
	public function destroyBuilding($buildingTypeID, $number) {
679
		if (!$this->hasBuilding($buildingTypeID)) {
680
			throw new Exception('Trying to destroy a nonexistent building');
681
		}
682
		$this->setBuilding($buildingTypeID, $this->getBuilding($buildingTypeID) - $number);
683
	}
684
685
	public function getCurrentlyBuilding() {
686
		if (!isset($this->currentlyBuilding)) {
687
			$this->currentlyBuilding = array();
688
689
			$this->db->query('SELECT * FROM planet_is_building WHERE ' . $this->SQL);
690
			while ($this->db->nextRecord()) {
691
				$this->currentlyBuilding[$this->db->getInt('building_slot_id')] = array(
692
					'BuildingSlotID' => $this->db->getInt('building_slot_id'),
693
					'ConstructionID' => $this->db->getInt('construction_id'),
694
					'ConstructorID' => $this->db->getInt('constructor_id'),
695
					'Finishes' => $this->db->getInt('time_complete'),
696
					'TimeRemaining' => $this->db->getInt('time_complete') - SmrSession::getTime(),
697
				);
698
			}
699
700
			// Check if construction has completed
701
			foreach ($this->currentlyBuilding as $id => $building) {
702
				if ($building['TimeRemaining'] <= 0) {
703
					unset($this->currentlyBuilding[$id]);
704
					$expGain = $this->getConstructionExp($building['ConstructionID']);
705
					$player = SmrPlayer::getPlayer($building['ConstructorID'], $this->getGameID());
706
					$player->increaseHOF(1, array('Planet', 'Buildings', 'Built'), HOF_ALLIANCE);
707
					$player->increaseExperience($expGain);
708
					$player->increaseHOF($expGain, array('Planet', 'Buildings', 'Experience'), HOF_ALLIANCE);
709
					$this->hasStoppedBuilding[] = $building['BuildingSlotID'];
710
					$this->increaseBuilding($building['ConstructionID'], 1);
711
712
					// WARNING: The above modifications to the player/planet are dangerous because
713
					// they may not be part of the current sector lock. But since they might not be,
714
					// we may as well just update now to avoid either a) needing to remember to call
715
					// this explicitly in all appropriate engine files or b) inconsistent exp display
716
					// if this is called during the template display only and therefore unsaved.
717
					$player->update();
718
					$this->update();
719
				}
720
			}
721
		}
722
		return $this->currentlyBuilding;
723
	}
724
725
	public function getMaxBuildings($buildingTypeID = false) {
726
		if ($buildingTypeID === false) {
727
			$structs = $this->typeInfo::STRUCTURES;
728
			return array_combine(array_keys($structs),
729
			                     array_column($structs, 'max_amount'));
730
		}
731
		return $this->getStructureTypes($buildingTypeID)->maxAmount();
732
	}
733
734
	public function getTypeID() {
735
		return $this->typeID;
736
	}
737
738
	public function setTypeID($num) {
739
		if (isset($this->typeID) && $this->typeID == $num) {
740
			return;
741
		}
742
		$this->typeID = $num;
743
		$this->db->query('UPDATE planet SET planet_type_id = ' . $this->db->escapeNumber($num) . ' WHERE ' . $this->SQL);
744
		$this->typeInfo = SmrPlanetType::getTypeInfo($this->getTypeID());
745
746
		//trim buildings first
747
		foreach ($this->getBuildings() as $id => $amt) {
748
			if ($this->getMaxBuildings($id) < $amt) {
749
				$this->destroyBuilding($id, $amt - $this->getMaxBuildings($id));
750
			}
751
		}
752
753
		//trim excess defenses
754
		$this->checkForExcessDefense();
755
756
		$this->hasChanged = true;
757
		$this->update();
758
	}
759
760
	public function getTypeImage() {
761
		return $this->typeInfo->imageLink();
762
	}
763
764
	public function getTypeName() {
765
		return $this->typeInfo->name();
766
	}
767
768
	public function getTypeDescription() {
769
		return $this->typeInfo->description();
770
	}
771
772
	public function getMaxAttackers() {
773
		return $this->typeInfo->maxAttackers();
774
	}
775
776
	public function getMaxLanded() {
777
		return $this->typeInfo->maxLanded();
778
	}
779
780
	public function getStructureTypes($structureID = false) {
781
		return $this->typeInfo->structureTypes($structureID);
782
	}
783
784
	public function hasStructureType($structureID) {
785
		return isset($this->getStructureTypes()[$structureID]);
786
	}
787
788
	/**
789
	 * Specifies which menu options the planet has.
790
	 */
791
	public function hasMenuOption($option) {
792
		// We do not set options that are unavailable
793
		return in_array($option, $this->typeInfo->menuOptions());
794
	}
795
796
	public function doDelayedUpdates() {
797
		$this->setShields($this->getShields(true));
798
		$this->delayedShieldsDelta = 0;
799
		$this->setCDs($this->getCDs(true));
800
		$this->delayedCDsDelta = 0;
801
		$this->setArmour($this->getArmour(true));
802
		$this->delayedArmourDelta = 0;
803
	}
804
805
	public function update() {
806
		if (!$this->exists()) {
807
			return;
808
		}
809
		$this->doDelayedUpdates();
810
		if ($this->hasChanged) {
811
			$this->db->query('UPDATE planet SET
812
									owner_id = ' . $this->db->escapeNumber($this->ownerID) . ',
813
									password = '.$this->db->escapeString($this->password) . ',
814
									planet_name = ' . $this->db->escapeString($this->planetName) . ',
815
									shields = ' . $this->db->escapeNumber($this->shields) . ',
816
									armour = ' . $this->db->escapeNumber($this->armour) . ',
817
									drones = ' . $this->db->escapeNumber($this->drones) . '
818
								WHERE ' . $this->SQL);
819
			$this->hasChanged = false;
820
		}
821
822
		// Separate update for financial since these can be modified by looking
823
		// at the planet list (i.e. you might not have sector lock and could
824
		// cause a race condition with events happening in the planet sector).
825
		if ($this->hasChangedFinancial) {
826
			$this->db->query('UPDATE planet SET
827
									credits = ' . $this->db->escapeNumber($this->credits) . ',
828
									bonds = ' . $this->db->escapeNumber($this->bonds) . ',
829
									maturity = ' . $this->db->escapeNumber($this->maturity) . '
830
								WHERE ' . $this->SQL);
831
			$this->hasChangedFinancial = false;
832
		}
833
834
		if ($this->hasChangedStockpile) {
835
			// write stockpile info
836
			foreach ($this->getStockpile() as $id => $amount) {
837
				if ($amount != 0) {
838
					$this->db->query('REPLACE INTO planet_has_cargo (game_id, sector_id, good_id, amount) ' .
839
										 'VALUES(' . $this->db->escapeNumber($this->getGameID()) . ', ' . $this->db->escapeNumber($this->getSectorID()) . ', ' . $this->db->escapeNumber($id) . ', ' . $this->db->escapeNumber($amount) . ')');
840
				} else {
841
					$this->db->query('DELETE FROM planet_has_cargo WHERE ' . $this->SQL . '
842
										AND good_id = ' . $this->db->escapeNumber($id));
843
				}
844
			}
845
		}
846
847
		if (count($this->hasChangedWeapons) > 0) {
848
			foreach (array_keys($this->hasChangedWeapons) as $orderID) {
849
				if (isset($this->mountedWeapons[$orderID])) {
850
					$this->db->query('REPLACE INTO planet_has_weapon (game_id, sector_id, order_id, weapon_type_id, bonus_accuracy, bonus_damage) VALUES (' . $this->db->escapeNumber($this->getGameID()) . ',' . $this->db->escapeNumber($this->getSectorID()) . ',' . $this->db->escapeNumber($orderID) . ',' . $this->db->escapeNumber($this->mountedWeapons[$orderID]->getWeaponTypeID()) . ',' . $this->db->escapeBoolean($this->mountedWeapons[$orderID]->hasBonusAccuracy()) . ',' . $this->db->escapeBoolean($this->mountedWeapons[$orderID]->hasBonusDamage()) . ')');
851
				} else {
852
					$this->db->query('DELETE FROM planet_has_weapon WHERE ' . $this->SQL . ' AND order_id=' . $this->db->escapeNumber($orderID));
853
				}
854
			}
855
			$this->hasChangedWeapons = [];
856
		}
857
858
		if (count($this->hasStoppedBuilding) > 0) {
859
			$this->db->query('DELETE FROM planet_is_building WHERE ' . $this->SQL . '
860
								AND building_slot_id IN (' . $this->db->escapeArray($this->hasStoppedBuilding) . ') LIMIT ' . count($this->hasStoppedBuilding));
861
			$this->hasStoppedBuilding = array();
862
		}
863
		// write building info
864
		foreach ($this->hasChangedBuildings as $id => $hasChanged) {
865
			if ($hasChanged === true) {
866
				if ($this->hasBuilding($id)) {
867
					$this->db->query('REPLACE INTO planet_has_building (game_id, sector_id, construction_id, amount) ' .
868
										'VALUES(' . $this->db->escapeNumber($this->gameID) . ', ' . $this->db->escapeNumber($this->sectorID) . ', ' . $this->db->escapeNumber($id) . ', ' . $this->db->escapeNumber($this->getBuilding($id)) . ')');
869
				} else {
870
					$this->db->query('DELETE FROM planet_has_building WHERE ' . $this->SQL . '
871
										AND construction_id = ' . $this->db->escapeNumber($id));
872
				}
873
				$this->hasChangedBuildings[$id] = false;
874
			}
875
		}
876
	}
877
878
	public function getLevel() {
879
		return array_sum($this->getBuildings()) / 3;
880
	}
881
882
	public function getMaxLevel() {
883
		return array_sum($this->getMaxBuildings()) / 3;
884
	}
885
886
	public function accuracy() {
887
		if ($this->hasWeapons()) {
888
			$weapons = $this->getWeapons();
889
			return $weapons[0]->getModifiedPlanetAccuracy($this);
890
		}
891
		return 0;
892
	}
893
894
	/**
895
	 * Returns the accuracy bonus for mounted weaons (as a percent)
896
	 */
897
	public function getAccuracyBonus() {
898
		return 5 * $this->getBuilding(PLANET_RADAR);
899
	}
900
901
	public function getRemainingStockpile($id) {
902
		return self::MAX_STOCKPILE - $this->getStockpile($id);
903
	}
904
905
	/**
906
	 * Returns true if there is a building in progress
907
	 */
908
	public function hasCurrentlyBuilding() {
909
		return count($this->getCurrentlyBuilding()) > 0;
910
	}
911
912
	public function canBuild(AbstractSmrPlayer $constructor, $constructionID) {
913
		if ($this->hasCurrentlyBuilding()) {
914
			return 'There is already a building in progress!';
915
		}
916
		if ($this->getBuilding($constructionID) >= $this->getMaxBuildings($constructionID)) {
917
			return 'This planet has reached the maximum buildings of that type.';
918
		}
919
		$cost = $this->getStructureTypes($constructionID)->creditCost();
920
		if ($constructor->getCredits() < $cost) {
921
			return 'You do not have enough credits.';
922
		}
923
		foreach ($this->getStructureTypes($constructionID)->hardwareCost() as $hardwareID) {
924
			if (!$constructor->getShip()->getHardware($hardwareID)) {
925
				return 'You do not have the hardware needed for this type of building!';
926
			}
927
		}
928
		// take the goods that are needed
929
		foreach ($this->getStructureTypes($constructionID)->goods() as $goodID => $amount) {
930
			if ($this->getStockpile($goodID) < $amount) {
931
				return 'There are not enough goods available.';
932
			}
933
		}
934
		return true;
935
	}
936
937
	// Modifier for planet building based on the number of buildings.
938
	// The average value of this modifier should roughly be 1.
939
	private function getCompletionModifier($constructionID) {
940
		$currentBuildings = $this->getBuilding($constructionID);
941
		$maxBuildings = $this->getMaxBuildings($constructionID);
942
		return 0.01 + 2.97 * pow($currentBuildings / $maxBuildings, 2);
943
	}
944
945
	// Amount of exp gained to build the next building of this type
946
	private function getConstructionExp($constructionID) {
947
		$expGain = $this->getStructureTypes($constructionID)->expGain();
948
		return $expGain;
949
	}
950
951
	// Amount of time (in seconds) to build the next building of this type
952
	public function getConstructionTime($constructionID) : int {
953
		$baseTime = $this->getStructureTypes($constructionID)->baseTime();
954
		$constructionTime = ICeil($baseTime * $this->getCompletionModifier($constructionID) / $this->getGame()->getGameSpeed());
955
		return $constructionTime;
956
	}
957
958
	public function startBuilding(AbstractSmrPlayer $constructor, $constructionID) {
959
		if (($message = $this->canBuild($constructor, $constructionID)) !== true) {
960
			throw new Exception('Unable to start building: ' . $message);
961
		}
962
		$constructor->decreaseCredits($this->getStructureTypes($constructionID)->creditCost());
963
		// take the goods that are needed
964
		foreach ($this->getStructureTypes($constructionID)->goods() as $goodID => $amount) {
965
			$this->decreaseStockpile($goodID, $amount);
966
		}
967
		foreach ($this->getStructureTypes($constructionID)->hardwareCost() as $hardwareID) {
968
			$constructor->getShip()->setHardware($hardwareID, 0);
969
		}
970
971
		// gets the time for the buildings
972
		$timeComplete = SmrSession::getTime() + $this->getConstructionTime($constructionID);
973
		$this->db->query('INSERT INTO planet_is_building (game_id, sector_id, construction_id, constructor_id, time_complete) ' .
974
						'VALUES (' . $this->db->escapeNumber($this->getGameID()) . ', ' . $this->db->escapeNumber($this->getSectorID()) . ', ' . $this->db->escapeNumber($constructionID) . ', ' . $this->db->escapeNumber($constructor->getAccountID()) . ',' . $this->db->escapeNumber($timeComplete) . ')');
975
976
		$this->currentlyBuilding[$this->db->getInsertID()] = array(
977
			'BuildingSlotID' => $this->db->getInsertID(),
978
			'ConstructionID' => $constructionID,
979
			'ConstructorID' => $constructor->getAccountID(),
980
			'Finishes' => $timeComplete,
981
			'TimeRemaining' => $timeComplete - SmrSession::getTime()
982
		);
983
	}
984
985
	public function stopBuilding($constructionID) {
986
		$matchingBuilding = false;
987
		$latestFinish = 0;
988
		foreach ($this->getCurrentlyBuilding() as $key => $building) {
989
			if ($building['ConstructionID'] == $constructionID && $building['Finishes'] > $latestFinish) {
990
				$latestFinish = $building['Finishes'];
991
				$matchingBuilding = $building;
992
			}
993
		}
994
		if ($matchingBuilding) {
995
			$this->hasStoppedBuilding[] = $matchingBuilding['BuildingSlotID'];
996
			unset($this->currentlyBuilding[$matchingBuilding['BuildingSlotID']]);
997
			return true;
998
		}
999
		return false;
1000
	}
1001
1002
	public function setName($name) {
1003
		if ($this->planetName == $name) {
1004
			return;
1005
		}
1006
		$this->planetName = $name;
1007
		$this->hasChanged = true;
1008
	}
1009
1010
	/**
1011
	 * Returns the name of the planet, suitably escaped for HTML display.
1012
	 */
1013
	public function getDisplayName() {
1014
		return htmlentities($this->planetName);
1015
	}
1016
1017
	/**
1018
	 * Returns the name of the planet, intended for combat messages.
1019
	 */
1020
	public function getCombatName() {
1021
		return '<span style="color:yellow;font-variant:small-caps">' . $this->getDisplayName() . ' (#' . $this->getSectorID() . ')</span>';
1022
	}
1023
1024
	public function isInhabitable() {
1025
		return $this->inhabitableTime <= SmrSession::getTime();
1026
	}
1027
1028
	public function getInhabitableTime() {
1029
		return $this->inhabitableTime;
1030
	}
1031
1032
	public function getExamineHREF() {
1033
		return SmrSession::getNewHREF(create_container('skeleton.php', 'planet_examine.php'));
1034
	}
1035
1036
	public function getLandHREF() {
1037
		return SmrSession::getNewHREF(create_container('planet_land_processing.php'));
1038
	}
1039
1040
	public function getAttackHREF() {
1041
		return SmrSession::getNewHREF(create_container('planet_attack_processing.php'));
1042
	}
1043
1044
	public function getBuildHREF($structureID) {
1045
		$container = create_container('planet_construction_processing.php');
1046
		$container['construction_id'] = $structureID;
1047
		$container['action'] = 'Build';
1048
		return SmrSession::getNewHREF($container);
1049
	}
1050
1051
	public function getCancelHREF($structureID) {
1052
		$container = create_container('planet_construction_processing.php');
1053
		$container['construction_id'] = $structureID;
1054
		$container['action'] = 'Cancel';
1055
		return SmrSession::getNewHREF($container);
1056
	}
1057
1058
	public function getFinancesHREF() {
1059
		return SmrSession::getNewHREF(create_container('planet_financial_processing.php'));
1060
	}
1061
1062
	public function getBondConfirmationHREF() {
1063
		return SmrSession::getNewHREF(create_container('skeleton.php', 'planet_bond_confirmation.php'));
1064
	}
1065
1066
	public function attackedBy(AbstractSmrPlayer $trigger, array $attackers) {
1067
		$trigger->increaseHOF(1, array('Combat', 'Planet', 'Number Of Triggers'), HOF_PUBLIC);
1068
		foreach ($attackers as $attacker) {
1069
			$attacker->increaseHOF(1, array('Combat', 'Planet', 'Number Of Attacks'), HOF_PUBLIC);
1070
			$this->db->query('REPLACE INTO player_attacks_planet (game_id, account_id, sector_id, time, level) VALUES ' .
1071
					'(' . $this->db->escapeNumber($this->getGameID()) . ', ' . $this->db->escapeNumber($attacker->getAccountID()) . ', ' . $this->db->escapeNumber($this->getSectorID()) . ', ' . $this->db->escapeNumber(SmrSession::getTime()) . ', ' . $this->db->escapeNumber($this->getLevel()) . ')');
1072
		}
1073
1074
		// Add each unique attack to news unless it was already added recently.
1075
		// Note: Attack uniqueness determined by planet owner.
1076
		$owner = $this->getOwner();
1077
		$this->db->query('SELECT 1 FROM news WHERE type = \'BREAKING\' AND game_id = ' . $this->db->escapeNumber($trigger->getGameID()) . ' AND dead_id=' . $this->db->escapeNumber($owner->getAccountID()) . ' AND time > ' . $this->db->escapeNumber(SmrSession::getTime() - self::TIME_ATTACK_NEWS_COOLDOWN) . ' LIMIT 1');
1078
		if ($this->db->getNumRows() == 0) {
1079
			if (count($attackers) >= 5) {
1080
				$text = count($attackers) . ' members of ' . $trigger->getAllianceBBLink() . ' have been spotted attacking ' .
1081
					$this->getDisplayName() . ' in sector ' . Globals::getSectorBBLink($this->getSectorID()) . '. The planet is owned by ' . $owner->getBBLink();
1082
				if ($owner->hasAlliance()) {
1083
					$text .= ', a member of ' . $owner->getAllianceBBLink();
1084
				}
1085
				$text .= '.';
1086
				$this->db->query('INSERT INTO news (game_id, time, news_message, type,killer_id,killer_alliance,dead_id,dead_alliance) VALUES (' . $this->db->escapeNumber($this->getGameID()) . ', ' . $this->db->escapeNumber(SmrSession::getTime()) . ', ' . $this->db->escapeString($text) . ', \'BREAKING\',' . $this->db->escapeNumber($trigger->getAccountID()) . ',' . $this->db->escapeNumber($trigger->getAllianceID()) . ',' . $this->db->escapeNumber($owner->getAccountID()) . ',' . $this->db->escapeNumber($owner->getAllianceID()) . ')');
1087
			}
1088
		}
1089
	}
1090
1091
1092
1093
	public function getPlayers() {
1094
		return SmrPlayer::getPlanetPlayers($this->getGameID(), $this->getSectorID());
1095
	}
1096
1097
	public function countPlayers() {
1098
		return count($this->getPlayers());
1099
	}
1100
1101
	public function hasPlayers() {
1102
		return count($this->getPlayers()) > 0;
1103
	}
1104
1105
	public function getOtherTraders(AbstractSmrPlayer $player) {
1106
		$players = SmrPlayer::getPlanetPlayers($this->getGameID(), $this->getSectorID()); //Do not use & because we unset something and only want that in what we return
1107
		unset($players[$player->getAccountID()]);
1108
		return $players;
1109
	}
1110
1111
	public function hasOtherTraders(AbstractSmrPlayer $player) {
1112
		return count($this->getOtherTraders($player)) > 0;
1113
	}
1114
1115
	public function hasEnemyTraders(AbstractSmrPlayer $player) {
1116
		if (!$this->hasOtherTraders($player)) {
1117
			return false;
1118
		}
1119
		$otherPlayers = $this->getOtherTraders($player);
1120
		foreach ($otherPlayers as $otherPlayer) {
1121
			if (!$player->traderNAPAlliance($otherPlayer)) {
1122
				return true;
1123
			}
1124
		}
1125
		return false;
1126
	}
1127
1128
	public function hasFriendlyTraders(AbstractSmrPlayer $player) {
1129
		if (!$this->hasOtherTraders($player)) {
1130
			return false;
1131
		}
1132
		$otherPlayers = $this->getOtherTraders($player);
1133
		foreach ($otherPlayers as $otherPlayer) {
1134
			if ($player->traderNAPAlliance($otherPlayer)) {
1135
				return true;
1136
			}
1137
		}
1138
		return false;
1139
	}
1140
1141
	public function getWeapons() {
1142
		$weapons = $this->getMountedWeapons();
1143
		for ($i = 0; $i < $this->getBuilding(PLANET_TURRET); ++$i) {
1144
			$weapons[] = SmrWeapon::getWeapon(WEAPON_PLANET_TURRET);
1145
		}
1146
		return $weapons;
1147
	}
1148
1149
	public function hasWeapons() {
1150
		return count($this->getWeapons()) > 0;
1151
	}
1152
1153
	public function &shootPlayers(array $targetPlayers) {
1154
		$results = array('Planet' => $this, 'TotalDamage' => 0, 'TotalDamagePerTargetPlayer' => array());
1155
		foreach ($targetPlayers as $targetPlayer) {
1156
			$results['TotalDamagePerTargetPlayer'][$targetPlayer->getAccountID()] = 0;
1157
		}
1158
		if ($this->isDestroyed()) {
1159
			$results['DeadBeforeShot'] = true;
1160
			return $results;
1161
		}
1162
		$results['DeadBeforeShot'] = false;
1163
		$weapons = $this->getWeapons();
1164
		foreach ($weapons as $orderID => $weapon) {
1165
			$results['Weapons'][$orderID] =& $weapon->shootPlayerAsPlanet($this, array_rand_value($targetPlayers));
1166
			if ($results['Weapons'][$orderID]['Hit']) {
1167
				$results['TotalDamage'] += $results['Weapons'][$orderID]['ActualDamage']['TotalDamage'];
1168
				$results['TotalDamagePerTargetPlayer'][$results['Weapons'][$orderID]['TargetPlayer']->getAccountID()] += $results['Weapons'][$orderID]['ActualDamage']['TotalDamage'];
1169
			}
1170
		}
1171
		if ($this->hasCDs()) {
1172
			$thisCDs = new SmrCombatDrones($this->getGameID(), $this->getCDs(), true);
1173
			$results['Drones'] =& $thisCDs->shootPlayerAsPlanet($this, array_rand_value($targetPlayers));
1174
			$results['TotalDamage'] += $results['Drones']['ActualDamage']['TotalDamage'];
1175
			$results['TotalDamagePerTargetPlayer'][$results['Drones']['TargetPlayer']->getAccountID()] += $results['Drones']['ActualDamage']['TotalDamage'];
1176
		}
1177
		return $results;
1178
	}
1179
1180
	/**
1181
	 * Returns an array of structure losses due to damage taken.
1182
	 */
1183
	public function checkForDowngrade($damage) : array {
1184
		$results = [];
1185
		// For every 70 damage there is a 15% chance of destroying a structure.
1186
		// Which structure is destroyed depends on the ratio of buildings and
1187
		// the time it takes to build them.
1188
		$numChances = floor($damage / self::DAMAGE_NEEDED_FOR_DOWNGRADE_CHANCE);
1189
		for ($i = 0; $i < $numChances; $i++) {
1190
			// Stop if the planet has no more buildlings
1191
			if ($this->getLevel() == 0) {
1192
				break;
1193
			}
1194
			//15% chance to destroy something
1195
			if (rand(1, 100) <= self::CHANCE_TO_DOWNGRADE) {
1196
				$chanceFactors = [];
1197
				foreach ($this->getStructureTypes() as $structureID => $structure) {
1198
					$chanceFactors[$structureID] = ($this->getBuilding($structureID) / $this->getMaxBuildings($structureID)) / $structure->baseTime();
1199
				}
1200
				$destroyID = getWeightedRandom($chanceFactors);
1201
				$this->destroyBuilding($destroyID, 1);
1202
				$this->checkForExcessDefense();
1203
				if (isset($results[$destroyID])) {
1204
					$results[$destroyID] += 1;
1205
				} else {
1206
					$results[$destroyID] = 1;
1207
				}
1208
			}
1209
		}
1210
		return $results;
1211
	}
1212
1213
	public function &doWeaponDamage(array $damage, $delayed) {
1214
		$alreadyDead = $this->isDestroyed(true);
1215
		$shieldDamage = 0;
1216
		$cdDamage = 0;
1217
		$armourDamage = 0;
1218
		if (!$alreadyDead) {
1219
			if ($damage['Shield'] || !$this->hasShields(true)) {
1220
				$shieldDamage = $this->doShieldDamage(min($damage['MaxDamage'], $damage['Shield']), $delayed);
1221
				$damage['Shield'] -= $shieldDamage;
1222
				$damage['MaxDamage'] -= $shieldDamage;
1223
1224
				if (!$this->hasShields(true) && ($shieldDamage == 0 || $damage['Rollover'])) {
1225
					if ($this->hasCDs(true)) {
1226
						$cdDamage = $this->doCDDamage(min($damage['MaxDamage'], $damage['Armour']), $delayed);
1227
						$damage['Armour'] -= $cdDamage;
1228
						$damage['MaxDamage'] -= $cdDamage;
1229
					}
1230
					if ($this->hasArmour(true) && ($cdDamage == 0 || $damage['Rollover'])) {
1231
						$armourDamage = $this->doArmourDamage(min($damage['MaxDamage'], $damage['Armour']), $delayed);
1232
						$damage['Armour'] -= $armourDamage;
1233
						$damage['MaxDamage'] -= $armourDamage;
1234
					}
1235
				}
1236
1237
			} else { // hit drones behind shields - we should only use this reduced damage branch if we cannot hit shields.
1238
				$cdDamage = $this->doCDDamage(IFloor(min($damage['MaxDamage'], $damage['Armour']) * DRONES_BEHIND_SHIELDS_DAMAGE_PERCENT), $delayed);
1239
			}
1240
		}
1241
1242
		$return = array(
1243
			'KillingShot' => !$alreadyDead && $this->isDestroyed(true),
1244
			'TargetAlreadyDead' => $alreadyDead,
1245
			'Shield' => $shieldDamage,
1246
			'Armour' => $armourDamage,
1247
			'HasShields' => $this->hasShields(true),
1248
			'HasArmour' => $this->hasArmour(true),
1249
			'CDs' => $cdDamage,
1250
			'NumCDs' => $cdDamage / CD_ARMOUR,
1251
			'HasCDs' => $this->hasCDs(true),
1252
			'TotalDamage' => $shieldDamage + $cdDamage + $armourDamage
1253
		);
1254
		return $return;
1255
	}
1256
1257
	protected function doShieldDamage($damage, $delayed) {
1258
		$actualDamage = min($this->getShields(true), $damage);
1259
		$this->decreaseShields($actualDamage, $delayed);
1260
		return $actualDamage;
1261
	}
1262
1263
	protected function doCDDamage($damage, $delayed) {
1264
		$actualDamage = min($this->getCDs(true), IFloor($damage / CD_ARMOUR));
1265
		$this->decreaseCDs($actualDamage, $delayed);
1266
		return $actualDamage * CD_ARMOUR;
1267
	}
1268
1269
	protected function doArmourDamage($damage, $delayed) {
1270
		$actualDamage = min($this->getArmour(true), $damage);
1271
		$this->decreaseArmour($actualDamage, $delayed);
1272
		return $actualDamage;
1273
	}
1274
1275
	public function creditCurrentAttackersForKill() {
1276
		//get all players involved for HoF
1277
		$this->db->query('SELECT account_id,level FROM player_attacks_planet WHERE ' . $this->SQL . ' AND time > ' . $this->db->escapeNumber(SmrSession::getTime() - self::TIME_TO_CREDIT_BUST));
1278
		while ($this->db->nextRecord()) {
1279
			$currPlayer = SmrPlayer::getPlayer($this->db->getInt('account_id'), $this->getGameID());
1280
			$currPlayer->increaseHOF($this->db->getInt('level'), array('Combat', 'Planet', 'Levels'), HOF_PUBLIC);
1281
			$currPlayer->increaseHOF(1, array('Combat', 'Planet', 'Completed'), HOF_PUBLIC);
1282
		}
1283
		$this->db->query('DELETE FROM player_attacks_planet WHERE ' . $this->SQL);
1284
	}
1285
1286
	public function &killPlanetByPlayer(AbstractSmrPlayer $killer) {
0 ignored issues
show
Unused Code introduced by
The parameter $killer 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

1286
	public function &killPlanetByPlayer(/** @scrutinizer ignore-unused */ AbstractSmrPlayer $killer) {

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...
1287
		$return = array();
1288
		$this->creditCurrentAttackersForKill();
1289
1290
		//kick everyone from planet
1291
		$this->db->query('UPDATE player SET land_on_planet = \'FALSE\' WHERE ' . $this->SQL);
1292
		$this->removeOwner();
1293
		$this->removePassword();
1294
		return $return;
1295
	}
1296
}
1297