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 — master ( f174b5...646f17 )
by Dan
21s queued 18s
created

SmrSector::__construct()   A

Complexity

Conditions 5
Paths 9

Size

Total Lines 34
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

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

842
	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...
843
		// Whether bumping or attacking, only the current player fires at forces
844
		return array($attackingPlayer);
845
	}
846
847
	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

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