sfcnr/gamemodes/irresistible/cnr/features/race.pwn

1015 lines
39 KiB
Plaintext

/*
* Irresistible Gaming (c) 2018
* Developed by Lorenc Pekaj
* Module: cnr\features\race.pwn
* Purpose: racing system between players
*/
/* ** Includes ** */
#include < YSI\y_hooks >
/* ** Definitions ** */
#define MAX_RACES ( 32 )
#define RACE_STREET_RACE 0
#define RACE_OUTRUN 1
#define OUTRUN_DIST 6.0
/* ** Variables ** */
enum E_RACE_DATA
{
E_LOBBY_HOST, E_MODE, E_ENTRY_FEE,
E_POOL, E_RACE_FINISH_SET, Float: E_FINISH_POS[ 3 ],
E_CD_TIMER, E_FINISH_MAP_ICON, Float: E_POSITION_PRIZE[ 3 ],
E_START_CHECKPOINT, E_FINISH_CHECKPOINT, E_FINISHED_COUNT,
bool: E_STARTED, E_OUTRUN_SPHERE, E_OUTRUN_OBJECT,
E_OUTRUN_LEAD, E_OUTRUN_TIMER, Float: E_OUTRUN_DISTANCE
};
enum E_RACE_DEST_DATA
{
E_NAME[ 16 ],
Float: E_X, Float: E_Y, Float: E_Z
};
static stock
g_raceFinalDestinations [ ] [ E_RACE_DEST_DATA ] =
{
{ "LS Airport", 1487.0245, -2493.751, 13.2720 },
{ "LS Pier", 369.61540, -2011.367, 7.39200 },
{ "LS Grove Street", 2487.9739, -1666.938, 13.0633 },
{ "LV Airport", 1477.6246, 1207.3376, 10.8203 },
{ "LV Old Strip", 2350.5371, 2143.6689, 10.6815 },
{ "El Quebrados", -885.4323, 1660.3818, 27.0871 },
{ "SF Airport", -1117.212, 375.26310, 14.1484 },
{ "Mount Chiliad", -2324.256, -1624.915, 483.883 },
{ "SF Gant Bridge", -2681.314, 1763.9274, 68.4844 },
{ "SF Dealership ", -2422.670, -609.4055, 132.562 }
},
g_raceData [ MAX_RACES ] [ E_RACE_DATA ],
Iterator: races < MAX_RACES >,
p_raceLobbyId [ MAX_PLAYERS ] = { -1, ... },
p_raceInvited [ MAX_PLAYERS ] [ MAX_RACES ]
;
/* ** Hooks ** */
hook OnPlayerDisconnect( playerid, reason )
{
for ( new i = 0; i < MAX_RACES; i ++ ) {
p_raceInvited[ playerid ] [ i ] = false;
}
return 1;
}
hook OnPlayerEnterDynArea( playerid, areaid )
{
new
raceid = p_raceLobbyId[ playerid ];
if ( Iter_Contains( races, raceid ) && g_raceData[ raceid ] [ E_OUTRUN_SPHERE ] == areaid && g_raceData[ raceid ] [ E_OUTRUN_LEAD ] != playerid )
{
new
vehicleid = GetPlayerVehicleID( playerid );
// new leader
g_raceData[ raceid ] [ E_OUTRUN_LEAD ] = playerid;
// alert
foreach (new i : Player) if ( p_raceLobbyId[ i ] == raceid ) {
PlayerPlaySound( i, 1149, 0.0, 0.0, 0.0 );
GameTextForPlayer( i, sprintf( "~b~~h~%s leading", ReturnPlayerName( playerid ) ), 2000, 3 );
SendClientMessageFormatted( i, -1, ""COL_GREY"[RACE]"COL_WHITE" %s(%d) has taken the lead for the race.", ReturnPlayerName( playerid ), playerid );
}
// see if ahead
AttachDynamicObjectToVehicle( g_raceData[ raceid ] [ E_OUTRUN_OBJECT ], vehicleid, 0.0, OUTRUN_DIST, -15.0, 0.0, 0.0, 0.0 );
AttachDynamicAreaToVehicle( g_raceData[ raceid ] [ E_OUTRUN_SPHERE ], vehicleid, 0.0, OUTRUN_DIST * 2 );
}
return 1;
}
hook OnPlayerStateChange( playerid, newstate, oldstate )
{
if ( newstate != PLAYER_STATE_DRIVER && oldstate == PLAYER_STATE_DRIVER )
{
new
iRace = p_raceLobbyId[ playerid ];
if ( Iter_Contains( races, iRace ) )
{
SendClientMessageToRace( iRace, COLOR_GREY, "[RACE]"COL_WHITE" %s(%d) has exited their vehicle and left the race.", ReturnPlayerName( playerid ), playerid );
RemovePlayerFromRace( playerid );
}
}
return 1;
}
hook OnPlayerEnterDynRaceCP( playerid, checkpointid )
{
new
raceid = p_raceLobbyId[ playerid ];
if ( raceid != -1 && g_raceData[ raceid ] [ E_FINISH_CHECKPOINT ] == checkpointid )
{
if ( ! g_raceData[ raceid ] [ E_STARTED ] )
return SendError( playerid, "The race has not started." );
new
position = ++ g_raceData[ raceid ] [ E_FINISHED_COUNT ];
// give prize and alert
if ( 1 <= position <= 3 )
GivePlayerRaceWin( playerid, position, raceid );
// close race after members finished
new
members = GetRaceMemberCount( raceid );
// printf ("Position : %d, Members : %d", position, members);
if ( position >= 3 || position >= members ) {
DestroyRace( raceid );
// print ("Shut race");
}
return 1;
}
// printf("Entered Race Checkpoint : {user:%s,veh:%d,biz_veh:%d,valid_biz:%d}", ReturnPlayerName( playerid ), iVehicle, g_isBusinessVehicle[ iVehicle ],Iter_Contains( business, g_isBusinessVehicle[ iVehicle ] ));
return 1;
}
hook OnDialogResponse( playerid, dialogid, response, listitem, inputtext[ ] )
{
if ( dialogid == DIALOG_RACE && response )
{
new
raceid = GetPVarInt( playerid, "editing_race" );
if ( ! IsRaceHost( playerid, raceid ) )
return SendError( playerid, "You are not the host of this lobby." );
switch ( listitem )
{
case 0: ShowPlayerDialog( playerid, DIALOG_RACE_MODE, DIALOG_STYLE_TABLIST, ""COL_GOLD"Race Configuration - Race Mode", ""COL_GREY"Street Race\t"COL_WHITE"Racers must meet the final destination\n"COL_GREY"Outrun"COL_WHITE"\tRacer must outrun everyone by 100 metres", "Select", "Close" );
case 1: ShowPlayerDialog( playerid, DIALOG_RACE_FEE, DIALOG_STYLE_INPUT, ""COL_GOLD"Race Configuration - Entry Fee", ""COL_WHITE"Specify the required entry fee for this race (minimum $1,000 - max $10,000,000)", "Specify", "Close" );
case 2: ShowPlayerDialog( playerid, DIALOG_RACE_POS, DIALOG_STYLE_TABLIST_HEADERS, ""COL_GOLD"Race Configuration - Prize Distribution", ""COL_WHITE"1st Position\t"COL_WHITE"2nd Position\t"COL_WHITE"3rd Position\n100%\t0%\t0%\n90%\t5%\t5%\n80%\t10%\t10%\n70%\t15%\t15%\n60%\t20%\t20%\n", "Select", "Close" );
case 3:
{
if ( g_raceData[ raceid ] [ E_MODE ] == RACE_OUTRUN )
return ShowPlayerDialog( playerid, DIALOG_RACE_DISTANCE, DIALOG_STYLE_INPUT, ""COL_GOLD"Race Configuration - Outrun Distance", ""COL_WHITE"Specify the required outrun distance (minimum 10.0m - max 250.0m)", "Specify", "Close" );
return ShowPlayerDialog( playerid, DIALOG_RACE_DEST, DIALOG_STYLE_TABLIST_HEADERS, ""COL_GOLD"Race Configuration - Destination", ""COL_WHITE""COL_WHITE"How do you want to set the final destination?\nPreset Destinations\nSelect Using Minimap\nUse Coordinates", "Select", "Back" );
}
case 4:
{
erase( szLargeString );
foreach (new i : Player) if ( p_raceLobbyId[ i ] == raceid && g_raceData[ raceid ] [ E_LOBBY_HOST ] != i ) {
format( szLargeString, sizeof( szLargeString ), "%s%s(%d)\n", szLargeString, ReturnPlayerName( i ), i );
}
if ( strlen( szLargeString ) ) {
return ShowPlayerDialog( playerid, DIALOG_RACE_KICK, DIALOG_STYLE_LIST, ""COL_GOLD"Race Configuration - Player Management", szLargeString, "Kick", "Back" );
} else {
SendError( playerid, "There are no racers to show." );
return ShowRaceConfiguration( playerid, raceid );
}
}
}
}
else if ( dialogid == DIALOG_RACE_DEST )
{
new
raceid = GetPVarInt( playerid, "editing_race" );
if ( ! IsRaceHost( playerid, raceid ) )
return SendError( playerid, "You are not the host of this lobby." );
if ( ! response )
return ShowRaceConfiguration( playerid, raceid );
switch ( listitem )
{
case 0: ShowPlayerDialog( playerid, DIALOG_RACE_PRESELECT, DIALOG_STYLE_LIST, ""COL_GOLD"Race Configuration - Destination", "LS Airport\nLS Pier\nLS Grove Street\nLV Airport\nLV Old Strip\nEl Quebrados\nSF Airport\nMount Chilliad\nTierra Robada SF Bridge\nSF Dealership", "Select", "Back" );
case 1:
{
g_raceData[ raceid ] [ E_RACE_FINISH_SET ] = 2;
g_raceData[ raceid ] [ E_FINISH_POS ] [ 0 ] = 0.0;
g_raceData[ raceid ] [ E_FINISH_POS ] [ 1 ] = 0.0;
g_raceData[ raceid ] [ E_FINISH_POS ] [ 2 ] = 0.0;
return SendServerMessage( playerid, "You are now setting the race destination. Use the MINIMAP to pick the finish position." );
}
case 2: ShowPlayerDialog( playerid, DIALOG_RACE_CUSTOM_DEST, DIALOG_STYLE_INPUT, ""COL_GOLD"Race Configuration - Custom Destination", ""COL_WHITE"Please specify the final destination coordinates, seperated by white spaces. E.g 0.0 0.0 0.0", "Set", "Back" );
}
return 1;
}
else if ( dialogid == DIALOG_RACE_CUSTOM_DEST )
{
new
raceid = GetPVarInt( playerid, "editing_race" );
if ( ! IsRaceHost( playerid, raceid ) )
return SendError( playerid, "You are not the host of this lobby." );
if ( ! response )
return ShowPlayerDialog( playerid, DIALOG_RACE_DEST, DIALOG_STYLE_TABLIST_HEADERS, ""COL_GOLD"Race Configuration - Destination", ""COL_WHITE""COL_WHITE"How do you want to set the final destination?\nPreset Destinations\nSelect Using Minimap\nUse Coordinates", "Select", "Back" );
new
Float: X, Float: Y, Float: Z;
if ( sscanf( inputtext, "fff", X, Y, Z ) ) {
SendError( playerid, "Make sure coordinates are seperated by white spaces and are numbers (e.g 0.0 0.0 0.0)" );
return ShowPlayerDialog( playerid, DIALOG_RACE_CUSTOM_DEST, DIALOG_STYLE_INPUT, ""COL_GOLD"Race Configuration - Custom Destination", ""COL_WHITE"Please specify the final destination coordinates, seperated by white spaces. E.g 0.0 0.0 0.0", "Set", "Back" );
}
SetRaceDestination( raceid, X, Y, Z );
SendClientMessageToRace( raceid, COLOR_GREY, "[RACE]"COL_WHITE" %s(%d) has set the race final destination to %0.4f %0.4f %0.4f.", ReturnPlayerName( playerid ), playerid, X, Y, Z );
return 1;
}
else if ( dialogid == DIALOG_RACE_PRESELECT )
{
new
raceid = GetPVarInt( playerid, "editing_race" );
if ( ! IsRaceHost( playerid, raceid ) )
return SendError( playerid, "You are not the host of this lobby." );
if ( ! response )
return ShowPlayerDialog( playerid, DIALOG_RACE_DEST, DIALOG_STYLE_TABLIST_HEADERS, ""COL_GOLD"Race Configuration - Destination", ""COL_WHITE""COL_WHITE"How do you want to set the final destination?\nPreset Destinations\nSelect Using Minimap\nUse Coordinates", "Select", "Back" );
SetRaceDestination( raceid, g_raceFinalDestinations[ listitem ] [ E_X ], g_raceFinalDestinations[ listitem ] [ E_Y ], g_raceFinalDestinations[ listitem ] [ E_Z ] );
SendClientMessageToRace( raceid, COLOR_GREY, "[RACE]"COL_WHITE" %s(%d) has set the race final destination to %s.", ReturnPlayerName( playerid ), playerid, g_raceFinalDestinations[ listitem ] [ E_NAME ] );
return 1;
}
else if ( dialogid == DIALOG_RACE_KICK )
{
new
raceid = GetPVarInt( playerid, "editing_race" );
if ( ! IsRaceHost( playerid, raceid ) )
return SendError( playerid, "You are not the host of this lobby." );
ShowRaceConfiguration( playerid, raceid );
if ( ! response )
return 1;
new
x = 0;
foreach (new i : Player) if ( p_raceLobbyId[ i ] == raceid && g_raceData[ raceid ] [ E_LOBBY_HOST ] != i )
{
if ( x == listitem )
{
SendClientMessageToRace( raceid, -1, ""COL_GREY"[RACE]"COL_WHITE" %s(%d) has been kicked from the race.", ReturnPlayerName( i ), i );
RemovePlayerFromRace( i );
return 1;
}
x++;
}
return SendError( playerid, "There was an error trying to remove this player from the race." );
}
else if ( dialogid == DIALOG_RACE_MODE )
{
new
raceid = GetPVarInt( playerid, "editing_race" );
if ( ! IsRaceHost( playerid, raceid ) )
return SendError( playerid, "You are not the host of this lobby." );
if ( response )
{
g_raceData[ raceid ] [ E_MODE ] = listitem;
SendServerMessage( playerid, "You have set the race mode to "COL_GREY"%s"COL_WHITE".", g_raceData[ raceid ] [ E_MODE ] == RACE_STREET_RACE ? ( "Streetrace" ) : ( "Outrun" ) );
}
return ShowRaceConfiguration( playerid, raceid );
}
else if ( dialogid == DIALOG_RACE_DISTANCE )
{
new
raceid = GetPVarInt( playerid, "editing_race" );
if ( ! IsRaceHost( playerid, raceid ) )
return SendError( playerid, "You are not the host of this lobby." );
if ( ! response )
return ShowRaceConfiguration( playerid, raceid );
new
Float: distance;
if ( sscanf( inputtext, "f", distance ) || ! ( 10.0 <= distance <= 300.0 ) ) {
SendError( playerid, "Please specify a race distance between 10.0m and 300.0m." );
return ShowPlayerDialog( playerid, DIALOG_RACE_DISTANCE, DIALOG_STYLE_INPUT, ""COL_GOLD"Race Configuration - Outrun Distance", ""COL_WHITE"Specify the required outrun distance (minimum 10.0m - max 250.0m)", "Specify", "Close" );
}
g_raceData[ raceid ] [ E_OUTRUN_DISTANCE ] = distance;
SendClientMessageToRace( raceid, -1, ""COL_GREY"[RACE]"COL_WHITE" The outrun distance has been set to %0.1f metres.", distance );
return ShowRaceConfiguration( playerid, raceid );
}
else if ( dialogid == DIALOG_RACE_FEE )
{
new
raceid = GetPVarInt( playerid, "editing_race" );
if ( ! IsRaceHost( playerid, raceid ) )
return SendError( playerid, "You are not the host of this lobby." );
if ( ! response )
return ShowRaceConfiguration( playerid, raceid );
new
fee;
if ( sscanf( inputtext, "d", fee ) || ! ( 0 <= fee <= 10000000 ) ) {
SendError( playerid, "Please specify an entry fee between $0 and $10,000,000." );
return ShowPlayerDialog( playerid, DIALOG_RACE_FEE, DIALOG_STYLE_INPUT, ""COL_GOLD"Race Configuration - Entry Fee", ""COL_WHITE"Specify the required entry fee for this race (minimum $1,000 - max $10,000,000)", "Specify", "Close" );
}
g_raceData[ raceid ] [ E_ENTRY_FEE ] = fee;
SendClientMessageToRace( raceid, COLOR_GREY, "[RACE]"COL_WHITE" The entry fee for the race has been set to %s.", cash_format( fee ) );
return ShowRaceConfiguration( playerid, raceid );
}
else if ( dialogid == DIALOG_RACE_POS )
{
new
raceid = GetPVarInt( playerid, "editing_race" );
if ( ! IsRaceHost( playerid, raceid ) )
return SendError( playerid, "You are not the host of this lobby." );
if ( response )
{
static const
Float: g_prizePoolDistribution[ ] [ 3 ] =
{
{ 1.0, 0.0, 0.0 },
{ 0.9, 0.05, 0.05 },
{ 0.8, 0.1, 0.1 },
{ 0.7, 0.15, 0.15 },
{ 0.6, 0.2, 0.2 }
}
;
// position prize
g_raceData[ raceid ] [ E_POSITION_PRIZE ] [ 0 ] = g_prizePoolDistribution[ listitem ] [ 0 ];
g_raceData[ raceid ] [ E_POSITION_PRIZE ] [ 1 ] = g_prizePoolDistribution[ listitem ] [ 1 ];
g_raceData[ raceid ] [ E_POSITION_PRIZE ] [ 2 ] = g_prizePoolDistribution[ listitem ] [ 2 ];
// alert
SendServerMessage( playerid, "The prize pool distribution is now %0.0f%% for 1st, %0.0f%% for 2nd and %0.0f%% for 3rd place.", 100.0 * g_prizePoolDistribution[ listitem ] [ 0 ], 100.0 * g_prizePoolDistribution[ listitem ] [ 1 ], 100.0 * g_prizePoolDistribution[ listitem ] [ 2 ] );
}
return ShowRaceConfiguration( playerid, raceid );
}
return 1;
}
hook OnPlayerClickMap( playerid, Float: fX, Float: fY, Float: fZ )
{
new
raceid = GetPVarInt( playerid, "editing_race" );
if ( IsRaceHost( playerid, raceid ) )
{
if ( g_raceData[ raceid ] [ E_RACE_FINISH_SET ] == 2 )
{
new
Float: nodeX, Float: nodeY, Float: nodeZ,
nodeid = NearestNodeFromPoint( fX, fY, fZ )
;
GetNodePos( nodeid, nodeX, nodeY, nodeZ );
// set destination
SetRaceDestination( raceid, nodeX, nodeY, nodeZ + 1.0 );
// alert
ShowRaceConfiguration( playerid, raceid );
SendServerMessage( playerid, "You have selected the final destination for the race, use "COL_GREY"/race start"COL_WHITE" to begin." );
}
}
return 1;
}
/* ** Commands ** */
CMD:race( playerid, params[ ] )
{
if ( ! IsPlayerInAnyVehicle( playerid ) )
return SendError( playerid, "You must be in a vehicle to use this command" );
if ( !strcmp( params, "create", false, 6 ) )
{
new
prizePool;
if ( sscanf( params[ 7 ], "d", prizePool ) )
return SendUsage( playerid, "/race create [INITIAL PRIZE POOL]" );
if ( prizePool < 1000 )
return SendError( playerid, "The minimum initial prize pool must be $1,000." );
if ( prizePool > GetPlayerCash( playerid ) )
return SendError( playerid, "You don't have this amount of money." );
if ( p_raceLobbyId[ playerid ] != -1 )
return SendError( playerid, "You are currently in a race lobby, use "COL_GREY"/race leave"COL_WHITE" to exit." );
new
id = Iter_Free(races);
if ( id != ITER_NONE )
{
// clear race
DestroyDynamicMapIcon( g_raceData[ id ] [ E_FINISH_MAP_ICON ] );
DestroyDynamicRaceCP( g_raceData[ id ] [ E_FINISH_CHECKPOINT ] );
// default race lobby data
g_raceData[ id ] [ E_LOBBY_HOST ] = playerid;
g_raceData[ id ] [ E_MODE ] = RACE_STREET_RACE;
g_raceData[ id ] [ E_ENTRY_FEE ] = 1000;
g_raceData[ id ] [ E_POOL ] = prizePool;
g_raceData[ id ] [ E_RACE_FINISH_SET ] = 0;
g_raceData[ id ] [ E_STARTED ] = false;
g_raceData[ id ] [ E_OUTRUN_DISTANCE ] = 250.0;
g_raceData[ id ] [ E_FINISHED_COUNT ] = 0;
g_raceData[ id ] [ E_POSITION_PRIZE ] [ 0 ] = 1.0, g_raceData[ id ] [ E_POSITION_PRIZE ] [ 1 ] = 0.0, g_raceData[ id ] [ E_POSITION_PRIZE ] [ 2 ] = 0.0;
g_raceData[ id ] [ E_FINISH_POS ] [ 0 ] = 1.0, g_raceData[ id ] [ E_FINISH_POS ] [ 1 ] = 0.0, g_raceData[ id ] [ E_FINISH_POS ] [ 2 ] = 0.0;
// reset user cash
p_raceLobbyId[ playerid ] = id;
GivePlayerCash( playerid, -prizePool );
// config
ShowRaceConfiguration( playerid, id );
// iter
Iter_Add( races, id );
}
else return SendError( playerid, "Unable to create a race as there are too many currently on-going." );
}
else if ( !strcmp( params, "invite", false, 6 ) )
{
new
raceid = p_raceLobbyId[ playerid ];
if ( ! Iter_Contains( races, raceid ) )
return SendError( playerid, "You are not in any race." );
new
inviteid;
if ( sscanf( params[ 7 ], "u", inviteid ) )
return SendUsage( playerid, "/race invite [PLAYER]" );
if ( ! IsPlayerConnected( inviteid ) || IsPlayerNPC( inviteid ) )
return SendServerMessage( playerid, "This player is not connected" );
if ( GetDistanceBetweenPlayers( inviteid, playerid ) > 50.0 )
return SendError( playerid, "This player must be within 50 meters to you." );
if ( p_raceLobbyId[ inviteid ] != -1 )
return SendError( playerid, "This player is currently already in a race lobby." );
if( g_raceData[ raceid ] [ E_STARTED ] )
return SendError( playerid, "You cannot invite players once you start the race." );
p_raceInvited[ inviteid ] [ raceid ] = true;
SendClientMessageFormatted( inviteid, COLOR_GREY, "[RACE]{FFFFFF} %s(%d) has invited you to their race for %s, to join type \"/race join %d\"", ReturnPlayerName( playerid ), playerid, g_raceData[ raceid ] [ E_ENTRY_FEE ] <= 0 ? ( "free" ) : ( cash_format( g_raceData[ raceid ] [ E_ENTRY_FEE ] ) ), raceid );
SendClientMessageFormatted( playerid, COLOR_GREY, "[RACE]{FFFFFF} You have invited %s(%d) to join your race.", ReturnPlayerName( inviteid ), inviteid );
return 1;
}
else if ( !strcmp( params, "join", false, 4 ) )
{
new
raceid;
if ( sscanf( params[ 5 ], "d", raceid ) ) return SendUsage( playerid, "/race join [RACE_ID]" );
else if ( ! Iter_Contains( races, raceid ) ) return SendError( playerid, "This race lobby does not exist." );
else if ( ! p_raceInvited[ playerid ] [ raceid ] ) return SendError( playerid, "You have not been invited to this race lobby." );
else if( GetDistanceBetweenPlayers( playerid, g_raceData[ raceid ] [ E_LOBBY_HOST ] ) > 50.0 ) return SendError( playerid, "This player must be within 50 meters to you." );
else if ( g_raceData[ raceid ] [ E_STARTED ] ) return SendError( playerid, "The race has already started." );
else if ( g_raceData[ raceid ] [ E_ENTRY_FEE ] > GetPlayerCash( playerid ) ) return SendError( playerid, "You need at least %s to join the race.", cash_format( g_raceData[ raceid ] [ E_ENTRY_FEE ] - GetPlayerCash( playerid ) ) );
else
{
// enter race lobby
p_raceLobbyId[ playerid ] = raceid;
p_raceInvited[ playerid ] [ raceid ] = false;
// alert race players
SendClientMessageToRace( raceid, COLOR_GREY, "[RACE]{FFFFFF} %s(%d) has joined the race.", ReturnPlayerName( playerid ), playerid );
// show checkpoint
if ( g_raceData[ raceid ] [ E_MODE ] == RACE_STREET_RACE && g_raceData[ raceid ] [ E_RACE_FINISH_SET ] == 1 )
{
Streamer_AppendArrayData( STREAMER_TYPE_MAP_ICON, g_raceData[ raceid ] [ E_FINISH_MAP_ICON ], E_STREAMER_PLAYER_ID, playerid );
Streamer_AppendArrayData( STREAMER_TYPE_RACE_CP, g_raceData[ raceid ] [ E_FINISH_CHECKPOINT ], E_STREAMER_PLAYER_ID, playerid );
}
// remove entry fee
GivePlayerCash( playerid, -g_raceData[ raceid ] [ E_ENTRY_FEE ] );
g_raceData[ raceid ] [ E_POOL ] += g_raceData[ raceid ] [ E_ENTRY_FEE ];
}
return 1;
}
else if ( strmatch( params, "config" ) )
{
new
raceid = p_raceLobbyId[ playerid ];
if ( ! IsRaceHost( playerid, raceid ) )
return SendError( playerid, "You must be a race lobby host in order to use this command." );
return ShowRaceConfiguration( playerid, raceid );
}
else if ( ! strcmp( params, "start", false, 5 ) )
{
new
raceid = p_raceLobbyId[ playerid ],
vehicleid = GetPlayerVehicleID( playerid ),
Float: X, Float: Y, Float: Z, Float: A,
countdown, Float: cpsize
;
if ( ! IsRaceHost( playerid, raceid ) )
return SendError( playerid, "You must be a race lobby host in order to use this command." );
new
racers = GetRaceMemberCount( raceid );
if ( racers < 2 )
return SendError( playerid, "You need at least 2 racers to start the race." );
if ( g_raceData[ raceid ] [ E_RACE_FINISH_SET ] != 1 && g_raceData[ raceid ] [ E_MODE ] == RACE_STREET_RACE )
return SendError( playerid, "You must set a finishing location for the race." );
if ( g_raceData[ raceid ] [ E_STARTED ] )
return SendError( playerid, "The race has already started." );
if ( g_raceData[ raceid ] [ E_POOL ] < 1000 )
return SendError( playerid, "The race must have a prize pool of at least $1,000." );
if ( sscanf( params[ 6 ], "D(15)F(15)", countdown, cpsize ) )
return SendUsage( playerid, "/race start [COUNT_DOWN_TIME (15)] [CHECKPOINT_SIZE (15.0)]" );
if ( ! ( 3 <= countdown <= 60 ) )
return SendError( playerid, "Countdown must be between 3 and 60 seconds." );
if ( !( 3.0 <= cpsize <= 40.0 ) )
return SendError( playerid, "The checkpoint size must be between 3.0 and 40.0" );
GetVehiclePos( vehicleid, X, Y, Z );
GetVehicleZAngle( vehicleid, A );
// destroy checkpoint/icon again
DestroyDynamicRaceCP( g_raceData[ raceid ] [ E_START_CHECKPOINT ] );
// place checkpoint
g_raceData[ raceid ] [ E_START_CHECKPOINT ] = CreateDynamicRaceCP( 0, X, Y, Z, X + 20.0 * floatsin( -A, degrees ), Y + 20.0 * floatcos( -A, degrees ), Z, cpsize, -1, -1, 0 );
// trigger started
g_raceData[ raceid ] [ E_STARTED ] = true;
// reset players in map icon/cp
Streamer_RemoveArrayData( STREAMER_TYPE_RACE_CP, g_raceData[ raceid ] [ E_START_CHECKPOINT ], E_STREAMER_PLAYER_ID, 0 );
// stream to players
foreach (new i : Player) if ( p_raceLobbyId[ i ] == raceid ) {
Streamer_AppendArrayData( STREAMER_TYPE_RACE_CP, g_raceData[ raceid ] [ E_START_CHECKPOINT ], E_STREAMER_PLAYER_ID, i );
Streamer_Update( i );
}
// see if racers is 2
if ( racers == 2 && g_raceData[ raceid ] [ E_POSITION_PRIZE ] [ 0 ] != 1.0 )
{
new
Float: finalIncrease = g_raceData[ raceid ] [ E_POSITION_PRIZE ] [ 2 ] / 2.0;
// reset profit ratio
g_raceData[ raceid ] [ E_POSITION_PRIZE ] [ 0 ] += finalIncrease;
g_raceData[ raceid ] [ E_POSITION_PRIZE ] [ 1 ] += finalIncrease;
g_raceData[ raceid ] [ E_POSITION_PRIZE ] [ 2 ] = 0.0;
// alert
SendClientMessageToRace( raceid, COLOR_GREY, "[RACE]"COL_WHITE" As this is a two player race, the prize ratio is now %0.1f-%0.1f.", g_raceData[ raceid ] [ E_POSITION_PRIZE ] [ 0 ] * 100.0, g_raceData[ raceid ] [ E_POSITION_PRIZE ] [ 1 ] * 100.0 );
}
// tax races
g_raceData[ raceid ] [ E_POOL ] = floatround( float( g_raceData[ raceid ] [ E_POOL ] ) * 0.95 );
// restart timer
KillTimer( g_raceData[ raceid ] [ E_CD_TIMER ] );
g_raceData[ raceid ] [ E_CD_TIMER ] = SetTimerEx( "OnRaceCountdown", 960, false, "dd", raceid, countdown );
return 1;
}
else if ( strmatch( params, "stop" ) )
{
new
raceid = p_raceLobbyId[ playerid ];
if ( ! IsRaceHost( playerid, raceid ) )
return SendError( playerid, "You must be a race lobby host in order to use this command." );
if ( ! g_raceData[ raceid ] [ E_STARTED ] )
return SendError( playerid, "The race must be started." );
SendClientMessageToRace( raceid, -1, ""COL_GREY"[RACE]"COL_WHITE" %s(%d) has ended the race.", ReturnPlayerName( playerid ), playerid );
return DestroyRace( raceid );
}
else if ( strmatch( params, "leave" ) )
{
new
raceid = p_raceLobbyId[ playerid ];
if ( ! Iter_Contains( races, raceid ) )
return SendError( playerid, "You are not in any race." );
SendClientMessageToRace( raceid, -1, ""COL_GREY"[RACE]"COL_WHITE" %s(%d) has left the race.", ReturnPlayerName( playerid ), playerid );
return RemovePlayerFromRace( playerid );
}
else if ( ! strcmp( params, "kick", false, 4 ) )
{
new
raceid = p_raceLobbyId[ playerid ], kickid;
if ( ! IsRaceHost( playerid, raceid ) ) return SendError( playerid, "You are not a lobby host for any race." );
else if ( sscanf( params[ 5 ], "u", kickid ) ) return SendUsage( playerid, "/race kick [PLAYER]" );
else if ( ! IsPlayerConnected( kickid ) || IsPlayerNPC( kickid ) ) return SendError( playerid, "This player is not connected." );
else if ( p_raceLobbyId[ kickid ] != raceid ) return SendError( playerid, "This player is not in your race." );
else
{
SendClientMessageToRace( raceid, -1, ""COL_GREY"[RACE]"COL_WHITE" %s(%d) has been kicked from the race.", ReturnPlayerName( kickid ), kickid );
RemovePlayerFromRace( kickid );
}
return 1;
}
else if ( ! strcmp( params, "contribute", false, 10 ) )
{
new
raceid = p_raceLobbyId[ playerid ], amount;
if ( sscanf( params[ 11 ], "d", amount ) ) return SendUsage( playerid, "/race donate [AMOUNT]" );
else if ( ! Iter_Contains( races, raceid ) ) return SendError( playerid, "You are not in any race." );
else if ( amount < 100 ) return SendError( playerid, "The minimum contribution amount is $100." );
else if ( amount > GetPlayerCash( playerid ) ) return SendError( playerid, "You don't have enough money to contribute that amount." );
else
{
GivePlayerCash( playerid, -amount );
g_raceData[ raceid ] [ E_POOL ] += amount;
SendClientMessageToRace( raceid, COLOR_GREY, "[RACE]"COL_WHITE" %s(%d) has contributed %s to the prize pool (total %s).", ReturnPlayerName( playerid ), playerid, cash_format( amount ), cash_format( g_raceData[ raceid ] [ E_POOL ] ) );
return 1;
}
}
return SendUsage( playerid, "/race [CREATE/INVITE/JOIN/LEAVE/KICK/CONFIG/START/CONTRIBUTE/STOP]" );
}
/* ** Functions ** */
stock SendClientMessageToRace( raceid, colour, format[ ], va_args<> )
{
static
out[ 144 ];
va_format( out, sizeof( out ), format, va_start<3> );
foreach(new i : Player)
{
if ( p_raceLobbyId[ i ] == raceid )
SendClientMessage( i, colour, out );
}
return 1;
}
function OnRaceCountdown( raceid, time )
{
if ( raceid == -1 || ! Iter_Contains( races, raceid ) )
return;
foreach (new playerid : Player) if ( p_raceLobbyId[ playerid ] == raceid ) {
if ( ! IsPlayerInDynamicRaceCP( playerid, g_raceData[ raceid ] [ E_START_CHECKPOINT ] ) ) {
SendClientMessageToRace( raceid, -1, ""COL_GREY"[RACE]"COL_WHITE" The race cannot be started as %s(%d) is not in the starting checkpoint.", ReturnPlayerName( playerid ), playerid );
g_raceData[ raceid ] [ E_CD_TIMER ] = SetTimerEx( "OnRaceCountdown", 960, false, "dd", raceid, time );
return;
}
}
if ( time <= 0 )
{
// destroy starting checkpoint
DestroyDynamicRaceCP( g_raceData[ raceid ] [ E_START_CHECKPOINT ] );
g_raceData[ raceid ] [ E_CD_TIMER ] = -1;
if ( g_raceData[ raceid ] [ E_MODE ] == RACE_OUTRUN )
{
new hostid = g_raceData[ raceid ] [ E_LOBBY_HOST ];
new vehicleid = GetPlayerVehicleID( hostid );
// create sphere obj
g_raceData[ raceid ] [ E_OUTRUN_LEAD ] = hostid;
g_raceData[ raceid ] [ E_OUTRUN_SPHERE ] = CreateDynamicCircle( 0.0, 0.0, 10.0 );
g_raceData[ raceid ] [ E_OUTRUN_OBJECT ] = CreateDynamicObject( 11752, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1, -1, .playerid = 0 );
g_raceData[ raceid ] [ E_OUTRUN_TIMER ] = SetTimerEx( "OnRaceOutrun", 250, true, "d", raceid );
// attach objects
Streamer_RemoveArrayData( STREAMER_TYPE_OBJECT, g_raceData[ raceid ] [ E_OUTRUN_OBJECT ], E_STREAMER_PLAYER_ID, 0 );
AttachDynamicObjectToVehicle( g_raceData[ raceid ] [ E_OUTRUN_OBJECT ], vehicleid, 0.0, OUTRUN_DIST, -15.0, 0.0, 0.0, 0.0 );
AttachDynamicAreaToVehicle( g_raceData[ raceid ] [ E_OUTRUN_SPHERE ], vehicleid, 0.0, OUTRUN_DIST * 2 );
foreach ( new playerid : Player ) if ( p_raceLobbyId[ playerid ] == raceid )
{
// show checkpoint for player
Streamer_AppendArrayData( STREAMER_TYPE_OBJECT, g_raceData[ raceid ] [ E_OUTRUN_OBJECT ], E_STREAMER_PLAYER_ID, playerid );
Streamer_Update( playerid );
// show gametext
GameTextForPlayer( playerid, "~g~GO!", 2000, 3 );
PlayerPlaySound( playerid, 1057, 0.0, 0.0, 0.0 );
}
}
}
else
{
foreach (new playerid : Player) if ( p_raceLobbyId[ playerid ] == raceid )
{
GameTextForPlayer( playerid, sprintf( "~y~%d", time ), 2000, 3 );
PlayerPlaySound( playerid, 1056, 0.0, 0.0, 0.0 );
}
g_raceData[ raceid ] [ E_CD_TIMER ] = SetTimerEx( "OnRaceCountdown", 960, false, "dd", raceid, time - 1 );
}
}
stock ShowRaceConfiguration( playerid, raceid )
{
format( szLargeString, sizeof( szLargeString ), ""COL_WHITE"The current prize pool is %s\t \n"COL_GREY"Race Mode\t%s\n"COL_GREY"Entry Fee\t%s\n"COL_GREY"Prize Distribution\t%0.0f-%0.0f-%0.0f\n",
cash_format( g_raceData[ raceid ] [ E_POOL ] ), g_raceData[ raceid ] [ E_MODE ] == RACE_STREET_RACE ? ( "Streetrace" ) : ( "Outrun" ), cash_format( g_raceData[ raceid ] [ E_ENTRY_FEE ] ),
100.0 * g_raceData[ raceid ] [ E_POSITION_PRIZE ] [ 0 ], 100.0 * g_raceData[ raceid ] [ E_POSITION_PRIZE ] [ 1 ], 100.0 * g_raceData[ raceid ] [ E_POSITION_PRIZE ] [ 2 ]
);
if ( g_raceData[ raceid ] [ E_MODE ] == RACE_STREET_RACE ) {
format( szLargeString, sizeof( szLargeString ), "%s"COL_GREY"Finish Destination\t%s\n", szLargeString, g_raceData[ raceid ] [ E_RACE_FINISH_SET ] == 1 ? ( ""COL_GREEN"ACTIVE" ) : ( ""COL_ORANGE"NOT SET" ) );
} else {
format( szLargeString, sizeof( szLargeString ), "%s"COL_GREY"Outrun Distance\t%0.1f meters\n", szLargeString, g_raceData[ raceid ] [ E_OUTRUN_DISTANCE ] );
}
strcat( szLargeString, ""COL_GREY"View Racers\t " );
SetPVarInt( playerid, "editing_race", raceid );
ShowPlayerDialog( playerid, DIALOG_RACE, DIALOG_STYLE_TABLIST_HEADERS, ""COL_GOLD"Race Configuration", szLargeString, "Select", "Close" );
return 1;
}
function OnRaceOutrun( raceid )
{
new
racers = 0, ahead_count = 0, lead = g_raceData[ raceid ] [ E_OUTRUN_LEAD ];
foreach (new i : Player) if ( p_raceLobbyId[ i ] == raceid && lead != i )
{
// increment racers
racers ++;
// check if racer is ahead
if ( GetDistanceBetweenPlayers( lead, i ) > g_raceData[ raceid ] [ E_OUTRUN_DISTANCE ] )
ahead_count ++;
}
// player is ahead of all players
if ( ahead_count == racers )
{
new
position = ++ g_raceData[ raceid ] [ E_FINISHED_COUNT ];
// give prize and alert
if ( 1 <= position <= 3 )
GivePlayerRaceWin( lead, position, raceid );
// end race if position over 3 or whatever the race count is or only 1 prize
printf ("position : %d, Racers : %d", position, racers);
if ( position >= 3 || position >= racers || g_raceData[ raceid ] [ E_POSITION_PRIZE ] [ 0 ] == 1.0 )
{
// incase there is a final player and a remaining prize pool
if ( g_raceData[ raceid ] [ E_POSITION_PRIZE ] [ 0 ] != 1.0 )
{
new
closestRacer = GetClosestRacer( lead, raceid, .exceptid = lead );
printf( "Yes we have a pool of %d, closest %d", g_raceData[ raceid ] [ E_POOL ], closestRacer );
if ( IsPlayerConnected( closestRacer ) )
{
new
finalPosition = ++ g_raceData[ raceid ] [ E_FINISHED_COUNT ];
printf( "position %d", finalPosition );
if ( 1 <= finalPosition <= 3 )
GivePlayerRaceWin( closestRacer, finalPosition, raceid );
}
}
else
{
SendClientMessageToRace( raceid, -1, ""COL_GREY"[RACE]"COL_WHITE" The race has ended as there can only be one winner." );
}
// destroy race
print ("Shut race");
return DestroyRace( raceid );
}
// transfer leader
else
{
new
closestRacer = GetClosestRacer( lead, raceid, .exceptid = lead );
if ( IsPlayerConnected( closestRacer ) )
{
new
iVehicle = GetPlayerVehicleID( closestRacer );
// new leader
g_raceData[ raceid ] [ E_OUTRUN_LEAD ] = closestRacer;
SendClientMessageToRace( raceid, -1, ""COL_GREY"[RACE]"COL_WHITE" %s(%d) has taken the lead for the race.", ReturnPlayerName( closestRacer ), closestRacer );
// see if ahead
AttachDynamicObjectToVehicle( g_raceData[ raceid ] [ E_OUTRUN_OBJECT ], iVehicle, 0.0, OUTRUN_DIST, -15.0, 0.0, 0.0, 0.0 );
AttachDynamicAreaToVehicle( g_raceData[ raceid ] [ E_OUTRUN_SPHERE ], iVehicle, 0.0, OUTRUN_DIST * 2 );
}
}
}
return 1;
}
stock GetClosestRacer( playerid, raceid, exceptid = INVALID_PLAYER_ID, &Float: distance = FLOAT_INFINITY ) {
new
iCurrent = INVALID_PLAYER_ID,
Float: fX, Float: fY, Float: fZ, Float: fTmp
;
if ( GetPlayerPos( playerid, fX, fY, fZ ) )
{
foreach ( new i : Player ) if ( p_raceLobbyId[ i ] == raceid && exceptid != i && GetPlayerVehicleSeat( i ) == 0 )
{
if ( 0.0 < ( fTmp = GetPlayerDistanceFromPoint( i, fX, fY, fZ ) ) < distance )
{
distance = fTmp;
iCurrent = i;
}
}
}
return iCurrent;
}
stock GivePlayerRaceWin( playerid, position, raceid )
{
new
prizeMoney = floatround( float( g_raceData[ raceid ] [ E_POOL ] ) * g_raceData[ raceid ] [ E_POSITION_PRIZE ] [ ( position - 1 ) ] );
// give cash & reduce prize pool
GivePlayerCash( playerid, prizeMoney );
// announcement
foreach (new i : Player) if ( p_raceLobbyId[ i ] == raceid ) {
PlayerPlaySound( i, 1149, 0.0, 0.0, 0.0 );
GameTextForPlayer( i, sprintf( "~y~~h~%d%s~w~ %s", position, positionToString( position ), ReturnPlayerName( playerid ) ), 2000, 3 );
SendClientMessageFormatted( i, COLOR_GREY, "[RACE]"COL_WHITE" %s(%d) has finished the race in %d%s position (prize %s).", ReturnPlayerName( playerid ), playerid, position, positionToString( position ), cash_format( prizeMoney ) );
}
// remove from race
RemovePlayerFromRace( playerid );
}
stock DestroyRace( raceid )
{
// remove players from race
foreach (new playerid : Player) {
if ( p_raceLobbyId[ playerid ] == raceid ) {
p_raceLobbyId[ playerid ] = -1;
}
}
// remove race vars
Iter_Remove( races, raceid );
g_raceData[ raceid ] [ E_STARTED ] = false;
// destroy race cps
DestroyDynamicObject( g_raceData[ raceid ] [ E_OUTRUN_OBJECT ] ), g_raceData[ raceid ] [ E_OUTRUN_OBJECT ] = -1;
DestroyDynamicArea( g_raceData[ raceid ] [ E_OUTRUN_SPHERE ] ), g_raceData[ raceid ] [ E_OUTRUN_SPHERE ] = -1;
DestroyDynamicRaceCP( g_raceData[ raceid ] [ E_START_CHECKPOINT ] ), g_raceData[ raceid ] [ E_START_CHECKPOINT ] = -1;
DestroyDynamicRaceCP( g_raceData[ raceid ] [ E_FINISH_CHECKPOINT ] ), g_raceData[ raceid ] [ E_FINISH_CHECKPOINT ] = -1;
DestroyDynamicMapIcon( g_raceData[ raceid ] [ E_FINISH_MAP_ICON ] ), g_raceData[ raceid ] [ E_FINISH_MAP_ICON ] = -1;
KillTimer( g_raceData[ raceid ] [ E_OUTRUN_TIMER ] ), g_raceData[ raceid ] [ E_OUTRUN_TIMER ] = -1;
return 1;
}
stock RemovePlayerFromRace( playerid )
{
new
newLeader = INVALID_PLAYER_ID,
raceid = p_raceLobbyId[ playerid ]
;
if ( ! Iter_Contains( races, raceid ) )
return 0;
// hide checkpoints
Streamer_RemoveArrayData( STREAMER_TYPE_OBJECT, g_raceData[ raceid ] [ E_OUTRUN_OBJECT ], E_STREAMER_PLAYER_ID, playerid );
Streamer_RemoveArrayData( STREAMER_TYPE_RACE_CP, g_raceData[ raceid ] [ E_START_CHECKPOINT ], E_STREAMER_PLAYER_ID, playerid );
Streamer_RemoveArrayData( STREAMER_TYPE_RACE_CP, g_raceData[ raceid ] [ E_FINISH_CHECKPOINT ], E_STREAMER_PLAYER_ID, playerid );
Streamer_RemoveArrayData( STREAMER_TYPE_MAP_ICON, g_raceData[ raceid ] [ E_FINISH_MAP_ICON ], E_STREAMER_PLAYER_ID, playerid );
// kick player out
p_raceLobbyId[ playerid ] = -1;
// assign new leader if possible
if ( g_raceData[ raceid ] [ E_LOBBY_HOST ] == playerid )
{
foreach (new i : Player) if ( p_raceLobbyId[ i ] == raceid ) {
newLeader = i;
break;
}
if ( IsPlayerConnected( newLeader ) )
{
g_raceData[ raceid ] [ E_LOBBY_HOST ] = newLeader;
// SendClientMessageToRace( raceid, COLOR_GREY, "[RACE]"COL_WHITE" %s(%d) is the new lobby host.", ReturnPlayerName( newLeader ), newLeader );
}
else
{
printf("Destroyed empty race lobby %d", raceid);
DestroyRace( raceid );
}
}
// maybe the outrun lead left
else if ( g_raceData[ raceid ] [ E_MODE ] == RACE_OUTRUN && g_raceData[ raceid ] [ E_OUTRUN_LEAD ] == playerid )
{
new
closestRacer = GetClosestRacer( playerid, raceid, .exceptid = playerid );
if ( IsPlayerConnected( closestRacer ) )
{
new
iVehicle = GetPlayerVehicleID( closestRacer );
// new leader
g_raceData[ raceid ] [ E_OUTRUN_LEAD ] = closestRacer;
SendClientMessageToRace( raceid, -1, ""COL_GREY"[RACE]"COL_WHITE" %s(%d) has taken the lead for the race.", ReturnPlayerName( closestRacer ), closestRacer );
// see if ahead
AttachDynamicObjectToVehicle( g_raceData[ raceid ] [ E_OUTRUN_OBJECT ], iVehicle, 0.0, OUTRUN_DIST, -15.0, 0.0, 0.0, 0.0 );
AttachDynamicAreaToVehicle( g_raceData[ raceid ] [ E_OUTRUN_SPHERE ], iVehicle, 0.0, OUTRUN_DIST * 2 );
}
else
{
SendClientMessageToRace( raceid, -1, ""COL_GREY"[RACE]"COL_WHITE" The race has ended as a new leader couldn't be set." );
DestroyRace( raceid );
}
}
return 1;
}
stock GetRaceMemberCount( raceid ) {
new
count = 0;
foreach (new playerid : Player) if ( p_raceLobbyId[ playerid ] == raceid ) {
count ++;
}
return count;
}
stock SetRaceDestination( raceid, Float: fX, Float: fY, Float: fZ)
{
// set race position
g_raceData[ raceid ] [ E_FINISH_POS ] [ 0 ] = fX;
g_raceData[ raceid ] [ E_FINISH_POS ] [ 1 ] = fY;
g_raceData[ raceid ] [ E_FINISH_POS ] [ 2 ] = fZ;
g_raceData[ raceid ] [ E_RACE_FINISH_SET ] = 1;
// destroy checkpoint/icon again
DestroyDynamicMapIcon( g_raceData[ raceid ] [ E_FINISH_MAP_ICON ] );
DestroyDynamicRaceCP( g_raceData[ raceid ] [ E_FINISH_CHECKPOINT ] );
// place checkpoint
g_raceData[ raceid ] [ E_FINISH_MAP_ICON ] = CreateDynamicMapIcon( fX, fY, fZ, 53, -1, -1, -1, 0, 6000.0, MAPICON_GLOBAL );
g_raceData[ raceid ] [ E_FINISH_CHECKPOINT ] = CreateDynamicRaceCP( 1, g_raceData[ raceid ] [ E_FINISH_POS ] [ 0 ], g_raceData[ raceid ] [ E_FINISH_POS ] [ 1 ], g_raceData[ raceid ] [ E_FINISH_POS ] [ 2 ], 0, 0, 0, 5.0, -1, -1, 0 );
// reset players in map icon/cp
Streamer_RemoveArrayData( STREAMER_TYPE_MAP_ICON, g_raceData[ raceid ] [ E_FINISH_MAP_ICON ], E_STREAMER_PLAYER_ID, 0 );
Streamer_RemoveArrayData( STREAMER_TYPE_RACE_CP, g_raceData[ raceid ] [ E_FINISH_CHECKPOINT ], E_STREAMER_PLAYER_ID, 0 );
// stream to players
foreach (new i : Player) if ( p_raceLobbyId[ i ] == raceid ) {
Streamer_AppendArrayData( STREAMER_TYPE_MAP_ICON, g_raceData[ raceid ] [ E_FINISH_MAP_ICON ], E_STREAMER_PLAYER_ID, i );
Streamer_AppendArrayData( STREAMER_TYPE_RACE_CP, g_raceData[ raceid ] [ E_FINISH_CHECKPOINT ], E_STREAMER_PLAYER_ID, i );
}
}
stock IsRaceHost( playerid, raceid ) {
if ( raceid == -1 || ! Iter_Contains( races, raceid ) )
return false;
return g_raceData[ raceid ] [ E_LOBBY_HOST ] == playerid;
}