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
Push — main ( 2c9538...02418a )
by Dan
38s queued 18s
created

AbstractSmrLocation::getShipsSold()   B

Complexity

Conditions 7
Paths 3

Size

Total Lines 20
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 12
nc 3
nop 0
dl 0
loc 20
rs 8.8333
c 0
b 0
f 0
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