diff options
Diffstat (limited to 'sourcemod/scripting/gokz-racing')
| -rw-r--r-- | sourcemod/scripting/gokz-racing/announce.sp | 229 | ||||
| -rw-r--r-- | sourcemod/scripting/gokz-racing/api.sp | 107 | ||||
| -rw-r--r-- | sourcemod/scripting/gokz-racing/commands.sp | 47 | ||||
| -rw-r--r-- | sourcemod/scripting/gokz-racing/duel_menu.sp | 534 | ||||
| -rw-r--r-- | sourcemod/scripting/gokz-racing/race.sp | 221 | ||||
| -rw-r--r-- | sourcemod/scripting/gokz-racing/race_menu.sp | 464 | ||||
| -rw-r--r-- | sourcemod/scripting/gokz-racing/racer.sp | 439 |
7 files changed, 2041 insertions, 0 deletions
diff --git a/sourcemod/scripting/gokz-racing/announce.sp b/sourcemod/scripting/gokz-racing/announce.sp new file mode 100644 index 0000000..7b6a920 --- /dev/null +++ b/sourcemod/scripting/gokz-racing/announce.sp @@ -0,0 +1,229 @@ +/* + Chat messages of race and racer events. +*/ + + + +// =====[ PUBLIC ]===== + +/** + * Prints a message to chat for all clients in a race, formatting colours + * and optionally adding the chat prefix. If using the chat prefix, specify + * a colour at the beginning of the message e.g. "{default}Hello!". + * + * @param raceID ID of the race. + * @param specs Whether to also include racer spectators. + * @param addPrefix Whether to add the chat prefix. + * @param format Formatting rules. + * @param any Variable number of format parameters. + */ +void PrintToChatAllInRace(int raceID, bool specs, bool addPrefix, const char[] format, any...) +{ + char buffer[1024]; + for (int client = 1; client <= MaxClients; client++) + { + if (IsClientInGame(client) && GetRaceID(client) == raceID) + { + SetGlobalTransTarget(client); + VFormat(buffer, sizeof(buffer), format, 5); + GOKZ_PrintToChat(client, addPrefix, buffer); + + if (specs) + { + for (int target = 1; target <= MaxClients; target++) + { + if (IsClientInGame(target) && GetObserverTarget(target) == client && GetRaceID(target) != raceID) + { + SetGlobalTransTarget(target); + VFormat(buffer, sizeof(buffer), format, 5); + GOKZ_PrintToChat(target, addPrefix, buffer); + } + } + } + } + } +} + + + +// =====[ EVENTS ]===== + +void OnFinish_Announce(int client, int raceID, int place) +{ + switch (GetRaceInfo(raceID, RaceInfo_Type)) + { + case RaceType_Normal: + { + if (place == 1) + { + PrintToChatAllInRace(raceID, true, true, "%t", "Race Won", client); + } + else + { + ArrayList unfinishedRacers = GetUnfinishedRacers(raceID); + if (unfinishedRacers.Length >= 1) + { + PrintToChatAllInRace(raceID, true, true, "%t", "Race Placed", client, place); + } + else + { + PrintToChatAllInRace(raceID, true, true, "%t", "Race Lost", client, place); + } + delete unfinishedRacers; + } + } + case RaceType_Duel: + { + ArrayList unfinishedRacers = GetUnfinishedRacers(raceID); + if (unfinishedRacers.Length == 1) + { + int opponent = unfinishedRacers.Get(0); + GOKZ_PrintToChatAll(true, "%t", "Duel Won", client, opponent); + } + delete unfinishedRacers; + } + } +} + +void OnSurrender_Announce(int client, int raceID) +{ + switch (GetRaceInfo(raceID, RaceInfo_Type)) + { + case RaceType_Normal: + { + PrintToChatAllInRace(raceID, true, true, "%t", "Race Surrendered", client); + } + case RaceType_Duel: + { + ArrayList unfinishedRacers = GetUnfinishedRacers(raceID); + if (unfinishedRacers.Length == 1) + { + int opponent = unfinishedRacers.Get(0); + GOKZ_PrintToChatAll(true, "%t", "Duel Surrendered", client, opponent); + } + delete unfinishedRacers; + } + } +} + +void OnRequestReceived_Announce(int client, int raceID) +{ + int host = GetRaceHost(raceID); + + switch (GetRaceInfo(raceID, RaceInfo_Type)) + { + case RaceType_Normal: + { + GOKZ_PrintToChat(client, true, "%t", "Race Request Received", host); + } + case RaceType_Duel: + { + GOKZ_PrintToChat(client, true, "%t", "Duel Request Received", host); + } + } + + int cpRule = GetRaceInfo(raceID, RaceInfo_CheckpointRule); + int cdRule = GetRaceInfo(raceID, RaceInfo_CooldownRule); + int mode = GetRaceInfo(raceID, RaceInfo_Mode); + int course = GetRaceInfo(raceID, RaceInfo_Course); + + char courseStr[32]; + if (course == 0) + { + FormatEx(courseStr, sizeof(courseStr), "%T", "Race Rules - Main Course", client); + } + else + { + FormatEx(courseStr, sizeof(courseStr), "%T %d", "Race Rules - Bonus Course", client, course); + } + + if (cpRule == -1 && cdRule == 0) + { + GOKZ_PrintToChat(client, false, "%t", "Race Rules - Unlimited", gC_ModeNames[mode], courseStr); + } + if (cpRule == -1 && cdRule > 0) + { + GOKZ_PrintToChat(client, false, "%t", "Race Rules - Limited Cooldown", gC_ModeNames[mode], courseStr, cdRule); + } + if (cpRule == 0) + { + GOKZ_PrintToChat(client, false, "%t", "Race Rules - No Checkpoints", gC_ModeNames[mode], courseStr); + } + if (cpRule > 0 && cdRule == 0) + { + GOKZ_PrintToChat(client, false, "%t", "Race Rules - Limited Checkpoints", gC_ModeNames[mode], courseStr, cpRule); + } + if (cpRule > 0 && cdRule > 0) + { + GOKZ_PrintToChat(client, false, "%t", "Race Rules - Limited", gC_ModeNames[mode], courseStr, cpRule, cdRule); + } + + GOKZ_PrintToChat(client, false, "%t", "You Have Seconds To Accept", RoundFloat(RC_REQUEST_TIMEOUT_TIME)); +} + +void OnRequestAccepted_Announce(int client, int raceID) +{ + int host = GetRaceHost(raceID); + + switch (GetRaceInfo(raceID, RaceInfo_Type)) + { + case RaceType_Normal: + { + PrintToChatAllInRace(raceID, true, true, "%t", "Race Request Accepted", client, host); + } + case RaceType_Duel: + { + GOKZ_PrintToChatAll(true, "%t", "Duel Request Accepted", client, host); + } + } +} + +void OnRequestDeclined_Announce(int client, int raceID, bool timeout) +{ + int host = GetRaceHost(raceID); + + if (timeout) + { + switch (GetRaceInfo(raceID, RaceInfo_Type)) + { + case RaceType_Normal: + { + GOKZ_PrintToChat(client, true, "%t", "Race Request Not Accepted In Time (Target)"); + GOKZ_PrintToChat(host, true, "%t", "Race Request Not Accepted In Time (Host)", client); + } + case RaceType_Duel: + { + GOKZ_PrintToChat(client, true, "%t", "Duel Request Not Accepted In Time (Target)"); + GOKZ_PrintToChat(host, true, "%t", "Duel Request Not Accepted In Time (Host)", client); + } + } + } + else + { + GOKZ_PrintToChat(client, true, "%t", "You Have Declined"); + GOKZ_PrintToChat(host, true, "%t", "Player Has Declined", client); + } +} + +void OnRaceStarted_Announce(int raceID) +{ + if (GetRaceInfo(raceID, RaceInfo_Type) == RaceType_Normal) + { + PrintToChatAllInRace(raceID, true, true, "%t", "Race Host Started Countdown", GetRaceHost(raceID)); + } +} + +void OnRaceAborted_Announce(int raceID) +{ + for (int client = 1; client <= MaxClients; client++) + { + if (IsClientInGame(client) && GetRaceID(client) == raceID) + { + GOKZ_PrintToChat(client, true, "%t", "Race Has Been Aborted"); + if (GetStatus(client) == RacerStatus_Racing) + { + GOKZ_PlayErrorSound(client); + } + } + } +}
\ No newline at end of file diff --git a/sourcemod/scripting/gokz-racing/api.sp b/sourcemod/scripting/gokz-racing/api.sp new file mode 100644 index 0000000..13d82a3 --- /dev/null +++ b/sourcemod/scripting/gokz-racing/api.sp @@ -0,0 +1,107 @@ +static GlobalForward H_OnFinish; +static GlobalForward H_OnSurrender; +static GlobalForward H_OnRequestReceived; +static GlobalForward H_OnRequestAccepted; +static GlobalForward H_OnRequestDeclined; +static GlobalForward H_OnRaceRegistered; +static GlobalForward H_OnRaceInfoChanged; + + + +// =====[ FORWARDS ]===== + +void CreateGlobalForwards() +{ + H_OnFinish = new GlobalForward("GOKZ_RC_OnFinish", ET_Ignore, Param_Cell, Param_Cell, Param_Cell); + H_OnSurrender = new GlobalForward("GOKZ_RC_OnSurrender", ET_Ignore, Param_Cell, Param_Cell); + H_OnRequestReceived = new GlobalForward("GOKZ_RC_OnRequestReceived", ET_Ignore, Param_Cell, Param_Cell); + H_OnRequestAccepted = new GlobalForward("GOKZ_RC_OnRequestAccepted", ET_Ignore, Param_Cell, Param_Cell); + H_OnRequestDeclined = new GlobalForward("GOKZ_RC_OnRequestDeclined", ET_Ignore, Param_Cell, Param_Cell, Param_Cell); + H_OnRaceRegistered = new GlobalForward("GOKZ_RC_OnRaceRegistered", ET_Ignore, Param_Cell); + H_OnRaceInfoChanged = new GlobalForward("GOKZ_RC_OnRaceInfoChanged", ET_Ignore, Param_Cell, Param_Cell, Param_Cell, Param_Cell); +} + +void Call_OnFinish(int client, int raceID, int place) +{ + Call_StartForward(H_OnFinish); + Call_PushCell(client); + Call_PushCell(raceID); + Call_PushCell(place); + Call_Finish(); +} + +void Call_OnSurrender(int client, int raceID) +{ + Call_StartForward(H_OnSurrender); + Call_PushCell(client); + Call_PushCell(raceID); + Call_Finish(); +} + +void Call_OnRequestReceived(int client, int raceID) +{ + Call_StartForward(H_OnRequestReceived); + Call_PushCell(client); + Call_PushCell(raceID); + Call_Finish(); +} + +void Call_OnRequestAccepted(int client, int raceID) +{ + Call_StartForward(H_OnRequestAccepted); + Call_PushCell(client); + Call_PushCell(raceID); + Call_Finish(); +} + +void Call_OnRequestDeclined(int client, int raceID, bool timeout) +{ + Call_StartForward(H_OnRequestDeclined); + Call_PushCell(client); + Call_PushCell(raceID); + Call_PushCell(timeout); + Call_Finish(); +} + +void Call_OnRaceRegistered(int raceID) +{ + Call_StartForward(H_OnRaceRegistered); + Call_PushCell(raceID); + Call_Finish(); +} + +void Call_OnRaceInfoChanged(int raceID, RaceInfo infoIndex, int oldValue, int newValue) +{ + Call_StartForward(H_OnRaceInfoChanged); + Call_PushCell(raceID); + Call_PushCell(infoIndex); + Call_PushCell(oldValue); + Call_PushCell(newValue); + Call_Finish(); +} + + + +// =====[ NATIVES ]===== + +void CreateNatives() +{ + CreateNative("GOKZ_RC_GetRaceInfo", Native_GetRaceInfo); + CreateNative("GOKZ_RC_GetStatus", Native_GetStatus); + CreateNative("GOKZ_RC_GetRaceID", Native_GetRaceID); +} + +public int Native_GetRaceInfo(Handle plugin, int numParams) +{ + return GetRaceInfo(GetNativeCell(1), GetNativeCell(2)); +} + +public int Native_GetStatus(Handle plugin, int numParams) +{ + return GetStatus(GetNativeCell(1)); +} + +public int Native_GetRaceID(Handle plugin, int numParams) +{ + return GetRaceID(GetNativeCell(1)); +}
\ No newline at end of file diff --git a/sourcemod/scripting/gokz-racing/commands.sp b/sourcemod/scripting/gokz-racing/commands.sp new file mode 100644 index 0000000..9fbd7ab --- /dev/null +++ b/sourcemod/scripting/gokz-racing/commands.sp @@ -0,0 +1,47 @@ +void RegisterCommands() +{ + RegConsoleCmd("sm_accept", CommandAccept, "[KZ] Accept an incoming race request."); + RegConsoleCmd("sm_decline", CommandDecline, "[KZ] Decline an incoming race request."); + RegConsoleCmd("sm_surrender", CommandSurrender, "[KZ] Surrender your race."); + RegConsoleCmd("sm_duel", CommandDuel, "[KZ] Open the duel menu."); + RegConsoleCmd("sm_challenge", CommandDuel, "[KZ] Open the duel menu."); + RegConsoleCmd("sm_abort", CommandAbort, "[KZ] Abort the race you are hosting."); + + RegAdminCmd("sm_race", CommandRace, ADMFLAG_RESERVATION, "[KZ] Open the race hosting menu."); +} + +public Action CommandAccept(int client, int args) +{ + AcceptRequest(client); + return Plugin_Handled; +} + +public Action CommandDecline(int client, int args) +{ + DeclineRequest(client); + return Plugin_Handled; +} + +public Action CommandSurrender(int client, int args) +{ + SurrenderRacer(client); + return Plugin_Handled; +} + +public Action CommandDuel(int client, int args) +{ + DisplayDuelMenu(client); + return Plugin_Handled; +} + +public Action CommandAbort(int client, int args) +{ + AbortHostedRace(client); + return Plugin_Handled; +} + +public Action CommandRace(int client, int args) +{ + DisplayRaceMenu(client); + return Plugin_Handled; +}
\ No newline at end of file diff --git a/sourcemod/scripting/gokz-racing/duel_menu.sp b/sourcemod/scripting/gokz-racing/duel_menu.sp new file mode 100644 index 0000000..44c519f --- /dev/null +++ b/sourcemod/scripting/gokz-racing/duel_menu.sp @@ -0,0 +1,534 @@ +/* + A menu for initiating 1v1 races. +*/ + + + +#define ITEM_INFO_CHALLENGE "ch" +#define ITEM_INFO_ABORT "ab" +#define ITEM_INFO_MODE "md" +#define ITEM_INFO_COURSE "co" +#define ITEM_INFO_TELEPORT "tp" + +static int duelMenuMode[MAXPLAYERS + 1]; +static int duelMenuCourse[MAXPLAYERS + 1]; +static int duelMenuCheckpointLimit[MAXPLAYERS + 1]; +static int duelMenuCheckpointCooldown[MAXPLAYERS + 1]; + + + +// =====[ PICK MODE ]===== + +void DisplayDuelMenu(int client, bool reset = true) +{ + if (InRace(client) && (!IsRaceHost(client) || GetRaceInfo(GetRaceID(client), RaceInfo_Type) != RaceType_Duel)) + { + GOKZ_PrintToChat(client, true, "%t", "You Are Already Part Of A Race"); + GOKZ_PlayErrorSound(client); + return; + } + + if (reset) + { + duelMenuMode[client] = GOKZ_GetCoreOption(client, Option_Mode); + } + + Menu menu = new Menu(MenuHandler_Duel); + menu.SetTitle("%T", "Duel Menu - Title", client); + DuelMenuAddItems(client, menu); + menu.Display(client, MENU_TIME_FOREVER); +} + +public int MenuHandler_Duel(Menu menu, MenuAction action, int param1, int param2) +{ + if (action == MenuAction_Select) + { + char info[16]; + menu.GetItem(param2, info, sizeof(info)); + + if (StrEqual(info, ITEM_INFO_CHALLENGE, false)) + { + if (!DisplayDuelOpponentMenu(param1)) + { + DisplayDuelMenu(param1, false); + } + } + else if (StrEqual(info, ITEM_INFO_ABORT, false)) + { + AbortHostedRace(param1); + DisplayDuelMenu(param1, false); + } + else if (StrEqual(info, ITEM_INFO_MODE, false)) + { + DisplayDuelModeMenu(param1); + } + else if (StrEqual(info, ITEM_INFO_COURSE, false)) + { + int course = duelMenuCourse[param1]; + do + { + course++; + if (!GOKZ_IsValidCourse(course)) + { + course = 0; + } + } while (!GOKZ_GetCourseRegistered(course) && course != duelMenuCourse[param1]); + duelMenuCourse[param1] = course; + DisplayDuelMenu(param1, false); + } + else if (StrEqual(info, ITEM_INFO_TELEPORT, false)) + { + DisplayDuelCheckpointMenu(param1); + } + } + else if (action == MenuAction_End) + { + delete menu; + } + return 0; +} + +void DuelMenuAddItems(int client, Menu menu) +{ + char display[64]; + + menu.RemoveAllItems(); + + FormatEx(display, sizeof(display), "%T", "Duel Menu - Choose Opponent", client); + menu.AddItem(ITEM_INFO_CHALLENGE, display, InRace(client) ? ITEMDRAW_DISABLED : ITEMDRAW_DEFAULT); + + FormatEx(display, sizeof(display), "%T\n \n%T", "Race Menu - Abort Race", client, "Race Menu - Rules", client); + menu.AddItem(ITEM_INFO_ABORT, display, InRace(client) ? ITEMDRAW_DEFAULT : ITEMDRAW_DISABLED); + + FormatEx(display, sizeof(display), "%s", gC_ModeNames[duelMenuMode[client]]); + menu.AddItem(ITEM_INFO_MODE, display, InRace(client) ? ITEMDRAW_DISABLED : ITEMDRAW_DEFAULT); + + if (duelMenuCourse[client] == 0) + { + FormatEx(display, sizeof(display), "%T", "Race Rules - Main Course", client); + } + else + { + FormatEx(display, sizeof(display), "%T %d", "Race Rules - Bonus Course", client, duelMenuCourse[client]); + } + menu.AddItem(ITEM_INFO_COURSE, display, InRace(client) ? ITEMDRAW_DISABLED : ITEMDRAW_DEFAULT); + + FormatEx(display, sizeof(display), "%s", GetDuelRuleSummary(client, duelMenuCheckpointLimit[client], duelMenuCheckpointCooldown[client])); + menu.AddItem(ITEM_INFO_TELEPORT, display, InRace(client) ? ITEMDRAW_DISABLED : ITEMDRAW_DEFAULT); +} + + + +// =====[ MODE MENU ]===== + +static void DisplayDuelModeMenu(int client) +{ + Menu menu = new Menu(MenuHandler_DuelMode); + menu.ExitButton = false; + menu.ExitBackButton = true; + menu.SetTitle("%T", "Mode Rule Menu - Title", client); + GOKZ_MenuAddModeItems(client, menu, true); + menu.Display(client, MENU_TIME_FOREVER); +} + +public int MenuHandler_DuelMode(Menu menu, MenuAction action, int param1, int param2) +{ + if (action == MenuAction_Select) + { + duelMenuMode[param1] = param2; + DisplayDuelMenu(param1, false); + } + else if (action == MenuAction_Cancel) + { + DisplayDuelMenu(param1, false); + } + else if (action == MenuAction_End) + { + delete menu; + } + return 0; +} + + + +// =====[ CHECKPOINT MENU ]===== + +static void DisplayDuelCheckpointMenu(int client) +{ + Menu menu = new Menu(MenuHandler_DuelCheckpoint); + menu.ExitButton = false; + menu.ExitBackButton = true; + menu.SetTitle("%T", "Checkpoint Rule Menu - Title", client); + DuelCheckpointMenuAddItems(client, menu); + menu.Display(client, MENU_TIME_FOREVER); +} + +public int MenuHandler_DuelCheckpoint(Menu menu, MenuAction action, int param1, int param2) +{ + if (action == MenuAction_Select) + { + switch (param2) + { + case CheckpointRule_None: + { + duelMenuCheckpointCooldown[param1] = 0; + duelMenuCheckpointLimit[param1] = 0; + DisplayDuelMenu(param1, false); + } + case CheckpointRule_Limit: + { + DisplayCheckpointLimitMenu(param1); + } + case CheckpointRule_Cooldown: + { + DisplayCheckpointCooldownMenu(param1); + } + case CheckpointRule_Unlimited: + { + duelMenuCheckpointCooldown[param1] = 0; + duelMenuCheckpointLimit[param1] = -1; + DisplayDuelMenu(param1, false); + } + } + } + else if (action == MenuAction_Cancel) + { + DisplayDuelMenu(param1, false); + } + else if (action == MenuAction_End) + { + delete menu; + } + return 0; +} + +void DuelCheckpointMenuAddItems(int client, Menu menu) +{ + char display[32]; + + menu.RemoveAllItems(); + + FormatEx(display, sizeof(display), "%T", "Checkpoint Rule - None", client); + menu.AddItem("", display); + + if (duelMenuCheckpointLimit[client] == -1) + { + FormatEx(display, sizeof(display), "%T", "Checkpoint Rule - No Checkpoint Limit", client); + } + else + { + FormatEx(display, sizeof(display), "%T", "Checkpoint Rule - Checkpoint Limit", client, duelMenuCheckpointLimit[client]); + } + menu.AddItem("", display); + + if (duelMenuCheckpointCooldown[client] == 0) + { + FormatEx(display, sizeof(display), "%T", "Checkpoint Rule - No Checkpoint Cooldown", client); + } + else + { + FormatEx(display, sizeof(display), "%T", "Checkpoint Rule - Checkpoint Cooldown", client, duelMenuCheckpointCooldown[client]); + } + menu.AddItem("", display); + + FormatEx(display, sizeof(display), "%T", "Checkpoint Rule - Unlimited", client); + menu.AddItem("", display); +} + + + +// =====[ CP LIMIT MENU ]===== + +static void DisplayCheckpointLimitMenu(int client) +{ + char display[32]; + + Menu menu = new Menu(MenuHandler_DuelCheckpointLimit); + menu.ExitButton = false; + menu.ExitBackButton = true; + + if (duelMenuCheckpointLimit[client] == -1) + { + menu.SetTitle("%T", "Checkpoint Limit Menu - Title Unlimited", client); + } + else + { + menu.SetTitle("%T", "Checkpoint Limit Menu - Title Limited", client, duelMenuCheckpointLimit[client]); + } + + FormatEx(display, sizeof(display), "%T", "Checkpoint Limit Menu - Add One", client); + menu.AddItem("+1", display); + + FormatEx(display, sizeof(display), "%T", "Checkpoint Limit Menu - Add Five", client); + menu.AddItem("+5", display); + + FormatEx(display, sizeof(display), "%T", "Checkpoint Limit Menu - Remove One", client); + menu.AddItem("-1", display); + + FormatEx(display, sizeof(display), "%T", "Checkpoint Limit Menu - Remove Five", client); + menu.AddItem("-5", display); + + FormatEx(display, sizeof(display), "%T", "Checkpoint Limit Menu - Unlimited", client); + menu.AddItem("Unlimited", display); + + menu.Display(client, MENU_TIME_FOREVER); +} + +public int MenuHandler_DuelCheckpointLimit(Menu menu, MenuAction action, int param1, int param2) +{ + if (action == MenuAction_Select) + { + char item[32]; + menu.GetItem(param2, item, sizeof(item)); + if (StrEqual(item, "+1")) + { + if (duelMenuCheckpointLimit[param1] == -1) + { + duelMenuCheckpointLimit[param1]++; + } + duelMenuCheckpointLimit[param1]++; + } + if (StrEqual(item, "+5")) + { + if (duelMenuCheckpointLimit[param1] == -1) + { + duelMenuCheckpointLimit[param1]++; + } + duelMenuCheckpointLimit[param1] += 5; + } + if (StrEqual(item, "-1")) + { + duelMenuCheckpointLimit[param1]--; + } + if (StrEqual(item, "-5")) + { + duelMenuCheckpointLimit[param1] -= 5; + } + if (StrEqual(item, "Unlimited")) + { + duelMenuCheckpointLimit[param1] = -1; + DisplayDuelCheckpointMenu(param1); + return 0; + } + + duelMenuCheckpointLimit[param1] = duelMenuCheckpointLimit[param1] < 0 ? 0 : duelMenuCheckpointLimit[param1]; + DisplayCheckpointLimitMenu(param1); + } + else if (action == MenuAction_Cancel) + { + DisplayDuelCheckpointMenu(param1); + } + else if (action == MenuAction_End) + { + delete menu; + } + return 0; +} + + + +// =====[ CP COOLDOWN MENU ]===== + +static void DisplayCheckpointCooldownMenu(int client) +{ + char display[32]; + + Menu menu = new Menu(MenuHandler_DuelCPCooldown); + menu.ExitButton = false; + menu.ExitBackButton = true; + + if (duelMenuCheckpointCooldown[client] == -1) + { + menu.SetTitle("%T", "Checkpoint Cooldown Menu - Title None", client); + } + else + { + menu.SetTitle("%T", "Checkpoint Cooldown Menu - Title Limited", client, duelMenuCheckpointCooldown[client]); + } + + FormatEx(display, sizeof(display), "%T", "Checkpoint Cooldown Menu - Add One Second", client); + menu.AddItem("+1", display); + + FormatEx(display, sizeof(display), "%T", "Checkpoint Cooldown Menu - Add Five Seconds", client); + menu.AddItem("+5", display); + + FormatEx(display, sizeof(display), "%T", "Checkpoint Cooldown Menu - Remove One Second", client); + menu.AddItem("-1", display); + + FormatEx(display, sizeof(display), "%T", "Checkpoint Cooldown Menu - Remove Five Seconds", client); + menu.AddItem("-5", display); + + FormatEx(display, sizeof(display), "%T", "Checkpoint Cooldown Menu - None", client); + menu.AddItem("None", display); + + menu.Display(client, MENU_TIME_FOREVER); +} + +public int MenuHandler_DuelCPCooldown(Menu menu, MenuAction action, int param1, int param2) +{ + if (action == MenuAction_Select) + { + char item[32]; + menu.GetItem(param2, item, sizeof(item)); + if (StrEqual(item, "+1")) + { + if (duelMenuCheckpointCooldown[param1] == -1) + { + duelMenuCheckpointCooldown[param1]++; + } + duelMenuCheckpointCooldown[param1]++; + } + if (StrEqual(item, "+5")) + { + if (duelMenuCheckpointCooldown[param1] == -1) + { + duelMenuCheckpointCooldown[param1]++; + } + duelMenuCheckpointCooldown[param1] += 5; + } + if (StrEqual(item, "-1")) + { + duelMenuCheckpointCooldown[param1]--; + } + if (StrEqual(item, "-5")) + { + duelMenuCheckpointCooldown[param1] -= 5; + } + if (StrEqual(item, "None")) + { + duelMenuCheckpointCooldown[param1] = 0; + DisplayDuelCheckpointMenu(param1); + return 0; + } + + duelMenuCheckpointCooldown[param1] = duelMenuCheckpointCooldown[param1] < 0 ? 0 : duelMenuCheckpointCooldown[param1]; + DisplayCheckpointCooldownMenu(param1); + } + else if (action == MenuAction_Cancel) + { + DisplayDuelCheckpointMenu(param1); + } + else if (action == MenuAction_End) + { + delete menu; + } + return 0; +} + + + +// =====[ OPPONENT MENU ]===== + +static bool DisplayDuelOpponentMenu(int client) +{ + Menu menu = new Menu(MenuHandler_DuelOpponent); + menu.ExitButton = false; + menu.ExitBackButton = true; + menu.SetTitle("%T", "Duel Opponent Selection Menu - Title", client); + if (DuelOpponentMenuAddItems(client, menu) == 0) + { + GOKZ_PrintToChat(client, true, "%t", "No Opponents Available"); + GOKZ_PlayErrorSound(client); + delete menu; + return false; + } + menu.Display(client, MENU_TIME_FOREVER); + + return true; +} + +static int DuelOpponentMenuAddItems(int client, Menu menu) +{ + int count = 0; + for (int i = 1; i <= MaxClients; i++) + { + char display[MAX_NAME_LENGTH]; + if (i != client && IsClientInGame(i) && !IsFakeClient(i) && !InRace(i)) + { + FormatEx(display, sizeof(display), "%N", i); + menu.AddItem(IntToStringEx(GetClientUserId(i)), display); + count++; + } + } + return count; +} + +public int MenuHandler_DuelOpponent(Menu menu, MenuAction action, int param1, int param2) +{ + if (action == MenuAction_Select) + { + char info[16]; + menu.GetItem(param2, info, sizeof(info)); + int target = GetClientOfUserId(StringToInt(info)); + if (IsValidClient(target)) + { + if (SendDuelRequest(param1, target)) + { + GOKZ_PrintToChat(param1, true, "%t", "Duel Request Sent", target); + } + else + { + DisplayDuelOpponentMenu(param1); + } + } + else + { + GOKZ_PrintToChat(param1, true, "%t", "Player No Longer Valid"); + GOKZ_PlayErrorSound(param1); + DisplayDuelOpponentMenu(param1); + } + } + else if (action == MenuAction_Cancel) + { + DisplayDuelMenu(param1, false); + } + else if (action == MenuAction_End) + { + delete menu; + } + return 0; +} + +static bool SendDuelRequest(int host, int target) +{ + if (InRace(target)) + { + GOKZ_PrintToChat(host, true, "%t", "Player Already In A Race", target); + GOKZ_PlayErrorSound(host); + return false; + } + + HostRace(host, RaceType_Duel, duelMenuCourse[host], duelMenuMode[host], duelMenuCheckpointLimit[host], duelMenuCheckpointCooldown[host]); + return SendRequest(host, target); +} + + + +// =====[ PRIVATE ]===== + +char[] GetDuelRuleSummary(int client, int checkpointLimit, int checkpointCooldown) +{ + char rulesString[64]; + if (checkpointLimit == -1 && checkpointCooldown == 0) + { + FormatEx(rulesString, sizeof(rulesString), "%T", "Rule Summary - Unlimited", client); + } + else if (checkpointLimit > 0 && checkpointCooldown == 0) + { + FormatEx(rulesString, sizeof(rulesString), "%T", "Rule Summary - Limited Checkpoints", client, checkpointLimit); + } + else if (checkpointLimit == -1 && checkpointCooldown > 0) + { + FormatEx(rulesString, sizeof(rulesString), "%T", "Rule Summary - Limited Cooldown", client, checkpointCooldown); + } + else if (checkpointLimit > 0 && checkpointCooldown > 0) + { + FormatEx(rulesString, sizeof(rulesString), "%T", "Rule Summary - Limited Everything", client, checkpointLimit, checkpointCooldown); + } + else if (checkpointLimit == 0) + { + FormatEx(rulesString, sizeof(rulesString), "%T", "Rule Summary - No Checkpoints", client); + } + + return rulesString; +}
\ No newline at end of file diff --git a/sourcemod/scripting/gokz-racing/race.sp b/sourcemod/scripting/gokz-racing/race.sp new file mode 100644 index 0000000..5ddc2a8 --- /dev/null +++ b/sourcemod/scripting/gokz-racing/race.sp @@ -0,0 +1,221 @@ +/* + Race info storing and accessing using a StringMap. + Each race is given a unique race ID when created. + See the RaceInfo enum for what information is accessible. + See the RaceStatus enum for possible race states. +*/ + + + +static StringMap raceInfo; +static int lastRaceID; + + + +// =====[ GENERAL ]===== + +int GetRaceInfo(int raceID, RaceInfo prop) +{ + ArrayList info; + if (raceInfo.GetValue(IntToStringEx(raceID), info)) + { + return info.Get(view_as<int>(prop)); + } + else + { + return -1; + } +} + +static bool SetRaceInfo(int raceID, RaceInfo prop, int value) +{ + ArrayList info; + if (raceInfo.GetValue(IntToStringEx(raceID), info)) + { + int oldValue = info.Get(view_as<int>(prop)); + if (oldValue != value) + { + info.Set(view_as<int>(prop), value); + Call_OnRaceInfoChanged(raceID, prop, oldValue, value); + } + return true; + } + else + { + return false; + } +} + +int IncrementFinishedRacerCount(int raceID) +{ + int finishedRacers = GetRaceInfo(raceID, RaceInfo_FinishedRacerCount) + 1; + SetRaceInfo(raceID, RaceInfo_FinishedRacerCount, finishedRacers); + return finishedRacers; +} + +int GetRaceHost(int raceID) +{ + return GetClientOfUserId(GetRaceInfo(raceID, RaceInfo_HostUserID)); +} + +ArrayList GetUnfinishedRacers(int raceID) +{ + ArrayList racers = new ArrayList(); + for (int i = 1; i <= MaxClients; i++) + { + if (GetRaceID(i) == raceID && !IsFinished(i)) + { + racers.Push(i); + } + } + return racers; +} + +int GetUnfinishedRacersCount(int raceID) +{ + ArrayList racers = GetUnfinishedRacers(raceID); + int count = racers.Length; + delete racers; + return count; +} + +ArrayList GetAcceptedRacers(int raceID) +{ + ArrayList racers = new ArrayList(); + for (int i = 1; i <= MaxClients; i++) + { + if (GetRaceID(i) == raceID && IsAccepted(i)) + { + racers.Push(i); + } + } + return racers; +} + +int GetAcceptedRacersCount(int raceID) +{ + ArrayList racers = GetAcceptedRacers(raceID); + int count = racers.Length; + delete racers; + return count; +} + + + +// =====[ REGISTRATION ]===== + +int RegisterRace(int host, int type, int course, int mode, int checkpointRule, int cooldownRule) +{ + int raceID = ++lastRaceID; + + ArrayList info = new ArrayList(1, view_as<int>(RACEINFO_COUNT)); + info.Set(view_as<int>(RaceInfo_ID), raceID); + info.Set(view_as<int>(RaceInfo_Status), RaceStatus_Pending); + info.Set(view_as<int>(RaceInfo_HostUserID), GetClientUserId(host)); + info.Set(view_as<int>(RaceInfo_FinishedRacerCount), 0); + info.Set(view_as<int>(RaceInfo_Type), type); + info.Set(view_as<int>(RaceInfo_Course), course); + info.Set(view_as<int>(RaceInfo_Mode), mode); + info.Set(view_as<int>(RaceInfo_CheckpointRule), checkpointRule); + info.Set(view_as<int>(RaceInfo_CooldownRule), cooldownRule); + + raceInfo.SetValue(IntToStringEx(raceID), info); + + Call_OnRaceRegistered(raceID); + + return raceID; +} + +static void UnregisterRace(int raceID) +{ + ArrayList info; + if (raceInfo.GetValue(IntToStringEx(raceID), info)) + { + delete info; + raceInfo.Remove(IntToStringEx(raceID)); + } +} + + + +// =====[ START ]===== + +bool StartRace(int raceID) +{ + SetRaceInfo(raceID, RaceInfo_Status, RaceStatus_Countdown); + + for (int client = 1; client <= MaxClients; client++) + { + if (GetRaceID(client) == raceID) + { + StartRacer(client); + GOKZ_PrintToChat(client, true, "%t", "Race Countdown Started"); + } + } + + CreateTimer(RC_COUNTDOWN_TIME, Timer_EndCountdown, raceID); + + return true; +} + +public Action Timer_EndCountdown(Handle timer, int raceID) +{ + SetRaceInfo(raceID, RaceInfo_Status, RaceStatus_Started); + return Plugin_Continue; +} + + + +// =====[ ABORT ]===== + +bool AbortRace(int raceID) +{ + SetRaceInfo(raceID, RaceInfo_Status, RaceStatus_Aborting); + + for (int client = 1; client <= MaxClients; client++) + { + if (GetRaceID(client) == raceID) + { + AbortRacer(client); + GOKZ_PrintToChat(client, true, "%t", "Race Has Been Aborted"); + GOKZ_PlayErrorSound(client); + } + } + + UnregisterRace(raceID); + + return true; +} + + + +// =====[ EVENTS ]===== + +void OnPluginStart_Race() +{ + raceInfo = new StringMap(); +} + +void OnFinish_Race(int raceID) +{ + if (GetUnfinishedRacersCount(raceID) == 0) + { + UnregisterRace(raceID); + } +} + +void OnRequestAccepted_Race(int raceID) +{ + if (GetRaceInfo(raceID, RaceInfo_Type) == RaceType_Duel) + { + StartRace(raceID); + } +} + +void OnRequestDeclined_Race(int raceID) +{ + if (GetRaceInfo(raceID, RaceInfo_Type) == RaceType_Duel) + { + AbortRace(raceID); + } +}
\ No newline at end of file diff --git a/sourcemod/scripting/gokz-racing/race_menu.sp b/sourcemod/scripting/gokz-racing/race_menu.sp new file mode 100644 index 0000000..0518a2a --- /dev/null +++ b/sourcemod/scripting/gokz-racing/race_menu.sp @@ -0,0 +1,464 @@ +/* + A menu for hosting big races. +*/ + + + +#define ITEM_INFO_START "st" +#define ITEM_INFO_ABORT "ab" +#define ITEM_INFO_INVITE "iv" +#define ITEM_INFO_MODE "md" +#define ITEM_INFO_COURSE "co" +#define ITEM_INFO_TELEPORT "tp" + +static int raceMenuMode[MAXPLAYERS + 1]; +static int raceMenuCourse[MAXPLAYERS + 1]; +static int raceMenuCheckpointLimit[MAXPLAYERS + 1]; +static int raceMenuCheckpointCooldown[MAXPLAYERS + 1]; + + + +// =====[ PICK MODE ]===== + +void DisplayRaceMenu(int client, bool reset = true) +{ + if (InRace(client) && (!IsRaceHost(client) || GetRaceInfo(GetRaceID(client), RaceInfo_Type) != RaceType_Normal)) + { + GOKZ_PrintToChat(client, true, "%t", "You Are Already Part Of A Race"); + GOKZ_PlayErrorSound(client); + return; + } + + if (reset) + { + raceMenuMode[client] = GOKZ_GetCoreOption(client, Option_Mode); + raceMenuCourse[client] = 0; + } + + Menu menu = new Menu(MenuHandler_Race); + menu.SetTitle("%T", "Race Menu - Title", client); + RaceMenuAddItems(client, menu); + menu.Display(client, MENU_TIME_FOREVER); +} + +public int MenuHandler_Race(Menu menu, MenuAction action, int param1, int param2) +{ + if (action == MenuAction_Select) + { + char info[16]; + menu.GetItem(param2, info, sizeof(info)); + + if (StrEqual(info, ITEM_INFO_START, false)) + { + if (!StartHostedRace(param1)) + { + DisplayRaceMenu(param1, false); + } + } + else if (StrEqual(info, ITEM_INFO_ABORT, false)) + { + AbortHostedRace(param1); + DisplayRaceMenu(param1, false); + } + else if (StrEqual(info, ITEM_INFO_INVITE, false)) + { + if (!InRace(param1)) + { + HostRace(param1, RaceType_Normal, raceMenuCourse[param1], raceMenuMode[param1], raceMenuCheckpointLimit[param1], raceMenuCheckpointCooldown[param1]); + } + + SendRequestAll(param1); + GOKZ_PrintToChat(param1, true, "%t", "You Invited Everyone"); + DisplayRaceMenu(param1, false); + } + else if (StrEqual(info, ITEM_INFO_MODE, false)) + { + DisplayRaceModeMenu(param1); + } + else if (StrEqual(info, ITEM_INFO_COURSE, false)) + { + int course = raceMenuCourse[param1]; + do + { + course++; + if (!GOKZ_IsValidCourse(course)) + { + course = 0; + } + } while (!GOKZ_GetCourseRegistered(course) && course != raceMenuCourse[param1]); + raceMenuCourse[param1] = course; + DisplayRaceMenu(param1, false); + } + else if (StrEqual(info, ITEM_INFO_TELEPORT, false)) + { + DisplayRaceCheckpointMenu(param1); + } + } + else if (action == MenuAction_End) + { + delete menu; + } + return 0; +} + +void RaceMenuAddItems(int client, Menu menu) +{ + char display[32]; + + menu.RemoveAllItems(); + + bool pending = GetRaceInfo(GetRaceID(client), RaceInfo_Status) == RaceStatus_Pending; + FormatEx(display, sizeof(display), "%T", "Race Menu - Start Race", client); + menu.AddItem(ITEM_INFO_START, display, (InRace(client) && pending) ? ITEMDRAW_DEFAULT : ITEMDRAW_DISABLED); + + FormatEx(display, sizeof(display), "%T", "Race Menu - Invite Everyone", client); + menu.AddItem(ITEM_INFO_INVITE, display, (!InRace(client) || pending) ? ITEMDRAW_DEFAULT : ITEMDRAW_DISABLED); + + FormatEx(display, sizeof(display), "%T\n \n%T", "Race Menu - Abort Race", client, "Race Menu - Rules", client); + menu.AddItem(ITEM_INFO_ABORT, display, InRace(client) ? ITEMDRAW_DEFAULT : ITEMDRAW_DISABLED); + + FormatEx(display, sizeof(display), "%s", gC_ModeNames[raceMenuMode[client]]); + menu.AddItem(ITEM_INFO_MODE, display, InRace(client) ? ITEMDRAW_DISABLED : ITEMDRAW_DEFAULT); + + if (raceMenuCourse[client] == 0) + { + FormatEx(display, sizeof(display), "%T", "Race Rules - Main Course", client); + } + else + { + FormatEx(display, sizeof(display), "%T %d", "Race Rules - Bonus Course", client, raceMenuCourse[client]); + } + menu.AddItem(ITEM_INFO_COURSE, display, InRace(client) ? ITEMDRAW_DISABLED : ITEMDRAW_DEFAULT); + + FormatEx(display, sizeof(display), "%s", GetRaceRuleSummary(client, raceMenuCheckpointLimit[client], raceMenuCheckpointCooldown[client])); + menu.AddItem(ITEM_INFO_TELEPORT, display, InRace(client) ? ITEMDRAW_DISABLED : ITEMDRAW_DEFAULT); +} + + + +// =====[ MODE MENU ]===== + +static void DisplayRaceModeMenu(int client) +{ + Menu menu = new Menu(MenuHandler_RaceMode); + menu.ExitButton = false; + menu.ExitBackButton = true; + menu.SetTitle("%T", "Mode Rule Menu - Title", client); + GOKZ_MenuAddModeItems(client, menu, true); + menu.Display(client, MENU_TIME_FOREVER); +} + +public int MenuHandler_RaceMode(Menu menu, MenuAction action, int param1, int param2) +{ + if (action == MenuAction_Select) + { + raceMenuMode[param1] = param2; + DisplayRaceMenu(param1, false); + } + else if (action == MenuAction_Cancel) + { + DisplayRaceMenu(param1, false); + } + else if (action == MenuAction_End) + { + delete menu; + } + return 0; +} + + + +// =====[ CHECKPOINT MENU ]===== + +static void DisplayRaceCheckpointMenu(int client) +{ + Menu menu = new Menu(MenuHandler_RaceCheckpoint); + menu.ExitButton = false; + menu.ExitBackButton = true; + menu.SetTitle("%T", "Checkpoint Rule Menu - Title", client); + RaceCheckpointMenuAddItems(client, menu); + menu.Display(client, MENU_TIME_FOREVER); +} + +public int MenuHandler_RaceCheckpoint(Menu menu, MenuAction action, int param1, int param2) +{ + if (action == MenuAction_Select) + { + switch (param2) + { + case CheckpointRule_None: + { + raceMenuCheckpointLimit[param1] = 0; + raceMenuCheckpointCooldown[param1] = 0; + DisplayRaceMenu(param1, false); + } + case CheckpointRule_Limit: + { + DisplayCheckpointLimitMenu(param1); + } + case CheckpointRule_Cooldown: + { + DisplayCheckpointCooldownMenu(param1); + } + case CheckpointRule_Unlimited: + { + raceMenuCheckpointLimit[param1] = -1; + raceMenuCheckpointCooldown[param1] = 0; + DisplayRaceMenu(param1, false); + } + } + } + else if (action == MenuAction_Cancel) + { + DisplayRaceMenu(param1, false); + } + else if (action == MenuAction_End) + { + delete menu; + } + return 0; +} + +void RaceCheckpointMenuAddItems(int client, Menu menu) +{ + char display[32]; + + menu.RemoveAllItems(); + + FormatEx(display, sizeof(display), "%T", "Checkpoint Rule - None", client); + menu.AddItem("", display); + + if (raceMenuCheckpointLimit[client] == -1) + { + FormatEx(display, sizeof(display), "%T", "Checkpoint Rule - No Checkpoint Limit", client); + } + else + { + FormatEx(display, sizeof(display), "%T", "Checkpoint Rule - Checkpoint Limit", client, raceMenuCheckpointLimit[client]); + } + menu.AddItem("", display); + + if (raceMenuCheckpointCooldown[client] == 0) + { + FormatEx(display, sizeof(display), "%T", "Checkpoint Rule - No Checkpoint Cooldown", client); + } + else + { + FormatEx(display, sizeof(display), "%T", "Checkpoint Rule - Checkpoint Cooldown", client, raceMenuCheckpointCooldown[client]); + } + menu.AddItem("", display); + + FormatEx(display, sizeof(display), "%T", "Checkpoint Rule - Unlimited", client); + menu.AddItem("", display); +} + + + +// =====[ CP LIMIT MENU ]===== + +static void DisplayCheckpointLimitMenu(int client) +{ + char display[32]; + + Menu menu = new Menu(MenuHandler_RaceCheckpointLimit); + menu.ExitButton = false; + menu.ExitBackButton = true; + + if (raceMenuCheckpointLimit[client] == -1) + { + menu.SetTitle("%T", "Checkpoint Limit Menu - Title Unlimited", client); + } + else + { + menu.SetTitle("%T", "Checkpoint Limit Menu - Title Limited", client, raceMenuCheckpointLimit[client]); + } + + FormatEx(display, sizeof(display), "%T", "Checkpoint Limit Menu - Add One", client); + menu.AddItem("+1", display); + + FormatEx(display, sizeof(display), "%T", "Checkpoint Limit Menu - Add Five", client); + menu.AddItem("+5", display); + + FormatEx(display, sizeof(display), "%T", "Checkpoint Limit Menu - Remove One", client); + menu.AddItem("-1", display); + + FormatEx(display, sizeof(display), "%T", "Checkpoint Limit Menu - Remove Five", client); + menu.AddItem("-5", display); + + FormatEx(display, sizeof(display), "%T", "Checkpoint Limit Menu - Unlimited", client); + menu.AddItem("Unlimited", display); + + menu.Display(client, MENU_TIME_FOREVER); +} + +public int MenuHandler_RaceCheckpointLimit(Menu menu, MenuAction action, int param1, int param2) +{ + if (action == MenuAction_Select) + { + char item[32]; + menu.GetItem(param2, item, sizeof(item)); + if (StrEqual(item, "+1")) + { + if (raceMenuCheckpointLimit[param1] == -1) + { + raceMenuCheckpointLimit[param1]++; + } + raceMenuCheckpointLimit[param1]++; + } + if (StrEqual(item, "+5")) + { + if (raceMenuCheckpointLimit[param1] == -1) + { + raceMenuCheckpointLimit[param1]++; + } + raceMenuCheckpointLimit[param1] += 5; + } + if (StrEqual(item, "-1")) + { + raceMenuCheckpointLimit[param1]--; + } + if (StrEqual(item, "-5")) + { + raceMenuCheckpointLimit[param1] -= 5; + } + if (StrEqual(item, "Unlimited")) + { + raceMenuCheckpointLimit[param1] = -1; + DisplayRaceCheckpointMenu(param1); + return 0; + } + + raceMenuCheckpointLimit[param1] = raceMenuCheckpointLimit[param1] < 0 ? 0 : raceMenuCheckpointLimit[param1]; + DisplayCheckpointLimitMenu(param1); + } + else if (action == MenuAction_Cancel) + { + DisplayRaceCheckpointMenu(param1); + } + else if (action == MenuAction_End) + { + delete menu; + } + return 0; +} + + + +// =====[ CP COOLDOWN MENU ]===== + +static void DisplayCheckpointCooldownMenu(int client) +{ + char display[32]; + + Menu menu = new Menu(MenuHandler_RaceCPCooldown); + menu.ExitButton = false; + menu.ExitBackButton = true; + + if (raceMenuCheckpointCooldown[client] == -1) + { + menu.SetTitle("%T", "Checkpoint Cooldown Menu - Title None", client); + } + else + { + menu.SetTitle("%T", "Checkpoint Cooldown Menu - Title Limited", client, raceMenuCheckpointCooldown[client]); + } + + FormatEx(display, sizeof(display), "%T", "Checkpoint Cooldown Menu - Add One Second", client); + menu.AddItem("+1", display); + + FormatEx(display, sizeof(display), "%T", "Checkpoint Cooldown Menu - Add Five Seconds", client); + menu.AddItem("+5", display); + + FormatEx(display, sizeof(display), "%T", "Checkpoint Cooldown Menu - Remove One Second", client); + menu.AddItem("-1", display); + + FormatEx(display, sizeof(display), "%T", "Checkpoint Cooldown Menu - Remove Five Seconds", client); + menu.AddItem("-5", display); + + FormatEx(display, sizeof(display), "%T", "Checkpoint Cooldown Menu - None", client); + menu.AddItem("None", display); + + menu.Display(client, MENU_TIME_FOREVER); +} + +public int MenuHandler_RaceCPCooldown(Menu menu, MenuAction action, int param1, int param2) +{ + if (action == MenuAction_Select) + { + char item[32]; + menu.GetItem(param2, item, sizeof(item)); + if (StrEqual(item, "+1")) + { + if (raceMenuCheckpointCooldown[param1] == -1) + { + raceMenuCheckpointCooldown[param1]++; + } + raceMenuCheckpointCooldown[param1]++; + } + if (StrEqual(item, "+5")) + { + if (raceMenuCheckpointCooldown[param1] == -1) + { + raceMenuCheckpointCooldown[param1]++; + } + raceMenuCheckpointCooldown[param1] += 5; + } + if (StrEqual(item, "-1")) + { + raceMenuCheckpointCooldown[param1]--; + } + if (StrEqual(item, "-5")) + { + raceMenuCheckpointCooldown[param1] -= 5; + } + if (StrEqual(item, "None")) + { + raceMenuCheckpointCooldown[param1] = 0; + DisplayRaceCheckpointMenu(param1); + return 0; + } + + raceMenuCheckpointCooldown[param1] = raceMenuCheckpointCooldown[param1] < 0 ? 0 : raceMenuCheckpointCooldown[param1]; + DisplayCheckpointCooldownMenu(param1); + } + else if (action == MenuAction_Cancel) + { + DisplayRaceCheckpointMenu(param1); + } + else if (action == MenuAction_End) + { + delete menu; + } + return 0; +} + + + +// =====[ PRIVATE ]===== + +char[] GetRaceRuleSummary(int client, int checkpointLimit, int checkpointCooldown) +{ + char rulesString[64]; + if (checkpointLimit == -1 && checkpointCooldown == 0) + { + FormatEx(rulesString, sizeof(rulesString), "%T", "Rule Summary - Unlimited", client); + } + else if (checkpointLimit > 0 && checkpointCooldown == 0) + { + FormatEx(rulesString, sizeof(rulesString), "%T", "Rule Summary - Limited Checkpoints", client, checkpointLimit); + } + else if (checkpointLimit == -1 && checkpointCooldown > 0) + { + FormatEx(rulesString, sizeof(rulesString), "%T", "Rule Summary - Limited Cooldown", client, checkpointCooldown); + } + else if (checkpointLimit > 0 && checkpointCooldown > 0) + { + FormatEx(rulesString, sizeof(rulesString), "%T", "Rule Summary - Limited Everything", client, checkpointLimit, checkpointCooldown); + } + else if (checkpointLimit == 0) + { + FormatEx(rulesString, sizeof(rulesString), "%T", "Rule Summary - No Checkpoints", client); + } + + return rulesString; +}
\ No newline at end of file diff --git a/sourcemod/scripting/gokz-racing/racer.sp b/sourcemod/scripting/gokz-racing/racer.sp new file mode 100644 index 0000000..eb4ff3f --- /dev/null +++ b/sourcemod/scripting/gokz-racing/racer.sp @@ -0,0 +1,439 @@ +/* + Functions that affect the state of clients participating in a race. + See the RacerStatus enum for possible states. +*/ + + + +static int racerStatus[MAXPLAYERS + 1]; +static int racerRaceID[MAXPLAYERS + 1]; +static float lastTimerStartTime[MAXPLAYERS + 1]; +static float lastCheckpointTime[MAXPLAYERS + 1]; + + + +// =====[ EVENTS ]===== + +Action OnTimerStart_Racer(int client, int course) +{ + if (InCountdown(client) + || InStartedRace(client) && (!InRaceMode(client) || !IsRaceCourse(client, course))) + { + return Plugin_Stop; + } + + return Plugin_Continue; +} + +Action OnTimerStart_Post_Racer(int client) +{ + lastTimerStartTime[client] = GetGameTime(); + return Plugin_Continue; +} + +Action OnMakeCheckpoint_Racer(int client) +{ + if (GOKZ_GetTimerRunning(client) && InStartedRace(client)) + { + int checkpointRule = GetRaceInfo(GetRaceID(client), RaceInfo_CheckpointRule); + if (checkpointRule == 0) + { + GOKZ_PrintToChat(client, true, "%t", "Checkpoints Not Allowed During Race"); + GOKZ_PlayErrorSound(client); + return Plugin_Handled; + } + else if (checkpointRule != -1 && GOKZ_GetCheckpointCount(client) >= checkpointRule) + { + GOKZ_PrintToChat(client, true, "%t", "No Checkpoints Left"); + GOKZ_PlayErrorSound(client); + return Plugin_Handled; + } + + float cooldownRule = float(GetRaceInfo(GetRaceID(client), RaceInfo_CooldownRule)); + float timeSinceLastCheckpoint = FloatMin( + GetGameTime() - lastTimerStartTime[client], + GetGameTime() - lastCheckpointTime[client]); + if (timeSinceLastCheckpoint < cooldownRule) + { + GOKZ_PrintToChat(client, true, "%t", "Checkpoint On Cooldown", (cooldownRule - timeSinceLastCheckpoint)); + GOKZ_PlayErrorSound(client); + return Plugin_Handled; + } + } + + return Plugin_Continue; +} + +void OnMakeCheckpoint_Post_Racer(int client) +{ + lastCheckpointTime[client] = GetGameTime(); +} + +Action OnUndoTeleport_Racer(int client) +{ + if (GOKZ_GetTimerRunning(client) + && InStartedRace(client) + && GetRaceInfo(GetRaceID(client), RaceInfo_CheckpointRule) != -1) + { + GOKZ_PrintToChat(client, true, "%t", "Undo TP Not Allowed During Race"); + GOKZ_PlayErrorSound(client); + return Plugin_Handled; + } + + return Plugin_Continue; +} + + + +// =====[ GENERAL ]===== + +int GetStatus(int client) +{ + return racerStatus[client]; +} + +int GetRaceID(int client) +{ + return racerRaceID[client]; +} + +bool InRace(int client) +{ + return GetStatus(client) != RacerStatus_Available; +} + +bool InStartedRace(int client) +{ + return GetStatus(client) == RacerStatus_Racing; +} + +bool InCountdown(int client) +{ + return GetRaceInfo(GetRaceID(client), RaceInfo_Status) == RaceStatus_Countdown; +} + +bool InRaceMode(int client) +{ + return GOKZ_GetCoreOption(client, Option_Mode) == GetRaceInfo(GetRaceID(client), RaceInfo_Mode); +} + +bool IsRaceCourse(int client, int course) +{ + return course == GetRaceInfo(GetRaceID(client), RaceInfo_Course); +} + +bool IsFinished(int client) +{ + int status = GetStatus(client); + return status == RacerStatus_Finished || status == RacerStatus_Surrendered; +} + +bool IsAccepted(int client) +{ + return GetStatus(client) == RacerStatus_Accepted; +} + +bool IsRaceHost(int client) +{ + return GetRaceHost(GetRaceID(client)) == client; +} + +static void ResetRacer(int client) +{ + racerStatus[client] = RacerStatus_Available; + racerRaceID[client] = -1; + lastTimerStartTime[client] = 0.0; + lastCheckpointTime[client] = 0.0; +} + +static void ResetRacersInRace(int raceID) +{ + for (int client = 1; client <= MaxClients; client++) + { + if (racerRaceID[client] == raceID) + { + ResetRacer(client); + } + } +} + + + +// =====[ RACING ]===== + +void StartRacer(int client) +{ + if (racerStatus[client] == RacerStatus_Pending) + { + DeclineRequest(client, true); + return; + } + + if (racerStatus[client] != RacerStatus_Accepted) + { + return; + } + + racerStatus[client] = RacerStatus_Racing; + + // Prepare the racer + GOKZ_StopTimer(client); + GOKZ_SetCoreOption(client, Option_Mode, GetRaceInfo(racerRaceID[client], RaceInfo_Mode)); + + int course = GetRaceInfo(racerRaceID[client], RaceInfo_Course); + if (GOKZ_SetStartPositionToMapStart(client, course)) + { + GOKZ_TeleportToStart(client); + } + else + { + GOKZ_PrintToChat(client, true, "%t", "No Start Found", course); + } +} + +bool FinishRacer(int client, int course) +{ + if (racerStatus[client] != RacerStatus_Racing || + course != GetRaceInfo(racerRaceID[client], RaceInfo_Course)) + { + return false; + } + + racerStatus[client] = RacerStatus_Finished; + + int raceID = racerRaceID[client]; + int place = IncrementFinishedRacerCount(raceID); + + Call_OnFinish(client, raceID, place); + + CheckRaceFinished(raceID); + + return true; +} + +bool SurrenderRacer(int client) +{ + if (racerStatus[client] == RacerStatus_Available + || racerStatus[client] == RacerStatus_Surrendered) + { + return false; + } + + racerStatus[client] = RacerStatus_Surrendered; + + int raceID = racerRaceID[client]; + + Call_OnSurrender(client, raceID); + + CheckRaceFinished(raceID); + + return true; +} + +// Auto-finish last remaining racer, and reset everyone if no one is left +static void CheckRaceFinished(int raceID) +{ + ArrayList remainingRacers = GetUnfinishedRacers(raceID); + if (remainingRacers.Length == 1) + { + int lastRacer = remainingRacers.Get(0); + FinishRacer(lastRacer, GetRaceInfo(racerRaceID[lastRacer], RaceInfo_Course)); + } + else if (remainingRacers.Length == 0) + { + ResetRacersInRace(raceID); + } + delete remainingRacers; +} + +bool AbortRacer(int client) +{ + if (racerStatus[client] == RacerStatus_Available) + { + return false; + } + + ResetRacer(client); + + return true; +} + + + +// =====[ HOSTING ]===== + +int HostRace(int client, int type, int course, int mode, int checkpointRule, int cooldownRule) +{ + if (InRace(client)) + { + return -1; + } + + int raceID = RegisterRace(client, type, course, mode, checkpointRule, cooldownRule); + racerRaceID[client] = raceID; + racerStatus[client] = RacerStatus_Accepted; + + return raceID; +} + +bool StartHostedRace(int client) +{ + if (!InRace(client) || !IsRaceHost(client)) + { + GOKZ_PrintToChat(client, true, "%t", "You Are Not Hosting A Race"); + GOKZ_PlayErrorSound(client); + return false; + } + + int raceID = racerRaceID[client]; + + if (GetRaceInfo(raceID, RaceInfo_Status) != RaceStatus_Pending) + { + GOKZ_PrintToChat(client, true, "%t", "Race Already Started"); + GOKZ_PlayErrorSound(client); + return false; + } + + if (GetAcceptedRacersCount(raceID) <= 1) + { + GOKZ_PrintToChat(client, true, "%t", "No One Accepted"); + GOKZ_PlayErrorSound(client); + return false; + } + + return StartRace(raceID); +} + +bool AbortHostedRace(int client) +{ + if (!InRace(client) || !IsRaceHost(client)) + { + GOKZ_PrintToChat(client, true, "%t", "You Are Not Hosting A Race"); + GOKZ_PlayErrorSound(client); + return false; + } + + int raceID = racerRaceID[client]; + + return AbortRace(raceID); +} + + + +// =====[ REQUESTS ]===== + +bool SendRequest(int host, int target) +{ + if (IsFakeClient(target) || target == host || InRace(target) + || !IsRaceHost(host) || GetRaceInfo(racerRaceID[host], RaceInfo_Status) != RaceStatus_Pending) + { + return false; + } + + int raceID = racerRaceID[host]; + + racerRaceID[target] = raceID; + racerStatus[target] = RacerStatus_Pending; + + // Host callback + DataPack data = new DataPack(); + data.WriteCell(GetClientUserId(host)); + data.WriteCell(GetClientUserId(target)); + data.WriteCell(raceID); + CreateTimer(RC_REQUEST_TIMEOUT_TIME, Timer_RequestTimeout, data); + + Call_OnRequestReceived(target, raceID); + + return true; +} + +public Action Timer_RequestTimeout(Handle timer, DataPack data) +{ + data.Reset(); + int host = GetClientOfUserId(data.ReadCell()); + int target = GetClientOfUserId(data.ReadCell()); + int raceID = data.ReadCell(); + delete data; + + if (!IsValidClient(host) || racerRaceID[host] != raceID + || !IsValidClient(target) || racerRaceID[target] != raceID) + { + return Plugin_Continue; + } + + // If haven't accepted by now, auto decline the race + if (racerStatus[target] == RacerStatus_Pending) + { + DeclineRequest(target, true); + } + return Plugin_Continue; +} + +int SendRequestAll(int host) +{ + int sentCount = 0; + for (int target = 1; target <= MaxClients; target++) + { + if (IsClientInGame(target) && SendRequest(host, target)) + { + sentCount++; + } + } + return sentCount; +} + +bool AcceptRequest(int client) +{ + if (GetStatus(client) != RacerStatus_Pending) + { + return false; + } + + racerStatus[client] = RacerStatus_Accepted; + + Call_OnRequestAccepted(client, racerRaceID[client]); + + return true; +} + +bool DeclineRequest(int client, bool timeout = false) +{ + if (GetStatus(client) != RacerStatus_Pending) + { + return false; + } + + int raceID = racerRaceID[client]; + ResetRacer(client); + + Call_OnRequestDeclined(client, raceID, timeout); + + return true; +} + + + +// =====[ EVENTS ]===== + +void OnClientPutInServer_Racer(int client) +{ + ResetRacer(client); +} + +void OnClientDisconnect_Racer(int client) +{ + // Abort if player was the host of the race, else surrender + if (InRace(client)) + { + if (IsRaceHost(client) && GetRaceInfo(racerRaceID[client], RaceInfo_Status) == RaceStatus_Pending) + { + AbortRace(racerRaceID[client]); + } + else + { + SurrenderRacer(client); + } + } + + ResetRacer(client); +}
\ No newline at end of file |
