From adcc5acfef980f947ad391dd44c312daf995b09e Mon Sep 17 00:00:00 2001 From: Sergey Shorokhov Date: Thu, 18 Apr 2024 00:11:07 +0300 Subject: [PATCH 1/3] add ConVars: - `redm_tickets` - `redm_tickets_hud_update_freq` - `redm_tickets_hud_blink_percent` - `redm_tickets_hud_type` - `redm_tickets_hud_x` - `redm_tickets_hud_y` - `redm_tickets_hud_gap` --- .../addons/amxmodx/data/lang/redm/redm.txt | 3 + .../addons/amxmodx/scripting/ReDeathmatch.sma | 1 + .../ReDeathmatch/Features/Tickets.inc | 265 ++++++++++++++++++ .../scripting/ReDeathmatch/ReDM_features.inc | 2 + .../addons/amxmodx/scripting/include/redm.inc | 17 ++ 5 files changed, 288 insertions(+) create mode 100644 cstrike/addons/amxmodx/scripting/ReDeathmatch/Features/Tickets.inc diff --git a/cstrike/addons/amxmodx/data/lang/redm/redm.txt b/cstrike/addons/amxmodx/data/lang/redm/redm.txt index 4d3e004..3e434db 100644 --- a/cstrike/addons/amxmodx/data/lang/redm/redm.txt +++ b/cstrike/addons/amxmodx/data/lang/redm/redm.txt @@ -18,6 +18,7 @@ NextGameMode = next game mode Choose = Choose Selecting = Selecting Extend = extend +TicketsTemplateType1 = T %s %s CT [ru] @@ -40,6 +41,7 @@ NextGameMode = следующий игровой режим Choose = Выберите Selecting = Выбор Extend = продлить +TicketsTemplateType1 = Т %s %s КТ [cn] Currently = 当前 @@ -61,3 +63,4 @@ NextGameMode = 下一个游戏模式 Choose = 选择 Selecting = 选中 Extend = 扩展 +TicketsTemplateType1 = T %s %s CT \ No newline at end of file diff --git a/cstrike/addons/amxmodx/scripting/ReDeathmatch.sma b/cstrike/addons/amxmodx/scripting/ReDeathmatch.sma index f07001f..13b5cf2 100644 --- a/cstrike/addons/amxmodx/scripting/ReDeathmatch.sma +++ b/cstrike/addons/amxmodx/scripting/ReDeathmatch.sma @@ -137,6 +137,7 @@ public CSGameRules_RestartRound() { return RoundModes_RestartRound() + Tickets_RestartRound() } public CSGameRules_PlayerKilled_Post(const victim, const killer, const inflictor) { diff --git a/cstrike/addons/amxmodx/scripting/ReDeathmatch/Features/Tickets.inc b/cstrike/addons/amxmodx/scripting/ReDeathmatch/Features/Tickets.inc new file mode 100644 index 0000000..932ea19 --- /dev/null +++ b/cstrike/addons/amxmodx/scripting/ReDeathmatch/Features/Tickets.inc @@ -0,0 +1,265 @@ +static g_respawnsCount[any: TeamName] + +static redm_tickets +static Float: redm_tickets_hud_update_freq +static Float: redm_tickets_hud_blink_percent +static redm_tickets_hud_type +static Float: redm_tickets_hud_x +static Float: redm_tickets_hud_y +static redm_tickets_hud_gap + +Tickets_Init() { + RegisterHookChain(RG_CBasePlayer_Killed, "CBasePlayer_Killed", .post = true) + RegisterHookChain(RG_CSGameRules_FPlayerCanRespawn, "CSGameRules_FPlayerCanRespawn", .post = true) + + bind_pcvar_num( + create_cvar( + "redm_tickets", "150", + .has_min = true, .min_val = 0.0, + .flags = _FCVAR_INTEGER, + .description = "Number of times a team can ^n\ + have players respawn before they stop ^n\ + being able to respawn. ^n\ + `0` - disabled" + ), + redm_tickets + ) + hook_cvar_change(get_cvar_pointer("redm_tickets"), "CvarChange_redm_tickets") + + bind_pcvar_float( + create_cvar( + "redm_tickets_hud_update_freq", "1.0", + .has_min = true, .min_val = 0.0, + .flags = _FCVAR_FLOAT, + .description = "Tickets HUD update frequency." + ), + redm_tickets_hud_update_freq + ) + bind_pcvar_float( + create_cvar( + "redm_tickets_hud_blink_percent", "10.0", + .has_min = true, .min_val = 0.0, + .has_max = true, .max_val = 100.0, + .flags = _FCVAR_FLOAT, + .description = "Minimum percentage of tickets to start blinking." + ), + redm_tickets_hud_blink_percent + ) + bind_pcvar_num( + create_cvar( + "redm_tickets_hud_type", "1", + .has_min = true, .min_val = 0.0, + .has_max = true, .max_val = 1.0, + .flags = _FCVAR_INTEGER, + .description = "Ticket HUD display type. ^n\ + 0 - without colors and effects; ^n\ + 1 - color, with effects." + ), + redm_tickets_hud_type + ) + bind_pcvar_float( + create_cvar( + "redm_tickets_hud_x", "0.5", + .has_min = true, .min_val = 0.0, + .has_max = true, .max_val = 1.0, + .flags = _FCVAR_FLOAT, + .description = "Tickets HUD X position." + ), + redm_tickets_hud_x + ) + bind_pcvar_float( + create_cvar( + "redm_tickets_hud_y", "0.12", + .has_min = true, .min_val = 0.0, + .has_max = true, .max_val = 1.0, + .flags = _FCVAR_FLOAT, + .description = "Tickets HUD Y position." + ), + redm_tickets_hud_y + ) + bind_pcvar_num( + create_cvar( + "redm_tickets_hud_gap", "10", + .flags = _FCVAR_INTEGER, + .description = "Space between tickets for teams (when type = 2)." + ), + redm_tickets_hud_gap + ) + + CTickets_UpdateHUD() +} + +public CvarChange_redm_tickets(const cvar, const oldValue[], const value[]) { + CTickets_UpdateHUD() + for (new player = 1; player <= MaxClients; player++) { + if (!is_user_connected(player)) + continue + + new TeamName: team = get_member(player, m_iTeam) + if (CTickets_TeamTicketsLeft(team) == 0) + continue + + CTickets_PreventPlayerSpawning(player, .isSpawnAvailable = true) + } +} + +Tickets_RestartRound() { + arrayset(g_respawnsCount, 0, sizeof(g_respawnsCount)) + + CTickets_UpdateHUD() +} + +public CBasePlayer_Killed(const player, const killer, const gib) { + if (!IsActive()) + return + + if (redm_tickets <= 0) + return + + new TeamName: team = get_member(player, m_iTeam) + g_respawnsCount[team]++ + + if (CTickets_TeamTicketsLeft(team) != 0) + return + + CTickets_PreventPlayerSpawning(player) +} + +public CSGameRules_FPlayerCanRespawn(const player) { + if (!IsActive()) + return HC_CONTINUE + + if (redm_tickets <= 0) + return HC_CONTINUE + + new TeamName: team = get_member(player, m_iTeam) + + SetHookChainReturn(ATYPE_INTEGER, CTickets_TeamTicketsLeft(team) != 0) + return HC_SUPERCEDE +} + +public CTickets_UpdateHUD() { + if (!IsActive()) + return + + if (redm_tickets <= 0) + return + + CTickets_ShowPlayerTicketsHUD(.player = 0) + + const taskId = 9999 + remove_task(taskId) + set_task_ex(redm_tickets_hud_update_freq, "CTickets_UpdateHUD", .id = taskId) +} + +static CTickets_ShowPlayerTicketsHUD(const player = 0) { + new ticketsT = CTickets_TeamTicketsLeft(TEAM_TERRORIST) + new ticketsCT = CTickets_TeamTicketsLeft(TEAM_CT) + new digitsCount = UTIL_getDigitsCount(redm_tickets) + + /* 0.0093 = 16:9 + 0.0167 = 4:3 */ + const Float: charSize = 0.0167 + const Float: overlapTime = 0.1 + + static digitsTemplate[32] + formatex(digitsTemplate, charsmax(digitsTemplate), + "%%" + "0%i" + "d", digitsCount + ) + + if (redm_tickets_hud_type == 0) { + new buffer[256] + formatex(buffer, charsmax(buffer), "%l", + "TicketsTemplateType1", + digitsTemplate, + digitsTemplate + ) + + formatex(buffer, charsmax(buffer), buffer, + ticketsT, + ticketsCT + ) + + /* 0.72 = 4:3 + 0.442 = 16:9 */ + const Float: charsFactor = 0.72 + new Float: stringWidth = strlen(buffer) * charSize * charsFactor + new Float: centerOffset = stringWidth / -2.0 + + set_dhudmessage( + .red = 200, + .green = 200, + .blue = 200, + .x = (redm_tickets_hud_x + centerOffset), + .y = redm_tickets_hud_y, + .effects = 0, + .fadeintime = 0.0, + .fadeouttime = 0.0, + .holdtime = (redm_tickets_hud_update_freq + overlapTime) + ) + show_dhudmessage(player, "%s", buffer) + + return + } + + new TeamName: looserTeam = TEAM_UNASSIGNED + if (ticketsCT < ticketsT) + looserTeam = TEAM_CT + else if (ticketsCT > ticketsT) + looserTeam = TEAM_TERRORIST + + new bool: shouldBlink = CTickets_TeamTicketsLeft(looserTeam) <= (redm_tickets * (redm_tickets_hud_blink_percent / 100)) + if (!shouldBlink) + looserTeam = TEAM_UNASSIGNED + + new Float: fadeoutTime = (redm_tickets_hud_update_freq * 0.5) + new Float: holdTimeBlink = (redm_tickets_hud_update_freq - fadeoutTime) + + // Position calculating + new Float: digitsWidth = digitsCount * charSize + new Float: centerOffset = digitsWidth / -2.0 + + /* 200 = 16:9 + 120 = 4:3 */ + const Float: paddingFactor = 120.0 + new Float: padding = (digitsCount / paddingFactor) + ((redm_tickets_hud_gap * charSize) / 2) + + // T tickets + set_dhudmessage( + .red = 255, + .green = 100, + .blue = 0, + .x = (redm_tickets_hud_x + centerOffset) - padding, + .y = redm_tickets_hud_y, + .effects = 0, + .fadeintime = 0.0, + .fadeouttime = (looserTeam == TEAM_TERRORIST) ? fadeoutTime : 0.0, + .holdtime = (looserTeam == TEAM_TERRORIST) ? holdTimeBlink : (redm_tickets_hud_update_freq + overlapTime) + ) + show_dhudmessage(player, digitsTemplate, ticketsT) + + // CT tickets + set_dhudmessage( + .red = 0, + .green = 100, + .blue = 255, + .x = (redm_tickets_hud_x + centerOffset) + padding, + .y = redm_tickets_hud_y, + .effects = 0, + .fadeintime = 0.0, + .fadeouttime = (looserTeam == TEAM_CT) ? fadeoutTime : 0.0, + .holdtime = (looserTeam == TEAM_CT) ? holdTimeBlink : (redm_tickets_hud_update_freq + overlapTime) + ) + + show_dhudmessage(player, digitsTemplate, ticketsCT) +} + +static Float: CTickets_PreventPlayerSpawning(const player, const bool: isSpawnAvailable = false) { + set_member(player, m_flRespawnPending, isSpawnAvailable ? get_gametime() : 9999999.0) + + return get_member(player, m_flRespawnPending) +} + +static CTickets_TeamTicketsLeft(const TeamName: team) { + return max(0, redm_tickets - g_respawnsCount[team]) +} diff --git a/cstrike/addons/amxmodx/scripting/ReDeathmatch/ReDM_features.inc b/cstrike/addons/amxmodx/scripting/ReDeathmatch/ReDM_features.inc index df8136c..f09b996 100644 --- a/cstrike/addons/amxmodx/scripting/ReDeathmatch/ReDM_features.inc +++ b/cstrike/addons/amxmodx/scripting/ReDeathmatch/ReDM_features.inc @@ -1,4 +1,5 @@ #include "ReDeathmatch/Features/AimBarriers.inc" +#include "ReDeathmatch/Features/Tickets.inc" static g_fwdPrecacheEvent = -1 static g_gunsEventsId @@ -51,6 +52,7 @@ Features_Init() { RegisterHookChain(RG_HandleMenu_ChooseTeam, "HandleMenu_ChooseTeam", .post = true) AimBarriers_Init() + Tickets_Init() bind_pcvar_num( create_cvar( diff --git a/cstrike/addons/amxmodx/scripting/include/redm.inc b/cstrike/addons/amxmodx/scripting/include/redm.inc index 0ea68bd..f77d2b5 100644 --- a/cstrike/addons/amxmodx/scripting/include/redm.inc +++ b/cstrike/addons/amxmodx/scripting/include/redm.inc @@ -626,3 +626,20 @@ enum Bounds_s { bool: b_hasBound, Float: b_value } + +/** + * Gets the number of digits in a given number. + * + * @param number The number to get the digit count for. + * @return The number of digits in the given number. + */ +stock UTIL_getDigitsCount(number) { + return 1 + floatround( + floatlog( + floatabs( + float(number) + ) + ), + floatround_floor + ) +} From 01ea864b1543f23152e76f8fd1eb4cd6405a0d10 Mon Sep 17 00:00:00 2001 From: Sergey Shorokhov Date: Thu, 18 Apr 2024 00:19:56 +0300 Subject: [PATCH 2/3] add functions docs --- .../ReDeathmatch/Features/Tickets.inc | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/cstrike/addons/amxmodx/scripting/ReDeathmatch/Features/Tickets.inc b/cstrike/addons/amxmodx/scripting/ReDeathmatch/Features/Tickets.inc index 932ea19..ade97af 100644 --- a/cstrike/addons/amxmodx/scripting/ReDeathmatch/Features/Tickets.inc +++ b/cstrike/addons/amxmodx/scripting/ReDeathmatch/Features/Tickets.inc @@ -89,6 +89,21 @@ Tickets_Init() { CTickets_UpdateHUD() } +/** + * CvarChange_redm_tickets function + * Called when the value of the "redm_tickets" configuration variable changes + * + * @param cvar Name of the configuration variable whose value has changed + * @param oldValue Previous value of the configuration variable + * @param value New value of the configuration variable + * + * The function performs the following actions: + * 1. Updates the information about the number of tickets on the HUD + * 2. For each connected player: + * - Retrieves the team the player belongs to + * - If the player's team has 0 tickets left, skips this player + * - Allows the player to spawn, if possible + */ public CvarChange_redm_tickets(const cvar, const oldValue[], const value[]) { CTickets_UpdateHUD() for (new player = 1; player <= MaxClients; player++) { @@ -125,6 +140,11 @@ public CBasePlayer_Killed(const player, const killer, const gib) { CTickets_PreventPlayerSpawning(player) } +/** + * Determines whether a player is allowed to respawn + * + * @param player The player who wants to respawn + */ public CSGameRules_FPlayerCanRespawn(const player) { if (!IsActive()) return HC_CONTINUE @@ -152,6 +172,11 @@ public CTickets_UpdateHUD() { set_task_ex(redm_tickets_hud_update_freq, "CTickets_UpdateHUD", .id = taskId) } +/** + * Displays the player's team tickets HUD. + * + * @param player The player index to display the HUD for. If 0, it will be displayed for all players. + */ static CTickets_ShowPlayerTicketsHUD(const player = 0) { new ticketsT = CTickets_TeamTicketsLeft(TEAM_TERRORIST) new ticketsCT = CTickets_TeamTicketsLeft(TEAM_CT) @@ -254,12 +279,25 @@ static CTickets_ShowPlayerTicketsHUD(const player = 0) { show_dhudmessage(player, digitsTemplate, ticketsCT) } +/** + * Prevents or allows a player to spawn based on the remaining team tickets. + * + * @param player The player index. + * @param isSpawnAvailable Indicates whether the player should be allowed to spawn. + * @return The player's respawn pending time. + */ static Float: CTickets_PreventPlayerSpawning(const player, const bool: isSpawnAvailable = false) { set_member(player, m_flRespawnPending, isSpawnAvailable ? get_gametime() : 9999999.0) return get_member(player, m_flRespawnPending) } +/** + * Calculates the number of tickets left for the given team. + * + * @param team The team to check. + * @return The number of tickets left for the team. + */ static CTickets_TeamTicketsLeft(const TeamName: team) { return max(0, redm_tickets - g_respawnsCount[team]) } From 05b06a897cdc3d550e33d6f317b3f8067191342c Mon Sep 17 00:00:00 2001 From: Sergey Shorokhov Date: Thu, 18 Apr 2024 00:22:53 +0300 Subject: [PATCH 3/3] disable tickets as default --- .../addons/amxmodx/scripting/ReDeathmatch/Features/Tickets.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cstrike/addons/amxmodx/scripting/ReDeathmatch/Features/Tickets.inc b/cstrike/addons/amxmodx/scripting/ReDeathmatch/Features/Tickets.inc index ade97af..68b3a49 100644 --- a/cstrike/addons/amxmodx/scripting/ReDeathmatch/Features/Tickets.inc +++ b/cstrike/addons/amxmodx/scripting/ReDeathmatch/Features/Tickets.inc @@ -14,7 +14,7 @@ Tickets_Init() { bind_pcvar_num( create_cvar( - "redm_tickets", "150", + "redm_tickets", "0", .has_min = true, .min_val = 0.0, .flags = _FCVAR_INTEGER, .description = "Number of times a team can ^n\