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 ( 95b1d9...96af7f )
by Dan
21s queued 17s
created

SmrSector::__construct()   B

Complexity

Conditions 7
Paths 15

Size

Total Lines 33
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
cc 7
eloc 23
c 2
b 1
f 0
nc 15
nop 4
dl 0
loc 33
rs 8.6186
1
<?php declare(strict_types=1);
2
3
// Exception thrown when a sector cannot be found in the database
4
class SectorNotFoundException extends Exception {}
5
6
class SmrSector {
7
	protected static array $CACHE_SECTORS = [];
8
	protected static array $CACHE_GALAXY_SECTORS = [];
9
	protected static array $CACHE_LOCATION_SECTORS = [];
10
11
	protected Smr\Database $db;
12
	protected string $SQL;
13
14
	protected int $gameID;
15
	protected int $sectorID;
16
	protected int $battles;
17
	protected int $galaxyID;
18
	protected array $visited = [];
19
	protected array $links = [];
20
	protected int $warp;
21
22
	protected bool $hasChanged = false;
23
	protected bool $isNew = false;
24
25
	/**
26
	 * Maps the SmrSector link direction names to database columns.
27
	 */
28
	protected const LINK_DIR_MAPPING = [
29
		'Up' => 'link_up',
30
		'Down' => 'link_down',
31
		'Left' => 'link_left',
32
		'Right' => 'link_right',
33
	];
34
35
	/**
36
	 * Constructs the sector to determine if it exists.
37
	 * Returns a boolean value.
38
	 */
39
	public static function sectorExists(int $gameID, int $sectorID) : bool {
40
		try {
41
			self::getSector($gameID, $sectorID);
42
			return true;
43
		} catch (SectorNotFoundException $e) {
44
			return false;
45
		}
46
	}
47
48
	public static function getGalaxySectors(int $gameID, int $galaxyID, bool $forceUpdate = false) : array {
49
		if ($forceUpdate || !isset(self::$CACHE_GALAXY_SECTORS[$gameID][$galaxyID])) {
50
			$db = Smr\Database::getInstance();
51
			$dbResult = $db->read('SELECT * FROM sector WHERE game_id = ' . $db->escapeNumber($gameID) . ' AND galaxy_id=' . $db->escapeNumber($galaxyID) . ' ORDER BY sector_id ASC');
52
			$sectors = array();
53
			foreach ($dbResult->records() as $dbRecord) {
54
				$sectorID = $dbRecord->getInt('sector_id');
55
				$sectors[$sectorID] = self::getSector($gameID, $sectorID, $forceUpdate, $dbRecord);
56
			}
57
			self::$CACHE_GALAXY_SECTORS[$gameID][$galaxyID] = $sectors;
58
		}
59
		return self::$CACHE_GALAXY_SECTORS[$gameID][$galaxyID];
60
	}
61
62
	public static function getLocationSectors(int $gameID, int $locationTypeID, bool $forceUpdate = false) : array {
63
		if ($forceUpdate || !isset(self::$CACHE_LOCATION_SECTORS[$gameID][$locationTypeID])) {
64
			$db = Smr\Database::getInstance();
65
			$dbResult = $db->read('SELECT * FROM location JOIN sector USING (game_id, sector_id) WHERE location_type_id = ' . $db->escapeNumber($locationTypeID) . ' AND game_id=' . $db->escapeNumber($gameID) . ' ORDER BY sector_id ASC');
66
			$sectors = array();
67
			foreach ($dbResult->records() as $dbRecord) {
68
				$sectorID = $dbRecord->getInt('sector_id');
69
				$sectors[$sectorID] = self::getSector($gameID, $sectorID, $forceUpdate, $dbRecord);
70
			}
71
			self::$CACHE_LOCATION_SECTORS[$gameID][$locationTypeID] = $sectors;
72
		}
73
		return self::$CACHE_LOCATION_SECTORS[$gameID][$locationTypeID];
74
	}
75
76
	public static function getSector(int $gameID, int $sectorID, bool $forceUpdate = false, Smr\DatabaseRecord $dbRecord = null) : self {
77
		if (!isset(self::$CACHE_SECTORS[$gameID][$sectorID]) || $forceUpdate) {
78
			self::$CACHE_SECTORS[$gameID][$sectorID] = new SmrSector($gameID, $sectorID, false, $dbRecord);
79
		}
80
		return self::$CACHE_SECTORS[$gameID][$sectorID];
81
	}
82
83
	public static function clearCache() : void {
84
		self::$CACHE_LOCATION_SECTORS = array();
85
		self::$CACHE_GALAXY_SECTORS = array();
86
		self::$CACHE_SECTORS = array();
87
	}
88
89
	public static function saveSectors() : void {
90
		foreach (self::$CACHE_SECTORS as $gameSectors) {
91
			foreach ($gameSectors as $sector) {
92
				$sector->update();
93
			}
94
		}
95
	}
96
97
	public static function createSector(int $gameID, int $sectorID) : self {
98
		if (!isset(self::$CACHE_SECTORS[$gameID][$sectorID])) {
99
			$s = new SmrSector($gameID, $sectorID, true);
100
			self::$CACHE_SECTORS[$gameID][$sectorID] = $s;
101
		}
102
		return self::$CACHE_SECTORS[$gameID][$sectorID];
103
	}
104
105
	protected function __construct(int $gameID, int $sectorID, bool $create = false, Smr\DatabaseRecord $dbRecord = null) {
106
		$this->db = Smr\Database::getInstance();
107
		$this->SQL = 'game_id = ' . $this->db->escapeNumber($gameID) . ' AND sector_id = ' . $this->db->escapeNumber($sectorID);
108
109
		// Do we already have a database record for this sector?
110
		if ($dbRecord === null) {
111
			$dbResult = $this->db->read('SELECT * FROM sector WHERE ' . $this->SQL . ' LIMIT 1');
112
			if ($dbResult->hasRecord()) {
113
				$dbRecord = $dbResult->record();
114
			}
115
		}
116
		$sectorExists = $dbRecord !== null;
117
118
		$this->gameID = $gameID;
119
		$this->sectorID = $sectorID;
120
121
		if ($sectorExists) {
122
			$this->galaxyID = $dbRecord->getInt('galaxy_id');
123
			$this->battles = $dbRecord->getInt('battles');
124
125
			foreach (self::LINK_DIR_MAPPING as $dir => $dbColumn) {
126
				$link = $dbRecord->getInt($dbColumn);
127
				if ($link !== 0) {
128
					$this->links[$dir] = $link;
129
				}
130
			}
131
			$this->warp = $dbRecord->getInt('warp');
132
		} elseif ($create) {
133
			$this->battles = 0;
134
			$this->warp = 0;
135
			$this->isNew = true;
136
		} else {
137
			throw new SectorNotFoundException('No sector ' . $sectorID . ' in game ' . $gameID);
138
		}
139
	}
140
141
	public function update() : void {
142
		if ($this->isNew) {
143
			$this->db->write('INSERT INTO sector(sector_id,game_id,galaxy_id,link_up,link_down,link_left,link_right,warp)
144
								values
145
								(' . $this->db->escapeNumber($this->getSectorID()) .
146
								',' . $this->db->escapeNumber($this->getGameID()) .
147
								',' . $this->db->escapeNumber($this->getGalaxyID()) .
148
								',' . $this->db->escapeNumber($this->getLinkUp()) .
149
								',' . $this->db->escapeNumber($this->getLinkDown()) .
150
								',' . $this->db->escapeNumber($this->getLinkLeft()) .
151
								',' . $this->db->escapeNumber($this->getLinkRight()) .
152
								',' . $this->db->escapeNumber($this->getWarp()) .
153
								')');
154
		} elseif ($this->hasChanged) {
155
			$this->db->write('UPDATE sector SET battles = ' . $this->db->escapeNumber($this->getBattles()) .
156
									', galaxy_id=' . $this->db->escapeNumber($this->getGalaxyID()) .
157
									', link_up=' . $this->db->escapeNumber($this->getLinkUp()) .
158
									', link_right=' . $this->db->escapeNumber($this->getLinkRight()) .
159
									', link_down=' . $this->db->escapeNumber($this->getLinkDown()) .
160
									', link_left=' . $this->db->escapeNumber($this->getLinkLeft()) .
161
									', warp=' . $this->db->escapeNumber($this->getWarp()) .
162
								' WHERE ' . $this->SQL . ' LIMIT 1');
163
		}
164
		$this->isNew = false;
165
		$this->hasChanged = false;
166
	}
167
168
	public function markVisited(AbstractSmrPlayer $player) : void {
169
		if ($this->hasPort()) {
170
			$this->getPort()->addCachePort($player->getAccountID());
171
		}
172
173
		//now delete the entry from visited
174
		if (!$this->isVisited($player)) {
175
			$this->db->write('DELETE FROM player_visited_sector WHERE ' . $this->SQL . '
176
								 AND account_id = ' . $this->db->escapeNumber($player->getAccountID()) . ' LIMIT 1');
177
		}
178
		$this->visited[$player->getAccountID()] = true;
179
	}
180
181
	public function hasWeaponShop() : bool {
182
		foreach ($this->getLocations() as $location) {
183
			if ($location->isWeaponSold()) {
184
				return true;
185
			}
186
		}
187
		return false;
188
	}
189
190
	public function hasHQ() : bool {
191
		foreach ($this->getLocations() as $location) {
192
			if ($location->isHQ()) {
193
				return true;
194
			}
195
		}
196
		return false;
197
	}
198
199
	public function hasUG() : bool {
200
		foreach ($this->getLocations() as $location) {
201
			if ($location->isUG()) {
202
				return true;
203
			}
204
		}
205
		return false;
206
	}
207
208
	public function hasShipShop() : bool {
209
		foreach ($this->getLocations() as $location) {
210
			if ($location->isShipSold()) {
211
				return true;
212
			}
213
		}
214
		return false;
215
	}
216
217
	public function offersFederalProtection() : bool {
218
		foreach ($this->getLocations() as $location) {
219
			if ($location->isFed()) {
220
				return true;
221
			}
222
		}
223
		return false;
224
	}
225
226
	public function getFedRaceIDs() : array {
227
		$raceIDs = array();
228
		foreach ($this->getLocations() as $location) {
229
			if ($location->isFed()) {
230
				$raceIDs[$location->getRaceID()] = $location->getRaceID();
231
			}
232
		}
233
		return $raceIDs;
234
	}
235
236
	public function hasBar() : bool {
237
		foreach ($this->getLocations() as $location) {
238
			if ($location->isBar()) {
239
				return true;
240
			}
241
		}
242
		return false;
243
	}
244
245
	public function hasHardwareShop() : bool {
246
		foreach ($this->getLocations() as $location) {
247
			if ($location->isHardwareSold()) {
248
				return true;
249
			}
250
		}
251
		return false;
252
	}
253
254
	public function hasBank() : bool {
255
		foreach ($this->getLocations() as $location) {
256
			if ($location->isBank()) {
257
				return true;
258
			}
259
		}
260
		return false;
261
	}
262
263
	public function enteringSector(AbstractSmrPlayer $player, int $movementType) : void {
264
		// send scout messages to user
265
		$message = 'Your forces have spotted ' . $player->getBBLink() . ' ';
266
		$message .= match($movementType) {
267
			MOVEMENT_JUMP => 'jumping into',
268
			MOVEMENT_WARP => 'warping into',
269
			MOVEMENT_WALK => 'entering',
270
		};
271
		$message .= ' sector ' . Globals::getSectorBBLink($this->getSectorID());
272
273
		$forces = $this->getForces();
274
		foreach ($forces as $force) {
275
			$force->ping($message, $player);
276
		}
277
	}
278
279
	public function leavingSector(AbstractSmrPlayer $player, int $movementType) : void {
280
		// send scout messages to user
281
		$message = 'Your forces have spotted ' . $player->getBBLink() . ' ';
282
		$message .= match($movementType) {
283
			MOVEMENT_JUMP => 'jumping from',
284
			MOVEMENT_WARP => 'warping from',
285
			MOVEMENT_WALK => 'leaving',
286
		};
287
		$message .= ' sector ' . Globals::getSectorBBLink($this->getSectorID());
288
289
		// iterate over all scout drones in sector
290
		foreach ($this->getForces() as $force) {
291
			$force->ping($message, $player);
292
		}
293
		$this->db->write('UPDATE sector_has_forces SET refresher = 0 WHERE ' . $this->SQL . '
294
								AND refresher = ' . $this->db->escapeNumber($player->getAccountID()));
295
	}
296
297
	public function diedHere(AbstractSmrPlayer $player) : void {
298
		// iterate over all scout drones in sector
299
		foreach ($this->getForces() as $force) {
300
			// send scout messages to user
301
			$message = 'Your forces have spotted that ' . $player->getBBLink() . ' has been <span class="red">DESTROYED</span> in sector ' . Globals::getSectorBBLink($this->sectorID);
302
			$force->ping($message, $player);
303
		}
304
	}
305
306
	public function getSQL() : string {
307
		return $this->SQL;
308
	}
309
310
	public function getGameID() : int {
311
		return $this->gameID;
312
	}
313
314
	public function getSectorID() : int {
315
		return $this->sectorID;
316
	}
317
318
	public function getGalaxyID() : int {
319
		return $this->galaxyID;
320
	}
321
322
	public function setGalaxyID(int $galaxyID) : void {
323
		if (isset($this->galaxyID) && $this->galaxyID == $galaxyID) {
324
			return;
325
		}
326
		$this->galaxyID = $galaxyID;
327
		$this->hasChanged = true;
328
	}
329
330
	/**
331
	 * Returns the number of linked sectors (excluding warps)
332
	 */
333
	public function getNumberOfLinks() : int {
334
		return count($this->links);
335
	}
336
337
	/**
338
	 * Returns the number of linked sectors (including warps)
339
	 */
340
	public function getNumberOfConnections() : int {
341
		$links = $this->getNumberOfLinks();
342
		if ($this->hasWarp()) {
343
			$links++;
344
		}
345
		return $links;
346
	}
347
348
	public function getGalaxy() : SmrGalaxy {
349
		return SmrGalaxy::getGalaxy($this->getGameID(), $this->getGalaxyID());
350
	}
351
352
	public function getNeighbourID(string $dir) : int {
353
		if ($this->hasLink($dir)) {
354
			return $this->getLink($dir);
355
		}
356
		$galaxy = $this->getGalaxy();
357
		$neighbour = $this->getSectorID();
358
		switch ($dir) {
359
			case 'Up':
360
				$neighbour -= $galaxy->getWidth();
361
				if ($neighbour < $galaxy->getStartSector()) {
362
					$neighbour += $galaxy->getSize();
363
				}
364
			break;
365
			case 'Down':
366
				$neighbour += $galaxy->getWidth();
367
				if ($neighbour > $galaxy->getEndSector()) {
368
					$neighbour -= $galaxy->getSize();
369
				}
370
			break;
371
			case 'Left':
372
				$neighbour -= 1;
373
				if ((1 + $neighbour - $galaxy->getStartSector()) % $galaxy->getWidth() == 0) {
374
					$neighbour += $galaxy->getWidth();
375
				}
376
			break;
377
			case 'Right':
378
				$neighbour += 1;
379
				if (($neighbour - $galaxy->getStartSector()) % $galaxy->getWidth() == 0) {
380
					$neighbour -= $galaxy->getWidth();
381
				}
382
			break;
383
			default:
384
				throw new Exception($dir . ': is not a valid direction');
385
		}
386
		return $neighbour;
387
	}
388
389
	public function getSectorDirection(int $sectorID) : string {
390
		if ($sectorID == $this->getSectorID()) {
391
			return 'Current';
392
		}
393
		$dir = array_search($sectorID, $this->getLinks());
394
		if ($dir !== false) {
395
			return $dir;
396
		}
397
		if ($sectorID == $this->getWarp()) {
398
			return 'Warp';
399
		}
400
		return 'None';
401
	}
402
403
	public function getNeighbourSector(string $dir) : self {
404
		return SmrSector::getSector($this->getGameID(), $this->getNeighbourID($dir));
405
	}
406
407
	public function getLinks() : array {
408
		return $this->links;
409
	}
410
411
	public function isLinked(int $sectorID) : bool {
412
		return in_array($sectorID, $this->links) || $sectorID == $this->getWarp();
413
	}
414
415
	public function getLink(string $name) : int {
416
		return $this->links[$name] ?? 0;
417
	}
418
419
	public function hasLink(string $name) : bool {
420
		return $this->getLink($name) != 0;
421
	}
422
423
	public function getLinkSector(string $name) : self|false {
424
		if ($this->hasLink($name)) {
425
			return SmrSector::getSector($this->getGameID(), $this->getLink($name));
426
		}
427
		return false;
428
	}
429
430
	/**
431
	 * Cannot be used for Warps
432
	 */
433
	public function setLink(string $name, int $linkID) : void {
434
		if ($this->getLink($name) == $linkID) {
435
			return;
436
		}
437
		if ($linkID == $this->sectorID) {
438
			throw new Exception('Sector must not link to itself!');
439
		}
440
		if ($linkID == 0) {
441
			unset($this->links[$name]);
442
		} else {
443
			$this->links[$name] = $linkID;
444
		}
445
		$this->hasChanged = true;
446
	}
447
448
	/**
449
	 * Cannot be used for Warps
450
	 */
451
	public function setLinkSector(string $dir, SmrSector $linkSector) : void {
452
		$this->setLink($dir, $linkSector->getSectorID());
453
		$linkSector->setLink(self::oppositeDir($dir), $this->getSectorID());
454
	}
455
456
	/**
457
	 * Cannot be used for Warps
458
	 */
459
	public function enableLink(string $dir) : void {
460
		$this->setLinkSector($dir, $this->getNeighbourSector($dir));
461
	}
462
463
	/**
464
	 * Cannot be used for Warps
465
	 */
466
	public function disableLink(string $dir) : void {
467
		$this->setLink($dir, 0);
468
		$this->getNeighbourSector($dir)->setLink(self::oppositeDir($dir), 0);
469
	}
470
471
	/**
472
	 * Cannot be used for Warps
473
	 */
474
	public function toggleLink(string $dir) : void {
475
		if ($this->hasLink($dir)) {
476
			$this->disableLink($dir);
477
		} else {
478
			$this->enableLink($dir);
479
		}
480
	}
481
482
	public static function oppositeDir(string $dir) : string {
483
		return match($dir) {
484
			'Up' => 'Down',
485
			'Down' => 'Up',
486
			'Left' => 'Right',
487
			'Right' => 'Left',
488
			'Warp' => 'Warp',
489
		};
490
	}
491
492
	public function getLinkUp() : int {
493
		return $this->getLink('Up');
494
	}
495
496
	public function setLinkUp(int $linkID) : void {
497
		$this->setLink('Up', $linkID);
498
	}
499
500
	public function hasLinkUp() : bool {
501
		return $this->hasLink('Up');
502
	}
503
504
	public function getLinkDown() : int {
505
		return $this->getLink('Down');
506
	}
507
508
	public function setLinkDown(int $linkID) : void {
509
		$this->setLink('Down', $linkID);
510
	}
511
512
	public function hasLinkDown() : bool {
513
		return $this->hasLink('Down');
514
	}
515
516
	public function getLinkLeft() : int {
517
		return $this->getLink('Left');
518
	}
519
520
	public function hasLinkLeft() : bool {
521
		return $this->hasLink('Left');
522
	}
523
524
	public function setLinkLeft(int $linkID) : void {
525
		$this->setLink('Left', $linkID);
526
	}
527
528
	public function getLinkRight() : int {
529
		return $this->getLink('Right');
530
	}
531
532
	public function hasLinkRight() : bool {
533
		return $this->hasLink('Right');
534
	}
535
536
	public function setLinkRight(int $linkID) : void {
537
		$this->setLink('Right', $linkID);
538
	}
539
540
	/**
541
	 * Returns the warp sector if the sector has a warp; returns 0 otherwise.
542
	 */
543
	public function getWarp() : int {
544
		return $this->warp;
545
	}
546
547
	public function getWarpSector() : self {
548
		return SmrSector::getSector($this->getGameID(), $this->getWarp());
549
	}
550
551
	public function hasWarp() : bool {
552
		return $this->getWarp() != 0;
553
	}
554
555
	/**
556
	 * Set the warp sector for both $this and $warp to ensure
557
	 * a consistent 2-way warp.
558
	 */
559
	public function setWarp(SmrSector $warp) : void {
560
		if ($this->getWarp() == $warp->getSectorID() &&
561
		    $warp->getWarp() == $this->getSectorID()) {
562
			// Warps are already set correctly!
563
			return;
564
		}
565
566
		if ($this->equals($warp)) {
567
			throw new Exception('Sector must not warp to itself!');
568
		}
569
570
		// Can only have 1 warp per sector
571
		foreach ([[$warp, $this], [$this, $warp]] as list($A, $B)) {
572
			if ($A->hasWarp() && $A->getWarp() != $B->getSectorID()) {
573
				throw new Exception('Sector ' . $A->getSectorID() . ' already has a warp (to ' . $A->getWarp() . ')!');
574
			}
575
		}
576
577
		$this->warp = $warp->getSectorID();
578
		$this->hasChanged = true;
579
580
		if ($warp->getWarp() != $this->getSectorID()) {
581
			// Set the other side if needed
582
			$warp->setWarp($this);
583
		}
584
	}
585
586
	/**
587
	 * Remove the warp sector for both sides of the warp.
588
	 */
589
	public function removeWarp() : void {
590
		if (!$this->hasWarp()) {
591
			return;
592
		}
593
594
		$warp = $this->getWarpSector();
595
		if ($warp->hasWarp() && $warp->getWarp() != $this->getSectorID()) {
596
			throw new Exception('Warp sectors do not match');
597
		}
598
599
		$this->warp = 0;
600
		$this->hasChanged = true;
601
602
		if ($warp->hasWarp()) {
603
			$warp->removeWarp();
604
		}
605
	}
606
607
	public function hasPort() : bool {
608
		return $this->getPort()->exists();
609
	}
610
611
	public function getPort() : SmrPort {
612
		return SmrPort::getPort($this->getGameID(), $this->getSectorID());
613
	}
614
615
	public function createPort() : SmrPort {
616
		return SmrPort::createPort($this->getGameID(), $this->getSectorID());
617
	}
618
619
	public function removePort() : void {
620
		SmrPort::removePort($this->getGameID(), $this->getSectorID());
621
	}
622
623
	public function hasCachedPort(AbstractSmrPlayer $player = null) : bool {
624
		return $this->getCachedPort($player) !== false;
625
	}
626
627
	public function getCachedPort(AbstractSmrPlayer $player = null) : SmrPort|false {
628
		if ($player === null) {
629
			return false;
630
		}
631
		return SmrPort::getCachedPort($this->getGameID(), $this->getSectorID(), $player->getAccountID());
632
	}
633
634
	public function hasAnyLocationsWithAction() : bool {
635
		$locations = SmrLocation::getSectorLocations($this->getGameID(), $this->getSectorID());
636
		$hasAction = false;
637
		foreach ($locations as $location) {
638
			if ($location->hasAction()) {
639
				$hasAction = true;
640
			}
641
		}
642
		return $hasAction;
643
	}
644
645
	public function hasLocation(int $locationTypeID = null) : bool {
646
		$locations = $this->getLocations();
647
		if (count($locations) == 0) {
648
			return false;
649
		}
650
		if ($locationTypeID === null) {
651
			return true;
652
		}
653
		foreach ($locations as $location) {
654
			if ($location->getTypeID() == $locationTypeID) {
655
				return true;
656
			}
657
		}
658
		return false;
659
	}
660
661
	public function getLocations() : array {
662
		return SmrLocation::getSectorLocations($this->getGameID(), $this->getSectorID());
663
	}
664
665
	public function addLocation(SmrLocation $location) : void {
666
		SmrLocation::addSectorLocation($this->getGameID(), $this->getSectorID(), $location);
667
	}
668
669
	public function removeAllLocations() : void {
670
		SmrLocation::removeSectorLocations($this->getGameID(), $this->getSectorID());
671
	}
672
673
	public function hasPlanet() : bool {
674
		return $this->getPlanet()->exists();
675
	}
676
677
	public function getPlanet() : SmrPlanet {
678
		return SmrPlanet::getPlanet($this->getGameID(), $this->getSectorID());
679
	}
680
681
	public function createPlanet(int $type = 1) : SmrPlanet {
682
		return SmrPlanet::createPlanet($this->getGameID(), $this->getSectorID(), $type);
683
	}
684
685
	public function removePlanet() : void {
686
		SmrPlanet::removePlanet($this->getGameID(), $this->getSectorID());
687
	}
688
689
	/**
690
	 * Removes ports, planets, locations, and warps from this sector.
691
	 * NOTE: This should only be used by the universe generator!
692
	 */
693
	public function removeAllFixtures() : void {
694
		if ($this->hasPort()) {
695
			$this->removePort();
696
		}
697
		if ($this->hasPlanet()) {
698
			$this->removePlanet();
699
		}
700
		if ($this->hasLocation()) {
701
			$this->removeAllLocations();
702
		}
703
		if ($this->hasWarp()) {
704
			$this->removeWarp();
705
		}
706
	}
707
708
	public function hasForces() : bool {
709
		return count($this->getForces()) > 0;
710
	}
711
712
	public function hasEnemyForces(AbstractSmrPlayer $player = null) : bool {
713
		if ($player == null || !$this->hasForces()) {
714
			return false;
715
		}
716
		foreach ($this->getForces() as $force) {
717
			if (!$player->forceNAPAlliance($force->getOwner())) {
718
				return true;
719
			}
720
		}
721
		return false;
722
	}
723
724
	public function getEnemyForces(AbstractSmrPlayer $player) : array {
725
		$enemyForces = array();
726
		foreach ($this->getForces() as $force) {
727
			if (!$player->forceNAPAlliance($force->getOwner())) {
728
				$enemyForces[] = $force;
729
			}
730
		}
731
		return $enemyForces;
732
	}
733
734
	/**
735
	 * Returns true if any forces in this sector belong to $player.
736
	 */
737
	public function hasPlayerForces(AbstractSmrPlayer $player) : bool {
738
		foreach ($this->getForces() as $force) {
739
			if ($player->getAccountID() == $force->getOwnerID()) {
740
				return true;
741
			}
742
		}
743
		return false;
744
	}
745
746
	public function hasFriendlyForces(AbstractSmrPlayer $player = null) : bool {
747
		if ($player == null || !$this->hasForces()) {
748
			return false;
749
		}
750
		foreach ($this->getForces() as $force) {
751
			if ($player->forceNAPAlliance($force->getOwner())) {
752
				return true;
753
			}
754
		}
755
		return false;
756
	}
757
758
	public function getFriendlyForces(AbstractSmrPlayer $player) : array {
759
		$friendlyForces = array();
760
		foreach ($this->getForces() as $force) {
761
			if ($player->forceNAPAlliance($force->getOwner())) {
762
				$friendlyForces[] = $force;
763
			}
764
		}
765
		return $friendlyForces;
766
	}
767
768
	public function getForces() : array {
769
		return SmrForce::getSectorForces($this->getGameID(), $this->getSectorID());
770
	}
771
772
	public function getPlayers() : array {
773
		return SmrPlayer::getSectorPlayers($this->getGameID(), $this->getSectorID());
774
	}
775
776
	public function hasPlayers() : bool {
777
		return count($this->getPlayers()) > 0;
778
	}
779
780
	public function getOtherTraders(AbstractSmrPlayer $player) : array {
781
		$players = SmrPlayer::getSectorPlayers($this->getGameID(), $this->getSectorID()); //Do not use & because we unset something and only want that in what we return
782
		unset($players[$player->getAccountID()]);
783
		return $players;
784
	}
785
786
	public function hasOtherTraders(AbstractSmrPlayer $player) : bool {
787
		return count($this->getOtherTraders($player)) > 0;
788
	}
789
790
	public function hasEnemyTraders(AbstractSmrPlayer $player = null) : bool {
791
		if ($player == null || !$this->hasOtherTraders($player)) {
792
			return false;
793
		}
794
		$otherPlayers = $this->getOtherTraders($player);
795
		foreach ($otherPlayers as $otherPlayer) {
796
			if (!$player->traderNAPAlliance($otherPlayer)
797
				&& !$otherPlayer->hasNewbieTurns()
798
				&& !$otherPlayer->hasFederalProtection()) {
799
				return true;
800
			}
801
		}
802
		return false;
803
	}
804
805
	public function hasFriendlyTraders(AbstractSmrPlayer $player = null) : bool {
806
		if ($player == null || !$this->hasOtherTraders($player)) {
807
			return false;
808
		}
809
		$otherPlayers = $this->getOtherTraders($player);
810
		foreach ($otherPlayers as $otherPlayer) {
811
			if ($player->traderNAPAlliance($otherPlayer)) {
812
				return true;
813
			}
814
		}
815
		return false;
816
	}
817
818
	/**
819
	 * Is the $player's alliance flagship in this sector?
820
	 */
821
	public function hasAllianceFlagship(AbstractSmrPlayer $player = null) : bool {
822
		if (is_null($player) || !$player->hasAlliance() || !$player->getAlliance()->hasFlagship()) {
823
			return false;
824
		}
825
		$flagshipID = $player->getAlliance()->getFlagshipID();
826
		foreach ($this->getPlayers() as $sectorPlayer) {
827
			if ($sectorPlayer->getAccountID() == $flagshipID) {
828
				return true;
829
			}
830
		}
831
		return false;
832
	}
833
834
	public function hasProtectedTraders(AbstractSmrPlayer $player = null) : bool {
835
		if ($player == null || !$this->hasOtherTraders($player)) {
836
			return false;
837
		}
838
		$otherPlayers = $this->getOtherTraders($player);
839
		foreach ($otherPlayers as $otherPlayer) {
840
			if (!$player->traderNAPAlliance($otherPlayer)
841
				&& ($otherPlayer->hasNewbieTurns() || $otherPlayer->hasFederalProtection())) {
842
				return true;
843
			}
844
		}
845
		return false;
846
	}
847
848
	public function getFightingTradersAgainstForces(AbstractSmrPlayer $attackingPlayer, bool $bump) : array {
0 ignored issues
show
Unused Code introduced by
The parameter $bump 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

848
	public function getFightingTradersAgainstForces(AbstractSmrPlayer $attackingPlayer, /** @scrutinizer ignore-unused */ bool $bump) : 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...
849
		// Whether bumping or attacking, only the current player fires at forces
850
		return array($attackingPlayer);
851
	}
852
853
	public function getFightingTradersAgainstPort(AbstractSmrPlayer $attackingPlayer, SmrPort $defendingPort) : array {
0 ignored issues
show
Unused Code introduced by
The parameter $defendingPort 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

853
	public function getFightingTradersAgainstPort(AbstractSmrPlayer $attackingPlayer, /** @scrutinizer ignore-unused */ SmrPort $defendingPort) : 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...
854
		$fightingPlayers = array();
855
		$alliancePlayers = SmrPlayer::getSectorPlayersByAlliances($this->getGameID(), $this->getSectorID(), array($attackingPlayer->getAllianceID()));
856
		foreach ($alliancePlayers as $accountID => $player) {
857
			if ($player->canFight()) {
858
				if ($attackingPlayer->traderAttackPortAlliance($player)) {
859
					$fightingPlayers[$accountID] = $alliancePlayers[$accountID];
860
				}
861
			}
862
		}
863
		return self::limitFightingTraders($fightingPlayers, $attackingPlayer, MAXIMUM_PORT_FLEET_SIZE);
864
	}
865
866
	public function getFightingTradersAgainstPlanet(AbstractSmrPlayer $attackingPlayer, SmrPlanet $defendingPlanet) : array {
867
		$fightingPlayers = array();
868
		$alliancePlayers = SmrPlayer::getSectorPlayersByAlliances($this->getGameID(), $this->getSectorID(), array($attackingPlayer->getAllianceID()));
869
		if (count($alliancePlayers) > 0) {
870
			$planetOwner = $defendingPlanet->getOwner();
871
			foreach ($alliancePlayers as $accountID => $player) {
872
				if ($player->canFight()) {
873
					if ($attackingPlayer->traderAttackPlanetAlliance($player) && !$planetOwner->planetNAPAlliance($player)) {
874
						$fightingPlayers[$accountID] = $alliancePlayers[$accountID];
875
					}
876
				}
877
			}
878
		}
879
		return self::limitFightingTraders($fightingPlayers, $attackingPlayer, min($defendingPlanet->getMaxAttackers(), MAXIMUM_PLANET_FLEET_SIZE));
880
	}
881
882
	public function getFightingTraders(AbstractSmrPlayer $attackingPlayer, AbstractSmrPlayer $defendingPlayer, bool $checkForCloak = false) : array {
883
		if ($attackingPlayer->traderNAPAlliance($defendingPlayer)) {
884
			throw new Exception('These traders are NAPed.');
885
		}
886
		$fightingPlayers = array('Attackers' => array(), 'Defenders' => array());
887
		$alliancePlayers = SmrPlayer::getSectorPlayersByAlliances($this->getGameID(), $this->getSectorID(), array($attackingPlayer->getAllianceID(), $defendingPlayer->getAllianceID()));
888
		$attackers = array();
889
		$defenders = array();
890
		foreach ($alliancePlayers as $accountID => $player) {
891
			if ($player->canFight()) {
892
				if ($attackingPlayer->traderAttackTraderAlliance($player) && !$defendingPlayer->traderDefendTraderAlliance($player) && !$defendingPlayer->traderNAPAlliance($player)) {
893
					$attackers[] = $alliancePlayers[$accountID];
894
				} elseif ($defendingPlayer->traderDefendTraderAlliance($player) && !$attackingPlayer->traderAttackTraderAlliance($player) && !$attackingPlayer->traderNAPAlliance($player) && ($checkForCloak === false || $attackingPlayer->canSee($player))) {
895
					$defenders[] = $alliancePlayers[$accountID];
896
				}
897
			}
898
		}
899
		$attackers = self::limitFightingTraders($attackers, $attackingPlayer, MAXIMUM_PVP_FLEET_SIZE);
900
		shuffle($attackers);
901
		foreach ($attackers as $attacker) {
902
			$fightingPlayers['Attackers'][$attacker->getAccountID()] = $attacker;
903
		}
904
		$defenders = self::limitFightingTraders($defenders, $defendingPlayer, MAXIMUM_PVP_FLEET_SIZE);
905
		shuffle($defenders);
906
		foreach ($defenders as $defender) {
907
			$fightingPlayers['Defenders'][$defender->getAccountID()] = $defender;
908
		}
909
		return $fightingPlayers;
910
	}
911
912
	public static function limitFightingTraders(array $fightingPlayers, AbstractSmrPlayer $keepPlayer, int $maximumFleetSize) : array {
913
		// Cap fleets to the required size
914
		$fleet_size = count($fightingPlayers);
915
		if ($fleet_size > $maximumFleetSize) {
916
			// We use random key to stop the same people being capped all the time
917
			for ($j = 0; $j < $fleet_size - $maximumFleetSize; ++$j) {
918
				do {
919
					$key = array_rand($fightingPlayers);
920
				} while ($keepPlayer->equals($fightingPlayers[$key]));
921
				unset($fightingPlayers[$key]);
922
			}
923
		}
924
		return $fightingPlayers;
925
	}
926
927
	public function getPotentialFightingTraders(AbstractSmrPlayer $attackingPlayer) : array {
928
		$fightingPlayers = array();
929
		$alliancePlayers = SmrPlayer::getSectorPlayersByAlliances($this->getGameID(), $this->getSectorID(), array($attackingPlayer->getAllianceID()));
930
		foreach ($alliancePlayers as $accountID => $player) {
931
			if ($player->canFight()) {
932
				if ($attackingPlayer->traderAttackTraderAlliance($player)) {
933
					$fightingPlayers['Attackers'][$accountID] = $player;
934
				}
935
			}
936
		}
937
		return $fightingPlayers;
938
	}
939
940
	public function getBattles() : int {
941
		return $this->battles;
942
	}
943
944
	public function setBattles(int $amount) : void {
945
		if ($this->battles == $amount) {
946
			return;
947
		}
948
		$this->battles = $amount;
949
		$this->hasChanged = true;
950
	}
951
952
	public function decreaseBattles(int $amount) : void {
953
		$this->setBattles($this->battles - $amount);
954
	}
955
956
	public function increaseBattles(int $amount) : void {
957
		$this->setBattles($this->battles + $amount);
958
	}
959
960
	public function equals(SmrSector $otherSector) : bool {
961
		return $otherSector->getSectorID() == $this->getSectorID() && $otherSector->getGameID() == $this->getGameID();
962
	}
963
964
	public function isLinkedSector(SmrSector $otherSector) : bool {
965
		return $otherSector->getGameID() == $this->getGameID() && $this->isLinked($otherSector->getSectorID());
966
	}
967
968
	public function isVisited(AbstractSmrPlayer $player = null) : bool {
969
		if ($player === null) {
970
			return true;
971
		}
972
		if (!isset($this->visited[$player->getAccountID()])) {
973
			$dbResult = $this->db->read('SELECT 1 FROM player_visited_sector WHERE ' . $this->SQL . ' AND account_id=' . $this->db->escapeNumber($player->getAccountID()) . ' LIMIT 1');
974
			$this->visited[$player->getAccountID()] = !$dbResult->hasRecord();
975
		}
976
		return $this->visited[$player->getAccountID()];
977
	}
978
979
	public function getLocalMapMoveHREF(AbstractSmrPlayer $player) : string {
980
		return Globals::getSectorMoveHREF($player, $this->getSectorID(), 'map_local.php');
981
	}
982
983
	public function getCurrentSectorMoveHREF(AbstractSmrPlayer $player) : string {
984
		return Globals::getCurrentSectorMoveHREF($player, $this->getSectorID());
985
	}
986
987
	public function getGalaxyMapHREF() : string {
988
		return '?sector_id=' . $this->getSectorID();
989
	}
990
991
	public function getSectorScanHREF(AbstractSmrPlayer $player) : string {
992
		return Globals::getSectorScanHREF($player, $this->getSectorID());
993
	}
994
995
	public function hasX(mixed $x, AbstractSmrPlayer $player = null) : bool {
996
		if ($x instanceof SmrSector) {
997
			return $this->equals($x);
998
		}
999
		if ($x == 'Port') {
1000
			return $this->hasPort();
1001
		}
1002
		if ($x == 'Location') {
1003
			return $this->hasLocation();
1004
		}
1005
		if ($x instanceof SmrLocation) {
1006
			return $this->hasLocation($x->getTypeID());
1007
		}
1008
		if ($x instanceof SmrGalaxy) {
1009
			return $x->contains($this);
1010
		}
1011
1012
		if (is_array($x) && $x['Type'] == 'Good') { //Check if it's possible for port to have X, hacky but nice performance gains
1013
			if ($this->hasPort() && $this->getPort()->hasX($x)) {
1014
				return true;
1015
			}
1016
		}
1017
1018
		//Check if it's possible for location to have X, hacky but nice performance gains
1019
		if ($x instanceof SmrWeaponType || $x instanceof SmrShipType || (is_array($x) && $x['Type'] == 'Hardware') || (is_string($x) && ($x == 'Bank' || $x == 'Bar' || $x == 'Fed' || $x == 'SafeFed' || $x == 'HQ' || $x == 'UG' || $x == 'Hardware' || $x == 'Ship' || $x == 'Weapon'))) {
1020
			foreach ($this->getLocations() as $loc) {
1021
				if ($loc->hasX($x, $player)) {
1022
					return true;
1023
				}
1024
			}
1025
		}
1026
		return false;
1027
	}
1028
}
1029