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
Push — master ( f73b31...039d88 )
by Dan
04:23
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
	protected function __construct($gameID, $accountID, $db = null) {
151
		parent::__construct();
152
		$this->db = new SmrMySqlDatabase();
153
		$this->SQL = 'account_id = ' . $this->db->escapeNumber($accountID) . ' AND game_id = ' . $this->db->escapeNumber($gameID);
154
155
		if (isset($db)) {
156
			$playerExists = true;
157
		} else {
158
			$db = $this->db;
159
			$this->db->query('SELECT * FROM player WHERE ' . $this->SQL . ' LIMIT 1');
160
			$playerExists = $db->nextRecord();
161
		}
162
163
		if ($playerExists) {
164
			$this->accountID = (int)$accountID;
165
			$this->gameID = (int)$gameID;
166
			$this->playerName = $db->getField('player_name');
167
			$this->playerID = $db->getInt('player_id');
168
			$this->sectorID = $db->getInt('sector_id');
169
			$this->lastSectorID = $db->getInt('last_sector_id');
170
			$this->turns = $db->getInt('turns');
171
			$this->lastTurnUpdate = $db->getInt('last_turn_update');
172
			$this->newbieTurns = $db->getInt('newbie_turns');
173
			$this->lastNewsUpdate = $db->getInt('last_news_update');
174
			$this->attackColour = $db->getField('attack_warning');
175
			$this->dead = $db->getBoolean('dead');
176
			$this->npc = $db->getBoolean('npc');
177
			$this->newbieStatus = $db->getBoolean('newbie_status');
178
			$this->landedOnPlanet = $db->getBoolean('land_on_planet');
179
			$this->lastActive = $db->getInt('last_active');
180
			$this->lastCPLAction = $db->getInt('last_cpl_action');
181
			$this->raceID = $db->getInt('race_id');
182
			$this->credits = $db->getInt('credits');
183
			$this->experience = $db->getInt('experience');
184
			$this->alignment = $db->getInt('alignment');
185
			$this->militaryPayment = $db->getInt('military_payment');
186
			$this->allianceID = $db->getInt('alliance_id');
187
			$this->allianceJoinable = $db->getInt('alliance_join');
188
			$this->shipID = $db->getInt('ship_type_id');
189
			$this->kills = $db->getInt('kills');
190
			$this->deaths = $db->getInt('deaths');
191
			$this->assists = $db->getInt('assists');
192
			$this->lastPort = $db->getInt('last_port');
193
			$this->bank = $db->getInt('bank');
194
			$this->zoom = $db->getInt('zoom');
195
			$this->displayMissions = $db->getBoolean('display_missions');
196
			$this->displayWeapons = $db->getBoolean('display_weapons');
197
			$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...
198
			$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...
199
			$this->ignoreGlobals = $db->getBoolean('ignore_globals');
200
			$this->newbieWarning = $db->getBoolean('newbie_warning');
201
			$this->nameChanged = $db->getBoolean('name_changed');
202
			$this->combatDronesKamikazeOnMines = $db->getBoolean('combat_drones_kamikaze_on_mines');
203
		} else {
204
			throw new PlayerNotFoundException('Invalid accountID: ' . $accountID . ' OR gameID:' . $gameID);
205
		}
206
	}
207
208
	/**
209
	 * Insert a new player into the database. Returns the new player object.
210
	 */
211
	public static function createPlayer($accountID, $gameID, $playerName, $raceID, $isNewbie, $npc=false) {
212
		// Put the player in a sector with an HQ
213
		$startSectorID = self::getHome($gameID, $raceID);
214
215
		$db = new SmrMySqlDatabase();
216
		$db->lockTable('player');
217
218
		// Escape html elements so the name displays correctly
219
		$playerName = htmlentities($playerName);
220
221
		// Player names must be unique within each game
222
		$db->query('SELECT 1 FROM player WHERE game_id = ' . $db->escapeNumber($gameID) . ' AND player_name = ' . $db->escapeString($playerName) . ' LIMIT 1');
223
		if ($db->nextRecord() > 0) {
224
			$db->unlock();
225
			create_error('The player name already exists.');
226
		}
227
228
		// get last registered player id in that game and increase by one.
229
		$db->query('SELECT MAX(player_id) FROM player WHERE game_id = ' . $db->escapeNumber($gameID));
230
		if ($db->nextRecord()) {
231
			$playerID = $db->getInt('MAX(player_id)') + 1;
232
		} else {
233
			$playerID = 1;
234
		}
235
236
		$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)
