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

AbstractSmrLocation::setBank()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

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