diff --git a/gamemodes/irresistible/cnr/features/_features.pwn b/gamemodes/irresistible/cnr/features/_features.pwn index 831b6b7..ad970d9 100644 --- a/gamemodes/irresistible/cnr/features/_features.pwn +++ b/gamemodes/irresistible/cnr/features/_features.pwn @@ -41,6 +41,7 @@ #include "irresistible\cnr\features\car_jacker.pwn" #include "irresistible\cnr\features\damage_feed.pwn" #include "irresistible\cnr\features\weed.pwn" +#include "irresistible\cnr\features\race.pwn" // disabled // #include "irresistible\cnr\features\eastereggs.pwn" diff --git a/gamemodes/irresistible/cnr/features/race.pwn b/gamemodes/irresistible/cnr/features/race.pwn new file mode 100644 index 0000000..1b2d1e3 --- /dev/null +++ b/gamemodes/irresistible/cnr/features/race.pwn @@ -0,0 +1,1015 @@ +/* + * 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 - 5.0 ); + } + 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 ); + } + + // 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 ) + { + foreach (new playerid : Player) if ( p_raceLobbyId[ playerid ] == raceid ) + { + if ( g_raceData[ raceid ] [ E_MODE ] == RACE_OUTRUN ) + { + // create sphere ahead of leader + if ( g_raceData[ raceid ] [ E_LOBBY_HOST ] == playerid ) + { + new + vehicleid = GetPlayerVehicleID( playerid ), Float: A; + + GetVehicleZAngle( vehicleid, A ); + + // create sphere obj + g_raceData[ raceid ] [ E_OUTRUN_LEAD ] = playerid; + 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, 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 - 4.0 ); + } + + // show checkpoint for player + Streamer_AppendArrayData( STREAMER_TYPE_OBJECT, g_raceData[ raceid ] [ E_OUTRUN_OBJECT ], E_STREAMER_PLAYER_ID, playerid ); + } + + // destroy starting checkpoint + DestroyDynamicRaceCP( g_raceData[ raceid ] [ E_START_CHECKPOINT ] ); + + // show gametext + GameTextForPlayer( playerid, "~g~GO!", 2000, 3 ); + PlayerPlaySound( playerid, 1057, 0.0, 0.0, 0.0 ); + } + g_raceData[ raceid ] [ E_CD_TIMER ] = -1; + } + 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 - 4.0 ); + } + } + } + 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 - 4.0 ); + } + 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; +} diff --git a/gamemodes/sf-cnr.pwn b/gamemodes/sf-cnr.pwn index 48412f6..9d129d9 100644 --- a/gamemodes/sf-cnr.pwn +++ b/gamemodes/sf-cnr.pwn @@ -328,51 +328,6 @@ new g_alcatrazTimestamp = 0 ; -/* ** Rank System ** */ - -/* ** Race System ** */ -#define MAX_RACES ( 32 ) - -#define RACE_STREET_RACE 0 -#define RACE_OUTRUN 1 -#define OUTRUN_DIST 6.0 - -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 -}; - -new - 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, - p_raceLobbyId[ MAX_PLAYERS ] = { -1, ... }, - p_raceInvited[ MAX_PLAYERS ] [ MAX_RACES ] -; - /* ** Forwards ** */ public OnPlayerDriveVehicle( playerid, vehicleid ); public OnServerUpdateTimer( ); @@ -2172,7 +2127,6 @@ public OnPlayerDisconnect( playerid, reason ) } if ( i < MAX_GANGS ) p_gangInvited[ playerid ] [ i ] = false; - if ( i < MAX_RACES ) p_raceInvited[ playerid ] [ i ] = false; p_BlockedPM[ playerid ] [ i ] = false; } @@ -3676,274 +3630,6 @@ CMD:casino( playerid, params[ ] ) return SendUsage( playerid, "/casino [REWARDS/POINTS]" ); } -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 ); - } - - // 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]" ); -} CMD:suggest( playerid, params[ ] ) return cmd_feedback( playerid, params ); CMD:feedback( playerid, params[ ] ) @@ -7782,7 +7468,6 @@ public OnPlayerEnterVehicle(playerid, vehicleid, ispassenger) public OnPlayerExitVehicle(playerid, vehicleid) { new - iRace = p_raceLobbyId[ playerid ], iModel = GetVehicleModel( vehicleid ), iObject = GetGVarInt( "heli_gunner", vehicleid ) ; @@ -7797,12 +7482,6 @@ public OnPlayerExitVehicle(playerid, vehicleid) p_LastAttachedVehicle[ playerid ] = INVALID_VEHICLE_ID; } - - 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; } @@ -8273,35 +7952,11 @@ public OnPlayerAccessEntrance( playerid, entranceid ) public OnPlayerEnterDynamicArea( playerid, areaid ) { - new vehicleid = GetPlayerVehicleID( playerid ); - - // races - 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 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 - 5.0 ); - } return 1; } public OnPlayerEnterDynamicRaceCP( playerid, checkpointid ) { - new raceid = p_raceLobbyId[ playerid ]; - static aPlayer[ 1 ]; aPlayer[ 0 ] = playerid; if ( checkpointid == p_PawnStoreExport[ playerid ] ) @@ -8339,31 +7994,6 @@ public OnPlayerEnterDynamicRaceCP( playerid, checkpointid ) } return 1; } - - else 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; } @@ -11297,227 +10927,6 @@ public OnDialogResponse( playerid, dialogid, response, listitem, inputtext[ ] ) format( szLargeString, sizeof( szLargeString ), ""COL_GOLD"Thank you for your feedback!"COL_WHITE" If it can make a positive impact on the server then you will be rewarded.\n\nYou can speak as freely as you want. Be vulgar, serious if you need to. It's okay as long as it's constructive.\n\nHere is what you have submitted!\n\n"COL_GREY"%s", szFeedback ); return ShowPlayerDialog( playerid, DIALOG_NULL, DIALOG_STYLE_MSGBOX, ""COL_GOLD"Server Feedback", szLargeString, "Close", "" ); } - 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 ); - } - } - } - } - 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; - } - 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; - } - 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; - } - 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." ); - } - 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 ); - } - 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 ); - } - 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 ); - } - 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 ); - } if ( dialogid == DIALOG_AIRPORT && response ) { if ( IsPlayerJailed( playerid ) ) return SendError( playerid, "You cannot travel while you're in jail." ); @@ -11765,29 +11174,7 @@ thread OnHighScoreCheck( playerid, highscore_item ) public 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." ); - } - } - else if ( IsPlayerAdminOnDuty( playerid ) ) + if ( IsPlayerAdminOnDuty( playerid ) ) { SetPlayerPosFindZ( playerid, fX, fY, fZ ); printf( "Admin %s Teleported To %f, %f, %f", ReturnPlayerName( playerid ), fX, fY, fZ ); @@ -15522,362 +14909,6 @@ stock discordLevelToString( DCC_User: user ) } #endif - -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 ) - { - foreach (new playerid : Player) if ( p_raceLobbyId[ playerid ] == raceid ) - { - if ( g_raceData[ raceid ] [ E_MODE ] == RACE_OUTRUN ) - { - // create sphere ahead of leader - if ( g_raceData[ raceid ] [ E_LOBBY_HOST ] == playerid ) - { - new - vehicleid = GetPlayerVehicleID( playerid ), Float: A; - - GetVehicleZAngle( vehicleid, A ); - - // create sphere obj - g_raceData[ raceid ] [ E_OUTRUN_LEAD ] = playerid; - 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, 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 - 4.0 ); - } - - // show checkpoint for player - Streamer_AppendArrayData( STREAMER_TYPE_OBJECT, g_raceData[ raceid ] [ E_OUTRUN_OBJECT ], E_STREAMER_PLAYER_ID, playerid ); - } - - // destroy starting checkpoint - DestroyDynamicRaceCP( g_raceData[ raceid ] [ E_START_CHECKPOINT ] ); - - // show gametext - GameTextForPlayer( playerid, "~g~GO!", 2000, 3 ); - PlayerPlaySound( playerid, 1057, 0.0, 0.0, 0.0 ); - } - g_raceData[ raceid ] [ E_CD_TIMER ] = -1; - } - 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 - 4.0 ); - } - } - } - 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 - 4.0 ); - } - 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; -} - public OnVehicleStreamOut(vehicleid, forplayerid) { return 1;