237
					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) . ')');
238
239
		$db->unlock();
240
241
		return SmrPlayer::getPlayer($accountID, $gameID);
242
	}
243
244
	// Get array of players whose info can be accessed by this player.
245
	// Skips players who are not in the same alliance as this player.
246
	public function getSharingPlayers($forceUpdate = false) {
247
		$results = array($this);
248
249
		// Only return this player if not in an alliance
250
		if (!$this->hasAlliance()) {
251
			return $results;
252
		}
253
254
		// Get other players who are sharing info for this game.
255
		// NOTE: game_id=0 means that player shares info for all games.
256
		$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()) . ')');
257
		while ($this->db->nextRecord()) {
258
			try {
259
				$otherPlayer = SmrPlayer::getPlayer($this->db->getInt('from_account_id'),
260
				                                    $this->getGameID(), $forceUpdate);
261
			} catch (PlayerNotFoundException $e) {
262
				// Skip players that have not joined this game
263
				continue;
264
			}
265
266
			// players must be in the same alliance
267
			if ($this->sameAlliance($otherPlayer)) {
268
				$results[] = $otherPlayer;
269
			}
270
		}
271
		return $results;
272
	}
273
274
	public function getShip($forceUpdate = false) {
275
		return SmrShip::getShip($this, $forceUpdate);
276
	}
277
278
	public function getAccount() {
279
		return SmrAccount::getAccount($this->getAccountID());
280
	}
281
282
	public function getZoom() {
283
		return $this->zoom;
284
	}
285
286
	protected function setZoom($zoom) {
287
		// Set the zoom level between [1, 9]
288
		$zoom = max(1, min(9, $zoom));
289
		if ($this->zoom == $zoom) {
290
			return;
291
		}
292
		$this->zoom = $zoom;
293
		$this->hasChanged = true;
294
//		$this->db->query('UPDATE player SET zoom = ' . $zoom . ' WHERE ' . $this->SQL . ' LIMIT 1');
295
	}
296
297
	public function increaseZoom($zoom) {
298
		if ($zoom < 0) {
299
			throw new Exception('Trying to increase negative zoom.');
300
		}
301
		$this->setZoom($this->getZoom() + $zoom);
302
	}
303
304
	public function decreaseZoom($zoom) {
305
		if ($zoom < 0) {
306
			throw new Exception('Trying to decrease negative zoom.');
307
		}
308
		$this->setZoom($this->getZoom() - $zoom);
309
	}
310
311
	public function setSectorID($sectorID) {
312
		$port = SmrPort::getPort($this->getGameID(), $this->getSectorID());
313
		$port->addCachePort($this->getAccountID()); //Add port of sector we were just in, to make sure it is left totally up to date.
314
315
		parent::setSectorID($sectorID);
316
317
		$port = SmrPort::getPort($this->getGameID(), $sectorID);
318
		$port->addCachePort($this->getAccountID()); //Add the port of sector we are now in.
319
	}
320
321
	public function setLastSectorID($lastSectorID) {
322
		if ($this->lastSectorID == $lastSectorID) {
323
			return;
324
		}
325
		$this->lastSectorID = $lastSectorID;
326
		$this->hasChanged = true;
327
//		$this->db->query('UPDATE player SET last_sector_id = '.$this->lastSectorID.' WHERE '.$this->SQL.' LIMIT 1');
328
	}
