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

Completed
Push — master ( ef1d69...78c0d4 )
by Dan
22s queued 17s
created

SmrForce::getSectorID()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 2
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php declare(strict_types=1);
2
3
class SmrForce {
4
	protected static array $CACHE_FORCES = [];
5
	protected static array $CACHE_SECTOR_FORCES = [];
6
	protected static array $TIDIED_UP = [];
7
8
	const LOWEST_MAX_EXPIRE_SCOUTS_ONLY = 432000; // 5 days
9
	const TIME_PER_SCOUT_ONLY = 86400; // 1 = 1 day
10
	const TIME_PERCENT_PER_SCOUT = 0.02; // 1/50th
11
	const TIME_PERCENT_PER_COMBAT = 0.02; // 1/50th
12
	const TIME_PERCENT_PER_MINE = 0.02; // 1/50th
13
	const REFRESH_ALL_TIME_PER_STACK = 1; // 1 second
14
15
	const MAX_MINES = 50;
16
	const MAX_CDS = 50;
17
	const MAX_SDS = 5;
18
19
	protected static $refreshAllHREF;
20
21
	protected Smr\Database $db;
22
	protected string $SQL;
23
24
	protected int $ownerID;
25
	protected int $sectorID;
26
	protected int $gameID;
27
	protected int $combatDrones = 0;
28
	protected int $scoutDrones = 0;
29
	protected int $mines = 0;
30
	protected int $expire = 0;
31
32
	protected bool $isNew;
33
	protected bool $hasChanged = false;
34
35
	public function __sleep() {
36
		return ['ownerID', 'sectorID', 'gameID'];
37
	}
38
39
	public static function refreshCache() : void {
40
		foreach (self::$CACHE_FORCES as $gameID => &$gameForces) {
41
			foreach ($gameForces as $sectorID => &$gameSectorForces) {
42
				foreach ($gameSectorForces as $ownerID => &$forces) {
43
					$forces = self::getForce($gameID, $sectorID, $ownerID, true);
44
				}
45
			}
46
		}
47
	}
48
49
	public static function clearCache() : void {
50
		self::$CACHE_FORCES = [];
51
		self::$CACHE_SECTOR_FORCES = [];
52
	}
53
54
	public static function saveForces() : void {
55
		foreach (self::$CACHE_FORCES as $gameForces) {
56
			foreach ($gameForces as $gameSectorForces) {
57
				foreach ($gameSectorForces as $forces) {
58
					$forces->update();
59
				}
60
			}
61
		}
62
	}
63
64
	public static function getGalaxyForces(int $gameID, int $galaxyID, bool $forceUpdate = false) : array {
65
		$db = Smr\Database::getInstance();
66
		$db->query('SELECT sector_has_forces.*, sector_id FROM sector LEFT JOIN sector_has_forces USING(game_id, sector_id) WHERE game_id = ' . $db->escapeNumber($gameID) . ' AND galaxy_id = ' . $db->escapeNumber($galaxyID));
67
		$galaxyForces = [];
68
		while ($db->nextRecord()) {
69
			$sectorID = $db->getInt('sector_id');
70
			if (!$db->hasField('owner_id')) {
71
				self::$CACHE_SECTOR_FORCES[$gameID][$sectorID] = [];
72
			} else {
73
				$ownerID = $db->getInt('owner_id');
74
				$force = self::getForce($gameID, $sectorID, $ownerID, $forceUpdate, $db);
75
				self::$CACHE_SECTOR_FORCES[$gameID][$sectorID][$ownerID] = $force;
76
				$galaxyForces[$sectorID][$ownerID] = $force;
77
			}
78
		}
79
		return $galaxyForces;
80
	}
81
82
	public static function getSectorForces(int $gameID, int $sectorID, bool $forceUpdate = false) : array {
83
		if ($forceUpdate || !isset(self::$CACHE_SECTOR_FORCES[$gameID][$sectorID])) {
84
			self::tidyUpForces(SmrGalaxy::getGalaxyContaining($gameID, $sectorID));
85
			$db = Smr\Database::getInstance();
86
			$db->query('SELECT * FROM sector_has_forces WHERE sector_id = ' . $db->escapeNumber($sectorID) . ' AND game_id=' . $db->escapeNumber($gameID) . ' ORDER BY expire_time ASC');
87
			$forces = array();
88
			while ($db->nextRecord()) {
89
				$ownerID = $db->getInt('owner_id');
90
				$forces[$ownerID] = self::getForce($gameID, $sectorID, $ownerID, $forceUpdate, $db);
91
			}
92
			self::$CACHE_SECTOR_FORCES[$gameID][$sectorID] = $forces;
93
		}
94
		return self::$CACHE_SECTOR_FORCES[$gameID][$sectorID];
95
	}
96
97
	public static function getForce(int $gameID, int $sectorID, int $ownerID, bool $forceUpdate = false, Smr\Database $db = null) : self {
98
		if ($forceUpdate || !isset(self::$CACHE_FORCES[$gameID][$sectorID][$ownerID])) {
99
			self::tidyUpForces(SmrGalaxy::getGalaxyContaining($gameID, $sectorID));
100
			$p = new SmrForce($gameID, $sectorID, $ownerID, $db);
101
			self::$CACHE_FORCES[$gameID][$sectorID][$ownerID] = $p;
102
		}
103
		return self::$CACHE_FORCES[$gameID][$sectorID][$ownerID];
104
	}
105
106
	public static function tidyUpForces(SmrGalaxy $galaxyToTidy) : void {
107
		if (!isset(self::$TIDIED_UP[$galaxyToTidy->getGameID()][$galaxyToTidy->getGalaxyID()])) {
108
			self::$TIDIED_UP[$galaxyToTidy->getGameID()][$galaxyToTidy->getGalaxyID()] = true;
109
			$db = Smr\Database::getInstance();
110
			$db->query('UPDATE sector_has_forces
111
						SET refresher=0,
112
							expire_time = (refresh_at + if(combat_drones+mines=0,
113
															LEAST('.$db->escapeNumber(self::LOWEST_MAX_EXPIRE_SCOUTS_ONLY) . ', scout_drones*' . $db->escapeNumber(self::TIME_PER_SCOUT_ONLY) . '),
114
															LEAST('.$db->escapeNumber($galaxyToTidy->getMaxForceTime()) . ', (combat_drones*' . $db->escapeNumber(self::TIME_PERCENT_PER_COMBAT) . '+scout_drones*' . $db->escapeNumber(self::TIME_PERCENT_PER_SCOUT) . '+mines*' . $db->escapeNumber(self::TIME_PERCENT_PER_MINE) . ')*' . $db->escapeNumber($galaxyToTidy->getMaxForceTime()) . ')
115
														))
116
						WHERE game_id = '.$db->escapeNumber($galaxyToTidy->getGameID()) . ' AND sector_id >= ' . $db->escapeNumber($galaxyToTidy->getStartSector()) . ' AND sector_id <= ' . $db->escapeNumber($galaxyToTidy->getEndSector()) . ' AND refresher != 0 AND refresh_at <= ' . $db->escapeNumber(Smr\Epoch::time()));
117
			$db->query('DELETE FROM sector_has_forces WHERE expire_time < ' . $db->escapeNumber(Smr\Epoch::time()));
118
		}
119
	}
120
121
	protected function __construct(int $gameID, int $sectorID, int $ownerID, Smr\Database $db = null) {
122
		$this->db = Smr\Database::getInstance();
123
		$this->SQL = 'game_id = ' . $this->db->escapeNumber($gameID) . '
124
		              AND sector_id = '.$this->db->escapeNumber($sectorID) . '
125
		              AND owner_id = '.$this->db->escapeNumber($ownerID);
126
127
		if (isset($db)) {
128
			$this->isNew = false;
129
		} else {
130
			$db = $this->db;
131
			$this->db->query('SELECT * FROM sector_has_forces WHERE ' . $this->SQL);
132
			$this->isNew = !$db->nextRecord();
133
		}
134
135
		$this->gameID = $gameID;
136
		$this->ownerID = $ownerID;
137
		$this->sectorID = $sectorID;
138
		if (!$this->isNew) {
139
			$this->combatDrones = $db->getInt('combat_drones');
140
			$this->scoutDrones = $db->getInt('scout_drones');
141
			$this->mines = $db->getInt('mines');
142
			$this->expire = $db->getInt('expire_time');
143
		}
144
	}
145
146
	public function exists() : bool {
147
		return ($this->hasCDs() || $this->hasSDs() || $this->hasMines()) && !$this->hasExpired();
148
	}
149
150
	public function hasMaxCDs() : bool {
151
		return $this->getCDs() >= self::MAX_CDS;
152
	}
153
154
	public function hasMaxSDs() : bool {
155
		return $this->getSDs() >= self::MAX_SDS;
156
	}
157
158
	public function hasMaxMines() : bool {
159
		return $this->getMines() >= self::MAX_MINES;
160
	}
161
162
	public function hasCDs() : bool {
163
		return $this->getCDs() > 0;
164
	}
165
166
	public function hasSDs() : bool {
167
		return $this->getSDs() > 0;
168
	}
169
170
	public function hasMines() : bool {
171
		return $this->getMines() > 0;
172
	}
173
174
	public function getCDs() : int {
175
		return $this->combatDrones;
176
	}
177
178
	public function getSDs() : int {
179
		return $this->scoutDrones;
180
	}
181
182
	public function getMines() : int {
183
		return $this->mines;
184
	}
185
186
	public function addMines(int $amount) : void {
187
		if ($amount < 0) {
188
			throw new Exception('Cannot add negative mines.');
189
		}
190
		$this->setMines($this->getMines() + $amount);
191
	}
192
193
	public function addCDs(int $amount) : void {
194
		if ($amount < 0) {
195
			throw new Exception('Cannot add negative CDs.');
196
		}
197
		$this->setCDs($this->getCDs() + $amount);
198
	}
199
200
	public function addSDs(int $amount) : void {
201
		if ($amount < 0) {
202
			throw new Exception('Cannot add negative SDs.');
203
		}
204
		$this->setSDs($this->getSDs() + $amount);
205
	}
206
207
	public function takeMines(int $amount) : void {
208
		if ($amount < 0) {
209
			throw new Exception('Cannot take negative mines.');
210
		}
211
		$this->setMines($this->getMines() - $amount);
212
	}
213
214
	public function takeCDs(int $amount) : void {
215
		if ($amount < 0) {
216
			throw new Exception('Cannot take negative CDs.');
217
		}
218
		$this->setCDs($this->getCDs() - $amount);
219
	}
220
221
	public function takeSDs(int $amount) : void {
222
		if ($amount < 0) {
223
			throw new Exception('Cannot take negative SDs.');
224
		}
225
		$this->setSDs($this->getSDs() - $amount);
226
	}
227
228
	public function setMines(int $amount) : void {
229
		if ($amount < 0) {
230
			throw new Exception('Cannot set negative mines.');
231
		}
232
		if ($amount == $this->getMines()) {
233
			return;
234
		}
235
		$this->hasChanged = true;
236
		$this->mines = $amount;
237
	}
238
239
	public function setCDs(int $amount) : void {
240
		if ($amount < 0) {
241
			throw new Exception('Cannot set negative CDs.');
242
		}
243
		if ($amount == $this->getCDs()) {
244
			return;
245
		}
246
		$this->hasChanged = true;
247
		$this->combatDrones = $amount;
248
	}
249
250
	public function setSDs(int $amount) : void {
251
		if ($amount < 0) {
252
			throw new Exception('Cannot set negative SDs.');
253
		}
254
		if ($amount == $this->getSDs()) {
255
			return;
256
		}
257
		$this->hasChanged = true;
258
		$this->scoutDrones = $amount;
259
	}
260
261
	public function hasExpired() : bool {
262
		return $this->expire < Smr\Epoch::time();
263
	}
264
265
	public function getExpire() {
266
		return $this->expire;
267
	}
268
269
	public function setExpire(int $time) : void {
270
		if ($time < 0) {
271
			throw new Exception('Cannot set negative expiry.');
272
		}
273
		if ($time == $this->getExpire()) {
274
			return;
275
		}
276
		if ($time > Smr\Epoch::time() + $this->getMaxExpireTime()) {
277
			$time = Smr\Epoch::time() + $this->getMaxExpireTime();
278
		}
279
		$this->hasChanged = true;
280
		$this->expire = $time;
281
		if (!$this->isNew) {
282
			$this->update();
283
		}
284
	}
285
286
	public function updateExpire() : void {
287
		// Changed (26/10/05) - scout drones count * 2
288
		if ($this->getCDs() == 0 && $this->getMines() == 0 && $this->getSDs() > 0) {
289
			$time = self::TIME_PER_SCOUT_ONLY * $this->getSDs();
290
		} else {
291
			$time = ($this->getCDs() * self::TIME_PERCENT_PER_COMBAT + $this->getSDs() * self::TIME_PERCENT_PER_SCOUT + $this->getMines() * self::TIME_PERCENT_PER_MINE) * $this->getMaxGalaxyExpireTime();
292
		}
293
		$this->setExpire(Smr\Epoch::time() + IFloor($time));
294
	}
295
296
	public function getMaxExpireTime() : int {
297
		if ($this->hasCDs() || $this->hasMines()) {
298
			return $this->getMaxGalaxyExpireTime();
299
		}
300
		if (!$this->hasCDs() && !$this->hasMines() && $this->hasSDs()) {
301
			return max(self::LOWEST_MAX_EXPIRE_SCOUTS_ONLY, $this->getMaxGalaxyExpireTime());
302
		}
303
		return 0;
304
	}
305
306
	public function getMaxGalaxyExpireTime() : int {
307
		return $this->getGalaxy()->getMaxForceTime();
308
	}
309
310
	public function getBumpTurnCost(AbstractSmrShip $ship) : int {
311
		$mines = $this->getMines();
312
		if ($mines <= 1) {
313
			return 0;
314
		}
315
		if ($mines < 10) {
316
			$turns = 1;
317
		} elseif ($mines < 25) {
318
			$turns = 2;
319
		} else {
320
			$turns = 3;
321
		}
322
		if ($ship->isFederal() || $ship->hasDCS()) {
323
			$turns -= 1;
324
		}
325
		return $turns;
326
	}
327
328
	public function getAttackTurnCost(AbstractSmrShip $ship) : int {
329
		if ($ship->isFederal() || $ship->hasDCS()) {
330
			return 2;
331
		}
332
		return 3;
333
	}
334
335
	public function getOwnerID() : int {
336
		return $this->ownerID;
337
	}
338
339
	public function getGameID() : int {
340
		return $this->gameID;
341
	}
342
343
	public function getSector() : SmrSector {
344
		return SmrSector::getSector($this->getGameID(), $this->getSectorID());
345
	}
346
347
	public function getSectorID() : int {
348
		return $this->sectorID;
349
	}
350
351
	public function ping(string $pingMessage, AbstractSmrPlayer $playerPinging, bool $skipCheck = false) : void {
352
		if (!$this->hasSDs() && !$skipCheck) {
353
			return;
354
		}
355
		$owner = $this->getOwner();
356
		if (!$playerPinging->sameAlliance($owner)) {
357
			$playerPinging->sendMessage($owner->getAccountID(), MSG_SCOUT, $pingMessage, false);
358
		}
359
	}
360
361
	public function getGalaxy() : SmrGalaxy {
362
		return SmrGalaxy::getGalaxyContaining($this->getGameID(), $this->getSectorID());
363
	}
364
365
	public function getOwner() : AbstractSmrPlayer {
366
		return SmrPlayer::getPlayer($this->getOwnerID(), $this->getGameID());
367
	}
368
369
	public function update() : void {
370
		if (!$this->isNew) {
371
			if (!$this->exists()) {
372
				$this->db->query('DELETE FROM sector_has_forces WHERE ' . $this->SQL);
373
				$this->isNew = true;
374
			} elseif ($this->hasChanged) {
375
				$this->db->query('UPDATE sector_has_forces SET combat_drones = ' . $this->db->escapeNumber($this->combatDrones) . ', scout_drones = ' . $this->db->escapeNumber($this->scoutDrones) . ', mines = ' . $this->db->escapeNumber($this->mines) . ', expire_time = ' . $this->db->escapeNumber($this->expire) . ' WHERE ' . $this->SQL);
376
			}
377
		} elseif ($this->exists()) {
378
			$this->db->query('INSERT INTO sector_has_forces (game_id, sector_id, owner_id, combat_drones, scout_drones, mines, expire_time)
379
								VALUES('.$this->db->escapeNumber($this->gameID) . ', ' . $this->db->escapeNumber($this->sectorID) . ', ' . $this->db->escapeNumber($this->ownerID) . ', ' . $this->db->escapeNumber($this->combatDrones) . ', ' . $this->db->escapeNumber($this->scoutDrones) . ', ' . $this->db->escapeNumber($this->mines) . ', ' . $this->db->escapeNumber($this->expire) . ')');
380
			$this->isNew = false;
381
		}
382
		// This instance is now in sync with the database
383
		$this->hasChanged = false;
384
	}
385
386
	/**
387
	 * Update the table fields associated with using Refresh All
388
	 */
389
	public function updateRefreshAll(SmrPlayer $player, int $refreshTime) : void {
390
		$this->db->query('UPDATE sector_has_forces SET refresh_at=' . $this->db->escapeNumber($refreshTime) . ', refresher=' . $this->db->escapeNumber($player->getAccountID()) . ' WHERE ' . $this->SQL);
391
	}
392
393
	public function getExamineDropForcesHREF() : string {
394
		$container = Page::create('skeleton.php', 'forces_drop.php');
395
		$container['owner_id'] = $this->getOwnerID();
396
		return $container->href();
397
	}
398
399
	public function getAttackForcesHREF() : string {
400
		$container = Page::create('forces_attack_processing.php');
401
		$container['action'] = 'attack';
402
		$container['owner_id'] = $this->getOwnerID();
403
		return $container->href();
404
	}
405
406
	public function getRefreshHREF() : string {
407
		$container = Page::create('forces_refresh_processing.php');
408
		$container['owner_id'] = $this->getOwnerID();
409
		return $container->href();
410
	}
411
412
	protected function getDropContainer() : Page {
413
		$container = Page::create('forces_drop_processing.php');
414
		$container['owner_id'] = $this->getOwnerID();
415
		return $container;
416
	}
417
418
	public function getDropSDHREF() : string {
419
		$container = $this->getDropContainer();
420
		$container['drop_scout_drones'] = 1;
421
		return $container->href();
422
	}
423
424
	public function getTakeSDHREF() : string {
425
		$container = $this->getDropContainer();
426
		$container['take_scout_drones'] = 1;
427
		return $container->href();
428
	}
429
430
	public function getDropCDHREF() : string {
431
		$container = $this->getDropContainer();
432
		$container['drop_combat_drones'] = 1;
433
		return $container->href();
434
	}
435
436
	public function getTakeCDHREF() : string {
437
		$container = $this->getDropContainer();
438
		$container['take_combat_drones'] = 1;
439
		return $container->href();
440
	}
441
442
	public function getDropMineHREF() : string {
443
		$container = $this->getDropContainer();
444
		$container['drop_mines'] = 1;
445
		return $container->href();
446
	}
447
448
	public function getTakeMineHREF() : string {
449
		$container = $this->getDropContainer();
450
		$container['take_mines'] = 1;
451
		return $container->href();
452
	}
453
454
	public static function getRefreshAllHREF() : string {
455
		$container = Page::create('forces_mass_refresh.php');
456
		return $container->href();
457
	}
458
459
	public function shootPlayers(array $targetPlayers, bool $minesAreAttacker) : array {
460
		$results = array('TotalDamage' => 0);
461
		if (!$this->exists()) {
462
			$results['DeadBeforeShot'] = true;
463
			return $results;
464
		}
465
		$results['DeadBeforeShot'] = false;
466
467
		if ($this->hasMines()) {
468
			$thisMines = new SmrMines($this->getMines());
469
			$results['Results']['Mines'] = $thisMines->shootPlayerAsForce($this, array_rand_value($targetPlayers), $minesAreAttacker);
470
			$results['TotalDamage'] += $results['Results']['Mines']['ActualDamage']['TotalDamage'];
471
		}
472
473
		if ($this->hasCDs()) {
474
			$thisCDs = new SmrCombatDrones($this->getCDs());
475
			$results['Results']['Drones'] = $thisCDs->shootPlayerAsForce($this, array_rand_value($targetPlayers));
476
			$results['TotalDamage'] += $results['Results']['Drones']['ActualDamage']['TotalDamage'];
477
		}
478
479
		if (!$minesAreAttacker) {
480
			if ($this->hasSDs()) {
481
				$thisSDs = new SmrScoutDrones($this->getSDs());
482
				$results['Results']['Scouts'] = $thisSDs->shootPlayerAsForce($this, array_rand_value($targetPlayers));
483
				$results['TotalDamage'] += $results['Results']['Scouts']['ActualDamage']['TotalDamage'];
484
			}
485
		}
486
487
		$results['ForcesDestroyed'] = !$this->exists();
488
		return $results;
489
	}
490
491
	public function doWeaponDamage(array $damage) : array {
492
		$alreadyDead = !$this->exists();
493
		$minesDamage = 0;
494
		$cdDamage = 0;
495
		$sdDamage = 0;
496
		if (!$alreadyDead) {
497
			$minesDamage = $this->doMinesDamage(min($damage['MaxDamage'], $damage['Armour']));
498
			$damage['Armour'] -= $minesDamage;
499
			$damage['MaxDamage'] -= $minesDamage;
500
			if (!$this->hasMines() && ($minesDamage == 0 || $damage['Rollover'])) {
501
				$cdDamage = $this->doCDDamage(min($damage['MaxDamage'], $damage['Armour']));
502
				$damage['Armour'] -= $cdDamage;
503
				$damage['MaxDamage'] -= $cdDamage;
504
				if (!$this->hasCDs() && ($cdDamage == 0 || $damage['Rollover'])) {
505
					$sdDamage = $this->doSDDamage(min($damage['MaxDamage'], $damage['Armour']));
506
				}
507
			}
508
		}
509
		$return = array(
510
						'KillingShot' => !$alreadyDead && !$this->exists(),
511
						'TargetAlreadyDead' => $alreadyDead,
512
						'Mines' => $minesDamage,
513
						'NumMines' => $minesDamage / MINE_ARMOUR,
514
						'HasMines' => $this->hasMines(),
515
						'CDs' => $cdDamage,
516
						'NumCDs' => $cdDamage / CD_ARMOUR,
517
						'HasCDs' => $this->hasCDs(),
518
						'SDs' => $sdDamage,
519
						'NumSDs' => $sdDamage / SD_ARMOUR,
520
						'HasSDs' => $this->hasSDs(),
521
						'TotalDamage' => $minesDamage + $cdDamage + $sdDamage
522
		);
523
		return $return;
524
	}
525
526
	protected function doMinesDamage(int $damage) : int {
527
		$actualDamage = min($this->getMines(), IFloor($damage / MINE_ARMOUR));
528
		$this->takeMines($actualDamage);
529
		return $actualDamage * MINE_ARMOUR;
530
	}
531
532
	protected function doCDDamage(int $damage) : int {
533
		$actualDamage = min($this->getCDs(), IFloor($damage / CD_ARMOUR));
534
		$this->takeCDs($actualDamage);
535
		return $actualDamage * CD_ARMOUR;
536
	}
537
538
	protected function doSDDamage(int $damage) : int {
539
		$actualDamage = min($this->getSDs(), IFloor($damage / SD_ARMOUR));
540
		$this->takeSDs($actualDamage);
541
		return $actualDamage * SD_ARMOUR;
542
	}
543
544
	public function killForcesByPlayer(AbstractSmrPlayer $killer) : array {
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

544
	public function killForcesByPlayer(/** @scrutinizer ignore-unused */ AbstractSmrPlayer $killer) : array {

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...
545
		$return = array();
546
		return $return;
547
	}
548
}
549