Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

Failed Conditions
Pull Request — master (#1038)
by Dan
11:47
created

ForwardException

Complexity

Total Complexity 0

Size/Duplication

Total Lines 1
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 0
eloc 1
dl 0
loc 1
c 0
b 0
f 0
1
<?php declare(strict_types=1);
2
3
// Use this exception to help override container forwarding for NPC's
4
class ForwardException extends Exception {}
5
6
function overrideForward($container) {
7
	global $forwardedContainer;
8
	$forwardedContainer = $container;
9
	if ($container['body'] == 'error.php') {
10
		// We hit a create_error - this shouldn't happen for an NPC often,
11
		// for now we want to throw an exception for it for testing.
12
		debug('Hit an error');
13
		throw new Exception($container['message']);
14
	}
15
	// We have to throw the exception to get back up the stack,
16
	// otherwise we quickly hit problems of overflowing the stack.
17
	throw new ForwardException;
18
}
19
const OVERRIDE_FORWARD = true;
20
21
// Must be defined before anything that might throw an exception
22
const NPC_SCRIPT = true;
23
24
// global config
25
require_once(realpath(dirname(__FILE__)) . '/../../bootstrap.php');
26
// bot config
27
require_once(CONFIG . 'npc/config.specific.php');
28
// needed libs
29
require_once(get_file_loc('smr.inc.php'));
30
require_once(get_file_loc('shop_goods.inc.php'));
31
32
// Raise exceptions for all types of errors for improved error reporting
33
// and to attempt to shut down the NPCs cleanly on errors.
34
set_error_handler("exception_error_handler");
35
36
const SHIP_UPGRADE_PATH = array(
37
	RACE_ALSKANT => array(
38
		SHIP_TYPE_TRADE_MASTER,
39
		SHIP_TYPE_TRIP_MAKER,
40
		SHIP_TYPE_SMALL_TIMER
41
	),
42
	RACE_CREONTI => array(
43
		SHIP_TYPE_LEVIATHAN,
44
		SHIP_TYPE_MEDIUM_CARGO_HULK
45
	),
46
	RACE_HUMAN => array(
47
		SHIP_TYPE_AMBASSADOR,
48
		SHIP_TYPE_RENAISSANCE,
49
		SHIP_TYPE_LIGHT_FREIGHTER
50
	),
51
	RACE_IKTHORNE => array(
52
		SHIP_TYPE_FAVOURED_OFFSPRING,
53
		SHIP_TYPE_PROTO_CARRIER,
54
		SHIP_TYPE_TINY_DELIGHT
55
	),
56
	RACE_SALVENE => array(
57
		SHIP_TYPE_DRUDGE,
58
		SHIP_TYPE_HATCHLINGS_DUE
59
	),
60
	RACE_THEVIAN => array(
61
		SHIP_TYPE_EXPEDITER,
62
		SHIP_TYPE_SWIFT_VENTURE
63
	),
64
	RACE_WQHUMAN => array(
65
		SHIP_TYPE_BLOCKADE_RUNNER,
66
		SHIP_TYPE_NEGOTIATOR,
67
		SHIP_TYPE_SLIP_FREIGHTER
68
	),
69
	RACE_NIJARIN => array(
70
		SHIP_TYPE_VENGEANCE,
71
		SHIP_TYPE_REDEEMER
72
	)
73
);
74
75
76
try {
77
	$db = Smr\Database::getInstance();
78
	debug('Script started');
79
80
	// Make sure NPC's have been set up in the database
81
	$db->query('SELECT 1 FROM npc_logins LIMIT 1');
82
	if (!$db->nextRecord()) {
83
		debug('No NPCs have been created yet!');
84
		exit;
85
	}
86
87
	try {
88
		changeNPCLogin();
89
	} catch (ForwardException $e) {}
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
90
91
	NPCStuff();
92
} catch (Throwable $e) {
93
	logException($e);
94
	// Try to shut down cleanly
95
	exitNPC();
96
}
97
98
99
function NPCStuff() {
100
	global $actions, $previousContainer;
101
102
	$underAttack = false;
103
	$actions = -1;
104
105
	$session = Smr\Session::getInstance();
106
	$db = Smr\Database::getInstance();
0 ignored issues
show
Unused Code introduced by
The assignment to $db is dead and can be removed.
Loading history...
107
108
	while (true) {
109
		// Clear the $_REQUEST global, in case we had set it, to avoid
110
		// contaminating subsequent page processing.
111
		$_REQUEST = [];
112
113
		$actions++;
114
115
		// Avoid infinite loops by restricting the number of actions
116
		if ($actions > NPC_MAX_ACTIONS) {
117
			debug('Reached maximum number of actions: ' . NPC_MAX_ACTIONS);
118
			changeNPCLogin();
119
		}
120
121
		try {
122
			$TRADE_ROUTE =& $GLOBALS['TRADE_ROUTE'];
123
			debug('Action #' . $actions);
124
125
			//We have to reload player on each loop
126
			$player = $session->getPlayer(true);
127
			// Sanity check to be certain we actually have an NPC
128
			if (!$player->isNPC()) {
129
				throw new Exception('Player is not an NPC!');
130
			}
131
			$player->updateTurns();
132
133
			if ($actions == 0) {
134
				if ($player->getTurns() <= rand($player->getMaxTurns() / 2, $player->getMaxTurns()) && ($player->hasNewbieTurns() || $player->hasFederalProtection())) {
135
					debug('We don\'t have enough turns to bother starting trading, and we are protected: ' . $player->getTurns());
136
					changeNPCLogin();
137
				}
138
139
				// Ensure the NPC doesn't think it's under attack at startup,
140
				// since this could cause it to get stuck in a loop in Fed.
141
				$player->removeUnderAttack();
142
				$player->update();
143
			}
144
145
			if (!isset($TRADE_ROUTE)) { //We only want to change trade route if there isn't already one set.
146
				$TRADE_ROUTES =& findRoutes($player);
147
				$TRADE_ROUTE =& changeRoute($TRADE_ROUTES);
148
			}
149
150
			if ($player->isDead()) {
151
				debug('Some evil person killed us, let\'s move on now.');
152
				$previousContainer = null; //We died, we don't care what we were doing beforehand.
153
				$TRADE_ROUTE =& changeRoute($TRADE_ROUTES); //Change route
154
				processContainer(Page::create('death_processing.php'));
155
			}
156
			if ($player->getNewbieTurns() <= NEWBIE_TURNS_WARNING_LIMIT && $player->getNewbieWarning()) {
157
				processContainer(Page::create('newbie_warning_processing.php'));
158
			}
159
160
			$fedContainer = null;
161
			$var = $session->getCurrentVar();
162
			if (isset($var['url']) && $var['url'] == 'shop_ship_processing.php' && ($fedContainer = plotToFed($player, true)) !== true) { //We just bought a ship, we should head back to our trade gal/uno - we use HQ for now as it's both in our gal and a UNO, plus it's safe which is always a bonus
163
				processContainer($fedContainer);
164
			} elseif (!$underAttack && $player->isUnderAttack() === true
165
				&& ($player->hasPlottedCourse() === false || $player->getPlottedCourse()->getEndSector()->offersFederalProtection() === false)
166
				&& ($fedContainer == null ? $fedContainer = plotToFed($player, true) : $fedContainer) !== true) {
167
				// We're under attack and need to plot course to fed.
168
				debug('Under Attack');
169
				$underAttack = true;
170
				processContainer($fedContainer);
171
			} elseif ($player->hasPlottedCourse() === true && $player->getPlottedCourse()->getEndSector()->offersFederalProtection()) { //We have a route to fed to follow, figure it's probably a damned sensible thing to follow.
172
				debug('Follow Course: ' . $player->getPlottedCourse()->getNextOnPath());
173
				processContainer(moveToSector($player, $player->getPlottedCourse()->getNextOnPath()));
174
			} elseif (($container = canWeUNO($player, true)) !== false) { //We have money and are at a uno, let's uno!
175
				debug('We\'re UNOing');
176
				processContainer($container);
177
			} elseif ($player->hasPlottedCourse() === true) { //We have a route to follow, figure it's probably a sensible thing to follow.
178
				debug('Follow Course: ' . $player->getPlottedCourse()->getNextOnPath());
179
				processContainer(moveToSector($player, $player->getPlottedCourse()->getNextOnPath()));
180
			} elseif ($player->getTurns() < NPC_LOW_TURNS || ($player->getTurns() < $player->getMaxTurns() / 2 && $player->getNewbieTurns() < NPC_LOW_NEWBIE_TURNS) || $underAttack) { //We're low on turns or have been under attack and need to plot course to fed
181
				if ($player->getTurns() < NPC_LOW_TURNS) {
182
					debug('Low Turns:' . $player->getTurns());
183
				}
184
				if ($underAttack) {
185
					debug('Fedding after attack.');
186
				}
187
				if ($player->hasNewbieTurns()) { //We have newbie turns, we can just wait here.
188
					debug('We have newbie turns, let\'s just switch to another NPC.');
189
					changeNPCLogin();
190
				}
191
				if ($player->hasFederalProtection()) {
192
					debug('We are in fed, time to switch to another NPC.');
193
					changeNPCLogin();
194
				}
195
				$ship = $player->getShip();
196
				processContainer(plotToFed($player, !$ship->hasMaxShields() || !$ship->hasMaxArmour() || !$ship->hasMaxCargoHolds()));
197
			} elseif (($container = checkForShipUpgrade($player)) !== false) { //We have money and are at a uno, let's uno!
198
				debug('We\'re reshipping!');
199
				processContainer($container);
200
			} elseif (($container = canWeUNO($player, false)) !== false) { //We need to UNO and have enough money to do it properly so let's do it sooner rather than later.
201
				debug('We need to UNO, so off we go!');
202
				processContainer($container);
203
			} elseif ($TRADE_ROUTE instanceof \Routes\Route) {
204
				debug('Trade Route');
205
				$forwardRoute = $TRADE_ROUTE->getForwardRoute();
206
				$returnRoute = $TRADE_ROUTE->getReturnRoute();
207
				if ($forwardRoute->getBuySectorId() == $player->getSectorID() || $returnRoute->getBuySectorId() == $player->getSectorID()) {
208
					if ($forwardRoute->getBuySectorId() == $player->getSectorID()) {
209
						$buyRoute = $forwardRoute;
210
						$sellRoute = $returnRoute;
211
					} elseif ($returnRoute->getBuySectorId() == $player->getSectorID()) {
212
						$buyRoute = $returnRoute;
213
						$sellRoute = $forwardRoute;
214
					}
215
216
					$ship = $player->getShip();
217
					if ($ship->getUsedHolds() > 0) {
218
						if ($ship->hasCargo($sellRoute->getGoodID())) { //Sell goods
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $sellRoute does not seem to be defined for all execution paths leading up to this point.
Loading history...
219
							$goodID = $sellRoute->getGoodID();
220
221
							$port = $player->getSector()->getPort();
222
							$tradeable = checkPortTradeable($port, $player);
223
224
							if ($tradeable === true && $port->getGoodAmount($goodID) >= $ship->getCargo($sellRoute->getGoodID())) { //TODO: Sell what we can rather than forcing sell all at once?
225
								//Sell goods
226
								debug('Sell Goods');
227
								processContainer(tradeGoods($goodID, $player, $port));
228
							} else {
229
								//Move to next route or fed.
230
								if (($TRADE_ROUTE =& changeRoute($TRADE_ROUTES)) === false) {
231
									debug('Changing Route Failed');
232
									processContainer(plotToFed($player));
233
								} else {
234
									debug('Route Changed');
235
									continue;
236
								}
237
							}
238
						} elseif ($ship->hasCargo($buyRoute->getGoodID()) === true) { //We've bought goods, plot to sell
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $buyRoute does not seem to be defined for all execution paths leading up to this point.
Loading history...
239
							debug('Plot To Sell: ' . $buyRoute->getSellSectorId());
240
							processContainer(plotToSector($player, $buyRoute->getSellSectorId()));
241
						} else {
242
							//Dump goods
243
							debug('Dump Goods');
244
							processContainer(dumpCargo($player));
245
						}
246
					} else { //Buy goods
247
						$goodID = $buyRoute->getGoodID();
248
249
						$port = $player->getSector()->getPort();
250
						$tradeable = checkPortTradeable($port, $player);
251
252
						if ($tradeable === true && $port->getGoodAmount($goodID) >= $ship->getEmptyHolds()) { //Buy goods
253
							debug('Buy Goods');
254
							processContainer(tradeGoods($goodID, $player, $port));
255
						} else {
256
							//Move to next route or fed.
257
							if (($TRADE_ROUTE =& changeRoute($TRADE_ROUTES)) === false) {
258
								debug('Changing Route Failed');
259
								processContainer(plotToFed($player));
260
							} else {
261
								debug('Route Changed');
262
								continue;
263
							}
264
						}
265
					}
266
				} else {
267
					debug('Plot To Buy: ' . $forwardRoute->getBuySectorId());
268
					processContainer(plotToSector($player, $forwardRoute->getBuySectorId()));
269
				}
270
			} else { //Something weird is going on.. Let's fed and wait.
271
				debug('No actual action? Wtf?');
272
				processContainer(plotToFed($player));
273
			}
274
			/*
275
			else { //Otherwise let's run around at random.
276
				$links = $player->getSector()->getLinks();
277
				$moveTo = $links[array_rand($links)];
278
				debug('Random Wanderings: '.$moveTo);
279
				processContainer(moveToSector($player,$moveTo));
280
			}
281
			*/
282
		} catch (ForwardException $e) {
283
			global $lock;
284
			if ($lock) { //only save if we have the lock.
285
				SmrSector::saveSectors();
286
				SmrShip::saveShips();
287
				SmrPlayer::savePlayers();
288
				SmrForce::saveForces();
289
				SmrPort::savePorts();
290
				if (class_exists('WeightedRandom', false)) {
291
					WeightedRandom::saveWeightedRandoms();
292
				}
293
				release_lock();
294
			}
295
			//Clean up the caches as the data may get changed by other players
296
			clearCaches();
297
			//Clear up some global vars
298
			global $locksFailed;
299
			$locksFailed = array();
300
			$_REQUEST = array();
301
			//Have a sleep between actions
302
			sleepNPC();
303
		}
304
	}
305
	debug('Actions Finished.');
306
	exitNPC();
307
}
308
309
function clearCaches() {
310
	SmrSector::clearCache();
311
	SmrPlayer::clearCache();
312
	SmrShip::clearCache();
313
	SmrForce::clearCache();
314
	SmrPort::clearCache();
315
}
316
317
function debug($message, $debugObject = null) {
318
	echo date('Y-m-d H:i:s - ') . $message . ($debugObject !== null ?EOL.var_export($debugObject, true) : '') . EOL;
319
	if (NPC_LOG_TO_DATABASE) {
320
		$session = Smr\Session::getInstance();
321
		$accountID = $session->getAccountID();
322
		$var = $session->getCurrentVar();
323
		$db = Smr\Database::getInstance();
324
		$db->query('INSERT INTO npc_logs (script_id, npc_id, time, message, debug_info, var) VALUES (' . (defined('SCRIPT_ID') ?SCRIPT_ID:0) . ', ' . $accountID() . ',NOW(),' . $db->escapeString($message) . ',' . $db->escapeString(var_export($debugObject, true)) . ',' . $db->escapeString(var_export($var, true)) . ')');
325
326
		// On the first call to debug, we need to update the script_id retroactively
327
		if (!defined('SCRIPT_ID')) {
328
			define('SCRIPT_ID', $db->getInsertID());
329
			$db->query('UPDATE npc_logs SET script_id=' . SCRIPT_ID . ' WHERE log_id=' . SCRIPT_ID);
330
		}
331
	}
332
}
333
334
function processContainer($container) {
335
	global $forwardedContainer, $previousContainer;
336
	$session = Smr\Session::getInstance();
337
	$player = $session->getPlayer();
338
	if ($container == $previousContainer && $forwardedContainer['body'] != 'forces_attack.php') {
339
		debug('We are executing the same container twice?', array('ForwardedContainer' => $forwardedContainer, 'Container' => $container));
340
		if ($player->hasNewbieTurns() || $player->hasFederalProtection()) {
341
			// Only throw the exception if we have protection, otherwise let's hope that the NPC will be able to find its way to safety rather than dying in the open.
342
			throw new Exception('We are executing the same container twice?');
343
		}
344
	}
345
	clearCaches(); //Clear caches of anything we have used for decision making before processing container and getting lock.
346
	$previousContainer = $container;
347
	debug('Executing container', $container);
348
	// The next "page request" must occur at an updated time.
349
	Smr\Epoch::update();
350
	$session->setCurrentVar($container, false);
351
	acquire_lock($player->getSectorID()); // Lock now to skip var update in do_voodoo
352
	do_voodoo();
353
}
354
355
function sleepNPC() {
356
	usleep(rand(MIN_SLEEP_TIME, MAX_SLEEP_TIME)); //Sleep for a random time
357
}
358
359
// Releases an NPC when it is done working
360
function releaseNPC() {
361
	$session = Smr\Session::getInstance();
362
	if (!$session->hasAccount()) {
363
		debug('releaseNPC: no NPC to release');
364
		return;
365
	}
366
	$login = $session->getAccount()->getLogin();
367
	$db = Smr\Database::getInstance();
368
	$db->query('UPDATE npc_logins SET working=' . $db->escapeBoolean(false) . ' WHERE login=' . $db->escapeString($login));
369
	if ($db->getChangedRows() > 0) {
370
		debug('Released NPC: ' . $login);
371
	} else {
372
		debug('Failed to release NPC: ' . $login);
373
	}
374
}
375
376
function exitNPC() {
377
	debug('Exiting NPC script.');
378
	releaseNPC();
379
	release_lock();
380
	exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
381
}
382
383
function changeNPCLogin() {
384
	global $actions, $previousContainer;
385
	if ($actions > 0) {
386
		debug('We have taken actions and now want to change NPC, let\'s exit and let next script choose a new NPC to reset execution time', getrusage());
387
		exitNPC();
388
	}
389
390
	$actions = -1;
391
	$GLOBALS['TRADE_ROUTE'] = null;
392
393
	// Release previous NPC, if any
394
	releaseNPC();
395
396
	// We chose a new NPC, we don't care what we were doing beforehand.
397
	$previousContainer = null;
398
399
	// Lacking a convenient way to get up-to-date turns, order NPCs by how
400
	// recently they have taken an action.
401
	debug('Choosing new NPC');
402
	static $availableNpcs = null;
403
404
	$db = Smr\Database::getInstance();
405
	$session = Smr\Session::getInstance();
406
407
	if (is_null($availableNpcs)) {
408
		// Make sure to select NPCs from active games only
409
		$db->query('SELECT account_id, game_id FROM player JOIN account USING(account_id) JOIN npc_logins USING(login) JOIN game USING(game_id) WHERE active=\'TRUE\' AND working=\'FALSE\' AND start_time < ' . $db->escapeNumber(Smr\Epoch::time()) . ' AND end_time > ' . $db->escapeNumber(Smr\Epoch::time()) . ' ORDER BY last_turn_update ASC');
410
		while ($db->nextRecord()) {
411
			$availableNpcs[] = [
412
				'account_id' => $db->getInt('account_id'),
413
				'game_id' => $db->getInt('game_id'),
414
			];
415
		}
416
	}
417
418
	if (empty($availableNpcs)) {
419
		debug('No free NPCs');
420
		exitNPC();
421
	}
422
423
	// Pop an NPC off the top of the stack to activate
424
	$npc = array_shift($availableNpcs);
425
426
	// Update session info for this chosen NPC
427
	$account = SmrAccount::getAccount($npc['account_id']);
428
	$session->setAccount($account);
429
	$session->updateGame($npc['game_id']);
430
431
	$db->query('UPDATE npc_logins SET working=' . $db->escapeBoolean(true) . ' WHERE login=' . $db->escapeString($account->getLogin()));
432
	debug('Chosen NPC: ' . $account->getLogin() . ' (game ' . $session->getGameID() . ')');
433
434
	throw new ForwardException;
435
}
436
437
function canWeUNO(AbstractSmrPlayer $player, $oppurtunisticOnly) {
438
	if ($player->getCredits() < MINUMUM_RESERVE_CREDITS) {
439
		return false;
440
	}
441
	$ship = $player->getShip();
442
	if ($ship->hasMaxShields() && $ship->hasMaxArmour() && $ship->hasMaxCargoHolds()) {
443
		return false;
444
	}
445
	$sector = $player->getSector();
446
447
	// We buy armour in preference to shields as it's cheaper.
448
	// We buy cargo holds last if we have no newbie turns because we'd rather not die
449
	$hardwareArray = array(HARDWARE_ARMOUR, HARDWARE_SHIELDS, HARDWARE_CARGO);
450
451
	$amount = 0;
452
453
	foreach ($sector->getLocations() as $location) {
454
		if ($location->isHardwareSold()) {
455
			$hardwareSold = $location->getHardwareSold();
456
			if ($player->getNewbieTurns() > MIN_NEWBIE_TURNS_TO_BUY_CARGO && !$ship->hasMaxCargoHolds() && isset($hardwareSold[HARDWARE_CARGO]) && ($amount = floor(($player->getCredits() - MINUMUM_RESERVE_CREDITS) / Globals::getHardwareCost(HARDWARE_CARGO))) > 0) { // Buy cargo holds first if we have plenty of newbie turns left.
457
				$hardwareID = HARDWARE_CARGO;
458
			} else {
459
				foreach ($hardwareArray as $hardwareArrayID) {
460
					if (!$ship->hasMaxHardware($hardwareArrayID) && isset($hardwareSold[$hardwareArrayID]) && ($amount = floor(($player->getCredits() - MINUMUM_RESERVE_CREDITS) / Globals::getHardwareCost($hardwareArrayID))) > 0) {
461
						$hardwareID = $hardwareArrayID;
462
						break;
463
					}
464
				}
465
			}
466
			if (isset($hardwareID)) {
467
				return doUNO($hardwareID, min($ship->getMaxHardware($hardwareID) - $ship->getHardware($hardwareID), $amount));
468
			}
469
		}
470
	}
471
472
	if ($oppurtunisticOnly === true) {
473
		return false;
474
	}
475
476
	if ($player->getCredits() - $ship->getCostToUNO() < MINUMUM_RESERVE_CREDITS) {
477
		return false; //Only do non-oppurtunistic UNO if we have the money to do it properly!
478
	}
479
480
	foreach ($hardwareArray as $hardwareArrayID) {
481
		if (!$ship->hasMaxHardware($hardwareArrayID)) {
482
			$hardwareNeededID = $hardwareArrayID;
0 ignored issues
show
Unused Code introduced by
The assignment to $hardwareNeededID is dead and can be removed.
Loading history...
483
			return plotToNearest($player, Globals::getHardwareTypes($hardwareArrayID));
484
		}
485
	}
486
}
487
488
function doUNO($hardwareID, $amount) {
489
	debug('Buying ' . $amount . ' units of "' . Globals::getHardwareName($hardwareID) . '"');
490
	$_REQUEST = [
491
		'amount' => $amount,
492
		'action' => 'Buy',
493
	];
494
	return Page::create('shop_hardware_processing.php', '', array('hardware_id'=>$hardwareID));
495
}
496
497
function tradeGoods($goodID, AbstractSmrPlayer $player, SmrPort $port) {
498
	sleepNPC(); //We have an extra sleep at port to make the NPC more vulnerable.
499
	$ship = $player->getShip();
500
	$relations = $player->getRelation($port->getRaceID());
501
502
	$transaction = $port->getGoodTransaction($goodID);
503
504
	if ($transaction === TRADER_BUYS) {
505
		$amount = $ship->getEmptyHolds();
506
	} else {
507
		$amount = $ship->getCargo($goodID);
508
	}
509
510
	$idealPrice = $port->getIdealPrice($goodID, $transaction, $amount, $relations);
511
	$offeredPrice = $port->getOfferPrice($idealPrice, $relations, $transaction);
0 ignored issues
show
Bug introduced by
It seems like $transaction can also be of type null; however, parameter $transactionType of AbstractSmrPort::getOfferPrice() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

511
	$offeredPrice = $port->getOfferPrice($idealPrice, $relations, /** @scrutinizer ignore-type */ $transaction);
Loading history...
512
513
	$_REQUEST = ['action' => $transaction];
514
	return Page::create('shop_goods_processing.php', '', array('offered_price'=>$offeredPrice, 'ideal_price'=>$idealPrice, 'amount'=>$amount, 'good_id'=>$goodID, 'bargain_price'=>$offeredPrice));
515
}
516
517
function dumpCargo($player) {
518
	$ship = $player->getShip();
519
	$cargo = $ship->getCargo();
520
	debug('Ship Cargo', $cargo);
521
	foreach ($cargo as $goodID => $amount) {
522
		if ($amount > 0) {
523
			return Page::create('cargo_dump_processing.php', '', array('good_id'=>$goodID, 'amount'=>$amount));
524
		}
525
	}
526
}
527
528
function plotToSector($player, $sectorID) {
529
	return Page::create('course_plot_processing.php', '', array('from'=>$player->getSectorID(), 'to'=>$sectorID));
530
}
531
532
function plotToFed($player, $plotToHQ = false) {
533
	debug('Plotting To Fed', $plotToHQ);
534
535
	// Always drop illegal goods before heading to fed space
536
	if ($player->getShip()->hasIllegalGoods()) {
537
		debug('Dumping illegal goods');
538
		processContainer(dumpCargo($player));
539
	}
540
541
	$fedLocID = $player->getRaceID() + ($plotToHQ ? LOCATION_GROUP_RACIAL_HQS : LOCATION_GROUP_RACIAL_BEACONS);
542
	if ($player->getSector()->hasLocation($fedLocID)) {
543
		debug('Plotted to fed whilst in fed, switch NPC and wait for turns');
544
		changeNPCLogin();
545
		return true;
546
	}
547
	return plotToNearest($player, SmrLocation::getLocation($fedLocID));
548
}
549
550
function plotToNearest(AbstractSmrPlayer $player, $realX) {
551
	debug('Plotting To: ', $realX); //TODO: Can we make the debug output a bit nicer?
552
553
	if ($player->getSector()->hasX($realX)) { //Check if current sector has what we're looking for before we attempt to plot and get error.
554
		debug('Already available in sector');
555
		return true;
556
	}
557
558
	return Page::create('course_plot_nearest_processing.php', '', array('RealX'=>$realX));
559
}
560
function moveToSector($player, $targetSector) {
561
	debug('Moving from #' . $player->getSectorID() . ' to #' . $targetSector);
562
	return Page::create('sector_move_processing.php', '', array('target_sector'=>$targetSector, 'target_page'=>''));
563
}
564
565
function checkForShipUpgrade(AbstractSmrPlayer $player) {
566
	foreach (SHIP_UPGRADE_PATH[$player->getRaceID()] as $upgradeShipID) {
567
		if ($player->getShipTypeID() == $upgradeShipID) {
568
			//We can't upgrade, only downgrade.
569
			return false;
570
		}
571
		$cost = $player->getShip()->getCostToUpgrade($upgradeShipID);
572
		if ($cost <= 0 || $player->getCredits() - $cost > MINUMUM_RESERVE_CREDITS) {
573
			return doShipUpgrade($player, $upgradeShipID);
574
		}
575
	}
576
	debug('Could not find a ship on the upgrade path.');
577
	return false;
578
}
579
580
function doShipUpgrade(AbstractSmrPlayer $player, $upgradeShipID) {
581
	$plotNearest = plotToNearest($player, AbstractSmrShip::getBaseShip($upgradeShipID));
582
583
	if ($plotNearest == true) { //We're already there!
584
		//TODO: We're going to want to UNO after upgrading
585
		return Page::create('shop_ship_processing.php', '', array('ship_id'=>$upgradeShipID));
586
	} //Otherwise return the plot
587
	return $plotNearest;
588
}
589
590
function &changeRoute(array &$tradeRoutes) {
591
	$false = false;
592
	if (count($tradeRoutes) == 0) {
593
		return $false;
594
	}
595
	$routeKey = array_rand($tradeRoutes);
596
	$tradeRoute =& $tradeRoutes[$routeKey];
597
	unset($tradeRoutes[$routeKey]);
598
	$GLOBALS['TRADE_ROUTE'] =& $tradeRoute;
599
	debug('Switched route', $tradeRoute);
600
	return $tradeRoute;
601
}
602
603
function &findRoutes($player) {
604
	debug('Finding Routes');
605
606
	$tradeGoods = array(GOODS_NOTHING => false);
607
	foreach (Globals::getGoods() as $goodID => $good) {
608
		if ($player->meetsAlignmentRestriction($good['AlignRestriction'])) {
609
			$tradeGoods[$goodID] = true;
610
		} else {
611
			$tradeGoods[$goodID] = false;
612
		}
613
	}
614
615
	// Only allow NPCs to trade at ports of their race and neutral ports
616
	$tradeRaces = array();
617
	foreach (Globals::getRaces() as $raceID => $race) {
618
		$tradeRaces[$raceID] = false;
619
	}
620
	$tradeRaces[$player->getRaceID()] = true;
621
	$tradeRaces[RACE_NEUTRAL] = true;
622
623
	$galaxy = $player->getSector()->getGalaxy();
624
625
	$maxNumberOfPorts = 2;
626
	$routesForPort = -1;
627
	$numberOfRoutes = 100;
628
	$maxDistance = 15;
629
630
	$startSectorID = $galaxy->getStartSector();
631
	$endSectorID = $galaxy->getEndSector();
632
633
	$db = Smr\Database::getInstance();
634
	$db->query('SELECT routes FROM route_cache WHERE game_id=' . $db->escapeNumber($player->getGameID()) . ' AND max_ports=' . $db->escapeNumber($maxNumberOfPorts) . ' AND goods_allowed=' . $db->escapeObject($tradeGoods) . ' AND races_allowed=' . $db->escapeObject($tradeRaces) . ' AND start_sector_id=' . $db->escapeNumber($startSectorID) . ' AND end_sector_id=' . $db->escapeNumber($endSectorID) . ' AND routes_for_port=' . $db->escapeNumber($routesForPort) . ' AND max_distance=' . $db->escapeNumber($maxDistance));
635
	if ($db->nextRecord()) {
636
		$routes = $db->getObject('routes', true);
637
		debug('Using Cached Routes: #' . count($routes));
638
		return $routes;
639
	} else {
640
		debug('Generating Routes');
641
		$allSectors = array();
642
		foreach (SmrGalaxy::getGameGalaxies($player->getGameID()) as $galaxy) {
643
			$allSectors += $galaxy->getSectors(); //Merge arrays
644
		}
645
646
		$distances = Plotter::calculatePortToPortDistances($allSectors, $maxDistance, $startSectorID, $endSectorID);
647
648
		if ($maxNumberOfPorts == 1) {
0 ignored issues
show
introduced by
The condition $maxNumberOfPorts == 1 is always false.
Loading history...
649
			$allRoutes = \Routes\RouteGenerator::generateOneWayRoutes($allSectors, $distances, $tradeGoods, $tradeRaces, $routesForPort);
650
		} else {
651
			$allRoutes = \Routes\RouteGenerator::generateMultiPortRoutes($maxNumberOfPorts, $allSectors, $tradeGoods, $tradeRaces, $distances, $routesForPort, $numberOfRoutes);
652
		}
653
654
		unset($distances);
655
656
		$routesMerged = array();
657
		foreach ($allRoutes[\Routes\RouteGenerator::MONEY_ROUTE] as $multi => $routesByMulti) {
658
			$routesMerged += $routesByMulti; //Merge arrays
659
		}
660
661
		unset($allSectors);
662
		SmrPort::clearCache();
663
		SmrSector::clearCache();
664
665
		if (count($routesMerged) == 0) {
666
			debug('Could not find any routes! Try another NPC.');
667
			changeNPCLogin();
668
		}
669
670
		$db->query('INSERT INTO route_cache ' .
671
				'(game_id, max_ports, goods_allowed, races_allowed, start_sector_id, end_sector_id, routes_for_port, max_distance, routes)' .
672
				' VALUES (' . $db->escapeNumber($player->getGameID()) . ', ' . $db->escapeNumber($maxNumberOfPorts) . ', ' . $db->escapeObject($tradeGoods) . ', ' . $db->escapeObject($tradeRaces) . ', ' . $db->escapeNumber($startSectorID) . ', ' . $db->escapeNumber($endSectorID) . ', ' . $db->escapeNumber($routesForPort) . ', ' . $db->escapeNumber($maxDistance) . ', ' . $db->escapeObject($routesMerged, true) . ')');
673
		debug('Found Routes: #' . count($routesMerged));
674
		return $routesMerged;
675
	}
676
}
677