329
330
	public function leaveAlliance(AbstractSmrPlayer $kickedBy = null) {
331
		$allianceID = $this->getAllianceID();
332
		$alliance = $this->getAlliance();
333
		if ($kickedBy != null) {
334
			$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

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

831
		if ($killer->/** @scrutinizer ignore-call */ hasCustomShipName()) {
Loading history...
832
			$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

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

1154
			$this->doHOFSave(/** @scrutinizer ignore-type */ $this->hasHOFChanged);
Loading history...
1155
		}
1156
		if (!empty(self::$hasHOFVisChanged)) {
1157
			foreach (self::$hasHOFVisChanged as $hofType => $changeType) {
1158
				if ($changeType == self::HOF_NEW) {
1159
					$this->db->query('INSERT INTO hof_visibility (type, visibility) VALUES (' . $this->db->escapeString($hofType) . ',' . $this->db->escapeString(self::$HOFVis[$hofType]) . ')');
1160
				} else {
1161
					$this->db->query('UPDATE hof_visibility SET visibility = ' . $this->db->escapeString(self::$HOFVis[$hofType]) . ' WHERE type = ' . $this->db->escapeString($hofType) . ' LIMIT 1');
1162
				}
1163
				unset(self::$hasHOFVisChanged[$hofType]);
1164
			}
1165
		}
1166
	}
1167
	protected function doHOFSave(array &$hasChangedList, array $typeList = array()) {
1168
		foreach ($hasChangedList as $type => &$hofChanged) {
1169
			$tempTypeList = $typeList;
1170
			$tempTypeList[] = $type;
1171
			if (is_array($hofChanged)) {
1172
				$this->doHOFSave($hofChanged, $tempTypeList);
1173
			} else {
1174
				$amount = $this->getHOF($tempTypeList);
1175
				if ($hofChanged == self::HOF_NEW) {
1176
					if ($amount > 0) {
1177
						$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) . ')');
1178
					}
1179
				} elseif ($hofChanged == self::HOF_CHANGED) {
1180
	//				if($amount > 0)
1181
						$this->db->query('UPDATE player_hof
1182
							SET amount=' . $this->db->escapeNumber($amount) . '
1183
							WHERE ' . $this->SQL . ' AND type = ' . $this->db->escapeArray($tempTypeList, false, true, ':', false) . ' LIMIT 1');
1184
	//				else
1185
	//					$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');
1186
	//				}
1187
				}
1188
				$hofChanged = false;
1189
			}
1190
		}
1191
	}
1192
1193
	protected function getHOFData() {
1194
		if (!isset($this->HOF)) {
1195
			//Get Player HOF
1196
			$this->db->query('SELECT type,amount FROM player_hof WHERE ' . $this->SQL);
1197
			$this->HOF = array();
1198
			while ($this->db->nextRecord()) {
1199
				$hof =& $this->HOF;
1200
				$typeList = explode(':', $this->db->getField('type'));
1201
				foreach ($typeList as $type) {
1202
					if (!isset($hof[$type])) {
1203
						$hof[$type] = array();
1204
					}
1205
					$hof =& $hof[$type];
1206
				}
1207
				$hof = $this->db->getFloat('amount');
1208
			}
1209
			self::getHOFVis();
1210
		}
1211
	}
1212
1213
	public static function getHOFVis() {
1214
		if (!isset(self::$HOFVis)) {
1215
			//Get Player HOF Vis
1216
			$db = new SmrMySqlDatabase();
1217
			$db->query('SELECT type,visibility FROM hof_visibility');
1218
			self::$HOFVis = array();
1219
			while ($db->nextRecord()) {
1220
				self::$HOFVis[$db->getField('type')] = $db->getField('visibility');
1221
			}
1222
		}
1223
	}
1224
1225
	protected function getBountiesData() {
1226
		if (!isset($this->bounties)) {
1227
			$this->bounties = array();
1228
			$this->db->query('SELECT * FROM bounty WHERE ' . $this->SQL);
1229
			while ($this->db->nextRecord()) {
1230
				$this->bounties[$this->db->getInt('bounty_id')] = array(
1231
							'Amount' => $this->db->getInt('amount'),
1232
							'SmrCredits' => $this->db->getInt('smr_credits'),
1233
							'Type' => $this->db->getField('type'),
1234
							'Claimer' => $this->db->getInt('claimer_id'),
1235
							'Time' => $this->db->getInt('time'),
1236
							'ID' => $this->db->getInt('bounty_id'),
1237
							'New' => false);
1238
			}
1239
		}
1240
	}
1241
1242
	// Get bounties that can be claimed by this player
1243
	// Type must be 'HQ' or 'UG'
1244
	public function getClaimableBounties($type) {
1245
		$bounties = array();
1246
		$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));
1247
		while ($this->db->nextRecord()) {
1248
			$bounties[] = array(
1249
				'player' => SmrPlayer::getPlayer($this->db->getInt('account_id'), $this->getGameID()),
1250
				'bounty_id' => $this->db->getInt('bounty_id'),
1251
				'credits' => $this->db->getInt('amount'),
1252
				'smr_credits' => $this->db->getInt('smr_credits'),
1253
			);
1254
		}
1255
		return $bounties;
1256
	}
1257
1258
	/**
1259
	 * Has this player been designated as the alliance flagship?
1260
	 */
1261
	public function isFlagship() {
1262
		return $this->hasAlliance() && $this->getAlliance()->getFlagshipID() == $this->getAccountID();
1263
	}
1264
1265
	public function isPresident() {
1266
		return Council::getPresidentID($this->getGameID(), $this->getRaceID()) == $this->getAccountID();
1267
	}
