1012 lines
39 KiB
Plaintext
1012 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 OnPlayerExitVehicle( playerid, vehicleid )
|
|
{
|
|
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 ] = 50.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( playerid ) == 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;
|
|
}
|