From 7cd214455ae178e058f1ef19f4000ea5a0ecdbe2 Mon Sep 17 00:00:00 2001 From: Sergey Shorokhov Date: Sun, 14 Apr 2024 22:26:05 +0300 Subject: [PATCH 1/5] fix: spawns count for gamedll (#111) --- cstrike/addons/amxmodx/scripting/redm_spawns.sma | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/cstrike/addons/amxmodx/scripting/redm_spawns.sma b/cstrike/addons/amxmodx/scripting/redm_spawns.sma index a04eeca..cc6cab8 100644 --- a/cstrike/addons/amxmodx/scripting/redm_spawns.sma +++ b/cstrike/addons/amxmodx/scripting/redm_spawns.sma @@ -72,7 +72,7 @@ public plugin_init() { } get_mapname(g_mapName, charsmax(g_mapName)) - GameDLLSpawnsCountFix() + RegisterHookChain(RG_CSGameRules_RestartRound, "CSGameRules_RestartRound", .post = true) register_clcmd("enter_spawnGroup", "ClCmd_EnterSpawnGroup") @@ -872,10 +872,14 @@ static ConvertOldSpawnsFile(const file[]) { return spawnsCount } +public CSGameRules_RestartRound() { + GameDLLSpawnsCountFix() +} + static GameDLLSpawnsCountFix() { set_member_game(m_bLevelInitialized, true) - set_member_game(m_iSpawnPointCount_CT, 32) - set_member_game(m_iSpawnPointCount_Terrorist, 32) + set_member_game(m_iSpawnPointCount_CT, 64) + set_member_game(m_iSpawnPointCount_Terrorist, 64) } public bool: SpawnPreset_Random(const player) { From eba56471f905bb843ad9af0e70ea994063a465bd Mon Sep 17 00:00:00 2001 From: Sergey Shorokhov Date: Sun, 14 Apr 2024 23:00:05 +0300 Subject: [PATCH 2/5] new ConVars: `redm_changeteam_unlimited`, `redm_changeteam_freq` (#109) --- .../scripting/ReDeathmatch/ReDM_features.inc | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/cstrike/addons/amxmodx/scripting/ReDeathmatch/ReDM_features.inc b/cstrike/addons/amxmodx/scripting/ReDeathmatch/ReDM_features.inc index 065beaf..df8136c 100644 --- a/cstrike/addons/amxmodx/scripting/ReDeathmatch/ReDM_features.inc +++ b/cstrike/addons/amxmodx/scripting/ReDeathmatch/ReDM_features.inc @@ -4,6 +4,7 @@ static g_fwdPrecacheEvent = -1 static g_gunsEventsId static g_oldGroupinfo[MAX_PLAYERS + 1] static bool: g_protectionState[MAX_PLAYERS + 1] +static Float: g_nextPlayerChooseTeam[MAX_PLAYERS + 1] static bool: redm_open_equip_menu_by_g static bool: redm_block_drop_weapon @@ -19,6 +20,8 @@ static bool: redm_hide_other_deathnotice static redm_protection_color_t[32] static redm_protection_color_ct[32] static bool: mp_respawn_immunity_effects +static bool: redm_changeteam_unlimited +static Float: redm_changeteam_freq Features_Precache() { AimBarriers_Precache() @@ -43,6 +46,9 @@ Features_Init() { RegisterHookChain(RG_CBasePlayer_SetSpawnProtection, "CBasePlayer_SetSpawnProtection_Post", .post = true) RegisterHookChain(RG_CBasePlayer_RemoveSpawnProtection, "CBasePlayer_RemoveSpawnProtection", .post = true) RegisterHookChain(RG_CBasePlayer_DropPlayerItem, "CBasePlayer_DropPlayerItem", .post = false) + RegisterHookChain(RG_ShowVGUIMenu, "ShowVGUIMenu_Pre", .post = false) + RegisterHookChain(RG_HandleMenu_ChooseTeam, "HandleMenu_ChooseTeam_Pre", .post = false) + RegisterHookChain(RG_HandleMenu_ChooseTeam, "HandleMenu_ChooseTeam", .post = true) AimBarriers_Init() @@ -170,6 +176,29 @@ Features_Init() { bind_pcvar_num(get_cvar_pointer("mp_respawn_immunity_effects"), mp_respawn_immunity_effects) + bind_pcvar_num( + create_cvar( + "redm_changeteam_unlimited", + "0", + .has_min = true, .min_val = 0.0, + .has_max = true, .max_val = 1.0, + .flags = _FCVAR_BOOLEAN, + .description = "Remove limits on player's team switching." + ), + redm_changeteam_unlimited + ) + bind_pcvar_float( + create_cvar( + "redm_changeteam_freq", + "2", + .has_min = true, .min_val = 0.0, + .has_max = true, .max_val = 240.0, + .flags = _FCVAR_FLOAT, + .description = "Limit the frequency of team changes. ^n\ + In seconds." + ), + redm_changeteam_freq + ) register_clcmd("drop", "ClCmd_Drop", .FlagManager = false) } @@ -466,6 +495,65 @@ public CBasePlayer_DropPlayerItem(const player, const itemName[]) { return HC_SUPERCEDE } +public ShowVGUIMenu_Pre(const player, VGUIMenu: menuType, bitsSlots, oldMenu[]) +{ + if (!redm_changeteam_unlimited) + return + + if (menuType != VGUI_Menu_Team) + return + + // Force `old menu` style for better control + set_member(player, m_bForceShowMenu, true) + + // Force `6. Spectate` case + SetHookChainArg(3, ATYPE_INTEGER, (MENU_KEY_1 | MENU_KEY_2 | MENU_KEY_5 | MENU_KEY_6 | MENU_KEY_0)) + SetHookChainArg(4, ATYPE_STRING, "#IG_Team_Select_Spect") +} + +public HandleMenu_ChooseTeam_Pre(const player, const MenuChooseTeam: slot) { + if (!redm_changeteam_unlimited) + return HC_CONTINUE + + if (get_gametime() < g_nextPlayerChooseTeam[player]) { + client_printex(player, print_center, "#Only_1_Team_Change") + + new bool: shouldMenuClose = false + SetHookChainReturn(ATYPE_INTEGER, shouldMenuClose) + return HC_SUPERCEDE + } + + // Remove gamedll restrictions + GameDLL_Hack(player, true) + + return HC_CONTINUE +} +public HandleMenu_ChooseTeam(const player, const MenuChooseTeam: slot) { + if (!redm_changeteam_unlimited) + return + + // Revert gamedll workflow + GameDLL_Hack(player, false) + + new bool: teamChanged = GetHookChainReturn(ATYPE_INTEGER) + if (!teamChanged) + return + + if (!get_member(player, m_bJustConnected)) + g_nextPlayerChooseTeam[player] = get_gametime() + redm_changeteam_freq +} + +static GameDLL_Hack(const player, const bool: apply) { + static bool: old_freezePeriod + if (apply) { + old_freezePeriod = get_member_game(m_bFreezePeriod) + set_member_game(m_bFreezePeriod, true) + } else { + set_member_game(m_bFreezePeriod, old_freezePeriod) + set_member(player, m_bTeamChanged, false) + } +} + stock WeaponIdType: GetCurrentWeapon(const player) { new activeItem = get_member(player, m_pActiveItem) if (!activeItem) From 5737424631e6eccc7bd2d21bf20a12309ebbb641 Mon Sep 17 00:00:00 2001 From: Sergey Shorokhov Date: Thu, 18 Apr 2024 00:24:16 +0300 Subject: [PATCH 3/5] feature: `Tickets` system. (#112) * 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` * add functions docs * disable tickets as default --- .../addons/amxmodx/data/lang/redm/redm.txt | 3 + .../addons/amxmodx/scripting/ReDeathmatch.sma | 1 + .../ReDeathmatch/Features/Tickets.inc | 303 ++++++++++++++++++ .../scripting/ReDeathmatch/ReDM_features.inc | 2 + .../addons/amxmodx/scripting/include/redm.inc | 17 + 5 files changed, 326 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..68b3a49 --- /dev/null +++ b/cstrike/addons/amxmodx/scripting/ReDeathmatch/Features/Tickets.inc @@ -0,0 +1,303 @@ +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", "0", + .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() +} + +/** + * 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++) { + 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) +} + +/** + * 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 + + 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) +} + +/** + * 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) + 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) +} + +/** + * 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]) +} 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 c394bbad8b1abc3d57b0fec551ad2854b1195edf Mon Sep 17 00:00:00 2001 From: Sergey Shorokhov Date: Thu, 18 Apr 2024 00:56:22 +0300 Subject: [PATCH 4/5] fix: redm_randomspawn can't get from config (#115) --- .../addons/amxmodx/scripting/redm_spawns.sma | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/cstrike/addons/amxmodx/scripting/redm_spawns.sma b/cstrike/addons/amxmodx/scripting/redm_spawns.sma index cc6cab8..e56ec0c 100644 --- a/cstrike/addons/amxmodx/scripting/redm_spawns.sma +++ b/cstrike/addons/amxmodx/scripting/redm_spawns.sma @@ -79,10 +79,28 @@ public plugin_init() { redm_addstyle("preset", "SpawnPreset_DefaultPreset") redm_addstyle("random", "SpawnPreset_Random") + Create_Convars() + ROGInitialize(.MinDistance = 150.0) } -public plugin_cfg() { +public plugin_cfg() { + RegisterHookChain(RG_CBasePlayer_UseEmpty, "CBasePlayer_UseEmpty", .post = false) + + register_concmd("redm_edit_spawns", "ConCmd_EditSpawns", + MENU_FLAG, + "Edits spawn configuration" + ) + + register_concmd("redm_convert_spawns", "ConCmd_ConvertOldSpawns", + MENU_FLAG, + "Convert old spawns to new format" + ) + + Editor_ReloadSpawns() +} + +static Create_Convars() { bind_pcvar_num(get_cvar_pointer("mp_freeforall"), mp_freeforall) // set_pcvar_bounds(get_cvar_pointer("mp_forcerespawn"), CvarBound_Lower, true, 0.1) // TODO @@ -119,20 +137,6 @@ public plugin_cfg() { ), redm_randomspawn_dist ) - - RegisterHookChain(RG_CBasePlayer_UseEmpty, "CBasePlayer_UseEmpty", .post = false) - - register_concmd("redm_edit_spawns", "ConCmd_EditSpawns", - MENU_FLAG, - "Edits spawn configuration" - ) - - register_concmd("redm_convert_spawns", "ConCmd_ConvertOldSpawns", - MENU_FLAG, - "Convert old spawns to new format" - ) - - Editor_ReloadSpawns() } public client_putinserver(player) { From 8f191f543b3820b35af833f97ebec1b1d80c88ed Mon Sep 17 00:00:00 2001 From: Sergey Shorokhov Date: Thu, 18 Apr 2024 00:58:31 +0300 Subject: [PATCH 5/5] add `redm_tickets` description in config --- .../configs/redm/gamemode_deathmatch.json | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/cstrike/addons/amxmodx/configs/redm/gamemode_deathmatch.json b/cstrike/addons/amxmodx/configs/redm/gamemode_deathmatch.json index 6a9d00b..c770deb 100644 --- a/cstrike/addons/amxmodx/configs/redm/gamemode_deathmatch.json +++ b/cstrike/addons/amxmodx/configs/redm/gamemode_deathmatch.json @@ -116,7 +116,33 @@ // On spawn protection colors [RGBA] "redm_protection_color_t": "200 0 0 15", "redm_protection_color_ct": "0 0 200 15", - + + /* Number of times a team can + have players respawn before they stop + being able to respawn. + `0` - disabled */ + "redm_tickets": "0", + + // Tickets HUD update frequency. + "redm_tickets_hud_update_freq": "1.0", + + // Minimum percentage of tickets to start blinking. + "redm_tickets_hud_blink_percent": "10", + + /* Ticket HUD display type. + 0 - without colors and effects; + 1 - color, with effects. */ + "redm_tickets_hud_type": "1", + + // Tickets HUD X position. + "redm_tickets_hud_x": "0.5", + + // Tickets HUD Y position. + "redm_tickets_hud_y": "0.12", + + // Space between tickets for teams (when type = 2). + "redm_tickets_hud_gap": "10", + // ReDM: Modes /* Game mode change mode. `0`, `disable` - disable modes