#!/usr/bin/php
<?php
/* Race Script
Includes highscores per map, finish timers, map/cfg rotator, and more
Requires a TargetZone as a finish line, winzones will not work
If you are not using Rx Hosting your directory needs write permission
Made by dukevin (dukevinjduke@gmail.com)
*/
//Edit the line below:
$save_directory_location = "/home/duke/aa/YOURSERVERDIR/smart/var/logs/"; //The directory to save files. Must end with trailing slash
$countdown_secs_after_finish = 30; //After someone finishes, how many seconds on the timer before the round ends?
$countdown_if_last_player = true; //If only 1 player remaining, start the timer?
$max_time_limit = -1; //start the timer after this many seconds, -1 to disable time limits
$points = array(10, 7, 5, 4, 3, 2, 1, 1, 1, 1); //points awarded for 1st, 2nd, 3rd, 4th, 5th, etc...
$display_mode = 2; //If set to "1" display a console_message of the map name, 2 for center_message, 3 for both, 0 for no message
$display_prefix = "Current race: 0xffff00"; //When showing the map name, this is appended before it, if $display_mode is 0, this is disabled
$smart_time = true; //If true, the game will determine how many seconds to put on the timer based on previous records instead of using $countdown_secs_after_finish which will be used as the minimum amount of time instead.
$countdown_last_player_extra_time = 0; //If only 1 player remaining, give the last racer this many seconds of extra time to finish. Not needed if $smart_time is true
//Below, inbetween quotes, if you are including maps, put something like: "/path/to/map.aamap.xml". If you are using cfgs, put "mycfg.cfg"
$includes = array("path/to/map.aamap.xml", "mycfg.cfg", "sumo.cfg", "folder/fort.cfg");
$rotate_mode = 3; //Set to 1 for simple random, 2 to play each map in order, 3 to randomly pick a map that hasn't been picked yet.
$num_map_plays = 1; //play each map how many times before switching to next map
$zombie_win = false; //Does the winner get a zombie? (adjust zombie_zone_speed to fit your liking)
$precision = 2; //How many decimal places to show times? A value of 3 shows X.xxx, 2 shows X.xx. 5 is max.
/*===================== End of customization options =============================*/
$reccomended_settings = array("target_initial_score 0", "sp_game_type 0", "game_type 0", "sp_finish_type 0", "finish_type 0", "sp_num_ais 0", "num_ais 0", "shot_seek_update_time 0.1", "ladderlog_write_all 1|1"); //these settings should be set for racing mode and is run every round
class Player
{
var $name;
var $pname;
var $place;
var $finish_time;
var $best_time;
var $num_plays;
function __construct($name)
{
$this->name = $name;
$this->read();
if(empty($this->num_plays)) $this->num_plays = 0;
}
function read()
{
global $file, $precision;
$lines = file($file);
foreach($lines as $line)
{
$pieces = explode(" ", $line); //time name num_plays
if($pieces[1] == $this->name)
{
$this->best_time = round($pieces[0], $precision);
$this->num_plays = $pieces[2];
break;
}
}
}
function write()
{
global $file, $current_inc;
$found = false;
$lines = file($file);
if(!file_exists($file))
{
c("0xff7777Warning: 0xffffffFile '$current_inc.txt' not found, file creation may have failed. Retrying...");
$fp = fopen($file, 'w');
}
foreach($lines as $i => $line)
{
$pieces = explode(" ", $line);
$pieces[1] = trim($pieces[1]);
if($pieces[1] == $this->name)
{
$lines[$i] = "$this->finish_time $this->name $this->num_plays \n";
$found = true;
break;
}
}
if(!$found)
{
$lines[] = "$this->finish_time $this->name $this->num_plays \n";
}
sort(&$lines, SORT_NUMERIC);
file_put_contents($file, $lines);
fclose($fp);
}
function finished($time, $place)
{
global $points, $players, $current_inc, $precision;
$this->num_plays++;
$time = round($time,$precision);
$this->finish_time = $time;
$this->place = $place;
$sufx = suffix($place);
$score = $points[$place-1];
if(empty($score)) $score = 0;
if($place > 1)
{
foreach($players as $p)
{
if($p->place == 1)
{
$first = $p->finish_time;
$difference = " 0xffffff(" . round($this->finish_time - $p->finish_time,$precision) . " s behind 1st)";
break;
}
}
}
else
$difference = "";
c("0x55ccff > 0xffff99$this->pname 0x55ccfffinished $place$sufx in 0xffff99$time 0x55ccffseconds.$difference 0x808080+{$score} pts");
if($this->finish_time >= $this->best_time && !empty($this->best_time)) //took longer than alltime best
{
pm($this->name, " 0xaaaaffYour all-time best on 0xddddff{$current_inc}0xaaaaff is 0xffffff{$this->best_time} 0xaaaaffs, you are 0xffffff". round($this->finish_time - $this->best_time, $precision)." 0xaaaaffs 0xff7777slower0xaaaaff.");
}
if($this->finish_time < $this->best_time || empty($this->best_time))
{
if(!empty($this->best_time)) $oldrank = filetee($this->name, $this->name, 1, false, true) + 1;
$this->write();
$newrank = filetee($this->name, $this->name, 1, false, true) + 1;
if(empty($this->best_time))
pm($this->name, " 0xaaaaffThis is your first time finishing 0xddddff{$current_inc}0xaaaaff!");
else pm($this->name, " 0x3388ff* 0x55ff88New personal record! You improved 0xffffff" . round($this->best_time - $this->finish_time,$precision) . "0x55ff88 s raising 0xffffff". ($oldrank - $newrank) ." 0x55ff88ranks!");
if($newrank == 1) c("0xff3333*0xffff33*0x33ff33*0x3366ff*0xffd700Congratulate 0xffffff$this->name 0xffd700for setting a new record on 0xffddaa$current_inc 0xffd700as the fastest time ever!0xff3333*0xffff33*0x33ff33*0x3366ff*");
else if($newrank == 2) c("0xff3333*0xffff33*0x33ff33*0x3366ff*0xffffff$this->name 0xc0c0c0just ranked as the 0xffffff2nd 0xc0c0c0fastest time for $current_inc!0xff3333*0xffff33*0x33ff33*0x3366ff*\n");
else if($newrank == 3) c("0xff3333*0xffff33*0x33ff33*0x3366ff*0xffffff$this->name 0xcd7f32just ranked as the 0xffcc003rd 0xcd7f32fastest time for $current_inc!0xff3333*0xffff33*0x33ff33*0x3366ff*");
else
pm($this->name, " 0xaaaaaaYou are now rank 0xffffff$newrank 0xaaaaaaon the list of fastest times for 0xcccccc{$current_inc}0xaaaaaa!");
}
echo "add_score_player $this->name " . $score . "\n";
}
}
$players = array();
$finished = 0;
$i=0;
while(true)
{
$line = rtrim(fgets(STDIN, 1024));
if(preg_match("/^CYCLE_CREATED/", $line))
{
$split = explode(" ",$line);
$name = $split[1];
if(getObject($name) !== false)
{
$num_racers++;
goto end;
}
if(strstr($name,"\""))
{
c("0x000000 ! 0xffff00 $name is not allowed to use the characters 0xffffff« or \" 0xffff00 in their name.");
$clean = str_replace("\"","_",$name);
$wo = str_replace("\"","",$name);
echo "rename $wo $clean \n";
echo "rename $name $clean \n";
}
$players[$i] = new Player($split[1]);
if(isset($pretty))
{
for($x = 0; $x < sizeof($pretty); $x++)
{
$crack = explode("*%$", $pretty[$x]);
if($crack[0] == $players[$i]->name)
$players[$i]->pname = trim($crack[1]);
}
}
if(!isset($players[$i]->pname))
$players[$i]->pname = $players[$i]->name;
if($i == 0)
{
$xPos = $split[2];
$yPos = $split[3];
}
if(!empty($players[$i]->best_time)) $rank = filetee($players[$i]->name, $players[$i]->name, 1, false, true) + 1;
$sufx = suffix($rank);
pm($players[$i]->name, "0x808080Top 3 times for $current_inc:");
filetee($players[$i]->name, $players[$i]->name, 3, true);
if(!(strstr($players[$i]->name, '@')))
$remind = "0x808080(Maybe because you're logged out?)";
else
$remind = null;
if(!empty($players[$i]->best_time)) pm($players[$i]->name, "0x99ddffYour best time for $current_inc is 0xffffff{$players[$i]->best_time}0x99ddff s ranking 0xffffff$rank{$sufx}0x99ddff.");
if(empty($players[$i]->best_time)) pm($players[$i]->name, "0x99ddffYou have never won 0xcccccc$current_inc 0x99ddffbefore. $remind");
$i++;
$num_racers = $i;
}
if(preg_match("/^PLAYER_ENTERED/", $line))
{
//PLAYER_ENTERED rx"dukevin 76.174.69.46 Rx«dukevin
$split = explode(" ", $line);
$pretty[] = $split[1] . "*%$" . $split[3] . " " . $split[4] . " " . $split[5];
$pretty = array_slice($pretty,sizeof($pretty)-40,40);
}
if(preg_match("/^PLAYER_RENAMED/", $line))
{
$split = explode(" ", $line);
$pretty[] = $split[2] . "*%$" . $split[5] . " " . $split[6] . " " . $split[7];
}
if(preg_match("/^ROUND_COMMENCING/", $line))
{
foreach($reccomended_settings as $s)
echo "$s \n";
unset($players);
$finished = 0;
$num_racers = 0;
$a_player_finished = false;
$i = 0;
if(empty($num_plays)) $num_plays = 0;
$num_plays++;
if($num_plays >= $num_map_plays)
{
$pick = pickmap($includes, $rotate_mode);
$num_plays = 0;
}
$current_inc = retrieve($pick);
c("Reading $current_inc.txt...");
switch($display_mode)
{
case 3:
c("$display_prefix$current_inc");
case 2:
echo "center_message $display_prefix$current_inc\n";
break;
case 1:
c("$display_prefix$current_inc");
}
$file = "$save_directory_location" . "$current_inc" . ".txt";
if(!file_exists($file))
{
c("0xffff77Warning: 0xffffff$current_inc.txt not found, creating it...");
fopen($file, 'w');
sleep(1);
if(!file_exists($file))
c("0xff7777Warning: 0xffffffFile creation failed, scores cannot be saved! Does your directory have write permission?");
}
if($smart_time)
$countdown_secs_after_finish = smart_time($countdown_secs_after_finish);
echo "target_survive_time " . ($countdown_secs_after_finish+1) ."\n";
if(mt_rand(0,7)==7) c("0x808080You can type /stats to view high scores for maps.");
@clock("stop");
}
if(preg_match("/^TARGETZONE_CONQUERED/", $line) || preg_match("/^ROUND_SCORE/",$line))
{
if($finished == 0 || $a_player_finished == true)
goto end;
@clock("stop");
$split = explode(" ", $line);
if(preg_match("/^TARGETZONE_CONQUERED/", $line))
{
echo "spawn_zone win $split[3] $split[4] 1000000 10000 0 0 false 0 0 0\n";
foreach($players as $p)
filetee($p->name, $p->name, 3);
}
sleep(1);
if(!empty($split[6])) echo "center_message Winner: {$split[6]}\n";
echo "collapse_zone\n";
if(empty($split[6]))
echo "spawn_zone win $xPos $yPos 1000000 10000 0 0 false 0 0 0\n";
$a_player_finished = true;
}
if(preg_match("/^INVALID_COMMAND/", $line))
{
$split = explode(" ", $line);
//INVALID_COMMAND /cast rx"dukevin 76.174.69.46 accesslevel argument 50
if($split[1] == "/stats" || $split[1] == "/s"|| $split[1] == "/search")
{
$obj = getObject($split[2]);
if(empty($split[6]) || !isset($split[6])) $split[6] = 3;
if(empty($split[5]) || !isset($split[5]))
$split[5] = $split[2];
search($obj, $split[5], $split[6]);
}
elseif($split[1] == "/version" || $split[1] == "/v" || $split[1] == "/ver" || $split[1] == "/vers")
{
$obj = getObject($split[2]);
version($obj);
}
elseif($split[1] == "/debug")
{
pm($split[2], "Number of maps rotating: " . count($includes));
pm($split[2], "Number pnames in memory: " . count($pretty));
pm($split[2], "Player Objects:");
foreach($players as $p)
pm($split[2], "$p->name");
}
else
{
pm($split[2], "0xff7777Invalid command, try 0xffffff/stats ");
}
}
if(preg_match("/^TARGETZONE_PLAYER_ENTER/", $line))
{
//TARGETZONE_PLAYER_ENTER 2 zonename 100 100 dukevin 90.8824 103.506 1 0 37.6366
$split = explode(" ", $line);
$name = $split[5];
$time = $split[10];
$obj = getObject($name);
if(!method_exists($obj,'finished'))
{
c("0xffff77Warning: 0xffffffNo object to operate on, perhaps you just loaded scripts?");
echo "admin_kill_message 0\n";
echo "kill $name\n";
echo "admin_kill_message 1\n";
goto end;
}
if($obj->finish_time > 0) //already hit the finish line
goto end;
$finished++;
$obj->finished($time, $finished);
$num_racers--;
if($zombie_win) zombieplz($split[5], $split[6], $split[7]);
@clock("start", $countdown_secs_after_finish);
if($num_racers <= 0)
echo "target_survive_time " . 1 ."\n";
}
if(preg_match("/^GAME_TIME/", $line))
{
@clock("tick");
if($max_time_limit <= 0)
goto end;
else
{
$split = explode(" ", $line);
if($split[1] == $max_time_limit)
clock("start", $countdown_secs_after_finish, "kill");
}
}
if(preg_match("/^DEATH_FRAG|DEATH_SUICIDE|PLAYER_KILLED|DEATH_SHOT_FRAG|DEATH_DEATHZONE|DEATH_SHOT_SUICIDE|DEATH_TEAMKILL|DEATH_SHOT_TEAMKILL|DEATH_ZOMBIEZONE|DEATH_DEATHSHOT|DEATH_SELF_DESTRUCT/", $line))
{
$split = explode(" ", $line);
if(empty(getObject($split[1])->finish_time))
$num_racers--;
if(sizeof($players) <= 1) //if only 1 player, don't start the timer
goto end;
if($countdown_if_last_player == false) //don't start the timer if user doesn't want it
goto end;
if($num_racers <= 1)
{
if(@clock("getState") == "active" && (empty(getObject($split[1])->finish_time))) //only winner alive, last racer died &&racer not finish
{
echo "spawn_zone win $xPos $yPos 1000000 100000 0 0 false 0 0 0\n";
foreach($players as $p)
filetee($p->name, $p->name, 3);
@clock("stop");
goto end;
}
if($num_racers != 0) clock("start", $countdown_secs_after_finish + $countdown_last_player_extra_time, "kill");
}
}
end:
}
function clock($action, $duration, $options)
{
static $state, $time, $options;
if($action == "start")
{
if($state == "active")
return;
$state = "active";
$time = $duration;
echo("center_message 0xff7777$time \n");
}
if($action == "stop" || $state == "stopped")
{
$state = "stopped";
$options = null;
return;
}
if($action == "tick")
{
if($state == "stopped")
return;
$time--;
echo("center_message 0xff7777$time \n");
if(empty($time) || $time < 0)
{
if($options = "kill")
{
global $players;
echo "admin_kill_message 0\n";
foreach($players as $p)
echo "kill $p->name \n";
echo "admin_kill_message 1\n";
}
sleep(1);
foreach($players as $p)
filetee($p->name, $p->name, 3);
$state = "stopped";
return;
}
}
if($action == "getState")
{
return $state;
}
}
function filetee($search, $requester, $amount = 3, $display_top = false, $onlyrank = false)
{
global $file;
$found = false;
$lines = file($file);
foreach($lines as $i => $line)
{
$pieces = explode(" ", $line);
$pieces[1] = trim($pieces[1]);
if($pieces[1] == $search)
{
$found = true;
if($onlyrank) return $i;
break;
}
}
if($display_top) $i = 0;
if($amount > 150) $amount = 150;
if($amount %2 == 0) $amount += 1;
$amount = floor($amount/2);
if($i == 0 || $i == sizeof($lines)-1) //display an extra entry for 1st and last place
$amount += 1;
if($found == false && $display_top == false)
return false;
for($x = $i-$amount; $x <= $i+$amount; $x++)
{
$cut = explode(" ", $lines[$x]);
$color = ($cut[1] == $search) ? '0xffff88' : '0xcccccc';
if(!empty($cut[0])) pm($requester, $color . ($x+1) . ") $cut[0] - $cut[1]");
usleep(20000);
}
}
function suffix($number)
{
$n = (string)$number;
$n = $n[strlen($n)-1];
if($n == 1) $sufx = "st";
elseif($n == 2) $sufx = "nd";
elseif($n == 3) $sufx = "rd";
else $sufx = "th";
if($number == 11 || $number == 12 || $number == 13) //english is weird
$sufx = "th";
return $sufx;
}
function retrieve($mapname)
{
$type = explode(".", $mapname);
if($type[sizeof($type)-1] == "xml") $head = "MAP_FILE";
elseif($type[sizeof($type)-1] == "cfg") $head = "INCLUDE";
$tail = $mapname;
echo $head . " " . $tail . "\n";
$cut = explode("/", $tail);
$split = explode(".", $cut[sizeof($cut)-1]);
foreach($split as $element)
{
if($element == "aamap" || $element == "xml" || $element == "cfg")
unset($element);
$current_inc .= $element;
}
$cut = explode("-", $current_inc);
$current_inc = trim($cut[0]);
if(empty($current_inc)) c("0xff7777Warning: 0xffffffUnable to retrieve current map name.");
return empty($current_inc) ? "null" : $current_inc;
}
function zombieplz($name, $x, $y)
{
echo "spawn_zone zombieOwner $name $name $x $y 1 0 0 0 false\n";
}
function c($string){
echo "console_message $string \n";
}
function pm($name, $string){
echo "player_message {$name} \"{$string}\"\n";
}
function getObject($name)
{
global $players;
foreach($players as $p)
{
if($p->name == $name)
return $p;
}
foreach($players as $p)
{
if($p->pname == $name)
return $p;
}
return false;
}
function pickmap($includes, $rotate_mode)
{
static $i;
if($rotate_mode == 1)
return $includes[mt_rand(0, sizeof($includes)-1)];
if($rotate_mode == 2)
{
if($i > sizeof($includes)-1 || empty($i)) $i = 0;
$x = $i;
$i++;
return $includes[$x];
}
if($rotate_mode == 3)
{
if($i > sizeof($includes)-1 || empty($i)) $i = 0;
if($i == 0) shuffle($includes);
$x = $i;
$i++;
return $includes[$x];
}
}
function smart_time($countdown_secs_after_finish)
{
global $file, $current_inc;
static $initial_countdown_secs;
if(empty($initial_countdown_secs)) $initial_countdown_secs = $countdown_secs_after_finish;
if($initial_countdown_secs < 100)
$MAX_TIME = 75; //smart time will not allow more than 75 secs
else
$MAX_TIME = $initial_countdown_secs * 1.3;
$lines = file($file);
if(!file_exists($file)) return $initial_countdown_secs;
$split = explode(" ", $lines[0]);
$hs = $split[0];
$split = explode(" ", $lines[1]);
$hs2nd = $split[0];
if(empty($hs)) return 60;
if($hs*1.3 < $initial_countdown_secs) return $initial_countdown_secs;
if(empty($hs2nd)) return $hs * 2 > $MAX_TIME ? $MAX_TIME : round($hs*2);
if(($hs2nd - $hs) > 8) $hs = $hs2nd;
return $hs * 1.3 > $MAX_TIME ? $MAX_TIME : round($hs*1.3);
}
function search($me, $search, $amount = 3)
{
global $players;
$count = 0;
foreach($players as $p)
{
if($p->pname == $search || $p->name == $search)
{
$found = $p->name;
$count = 1;
break;
}
if(stripos("{$p->pname}","$search") !== false || stripos("{$p->name}","$search") !== false)
{
$count++;
$found = $p->name;
}
}
if($count > 1)
pm($me->name, "More than one player containing '$search', using $found.");
if(empty($count))
{
if(filetee($search, $me->name, $amount, false, false) == false)
{
global $file;
$lines = file($file);
foreach($lines as $line)
{
$pieces = explode(" ", $line);
if(stripos("$pieces[1]","$search") !== false)
{
$found = $pieces[1];
$count++;
break;
}
}
}
}
if(empty($found) || empty($count) || filetee($found, $me->name, -2, false, false) === false)
{
pm($me->name, "No times found for player: $search");
return;
}
filetee($found, $me->name, $amount, false, false);
}
function version($me)
{
pm($me->name, "0xffff00Script programming by 0xffffffdukevin");
pm($me->name, "0xffff00Email at 0xffffffdukevinjduke@gmail.com0xffff00 for suggestions, comments");
pm($me->name, "0xffff00Made for smart's server");
}
/*
known bugs:
-/stats on rank 1 display twise
-respawn breaks script
-you should login to protect your times not showing
-times showed twise if time run out -> instead make all score things shown before round_Commencing?
foreach($players as $p)
{
if(!(strstr($p->name, '@')))
pm($p->name, "0xffddaaYou should /login to protect your times!");
}
- $me->num_plays doesn't incriment unless you beat your time //because it doesn't write
ranking x out of y
*/
?>