1268
1269
	public function isOnCouncil() {
1270
		return Council::isOnCouncil($this->getGameID(), $this->getRaceID(), $this->getAccountID());
1271
	}
1272
1273
	public function setNewbieWarning($bool) {
1274
		if ($this->newbieWarning == $bool) {
1275
			return;
1276
		}
1277
		$this->newbieWarning = $bool;
1278
		$this->hasChanged = true;
1279
	}
1280
1281
	public function getNewbieWarning() {
1282
		return $this->newbieWarning;
1283
	}
1284
1285
	public function getTickers() {
1286
		if (!isset($this->tickers)) {
1287
			$this->tickers = array();
1288
			//get ticker info
1289
			$this->db->query('SELECT type,time,expires,recent FROM player_has_ticker WHERE ' . $this->SQL . ' AND expires > ' . $this->db->escapeNumber(TIME));
1290
			while ($this->db->nextRecord()) {
1291
				$this->tickers[$this->db->getField('type')] = array('Type' => $this->db->getField('type'),
1292
																				'Time' => $this->db->getInt('time'),
1293
																				'Expires' => $this->db->getInt('expires'),
1294
																				'Recent' => $this->db->getField('recent'));
1295
			}
1296
		}
1297
		return $this->tickers;
1298
	}
1299
1300
	public function hasTickers() {
1301
		return count($this->getTickers()) > 0;
1302
	}
1303
1304
	public function getTicker($tickerType) {
1305
		$tickers = $this->getTickers();
1306
		if (isset($tickers[$tickerType])) {
1307
			return $tickers[$tickerType];
1308
		}
1309
		return false;
1310
	}
1311
1312
	public function hasTicker($tickerType) {
1313
		return $this->getTicker($tickerType) !== false;
1314
	}
1315
1316
	public function getTurnsLevel() {
1317
		if (!$this->hasTurns()) {
1318
			return 'NONE';
1319
		}
1320
		if ($this->getTurns() <= 25) {
1321
			return 'LOW';
1322
		}
1323
		if ($this->getTurns() <= 75) {
1324
			return 'MEDIUM';
1325
		}
1326
		return 'HIGH';
1327
	}
1328
1329
	/**
1330
	 * Returns the CSS class color to use when displaying the player's turns
1331
	 */
1332
	public function getTurnsColor() {
1333
		switch ($this->getTurnsLevel()) {
1334
			case 'NONE':
1335
			case 'LOW':
1336
				return 'red';
1337
			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...
1338
			case 'MEDIUM':
1339
				return 'yellow';
1340
			break;
1341
			default:
1342
				return 'green';
1343
		}
1344
	}
1345
1346
	public function update() {
1347
		$this->save();
1348
	}
