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

Failed Conditions
Pull Request — main (#1487)
by Dan
06:01
created

AbstractSmrLocation::getGalaxyLocations()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 10
c 0
b 0
f 0
nc 2
nop 3
dl 0
loc 12
rs 9.9332
1
<?php declare(strict_types=1);
2
3
use Smr\Database;
4
use Smr\DatabaseRecord;
5
use Smr\ShipClass;
6
7
class AbstractSmrLocation {
8
9
	/** @var array<int, SmrLocation> */
10
	protected static array $CACHE_ALL_LOCATIONS;
11
	/** @var array<int, SmrLocation> */
12
	protected static array $CACHE_LOCATIONS = [];
13
	/** @var array<int, array<int, array<int, SmrLocation>>> */
14
	protected static array $CACHE_SECTOR_LOCATIONS = [];
15
16
	protected Database $db;
17
	protected readonly string $SQL;
18
19
	protected string $name;
20
	protected ?string $processor;
21
	protected string $image;
22
23
	protected bool $fed;
24
	protected bool $bank;
25
	protected bool $bar;
26
	protected bool $HQ;
27
	protected bool $UG;
28
29
	/** @var array<int, array<string, string|int>> */
30
	protected array $hardwareSold;
31
	/** @var array<int, SmrShipType> */
32
	protected array $shipsSold;
33
	/** @var array<int, SmrWeapon> */
34
	protected array $weaponsSold;
35
36
	public static function clearCache(): void {
37
		self::$CACHE_ALL_LOCATIONS = [];
38
		self::$CACHE_LOCATIONS = [];
39
		self::$CACHE_SECTOR_LOCATIONS = [];
40
	}
41
42
	/**
43
	 * @return array<int, SmrLocation>
44
	 */
45
	public static function getAllLocations(int $gameID, bool $forceUpdate = false): array {
46
		if ($forceUpdate || !isset(self::$CACHE_ALL_LOCATIONS)) {
47
			$db = Database::getInstance();
48
			$dbResult = $db->read('SELECT * FROM location_type ORDER BY location_type_id');
49
			$locations = [];
50
			foreach ($dbResult->records() as $dbRecord) {
51
				$locationTypeID = $dbRecord->getInt('location_type_id');
52
				$locations[$locationTypeID] = SmrLocation::getLocation($gameID, $locationTypeID, $forceUpdate, $dbRecord);
53
			}
54
			self::$CACHE_ALL_LOCATIONS = $locations;
55
		}
56
		return self::$CACHE_ALL_LOCATIONS;
57
	}
58
59
	/**
60
	 * @return array<int, array<int, SmrLocation>>
61
	 */
62
	public static function getGalaxyLocations(int $gameID, int $galaxyID, bool $forceUpdate = false): array {
63
		$db = Database::getInstance();
64
		$dbResult = $db->read('SELECT location_type.*, sector_id FROM location LEFT JOIN sector USING(game_id, sector_id) LEFT JOIN location_type USING (location_type_id) WHERE game_id = ' . $db->escapeNumber($gameID) . ' AND galaxy_id = ' . $db->escapeNumber($galaxyID));
65
		$galaxyLocations = [];
66
		foreach ($dbResult->records() as $dbRecord) {
67
			$sectorID = $dbRecord->getInt('sector_id');
68
			$locationTypeID = $dbRecord->getInt('location_type_id');
69
			$location = self::getLocation($gameID, $locationTypeID, $forceUpdate, $dbRecord);
70
			self::$CACHE_SECTOR_LOCATIONS[$gameID][$sectorID][$locationTypeID] = $location;
71
			$galaxyLocations[$sectorID][$locationTypeID] = $location;
72
		}
73
		return $galaxyLocations;
74
	}
75
76
	/**
77
	 * @return array<int, SmrLocation>
78
	 */
79
	public static function getSectorLocations(int $gameID, int $sectorID, bool $forceUpdate = false): array {
80
		if ($forceUpdate || !isset(self::$CACHE_SECTOR_LOCATIONS[$gameID][$sectorID])) {
81
			$db = Database::getInstance();
82
			$dbResult = $db->read('SELECT * FROM location LEFT JOIN location_type USING (location_type_id) WHERE sector_id = ' . $db->escapeNumber($sectorID) . ' AND game_id=' . $db->escapeNumber($gameID));
83
			$locations = [];
84
			foreach ($dbResult->records() as $dbRecord) {
85
				$locationTypeID = $dbRecord->getInt('location_type_id');
86
				$locations[$locationTypeID] = self::getLocation($gameID, $locationTypeID, $forceUpdate, $dbRecord);
87
			}
88
			self::$CACHE_SECTOR_LOCATIONS[$gameID][$sectorID] = $locations;
89
		}
90
		return self::$CACHE_SECTOR_LOCATIONS[$gameID][$sectorID];
91
	}
92
93
	public static function addSectorLocation(int $gameID, int $sectorID, SmrLocation $location): void {
94
		self::getSectorLocations($gameID, $sectorID); // make sure cache is populated
95
		$db = Database::getInstance();
96
		$db->insert('location', [
97
			'game_id' => $db->escapeNumber($gameID),
98
			'sector_id' => $db->escapeNumber($sectorID),
99
			'location_type_id' => $db->escapeNumber($location->getTypeID()),
100
		]);
101
		self::$CACHE_SECTOR_LOCATIONS[$gameID][$sectorID][$location->getTypeID()] = $location;
102
	}
103
104
	public static function moveSectorLocation(int $gameID, int $oldSectorID, int $newSectorID, SmrLocation $location): void {
105
		if ($oldSectorID === $newSectorID) {
106
			return;
107
		}
108
109
		// Make sure cache is populated
110
		self::getSectorLocations($gameID, $oldSectorID);
111
		self::getSectorLocations($gameID, $newSectorID);
112
113
		$db = Database::getInstance();
114
		$db->write('UPDATE location SET sector_id = ' . $db->escapeNumber($newSectorID) . ' WHERE game_id = ' . $db->escapeNumber($gameID) . ' AND sector_id = ' . $db->escapeNumber($oldSectorID) . ' AND location_type_id = ' . $location->getTypeID());
115
		unset(self::$CACHE_SECTOR_LOCATIONS[$gameID][$oldSectorID][$location->getTypeID()]);
116
		self::$CACHE_SECTOR_LOCATIONS[$gameID][$newSectorID][$location->getTypeID()] = $location;
117
118
		// Preserve the same element order that we'd have in getSectorLocations
119
		ksort(self::$CACHE_SECTOR_LOCATIONS[$gameID][$newSectorID]);
120
	}
121
122
	public static function removeSectorLocations(int $gameID, int $sectorID): void {
123
		$db = Database::getInstance();
124
		$db->write('DELETE FROM location WHERE game_id = ' . $db->escapeNumber($gameID) . ' AND sector_id = ' . $db->escapeNumber($sectorID));
125
		self::$CACHE_SECTOR_LOCATIONS[$gameID][$sectorID] = [];
126
	}
127
128
	public static function getLocation(int $gameID, int $locationTypeID, bool $forceUpdate = false, DatabaseRecord $dbRecord = null): SmrLocation {
129
		if ($forceUpdate || !isset(self::$CACHE_LOCATIONS[$locationTypeID])) {
130
			self::$CACHE_LOCATIONS[$locationTypeID] = new SmrLocation($gameID, $locationTypeID, $dbRecord);
131
		}
132
		return self::$CACHE_LOCATIONS[$locationTypeID];
133
	}
134
135
	protected function __construct(
136
		protected readonly int $gameID, // use 0 to be independent of game
137
		protected readonly int $typeID,
138
		DatabaseRecord $dbRecord = null
139
	) {
140
		$this->db = Database::getInstance();
141
		$this->SQL = 'location_type_id = ' . $this->db->escapeNumber($typeID);
0 ignored issues
show
Bug introduced by
The property SQL is declared read-only in AbstractSmrLocation.
Loading history...
142
143
		if ($dbRecord === null) {
144
			$dbResult = $this->db->read('SELECT * FROM location_type WHERE ' . $this->SQL);
145
			if ($dbResult->hasRecord()) {
146
				$dbRecord = $dbResult->record();
147
			}
148
		}
149
		$locationExists = $dbRecord !== null;
150
151
		if ($locationExists) {
152
			$this->name = $dbRecord->getString('location_name');
153
			$this->processor = $dbRecord->getNullableString('location_processor');
154
			$this->image = $dbRecord->getString('location_image');
155
		} else {
156
			throw new Exception('Cannot find location: ' . $typeID);
157
		}
158
	}
159
160
	public function getGameID(): int {
161
		return $this->gameID;
162
	}
163
164
	public function getTypeID(): int {
165
		return $this->typeID;
166
	}
167
168
	public function getRaceID(): int {
169
		if ($this->isFed() && $this->getTypeID() != LOCATION_TYPE_FEDERAL_BEACON) {
170
			return $this->getTypeID() - LOCATION_GROUP_RACIAL_BEACONS;
171
		}
172
		if ($this->isHQ() && $this->getTypeID() != LOCATION_TYPE_FEDERAL_HQ) {
173
			return $this->getTypeID() - LOCATION_GROUP_RACIAL_HQS;
174
		}
175
		return RACE_NEUTRAL;
176
	}
177
178
	public function getName(): string {
179
		return $this->name;
180
	}
181
182
	public function setName(string $name): void {
183
		$name = htmlentities($name, ENT_COMPAT, 'utf-8');
184
		if ($this->name === $name) {
185
			return;
186
		}
187
		$this->name = $name;
188
		$this->db->write('UPDATE location_type SET location_name=' . $this->db->escapeString($this->name) . ' WHERE ' . $this->SQL);
189
	}
190
191
	public function hasAction(): bool {
192
		return $this->processor !== null;
193
	}
194
195
	public function getAction(): ?string {
196
		return $this->processor;
197
	}
198
199
	public function getImage(): string {
200
		return $this->image;
201
	}
202
203
	public function isFed(): bool {
204
		if (!isset($this->fed)) {
205
			$dbResult = $this->db->read('SELECT 1 FROM location_is_fed WHERE ' . $this->SQL);
206
			$this->fed = $dbResult->hasRecord();
207
		}
208
		return $this->fed;
209
	}
210
211
	public function setFed(bool $bool): void {
212
		if ($this->fed === $bool) {
213
			return;
214
		}
215
		if ($bool) {
216
			$this->db->write('INSERT IGNORE INTO location_is_fed (location_type_id) values (' . $this->db->escapeNumber($this->getTypeID()) . ')');
217
		} else {
218
			$this->db->write('DELETE FROM location_is_fed WHERE ' . $this->SQL);
219
		}
220
		$this->fed = $bool;
221
	}
222
223
	public function isBank(): bool {
224
		if (!isset($this->bank)) {
225
			$dbResult = $this->db->read('SELECT 1 FROM location_is_bank WHERE ' . $this->SQL);
226
			$this->bank = $dbResult->hasRecord();
227
		}
228
		return $this->bank;
229
	}
230
231
	public function setBank(bool $bool): void {
232
		if ($this->bank === $bool) {
233
			return;
234
		}
235
		if ($bool) {
236
			$this->db->insert('location_is_bank', [
237
				'location_type_id' => $this->db->escapeNumber($this->getTypeID()),
238
			]);
239
		} else {
240
			$this->db->write('DELETE FROM location_is_bank WHERE ' . $this->SQL);
241
		}
242
		$this->bank = $bool;
243
	}
244
245
	public function isBar(): bool {
246
		if (!isset($this->bar)) {
247
			$dbResult = $this->db->read('SELECT 1 FROM location_is_bar WHERE ' . $this->SQL);
248
			$this->bar = $dbResult->hasRecord();
249
		}
250
		return $this->bar;
251
	}
252
253
	public function setBar(bool $bool): void {
254
		if ($this->bar === $bool) {
255
			return;
256
		}
257
		if ($bool) {
258
			$this->db->write('INSERT IGNORE INTO location_is_bar (location_type_id) values (' . $this->db->escapeNumber($this->getTypeID()) . ')');
259
		} else {
260
			$this->db->write('DELETE FROM location_is_bar WHERE ' . $this->SQL);
261
		}
262
		$this->bar = $bool;
263
	}
264
265
	public function isHQ(): bool {
266
		if (!isset($this->HQ)) {
267
			$dbResult = $this->db->read('SELECT 1 FROM location_is_hq WHERE ' . $this->SQL);
268
			$this->HQ = $dbResult->hasRecord();
269
		}
270
		return $this->HQ;
271
	}
272
273
	public function setHQ(bool $bool): void {
274
		if ($this->HQ === $bool) {
275
			return;
276
		}
277
		if ($bool) {
278
			$this->db->write('INSERT IGNORE INTO location_is_hq (location_type_id) values (' . $this->db->escapeNumber($this->getTypeID()) . ')');
279
		} else {
280
			$this->db->write('DELETE FROM location_is_hq WHERE ' . $this->SQL);
281
		}
282
		$this->HQ = $bool;
283
	}
284
285
	public function isUG(): bool {
286
		if (!isset($this->UG)) {
287
			$dbResult = $this->db->read('SELECT 1 FROM location_is_ug WHERE ' . $this->SQL);
288
			$this->UG = $dbResult->hasRecord();
289
		}
290
		return $this->UG;
291
	}
292
293
	public function setUG(bool $bool): void {
294
		if ($this->UG === $bool) {
295
			return;
296
		}
297
		if ($bool) {
298
			$this->db->insert('location_is_ug', [
299
				'location_type_id' => $this->db->escapeNumber($this->getTypeID()),
300
			]);
301
		} else {
302
			$this->db->write('DELETE FROM location_is_ug WHERE ' . $this->SQL);
303
		}
304
		$this->UG = $bool;
305
	}
306
307
	/**
308
	 * @return array<int, array<string, string|int>>
309
	 */
310
	public function getHardwareSold(): array {
311
		if (!isset($this->hardwareSold)) {
312
			$this->hardwareSold = [];
313
			$dbResult = $this->db->read('SELECT hardware_type_id FROM location_sells_hardware WHERE ' . $this->SQL);
314
			foreach ($dbResult->records() as $dbRecord) {
315
				$this->hardwareSold[$dbRecord->getInt('hardware_type_id')] = Globals::getHardwareTypes($dbRecord->getInt('hardware_type_id'));
316
			}
317
		}
318
		return $this->hardwareSold;
319
	}
320
321
	public function isHardwareSold(int $hardwareTypeID = null): bool {
322
		$hardware = $this->getHardwareSold();
323
		if ($hardwareTypeID === null) {
324
			return count($hardware) != 0;
325
		}
326
		return isset($hardware[$hardwareTypeID]);
327
	}
328
329
	public function addHardwareSold(int $hardwareTypeID): void {
330
		if ($this->isHardwareSold($hardwareTypeID)) {
331
			return;
332
		}
333
		$dbResult = $this->db->read('SELECT 1 FROM hardware_type WHERE hardware_type_id = ' . $this->db->escapeNumber($hardwareTypeID));
334
		if (!$dbResult->hasRecord()) {
335
			throw new Exception('Invalid hardware type id given');
336
		}
337
		$this->db->insert('location_sells_hardware', [
338
			'location_type_id' => $this->db->escapeNumber($this->getTypeID()),
339
			'hardware_type_id' => $this->db->escapeNumber($hardwareTypeID),
340
		]);
341
		$this->hardwareSold[$hardwareTypeID] = Globals::getHardwareTypes($hardwareTypeID);
342
	}
343
344
	public function removeHardwareSold(int $hardwareTypeID): void {
345
		if (!$this->isHardwareSold($hardwareTypeID)) {
346
			return;
347
		}
348
		$this->db->write('DELETE FROM location_sells_hardware WHERE ' . $this->SQL . ' AND hardware_type_id = ' . $this->db->escapeNumber($hardwareTypeID));
349
		unset($this->hardwareSold[$hardwareTypeID]);
350
	}
351
352
	/**
353
	 * @return array<int, SmrShipType>
354
	 */
355
	public function getShipsSold(): array {
356
		if (!isset($this->shipsSold)) {
357
			$this->shipsSold = [];
358
			$dbResult = $this->db->read('SELECT * FROM location_sells_ships JOIN ship_type USING (ship_type_id) WHERE ' . $this->SQL);
359
			foreach ($dbResult->records() as $dbRecord) {
360
				$shipTypeID = $dbRecord->getInt('ship_type_id');
361
				$this->shipsSold[$shipTypeID] = SmrShipType::get($shipTypeID, $dbRecord);
362
			}
363
364
			if ($this->gameID > 0 && SmrGame::getGame($this->gameID)->isGameType(SmrGame::GAME_TYPE_HUNTER_WARS)) {
365
				// Remove ships that are not allowed in Hunter Wars
366
				unset($this->shipsSold[SHIP_TYPE_PLANETARY_SUPER_FREIGHTER]);
367
				foreach ($this->shipsSold as $shipID => $ship) {
368
					if ($ship->getClass() === ShipClass::Raider) {
369
						unset($this->shipsSold[$shipID]);
370
					}
371
				}
372
			}
373
		}
374
		return $this->shipsSold;
375
	}
376
377
	public function isShipSold(int $shipTypeID = null): bool {
378
		$ships = $this->getShipsSold();
379
		if ($shipTypeID === null) {
380
			return count($ships) != 0;
381
		}
382
		return isset($ships[$shipTypeID]);
383
	}
384
385
	public function addShipSold(int $shipTypeID): void {
386
		if ($this->isShipSold($shipTypeID)) {
387
			return;
388
		}
389
		$ship = SmrShipType::get($shipTypeID);
390
		$this->db->insert('location_sells_ships', [
391
			'location_type_id' => $this->db->escapeNumber($this->getTypeID()),
392
			'ship_type_id' => $this->db->escapeNumber($shipTypeID),
393
		]);
394
		$this->shipsSold[$shipTypeID] = $ship;
395
	}
396
397
	public function removeShipSold(int $shipTypeID): void {
398
		if (!$this->isShipSold($shipTypeID)) {
399
			return;
400
		}
401
		$this->db->write('DELETE FROM location_sells_ships WHERE ' . $this->SQL . ' AND ship_type_id = ' . $this->db->escapeNumber($shipTypeID));
402
		unset($this->shipsSold[$shipTypeID]);
403
	}
404
405
	/**
406
	 * @return array<int, SmrWeapon>
407
	 */
408
	public function getWeaponsSold(): array {
409
		if (!isset($this->weaponsSold)) {
410
			$this->weaponsSold = [];
411
			$dbResult = $this->db->read('SELECT * FROM location_sells_weapons JOIN weapon_type USING (weapon_type_id) WHERE ' . $this->SQL);
412
			foreach ($dbResult->records() as $dbRecord) {
413
				$weaponTypeID = $dbRecord->getInt('weapon_type_id');
414
				$this->weaponsSold[$weaponTypeID] = SmrWeapon::getWeapon($weaponTypeID, $dbRecord);
415
			}
416
		}
417
		return $this->weaponsSold;
418
	}
419
420
	public function isWeaponSold(int $weaponTypeID = null): bool {
421
		$weapons = $this->getWeaponsSold();
422
		if ($weaponTypeID === null) {
423
			return count($weapons) != 0;
424
		}
425
		return isset($weapons[$weaponTypeID]);
426
	}
427
428
	public function addWeaponSold(int $weaponTypeID): void {
429
		if ($this->isWeaponSold($weaponTypeID)) {
430
			return;
431
		}
432
		$weapon = SmrWeapon::getWeapon($weaponTypeID);
433
		$this->db->insert('location_sells_weapons', [
434
			'location_type_id' => $this->db->escapeNumber($this->getTypeID()),
435
			'weapon_type_id' => $this->db->escapeNumber($weaponTypeID),
436
		]);
437
		$this->weaponsSold[$weaponTypeID] = $weapon;
438
	}
439
440
	public function removeWeaponSold(int $weaponTypeID): void {
441
		if (!$this->isWeaponSold($weaponTypeID)) {
442
			return;
443
		}
444
		$this->db->write('DELETE FROM location_sells_weapons WHERE ' . $this->SQL . ' AND weapon_type_id = ' . $this->db->escapeNumber($weaponTypeID));
445
		unset($this->weaponsSold[$weaponTypeID]);
446
	}
447
448
	/**
449
	 * @return array<SmrLocation>
450
	 */
451
	public function getLinkedLocations(): array {
452
		$linkedLocations = [];
453
		if ($this->isHQ()) {
454
			if ($this->getTypeID() == LOCATION_TYPE_FEDERAL_HQ) {
455
				$linkedLocations[] = SmrLocation::getLocation($this->gameID, LOCATION_TYPE_FEDERAL_BEACON);
456
				$linkedLocations[] = SmrLocation::getLocation($this->gameID, LOCATION_TYPE_FEDERAL_MINT);
457
			} else {
458
				$raceID = $this->getRaceID();
459
				$linkedLocations[] = SmrLocation::getLocation($this->gameID, LOCATION_GROUP_RACIAL_BEACONS + $raceID);
460
				$linkedLocations[] = SmrLocation::getLocation($this->gameID, LOCATION_GROUP_RACIAL_SHIPS + $raceID);
461
				$linkedLocations[] = SmrLocation::getLocation($this->gameID, LOCATION_GROUP_RACIAL_SHOPS + $raceID);
462
			}
463
		}
464
		return $linkedLocations;
465
	}
466
467
	public function getExamineHREF(): string {
468
		$container = Page::create($this->getAction());
0 ignored issues
show
Bug introduced by
It seems like $this->getAction() can also be of type null; however, parameter $file of Page::create() does only seem to accept string, 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

468
		$container = Page::create(/** @scrutinizer ignore-type */ $this->getAction());
Loading history...
469
		$container['LocationID'] = $this->getTypeID();
470
		return $container->href();
471
	}
472
473
	public function getEditHREF(): string {
474
		$container = Page::create('location_edit.php');
475
		$container['location_type_id'] = $this->getTypeID();
476
		return $container->href();
477
	}
478
479
	public function equals(SmrLocation $otherLocation): bool {
480
		return $this->getTypeID() == $otherLocation->getTypeID();
481
	}
482
483
	public function hasX(mixed $x, AbstractSmrPlayer $player = null): bool {
484
		if ($x instanceof SmrWeaponType) {
485
			return $this->isWeaponSold($x->getWeaponTypeID());
486
		}
487
		if ($x instanceof SmrShipType) {
488
			return $this->isShipSold($x->getTypeID());
489
		}
490
		if (is_array($x) && $x['Type'] == 'Hardware') { // instanceof ShipEquipment)
491
			return $this->isHardwareSold($x['ID']);
492
		}
493
		if (is_string($x)) {
494
			if ($x == 'Bank') {
495
				return $this->isBank();
496
			}
497
			if ($x == 'Bar') {
498
				return $this->isBar();
499
			}
500
			if ($x == 'Fed') {
501
				return $this->isFed();
502
			}
503
			if ($x == 'SafeFed') {
504
				return $player != null && $this->isFed() && $player->canBeProtectedByRace($this->getRaceID());
505
			}
506
			if ($x == 'HQ') {
507
				return $this->isHQ();
508
			}
509
			if ($x == 'UG') {
510
				return $this->isUG();
511
			}
512
			if ($x == 'Hardware') {
513
				return $this->isHardwareSold();
514
			}
515
			if ($x == 'Ship') {
516
				return $this->isShipSold();
517
			}
518
			if ($x == 'Weapon') {
519
				return $this->isWeaponSold();
520
			}
521
		}
522
		return false;
523
	}
524
525
}
526