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

Passed
Pull Request — master (#835)
by Dan
04:10
created

SmrPlayer::doMessageSending()   B

Complexity

Conditions 7
Paths 8

Size

Total Lines 43
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 32
dl 0
loc 43
rs 8.4746
c 0
b 0
f 0
cc 7
nc 8
nop 8

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php declare(strict_types=1);
2
3
// Exception thrown when a player cannot be found in the database
4
class PlayerNotFoundException extends Exception {}
5
6
class SmrPlayer extends AbstractSmrPlayer {
7
	const TIME_FOR_FEDERAL_BOUNTY_ON_PR = 10800;
8
	const TIME_FOR_ALLIANCE_SWITCH = 0;
9
	protected static $CACHE_SECTOR_PLAYERS = array();
10
	protected static $CACHE_PLANET_PLAYERS = array();
11
	protected static $CACHE_ALLIANCE_PLAYERS = array();
12
	protected static $CACHE_PLAYERS = array();
13
14
	protected $db;
15
	protected $SQL;
16
17
	protected $newbieWarning;
18
	protected $tickers;
19
	protected $lastTurnUpdate;
20
	protected $lastNewsUpdate;
21
	protected $attackColour;
22
//	protected $pastKnowledge;
23
	protected $allianceJoinable;
24
	protected $lastPort;
25
	protected $bank;
26
	protected $zoom;
27
	protected $displayMissions;
28
	protected $displayWeapons;
29
	protected $ignoreGlobals;
30
	protected $plottedCourse;
31
	protected $plottedCourseFrom;
32
	protected $nameChanged;
33
	protected $combatDronesKamikazeOnMines;
34
	protected $customShipName;
35
	protected $storedDestinations;
36
37
38
	public static function refreshCache() {
39
		foreach (self::$CACHE_PLAYERS as $gameID => &$gamePlayers) {
40
			foreach ($gamePlayers as $accountID => &$player) {
41
				$player = self::getPlayer($accountID, $gameID, true);
42
			}
43
		}
44
	}
45
46
	public static function clearCache() {
47
		self::$CACHE_PLAYERS = array();
48
		self::$CACHE_SECTOR_PLAYERS = array();
49
	}
50
51
	public static function savePlayers() {
52
		foreach (self::$CACHE_PLAYERS as $gamePlayers) {
53
			foreach ($gamePlayers as $player) {
54
				$player->save();
55
			}
56
		}
57
	}
58
59
	public static function getSectorPlayersByAlliances($gameID, $sectorID, array $allianceIDs, $forceUpdate = false) {
60
		$players = self::getSectorPlayers($gameID, $sectorID, $forceUpdate); // Don't use & as we do an unset
61
		foreach ($players as $accountID => $player) {
62
			if (!in_array($player->getAllianceID(), $allianceIDs)) {
63
				unset($players[$accountID]);
64
			}
65
		}
66
		return $players;
67
	}
68
69
	/**
70
	 * Returns the same players as getSectorPlayers (e.g. not on planets),
71
	 * but for an entire galaxy rather than a single sector. This is useful
72
	 * for reducing the number of queries in galaxy-wide processing.
73
	 */
74
	public static function getGalaxyPlayers($gameID, $galaxyID, $forceUpdate = false) {
75
		$db = new SmrMySqlDatabase();
76
		$db->query('SELECT player.*, sector_id FROM sector LEFT JOIN player USING(game_id, sector_id) WHERE game_id = ' . $db->escapeNumber($gameID) . ' AND land_on_planet = ' . $db->escapeBoolean(false) . ' AND (last_cpl_action > ' . $db->escapeNumber(TIME - TIME_BEFORE_INACTIVE) . ' OR newbie_turns = 0) AND galaxy_id = ' . $db->escapeNumber($galaxyID));
77
		$galaxyPlayers = [];
78
		while ($db->nextRecord()) {
79
			$sectorID = $db->getInt('sector_id');
80
			if (!$db->hasField('account_id')) {
81
				self::$CACHE_SECTOR_PLAYERS[$gameID][$sectorID] = [];
82
			} else {
83
				$accountID = $db->getInt('account_id');
84
				$player = self::getPlayer($accountID, $gameID, $forceUpdate, $db);
85
				self::$CACHE_SECTOR_PLAYERS[$gameID][$sectorID][$accountID] = $player;
86
				$galaxyPlayers[$sectorID][$accountID] = $player;
87
			}
88
		}
89
		return $galaxyPlayers;
90
	}
91
92
	public static function getSectorPlayers($gameID, $sectorID, $forceUpdate = false) {
93
		if ($forceUpdate || !isset(self::$CACHE_SECTOR_PLAYERS[$gameID][$sectorID])) {
94
			$db = new SmrMySqlDatabase();
95
			$db->query('SELECT * FROM player WHERE sector_id = ' . $db->escapeNumber($sectorID) . ' AND game_id=' . $db->escapeNumber($gameID) . ' AND land_on_planet = ' . $db->escapeBoolean(false) . ' AND (last_cpl_action > ' . $db->escapeNumber(TIME - TIME_BEFORE_INACTIVE) . ' OR newbie_turns = 0) AND account_id NOT IN (' . $db->escapeArray(Globals::getHiddenPlayers()) . ') ORDER BY last_cpl_action DESC');
96
			$players = array();
97
			while ($db->nextRecord()) {
98
				$accountID = $db->getInt('account_id');
99
				$players[$accountID] = self::getPlayer($accountID, $gameID, $forceUpdate, $db);
100
			}
101
			self::$CACHE_SECTOR_PLAYERS[$gameID][$sectorID] = $players;
102
		}
103
		return self::$CACHE_SECTOR_PLAYERS[$gameID][$sectorID];
104
	}
105
106
	public static function getPlanetPlayers($gameID, $sectorID, $forceUpdate = false) {
107
		if ($forceUpdate || !isset(self::$CACHE_PLANET_PLAYERS[$gameID][$sectorID])) {
108
			$db = new SmrMySqlDatabase();
109
			$db->query('SELECT * FROM player WHERE sector_id = ' . $db->escapeNumber($sectorID) . ' AND game_id=' . $db->escapeNumber($gameID) . ' AND land_on_planet = ' . $db->escapeBoolean(true) . ' AND account_id NOT IN (' . $db->escapeArray(Globals::getHiddenPlayers()) . ') ORDER BY last_cpl_action DESC');
110
			$players = array();
111
			while ($db->nextRecord()) {
112
				$accountID = $db->getInt('account_id');
113
				$players[$accountID] = self::getPlayer($accountID, $gameID, $forceUpdate, $db);
114
			}
115
			self::$CACHE_PLANET_PLAYERS[$gameID][$sectorID] = $players;
116
		}
117
		return self::$CACHE_PLANET_PLAYERS[$gameID][$sectorID];
118
	}
119
120
	public static function getAlliancePlayers($gameID, $allianceID, $forceUpdate = false) {
121
		if ($forceUpdate || !isset(self::$CACHE_ALLIANCE_PLAYERS[$gameID][$allianceID])) {
122
			$db = new SmrMySqlDatabase();
123
			$db->query('SELECT * FROM player WHERE alliance_id = ' . $db->escapeNumber($allianceID) . ' AND game_id=' . $db->escapeNumber($gameID) . ' ORDER BY experience DESC');
124
			$players = array();
125
			while ($db->nextRecord()) {
126
				$accountID = $db->getInt('account_id');
127
				$players[$accountID] = self::getPlayer($accountID, $gameID, $forceUpdate, $db);
128
			}
129
			self::$CACHE_ALLIANCE_PLAYERS[$gameID][$allianceID] = $players;
130
		}
131
		return self::$CACHE_ALLIANCE_PLAYERS[$gameID][$allianceID];
132
	}
133
134
	public static function getPlayer($accountID, $gameID, $forceUpdate = false, $db = null) {
135
		if ($forceUpdate || !isset(self::$CACHE_PLAYERS[$gameID][$accountID])) {
136
			self::$CACHE_PLAYERS[$gameID][$accountID] = new SmrPlayer($gameID, $accountID, $db);
137
		}
138
		return self::$CACHE_PLAYERS[$gameID][$accountID];
139
	}
140
141
	public static function getPlayerByPlayerID($playerID, $gameID, $forceUpdate = false) {
142
		$db = new SmrMySqlDatabase();
143
		$db->query('SELECT * FROM player WHERE game_id = ' . $db->escapeNumber($gameID) . ' AND player_id = ' . $db->escapeNumber($playerID) . ' LIMIT 1');
144
		if ($db->nextRecord()) {
145
			return self::getPlayer($db->getInt('account_id'), $gameID, $forceUpdate, $db);
146
		}
147
		throw new PlayerNotFoundException('Player ID not found.');
148
	}
149
150
	public static function getPlayerByPlayerName($playerName, $gameID, $forceUpdate = false) {
151
		$db = new SmrMySqlDatabase();
152
		$db->query('SELECT * FROM player WHERE game_id = ' . $db->escapeNumber($gameID) . ' AND player_name = ' . $db->escapeString($playerName) . ' LIMIT 1');
153
		if ($db->nextRecord()) {
154
			return self::getPlayer($db->getInt('account_id'), $gameID, $forceUpdate, $db);
155
		}
156
		throw new PlayerNotFoundException('Player Name not found.');
157
	}
158
159
	protected function __construct($gameID, $accountID, $db = null) {
160
		parent::__construct();
161
		$this->db = new SmrMySqlDatabase();
162
		$this->SQL = 'account_id = ' . $this->db->escapeNumber($accountID) . ' AND game_id = ' . $this->db->escapeNumber($gameID);
163
164
		if (isset($db)) {
165
			$playerExists = true;
166
		} else {
167
			$db = $this->db;
168
			$this->db->query('SELECT * FROM player WHERE ' . $this->SQL . ' LIMIT 1');
169
			$playerExists = $db->nextRecord();
170
		}
171
172
		if ($playerExists) {
173
			$this->accountID = (int)$accountID;
174
			$this->gameID = (int)$gameID;
175
			$this->playerName = $db->getField('player_name');
176
			$this->playerID = $db->getInt('player_id');
177
			$this->sectorID = $db->getInt('sector_id');
178
			$this->lastSectorID = $db->getInt('last_sector_id');
179
			$this->turns = $db->getInt('turns');
180
			$this->lastTurnUpdate = $db->getInt('last_turn_update');
181
			$this->newbieTurns = $db->getInt('newbie_turns');
182
			$this->lastNewsUpdate = $db->getInt('last_news_update');
183
			$this->attackColour = $db->getField('attack_warning');
184
			$this->dead = $db->getBoolean('dead');
185
			$this->npc = $db->getBoolean('npc');
186
			$this->newbieStatus = $db->getBoolean('newbie_status');
187
			$this->landedOnPlanet = $db->getBoolean('land_on_planet');
188
			$this->lastActive = $db->getInt('last_active');
189
			$this->lastCPLAction = $db->getInt('last_cpl_action');
190
			$this->raceID = $db->getInt('race_id');
191
			$this->credits = $db->getInt('credits');
192
			$this->experience = $db->getInt('experience');
193
			$this->alignment = $db->getInt('alignment');
194
			$this->militaryPayment = $db->getInt('military_payment');
195
			$this->allianceID = $db->getInt('alliance_id');
196
			$this->allianceJoinable = $db->getInt('alliance_join');
197
			$this->shipID = $db->getInt('ship_type_id');
198
			$this->kills = $db->getInt('kills');
199
			$this->deaths = $db->getInt('deaths');
200
			$this->assists = $db->getInt('assists');
201
			$this->lastPort = $db->getInt('last_port');
202
			$this->bank = $db->getInt('bank');
203
			$this->zoom = $db->getInt('zoom');
204
			$this->displayMissions = $db->getBoolean('display_missions');
205
			$this->displayWeapons = $db->getBoolean('display_weapons');
206
			$this->forceDropMessages = $db->getBoolean('force_drop_messages');
0 ignored issues
show
Bug Best Practice introduced by
The property forceDropMessages does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
207
			$this->groupScoutMessages = $db->getField('group_scout_messages');
0 ignored issues
show
Bug Best Practice introduced by
The property groupScoutMessages does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
208
			$this->ignoreGlobals = $db->getBoolean('ignore_globals');
209
			$this->newbieWarning = $db->getBoolean('newbie_warning');
210
			$this->nameChanged = $db->getBoolean('name_changed');
211
			$this->combatDronesKamikazeOnMines = $db->getBoolean('combat_drones_kamikaze_on_mines');
212
		} else {
213
			throw new PlayerNotFoundException('Invalid accountID: ' . $accountID . ' OR gameID:' . $gameID);
214
		}
215
	}
216
217
	/**
218
	 * Insert a new player into the database. Returns the new player object.
219
	 */
220
	public static function createPlayer($accountID, $gameID, $playerName, $raceID, $isNewbie, $npc=false) {
221
		// Put the player in a sector with an HQ
222
		$startSectorID = self::getHome($gameID, $raceID);
223
224
		$db = new SmrMySqlDatabase();
225
		$db->lockTable('player');
226
227
		// Escape html elements so the name displays correctly
228
		$playerName = htmlentities($playerName);
229
230
		// Player names must be unique within each game
231
		$db->query('SELECT 1 FROM player WHERE game_id = ' . $db->escapeNumber($gameID) . ' AND player_name = ' . $db->escapeString($playerName) . ' LIMIT 1');
232
		if ($db->nextRecord() > 0) {
233
			$db->unlock();
234
			create_error('The player name already exists.');
235
		}
236
237
		// get last registered player id in that game and increase by one.
238
		$db->query('SELECT MAX(player_id) FROM player WHERE game_id = ' . $db->escapeNumber($gameID));
239
		if ($db->nextRecord()) {
240
			$playerID = $db->getInt('MAX(player_id)') + 1;
241
		} else {
242
			$playerID = 1;
243
		}
244
245
		$db->query('INSERT INTO player (account_id, game_id, player_id, player_name, race_id, sector_id, last_cpl_action, last_active, npc, newbie_status)
246
					VALUES(' . $db->escapeNumber($accountID) . ', ' . $db->escapeNumber($gameID) . ', ' . $db->escapeNumber($playerID) . ', ' . $db->escapeString($playerName) . ', ' . $db->escapeNumber($raceID) . ', ' . $db->escapeNumber($startSectorID) . ', ' . $db->escapeNumber(TIME) . ', ' . $db->escapeNumber(TIME) . ',' . $db->escapeBoolean($npc) . ',' . $db->escapeBoolean($isNewbie) . ')');
247
248
		$db->unlock();
249
250
		return SmrPlayer::getPlayer($accountID, $gameID);
251
	}
252
253
	// Get array of players whose info can be accessed by this player.
254
	// Skips players who are not in the same alliance as this player.
255
	public function getSharingPlayers($forceUpdate = false) {
256
		$results = array($this);
257
258
		// Only return this player if not in an alliance
259
		if (!$this->hasAlliance()) {
260
			return $results;
261
		}
262
263
		// Get other players who are sharing info for this game.
264
		// NOTE: game_id=0 means that player shares info for all games.
265
		$this->db->query('SELECT from_account_id FROM account_shares_info WHERE to_account_id=' . $this->db->escapeNumber($this->getAccountID()) . ' AND (game_id=0 OR game_id=' . $this->db->escapeNumber($this->getGameID()) . ')');
266
		while ($this->db->nextRecord()) {
267
			try {
268
				$otherPlayer = SmrPlayer::getPlayer($this->db->getInt('from_account_id'),
269
				                                    $this->getGameID(), $forceUpdate);
270
			} catch (PlayerNotFoundException $e) {
271
				// Skip players that have not joined this game
272
				continue;
273
			}
274
275
			// players must be in the same alliance
276
			if ($this->sameAlliance($otherPlayer)) {
277
				$results[] = $otherPlayer;
278
			}
279
		}
280
		return $results;
281
	}
282
283
	public function getShip($forceUpdate = false) {
284
		return SmrShip::getShip($this, $forceUpdate);
285
	}
286
287
	public function getAccount() {
288
		return SmrAccount::getAccount($this->getAccountID());
289
	}
290
291
	public function getZoom() {
292
		return $this->zoom;
293
	}
294
295
	protected function setZoom($zoom) {
296
		// Set the zoom level between [1, 9]
297
		$zoom = max(1, min(9, $zoom));
298
		if ($this->zoom == $zoom) {
299
			return;
300
		}
301
		$this->zoom = $zoom;
302
		$this->hasChanged = true;
303
//		$this->db->query('UPDATE player SET zoom = ' . $zoom . ' WHERE ' . $this->SQL . ' LIMIT 1');
304
	}
305
306
	public function increaseZoom($zoom) {
307
		if ($zoom < 0) {
308
			throw new Exception('Trying to increase negative zoom.');
309
		}
310
		$this->setZoom($this->getZoom() + $zoom);
311
	}
312
313
	public function decreaseZoom($zoom) {
314
		if ($zoom < 0) {
315
			throw new Exception('Trying to decrease negative zoom.');
316
		}
317
		$this->setZoom($this->getZoom() - $zoom);
318
	}
319
320
	public function setSectorID($sectorID) {
321
		$port = SmrPort::getPort($this->getGameID(), $this->getSectorID());
322
		$port->addCachePort($this->getAccountID()); //Add port of sector we were just in, to make sure it is left totally up to date.
323
324
		parent::setSectorID($sectorID);
325
326
		$port = SmrPort::getPort($this->getGameID(), $sectorID);
327
		$port->addCachePort($this->getAccountID()); //Add the port of sector we are now in.
328
	}
329
330
	public function setLastSectorID($lastSectorID) {
331
		if ($this->lastSectorID == $lastSectorID) {
332
			return;
333
		}
334
		$this->lastSectorID = $lastSectorID;
335
		$this->hasChanged = true;
336
//		$this->db->query('UPDATE player SET last_sector_id = '.$this->lastSectorID.' WHERE '.$this->SQL.' LIMIT 1');
337
	}
338
339
	public function leaveAlliance(AbstractSmrPlayer $kickedBy = null) {
340
		$allianceID = $this->getAllianceID();
341
		$alliance = $this->getAlliance();
342
		if ($kickedBy != null) {
343
			$kickedBy->sendMessage($this->getAccountID(), MSG_PLAYER, 'You were kicked out of the alliance!', false);
0 ignored issues
show
Bug introduced by
The method sendMessage() does not exist on AbstractSmrPlayer. It seems like you code against a sub-type of AbstractSmrPlayer such as SmrPlayer. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

343
			$kickedBy->/** @scrutinizer ignore-call */ 
344
              sendMessage($this->getAccountID(), MSG_PLAYER, 'You were kicked out of the alliance!', false);
Loading history...
344
			$this->actionTaken('PlayerKicked', array('Alliance' => $alliance, 'Player' => $kickedBy));
345
			$kickedBy->actionTaken('KickPlayer', array('Alliance' => $alliance, 'Player' => $this));
346
		} elseif ($this->isAllianceLeader()) {
347
			$this->actionTaken('DisbandAlliance', array('Alliance' => $alliance));
348
		} else {
349
			$this->actionTaken('LeaveAlliance', array('Alliance' => $alliance));
350
			if ($alliance->getLeaderID() != 0 && $alliance->getLeaderID() != ACCOUNT_ID_NHL) {
351
				$this->sendMessage($alliance->getLeaderID(), MSG_PLAYER, 'I left your alliance!', false);
352
			}
353
		}
354
355
		$this->setAllianceID(0);
356
		$this->db->query('DELETE FROM player_has_alliance_role WHERE ' . $this->SQL);
357
358
		if (!$this->isAllianceLeader() && $allianceID != NHA_ID) { // Don't have a delay for switching alliance after leaving NHA, or for disbanding an alliance.
359
			$this->setAllianceJoinable(TIME + self::TIME_FOR_ALLIANCE_SWITCH);
360
			$alliance->getLeader()->setAllianceJoinable(TIME + self::TIME_FOR_ALLIANCE_SWITCH); //We set the joinable time for leader here, that way a single player alliance won't cause a player to wait before switching.
361
		}
362
	}
363
364
	/**
365
	 * Join an alliance (used for both Leader and New Member roles)
366
	 */
367
	public function joinAlliance($allianceID) {
368
		$this->setAllianceID($allianceID);
369
		$alliance = $this->getAlliance();
370
371
		if (!$this->isAllianceLeader()) {
372
			// Do not throw an exception if the NHL account doesn't exist.
373
			try {
374
				$this->sendMessage($alliance->getLeaderID(), MSG_PLAYER, 'I joined your alliance!', false);
375
			} catch (AccountNotFoundException $e) {
376
				if ($alliance->getLeaderID() != ACCOUNT_ID_NHL) {
377
					throw $e;
378
				}
379
			}
380
381
			$roleID = ALLIANCE_ROLE_NEW_MEMBER;
382
		} else {
383
			$roleID = ALLIANCE_ROLE_LEADER;
384
		}
385
		$this->db->query('INSERT INTO player_has_alliance_role (game_id, account_id, role_id, alliance_id) VALUES (' . $this->db->escapeNumber($this->getGameID()) . ', ' . $this->db->escapeNumber($this->getAccountID()) . ', ' . $this->db->escapeNumber($roleID) . ',' . $this->db->escapeNumber($this->getAllianceID()) . ')');
386
387
		$this->actionTaken('JoinAlliance', array('Alliance' => $alliance));
388
389
		// Joining an alliance cancels all open invitations
390
		$this->db->query('DELETE FROM alliance_invites_player WHERE ' . $this->SQL);
391
	}
392
393
	public function getAllianceJoinable() {
394
		return $this->allianceJoinable;
395
	}
396
397
	private function setAllianceJoinable($time) {
398
		if ($this->allianceJoinable == $time) {
399
			return;
400
		}
401
		$this->allianceJoinable = $time;
402
		$this->hasChanged = true;
403
	}
404
405
	public function getAttackColour() {
406
		return $this->attackColour;
407
	}
408
409
	public function setAttackColour($colour) {
410
		if ($this->attackColour == $colour) {
411
			return;
412
		}
413
		$this->attackColour = $colour;
414
		$this->hasChanged = true;
415
//		$this->db->query('UPDATE player SET attack_warning = ' . $this->db->escapeString($this->attackColour) . ' WHERE ' . $this->SQL . ' LIMIT 1');
416
	}
417
418
	public function getBank() {
419
		return $this->bank;
420
	}
421
422
	public function increaseBank($credits) {
423
		if ($credits < 0) {
424
			throw new Exception('Trying to increase negative credits.');
425
		}
426
		if ($credits == 0) {
427
			return;
428
		}
429
		$credits += $this->bank;
430
		$this->setBank($credits);
431
	}
432
	public function decreaseBank($credits) {
433
		if ($credits < 0) {
434
			throw new Exception('Trying to decrease negative credits.');
435
		}
436
		if ($credits == 0) {
437
			return;
438
		}
439
		$credits = $this->bank - $credits;
440
		$this->setBank($credits);
441
	}
442
	public function setBank($credits) {
443
		if ($this->bank == $credits) {
444
			return;
445
		}
446
		if ($credits < 0) {
447
			throw new Exception('Trying to set negative credits.');
448
		}
449
		if ($credits > MAX_MONEY) {
450
			throw new Exception('Trying to set more than max credits.');
451
		}
452
		$this->bank = $credits;
453
		$this->hasChanged = true;
454
//		$this->db->query('UPDATE player SET bank = '.$this->bank.' WHERE '.$this->SQL.' LIMIT 1');
455
	}
456
457
	public function getLastNewsUpdate() {
458
		return $this->lastNewsUpdate;
459
	}
460
461
	private function setLastNewsUpdate($time) {
462
		if ($this->lastNewsUpdate == $time) {
463
			return;
464
		}
465
		$this->lastNewsUpdate = $time;
466
		$this->hasChanged = true;
467
//		$this->db->query('UPDATE player SET last_news_update = ' . $time . ' WHERE ' . $this->SQL . ' LIMIT 1');
468
	}
469
470
	public function updateLastNewsUpdate() {
471
		$this->setLastNewsUpdate(TIME);
472
	}
473
474
//	function getPastKnowledge() {
475
//		return $this->pastKnowledge;
476
//	}
477
478
	/**
479
	 * Calculate the time in seconds between the given time and when the
480
	 * player will be at max turns.
481
	 */
482
	public function getTimeUntilMaxTurns($time, $forceUpdate = false) {
483
		$timeDiff = $time - $this->getLastTurnUpdate();
484
		$turnsDiff = $this->getMaxTurns() - $this->getTurns();
485
		$ship = $this->getShip($forceUpdate);
486
		$maxTurnsTime = ICeil(($turnsDiff * 3600 / $ship->getRealSpeed())) - $timeDiff;
487
		// If already at max turns, return 0
488
		return max(0, $maxTurnsTime);
489
	}
490
491
	/**
492
	 * Grant the player their starting turns.
493
	 */
494
	public function giveStartingTurns() {
495
		$startTurns = IFloor($this->getShip()->getRealSpeed() * $this->getGame()->getStartTurnHours());
496
		$this->giveTurns($startTurns);
497
		$this->setLastTurnUpdate($this->getGame()->getStartTime());
498
	}
499
500
	// Turns only update when player is active.
501
	// Calculate turns gained between given time and the last turn update
502
	public function getTurnsGained($time, $forceUpdate = false) : int {
503
		$timeDiff = $time - $this->getLastTurnUpdate();
504
		$ship = $this->getShip($forceUpdate);
505
		$extraTurns = IFloor($timeDiff * $ship->getRealSpeed() / 3600);
506
		return $extraTurns;
507
	}
508
509
	public function updateTurns() {
510
		// is account validated?
511
		if (!$this->getAccount()->isValidated()) {
512
			return;
513
		}
514
515
		// how many turns would he get right now?
516
		$extraTurns = $this->getTurnsGained(TIME);
517
518
		// do we have at least one turn to give?
519
		if ($extraTurns > 0) {
520
			// recalc the time to avoid rounding errors
521
			$newLastTurnUpdate = $this->getLastTurnUpdate() + ICeil($extraTurns * 3600 / $this->getShip()->getRealSpeed());
522
			$this->setLastTurnUpdate($newLastTurnUpdate);
523
			$this->giveTurns($extraTurns);
524
		}
525
	}
526
527
	public function isIgnoreGlobals() {
528
		return $this->ignoreGlobals;
529
	}
530
531
	public function setIgnoreGlobals($bool) {
532
		if ($this->ignoreGlobals == $bool) {
533
			return;
534
		}
535
		$this->ignoreGlobals = $bool;
536
		$this->hasChanged = true;
537
//		$this->db->query('UPDATE player SET ignore_globals = '.$this->db->escapeBoolean($bool).' WHERE '.$this->SQL.' LIMIT 1');
538
	}
539
540
541
	public function getLastPort() {
542
		return $this->lastPort;
543
	}
544
545
	public function setLastPort($lastPort) {
546
		if ($this->lastPort == $lastPort) {
547
			return;
548
		}
549
		$this->lastPort = $lastPort;
550
		$this->hasChanged = true;
551
//		$this->db->query('UPDATE player SET last_port = ' . $this->lastPort . ' WHERE ' . $this->SQL . ' LIMIT 1');
552
	}
553
554
	public function isDisplayMissions() {
555
		return $this->displayMissions;
556
	}
557
558
	public function setDisplayMissions($bool) {
559
		if ($this->displayMissions == $bool) {
560
			return;
561
		}
562
		$this->displayMissions = $bool;
563
		$this->hasChanged = true;
564
	}
565
566
	public function isDisplayWeapons() {
567
		return $this->displayWeapons;
568
	}
569
570
	/**
571
	 * Should weapons be displayed in the right panel?
572
	 * This updates the player database directly because it is used with AJAX,
573
	 * which does not acquire a sector lock.
574
	 */
575
	public function setDisplayWeapons($bool) {
576
		if ($this->displayWeapons == $bool) {
577
			return;
578
		}
579
		$this->displayWeapons = $bool;
580
		$this->db->query('UPDATE player SET display_weapons=' . $this->db->escapeBoolean($this->displayWeapons) . ' WHERE ' . $this->SQL);
581
	}
582
583
	public function isForceDropMessages() {
584
		return $this->forceDropMessages;
585
	}
586
587
	public function setForceDropMessages($bool) {
588
		if ($this->forceDropMessages == $bool) {
589
			return;
590
		}
591
		$this->forceDropMessages = $bool;
0 ignored issues
show
Bug Best Practice introduced by
The property forceDropMessages does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
592
		$this->hasChanged = true;
593
	}
594
595
	public function getScoutMessageGroupLimit() {
596
		if ($this->groupScoutMessages == 'ALWAYS') {
597
			return 0;
598
		} elseif ($this->groupScoutMessages == 'AUTO') {
599
			return MESSAGES_PER_PAGE;
600
		} elseif ($this->groupScoutMessages == 'NEVER') {
601
			return PHP_INT_MAX;
602
		}
603
	}
604
605
	public function getGroupScoutMessages() {
606
		return $this->groupScoutMessages;
607
	}
608
609
	public function setGroupScoutMessages($setting) {
610
		if ($this->groupScoutMessages == $setting) {
611
			return;
612
		}
613
		$this->groupScoutMessages = $setting;
0 ignored issues
show
Bug Best Practice introduced by
The property groupScoutMessages does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
614
		$this->hasChanged = true;
615
	}
616
617
	public function getLastTurnUpdate() {
618
		return $this->lastTurnUpdate;
619
	}
620
621
	public function setLastTurnUpdate($time) {
622
		if ($this->lastTurnUpdate == $time) {
623
			return;
624
		}
625
		$this->lastTurnUpdate = $time;
626
		$this->hasChanged = true;
627
//		$sql = $this->db->query('UPDATE player SET last_turn_update = ' . $this->lastTurnUpdate . ' WHERE '. $this->SQL . ' LIMIT 1');
628
	}
629
630
	protected function getPureRelationsData() {
631
		if (!isset($this->pureRelations)) {
632
			//get relations
633
			$RACES = Globals::getRaces();
634
			$this->pureRelations = array();
635
			foreach ($RACES as $raceID => $raceName) {
636
				$this->pureRelations[$raceID] = 0;
637
			}
638
			$this->db->query('SELECT race_id,relation FROM player_has_relation WHERE ' . $this->SQL . ' LIMIT ' . count($RACES));
639
			while ($this->db->nextRecord()) {
640
				$this->pureRelations[$this->db->getInt('race_id')] = $this->db->getInt('relation');
641
			}
642
		}
643
	}
644
645
	/**
646
	 * Increases personal relations from trading $numGoods units with the race
647
	 * of the port given by $raceID.
648
	 */
649
	public function increaseRelationsByTrade($numGoods, $raceID) {
650
		$relations = ICeil(min($numGoods, 300) / 30);
651
		//Cap relations to a max of 1 after 500 have been reached
652
		if ($this->getPureRelation($raceID) + $relations >= 500) {
653
			$relations = max(1, min($relations, 500 - $this->getPureRelation($raceID)));
654
		}
655
		$this->increaseRelations($relations, $raceID);
656
	}
657
658
	public function decreaseRelationsByTrade($numGoods, $raceID) {
659
		$relations = ICeil(min($numGoods, 300) / 30);
660
		$this->decreaseRelations($relations, $raceID);
661
	}
662
663
	public function increaseRelations($relations, $raceID) {
664
		if ($relations < 0) {
665
			throw new Exception('Trying to increase negative relations.');
666
		}
667
		if ($relations == 0) {
668
			return;
669
		}
670
		$relations += $this->getPureRelation($raceID);
671
		$this->setRelations($relations, $raceID);
672
	}
673
	public function decreaseRelations($relations, $raceID) {
674
		if ($relations < 0) {
675
			throw new Exception('Trying to decrease negative relations.');
676
		}
677
		if ($relations == 0) {
678
			return;
679
		}
680
		$relations = $this->getPureRelation($raceID) - $relations;
681
		$this->setRelations($relations, $raceID);
682
	}
683
	public function setRelations($relations, $raceID) {
684
		$this->getRelations();
685
		if ($this->pureRelations[$raceID] == $relations) {
686
			return;
687
		}
688
		if ($relations < MIN_RELATIONS) {
689
			$relations = MIN_RELATIONS;
690
		}
691
		$relationsDiff = IRound($relations - $this->pureRelations[$raceID]);
692
		$this->pureRelations[$raceID] = $relations;
693
		$this->relations[$raceID] += $relationsDiff;
694
		$this->db->query('REPLACE INTO player_has_relation (account_id,game_id,race_id,relation) values (' . $this->db->escapeNumber($this->getAccountID()) . ',' . $this->db->escapeNumber($this->getGameID()) . ',' . $this->db->escapeNumber($raceID) . ',' . $this->db->escapeNumber($this->pureRelations[$raceID]) . ')');
695
	}
696
697
	/**
698
	 * Use this method when the player is changing their own name.
699
	 * This will flag the player as having used their free name change.
700
	 */
701
	public function setPlayerNameByPlayer($playerName) {
702
		$this->playerName = $playerName;
703
		$this->setNameChanged(true);
704
		$this->hasChanged = true;
705
	}
706
707
	public function isNameChanged() {
708
		return $this->nameChanged;
709
	}
710
711
	public function setNameChanged($bool) {
712
		$this->nameChanged = $bool;
713
		$this->hasChanged = true;
714
	}
715
716
	public function hasCustomShipName() {
717
		return $this->getCustomShipName() !== false;
718
	}
719
720
	public function getCustomShipName() {
721
		if (!isset($this->customShipName)) {
722
			$this->db->query('SELECT * FROM ship_has_name WHERE ' . $this->SQL . ' LIMIT 1');
723
			if ($this->db->nextRecord()) {
724
				$this->customShipName = $this->db->getField('ship_name');
725
			} else {
726
				$this->customShipName = false;
727
			}
728
		}
729
		return $this->customShipName;
730
	}
731
732
	public function setCustomShipName(string $name) {
733
		$this->db->query('REPLACE INTO ship_has_name (game_id, account_id, ship_name)
734
			VALUES (' . $this->db->escapeNumber($this->getGameID()) . ', ' . $this->db->escapeNumber($this->getAccountID()) . ', ' . $this->db->escapeString($name) . ')');
735
	}
736
737
	public function getKnowledge($knowledgeType = false) {
738
		if (!isset($this->knowledge)) {
739
			//get players faction knowledge
740
			$this->db->query('SELECT * FROM player_knows_faction WHERE ' . $this->SQL . ' LIMIT 1');
741
			if ($this->db->nextRecord()) {
742
				$this->knowledge['Erebus'] = $this->db->getInt('erebus');
0 ignored issues
show
Bug Best Practice introduced by
The property knowledge does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
743
				$this->knowledge['Aether'] = $this->db->getInt('aether');
744
				$this->knowledge['Tartarus'] = $this->db->getInt('tartarus');
745
				$this->knowledge['Nyx'] = $this->db->getInt('nyx');
746
				$this->knowledge['Federation'] = 0;
747
				$this->knowledge['Underground'] = 0;
748
			} else {
749
				$this->knowledge['Erebus'] = 0;
750
				$this->knowledge['Aether'] = 0;
751
				$this->knowledge['Tartarus'] = 0;
752
				$this->knowledge['Nyx'] = 0;
753
				$this->knowledge['Federation'] = 0;
754
				$this->knowledge['Underground'] = 0;
755
			}
756
		}
757
		if ($knowledgeType === false) {
758
			return $this->knowledge;
759
		}
760
		if (isset($this->knowledge[$knowledgeType])) {
761
			return $this->knowledge[$knowledgeType];
762
		}
763
		return false;
764
	}
765
766
	public function killPlayer($sectorID) {
767
		$sector = SmrSector::getSector($this->getGameID(), $sectorID);
768
		//msg taken care of in trader_att_proc.php
769
		// forget plotted course
770
		$this->deletePlottedCourse();
771
772
		$sector->diedHere($this);
773
774
		// if we are in an alliance we increase their deaths
775
		if ($this->hasAlliance()) {
776
			$this->db->query('UPDATE alliance SET alliance_deaths = alliance_deaths + 1
777
							WHERE game_id = ' . $this->db->escapeNumber($this->getGameID()) . ' AND alliance_id = ' . $this->db->escapeNumber($this->getAllianceID()) . ' LIMIT 1');
778
		}
779
780
		// record death stat
781
		$this->increaseHOF(1, array('Dying', 'Deaths'), HOF_PUBLIC);
782
		//record cost of ship lost
783
		$this->increaseHOF($this->getShip()->getCost(), array('Dying', 'Money', 'Cost Of Ships Lost'), HOF_PUBLIC);
784
		// reset turns since last death
785
		$this->setHOF(0, array('Movement', 'Turns Used', 'Since Last Death'), HOF_ALLIANCE);
786
787
		// 1/4 of ship value -> insurance
788
		$newCredits = IRound($this->getShip()->getCost() / 4);
789
		$old_speed = $this->getShip()->getSpeed();
790
791
		if ($newCredits < 100000) {
792
			$newCredits = 100000;
793
		}
794
		$this->setCredits($newCredits);
795
796
		$this->setSectorID($this::getHome($this->getGameID(), $this->getRaceID()));
797
		$this->increaseDeaths(1);
798
		$this->setLandedOnPlanet(false);
799
		$this->setDead(true);
800
		$this->setNewbieWarning(true);
801
		$this->getShip()->getPod($this->hasNewbieStatus());
802
803
		// Update turns due to ship change
804
		$new_speed = $this->getShip()->getSpeed();
805
		$this->setTurns(IRound($this->turns / $old_speed * $new_speed));
806
		$this->setNewbieTurns(100);
807
	}
808
809
	static public function getHome($gameID, $raceID) {
810
		// get his home sector
811
		$hq_id = GOVERNMENT + $raceID;
812
		$raceHqSectors = SmrSector::getLocationSectors($gameID, $hq_id);
813
		if (!empty($raceHqSectors)) {
814
			// If race has multiple HQ's for some reason, use the first one
815
			return key($raceHqSectors);
816
		} else {
817
			return 1;
818
		}
819
	}
820
821
	public function incrementAllianceVsKills($otherID) {
822
		$values = [$this->getGameID(), $this->getAllianceID(), $otherID, 1];
823
		$this->db->query('INSERT INTO alliance_vs_alliance (game_id, alliance_id_1, alliance_id_2, kills) VALUES (' . $this->db->escapeArray($values) . ') ON DUPLICATE KEY UPDATE kills = kills + 1');
824
	}
825
826
	public function incrementAllianceVsDeaths($otherID) {
827
		$values = [$this->getGameID(), $otherID, $this->getAllianceID(), 1];
828
		$this->db->query('INSERT INTO alliance_vs_alliance (game_id, alliance_id_1, alliance_id_2, kills) VALUES (' . $this->db->escapeArray($values) . ') ON DUPLICATE KEY UPDATE kills = kills + 1');
829
	}
830
831
	public function &killPlayerByPlayer(AbstractSmrPlayer $killer) {
832
		$return = array();
833
		$msg = $this->getBBLink();
834
835
		if ($this->hasCustomShipName()) {
836
			$named_ship = strip_tags($this->getCustomShipName(), '<font><span><img>');
837
			$msg .= ' flying <span class="yellow">' . $named_ship . '</span>';
838
		}
839
		$msg .= ' was destroyed by ' . $killer->getBBLink();
840
		if ($killer->hasCustomShipName()) {
0 ignored issues
show
Bug introduced by
The method hasCustomShipName() does not exist on AbstractSmrPlayer. It seems like you code against a sub-type of AbstractSmrPlayer such as SmrPlayer. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

840
		if ($killer->/** @scrutinizer ignore-call */ hasCustomShipName()) {
Loading history...
841
			$named_ship = strip_tags($killer->getCustomShipName(), '<font><span><img>');
0 ignored issues
show
Bug introduced by
The method getCustomShipName() does not exist on AbstractSmrPlayer. It seems like you code against a sub-type of AbstractSmrPlayer such as SmrPlayer. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

841
			$named_ship = strip_tags($killer->/** @scrutinizer ignore-call */ getCustomShipName(), '<font><span><img>');
Loading history...
842
			$msg .= ' flying <span class="yellow">' . $named_ship . '</span>';
843
		}
844
		$msg .= ' in Sector&nbsp;' . Globals::getSectorBBLink($this->getSectorID());
845
		$this->getSector()->increaseBattles(1);
846
		$this->db->query('INSERT INTO news (game_id,time,news_message,type,killer_id,killer_alliance,dead_id,dead_alliance) VALUES (' . $this->db->escapeNumber($this->getGameID()) . ',' . $this->db->escapeNumber(TIME) . ',' . $this->db->escapeString($msg, true) . ',\'regular\',' . $this->db->escapeNumber($killer->getAccountID()) . ',' . $this->db->escapeNumber($killer->getAllianceID()) . ',' . $this->db->escapeNumber($this->getAccountID()) . ',' . $this->db->escapeNumber($this->getAllianceID()) . ')');
847
848
		self::sendMessageFromFedClerk($this->getGameID(), $this->getAccountID(), 'You were <span class="red">DESTROYED</span> by ' . $killer->getBBLink() . ' in sector ' . Globals::getSectorBBLink($this->getSectorID()));
849
		self::sendMessageFromFedClerk($this->getGameID(), $killer->getAccountID(), 'You <span class="red">DESTROYED</span>&nbsp;' . $this->getBBLink() . ' in sector ' . Globals::getSectorBBLink($this->getSectorID()));
850
851
		// Dead player loses between 5% and 25% experience
852
		$expLossPercentage = 0.15 + 0.10 * ($this->getLevelID() - $killer->getLevelID()) / $this->getMaxLevel();
853
		$return['DeadExp'] = max(0, IFloor($this->getExperience() * $expLossPercentage));
854
		$this->decreaseExperience($return['DeadExp']);
855
856
		// Killer gains 50% of the lost exp
857
		$return['KillerExp'] = max(0, ICeil(0.5 * $return['DeadExp']));
858
		$killer->increaseExperience($return['KillerExp']);
859
860
		$return['KillerCredits'] = $this->getCredits();
861
		$killer->increaseCredits($return['KillerCredits']);
862
863
		// The killer may change alignment
864
		$relations = Globals::getRaceRelations($this->getGameID(), $this->getRaceID());
865
		$relation = $relations[$killer->getRaceID()];
866
867
		$alignChangePerRelation = 0.1;
868
		if ($relation >= RELATIONS_PEACE || $relation <= RELATIONS_WAR) {
869
			$alignChangePerRelation = 0.04;
870
		}
871
872
		$return['KillerAlign'] = -$relation * $alignChangePerRelation; //Lose relations when killing a peaceful race
873
		if ($return['KillerAlign'] > 0) {
874
			$killer->increaseAlignment($return['KillerAlign']);
875
		} else {
876
			$killer->decreaseAlignment(-$return['KillerAlign']);
877
		}
878
		// War setting gives them military pay
879
		if ($relation <= RELATIONS_WAR) {
880
			$killer->increaseMilitaryPayment(-IFloor($relation * 100 * pow($return['KillerExp'] / 2, 0.25)));
881
		}
882
883
		//check for federal bounty being offered for current port raiders;
884
		$this->db->query('DELETE FROM player_attacks_port WHERE time < ' . $this->db->escapeNumber(TIME - self::TIME_FOR_FEDERAL_BOUNTY_ON_PR));
885
		$query = 'SELECT 1
886
					FROM player_attacks_port
887
					JOIN port USING(game_id, sector_id)
888
					JOIN player USING(game_id, account_id)
889
					WHERE armour > 0 AND ' . $this->SQL . ' LIMIT 1';
890
		$this->db->query($query);
891
		if ($this->db->nextRecord()) {
892
			$bounty = IFloor(DEFEND_PORT_BOUNTY_PER_LEVEL * $this->getLevelID());
893
			$this->increaseCurrentBountyAmount('HQ', $bounty);
894
		}
895
896
		// Killer get marked as claimer of podded player's bounties even if they don't exist
897
		$this->setBountiesClaimable($killer);
898
899
		// If the alignment difference is greater than 200 then a bounty may be set
900
		$alignmentDiff = abs($this->getAlignment() - $killer->getAlignment());
901
		$return['BountyGained'] = array(
902
			'Type' => 'None',
903
			'Amount' => 0
904
		);
905
		if ($alignmentDiff >= 200) {
906
			// If the podded players alignment makes them deputy or member then set bounty
907
			if ($this->getAlignment() >= 100) {
908
				$return['BountyGained']['Type'] = 'HQ';
909
			} elseif ($this->getAlignment() <= 100) {
910
				$return['BountyGained']['Type'] = 'UG';
911
			}
912
913
			if ($return['BountyGained']['Type'] != 'None') {
914
				$return['BountyGained']['Amount'] = IFloor(pow($alignmentDiff, 2.56));
915
				$killer->increaseCurrentBountyAmount($return['BountyGained']['Type'], $return['BountyGained']['Amount']);
916
			}
917
		}
918
919
		if ($this->isNPC()) {
920
			$killer->increaseHOF($return['KillerExp'], array('Killing', 'NPC', 'Experience', 'Gained'), HOF_PUBLIC);
921
			$killer->increaseHOF($this->getExperience(), array('Killing', 'NPC', 'Experience', 'Of Traders Killed'), HOF_PUBLIC);
922
923
			$killer->increaseHOF($return['DeadExp'], array('Killing', 'Experience', 'Lost By NPCs Killed'), HOF_PUBLIC);
924
925
			$killer->increaseHOF($return['KillerCredits'], array('Killing', 'NPC', 'Money', 'Lost By Traders Killed'), HOF_PUBLIC);
926
			$killer->increaseHOF($return['KillerCredits'], array('Killing', 'NPC', 'Money', 'Gain'), HOF_PUBLIC);
927
			$killer->increaseHOF($this->getShip()->getCost(), array('Killing', 'NPC', 'Money', 'Cost Of Ships Killed'), HOF_PUBLIC);
928
929
			if ($return['KillerAlign'] > 0) {
930
				$killer->increaseHOF($return['KillerAlign'], array('Killing', 'NPC', 'Alignment', 'Gain'), HOF_PUBLIC);
931
			} else {
932
				$killer->increaseHOF(-$return['KillerAlign'], array('Killing', 'NPC', 'Alignment', 'Loss'), HOF_PUBLIC);
933
			}
934
935
			$killer->increaseHOF($return['BountyGained']['Amount'], array('Killing', 'NPC', 'Money', 'Bounty Gained'), HOF_PUBLIC);
936
937
			$killer->increaseHOF(1, array('Killing', 'NPC Kills'), HOF_PUBLIC);
938
		} else {
939
			$killer->increaseHOF($return['KillerExp'], array('Killing', 'Experience', 'Gained'), HOF_PUBLIC);
940
			$killer->increaseHOF($this->getExperience(), array('Killing', 'Experience', 'Of Traders Killed'), HOF_PUBLIC);
941
942
			$killer->increaseHOF($return['DeadExp'], array('Killing', 'Experience', 'Lost By Traders Killed'), HOF_PUBLIC);
943
944
			$killer->increaseHOF($return['KillerCredits'], array('Killing', 'Money', 'Lost By Traders Killed'), HOF_PUBLIC);
945
			$killer->increaseHOF($return['KillerCredits'], array('Killing', 'Money', 'Gain'), HOF_PUBLIC);
946
			$killer->increaseHOF($this->getShip()->getCost(), array('Killing', 'Money', 'Cost Of Ships Killed'), HOF_PUBLIC);
947
948
			if ($return['KillerAlign'] > 0) {
949
				$killer->increaseHOF($return['KillerAlign'], array('Killing', 'Alignment', 'Gain'), HOF_PUBLIC);
950
			} else {
951
				$killer->increaseHOF(-$return['KillerAlign'], array('Killing', 'Alignment', 'Loss'), HOF_PUBLIC);
952
			}
953
954
			$killer->increaseHOF($return['BountyGained']['Amount'], array('Killing', 'Money', 'Bounty Gained'), HOF_PUBLIC);
955
956
			if ($this->getShip()->getAttackRatingWithMaxCDs() <= MAX_ATTACK_RATING_NEWBIE && $this->hasNewbieStatus() && !$killer->hasNewbieStatus()) { //Newbie kill
957
				$killer->increaseHOF(1, array('Killing', 'Newbie Kills'), HOF_PUBLIC);
958
			} else {
959
				$killer->increaseKills(1);
960
				$killer->increaseHOF(1, array('Killing', 'Kills'), HOF_PUBLIC);
961
962
				if ($killer->hasAlliance()) {
963
					$this->db->query('UPDATE alliance SET alliance_kills=alliance_kills+1 WHERE alliance_id=' . $this->db->escapeNumber($killer->getAllianceID()) . ' AND game_id=' . $this->db->escapeNumber($killer->getGameID()) . ' LIMIT 1');
964
				}
965
966
				// alliance vs. alliance stats
967
				$this->incrementAllianceVsDeaths($killer->getAllianceID());
968
			}
969
		}
970
971
		$this->increaseHOF($return['BountyGained']['Amount'], array('Dying', 'Players', 'Money', 'Bounty Gained By Killer'), HOF_PUBLIC);
972
		$this->increaseHOF($return['KillerExp'], array('Dying', 'Players', 'Experience', 'Gained By Killer'), HOF_PUBLIC);
973
		$this->increaseHOF($return['DeadExp'], array('Dying', 'Experience', 'Lost'), HOF_PUBLIC);
974
		$this->increaseHOF($return['DeadExp'], array('Dying', 'Players', 'Experience', 'Lost'), HOF_PUBLIC);
975
		$this->increaseHOF($return['KillerCredits'], array('Dying', 'Players', 'Money Lost'), HOF_PUBLIC);
976
		$this->increaseHOF($this->getShip()->getCost(), array('Dying', 'Players', 'Money', 'Cost Of Ships Lost'), HOF_PUBLIC);
977
		$this->increaseHOF(1, array('Dying', 'Players', 'Deaths'), HOF_PUBLIC);
978
979
		$this->killPlayer($this->getSectorID());
980
		return $return;
981
	}
982
983
	public function &killPlayerByForces(SmrForce $forces) {
984
		$return = array();
985
		$owner = $forces->getOwner();
986
		// send a message to the person who died
987
		self::sendMessageFromFedClerk($this->getGameID(), $owner->getAccountID(), 'Your forces <span class="red">DESTROYED </span>' . $this->getBBLink() . ' in sector ' . Globals::getSectorBBLink($forces->getSectorID()));
988
		self::sendMessageFromFedClerk($this->getGameID(), $this->getAccountID(), 'You were <span class="red">DESTROYED</span> by ' . $owner->getBBLink() . '\'s forces in sector ' . Globals::getSectorBBLink($this->getSectorID()));
989
990
		$news_message = $this->getBBLink();
991
		if ($this->hasCustomShipName()) {
992
			$named_ship = strip_tags($this->getCustomShipName(), '<font><span><img>');
993
			$news_message .= ' flying <span class="yellow">' . $named_ship . '</span>';
994
		}
995
		$news_message .= ' was destroyed by ' . $owner->getBBLink() . '\'s forces in sector ' . Globals::getSectorBBLink($forces->getSectorID());
996
		// insert the news entry
997
		$this->db->query('INSERT INTO news (game_id, time, news_message,killer_id,killer_alliance,dead_id,dead_alliance)
998
						VALUES(' . $this->db->escapeNumber($this->getGameID()) . ', ' . $this->db->escapeNumber(TIME) . ', ' . $this->db->escapeString($news_message) . ',' . $this->db->escapeNumber($owner->getAccountID()) . ',' . $this->db->escapeNumber($owner->getAllianceID()) . ',' . $this->db->escapeNumber($this->getAccountID()) . ',' . $this->db->escapeNumber($this->getAllianceID()) . ')');
999
1000
		// Player loses 15% experience
1001
		$expLossPercentage = .15;
1002
		$return['DeadExp'] = IFloor($this->getExperience() * $expLossPercentage);
1003
		$this->decreaseExperience($return['DeadExp']);
1004
1005
		$return['LostCredits'] = $this->getCredits();
1006
1007
		// alliance vs. alliance stats
1008
		$this->incrementAllianceVsDeaths(ALLIANCE_VS_FORCES);
1009
		$owner->incrementAllianceVsKills(ALLIANCE_VS_FORCES);
1010
1011
		$this->increaseHOF($return['DeadExp'], array('Dying', 'Experience', 'Lost'), HOF_PUBLIC);
1012
		$this->increaseHOF($return['DeadExp'], array('Dying', 'Forces', 'Experience Lost'), HOF_PUBLIC);
1013
		$this->increaseHOF($return['LostCredits'], array('Dying', 'Forces', 'Money Lost'), HOF_PUBLIC);
1014
		$this->increaseHOF($this->getShip()->getCost(), array('Dying', 'Forces', 'Cost Of Ships Lost'), HOF_PUBLIC);
1015
		$this->increaseHOF(1, array('Dying', 'Forces', 'Deaths'), HOF_PUBLIC);
1016
1017
		$this->killPlayer($forces->getSectorID());
1018
		return $return;
1019
	}
1020
1021
	public function &killPlayerByPort(SmrPort $port) {
1022
		$return = array();
1023
		// send a message to the person who died
1024
		self::sendMessageFromFedClerk($this->getGameID(), $this->getAccountID(), 'You were <span class="red">DESTROYED</span> by the defenses of ' . $port->getDisplayName());
1025
1026
		$news_message = $this->getBBLink();
1027
		if ($this->hasCustomShipName()) {
1028
			$named_ship = strip_tags($this->getCustomShipName(), '<font><span><img>');
1029
			$news_message .= ' flying <span class="yellow">' . $named_ship . '</span>';
1030
		}
1031
		$news_message .= ' was destroyed while invading ' . $port->getDisplayName() . '.';
1032
		// insert the news entry
1033
		$this->db->query('INSERT INTO news (game_id, time, news_message,killer_id,dead_id,dead_alliance)
1034
						VALUES(' . $this->db->escapeNumber($this->getGameID()) . ', ' . $this->db->escapeNumber(TIME) . ', ' . $this->db->escapeString($news_message) . ',' . $this->db->escapeNumber(ACCOUNT_ID_PORT) . ',' . $this->db->escapeNumber($this->getAccountID()) . ',' . $this->db->escapeNumber($this->getAllianceID()) . ')');
1035
1036
		// Player loses between 15% and 20% experience
1037
		$expLossPercentage = .20 - .05 * ($port->getLevel() - 1) / ($port->getMaxLevel() - 1);
1038
		$return['DeadExp'] = max(0, IFloor($this->getExperience() * $expLossPercentage));
1039
		$this->decreaseExperience($return['DeadExp']);
1040
1041
		$return['LostCredits'] = $this->getCredits();
1042
1043
		// alliance vs. alliance stats
1044
		$this->incrementAllianceVsDeaths(ALLIANCE_VS_PORTS);
1045
1046
		$this->increaseHOF($return['DeadExp'], array('Dying', 'Experience', 'Lost'), HOF_PUBLIC);
1047
		$this->increaseHOF($return['DeadExp'], array('Dying', 'Ports', 'Experience Lost'), HOF_PUBLIC);
1048
		$this->increaseHOF($return['LostCredits'], array('Dying', 'Ports', 'Money Lost'), HOF_PUBLIC);
1049
		$this->increaseHOF($this->getShip()->getCost(), array('Dying', 'Ports', 'Cost Of Ships Lost'), HOF_PUBLIC);
1050
		$this->increaseHOF(1, array('Dying', 'Ports', 'Deaths'), HOF_PUBLIC);
1051
1052
		$this->killPlayer($port->getSectorID());
1053
		return $return;
1054
	}
1055
1056
	public function &killPlayerByPlanet(SmrPlanet $planet) {
1057
		$return = array();
1058
		// send a message to the person who died
1059
		$planetOwner = $planet->getOwner();
1060
		self::sendMessageFromFedClerk($this->getGameID(), $planetOwner->getAccountID(), 'Your planet <span class="red">DESTROYED</span>&nbsp;' . $this->getBBLink() . ' in sector ' . Globals::getSectorBBLink($planet->getSectorID()));
1061
		self::sendMessageFromFedClerk($this->getGameID(), $this->getAccountID(), 'You were <span class="red">DESTROYED</span> by the planetary defenses of ' . $planet->getDisplayName());
1062
1063
		$news_message = $this->getBBLink();
1064
		if ($this->hasCustomShipName()) {
1065
			$named_ship = strip_tags($this->getCustomShipName(), '<font><span><img>');
1066
			$news_message .= ' flying <span class="yellow">' . $named_ship . '</span>';
1067
		}
1068
		$news_message .= ' was destroyed by ' . $planet->getDisplayName() . '\'s planetary defenses in sector ' . Globals::getSectorBBLink($planet->getSectorID()) . '.';
1069
		// insert the news entry
1070
		$this->db->query('INSERT INTO news (game_id, time, news_message,killer_id,killer_alliance,dead_id,dead_alliance)
1071
						VALUES(' . $this->db->escapeNumber($this->getGameID()) . ', ' . $this->db->escapeNumber(TIME) . ', ' . $this->db->escapeString($news_message) . ',' . $this->db->escapeNumber($planetOwner->getAccountID()) . ',' . $this->db->escapeNumber($planetOwner->getAllianceID()) . ',' . $this->db->escapeNumber($this->getAccountID()) . ',' . $this->db->escapeNumber($this->getAllianceID()) . ')');
1072
1073
		// Player loses between 15% and 20% experience
1074
		$expLossPercentage = .20 - .05 * $planet->getLevel() / $planet->getMaxLevel();
1075
		$return['DeadExp'] = max(0, IFloor($this->getExperience() * $expLossPercentage));
1076
		$this->decreaseExperience($return['DeadExp']);
1077
1078
		$return['LostCredits'] = $this->getCredits();
1079
1080
		// alliance vs. alliance stats
1081
		$this->incrementAllianceVsDeaths(ALLIANCE_VS_PLANETS);
1082
		$planetOwner->incrementAllianceVsKills(ALLIANCE_VS_PLANETS);
1083
1084
		$this->increaseHOF($return['DeadExp'], array('Dying', 'Experience', 'Lost'), HOF_PUBLIC);
1085
		$this->increaseHOF($return['DeadExp'], array('Dying', 'Planets', 'Experience Lost'), HOF_PUBLIC);
1086
		$this->increaseHOF($return['LostCredits'], array('Dying', 'Planets', 'Money Lost'), HOF_PUBLIC);
1087
		$this->increaseHOF($this->getShip()->getCost(), array('Dying', 'Planets', 'Cost Of Ships Lost'), HOF_PUBLIC);
1088
		$this->increaseHOF(1, array('Dying', 'Planets', 'Deaths'), HOF_PUBLIC);
1089
1090
		$this->killPlayer($planet->getSectorID());
1091
		return $return;
1092
	}
1093
1094
	public function save() {
1095
		if ($this->hasChanged === true) {
1096
			$this->db->query('UPDATE player SET player_name=' . $this->db->escapeString($this->playerName) .
1097
				', player_id=' . $this->db->escapeNumber($this->playerID) .
1098
				', sector_id=' . $this->db->escapeNumber($this->sectorID) .
1099
				', last_sector_id=' . $this->db->escapeNumber($this->lastSectorID) .
1100
				', turns=' . $this->db->escapeNumber($this->turns) .
1101
				', last_turn_update=' . $this->db->escapeNumber($this->lastTurnUpdate) .
1102
				', newbie_turns=' . $this->db->escapeNumber($this->newbieTurns) .
1103
				', last_news_update=' . $this->db->escapeNumber($this->lastNewsUpdate) .
1104
				', attack_warning=' . $this->db->escapeString($this->attackColour) .
1105
				', dead=' . $this->db->escapeBoolean($this->dead) .
1106
				', newbie_status=' . $this->db->escapeBoolean($this->newbieStatus) .
1107
				', land_on_planet=' . $this->db->escapeBoolean($this->landedOnPlanet) .
1108
				', last_active=' . $this->db->escapeNumber($this->lastActive) .
1109
				', last_cpl_action=' . $this->db->escapeNumber($this->lastCPLAction) .
1110
				', race_id=' . $this->db->escapeNumber($this->raceID) .
1111
				', credits=' . $this->db->escapeNumber($this->credits) .
1112
				', experience=' . $this->db->escapeNumber($this->experience) .
1113
				', alignment=' . $this->db->escapeNumber($this->alignment) .
1114
				', military_payment=' . $this->db->escapeNumber($this->militaryPayment) .
1115
//				', past_knowledge='.$this->db->escapeString($this->pastKnowledge).
1116
				', alliance_id=' . $this->db->escapeNumber($this->allianceID) .
1117
				', alliance_join=' . $this->db->escapeNumber($this->allianceJoinable) .
1118
				', ship_type_id=' . $this->db->escapeNumber($this->shipID) .
1119
				', kills=' . $this->db->escapeNumber($this->kills) .
1120
				', deaths=' . $this->db->escapeNumber($this->deaths) .
1121
				', assists=' . $this->db->escapeNumber($this->assists) .
1122
				', last_port=' . $this->db->escapeNumber($this->lastPort) .
1123
				', bank=' . $this->db->escapeNumber($this->bank) .
1124
				', zoom=' . $this->db->escapeNumber($this->zoom) .
1125
				', display_missions=' . $this->db->escapeBoolean($this->displayMissions) .
1126
				', force_drop_messages=' . $this->db->escapeBoolean($this->forceDropMessages) .
1127
				', group_scout_messages=' . $this->db->escapeString($this->groupScoutMessages) .
1128
				', ignore_globals=' . $this->db->escapeBoolean($this->ignoreGlobals) .
1129
				', newbie_warning = ' . $this->db->escapeBoolean($this->newbieWarning) .
1130
				', name_changed = ' . $this->db->escapeBoolean($this->nameChanged) .
1131
				', combat_drones_kamikaze_on_mines = ' . $this->db->escapeBoolean($this->combatDronesKamikazeOnMines) .
1132
				' WHERE ' . $this->SQL . ' LIMIT 1');
1133
			$this->hasChanged = false;
1134
		}
1135
		foreach ($this->hasBountyChanged as $key => &$bountyChanged) {
1136
			if ($bountyChanged === true) {
1137
				$bountyChanged = false;
1138
				$bounty = $this->getBounty($key);
1139
				if ($bounty['New'] === true) {
1140
					if ($bounty['Amount'] > 0 || $bounty['SmrCredits'] > 0) {
1141
						$this->db->query('INSERT INTO bounty (account_id,game_id,type,amount,smr_credits,claimer_id,time) VALUES (' . $this->db->escapeNumber($this->getAccountID()) . ',' . $this->db->escapeNumber($this->getGameID()) . ',' . $this->db->escapeString($bounty['Type']) . ',' . $this->db->escapeNumber($bounty['Amount']) . ',' . $this->db->escapeNumber($bounty['SmrCredits']) . ',' . $this->db->escapeNumber($bounty['Claimer']) . ',' . $this->db->escapeNumber($bounty['Time']) . ')');
1142
					}
1143
				} else {
1144
					if ($bounty['Amount'] > 0 || $bounty['SmrCredits'] > 0) {
1145
						$this->db->query('UPDATE bounty
1146
							SET amount=' . $this->db->escapeNumber($bounty['Amount']) . ',
1147
							smr_credits=' . $this->db->escapeNumber($bounty['SmrCredits']) . ',
1148
							type=' . $this->db->escapeString($bounty['Type']) . ',
1149
							claimer_id=' . $this->db->escapeNumber($bounty['Claimer']) . ',
1150
							time=' . $this->db->escapeNumber($bounty['Time']) . '
1151
							WHERE bounty_id=' . $this->db->escapeNumber($bounty['ID']) . ' AND ' . $this->SQL . ' LIMIT 1');
1152
					} else {
1153
						$this->db->query('DELETE FROM bounty WHERE bounty_id=' . $this->db->escapeNumber($bounty['ID']) . ' AND ' . $this->SQL . ' LIMIT 1');
1154
					}
1155
				}
1156
			}
1157
		}
1158
		$this->saveHOF();
1159
	}
1160
1161
	public function saveHOF() {
1162
		if ($this->hasHOFChanged !== false) {
1163
			$this->doHOFSave($this->hasHOFChanged);
0 ignored issues
show
Bug introduced by
$this->hasHOFChanged of type true is incompatible with the type array expected by parameter $hasChangedList of SmrPlayer::doHOFSave(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1163
			$this->doHOFSave(/** @scrutinizer ignore-type */ $this->hasHOFChanged);
Loading history...
1164
		}
1165
		if (!empty(self::$hasHOFVisChanged)) {
1166
			foreach (self::$hasHOFVisChanged as $hofType => $changeType) {
1167
				if ($changeType == self::HOF_NEW) {
1168
					$this->db->query('INSERT INTO hof_visibility (type, visibility) VALUES (' . $this->db->escapeString($hofType) . ',' . $this->db->escapeString(self::$HOFVis[$hofType]) . ')');
1169
				} else {
1170
					$this->db->query('UPDATE hof_visibility SET visibility = ' . $this->db->escapeString(self::$HOFVis[$hofType]) . ' WHERE type = ' . $this->db->escapeString($hofType) . ' LIMIT 1');
1171
				}
1172
				unset(self::$hasHOFVisChanged[$hofType]);
1173
			}
1174
		}
1175
	}
1176
	protected function doHOFSave(array &$hasChangedList, array $typeList = array()) {
1177
		foreach ($hasChangedList as $type => &$hofChanged) {
1178
			$tempTypeList = $typeList;
1179
			$tempTypeList[] = $type;
1180
			if (is_array($hofChanged)) {
1181
				$this->doHOFSave($hofChanged, $tempTypeList);
1182
			} else {
1183
				$amount = $this->getHOF($tempTypeList);
1184
				if ($hofChanged == self::HOF_NEW) {
1185
					if ($amount > 0) {
1186
						$this->db->query('INSERT INTO player_hof (account_id,game_id,type,amount) VALUES (' . $this->db->escapeNumber($this->getAccountID()) . ',' . $this->db->escapeNumber($this->getGameID()) . ',' . $this->db->escapeArray($tempTypeList, false, true, ':', false) . ',' . $this->db->escapeNumber($amount) . ')');
1187
					}
1188
				} elseif ($hofChanged == self::HOF_CHANGED) {
1189
	//				if($amount > 0)
1190
						$this->db->query('UPDATE player_hof
1191
							SET amount=' . $this->db->escapeNumber($amount) . '
1192
							WHERE ' . $this->SQL . ' AND type = ' . $this->db->escapeArray($tempTypeList, false, true, ':', false) . ' LIMIT 1');
1193
	//				else
1194
	//					$this->db->query('DELETE FROM player_hof WHERE account_id=' . $this->getAccountID() . ' AND game_id = ' . $this->getGameID() . ' AND type = ' . $this->db->escapeArray($tempTypeList,false,true,':',false) . ' LIMIT 1');
1195
	//				}
1196
				}
1197
				$hofChanged = false;
1198
			}
1199
		}
1200
	}
1201
1202
	protected function getHOFData() {
1203
		if (!isset($this->HOF)) {
1204
			//Get Player HOF
1205
			$this->db->query('SELECT type,amount FROM player_hof WHERE ' . $this->SQL);
1206
			$this->HOF = array();
1207
			while ($this->db->nextRecord()) {
1208
				$hof =& $this->HOF;
1209
				$typeList = explode(':', $this->db->getField('type'));
1210
				foreach ($typeList as $type) {
1211
					if (!isset($hof[$type])) {
1212
						$hof[$type] = array();
1213
					}
1214
					$hof =& $hof[$type];
1215
				}
1216
				$hof = $this->db->getFloat('amount');
1217
			}
1218
			self::getHOFVis();
1219
		}
1220
	}
1221
1222
	public static function getHOFVis() {
1223
		if (!isset(self::$HOFVis)) {
1224
			//Get Player HOF Vis
1225
			$db = new SmrMySqlDatabase();
1226
			$db->query('SELECT type,visibility FROM hof_visibility');
1227
			self::$HOFVis = array();
1228
			while ($db->nextRecord()) {
1229
				self::$HOFVis[$db->getField('type')] = $db->getField('visibility');
1230
			}
1231
		}
1232
	}
1233
1234
	protected function getBountiesData() {
1235
		if (!isset($this->bounties)) {
1236
			$this->bounties = array();
1237
			$this->db->query('SELECT * FROM bounty WHERE ' . $this->SQL);
1238
			while ($this->db->nextRecord()) {
1239
				$this->bounties[$this->db->getInt('bounty_id')] = array(
1240
							'Amount' => $this->db->getInt('amount'),
1241
							'SmrCredits' => $this->db->getInt('smr_credits'),
1242
							'Type' => $this->db->getField('type'),
1243
							'Claimer' => $this->db->getInt('claimer_id'),
1244
							'Time' => $this->db->getInt('time'),
1245
							'ID' => $this->db->getInt('bounty_id'),
1246
							'New' => false);
1247
			}
1248
		}
1249
	}
1250
1251
	// Get bounties that can be claimed by this player
1252
	// Type must be 'HQ' or 'UG'
1253
	public function getClaimableBounties($type) {
1254
		$bounties = array();
1255
		$this->db->query('SELECT * FROM bounty WHERE claimer_id=' . $this->db->escapeNumber($this->getAccountID()) . ' AND game_id=' . $this->db->escapeNumber($this->getGameID()) . ' AND type=' . $this->db->escapeString($type));
1256
		while ($this->db->nextRecord()) {
1257
			$bounties[] = array(
1258
				'player' => SmrPlayer::getPlayer($this->db->getInt('account_id'), $this->getGameID()),
1259
				'bounty_id' => $this->db->getInt('bounty_id'),
1260
				'credits' => $this->db->getInt('amount'),
1261
				'smr_credits' => $this->db->getInt('smr_credits'),
1262
			);
1263
		}
1264
		return $bounties;
1265
	}
1266
1267
	/**
1268
	 * Has this player been designated as the alliance flagship?
1269
	 */
1270
	public function isFlagship() {
1271
		return $this->hasAlliance() && $this->getAlliance()->getFlagshipID() == $this->getAccountID();
1272
	}
1273
1274
	public function isPresident() {
1275
		return Council::getPresidentID($this->getGameID(), $this->getRaceID()) == $this->getAccountID();
1276
	}
1277
1278
	public function isOnCouncil() {
1279
		return Council::isOnCouncil($this->getGameID(), $this->getRaceID(), $this->getAccountID());
1280
	}
1281
1282
	public function setNewbieWarning($bool) {
1283
		if ($this->newbieWarning == $bool) {
1284
			return;
1285
		}
1286
		$this->newbieWarning = $bool;
1287
		$this->hasChanged = true;
1288
	}
1289
1290
	public function getNewbieWarning() {
1291
		return $this->newbieWarning;
1292
	}
1293
1294
	public function getTickers() {
1295
		if (!isset($this->tickers)) {
1296
			$this->tickers = array();
1297
			//get ticker info
1298
			$this->db->query('SELECT type,time,expires,recent FROM player_has_ticker WHERE ' . $this->SQL . ' AND expires > ' . $this->db->escapeNumber(TIME));
1299
			while ($this->db->nextRecord()) {
1300
				$this->tickers[$this->db->getField('type')] = array('Type' => $this->db->getField('type'),
1301
																				'Time' => $this->db->getInt('time'),
1302
																				'Expires' => $this->db->getInt('expires'),
1303
																				'Recent' => $this->db->getField('recent'));
1304
			}
1305
		}
1306
		return $this->tickers;
1307
	}
1308
1309
	public function hasTickers() {
1310
		return count($this->getTickers()) > 0;
1311
	}
1312
1313
	public function getTicker($tickerType) {
1314
		$tickers = $this->getTickers();
1315
		if (isset($tickers[$tickerType])) {
1316
			return $tickers[$tickerType];
1317
		}
1318
		return false;
1319
	}
1320
1321
	public function hasTicker($tickerType) {
1322
		return $this->getTicker($tickerType) !== false;
1323
	}
1324
1325
	public function getTurnsLevel() {
1326
		if (!$this->hasTurns()) {
1327
			return 'NONE';
1328
		}
1329
		if ($this->getTurns() <= 25) {
1330
			return 'LOW';
1331
		}
1332
		if ($this->getTurns() <= 75) {
1333
			return 'MEDIUM';
1334
		}
1335
		return 'HIGH';
1336
	}
1337
1338
	/**
1339
	 * Returns the CSS class color to use when displaying the player's turns
1340
	 */
1341
	public function getTurnsColor() {
1342
		switch ($this->getTurnsLevel()) {
1343
			case 'NONE':
1344
			case 'LOW':
1345
				return 'red';
1346
			break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
1347
			case 'MEDIUM':
1348
				return 'yellow';
1349
			break;
1350
			default:
1351
				return 'green';
1352
		}
1353
	}
1354
1355
	public function update() {
1356
		$this->save();
1357
	}
1358
1359
	protected static function doMessageSending($senderID, $receiverID, $gameID, $messageTypeID, $message, $expires, $senderDelete = false, $unread = true) {
1360
		$message = trim($message);
1361
		$db = new SmrMySqlDatabase();
1362
		// send him the message
1363
		$db->query('INSERT INTO message
1364
			(account_id,game_id,message_type_id,message_text,
1365
			sender_id,send_time,expire_time,sender_delete) VALUES(' .
1366
			$db->escapeNumber($receiverID) . ',' .
1367
			$db->escapeNumber($gameID) . ',' .
1368
			$db->escapeNumber($messageTypeID) . ',' .
1369
			$db->escapeString($message) . ',' .
1370
			$db->escapeNumber($senderID) . ',' .
1371
			$db->escapeNumber(TIME) . ',' .
1372
			$db->escapeNumber($expires) . ',' .
1373
			$db->escapeBoolean($senderDelete) . ')'
1374
		);
1375
1376
		if ($unread === true) {
1377
			// give him the message icon
1378
			$db->query('REPLACE INTO player_has_unread_messages (game_id, account_id, message_type_id) VALUES
1379
						(' . $db->escapeNumber($gameID) . ', ' . $db->escapeNumber($receiverID) . ', ' . $db->escapeNumber($messageTypeID) . ')');
1380
		}
1381
1382
		switch ($messageTypeID) {
1383
			case MSG_PLAYER:
1384
				$receiverAccount = SmrAccount::getAccount($receiverID);
1385
				if ($receiverAccount->isValidated() && $receiverAccount->isReceivingMessageNotifications($messageTypeID) && !$receiverAccount->isLoggedIn()) {
1386
					require_once(get_file_loc('message.functions.inc'));
1387
					$sender = getMessagePlayer($senderID, $gameID, $messageTypeID);
1388
					if ($sender instanceof SmrPlayer) {
1389
						$sender = $sender->getDisplayName();
1390
					}
1391
					$mail = setupMailer();
1392
					$mail->Subject = 'Message Notification';
1393
					$mail->setFrom('[email protected]', 'SMR Notifications');
1394
					$bbifiedMessage = 'From: ' . $sender . ' Date: ' . date($receiverAccount->getShortDateFormat() . ' ' . $receiverAccount->getShortTimeFormat(), TIME) . "<br/>\r\n<br/>\r\n" . bbifyMessage($message, true);
1395
					$mail->msgHTML($bbifiedMessage);
1396
					$mail->AltBody = strip_tags($bbifiedMessage);
1397
					$mail->addAddress($receiverAccount->getEmail(), $receiverAccount->getHofName());
1398
					$mail->send();
1399
					$receiverAccount->decreaseMessageNotifications($messageTypeID, 1);
1400
				}
1401
			break;
1402
		}
1403
	}
1404
1405
	public function sendMessageToBox($boxTypeID, $message) {
1406
		// send him the message
1407
		SmrAccount::doMessageSendingToBox($this->getAccountID(), $boxTypeID, $message, $this->getGameID());
1408
	}
1409
1410
	public function sendGlobalMessage($message, $canBeIgnored = true) {
1411
		if ($canBeIgnored) {
1412
			if ($this->getAccount()->isMailBanned()) {
1413
				create_error('You are currently banned from sending messages');
1414
			}
1415
		}
1416
		$this->sendMessageToBox(BOX_GLOBALS, $message);
1417
1418
		// send to all online player
1419
		$db = new SmrMySqlDatabase();
1420
		$db->query('SELECT account_id
1421
					FROM active_session
1422
					JOIN player USING (game_id, account_id)
1423
					WHERE active_session.last_accessed >= ' . $db->escapeNumber(TIME - SmrSession::TIME_BEFORE_EXPIRY) . '
1424
						AND game_id = ' . $db->escapeNumber($this->getGameID()) . '
1425
						AND ignore_globals = \'FALSE\'
1426
						AND account_id != ' . $db->escapeNumber($this->getAccountID()));
1427
1428
		while ($db->nextRecord()) {
1429
			$this->sendMessage($db->getInt('account_id'), MSG_GLOBAL, $message, $canBeIgnored);
1430
		}
1431
		$this->sendMessage($this->getAccountID(), MSG_GLOBAL, $message, $canBeIgnored, false);
1432
	}
1433
1434
	public function sendMessage($receiverID, $messageTypeID, $message, $canBeIgnored = true, $unread = true, $expires = false, $senderDelete = false) {
1435
		//get expire time
1436
		if ($canBeIgnored) {
1437
			if ($this->getAccount()->isMailBanned()) {
1438
				create_error('You are currently banned from sending messages');
1439
			}
1440
			// Don't send messages to players ignoring us
1441
			$this->db->query('SELECT account_id FROM message_blacklist WHERE account_id=' . $this->db->escapeNumber($receiverID) . ' AND blacklisted_id=' . $this->db->escapeNumber($this->getAccountID()) . ' LIMIT 1');
1442
			if ($this->db->nextRecord()) {
1443
				return;
1444
			}
1445
		}
1446
1447
		$message = word_filter($message);
1448
1449
		// If expires not specified, use default based on message type
1450
		if ($expires === false) {
1451
			switch ($messageTypeID) {
1452
				case MSG_GLOBAL: //We don't send globals to the box here or it gets done loads of times.
1453
					$expires = 3600; // 1h
1454
				break;
1455
				case MSG_PLAYER:
1456
					$expires = 86400 * 31;
1457
				break;
1458
				case MSG_PLANET:
1459
					$expires = 86400 * 7;
1460
				break;
1461
				case MSG_SCOUT:
1462
					$expires = 86400 * 3;
1463
				break;
1464
				case MSG_POLITICAL:
1465
					$expires = 86400 * 31;
1466
				break;
1467
				case MSG_ALLIANCE:
1468
					$expires = 86400 * 31;
1469
				break;
1470
				case MSG_ADMIN:
1471
					$expires = 86400 * 365;
1472
				break;
1473
				case MSG_CASINO:
1474
					$expires = 86400 * 31;
1475
				break;
1476
				default:
1477
					$expires = 86400 * 7;
1478
			}
1479
			$expires += TIME;
1480
		}
1481
1482
		// Do not put scout messages in the sender's sent box
1483
		if ($messageTypeID == MSG_SCOUT) {
1484
			$senderDelete = true;
1485
		}
1486
1487
		// send him the message
1488
		self::doMessageSending($this->getAccountID(), $receiverID, $this->getGameID(), $messageTypeID, $message, $expires, $senderDelete, $unread);
1489
	}
1490
1491
	public function sendMessageFromOpAnnounce($receiverID, $message, $expires = false) {
1492
		// get expire time if not set
1493
		if ($expires === false) {
1494
			$expires = TIME + 86400 * 14;
1495
		}
1496
		self::doMessageSending(ACCOUNT_ID_OP_ANNOUNCE, $receiverID, $this->getGameID(), MSG_ALLIANCE, $message, $expires);
1497
	}
1498
1499
	public function sendMessageFromAllianceCommand($receiverID, $message) {
1500
		$expires = TIME + 86400 * 365;
1501
		self::doMessageSending(ACCOUNT_ID_ALLIANCE_COMMAND, $receiverID, $this->getGameID(), MSG_PLAYER, $message, $expires);
1502
	}
1503
1504
	public static function sendMessageFromPlanet($gameID, $receiverID, $message) {
1505
		//get expire time
1506
		$expires = TIME + 86400 * 31;
1507
		// send him the message
1508
		self::doMessageSending(ACCOUNT_ID_PLANET, $receiverID, $gameID, MSG_PLANET, $message, $expires);
1509
	}
1510
1511
	public static function sendMessageFromPort($gameID, $receiverID, $message) {
1512
		//get expire time
1513
		$expires = TIME + 86400 * 31;
1514
		// send him the message
1515
		self::doMessageSending(ACCOUNT_ID_PORT, $receiverID, $gameID, MSG_PLAYER, $message, $expires);
1516
	}
1517
1518
	public static function sendMessageFromFedClerk($gameID, $receiverID, $message) {
1519
		$expires = TIME + 86400 * 365;
1520
		self::doMessageSending(ACCOUNT_ID_FED_CLERK, $receiverID, $gameID, MSG_PLAYER, $message, $expires);
1521
	}
1522
1523
	public static function sendMessageFromAdmin($gameID, $receiverID, $message, $expires = false) {
1524
		//get expire time
1525
		if ($expires === false) {
1526
			$expires = TIME + 86400 * 365;
1527
		}
1528
		// send him the message
1529
		self::doMessageSending(ACCOUNT_ID_ADMIN, $receiverID, $gameID, MSG_ADMIN, $message, $expires);
1530
	}
1531
1532
	public static function sendMessageFromAllianceAmbassador($gameID, $receiverID, $message, $expires = false) {
1533
		//get expire time
1534
		if ($expires === false) {
1535
			$expires = TIME + 86400 * 31;
1536
		}
1537
		// send him the message
1538
		self::doMessageSending(ACCOUNT_ID_ALLIANCE_AMBASSADOR, $receiverID, $gameID, MSG_ALLIANCE, $message, $expires);
1539
	}
1540
1541
	public static function sendMessageFromCasino($gameID, $receiverID, $message, $expires = false) {
1542
		//get expire time
1543
		if ($expires === false) {
1544
			$expires = TIME + 86400 * 7;
1545
		}
1546
		// send him the message
1547
		self::doMessageSending(ACCOUNT_ID_CASINO, $receiverID, $gameID, MSG_CASINO, $message, $expires);
1548
	}
1549
1550
	public static function sendMessageFromRace($raceID, $gameID, $receiverID, $message, $expires = false) {
1551
		//get expire time
1552
		if ($expires === false) {
1553
			$expires = TIME + 86400 * 5;
1554
		}
1555
		// send him the message
1556
		self::doMessageSending(ACCOUNT_ID_GROUP_RACES + $raceID, $receiverID, $gameID, MSG_POLITICAL, $message, $expires);
1557
	}
1558
1559
	public function setMessagesRead($messageTypeID) {
1560
		$this->db->query('DELETE FROM player_has_unread_messages
1561
							WHERE '.$this->SQL . ' AND message_type_id = ' . $this->db->escapeNumber($messageTypeID));
1562
	}
1563
1564
	public function getPlottedCourse() {
1565
		if (!isset($this->plottedCourse)) {
1566
			// check if we have a course plotted
1567
			$this->db->query('SELECT course FROM player_plotted_course WHERE ' . $this->SQL . ' LIMIT 1');
1568
1569
			if ($this->db->nextRecord()) {
1570
				// get the course back
1571
				$this->plottedCourse = unserialize($this->db->getField('course'));
1572
			} else {
1573
				$this->plottedCourse = false;
1574
			}
1575
		}
1576
1577
		// Update the plotted course if we have moved since the last query
1578
		if ($this->plottedCourse !== false && (!isset($this->plottedCourseFrom) || $this->plottedCourseFrom != $this->getSectorID())) {
1579
			$this->plottedCourseFrom = $this->getSectorID();
1580
1581
			if ($this->plottedCourse->getNextOnPath() == $this->getSectorID()) {
1582
				// We have walked into the next sector of the course
1583
				$this->plottedCourse->followPath();
1584
				$this->setPlottedCourse($this->plottedCourse);
1585
			} elseif ($this->plottedCourse->isInPath($this->getSectorID())) {
1586
				// We have skipped to some later sector in the course
1587
				$this->plottedCourse->skipToSector($this->getSectorID());
1588
				$this->setPlottedCourse($this->plottedCourse);
1589
			}
1590
		}
1591
		return $this->plottedCourse;
1592
	}
1593
1594
	public function setPlottedCourse(Distance $plottedCourse) {
1595
		$hadPlottedCourse = $this->hasPlottedCourse();
1596
		$this->plottedCourse = $plottedCourse;
1597
		if ($this->plottedCourse->getTotalSectors() > 0) {
1598
			$this->db->query('REPLACE INTO player_plotted_course
1599
				(account_id, game_id, course)
1600
				VALUES(' . $this->db->escapeNumber($this->getAccountID()) . ', ' . $this->db->escapeNumber($this->getGameID()) . ', ' . $this->db->escapeBinary(serialize($this->plottedCourse)) . ')');
1601
		} elseif ($hadPlottedCourse) {
1602
			$this->deletePlottedCourse();
1603
		}
1604
	}
1605
1606
	public function hasPlottedCourse() {
1607
		return $this->getPlottedCourse() !== false;
1608
	}
1609
1610
	public function isPartOfCourse($sectorOrSectorID) {
1611
		if (!$this->hasPlottedCourse()) {
1612
			return false;
1613
		}
1614
		if ($sectorOrSectorID instanceof SmrSector) {
1615
			$sectorID = $sectorOrSectorID->getSectorID();
1616
		} else {
1617
			$sectorID = $sectorOrSectorID;
1618
		}
1619
		return $this->getPlottedCourse()->isInPath($sectorID);
1620
	}
1621
1622
	public function deletePlottedCourse() {
1623
		$this->plottedCourse = false;
1624
		$this->db->query('DELETE FROM player_plotted_course WHERE ' . $this->SQL . ' LIMIT 1');
1625
	}
1626
1627
	// Computes the turn cost and max misjump between current and target sector
1628
	public function getJumpInfo(SmrSector $targetSector) {
1629
		$path = Plotter::findDistanceToX($targetSector, $this->getSector(), true);
1630
		if ($path === false) {
1631
			create_error('Unable to plot from ' . $this->getSectorID() . ' to ' . $targetSector->getSectorID() . '.');
1632
		}
1633
		$distance = $path->getRelativeDistance();
1634
1635
		$turnCost = max(TURNS_JUMP_MINIMUM, IRound($distance * TURNS_PER_JUMP_DISTANCE));
1636
		$maxMisjump = max(0, IRound(($distance - $turnCost) * MISJUMP_DISTANCE_DIFF_FACTOR / (1 + $this->getLevelID() * MISJUMP_LEVEL_FACTOR)));
1637
		return array('turn_cost' => $turnCost, 'max_misjump' => $maxMisjump);
1638
	}
1639
1640
	public function __sleep() {
1641
		return array('accountID', 'gameID', 'sectorID', 'alignment', 'playerID', 'playerName');
1642
	}
1643
1644
	public function &getStoredDestinations() {
1645
		if (!isset($this->storedDestinations)) {
1646
			$this->storedDestinations = array();
1647
			$this->db->query('SELECT * FROM player_stored_sector WHERE ' . $this->SQL);
1648
			while ($this->db->nextRecord()) {
1649
				$this->storedDestinations[] = array(
1650
					'Label' => $this->db->getField('label'),
1651
					'SectorID' => $this->db->getInt('sector_id'),
1652
					'OffsetTop' => $this->db->getInt('offset_top'),
1653
					'OffsetLeft' => $this->db->getInt('offset_left')
1654
				);
1655
			}
1656
		}
1657
		return $this->storedDestinations;
1658
	}
1659
1660
1661
	public function moveDestinationButton($sectorID, $offsetTop, $offsetLeft) {
1662
1663
		if (!is_numeric($offsetLeft) || !is_numeric($offsetTop)) {
1664
			create_error('The position of the saved sector must be numeric!.');
1665
		}
1666
		$offsetTop = round($offsetTop);
1667
		$offsetLeft = round($offsetLeft);
1668
1669
		if ($offsetLeft < 0 || $offsetLeft > 500 || $offsetTop < 0 || $offsetTop > 300) {
1670
			create_error('The saved sector must be in the box!');
1671
		}
1672
1673
		$storedDestinations =& $this->getStoredDestinations();
1674
		foreach ($storedDestinations as &$sd) {
1675
			if ($sd['SectorID'] == $sectorID) {
1676
				$sd['OffsetTop'] = $offsetTop;
1677
				$sd['OffsetLeft'] = $offsetLeft;
1678
				$this->db->query('
1679
					UPDATE player_stored_sector
1680
						SET offset_left = ' . $this->db->escapeNumber($offsetLeft) . ', offset_top=' . $this->db->escapeNumber($offsetTop) . '
1681
					WHERE ' . $this->SQL . ' AND sector_id = ' . $this->db->escapeNumber($sectorID)
1682
				);
1683
				return true;
1684
			}
1685
		}
1686
1687
		create_error('You do not have a saved sector for #' . $sectorID);
1688
	}
1689
1690
	public function addDestinationButton($sectorID, $label) {
1691
1692
		if (!is_numeric($sectorID) || !SmrSector::sectorExists($this->getGameID(), $sectorID)) {
1693
			create_error('You want to add a non-existent sector?');
1694
		}
1695
1696
		// sector already stored ?
1697
		foreach ($this->getStoredDestinations() as $sd) {
1698
			if ($sd['SectorID'] == $sectorID) {
1699
				create_error('Sector already stored!');
1700
			}
1701
		}
1702
1703
		$this->storedDestinations[] = array(
1704
			'Label' => $label,
1705
			'SectorID' => (int)$sectorID,
1706
			'OffsetTop' => 1,
1707
			'OffsetLeft' => 1
1708
		);
1709
1710
		$this->db->query('
1711
			INSERT INTO player_stored_sector (account_id, game_id, sector_id, label, offset_top, offset_left)
1712
			VALUES (' . $this->db->escapeNumber($this->getAccountID()) . ', ' . $this->db->escapeNumber($this->getGameID()) . ', ' . $this->db->escapeNumber($sectorID) . ',' . $this->db->escapeString($label, true) . ',1,1)'
1713
		);
1714
	}
1715
1716
	public function deleteDestinationButton($sectorID) {
1717
		if (!is_numeric($sectorID) || $sectorID < 1) {
1718
			create_error('You want to remove a non-existent sector?');
1719
		}
1720
1721
		foreach ($this->getStoredDestinations() as $key => $sd) {
1722
			if ($sd['SectorID'] == $sectorID) {
1723
				$this->db->query('
1724
					DELETE FROM player_stored_sector
1725
					WHERE ' . $this->SQL . '
1726
					AND sector_id = ' . $this->db->escapeNumber($sectorID)
1727
				);
1728
				unset($this->storedDestinations[$key]);
1729
				return true;
1730
			}
1731
		}
1732
		return false;
1733
	}
1734
1735
	public function getExperienceRank() {
1736
		return $this->computeRanking('experience', $this->getExperience());
1737
	}
1738
	public function getKillsRank() {
1739
		return $this->computeRanking('kills', $this->getKills());
1740
	}
1741
	public function getDeathsRank() {
1742
		return $this->computeRanking('deaths', $this->getDeaths());
1743
	}
1744
	public function getAssistsRank() {
1745
		return $this->computeRanking('assists', $this->getAssists());
1746
	}
1747
	private function computeRanking($dbField, $playerAmount) {
1748
		$this->db->query('SELECT count(*) FROM player
1749
			WHERE game_id = ' . $this->db->escapeNumber($this->getGameID()) . '
1750
			AND (
1751
				'.$dbField . ' > ' . $this->db->escapeNumber($playerAmount) . '
1752
				OR (
1753
					'.$dbField . ' = ' . $this->db->escapeNumber($playerAmount) . '
1754
					AND player_name <= ' . $this->db->escapeString($this->getPlayerName()) . '
1755
				)
1756
			)');
1757
		$this->db->nextRecord();
1758
		$rank = $this->db->getInt('count(*)');
1759
		return $rank;
1760
	}
1761
}
1762