1349
1350
	protected static function doMessageSending($senderID, $receiverID, $gameID, $messageTypeID, $message, $expires, $senderDelete = false, $unread = true) {
1351
		$message = trim($message);
1352
		$db = new SmrMySqlDatabase();
1353
		// send him the message
1354
		$db->query('INSERT INTO message
1355
			(account_id,game_id,message_type_id,message_text,
1356
			sender_id,send_time,expire_time,sender_delete) VALUES(' .
1357
			$db->escapeNumber($receiverID) . ',' .
1358
			$db->escapeNumber($gameID) . ',' .
1359
			$db->escapeNumber($messageTypeID) . ',' .
1360
			$db->escapeString($message) . ',' .
1361
			$db->escapeNumber($senderID) . ',' .
1362
			$db->escapeNumber(TIME) . ',' .
1363
			$db->escapeNumber($expires) . ',' .
1364
			$db->escapeBoolean($senderDelete) . ')'
1365
		);
1366
1367
		if ($unread === true) {
1368
			// give him the message icon
1369
			$db->query('REPLACE INTO player_has_unread_messages (game_id, account_id, message_type_id) VALUES
1370
						(' . $db->escapeNumber($gameID) . ', ' . $db->escapeNumber($receiverID) . ', ' . $db->escapeNumber($messageTypeID) . ')');
1371
		}
1372
1373
		switch ($messageTypeID) {
1374
			case MSG_PLAYER:
1375
				$receiverAccount = SmrAccount::getAccount($receiverID);
1376
				if ($receiverAccount->isValidated() && $receiverAccount->isReceivingMessageNotifications($messageTypeID) && !$receiverAccount->isLoggedIn()) {
1377
					require_once(get_file_loc('message.functions.inc'));
1378
					$sender = getMessagePlayer($senderID, $gameID, $messageTypeID);
1379
					if ($sender instanceof SmrPlayer) {
1380
						$sender = $sender->getDisplayName();
1381
					}
1382
					$mail = setupMailer();
1383
					$mail->Subject = 'Message Notification';
1384
					$mail->setFrom('[email protected]', 'SMR Notifications');
1385
					$bbifiedMessage = 'From: ' . $sender . ' Date: ' . date($receiverAccount->getShortDateFormat() . ' ' . $receiverAccount->getShortTimeFormat(), TIME) . "<br/>\r\n<br/>\r\n" . bbifyMessage($message, true);
1386
					$mail->msgHTML($bbifiedMessage);
1387
					$mail->AltBody = strip_tags($bbifiedMessage);
1388
					$mail->addAddress($receiverAccount->getEmail(), $receiverAccount->getHofName());
1389
					$mail->send();
1390
					$receiverAccount->decreaseMessageNotifications($messageTypeID, 1);
1391
				}
1392
			break;
1393
		}
1394
	}
1395
1396
	public function sendMessageToBox($boxTypeID, $message) {
1397
		// send him the message
1398
		SmrAccount::doMessageSendingToBox($this->getAccountID(), $boxTypeID, $message, $this->getGameID());
1399
	}
1400
1401
	public function sendGlobalMessage($message, $canBeIgnored = true) {
1402
		if ($canBeIgnored) {
1403
			if ($this->getAccount()->isMailBanned()) {
1404
				create_error('You are currently banned from sending messages');
1405
			}
1406
		}
1407
		$this->sendMessageToBox(BOX_GLOBALS, $message);
1408
1409
		// send to all online player
1410
		$db = new SmrMySqlDatabase();
1411
		$db->query('SELECT account_id
1412
					FROM active_session
1413
					JOIN player USING (game_id, account_id)
1414
					WHERE active_session.last_accessed >= ' . $db->escapeNumber(TIME - SmrSession::TIME_BEFORE_EXPIRY) . '
1415
						AND game_id = ' . $db->escapeNumber($this->getGameID()) . '
1416
						AND ignore_globals = \'FALSE\'
1417
						AND account_id != ' . $db->escapeNumber($this->getAccountID()));
1418
1419
		while ($db->nextRecord()) {
1420
			$this->sendMessage($db->getInt('account_id'), MSG_GLOBAL, $message, $canBeIgnored);
1421
		}
1422
		$this->sendMessage($this->getAccountID(), MSG_GLOBAL, $message, $canBeIgnored, false);
1423
	}
1424
1425
	public function sendMessage($receiverID, $messageTypeID, $message, $canBeIgnored = true, $unread = true, $expires = false, $senderDelete = false) {
1426
		//get expire time
1427
		if ($canBeIgnored) {
1428
			if ($this->getAccount()->isMailBanned()) {
1429
				create_error('You are currently banned from sending messages');
1430
			}
1431
			// Don't send messages to players ignoring us
1432
			$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');
1433
			if ($this->db->nextRecord()) {
1434
				return;
1435
			}
1436
		}
1437
1438
		$message = word_filter($message);
1439
1440
		// If expires not specified, use default based on message type
1441
		if ($expires === false) {
1442
			switch ($messageTypeID) {
1443
				case MSG_GLOBAL: //We don't send globals to the box here or it gets done loads of times.
1444
					$expires = 3600; // 1h
1445
				break;
1446
				case MSG_PLAYER:
1447
					$expires = 86400 * 31;
1448
				break;
1449
				case MSG_PLANET:
1450
					$expires = 86400 * 7;
1451
				break;
1452
				case MSG_SCOUT:
1453
					$expires = 86400 * 3;
1454
				break;
1455
				case MSG_POLITICAL:
1456
					$expires = 86400 * 31;
1457
				break;
1458
				case MSG_ALLIANCE:
1459
					$expires = 86400 * 31;
1460
				break;
1461
				case MSG_ADMIN:
1462
					$expires = 86400 * 365;
1463
				break;
1464
				case MSG_CASINO:
1465
					$expires = 86400 * 31;
1466
				break;
1467
				default:
1468
					$expires = 86400 * 7;
1469
			}
1470
			$expires += TIME;
1471
		}
1472
1473
		// Do not put scout messages in the sender's sent box
1474
		if ($messageTypeID == MSG_SCOUT) {
1475
			$senderDelete = true;
1476
		}
1477
1478
		// send him the message
1479
		self::doMessageSending($this->getAccountID(), $receiverID, $this->getGameID(), $messageTypeID, $message, $expires, $senderDelete, $unread);
1480
	}
1481
1482
	public function sendMessageFromOpAnnounce($receiverID, $message, $expires = false) {
1483
		// get expire time if not set
1484
		if ($expires === false) {
1485
			$expires = TIME + 86400 * 14;
1486
		}
1487
		self::doMessageSending(ACCOUNT_ID_OP_ANNOUNCE, $receiverID, $this->getGameID(), MSG_ALLIANCE, $message, $expires);
1488
	}
1489
1490
	public function sendMessageFromAllianceCommand($receiverID, $message) {
1491
		$expires = TIME + 86400 * 365;
1492
		self::doMessageSending(ACCOUNT_ID_ALLIANCE_COMMAND, $receiverID, $this->getGameID(), MSG_PLAYER, $message, $expires);
1493
	}
1494
1495
	public static function sendMessageFromPlanet($gameID, $receiverID, $message) {
1496
		//get expire time
1497
		$expires = TIME + 86400 * 31;
1498
		// send him the message
1499
		self::doMessageSending(ACCOUNT_ID_PLANET, $receiverID, $gameID, MSG_PLANET, $message, $expires);
1500
	}
1501
1502
	public static function sendMessageFromPort($gameID, $receiverID, $message) {
1503
		//get expire time
1504
		$expires = TIME + 86400 * 31;
1505
		// send him the message
1506
		self::doMessageSending(ACCOUNT_ID_PORT, $receiverID, $gameID, MSG_PLAYER, $message, $expires);
1507
	}
1508
1509
	public static function sendMessageFromFedClerk($gameID, $receiverID, $message) {
1510
		$expires = TIME + 86400 * 365;
1511
		self::doMessageSending(ACCOUNT_ID_FED_CLERK, $receiverID, $gameID, MSG_PLAYER, $message, $expires);
1512
	}
1513
1514
	public static function sendMessageFromAdmin($gameID, $receiverID, $message, $expires = false) {
1515
		//get expire time
1516
		if ($expires === false) {
1517
			$expires = TIME + 86400 * 365;
1518
		}
1519
		// send him the message
1520
		self::doMessageSending(ACCOUNT_ID_ADMIN, $receiverID, $gameID, MSG_ADMIN, $message, $expires);
1521
	}
1522
1523
	public static function sendMessageFromAllianceAmbassador($gameID, $receiverID, $message, $expires = false) {
1524
		//get expire time
1525
		if ($expires === false) {
1526
			$expires = TIME + 86400 * 31;
1527
		}
1528
		// send him the message
1529
		self::doMessageSending(ACCOUNT_ID_ALLIANCE_AMBASSADOR, $receiverID, $gameID, MSG_ALLIANCE, $message, $expires);
1530
	}
1531
1532
	public static function sendMessageFromCasino($gameID, $receiverID, $message, $expires = false) {
1533
		//get expire time
1534
		if ($expires === false) {
1535
			$expires = TIME + 86400 * 7;
1536
		}
1537
		// send him the message
1538
		self::doMessageSending(ACCOUNT_ID_CASINO, $receiverID, $gameID, MSG_CASINO, $message, $expires);
1539
	}
1540
1541
	public static function sendMessageFromRace($raceID, $gameID, $receiverID, $message, $expires = false) {
1542
		//get expire time
1543
		if ($expires === false) {
1544
			$expires = TIME + 86400 * 5;
1545
		}
1546
		// send him the message
1547
		self::doMessageSending(ACCOUNT_ID_GROUP_RACES + $raceID, $receiverID, $gameID, MSG_POLITICAL, $message, $expires);
1548
	}
1549
1550
	public function setMessagesRead($messageTypeID) {
1551
		$this->db->query('DELETE FROM player_has_unread_messages
1552
							WHERE '.$this->SQL . ' AND message_type_id = ' . $this->db->escapeNumber($messageTypeID));
1553
	}
1554
1555
	public function getPlottedCourse() {
1556
		if (!isset($this->plottedCourse)) {
1557
			// check if we have a course plotted
1558
			$this->db->query('SELECT course FROM player_plotted_course WHERE ' . $this->SQL . ' LIMIT 1');
1559
1560
			if ($this->db->nextRecord()) {
1561
				// get the course back
1562
				$this->plottedCourse = unserialize($this->db->getField('course'));
1563
			} else {
1564
				$this->plottedCourse = false;
1565
			}
1566
		}
1567
1568
		// Update the plotted course if we have moved since the last query
1569
		if ($this->plottedCourse !== false && (!isset($this->plottedCourseFrom) || $this->plottedCourseFrom != $this->getSectorID())) {
1570
			$this->plottedCourseFrom = $this->getSectorID();
1571
1572
			if ($this->plottedCourse->getNextOnPath() == $this->getSectorID()) {
1573
				// We have walked into the next sector of the course
1574
				$this->plottedCourse->followPath();
1575
				$this->setPlottedCourse($this->plottedCourse);
1576
			} elseif ($this->plottedCourse->isInPath($this->getSectorID())) {
1577
				// We have skipped to some later sector in the course
1578
				$this->plottedCourse->skipToSector($this->getSectorID());
1579
				$this->setPlottedCourse($this->plottedCourse);
1580
			}
1581
		}
1582
		return $this->plottedCourse;
1583
	}
1584
1585
	public function setPlottedCourse(Distance $plottedCourse) {
1586
		$hadPlottedCourse = $this->hasPlottedCourse();
1587
		$this->plottedCourse = $plottedCourse;
1588
		if ($this->plottedCourse->getTotalSectors() > 0) {
1589
			$this->db->query('REPLACE INTO player_plotted_course
1590
				(account_id, game_id, course)
1591
				VALUES(' . $this->db->escapeNumber($this->getAccountID()) . ', ' . $this->db->escapeNumber($this->getGameID()) . ', ' . $this->db->escapeBinary(serialize($this->plottedCourse)) . ')');
1592
		} elseif ($hadPlottedCourse) {
1593
			$this->deletePlottedCourse();
1594
		}
1595
	}
1596
1597
	public function hasPlottedCourse() {
1598
		return $this->getPlottedCourse() !== false;
1599
	}
1600
1601
	public function isPartOfCourse($sectorOrSectorID) {
1602
		if (!$this->hasPlottedCourse()) {
1603
			return false;
1604
		}
1605
		if ($sectorOrSectorID instanceof SmrSector) {
1606
			$sectorID = $sectorOrSectorID->getSectorID();
1607
		} else {
1608
			$sectorID = $sectorOrSectorID;
1609
		}
1610
		return $this->getPlottedCourse()->isInPath($sectorID);
1611
	}
1612
1613
	public function deletePlottedCourse() {
1614
		$this->plottedCourse = false;
1615
		$this->db->query('DELETE FROM player_plotted_course WHERE ' . $this->SQL . ' LIMIT 1');
1616
	}
1617
1618
	// Computes the turn cost and max misjump between current and target sector
1619
	public function getJumpInfo(SmrSector $targetSector) {
1620
		$path = Plotter::findDistanceToX($targetSector, $this->getSector(), true);
1621
		if ($path === false) {
1622
			create_error('Unable to plot from ' . $this->getSectorID() . ' to ' . $targetSector->getSectorID() . '.');
1623
		}
1624
		$distance = $path->getRelativeDistance();
1625
1626
		$turnCost = max(TURNS_JUMP_MINIMUM, IRound($distance * TURNS_PER_JUMP_DISTANCE));
1627
		$maxMisjump = max(0, IRound(($distance - $turnCost) * MISJUMP_DISTANCE_DIFF_FACTOR / (1 + $this->getLevelID() * MISJUMP_LEVEL_FACTOR)));
1628
		return array('turn_cost' => $turnCost, 'max_misjump' => $maxMisjump);
1629
	}
1630
1631
	public function __sleep() {
1632
		return array('accountID', 'gameID', 'sectorID', 'alignment', 'playerID', 'playerName');
1633
	}
1634
1635
	public function &getStoredDestinations() {
1636
		if (!isset($this->storedDestinations)) {
1637
			$this->storedDestinations = array();
1638
			$this->db->query('SELECT * FROM player_stored_sector WHERE ' . $this->SQL);
1639
			while ($this->db->nextRecord()) {
1640
				$this->storedDestinations[] = array(
1641
					'Label' => $this->db->getField('label'),
1642
					'SectorID' => $this->db->getInt('sector_id'),
1643
					'OffsetTop' => $this->db->getInt('offset_top'),
1644
					'OffsetLeft' => $this->db->getInt('offset_left')
1645
				);
1646
			}
1647
		}
1648
		return $this->storedDestinations;
1649
	}
1650
1651
1652
	public function moveDestinationButton($sectorID, $offsetTop, $offsetLeft) {
1653
1654
		if (!is_numeric($offsetLeft) || !is_numeric($offsetTop)) {
1655
			create_error('The position of the saved sector must be numeric!.');
1656
		}
1657
		$offsetTop = round($offsetTop);
1658
		$offsetLeft = round($offsetLeft);
1659
1660
		if ($offsetLeft < 0 || $offsetLeft > 500 || $offsetTop < 0 || $offsetTop > 300) {
1661
			create_error('The saved sector must be in the box!');
1662
		}
1663
1664
		$storedDestinations =& $this->getStoredDestinations();
1665
		foreach ($storedDestinations as &$sd) {
1666
			if ($sd['SectorID'] == $sectorID) {
1667
				$sd['OffsetTop'] = $offsetTop;
1668
				$sd['OffsetLeft'] = $offsetLeft;
1669
				$this->db->query('
1670
					UPDATE player_stored_sector
1671
						SET offset_left = ' . $this->db->escapeNumber($offsetLeft) . ', offset_top=' . $this->db->escapeNumber($offsetTop) . '
1672
					WHERE ' . $this->SQL . ' AND sector_id = ' . $this->db->escapeNumber($sectorID)
1673
				);
1674
				return true;
1675
			}
1676
		}
1677
1678
		create_error('You do not have a saved sector for #' . $sectorID);
1679
	}
1680
1681
	public function addDestinationButton($sectorID, $label) {
1682
1683
		if (!is_numeric($sectorID) || !SmrSector::sectorExists($this->getGameID(), $sectorID)) {
1684
			create_error('You want to add a non-existent sector?');
1685
		}
1686
1687
		// sector already stored ?
1688
		foreach ($this->getStoredDestinations() as $sd) {
1689
			if ($sd['SectorID'] == $sectorID) {
1690
				create_error('Sector already stored!');
1691
			}
1692
		}
1693
1694
		$this->storedDestinations[] = array(
1695
			'Label' => $label,
1696
			'SectorID' => (int)$sectorID,
1697
			'OffsetTop' => 1,
1698
			'OffsetLeft' => 1
1699
		);
1700
1701
		$this->db->query('
1702
			INSERT INTO player_stored_sector (account_id, game_id, sector_id, label, offset_top, offset_left)
1703
			VALUES (' . $this->db->escapeNumber($this->getAccountID()) . ', ' . $this->db->escapeNumber($this->getGameID()) . ', ' . $this->db->escapeNumber($sectorID) . ',' . $this->db->escapeString($label, true) . ',1,1)'
1704
		);
1705
	}
1706
1707
	public function deleteDestinationButton($sectorID) {
1708
		if (!is_numeric($sectorID) || $sectorID < 1) {
1709
			create_error('You want to remove a non-existent sector?');
1710
		}
1711
1712
		foreach ($this->getStoredDestinations() as $key => $sd) {
1713
			if ($sd['SectorID'] == $sectorID) {
1714
				$this->db->query('
1715
					DELETE FROM player_stored_sector
1716
					WHERE ' . $this->SQL . '
1717
					AND sector_id = ' . $this->db->escapeNumber($sectorID)
1718
				);
1719
				unset($this->storedDestinations[$key]);
1720
				return true;
1721
			}
1722
		}
1723
		return false;
1724
	}
1725
1726
	public function getExperienceRank() {
1727
		return $this->computeRanking('experience', $this->getExperience());
1728
	}
1729
	public function getKillsRank() {
1730
		return $this->computeRanking('kills', $this->getKills());
1731
	}
1732
	public function getDeathsRank() {
1733
		return $this->computeRanking('deaths', $this->getDeaths());
1734
	}
1735
	public function getAssistsRank() {
1736
		return $this->computeRanking('assists', $this->getAssists());
1737
	}
1738
	private function computeRanking($dbField, $playerAmount) {
1739
		$this->db->query('SELECT count(*) FROM player
1740
			WHERE game_id = ' . $this->db->escapeNumber($this->getGameID()) . '
1741
			AND (
1742
				'.$dbField . ' > ' . $this->db->escapeNumber($playerAmount) . '
1743
				OR (
1744
					'.$dbField . ' = ' . $this->db->escapeNumber($playerAmount) . '
1745
					AND player_name <= ' . $this->db->escapeString($this->getPlayerName()) . '
1746
				)
1747
			)');
1748
		$this->db->nextRecord();
1749
		$rank = $this->db->getInt('count(*)');
1750
		return $rank;
1751
	}
1752
}
1753