810 lines
28 KiB
PHP
810 lines
28 KiB
PHP
<?php
|
|
/* vim: set noexpandtab tabstop=2 softtabstop=2 shiftwidth=2: */
|
|
|
|
/**
|
|
* Checkpoints plugin.
|
|
* Provides checkpoints/finish tracking, and displays checkpoint passages
|
|
* via automatic pop-ups (useful only in Rounds/Team/Cup modes).
|
|
* On TMF, manages the CP panel during playing, retiring & spectating,
|
|
* as well as the Cup mode's warm-up phase. Disabled in Stunts mode.
|
|
* Created by Xymph
|
|
*
|
|
* Dependencies: used by plugin.dedimania.php & plugin.localdatabase.php
|
|
*/
|
|
|
|
Aseco::registerEvent('onPlayerConnect', 'addplayer_cp');
|
|
Aseco::registerEvent('onPlayerDisconnect', 'removeplayer_cp');
|
|
Aseco::registerEvent('onNewChallenge', 'reset_checkp');
|
|
Aseco::registerEvent('onBeginRound', 'clear_curr_cp');
|
|
Aseco::registerEvent('onEndRace', 'disable_checkp');
|
|
Aseco::registerEvent('onRestartChallenge', 'restart_checkp');
|
|
Aseco::registerEvent('onCheckpoint', 'store_checkp');
|
|
Aseco::registerEvent('onPlayerFinish1', 'store_finish'); // use pre event before local/Dedimania record processsing
|
|
Aseco::registerEvent('onPlayerInfoChanged', 'spec_togglecp');
|
|
|
|
Aseco::addChatCommand('cps', 'Sets local record checkpoints tracking');
|
|
Aseco::addChatCommand('cpsspec', 'Shows checkpoints of spectated player');
|
|
Aseco::addChatCommand('cptms', 'Displays all local records\' checkpoint times');
|
|
Aseco::addChatCommand('sectms', 'Displays all local records\' sector times');
|
|
|
|
global $checkpoints, $checkpoint_tests;
|
|
$checkpoints = array();
|
|
$checkpoint_tests = false; // after reload no tests until end race
|
|
|
|
class Checkpoints {
|
|
var $loclrec;
|
|
var $dedirec;
|
|
var $best_time;
|
|
var $best_fin;
|
|
var $best_cps;
|
|
var $curr_fin;
|
|
var $curr_cps;
|
|
var $speccers;
|
|
|
|
// init empty checkpoints
|
|
function Checkpoints() {
|
|
$this->loclrec = -1; // -1 = off, 0 = own/last rec, 1-max = rec #1-max
|
|
$this->dedirec = -1; // -1 = off, 0 = own/last rec, 1-30 = rec #1-30
|
|
$this->best_time = 0;
|
|
$this->best_fin = PHP_INT_MAX;
|
|
$this->curr_fin = PHP_INT_MAX;
|
|
$this->best_cps = array();
|
|
$this->curr_cps = array();
|
|
$this->speccers = array();
|
|
}
|
|
} // class Checkpoints
|
|
|
|
function chat_cps($aseco, $command) {
|
|
global $checkpoints;
|
|
|
|
$player = $command['author'];
|
|
$login = $player->login;
|
|
|
|
// check for relay server
|
|
if ($aseco->server->isrelay) {
|
|
$message = formatText($aseco->getChatMessage('NOTONRELAY'));
|
|
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($message), $login);
|
|
return;
|
|
}
|
|
|
|
if ($aseco->settings['display_checkpoints']) {
|
|
// set local checkpoints tracking
|
|
$param = $command['params'];
|
|
if (strtolower($param) == 'off') {
|
|
$checkpoints[$login]->loclrec = -1;
|
|
$checkpoints[$login]->dedirec = -1;
|
|
$message = '{#server}> Local checkpoints tracking: {#highlite}OFF';
|
|
}
|
|
elseif ($param == '') {
|
|
$checkpoints[$login]->loclrec = 0;
|
|
$checkpoints[$login]->dedirec = -1;
|
|
$message = '{#server}> Local checkpoints tracking: {#highlite}ON {#server}(your own or the last record)';
|
|
}
|
|
elseif (is_numeric($param) && $param > 0 && $param <= $aseco->server->records->max) {
|
|
$checkpoints[$login]->loclrec = intval($param);
|
|
$checkpoints[$login]->dedirec = -1;
|
|
$message = '{#server}> Local checkpoints tracking record: {#highlite}' . $checkpoints[$login]->loclrec;
|
|
}
|
|
else {
|
|
$message = '{#server}> {#error}No such Local record {#highlite}$i ' . $param;
|
|
}
|
|
|
|
// handle TMF checkpoints panel
|
|
if ($aseco->server->getGame() == 'TMF') {
|
|
if ($checkpoints[$login]->loclrec == -1) {
|
|
// disable CP panel
|
|
if ($aseco->settings['enable_cpsspec'] && !empty($checkpoints[$login]->speccers))
|
|
cpspanel_off($aseco, $login . ',' . implode(',', $checkpoints[$login]->speccers));
|
|
else
|
|
cpspanel_off($aseco, $login);
|
|
} else {
|
|
// enable CP panel unless spectator, Stunts mode, or warm-up
|
|
if (!$player->isspectator && $aseco->server->gameinfo->mode != Gameinfo::STNT && !$aseco->warmup_phase) {
|
|
if ($aseco->settings['enable_cpsspec'] && !empty($checkpoints[$login]->speccers))
|
|
display_cpspanel($aseco, $login . ',' . implode(',', $checkpoints[$login]->speccers), 0, '$00f -.--');
|
|
else
|
|
display_cpspanel($aseco, $login, 0, '$00f -.--');
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
$message = '{#server}> {#error}Checkpoints tracking permanently disabled by server';
|
|
}
|
|
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($message), $login);
|
|
} // chat_cps
|
|
|
|
function chat_cpsspec($aseco, $command) {
|
|
global $checkpoints;
|
|
|
|
$player = $command['author'];
|
|
$login = $player->login;
|
|
|
|
// check for relay server
|
|
if ($aseco->server->isrelay) {
|
|
$message = formatText($aseco->getChatMessage('NOTONRELAY'));
|
|
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($message), $login);
|
|
return;
|
|
}
|
|
|
|
if ($aseco->server->getGame() == 'TMF') {
|
|
if ($aseco->settings['enable_cpsspec']) {
|
|
// toggle cpsspec setting
|
|
if ($player->speclogin != '') {
|
|
// if subscribed, unsubscribe first
|
|
if ($player->speclogin != ',' && isset($checkpoints[$player->speclogin])) {
|
|
if (($i = array_search($login, $checkpoints[$player->speclogin]->speccers)) !== false)
|
|
unset($checkpoints[$player->speclogin]->speccers[$i]);
|
|
}
|
|
$player->speclogin = '';
|
|
cpspanel_off($aseco, $login);
|
|
} else {
|
|
// if spectator, subscribe
|
|
$aseco->client->query('GetPlayerInfo', $login, 1);
|
|
$info = $aseco->client->getResponse();
|
|
$targetid = floor($info['SpectatorStatus'] / 10000);
|
|
// check for player or free camera
|
|
if ($info['SpectatorStatus'] == 0 || $targetid == 255) {
|
|
$player->speclogin = ','; // no target
|
|
} else {
|
|
// find login for target
|
|
foreach ($aseco->server->players->player_list as $pl) {
|
|
if ($pl->pid == $targetid) {
|
|
$player->speclogin = $pl->login;
|
|
// subscribe to this player
|
|
if (!in_array($login, $checkpoints[$player->speclogin]->speccers))
|
|
$checkpoints[$player->speclogin]->speccers[] = $login;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// show chat message
|
|
$message = '{#server}> Spectated player checkpoints tracking ';
|
|
if ($player->speclogin != '')
|
|
$message .= 'enabled';
|
|
else
|
|
$message .= 'disabled';
|
|
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($message), $login);
|
|
} else {
|
|
$message = $aseco->getChatMessage('NO_CPSSPEC');
|
|
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($message), $login);
|
|
}
|
|
} else {
|
|
$message = $aseco->getChatMessage('FOREVER_ONLY');
|
|
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($message), $login);
|
|
}
|
|
} // chat_cpsspec
|
|
|
|
|
|
function chat_cptms($aseco, $command) {
|
|
chat_sectms($aseco, $command, false);
|
|
} // chat_cptms
|
|
|
|
function chat_sectms($aseco, $command, $diff = true) {
|
|
|
|
$player = $command['author'];
|
|
$login = $player->login;
|
|
|
|
// check for relay server
|
|
if ($aseco->server->isrelay) {
|
|
$message = formatText($aseco->getChatMessage('NOTONRELAY'));
|
|
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($message), $login);
|
|
return;
|
|
}
|
|
|
|
if (!$total = $aseco->server->records->count()) {
|
|
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors('{#server}> {#error}No records found!'), $login);
|
|
return;
|
|
}
|
|
|
|
// find sector count from first record with CP times
|
|
$cpscnt = '?';
|
|
for ($i = 0; $i < $total; $i++) {
|
|
$cur_record = $aseco->server->records->getRecord($i);
|
|
if (!empty($cur_record->checks)) {
|
|
$cpscnt = count($cur_record->checks);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// display popup window for TMN
|
|
if ($aseco->server->getGame() == 'TMN') {
|
|
$head = 'Current TOP ' . $aseco->server->records->max . ' Local ' . ($diff ? 'Sector' : 'CP') . ' Times (' . $cpscnt . '):' . LF;
|
|
$cpsmax = 9;
|
|
$msg = '';
|
|
$lines = 0;
|
|
$player->msgs = array();
|
|
$player->msgs[0] = 1;
|
|
|
|
// create list of records
|
|
for ($i = 0; $i < $total; $i++) {
|
|
$cur_record = $aseco->server->records->getRecord($i);
|
|
$msg .= str_pad($i+1, 2, '0', STR_PAD_LEFT) . '. '
|
|
. ($cur_record->new ? '{#black}' : '')
|
|
. formatTime($cur_record->score);
|
|
// append up to $cpsmax sector/CP times
|
|
if (!empty($cur_record->checks)) {
|
|
$j = 1;
|
|
$pr = 0;
|
|
$msg .= '$n';
|
|
foreach ($cur_record->checks as $cp) {
|
|
$msg .= ' ' . formatTime($cp - $pr);
|
|
if ($diff) $pr = $cp;
|
|
if (++$j > $cpsmax) {
|
|
if ($cpscnt > $cpsmax) $msg .= ' +';
|
|
break;
|
|
}
|
|
}
|
|
$msg .= '$m';
|
|
}
|
|
$msg .= LF;
|
|
if (++$lines > 9) {
|
|
$player->msgs[] = $aseco->formatColors($head . $msg);
|
|
$lines = 0;
|
|
$msg = '';
|
|
}
|
|
}
|
|
// add if last batch exists
|
|
if ($msg != '')
|
|
$player->msgs[] = $aseco->formatColors($head . $msg);
|
|
|
|
// display popup message
|
|
if (count($player->msgs) == 2) {
|
|
$aseco->client->query('SendDisplayServerMessageToLogin', $login, $player->msgs[1], 'OK', '', 0);
|
|
} else { // > 2
|
|
$aseco->client->query('SendDisplayServerMessageToLogin', $login, $player->msgs[1], 'Close', 'Next', 0);
|
|
}
|
|
|
|
// display ManiaLink window for TMF
|
|
} elseif ($aseco->server->getGame() == 'TMF') {
|
|
$head = 'Current TOP ' . $aseco->server->records->max . ' Local ' . ($diff ? 'Sector' : 'CP') . ' Times (' . $cpscnt . '):';
|
|
$cpsmax = 12;
|
|
// compute widths
|
|
$width = 0.1 + 0.18 + min($cpscnt, $cpsmax) * 0.1 + ($cpscnt > $cpsmax ? 0.06 : 0.0);
|
|
if ($width < 1.0) $width = 1.0;
|
|
$widths = array($width, 0.1, 0.18);
|
|
for ($i = 0; $i < min($cpscnt, $cpsmax); $i++)
|
|
$widths[] = 0.1; // cp
|
|
if ($cpscnt > $cpsmax)
|
|
$widths[] = 0.06;
|
|
|
|
$msg = array();
|
|
$lines = 0;
|
|
$player->msgs = array();
|
|
$player->msgs[0] = array(1, $head, $widths, array('BgRaceScore2', 'Podium'));
|
|
|
|
// create list of records
|
|
for ($i = 0; $i < $total; $i++) {
|
|
$cur_record = $aseco->server->records->getRecord($i);
|
|
$line = array();
|
|
$line[] = str_pad($i+1, 2, '0', STR_PAD_LEFT) . '.';
|
|
$line[] = ($cur_record->new ? '{#black}' : '') .
|
|
($aseco->server->gameinfo->mode == Gameinfo::STNT ?
|
|
$cur_record->score : formatTime($cur_record->score));
|
|
// append up to $cpsmax sector/CP times
|
|
if (!empty($cur_record->checks)) {
|
|
$j = 1;
|
|
$pr = 0;
|
|
foreach ($cur_record->checks as $cp) {
|
|
$line[] = '$n' . formatTime($cp - $pr);
|
|
if ($diff) $pr = $cp;
|
|
if (++$j > $cpsmax) {
|
|
if ($cpscnt > $cpsmax) $line[] = '+';
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
$msg[] = $line;
|
|
if (++$lines > 14) {
|
|
$player->msgs[] = $msg;
|
|
$lines = 0;
|
|
$msg = array();
|
|
}
|
|
}
|
|
// add if last batch exists
|
|
if (!empty($msg))
|
|
$player->msgs[] = $msg;
|
|
|
|
// display ManiaLink message
|
|
display_manialink_multi($player);
|
|
|
|
// show chat message for TMO & TMS
|
|
} else {
|
|
$msg = $aseco->formatColors('{#server}> {#error}No sector times available');
|
|
$aseco->client->query('ChatSendServerMessageToLogin', $msg, $login);
|
|
}
|
|
} // chat_sectms
|
|
|
|
|
|
// called @ onPlayerConnect
|
|
function addplayer_cp($aseco, $player) {
|
|
global $checkpoints;
|
|
|
|
$login = $player->login;
|
|
|
|
$checkpoints[$login] = new Checkpoints();
|
|
// set first lap reference in Laps mode
|
|
if ($aseco->server->gameinfo->mode == Gameinfo::LAPS)
|
|
$checkpoints[$login]->curr_fin = 0;
|
|
if ($aseco->settings['display_checkpoints']) {
|
|
// set personal or default CPs
|
|
if ($cps = ldb_getCPs($aseco, $login)) {
|
|
$checkpoints[$login]->loclrec = $cps['cps'];
|
|
$checkpoints[$login]->dedirec = $cps['dedicps'];
|
|
} else {
|
|
if ($aseco->settings['auto_enable_cps'])
|
|
$checkpoints[$login]->loclrec = 0;
|
|
if ($aseco->settings['auto_enable_dedicps'])
|
|
$checkpoints[$login]->dedirec = 0;
|
|
}
|
|
}
|
|
} // addplayer_cp
|
|
|
|
// called @ onPlayerDisconnect
|
|
function removeplayer_cp($aseco, $player) {
|
|
global $checkpoints;
|
|
|
|
$login = $player->login;
|
|
|
|
ldb_setCPs($aseco, $login,
|
|
$checkpoints[$login]->loclrec, $checkpoints[$login]->dedirec);
|
|
|
|
// free up memory
|
|
unset($checkpoints[$login]);
|
|
} // removeplayer_cp
|
|
|
|
// called @ onNewChallenge
|
|
function reset_checkp($aseco, $challenge) {
|
|
global $checkpoints, $laps_cpcount;
|
|
|
|
// clear all checkpoints
|
|
foreach ($checkpoints as $login => $cp) {
|
|
$checkpoints[$login]->best_cps = array();
|
|
$checkpoints[$login]->curr_cps = array();
|
|
$checkpoints[$login]->best_fin = PHP_INT_MAX;
|
|
if ($aseco->server->gameinfo->mode == Gameinfo::LAPS)
|
|
$checkpoints[$login]->curr_fin = 0;
|
|
else
|
|
$checkpoints[$login]->curr_fin = PHP_INT_MAX;
|
|
}
|
|
|
|
// set local checkpoint references
|
|
if ($aseco->settings['display_checkpoints']) {
|
|
foreach ($checkpoints as $login => $cp) {
|
|
$lrec = $checkpoints[$login]->loclrec - 1;
|
|
|
|
// check for specific record
|
|
if ($lrec+1 > 0) {
|
|
// if specific record unavailable, use last one
|
|
if ($lrec > $aseco->server->records->count() - 1)
|
|
$lrec = $aseco->server->records->count() - 1;
|
|
$curr = $aseco->server->records->getRecord($lrec);
|
|
// check for valid checkpoints
|
|
if (!empty($curr->checks) && $curr->score == end($curr->checks)) {
|
|
$checkpoints[$login]->best_fin = $curr->score;
|
|
$checkpoints[$login]->best_cps = $curr->checks;
|
|
}
|
|
}
|
|
elseif ($lrec+1 == 0) {
|
|
// search for own/last record
|
|
$lrec = 0;
|
|
while ($lrec < $aseco->server->records->count()) {
|
|
$curr = $aseco->server->records->getRecord($lrec++);
|
|
if ($curr->player->login == $login)
|
|
break;
|
|
}
|
|
// check for valid checkpoints
|
|
if (!empty($curr->checks) && $curr->score == end($curr->checks)) {
|
|
$checkpoints[$login]->best_fin = $curr->score;
|
|
$checkpoints[$login]->best_cps = $curr->checks;
|
|
}
|
|
} // else -1
|
|
}
|
|
}
|
|
|
|
// CP count only for Laps mode
|
|
if ($aseco->server->getGame() == 'TMF')
|
|
$laps_cpcount = $challenge->nbchecks;
|
|
else
|
|
$laps_cpcount = 0;
|
|
} // reset_checkp
|
|
|
|
// called @ onBeginRound
|
|
function clear_curr_cp($aseco) {
|
|
global $checkpoints;
|
|
|
|
// if Stunts mode or warm-up, bail out immediately
|
|
if ($aseco->server->gameinfo->mode == Gameinfo::STNT || $aseco->warmup_phase) return;
|
|
|
|
// clear current checkpoints
|
|
foreach ($checkpoints as $login => $cp) {
|
|
$checkpoints[$login]->curr_cps = array();
|
|
// set first lap reference in Laps mode, otherwise max time
|
|
if ($aseco->server->gameinfo->mode == Gameinfo::LAPS)
|
|
$checkpoints[$login]->curr_fin = 0;
|
|
else
|
|
$checkpoints[$login]->curr_fin = PHP_INT_MAX;
|
|
|
|
// reset CP panel unless spectator
|
|
if ($aseco->server->getGame() == 'TMF' && $checkpoints[$login]->loclrec != -1) {
|
|
$player = $aseco->server->players->getPlayer($login);
|
|
if (!$player->isspectator) {
|
|
if ($aseco->settings['enable_cpsspec'] && !empty($checkpoints[$login]->speccers))
|
|
display_cpspanel($aseco, $login . ',' . implode(',', $checkpoints[$login]->speccers), 0, '$00f -.--');
|
|
else
|
|
display_cpspanel($aseco, $login, 0, '$00f -.--');
|
|
}
|
|
}
|
|
}
|
|
} // clear_curr_cp
|
|
|
|
// called @ onEndRace
|
|
function disable_checkp($aseco, $data) {
|
|
global $checkpoint_tests;
|
|
|
|
// disable CP panels at end of track
|
|
if ($aseco->server->getGame() == 'TMF') {
|
|
allcpspanels_off($aseco);
|
|
}
|
|
|
|
$checkpoint_tests = true; // now commence cheat tests
|
|
} // disable_checkp
|
|
|
|
// called @ onRestartChallenge
|
|
function restart_checkp($aseco, $data) {
|
|
global $checkpoints, $checkpoint_tests;
|
|
|
|
// clear current checkpoints
|
|
foreach ($checkpoints as $login => $cp) {
|
|
$checkpoints[$login]->curr_cps = array();
|
|
// set first lap reference in Laps mode, otherwise max time
|
|
if ($aseco->server->gameinfo->mode == Gameinfo::LAPS)
|
|
$checkpoints[$login]->curr_fin = 0;
|
|
else
|
|
$checkpoints[$login]->curr_fin = PHP_INT_MAX;
|
|
}
|
|
|
|
$checkpoint_tests = true; // now commence cheat tests
|
|
} // restart_checkp
|
|
|
|
// called @ onCheckpoint
|
|
// TMN: [0]=PlayerUid, [1]=Login, [2]=Time, [3]=Score, [4]=CheckpointIndex
|
|
// TMF: [0]=PlayerUid, [1]=Login, [2]=TimeOrScore, [3]=CurLap, [4]=CheckpointIndex
|
|
function store_checkp($aseco, $checkpt) {
|
|
global $rasp, $checkpoints, $laps_cpcount, $checkpoint_tests,
|
|
$feature_stats; // from rasp.settings.php
|
|
|
|
// if Stunts mode, bail out immediately
|
|
// no checkpoints during warm-up, so no need to check that
|
|
if ($aseco->server->gameinfo->mode == Gameinfo::STNT) return;
|
|
|
|
// if undefined login, bail out too
|
|
$login = $checkpt[1];
|
|
if (!isset($checkpoints[$login])) return;
|
|
|
|
// check for Laps mode
|
|
if ($aseco->server->gameinfo->mode != Gameinfo::LAPS) {
|
|
|
|
// reset for next run in TimeAttack mode
|
|
if ($aseco->server->gameinfo->mode == Gameinfo::TA && $checkpt[4] == 0)
|
|
$checkpoints[$login]->curr_cps = array();
|
|
|
|
// check for cheated checkpoints:
|
|
// non-positive time, wrong index, or time less than preceding one
|
|
if ($checkpt[2] <= 0 || $checkpt[4] != count($checkpoints[$login]->curr_cps) ||
|
|
($checkpt[4] > 0 && $checkpt[2] < end($checkpoints[$login]->curr_cps))) {
|
|
if ($checkpoint_tests) {
|
|
$aseco->processCheater($login, $checkpoints[$login]->curr_cps, $checkpt, -1);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// store current checkpoint
|
|
$checkpoints[$login]->curr_cps[$checkpt[4]] = $checkpt[2];
|
|
|
|
// check if displaying for this player, and for best checkpoints
|
|
if ($checkpoints[$login]->loclrec != -1 &&
|
|
isset($checkpoints[$login]->best_cps[$checkpt[4]])) {
|
|
|
|
// check whether not last one (Finish) on TMN
|
|
$check = $checkpt[4] + 1;
|
|
if ($aseco->server->getGame() == 'TMF' ||
|
|
$check < count($checkpoints[$login]->best_cps)) {
|
|
|
|
$diff = $checkpoints[$login]->curr_cps[$checkpt[4]] -
|
|
$checkpoints[$login]->best_cps[$checkpt[4]];
|
|
// check for improvement
|
|
if ($diff < 0) {
|
|
$diff = abs($diff);
|
|
$sign = '$00f-'; // blue
|
|
} elseif ($diff == 0) {
|
|
$sign = '$00f'; // blue
|
|
} else { // $diff > 0
|
|
$sign = '$f00+'; // red
|
|
}
|
|
$sec = floor($diff/1000);
|
|
$hun = ($diff - ($sec * 1000)) / 10;
|
|
|
|
if ($aseco->server->getGame() == 'TMN') {
|
|
$cpmsg = '$nCP' . $check . ': $m$000' . formatTime($checkpt[2])
|
|
. ' $w' . $sign . sprintf('%d.%02d', $sec, $hun);
|
|
// display temporary popup message
|
|
$aseco->client->query('SendDisplayServerMessageToLogin', $login,
|
|
$cpmsg, '', '', 2000); // timeout 2 secs
|
|
} else { // TMF
|
|
// check for Finish checkpoint
|
|
if ($check == count($checkpoints[$login]->best_cps))
|
|
$check = 'F';
|
|
// update CP panel
|
|
if ($aseco->settings['enable_cpsspec'] && !empty($checkpoints[$login]->speccers))
|
|
display_cpspanel($aseco, $login . ',' . implode(',', $checkpoints[$login]->speccers), $check,
|
|
$sign . sprintf('%d.%02d', $sec, $hun));
|
|
else
|
|
display_cpspanel($aseco, $login, $check,
|
|
$sign . sprintf('%d.%02d', $sec, $hun));
|
|
}
|
|
}
|
|
}
|
|
|
|
} else { // Laps
|
|
|
|
// no support on TMN because PlayerCheckpoint event doesn't supply CurLap
|
|
if ($aseco->server->getGame() != 'TMF') return;
|
|
|
|
// check for cheated checkpoints:
|
|
// non-positive time, negative index
|
|
if ($checkpt[2] <= 0 || $checkpt[4] < 0) {
|
|
if ($checkpoint_tests) {
|
|
$aseco->processCheater($login, $checkpoints[$login]->curr_cps, $checkpt, -1);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// in TMN get checkpoints count/lap from first player to complete first lap
|
|
if ($laps_cpcount == 0 && $checkpt[3] == 1)
|
|
$laps_cpcount = $checkpt[4] + 1;
|
|
|
|
// get relative CP in this lap
|
|
if ($laps_cpcount > 0)
|
|
$relcheck = $checkpt[4] % $laps_cpcount;
|
|
else // first lap
|
|
$relcheck = $checkpt[4];
|
|
|
|
// check for cheated checkpoints:
|
|
// wrong index, time not more than reference, relative time less than preceding one
|
|
if ($relcheck != count($checkpoints[$login]->curr_cps) ||
|
|
$checkpt[2] < $checkpoints[$login]->curr_fin ||
|
|
($relcheck > 0 && $checkpt[2] - $checkpoints[$login]->curr_fin < end($checkpoints[$login]->curr_cps))) {
|
|
if ($checkpoint_tests) {
|
|
$aseco->processCheater($login, $checkpoints[$login]->curr_cps, $checkpt, -1);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// store current checkpoint for current lap, relative to reference
|
|
$checkpoints[$login]->curr_cps[$relcheck] = $checkpt[2] - $checkpoints[$login]->curr_fin;
|
|
|
|
// check for a completed lap
|
|
if ($checkpt[3] * $laps_cpcount != $checkpt[4] + 1) {
|
|
|
|
// check if displaying for this player, and for best checkpoints
|
|
if ($checkpoints[$login]->loclrec != -1 &&
|
|
isset($checkpoints[$login]->best_cps[$relcheck])) {
|
|
|
|
// check for improvement
|
|
$diff = $checkpoints[$login]->curr_cps[$relcheck] - $checkpoints[$login]->best_cps[$relcheck];
|
|
if ($diff < 0) {
|
|
$diff = abs($diff);
|
|
$sign = '$00f-'; // blue
|
|
} elseif ($diff == 0) {
|
|
$sign = '$00f'; // blue
|
|
} else { // $diff > 0
|
|
$sign = '$f00+'; // red
|
|
}
|
|
$sec = floor($diff/1000);
|
|
$hun = ($diff - ($sec * 1000)) / 10;
|
|
|
|
// update CP panel
|
|
if ($aseco->settings['enable_cpsspec'] && !empty($checkpoints[$login]->speccers))
|
|
display_cpspanel($aseco, $login . ',' . implode(',', $checkpoints[$login]->speccers), $relcheck + 1,
|
|
$sign . sprintf('%d.%02d', $sec, $hun));
|
|
else
|
|
display_cpspanel($aseco, $login, $relcheck + 1,
|
|
$sign . sprintf('%d.%02d', $sec, $hun));
|
|
}
|
|
|
|
} else { // completed lap
|
|
|
|
// store current lap finish as reference for next lap
|
|
$checkpoints[$login]->curr_fin = $checkpt[2];
|
|
|
|
// build a record object with the current lap information
|
|
$finish_item = new Record();
|
|
$finish_item->player = $aseco->server->players->getPlayer($login);
|
|
$finish_item->score = $checkpoints[$login]->curr_cps[$relcheck];
|
|
$finish_item->date = strftime('%Y-%m-%d %H:%M:%S');
|
|
$finish_item->challenge = clone $aseco->server->challenge;
|
|
unset($finish_item->challenge->gbx); // reduce memory usage
|
|
unset($finish_item->challenge->tmx);
|
|
|
|
// store current lap
|
|
if ($feature_stats) {
|
|
$rasp->insertTime($finish_item, implode(',', $checkpoints[$login]->curr_cps));
|
|
}
|
|
|
|
// process for local and Dedimania records
|
|
$finish_item->new = true; // set lap 'Finish' flag
|
|
ldb_playerFinish($aseco, $finish_item);
|
|
$finish_item->new = true; // ditto
|
|
if (function_exists('dedimania_playerfinish'))
|
|
dedimania_playerfinish($aseco, $finish_item);
|
|
|
|
// check for new best lap
|
|
$diff = $checkpoints[$login]->curr_cps[$relcheck] - $checkpoints[$login]->best_fin;
|
|
if ($diff < 0) {
|
|
// store new best lap
|
|
$checkpoints[$login]->best_fin = $checkpoints[$login]->curr_cps[$relcheck];
|
|
$checkpoints[$login]->best_cps = $checkpoints[$login]->curr_cps;
|
|
// store timestamp for sorting in case of equal bests
|
|
$checkpoints[$login]->best_time = microtime(true);
|
|
}
|
|
|
|
// check if displaying for this player, and not first lap
|
|
if ($checkpoints[$login]->loclrec != -1 && $checkpt[4] + 1 >= $laps_cpcount) {
|
|
// check for improvement
|
|
if ($diff < 0) {
|
|
$diff = abs($diff);
|
|
$sign = '$00f-'; // blue
|
|
} elseif ($diff == 0) {
|
|
$sign = '$00f'; // blue
|
|
} else { // $diff > 0
|
|
$sign = '$f00+'; // red
|
|
}
|
|
$sec = floor($diff/1000);
|
|
$hun = ($diff - ($sec * 1000)) / 10;
|
|
|
|
// indicate Lap Finish checkpoint
|
|
$relcheck = 'L';
|
|
// update CP panel
|
|
if ($aseco->settings['enable_cpsspec'] && !empty($checkpoints[$login]->speccers))
|
|
display_cpspanel($aseco, $login . ',' . implode(',', $checkpoints[$login]->speccers), $relcheck,
|
|
$sign . sprintf('%d.%02d', $sec, $hun));
|
|
else
|
|
display_cpspanel($aseco, $login, $relcheck,
|
|
$sign . sprintf('%d.%02d', $sec, $hun));
|
|
}
|
|
|
|
// reset for next lap
|
|
$checkpoints[$login]->curr_cps = array();
|
|
}
|
|
}
|
|
} // store_checkp
|
|
|
|
// called @ onPlayerFinish1
|
|
function store_finish($aseco, $finish_item) {
|
|
global $checkpoints, $checkpoint_tests;
|
|
|
|
// if Laps or Stunts mode, bail out immediately
|
|
// no finishes during warm-up, so no need to check that
|
|
if ($aseco->server->gameinfo->mode == Gameinfo::LAPS ||
|
|
$aseco->server->gameinfo->mode == Gameinfo::STNT) return;
|
|
|
|
$login = $finish_item->player->login;
|
|
// in case of CP order problem
|
|
sort($checkpoints[$login]->curr_cps);
|
|
|
|
// check for actual finish
|
|
if ($finish_item->score > 0) {
|
|
// compute number of checkpoints (incl. multilaps except in TA mode)
|
|
$reqchecks = $finish_item->challenge->nbchecks;
|
|
if ($aseco->server->getGame() == 'TMF' && $aseco->server->gameinfo->mode != Gameinfo::TA &&
|
|
$finish_item->challenge->laprace) {
|
|
if ($finish_item->challenge->forcedlaps != 0)
|
|
$reqchecks *= $finish_item->challenge->forcedlaps;
|
|
else
|
|
$reqchecks *= $finish_item->challenge->nblaps;
|
|
}
|
|
|
|
// check for required number of checkpoints on TMF
|
|
if ($aseco->server->getGame() == 'TMF' && $reqchecks != count($checkpoints[$login]->curr_cps)) {
|
|
if ($checkpoint_tests) {
|
|
trigger_error('CPs for ' . $login . ' required: ' . $reqchecks . ' present: ' . count($checkpoints[$login]->curr_cps) .
|
|
' - ' . implode(',', $checkpoints[$login]->curr_cps), E_USER_WARNING);
|
|
}
|
|
// reset to prevent local/Dedimania records
|
|
$finish_item->score = 0;
|
|
// check for finish equal last checkpoint
|
|
} elseif ($finish_item->score == end($checkpoints[$login]->curr_cps)) {
|
|
$checkpoints[$login]->curr_fin = $finish_item->score;
|
|
|
|
// check for improvement
|
|
if ($checkpoints[$login]->curr_fin < $checkpoints[$login]->best_fin) {
|
|
$checkpoints[$login]->best_fin = $checkpoints[$login]->curr_fin;
|
|
$checkpoints[$login]->best_cps = $checkpoints[$login]->curr_cps;
|
|
// store timestamp for sorting in case of equal bests
|
|
$checkpoints[$login]->best_time = microtime(true);
|
|
}
|
|
} else {
|
|
if ($checkpoint_tests) {
|
|
$aseco->processCheater($login, $checkpoints[$login]->curr_cps, false, $finish_item->score);
|
|
// reset to prevent local/Dedimania records
|
|
$finish_item->score = 0;
|
|
}
|
|
}
|
|
}
|
|
// check for player retire in TimeAttack mode
|
|
elseif ($aseco->server->getGame() == 'TMF' && $aseco->server->gameinfo->mode == Gameinfo::TA &&
|
|
$finish_item->score == 0 && $checkpoints[$login]->loclrec != -1) {
|
|
// reset CP panel
|
|
if ($aseco->settings['enable_cpsspec'] && !empty($checkpoints[$login]->speccers))
|
|
display_cpspanel($aseco, $login . ',' . implode(',', $checkpoints[$login]->speccers), 0, '$00f -.--');
|
|
else
|
|
display_cpspanel($aseco, $login, 0, '$00f -.--');
|
|
}
|
|
} // store_finish
|
|
|
|
// called @ onPlayerInfoChanged
|
|
function spec_togglecp($aseco, $playerinfo) {
|
|
global $checkpoints;
|
|
|
|
// if Stunts mode or warm-up, bail out immediately
|
|
if ($aseco->server->gameinfo->mode == Gameinfo::STNT || $aseco->warmup_phase) return;
|
|
|
|
$login = $playerinfo['Login'];
|
|
$player = $aseco->server->players->getPlayer($login);
|
|
// if no real spectator status change, bail out immediately
|
|
if ($player->prevstatus == $player->isspectator) return;
|
|
|
|
// check if CPS active
|
|
if (isset($checkpoints[$login]) && $checkpoints[$login]->loclrec != -1) {
|
|
// check spectator status
|
|
if ($playerinfo['SpectatorStatus'] != 0) {
|
|
// disable CP panel
|
|
if ($aseco->settings['enable_cpsspec'] && !empty($checkpoints[$login]->speccers))
|
|
cpspanel_off($aseco, $login . ',' . implode(',', $checkpoints[$login]->speccers));
|
|
else
|
|
cpspanel_off($aseco, $login);
|
|
} else {
|
|
// enable CP panel
|
|
if ($aseco->settings['enable_cpsspec'] && !empty($checkpoints[$login]->speccers))
|
|
display_cpspanel($aseco, $login . ',' . implode(',', $checkpoints[$login]->speccers), 0, '$00f -.--');
|
|
else
|
|
display_cpspanel($aseco, $login, 0, '$00f -.--');
|
|
}
|
|
}
|
|
|
|
// check for spectated player update
|
|
if ($aseco->settings['enable_cpsspec']) {
|
|
// check if /cpsspec enabled
|
|
if ($player->speclogin != '') {
|
|
$targetid = floor($playerinfo['SpectatorStatus'] / 10000);
|
|
|
|
// check for player status or free camera
|
|
if ($playerinfo['SpectatorStatus'] == 0 || $targetid == 255) {
|
|
// if subscribed, unsubscribe first
|
|
if ($player->speclogin != ',' && isset($checkpoints[$player->speclogin])) {
|
|
if (($i = array_search($login, $checkpoints[$player->speclogin]->speccers)) !== false)
|
|
unset($checkpoints[$player->speclogin]->speccers[$i]);
|
|
}
|
|
$player->speclogin = ','; // no target
|
|
} else {
|
|
// ignore stray self-spectating PlayerInfoChanged events
|
|
if ($player->pid != $targetid) {
|
|
// find login for target
|
|
foreach ($aseco->server->players->player_list as $pl) {
|
|
if ($pl->pid == $targetid) {
|
|
$player->speclogin = $pl->login;
|
|
// subscribe to this player
|
|
if (!in_array($login, $checkpoints[$player->speclogin]->speccers))
|
|
$checkpoints[$player->speclogin]->speccers[] = $login;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} // spec_togglecp
|
|
?>
|