From ceeaf938a215df3009f0ebfe78d0821766d49b49 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sat, 27 Dec 2025 20:22:44 +0100 Subject: [PATCH 001/128] Update NativeVotes! More info in README.md (+ French translations) --- .github/workflows/nativevotes.yml | 15 +- .gitignore | 77 ++ README.md | 71 +- .../scripting/csgo_votestart_test.sp | 240 ----- .../scripting/include/multicolors.inc | 460 ++++++++ .../scripting/include/multicolors/colors.inc | 897 ++++++++++++++++ .../include/multicolors/morecolors.inc | 992 ++++++++++++++++++ .../scripting/include/nativevotes.inc | 1 + addons/sourcemod/scripting/nativevotes.sp | 10 +- .../sourcemod/scripting/nativevotes/game.sp | 8 +- ...ommands.sp => nativevotes_basecommands.sp} | 376 +++---- .../scripting/nativevotes_basevotes.sp | 734 +++++++++++++ .../nativevotes_basevotes/voteban.sp | 202 ++++ .../nativevotes_basevotes/votekick.sp | 190 ++++ .../nativevotes_basevotes/votemap.sp | 336 ++++++ .../scripting/nativevotes_funvotes.sp | 562 ++++++++++ .../nativevotes_funvotes/votealltalk.sp | 140 +++ .../nativevotes_funvotes/voteburn.sp | 200 ++++ .../scripting/nativevotes_funvotes/voteff.sp | 121 +++ .../nativevotes_funvotes/votegravity.sp | 164 +++ .../nativevotes_funvotes/voteslay.sp | 199 ++++ .../nativevotes_kickvote_immunity.sp | 6 +- .../scripting/nativevotes_mapchooser.sp | 38 +- .../scripting/nativevotes_nominations.sp | 264 +++-- .../scripting/nativevotes_rockthevote.sp | 161 ++- .../scripting/nativevotes_votemanager_test.sp | 252 ----- .../sourcemod/scripting/nativevotes_voterp.sp | 103 ++ .../scripting/nativevotes_votetest.sp | 437 -------- .../scripting/votedelay_changelevel.sp | 223 ---- addons/sourcemod/scripting/votediagnostics.sp | 790 -------------- addons/sourcemod/scripting/votefailed.sp | 151 --- .../translations/fr/nativevotes.phrases.txt | 12 + 32 files changed, 5926 insertions(+), 2506 deletions(-) create mode 100644 .gitignore delete mode 100644 addons/sourcemod/scripting/csgo_votestart_test.sp create mode 100644 addons/sourcemod/scripting/include/multicolors.inc create mode 100644 addons/sourcemod/scripting/include/multicolors/colors.inc create mode 100644 addons/sourcemod/scripting/include/multicolors/morecolors.inc rename addons/sourcemod/scripting/{nativevotes-basecommands.sp => nativevotes_basecommands.sp} (87%) create mode 100644 addons/sourcemod/scripting/nativevotes_basevotes.sp create mode 100644 addons/sourcemod/scripting/nativevotes_basevotes/voteban.sp create mode 100644 addons/sourcemod/scripting/nativevotes_basevotes/votekick.sp create mode 100644 addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp create mode 100644 addons/sourcemod/scripting/nativevotes_funvotes.sp create mode 100644 addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp create mode 100644 addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp create mode 100644 addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp create mode 100644 addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp create mode 100644 addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp delete mode 100644 addons/sourcemod/scripting/nativevotes_votemanager_test.sp create mode 100644 addons/sourcemod/scripting/nativevotes_voterp.sp delete mode 100644 addons/sourcemod/scripting/nativevotes_votetest.sp delete mode 100644 addons/sourcemod/scripting/votedelay_changelevel.sp delete mode 100644 addons/sourcemod/scripting/votediagnostics.sp delete mode 100644 addons/sourcemod/scripting/votefailed.sp create mode 100644 addons/sourcemod/translations/fr/nativevotes.phrases.txt diff --git a/.github/workflows/nativevotes.yml b/.github/workflows/nativevotes.yml index b803e90..028b4b2 100644 --- a/.github/workflows/nativevotes.yml +++ b/.github/workflows/nativevotes.yml @@ -4,6 +4,9 @@ name: Nativevotes Autobuild on: push: branches: [ master ] + pull_request: + branches: [ master ] + workflow_dispatch: jobs: run: @@ -29,7 +32,6 @@ jobs: bash .github/nv_compile.sh bash - - name: Setup SourcePawn 1.12 Compiler uses: rumblefrog/setup-sp@master with: @@ -41,6 +43,17 @@ jobs: bash .github/nv_compile.sh bash + - name: Setup SourcePawn 1.13 Compiler + uses: rumblefrog/setup-sp@master + with: + version: "1.13.x" + + - name: Compile plugins 1.13 + shell: bash + run: | + bash .github/nv_compile.sh + bash + - name: Set Commit Hash id: commit_info run: echo "::set-output name=sha_short::$(git rev-parse --short HEAD)" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..af0ad3f --- /dev/null +++ b/.gitignore @@ -0,0 +1,77 @@ +*.bat +*.sh +*.exe +*.log +*.smx + +admin.inc +adminmenu.inc +adt_array.inc +adt_stack.inc +adt_trie.inc +adt.inc +banning.inc +basecomm.inc +bitbuffer.inc +clientprefs.inc +clients.inc +commandfilters.inc +commandline.inc +console.inc +convars.inc +core.inc +cstrike.inc +datapack.inc +dbi.inc +dhooks.inc +entity_prop_stocks.inc +entity.inc +entitylump.inc +events.inc +files.inc +float.inc +functions.inc +geoip.inc +halflife.inc +handles.inc +helpers.inc +keyvalues.inc +lang.inc +logging.inc +mapchooser.inc +menus.inc +nextmap.inc +profiler.inc +protobuf.inc +regex.inc +sdkhooks.inc +sdktools_client.inc +sdktools_engine.inc +sdktools_entinput.inc +sdktools_entoutput.inc +sdktools_functions.inc +sdktools_gamerules.inc +sdktools_hooks.inc +sdktools_sound.inc +sdktools_stocks.inc +sdktools_stringtables.inc +sdktools_tempents_stocks.inc +sdktools_tempents.inc +sdktools_trace.inc +sdktools_variant_t.inc +sdktools_voice.inc +sdktools.inc +sorting.inc +sourcemod.inc +string.inc +testing.inc +textparse.inc +tf2_stocks.inc +tf2.inc +timers.inc +topmenus.inc +usermessages.inc +vector.inc +version_auto.inc +version.inc +virtual_address.inc \ No newline at end of file diff --git a/README.md b/README.md index 60b6b59..ca9d53a 100644 --- a/README.md +++ b/README.md @@ -1,47 +1,24 @@ -sourcemod-nativevotes-updated -===================== - -### NativeVotes SourceMod plugin, with the following fixes: - -@nosoop -- [Prevent NativeVote menu callback from freeing in-use forward handle](https://github.com/powerlord/sourcemod-nativevotes/pull/3) -- [Hotfix for 2022-06-22 Update](https://github.com/sapphonie/sourcemod-nativevotes-updated/pull/11) - -@rowedahelicon -- [Fixed MvM mission votes from causing an Invalid forward handle](https://github.com/powerlord/sourcemod-nativevotes/pull/8) - -@Blueberryy -- [Added Russian translation](https://github.com/powerlord/sourcemod-nativevotes/pull/7) - -@arthurdead -- [Sourcemod 1.11 syntax update](https://github.com/arthurdead/sourcemod-nativevotes/tree/sm111-fix) - -@sapphonie (me) -- [Stop memleak with FireToClient](https://github.com/powerlord/sourcemod-nativevotes/pull/9) - -@justkamiii -- [Fix caps](https://github.com/sapphonie/sourcemod-nativevotes-updated/pull/7) - -@HotoCocoaco -- [Add chi translation](https://github.com/sapphonie/sourcemod-nativevotes-updated/pull/8) - -@fdxx -- [Fixed L4D2_VotePass](https://github.com/sapphonie/sourcemod-nativevotes-updated/pull/11) - -@satanskitty -- [danish and turkish translations](https://github.com/sapphonie/sourcemod-nativevotes-updated/pull/17) - -@Spaenny -- [added german translation](https://github.com/sapphonie/sourcemod-nativevotes-updated/pull/18) - -@iBoonie -- [fixed a bug where you can spam invalid votes and forcibly prematurely change the level](https://github.com/sapphonie/sourcemod-nativevotes-updated/pull/20) -- [removed an unnecessary ThrowNativeError](https://github.com/sapphonie/sourcemod-nativevotes-updated/pull/21) - -This repo also autoreleases all changes to master, compiled on SM 1.11 and SM 1.12. Check out the latest release [here](https://github.com/sapphonie/sourcemod-nativevotes-updated/releases/latest). - -NativeVotes is a SourceMod Plugin that lets plugins use the L4D/L4D2/TF2/CSGO built-in vote screens. -It supplies an API that plugins can use to create and display these votes. - -It replaces the BuiltinVotes SourceMod extension and has a few new features as well as bugfixes. - +![](https://wiki.teamfortress.com/w/images/8/88/Voting_YYN.png) +# / NativeVotes — Continued! +This fork aims to expand upon [ sapphonie](https://github.com/sapphonie)['s work](https://github.com/sapphonie/sourcemod-nativevotes-updated). + +> [!WARNING] +> This plugin has only been tested in [ **Team Fortress 2**](https://store.steampowered.com/app/440)‼ If it doesn't work in any other game, open an [issue](https://github.com/Heapons/sourcemod-nativevotes-updated/issues/new). + +# To-Do (Wishlist...?) +## General +- [x] Include [NativeVotes BaseVotes and FunVotes](https://github.com/powerlord/sourcemod-nativevotes-basevotes) in this repository. +- Colored chat. + - [x] Team-colored player names. + - [x] Highlight map names. + - [] Parse workshop map titles. +- [x] Update [Nominations](https://github.com/Heapons/sourcemod-nativevotes-updated/blob/master/addons/sourcemod/scripting/nativevotes_nominations.sp) and [Rock The Vote](https://github.com/Heapons/sourcemod-nativevotes-updated/blob/master/addons/sourcemod/scripting/nativevotes_rockthevote.sp) to match with the latest [Sourcemod](https://github.com/alliedmodders/sourcemod/tree/master/plugins) version. +- [x] Add [`sm_voterp`](https://github.com/Heapons/sourcemod-nativevotes-updated/blob/master/addons/sourcemod/scripting/nativevotes_voterp.sp). + - Controls `tf_medieval_autorp` cvar. +## [Rock The Vote](https://github.com/Heapons/sourcemod-nativevotes-updated/blob/master/addons/sourcemod/scripting/nativevotes_rockthevote.sp) +- [x] Add `sm_forcertv` for admins. +- [x] Allow players to retract their rock-the-vote. + - Execute `sm_rtv` again to undo. +## [Nominations](https://github.com/Heapons/sourcemod-nativevotes-updated/blob/master/addons/sourcemod/scripting/nativevotes_nominations.sp) +- [] Download and nominate workshop maps. + - Inspired by [[TF2] Workshop Vote](https://forums.alliedmods.net/showthread.php?p=2717878). \ No newline at end of file diff --git a/addons/sourcemod/scripting/csgo_votestart_test.sp b/addons/sourcemod/scripting/csgo_votestart_test.sp deleted file mode 100644 index be36804..0000000 --- a/addons/sourcemod/scripting/csgo_votestart_test.sp +++ /dev/null @@ -1,240 +0,0 @@ -/** - * vim: set ts=4 : - * ============================================================================= - * CSGO VoteStart Multiple Test - * Test CS:GO Start Vote Panel - * - * CSGO VoteStart Multiple Test(C) 2014 Ross Bemrose (Powerlord). All rights reserved. - * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved. - * ============================================================================= - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, version 3.0, as published by the - * Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - * - * As a special exception, AlliedModders LLC gives you permission to link the - * code of this program (as well as its derivative works) to "Half-Life 2," the - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software - * by the Valve Corporation. You must obey the GNU General Public License in - * all respects for all other code used. Additionally, AlliedModders LLC grants - * this exception to all derivative works. AlliedModders LLC defines further - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), - * or . - * - * Version: $Id$ - */ - -#include -#include - -#pragma semicolon 1 - -#define VERSION "1.1" - -new bool:g_bVoteActive = false; - -public Plugin:myinfo = -{ - name = "CSGO VoteStart Multiple Test", - author = "Powerlord", - description = "Test CS:GO VoteStart Panels", - version = VERSION, - url = "https://forums.alliedmods.net/showthread.php?t=208008" -} - -public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max) -{ - if (GetEngineVersion() != Engine_CSGO) - { - strcopy(error, err_max, "Only runs on CS:GO"); - return APLRes_Failure; - } - return APLRes_Success; -} - -public OnPluginStart() -{ - LoadTranslations("core.phrases"); - - RegAdminCmd("votemulti", Cmd_VoteMulti, ADMFLAG_VOTE, "Multiple Choice Vote"); - RegAdminCmd("voteyesno", Cmd_VoteYesNo, ADMFLAG_VOTE, "YesNo Choice Vote"); - RegAdminCmd("voteclear", Cmd_VoteFail, ADMFLAG_VOTE, "Multiple Choice Vote"); - - AddCommandListener(Listener_Vote, "vote"); -} - -public Action:Cmd_VoteMulti(client, args) -{ - new entity = FindEntityByClassname(-1, "vote_controller"); - if (entity > -1) - { - SetEntProp(entity, Prop_Send, "m_bIsYesNoVote", false); - //SetEntProp(entity, Prop_Send, "m_iActiveIssueIndex", 3); - SetEntProp(entity, Prop_Send, "m_iOnlyTeamToVote", -1); - for (new i = 0; i < 5; i++) - { - SetEntProp(entity, Prop_Send, "m_nVoteOptionCount", 0, _, i); - } - } - - new Handle:optionsEvent = CreateEvent("vote_options"); - - SetEventString(optionsEvent, "option1", "de_dust"); - SetEventString(optionsEvent, "option2", "de_dust2"); - SetEventString(optionsEvent, "option3", "cs_italy"); - SetEventString(optionsEvent, "option4", "de_sugarcane"); - SetEventString(optionsEvent, "option5", "ar_shoots"); - SetEventInt(optionsEvent, "count", 5); - FireEvent(optionsEvent); - - if (entity > -1) - { - SetEntProp(entity, Prop_Send, "m_nPotentialVotes", GetClientCount(true)); - } - - LogMessage("Finished setting vote_controller properties."); - - g_bVoteActive = true; - - CreateTimer(0.2, Timer_StartMultiVote, GetClientUserId(client)); - return Plugin_Handled; -} - -public Action:Timer_StartMultiVote(Handle:timer, any:userid) -{ - new client = GetClientOfUserId(userid); - if (client <= 0 && client > MaxClients) - { - return Plugin_Continue; - } - - new Handle:voteStart = StartMessageAll("VoteStart", USERMSG_RELIABLE); - PbSetInt(voteStart, "team", -1); - PbSetInt(voteStart, "ent_idx", 99); - PbSetString(voteStart, "disp_str", "#SFUI_vote_nextlevel_choices"); - PbSetString(voteStart, "details_str", ""); - PbSetBool(voteStart, "is_yes_no_vote", false); - PbSetString(voteStart, "other_team_str", "#SFUI_otherteam_vote_unimplemented"); - PbSetInt(voteStart, "vote_type", 1); - EndMessage(); - - return Plugin_Stop; -} - - -public Action:Cmd_VoteYesNo(client, args) -{ - new entity = FindEntityByClassname(-1, "vote_controller"); - if (entity > -1) - { - SetEntProp(entity, Prop_Send, "m_bIsYesNoVote", true); - //SetEntProp(entity, Prop_Send, "m_iActiveIssueIndex", 2); - SetEntProp(entity, Prop_Send, "m_iOnlyTeamToVote", -1); - for (new i = 0; i < 5; i++) - { - SetEntProp(entity, Prop_Send, "m_nVoteOptionCount", 0, _, i); - } - } - - new Handle:optionsEvent = CreateEvent("vote_options"); - SetEventString(optionsEvent, "option1", "Yes"); - SetEventString(optionsEvent, "option2", "No"); - SetEventString(optionsEvent, "option3", ""); - SetEventString(optionsEvent, "option4", ""); - SetEventString(optionsEvent, "option5", ""); - SetEventInt(optionsEvent, "count", 2); - FireEvent(optionsEvent); - - if (entity > -1) - { - SetEntProp(entity, Prop_Send, "m_nPotentialVotes", GetClientCount(true)); - } - - g_bVoteActive = true; - - LogMessage("Finished setting vote_controller properties."); - - CreateTimer(0.2, Timer_StartYesNoVote, GetClientUserId(client)); - return Plugin_Handled; -} - -public Action:Timer_StartYesNoVote(Handle:timer, any:userid) -{ - new client = GetClientOfUserId(userid); - if (client <= 0 && client > MaxClients) - { - return Plugin_Continue; - } - - new Handle:voteStart = StartMessageAll("VoteStart", USERMSG_RELIABLE); - PbSetInt(voteStart, "team", -1); - PbSetInt(voteStart, "ent_idx", client); - PbSetString(voteStart, "disp_str", "#SFUI_vote_changelevel"); - PbSetString(voteStart, "details_str", "de_dust2"); - PbSetBool(voteStart, "is_yes_no_vote", true); - PbSetString(voteStart, "other_team_str", "#SFUI_otherteam_vote_unimplemented"); - PbSetInt(voteStart, "vote_type", 1); - EndMessage(); - - return Plugin_Stop; -} - -public Action:Cmd_VoteFail(client, args) -{ - if (g_bVoteActive) - VoteFail(); - - return Plugin_Handled; -} - -public Action:Listener_Vote(client, const String:command[], argc) -{ - if (!g_bVoteActive) - { - return Plugin_Continue; - } - - new ReplySource:oldSource = SetCmdReplySource(SM_REPLY_TO_CHAT); - ReplyToCommand(client, "Caught vote."); - - VoteFail(); - - SetCmdReplySource(oldSource); - return Plugin_Handled; -} - -VoteFail() -{ - g_bVoteActive = false; - new Handle:voteFailed = StartMessageAll("VoteFailed", USERMSG_RELIABLE); - - PbSetInt(voteFailed, "team", -1); - PbSetInt(voteFailed, "reason", 0); - EndMessage(); - - CreateTimer(5.0, Timer_ResetData); -} - -public Action:Timer_ResetData(Handle:timer) -{ - new entity = FindEntityByClassname(-1, "vote_controller"); - if (entity > -1) - { - //SetEntProp(entity, Prop_Send, "m_iActiveIssueIndex", -1); - for (new i = 0; i < 5; i++) - { - SetEntProp(entity, Prop_Send, "m_nVoteOptionCount", 0, _, i); - } - SetEntProp(entity, Prop_Send, "m_nPotentialVotes", 0); - SetEntProp(entity, Prop_Send, "m_iOnlyTeamToVote", -1); - SetEntProp(entity, Prop_Send, "m_bIsYesNoVote", true); - } -} \ No newline at end of file diff --git a/addons/sourcemod/scripting/include/multicolors.inc b/addons/sourcemod/scripting/include/multicolors.inc new file mode 100644 index 0000000..e6612a7 --- /dev/null +++ b/addons/sourcemod/scripting/include/multicolors.inc @@ -0,0 +1,460 @@ +#if defined _multicolors_included + #endinput +#endif +#define _multicolors_included + +#define MuCo_VERSION "2.1.2" + +#include +#include + +/* +* +* Credits: +* - Popoklopsi +* - Powerlord +* - exvel +* - Dr. McKay +* +* Based on stamm-colors +* - https://github.com/popoklopsi/Stamm/blob/master/include/stamm/stamm-colors.inc +* +*/ + + + +#define PREFIX_MAX_LENGTH 64 +#define PREFIX_SEPARATOR "{default} " + +/* Global var to check whether colors are fixed or not */ +static bool g_bCFixColors; + +static char g_sCPrefix[PREFIX_MAX_LENGTH]; + +/** + * Add a chat prefix before all chat msg + * + * @param sPrefix Prefix + */ +stock void CSetPrefix(const char[] sPrefix, any ...) { + if (!sPrefix[0]) { + return; + } + + SetGlobalTransTarget(LANG_SERVER); + VFormat(g_sCPrefix, sizeof(g_sCPrefix) - strlen(PREFIX_SEPARATOR), sPrefix, 2); + + // Add ending space + Format(g_sCPrefix, sizeof(g_sCPrefix), "%s%s", g_sCPrefix, PREFIX_SEPARATOR); +} + +/** + * Add a chat prefix before all chat msg + * + * @param sPrefix Prefix + */ +stock void CClearPrefix() { + g_sCPrefix[0] = '\0'; +} + +/** + * Writes a message to a client with the correct stock for the game. + * + * @param client Client index. + * @param message Message (formatting rules). + * + * @error If the client is not connected an error will be thrown. + */ +stock void CPrintToChat(int client, const char[] message, any ...) { + char buffer[MAX_MESSAGE_LENGTH]; + SetGlobalTransTarget(client); + VFormat(buffer, sizeof(buffer), message, 3); + + if (!g_bCFixColors) { + CFixColors(); + } + + if (!IsSource2009()) { + C_PrintToChat(client, "%s%s", g_sCPrefix, buffer); + } + else { + MC_PrintToChat(client, "%s%s", g_sCPrefix, buffer); + } +} + +/** + * Prints a message to all clients in the chat area. + * Supports color tags. + * + * @param client Client index. + * @param message Message (formatting rules) + */ +stock void CPrintToChatAll(const char[] message, any ...) { + if (!g_bCFixColors) { + CFixColors(); + } + + char buffer[MAX_BUFFER_LENGTH]; + if (!IsSource2009()) { + for (int i = 1; i <= MaxClients; ++i) { + if (IsClientInGame(i) && !IsFakeClient(i) && !C_SkipList[i]) { + SetGlobalTransTarget(i); + VFormat(buffer, sizeof(buffer), message, 2); + + C_PrintToChat(i, "%s", buffer); + } + + C_SkipList[i] = false; + } + } + else { + MC_CheckTrie(); + + char buffer2[MAX_BUFFER_LENGTH]; + + for (int i = 1; i <= MaxClients; ++i) { + if (!IsClientInGame(i) || MC_SkipList[i]) { + MC_SkipList[i] = false; + continue; + } + + SetGlobalTransTarget(i); + Format(buffer, sizeof(buffer), "\x01%s", message); + VFormat(buffer2, sizeof(buffer2), buffer, 2); + + MC_ReplaceColorCodes(buffer2); + MC_SendMessage(i, buffer2); + } + } +} + +/** + * Writes a message to all of a client's observers. + * + * @param target Client index. + * @param message Message (formatting rules). + */ +stock void CPrintToChatObservers(int target, const char[] message, any ...) { + char buffer[MAX_MESSAGE_LENGTH]; + SetGlobalTransTarget(LANG_SERVER); + VFormat(buffer, sizeof(buffer), message, 3); + + if (!g_bCFixColors) { + CFixColors(); + } + + for (int client = 1; client <= MaxClients; client++) { + if (IsClientInGame(client) && !IsPlayerAlive(client) && !IsFakeClient(client)) { + int observee = GetEntPropEnt(client, Prop_Send, "m_hObserverTarget"); + int ObserverMode = GetEntProp(client, Prop_Send, "m_iObserverMode"); + + if (observee == target && (ObserverMode == 4 || ObserverMode == 5)) { + CPrintToChat(client, buffer); + } + } + } +} + +/** + * Writes a message to a client with the correct stock for the game. + * + * @param client Client index. + * @param author Author index. + * @param message Message (formatting rules). + * + * @error If the client is not connected an error will be thrown. + */ +stock void CPrintToChatEx(int client, int author, const char[] message, any ...) { + char buffer[MAX_MESSAGE_LENGTH]; + SetGlobalTransTarget(client); + VFormat(buffer, sizeof(buffer), message, 4); + + if (!g_bCFixColors) { + CFixColors(); + } + + if (!IsSource2009()) { + C_PrintToChatEx(client, author, "%s%s", g_sCPrefix, buffer); + } + else { + MC_PrintToChatEx(client, author, "%s%s", g_sCPrefix, buffer); + } +} + +/** + * Writes a message to all clients with the correct stock for the game. + * + * @param author Author index. + * @param message Message (formatting rules). + */ +stock void CPrintToChatAllEx(int author, const char[] message, any ...) { + char buffer[MAX_MESSAGE_LENGTH]; + SetGlobalTransTarget(LANG_SERVER); + VFormat(buffer, sizeof(buffer), message, 3); + + if (!g_bCFixColors) { + CFixColors(); + } + + if (!IsSource2009()) { + C_PrintToChatAllEx(author, "%s%s", g_sCPrefix, buffer); + } + else { + MC_PrintToChatAllEx(author, "%s%s", g_sCPrefix, buffer); + } +} + +/** + * Writes a message to all of a client's observers with the correct + * game stock. + * + * @param target Client index. + * @param message Message (formatting rules). + */ +stock void CPrintToChatObserversEx(int target, const char[] message, any ...) { + char buffer[MAX_MESSAGE_LENGTH]; + SetGlobalTransTarget(LANG_SERVER); + VFormat(buffer, sizeof(buffer), message, 3); + + if (!g_bCFixColors) { + CFixColors(); + } + + for (int client = 1; client <= MaxClients; client++) { + if (IsClientInGame(client) && !IsPlayerAlive(client) && !IsFakeClient(client)) { + int observee = GetEntPropEnt(client, Prop_Send, "m_hObserverTarget"); + int ObserverMode = GetEntProp(client, Prop_Send, "m_iObserverMode"); + + if (observee == target && (ObserverMode == 4 || ObserverMode == 5)) { + CPrintToChatEx(client, target, buffer); + } + } + } +} + +/** + * Replies to a command with colors + * + * @param client Client to reply to + * @param message Message (formatting rules) + */ +stock void CReplyToCommand(int client, const char[] message, any ...) { + char buffer[MAX_MESSAGE_LENGTH]; + SetGlobalTransTarget(client); + VFormat(buffer, sizeof(buffer), message, 3); + + if (!g_bCFixColors) { + CFixColors(); + } + + if (!IsSource2009()) { + C_ReplyToCommand(client, "%s%s", g_sCPrefix, buffer); + } + else { + MC_ReplyToCommand(client, "%s%s", g_sCPrefix, buffer); + } +} + +/** + * Replies to a command with colors + * + * @param client Client to reply to + * @param author Client to use for {teamcolor} + * @param message Message (formatting rules) + */ +stock void CReplyToCommandEx(int client, int author, const char[] message, any ...) { + char buffer[MAX_MESSAGE_LENGTH]; + SetGlobalTransTarget(client); + VFormat(buffer, sizeof(buffer), message, 4); + + if (!g_bCFixColors) { + CFixColors(); + } + + if (!IsSource2009()) { + C_ReplyToCommandEx(client, author, "%s%s", g_sCPrefix, buffer); + } + else { + MC_ReplyToCommandEx(client, author, "%s%s", g_sCPrefix, buffer); + } +} + +/** + * Remove all tags and print to server + * + * @param message Message (formatting rules) + */ +stock void CPrintToServer(const char[] message, any ...) { + char buffer[MAX_MESSAGE_LENGTH]; + char prefixBuffer[PREFIX_MAX_LENGTH]; + SetGlobalTransTarget(LANG_SERVER); + VFormat(buffer, sizeof(buffer), message, 2); + strcopy(prefixBuffer, sizeof(prefixBuffer), g_sCPrefix); + + if (!g_bCFixColors) { + CFixColors(); + } + + CRemoveTags(buffer, sizeof(buffer)); + CRemoveTags(prefixBuffer, sizeof(prefixBuffer)); + + PrintToServer("%s%s", prefixBuffer, buffer); +} + +/** + * Displays usage of an admin command to users depending on the + * setting of the sm_show_activity cvar. + * + * This version does not display a message to the originating client + * if used from chat triggers or menus. If manual replies are used + * for these cases, then this function will suffice. Otherwise, + * CShowActivity2() is slightly more useful. + * Supports color tags. + * + * @param client Client index doing the action, or 0 for server. + * @param format Formatting rules. + * @param ... Variable number of format parameters. + * + * @error + */ +stock void CShowActivity(int author, const char[] message, any ...) { + char buffer[MAX_MESSAGE_LENGTH]; + SetGlobalTransTarget(LANG_SERVER); + VFormat(buffer, sizeof(buffer), message, 3); + + if (!g_bCFixColors) { + CFixColors(); + } + + if (!IsSource2009()) { + C_ShowActivity(author, "%s", buffer); + } + else { + MC_ShowActivity(author, "%s", buffer); + } +} + +/** + * Same as C_ShowActivity(), except the tag parameter is used instead of "[SM] " (note that you must supply any spacing). + * Supports color tags. + * + * @param client Client index doing the action, or 0 for server. + * @param tags Tag to display with. + * @param format Formatting rules. + * @param ... Variable number of format parameters. + * + * @error + */ +stock void CShowActivityEx(int author, const char[] tag, const char[] message, any ...) { + char buffer[MAX_MESSAGE_LENGTH]; + SetGlobalTransTarget(LANG_SERVER); + VFormat(buffer, sizeof(buffer), message, 4); + + if (!g_bCFixColors) { + CFixColors(); + } + + if (!IsSource2009()) { + C_ShowActivityEx(author, tag, "%s", buffer); + } + else { + MC_ShowActivityEx(author, tag, "%s", buffer); + } +} + +/** + * Displays usage of an admin command to users depending on the setting of the sm_show_activity cvar. + * All users receive a message in their chat text, except for the originating client, + * who receives the message based on the current ReplySource. + * Supports color tags. + * + * @param client Client index doing the action, or 0 for server. + * @param tags Tag to prepend to the message. + * @param format Formatting rules. + * @param ... Variable number of format parameters. + * + * @error + */ + stock void CShowActivity2(int author, const char[] tag, const char[] message, any ...) { + char buffer[MAX_MESSAGE_LENGTH]; + SetGlobalTransTarget(LANG_SERVER); + VFormat(buffer, sizeof(buffer), message, 4); + + if (!g_bCFixColors) { + CFixColors(); + } + + if (!IsSource2009()) { + C_ShowActivity2(author, tag, "%s", buffer); + } + else { + MC_ShowActivity2(author, tag, "%s", buffer); + } +} + +/** + * Replaces color tags in a string with color codes + * + * @param message String. + * @param maxlength Maximum length of the string buffer. + */ +stock void CFormatColor(char[] message, int maxlength, int author = -1) { + if (!g_bCFixColors) { + CFixColors(); + } + + if (!IsSource2009()) { + if (author == 0) { + author = -1; + } + + C_Format(message, maxlength, author); + } + else { + if (author == -1) { + author = 0; + } + + MC_ReplaceColorCodes(message, author, false, maxlength); + } +} + +/** + * Removes color tags from a message + * + * @param message Message to remove tags from + * @param maxlen Maximum buffer length + */ +stock void CRemoveTags(char[] message, int maxlen) { + if (!IsSource2009()) { + C_RemoveTags(message, maxlen); + } + else { + MC_RemoveTags(message, maxlen); + } +} + +/** + * Fixes missing Lightgreen color. + */ +stock void CFixColors() { + g_bCFixColors = true; + + // Replace lightgreen if not exists + if (!C_ColorAllowed(Color_Lightgreen)) { + if (C_ColorAllowed(Color_Lime)) { + C_ReplaceColor(Color_Lightgreen, Color_Lime); + } + else if (C_ColorAllowed(Color_Olive)) { + C_ReplaceColor(Color_Lightgreen, Color_Olive); + } + } +} + +stock bool IsSource2009() { + return (GetEngineVersion() == Engine_CSS + || GetEngineVersion() == Engine_HL2DM + || GetEngineVersion() == Engine_DODS + || GetEngineVersion() == Engine_TF2 + || GetEngineVersion() == Engine_SDK2013); +} diff --git a/addons/sourcemod/scripting/include/multicolors/colors.inc b/addons/sourcemod/scripting/include/multicolors/colors.inc new file mode 100644 index 0000000..65c3f1c --- /dev/null +++ b/addons/sourcemod/scripting/include/multicolors/colors.inc @@ -0,0 +1,897 @@ +/************************************************************************** + * * + * Colored Chat Functions * + * Author: exvel, Editor: Popoklopsi, Powerlord, Bara * + * Version: 2.0.0-MC * + * * + **************************************************************************/ + + +#if defined _colors_included + #endinput +#endif +#define _colors_included + +#define MAX_MESSAGE_LENGTH 250 +#define MAX_COLORS 18 + +#define SERVER_INDEX 0 +#define NO_INDEX -1 +#define NO_PLAYER -2 + +enum C_Colors { + Color_Default = 0, + Color_Darkred, + Color_Green, + Color_Lightgreen, + Color_Red, + Color_Blue, + Color_Olive, + Color_Lime, + Color_Lightred, + Color_Purple, + Color_Grey, + Color_Yellow, + Color_Orange, + Color_Bluegrey, + Color_Lightblue, + Color_Darkblue, + Color_Grey2, + Color_Orchid, + Color_Lightred2 +} + +/* C_Colors' properties */ +char C_Tag[][] = {"{default}", "{darkred}", "{green}", "{lightgreen}", "{red}", "{blue}", "{olive}", "{lime}", "{lightred}", "{purple}", "{grey}", "{yellow}", "{orange}", "{bluegrey}", "{lightblue}", "{darkblue}", "{grey2}", "{orchid}", "{lightred2}"}; +char C_TagCode[][] = {"\x01", "\x02", "\x04", "\x03", "\x03", "\x03", "\x05", "\x06", "\x07", "\x03", "\x08", "\x09", "\x10", "\x0A", "\x0B", "\x0C", "\x0D", "\x0E", "\x0F"}; +bool C_TagReqSayText2[] = {false, false, false, true, true, true, false, false, false, false, false, false, false, false, false, false, false, false, false}; +bool C_EventIsHooked; +bool C_SkipList[MAXPLAYERS+1]; + +/* Game default profile */ +bool C_Profile_Colors[] = {true, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}; +int C_Profile_TeamIndex[] = {NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX}; +bool C_Profile_SayText2; + +static ConVar sm_show_activity; + +/** + * Prints a message to a specific client in the chat area. + * Supports color tags. + * + * @param client Client index. + * @param szMessage Message (formatting rules). + * @return No return + * + * On error/Errors: If the client is not connected an error will be thrown. + */ +stock void C_PrintToChat(int client, const char[] szMessage, any ...) { + if (client <= 0 || client > MaxClients) { + ThrowError("Invalid client index %d", client); + } + + if (!IsClientInGame(client)) { + ThrowError("Client %d is not in game", client); + } + + char szBuffer[MAX_MESSAGE_LENGTH]; + char szCMessage[MAX_MESSAGE_LENGTH]; + + SetGlobalTransTarget(client); + + Format(szBuffer, sizeof(szBuffer), "\x01%s", szMessage); + VFormat(szCMessage, sizeof(szCMessage), szBuffer, 3); + + int index = C_Format(szCMessage, sizeof(szCMessage)); + + if (index == NO_INDEX) { + PrintToChat(client, "%s", szCMessage); + } + else { + C_SayText2(client, index, szCMessage); + } +} + +/** + * Reples to a message in a command. A client index of 0 will use PrintToServer(). + * If the command was from the console, PrintToConsole() is used. If the command was from chat, C_PrintToChat() is used. + * Supports color tags. + * + * @param client Client index, or 0 for server. + * @param szMessage Formatting rules. + * @param ... Variable number of format parameters. + * @return No return + * + * On error/Errors: If the client is not connected or invalid. + */ +stock void C_ReplyToCommand(int client, const char[] szMessage, any ...) { + char szCMessage[MAX_MESSAGE_LENGTH]; + SetGlobalTransTarget(client); + VFormat(szCMessage, sizeof(szCMessage), szMessage, 3); + + if (client == 0) { + C_RemoveTags(szCMessage, sizeof(szCMessage)); + PrintToServer("%s", szCMessage); + } + else if (GetCmdReplySource() == SM_REPLY_TO_CONSOLE) { + C_RemoveTags(szCMessage, sizeof(szCMessage)); + PrintToConsole(client, "%s", szCMessage); + } + else { + C_PrintToChat(client, "%s", szCMessage); + } +} + +/** + * Reples to a message in a command. A client index of 0 will use PrintToServer(). + * If the command was from the console, PrintToConsole() is used. If the command was from chat, C_PrintToChat() is used. + * Supports color tags. + * + * @param client Client index, or 0 for server. + * @param author Author index whose color will be used for teamcolor tag. + * @param szMessage Formatting rules. + * @param ... Variable number of format parameters. + * @return No return + * + * On error/Errors: If the client is not connected or invalid. + */ +stock void C_ReplyToCommandEx(int client, int author, const char[] szMessage, any ...) { + char szCMessage[MAX_MESSAGE_LENGTH]; + SetGlobalTransTarget(client); + VFormat(szCMessage, sizeof(szCMessage), szMessage, 4); + + if (client == 0) { + C_RemoveTags(szCMessage, sizeof(szCMessage)); + PrintToServer("%s", szCMessage); + } + else if (GetCmdReplySource() == SM_REPLY_TO_CONSOLE) { + C_RemoveTags(szCMessage, sizeof(szCMessage)); + PrintToConsole(client, "%s", szCMessage); + } + else { + C_PrintToChatEx(client, author, "%s", szCMessage); + } +} + +/** + * Prints a message to all clients in the chat area. + * Supports color tags. + * + * @param client Client index. + * @param szMessage Message (formatting rules) + * @return No return + */ +stock void C_PrintToChatAll(const char[] szMessage, any ...) { + char szBuffer[MAX_MESSAGE_LENGTH]; + + for (int i = 1; i <= MaxClients; ++i) { + if (IsClientInGame(i) && !IsFakeClient(i) && !C_SkipList[i]) { + SetGlobalTransTarget(i); + VFormat(szBuffer, sizeof(szBuffer), szMessage, 2); + + C_PrintToChat(i, "%s", szBuffer); + } + + C_SkipList[i] = false; + } +} + +/** + * Prints a message to a specific client in the chat area. + * Supports color tags and teamcolor tag. + * + * @param client Client index. + * @param author Author index whose color will be used for teamcolor tag. + * @param szMessage Message (formatting rules). + * @return No return + * + * On error/Errors: If the client or author are not connected an error will be thrown. + */ +stock void C_PrintToChatEx(int client, int author, const char[] szMessage, any ...) { + if (client <= 0 || client > MaxClients) { + ThrowError("Invalid client index %d", client); + } + + if (!IsClientInGame(client)) { + ThrowError("Client %d is not in game", client); + } + + if (author < 0 || author > MaxClients) { + ThrowError("Invalid client index %d", author); + } + + char szBuffer[MAX_MESSAGE_LENGTH]; + char szCMessage[MAX_MESSAGE_LENGTH]; + + SetGlobalTransTarget(client); + + Format(szBuffer, sizeof(szBuffer), "\x01%s", szMessage); + VFormat(szCMessage, sizeof(szCMessage), szBuffer, 4); + + int index = C_Format(szCMessage, sizeof(szCMessage), author); + + if (index == NO_INDEX) { + PrintToChat(client, "%s", szCMessage); + } + else { + C_SayText2(client, author, szCMessage); + } +} + +/** + * Prints a message to all clients in the chat area. + * Supports color tags and teamcolor tag. + * + * @param author Author index whos color will be used for teamcolor tag. + * @param szMessage Message (formatting rules). + * @return No return + * + * On error/Errors: If the author is not connected an error will be thrown. + */ +stock void C_PrintToChatAllEx(int author, const char[] szMessage, any ...) { + if (author < 0 || author > MaxClients) { + ThrowError("Invalid client index %d", author); + } + + if (!IsClientInGame(author)) { + ThrowError("Client %d is not in game", author); + } + + char szBuffer[MAX_MESSAGE_LENGTH]; + + for (int i = 1; i <= MaxClients; ++i) { + if (IsClientInGame(i) && !IsFakeClient(i) && !C_SkipList[i]) { + SetGlobalTransTarget(i); + VFormat(szBuffer, sizeof(szBuffer), szMessage, 3); + + C_PrintToChatEx(i, author, "%s", szBuffer); + } + + C_SkipList[i] = false; + } +} + +/** + * Removes color tags from the string. + * + * @param szMessage String. + * @return No return + */ +stock void C_RemoveTags(char[] szMessage, int maxlength) { + for (int i = 0; i < MAX_COLORS; i++) { + ReplaceString(szMessage, maxlength, C_Tag[i], "", false); + } + + ReplaceString(szMessage, maxlength, "{teamcolor}", "", false); +} + +/** + * Checks whether a color is allowed or not + * + * @param tag Color Tag. + * @return True when color is supported, otherwise false + */ +stock bool C_ColorAllowed(C_Colors color) { + if (!C_EventIsHooked) { + C_SetupProfile(); + + C_EventIsHooked = true; + } + + return C_Profile_Colors[color]; +} + +/** + * Replace the color with another color + * Handle with care! + * + * @param color color to replace. + * @param newColor color to replace with. + * @noreturn + */ +stock void C_ReplaceColor(C_Colors color, C_Colors newColor) { + if (!C_EventIsHooked) { + C_SetupProfile(); + + C_EventIsHooked = true; + } + + C_Profile_Colors[color] = C_Profile_Colors[newColor]; + C_Profile_TeamIndex[color] = C_Profile_TeamIndex[newColor]; + + C_TagReqSayText2[color] = C_TagReqSayText2[newColor]; + Format(C_TagCode[color], sizeof(C_TagCode[]), C_TagCode[newColor]); +} + +/** + * This function should only be used right in front of + * C_PrintToChatAll or C_PrintToChatAllEx and it tells + * to those funcions to skip specified client when printing + * message to all clients. After message is printed client will + * no more be skipped. + * + * @param client Client index + * @return No return + */ +stock void C_SkipNextClient(int client) { + if (client <= 0 || client > MaxClients) { + ThrowError("Invalid client index %d", client); + } + + C_SkipList[client] = true; +} + +/** + * Replaces color tags in a string with color codes + * + * @param szMessage String. + * @param maxlength Maximum length of the string buffer. + * @return Client index that can be used for SayText2 author index + * + * On error/Errors: If there is more then one team color is used an error will be thrown. + */ +stock int C_Format(char[] szMessage, int maxlength, int author = NO_INDEX) { + /* Hook event for auto profile setup on map start */ + if (!C_EventIsHooked) { + C_SetupProfile(); + HookEvent("server_spawn", C_Event_MapStart, EventHookMode_PostNoCopy); + + C_EventIsHooked = true; + } + + int iRandomPlayer = NO_INDEX; + + // On CS:GO set invisible precolor + if (GetEngineVersion() == Engine_CSGO) { + Format(szMessage, maxlength, " %s", szMessage); + } + + /* If author was specified replace {teamcolor} tag */ + if (author != NO_INDEX) { + if (C_Profile_SayText2) { + ReplaceString(szMessage, maxlength, "{teamcolor}", "\x03", false); + + iRandomPlayer = author; + } + /* If saytext2 is not supported by game replace {teamcolor} with green tag */ + else { + ReplaceString(szMessage, maxlength, "{teamcolor}", C_TagCode[Color_Green], false); + } + } + else { + ReplaceString(szMessage, maxlength, "{teamcolor}", "", false); + } + + /* For other color tags we need a loop */ + for (int i = 0; i < MAX_COLORS; i++) { + /* If tag not found - skip */ + if (StrContains(szMessage, C_Tag[i], false) == -1) { + continue; + } + + /* If tag is not supported by game replace it with green tag */ + else if (!C_Profile_Colors[i]) { + ReplaceString(szMessage, maxlength, C_Tag[i], C_TagCode[Color_Green], false); + } + + /* If tag doesn't need saytext2 simply replace */ + else if (!C_TagReqSayText2[i]) { + ReplaceString(szMessage, maxlength, C_Tag[i], C_TagCode[i], false); + } + + /* Tag needs saytext2 */ + else { + /* If saytext2 is not supported by game replace tag with green tag */ + if (!C_Profile_SayText2) { + ReplaceString(szMessage, maxlength, C_Tag[i], C_TagCode[Color_Green], false); + } + + /* Game supports saytext2 */ + else { + /* If random player for tag wasn't specified replace tag and find player */ + if (iRandomPlayer == NO_INDEX) { + /* Searching for valid client for tag */ + iRandomPlayer = C_FindRandomPlayerByTeam(C_Profile_TeamIndex[i]); + + /* If player not found replace tag with green color tag */ + if (iRandomPlayer == NO_PLAYER) { + ReplaceString(szMessage, maxlength, C_Tag[i], C_TagCode[Color_Green], false); + } + + /* If player was found simply replace */ + else { + ReplaceString(szMessage, maxlength, C_Tag[i], C_TagCode[i], false); + } + + } + /* If found another team color tag throw error */ + else { + //ReplaceString(szMessage, maxlength, C_Tag[i], ""); + ThrowError("Using two team colors in one message is not allowed"); + } + } + } + } + + return iRandomPlayer; +} + +/** + * Finds a random player with specified team + * + * @param color_team Client team. + * @return Client index or NO_PLAYER if no player found + */ +stock int C_FindRandomPlayerByTeam(int color_team) { + if (color_team == SERVER_INDEX) { + return 0; + } + + int[] players = new int[MaxClients]; + int count; + + for (int i = 1; i <= MaxClients; ++i) { + if (IsClientInGame(i) && GetClientTeam(i) == color_team) { + players[count++] = i; + } + } + + if (count) { + return players[GetRandomInt(0, count-1)]; + } + + return NO_PLAYER; +} + +/** + * Sends a SayText2 usermessage to a client + * + * @param szMessage Client index + * @param maxlength Author index + * @param szMessage Message + * @return No return. + */ +stock void C_SayText2(int client, int author, const char[] szMessage) { + Handle hBuffer = StartMessageOne("SayText2", client, USERMSG_RELIABLE|USERMSG_BLOCKHOOKS); + + if (GetFeatureStatus(FeatureType_Native, "GetUserMessageType") == FeatureStatus_Available && GetUserMessageType() == UM_Protobuf) { + Protobuf pb = UserMessageToProtobuf(hBuffer); + pb.SetInt("ent_idx", author); + pb.SetBool("chat", true); + pb.SetString("msg_name", szMessage); + pb.AddString("params", ""); + pb.AddString("params", ""); + pb.AddString("params", ""); + pb.AddString("params", ""); + } + else { + BfWrite bf = UserMessageToBfWrite(hBuffer); + bf.WriteByte(author); + bf.WriteByte(true); + bf.WriteString(szMessage); + } + + EndMessage(); +} + +/** + * Creates game color profile + * This function must be edited if you want to add more games support + * + * @return No return. + */ +stock void C_SetupProfile() { + EngineVersion engine = GetEngineVersion(); + + if (engine == Engine_CSS) { + C_Profile_Colors[Color_Lightgreen] = true; + C_Profile_Colors[Color_Red] = true; + C_Profile_Colors[Color_Blue] = true; + C_Profile_Colors[Color_Olive] = true; + C_Profile_TeamIndex[Color_Lightgreen] = SERVER_INDEX; + C_Profile_TeamIndex[Color_Red] = 2; + C_Profile_TeamIndex[Color_Blue] = 3; + C_Profile_SayText2 = true; + } + else if (engine == Engine_CSGO) { + C_Profile_Colors[Color_Red] = true; + C_Profile_Colors[Color_Blue] = true; + C_Profile_Colors[Color_Olive] = true; + C_Profile_Colors[Color_Darkred] = true; + C_Profile_Colors[Color_Lime] = true; + C_Profile_Colors[Color_Lightred] = true; + C_Profile_Colors[Color_Purple] = true; + C_Profile_Colors[Color_Grey] = true; + C_Profile_Colors[Color_Yellow] = true; + C_Profile_Colors[Color_Orange] = true; + C_Profile_Colors[Color_Bluegrey] = true; + C_Profile_Colors[Color_Lightblue] = true; + C_Profile_Colors[Color_Darkblue] = true; + C_Profile_Colors[Color_Grey2] = true; + C_Profile_Colors[Color_Orchid] = true; + C_Profile_Colors[Color_Lightred2] = true; + C_Profile_TeamIndex[Color_Red] = 2; + C_Profile_TeamIndex[Color_Blue] = 3; + C_Profile_SayText2 = true; + } + else if (engine == Engine_TF2) { + C_Profile_Colors[Color_Lightgreen] = true; + C_Profile_Colors[Color_Red] = true; + C_Profile_Colors[Color_Blue] = true; + C_Profile_Colors[Color_Olive] = true; + C_Profile_TeamIndex[Color_Lightgreen] = SERVER_INDEX; + C_Profile_TeamIndex[Color_Red] = 2; + C_Profile_TeamIndex[Color_Blue] = 3; + C_Profile_SayText2 = true; + } + else if (engine == Engine_Left4Dead || engine == Engine_Left4Dead2) { + C_Profile_Colors[Color_Lightgreen] = true; + C_Profile_Colors[Color_Red] = true; + C_Profile_Colors[Color_Blue] = true; + C_Profile_Colors[Color_Olive] = true; + C_Profile_TeamIndex[Color_Lightgreen] = SERVER_INDEX; + C_Profile_TeamIndex[Color_Red] = 3; + C_Profile_TeamIndex[Color_Blue] = 2; + C_Profile_SayText2 = true; + } + else if (engine == Engine_HL2DM) { + /* hl2mp profile is based on mp_teamplay convar */ + if (GetConVarBool(FindConVar("mp_teamplay"))) { + C_Profile_Colors[Color_Red] = true; + C_Profile_Colors[Color_Blue] = true; + C_Profile_Colors[Color_Olive] = true; + C_Profile_TeamIndex[Color_Red] = 3; + C_Profile_TeamIndex[Color_Blue] = 2; + C_Profile_SayText2 = true; + } + else { + C_Profile_SayText2 = false; + C_Profile_Colors[Color_Olive] = true; + } + } + else if (engine == Engine_DODS) { + C_Profile_Colors[Color_Olive] = true; + C_Profile_SayText2 = false; + } + /* Profile for other games */ + else { + if (GetUserMessageId("SayText2") == INVALID_MESSAGE_ID) { + C_Profile_SayText2 = false; + } + else { + C_Profile_Colors[Color_Red] = true; + C_Profile_Colors[Color_Blue] = true; + C_Profile_TeamIndex[Color_Red] = 2; + C_Profile_TeamIndex[Color_Blue] = 3; + C_Profile_SayText2 = true; + } + } +} + +public void C_Event_MapStart(Event event, const char[] name, bool dontBroadcast) { + C_SetupProfile(); + + for (int i = 1; i <= MaxClients; ++i) { + C_SkipList[i] = false; + } +} + +/** + * Displays usage of an admin command to users depending on the + * setting of the sm_show_activity cvar. + * + * This version does not display a message to the originating client + * if used from chat triggers or menus. If manual replies are used + * for these cases, then this function will suffice. Otherwise, + * C_ShowActivity2() is slightly more useful. + * Supports color tags. + * + * @param client Client index doing the action, or 0 for server. + * @param format Formatting rules. + * @param ... Variable number of format parameters. + * @noreturn + * @error + */ +stock int C_ShowActivity(int client, const char[] format, any ...) { + if (sm_show_activity == null) { + sm_show_activity = FindConVar("sm_show_activity"); + } + + char tag[] = "[SM] "; + + char szBuffer[MAX_MESSAGE_LENGTH]; + //char szCMessage[MAX_MESSAGE_LENGTH]; + int value = sm_show_activity.IntValue; + ReplySource replyto = GetCmdReplySource(); + + char name[MAX_NAME_LENGTH] = "Console"; + char sign[MAX_NAME_LENGTH] = "ADMIN"; + bool display_in_chat = false; + if (client != 0) { + if (client < 0 || client > MaxClients || !IsClientConnected(client)) { + ThrowError("Client index %d is invalid", client); + } + + GetClientName(client, name, sizeof(name)); + AdminId id = GetUserAdmin(client); + if (id == INVALID_ADMIN_ID || !GetAdminFlag(id, Admin_Generic, Access_Effective)) { + sign = "PLAYER"; + } + + /* Display the message to the client? */ + if (replyto == SM_REPLY_TO_CONSOLE) { + SetGlobalTransTarget(client); + VFormat(szBuffer, sizeof(szBuffer), format, 3); + + C_RemoveTags(szBuffer, sizeof(szBuffer)); + PrintToConsole(client, "%s%s\n", tag, szBuffer); + display_in_chat = true; + } + } + else { + SetGlobalTransTarget(LANG_SERVER); + VFormat(szBuffer, sizeof(szBuffer), format, 3); + + C_RemoveTags(szBuffer, sizeof(szBuffer)); + PrintToServer("%s%s\n", tag, szBuffer); + } + + if (!value) { + return 1; + } + + for (int i = 1; i <= MaxClients; ++i) { + if (!IsClientInGame(i) || IsFakeClient(i) || (display_in_chat && i == client)) { + continue; + } + + AdminId id = GetUserAdmin(i); + SetGlobalTransTarget(i); + if (id == INVALID_ADMIN_ID || !GetAdminFlag(id, Admin_Generic, Access_Effective)) { + /* Treat this as a normal user. */ + if ((value & 1) | (value & 2)) { + char newsign[MAX_NAME_LENGTH]; + + if ((value & 2) || (i == client)) { + newsign = name; + } + else { + newsign = sign; + } + + VFormat(szBuffer, sizeof(szBuffer), format, 3); + + C_PrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer); + } + } + else { + /* Treat this as an admin user */ + bool is_root = GetAdminFlag(id, Admin_Root, Access_Effective); + if ((value & 4) || (value & 8) || ((value & 16) && is_root)) { + char newsign[MAX_NAME_LENGTH]; + + if ((value & 8) || ((value & 16) && is_root) || (i == client)) { + newsign = name; + } + else { + newsign = sign; + } + + VFormat(szBuffer, sizeof(szBuffer), format, 3); + + C_PrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer); + } + } + } + + return 1; +} + +/** + * Same as C_ShowActivity(), except the tag parameter is used instead of "[SM] " (note that you must supply any spacing). + * Supports color tags. + * + * @param client Client index doing the action, or 0 for server. + * @param tags Tag to display with. + * @param format Formatting rules. + * @param ... Variable number of format parameters. + * @noreturn + * @error + */ +stock int C_ShowActivityEx(int client, const char[] tag, const char[] format, any ...) { + if (sm_show_activity == null) { + sm_show_activity = FindConVar("sm_show_activity"); + } + + char szBuffer[MAX_MESSAGE_LENGTH]; + //char szCMessage[MAX_MESSAGE_LENGTH]; + int value = sm_show_activity.IntValue; + ReplySource replyto = GetCmdReplySource(); + + char name[MAX_NAME_LENGTH] = "Console"; + char sign[MAX_NAME_LENGTH] = "ADMIN"; + bool display_in_chat = false; + if (client != 0) { + if (client < 0 || client > MaxClients || !IsClientConnected(client)) { + ThrowError("Client index %d is invalid", client); + } + + GetClientName(client, name, sizeof(name)); + AdminId id = GetUserAdmin(client); + if (id == INVALID_ADMIN_ID || !GetAdminFlag(id, Admin_Generic, Access_Effective)) { + sign = "PLAYER"; + } + + /* Display the message to the client? */ + if (replyto == SM_REPLY_TO_CONSOLE) { + SetGlobalTransTarget(client); + VFormat(szBuffer, sizeof(szBuffer), format, 4); + + C_RemoveTags(szBuffer, sizeof(szBuffer)); + PrintToConsole(client, "%s%s\n", tag, szBuffer); + display_in_chat = true; + } + } + else { + SetGlobalTransTarget(LANG_SERVER); + VFormat(szBuffer, sizeof(szBuffer), format, 4); + + C_RemoveTags(szBuffer, sizeof(szBuffer)); + PrintToServer("%s%s\n", tag, szBuffer); + } + + if (!value) { + return 1; + } + + for (int i = 1; i <= MaxClients; ++i) { + if (!IsClientInGame(i) || IsFakeClient(i) || (display_in_chat && i == client)) { + continue; + } + + AdminId id = GetUserAdmin(i); + SetGlobalTransTarget(i); + if (id == INVALID_ADMIN_ID || !GetAdminFlag(id, Admin_Generic, Access_Effective)) { + /* Treat this as a normal user. */ + if ((value & 1) | (value & 2)) { + char newsign[MAX_NAME_LENGTH]; + + if ((value & 2) || (i == client)) { + newsign = name; + } + else { + newsign = sign; + } + + VFormat(szBuffer, sizeof(szBuffer), format, 4); + + C_PrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer); + } + } + else { + /* Treat this as an admin user */ + bool is_root = GetAdminFlag(id, Admin_Root, Access_Effective); + if ((value & 4) || (value & 8) || ((value & 16) && is_root)) { + char newsign[MAX_NAME_LENGTH]; + + if ((value & 8) || ((value & 16) && is_root) || (i == client)) { + newsign = name; + } + else { + newsign = sign; + } + + VFormat(szBuffer, sizeof(szBuffer), format, 4); + + C_PrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer); + } + } + } + + return 1; +} + +/** + * Displays usage of an admin command to users depending on the setting of the sm_show_activity cvar. + * All users receive a message in their chat text, except for the originating client, + * who receives the message based on the current ReplySource. + * Supports color tags. + * + * @param client Client index doing the action, or 0 for server. + * @param tags Tag to prepend to the message. + * @param format Formatting rules. + * @param ... Variable number of format parameters. + * @noreturn + * @error + */ +stock int C_ShowActivity2(int client, const char[] tag, const char[] format, any ...) { + if (sm_show_activity == null) { + sm_show_activity = FindConVar("sm_show_activity"); + } + + char szBuffer[MAX_MESSAGE_LENGTH]; + //char szCMessage[MAX_MESSAGE_LENGTH]; + int value = sm_show_activity.IntValue; + // ReplySource replyto = GetCmdReplySource(); + + char name[MAX_NAME_LENGTH] = "Console"; + char sign[MAX_NAME_LENGTH] = "ADMIN"; + if (client != 0) { + if (client < 0 || client > MaxClients || !IsClientConnected(client)) { + ThrowError("Client index %d is invalid", client); + } + + GetClientName(client, name, sizeof(name)); + AdminId id = GetUserAdmin(client); + if (id == INVALID_ADMIN_ID || !GetAdminFlag(id, Admin_Generic, Access_Effective)) { + sign = "PLAYER"; + } + + SetGlobalTransTarget(client); + VFormat(szBuffer, sizeof(szBuffer), format, 4); + + /* We don't display directly to the console because the chat text + * simply gets added to the console, so we don't want it to print + * twice. + */ + C_PrintToChatEx(client, client, "%s%s", tag, szBuffer); + } + else { + SetGlobalTransTarget(LANG_SERVER); + VFormat(szBuffer, sizeof(szBuffer), format, 4); + + C_RemoveTags(szBuffer, sizeof(szBuffer)); + PrintToServer("%s%s\n", tag, szBuffer); + } + + if (!value) { + return 1; + } + + for (int i = 1; i <= MaxClients; ++i) { + if (!IsClientInGame(i) || IsFakeClient(i) || i == client) { + continue; + } + + AdminId id = GetUserAdmin(i); + SetGlobalTransTarget(i); + if (id == INVALID_ADMIN_ID + || !GetAdminFlag(id, Admin_Generic, Access_Effective)) { + /* Treat this as a normal user. */ + if ((value & 1) | (value & 2)) { + char newsign[MAX_NAME_LENGTH]; + + if ((value & 2)) { + newsign = name; + } + else { + newsign = sign; + } + + VFormat(szBuffer, sizeof(szBuffer), format, 4); + + C_PrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer); + } + } + else { + /* Treat this as an admin user */ + bool is_root = GetAdminFlag(id, Admin_Root, Access_Effective); + if ((value & 4) || (value & 8) || ((value & 16) && is_root)) { + char newsign[MAX_NAME_LENGTH]; + + if ((value & 8) || ((value & 16) && is_root)) { + newsign = name; + } + else { + newsign = sign; + } + + VFormat(szBuffer, sizeof(szBuffer), format, 4); + + C_PrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer); + } + } + } + + return 1; +} diff --git a/addons/sourcemod/scripting/include/multicolors/morecolors.inc b/addons/sourcemod/scripting/include/multicolors/morecolors.inc new file mode 100644 index 0000000..2020b60 --- /dev/null +++ b/addons/sourcemod/scripting/include/multicolors/morecolors.inc @@ -0,0 +1,992 @@ +// MOAR COLORS +// By Dr. McKay +// Inspired by: https://forums.alliedmods.net/showthread.php?t=96831 + +#if defined _more_colors_included + #endinput +#endif +#define _more_colors_included + +#pragma newdecls optional +#include + +#define MORE_COLORS_VERSION "2.0.0-MC" +#define MC_MAX_MESSAGE_LENGTH 256 +#define MAX_BUFFER_LENGTH (MC_MAX_MESSAGE_LENGTH * 4) + +#define MCOLOR_RED 0xFF4040 +#define MCOLOR_BLUE 0x99CCFF +#define MCOLOR_GRAY 0xCCCCCC +#define MCOLOR_GREEN 0x3EFF3E + +#define MC_GAME_DODS 0 + +bool MC_SkipList[MAXPLAYERS+1]; +StringMap MC_Trie; +int MC_TeamColors[][] = {{0xCCCCCC, 0x4D7942, 0xFF4040}}; // Multi-dimensional array for games that don't support SayText2. First index is the game index (as defined by the GAME_ defines), second index is team. 0 = spectator, 1 = team1, 2 = team2 + +static ConVar sm_show_activity; + +/** + * Prints a message to a specific client in the chat area. + * Supports color tags. + * + * @param client Client index. + * @param message Message (formatting rules). + * + * On error/Errors: If the client is not connected an error will be thrown. + */ +stock void MC_PrintToChat(int client, const char[] message, any ...) { + MC_CheckTrie(); + + if (client <= 0 || client > MaxClients) { + ThrowError("Invalid client index %i", client); + } + + if (!IsClientInGame(client)) { + ThrowError("Client %i is not in game", client); + } + + char buffer[MAX_BUFFER_LENGTH]; + char buffer2[MAX_BUFFER_LENGTH]; + + SetGlobalTransTarget(client); + Format(buffer, sizeof(buffer), "\x01%s", message); + VFormat(buffer2, sizeof(buffer2), buffer, 3); + + MC_ReplaceColorCodes(buffer2); + MC_SendMessage(client, buffer2); +} + +/** + * Prints a message to all clients in the chat area. + * Supports color tags. + * + * @param client Client index. + * @param message Message (formatting rules). + */ +stock void MC_PrintToChatAll(const char[] message, any ...) { + MC_CheckTrie(); + + char buffer[MAX_BUFFER_LENGTH], buffer2[MAX_BUFFER_LENGTH]; + + for (int i = 1; i <= MaxClients; ++i) { + if (!IsClientInGame(i) || MC_SkipList[i]) { + MC_SkipList[i] = false; + continue; + } + + SetGlobalTransTarget(i); + Format(buffer, sizeof(buffer), "\x01%s", message); + VFormat(buffer2, sizeof(buffer2), buffer, 2); + + MC_ReplaceColorCodes(buffer2); + MC_SendMessage(i, buffer2); + } +} + +/** + * Prints a message to a specific client in the chat area. + * Supports color tags and teamcolor tag. + * + * @param client Client index. + * @param author Author index whose color will be used for teamcolor tag. + * @param message Message (formatting rules). + * + * On error/Errors: If the client or author are not connected an error will be thrown + */ +stock void MC_PrintToChatEx(int client, int author, const char[] message, any ...) { + MC_CheckTrie(); + + if (client <= 0 || client > MaxClients) { + ThrowError("Invalid client index %i", client); + } + + if (!IsClientInGame(client)) { + ThrowError("Client %i is not in game", client); + } + + if (author <= 0 || author > MaxClients) { + ThrowError("Invalid client index %i", author); + } + + if (!IsClientInGame(author)) { + ThrowError("Client %i is not in game", author); + } + + char buffer[MAX_BUFFER_LENGTH], buffer2[MAX_BUFFER_LENGTH]; + SetGlobalTransTarget(client); + Format(buffer, sizeof(buffer), "\x01%s", message); + VFormat(buffer2, sizeof(buffer2), buffer, 4); + MC_ReplaceColorCodes(buffer2, author); + MC_SendMessage(client, buffer2, author); +} + +/** + * Prints a message to all clients in the chat area. + * Supports color tags and teamcolor tag. + * + * @param author Author index whose color will be used for teamcolor tag. + * @param message Message (formatting rules). + * + * On error/Errors: If the author is not connected an error will be thrown. + */ +stock void MC_PrintToChatAllEx(int author, const char[] message, any ...) { + MC_CheckTrie(); + + if (author <= 0 || author > MaxClients) { + ThrowError("Invalid client index %i", author); + } + + if (!IsClientInGame(author)) { + ThrowError("Client %i is not in game", author); + } + + char buffer[MAX_BUFFER_LENGTH]; + char buffer2[MAX_BUFFER_LENGTH]; + + for (int i = 1; i <= MaxClients; ++i) { + if (!IsClientInGame(i) || MC_SkipList[i]) { + MC_SkipList[i] = false; + continue; + } + + SetGlobalTransTarget(i); + Format(buffer, sizeof(buffer), "\x01%s", message); + VFormat(buffer2, sizeof(buffer2), buffer, 3); + + MC_ReplaceColorCodes(buffer2, author); + MC_SendMessage(i, buffer2, author); + } +} + +/** + * Sends a SayText2 usermessage + * + * @param client Client to send usermessage to + * @param message Message to send + */ +stock void MC_SendMessage(int client, const char[] message, int author = 0) { + if (author == 0) { + author = client; + } + + char buffer[MC_MAX_MESSAGE_LENGTH]; + strcopy(buffer, sizeof(buffer), message); + + UserMsg index = GetUserMessageId("SayText2"); + if (index == INVALID_MESSAGE_ID) { + if (GetEngineVersion() == Engine_DODS) { + int team = GetClientTeam(author); + if (team == 0) { + ReplaceString(buffer, sizeof(buffer), "\x03", "\x04", false); // Unassigned gets green + } + else { + char temp[16]; + Format(temp, sizeof(temp), "\x07%06X", MC_TeamColors[MC_GAME_DODS][team - 1]); + ReplaceString(buffer, sizeof(buffer), "\x03", temp, false); + } + } + + PrintToChat(client, "%s", buffer); + return; + } + + Handle buf = StartMessageOne("SayText2", client, USERMSG_RELIABLE|USERMSG_BLOCKHOOKS); + if (GetFeatureStatus(FeatureType_Native, "GetUserMessageType") == FeatureStatus_Available && GetUserMessageType() == UM_Protobuf) { + Protobuf pb = UserMessageToProtobuf(buf); + pb.SetInt("ent_idx", author); + pb.SetBool("chat", true); + pb.SetString("msg_name", buffer); + pb.AddString("params", ""); + pb.AddString("params", ""); + pb.AddString("params", ""); + pb.AddString("params", ""); + } + else { + BfWrite bf = UserMessageToBfWrite(buf); + bf.WriteByte(author); // Message author + bf.WriteByte(true); // Chat message + bf.WriteString(buffer); // Message text + } + + EndMessage(); +} + +/** + * This function should only be used right in front of + * MC_PrintToChatAll or MC_PrintToChatAllEx. It causes those functions + * to skip the specified client when printing the message. + * After printing the message, the client will no longer be skipped. + * + * @param client Client index + */ +stock void MC_SkipNextClient(int client) { + if (client <= 0 || client > MaxClients) { + ThrowError("Invalid client index %i", client); + } + + MC_SkipList[client] = true; +} + +/** + * Checks if the colors trie is initialized and initializes it if it's not (used internally) + * + * @return No return + */ +stock void MC_CheckTrie() { + if (MC_Trie == null) { + MC_Trie = MC_InitColorTrie(); + } +} + +/** + * Replaces color tags in a string with color codes (used internally by MC_PrintToChat, MC_PrintToChatAll, MC_PrintToChatEx, and MC_PrintToChatAllEx + * + * @param buffer String. + * @param author Optional client index to use for {teamcolor} tags, or 0 for none + * @param removeTags Optional boolean value to determine whether we're replacing tags with colors, or just removing tags, used by MC_RemoveTags + * @param maxlen Optional value for max buffer length, used by MC_RemoveTags + * + * On error/Errors: If the client index passed for author is invalid or not in game. + */ +stock void MC_ReplaceColorCodes(char[] buffer, int author = 0, bool removeTags = false, int maxlen = MAX_BUFFER_LENGTH) { + MC_CheckTrie(); + if (!removeTags) { + ReplaceString(buffer, maxlen, "{default}", "\x01", false); + } + else { + ReplaceString(buffer, maxlen, "{default}", "", false); + ReplaceString(buffer, maxlen, "{teamcolor}", "", false); + } + + if (author != 0 && !removeTags) { + if (author < 0 || author > MaxClients) { + ThrowError("Invalid client index %i", author); + } + + if (!IsClientInGame(author)) { + ThrowError("Client %i is not in game", author); + } + + ReplaceString(buffer, maxlen, "{teamcolor}", "\x03", false); + } + + int cursor = 0; + int value; + char tag[32], buff[32]; + char[] output = new char[maxlen]; + + strcopy(output, maxlen, buffer); + // Since the string's size is going to be changing, output will hold the replaced string and we'll search buffer + + Regex regex = new Regex("{[#a-zA-Z0-9]+}"); + for (int i = 0; i < 1000; i++) { // The RegEx extension is quite flaky, so we have to loop here :/. This loop is supposed to be infinite and broken by return, but conditions have been added to be safe. + if (regex.Match(buffer[cursor]) < 1) { + delete regex; + strcopy(buffer, maxlen, output); + return; + } + + regex.GetSubString(0, tag, sizeof(tag)); + MC_StrToLower(tag); + cursor = StrContains(buffer[cursor], tag, false) + cursor + 1; + strcopy(buff, sizeof(buff), tag); + ReplaceString(buff, sizeof(buff), "{", ""); + ReplaceString(buff, sizeof(buff), "}", ""); + + if (buff[0] == '#') { + if (strlen(buff) == 7) { + Format(buff, sizeof(buff), "\x07%s", buff[1]); + } + else if (strlen(buff) == 9) { + Format(buff, sizeof(buff), "\x08%s", buff[1]); + } + else { + continue; + } + + if (removeTags) { + ReplaceString(output, maxlen, tag, "", false); + } + else { + ReplaceString(output, maxlen, tag, buff, false); + } + } + else if (!MC_Trie.GetValue(buff, value)) { + continue; + } + + if (removeTags) { + ReplaceString(output, maxlen, tag, "", false); + } + else { + Format(buff, sizeof(buff), "\x07%06X", value); + ReplaceString(output, maxlen, tag, buff, false); + } + } + LogError("[MORE COLORS] Infinite loop broken."); +} + +/** + * Gets a part of a string + * + * @param input String to get the part from + * @param output Buffer to write to + * @param maxlen Max length of output buffer + * @param start Position to start at + * @param numChars Number of characters to return, or 0 for the end of the string + */ +stock void CSubString(const char[] input, char[] output, int maxlen, int start, int numChars = 0) { + int i = 0; + for (;;) { + if (i == maxlen - 1 || i >= numChars || input[start + i] == '\0') { + output[i] = '\0'; + return; + } + + output[i] = input[start + i]; + i++; + } +} + +/** + * Converts a string to lowercase + * + * @param buffer String to convert + */ +stock void MC_StrToLower(char[] buffer) { + int len = strlen(buffer); + for (int i = 0; i < len; i++) { + buffer[i] = CharToLower(buffer[i]); + } +} + +/** + * Adds a color to the colors trie + * + * @param name Color name, without braces + * @param color Hexadecimal representation of the color (0xRRGGBB) + * @return True if color was added successfully, false if a color already exists with that name + */ +stock bool MC_AddColor(const char[] name, int color) { + MC_CheckTrie(); + + int value; + + if (MC_Trie.GetValue(name, value)) { + return false; + } + + char newName[64]; + strcopy(newName, sizeof(newName), name); + + MC_StrToLower(newName); + MC_Trie.SetValue(newName, color); + return true; +} + +/** + * Removes color tags from a message + * + * @param message Message to remove tags from + * @param maxlen Maximum buffer length + */ +stock void MC_RemoveTags(char[] message, int maxlen) { + MC_ReplaceColorCodes(message, 0, true, maxlen); +} + +/** + * Replies to a command with colors + * + * @param client Client to reply to + * @param message Message (formatting rules) + */ +stock void MC_ReplyToCommand(int client, const char[] message, any ...) { + char buffer[MAX_BUFFER_LENGTH]; + SetGlobalTransTarget(client); + VFormat(buffer, sizeof(buffer), message, 3); + + if (client == 0) { + MC_RemoveTags(buffer, sizeof(buffer)); + PrintToServer("%s", buffer); + } + + if (GetCmdReplySource() == SM_REPLY_TO_CONSOLE) { + MC_RemoveTags(buffer, sizeof(buffer)); + PrintToConsole(client, "%s", buffer); + } + else { + MC_PrintToChat(client, "%s", buffer); + } +} + +/** + * Replies to a command with colors + * + * @param client Client to reply to + * @param author Client to use for {teamcolor} + * @param message Message (formatting rules) + */ +stock void MC_ReplyToCommandEx(int client, int author, const char[] message, any ...) { + char buffer[MAX_BUFFER_LENGTH]; + SetGlobalTransTarget(client); + VFormat(buffer, sizeof(buffer), message, 4); + + if (client == 0) { + MC_RemoveTags(buffer, sizeof(buffer)); + PrintToServer("%s", buffer); + } + + if (GetCmdReplySource() == SM_REPLY_TO_CONSOLE) { + MC_RemoveTags(buffer, sizeof(buffer)); + PrintToConsole(client, "%s", buffer); + } + else { + MC_PrintToChatEx(client, author, "%s", buffer); + } +} + +/** + * Displays usage of an admin command to users depending on the + * setting of the sm_show_activity cvar. + * + * This version does not display a message to the originating client + * if used from chat triggers or menus. If manual replies are used + * for these cases, then this function will suffice. Otherwise, + * MC_ShowActivity2() is slightly more useful. + * Supports color tags. + * + * @param client Client index doing the action, or 0 for server. + * @param format Formatting rules. + * @param ... Variable number of format parameters. + * @error + */ +stock int MC_ShowActivity(int client, const char[] format, any ...) { + if (sm_show_activity == null) { + sm_show_activity = FindConVar("sm_show_activity"); + } + + char tag[] = "[SM] "; + + char szBuffer[MC_MAX_MESSAGE_LENGTH]; + //char szCMessage[MC_MAX_MESSAGE_LENGTH]; + int value = sm_show_activity.IntValue; + ReplySource replyto = GetCmdReplySource(); + + char name[MAX_NAME_LENGTH] = "Console"; + char sign[MAX_NAME_LENGTH] = "ADMIN"; + bool display_in_chat = false; + if (client != 0) { + if (client < 0 || client > MaxClients || !IsClientConnected(client)) + ThrowError("Client index %d is invalid", client); + + GetClientName(client, name, sizeof(name)); + AdminId id = GetUserAdmin(client); + if (id == INVALID_ADMIN_ID || !id.HasFlag(Admin_Generic, Access_Effective)) { + sign = "PLAYER"; + } + + /* Display the message to the client? */ + if (replyto == SM_REPLY_TO_CONSOLE) { + SetGlobalTransTarget(client); + VFormat(szBuffer, sizeof(szBuffer), format, 3); + + MC_RemoveTags(szBuffer, sizeof(szBuffer)); + PrintToConsole(client, "%s%s\n", tag, szBuffer); + display_in_chat = true; + } + } + else { + SetGlobalTransTarget(LANG_SERVER); + VFormat(szBuffer, sizeof(szBuffer), format, 3); + + MC_RemoveTags(szBuffer, sizeof(szBuffer)); + PrintToServer("%s%s\n", tag, szBuffer); + } + + if (!value) { + return 1; + } + + for (int i = 1; i <= MaxClients; ++i) { + if (!IsClientInGame(i) || IsFakeClient(i) || (display_in_chat && i == client)) { + continue; + } + + AdminId id = GetUserAdmin(i); + SetGlobalTransTarget(i); + if (id == INVALID_ADMIN_ID || !id.HasFlag(Admin_Generic, Access_Effective)) { + /* Treat this as a normal user. */ + if ((value & 1) | (value & 2)) { + char newsign[MAX_NAME_LENGTH]; + + if ((value & 2) || (i == client)) { + newsign = name; + } + else { + newsign = sign; + } + + VFormat(szBuffer, sizeof(szBuffer), format, 3); + + MC_PrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer); + } + } + else { + /* Treat this as an admin user */ + bool is_root = id.HasFlag(Admin_Root, Access_Effective); + if ((value & 4) || (value & 8) || ((value & 16) && is_root)) { + char newsign[MAX_NAME_LENGTH]; + + if ((value & 8) || ((value & 16) && is_root) || (i == client)) { + newsign = name; + } + else { + newsign = sign; + } + + VFormat(szBuffer, sizeof(szBuffer), format, 3); + + MC_PrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer); + } + } + } + + return 1; +} + +/** + * Same as MC_ShowActivity(), except the tag parameter is used instead of "[SM] " (note that you must supply any spacing). + * Supports color tags. + * + * @param client Client index doing the action, or 0 for server. + * @param tags Tag to display with. + * @param format Formatting rules. + * @param ... Variable number of format parameters. + * @error + */ +stock int MC_ShowActivityEx(int client, const char[] tag, const char[] format, any ...) { + if (sm_show_activity == null) { + sm_show_activity = FindConVar("sm_show_activity"); + } + + char szBuffer[MC_MAX_MESSAGE_LENGTH]; + //char szCMessage[MC_MAX_MESSAGE_LENGTH]; + int value = sm_show_activity.IntValue; + ReplySource replyto = GetCmdReplySource(); + + char name[MAX_NAME_LENGTH] = "Console"; + char sign[MAX_NAME_LENGTH] = "ADMIN"; + bool display_in_chat = false; + if (client != 0) { + if (client < 0 || client > MaxClients || !IsClientConnected(client)) { + ThrowError("Client index %d is invalid", client); + } + + GetClientName(client, name, sizeof(name)); + AdminId id = GetUserAdmin(client); + if (id == INVALID_ADMIN_ID || !id.HasFlag(Admin_Generic, Access_Effective)) { + sign = "PLAYER"; + } + + /* Display the message to the client? */ + if (replyto == SM_REPLY_TO_CONSOLE) { + SetGlobalTransTarget(client); + VFormat(szBuffer, sizeof(szBuffer), format, 4); + + MC_RemoveTags(szBuffer, sizeof(szBuffer)); + PrintToConsole(client, "%s%s\n", tag, szBuffer); + display_in_chat = true; + } + } + else { + SetGlobalTransTarget(LANG_SERVER); + VFormat(szBuffer, sizeof(szBuffer), format, 4); + + MC_RemoveTags(szBuffer, sizeof(szBuffer)); + PrintToServer("%s%s\n", tag, szBuffer); + } + + if (!value) { + return 1; + } + + for (int i = 1; i <= MaxClients; ++i) { + if (!IsClientInGame(i) || IsFakeClient(i) || (display_in_chat && i == client)) { + continue; + } + + AdminId id = GetUserAdmin(i); + SetGlobalTransTarget(i); + if (id == INVALID_ADMIN_ID || !id.HasFlag(Admin_Generic, Access_Effective)) { + /* Treat this as a normal user. */ + if ((value & 1) | (value & 2)) { + char newsign[MAX_NAME_LENGTH]; + + if ((value & 2) || (i == client)) { + newsign = name; + } + else { + newsign = sign; + } + + VFormat(szBuffer, sizeof(szBuffer), format, 4); + + MC_PrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer); + } + } + else { + /* Treat this as an admin user */ + bool is_root = id.HasFlag(Admin_Root, Access_Effective); + if ((value & 4) || (value & 8) || ((value & 16) && is_root)) { + char newsign[MAX_NAME_LENGTH]; + + if ((value & 8) || ((value & 16) && is_root) || (i == client)) { + newsign = name; + } + else { + newsign = sign; + } + + VFormat(szBuffer, sizeof(szBuffer), format, 4); + + MC_PrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer); + } + } + } + + return 1; +} + +/** + * Displays usage of an admin command to users depending on the setting of the sm_show_activity cvar. + * All users receive a message in their chat text, except for the originating client, + * who receives the message based on the current ReplySource. + * Supports color tags. + * + * @param client Client index doing the action, or 0 for server. + * @param tags Tag to prepend to the message. + * @param format Formatting rules. + * @param ... Variable number of format parameters. + * @error + */ +stock int MC_ShowActivity2(int client, const char[] tag, const char[] format, any ...) { + if (sm_show_activity == null) { + sm_show_activity = FindConVar("sm_show_activity"); + } + + char szBuffer[MC_MAX_MESSAGE_LENGTH]; + //char szCMessage[MC_MAX_MESSAGE_LENGTH]; + int value = sm_show_activity.IntValue; + // ReplySource replyto = GetCmdReplySource(); + + char name[MAX_NAME_LENGTH] = "Console"; + char sign[MAX_NAME_LENGTH] = "ADMIN"; + + if (client != 0) { + if (client < 0 || client > MaxClients || !IsClientConnected(client)) { + ThrowError("Client index %d is invalid", client); + } + + GetClientName(client, name, sizeof(name)); + + AdminId id = GetUserAdmin(client); + if (id == INVALID_ADMIN_ID || !id.HasFlag(Admin_Generic, Access_Effective)) { + sign = "PLAYER"; + } + + SetGlobalTransTarget(client); + VFormat(szBuffer, sizeof(szBuffer), format, 4); + + /* We don't display directly to the console because the chat text + * simply gets added to the console, so we don't want it to print + * twice. + */ + MC_PrintToChatEx(client, client, "%s%s", tag, szBuffer); + } + else { + SetGlobalTransTarget(LANG_SERVER); + VFormat(szBuffer, sizeof(szBuffer), format, 4); + + MC_RemoveTags(szBuffer, sizeof(szBuffer)); + PrintToServer("%s%s\n", tag, szBuffer); + } + + if (!value) { + return 1; + } + + for (int i = 1; i <= MaxClients; ++i) { + if (!IsClientInGame(i) || IsFakeClient(i) || i == client) { + continue; + } + + AdminId id = GetUserAdmin(i); + SetGlobalTransTarget(i); + if (id == INVALID_ADMIN_ID || !id.HasFlag(Admin_Generic, Access_Effective)) { + /* Treat this as a normal user. */ + if ((value & 1) | (value & 2)) { + char newsign[MAX_NAME_LENGTH]; + + if ((value & 2)) { + newsign = name; + } + else { + newsign = sign; + } + + VFormat(szBuffer, sizeof(szBuffer), format, 4); + + MC_PrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer); + } + } + else { + /* Treat this as an admin user */ + bool is_root = id.HasFlag(Admin_Root, Access_Effective); + if ((value & 4) || (value & 8) || ((value & 16) && is_root)) { + char newsign[MAX_NAME_LENGTH]; + + + if ((value & 8) || ((value & 16) && is_root)) { + newsign = name; + } + else { + newsign = sign; + } + + VFormat(szBuffer, sizeof(szBuffer), format, 4); + + MC_PrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer); + } + } + } + + return 1; +} + +/** + * Determines whether a color name exists + * + * @param color The color name to check + * @return True if the color exists, false otherwise + */ +stock bool CColorExists(const char[] color) { + MC_CheckTrie(); + int temp; + return MC_Trie.GetValue(color, temp); +} + +/** + * Returns the hexadecimal representation of a client's team color (will NOT initialize the trie) + * + * @param client Client to get the team color for + * @return Client's team color in hexadecimal, or green if unknown + * On error/Errors: If the client index passed is invalid or not in game. + */ +stock int CGetTeamColor(int client) { + if (client <= 0 || client > MaxClients) { + ThrowError("Invalid client index %i", client); + } + + if (!IsClientInGame(client)) { + ThrowError("Client %i is not in game", client); + } + + int value; + switch(GetClientTeam(client)) { + case 1: { + value = MCOLOR_GRAY; + } + case 2: { + value = MCOLOR_RED; + } + case 3: { + value = MCOLOR_BLUE; + } + default: { + value = MCOLOR_GREEN; + } + } + + return value; +} + +stock StringMap MC_InitColorTrie() { + StringMap hTrie = new StringMap(); + hTrie.SetValue("aliceblue", 0xF0F8FF); + hTrie.SetValue("allies", 0x4D7942); // same as Allies team in DoD:S + hTrie.SetValue("ancient", 0xEB4B4B); // same as Ancient item rarity in Dota 2 + hTrie.SetValue("antiquewhite", 0xFAEBD7); + hTrie.SetValue("aqua", 0x00FFFF); + hTrie.SetValue("aquamarine", 0x7FFFD4); + hTrie.SetValue("arcana", 0xADE55C); // same as Arcana item rarity in Dota 2 + hTrie.SetValue("axis", 0xFF4040); // same as Axis team in DoD:S + hTrie.SetValue("azure", 0x007FFF); + hTrie.SetValue("beige", 0xF5F5DC); + hTrie.SetValue("bisque", 0xFFE4C4); + hTrie.SetValue("black", 0x000000); + hTrie.SetValue("blanchedalmond", 0xFFEBCD); + hTrie.SetValue("blue", 0x99CCFF); // same as BLU/Counter-Terrorist team color + hTrie.SetValue("blueviolet", 0x8A2BE2); + hTrie.SetValue("brown", 0xA52A2A); + hTrie.SetValue("burlywood", 0xDEB887); + hTrie.SetValue("cadetblue", 0x5F9EA0); + hTrie.SetValue("chartreuse", 0x7FFF00); + hTrie.SetValue("chocolate", 0xD2691E); + hTrie.SetValue("collectors", 0xAA0000); // same as Collector's item quality in TF2 + hTrie.SetValue("common", 0xB0C3D9); // same as Common item rarity in Dota 2 + hTrie.SetValue("community", 0x70B04A); // same as Community item quality in TF2 + hTrie.SetValue("coral", 0xFF7F50); + hTrie.SetValue("cornflowerblue", 0x6495ED); + hTrie.SetValue("cornsilk", 0xFFF8DC); + hTrie.SetValue("corrupted", 0xA32C2E); // same as Corrupted item quality in Dota 2 + hTrie.SetValue("crimson", 0xDC143C); + hTrie.SetValue("cyan", 0x00FFFF); + hTrie.SetValue("darkblue", 0x00008B); + hTrie.SetValue("darkcyan", 0x008B8B); + hTrie.SetValue("darkgoldenrod", 0xB8860B); + hTrie.SetValue("darkgray", 0xA9A9A9); + hTrie.SetValue("darkgrey", 0xA9A9A9); + hTrie.SetValue("darkgreen", 0x006400); + hTrie.SetValue("darkkhaki", 0xBDB76B); + hTrie.SetValue("darkmagenta", 0x8B008B); + hTrie.SetValue("darkolivegreen", 0x556B2F); + hTrie.SetValue("darkorange", 0xFF8C00); + hTrie.SetValue("darkorchid", 0x9932CC); + hTrie.SetValue("darkred", 0x8B0000); + hTrie.SetValue("darksalmon", 0xE9967A); + hTrie.SetValue("darkseagreen", 0x8FBC8F); + hTrie.SetValue("darkslateblue", 0x483D8B); + hTrie.SetValue("darkslategray", 0x2F4F4F); + hTrie.SetValue("darkslategrey", 0x2F4F4F); + hTrie.SetValue("darkturquoise", 0x00CED1); + hTrie.SetValue("darkviolet", 0x9400D3); + hTrie.SetValue("deeppink", 0xFF1493); + hTrie.SetValue("deepskyblue", 0x00BFFF); + hTrie.SetValue("dimgray", 0x696969); + hTrie.SetValue("dimgrey", 0x696969); + hTrie.SetValue("dodgerblue", 0x1E90FF); + hTrie.SetValue("exalted", 0xCCCCCD); // same as Exalted item quality in Dota 2 + hTrie.SetValue("firebrick", 0xB22222); + hTrie.SetValue("floralwhite", 0xFFFAF0); + hTrie.SetValue("forestgreen", 0x228B22); + hTrie.SetValue("frozen", 0x4983B3); // same as Frozen item quality in Dota 2 + hTrie.SetValue("fuchsia", 0xFF00FF); + hTrie.SetValue("fullblue", 0x0000FF); + hTrie.SetValue("fullred", 0xFF0000); + hTrie.SetValue("gainsboro", 0xDCDCDC); + hTrie.SetValue("genuine", 0x4D7455); // same as Genuine item quality in TF2 + hTrie.SetValue("ghostwhite", 0xF8F8FF); + hTrie.SetValue("gold", 0xFFD700); + hTrie.SetValue("goldenrod", 0xDAA520); + hTrie.SetValue("gray", 0xCCCCCC); // same as spectator team color + hTrie.SetValue("grey", 0xCCCCCC); + hTrie.SetValue("green", 0x3EFF3E); + hTrie.SetValue("greenyellow", 0xADFF2F); + hTrie.SetValue("haunted", 0x38F3AB); // same as Haunted item quality in TF2 + hTrie.SetValue("honeydew", 0xF0FFF0); + hTrie.SetValue("hotpink", 0xFF69B4); + hTrie.SetValue("immortal", 0xE4AE33); // same as Immortal item rarity in Dota 2 + hTrie.SetValue("indianred", 0xCD5C5C); + hTrie.SetValue("indigo", 0x4B0082); + hTrie.SetValue("ivory", 0xFFFFF0); + hTrie.SetValue("khaki", 0xF0E68C); + hTrie.SetValue("lavender", 0xE6E6FA); + hTrie.SetValue("lavenderblush", 0xFFF0F5); + hTrie.SetValue("lawngreen", 0x7CFC00); + hTrie.SetValue("legendary", 0xD32CE6); // same as Legendary item rarity in Dota 2 + hTrie.SetValue("lemonchiffon", 0xFFFACD); + hTrie.SetValue("lightblue", 0xADD8E6); + hTrie.SetValue("lightcoral", 0xF08080); + hTrie.SetValue("lightcyan", 0xE0FFFF); + hTrie.SetValue("lightgoldenrodyellow", 0xFAFAD2); + hTrie.SetValue("lightgray", 0xD3D3D3); + hTrie.SetValue("lightgrey", 0xD3D3D3); + hTrie.SetValue("lightgreen", 0x99FF99); + hTrie.SetValue("lightpink", 0xFFB6C1); + hTrie.SetValue("lightsalmon", 0xFFA07A); + hTrie.SetValue("lightseagreen", 0x20B2AA); + hTrie.SetValue("lightskyblue", 0x87CEFA); + hTrie.SetValue("lightslategray", 0x778899); + hTrie.SetValue("lightslategrey", 0x778899); + hTrie.SetValue("lightsteelblue", 0xB0C4DE); + hTrie.SetValue("lightyellow", 0xFFFFE0); + hTrie.SetValue("lime", 0x00FF00); + hTrie.SetValue("limegreen", 0x32CD32); + hTrie.SetValue("linen", 0xFAF0E6); + hTrie.SetValue("magenta", 0xFF00FF); + hTrie.SetValue("maroon", 0x800000); + hTrie.SetValue("mediumaquamarine", 0x66CDAA); + hTrie.SetValue("mediumblue", 0x0000CD); + hTrie.SetValue("mediumorchid", 0xBA55D3); + hTrie.SetValue("mediumpurple", 0x9370D8); + hTrie.SetValue("mediumseagreen", 0x3CB371); + hTrie.SetValue("mediumslateblue", 0x7B68EE); + hTrie.SetValue("mediumspringgreen", 0x00FA9A); + hTrie.SetValue("mediumturquoise", 0x48D1CC); + hTrie.SetValue("mediumvioletred", 0xC71585); + hTrie.SetValue("midnightblue", 0x191970); + hTrie.SetValue("mintcream", 0xF5FFFA); + hTrie.SetValue("mistyrose", 0xFFE4E1); + hTrie.SetValue("moccasin", 0xFFE4B5); + hTrie.SetValue("mythical", 0x8847FF); // same as Mythical item rarity in Dota 2 + hTrie.SetValue("navajowhite", 0xFFDEAD); + hTrie.SetValue("navy", 0x000080); + hTrie.SetValue("normal", 0xB2B2B2); // same as Normal item quality in TF2 + hTrie.SetValue("oldlace", 0xFDF5E6); + hTrie.SetValue("olive", 0x9EC34F); + hTrie.SetValue("olivedrab", 0x6B8E23); + hTrie.SetValue("orange", 0xFFA500); + hTrie.SetValue("orangered", 0xFF4500); + hTrie.SetValue("orchid", 0xDA70D6); + hTrie.SetValue("palegoldenrod", 0xEEE8AA); + hTrie.SetValue("palegreen", 0x98FB98); + hTrie.SetValue("paleturquoise", 0xAFEEEE); + hTrie.SetValue("palevioletred", 0xD87093); + hTrie.SetValue("papayawhip", 0xFFEFD5); + hTrie.SetValue("peachpuff", 0xFFDAB9); + hTrie.SetValue("peru", 0xCD853F); + hTrie.SetValue("pink", 0xFFC0CB); + hTrie.SetValue("plum", 0xDDA0DD); + hTrie.SetValue("powderblue", 0xB0E0E6); + hTrie.SetValue("purple", 0x800080); + hTrie.SetValue("rare", 0x4B69FF); // same as Rare item rarity in Dota 2 + hTrie.SetValue("red", 0xFF4040); // same as RED/Terrorist team color + hTrie.SetValue("rosybrown", 0xBC8F8F); + hTrie.SetValue("royalblue", 0x4169E1); + hTrie.SetValue("saddlebrown", 0x8B4513); + hTrie.SetValue("salmon", 0xFA8072); + hTrie.SetValue("sandybrown", 0xF4A460); + hTrie.SetValue("seagreen", 0x2E8B57); + hTrie.SetValue("seashell", 0xFFF5EE); + hTrie.SetValue("selfmade", 0x70B04A); // same as Self-Made item quality in TF2 + hTrie.SetValue("sienna", 0xA0522D); + hTrie.SetValue("silver", 0xC0C0C0); + hTrie.SetValue("skyblue", 0x87CEEB); + hTrie.SetValue("slateblue", 0x6A5ACD); + hTrie.SetValue("slategray", 0x708090); + hTrie.SetValue("slategrey", 0x708090); + hTrie.SetValue("snow", 0xFFFAFA); + hTrie.SetValue("springgreen", 0x00FF7F); + hTrie.SetValue("steelblue", 0x4682B4); + hTrie.SetValue("strange", 0xCF6A32); // same as Strange item quality in TF2 + hTrie.SetValue("tan", 0xD2B48C); + hTrie.SetValue("teal", 0x008080); + hTrie.SetValue("thistle", 0xD8BFD8); + hTrie.SetValue("tomato", 0xFF6347); + hTrie.SetValue("turquoise", 0x40E0D0); + hTrie.SetValue("uncommon", 0xB0C3D9); // same as Uncommon item rarity in Dota 2 + hTrie.SetValue("unique", 0xFFD700); // same as Unique item quality in TF2 + hTrie.SetValue("unusual", 0x8650AC); // same as Unusual item quality in TF2 + hTrie.SetValue("valve", 0xA50F79); // same as Valve item quality in TF2 + hTrie.SetValue("vintage", 0x476291); // same as Vintage item quality in TF2 + hTrie.SetValue("violet", 0xEE82EE); + hTrie.SetValue("wheat", 0xF5DEB3); + hTrie.SetValue("white", 0xFFFFFF); + hTrie.SetValue("whitesmoke", 0xF5F5F5); + hTrie.SetValue("yellow", 0xFFFF00); + hTrie.SetValue("yellowgreen", 0x9ACD32); + + return hTrie; +} diff --git a/addons/sourcemod/scripting/include/nativevotes.inc b/addons/sourcemod/scripting/include/nativevotes.inc index 426e6d1..9bfaaf7 100644 --- a/addons/sourcemod/scripting/include/nativevotes.inc +++ b/addons/sourcemod/scripting/include/nativevotes.inc @@ -31,6 +31,7 @@ #include #include +#include // NativeVotes 1.1 series diff --git a/addons/sourcemod/scripting/nativevotes.sp b/addons/sourcemod/scripting/nativevotes.sp index 4de0473..d6fc915 100644 --- a/addons/sourcemod/scripting/nativevotes.sp +++ b/addons/sourcemod/scripting/nativevotes.sp @@ -46,7 +46,7 @@ EngineVersion g_EngineVersion = Engine_Unknown; #include "nativevotes/data-keyvalues.sp" -#define VERSION "1.1.1fix" +#define VERSION "25w52a" #define LOGTAG "NV" @@ -148,7 +148,7 @@ public Plugin myinfo = author = "Powerlord", description = "Voting API to use the game's native vote panels. Compatible with L4D, L4D2, TF2, and CS:GO.", version = VERSION, - url = "https://forums.alliedmods.net/showthread.php?t=208008" + url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" } public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) @@ -746,7 +746,7 @@ void OnVoteSelect(NativeVote vote, int client, int item) if (g_Cvar_VoteChat.BoolValue) { - PrintToChatAll("[%s] %t", LOGTAG, phrase, name, choice); + CPrintToChatAll("[%s] %t", LOGTAG, phrase, name, choice); } if (g_Cvar_VoteClientConsole.BoolValue) @@ -1989,7 +1989,7 @@ public int Native_IsClientInVotePool(Handle plugin, int numParams) { int client = GetNativeCell(1); - if (client <= 0 || client > MaxClients) + if (!client || client > MaxClients) { ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index %d", client); return false; @@ -2379,7 +2379,7 @@ public int Native_SetTarget(Handle plugin, int numParams) int userid; char steamId[20]; - if (client <= 0) + if (!client) { userid = 0; steamId = ""; diff --git a/addons/sourcemod/scripting/nativevotes/game.sp b/addons/sourcemod/scripting/nativevotes/game.sp index 46f8286..ed941eb 100644 --- a/addons/sourcemod/scripting/nativevotes/game.sp +++ b/addons/sourcemod/scripting/nativevotes/game.sp @@ -736,7 +736,7 @@ void Game_DisplayRawVotePass(NativeVotesPassType passType, int team, int client= { case Engine_Left4Dead: { - if (!client) + if (client <= 0) { L4DL4D2_VotePassToTranslation(passType, translation, sizeof(translation)); @@ -2083,7 +2083,7 @@ static void L4D2_DisplayVote(NativeVote vote, int[] clients, int num_clients) static void L4D2_VotePass(const char[] translation, const char[] details, int team, int client=0) { BfWrite votePass; - if (!client) + if (client <= 0) { votePass = UserMessageToBfWrite(StartMessageAll("VotePass", USERMSG_RELIABLE)); } @@ -2509,7 +2509,7 @@ static void CSGO_VotePass(const char[] translation, const char[] details, int te { Protobuf votePass = null; - if (!client) + if (client <= 0) { votePass = UserMessageToProtobuf(StartMessageAll("VotePass", USERMSG_RELIABLE)); } @@ -2530,7 +2530,7 @@ static void TF2_VotePass(const char[] translation, const char[] details, int tea { BfWrite votePass = null; - if (!client) + if (client <= 0) { votePass = UserMessageToBfWrite(StartMessageAll("VotePass", USERMSG_RELIABLE)); } diff --git a/addons/sourcemod/scripting/nativevotes-basecommands.sp b/addons/sourcemod/scripting/nativevotes_basecommands.sp similarity index 87% rename from addons/sourcemod/scripting/nativevotes-basecommands.sp rename to addons/sourcemod/scripting/nativevotes_basecommands.sp index 8696a54..e50e8af 100644 --- a/addons/sourcemod/scripting/nativevotes-basecommands.sp +++ b/addons/sourcemod/scripting/nativevotes_basecommands.sp @@ -1,188 +1,188 @@ -/** - * vim: set ts=4 : - * ============================================================================= - * NativeVotes Basecommands Plugin - * Provides cancelvote and revote functionality for NativeVotes - * - * NativeVotes (C) 2011-2014 Ross Bemrose (Powerlord). All rights reserved. - * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved. - * ============================================================================= - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, version 3.0, as published by the - * Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - * - * As a special exception, AlliedModders LLC gives you permission to link the - * code of this program (as well as its derivative works) to "Half-Life 2," the - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software - * by the Valve Corporation. You must obey the GNU General Public License in - * all respects for all other code used. Additionally, AlliedModders LLC grants - * this exception to all derivative works. AlliedModders LLC defines further - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), - * or . - * - * Version: $Id$ - */ - -#include -#include - -#undef REQUIRE_PLUGIN -#include - -#define VERSION "1.1" -#pragma newdecls required -#pragma semicolon 1 - -TopMenu hTopMenu; - -public Plugin myinfo = -{ - name = "NativeVotes Basic Commands", - author = "Powerlord and AlliedModders LLC", - description = "Revote and Cancel support for NativeVotes", - version = VERSION, - url = "https://forums.alliedmods.net/showthread.php?t=208008" -} - -public void OnPluginStart() -{ - LoadTranslations("core.phrases"); - LoadTranslations("common.phrases"); - - AddCommandListener(Command_CancelVote, "sm_cancelvote"); - AddCommandListener(Command_ReVote, "sm_revote"); -} - -bool PerformCancelVote(int client) -{ - if (!NativeVotes_IsVoteInProgress()) - { - return false; - } - - ShowActivity2(client, "[NV] ", "%t", "Cancelled Vote"); - - NativeVotes_Cancel(); - return true; -} - -public Action Command_CancelVote(int client, const char[] command, int argc) -{ - if (!CheckCommandAccess(client, "sm_cancelvote", ADMFLAG_VOTE)) - { - if (IsVoteInProgress()) - { - // Let basecommands handle it - return Plugin_Continue; - } - - ReplyToCommand(client, "%t", "No Access"); - return Plugin_Stop; - } - - if (PerformCancelVote(client)) - { - return Plugin_Stop; - } - else - { - return Plugin_Continue; - } -} - -public void AdminMenu_CancelVote(Handle topmenu, - TopMenuAction action, - TopMenuObject object_id, - int param, - char[] buffer, - int maxlength) -{ - if (action == TopMenuAction_DisplayOption) - { - Format(buffer, maxlength, "%T", "Cancel vote", param); - } - else if (action == TopMenuAction_SelectOption) - { - PerformCancelVote(param); - RedisplayAdminMenu(topmenu, param); - } - else if (action == TopMenuAction_DrawOption) - { - buffer[0] = NativeVotes_IsVoteInProgress() ? ITEMDRAW_DEFAULT : ITEMDRAW_IGNORE; - } -} - -public Action Command_ReVote(int client, const char[] command, int argc) -{ - if (client == 0) - { - return Plugin_Continue; - } - - if (!NativeVotes_IsVoteInProgress()) - { - return Plugin_Continue; - } - - if (!NativeVotes_IsClientInVotePool(client)) - { - if (IsVoteInProgress()) - { - // Let basecommands handle it - return Plugin_Continue; - } - - ReplyToCommand(client, "[NV] %t", "Cannot participate in vote"); - return Plugin_Stop; - } - - if (NativeVotes_RedrawClientVote(client)) - { - return Plugin_Stop; - } - else if (!IsVoteInProgress()) - { - ReplyToCommand(client, "[NV] %t", "Cannot change vote"); - return Plugin_Stop; - } - - return Plugin_Continue; -} - -public void OnAdminMenuReady(Handle aTopMenu) -{ - TopMenu topmenu = TopMenu.FromHandle(aTopMenu); - - /* Block us from being called twice */ - if (topmenu == hTopMenu) - { - return; - } - - /* Save the Handle */ - hTopMenu = topmenu; - - TopMenuObject voting_commands = hTopMenu.FindCategory(ADMINMENU_VOTINGCOMMANDS); - - if (voting_commands != INVALID_TOPMENUOBJECT) - { - hTopMenu.AddItem("sm_cancelvote", AdminMenu_CancelVote, voting_commands, "sm_cancelvote", ADMFLAG_VOTE); - } -} - -public void OnLibraryRemoved(const char[] name) -{ - if (strcmp(name, "adminmenu") == 0) - { - hTopMenu = null; - } -} +/** + * vim: set ts=4 : + * ============================================================================= + * NativeVotes Basecommands Plugin + * Provides cancelvote and revote functionality for NativeVotes + * + * NativeVotes (C) 2011-2014 Ross Bemrose (Powerlord). All rights reserved. + * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#include +#include + +#undef REQUIRE_PLUGIN +#include + +#define VERSION "25w52a" +#pragma newdecls required +#pragma semicolon 1 + +TopMenu hTopMenu; + +public Plugin myinfo = +{ + name = "NativeVotes Basic Commands", + author = "Powerlord and AlliedModders LLC", + description = "Revote and Cancel support for NativeVotes", + version = VERSION, + url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" +} + +public void OnPluginStart() +{ + LoadTranslations("core.phrases"); + LoadTranslations("common.phrases"); + + AddCommandListener(Command_CancelVote, "sm_cancelvote"); + AddCommandListener(Command_ReVote, "sm_revote"); +} + +bool PerformCancelVote(int client) +{ + if (!NativeVotes_IsVoteInProgress()) + { + return false; + } + + CShowActivity2(client, "[{lightgreen}NativeVotes{default}] ", "%t", "Cancelled Vote"); + + NativeVotes_Cancel(); + return true; +} + +public Action Command_CancelVote(int client, const char[] command, int argc) +{ + if (!CheckCommandAccess(client, "sm_cancelvote", ADMFLAG_VOTE)) + { + if (IsVoteInProgress()) + { + // Let basecommands handle it + return Plugin_Continue; + } + + CReplyToCommand(client, "%t", "No Access"); + return Plugin_Stop; + } + + if (PerformCancelVote(client)) + { + return Plugin_Stop; + } + else + { + return Plugin_Continue; + } +} + +public void AdminMenu_CancelVote(Handle topmenu, + TopMenuAction action, + TopMenuObject object_id, + int param, + char[] buffer, + int maxlength) +{ + if (action == TopMenuAction_DisplayOption) + { + Format(buffer, maxlength, "%T", "Cancel vote", param); + } + else if (action == TopMenuAction_SelectOption) + { + PerformCancelVote(param); + RedisplayAdminMenu(topmenu, param); + } + else if (action == TopMenuAction_DrawOption) + { + buffer[0] = NativeVotes_IsVoteInProgress() ? ITEMDRAW_DEFAULT : ITEMDRAW_IGNORE; + } +} + +public Action Command_ReVote(int client, const char[] command, int argc) +{ + if (client == 0) + { + return Plugin_Continue; + } + + if (!NativeVotes_IsVoteInProgress()) + { + return Plugin_Continue; + } + + if (!NativeVotes_IsClientInVotePool(client)) + { + if (IsVoteInProgress()) + { + // Let basecommands handle it + return Plugin_Continue; + } + + CReplyToCommand(client, "[{lightgreen}NativeVotes{default}] %t", "Cannot participate in vote"); + return Plugin_Stop; + } + + if (NativeVotes_RedrawClientVote(client)) + { + return Plugin_Stop; + } + else if (!IsVoteInProgress()) + { + CReplyToCommand(client, "[{lightgreen}NativeVotes{default}] %t", "Cannot change vote"); + return Plugin_Stop; + } + + return Plugin_Continue; +} + +public void OnAdminMenuReady(Handle aTopMenu) +{ + TopMenu topmenu = TopMenu.FromHandle(aTopMenu); + + /* Block us from being called twice */ + if (topmenu == hTopMenu) + { + return; + } + + /* Save the Handle */ + hTopMenu = topmenu; + + TopMenuObject voting_commands = hTopMenu.FindCategory(ADMINMENU_VOTINGCOMMANDS); + + if (voting_commands != INVALID_TOPMENUOBJECT) + { + hTopMenu.AddItem("sm_cancelvote", AdminMenu_CancelVote, voting_commands, "sm_cancelvote", ADMFLAG_VOTE); + } +} + +public void OnLibraryRemoved(const char[] name) +{ + if (strcmp(name, "adminmenu") == 0) + { + hTopMenu = null; + } +} diff --git a/addons/sourcemod/scripting/nativevotes_basevotes.sp b/addons/sourcemod/scripting/nativevotes_basevotes.sp new file mode 100644 index 0000000..4e8e677 --- /dev/null +++ b/addons/sourcemod/scripting/nativevotes_basevotes.sp @@ -0,0 +1,734 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * NativeVotes Basic Votes Plugin + * Implements basic vote commands using NativeVotes. + * Based on the SourceMod version. + * + * NativeVotes (C)2011-2014 Ross Bemrose (Powerlord). All rights reserved. + * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#pragma semicolon 1 + +#include +#undef REQUIRE_PLUGIN +#include +#include + +#define VERSION "25w52a" + +public Plugin:myinfo = +{ + name = "NativeVotes Basic Votes", + author = "Powerlord and AlliedModders LLC", + description = "NativeVotes Basic Vote Commands", + version = VERSION, + url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" +}; + +#define VOTE_NO "###no###" +#define VOTE_YES "###yes###" + +//new Handle:g_hVoteMenu = INVALID_HANDLE; + +new Handle:g_Cvar_Limits[3] = {INVALID_HANDLE, ...}; +//new Handle:g_Cvar_VoteSay = INVALID_HANDLE; + +enum voteType +{ + map, + kick, + ban, + question +} + +new voteType:g_voteType = voteType:question; + +// Menu API does not provide us with a way to pass multiple peices of data with a single +// choice, so some globals are used to hold stuff. +// +#define VOTE_CLIENTID 0 +#define VOTE_USERID 1 +new g_voteClient[2]; /* Holds the target's client id and user id */ + +#define VOTE_NAME 0 +#define VOTE_AUTHID 1 +#define VOTE_IP 2 +new String:g_voteInfo[3][65]; /* Holds the target's name, authid, and IP */ + +new String:g_voteArg[256]; /* Used to hold ban/kick reasons or vote questions */ + + +new Handle:hTopMenu = INVALID_HANDLE; + +// NativeVotes +new bool:g_NativeVotes; + +//new g_Cvar_NativeVotesMenu = INVALID_HANDLE; + +#include "nativevotes_basevotes/votekick.sp" +#include "nativevotes_basevotes/voteban.sp" +#include "nativevotes_basevotes/votemap.sp" + +public OnPluginStart() +{ + LoadTranslations("common.phrases"); + LoadTranslations("basevotes.phrases"); + LoadTranslations("plugin.basecommands"); + LoadTranslations("basebans.phrases"); + + RegAdminCmd("sm_votemap", Command_Votemap, ADMFLAG_VOTE|ADMFLAG_CHANGEMAP, "sm_votemap [mapname2] ... [mapname5] "); + RegAdminCmd("sm_votekick", Command_Votekick, ADMFLAG_VOTE|ADMFLAG_KICK, "sm_votekick [reason]"); + RegAdminCmd("sm_voteban", Command_Voteban, ADMFLAG_VOTE|ADMFLAG_BAN, "sm_voteban [reason]"); + RegAdminCmd("sm_vote", Command_Vote, ADMFLAG_VOTE, "sm_vote [Answer1] [Answer2] ... [Answer5]"); + + /* + g_Cvar_Show = FindConVar("sm_vote_show"); + if (g_Cvar_Show == INVALID_HANDLE) + { + g_Cvar_Show = CreateConVar("sm_vote_show", "1", "Show player's votes? Default on.", 0, true, 0.0, true, 1.0); + } + */ + + g_Cvar_Limits[0] = CreateConVar("sm_vote_map", "0.60", "percent required for successful map vote.", 0, true, 0.05, true, 1.0); + g_Cvar_Limits[1] = CreateConVar("sm_vote_kick", "0.60", "percent required for successful kick vote.", 0, true, 0.05, true, 1.0); + g_Cvar_Limits[2] = CreateConVar("sm_vote_ban", "0.60", "percent required for successful ban vote.", 0, true, 0.05, true, 1.0); + CreateConVar("nativevotes_basevotes_version", VERSION, "NativeVotes Basic Votes version", FCVAR_PLUGIN|FCVAR_NOTIFY|FCVAR_DONTRECORD|FCVAR_SPONLY); + + g_SelectedMaps = CreateArray(PLATFORM_MAX_PATH); + + g_MapList = CreateMenu(MenuHandler_Map, MenuAction_DrawItem|MenuAction_Display); + SetMenuTitle(g_MapList, "%T", "Please select a map", LANG_SERVER); + SetMenuExitBackButton(g_MapList, true); + + decl String:mapListPath[PLATFORM_MAX_PATH]; + BuildPath(Path_SM, mapListPath, sizeof(mapListPath), "configs/adminmenu_maplist.ini"); + SetMapListCompatBind("sm_votemap menu", mapListPath); +} + +public OnAllPluginsLoaded() +{ + if (FindPluginByFile("basevotes.smx") != INVALID_HANDLE) + { + + LogMessage("Unloading basevotes to prevent conflicts..."); + ServerCommand("sm plugins unload basevotes"); + + decl String:oldPath[PLATFORM_MAX_PATH]; + decl String:newPath[PLATFORM_MAX_PATH]; + + BuildPath(Path_SM, oldPath, sizeof(oldPath), "plugins/basevotes.smx"); + BuildPath(Path_SM, newPath, sizeof(newPath), "plugins/disabled/basevotes.smx"); + if (RenameFile(newPath, oldPath)) + { + LogMessage("Moving basevotes to disabled."); + } + + //SetFailState("This plugin replaces basevotes. You cannot run both at once."); + } + + /* Account for late loading */ + new Handle:topmenu; + if (LibraryExists("adminmenu") && ((topmenu = GetAdminTopMenu()) != INVALID_HANDLE)) + { + OnAdminMenuReady(topmenu); + } + + g_NativeVotes = LibraryExists("nativevotes") && NativeVotes_IsVoteTypeSupported(NativeVotesType_Custom_YesNo); +} + +public OnLibraryAdded(const String:name[]) +{ + new Handle:topmenu; + if (StrEqual(name, "adminmenu") && ((topmenu = GetAdminTopMenu()) != INVALID_HANDLE)) + { + OnAdminMenuReady(topmenu); + } + else + if (StrEqual(name, "nativevotes") && NativeVotes_IsVoteTypeSupported(NativeVotesType_Custom_YesNo)) + { + g_NativeVotes = true; + } +} + +public OnLibraryRemoved(const String:name[]) +{ + if (StrEqual(name, "adminmenu")) + { + hTopMenu = INVALID_HANDLE; + } + else + if (StrEqual(name, "nativevotes")) + { + g_NativeVotes = false; + } +} + +public OnConfigsExecuted() +{ + g_mapCount = LoadMapList(g_MapList); +} + +public OnAdminMenuReady(Handle:topmenu) +{ + /* Block us from being called twice */ + if (topmenu == hTopMenu) + { + return; + } + + /* Save the Handle */ + hTopMenu = topmenu; + + /* Build the "Voting Commands" category */ + new TopMenuObject:voting_commands = FindTopMenuCategory(hTopMenu, ADMINMENU_VOTINGCOMMANDS); + + if (voting_commands != INVALID_TOPMENUOBJECT) + { + AddToTopMenu(hTopMenu, + "sm_votekick", + TopMenuObject_Item, + AdminMenu_VoteKick, + voting_commands, + "sm_votekick", + ADMFLAG_VOTE|ADMFLAG_KICK); + + AddToTopMenu(hTopMenu, + "sm_voteban", + TopMenuObject_Item, + AdminMenu_VoteBan, + voting_commands, + "sm_voteban", + ADMFLAG_VOTE|ADMFLAG_BAN); + + AddToTopMenu(hTopMenu, + "sm_votemap", + TopMenuObject_Item, + AdminMenu_VoteMap, + voting_commands, + "sm_votemap", + ADMFLAG_VOTE|ADMFLAG_CHANGEMAP); + } +} + +public Action:Command_Vote(client, args) +{ + if (args < 1) + { + CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] Usage: sm_vote [Answer1] [Answer2] ... [Answer5]"); + return Plugin_Handled; + } + + if (Internal_IsVoteInProgress()) + { + CReplyToCommand(client, "%t", "Vote in Progress"); + return Plugin_Handled; + } + + if (!TestVoteDelay(client)) + { + return Plugin_Handled; + } + + decl String:text[256]; + GetCmdArgString(text, sizeof(text)); + + decl String:answers[5][64]; + new answerCount; + new len = BreakString(text, g_voteArg, sizeof(g_voteArg)); + new pos = len; + + while (args > 1 && pos != -1 && answerCount < 5) + { + pos = BreakString(text[len], answers[answerCount], sizeof(answers[])); + answerCount++; + + if (pos != -1) + { + len += pos; + } + } + + LogAction(client, -1, "\"%L\" initiated a generic vote.", client); + CShowActivity2(client, "[{lightgreen}NativeVotes\x01] ", "%t", "Initiate Vote", g_voteArg); + + g_voteType = voteType:question; + new Handle:voteMenu; + + if (g_NativeVotes && (answerCount < 2 || NativeVotes_IsVoteTypeSupported(NativeVotesType_Custom_Mult)) ) + { + new NativeVotesType:nVoteType = answerCount < 2 ? NativeVotesType_Custom_YesNo : NativeVotesType_Custom_Mult; + + voteMenu = NativeVotes_Create(Handler_NativeVoteCallback, nVoteType, MenuAction:MENU_ACTIONS_ALL); + NativeVotes_SetTitle(voteMenu, g_voteArg); + + if (answerCount >= 2) + { + for (new i = 0; i < answerCount; i++) + { + NativeVotes_AddItem(voteMenu, answers[i], answers[i]); + } + } + + //NativeVotes_SetInitiator(voteMenu, client); + NativeVotes_DisplayToAll(voteMenu, 20); + } + else + { + voteMenu = CreateMenu(Handler_VoteCallback, MenuAction:MENU_ACTIONS_ALL); + SetMenuTitle(voteMenu, "%s?", g_voteArg); + + if (answerCount < 2) + { + AddMenuItem(voteMenu, VOTE_YES, "Yes"); + AddMenuItem(voteMenu, VOTE_NO, "No"); + } + else + { + for (new i = 0; i < answerCount; i++) + { + AddMenuItem(voteMenu, answers[i], answers[i]); + } + } + + SetMenuExitButton(voteMenu, false); + VoteMenuToAll(voteMenu, 20); + } + return Plugin_Handled; +} + +public Handler_VoteCallback(Handle:menu, MenuAction:action, param1, param2) +{ + switch (action) + { + case MenuAction_End: + { + //VoteMenuClose(); + CloseHandle(menu); + } + + case MenuAction_Display: + { + if (g_voteType != voteType:question) + { + decl String:title[64]; + GetMenuTitle(menu, title, sizeof(title)); + + decl String:buffer[255]; + Format(buffer, sizeof(buffer), "%T", title, param1, g_voteInfo[VOTE_NAME]); + + new Handle:panel = Handle:param2; + SetPanelTitle(panel, buffer); + } + } + + case MenuAction_DisplayItem: + { + decl String:display[64]; + GetMenuItem(menu, param2, "", 0, _, display, sizeof(display)); + + if (strcmp(display, "No") == 0 || strcmp(display, "Yes") == 0) + { + decl String:buffer[255]; + Format(buffer, sizeof(buffer), "%T", display, param1); + + return RedrawMenuItem(buffer); + } + } + + case MenuAction_VoteCancel: + { + if (param1 == VoteCancel_NoVotes) + { + CPrintToChatAll("%t", "No Votes Cast"); + } + } + + case MenuAction_VoteEnd: + { + decl String:item[64], String:display[64]; + new Float:percent, Float:limit, votes, totalVotes; + + GetMenuVoteInfo(param2, votes, totalVotes); + GetMenuItem(menu, param1, item, sizeof(item), _, display, sizeof(display)); + + if (strcmp(item, VOTE_NO) == 0 && param1 == 1) + { + votes = totalVotes - votes; // Reverse the votes to be in relation to the Yes option. + } + + percent = GetVotePercent(votes, totalVotes); + + if (g_voteType != voteType:question) + { + limit = GetConVarFloat(g_Cvar_Limits[g_voteType]); + } + + /* :TODO: g_voteClient[userid] needs to be checked */ + + // A multi-argument vote is "always successful", but have to check if its a Yes/No vote. + if ((strcmp(item, VOTE_YES) == 0 && FloatCompare(percent,limit) < 0 && param1 == 0) || (strcmp(item, VOTE_NO) == 0 && param1 == 1)) + { + /* :TODO: g_voteClient[userid] should be used here and set to -1 if not applicable. + */ + LogAction(-1, -1, "Vote failed."); + CPrintToChatAll("%t", "Vote Failed", RoundToNearest(100.0*limit), RoundToNearest(100.0*percent), totalVotes); + } + else + { + CPrintToChatAll("%t", "Vote Successful", RoundToNearest(100.0*percent), totalVotes); + + switch (g_voteType) + { + case (voteType:question): + { + if (strcmp(item, VOTE_NO) == 0 || strcmp(item, VOTE_YES) == 0) + { + for (new i = 1; i <= MaxClients; i++) + { + if (IsClientInGame(i) && !IsFakeClient(i)) + { + Format(item, sizeof(item), "%T", display, i); + CPrintToChat(i, "%t", "Vote End", g_voteArg, item); + } + } + } + else + { + CPrintToChatAll("%t", "Vote End", g_voteArg, item); + } + } + + case (voteType:map): + { + LogAction(-1, -1, "Changing map to %s due to vote.", item); + CPrintToChatAll("%t", "Changing map", item); + new Handle:dp; + CreateDataTimer(5.0, Timer_ChangeMap, dp); + WritePackString(dp, item); + } + + case (voteType:kick): + { + if (g_voteArg[0] == '\0') + { + strcopy(g_voteArg, sizeof(g_voteArg), "Votekicked"); + } + + if (GetClientOfUserId(g_voteClient[VOTE_USERID]) > 0 && !IsClientInKickQueue(g_voteClient[VOTE_CLIENTID])) + { + CPrintToChatAll("%t", "Kicked target", "_s", g_voteInfo[VOTE_NAME]); + LogAction(-1, g_voteClient[VOTE_CLIENTID], "Vote kick successful, kicked \"%L\" (reason \"%s\")", g_voteClient[VOTE_CLIENTID], g_voteArg); + + KickClient(g_voteClient[VOTE_CLIENTID], "%s", g_voteArg); + } + + } + + case (voteType:ban): + { + if (g_voteArg[0] == '\0') + { + strcopy(g_voteArg, sizeof(g_voteArg), "Votebanned"); + } + + if (GetClientOfUserId(g_voteClient[VOTE_USERID]) > 0) + { + CPrintToChatAll("%t", "Banned player", g_voteInfo[VOTE_NAME], 30); + LogAction(-1, g_voteClient[VOTE_CLIENTID], "Vote ban successful, banned \"%L\" (minutes \"30\") (reason \"%s\")", g_voteClient[VOTE_CLIENTID], g_voteArg); + + BanClient(g_voteClient[VOTE_CLIENTID], + 30, + BANFLAG_AUTO, + g_voteArg, + "Banned by vote", + "sm_voteban"); + } + } + } + } + } + } + + return 0; +} + +public Handler_NativeVoteCallback(Handle:menu, MenuAction:action, param1, param2) +{ + switch (action) + { + + case MenuAction_End: + { + NativeVotes_Close(menu); + } + + case MenuAction_Display: + { + new NativeVotesType:nVoteType = NativeVotes_GetType(menu); + if (g_voteType != voteType:question && (nVoteType == NativeVotesType_Custom_YesNo || nVoteType == NativeVotesType_Custom_Mult)) + { + decl String:title[64]; + NativeVotes_GetTitle(menu, title, sizeof(title)); + + decl String:buffer[255]; + Format(buffer, sizeof(buffer), "%T", title, param1, g_voteInfo[VOTE_NAME]); + + return _:NativeVotes_RedrawVoteTitle(buffer); + } + } + + case MenuAction_VoteCancel: + { + if (param1 == VoteCancel_NoVotes) + { + NativeVotes_DisplayFail(menu, NativeVotesFail_NotEnoughVotes); + CPrintToChatAll("%t", "No Votes Cast"); + } + else + { + NativeVotes_DisplayFail(menu, NativeVotesFail_Generic); + } + } + + case MenuAction_VoteEnd: + { + decl String:item[64], String:display[64]; + new Float:percent, Float:limit, votes, totalVotes; + + new NativeVotesType:nVoteType = NativeVotes_GetType(menu); + + NativeVotes_GetInfo(param2, votes, totalVotes); + NativeVotes_GetItem(menu, param1, item, sizeof(item), display, sizeof(display)); + + if (nVoteType == NativeVotesType_Custom_YesNo && param1 == NATIVEVOTES_VOTE_NO) + { + votes = totalVotes - votes; // Reverse the votes to be in relation to the Yes option. + } + + percent = GetVotePercent(votes, totalVotes); + + if (g_voteType != voteType:question) + { + limit = GetConVarFloat(g_Cvar_Limits[g_voteType]); + } + + /* :TODO: g_voteClient[userid] needs to be checked */ + + // A multi-argument vote is "always successful", but have to check if its a Yes/No vote. + if ((nVoteType != NativeVotesType_NextLevelMult && nVoteType != NativeVotesType_Custom_Mult) && ((param1 == NATIVEVOTES_VOTE_YES && FloatCompare(percent,limit) < 0) || (param1 == NATIVEVOTES_VOTE_NO))) + { + /* :TODO: g_voteClient[userid] should be used here and set to -1 if not applicable. + */ + NativeVotes_DisplayFail(menu, NativeVotesFail_Loses); + LogAction(-1, -1, "Vote failed."); + CPrintToChatAll("%t", "Vote Failed", RoundToNearest(100.0*limit), RoundToNearest(100.0*percent), totalVotes); + } + else + { + CPrintToChatAll("%t", "Vote Successful", RoundToNearest(100.0*percent), totalVotes); + + switch (g_voteType) + { + case (voteType:question): + { + if (nVoteType == NativeVotesType_Custom_YesNo) + { + for (new i = 1; i <= MaxClients; i++) + { + if (IsClientInGame(i) && !IsFakeClient(i)) + { + Format(item, sizeof(item), "%T", display, i); + CPrintToChat(i, "%t", "Vote End", g_voteArg, item); + NativeVotes_DisplayPassCustomToOne(menu, i, "%t", "Vote End", g_voteArg, item); + } + } + } + else + { + CPrintToChatAll("%t", "Vote End", g_voteArg, item); + NativeVotes_DisplayPassCustom(menu, "%t", "Vote End", g_voteArg, item); + } + } + + case (voteType:map): + { + if (nVoteType == NativeVotesType_ChgLevel) + { + NativeVotes_GetDetails(menu, item, sizeof(item)); + } + + //NativeVotes_DisplayPass(menu, item); + NativeVotes_DisplayPassEx(menu, NativeVotesPass_ChgLevel, item); + LogAction(-1, -1, "Changing map to %s due to vote.", item); + CPrintToChatAll("%t", "Changing map", item); + new Handle:dp; + CreateDataTimer(5.0, Timer_ChangeMap, dp); + WritePackString(dp, item); + } + + case (voteType:kick): + { + if (g_voteArg[0] == '\0') + { + strcopy(g_voteArg, sizeof(g_voteArg), "Votekicked"); + } + + if (GetClientOfUserId(g_voteClient[VOTE_USERID]) > 0 && !IsClientInKickQueue(g_voteClient[VOTE_CLIENTID])) + { + CPrintToChatAll("%t", "Kicked target", "_s", g_voteInfo[VOTE_NAME]); + LogAction(-1, g_voteClient[VOTE_CLIENTID], "Vote kick successful, kicked \"%L\" (reason \"%s\")", g_voteClient[VOTE_CLIENTID], g_voteArg); + + KickClient(g_voteClient[VOTE_CLIENTID], "%s", g_voteArg); + NativeVotes_DisplayPass(menu, g_voteInfo[VOTE_NAME]); + } + } + + case (voteType:ban): + { + if (g_voteArg[0] == '\0') + { + strcopy(g_voteArg, sizeof(g_voteArg), "Votebanned"); + } + + if (GetClientOfUserId(g_voteClient[VOTE_USERID]) > 0) + { + CPrintToChatAll("%t", "Banned player", g_voteInfo[VOTE_NAME], 30); + LogAction(-1, g_voteClient[VOTE_CLIENTID], "Vote ban successful, banned \"%L\" (minutes \"30\") (reason \"%s\")", g_voteClient[VOTE_CLIENTID], g_voteArg); + + BanClient(g_voteClient[VOTE_CLIENTID], + 30, + BANFLAG_AUTO, + g_voteArg, + "Banned by vote", + "sm_voteban"); + + NativeVotes_DisplayPassCustom(menu, "%t", "Banned player", g_voteInfo[VOTE_NAME], 30); + } + } + } + } + } + } + + return 0; +} + +/* +VoteSelect(Handle:menu, param1, param2 = 0) +{ + if (GetConVarInt(g_Cvar_VoteShow) == 1) + { + decl String:voter[64], String:junk[64], String:choice[64]; + GetClientName(param1, voter, sizeof(voter)); + GetMenuItem(menu, param2, junk, sizeof(junk), _, choice, sizeof(choice)); + CPrintToChatAll("%t", "Vote Select", LANG_SERVER, voter, choice); + } +} +*/ + +/* +VoteMenuClose() +{ + CloseHandle(g_hVoteMenu); + g_hVoteMenu = INVALID_HANDLE; +} +*/ + +Float:GetVotePercent(votes, totalVotes) +{ + return FloatDiv(float(votes),float(totalVotes)); +} + +bool:TestVoteDelay(client) +{ + new delay = Internal_CheckVoteDelay(); + + if (delay > 0) + { + if (delay > 60) + { + CReplyToCommand(client, "%t", "Vote Delay Minutes", delay % 60); + } + else + { + CReplyToCommand(client, "%t", "Vote Delay Seconds", delay); + } + + if (g_NativeVotes) + { + NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_Recent, delay); + } + + return false; + } + + return true; +} + +public Action:Timer_ChangeMap(Handle:timer, Handle:dp) +{ + decl String:mapname[65]; + + ResetPack(dp); + ReadPackString(dp, mapname, sizeof(mapname)); + + ForceChangeLevel(mapname, "sm_votemap Result"); + + return Plugin_Stop; +} + +bool:Internal_IsVoteInProgress() +{ + if (g_NativeVotes) + { + return NativeVotes_IsVoteInProgress(); + } + + return IsVoteInProgress(); +} + +Internal_CheckVoteDelay() +{ + if (g_NativeVotes) + { + return NativeVotes_CheckVoteDelay(); + } + + return CheckVoteDelay(); +} + +bool:Internal_IsNewVoteAllowed() +{ + if (g_NativeVotes) + { + return NativeVotes_IsNewVoteAllowed(); + } + + return IsNewVoteAllowed(); +} diff --git a/addons/sourcemod/scripting/nativevotes_basevotes/voteban.sp b/addons/sourcemod/scripting/nativevotes_basevotes/voteban.sp new file mode 100644 index 0000000..100a347 --- /dev/null +++ b/addons/sourcemod/scripting/nativevotes_basevotes/voteban.sp @@ -0,0 +1,202 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * NativeVotes Basic Votes Plugin + * Provides ban functionality + * + * NativeVotes (C)2011-2014 Ross Bemrose (Powerlord). All rights reserved. + * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +DisplayVoteBanMenu(client, target) +{ + g_voteClient[VOTE_CLIENTID] = target; + g_voteClient[VOTE_USERID] = GetClientUserId(target); + + char playerName[MAX_NAME_LENGTH]; int r, g, b, a, color; + GetEntityRenderColor(target, r, g, b, a); + color = (r << 16) | (g << 8) | b; + if (color != 0xFFFFFF) { + Format(playerName, sizeof(playerName), "{#%06X}%N\x01", color, target); + } + else { + Format(playerName, sizeof(playerName), "%s%N\x01", GetClientTeam(target) == 2 ? "{red}" : GetClientTeam(target) == 3 ? "{blue}" : "{grey}", target); + } + + GetClientName(target, g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[])); + GetClientIP(target, g_voteInfo[VOTE_IP], sizeof(g_voteInfo[])); + + LogAction(client, target, "\"%L\" initiated a ban vote against %s", client, playerName); + CShowActivity2(client, "[{lightgreen}NativeVotes\x01] ", "%t", "Initiated Vote Ban", playerName); + + g_voteType = voteType:ban; + + if (g_NativeVotes) + { + new Handle:voteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Custom_YesNo, MenuAction:MENU_ACTIONS_ALL); + NativeVotes_SetTitle(voteMenu, "Voteban Player"); + NativeVotes_DisplayToAll(voteMenu, 20); + NativeVotes_SetTarget(voteMenu, target); + } + else + { + new Handle:voteMenu = CreateMenu(Handler_VoteCallback, MenuAction:MENU_ACTIONS_ALL); + SetMenuTitle(voteMenu, "Voteban Player"); + AddMenuItem(voteMenu, VOTE_YES, "Yes"); + AddMenuItem(voteMenu, VOTE_NO, "No"); + SetMenuExitButton(voteMenu, false); + VoteMenuToAll(voteMenu, 20); + } +} + +DisplayBanTargetMenu(client) +{ + new Handle:menu = CreateMenu(MenuHandler_Ban); + + decl String:title[100]; + Format(title, sizeof(title), "%T:", "Ban vote", client); + SetMenuTitle(menu, title); + SetMenuExitBackButton(menu, true); + + AddTargetsToMenu(menu, client, false, false); + + DisplayMenu(menu, client, MENU_TIME_FOREVER); +} + +public AdminMenu_VoteBan(Handle:topmenu, + TopMenuAction:action, + TopMenuObject:object_id, + param, + String:buffer[], + maxlength) +{ + if (action == TopMenuAction_DisplayOption) + { + Format(buffer, maxlength, "%T", "Ban vote", param); + } + else if (action == TopMenuAction_SelectOption) + { + DisplayBanTargetMenu(param); + } + else if (action == TopMenuAction_DrawOption) + { + /* disable this option if a vote is already running */ + buffer[0] = Internal_IsNewVoteAllowed() ? ITEMDRAW_IGNORE : ITEMDRAW_DEFAULT; + } +} + +public MenuHandler_Ban(Handle:menu, MenuAction:action, param1, param2) +{ + if (action == MenuAction_End) + { + CloseHandle(menu); + } + else if (action == MenuAction_Cancel) + { + if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE) + { + DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory); + } + } + else if (action == MenuAction_Select) + { + decl String:info[32], String:name[32]; + new userid, target; + + GetMenuItem(menu, param2, info, sizeof(info), _, name, sizeof(name)); + userid = StringToInt(info); + + if ((target = GetClientOfUserId(userid)) == 0) + { + PrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Player no longer available"); + } + else if (!CanUserTarget(param1, target)) + { + PrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Unable to target"); + } + else + { + g_voteArg[0] = '\0'; + DisplayVoteBanMenu(param1, target); + } + } +} + +public Action:Command_Voteban(client, args) +{ + if (args < 1) + { + CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] Usage: sm_voteban [reason]"); + return Plugin_Handled; + } + + if (Internal_IsVoteInProgress()) + { + CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Vote in Progress"); + return Plugin_Handled; + } + + if (!TestVoteDelay(client)) + { + return Plugin_Handled; + } + + decl String:text[256], String:arg[64]; + GetCmdArgString(text, sizeof(text)); + + new len = BreakString(text, arg, sizeof(arg)); + + if (len != -1) + { + strcopy(g_voteArg, sizeof(g_voteArg), text[len]); + } + else + { + g_voteArg[0] = '\0'; + } + + decl String:target_name[MAX_TARGET_LENGTH]; + decl target_list[MAXPLAYERS], target_count, bool:tn_is_ml; + + if ((target_count = ProcessTargetString( + arg, + client, + target_list, + MAXPLAYERS, + COMMAND_FILTER_NO_MULTI|COMMAND_FILTER_NO_BOTS, + target_name, + sizeof(target_name), + tn_is_ml)) <= 0) + { + ReplyToTargetError(client, target_count); + return Plugin_Handled; + } + + DisplayVoteBanMenu(client, target_list[0]); + + return Plugin_Handled; +} diff --git a/addons/sourcemod/scripting/nativevotes_basevotes/votekick.sp b/addons/sourcemod/scripting/nativevotes_basevotes/votekick.sp new file mode 100644 index 0000000..415ab96 --- /dev/null +++ b/addons/sourcemod/scripting/nativevotes_basevotes/votekick.sp @@ -0,0 +1,190 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * NativeVotes Basic Votes Plugin + * Provides kick functionality + * + * NativeVotes (C)2011-2014 Ross Bemrose (Powerlord). All rights reserved. + * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +DisplayVoteKickMenu(client, target) +{ + g_voteClient[VOTE_CLIENTID] = target; + g_voteClient[VOTE_USERID] = GetClientUserId(target); + + char playerName[MAX_NAME_LENGTH]; int r, g, b, a, color; + GetEntityRenderColor(target, r, g, b, a); + color = (r << 16) | (g << 8) | b; + if (color != 0xFFFFFF) { + Format(playerName, sizeof(playerName), "{#%06X}%N\x01", color, target); + } + else { + Format(playerName, sizeof(playerName), "%s%N\x01", GetClientTeam(target) == 2 ? "{red}" : GetClientTeam(target) == 3 ? "{blue}" : "{grey}", target); + } + + GetClientName(target, g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[])); + + LogAction(client, target, "\"%L\" initiated a kick vote against %s", client, playerName); + CShowActivity(client, "%t", "Initiated Vote Kick", playerName); + + g_voteType = voteType:kick; + + if (g_NativeVotes) + { + new Handle:voteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Kick, MenuAction:MENU_ACTIONS_ALL); + // No title, builtin type + NativeVotes_SetTarget(voteMenu, target); + NativeVotes_DisplayToAll(voteMenu, 20); + } + else + { + new Handle:voteMenu = CreateMenu(Handler_VoteCallback, MenuAction:MENU_ACTIONS_ALL); + SetMenuTitle(voteMenu, "Votekick Player"); + AddMenuItem(voteMenu, VOTE_YES, "Yes"); + AddMenuItem(voteMenu, VOTE_NO, "No"); + SetMenuExitButton(voteMenu, false); + VoteMenuToAll(voteMenu, 20); + } +} + +DisplayKickTargetMenu(client) +{ + new Handle:menu = CreateMenu(MenuHandler_Kick); + + decl String:title[100]; + Format(title, sizeof(title), "%T:", "Kick vote", client); + SetMenuTitle(menu, title); + SetMenuExitBackButton(menu, true); + + AddTargetsToMenu(menu, client, false, false); + + DisplayMenu(menu, client, MENU_TIME_FOREVER); +} + +public AdminMenu_VoteKick(Handle:topmenu, + TopMenuAction:action, + TopMenuObject:object_id, + param, + String:buffer[], + maxlength) +{ + if (action == TopMenuAction_DisplayOption) + { + Format(buffer, maxlength, "%T", "Kick vote", param); + } + else if (action == TopMenuAction_SelectOption) + { + DisplayKickTargetMenu(param); + } + else if (action == TopMenuAction_DrawOption) + { + /* disable this option if a vote is already running */ + buffer[0] = !IsNewVoteAllowed() ? ITEMDRAW_IGNORE : ITEMDRAW_DEFAULT; + } +} + +public MenuHandler_Kick(Handle:menu, MenuAction:action, param1, param2) +{ + if (action == MenuAction_End) + { + CloseHandle(menu); + } + else if (action == MenuAction_Cancel) + { + if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE) + { + DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory); + } + } + else if (action == MenuAction_Select) + { + decl String:info[32], String:name[32]; + new userid, target; + + GetMenuItem(menu, param2, info, sizeof(info), _, name, sizeof(name)); + userid = StringToInt(info); + + if ((target = GetClientOfUserId(userid)) == 0) + { + PrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Player no longer available"); + } + else if (!CanUserTarget(param1, target)) + { + PrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Unable to target"); + } + else + { + g_voteArg[0] = '\0'; + DisplayVoteKickMenu(param1, target); + } + } +} + +public Action:Command_Votekick(client, args) +{ + if (args < 1) + { + CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] Usage: sm_votekick [reason]"); + return Plugin_Handled; + } + + if (Internal_IsVoteInProgress()) + { + CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Vote in Progress"); + return Plugin_Handled; + } + + if (!TestVoteDelay(client)) + { + return Plugin_Handled; + } + + decl String:text[256], String:arg[64]; + GetCmdArgString(text, sizeof(text)); + + new len = BreakString(text, arg, sizeof(arg)); + + new target = FindTarget(client, arg); + if (target == -1) + { + return Plugin_Handled; + } + + if (len != -1) + { + strcopy(g_voteArg, sizeof(g_voteArg), text[len]); + } + else + { + g_voteArg[0] = '\0'; + } + + DisplayVoteKickMenu(client, target); + + return Plugin_Handled; +} \ No newline at end of file diff --git a/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp b/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp new file mode 100644 index 0000000..0e7612c --- /dev/null +++ b/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp @@ -0,0 +1,336 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * NativeVotes Basic Votes Plugin + * Provides map functionality + * + * NativeVotes (C)2011-2014 Ross Bemrose (Powerlord). All rights reserved. + * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +new Handle:g_MapList = INVALID_HANDLE; +new g_mapCount; + +new Handle:g_SelectedMaps; +new bool:g_VoteMapInUse; + +DisplayVoteMapMenu(client, mapCount, String:maps[5][]) +{ + LogAction(client, -1, "\"%L\" initiated a map vote.", client); + CShowActivity2(client, "[{lightgreen}NativeVotes\x01] ", "%t", "Initiated Vote Map"); + + g_voteType = voteType:map; + + decl String:resolvedMaps[5][PLATFORM_MAX_PATH]; + decl String:displayNames[5][PLATFORM_MAX_PATH]; + for (new i = 0; i < mapCount; i++) + { + FindMap(maps[i], resolvedMaps[i], sizeof(resolvedMaps[])); + GetMapDisplayName(resolvedMaps[i], displayNames[i], sizeof(displayNames[])); + } + + if (g_NativeVotes && (mapCount == 1 || NativeVotes_IsVoteTypeSupported(NativeVotesType_NextLevelMult)) ) + { + new Handle:voteMenu; + + if (mapCount == 1) + { + strcopy(g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[]), displayNames[0]); + + voteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_ChgLevel, MenuAction:MENU_ACTIONS_ALL); + + // No title, builtin type + NativeVotes_SetDetails(voteMenu, displayNames[0]); + } + else + { + voteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_NextLevelMult, MenuAction:MENU_ACTIONS_ALL); + + g_voteInfo[VOTE_NAME][0] = '\0'; + + // No title, builtin type + for (new i = 0; i < mapCount; i++) + { + NativeVotes_AddItem(voteMenu, resolvedMaps[i], displayNames[i]); + } + } + + NativeVotes_DisplayToAll(voteMenu, 20); + } + else + { + new Handle:voteMenu = CreateMenu(Handler_VoteCallback, MenuAction:MENU_ACTIONS_ALL); + + if (mapCount == 1) + { + strcopy(g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[]), displayNames[0]); + + SetMenuTitle(voteMenu, "Change Map To"); + AddMenuItem(voteMenu, resolvedMaps[0], "Yes"); + AddMenuItem(voteMenu, VOTE_NO, "No"); + } + else + { + g_voteInfo[VOTE_NAME][0] = '\0'; + + SetMenuTitle(voteMenu, "Map Vote"); + for (new i = 0; i < mapCount; i++) + { + AddMenuItem(voteMenu, resolvedMaps[i], displayNames[i]); + } + } + + SetMenuExitButton(voteMenu, false); + VoteMenuToAll(voteMenu, 20); + } +} + +ResetMenu() +{ + g_VoteMapInUse = false; + ClearArray(g_SelectedMaps); +} + +ConfirmVote(client) +{ + new Handle:menu = CreateMenu(MenuHandler_Confirm); + + decl String:title[100]; + Format(title, sizeof(title), "%T:", "Confirm Vote", client); + SetMenuTitle(menu, title); + SetMenuExitBackButton(menu, true); + + decl String:itemtext[256]; + Format(itemtext, sizeof(itemtext), "%T", "Start the Vote", client); + AddMenuItem(menu, "Confirm", itemtext); + + DisplayMenu(menu, client, MENU_TIME_FOREVER); +} + +public MenuHandler_Confirm(Handle:menu, MenuAction:action, param1, param2) +{ + if (action == MenuAction_End) + { + CloseHandle(menu); + g_VoteMapInUse = false; + } + else if (action == MenuAction_Cancel) + { + ResetMenu(); + + if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE) + { + DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory); + } + } + else if (action == MenuAction_Select) + { + decl String:maps[5][64]; + new selectedmaps = GetArraySize(g_SelectedMaps); + + for (new i = 0; i < selectedmaps; i++) + { + GetArrayString(g_SelectedMaps, i, maps[i], sizeof(maps[])); + } + + DisplayVoteMapMenu(param1, selectedmaps, maps); + + ResetMenu(); + } +} + +public MenuHandler_Map(Handle:menu, MenuAction:action, param1, param2) +{ + if (action == MenuAction_Cancel) + { + if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE) + { + ConfirmVote(param1); + } + else // no action was selected. + { + /* Re-enable the menu option */ + ResetMenu(); + } + } + else if (action == MenuAction_DrawItem) + { + decl String:info[32], String:name[32]; + + GetMenuItem(menu, param2, info, sizeof(info), _, name, sizeof(name)); + + if (FindStringInArray(g_SelectedMaps, info) != -1) + { + return ITEMDRAW_IGNORE; + } + else + { + return ITEMDRAW_DEFAULT; + } + } + else if (action == MenuAction_Select) + { + decl String:info[32], String:name[32]; + + GetMenuItem(menu, param2, info, sizeof(info), _, name, sizeof(name)); + + PushArrayString(g_SelectedMaps, info); + + /* Redisplay the list */ + if (GetArraySize(g_SelectedMaps) < 5) + { + DisplayMenu(g_MapList, param1, MENU_TIME_FOREVER); + } + else + { + ConfirmVote(param1); + } + } + else if (action == MenuAction_Display) + { + decl String:title[128]; + Format(title, sizeof(title), "%T", "Please select a map", param1); + SetPanelTitle(Handle:param2, title); + } + + return 0; +} + +public AdminMenu_VoteMap(Handle:topmenu, + TopMenuAction:action, + TopMenuObject:object_id, + param, + String:buffer[], + maxlength) +{ + if (action == TopMenuAction_DisplayOption) + { + Format(buffer, maxlength, "%T", "Map vote", param); + } + else if (action == TopMenuAction_SelectOption) + { + if (!g_VoteMapInUse) + { + ResetMenu(); + g_VoteMapInUse = true; + DisplayMenu(g_MapList, param, MENU_TIME_FOREVER); + } + else + { + PrintToChat(param, "[{lightgreen}NativeVotes\x01] %T", "Map Vote In Use", param); + } + } + else if (action == TopMenuAction_DrawOption) + { + /* disable this option if a vote is already running, theres no maps listed or someone else has already acessed this menu */ + buffer[0] = (!IsNewVoteAllowed() || g_mapCount < 1 || g_VoteMapInUse) ? ITEMDRAW_IGNORE : ITEMDRAW_DEFAULT; + } +} + +public Action:Command_Votemap(client, args) +{ + if (args < 1) + { + CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] Usage: sm_votemap [mapname2] ... [mapname5]"); + return Plugin_Handled; + } + + if (Internal_IsVoteInProgress()) + { + CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Vote in Progress"); + return Plugin_Handled; + } + + if (!TestVoteDelay(client)) + { + return Plugin_Handled; + } + + decl String:text[256]; + GetCmdArgString(text, sizeof(text)); + + decl String:maps[5][64]; + new mapCount; + new len, pos; + + while (pos != -1 && mapCount < 5) + { + pos = BreakString(text[len], maps[mapCount], sizeof(maps[])); + + if (!IsMapValid(maps[mapCount])) + { + CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Map was not found", maps[mapCount]); + return Plugin_Handled; + } + + mapCount++; + + if (pos != -1) + { + len += pos; + } + } + + DisplayVoteMapMenu(client, mapCount, maps); + + return Plugin_Handled; +} + +new Handle:g_map_array = INVALID_HANDLE; +new g_map_serial = -1; + +LoadMapList(Handle:menu) +{ + new Handle:map_array; + + if ((map_array = ReadMapList(g_map_array, + g_map_serial, + "sm_votemap menu", + MAPLIST_FLAG_CLEARARRAY|MAPLIST_FLAG_NO_DEFAULT|MAPLIST_FLAG_MAPSFOLDER)) + != INVALID_HANDLE) + { + g_map_array = map_array; + } + + if (g_map_array == INVALID_HANDLE) + { + return 0; + } + + RemoveAllMenuItems(menu); + + decl String:map_name[64]; + new map_count = GetArraySize(g_map_array); + + for (new i = 0; i < map_count; i++) + { + GetArrayString(g_map_array, i, map_name, sizeof(map_name)); + AddMenuItem(menu, map_name, map_name); + } + + return map_count; +} diff --git a/addons/sourcemod/scripting/nativevotes_funvotes.sp b/addons/sourcemod/scripting/nativevotes_funvotes.sp new file mode 100644 index 0000000..99aa484 --- /dev/null +++ b/addons/sourcemod/scripting/nativevotes_funvotes.sp @@ -0,0 +1,562 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * NativeVotes Fun Votes Plugin + * Implements extra fun vote commands using NativeVotes. + * Based on the SourceMod version. + * + * NativeVotes (C)2011-2014 Ross Bemrose (Powerlord). All rights reserved. + * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#pragma semicolon 1 + +#include +#include + +#undef REQUIRE_PLUGIN +#include +#include + +#define VERSION "25w52a" + +public Plugin:myinfo = +{ + name = "NativeVotes Fun Votes", + author = "Powerlord and AlliedModders LLC", + description = "NativeVotes Fun Vote Commands", + version = VERSION, + url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" +}; + +#define VOTE_NO "###no###" +#define VOTE_YES "###yes###" + +//new Handle:g_hVoteMenu = INVALID_HANDLE; + +new Handle:g_Cvar_Limits[5] = {INVALID_HANDLE, ...}; +new Handle:g_Cvar_Gravity = INVALID_HANDLE; +new Handle:g_Cvar_Alltalk = INVALID_HANDLE; +new Handle:g_Cvar_FF = INVALID_HANDLE; + +// new Handle:g_Cvar_Show = INVALID_HANDLE; + +enum voteType +{ + gravity = 0, + burn, + slay, + alltalk, + ff +}; + +new voteType:g_voteType = voteType:gravity; + +// Menu API does not provide us with a way to pass multiple peices of data with a single +// choice, so some globals are used to hold stuff. +// +#define VOTE_CLIENTID 0 +#define VOTE_USERID 1 +new g_voteClient[2]; /* Holds the target's client id and user id */ + +#define VOTE_NAME 0 +#define VOTE_AUTHID 1 +#define VOTE_IP 2 +new String:g_voteInfo[3][65]; /* Holds the target's name, authid, and IP */ + +new Handle:hTopMenu = INVALID_HANDLE; + +// NativeVotes +new bool:g_NativeVotes; + +#include "nativevotes_funvotes/votegravity.sp" +#include "nativevotes_funvotes/voteburn.sp" +#include "nativevotes_funvotes/voteslay.sp" +#include "nativevotes_funvotes/votealltalk.sp" +#include "nativevotes_funvotes/voteff.sp" + +public OnPluginStart() +{ + LoadTranslations("common.phrases"); + LoadTranslations("basevotes.phrases"); + LoadTranslations("funvotes.phrases"); + LoadTranslations("funcommands.phrases"); + + RegAdminCmd("sm_votegravity", Command_VoteGravity, ADMFLAG_VOTE, "sm_votegravity [amount2] ... [amount5]"); + RegAdminCmd("sm_voteburn", Command_VoteBurn, ADMFLAG_VOTE|ADMFLAG_SLAY, "sm_voteburn "); + RegAdminCmd("sm_voteslay", Command_VoteSlay, ADMFLAG_VOTE|ADMFLAG_SLAY, "sm_voteslay "); + RegAdminCmd("sm_votealltalk", Command_VoteAlltalk, ADMFLAG_VOTE, "sm_votealltalk"); + RegAdminCmd("sm_voteff", Command_VoteFF, ADMFLAG_VOTE, "sm_voteff"); + + g_Cvar_Limits[0] = CreateConVar("sm_vote_gravity", "0.60", "percent required for successful gravity vote.", 0, true, 0.05, true, 1.0); + g_Cvar_Limits[1] = CreateConVar("sm_vote_burn", "0.60", "percent required for successful burn vote.", 0, true, 0.05, true, 1.0); + g_Cvar_Limits[2] = CreateConVar("sm_vote_slay", "0.60", "percent required for successful slay vote.", 0, true, 0.05, true, 1.0); + g_Cvar_Limits[3] = CreateConVar("sm_vote_alltalk", "0.60", "percent required for successful alltalk vote.", 0, true, 0.05, true, 1.0); + g_Cvar_Limits[4] = CreateConVar("sm_vote_ff", "0.60", "percent required for successful friendly fire vote.", 0, true, 0.05, true, 1.0); + CreateConVar("nativevotes_funvotes_version", VERSION, "NativeVotes Fun Votes version", FCVAR_PLUGIN|FCVAR_NOTIFY|FCVAR_DONTRECORD|FCVAR_SPONLY); + + g_Cvar_Gravity = FindConVar("sv_gravity"); + g_Cvar_Alltalk = FindConVar("sv_alltalk"); + g_Cvar_FF = FindConVar("mp_friendlyfire"); + + /* + g_Cvar_Show = FindConVar("sm_vote_show"); + if (g_Cvar_Show == INVALID_HANDLE) + { + g_Cvar_Show = CreateConVar("sm_vote_show", "1", "Show player's votes? Default on.", 0, true, 0.0, true, 1.0); + } + */ +} + +public OnAllPluginsLoaded() +{ + if (FindPluginByFile("basefunvotes.smx") != INVALID_HANDLE) + { + SetFailState("This plugin replaces basefuncommands. You cannot run both at once."); + } + + if (FindPluginByFile("funvotes.smx") != INVALID_HANDLE) + { + LogMessage("Unloading funvotes to prevent conflicts..."); + ServerCommand("sm plugins unload funvotes"); + + decl String:oldPath[PLATFORM_MAX_PATH]; + decl String:newPath[PLATFORM_MAX_PATH]; + + BuildPath(Path_SM, oldPath, sizeof(oldPath), "plugins/funvotes.smx"); + BuildPath(Path_SM, newPath, sizeof(newPath), "plugins/disabled/funvotes.smx"); + if (RenameFile(newPath, oldPath)) + { + LogMessage("Moving funvotes to disabled."); + } + + + //SetFailState("This plugin replaces funvotes. You cannot run both at once."); + + } + + /* Account for late loading */ + new Handle:topmenu; + if (LibraryExists("adminmenu") && ((topmenu = GetAdminTopMenu()) != INVALID_HANDLE)) + { + OnAdminMenuReady(topmenu); + } + + g_NativeVotes = LibraryExists("nativevotes") && NativeVotes_IsVoteTypeSupported(NativeVotesType_Custom_YesNo); +} + +public OnLibraryAdded(const String:name[]) +{ + new Handle:topmenu; + if (StrEqual(name, "adminmenu") && ((topmenu = GetAdminTopMenu()) != INVALID_HANDLE)) + { + OnAdminMenuReady(topmenu); + } + else + if (StrEqual(name, "nativevotes") && NativeVotes_IsVoteTypeSupported(NativeVotesType_Custom_YesNo)) + { + g_NativeVotes = true; + } +} + +public OnLibraryRemoved(const String:name[]) +{ + if (StrEqual(name, "adminmenu")) + { + hTopMenu = INVALID_HANDLE; + } + else + if (StrEqual(name, "nativevotes")) + { + g_NativeVotes = false; + } +} + +public OnAdminMenuReady(Handle:topmenu) +{ + /* Block us from being called twice */ + if (topmenu == hTopMenu) + { + return; + } + + /* Save the Handle */ + hTopMenu = topmenu; + + /* Build the "Voting Commands" category */ + new TopMenuObject:voting_commands = FindTopMenuCategory(hTopMenu, ADMINMENU_VOTINGCOMMANDS); + + if (voting_commands != INVALID_TOPMENUOBJECT) + { + AddToTopMenu(hTopMenu, + "sm_votegravity", + TopMenuObject_Item, + AdminMenu_VoteGravity, + voting_commands, + "sm_votegravity", + ADMFLAG_VOTE); + + AddToTopMenu(hTopMenu, + "sm_voteburn", + TopMenuObject_Item, + AdminMenu_VoteBurn, + voting_commands, + "sm_voteburn", + ADMFLAG_VOTE|ADMFLAG_SLAY); + + AddToTopMenu(hTopMenu, + "sm_voteslay", + TopMenuObject_Item, + AdminMenu_VoteSlay, + voting_commands, + "sm_voteslay", + ADMFLAG_VOTE|ADMFLAG_SLAY); + + AddToTopMenu(hTopMenu, + "sm_votealltalk", + TopMenuObject_Item, + AdminMenu_VoteAllTalk, + voting_commands, + "sm_votealltalk", + ADMFLAG_VOTE); + + AddToTopMenu(hTopMenu, + "sm_voteff", + TopMenuObject_Item, + AdminMenu_VoteFF, + voting_commands, + "sm_voteff", + ADMFLAG_VOTE); + } +} + +public Handler_VoteCallback(Handle:menu, MenuAction:action, param1, param2) +{ + switch (action) + { + case MenuAction_End: + { + //VoteMenuClose(); + CloseHandle(menu); + } + + case MenuAction_Display: + { + decl String:title[64]; + GetMenuTitle(menu, title, sizeof(title)); + + decl String:buffer[255]; + Format(buffer, sizeof(buffer), "%T", title, param1, g_voteInfo[VOTE_NAME]); + + new Handle:panel = Handle:param2; + SetPanelTitle(panel, buffer); + } + + case MenuAction_DisplayItem: + { + decl String:display[64]; + GetMenuItem(menu, param2, "", 0, _, display, sizeof(display)); + + if (strcmp(display, VOTE_NO) == 0 || strcmp(display, VOTE_YES) == 0) + { + decl String:buffer[255]; + Format(buffer, sizeof(buffer), "%T", display, param1); + + return RedrawMenuItem(buffer); + } + } + + case MenuAction_VoteCancel: + { + if (param1 == VoteCancel_NoVotes) + { + CPrintToChatAll("%t", "No Votes Cast"); + } + } + + case MenuAction_VoteEnd: + { + decl String:item[64]; + new Float:percent, Float:limit, votes, totalVotes; + + GetMenuVoteInfo(param2, votes, totalVotes); + GetMenuItem(menu, param1, item, sizeof(item)); + + if (strcmp(item, VOTE_NO) == 0 && param1 == 1) + { + votes = totalVotes - votes; // Reverse the votes to be in relation to the Yes option. + } + + percent = GetVotePercent(votes, totalVotes); + + limit = GetConVarFloat(g_Cvar_Limits[g_voteType]); + + /* :TODO: g_voteClient[userid] needs to be checked */ + + // A multi-argument vote is "always successful", but have to check if its a Yes/No vote. + if ((strcmp(item, VOTE_YES) == 0 && FloatCompare(percent,limit) < 0 && param1 == 0) || (strcmp(item, VOTE_NO) == 0 && param1 == 1)) + { + /* :TODO: g_voteClient[userid] should be used here and set to -1 if not applicable. + */ + LogAction(-1, -1, "Vote failed."); + CPrintToChatAll("%t", "Vote Failed", RoundToNearest(100.0*limit), RoundToNearest(100.0*percent), totalVotes); + } + else + { + CPrintToChatAll("%t", "Vote Successful", RoundToNearest(100.0*percent), totalVotes); + + switch (g_voteType) + { + case (voteType:gravity): + { + CPrintToChatAll("%t", "Cvar changed", "sv_gravity", item); + LogAction(-1, -1, "Changing gravity to %s due to vote.", item); + SetConVarInt(g_Cvar_Gravity, StringToInt(item)); + } + + case (voteType:burn): + { + CPrintToChatAll("%t", "Set target on fire", "_s", g_voteInfo[VOTE_NAME]); + LogAction(-1, g_voteClient[VOTE_CLIENTID], "Vote burn successful, igniting \"%L\"", g_voteClient[VOTE_CLIENTID]); + + IgniteEntity(g_voteClient[VOTE_CLIENTID], 19.8); + } + + case (voteType:slay): + { + CPrintToChatAll("%t", "Slayed player", g_voteInfo[VOTE_NAME]); + LogAction(-1, g_voteClient[VOTE_CLIENTID], "Vote slay successful, slaying \"%L\"", g_voteClient[VOTE_CLIENTID]); + + ExtinguishEntity(g_voteClient[VOTE_CLIENTID]); + ForcePlayerSuicide(g_voteClient[VOTE_CLIENTID]); + } + + case (voteType:alltalk): + { + CPrintToChatAll("%t", "Cvar changed", "sv_alltalk", (GetConVarBool(g_Cvar_Alltalk) ? "0" : "1")); + LogAction(-1, -1, "Changing alltalk to %s due to vote.", (GetConVarBool(g_Cvar_Alltalk) ? "0" : "1")); + SetConVarBool(g_Cvar_Alltalk, !GetConVarBool(g_Cvar_Alltalk)); + } + + case (voteType:ff): + { + CPrintToChatAll("%t", "Cvar changed", "mp_friendlyfire", (GetConVarBool(g_Cvar_FF) ? "0" : "1")); + LogAction(-1, -1, "Changing friendly fire to %s due to vote.", (GetConVarBool(g_Cvar_FF) ? "0" : "1")); + SetConVarBool(g_Cvar_FF, !GetConVarBool(g_Cvar_FF)); + } + } + } + } + } + + return 0; +} + +public Handler_NativeVoteCallback(Handle:menu, MenuAction:action, param1, param2) +{ + switch (action) + { + + case MenuAction_End: + { + NativeVotes_Close(menu); + } + + case MenuAction_Display: + { + new NativeVotesType:nVoteType = NativeVotes_GetType(menu); + if (nVoteType == NativeVotesType_Custom_YesNo || nVoteType == NativeVotesType_Custom_Mult) + { + decl String:title[64]; + NativeVotes_GetTitle(menu, title, sizeof(title)); + + decl String:buffer[255]; + Format(buffer, sizeof(buffer), "%T", title, param1, g_voteInfo[VOTE_NAME]); + + return _:NativeVotes_RedrawVoteTitle(buffer); + } + } + + case MenuAction_VoteCancel: + { + if (param1 == VoteCancel_NoVotes) + { + NativeVotes_DisplayFail(menu, NativeVotesFail_NotEnoughVotes); + CPrintToChatAll("%t", "No Votes Cast"); + } + else + { + NativeVotes_DisplayFail(menu, NativeVotesFail_Generic); + } + } + + case MenuAction_VoteEnd: + { + decl String:item[64]; + new Float:percent, Float:limit, votes, totalVotes; + + new NativeVotesType:nVoteType = NativeVotes_GetType(menu); + + NativeVotes_GetInfo(param2, votes, totalVotes); + NativeVotes_GetItem(menu, param1, item, sizeof(item)); + + if (nVoteType == NativeVotesType_Custom_YesNo && param1 == NATIVEVOTES_VOTE_NO) + { + votes = totalVotes - votes; // Reverse the votes to be in relation to the Yes option. + } + + percent = GetVotePercent(votes, totalVotes); + + limit = GetConVarFloat(g_Cvar_Limits[g_voteType]); + + /* :TODO: g_voteClient[userid] needs to be checked */ + + // A multi-argument vote is "always successful", but have to check if its a Yes/No vote. + if ((nVoteType != NativeVotesType_NextLevelMult && nVoteType != NativeVotesType_Custom_Mult) && ((param1 == NATIVEVOTES_VOTE_YES && FloatCompare(percent,limit) < 0) || (param1 == NATIVEVOTES_VOTE_NO))) + { + /* :TODO: g_voteClient[userid] should be used here and set to -1 if not applicable. + */ + NativeVotes_DisplayFail(menu, NativeVotesFail_Loses); + LogAction(-1, -1, "Vote failed."); + CPrintToChatAll("%t", "Vote Failed", RoundToNearest(100.0*limit), RoundToNearest(100.0*percent), totalVotes); + } + else + { + CPrintToChatAll("%t", "Vote Successful", RoundToNearest(100.0*percent), totalVotes); + + switch (g_voteType) + { + case (voteType:gravity): + { + CPrintToChatAll("%t", "Cvar changed", "sv_gravity", item); + NativeVotes_DisplayPassCustom(menu, "%t", "Cvar changed", "sv_gravity", item); + LogAction(-1, -1, "Changing gravity to %s due to vote.", item); + SetConVarInt(g_Cvar_Gravity, StringToInt(item)); + } + + case (voteType:burn): + { + CPrintToChatAll("%t", "Set target on fire", "_s", g_voteInfo[VOTE_NAME]); + NativeVotes_DisplayPassCustom(menu, "%t", "Set target on fire", "_s", g_voteInfo[VOTE_NAME]); + LogAction(-1, g_voteClient[VOTE_CLIENTID], "Vote burn successful, igniting \"%L\"", g_voteClient[VOTE_CLIENTID]); + + IgniteEntity(g_voteClient[VOTE_CLIENTID], 19.8); + } + + case (voteType:slay): + { + CPrintToChatAll("%t", "Slayed player", g_voteInfo[VOTE_NAME]); + NativeVotes_DisplayPassCustom(menu, "%t", "Slayed player", g_voteInfo[VOTE_NAME]); + LogAction(-1, g_voteClient[VOTE_CLIENTID], "Vote slay successful, slaying \"%L\"", g_voteClient[VOTE_CLIENTID]); + + ExtinguishEntity(g_voteClient[VOTE_CLIENTID]); + ForcePlayerSuicide(g_voteClient[VOTE_CLIENTID]); + } + + case (voteType:alltalk): + { + CPrintToChatAll("%t", "Cvar changed", "sv_alltalk", (GetConVarBool(g_Cvar_Alltalk) ? "0" : "1")); + NativeVotes_DisplayPassCustom(menu, "%t", "Cvar changed", "sv_alltalk", (GetConVarBool(g_Cvar_Alltalk) ? "0" : "1")); + LogAction(-1, -1, "Changing alltalk to %s due to vote.", (GetConVarBool(g_Cvar_Alltalk) ? "0" : "1")); + SetConVarBool(g_Cvar_Alltalk, !GetConVarBool(g_Cvar_Alltalk)); + } + + case (voteType:ff): + { + CPrintToChatAll("%t", "Cvar changed", "mp_friendlyfire", (GetConVarBool(g_Cvar_FF) ? "0" : "1")); + NativeVotes_DisplayPassCustom(menu, "%t", "Cvar changed", "mp_friendlyfire", (GetConVarBool(g_Cvar_FF) ? "0" : "1")); + LogAction(-1, -1, "Changing friendly fire to %s due to vote.", (GetConVarBool(g_Cvar_FF) ? "0" : "1")); + SetConVarBool(g_Cvar_FF, !GetConVarBool(g_Cvar_FF)); + } + } + } + } + } + + return 0; +} + +Float:GetVotePercent(votes, totalVotes) +{ + return FloatDiv(float(votes),float(totalVotes)); +} + +bool:TestVoteDelay(client) +{ + new delay = Internal_CheckVoteDelay(); + + if (delay > 0) + { + if (delay > 60) + { + CReplyToCommand(client, "%t", "Vote Delay Minutes", delay % 60); + } + else + { + CReplyToCommand(client, "%t", "Vote Delay Seconds", delay); + } + + if (g_NativeVotes) + { + NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_Recent, delay); + } + + return false; + } + + return true; +} + +bool:Internal_IsVoteInProgress() +{ + if (g_NativeVotes) + { + return NativeVotes_IsVoteInProgress(); + } + + return IsVoteInProgress(); +} + +Internal_CheckVoteDelay() +{ + if (g_NativeVotes) + { + return NativeVotes_CheckVoteDelay(); + } + + return CheckVoteDelay(); +} + +bool:Internal_IsNewVoteAllowed() +{ + if (g_NativeVotes) + { + return NativeVotes_IsNewVoteAllowed(); + } + + return IsNewVoteAllowed(); +} diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp b/addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp new file mode 100644 index 0000000..703dabc --- /dev/null +++ b/addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp @@ -0,0 +1,140 @@ + /** + * vim: set ts=4 : + * ============================================================================= + * NativeVotes Fun Votes Plugin + * Provides votealltalk functionality + * + * NativeVotes (C)2011-2014 Ross Bemrose (Powerlord). All rights reserved. + * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +DisplayVoteAllTalkMenu(client) +{ + if (Internal_IsVoteInProgress()) + { + CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Vote in Progress"); + return; + } + + if (!TestVoteDelay(client)) + { + return; + } + + LogAction(client, -1, "\"%L\" initiated an alltalk vote.", client); + CShowActivity2(client, "[{lightgreen}NativeVotes\x01] ", "%t", "Initiated Vote Alltalk"); + + g_voteType = voteType:alltalk; + g_voteInfo[VOTE_NAME][0] = '\0'; + + if (g_NativeVotes) + { + new Handle:hVoteMenu; + if (NativeVotes_IsVoteTypeSupported(NativeVotesType_AlltalkOn)) + { + new NativeVotesType:nVoteType; + if (GetConVarBool(g_Cvar_Alltalk)) + { + nVoteType = NativeVotesType_AlltalkOff; + } + else + { + nVoteType = NativeVotesType_AlltalkOn; + } + hVoteMenu = NativeVotes_Create(Handler_NativeVoteCallback, nVoteType, MenuAction:MENU_ACTIONS_ALL); + } + else + { + hVoteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Custom_YesNo, MenuAction:MENU_ACTIONS_ALL); + + if (GetConVarBool(g_Cvar_Alltalk)) + { + NativeVotes_SetTitle(hVoteMenu, "Votealltalk Off"); + } + else + { + NativeVotes_SetTitle(hVoteMenu, "Votealltalk On"); + } + } + + NativeVotes_DisplayToAll(hVoteMenu, 20); + } + else + { + new Handle:hVoteMenu = CreateMenu(Handler_VoteCallback, MenuAction:MENU_ACTIONS_ALL); + + if (GetConVarBool(g_Cvar_Alltalk)) + { + SetMenuTitle(hVoteMenu, "Votealltalk Off"); + } + else + { + SetMenuTitle(hVoteMenu, "Votealltalk On"); + } + + AddMenuItem(hVoteMenu, VOTE_YES, "Yes"); + AddMenuItem(hVoteMenu, VOTE_NO, "No"); + SetMenuExitButton(hVoteMenu, false); + VoteMenuToAll(hVoteMenu, 20); + } +} + + +public AdminMenu_VoteAllTalk(Handle:topmenu, + TopMenuAction:action, + TopMenuObject:object_id, + param, + String:buffer[], + maxlength) +{ + if (action == TopMenuAction_DisplayOption) + { + Format(buffer, maxlength, "%T", "Alltalk vote", param); + } + else if (action == TopMenuAction_SelectOption) + { + DisplayVoteAllTalkMenu(param); + } + else if (action == TopMenuAction_DrawOption) + { + /* disable this option if a vote is already running */ + buffer[0] = !Internal_IsNewVoteAllowed() ? ITEMDRAW_IGNORE : ITEMDRAW_DEFAULT; + } +} + +public Action:Command_VoteAlltalk(client, args) +{ + if (args > 0) + { + CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] Usage: sm_votealltalk"); + return Plugin_Handled; + } + + DisplayVoteAllTalkMenu(client); + + return Plugin_Handled; +} \ No newline at end of file diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp b/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp new file mode 100644 index 0000000..3acaa4f --- /dev/null +++ b/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp @@ -0,0 +1,200 @@ + /** + * vim: set ts=4 : + * ============================================================================= + * NativeVotes Fun Votes Plugin + * Provides voteburn functionality + * + * NativeVotes (C)2011-2014 Ross Bemrose (Powerlord). All rights reserved. + * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +DisplayVoteBurnMenu(client, target, String:name[]) +{ + if (!IsPlayerAlive(target)) + { + CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Cannot be performed on dead", name); + return; + } + + g_voteClient[VOTE_CLIENTID] = target; + + char playerName[MAX_NAME_LENGTH]; int r, g, b, a, color; + GetEntityRenderColor(target, r, g, b, a); + color = (r << 16) | (g << 8) | b; + if (color != 0xFFFFFF) { + Format(playerName, sizeof(playerName), "{#%06X}%N\x01", color, target); + } + else { + Format(playerName, sizeof(playerName), "%s%N\x01", GetClientTeam(target) == 2 ? "{red}" : GetClientTeam(target) == 3 ? "{blue}" : "{grey}", target); + } + + GetClientName(target, g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[])); + + LogAction(client, target, "\"%L\" initiated a burn vote against %s", client, playerName); + CShowActivity2(client, "[{lightgreen}NativeVotes\x01] ", "%t", "Initiated Vote Burn", playerName); + + g_voteType = voteType:burn; + + if (g_NativeVotes) + { + new Handle:hVoteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Custom_YesNo, MenuAction:MENU_ACTIONS_ALL); + NativeVotes_SetTitle(hVoteMenu, "Voteburn player"); + NativeVotes_SetTarget(hVoteMenu, target); + NativeVotes_DisplayToAll(hVoteMenu, 20); + } + else + { + new Handle:hVoteMenu = CreateMenu(Handler_VoteCallback, MenuAction:MENU_ACTIONS_ALL); + SetMenuTitle(hVoteMenu, "Voteburn player"); + AddMenuItem(hVoteMenu, VOTE_YES, "Yes"); + AddMenuItem(hVoteMenu, VOTE_NO, "No"); + SetMenuExitButton(hVoteMenu, false); + VoteMenuToAll(hVoteMenu, 20); + } +} + +DisplayBurnTargetMenu(client) +{ + new Handle:menu = CreateMenu(MenuHandler_Burn); + + decl String:title[100]; + Format(title, sizeof(title), "%T:", "Burn vote", client); + SetMenuTitle(menu, title); + SetMenuExitBackButton(menu, true); + + AddTargetsToMenu(menu, client, true, true); + + DisplayMenu(menu, client, MENU_TIME_FOREVER); +} + +public AdminMenu_VoteBurn(Handle:topmenu, + TopMenuAction:action, + TopMenuObject:object_id, + param, + String:buffer[], + maxlength) +{ + if (action == TopMenuAction_DisplayOption) + { + Format(buffer, maxlength, "%T", "Burn vote", param); + } + else if (action == TopMenuAction_SelectOption) + { + DisplayBurnTargetMenu(param); + } + else if (action == TopMenuAction_DrawOption) + { + /* disable this option if a vote is already running */ + buffer[0] = !Internal_IsNewVoteAllowed() ? ITEMDRAW_IGNORE : ITEMDRAW_DEFAULT; + } +} + +public MenuHandler_Burn(Handle:menu, MenuAction:action, param1, param2) +{ + if (action == MenuAction_End) + { + CloseHandle(menu); + } + else if (action == MenuAction_Cancel) + { + if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE) + { + DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory); + } + } + else if (action == MenuAction_Select) + { + decl String:info[32], String:name[32]; + new userid, target; + + GetMenuItem(menu, param2, info, sizeof(info), _, name, sizeof(name)); + userid = StringToInt(info); + + if ((target = GetClientOfUserId(userid)) == 0) + { + PrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Player no longer available"); + } + else if (!CanUserTarget(param1, target)) + { + PrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Unable to target"); + } + else if (!IsPlayerAlive(target)) + { + PrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Player has since died"); + } + else + { + DisplayVoteBurnMenu(param1, target, name); + } + } +} + +public Action:Command_VoteBurn(client, args) +{ + if (args < 1) + { + CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] Usage: sm_voteburn "); + return Plugin_Handled; + } + + if (Internal_IsVoteInProgress()) + { + CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Vote in Progress"); + return Plugin_Handled; + } + + if (!TestVoteDelay(client)) + { + return Plugin_Handled; + } + + decl String:text[256], String:arg[64]; + GetCmdArgString(text, sizeof(text)); + + BreakString(text, arg, sizeof(arg)); + + decl String:target_name[MAX_TARGET_LENGTH]; + decl target_list[MAXPLAYERS], target_count, bool:tn_is_ml; + + if ((target_count = ProcessTargetString( + arg, + client, + target_list, + MAXPLAYERS, + COMMAND_FILTER_NO_MULTI, + target_name, + sizeof(target_name), + tn_is_ml)) <= 0) + { + ReplyToTargetError(client, target_count); + return Plugin_Handled; + } + + DisplayVoteBurnMenu(client, target_list[0], arg); + + return Plugin_Handled; +} diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp b/addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp new file mode 100644 index 0000000..a46ff4a --- /dev/null +++ b/addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp @@ -0,0 +1,121 @@ + /** + * vim: set ts=4 : + * ============================================================================= + * NativeVotes Fun Votes Plugin + * Provides vote ff functionality + * + * NativeVotes (C)2011-2014 Ross Bemrose (Powerlord). All rights reserved. + * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +DisplayVoteFFMenu(client) +{ + if (Internal_IsVoteInProgress()) + { + CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Vote in Progress"); + return; + } + + if (!TestVoteDelay(client)) + { + return; + } + + LogAction(client, -1, "\"%L\" initiated a friendly fire vote.", client); + CShowActivity2(client, "[{lightgreen}NativeVotes\x01] ", "%t", "Initiated Vote FF"); + + g_voteType = voteType:ff; + g_voteInfo[VOTE_NAME][0] = '\0'; + + if (g_NativeVotes) + { + new Handle:hVoteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Custom_YesNo, MenuAction:MENU_ACTIONS_ALL); + + if (GetConVarBool(g_Cvar_FF)) + { + NativeVotes_SetTitle(hVoteMenu, "Voteff Off"); + } + else + { + NativeVotes_SetTitle(hVoteMenu, "Voteff On"); + } + NativeVotes_DisplayToAll(hVoteMenu, 20); + } + else + { + new Handle:hVoteMenu = CreateMenu(Handler_VoteCallback, MenuAction:MENU_ACTIONS_ALL); + + if (GetConVarBool(g_Cvar_FF)) + { + SetMenuTitle(hVoteMenu, "Voteff Off"); + } + else + { + SetMenuTitle(hVoteMenu, "Voteff On"); + } + + AddMenuItem(hVoteMenu, VOTE_YES, "Yes"); + AddMenuItem(hVoteMenu, VOTE_NO, "No"); + SetMenuExitButton(hVoteMenu, false); + VoteMenuToAll(hVoteMenu, 20); + } +} + +public AdminMenu_VoteFF(Handle:topmenu, + TopMenuAction:action, + TopMenuObject:object_id, + param, + String:buffer[], + maxlength) +{ + if (action == TopMenuAction_DisplayOption) + { + Format(buffer, maxlength, "%T", "Vote FF", param); + } + else if (action == TopMenuAction_SelectOption) + { + DisplayVoteFFMenu(param); + } + else if (action == TopMenuAction_DrawOption) + { + /* disable this option if a vote is already running */ + buffer[0] = !Internal_IsNewVoteAllowed() ? ITEMDRAW_IGNORE : ITEMDRAW_DEFAULT; + } +} + +public Action:Command_VoteFF(client, args) +{ + if (args > 0) + { + CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] Usage: sm_voteff"); + return Plugin_Handled; + } + + DisplayVoteFFMenu(client); + + return Plugin_Handled; +} \ No newline at end of file diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp b/addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp new file mode 100644 index 0000000..04ce41a --- /dev/null +++ b/addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp @@ -0,0 +1,164 @@ + /** + * vim: set ts=4 : + * ============================================================================= + * NativeVotes Fun Votes Plugin + * Provides votegravity functionality + * + * NativeVotes (C)2011-2014 Ross Bemrose (Powerlord). All rights reserved. + * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +DisplayVoteGravityMenu(client,count,String:items[5][]) +{ + LogAction(client, -1, "\"%L\" initiated a gravity vote.", client); + CShowActivity2(client, "[{lightgreen}NativeVotes\x01] ", "%t", "Initiated Vote Gravity"); + + g_voteType = voteType:gravity; + + if (g_NativeVotes && (count == 1 || NativeVotes_IsVoteTypeSupported(NativeVotesType_Custom_Mult)) ) + { + new Handle:hVoteMenu; + if (count == 1) + { + strcopy(g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[]), items[0]); + + hVoteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Custom_YesNo, MenuAction:MENU_ACTIONS_ALL); + NativeVotes_SetTitle(hVoteMenu, "Change Gravity To"); + // No details for custom votes + } + else + { + hVoteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Custom_Mult, MenuAction:MENU_ACTIONS_ALL); + NativeVotes_SetTitle(hVoteMenu, "Gravity Vote"); + for (new i = 0; i < count; i++) + { + NativeVotes_AddItem(hVoteMenu, items[i], items[i]); + } + } + NativeVotes_DisplayToAll(hVoteMenu, 20); + } + else + { + new Handle:hVoteMenu = CreateMenu(Handler_VoteCallback, MenuAction:MENU_ACTIONS_ALL); + + if (count == 1) + { + strcopy(g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[]), items[0]); + + SetMenuTitle(hVoteMenu, "Change Gravity To"); + AddMenuItem(hVoteMenu, items[0], "Yes"); + AddMenuItem(hVoteMenu, VOTE_NO, "No"); + } + else + { + g_voteInfo[VOTE_NAME][0] = '\0'; + + SetMenuTitle(hVoteMenu, "Gravity Vote"); + for (new i = 0; i < count; i++) + { + AddMenuItem(hVoteMenu, items[i], items[i]); + } + } + + SetMenuExitButton(hVoteMenu, false); + VoteMenuToAll(hVoteMenu, 20); + } +} + +public AdminMenu_VoteGravity(Handle:topmenu, + TopMenuAction:action, + TopMenuObject:object_id, + param, + String:buffer[], + maxlength) +{ + if (action == TopMenuAction_DisplayOption) + { + Format(buffer, maxlength, "%T", "Gravity vote", param); + } + else if (action == TopMenuAction_SelectOption) + { + /* Might need a better way of selecting the list of pre-defined gravity choices */ + new String:items[5][5] ={"200","400","800","1600","3200"}; + DisplayVoteGravityMenu(param,5, items); + } + else if (action == TopMenuAction_DrawOption) + { + /* disable this option if a vote is already running */ + buffer[0] = !Internal_IsNewVoteAllowed() ? ITEMDRAW_IGNORE : ITEMDRAW_DEFAULT; + } +} + +public Action:Command_VoteGravity(client, args) +{ + if (args < 1) + { + CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] Usage: sm_votegravity [amount2] ... [amount5]"); + return Plugin_Handled; + } + + if (Internal_IsVoteInProgress()) + { + CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Vote in Progress"); + return Plugin_Handled; + } + + if (!TestVoteDelay(client)) + { + return Plugin_Handled; + } + + decl String:text[256]; + GetCmdArgString(text, sizeof(text)); + + decl String:items[5][64]; + new count; + new len, pos; + + while (pos != -1 && count < 5) + { + pos = BreakString(text[len], items[count], sizeof(items[])); + + decl Float:temp; + if (StringToFloatEx(items[count], temp) == 0) + { + CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Invalid Amount"); + return Plugin_Handled; + } + + count++; + + if (pos != -1) + { + len += pos; + } + } + + DisplayVoteGravityMenu(client, count, items); + + return Plugin_Handled; +} \ No newline at end of file diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp b/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp new file mode 100644 index 0000000..5129dbe --- /dev/null +++ b/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp @@ -0,0 +1,199 @@ + /** + * vim: set ts=4 : + * ============================================================================= + * NativeVotes Fun Votes Plugin + * Provides voteslay functionality + * + * NativeVotes (C)2011-2014 Ross Bemrose (Powerlord). All rights reserved. + * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +DisplayVoteSlayMenu(client, target, String:name[]) +{ + if (!IsPlayerAlive(target)) + { + CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Cannot be performed on dead", name); + return; + } + + g_voteClient[VOTE_CLIENTID] = target; + + char playerName[MAX_NAME_LENGTH]; int r, g, b, a, color; + GetEntityRenderColor(target, r, g, b, a); + color = (r << 16) | (g << 8) | b; + if (color != 0xFFFFFF) { + Format(playerName, sizeof(playerName), "{#%06X}%N\x01", color, target); + } + else { + Format(playerName, sizeof(playerName), "%s%N\x01", GetClientTeam(target) == 2 ? "{red}" : GetClientTeam(target) == 3 ? "{blue}" : "{grey}", target); + } + // Use 'playerName' in printed messages, but keep g_voteInfo[VOTE_NAME] for vote panels + GetClientName(target, g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[])); + + LogAction(client, target, "\"%L\" initiated a slay vote against %s", client, playerName); + CShowActivity2(client, "[{lightgreen}NativeVotes\x01] ", "%t", "Initiated Vote Slay", playerName); + + g_voteType = voteType:slay; + + if (g_NativeVotes) + { + new Handle:hVoteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Custom_YesNo, MenuAction:MENU_ACTIONS_ALL); + NativeVotes_SetTitle(hVoteMenu, "Voteslay Player"); + NativeVotes_DisplayToAll(hVoteMenu, 20); + } + else + { + new Handle:hVoteMenu = CreateMenu(Handler_VoteCallback, MenuAction:MENU_ACTIONS_ALL); + SetMenuTitle(hVoteMenu, "Voteslay Player"); + AddMenuItem(hVoteMenu, VOTE_YES, "Yes"); + AddMenuItem(hVoteMenu, VOTE_NO, "No"); + SetMenuExitButton(hVoteMenu, false); + VoteMenuToAll(hVoteMenu, 20); + } +} + +DisplaySlayTargetMenu(client) +{ + new Handle:menu = CreateMenu(MenuHandler_Slay); + + decl String:title[100]; + Format(title, sizeof(title), "%T:", "Slay vote", client); + SetMenuTitle(menu, title); + SetMenuExitBackButton(menu, true); + + AddTargetsToMenu(menu, client, true, true); + + DisplayMenu(menu, client, MENU_TIME_FOREVER); +} + +public AdminMenu_VoteSlay(Handle:topmenu, + TopMenuAction:action, + TopMenuObject:object_id, + param, + String:buffer[], + maxlength) +{ + if (action == TopMenuAction_DisplayOption) + { + Format(buffer, maxlength, "%T", "Slay vote", param); + } + else if (action == TopMenuAction_SelectOption) + { + DisplaySlayTargetMenu(param); + } + else if (action == TopMenuAction_DrawOption) + { + /* disable this option if a vote is already running */ + buffer[0] = !Internal_IsNewVoteAllowed() ? ITEMDRAW_IGNORE : ITEMDRAW_DEFAULT; + } +} + +public MenuHandler_Slay(Handle:menu, MenuAction:action, param1, param2) +{ + if (action == MenuAction_End) + { + CloseHandle(menu); + } + else if (action == MenuAction_Cancel) + { + if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE) + { + DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory); + } + } + else if (action == MenuAction_Select) + { + decl String:info[32], String:name[32]; + new userid, target; + + GetMenuItem(menu, param2, info, sizeof(info), _, name, sizeof(name)); + userid = StringToInt(info); + + if ((target = GetClientOfUserId(userid)) == 0) + { + PrintToChat(param1, "[SM] %t", "Player no longer available"); + } + else if (!CanUserTarget(param1, target)) + { + PrintToChat(param1, "[SM] %t", "Unable to target"); + } + else if (!IsPlayerAlive(target)) + { + PrintToChat(param1, "[SM] %t", "Player has since died"); + } + else + { + DisplayVoteSlayMenu(param1, target, name); + } + } +} + +public Action:Command_VoteSlay(client, args) +{ + if (args < 1) + { + CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] Usage: sm_voteslay "); + return Plugin_Handled; + } + + if (Internal_IsVoteInProgress()) + { + CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Vote in Progress"); + return Plugin_Handled; + } + + if (!TestVoteDelay(client)) + { + return Plugin_Handled; + } + + decl String:text[256], String:arg[64]; + GetCmdArgString(text, sizeof(text)); + + BreakString(text, arg, sizeof(arg)); + + decl String:target_name[MAX_TARGET_LENGTH]; + decl target_list[MAXPLAYERS], target_count, bool:tn_is_ml; + + if ((target_count = ProcessTargetString( + arg, + client, + target_list, + MAXPLAYERS, + COMMAND_FILTER_NO_MULTI, + target_name, + sizeof(target_name), + tn_is_ml)) <= 0) + { + ReplyToTargetError(client, target_count); + return Plugin_Handled; + } + + DisplayVoteSlayMenu(client, target_list[0], arg); + + return Plugin_Handled; +} diff --git a/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp b/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp index 8bc7f44..b416e78 100644 --- a/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp +++ b/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp @@ -41,7 +41,7 @@ #pragma semicolon 1 #pragma newdecls required -#define VERSION "3.0.0" +#define VERSION "25w52a" ConVar g_Cvar_Votes; ConVar g_Cvar_KickVote; @@ -56,7 +56,7 @@ public Plugin myinfo = { author = "Powerlord", description = "Causes TF2 kick votes to fail against people who the current user can't target.", version = VERSION, - url = "https://forums.alliedmods.net/showthread.php?t=208008" + url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; public void OnPluginStart() @@ -161,7 +161,7 @@ public Action KickVoteHandler(int client, NativeVotesOverride overrideType, cons if (!CanUserTarget(client, target)) { NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_CantKickAdmin); - PrintToChat(client, "%t", "Unable to target"); + CPrintToChat(client, "%t", "Unable to target"); return Plugin_Stop; } diff --git a/addons/sourcemod/scripting/nativevotes_mapchooser.sp b/addons/sourcemod/scripting/nativevotes_mapchooser.sp index ffaccc0..fdc7bd8 100644 --- a/addons/sourcemod/scripting/nativevotes_mapchooser.sp +++ b/addons/sourcemod/scripting/nativevotes_mapchooser.sp @@ -45,7 +45,7 @@ #pragma semicolon 1 #pragma newdecls required -#define VERSION "1.8.0 beta 1" +#define VERSION "25w52a" public Plugin myinfo = { @@ -53,7 +53,7 @@ public Plugin myinfo = author = "AlliedModders LLC and Powerlord", description = "Automated Map Voting", version = VERSION, - url = "https://forums.alliedmods.net/showthread.php?t=208010" + url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; /* Valve ConVars */ @@ -306,7 +306,7 @@ public void OnMapEnd() GetCurrentMap(map, sizeof(map)); g_OldMapList.PushString(map); - if (g_OldMapList.Length > g_Cvar_ExcludeMaps.IntValue) + while (g_OldMapList.Length > g_Cvar_ExcludeMaps.IntValue) { g_OldMapList.Erase(0); } @@ -336,7 +336,7 @@ public Action Command_SetNextmap(int client, int args) { if (args < 1) { - ReplyToCommand(client, "[SM] Usage: sm_setnextmap "); + CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] Usage: sm_setnextmap "); return Plugin_Handled; } @@ -346,13 +346,14 @@ public Action Command_SetNextmap(int client, int args) if (FindMap(map, displayName, sizeof(displayName)) == FindMap_NotFound) { - ReplyToCommand(client, "[SM] %t", "Map was not found", map); + CReplyToCommand(client, "[{lightgreen}MapChooser\x01] %t", "Map was not found", map); return Plugin_Handled; } GetMapDisplayName(displayName, displayName, sizeof(displayName)); + Format(displayName, sizeof(displayName), "\x05%s\x01", displayName); - ShowActivity(client, "%t", "Changed Next Map", displayName); + CShowActivity(client, "[{lightgreen}MapChooser\x01] %t", "Changed Next Map", displayName); LogAction(client, -1, "\"%L\" changed nextmap to \"%s\"", client, map); SetNextMap(map); @@ -669,6 +670,7 @@ void InitiateVote(MapChange when, ArrayList inputlist=null) char displayName[PLATFORM_MAX_PATH]; g_NominateList.GetString(i, map, sizeof(map)); GetMapDisplayName(map, displayName, sizeof(displayName)); + Format(displayName, sizeof(displayName), "\x05%s\x01", displayName); if (g_NativeVotes) { @@ -721,6 +723,7 @@ void InitiateVote(MapChange when, ArrayList inputlist=null) /* Insert the map and increment our count */ char displayName[PLATFORM_MAX_PATH]; GetMapDisplayName(map, displayName, sizeof(displayName)); + Format(displayName, sizeof(displayName), "\x05%s\x01", displayName); if (g_NativeVotes) { g_VoteNative.AddItem(map, displayName); @@ -748,6 +751,7 @@ void InitiateVote(MapChange when, ArrayList inputlist=null) { char displayName[PLATFORM_MAX_PATH]; GetMapDisplayName(map, displayName, sizeof(displayName)); + Format(displayName, sizeof(displayName), "\x05%s\x01", displayName); if (g_NativeVotes) { g_VoteNative.AddItem(map, displayName); @@ -813,7 +817,7 @@ void InitiateVote(MapChange when, ArrayList inputlist=null) } LogAction(-1, -1, "Voting for next map has started."); - PrintToChatAll("[SM] %t", "Nextmap Voting Started"); + CPrintToChatAll("[{lightgreen}MapChooser\x01] %t", "Nextmap Voting Started"); } public void Handler_NV_VoteFinishedGeneric(NativeVote menu, @@ -900,7 +904,7 @@ public void Handler_VoteFinishedGenericShared(const char[] map, } } - PrintToChatAll("[SM] %t", "Current Map Extended", RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes); + CPrintToChatAll("[{lightgreen}MapChooser\x01] %t", "Current Map Extended", RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes); LogAction(-1, -1, "Voting for next map has finished. The current map has been extended."); if (isNativeVotes) @@ -916,7 +920,7 @@ public void Handler_VoteFinishedGenericShared(const char[] map, } else if (strcmp(map, VOTE_DONTCHANGE, false) == 0) { - PrintToChatAll("[SM] %t", "Current Map Stays", RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes); + CPrintToChatAll("[{lightgreen}MapChooser\x01] %t", "Current Map Stays", RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes); LogAction(-1, -1, "Voting for next map has finished. 'No Change' was the winner"); if (isNativeVotes) @@ -955,7 +959,7 @@ public void Handler_VoteFinishedGenericShared(const char[] map, g_VoteNative.DisplayPass(displayName); } - PrintToChatAll("[SM] %t", "Nextmap Voting Finished", displayName, RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes); + CPrintToChatAll("[{lightgreen}MapChooser\x01] %t", "Nextmap Voting Finished", displayName, RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes); LogAction(-1, -1, "Voting for next map has finished. Nextmap: %s.", map); } } @@ -1005,7 +1009,7 @@ public void Handler_NV_MapVoteFinished(NativeVote menu, float map2percent = float(item_votes[1])/ float(num_votes) * 100; - PrintToChatAll("[SM] %t", "Starting Runoff", g_Cvar_RunOffPercent.FloatValue, info1, map1percent, info2, map2percent); + CPrintToChatAll("[{lightgreen}MapChooser\x01] %t", "Starting Runoff", g_Cvar_RunOffPercent.FloatValue, info1, map1percent, info2, map2percent); LogMessage("Voting for next map was indecisive, beginning runoff vote"); return; @@ -1075,7 +1079,7 @@ public void Handler_MapVoteFinished(Menu menu, float map2percent = float(item_info[1][VOTEINFO_ITEM_VOTES])/ float(num_votes) * 100; - PrintToChatAll("[SM] %t", "Starting Runoff", g_Cvar_RunOffPercent.FloatValue, info1, map1percent, info2, map2percent); + CPrintToChatAll("[{lightgreen}MapChooser\x01] %t", "Starting Runoff", g_Cvar_RunOffPercent.FloatValue, info1, map1percent, info2, map2percent); LogMessage("Voting for next map was indecisive, beginning runoff vote"); return; @@ -1098,7 +1102,7 @@ public int Handler_MapVoteMenu(Menu menu, MenuAction action, int param1, int par case MenuAction_Display: { char buffer[255]; - Format(buffer, sizeof(buffer), "%T", "Vote Nextmap", param1); + Format(buffer, sizeof(buffer), "[{lightgreen}MapChooser\x01] %t", "Vote Nextmap", param1); Panel panel = view_as(param2); panel.SetTitle(buffer); @@ -1112,12 +1116,12 @@ public int Handler_MapVoteMenu(Menu menu, MenuAction action, int param1, int par menu.GetItem(param2, map, sizeof(map)); if (strcmp(map, VOTE_EXTEND, false) == 0) { - Format(buffer, sizeof(buffer), "%T", "Extend Map", param1); + Format(buffer, sizeof(buffer), "%t", "Extend Map", param1); return RedrawMenuItem(buffer); } else if (strcmp(map, VOTE_DONTCHANGE, false) == 0) { - Format(buffer, sizeof(buffer), "%T", "Dont Change", param1); + Format(buffer, sizeof(buffer), "%t", "Dont Change", param1); return RedrawMenuItem(buffer); } } @@ -1181,12 +1185,12 @@ public int Handler_NV_MapVoteMenu(NativeVote menu, MenuAction action, int param1 menu.GetItem(param2, map, sizeof(map)); if (strcmp(map, VOTE_EXTEND, false) == 0) { - Format(buffer, sizeof(buffer), "%T", "Extend Map", param1); + Format(buffer, sizeof(buffer), "%t", "Extend Map", param1); return view_as(NativeVotes_RedrawVoteItem(buffer)); } else if (strcmp(map, VOTE_DONTCHANGE, false) == 0) { - Format(buffer, sizeof(buffer), "%T", "Dont Change", param1); + Format(buffer, sizeof(buffer), "%t", "Dont Change", param1); return view_as(NativeVotes_RedrawVoteItem(buffer)); } } diff --git a/addons/sourcemod/scripting/nativevotes_nominations.sp b/addons/sourcemod/scripting/nativevotes_nominations.sp index 1a232a9..0578e3c 100644 --- a/addons/sourcemod/scripting/nativevotes_nominations.sp +++ b/addons/sourcemod/scripting/nativevotes_nominations.sp @@ -43,7 +43,7 @@ #pragma semicolon 1 #pragma newdecls required -#define VERSION "1.8.0 beta 4" +#define VERSION "25w52a" public Plugin myinfo = { @@ -51,11 +51,12 @@ public Plugin myinfo = author = "AlliedModders LLC and Powerlord", description = "Provides Map Nominations", version = VERSION, - url = "https://forums.alliedmods.net/showthread.php?t=208010" + url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; ConVar g_Cvar_ExcludeOld; ConVar g_Cvar_ExcludeCurrent; +ConVar g_Cvar_MaxMatches; Menu g_MapMenu = null; ArrayList g_MapList = null; @@ -88,10 +89,12 @@ public void OnPluginStart() g_Cvar_ExcludeOld = CreateConVar("sm_nominate_excludeold", "1", "Specifies if the current map should be excluded from the Nominations list", 0, true, 0.00, true, 1.0); g_Cvar_ExcludeCurrent = CreateConVar("sm_nominate_excludecurrent", "1", "Specifies if the MapChooser excluded maps should also be excluded from Nominations", 0, true, 0.00, true, 1.0); + g_Cvar_MaxMatches = CreateConVar("sm_nominate_maxfound", "0", "Maximum number of nomination matches to add to the menu. 0 = infinite.", _, true, 0.0); RegConsoleCmd("sm_nominate", Command_Nominate); RegAdminCmd("sm_nominate_addmap", Command_Addmap, ADMFLAG_CHANGEMAP, "sm_nominate_addmap - Forces a map to be on the next mapvote."); + RegAdminCmd("sm_reload_nominations", Cmd_ReloadNominations, ADMFLAG_RCON, "Reload the nomination map cycle in-place"); g_mapTrie = new StringMap(); } @@ -161,7 +164,7 @@ public void OnConfigsExecuted() } } - delete g_MapMenu; + BuildMapMenu(); } public void OnNominationRemoved(const char[] map, int owner) @@ -190,7 +193,7 @@ public Action Command_Addmap(int client, int args) { if (args < 1) { - ReplyToCommand(client, "[SM] Usage: sm_nominate_addmap "); + CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] Usage: sm_nominate_addmap "); return Plugin_Handled; } @@ -201,17 +204,18 @@ public Action Command_Addmap(int client, int args) if (FindMap(mapname, resolvedMap, sizeof(resolvedMap)) == FindMap_NotFound) { // We couldn't resolve the map entry to a filename, so... - ReplyToCommand(client, "%t", "Map was not found", mapname); + CReplyToCommand(client, "[{lightgreen}Nominations\x01] %t", "Map was not found", mapname); return Plugin_Handled; } char displayName[PLATFORM_MAX_PATH]; GetMapDisplayName(resolvedMap, displayName, sizeof(displayName)); + Format(displayName, sizeof(displayName), "\x05%s\x01", displayName); int status; if (!g_mapTrie.GetValue(resolvedMap, status)) { - ReplyToCommand(client, "%t", "Map was not found", displayName); + CReplyToCommand(client, "[{lightgreen}Nominations\x01] %t", "Map Not In Pool", displayName); return Plugin_Handled; } @@ -220,7 +224,7 @@ public Action Command_Addmap(int client, int args) if (result > Nominate_Replaced) { /* We assume already in vote is the casue because the maplist does a Map Validity check and we forced, so it can't be full */ - ReplyToCommand(client, "%t", "Map Already In Vote", displayName); + CReplyToCommand(client, "[{lightgreen}Nominations\x01] %t", "Map Already In Vote", displayName); return Plugin_Handled; } @@ -229,12 +233,18 @@ public Action Command_Addmap(int client, int args) g_mapTrie.SetValue(resolvedMap, MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_NOMINATED); - ReplyToCommand(client, "%t", "Map Inserted", displayName); + CReplyToCommand(client, "[{lightgreen}Nominations\x01] %t", "Map Inserted", displayName); LogAction(client, -1, "\"%L\" inserted map \"%s\".", client, mapname); return Plugin_Handled; } +Action Cmd_ReloadNominations(int client, int args) +{ + OnConfigsExecuted(); + return Plugin_Handled; +} + public void OnClientSayCommand_Post(int client, const char[] command, const char[] sArgs) { if (!client) @@ -246,7 +256,7 @@ public void OnClientSayCommand_Post(int client, const char[] command, const char { ReplySource old = SetCmdReplySource(SM_REPLY_TO_CHAT); - AttemptNominate(client); + OpenNominationMenu(client); SetCmdReplySource(old); } @@ -259,46 +269,126 @@ public Action Command_Nominate(int client, int args) return Plugin_Handled; } + ReplySource source = GetCmdReplySource(); + if (args == 0) { - AttemptNominate(client); + OpenNominationMenu(client); return Plugin_Handled; } char mapname[PLATFORM_MAX_PATH]; GetCmdArg(1, mapname, sizeof(mapname)); - return Internal_CommandNominate(client, mapname, false); + ArrayList results = new ArrayList(); + int matches = FindMatchingMaps(g_MapList, results, mapname); + + char mapResult[PLATFORM_MAX_PATH]; + + if (matches <= 0) + { + CReplyToCommand(client, "[{lightgreen}Nominations\x01] %t", "Map was not found", mapname); + } + // One result + else if (matches == 1) + { + // Get the result and nominate it + g_MapList.GetString(results.Get(0), mapResult, sizeof(mapResult)); + AttemptNominate(client, mapResult, sizeof(mapResult), false); +} + else if (matches > 1) + { + if (source == SM_REPLY_TO_CONSOLE) + { + // if source is console, attempt instead of displaying menu. + AttemptNominate(client, mapname, sizeof(mapname), false); + delete results; + return Plugin_Handled; + } + + // Display results to the client and end + Menu menu = new Menu(MenuHandler_MapSelect, MENU_ACTIONS_DEFAULT|MenuAction_DrawItem|MenuAction_DisplayItem); + menu.SetTitle("Select map"); + + for (int i = 0; i < results.Length; i++) + { + g_MapList.GetString(results.Get(i), mapResult, sizeof(mapResult)); + + char displayName[PLATFORM_MAX_PATH]; + GetMapDisplayName(mapResult, displayName, sizeof(displayName)); + + menu.AddItem(mapResult, displayName); + } + + menu.Display(client, 30); + } + + delete results; + + return Plugin_Handled; } -Action Internal_CommandNominate(int client, const char[] mapname, bool isVoteMenu) +int FindMatchingMaps(ArrayList mapList, ArrayList results, const char[] input) { - char resolvedMap[PLATFORM_MAX_PATH]; + int map_count = mapList.Length; + + if (!map_count) + { + return -1; + } + + int matches = 0; + char map[PLATFORM_MAX_PATH]; + + int maxmatches = g_Cvar_MaxMatches.IntValue; + + for (int i = 0; i < map_count; i++) + { + mapList.GetString(i, map, sizeof(map)); + if (StrContains(map, input) != -1) + { + results.Push(i); + matches++; + + if (maxmatches > 0 && matches >= maxmatches) + { + break; + } + } + } + + return matches; +} + +void AttemptNominate(int client, const char[] map, int size, bool isVoteMenu) +{ + char mapname[PLATFORM_MAX_PATH]; - if (FindMap(mapname, resolvedMap, sizeof(resolvedMap)) == FindMap_NotFound) + if (FindMap(map, mapname, size) == FindMap_NotFound) { // We couldn't resolve the map entry to a filename, so... - ReplyToCommand(client, "%t", "Map was not found", mapname); + CReplyToCommand(client, "[{lightgreen}Nominations\x01] %t", "Map was not found", mapname); if (isVoteMenu && g_NativeVotes) { NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_MapNotFound); } - return Plugin_Handled; + return; } char displayName[PLATFORM_MAX_PATH]; - GetMapDisplayName(resolvedMap, displayName, sizeof(displayName)); + GetMapDisplayName(mapname, displayName, sizeof(displayName)); + Format(displayName, sizeof(displayName), "\x05%s\x01", displayName); int status; - if (!g_mapTrie.GetValue(resolvedMap, status)) + if (!g_mapTrie.GetValue(mapname, status)) { - ReplyToCommand(client, "%t", "Map was not found", displayName); + CReplyToCommand(client, "[{lightgreen}Nominations\x01] %t", "Map Not In Pool", displayName); if (isVoteMenu && g_NativeVotes) { NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_MapNotValid); } - return Plugin_Handled; + return; } if ((status & MAPSTATUS_DISABLED) == MAPSTATUS_DISABLED) @@ -309,7 +399,7 @@ Action Internal_CommandNominate(int client, const char[] mapname, bool isVoteMen { NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_MapNotValid); } - ReplyToCommand(client, "[SM] %t", "Can't Nominate Current Map"); + CReplyToCommand(client, "[{lightgreen}Nominations\x01] %t", "Can't Nominate Current Map"); } if ((status & MAPSTATUS_EXCLUDE_PREVIOUS) == MAPSTATUS_EXCLUDE_PREVIOUS) @@ -318,7 +408,7 @@ Action Internal_CommandNominate(int client, const char[] mapname, bool isVoteMen { NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_MapNotValid); } - ReplyToCommand(client, "[SM] %t", "Map in Exclude List"); + CReplyToCommand(client, "[{lightgreen}Nominations\x01] %t", "Map in Exclude List"); } if ((status & MAPSTATUS_EXCLUDE_NOMINATED) == MAPSTATUS_EXCLUDE_NOMINATED) @@ -327,13 +417,13 @@ Action Internal_CommandNominate(int client, const char[] mapname, bool isVoteMen { NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_MapNotValid); } - ReplyToCommand(client, "[SM] %t", "Map Already Nominated"); + CReplyToCommand(client, "[{lightgreen}Nominations\x01] %t", "Map Already Nominated"); } - return Plugin_Handled; + return; } - NominateResult result = NominateMap(resolvedMap, false, client); + NominateResult result = NominateMap(mapname, false, client); if (result > Nominate_Replaced) { @@ -343,7 +433,7 @@ Action Internal_CommandNominate(int client, const char[] mapname, bool isVoteMen { NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_MapNotValid); } - ReplyToCommand(client, "%t", "Map Already In Vote", displayName); + CReplyToCommand(client, "[{lightgreen}Nominations\x01] %t", "Map Already In Vote", displayName); } else { @@ -351,40 +441,48 @@ Action Internal_CommandNominate(int client, const char[] mapname, bool isVoteMen { NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_MapNotValid); } - ReplyToCommand(client, "[SM] %t", "Map Already Nominated"); + CReplyToCommand(client, "[{lightgreen}Nominations\x01] %t", "Max Nominations"); } - return Plugin_Handled; + return; } /* Map was nominated! - Disable the menu item and update the trie */ - g_mapTrie.SetValue(resolvedMap, MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_NOMINATED); + g_mapTrie.SetValue(mapname, MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_NOMINATED); - char name[MAX_NAME_LENGTH]; - GetClientName(client, name, sizeof(name)); - PrintToChatAll("[SM] %t", "Map Nominated", name, displayName); + char name[MAX_NAME_LENGTH]; int r, g, b, a, color; + GetEntityRenderColor(client, r, g, b, a); + color = (r << 16) | (g << 8) | b; + if (color != 0xFFFFFF) { + Format(name, sizeof(name), "{#%06X}%N\x01", color, client); + } + else { + Format(name, sizeof(name), "%s%N\x01", GetClientTeam(client) == 2 ? "{red}" : GetClientTeam(client) == 3 ? "{blue}" : "{grey}", client); + } + + if (result == Nominate_Added) { + CPrintToChatAll("[{lightgreen}Nominations\x01] %t", "Map Nominated", name, displayName); + } else { + CReplyToCommand(client, "[{lightgreen}Nominations\x01] %t", "Map Nominated", name, displayName); + } - return Plugin_Handled; + return; } -void AttemptNominate(int client) +void OpenNominationMenu(int client) { - if (g_MapMenu == null) - { - BuildMapMenu(); - } - g_MapMenu.SetTitle("%T", "Nominate Title", client); + g_MapMenu.SetTitle("%t", "Nominate Title", client); g_MapMenu.Display(client, MENU_TIME_FOREVER); - - return; } void BuildMapMenu() { + delete g_MapMenu; + g_mapTrie.Clear(); - g_MapMenu = new Menu(Handler_MapSelectMenu, MENU_ACTIONS_DEFAULT|MenuAction_DrawItem|MenuAction_DisplayItem); + g_MapMenu = new Menu(MenuHandler_MapSelect, MENU_ACTIONS_DEFAULT|MenuAction_DrawItem|MenuAction_DisplayItem); char map[PLATFORM_MAX_PATH]; @@ -439,49 +537,23 @@ void BuildMapMenu() delete excludeMaps; } -public int Handler_MapSelectMenu(Menu menu, MenuAction action, int param1, int param2) +public int MenuHandler_MapSelect(Menu menu, MenuAction action, int param1, int param2) { switch (action) { case MenuAction_Select: { - char map[PLATFORM_MAX_PATH], name[MAX_NAME_LENGTH], displayName[PLATFORM_MAX_PATH]; - menu.GetItem(param2, map, sizeof(map), _, displayName, sizeof(displayName)); - - GetClientName(param1, name, sizeof(name)); - - NominateResult result = NominateMap(map, false, param1); - - /* Don't need to check for InvalidMap because the menu did that already */ - if (result == Nominate_AlreadyInVote) - { - PrintToChat(param1, "[SM] %t", "Map Already Nominated"); - return 0; - } - else if (result == Nominate_VoteFull) - { - PrintToChat(param1, "[SM] %t", "Max Nominations"); - return 0; - } - - g_mapTrie.SetValue(map, MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_NOMINATED); - - if (result == Nominate_Replaced) - { - PrintToChatAll("[SM] %t", "Map Nomination Changed", name, displayName); - return 0; - } - - PrintToChatAll("[SM] %t", "Map Nominated", name, displayName); + char mapname[PLATFORM_MAX_PATH]; + // Get the map name and attempt to nominate it + menu.GetItem(param2, mapname, sizeof(mapname)); + AttemptNominate(param1, mapname, sizeof(mapname), false); } - case MenuAction_DrawItem: { char map[PLATFORM_MAX_PATH]; menu.GetItem(param2, map, sizeof(map)); int status; - if (!g_mapTrie.GetValue(map, status)) { LogError("Menu selection of item not in trie. Major logic problem somewhere."); @@ -494,49 +566,55 @@ public int Handler_MapSelectMenu(Menu menu, MenuAction action, int param1, int p } return ITEMDRAW_DEFAULT; - } - case MenuAction_DisplayItem: { - char map[PLATFORM_MAX_PATH], displayName[PLATFORM_MAX_PATH]; - menu.GetItem(param2, map, sizeof(map), _, displayName, sizeof(displayName)); + char mapname[PLATFORM_MAX_PATH]; + menu.GetItem(param2, mapname, sizeof(mapname)); int status; - if (!g_mapTrie.GetValue(map, status)) + if (!g_mapTrie.GetValue(mapname, status)) { LogError("Menu selection of item not in trie. Major logic problem somewhere."); return 0; } - char display[PLATFORM_MAX_PATH + 64]; - if ((status & MAPSTATUS_DISABLED) == MAPSTATUS_DISABLED) { + char displayName[PLATFORM_MAX_PATH]; + GetMapDisplayName(mapname, displayName, sizeof(displayName)); + if ((status & MAPSTATUS_EXCLUDE_CURRENT) == MAPSTATUS_EXCLUDE_CURRENT) { - Format(display, sizeof(display), "%s (%T)", displayName, "Current Map", param1); - return RedrawMenuItem(display); + Format(mapname, sizeof(mapname), "%s (%T)", displayName, "Current Map", param1); + return RedrawMenuItem(mapname); } if ((status & MAPSTATUS_EXCLUDE_PREVIOUS) == MAPSTATUS_EXCLUDE_PREVIOUS) { - Format(display, sizeof(display), "%s (%T)", displayName, "Recently Played", param1); - return RedrawMenuItem(display); + Format(mapname, sizeof(mapname), "%s (%T)", displayName, "Recently Played", param1); + return RedrawMenuItem(mapname); } if ((status & MAPSTATUS_EXCLUDE_NOMINATED) == MAPSTATUS_EXCLUDE_NOMINATED) { - Format(display, sizeof(display), "%s (%T)", displayName, "Nominated", param1); - return RedrawMenuItem(display); + Format(mapname, sizeof(mapname), "%s (%T)", displayName, "Nominated", param1); + return RedrawMenuItem(mapname); } } + } + case MenuAction_End: + { + // This check allows the plugin to use the same callback + // for the main menu and the match menu. + if (menu != g_MapMenu) + { + delete menu; + } - return 0; } } - return 0; } @@ -593,11 +671,15 @@ public Action Menu_Nominate(int client, NativeVotesOverride overrideType, const ReplySource old = SetCmdReplySource(SM_REPLY_TO_CHAT); - Action myReturn = Internal_CommandNominate(client, voteArgument, true); + // awful hack, but whatever + char mapname[PLATFORM_MAX_PATH]; + strcopy(mapname, sizeof(mapname), voteArgument); + + AttemptNominate(client, mapname, sizeof(mapname), true); SetCmdReplySource(old); - return myReturn; + return Plugin_Handled; } public Action NativeVotes_OverrideMaps(StringMap mapList) @@ -647,4 +729,4 @@ public Action NativeVotes_OverrideMaps(StringMap mapList) } return Plugin_Continue; -} +} \ No newline at end of file diff --git a/addons/sourcemod/scripting/nativevotes_rockthevote.sp b/addons/sourcemod/scripting/nativevotes_rockthevote.sp index de79dfd..35855f1 100644 --- a/addons/sourcemod/scripting/nativevotes_rockthevote.sp +++ b/addons/sourcemod/scripting/nativevotes_rockthevote.sp @@ -50,7 +50,7 @@ #pragma semicolon 1 #pragma newdecls required -#define VERSION "1.8.0 beta 1" +#define VERSION "25w52a" public Plugin myinfo = { @@ -58,7 +58,7 @@ public Plugin myinfo = author = "AlliedModders LLC and Powerlord", description = "Provides RTV Map Voting", version = VERSION, - url = "https://forums.alliedmods.net/showthread.php?t=208010" + url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; ConVar g_Cvar_Needed; @@ -68,7 +68,6 @@ ConVar g_Cvar_Interval; ConVar g_Cvar_ChangeTime; ConVar g_Cvar_RTVPostVoteAction; -bool g_CanRTV = false; // True if RTV loaded maps and is active. bool g_RTVAllowed = false; // True if RTV is available to players. Used to delay rtv votes. int g_Voters = 0; // Total voters connected. Doesn't include fake clients. int g_Votes = 0; // Total number of "say rtv" votes @@ -99,8 +98,20 @@ public void OnPluginStart() g_Cvar_RTVPostVoteAction = CreateConVar("sm_rtv_postvoteaction", "0", "What to do with RTV's after a mapvote has completed. 0 - Allow, success = instant change, 1 - Deny", _, true, 0.0, true, 1.0); RegConsoleCmd("sm_rtv", Command_RTV); + RegAdminCmd("sm_forcertv", Command_ForceRTV, ADMFLAG_CHANGEMAP); AutoExecConfig(true, "rtv"); + + OnMapEnd(); + + /* Handle late load */ + for (int i=1; i<=MaxClients; i++) + { + if (IsClientConnected(i)) + { + OnClientConnected(i); + } + } } public void OnPluginEnd() @@ -164,68 +175,41 @@ public void TF2_OnWaitingForPlayersEnd() g_Warmup = false; } -public void OnMapStart() +public void OnMapEnd() { + g_RTVAllowed = false; g_Voters = 0; g_Votes = 0; g_VotesNeeded = 0; g_InChange = false; - - /* Handle late load */ - for (int i=1; i<=MaxClients; i++) - { - if (IsClientConnected(i)) - { - OnClientConnected(i); - } - } -} - -public void OnMapEnd() -{ - g_Warmup = false; - g_CanRTV = false; - g_RTVAllowed = false; } public void OnConfigsExecuted() { - g_CanRTV = true; - g_RTVAllowed = false; - g_RTVTime = GetTime() + g_Cvar_Interval.IntValue; CreateTimer(g_Cvar_InitialDelay.FloatValue, Timer_DelayRTV, _, TIMER_FLAG_NO_MAPCHANGE); } public void OnClientConnected(int client) { - if(IsFakeClient(client)) - return; - - g_Voted[client] = false; - - g_Voters++; - g_VotesNeeded = RoundToFloor(float(g_Voters) * g_Cvar_Needed.FloatValue); - - return; + if (!IsFakeClient(client)) + { + g_Voters++; + g_VotesNeeded = RoundToCeil(float(g_Voters) * g_Cvar_Needed.FloatValue); + } } public void OnClientDisconnect(int client) -{ - if(IsFakeClient(client)) - return; - - if(g_Voted[client]) +{ + if (g_Voted[client]) { g_Votes--; + g_Voted[client] = false; } - g_Voters--; - - g_VotesNeeded = RoundToFloor(float(g_Voters) * g_Cvar_Needed.FloatValue); - - if (!g_CanRTV) + if (!IsFakeClient(client)) { - return; + g_Voters--; + g_VotesNeeded = RoundToCeil(float(g_Voters) * g_Cvar_Needed.FloatValue); } if (g_Votes && @@ -244,7 +228,7 @@ public void OnClientDisconnect(int client) public void OnClientSayCommand_Post(int client, const char[] command, const char[] sArgs) { - if (!g_CanRTV || !client) + if (!client || IsChatTrigger()) { return; } @@ -261,7 +245,7 @@ public void OnClientSayCommand_Post(int client, const char[] command, const char public Action Command_RTV(int client, int args) { - if (!g_CanRTV || !client) + if (!client) { return Plugin_Handled; } @@ -271,11 +255,68 @@ public Action Command_RTV(int client, int args) return Plugin_Handled; } +public Action Command_ForceRTV(int client, int args) +{ + if (!g_RTVAllowed) + { + g_RTVAllowed = true; + } + + StartRTV(); + + return Plugin_Handled; +} + + +void RTVUndoMenuHandler(Menu menu, MenuAction action, int client, int param2) +{ + if (action == MenuAction_Select) + { + if (param2 == 0) // Yes + { + if (g_Voted[client]) + { + char name[MAX_NAME_LENGTH]; int r, g, b, a, color; + GetEntityRenderColor(client, r, g, b, a); + color = (r << 16) | (g << 8) | b; + if (color != 0xFFFFFF) { + Format(name, sizeof(name), "{#%06X}%N\x01", color, client); + } + else { + Format(name, sizeof(name), "%s%N\x01", GetClientTeam(client) == 2 ? "{red}" : GetClientTeam(client) == 3 ? "{blue}" : "{grey}", client); + } + + g_Voted[client] = false; + if (g_Votes > 0) g_Votes--; + //CReplyToCommand(client, "[{lightgreen}Rock The Vote\x01] %t", "Cancelled Vote"); + CPrintToChatAll("[{lightgreen}Rock The Vote\x01] %s: %t", name, "Cancelled Vote"); + } + } + } + delete menu; +} + +void ShowRTVUndoMenu(int client) +{ + char yes[64], no[64]; + Format(yes, sizeof(yes), "%T", "Yes", client); + Format(no, sizeof(no), "%T", "No", client); + + char title[128]; + Format(title, sizeof(title), "%T", "Cancel vote", client); + + Menu menu = new Menu(RTVUndoMenuHandler); + menu.SetTitle(title); + menu.AddItem("yes", yes); + menu.AddItem("no", no); + menu.Display(client, MENU_TIME_FOREVER); +} + void AttemptRTV(int client, bool isVoteMenu=false) { if (!g_RTVAllowed || (g_Cvar_RTVPostVoteAction.IntValue == 1 && HasEndOfMapVoteFinished())) { - ReplyToCommand(client, "[SM] %t", "RTV Not Allowed"); + CReplyToCommand(client, "[{lightgreen}Rock The Vote\x01] %t", "RTV Not Allowed"); if (isVoteMenu && g_NativeVotes) { if (g_Warmup) @@ -300,13 +341,13 @@ void AttemptRTV(int client, bool isVoteMenu=false) if (!CanMapChooserStartVote()) { - ReplyToCommand(client, "[SM] %t", "RTV Started"); + CReplyToCommand(client, "[{lightgreen}Rock The Vote\x01] %t", "RTV Started"); return; } if (GetClientCount(true) < g_Cvar_MinPlayers.IntValue) { - ReplyToCommand(client, "[SM] %t", "Minimal Players Not Met"); + CReplyToCommand(client, "[{lightgreen}Rock The Vote\x01] %t", "Minimal Players Not Met"); if (isVoteMenu && g_NativeVotes) { NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_Loading); @@ -316,21 +357,24 @@ void AttemptRTV(int client, bool isVoteMenu=false) if (g_Voted[client]) { - ReplyToCommand(client, "[SM] %t", "Already Voted", g_Votes, g_VotesNeeded); - if (isVoteMenu && g_NativeVotes) - { - NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_AlreadyActive); - } + ShowRTVUndoMenu(client); return; } - char name[MAX_NAME_LENGTH]; - GetClientName(client, name, sizeof(name)); + char name[MAX_NAME_LENGTH]; int r, g, b, a, color; + GetEntityRenderColor(client, r, g, b, a); + color = (r << 16) | (g << 8) | b; + if (color != 0xFFFFFF) { + Format(name, sizeof(name), "{#%06X}%N\x01", color, client); + } + else { + Format(name, sizeof(name), "%s%N\x01", GetClientTeam(client) == 2 ? "{red}" : GetClientTeam(client) == 3 ? "{blue}" : "{grey}", client); + } g_Votes++; g_Voted[client] = true; - PrintToChatAll("[SM] %t", "RTV Requested", name, g_Votes, g_VotesNeeded); + CPrintToChatAll("[{lightgreen}Rock The Vote\x01] %t", "RTV Requested", name, g_Votes, g_VotesNeeded); if (g_Votes >= g_VotesNeeded) { @@ -341,6 +385,8 @@ void AttemptRTV(int client, bool isVoteMenu=false) public Action Timer_DelayRTV(Handle timer) { g_RTVAllowed = true; + + return Plugin_Continue; } void StartRTV() @@ -357,8 +403,9 @@ void StartRTV() if (GetNextMap(map, sizeof(map))) { GetMapDisplayName(map, map, sizeof(map)); + Format(map, sizeof(map), "\x05%s\x01", map); - PrintToChatAll("[SM] %t", "Changing Maps", map); + CPrintToChatAll("[{lightgreen}Rock The Vote\x01] %t", "Changing Maps", map); CreateTimer(5.0, Timer_ChangeMap, _, TIMER_FLAG_NO_MAPCHANGE); g_InChange = true; diff --git a/addons/sourcemod/scripting/nativevotes_votemanager_test.sp b/addons/sourcemod/scripting/nativevotes_votemanager_test.sp deleted file mode 100644 index db992b6..0000000 --- a/addons/sourcemod/scripting/nativevotes_votemanager_test.sp +++ /dev/null @@ -1,252 +0,0 @@ -/** - * vim: set ts=4 : - * ============================================================================= - * NativeVotes VoteManager Test - * Test the VoteManger functionality of NativeVotes - * - * NativeVotes VoteManager Test (C)2014 Powerlord (Ross Bemrose). All rights - * reserved. - * ============================================================================= - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, version 3.0, as published by the - * Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - * - * As a special exception, AlliedModders LLC gives you permission to link the - * code of this program (as well as its derivative works) to "Half-Life 2," the - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software - * by the Valve Corporation. You must obey the GNU General Public License in - * all respects for all other code used. Additionally, AlliedModders LLC grants - * this exception to all derivative works. AlliedModders LLC defines further - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), - * or . - * - * Version: $Id$ - */ -#include - -#pragma newdecls required -#include "include/nativevotes" // Not optional - -#pragma semicolon 1 - -#define VERSION "1.2.0" - -ConVar g_Cvar_Enabled; - -public Plugin myinfo = { - name = "NativeVotes VoteManager Test", - author = "Powerlord", - description = "Test the VoteManger functionality of NativeVotes", - version = VERSION, - url = "https://forums.alliedmods.net/showthread.php?t=208008" -}; - -public void OnPluginStart() -{ - CreateConVar("nativevotes_votemanagertest_version", VERSION, "NativeVotes VoteManager Test version", FCVAR_NOTIFY|FCVAR_DONTRECORD|FCVAR_SPONLY); - g_Cvar_Enabled = CreateConVar("nativevotes_votemanagertest_enable", "1", "Enable NativeVotes VoteManager Test?", FCVAR_NOTIFY|FCVAR_DONTRECORD, true, 0.0, true, 1.0); - HookConVarChange(g_Cvar_Enabled, EnabledChanged); -} - -public void OnAllPluginsLoaded() -{ - if (g_Cvar_Enabled.BoolValue) - { - NativeVotes_RegisterVoteCommand(NativeVotesOverride_Restart, CallVoteTestHandler); - NativeVotes_RegisterVoteCommand(NativeVotesOverride_Kick, CallKickVoteHandler); - NativeVotes_RegisterVoteCommand(NativeVotesOverride_Scramble, CallVoteAdminTestHandler, CallVoteAdminVisHandler); - } -} - -public void OnPluginEnd() -{ - NativeVotes_UnregisterVoteCommand(NativeVotesOverride_Restart, CallVoteTestHandler); - NativeVotes_UnregisterVoteCommand(NativeVotesOverride_Kick, CallKickVoteHandler); - NativeVotes_UnregisterVoteCommand(NativeVotesOverride_Scramble, CallVoteAdminTestHandler, CallVoteAdminVisHandler); -} - -public void EnabledChanged(ConVar convar, const char[] oldValue, const char[] newValue) -{ - if (convar.BoolValue) - { - NativeVotes_RegisterVoteCommand(NativeVotesOverride_Restart, CallVoteTestHandler); - NativeVotes_RegisterVoteCommand(NativeVotesOverride_Kick, CallKickVoteHandler); - NativeVotes_RegisterVoteCommand(NativeVotesOverride_Scramble, CallVoteAdminTestHandler, CallVoteAdminVisHandler); - } - else - { - NativeVotes_UnregisterVoteCommand(NativeVotesOverride_Restart, CallVoteTestHandler); - NativeVotes_UnregisterVoteCommand(NativeVotesOverride_Kick, CallKickVoteHandler); - NativeVotes_UnregisterVoteCommand(NativeVotesOverride_Scramble, CallVoteAdminTestHandler, CallVoteAdminVisHandler); - } -} - -public Action CallVoteTestHandler(int client, NativeVotesOverride overrideType) -{ - ReplySource old = SetCmdReplySource(SM_REPLY_TO_CHAT); - - ReplyToCommand(client, "Attempted to call Restart vote"); - - SetCmdReplySource(old); - - return Plugin_Handled; -} - -public Action CallVoteAdminVisHandler(int client, NativeVotesOverride overrideType) -{ - if (CheckCommandAccess(client, "adminvotetest", ADMFLAG_VOTE, true)) - { - return Plugin_Continue; - } - - return Plugin_Handled; -} - -public Action CallVoteAdminTestHandler(int client, NativeVotesOverride overrideType) -{ - ReplySource old = SetCmdReplySource(SM_REPLY_TO_CHAT); - - ReplyToCommand(client, "Attempted to call Admin-protected Scramble vote"); - - SetCmdReplySource(old); - - return Plugin_Handled; -} - -public Action CallKickVoteHandler(int client, NativeVotesOverride overrideType, const char[] voteArgument, NativeVotesKickType kickType, int target) -{ - ReplySource old = SetCmdReplySource(SM_REPLY_TO_CHAT); - - int targetClient = GetClientOfUserId(target); - - char sKickType[32]; - NativeVotesType voteType = GetKickVoteTypeFromKickType(kickType, sKickType, sizeof(sKickType)); - - if (voteType == NativeVotesType_None) - { - ReplyToCommand(client, "No kick type found"); - return Plugin_Handled; - } - - if (targetClient == 0) - { - NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_PlayerNotFound, target); - ReplyToCommand(client, "Attempted to call Kick (%s) vote on unknown userid %d", sKickType, target); - return Plugin_Handled; - } - - if (!CanUserTarget(client, targetClient)) - { - NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_CantKickAdmin, target); - ReplyToCommand(client, "Attempted to call Kick (%s) vote on %N, but they have a higher immunity level than you.", sKickType, targetClient); - return Plugin_Handled; - } - - ReplyToCommand(client, "Calling Kick (%s) vote on %N", sKickType, targetClient); - - NativeVote vote = new NativeVote(KickVoteHandler, voteType); - vote.Initiator = client; - vote.SetTarget(targetClient); - vote.DisplayVoteToAll(20); - - SetCmdReplySource(old); - - return Plugin_Handled; -} - -public int KickVoteHandler(NativeVote vote, MenuAction action, int param1, int param2) -{ - switch (action) - { - case MenuAction_End: - { - vote.Close(); - } - - case MenuAction_VoteEnd: - { - int target = vote.GetTarget(); - - if (param1 == NATIVEVOTES_VOTE_YES) - { - if (target == 0) - { - vote.DisplayFail(NativeVotesFail_Generic); - PrintToChatAll("User disconnected before kick."); - } - char name[MAX_NAME_LENGTH+1]; - GetClientName(target, name, sizeof(name)); - vote.DisplayPass(name); - PrintToChatAll("Kick vote on %N passed.", target); - } - else - { - vote.DisplayFail(NativeVotesFail_Loses); - PrintToChatAll("Kick vote failed."); - } - } - - case MenuAction_VoteCancel: - { - if (param1 == VoteCancel_NoVotes) - { - vote.DisplayFail(NativeVotesFail_NotEnoughVotes); - PrintToChatAll("Kick vote had no votes."); - } - else - { - vote.DisplayFail(NativeVotesFail_Generic); - PrintToChatAll("Kick vote was cancelled."); - } - } - } -} - -NativeVotesType GetKickVoteTypeFromKickType(NativeVotesKickType kickType, char[] sKickType, int maxlength) -{ - NativeVotesType voteType; - - switch (kickType) - { - case NativeVotesKickType_Generic: - { - strcopy(sKickType, maxlength, "Generic"); - voteType = NativeVotesType_Kick; - } - - case NativeVotesKickType_Idle: - { - strcopy(sKickType, maxlength, "Idle"); - voteType = NativeVotesType_KickIdle; - } - - case NativeVotesKickType_Scamming: - { - strcopy(sKickType, maxlength, "Scamming"); - voteType = NativeVotesType_KickScamming; - } - - case NativeVotesKickType_Cheating: - { - strcopy(sKickType, maxlength, "Cheating"); - voteType = NativeVotesType_KickCheating; - } - - default: - { - strcopy(sKickType, maxlength, ""); - voteType = NativeVotesType_None; - } - } - - return voteType; -} \ No newline at end of file diff --git a/addons/sourcemod/scripting/nativevotes_voterp.sp b/addons/sourcemod/scripting/nativevotes_voterp.sp new file mode 100644 index 0000000..284e46d --- /dev/null +++ b/addons/sourcemod/scripting/nativevotes_voterp.sp @@ -0,0 +1,103 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * NativeVotes: sm_voterp - Vote to enable/disable tf_medieval_autorp + * Only works in TF2. + * ============================================================================= + */ + +#include +#include +#include +#include + +#pragma semicolon 1 +#pragma newdecls required + +#define VERSION "25w52a" + +public Plugin myinfo = +{ + name = "NativeVotes Medieval Auto-RP", + author = "Heapons", + description = "Provides Medieval Auto-RP voting.", + version = VERSION, + url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" +}; + +ConVar g_Cvar_Medieval; +ConVar g_Cvar_MedievalAutoRP; +ConVar g_Cvar_VoteDuration; + +public void OnPluginStart() +{ + if (GetEngineVersion() != Engine_TF2) + { + SetFailState("This plugin is for Team Fortress 2 only."); + } + + g_Cvar_Medieval = FindConVar("tf_medieval"); + g_Cvar_MedievalAutoRP = FindConVar("tf_medieval_autorp"); + if (g_Cvar_MedievalAutoRP == null) + { + SetFailState("Could not find tf_medieval_autorp cvar."); + } + + g_Cvar_VoteDuration = CreateConVar("sm_voterp_voteduration", "20", "Specifies how long the rp vote should be available for.", _, true, 5.0); + + RegConsoleCmd("sm_voterp", Command_VoteRP, "Vote to enable/disable tf_medieval_autorp"); +} + +public Action Command_VoteRP(int client, int args) +{ + if (!client || !IsClientInGame(client)) + { + return Plugin_Handled; + } + + if (NativeVotes_IsVoteInProgress()) + { + PrintToChat(client, "[{lightgreen}NativeVotes\x01] A vote is already in progress."); + return Plugin_Handled; + } + + bool isMedieval = g_Cvar_Medieval.BoolValue || GameRules_GetProp("m_bPlayingMedieval") || FindEntityByClassname(-1, "tf_logic_medieval") != -1; + if (!isMedieval) + { + PrintToChat(client, "[{lightgreen}NativeVotes\x01] This vote is only available in Medieval Mode."); + return Plugin_Handled; + } + + bool enabled = g_Cvar_MedievalAutoRP.BoolValue; + char title[64]; + Format(title, sizeof(title), "Turn Medieval Auto-RP %s?", enabled ? "off" : "on"); + + NativeVote vote = NativeVotes_Create(VoteHandler, NativeVotesType_Custom_YesNo, MENU_ACTIONS_ALL); + NativeVotes_SetTitle(vote, title); + NativeVotes_SetInitiator(vote, client); + NativeVotes_AddItem(vote, enabled ? "0" : "1", enabled ? "Disable" : "Enable"); + NativeVotes_AddItem(vote, enabled ? "1" : "0", enabled ? "Enable" : "Disable"); + NativeVotes_DisplayToAll(vote, g_Cvar_VoteDuration.IntValue); + return Plugin_Handled; +} + +public int VoteHandler(NativeVote vote, MenuAction action, int param1, int param2) +{ + switch (action) + { + case MenuAction_VoteEnd: + { + int item = param1; + char info[8]; + NativeVotes_GetItem(vote, item, info, sizeof(info)); + int value = StringToInt(info); + g_Cvar_MedievalAutoRP.SetInt(value); + NativeVotes_DisplayPass(vote, "tf_medieval_autorp set to %d", value); + } + case MenuAction_End: + { + NativeVotes_Close(vote); + } + } + return 0; +} diff --git a/addons/sourcemod/scripting/nativevotes_votetest.sp b/addons/sourcemod/scripting/nativevotes_votetest.sp deleted file mode 100644 index 8450c92..0000000 --- a/addons/sourcemod/scripting/nativevotes_votetest.sp +++ /dev/null @@ -1,437 +0,0 @@ -/** - * vim: set ts=4 : - * ============================================================================= - * NativeVotes Vote Tester - * Copyright (C) 2011-2013 Ross Bemrose (Powerlord). All rights reserved. - * ============================================================================= - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, version 3.0, as published by the - * Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - * - * As a special exception, AlliedModders LLC gives you permission to link the - * code of this program (as well as its derivative works) to "Half-Life 2," the - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software - * by the Valve Corporation. You must obey the GNU General Public License in - * all respects for all other code used. Additionally, AlliedModders LLC grants - * this exception to all derivative works. AlliedModders LLC defines further - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), - * or . - * - * Version: $Id$ - */ - -#include - -#pragma newdecls required -#include "include/nativevotes" - -#define VERSION "1.1" - -public Plugin myinfo = -{ - name = "NativeVotes Vote Tester", - author = "Powerlord", - description = "Various NativeVotes vote type tests", - version = "1.0", - url = "https://forums.alliedmods.net/showthread.php?t=208008" -} - -public void OnPluginStart() -{ - CreateConVar("nativevotestest_version", VERSION, "NativeVotes Vote Tester version", FCVAR_NOTIFY|FCVAR_DONTRECORD); - - RegAdminCmd("voteyesno", Cmd_TestYesNo, ADMFLAG_VOTE, "Test Yes/No votes"); - RegAdminCmd("votemult", Cmd_TestMult, ADMFLAG_VOTE, "Test Multiple Choice votes"); - RegAdminCmd("voteyesnocustom", Cmd_TestYesNoCustom, ADMFLAG_VOTE, "Test Multiple Choice vote with Custom Display text"); - RegAdminCmd("votemultcustom", Cmd_TestMultCustom, ADMFLAG_VOTE, "Test Multiple Choice vote with Custom Display text"); - RegAdminCmd("votenovote", Cmd_TestNoVote, ADMFLAG_VOTE, "Test Multiple Choice vote with \"No Vote\" option"); - RegAdminCmd("votenovotecustom", Cmd_TestNoVoteCustom, ADMFLAG_VOTE, "Test Multiple Choice vote with \"No Vote\" option and Custom Display text"); -} - -public Action Cmd_TestYesNo(int client, int args) -{ - if (!NativeVotes_IsVoteTypeSupported(NativeVotesType_Custom_YesNo)) - { - ReplyToCommand(client, "Game does not support Custom Yes/No votes."); - return Plugin_Handled; - } - - if (!NativeVotes_IsNewVoteAllowed()) - { - int seconds = NativeVotes_CheckVoteDelay(); - ReplyToCommand(client, "Vote is not allowed for %d more seconds", seconds); - return Plugin_Handled; - } - - NativeVote vote = new NativeVote(YesNoHandler, NativeVotesType_Custom_YesNo); - - vote.Initiator = client; - vote.SetDetails("Test Yes/No Vote"); - vote.DisplayVoteToAll(30); - - return Plugin_Handled; -} - -public int YesNoHandler(NativeVote vote, MenuAction action, int param1, int param2) -{ - switch (action) - { - case MenuAction_End: - { - vote.Close(); - } - - case MenuAction_VoteCancel: - { - if (param1 == VoteCancel_NoVotes) - { - vote.DisplayFail(NativeVotesFail_NotEnoughVotes); - } - else - { - vote.DisplayFail(NativeVotesFail_Generic); - } - } - - case MenuAction_VoteEnd: - { - if (param1 == NATIVEVOTES_VOTE_NO) - { - vote.DisplayFail(NativeVotesFail_Loses); - } - else - { - vote.DisplayPass("Test Yes/No Vote Passed!"); - // Do something because it passed - } - } - } -} - -public Action Cmd_TestMult(int client, int args) -{ - if (!NativeVotes_IsVoteTypeSupported(NativeVotesType_Custom_Mult)) - { - ReplyToCommand(client, "Game does not support Custom Multiple Choice votes."); - return Plugin_Handled; - } - - if (!NativeVotes_IsNewVoteAllowed()) - { - int seconds = NativeVotes_CheckVoteDelay(); - ReplyToCommand(client, "Vote is not allowed for %d more seconds", seconds); - return Plugin_Handled; - } - - NativeVote vote = new NativeVote(MultHandler, NativeVotesType_Custom_Mult); - - vote.Initiator = client; - vote.SetDetails("Test Mult Vote"); - vote.AddItem("choice1", "Choice 1"); - vote.AddItem("choice2", "Choice 2"); - vote.AddItem("choice3", "Choice 3"); - vote.AddItem("choice4", "Choice 4"); - vote.AddItem("choice5", "Choice 5"); - // 5 is currently the maximum number of choices in any game - vote.DisplayVoteToAll(30); - - return Plugin_Handled; -} - -public int MultHandler(NativeVote vote, MenuAction action, int param1, int param2) -{ - switch (action) - { - case MenuAction_End: - { - vote.Close(); - } - - case MenuAction_VoteCancel: - { - if (param1 == VoteCancel_NoVotes) - { - vote.DisplayFail(NativeVotesFail_NotEnoughVotes); - } - else - { - vote.DisplayFail(NativeVotesFail_Generic); - } - } - - case MenuAction_VoteEnd: - { - char info[64]; - char display[64]; - vote.GetItem(param1, info, sizeof(info), display, sizeof(display)); - - vote.DisplayPass(display); - - // Do something with info - } - } -} - -public Action Cmd_TestYesNoCustom(int client, int args) -{ - if (!NativeVotes_IsVoteTypeSupported(NativeVotesType_Custom_YesNo)) - { - ReplyToCommand(client, "Game does not support Custom Yes/No votes."); - return Plugin_Handled; - } - - if (!NativeVotes_IsNewVoteAllowed()) - { - int seconds = NativeVotes_CheckVoteDelay(); - ReplyToCommand(client, "Vote is not allowed for %d more seconds", seconds); - return Plugin_Handled; - } - - NativeVote vote = new NativeVote(YesNoCustomHandler, NativeVotesType_Custom_YesNo, NATIVEVOTES_ACTIONS_DEFAULT|MenuAction_Display); - - vote.Initiator = client; - vote.SetDetails("Test Yes/No Vote"); - vote.DisplayVoteToAll(30); - - return Plugin_Handled; -} - -public int YesNoCustomHandler(NativeVote vote, MenuAction action, int param1, int param2) -{ - switch (action) - { - case MenuAction_End: - { - vote.Close(); - } - - case MenuAction_Display: - { - char display[64]; - Format(display, sizeof(display), "%N Test Yes/No Vote", param1); - PrintToChat(param1, "New Menu Title: %s", display); - NativeVotes_RedrawVoteTitle(display); - return view_as(Plugin_Changed); - } - - case MenuAction_VoteCancel: - { - if (param1 == VoteCancel_NoVotes) - { - vote.DisplayFail(NativeVotesFail_NotEnoughVotes); - } - else - { - vote.DisplayFail(NativeVotesFail_Generic); - } - } - - case MenuAction_VoteEnd: - { - if (param1 == NATIVEVOTES_VOTE_NO) - { - vote.DisplayFail(NativeVotesFail_Loses); - } - else - { - vote.DisplayPass("Test Custom Yes/No Vote Passed!"); - // Do something because it passed - } - } - } - - return 0; -} - -public Action Cmd_TestMultCustom(int client, int args) -{ - if (!NativeVotes_IsVoteTypeSupported(NativeVotesType_Custom_Mult)) - { - ReplyToCommand(client, "Game does not support Custom Multiple Choice votes."); - return Plugin_Handled; - } - - if (!NativeVotes_IsNewVoteAllowed()) - { - int seconds = NativeVotes_CheckVoteDelay(); - ReplyToCommand(client, "Vote is not allowed for %d more seconds", seconds); - return Plugin_Handled; - } - - NativeVote vote = new NativeVote(MultCustomHandler, NativeVotesType_Custom_Mult, NATIVEVOTES_ACTIONS_DEFAULT|MenuAction_Display|MenuAction_DisplayItem); - - vote.Initiator = client; - vote.SetDetails("Test Mult Vote"); - vote.AddItem("choice1", "Choice 1"); - vote.AddItem("choice2", "Choice 2"); - vote.AddItem("choice3", "Choice 3"); - vote.AddItem("choice4", "Choice 4"); - vote.AddItem("choice5", "Choice 5"); - // 5 is currently the maximum number of choices in any game - vote.DisplayVoteToAll(30); - - return Plugin_Handled; -} - -public int MultCustomHandler(NativeVote vote, MenuAction action, int param1, int param2) -{ - switch (action) - { - case MenuAction_End: - { - vote.Close(); - } - - case MenuAction_Display: - { - char display[64]; - Format(display, sizeof(display), "%N Test Mult Vote", param1); - PrintToChat(param1, "New Menu Title: %s", display); - NativeVotes_RedrawVoteTitle(display); - return view_as(Plugin_Changed); - } - - case MenuAction_VoteCancel: - { - if (param1 == VoteCancel_NoVotes) - { - vote.DisplayFail(NativeVotesFail_NotEnoughVotes); - } - else - { - vote.DisplayFail(NativeVotesFail_Generic); - } - } - - case MenuAction_VoteEnd: - { - char info[64]; - char display[64]; - vote.GetItem(param1, info, sizeof(info), display, sizeof(display)); - - // Do something with info - //NativeVotes_DisplayPassCustom(vote, "%t Mult passed", "Translation Phrase"); - vote.DisplayPassCustom("%s passed", display); - } - - case MenuAction_DisplayItem: - { - char info[64]; - char display[64]; - - char buffer[64]; - - vote.GetItem(param2, info, sizeof(info), display, sizeof(display)); - - // This is generally how you'd do translations, but normally with %T and a format phrase - bool bReplace = false; - if (StrEqual(info, "choice1")) - { - Format(buffer, sizeof(buffer), "%N %s", param1, display); - bReplace = true; - } - else if (StrEqual(info, "choice2")) - { - Format(buffer, sizeof(buffer), "%N %s", param1, display); - bReplace = true; - } - else if (StrEqual(info, "choice3")) - { - Format(buffer, sizeof(buffer), "%N %s", param1, display); - bReplace = true; - } - else if (StrEqual(info, "choice4")) - { - Format(buffer, sizeof(buffer), "%N %s", param1, display); - bReplace = true; - } - else if (StrEqual(info, "choice5")) - { - Format(buffer, sizeof(buffer), "%N %s", param1, display); - bReplace = true; - } - - PrintToChat(param1, "New Menu Item %d: %s", param2, buffer); - - if (bReplace) - { - NativeVotes_RedrawVoteItem(buffer); - return view_as(Plugin_Changed); - } - } - } - - return 0; -} - -public Action Cmd_TestNoVote(int client, int args) -{ - if (!NativeVotes_IsVoteTypeSupported(NativeVotesType_Custom_Mult)) - { - ReplyToCommand(client, "Game does not support Custom Multiple Choice votes."); - return Plugin_Handled; - } - - if (!NativeVotes_IsNewVoteAllowed()) - { - int seconds = NativeVotes_CheckVoteDelay(); - ReplyToCommand(client, "Vote is not allowed for %d more seconds", seconds); - return Plugin_Handled; - } - - NativeVote vote = new NativeVote(MultHandler, NativeVotesType_Custom_Mult); - - vote.NoVoteButton = true; - vote.Initiator = client; - vote.SetDetails("Test Mult Vote with NoVote"); - vote.AddItem("choice1", "Choice 1"); - vote.AddItem("choice2", "Choice 2"); - vote.AddItem("choice3", "Choice 3"); - vote.AddItem("choice4", "Choice 4"); - vote.AddItem("choice5", "Choice 5"); - // 5 is currently the maximum number of choices in any game, but No Vote should make the max 4... - vote.DisplayVoteToAll(30); - - return Plugin_Handled; -} - -public Action Cmd_TestNoVoteCustom(int client, int args) -{ - if (!NativeVotes_IsVoteTypeSupported(NativeVotesType_Custom_Mult)) - { - ReplyToCommand(client, "Game does not support Custom Multiple Choice votes."); - return Plugin_Handled; - } - - if (!NativeVotes_IsNewVoteAllowed()) - { - int seconds = NativeVotes_CheckVoteDelay(); - ReplyToCommand(client, "Vote is not allowed for %d more seconds", seconds); - return Plugin_Handled; - } - - NativeVote vote = new NativeVote(MultCustomHandler, NativeVotesType_Custom_Mult, NATIVEVOTES_ACTIONS_DEFAULT|MenuAction_Display|MenuAction_DisplayItem); - - vote.NoVoteButton = true; - vote.Initiator = client; - vote.SetDetails("Test Mult Vote"); - vote.AddItem("choice1", "Choice 1"); - vote.AddItem("choice2", "Choice 2"); - vote.AddItem("choice3", "Choice 3"); - vote.AddItem("choice4", "Choice 4"); - vote.AddItem("choice5", "Choice 5"); - // 5 is currently the maximum number of choices in any game, but No Vote should make the max 4... - vote.DisplayVoteToAll(30); - - return Plugin_Handled; -} - diff --git a/addons/sourcemod/scripting/votedelay_changelevel.sp b/addons/sourcemod/scripting/votedelay_changelevel.sp deleted file mode 100644 index c370d30..0000000 --- a/addons/sourcemod/scripting/votedelay_changelevel.sp +++ /dev/null @@ -1,223 +0,0 @@ -/** - * vim: set ts=4 : - * ============================================================================= - * Vote Delay: Changelevel - * Delay ChangeLevel and NextLevel votes until X rounds have passed - * - * Vote Delay: Changelevel (C)2014 Powerlord (Ross Bemrose). All rights reserved. - * ============================================================================= - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, version 3.0, as published by the - * Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - * - * As a special exception, AlliedModders LLC gives you permission to link the - * code of this program (as well as its derivative works) to "Half-Life 2," the - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software - * by the Valve Corporation. You must obey the GNU General Public License in - * all respects for all other code used. Additionally, AlliedModders LLC grants - * this exception to all derivative works. AlliedModders LLC defines further - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), - * or . - * - * Version: $Id$ - */ -#include -#include -#pragma semicolon 1 - -#define VERSION "1.0.1" - -#define VOTE_STRING_SIZE 32 - -new Handle:g_Cvar_Enabled; -new Handle:g_Cvar_FullRounds; -new Handle:g_Cvar_Rounds; - -new g_VoteController = -1; -new g_bUserBuf = false; - -// Stolen from NativeVotes -enum NativeVotesCallFailType -{ - NativeVotesCallFail_Generic = 0, /**< Generic fail. */ - NativeVotesCallFail_Loading = 1, /**< L4D/L4D2: Players are still loading. */ - NativeVotesCallFail_Recent = 2, /**< TF2/CS:GO: You can't call another vote yet: Argument is seconds until you can call another vote. */ - NativeVotesCallFail_Disabled = 5, /**< TF2/CS:GO: Server has disabled that issue. */ - NativeVotesCallFail_MapNotFound = 6, /**< TF2/CS:GO: Server does not have that map. */ - NativeVotesCallFail_SpecifyMap = 7, /**< TF2/CS:GO: You must specify a map. */ - NativeVotesCallFail_Failed = 8, /**< TF2/CS:GO: This vote failed recently: Argument is seconds until this vote can be called again. */ - NativeVotesCallFail_WrongTeam = 9, /**< TF2/CS:GO: Team can't call this vote. */ - NativeVotesCallFail_Waiting = 10, /**< TF2/CS:GO: Vote can't be called during Waiting For Players. */ - NativeVotesCallFail_PlayerNotFound = 11, /**< TF2/CS:GO: Player to kick can't be found. Buggy in TF2. */ - NativeVotesCallFail_Unknown = 11, - NativeVotesCallFail_CantKickAdmin = 12, /**< TF2/CS:GO: Can't kick server admin. */ - NativeVotesCallFail_ScramblePending = 13, /**< TF2/CS:GO: Team Scramble is pending. */ - NativeVotesCallFail_Spectators = 14, /**< TF2/CS:GO: Spectators aren't allowed to call votes. */ - NativeVotesCallFail_LevelSet = 15, /**< TF2: Next level already set. */ - NativeVotesCallFail_Warmup = 15, /**< CSGO: Vote can't be called during Warmup */ - NativeVotesCallFail_MapNotValid = 16, /**< TF2: Map is not in MapCycle. */ - NativeVotesCallFail_KickTime = 17, /**< TF2: Cannot kick at this time: Argument is seconds until you can call another kick vote. */ - NativeVotesCallFail_KickDuringRound = 18, /**< TF2: Cannot kick during a round. */ -}; - -public Plugin:myinfo = { - name = "Vote Delay: Changelevel", - author = "Powerlord", - description = "Delay vote change until after X rounds have gone by", - version = VERSION, - url = "" -}; - -new roundCount = 1; - -public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max) -{ - new EngineVersion:engine = GetEngineVersion(); - - if (engine != Engine_TF2 && engine != Engine_CSGO) - { - strcopy(error, err_max, "Only works on TF2 and CS:GO"); - return APLRes_Failure; - } - return APLRes_Success; -} - -public OnPluginStart() -{ - CreateConVar("votedelay_changelevel_version", VERSION, "Vote Delay: Changelevel version", FCVAR_NOTIFY|FCVAR_DONTRECORD|FCVAR_SPONLY); - g_Cvar_Enabled = CreateConVar("votedelay_changelevel_enable", "1", "Enable Vote Delay: Changelevel?", FCVAR_NOTIFY|FCVAR_DONTRECORD, true, 0.0, true, 1.0); - g_Cvar_FullRounds = CreateConVar("votedelay_changelevel_fullrounds", "1", "Full rounds only? Only applies to TF2.", FCVAR_NONE, true, 0.0, true, 1.0); - g_Cvar_Rounds = CreateConVar("votedelay_changelevel_rounds", "4", "During what round should votes become available?", FCVAR_NONE, true, 0.0, true, 10.0); - - g_bUserBuf = (GetUserMessageType() == UM_Protobuf); - - AddCommandListener(Cmd_CallVote, "callvote"); - HookEvent("round_end", Event_RoundEnd); - HookEventEx("teamplay_round_win", Event_RoundWin); - - AutoExecConfig(true, "votedelay_changelevel"); -} - -public OnMapStart() -{ - roundCount = 1; -} - -public Event_RoundEnd(Handle:event, const String:name[], bool:dontBroadcast) -{ - if (!GameRules_GetProp("m_bWarmupPeriod")) - { - roundCount++; - } -} - -public Event_RoundWin(Handle:event, const String:name[], bool:dontBroadcast) -{ - if (GetConVarBool(g_Cvar_FullRounds) && !GetEventInt(event, "full_round")) - { - return; - } - - roundCount++; -} - -public Action:Cmd_CallVote(client, const String:command[], argc) -{ - if (client == 0) - { - return Plugin_Continue; - } - - if (!GetConVarBool(g_Cvar_Enabled) || argc == 0 || IsNativeVoteInProgress()) - { - return Plugin_Continue; - } - - decl String:voteCommand[VOTE_STRING_SIZE]; - GetCmdArg(1, voteCommand, VOTE_STRING_SIZE); - - if (!StrEqual(voteCommand, "ChangeLevel", false) && !StrEqual(voteCommand, "NextLevel", false)) - { - return Plugin_Continue; - } - - new voteRound = GetConVarInt(g_Cvar_Rounds); - - if (roundCount >= voteRound) - { - return Plugin_Continue; - } - - new ReplySource:source = SetCmdReplySource(SM_REPLY_TO_CHAT); - - // For lack of a better reason - TF2CSGO_CallVoteFail(client, NativeVotesCallFail_Disabled); - ReplyToCommand(client, "%s vote is disabled until after round %d, current round is %d", voteCommand, voteRound, roundCount); - - SetCmdReplySource(source); - - return Plugin_Stop; -} - -bool:IsNativeVoteInProgress() -{ - if (CheckVoteController()) - { - new activeIndex = GetEntProp(g_VoteController, Prop_Send, "m_iActiveIssueIndex"); - if (activeIndex > -1) - { - return true; - } - } - - return false; -} - -bool:CheckVoteController() -{ - new entity = -1; - if (g_VoteController != -1) - { - entity = EntRefToEntIndex(g_VoteController); - } - - if (entity == -1) - { - entity = FindEntityByClassname(-1, "vote_controller"); - if (entity == -1) - { - LogError("Could not find Vote Controller."); - return false; - } - - g_VoteController = EntIndexToEntRef(entity); - } - return true; -} - -TF2CSGO_CallVoteFail(client, NativeVotesCallFailType:reason, time=0) -{ - new Handle:callVoteFail = StartMessageOne("CallVoteFailed", client, USERMSG_RELIABLE); - - if(g_bUserBuf) - { - PbSetInt(callVoteFail, "reason", _:reason); - PbSetInt(callVoteFail, "time", time); - } - else - { - BfWriteByte(callVoteFail, _:reason); - BfWriteShort(callVoteFail, time); - } - EndMessage(); -} - diff --git a/addons/sourcemod/scripting/votediagnostics.sp b/addons/sourcemod/scripting/votediagnostics.sp deleted file mode 100644 index 78a88b9..0000000 --- a/addons/sourcemod/scripting/votediagnostics.sp +++ /dev/null @@ -1,790 +0,0 @@ -/** - * vim: set ts=4 : - * ============================================================================= - * Sniff L4D; L4D2; TF2; and CS:GO vote events, user messages, and commands - * - * NativeVotes (C) 2011-2014 Ross Bemrose (Powerlord). All rights reserved. - * ============================================================================= - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, version 3.0, as published by the - * Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - * - * As a special exception, AlliedModders LLC gives you permission to link the - * code of this program (as well as its derivative works) to "Half-Life 2," the - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software - * by the Valve Corporation. You must obey the GNU General Public License in - * all respects for all other code used. Additionally, AlliedModders LLC grants - * this exception to all derivative works. AlliedModders LLC defines further - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), - * or . - * - * Version: $Id$ - */ - -#pragma semicolon 1 - -#include -#include - -#define LOGFILE "vote_diagnostics.txt" - -#pragma newdecls required - -EngineVersion g_EngineVersion = Engine_Unknown; - -int g_VoteController = -1; - -#define MAX_ARG_SIZE 65 - -#define DELAY 6.0 - -public Plugin myinfo = -{ - name = "L4D,L4D2,TF2,CS:GO Vote Sniffer", - author = "Powerlord", - description = "Sniff voting commands, events, and usermessages", - version = "1.2.5", - url = "http://www.sourcemod.net/" -} - -public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) -{ - if (!Game_IsGameSupported()) - { - strcopy(error, err_max, "Unsupported game"); - return APLRes_Failure; - } - - if (!late) - { - switch (g_EngineVersion) - { - case Engine_Left4Dead, Engine_Left4Dead2: - { - CreateTimer(30.0, L4DL4D2_LogControllerValues, _, TIMER_FLAG_NO_MAPCHANGE); - } - - case Engine_CSGO, Engine_TF2, Engine_SDK2013, Engine_Insurgency: - { - CreateTimer(30.0, TF2CSGO_LogControllerValues, _, TIMER_FLAG_NO_MAPCHANGE); - } - } - } - return APLRes_Success; -} - -bool CheckVoteController() -{ - int entity = -1; - if (g_VoteController != -1) - { - entity = EntRefToEntIndex(g_VoteController); - } - - if (entity == -1) - { - entity = FindEntityByClassname(-1, "vote_controller"); - if (entity == -1) - { - LogError("Could not find Vote Controller."); - return false; - } - - g_VoteController = EntIndexToEntRef(entity); - } - return true; -} - -bool Game_IsGameSupported() -{ - g_EngineVersion = GetEngineVersion(); - - switch (g_EngineVersion) - { - case Engine_Left4Dead, Engine_Left4Dead2, Engine_CSGO, Engine_TF2, Engine_SDK2013, Engine_Insurgency: - { - return true; - } - } - - return false; -} - -public void OnPluginStart() -{ - switch (g_EngineVersion) - { - case Engine_Left4Dead: - { - HookEventEx("vote_changed", L4DL4D2_EventVoteChanged); - HookEventEx("vote_ended", L4D_EventVoteEnded); - HookEventEx("vote_started", L4D_EventVoteStarted); - HookEventEx("vote_passed", L4D_EventVotePassed); - HookEventEx("vote_failed", L4D_EventVoteFailed); - HookEventEx("vote_cast_yes", L4D_EventVoteYes); - HookEventEx("vote_cast_no", L4D_EventVoteNo); - - HookUserMessage(GetUserMessageId("VoteRegistered"), L4DL4D2_MessageVoteRegistered); - HookUserMessage(GetUserMessageId("CallVoteFailed"), L4DL4D2_MessageCallVoteFailed); - } - - case Engine_Left4Dead2: - { - HookEventEx("vote_changed", L4DL4D2_EventVoteChanged); - - HookUserMessage(GetUserMessageId("VoteRegistered"), L4DL4D2_MessageVoteRegistered); - HookUserMessage(GetUserMessageId("VoteStart"), L4D2_MessageVoteStart); - HookUserMessage(GetUserMessageId("VotePass"), L4D2_MessageVotePass); - HookUserMessage(GetUserMessageId("VoteFail"), L4D2_MessageVoteFail); - HookUserMessage(GetUserMessageId("CallVoteFailed"), L4DL4D2_MessageCallVoteFailed); - } - - case Engine_CSGO, Engine_TF2, Engine_SDK2013, Engine_Insurgency: - { - HookEventEx("vote_cast", TF2CSGO_EventVoteCast); - HookEventEx("vote_options", TF2CSGO_EventVoteOptions); - - if (GetUserMessageType() == UM_Protobuf) - { - HookUserMessage(GetUserMessageId("VoteSetup"), CSGO_MessageVoteSetup); - HookUserMessage(GetUserMessageId("VoteStart"), CSGO_MessageVoteStart); - HookUserMessage(GetUserMessageId("VotePass"), CSGO_MessageVotePass); - HookUserMessage(GetUserMessageId("VoteFailed"), CSGO_MessageVoteFail); - HookUserMessage(GetUserMessageId("CallVoteFailed"), CSGO_MessageCallVoteFailed); - } - else - { - HookUserMessage(GetUserMessageId("VoteSetup"), TF2_MessageVoteSetup); - HookUserMessage(GetUserMessageId("VoteStart"), TF2_MessageVoteStart); - HookUserMessage(GetUserMessageId("VotePass"), TF2_MessageVotePass); - HookUserMessage(GetUserMessageId("VoteFailed"), TF2_MessageVoteFail); - HookUserMessage(GetUserMessageId("CallVoteFailed"), TF2_MessageCallVoteFailed); - } - } - } - - AddCommandListener(CommandVote, "vote"); - AddCommandListener(CommandCallVote, "callvote"); - - char gameName[64]; - GetGameFolderName(gameName, sizeof(gameName)); - - LogToFile(LOGFILE, "Game: %s", gameName); -} - -/* -"vote_changed" -{ - "yesVotes" "byte" - "noVotes" "byte" - "potentialVotes" "byte" -} -*/ -public void L4DL4D2_EventVoteChanged(Event event, const char[] name, bool dontBroadcast) -{ - int yesVotes = event.GetInt("yesVotes"); - int noVotes = event.GetInt("noVotes"); - int potentialVotes = event.GetInt("potentialVotes"); - LogMessage("Vote Changed event: yesVotes: %d, noVotes: %d, potentialVotes: %d", - yesVotes, noVotes, potentialVotes); - -} - -/* -VoteRegistered Structure - - Byte Choice voted for, 0 = No, 1 = Yes - -*/ -public Action L4DL4D2_MessageVoteRegistered(UserMsg msg_id, BfRead message, const int[] players, int playersNum, bool reliable, bool init) -{ - int choice = BfReadByte(message); - - LogToFile(LOGFILE, "VoteRegistered Usermessage: choice: %d", choice); - return Plugin_Continue; -} - -/* -CallVoteFailed - - Byte Failure reason code (1-2, 5-15) - - Short Time until new vote allowed for code 2 - -message CCSUsrMsg_CallVoteFailed -{ - optional int32 reason = 1; - optional int32 time = 2; -} -*/ -public Action L4DL4D2_MessageCallVoteFailed(UserMsg msg_id, BfRead message, const int[] players, int playersNum, bool reliable, bool init) -{ - int reason; - int time; - - reason = BfReadByte(message); - - LogToFile(LOGFILE, "CallVoteFailed Usermessage: reason: %d", reason, time); - return Plugin_Continue; -} - -/* - "vote_started" -{ - "issue" "string" - "param1" "string" - "team" "byte" - "initiator" "long" // entity id of the player who initiated the vote -} -*/ -public void L4D_EventVoteStarted(Event event, const char[] name, bool dontBroadcast) -{ - char issue[MAX_ARG_SIZE]; - char param1[MAX_ARG_SIZE]; - event.GetString("issue", issue, sizeof(issue)); - event.GetString("param1", param1, sizeof(param1)); - int team = event.GetInt("team"); - int initiator = event.GetInt("initiator"); - LogToFile(LOGFILE, "Vote Start Event: issue: \"%s\", param1: \"%s\", team: %d, initiator: %d", issue, param1, team, initiator); - - if (CheckVoteController()) - { - LogToFile(LOGFILE, "Active Index for issue %s: %d", issue, GetEntProp(g_VoteController, Prop_Send, "m_activeIssueIndex")); - } -} - -/* -"vote_ended" -{ -} -*/ -public void L4D_EventVoteEnded(Event event, const char[] name, bool dontBroadcast) -{ - LogToFile(LOGFILE, "Vote Ended Event"); - - CreateTimer(DELAY, L4DL4D2_LogControllerValues, _, TIMER_FLAG_NO_MAPCHANGE); -} - -/* -"vote_passed" -{ - "details" "string" - "param1" "string" - "team" "byte" -} -*/ -public void L4D_EventVotePassed(Event event, const char[] name, bool dontBroadcast) -{ - char details[MAX_ARG_SIZE]; - char param1[MAX_ARG_SIZE]; - event.GetString("details", details, sizeof(details)); - event.GetString("param1", param1, sizeof(param1)); - int team = event.GetInt("team"); - LogToFile(LOGFILE, "Vote Passed event: details: %s, param1: %s, team: %d", details, param1, team); -} - -/* -"vote_failed" -{ - "team" "byte" -} -*/ -public void L4D_EventVoteFailed(Event event, const char[] name, bool dontBroadcast) -{ - int team = event.GetInt("team"); - LogToFile(LOGFILE, "Vote Failed event: team: %d", team); -} - -/* -"vote_cast_yes" -{ - "team" "byte" - "entityid" "long" // entity id of the voter -} -*/ -public void L4D_EventVoteYes(Event event, const char[] name, bool dontBroadcast) -{ - int team = event.GetInt("team"); - int client = event.GetInt("entityid"); - LogToFile(LOGFILE, "Vote Cast Yes event: team: %d, client: %N", team, client); -} - -/* -"vote_cast_no" -{ - "team" "byte" - "entityid" "long" // entity id of the voter -} -*/ -public void L4D_EventVoteNo(Event event, const char[] name, bool dontBroadcast) -{ - int team = event.GetInt("team"); - int client = event.GetInt("entityid"); - LogToFile(LOGFILE, "Vote Cast No event: team: %d, client: %N", team, client); -} - -/* -VoteStart Structure - - Byte Team index or -1 for all - - Byte Initiator client index (or 99 for Server?) - - String Vote issue phrase - - String Vote issue phrase argument - - String Initiator name - -*/ -public Action L4D2_MessageVoteStart(UserMsg msg_id, BfRead message, const int[] players, int playersNum, bool reliable, bool init) -{ - char issue[MAX_ARG_SIZE]; - char param1[MAX_ARG_SIZE]; - char initiatorName[MAX_NAME_LENGTH]; - - int team = message.ReadByte(); - int initiator = message.ReadByte(); - - message.ReadString(issue, MAX_ARG_SIZE); - message.ReadString(param1, MAX_ARG_SIZE); - message.ReadString(initiatorName, MAX_NAME_LENGTH); - - LogToFile(LOGFILE, "VoteStart Usermessage: team: %d, initiator: %d, issue: %s, param1: %s, player count: %d, initiatorName: %s", team, initiator, issue, param1, playersNum, initiatorName); - if (CheckVoteController()) - { - LogToFile(LOGFILE, "Active Index for issue %s: %d", issue, GetEntProp(g_VoteController, Prop_Send, "m_activeIssueIndex")); - } - - return Plugin_Continue; -} - -/* -VotePass Structure - - Byte Team index or -1 for all - - String Vote issue pass phrase - - String Vote issue pass phrase argument - -*/ -public Action L4D2_MessageVotePass(UserMsg msg_id, BfRead message, const int[] players, int playersNum, bool reliable, bool init) -{ - char issue[MAX_ARG_SIZE]; - char param1[MAX_ARG_SIZE]; - int team = message.ReadByte(); - - message.ReadString(issue, MAX_ARG_SIZE); - message.ReadString(param1, MAX_ARG_SIZE); - - LogToFile(LOGFILE, "VotePass Usermessage: team: %d, issue: %s, param1: %s", team, issue, param1); - - CreateTimer(DELAY, L4DL4D2_LogControllerValues, _, TIMER_FLAG_NO_MAPCHANGE); - return Plugin_Continue; -} - -/* -VoteFail Structure - - Byte Team index or -1 for all - -*/ -public Action L4D2_MessageVoteFail(UserMsg msg_id, BfRead message, const int[] players, int playersNum, bool reliable, bool init) -{ - int team = message.ReadByte(); - - LogToFile(LOGFILE, "VoteFail Usermessage: team: %d", team); - - CreateTimer(DELAY, L4DL4D2_LogControllerValues, _, TIMER_FLAG_NO_MAPCHANGE); - - return Plugin_Continue; -} - -/* -"vote_cast" -{ - "vote_option" "byte" // which option the player voted on - "team" "short" - "entityid" "long" // entity id of the voter -} -*/ -public void TF2CSGO_EventVoteCast(Event event, const char[] name, bool dontBroadcast) -{ - int vote_option = event.GetInt("vote_option"); - int team = event.GetInt("team"); - int entityid = event.GetInt("entityid"); - LogToFile(LOGFILE, "Vote Cast event: vote_option: %d, team: %d, client: %N", vote_option, team, entityid); -} - -/* -"vote_options" -{ - "count" "byte" // Number of options - up to MAX_VOTE_OPTIONS - "option1" "string" - "option2" "string" - "option3" "string" - "option4" "string" - "option5" "string" -} -*/ -public void TF2CSGO_EventVoteOptions(Event event, const char[] name, bool dontBroadcast) -{ - char option1[MAX_ARG_SIZE]; - char option2[MAX_ARG_SIZE]; - char option3[MAX_ARG_SIZE]; - char option4[MAX_ARG_SIZE]; - char option5[MAX_ARG_SIZE]; - - int count = event.GetInt("count"); - event.GetString("option1", option1, sizeof(option1)); - event.GetString("option2", option2, sizeof(option2)); - event.GetString("option3", option3, sizeof(option3)); - event.GetString("option4", option4, sizeof(option4)); - event.GetString("option5", option5, sizeof(option5)); - LogToFile(LOGFILE, "Vote Options event: count: %d, option1: %s, option2: %s, option3: %s, option4: %s, option5: %s", - count, option1, option2, option3, option4, option5); -} - -/* -message CCSUsrMsg_VoteSetup -{ - repeated string potential_issues = 1; -} -*/ -public Action CSGO_MessageVoteSetup(UserMsg msg_id, Protobuf message, const int[] players, int playersNum, bool reliable, bool init) -{ - char options[2049]; - int count = message.GetRepeatedFieldCount("potential_issues"); - for(int i = 0; i < count; i++) - { - char option[MAX_ARG_SIZE]; - message.ReadString("potential_issues", option, MAX_ARG_SIZE, i); - StrCat(options, sizeof(options), option); - StrCat(options, sizeof(options), " "); - } - - LogToFile(LOGFILE, "VoteSetup Usermessage: count: %d, options: %s", count, options); - - return Plugin_Continue; -} - -/* -VoteSetup - - Byte Option count - * issue multiple issues matching the number in the first byte - -issue - - String Vote name - - String Vote translation string - - Byte Whether a vote is enabled or not. -*/ -public Action TF2_MessageVoteSetup(UserMsg msg_id, BfRead message, const int[] players, int playersNum, bool reliable, bool init) -{ - char options[2049]; - int count = message.ReadByte(); - for (int i = 0; i < count; i++) - { - char option[MAX_ARG_SIZE*2+1]; - char potential_issue[MAX_ARG_SIZE]; - char translation[MAX_ARG_SIZE]; - message.ReadString(potential_issue, MAX_ARG_SIZE); - message.ReadString(translation, MAX_ARG_SIZE); - - int enabled = message.ReadByte(); - Format(option, sizeof(option), "(%s, %s, %d) ", potential_issue, translation, enabled); - - StrCat(options, sizeof(options), option); - } - - LogToFile(LOGFILE, "VoteSetup Usermessage: count: %d, options: %s", count, options); - - return Plugin_Continue; -} - -/* -message CCSUsrMsg_VoteStart -{ - optional int32 team = 1; - optional int32 ent_idx = 2; - optional int32 vote_type = 3; - optional string disp_str = 4; - optional string details_str = 5; - optional string other_team_str = 6; - optional bool is_yes_no_vote = 7; - -} -*/ -public Action CSGO_MessageVoteStart(UserMsg msg_id, Protobuf message, const int[] players, int playersNum, bool reliable, bool init) -{ - char issue[MAX_ARG_SIZE]; - char otherTeamIssue[MAX_ARG_SIZE]; - char param1[MAX_ARG_SIZE]; - int team; - int initiator; - bool yesNo; - int voteType; - - team = message.ReadInt("team"); - initiator = message.ReadInt("ent_idx"); - message.ReadString("disp_str", issue, MAX_ARG_SIZE); - message.ReadString("details_str", param1, MAX_ARG_SIZE); - yesNo = message.ReadBool("is_yes_no_vote"); - message.ReadString("other_team_str", otherTeamIssue, MAX_ARG_SIZE); - voteType = message.ReadInt("vote_type"); - - LogToFile(LOGFILE, "VoteStart Usermessage: team: %d, initiator: %d, issue: %s, otherTeamIssue: %s, param1: %s, yesNo: %d, player count: %d, voteType: %d", team, initiator, issue, otherTeamIssue, param1, yesNo, playersNum, voteType); - - CreateTimer(0.0, TF2CSGO_LogControllerValues, _, TIMER_FLAG_NO_MAPCHANGE); - - return Plugin_Continue; -} - -/* -VoteStart Structure - - Byte Team index or -1 for all - - Byte Initiator client index or 99 for Server - - String Vote issue phrase - - String Vote issue phrase argument - - Bool false for Yes/No, true for Multiple choice -*/ -public Action TF2_MessageVoteStart(UserMsg msg_id, BfRead message, const int[] players, int playersNum, bool reliable, bool init) -{ - char issue[MAX_ARG_SIZE]; - char param1[MAX_ARG_SIZE]; - int team; - int initiator; - bool yesNo; - - team = message.ReadByte(); - initiator = message.ReadByte(); - message.ReadString(issue, MAX_ARG_SIZE); - message.ReadString(param1, MAX_ARG_SIZE); - yesNo = message.ReadBool(); - - LogToFile(LOGFILE, "VoteStart Usermessage: team: %d, initiator: %d, issue: %s, param1: %s, yesNo: %d, player count: %d", team, initiator, issue, param1, yesNo, playersNum); - - CreateTimer(0.0, TF2CSGO_LogControllerValues, _, TIMER_FLAG_NO_MAPCHANGE); - - return Plugin_Continue; -} - -/* -message CCSUsrMsg_VotePass -{ - optional int32 team = 1; - optional int32 vote_type = 2; - optional string disp_str= 3; - optional string details_str = 4; -} -*/ -public Action CSGO_MessageVotePass(UserMsg msg_id, Protobuf message, const int[] players, int playersNum, bool reliable, bool init) -{ - char issue[MAX_ARG_SIZE]; - char param1[MAX_ARG_SIZE]; - int team; - int voteType; - - team = message.ReadInt("team"); - message.ReadString("disp_str", issue, MAX_ARG_SIZE); - message.ReadString("details_str", param1, MAX_ARG_SIZE); - voteType = message.ReadInt("vote_type"); - - LogToFile(LOGFILE, "VotePass Usermessage: team: %d, issue: %s, param1: %s, voteType: %d", team, issue, param1, voteType); - - CreateTimer(DELAY, TF2CSGO_LogControllerValues, _, TIMER_FLAG_NO_MAPCHANGE); - - return Plugin_Continue; -} - -/* -VotePass Structure - - Byte Team index or -1 for all - - String Vote issue pass phrase - - String Vote issue pass phrase argument -*/ -public Action TF2_MessageVotePass(UserMsg msg_id, BfRead message, const int[] players, int playersNum, bool reliable, bool init) -{ - char issue[MAX_ARG_SIZE]; - char param1[MAX_ARG_SIZE]; - int team; - - team = message.ReadByte(); - message.ReadString(issue, MAX_ARG_SIZE); - message.ReadString(param1, MAX_ARG_SIZE); - - LogToFile(LOGFILE, "VotePass Usermessage: team: %d, issue: %s, param1: %s", team, issue, param1); - - CreateTimer(DELAY, TF2CSGO_LogControllerValues, _, TIMER_FLAG_NO_MAPCHANGE); - - return Plugin_Continue; -} - -/* -message CCSUsrMsg_VoteFailed -{ - optional int32 team = 1; - optional int32 reason = 2; -} -*/ -public Action CSGO_MessageVoteFail(UserMsg msg_id, Protobuf message, const int[] players, int playersNum, bool reliable, bool init) -{ - int team = message.ReadInt("team"); - int reason = message.ReadInt("reason"); - - LogToFile(LOGFILE, "VoteFail Usermessage: team: %d, reason: %d", team, reason); - - CreateTimer(DELAY, TF2CSGO_LogControllerValues, _, TIMER_FLAG_NO_MAPCHANGE); - - return Plugin_Continue; -} - -/* -VoteFailed Structure - - Byte Team index or -1 for all - - Byte Failure reason code (0, 3-4) -*/ -public Action TF2_MessageVoteFail(UserMsg msg_id, BfRead message, const int[] players, int playersNum, bool reliable, bool init) -{ - int team = message.ReadByte(); - int reason = message.ReadByte(); - - LogToFile(LOGFILE, "VoteFail Usermessage: team: %d, reason: %d", team, reason); - - CreateTimer(DELAY, TF2CSGO_LogControllerValues, _, TIMER_FLAG_NO_MAPCHANGE); - - return Plugin_Continue; -} - -public Action TF2CSGO_LogControllerValues(Handle timer) -{ - if (!CheckVoteController()) - { - return Plugin_Continue; - } - - int team = GetEntProp(g_VoteController, Prop_Send, "m_iOnlyTeamToVote"); - int activeIssue = GetEntProp(g_VoteController, Prop_Send, "m_iActiveIssueIndex"); - int potentialVotes = GetEntProp(g_VoteController, Prop_Send, "m_nPotentialVotes"); - bool isYesNo = view_as(GetEntProp(g_VoteController, Prop_Send, "m_bIsYesNoVote")); - int voteCounts[5]; - for (int i = 0; i < 5; ++i) - { - voteCounts[i] = GetEntProp(g_VoteController, Prop_Send, "m_nVoteOptionCount", _, i); - } - - LogToFile(LOGFILE, "Vote Controller, issue: %d, team: %d, potentialVotes: %d, yesNo: %d, count1: %d, count2: %d, count3: %d, count4: %d, count5: %d", - activeIssue, team, potentialVotes, isYesNo, voteCounts[0], voteCounts[1], voteCounts[2], voteCounts[3], voteCounts[4]); - return Plugin_Continue; -} - -public Action L4DL4D2_LogControllerValues(Handle timer) -{ - if (!CheckVoteController()) - { - return Plugin_Continue; - } - - int team = GetEntProp(g_VoteController, Prop_Send, "m_onlyTeamToVote"); - int activeIssue = GetEntProp(g_VoteController, Prop_Send, "m_activeIssueIndex"); - int potentialVotes = GetEntProp(g_VoteController, Prop_Send, "potentialVotes"); - int voteCounts[2]; - voteCounts[0] = GetEntProp(g_VoteController, Prop_Send, "m_votesYes"); - voteCounts[1] = GetEntProp(g_VoteController, Prop_Send, "m_votesNo"); - - LogToFile(LOGFILE, "Vote Controller, issue: %d, team: %d, potentialVotes: %d, countYes: %d, countNo: %d", - activeIssue, team, potentialVotes, voteCounts[0], voteCounts[1]); - return Plugin_Continue; -} - -/* -message CCSUsrMsg_CallVoteFailed -{ - optional int32 reason = 1; - optional int32 time = 2; -} -*/ -public Action CSGO_MessageCallVoteFailed(UserMsg msg_id, Protobuf message, const int[] players, int playersNum, bool reliable, bool init) -{ - int reason = message.ReadInt("reason"); - int time = message.ReadInt("time"); - - LogToFile(LOGFILE, "CallVoteFailed Usermessage: reason: %d, time: %d", reason, time); - return Plugin_Continue; -} - -/* -CallVoteFailed - - Byte Failure reason code (1-2, 5-15) - - Short Time until new vote allowed for code 2 -*/ -public Action TF2_MessageCallVoteFailed(UserMsg msg_id, BfRead message, const int[] players, int playersNum, bool reliable, bool init) -{ - int reason = message.ReadByte(); - int time = message.ReadShort(); - - LogToFile(LOGFILE, "CallVoteFailed Usermessage: reason: %d, time: %d", reason, time); - return Plugin_Continue; -} - -/* - This is likely a client-side event as it never produced any values. - - "endmatch_mapvote_selecting_map" - { - "count" "byte" // Number of "ties" - "slot1" "byte" - "slot2" "byte" - "slot3" "byte" - "slot4" "byte" - "slot5" "byte" - "slot6" "byte" - "slot7" "byte" - "slot8" "byte" - "slot9" "byte" - "slot10" "byte" - } -*/ -/* -public void CSGO_EventMapVote(Event event, const char[] name, bool dontBroadcast) -{ - int count = event.GetInt("count"); - int slot1 = event.GetInt("slot1"); - int slot2 = event.GetInt("slot2"); - int slot3 = event.GetInt("slot3"); - int slot4 = event.GetInt("slot4"); - int slot5 = event.GetInt("slot5"); - int slot6 = event.GetInt("slot6"); - int slot7 = event.GetInt("slot7"); - int slot8 = event.GetInt("slot8"); - int slot9 = event.GetInt("slot9"); - int slot10 = event.GetInt("slot10"); - - LogToFile(LOGFILE, "Endmatch_Mapvote_SelectingMap event: count: %d, slot1: %d, slot2: %d, slot3: %d, slot4: %d, slot5: %d, slot6: %d, slot7: %d, slot8: %d, slot9: %d, slot10: %d ", - count, slot1, slot2, slot3, slot4, slot5, slot6, slot7, slot8, slot9, slot10); -} -*/ - -/* -Vote command - - String option1 through option5 (for TF2/CS:GO); Yes or No (for L4D/L4D2) - */ -public Action CommandVote(int client, const char[] command, int argc) -{ - char vote[MAX_ARG_SIZE]; - GetCmdArg(1, vote, sizeof(vote)); - - LogToFile(LOGFILE, "%N used vote command: %s %s", client, command, vote); - return Plugin_Continue; -} - -/* -callvote command - - String Vote type (Valid types are sent in the VoteSetup message) - - String target (or type - target for Kick) -*/ -public Action CommandCallVote(int client, const char[] command, int argc) -{ - char args[255]; - GetCmdArgString(args, sizeof(args)); - - LogToFile(LOGFILE, "callvote command: client: %N, command: %s", client, args); - return Plugin_Continue; -} diff --git a/addons/sourcemod/scripting/votefailed.sp b/addons/sourcemod/scripting/votefailed.sp deleted file mode 100644 index c684825..0000000 --- a/addons/sourcemod/scripting/votefailed.sp +++ /dev/null @@ -1,151 +0,0 @@ -/** - * vim: set ts=4 : - * ============================================================================= - * Vote Failed - * Send Vote Failed commands for TF2 and CS:GO - * Used to test the VoteFailed and CallVoteFailed usermessages - * - * Vote Failed (C)2014 Powerlord (Ross Bemrose). All rights reserved. - * ============================================================================= - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, version 3.0, as published by the - * Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - * - * As a special exception, AlliedModders LLC gives you permission to link the - * code of this program (as well as its derivative works) to "Half-Life 2," the - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software - * by the Valve Corporation. You must obey the GNU General Public License in - * all respects for all other code used. Additionally, AlliedModders LLC grants - * this exception to all derivative works. AlliedModders LLC defines further - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), - * or . - * - * Version: $Id$ - */ -#include -#pragma semicolon 1 -#pragma newdecls required - -#define VERSION "1.0.0" - -bool g_bUserBuf; - -ConVar g_Cvar_Enabled; - -public Plugin myinfo = { - name = "Vote Failed / Call Vote Failed displayer", - author = "Powerlord", - description = "Used to display specific vote failed and call vote failed messages to a user", - version = VERSION, - url = "https://forums.alliedmods.net/showthread.php?t=208008" -}; - -public void OnPluginStart() -{ - LoadTranslations("common.phrases"); - - CreateConVar("votefailed_version", VERSION, "Vote Failed version", FCVAR_NOTIFY|FCVAR_DONTRECORD|FCVAR_SPONLY); - g_Cvar_Enabled = CreateConVar("votefailed_enable", "1", "Enable Vote Failed?", FCVAR_NOTIFY|FCVAR_DONTRECORD, true, 0.0, true, 1.0); - g_bUserBuf = (GetUserMessageType() == UM_Protobuf); - - RegAdminCmd("votefail", Cmd_VoteFailed, ADMFLAG_GENERIC, "Show Call Vote Fail dialog to user"); - RegAdminCmd("callvotefail", Cmd_CallVoteFailed, ADMFLAG_GENERIC, "Show Call Vote Fail dialog to user"); -} - -public Action Cmd_VoteFailed(int client, int args) -{ - if (!g_Cvar_Enabled.BoolValue) - { - return Plugin_Continue; - } - - if (client == 0) - { - ReplyToCommand(client, "%t", "Command is in-game only"); - return Plugin_Handled; - } - - if (args == 0) - { - ReplyToCommand(client, "Requires at least one argument"); - return Plugin_Handled; - } - - char sReason[5]; - GetCmdArg(1, sReason, sizeof(sReason)); - - int reason = StringToInt(sReason); - - Handle voteFailed = StartMessageOne("VoteFailed", client, USERMSG_RELIABLE); - - if(g_bUserBuf) - { - PbSetInt(voteFailed, "team", 0); - PbSetInt(voteFailed, "reason", reason); - } - else - { - BfWriteByte(voteFailed, 0); - BfWriteByte(voteFailed, reason); - } - EndMessage(); - - return Plugin_Handled; -} - -public Action Cmd_CallVoteFailed(int client, int args) -{ - if (!g_Cvar_Enabled.BoolValue) - { - return Plugin_Continue; - } - - if (client == 0) - { - ReplyToCommand(client, "%t", "Command is in-game only"); - return Plugin_Handled; - } - - if (args == 0) - { - ReplyToCommand(client, "Requires at least one argument"); - return Plugin_Handled; - } - - char sReason[5]; - GetCmdArg(1, sReason, sizeof(sReason)); - - int reason = StringToInt(sReason); - int time = 0; - - if (args > 1) - { - char sTime[10]; - GetCmdArg(2, sTime, sizeof(sTime)); - time = StringToInt(sTime); - } - - Handle callVoteFail = StartMessageOne("CallVoteFailed", client, USERMSG_RELIABLE); - if(g_bUserBuf) - { - PbSetInt(callVoteFail, "reason", reason); - PbSetInt(callVoteFail, "time", time); - } - else - { - BfWriteByte(callVoteFail, reason); - BfWriteShort(callVoteFail, time); - } - EndMessage(); - - return Plugin_Handled; -} diff --git a/addons/sourcemod/translations/fr/nativevotes.phrases.txt b/addons/sourcemod/translations/fr/nativevotes.phrases.txt new file mode 100644 index 0000000..e74e73a --- /dev/null +++ b/addons/sourcemod/translations/fr/nativevotes.phrases.txt @@ -0,0 +1,12 @@ +"Phrases" +{ + "NativeVotes Revote" + { + "fr" "Ré-ouverture du vote..." + } + + "NativeVotes Revote No Time" + { + "fr" "Temps insuffisant pour voter à nouveau" + } +} \ No newline at end of file From f489c55c06e0efe228aa05d3ef0da3c444eb6103 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sat, 27 Dec 2025 20:24:55 +0100 Subject: [PATCH 002/128] Update Workflow --- .github/nv_compile.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/nv_compile.sh b/.github/nv_compile.sh index f370aac..d91bcea 100755 --- a/.github/nv_compile.sh +++ b/.github/nv_compile.sh @@ -6,10 +6,13 @@ versvers=$(spcomp64 -v | grep "Compiler" --color=never | cut -d " " -f 3 | cut - nv_plugs=("nativevotes") -nv_plugs+=("nativevotes-basecommands") +nv_plugs+=("nativevotes_basecommands") +nv_plugs+=("nativevotes_basevotes") +nv_plugs+=("nativevotes_funvotes") nv_plugs+=("nativevotes_mapchooser") nv_plugs+=("nativevotes_nominations") nv_plugs+=("nativevotes_rockthevote") +nv_plugs+=("nativevotes_voterp") nv_plugins_disabled=("nativevotes_votetest") nv_plugins_disabled+=("nativevotes_votemanager_test") From 409d115bab318656d616d14aa10fbb61e3c12641 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sat, 27 Dec 2025 20:26:51 +0100 Subject: [PATCH 003/128] Only compile in 1.13 --- .github/workflows/nativevotes.yml | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/.github/workflows/nativevotes.yml b/.github/workflows/nativevotes.yml index 028b4b2..4e5d910 100644 --- a/.github/workflows/nativevotes.yml +++ b/.github/workflows/nativevotes.yml @@ -21,28 +21,6 @@ jobs: with: submodules: true - - name: Setup SourcePawn 1.11 Compiler - uses: rumblefrog/setup-sp@master - with: - version: "1.11.x" - - - name: Compile plugins 1.11 - shell: bash - run: | - bash .github/nv_compile.sh - bash - - - name: Setup SourcePawn 1.12 Compiler - uses: rumblefrog/setup-sp@master - with: - version: "1.12.x" - - - name: Compile plugins 1.12 - shell: bash - run: | - bash .github/nv_compile.sh - bash - - name: Setup SourcePawn 1.13 Compiler uses: rumblefrog/setup-sp@master with: From 056a6e478d7e2fb37a904450ced1844ca77135cd Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sat, 27 Dec 2025 20:27:50 +0100 Subject: [PATCH 004/128] Remove disabled plugins --- .github/nv_compile.sh | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/.github/nv_compile.sh b/.github/nv_compile.sh index d91bcea..7029e83 100755 --- a/.github/nv_compile.sh +++ b/.github/nv_compile.sh @@ -14,13 +14,6 @@ nv_plugs+=("nativevotes_nominations") nv_plugs+=("nativevotes_rockthevote") nv_plugs+=("nativevotes_voterp") -nv_plugins_disabled=("nativevotes_votetest") -nv_plugins_disabled+=("nativevotes_votemanager_test") -nv_plugins_disabled+=("csgo_votestart_test") -nv_plugins_disabled+=("votedelay_changelevel") -nv_plugins_disabled+=("votediagnostics") -nv_plugins_disabled+=("votefailed") - # we start in git root pushd ./addons/sourcemod/scripting @@ -31,10 +24,6 @@ for target in "${nv_plugs[@]}"; do spcomp64 -i"./include/" "${target}".sp -o ../plugins/"${target}".smx || exit 1 done -for target in "${nv_plugins_disabled[@]}"; do - spcomp64 -i"./include/" "${target}".sp -o ../plugins/disabled/"${target}".smx || exit 1 -done - popd mkdir build || true From b888d7ff4565e95ad4dcc9c88f6705447d0e6e67 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sat, 27 Dec 2025 20:30:07 +0100 Subject: [PATCH 005/128] Update nv_compile.sh --- .github/nv_compile.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/nv_compile.sh b/.github/nv_compile.sh index 7029e83..8e8aaf7 100755 --- a/.github/nv_compile.sh +++ b/.github/nv_compile.sh @@ -17,9 +17,6 @@ nv_plugs+=("nativevotes_voterp") # we start in git root pushd ./addons/sourcemod/scripting -mkdir ../plugins/disabled -p || true - - for target in "${nv_plugs[@]}"; do spcomp64 -i"./include/" "${target}".sp -o ../plugins/"${target}".smx || exit 1 done From 460ab8d46720528d0f1181c46f025ed07a7bb8f4 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sat, 27 Dec 2025 20:31:14 +0100 Subject: [PATCH 006/128] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ca9d53a..2817a15 100644 --- a/README.md +++ b/README.md @@ -20,5 +20,5 @@ This fork aims to expand upon [ Date: Sat, 27 Dec 2025 22:08:11 +0100 Subject: [PATCH 009/128] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2817a15..dd46b97 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ This fork aims to expand upon [ Date: Sat, 27 Dec 2025 22:08:57 +0100 Subject: [PATCH 010/128] Fix Workflow what the heck was I doing... --- .github/workflows/nativevotes.yml | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/.github/workflows/nativevotes.yml b/.github/workflows/nativevotes.yml index 5154d76..133b678 100644 --- a/.github/workflows/nativevotes.yml +++ b/.github/workflows/nativevotes.yml @@ -2,19 +2,6 @@ name: Nativevotes Autobuild # modified from https://github.com/nosoop/NinjaBuild-SMPlugin/blob/master/contrib/github-workflow/build-on-version-tag-push.yml on: - push: - branches: [ master ] - paths-ignore: - - '.gitignore' - - 'LICENSE' - - 'README.md' - pull_request: - branches: [ master ] - paths-ignore: - - '.gitignore' - - 'LICENSE' - - 'README.md' - workflow_dispatch:on: push: branches: [ master ] paths-ignore: @@ -73,3 +60,4 @@ jobs: with: tag_name: workflow-build${{ github.run_number }} files: build/nativevotes* + From 82ef307cff3736da7f8998fc3104a03de96c0377 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sat, 27 Dec 2025 22:10:51 +0100 Subject: [PATCH 011/128] Create missing directory --- .github/nv_compile.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/nv_compile.sh b/.github/nv_compile.sh index 8e8aaf7..256c512 100755 --- a/.github/nv_compile.sh +++ b/.github/nv_compile.sh @@ -17,6 +17,8 @@ nv_plugs+=("nativevotes_voterp") # we start in git root pushd ./addons/sourcemod/scripting +mkdir ../plugins || true + for target in "${nv_plugs[@]}"; do spcomp64 -i"./include/" "${target}".sp -o ../plugins/"${target}".smx || exit 1 done From 0d9fa4161ad10bd217f11c247663652ef419dbc9 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sat, 27 Dec 2025 22:15:24 +0100 Subject: [PATCH 012/128] Make methodmap natives optional https://github.com/sapphonie/sourcemod-nativevotes-updated/pull/40 --- .../scripting/include/nativevotes.inc | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/addons/sourcemod/scripting/include/nativevotes.inc b/addons/sourcemod/scripting/include/nativevotes.inc index 9bfaaf7..db3fdb3 100644 --- a/addons/sourcemod/scripting/include/nativevotes.inc +++ b/addons/sourcemod/scripting/include/nativevotes.inc @@ -1503,6 +1503,37 @@ public SharedPlugin __pl_nativevotes = #if !defined REQUIRE_PLUGIN public void __pl_nativevotes_SetNTVOptional() { + MarkNativeAsOptional("NativeVote.NativeVote"); + MarkNativeAsOptional("NativeVote.Close"); + MarkNativeAsOptional("NativeVote.AddItem"); + MarkNativeAsOptional("NativeVote.InsertItem"); + MarkNativeAsOptional("NativeVote.RemoveItem"); + MarkNativeAsOptional("NativeVote.RemoveAllItems"); + MarkNativeAsOptional("NativeVote.GetItem"); + MarkNativeAsOptional("NativeVote.SetDetails"); + MarkNativeAsOptional("NativeVote.GetDetails"); + MarkNativeAsOptional("NativeVote.SetTitle"); + MarkNativeAsOptional("NativeVote.GetTitle"); + MarkNativeAsOptional("NativeVote.SetTarget"); + MarkNativeAsOptional("NativeVote.GetTarget"); + MarkNativeAsOptional("NativeVote.GetTargetSteam"); + MarkNativeAsOptional("NativeVote.DisplayVote"); + MarkNativeAsOptional("NativeVote.DisplayVoteToAll"); + MarkNativeAsOptional("NativeVote.DisplayPass"); + MarkNativeAsOptional("NativeVote.DisplayPassCustomToOne"); + MarkNativeAsOptional("NativeVote.DisplayPassCustom"); + MarkNativeAsOptional("NativeVote.DisplayPassEx"); + MarkNativeAsOptional("NativeVote.DisplayFail"); + MarkNativeAsOptional("NativeVote.OptionFlags.get"); + MarkNativeAsOptional("NativeVote.OptionFlags.set"); + MarkNativeAsOptional("NativeVote.NoVoteButton.set"); + MarkNativeAsOptional("NativeVote.VoteResultCallback.set"); + MarkNativeAsOptional("NativeVote.ItemCount.get"); + MarkNativeAsOptional("NativeVote.VoteType.get"); + MarkNativeAsOptional("NativeVote.Team.get"); + MarkNativeAsOptional("NativeVote.Team.set"); + MarkNativeAsOptional("NativeVote.Initiator.get"); + MarkNativeAsOptional("NativeVote.Initiator.set"); MarkNativeAsOptional("NativeVotes_IsVoteTypeSupported"); MarkNativeAsOptional("NativeVotes_Create"); MarkNativeAsOptional("NativeVotes_Close"); From 409a440d3fd261849625c57320c725491da097b9 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Tue, 6 Jan 2026 11:56:57 +0100 Subject: [PATCH 013/128] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dd46b97..1a004b5 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ This fork aims to expand upon [(Plugin_Changed); -} +} \ No newline at end of file diff --git a/addons/sourcemod/scripting/nativevotes/data-keyvalues.sp b/addons/sourcemod/scripting/nativevotes/data-keyvalues.sp index 6b66a51..4dc92a0 100644 --- a/addons/sourcemod/scripting/nativevotes/data-keyvalues.sp +++ b/addons/sourcemod/scripting/nativevotes/data-keyvalues.sp @@ -216,14 +216,7 @@ NativeVote Data_CreateVote(NativeVotesType voteType, MenuAction actions) vote.SetNum("actions", view_as(actions)); vote.SetNum("result_callback", view_as(voteResults)); vote.SetNum("initiator", NATIVEVOTES_SERVER_INDEX); - if (g_EngineVersion == Engine_TF2) - { - vote.SetNum("team", NATIVEVOTES_TF2_ALL_TEAMS); - } - else - { - vote.SetNum("team", NATIVEVOTES_ALL_TEAMS); - } + vote.SetNum("team", g_EngineVersion == Engine_TF2 ? NATIVEVOTES_TF2_ALL_TEAMS : NATIVEVOTES_ALL_TEAMS); vote.SetNum("flags", 0); vote.SetString("custom_title", ""); @@ -310,30 +303,30 @@ void Data_CloseVote(KeyValues vote) { return; } - + Handle handler = Data_GetHandler(vote); if (handler != null) { delete handler; } - + Handle voteResults = Data_GetResultCallback(vote); if (voteResults != null) { delete voteResults; } - + ArrayList infoArray = view_as(vote.GetNum(INFO, view_as(INVALID_HANDLE))); if (infoArray != null) { delete infoArray; } - + ArrayList displayArray = view_as(vote.GetNum(DISPLAY, view_as(INVALID_HANDLE))); if (displayArray != null) { delete displayArray; } - + delete vote; } diff --git a/addons/sourcemod/scripting/nativevotes/game.sp b/addons/sourcemod/scripting/nativevotes/game.sp index ed941eb..bb2a66d 100644 --- a/addons/sourcemod/scripting/nativevotes/game.sp +++ b/addons/sourcemod/scripting/nativevotes/game.sp @@ -385,13 +385,14 @@ bool Game_IsGameSupported(char[] engineName="", int maxlength=0) char gameDir[128]; GetGameFolderName(gameDir, sizeof(gameDir)); - if ( !StrEqual(gameDir, "tf") ) + if (!StrEqual(gameDir, "tf")) { isTF2SDKModHack = true; } - //Fix for Open Fortress - if ( g_EngineVersion == Engine_SDK2013 && ( StrEqual(gameDir, "open_fortress") || StrEqual(gameDir, "tf2classic") || StrEqual(gameDir, "pf2") ) ) + // Fix for TF2 sourcemods + // @note Regarding the 2025 SDK: https://github.com/alliedmodders/metamod-source/commit/dc41559c7905072feec38b6d45ea14c05da3b855 + if (g_EngineVersion == Engine_HL2DM || (g_EngineVersion == Engine_SDK2013 && (StrEqual(gameDir, "open_fortress") || StrEqual(gameDir, "tf2classic") || StrEqual(gameDir, "pf2")))) { g_EngineVersion = Engine_TF2; } @@ -1872,7 +1873,7 @@ static L4D_ClientSelectedItem(Handle:vote, client, item) { if (item > NATIVEVOTES_VOTE_INVALID && item <= Game_GetMaxItems()) { - new Handle:castEvent; + Handle castEvent; switch (item) { @@ -2345,9 +2346,9 @@ static void TF2CSGO_DisplayVote(NativeVote vote, int[] clients, int num_clients) { char option[8]; Format(option, sizeof(option), "%s%d", TF2CSGO_VOTE_PREFIX, i+1); - char display[TRANSLATION_LENGTH]; Data_GetItemDisplay(vote, i, display, sizeof(display)); + optionsEvent.SetString(option, display); } optionsEvent.SetInt("count", itemCount); @@ -2357,7 +2358,7 @@ static void TF2CSGO_DisplayVote(NativeVote vote, int[] clients, int num_clients) } optionsEvent.Fire(); } - + // Moved to mimic SourceSDK2013's server/vote_controller.cpp // For whatever reason, while the other props are set first, this one's set after the vote_options event if (CheckVoteController()) @@ -2376,7 +2377,6 @@ static void TF2CSGO_DisplayVote(NativeVote vote, int[] clients, int num_clients) } } - MenuAction actions = Data_GetActions(vote); for (int i = 0; i < num_clients; ++i) @@ -2391,12 +2391,12 @@ static void TF2CSGO_DisplayVote(NativeVote vote, int[] clients, int num_clients) } g_curDisplayClient = 0; - + if (!bYesNo) { TF2CSGO_SendOptionsToClient(vote, clients[i]); } - + Handle voteStart = StartMessageOne("VoteStart", clients[i], USERMSG_RELIABLE); if(g_bUserBuf) @@ -2443,7 +2443,6 @@ static void TF2CSGO_DisplayVote(NativeVote vote, int[] clients, int num_clients) } g_curDisplayClient = 0; - } static void TF2CSGO_SendOptionsToClient(NativeVote vote, int client) @@ -2529,7 +2528,7 @@ static void CSGO_VotePass(const char[] translation, const char[] details, int te static void TF2_VotePass(const char[] translation, const char[] details, int team, int client=0) { BfWrite votePass = null; - + if (client <= 0) { votePass = UserMessageToBfWrite(StartMessageAll("VotePass", USERMSG_RELIABLE)); @@ -3076,7 +3075,6 @@ static bool CSGO_VoteTypeToTranslation(NativeVotesType voteType, char[] translat case NativeVotesType_NextLevelMult: { - strcopy(translation, maxlength, CSGO_VOTE_NEXTLEVEL_MULTIPLE_START); bYesNo = false; } @@ -3349,25 +3347,13 @@ static stock NativeVotesType TF2_VoteStringToVoteType(const char[] voteString) } else if (StrEqual(voteString, TF2_VOTE_STRING_AUTOBALANCE, false)) { - if (g_Cvar_AutoBalance.BoolValue) - { - voteType = NativeVotesType_AutoBalanceOff; - } - else - { - voteType = NativeVotesType_AutoBalanceOn; - } + voteType = g_Cvar_AutoBalance.BoolValue ? + NativeVotesType_AutoBalanceOff : NativeVotesType_AutoBalanceOn; } else if (StrEqual(voteString, TF2_VOTE_STRING_CLASSLIMIT, false)) { - if (g_Cvar_ClassLimit.IntValue) - { - voteType = NativeVotesType_ClassLimitsOff; - } - else - { - voteType = NativeVotesType_ClassLimitsOn; - } + voteType = g_Cvar_ClassLimit.IntValue ? + NativeVotesType_ClassLimitsOff : NativeVotesType_ClassLimitsOn; } else if (StrEqual(voteString, TF2_VOTE_STRING_EXTEND, false)) { @@ -3719,5 +3705,4 @@ stock bool Game_IsVoteTypeYesNo(NativeVotesType voteType) } return true; -} - +} \ No newline at end of file diff --git a/addons/sourcemod/scripting/nativevotes_basecommands.sp b/addons/sourcemod/scripting/nativevotes_basecommands.sp index e50e8af..06ccd41 100644 --- a/addons/sourcemod/scripting/nativevotes_basecommands.sp +++ b/addons/sourcemod/scripting/nativevotes_basecommands.sp @@ -38,7 +38,7 @@ #undef REQUIRE_PLUGIN #include -#define VERSION "25w52a" +#define VERSION "26w02a" #pragma newdecls required #pragma semicolon 1 @@ -46,7 +46,7 @@ TopMenu hTopMenu; public Plugin myinfo = { - name = "NativeVotes Basic Commands", + name = "[NativeVotes] Basic Commands", author = "Powerlord and AlliedModders LLC", description = "Revote and Cancel support for NativeVotes", version = VERSION, diff --git a/addons/sourcemod/scripting/nativevotes_basevotes.sp b/addons/sourcemod/scripting/nativevotes_basevotes.sp index 4e8e677..776c9a8 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes.sp @@ -40,11 +40,11 @@ #include #include -#define VERSION "25w52a" +#define VERSION "26w02a" public Plugin:myinfo = { - name = "NativeVotes Basic Votes", + name = "[NativeVotes] Basic Votes", author = "Powerlord and AlliedModders LLC", description = "NativeVotes Basic Vote Commands", version = VERSION, @@ -54,10 +54,10 @@ public Plugin:myinfo = #define VOTE_NO "###no###" #define VOTE_YES "###yes###" -//new Handle:g_hVoteMenu = INVALID_HANDLE; +// Handle g_hVoteMenu; -new Handle:g_Cvar_Limits[3] = {INVALID_HANDLE, ...}; -//new Handle:g_Cvar_VoteSay = INVALID_HANDLE; +ConVar g_ConVars[question]; +// ConVar g_Cvar_VoteSay; enum voteType { @@ -67,41 +67,40 @@ enum voteType question } -new voteType:g_voteType = voteType:question; +voteType g_voteType = question; // Menu API does not provide us with a way to pass multiple peices of data with a single // choice, so some globals are used to hold stuff. // -#define VOTE_CLIENTID 0 -#define VOTE_USERID 1 -new g_voteClient[2]; /* Holds the target's client id and user id */ +#define VOTE_CLIENTID 0 +#define VOTE_USERID 1 +int g_voteClient[2]; // Holds the target's client id and user id -#define VOTE_NAME 0 -#define VOTE_AUTHID 1 -#define VOTE_IP 2 -new String:g_voteInfo[3][65]; /* Holds the target's name, authid, and IP */ +#define VOTE_NAME 0 +#define VOTE_AUTHID 1 +#define VOTE_IP 2 +char g_voteInfo[3][65]; // Holds the target's name, authid, and IP -new String:g_voteArg[256]; /* Used to hold ban/kick reasons or vote questions */ +char g_voteArg[256]; // Used to hold ban/kick reasons or vote questions - -new Handle:hTopMenu = INVALID_HANDLE; +Handle hTopMenu = INVALID_HANDLE; // NativeVotes -new bool:g_NativeVotes; +bool g_NativeVotes; -//new g_Cvar_NativeVotesMenu = INVALID_HANDLE; +//Handle g_Cvar_NativeVotesMenu = INVALID_HANDLE; #include "nativevotes_basevotes/votekick.sp" #include "nativevotes_basevotes/voteban.sp" #include "nativevotes_basevotes/votemap.sp" -public OnPluginStart() +public void OnPluginStart() { LoadTranslations("common.phrases"); LoadTranslations("basevotes.phrases"); LoadTranslations("plugin.basecommands"); LoadTranslations("basebans.phrases"); - + RegAdminCmd("sm_votemap", Command_Votemap, ADMFLAG_VOTE|ADMFLAG_CHANGEMAP, "sm_votemap [mapname2] ... [mapname5] "); RegAdminCmd("sm_votekick", Command_Votekick, ADMFLAG_VOTE|ADMFLAG_KICK, "sm_votekick [reason]"); RegAdminCmd("sm_voteban", Command_Voteban, ADMFLAG_VOTE|ADMFLAG_BAN, "sm_voteban [reason]"); @@ -115,33 +114,31 @@ public OnPluginStart() } */ - g_Cvar_Limits[0] = CreateConVar("sm_vote_map", "0.60", "percent required for successful map vote.", 0, true, 0.05, true, 1.0); - g_Cvar_Limits[1] = CreateConVar("sm_vote_kick", "0.60", "percent required for successful kick vote.", 0, true, 0.05, true, 1.0); - g_Cvar_Limits[2] = CreateConVar("sm_vote_ban", "0.60", "percent required for successful ban vote.", 0, true, 0.05, true, 1.0); + g_ConVars[map] = CreateConVar("sm_vote_map", "0.60", "percent required for successful map vote.", 0, true, 0.05, true, 1.0); + g_ConVars[kick] = CreateConVar("sm_vote_kick", "0.60", "percent required for successful kick vote.", 0, true, 0.05, true, 1.0); + g_ConVars[ban] = CreateConVar("sm_vote_ban", "0.60", "percent required for successful ban vote.", 0, true, 0.05, true, 1.0); CreateConVar("nativevotes_basevotes_version", VERSION, "NativeVotes Basic Votes version", FCVAR_PLUGIN|FCVAR_NOTIFY|FCVAR_DONTRECORD|FCVAR_SPONLY); - + g_SelectedMaps = CreateArray(PLATFORM_MAX_PATH); - + g_MapList = CreateMenu(MenuHandler_Map, MenuAction_DrawItem|MenuAction_Display); SetMenuTitle(g_MapList, "%T", "Please select a map", LANG_SERVER); SetMenuExitBackButton(g_MapList, true); - - decl String:mapListPath[PLATFORM_MAX_PATH]; + + char mapListPath[PLATFORM_MAX_PATH]; BuildPath(Path_SM, mapListPath, sizeof(mapListPath), "configs/adminmenu_maplist.ini"); SetMapListCompatBind("sm_votemap menu", mapListPath); } -public OnAllPluginsLoaded() +public void OnAllPluginsLoaded() { if (FindPluginByFile("basevotes.smx") != INVALID_HANDLE) { - LogMessage("Unloading basevotes to prevent conflicts..."); ServerCommand("sm plugins unload basevotes"); - - decl String:oldPath[PLATFORM_MAX_PATH]; - decl String:newPath[PLATFORM_MAX_PATH]; - + + char oldPath[PLATFORM_MAX_PATH], newPath[PLATFORM_MAX_PATH]; + BuildPath(Path_SM, oldPath, sizeof(oldPath), "plugins/basevotes.smx"); BuildPath(Path_SM, newPath, sizeof(newPath), "plugins/disabled/basevotes.smx"); if (RenameFile(newPath, oldPath)) @@ -153,7 +150,7 @@ public OnAllPluginsLoaded() } /* Account for late loading */ - new Handle:topmenu; + Handle topmenu; if (LibraryExists("adminmenu") && ((topmenu = GetAdminTopMenu()) != INVALID_HANDLE)) { OnAdminMenuReady(topmenu); @@ -162,39 +159,37 @@ public OnAllPluginsLoaded() g_NativeVotes = LibraryExists("nativevotes") && NativeVotes_IsVoteTypeSupported(NativeVotesType_Custom_YesNo); } -public OnLibraryAdded(const String:name[]) +public void OnLibraryAdded(const char[] name) { - new Handle:topmenu; + Handle topmenu; if (StrEqual(name, "adminmenu") && ((topmenu = GetAdminTopMenu()) != INVALID_HANDLE)) { OnAdminMenuReady(topmenu); } - else - if (StrEqual(name, "nativevotes") && NativeVotes_IsVoteTypeSupported(NativeVotesType_Custom_YesNo)) + else if (StrEqual(name, "nativevotes") && NativeVotes_IsVoteTypeSupported(NativeVotesType_Custom_YesNo)) { g_NativeVotes = true; } } -public OnLibraryRemoved(const String:name[]) +public void OnLibraryRemoved(const char[] name) { if (StrEqual(name, "adminmenu")) { hTopMenu = INVALID_HANDLE; } - else - if (StrEqual(name, "nativevotes")) + else if (StrEqual(name, "nativevotes")) { g_NativeVotes = false; } } -public OnConfigsExecuted() +public void OnConfigsExecuted() { g_mapCount = LoadMapList(g_MapList); } -public OnAdminMenuReady(Handle:topmenu) +public void OnAdminMenuReady(Handle topmenu) { /* Block us from being called twice */ if (topmenu == hTopMenu) @@ -206,7 +201,7 @@ public OnAdminMenuReady(Handle:topmenu) hTopMenu = topmenu; /* Build the "Voting Commands" category */ - new TopMenuObject:voting_commands = FindTopMenuCategory(hTopMenu, ADMINMENU_VOTINGCOMMANDS); + TopMenuObject voting_commands = FindTopMenuCategory(hTopMenu, ADMINMENU_VOTINGCOMMANDS); if (voting_commands != INVALID_TOPMENUOBJECT) { @@ -236,12 +231,12 @@ public OnAdminMenuReady(Handle:topmenu) } } -public Action:Command_Vote(client, args) +public Action Command_Vote(int client, int args) { if (args < 1) { CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] Usage: sm_vote [Answer1] [Answer2] ... [Answer5]"); - return Plugin_Handled; + return Plugin_Handled; } if (Internal_IsVoteInProgress()) @@ -255,54 +250,51 @@ public Action:Command_Vote(client, args) return Plugin_Handled; } - decl String:text[256]; + char text[256]; GetCmdArgString(text, sizeof(text)); - decl String:answers[5][64]; - new answerCount; - new len = BreakString(text, g_voteArg, sizeof(g_voteArg)); - new pos = len; - + char answers[5][64]; + int answerCount; + int len = BreakString(text, g_voteArg, sizeof(g_voteArg)); + int pos = len; + while (args > 1 && pos != -1 && answerCount < 5) - { + { pos = BreakString(text[len], answers[answerCount], sizeof(answers[])); answerCount++; - + if (pos != -1) { len += pos; - } + } } - LogAction(client, -1, "\"%L\" initiated a generic vote.", client); CShowActivity2(client, "[{lightgreen}NativeVotes\x01] ", "%t", "Initiate Vote", g_voteArg); - - g_voteType = voteType:question; - new Handle:voteMenu; - + g_voteType = question; + Handle voteMenu; + if (g_NativeVotes && (answerCount < 2 || NativeVotes_IsVoteTypeSupported(NativeVotesType_Custom_Mult)) ) { - new NativeVotesType:nVoteType = answerCount < 2 ? NativeVotesType_Custom_YesNo : NativeVotesType_Custom_Mult; - - voteMenu = NativeVotes_Create(Handler_NativeVoteCallback, nVoteType, MenuAction:MENU_ACTIONS_ALL); + NativeVotesType nVoteType = answerCount < 2 ? NativeVotesType_Custom_YesNo : NativeVotesType_Custom_Mult; + voteMenu = NativeVotes_Create(Handler_NativeVoteCallback, nVoteType, view_as(MENU_ACTIONS_ALL)); NativeVotes_SetTitle(voteMenu, g_voteArg); - + if (answerCount >= 2) { - for (new i = 0; i < answerCount; i++) + for (int i = 0; i < answerCount; i++) { NativeVotes_AddItem(voteMenu, answers[i], answers[i]); - } + } } - + //NativeVotes_SetInitiator(voteMenu, client); NativeVotes_DisplayToAll(voteMenu, 20); } else { - voteMenu = CreateMenu(Handler_VoteCallback, MenuAction:MENU_ACTIONS_ALL); + voteMenu = CreateMenu(Handler_VoteCallback, view_as(MENU_ACTIONS_ALL)); SetMenuTitle(voteMenu, "%s?", g_voteArg); - + if (answerCount < 2) { AddMenuItem(voteMenu, VOTE_YES, "Yes"); @@ -310,16 +302,16 @@ public Action:Command_Vote(client, args) } else { - for (new i = 0; i < answerCount; i++) + for (int i = 0; i < answerCount; i++) { AddMenuItem(voteMenu, answers[i], answers[i]); - } + } } - + SetMenuExitButton(voteMenu, false); - VoteMenuToAll(voteMenu, 20); + VoteMenuToAll(voteMenu, 20); } - return Plugin_Handled; + return Plugin_Handled; } public Handler_VoteCallback(Handle:menu, MenuAction:action, param1, param2) @@ -342,7 +334,7 @@ public Handler_VoteCallback(Handle:menu, MenuAction:action, param1, param2) decl String:buffer[255]; Format(buffer, sizeof(buffer), "%T", title, param1, g_voteInfo[VOTE_NAME]); - new Handle:panel = Handle:param2; + Handle panel = Handle:param2; SetPanelTitle(panel, buffer); } } @@ -386,7 +378,7 @@ public Handler_VoteCallback(Handle:menu, MenuAction:action, param1, param2) if (g_voteType != voteType:question) { - limit = GetConVarFloat(g_Cvar_Limits[g_voteType]); + limit = GetConVarFloat(g_ConVars[g_voteType]); } /* :TODO: g_voteClient[userid] needs to be checked */ @@ -405,7 +397,7 @@ public Handler_VoteCallback(Handle:menu, MenuAction:action, param1, param2) switch (g_voteType) { - case (voteType:question): + case question: { if (strcmp(item, VOTE_NO) == 0 || strcmp(item, VOTE_YES) == 0) { @@ -424,16 +416,16 @@ public Handler_VoteCallback(Handle:menu, MenuAction:action, param1, param2) } } - case (voteType:map): + case map: { LogAction(-1, -1, "Changing map to %s due to vote.", item); CPrintToChatAll("%t", "Changing map", item); - new Handle:dp; + DataPack dp; CreateDataTimer(5.0, Timer_ChangeMap, dp); WritePackString(dp, item); } - case (voteType:kick): + case kick: { if (g_voteArg[0] == '\0') { @@ -450,7 +442,7 @@ public Handler_VoteCallback(Handle:menu, MenuAction:action, param1, param2) } - case (voteType:ban): + case ban: { if (g_voteArg[0] == '\0') { @@ -475,7 +467,7 @@ public Handler_VoteCallback(Handle:menu, MenuAction:action, param1, param2) } } - return 0; + return Plugin_Continue; } public Handler_NativeVoteCallback(Handle:menu, MenuAction:action, param1, param2) @@ -535,7 +527,7 @@ public Handler_NativeVoteCallback(Handle:menu, MenuAction:action, param1, param2 if (g_voteType != voteType:question) { - limit = GetConVarFloat(g_Cvar_Limits[g_voteType]); + limit = GetConVarFloat(g_ConVars[g_voteType]); } /* :TODO: g_voteClient[userid] needs to be checked */ @@ -555,7 +547,7 @@ public Handler_NativeVoteCallback(Handle:menu, MenuAction:action, param1, param2 switch (g_voteType) { - case (voteType:question): + case question: { if (nVoteType == NativeVotesType_Custom_YesNo) { @@ -576,7 +568,7 @@ public Handler_NativeVoteCallback(Handle:menu, MenuAction:action, param1, param2 } } - case (voteType:map): + case map: { if (nVoteType == NativeVotesType_ChgLevel) { @@ -587,12 +579,12 @@ public Handler_NativeVoteCallback(Handle:menu, MenuAction:action, param1, param2 NativeVotes_DisplayPassEx(menu, NativeVotesPass_ChgLevel, item); LogAction(-1, -1, "Changing map to %s due to vote.", item); CPrintToChatAll("%t", "Changing map", item); - new Handle:dp; + DataPack dp; CreateDataTimer(5.0, Timer_ChangeMap, dp); WritePackString(dp, item); } - case (voteType:kick): + case kick: { if (g_voteArg[0] == '\0') { @@ -609,7 +601,7 @@ public Handler_NativeVoteCallback(Handle:menu, MenuAction:action, param1, param2 } } - case (voteType:ban): + case ban: { if (g_voteArg[0] == '\0') { @@ -636,7 +628,7 @@ public Handler_NativeVoteCallback(Handle:menu, MenuAction:action, param1, param2 } } - return 0; + return Plugin_Continue; } /* @@ -660,40 +652,40 @@ VoteMenuClose() } */ -Float:GetVotePercent(votes, totalVotes) +float GetVotePercent(int votes, int totalVotes) { - return FloatDiv(float(votes),float(totalVotes)); + return float(votes) / float(totalVotes); } -bool:TestVoteDelay(client) +bool TestVoteDelay(int client) { - new delay = Internal_CheckVoteDelay(); - - if (delay > 0) - { - if (delay > 60) - { - CReplyToCommand(client, "%t", "Vote Delay Minutes", delay % 60); - } - else - { - CReplyToCommand(client, "%t", "Vote Delay Seconds", delay); - } - + int delay = Internal_CheckVoteDelay(); + + if (delay > 0) + { + if (delay > 60) + { + CReplyToCommand(client, "%t", "Vote Delay Minutes", delay % 60); + } + else + { + CReplyToCommand(client, "%t", "Vote Delay Seconds", delay); + } + if (g_NativeVotes) { NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_Recent, delay); } - - return false; - } - + + return false; + } + return true; } -public Action:Timer_ChangeMap(Handle:timer, Handle:dp) +public Action Timer_ChangeMap(Handle timer, DataPack dp) { - decl String:mapname[65]; + char mapname[96]; ResetPack(dp); ReadPackString(dp, mapname, sizeof(mapname)); @@ -703,32 +695,29 @@ public Action:Timer_ChangeMap(Handle:timer, Handle:dp) return Plugin_Stop; } -bool:Internal_IsVoteInProgress() +bool Internal_IsVoteInProgress() { if (g_NativeVotes) { return NativeVotes_IsVoteInProgress(); } - - return IsVoteInProgress(); + return IsVoteInProgress(); } -Internal_CheckVoteDelay() +int Internal_CheckVoteDelay() { if (g_NativeVotes) { return NativeVotes_CheckVoteDelay(); } - - return CheckVoteDelay(); + return CheckVoteDelay(); } -bool:Internal_IsNewVoteAllowed() +bool Internal_IsNewVoteAllowed() { if (g_NativeVotes) { return NativeVotes_IsNewVoteAllowed(); } - return IsNewVoteAllowed(); } diff --git a/addons/sourcemod/scripting/nativevotes_basevotes/voteban.sp b/addons/sourcemod/scripting/nativevotes_basevotes/voteban.sp index 100a347..1f7d4ec 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes/voteban.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes/voteban.sp @@ -32,7 +32,7 @@ * Version: $Id$ */ -DisplayVoteBanMenu(client, target) +void DisplayVoteBanMenu(int client, int target) { g_voteClient[VOTE_CLIENTID] = target; g_voteClient[VOTE_USERID] = GetClientUserId(target); @@ -44,7 +44,7 @@ DisplayVoteBanMenu(client, target) Format(playerName, sizeof(playerName), "{#%06X}%N\x01", color, target); } else { - Format(playerName, sizeof(playerName), "%s%N\x01", GetClientTeam(target) == 2 ? "{red}" : GetClientTeam(target) == 3 ? "{blue}" : "{grey}", target); + Format(playerName, sizeof(playerName), "{teamcolor}%N\x01", target); } GetClientName(target, g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[])); @@ -57,42 +57,34 @@ DisplayVoteBanMenu(client, target) if (g_NativeVotes) { - new Handle:voteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Custom_YesNo, MenuAction:MENU_ACTIONS_ALL); + Handle voteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Custom_YesNo, view_as(MENU_ACTIONS_ALL)); NativeVotes_SetTitle(voteMenu, "Voteban Player"); NativeVotes_DisplayToAll(voteMenu, 20); NativeVotes_SetTarget(voteMenu, target); } else { - new Handle:voteMenu = CreateMenu(Handler_VoteCallback, MenuAction:MENU_ACTIONS_ALL); + Handle voteMenu = CreateMenu(Handler_VoteCallback, view_as(MENU_ACTIONS_ALL)); SetMenuTitle(voteMenu, "Voteban Player"); AddMenuItem(voteMenu, VOTE_YES, "Yes"); AddMenuItem(voteMenu, VOTE_NO, "No"); SetMenuExitButton(voteMenu, false); VoteMenuToAll(voteMenu, 20); - } + } } -DisplayBanTargetMenu(client) +void DisplayBanTargetMenu(int client) { - new Handle:menu = CreateMenu(MenuHandler_Ban); - - decl String:title[100]; + Handle menu = CreateMenu(MenuHandler_Ban); + char title[100]; Format(title, sizeof(title), "%T:", "Ban vote", client); SetMenuTitle(menu, title); SetMenuExitBackButton(menu, true); - AddTargetsToMenu(menu, client, false, false); - DisplayMenu(menu, client, MENU_TIME_FOREVER); } -public AdminMenu_VoteBan(Handle:topmenu, - TopMenuAction:action, - TopMenuObject:object_id, - param, - String:buffer[], - maxlength) +public void AdminMenu_VoteBan(Handle topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength) { if (action == TopMenuAction_DisplayOption) { @@ -103,73 +95,74 @@ public AdminMenu_VoteBan(Handle:topmenu, DisplayBanTargetMenu(param); } else if (action == TopMenuAction_DrawOption) - { + { /* disable this option if a vote is already running */ buffer[0] = Internal_IsNewVoteAllowed() ? ITEMDRAW_IGNORE : ITEMDRAW_DEFAULT; } } -public MenuHandler_Ban(Handle:menu, MenuAction:action, param1, param2) +public int MenuHandler_Ban(Handle menu, MenuAction action, int param1, int param2) { - if (action == MenuAction_End) - { - CloseHandle(menu); - } - else if (action == MenuAction_Cancel) + switch (action) { - if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE) + case MenuAction_End: { - DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory); + CloseHandle(menu); } - } - else if (action == MenuAction_Select) - { - decl String:info[32], String:name[32]; - new userid, target; - - GetMenuItem(menu, param2, info, sizeof(info), _, name, sizeof(name)); - userid = StringToInt(info); - - if ((target = GetClientOfUserId(userid)) == 0) + case MenuAction_Cancel: { - PrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Player no longer available"); + if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE) + { + DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory); + } } - else if (!CanUserTarget(param1, target)) + case MenuAction_Select: { - PrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Unable to target"); - } - else - { - g_voteArg[0] = '\0'; - DisplayVoteBanMenu(param1, target); + char info[32], name[32]; + int userid, target; + GetMenuItem(menu, param2, info, sizeof(info), _, name, sizeof(name)); + userid = StringToInt(info); + if ((target = GetClientOfUserId(userid)) == 0) + { + PrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Player no longer available"); + } + else if (!CanUserTarget(param1, target)) + { + PrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Unable to target"); + } + else + { + g_voteArg[0] = '\0'; + DisplayVoteBanMenu(param1, target); + } } } + + return Plugin_Continue; } -public Action:Command_Voteban(client, args) +public Action Command_Voteban(int client, int args) { if (args < 1) { CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] Usage: sm_voteban [reason]"); - return Plugin_Handled; + return Plugin_Handled; } if (Internal_IsVoteInProgress()) { CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Vote in Progress"); return Plugin_Handled; - } + } if (!TestVoteDelay(client)) { return Plugin_Handled; } - - decl String:text[256], String:arg[64]; + + char text[256], arg[64]; GetCmdArgString(text, sizeof(text)); - - new len = BreakString(text, arg, sizeof(arg)); - + int len = BreakString(text, arg, sizeof(arg)); if (len != -1) { strcopy(g_voteArg, sizeof(g_voteArg), text[len]); @@ -178,10 +171,10 @@ public Action:Command_Voteban(client, args) { g_voteArg[0] = '\0'; } - - decl String:target_name[MAX_TARGET_LENGTH]; - decl target_list[MAXPLAYERS], target_count, bool:tn_is_ml; - + + char target_name[MAX_TARGET_LENGTH]; + int target_list[MAXPLAYERS], target_count; + bool tn_is_ml; if ((target_count = ProcessTargetString( arg, client, @@ -195,8 +188,6 @@ public Action:Command_Voteban(client, args) ReplyToTargetError(client, target_count); return Plugin_Handled; } - DisplayVoteBanMenu(client, target_list[0]); - return Plugin_Handled; -} +} \ No newline at end of file diff --git a/addons/sourcemod/scripting/nativevotes_basevotes/votekick.sp b/addons/sourcemod/scripting/nativevotes_basevotes/votekick.sp index 415ab96..a7038af 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes/votekick.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes/votekick.sp @@ -32,7 +32,7 @@ * Version: $Id$ */ -DisplayVoteKickMenu(client, target) +void DisplayVoteKickMenu(int client, int target) { g_voteClient[VOTE_CLIENTID] = target; g_voteClient[VOTE_USERID] = GetClientUserId(target); @@ -44,26 +44,26 @@ DisplayVoteKickMenu(client, target) Format(playerName, sizeof(playerName), "{#%06X}%N\x01", color, target); } else { - Format(playerName, sizeof(playerName), "%s%N\x01", GetClientTeam(target) == 2 ? "{red}" : GetClientTeam(target) == 3 ? "{blue}" : "{grey}", target); + Format(playerName, sizeof(playerName), "{teamcolor}%N\x01", target); } GetClientName(target, g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[])); - LogAction(client, target, "\"%L\" initiated a kick vote against %s", client, playerName); + LogAction(client, target, "\"%L\" initiated a kick vote against %N", client, target); CShowActivity(client, "%t", "Initiated Vote Kick", playerName); - + g_voteType = voteType:kick; - + if (g_NativeVotes) { - new Handle:voteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Kick, MenuAction:MENU_ACTIONS_ALL); + Handle voteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Kick, view_as(MENU_ACTIONS_ALL)); // No title, builtin type NativeVotes_SetTarget(voteMenu, target); NativeVotes_DisplayToAll(voteMenu, 20); } else { - new Handle:voteMenu = CreateMenu(Handler_VoteCallback, MenuAction:MENU_ACTIONS_ALL); + Handle voteMenu = CreateMenu(Handler_VoteCallback, MENU_ACTIONS_ALL); SetMenuTitle(voteMenu, "Votekick Player"); AddMenuItem(voteMenu, VOTE_YES, "Yes"); AddMenuItem(voteMenu, VOTE_NO, "No"); @@ -72,26 +72,18 @@ DisplayVoteKickMenu(client, target) } } -DisplayKickTargetMenu(client) +void DisplayKickTargetMenu(int client) { - new Handle:menu = CreateMenu(MenuHandler_Kick); - - decl String:title[100]; + Handle menu = CreateMenu(MenuHandler_Kick); + char title[100]; Format(title, sizeof(title), "%T:", "Kick vote", client); SetMenuTitle(menu, title); SetMenuExitBackButton(menu, true); - AddTargetsToMenu(menu, client, false, false); - DisplayMenu(menu, client, MENU_TIME_FOREVER); } -public AdminMenu_VoteKick(Handle:topmenu, - TopMenuAction:action, - TopMenuObject:object_id, - param, - String:buffer[], - maxlength) +public void AdminMenu_VoteKick(Handle topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength) { if (action == TopMenuAction_DisplayOption) { @@ -102,13 +94,13 @@ public AdminMenu_VoteKick(Handle:topmenu, DisplayKickTargetMenu(param); } else if (action == TopMenuAction_DrawOption) - { + { /* disable this option if a vote is already running */ buffer[0] = !IsNewVoteAllowed() ? ITEMDRAW_IGNORE : ITEMDRAW_DEFAULT; } } -public MenuHandler_Kick(Handle:menu, MenuAction:action, param1, param2) +public int MenuHandler_Kick(Handle menu, MenuAction action, int param1, int param2) { if (action == MenuAction_End) { @@ -123,12 +115,10 @@ public MenuHandler_Kick(Handle:menu, MenuAction:action, param1, param2) } else if (action == MenuAction_Select) { - decl String:info[32], String:name[32]; - new userid, target; - + char info[32], name[32]; + int userid, target; GetMenuItem(menu, param2, info, sizeof(info), _, name, sizeof(name)); userid = StringToInt(info); - if ((target = GetClientOfUserId(userid)) == 0) { PrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Player no longer available"); @@ -143,38 +133,33 @@ public MenuHandler_Kick(Handle:menu, MenuAction:action, param1, param2) DisplayVoteKickMenu(param1, target); } } + return 0; } -public Action:Command_Votekick(client, args) +public Action Command_Votekick(int client, int args) { if (args < 1) { CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] Usage: sm_votekick [reason]"); - return Plugin_Handled; + return Plugin_Handled; } - if (Internal_IsVoteInProgress()) { CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Vote in Progress"); return Plugin_Handled; - } - + } if (!TestVoteDelay(client)) { return Plugin_Handled; } - - decl String:text[256], String:arg[64]; + char text[256], arg[64]; GetCmdArgString(text, sizeof(text)); - - new len = BreakString(text, arg, sizeof(arg)); - - new target = FindTarget(client, arg); + int len = BreakString(text, arg, sizeof(arg)); + int target = FindTarget(client, arg); if (target == -1) { return Plugin_Handled; } - if (len != -1) { strcopy(g_voteArg, sizeof(g_voteArg), text[len]); @@ -183,8 +168,6 @@ public Action:Command_Votekick(client, args) { g_voteArg[0] = '\0'; } - DisplayVoteKickMenu(client, target); - return Plugin_Handled; } \ No newline at end of file diff --git a/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp b/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp index 0e7612c..75e67c0 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp @@ -32,22 +32,22 @@ * Version: $Id$ */ -new Handle:g_MapList = INVALID_HANDLE; -new g_mapCount; +Handle g_MapList; +int g_mapCount; -new Handle:g_SelectedMaps; -new bool:g_VoteMapInUse; +Handle g_SelectedMaps; +bool g_VoteMapInUse; -DisplayVoteMapMenu(client, mapCount, String:maps[5][]) +void DisplayVoteMapMenu(int client, int mapCount, char maps[5][PLATFORM_MAX_PATH]) { LogAction(client, -1, "\"%L\" initiated a map vote.", client); CShowActivity2(client, "[{lightgreen}NativeVotes\x01] ", "%t", "Initiated Vote Map"); - g_voteType = voteType:map; + g_voteType = map; - decl String:resolvedMaps[5][PLATFORM_MAX_PATH]; - decl String:displayNames[5][PLATFORM_MAX_PATH]; - for (new i = 0; i < mapCount; i++) + char resolvedMaps[5][PLATFORM_MAX_PATH]; + char displayNames[5][PLATFORM_MAX_PATH]; + for (int i = 0; i < mapCount; i++) { FindMap(maps[i], resolvedMaps[i], sizeof(resolvedMaps[])); GetMapDisplayName(resolvedMaps[i], displayNames[i], sizeof(displayNames[])); @@ -55,35 +55,33 @@ DisplayVoteMapMenu(client, mapCount, String:maps[5][]) if (g_NativeVotes && (mapCount == 1 || NativeVotes_IsVoteTypeSupported(NativeVotesType_NextLevelMult)) ) { - new Handle:voteMenu; - + Handle voteMenu; if (mapCount == 1) { strcopy(g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[]), displayNames[0]); - - voteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_ChgLevel, MenuAction:MENU_ACTIONS_ALL); + + voteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_ChgLevel, view_as(MENU_ACTIONS_ALL)); // No title, builtin type NativeVotes_SetDetails(voteMenu, displayNames[0]); } else { - voteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_NextLevelMult, MenuAction:MENU_ACTIONS_ALL); - + voteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_NextLevelMult, view_as(MENU_ACTIONS_ALL)); + g_voteInfo[VOTE_NAME][0] = '\0'; // No title, builtin type - for (new i = 0; i < mapCount; i++) + for (int i = 0; i < mapCount; i++) { NativeVotes_AddItem(voteMenu, resolvedMaps[i], displayNames[i]); } } - NativeVotes_DisplayToAll(voteMenu, 20); } else { - new Handle:voteMenu = CreateMenu(Handler_VoteCallback, MenuAction:MENU_ACTIONS_ALL); + Handle voteMenu = CreateMenu(Handler_VoteCallback, MENU_ACTIONS_ALL); if (mapCount == 1) { @@ -98,134 +96,127 @@ DisplayVoteMapMenu(client, mapCount, String:maps[5][]) g_voteInfo[VOTE_NAME][0] = '\0'; SetMenuTitle(voteMenu, "Map Vote"); - for (new i = 0; i < mapCount; i++) + for (int i = 0; i < mapCount; i++) { AddMenuItem(voteMenu, resolvedMaps[i], displayNames[i]); - } + } } - SetMenuExitButton(voteMenu, false); VoteMenuToAll(voteMenu, 20); } } -ResetMenu() +void ResetMenu() { g_VoteMapInUse = false; ClearArray(g_SelectedMaps); } -ConfirmVote(client) +void ConfirmVote(int client) { - new Handle:menu = CreateMenu(MenuHandler_Confirm); + Handle menu = CreateMenu(MenuHandler_Confirm); + char title[100]; - decl String:title[100]; Format(title, sizeof(title), "%T:", "Confirm Vote", client); SetMenuTitle(menu, title); SetMenuExitBackButton(menu, true); - decl String:itemtext[256]; + char itemtext[256]; Format(itemtext, sizeof(itemtext), "%T", "Start the Vote", client); AddMenuItem(menu, "Confirm", itemtext); - DisplayMenu(menu, client, MENU_TIME_FOREVER); + DisplayMenu(menu, client, MENU_TIME_FOREVER); } -public MenuHandler_Confirm(Handle:menu, MenuAction:action, param1, param2) +public int MenuHandler_Confirm(Handle menu, MenuAction action, int param1, int param2) { - if (action == MenuAction_End) + switch (action) { - CloseHandle(menu); - g_VoteMapInUse = false; - } - else if (action == MenuAction_Cancel) - { - ResetMenu(); - - if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE) + case MenuAction_End: { - DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory); + CloseHandle(menu); + g_VoteMapInUse = false; } - } - else if (action == MenuAction_Select) - { - decl String:maps[5][64]; - new selectedmaps = GetArraySize(g_SelectedMaps); - - for (new i = 0; i < selectedmaps; i++) + case MenuAction_Cancel: { - GetArrayString(g_SelectedMaps, i, maps[i], sizeof(maps[])); - } - - DisplayVoteMapMenu(param1, selectedmaps, maps); - - ResetMenu(); - } -} - -public MenuHandler_Map(Handle:menu, MenuAction:action, param1, param2) -{ - if (action == MenuAction_Cancel) - { - if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE) - { - ConfirmVote(param1); + ResetMenu(); + if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE) + { + DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory); + } } - else // no action was selected. + case MenuAction_Select: { - /* Re-enable the menu option */ + char maps[5][64]; + int selectedmaps = GetArraySize(g_SelectedMaps); + for (int i = 0; i < selectedmaps; i++) + { + GetArrayString(g_SelectedMaps, i, maps[i], sizeof(maps[])); + } + DisplayVoteMapMenu(param1, selectedmaps, maps); ResetMenu(); } } - else if (action == MenuAction_DrawItem) + + return Plugin_Continue; +} + +public int MenuHandler_Map(Handle menu, MenuAction action, int param1, int param2) +{ + switch (action) { - decl String:info[32], String:name[32]; - - GetMenuItem(menu, param2, info, sizeof(info), _, name, sizeof(name)); - - if (FindStringInArray(g_SelectedMaps, info) != -1) + case MenuAction_Cancel: { - return ITEMDRAW_IGNORE; + if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE) + { + ConfirmVote(param1); + } + else // no action was selected. + { + /* Re-enable the menu option */ + ResetMenu(); + } } - else + case MenuAction_DrawItem: { - return ITEMDRAW_DEFAULT; + char info[32], name[32]; + GetMenuItem(menu, param2, info, sizeof(info), _, name, sizeof(name)); + if (FindStringInArray(g_SelectedMaps, info) != -1) + { + return ITEMDRAW_IGNORE; + } + else + { + return ITEMDRAW_DEFAULT; + } } - } - else if (action == MenuAction_Select) - { - decl String:info[32], String:name[32]; - - GetMenuItem(menu, param2, info, sizeof(info), _, name, sizeof(name)); - - PushArrayString(g_SelectedMaps, info); - - /* Redisplay the list */ - if (GetArraySize(g_SelectedMaps) < 5) + case MenuAction_Select: { - DisplayMenu(g_MapList, param1, MENU_TIME_FOREVER); + char info[32], name[32]; + GetMenuItem(menu, param2, info, sizeof(info), _, name, sizeof(name)); + PushArrayString(g_SelectedMaps, info); + /* Redisplay the list */ + if (GetArraySize(g_SelectedMaps) < 5) + { + DisplayMenu(g_MapList, param1, MENU_TIME_FOREVER); + } + else + { + ConfirmVote(param1); + } } - else + case MenuAction_Display: { - ConfirmVote(param1); + char title[128]; + Format(title, sizeof(title), "%T", "Please select a map", param1); + SetPanelTitle(view_as(param2), title); } } - else if (action == MenuAction_Display) - { - decl String:title[128]; - Format(title, sizeof(title), "%T", "Please select a map", param1); - SetPanelTitle(Handle:param2, title); - } - return 0; + return Plugin_Continue; } -public AdminMenu_VoteMap(Handle:topmenu, - TopMenuAction:action, - TopMenuObject:object_id, - param, - String:buffer[], - maxlength) +public void AdminMenu_VoteMap(Handle topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength) { if (action == TopMenuAction_DisplayOption) { @@ -239,24 +230,24 @@ public AdminMenu_VoteMap(Handle:topmenu, g_VoteMapInUse = true; DisplayMenu(g_MapList, param, MENU_TIME_FOREVER); } - else + else { PrintToChat(param, "[{lightgreen}NativeVotes\x01] %T", "Map Vote In Use", param); } } else if (action == TopMenuAction_DrawOption) - { + { /* disable this option if a vote is already running, theres no maps listed or someone else has already acessed this menu */ buffer[0] = (!IsNewVoteAllowed() || g_mapCount < 1 || g_VoteMapInUse) ? ITEMDRAW_IGNORE : ITEMDRAW_DEFAULT; } } -public Action:Command_Votemap(client, args) +public Action Command_Votemap(int client, int args) { if (args < 1) { CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] Usage: sm_votemap [mapname2] ... [mapname5]"); - return Plugin_Handled; + return Plugin_Handled; } if (Internal_IsVoteInProgress()) @@ -264,49 +255,48 @@ public Action:Command_Votemap(client, args) CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Vote in Progress"); return Plugin_Handled; } - - if (!TestVoteDelay(client)) + + if (!TestVoteDelay(client) && !CheckCommandAccess(client, "sm_votemap", ADMFLAG_CHANGEMAP)) { return Plugin_Handled; } - decl String:text[256]; + char text[256]; GetCmdArgString(text, sizeof(text)); - - decl String:maps[5][64]; - new mapCount; - new len, pos; + + char maps[5][PLATFORM_MAX_PATH]; + int mapCount; + int len, pos; while (pos != -1 && mapCount < 5) - { + { pos = BreakString(text[len], maps[mapCount], sizeof(maps[])); if (!IsMapValid(maps[mapCount])) { CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Map was not found", maps[mapCount]); return Plugin_Handled; - } + } mapCount++; if (pos != -1) { len += pos; - } + } } DisplayVoteMapMenu(client, mapCount, maps); - return Plugin_Handled; + return Plugin_Handled; } -new Handle:g_map_array = INVALID_HANDLE; -new g_map_serial = -1; +Handle g_map_array = INVALID_HANDLE; +int g_map_serial = -1; -LoadMapList(Handle:menu) +int LoadMapList(Handle menu) { - new Handle:map_array; - + Handle map_array; if ((map_array = ReadMapList(g_map_array, g_map_serial, "sm_votemap menu", @@ -323,10 +313,10 @@ LoadMapList(Handle:menu) RemoveAllMenuItems(menu); - decl String:map_name[64]; - new map_count = GetArraySize(g_map_array); + char map_name[96]; + int map_count = GetArraySize(g_map_array); - for (new i = 0; i < map_count; i++) + for (int i = 0; i < map_count; i++) { GetArrayString(g_map_array, i, map_name, sizeof(map_name)); AddMenuItem(menu, map_name, map_name); diff --git a/addons/sourcemod/scripting/nativevotes_funvotes.sp b/addons/sourcemod/scripting/nativevotes_funvotes.sp index 99aa484..d8edd16 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes.sp @@ -42,11 +42,11 @@ #include #include -#define VERSION "25w52a" +#define VERSION "26w02a" -public Plugin:myinfo = +public Plugin myinfo = { - name = "NativeVotes Fun Votes", + name = "[NativeVotes] Fun Votes", author = "Powerlord and AlliedModders LLC", description = "NativeVotes Fun Vote Commands", version = VERSION, @@ -56,42 +56,52 @@ public Plugin:myinfo = #define VOTE_NO "###no###" #define VOTE_YES "###yes###" -//new Handle:g_hVoteMenu = INVALID_HANDLE; +// Handle g_hVoteMenu; -new Handle:g_Cvar_Limits[5] = {INVALID_HANDLE, ...}; -new Handle:g_Cvar_Gravity = INVALID_HANDLE; -new Handle:g_Cvar_Alltalk = INVALID_HANDLE; -new Handle:g_Cvar_FF = INVALID_HANDLE; +enum +{ + sv_gravity, + sv_alltalk, + mp_friendlyfire, + + vote_gravity, + vote_burn, + vote_slay, + vote_alltalk, + vote_ff, -// new Handle:g_Cvar_Show = INVALID_HANDLE; + MAX_CONVARS +} + +ConVar g_ConVars[MAX_CONVARS]; enum voteType { - gravity = 0, + gravity, burn, slay, alltalk, ff }; -new voteType:g_voteType = voteType:gravity; +voteType g_voteType = gravity; // Menu API does not provide us with a way to pass multiple peices of data with a single // choice, so some globals are used to hold stuff. // -#define VOTE_CLIENTID 0 -#define VOTE_USERID 1 -new g_voteClient[2]; /* Holds the target's client id and user id */ +#define VOTE_CLIENTID 0 +#define VOTE_USERID 1 +int g_voteClient[2]; // Holds the target's client id and user id -#define VOTE_NAME 0 -#define VOTE_AUTHID 1 -#define VOTE_IP 2 -new String:g_voteInfo[3][65]; /* Holds the target's name, authid, and IP */ +#define VOTE_NAME 0 +#define VOTE_AUTHID 1 +#define VOTE_IP 2 +char g_voteInfo[3][65]; // Holds the target's name, authid, and IP -new Handle:hTopMenu = INVALID_HANDLE; +Handle hTopMenu; // NativeVotes -new bool:g_NativeVotes; +bool g_NativeVotes; #include "nativevotes_funvotes/votegravity.sp" #include "nativevotes_funvotes/voteburn.sp" @@ -99,7 +109,7 @@ new bool:g_NativeVotes; #include "nativevotes_funvotes/votealltalk.sp" #include "nativevotes_funvotes/voteff.sp" -public OnPluginStart() +public void OnPluginStart() { LoadTranslations("common.phrases"); LoadTranslations("basevotes.phrases"); @@ -112,91 +122,75 @@ public OnPluginStart() RegAdminCmd("sm_votealltalk", Command_VoteAlltalk, ADMFLAG_VOTE, "sm_votealltalk"); RegAdminCmd("sm_voteff", Command_VoteFF, ADMFLAG_VOTE, "sm_voteff"); - g_Cvar_Limits[0] = CreateConVar("sm_vote_gravity", "0.60", "percent required for successful gravity vote.", 0, true, 0.05, true, 1.0); - g_Cvar_Limits[1] = CreateConVar("sm_vote_burn", "0.60", "percent required for successful burn vote.", 0, true, 0.05, true, 1.0); - g_Cvar_Limits[2] = CreateConVar("sm_vote_slay", "0.60", "percent required for successful slay vote.", 0, true, 0.05, true, 1.0); - g_Cvar_Limits[3] = CreateConVar("sm_vote_alltalk", "0.60", "percent required for successful alltalk vote.", 0, true, 0.05, true, 1.0); - g_Cvar_Limits[4] = CreateConVar("sm_vote_ff", "0.60", "percent required for successful friendly fire vote.", 0, true, 0.05, true, 1.0); + g_ConVars[sv_gravity] = FindConVar("sv_gravity"); + g_ConVars[sv_alltalk] = FindConVar("sv_alltalk"); + g_ConVars[mp_friendlyfire] = FindConVar("mp_friendlyfire"); + g_ConVars[vote_gravity] = CreateConVar("sm_vote_gravity", "0.60", "Percent required for successful gravity vote.", 0, true, 0.05, true, 1.0); + g_ConVars[vote_burn] = CreateConVar("sm_vote_burn", "0.60", "Percent required for successful burn vote.", 0, true, 0.05, true, 1.0); + g_ConVars[vote_slay] = CreateConVar("sm_vote_slay", "0.60", "Percent required for successful slay vote.", 0, true, 0.05, true, 1.0); + g_ConVars[vote_alltalk] = CreateConVar("sm_vote_alltalk", "0.60", "Percent required for successful alltalk vote.", 0, true, 0.05, true, 1.0); + g_ConVars[vote_ff] = CreateConVar("sm_vote_ff", "0.60", "Percent required for successful friendly fire vote.", 0, true, 0.05, true, 1.0); CreateConVar("nativevotes_funvotes_version", VERSION, "NativeVotes Fun Votes version", FCVAR_PLUGIN|FCVAR_NOTIFY|FCVAR_DONTRECORD|FCVAR_SPONLY); - - g_Cvar_Gravity = FindConVar("sv_gravity"); - g_Cvar_Alltalk = FindConVar("sv_alltalk"); - g_Cvar_FF = FindConVar("mp_friendlyfire"); - - /* - g_Cvar_Show = FindConVar("sm_vote_show"); - if (g_Cvar_Show == INVALID_HANDLE) - { - g_Cvar_Show = CreateConVar("sm_vote_show", "1", "Show player's votes? Default on.", 0, true, 0.0, true, 1.0); - } - */ } -public OnAllPluginsLoaded() +public void OnAllPluginsLoaded() { - if (FindPluginByFile("basefunvotes.smx") != INVALID_HANDLE) + if (FindPluginByFile("basefunvotes.smx") != null) { - SetFailState("This plugin replaces basefuncommands. You cannot run both at once."); + SetFailState("This plugin replaces basefuncommands. You cannot run both at once."); } - if (FindPluginByFile("funvotes.smx") != INVALID_HANDLE) + if (FindPluginByFile("funvotes.smx") != null) { LogMessage("Unloading funvotes to prevent conflicts..."); ServerCommand("sm plugins unload funvotes"); - - decl String:oldPath[PLATFORM_MAX_PATH]; - decl String:newPath[PLATFORM_MAX_PATH]; - + + char oldPath[PLATFORM_MAX_PATH], newPath[PLATFORM_MAX_PATH]; + BuildPath(Path_SM, oldPath, sizeof(oldPath), "plugins/funvotes.smx"); BuildPath(Path_SM, newPath, sizeof(newPath), "plugins/disabled/funvotes.smx"); if (RenameFile(newPath, oldPath)) { LogMessage("Moving funvotes to disabled."); } - - //SetFailState("This plugin replaces funvotes. You cannot run both at once."); - } - /* Account for late loading */ - new Handle:topmenu; - if (LibraryExists("adminmenu") && ((topmenu = GetAdminTopMenu()) != INVALID_HANDLE)) + Handle topmenu; + if (LibraryExists("adminmenu") && ((topmenu = GetAdminTopMenu()) != null)) { OnAdminMenuReady(topmenu); } - + g_NativeVotes = LibraryExists("nativevotes") && NativeVotes_IsVoteTypeSupported(NativeVotesType_Custom_YesNo); } -public OnLibraryAdded(const String:name[]) +public void OnLibraryAdded(const char[] name) { - new Handle:topmenu; - if (StrEqual(name, "adminmenu") && ((topmenu = GetAdminTopMenu()) != INVALID_HANDLE)) + Handle topmenu; + if (StrEqual(name, "adminmenu") && ((topmenu = GetAdminTopMenu()) != null)) { OnAdminMenuReady(topmenu); } - else - if (StrEqual(name, "nativevotes") && NativeVotes_IsVoteTypeSupported(NativeVotesType_Custom_YesNo)) + else if (StrEqual(name, "nativevotes") && NativeVotes_IsVoteTypeSupported(NativeVotesType_Custom_YesNo)) { g_NativeVotes = true; } } -public OnLibraryRemoved(const String:name[]) +public void OnLibraryRemoved(const char[] name) { if (StrEqual(name, "adminmenu")) { - hTopMenu = INVALID_HANDLE; + hTopMenu = null; } - else - if (StrEqual(name, "nativevotes")) + else if (StrEqual(name, "nativevotes")) { g_NativeVotes = false; } } -public OnAdminMenuReady(Handle:topmenu) +public void OnAdminMenuReady(Handle topmenu) { /* Block us from being called twice */ if (topmenu == hTopMenu) @@ -208,8 +202,8 @@ public OnAdminMenuReady(Handle:topmenu) hTopMenu = topmenu; /* Build the "Voting Commands" category */ - new TopMenuObject:voting_commands = FindTopMenuCategory(hTopMenu, ADMINMENU_VOTINGCOMMANDS); - + TopMenuObject voting_commands = FindTopMenuCategory(hTopMenu, ADMINMENU_VOTINGCOMMANDS); + if (voting_commands != INVALID_TOPMENUOBJECT) { AddToTopMenu(hTopMenu, @@ -219,7 +213,7 @@ public OnAdminMenuReady(Handle:topmenu) voting_commands, "sm_votegravity", ADMFLAG_VOTE); - + AddToTopMenu(hTopMenu, "sm_voteburn", TopMenuObject_Item, @@ -227,7 +221,7 @@ public OnAdminMenuReady(Handle:topmenu) voting_commands, "sm_voteburn", ADMFLAG_VOTE|ADMFLAG_SLAY); - + AddToTopMenu(hTopMenu, "sm_voteslay", TopMenuObject_Item, @@ -235,7 +229,7 @@ public OnAdminMenuReady(Handle:topmenu) voting_commands, "sm_voteslay", ADMFLAG_VOTE|ADMFLAG_SLAY); - + AddToTopMenu(hTopMenu, "sm_votealltalk", TopMenuObject_Item, @@ -243,7 +237,7 @@ public OnAdminMenuReady(Handle:topmenu) voting_commands, "sm_votealltalk", ADMFLAG_VOTE); - + AddToTopMenu(hTopMenu, "sm_voteff", TopMenuObject_Item, @@ -254,7 +248,7 @@ public OnAdminMenuReady(Handle:topmenu) } } -public Handler_VoteCallback(Handle:menu, MenuAction:action, param1, param2) +public int Handler_VoteCallback(Handle menu, MenuAction action, int param1, int param2) { switch (action) { @@ -266,24 +260,24 @@ public Handler_VoteCallback(Handle:menu, MenuAction:action, param1, param2) case MenuAction_Display: { - decl String:title[64]; + char title[64]; GetMenuTitle(menu, title, sizeof(title)); - decl String:buffer[255]; + char buffer[255]; Format(buffer, sizeof(buffer), "%T", title, param1, g_voteInfo[VOTE_NAME]); - new Handle:panel = Handle:param2; + Handle panel = view_as(param2); SetPanelTitle(panel, buffer); } case MenuAction_DisplayItem: { - decl String:display[64]; + char display[64]; GetMenuItem(menu, param2, "", 0, _, display, sizeof(display)); if (strcmp(display, VOTE_NO) == 0 || strcmp(display, VOTE_YES) == 0) { - decl String:buffer[255]; + char buffer[255]; Format(buffer, sizeof(buffer), "%T", display, param1); return RedrawMenuItem(buffer); @@ -300,8 +294,9 @@ public Handler_VoteCallback(Handle:menu, MenuAction:action, param1, param2) case MenuAction_VoteEnd: { - decl String:item[64]; - new Float:percent, Float:limit, votes, totalVotes; + char item[64]; + float percent, limit; + int votes, totalVotes; GetMenuVoteInfo(param2, votes, totalVotes); GetMenuItem(menu, param1, item, sizeof(item)); @@ -313,9 +308,9 @@ public Handler_VoteCallback(Handle:menu, MenuAction:action, param1, param2) percent = GetVotePercent(votes, totalVotes); - limit = GetConVarFloat(g_Cvar_Limits[g_voteType]); + limit = GetConVarFloat(g_ConVars[g_voteType]); - /* :TODO: g_voteClient[userid] needs to be checked */ + // :TODO: g_voteClient[userid] needs to be checked // A multi-argument vote is "always successful", but have to check if its a Yes/No vote. if ((strcmp(item, VOTE_YES) == 0 && FloatCompare(percent,limit) < 0 && param1 == 0) || (strcmp(item, VOTE_NO) == 0 && param1 == 1)) @@ -331,14 +326,14 @@ public Handler_VoteCallback(Handle:menu, MenuAction:action, param1, param2) switch (g_voteType) { - case (voteType:gravity): + case gravity: { CPrintToChatAll("%t", "Cvar changed", "sv_gravity", item); LogAction(-1, -1, "Changing gravity to %s due to vote.", item); - SetConVarInt(g_Cvar_Gravity, StringToInt(item)); + SetConVarInt(g_ConVars[sv_gravity], StringToInt(item)); } - case (voteType:burn): + case burn: { CPrintToChatAll("%t", "Set target on fire", "_s", g_voteInfo[VOTE_NAME]); LogAction(-1, g_voteClient[VOTE_CLIENTID], "Vote burn successful, igniting \"%L\"", g_voteClient[VOTE_CLIENTID]); @@ -346,7 +341,7 @@ public Handler_VoteCallback(Handle:menu, MenuAction:action, param1, param2) IgniteEntity(g_voteClient[VOTE_CLIENTID], 19.8); } - case (voteType:slay): + case slay: { CPrintToChatAll("%t", "Slayed player", g_voteInfo[VOTE_NAME]); LogAction(-1, g_voteClient[VOTE_CLIENTID], "Vote slay successful, slaying \"%L\"", g_voteClient[VOTE_CLIENTID]); @@ -355,32 +350,31 @@ public Handler_VoteCallback(Handle:menu, MenuAction:action, param1, param2) ForcePlayerSuicide(g_voteClient[VOTE_CLIENTID]); } - case (voteType:alltalk): + case alltalk: { - CPrintToChatAll("%t", "Cvar changed", "sv_alltalk", (GetConVarBool(g_Cvar_Alltalk) ? "0" : "1")); - LogAction(-1, -1, "Changing alltalk to %s due to vote.", (GetConVarBool(g_Cvar_Alltalk) ? "0" : "1")); - SetConVarBool(g_Cvar_Alltalk, !GetConVarBool(g_Cvar_Alltalk)); + CPrintToChatAll("%t", "Cvar changed", "sv_alltalk", (GetConVarBool(g_ConVars[sv_alltalk]) ? "0" : "1")); + LogAction(-1, -1, "Changing alltalk to %s due to vote.", (GetConVarBool(g_ConVars[sv_alltalk]) ? "0" : "1")); + SetConVarBool(g_ConVars[sv_alltalk], !GetConVarBool(g_ConVars[sv_alltalk])); } - case (voteType:ff): + case ff: { - CPrintToChatAll("%t", "Cvar changed", "mp_friendlyfire", (GetConVarBool(g_Cvar_FF) ? "0" : "1")); - LogAction(-1, -1, "Changing friendly fire to %s due to vote.", (GetConVarBool(g_Cvar_FF) ? "0" : "1")); - SetConVarBool(g_Cvar_FF, !GetConVarBool(g_Cvar_FF)); + CPrintToChatAll("%t", "Cvar changed", "mp_friendlyfire", (GetConVarBool(g_ConVars[mp_friendlyfire]) ? "0" : "1")); + LogAction(-1, -1, "Changing friendly fire to %s due to vote.", (GetConVarBool(g_ConVars[mp_friendlyfire]) ? "0" : "1")); + SetConVarBool(g_ConVars[mp_friendlyfire], !GetConVarBool(g_ConVars[mp_friendlyfire])); } } } } } - return 0; + return Plugin_Continue; } -public Handler_NativeVoteCallback(Handle:menu, MenuAction:action, param1, param2) +public int Handler_NativeVoteCallback(Handle menu, MenuAction action, int param1, int param2) { switch (action) { - case MenuAction_End: { NativeVotes_Close(menu); @@ -388,16 +382,16 @@ public Handler_NativeVoteCallback(Handle:menu, MenuAction:action, param1, param2 case MenuAction_Display: { - new NativeVotesType:nVoteType = NativeVotes_GetType(menu); + NativeVotesType nVoteType = NativeVotes_GetType(menu); if (nVoteType == NativeVotesType_Custom_YesNo || nVoteType == NativeVotesType_Custom_Mult) { - decl String:title[64]; + char title[64]; NativeVotes_GetTitle(menu, title, sizeof(title)); - decl String:buffer[255]; + char buffer[255]; Format(buffer, sizeof(buffer), "%T", title, param1, g_voteInfo[VOTE_NAME]); - return _:NativeVotes_RedrawVoteTitle(buffer); + return NativeVotes_RedrawVoteTitle(buffer); } } @@ -416,10 +410,11 @@ public Handler_NativeVoteCallback(Handle:menu, MenuAction:action, param1, param2 case MenuAction_VoteEnd: { - decl String:item[64]; - new Float:percent, Float:limit, votes, totalVotes; + char item[64]; + float percent, limit; + int votes, totalVotes; - new NativeVotesType:nVoteType = NativeVotes_GetType(menu); + NativeVotesType nVoteType = NativeVotes_GetType(menu); NativeVotes_GetInfo(param2, votes, totalVotes); NativeVotes_GetItem(menu, param1, item, sizeof(item)); @@ -431,9 +426,9 @@ public Handler_NativeVoteCallback(Handle:menu, MenuAction:action, param1, param2 percent = GetVotePercent(votes, totalVotes); - limit = GetConVarFloat(g_Cvar_Limits[g_voteType]); + limit = GetConVarFloat(g_ConVars[g_voteType]); - /* :TODO: g_voteClient[userid] needs to be checked */ + // :TODO: g_voteClient[userid] needs to be checked // A multi-argument vote is "always successful", but have to check if its a Yes/No vote. if ((nVoteType != NativeVotesType_NextLevelMult && nVoteType != NativeVotesType_Custom_Mult) && ((param1 == NATIVEVOTES_VOTE_YES && FloatCompare(percent,limit) < 0) || (param1 == NATIVEVOTES_VOTE_NO))) @@ -450,15 +445,15 @@ public Handler_NativeVoteCallback(Handle:menu, MenuAction:action, param1, param2 switch (g_voteType) { - case (voteType:gravity): + case gravity: { CPrintToChatAll("%t", "Cvar changed", "sv_gravity", item); NativeVotes_DisplayPassCustom(menu, "%t", "Cvar changed", "sv_gravity", item); LogAction(-1, -1, "Changing gravity to %s due to vote.", item); - SetConVarInt(g_Cvar_Gravity, StringToInt(item)); + SetConVarInt(g_ConVars[sv_gravity], StringToInt(item)); } - case (voteType:burn): + case burn: { CPrintToChatAll("%t", "Set target on fire", "_s", g_voteInfo[VOTE_NAME]); NativeVotes_DisplayPassCustom(menu, "%t", "Set target on fire", "_s", g_voteInfo[VOTE_NAME]); @@ -467,7 +462,7 @@ public Handler_NativeVoteCallback(Handle:menu, MenuAction:action, param1, param2 IgniteEntity(g_voteClient[VOTE_CLIENTID], 19.8); } - case (voteType:slay): + case slay: { CPrintToChatAll("%t", "Slayed player", g_voteInfo[VOTE_NAME]); NativeVotes_DisplayPassCustom(menu, "%t", "Slayed player", g_voteInfo[VOTE_NAME]); @@ -477,86 +472,85 @@ public Handler_NativeVoteCallback(Handle:menu, MenuAction:action, param1, param2 ForcePlayerSuicide(g_voteClient[VOTE_CLIENTID]); } - case (voteType:alltalk): + case alltalk: { - CPrintToChatAll("%t", "Cvar changed", "sv_alltalk", (GetConVarBool(g_Cvar_Alltalk) ? "0" : "1")); - NativeVotes_DisplayPassCustom(menu, "%t", "Cvar changed", "sv_alltalk", (GetConVarBool(g_Cvar_Alltalk) ? "0" : "1")); - LogAction(-1, -1, "Changing alltalk to %s due to vote.", (GetConVarBool(g_Cvar_Alltalk) ? "0" : "1")); - SetConVarBool(g_Cvar_Alltalk, !GetConVarBool(g_Cvar_Alltalk)); + CPrintToChatAll("%t", "Cvar changed", "sv_alltalk", (GetConVarBool(g_ConVars[sv_alltalk]) ? "0" : "1")); + NativeVotes_DisplayPassCustom(menu, "%t", "Cvar changed", "sv_alltalk", (GetConVarBool(g_ConVars[sv_alltalk]) ? "0" : "1")); + LogAction(-1, -1, "Changing alltalk to %s due to vote.", (GetConVarBool(g_ConVars[sv_alltalk]) ? "0" : "1")); + SetConVarBool(g_ConVars[sv_alltalk], !GetConVarBool(g_ConVars[sv_alltalk])); } - case (voteType:ff): + case ff: { - CPrintToChatAll("%t", "Cvar changed", "mp_friendlyfire", (GetConVarBool(g_Cvar_FF) ? "0" : "1")); - NativeVotes_DisplayPassCustom(menu, "%t", "Cvar changed", "mp_friendlyfire", (GetConVarBool(g_Cvar_FF) ? "0" : "1")); - LogAction(-1, -1, "Changing friendly fire to %s due to vote.", (GetConVarBool(g_Cvar_FF) ? "0" : "1")); - SetConVarBool(g_Cvar_FF, !GetConVarBool(g_Cvar_FF)); + CPrintToChatAll("%t", "Cvar changed", "mp_friendlyfire", (GetConVarBool(g_ConVars[mp_friendlyfire]) ? "0" : "1")); + NativeVotes_DisplayPassCustom(menu, "%t", "Cvar changed", "mp_friendlyfire", (GetConVarBool(g_ConVars[mp_friendlyfire]) ? "0" : "1")); + LogAction(-1, -1, "Changing friendly fire to %s due to vote.", (GetConVarBool(g_ConVars[mp_friendlyfire]) ? "0" : "1")); + SetConVarBool(g_ConVars[mp_friendlyfire], !GetConVarBool(g_ConVars[mp_friendlyfire])); } } } } } - return 0; + return Plugin_Continue; } -Float:GetVotePercent(votes, totalVotes) +float GetVotePercent(int votes, int totalVotes) { - return FloatDiv(float(votes),float(totalVotes)); + return float(votes) / float(totalVotes); } -bool:TestVoteDelay(client) +bool TestVoteDelay(int client) { - new delay = Internal_CheckVoteDelay(); - - if (delay > 0) - { - if (delay > 60) - { - CReplyToCommand(client, "%t", "Vote Delay Minutes", delay % 60); - } - else - { - CReplyToCommand(client, "%t", "Vote Delay Seconds", delay); - } - + int delay = Internal_CheckVoteDelay(); + if (delay > 0) + { + if (delay > 60) + { + CReplyToCommand(client, "%t", "Vote Delay Minutes", delay % 60); + } + else + { + CReplyToCommand(client, "%t", "Vote Delay Seconds", delay); + } + if (g_NativeVotes) { NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_Recent, delay); } - return false; - } - + return false; + } + return true; } -bool:Internal_IsVoteInProgress() +bool Internal_IsVoteInProgress() { if (g_NativeVotes) { return NativeVotes_IsVoteInProgress(); } - return IsVoteInProgress(); + return IsVoteInProgress(); } -Internal_CheckVoteDelay() +int Internal_CheckVoteDelay() { if (g_NativeVotes) { return NativeVotes_CheckVoteDelay(); } - return CheckVoteDelay(); + return CheckVoteDelay(); } -bool:Internal_IsNewVoteAllowed() +bool Internal_IsNewVoteAllowed() { if (g_NativeVotes) { return NativeVotes_IsNewVoteAllowed(); } - + return IsNewVoteAllowed(); } diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp b/addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp index 703dabc..39ba6c2 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp @@ -32,7 +32,7 @@ * Version: $Id$ */ -DisplayVoteAllTalkMenu(client) +void DisplayVoteAllTalkMenu(int client) { if (Internal_IsVoteInProgress()) { @@ -53,11 +53,11 @@ DisplayVoteAllTalkMenu(client) if (g_NativeVotes) { - new Handle:hVoteMenu; + Handle hVoteMenu; if (NativeVotes_IsVoteTypeSupported(NativeVotesType_AlltalkOn)) { new NativeVotesType:nVoteType; - if (GetConVarBool(g_Cvar_Alltalk)) + if (GetConVarBool(g_ConVars[sv_alltalk])) { nVoteType = NativeVotesType_AlltalkOff; } @@ -65,13 +65,13 @@ DisplayVoteAllTalkMenu(client) { nVoteType = NativeVotesType_AlltalkOn; } - hVoteMenu = NativeVotes_Create(Handler_NativeVoteCallback, nVoteType, MenuAction:MENU_ACTIONS_ALL); + hVoteMenu = NativeVotes_Create(Handler_NativeVoteCallback, nVoteType, view_as(MENU_ACTIONS_ALL)); } else { - hVoteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Custom_YesNo, MenuAction:MENU_ACTIONS_ALL); + hVoteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Custom_YesNo, view_as(MENU_ACTIONS_ALL)); - if (GetConVarBool(g_Cvar_Alltalk)) + if (GetConVarBool(g_ConVars[sv_alltalk])) { NativeVotes_SetTitle(hVoteMenu, "Votealltalk Off"); } @@ -85,9 +85,9 @@ DisplayVoteAllTalkMenu(client) } else { - new Handle:hVoteMenu = CreateMenu(Handler_VoteCallback, MenuAction:MENU_ACTIONS_ALL); + Handle hVoteMenu = CreateMenu(Handler_VoteCallback, MENU_ACTIONS_ALL); - if (GetConVarBool(g_Cvar_Alltalk)) + if (GetConVarBool(g_ConVars[sv_alltalk])) { SetMenuTitle(hVoteMenu, "Votealltalk Off"); } @@ -104,34 +104,32 @@ DisplayVoteAllTalkMenu(client) } -public AdminMenu_VoteAllTalk(Handle:topmenu, - TopMenuAction:action, - TopMenuObject:object_id, - param, - String:buffer[], - maxlength) +public void AdminMenu_VoteAllTalk(Handle topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength) { - if (action == TopMenuAction_DisplayOption) + switch (action) { - Format(buffer, maxlength, "%T", "Alltalk vote", param); - } - else if (action == TopMenuAction_SelectOption) - { - DisplayVoteAllTalkMenu(param); - } - else if (action == TopMenuAction_DrawOption) - { - /* disable this option if a vote is already running */ - buffer[0] = !Internal_IsNewVoteAllowed() ? ITEMDRAW_IGNORE : ITEMDRAW_DEFAULT; + case TopMenuAction_DisplayOption: + { + Format(buffer, maxlength, "%T", "Alltalk vote", param); + } + case TopMenuAction_SelectOption: + { + DisplayVoteAllTalkMenu(param); + } + case TopMenuAction_DrawOption: + { + /* disable this option if a vote is already running */ + buffer[0] = !Internal_IsNewVoteAllowed() ? ITEMDRAW_IGNORE : ITEMDRAW_DEFAULT; + } } } -public Action:Command_VoteAlltalk(client, args) +public Action Command_VoteAlltalk(int client, int args) { if (args > 0) { CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] Usage: sm_votealltalk"); - return Plugin_Handled; + return Plugin_Handled; } DisplayVoteAllTalkMenu(client); diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp b/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp index 3acaa4f..a496e27 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp @@ -1,4 +1,4 @@ - /** +/** * vim: set ts=4 : * ============================================================================= * NativeVotes Fun Votes Plugin @@ -32,7 +32,7 @@ * Version: $Id$ */ -DisplayVoteBurnMenu(client, target, String:name[]) +void DisplayVoteBurnMenu(int client, int target, char name[MAX_NAME_LENGTH]) { if (!IsPlayerAlive(target)) { @@ -49,7 +49,7 @@ DisplayVoteBurnMenu(client, target, String:name[]) Format(playerName, sizeof(playerName), "{#%06X}%N\x01", color, target); } else { - Format(playerName, sizeof(playerName), "%s%N\x01", GetClientTeam(target) == 2 ? "{red}" : GetClientTeam(target) == 3 ? "{blue}" : "{grey}", target); + Format(playerName, sizeof(playerName), "{teamcolor}%N\x01", target); } GetClientName(target, g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[])); @@ -61,14 +61,14 @@ DisplayVoteBurnMenu(client, target, String:name[]) if (g_NativeVotes) { - new Handle:hVoteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Custom_YesNo, MenuAction:MENU_ACTIONS_ALL); + Handle hVoteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Custom_YesNo, view_as(MENU_ACTIONS_ALL)); NativeVotes_SetTitle(hVoteMenu, "Voteburn player"); NativeVotes_SetTarget(hVoteMenu, target); NativeVotes_DisplayToAll(hVoteMenu, 20); } else { - new Handle:hVoteMenu = CreateMenu(Handler_VoteCallback, MenuAction:MENU_ACTIONS_ALL); + Handle hVoteMenu = CreateMenu(Handler_VoteCallback, view_as(MENU_ACTIONS_ALL)); SetMenuTitle(hVoteMenu, "Voteburn player"); AddMenuItem(hVoteMenu, VOTE_YES, "Yes"); AddMenuItem(hVoteMenu, VOTE_NO, "No"); @@ -77,109 +77,108 @@ DisplayVoteBurnMenu(client, target, String:name[]) } } -DisplayBurnTargetMenu(client) +void DisplayBurnTargetMenu(int client) { - new Handle:menu = CreateMenu(MenuHandler_Burn); + Handle menu = CreateMenu(MenuHandler_Burn); - decl String:title[100]; + char title[100]; Format(title, sizeof(title), "%T:", "Burn vote", client); SetMenuTitle(menu, title); SetMenuExitBackButton(menu, true); - AddTargetsToMenu(menu, client, true, true); - DisplayMenu(menu, client, MENU_TIME_FOREVER); } -public AdminMenu_VoteBurn(Handle:topmenu, - TopMenuAction:action, - TopMenuObject:object_id, - param, - String:buffer[], - maxlength) +public void AdminMenu_VoteBurn(Handle topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength) { - if (action == TopMenuAction_DisplayOption) - { - Format(buffer, maxlength, "%T", "Burn vote", param); - } - else if (action == TopMenuAction_SelectOption) + switch (action) { - DisplayBurnTargetMenu(param); - } - else if (action == TopMenuAction_DrawOption) - { - /* disable this option if a vote is already running */ - buffer[0] = !Internal_IsNewVoteAllowed() ? ITEMDRAW_IGNORE : ITEMDRAW_DEFAULT; + case TopMenuAction_DisplayOption: + { + Format(buffer, maxlength, "%T", "Burn vote", param); + } + case TopMenuAction_SelectOption: + { + DisplayBurnTargetMenu(param); + } + case TopMenuAction_DrawOption: + { + /* disable this option if a vote is already running */ + buffer[0] = !Internal_IsNewVoteAllowed() ? ITEMDRAW_IGNORE : ITEMDRAW_DEFAULT; + } } } -public MenuHandler_Burn(Handle:menu, MenuAction:action, param1, param2) +public int MenuHandler_Burn(Handle menu, MenuAction action, int param1, int param2) { - if (action == MenuAction_End) - { - CloseHandle(menu); - } - else if (action == MenuAction_Cancel) + switch (action) { - if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE) + case MenuAction_End: { - DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory); + CloseHandle(menu); } - } - else if (action == MenuAction_Select) - { - decl String:info[32], String:name[32]; - new userid, target; - - GetMenuItem(menu, param2, info, sizeof(info), _, name, sizeof(name)); - userid = StringToInt(info); - - if ((target = GetClientOfUserId(userid)) == 0) - { - PrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Player no longer available"); - } - else if (!CanUserTarget(param1, target)) - { - PrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Unable to target"); - } - else if (!IsPlayerAlive(target)) + case MenuAction_Cancel: { - PrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Player has since died"); + if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE) + { + DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory); + } } - else + case MenuAction_Select: { - DisplayVoteBurnMenu(param1, target, name); + char info[32], name[32]; + int userid, target; + GetMenuItem(menu, param2, info, sizeof(info), _, name, sizeof(name)); + userid = StringToInt(info); + if ((target = GetClientOfUserId(userid)) == 0) + { + PrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Player no longer available"); + } + else if (!CanUserTarget(param1, target)) + { + PrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Unable to target"); + } + else if (!IsPlayerAlive(target)) + { + PrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Player has since died"); + } + else + { + DisplayVoteBurnMenu(param1, target, name); + } } } + + return Plugin_Continue; } -public Action:Command_VoteBurn(client, args) +public Action Command_VoteBurn(int client, int args) { if (args < 1) { CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] Usage: sm_voteburn "); - return Plugin_Handled; + return Plugin_Handled; } if (Internal_IsVoteInProgress()) { CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Vote in Progress"); return Plugin_Handled; - } + } if (!TestVoteDelay(client)) { return Plugin_Handled; } - decl String:text[256], String:arg[64]; + char text[256], arg[64]; GetCmdArgString(text, sizeof(text)); BreakString(text, arg, sizeof(arg)); - decl String:target_name[MAX_TARGET_LENGTH]; - decl target_list[MAXPLAYERS], target_count, bool:tn_is_ml; - + char target_name[MAX_TARGET_LENGTH]; + int target_list[MAXPLAYERS], target_count; + bool tn_is_ml; if ((target_count = ProcessTargetString( arg, client, @@ -197,4 +196,4 @@ public Action:Command_VoteBurn(client, args) DisplayVoteBurnMenu(client, target_list[0], arg); return Plugin_Handled; -} +} \ No newline at end of file diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp b/addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp index a46ff4a..a081290 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp @@ -32,7 +32,7 @@ * Version: $Id$ */ -DisplayVoteFFMenu(client) +void DisplayVoteFFMenu(int client) { if (Internal_IsVoteInProgress()) { @@ -53,9 +53,9 @@ DisplayVoteFFMenu(client) if (g_NativeVotes) { - new Handle:hVoteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Custom_YesNo, MenuAction:MENU_ACTIONS_ALL); + Handle hVoteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Custom_YesNo, view_as(MENU_ACTIONS_ALL)); - if (GetConVarBool(g_Cvar_FF)) + if (GetConVarBool(g_ConVars[mp_friendlyfire])) { NativeVotes_SetTitle(hVoteMenu, "Voteff Off"); } @@ -67,9 +67,9 @@ DisplayVoteFFMenu(client) } else { - new Handle:hVoteMenu = CreateMenu(Handler_VoteCallback, MenuAction:MENU_ACTIONS_ALL); + Handle hVoteMenu = CreateMenu(Handler_VoteCallback, view_as(MENU_ACTIONS_ALL)); - if (GetConVarBool(g_Cvar_FF)) + if (GetConVarBool(g_ConVars[mp_friendlyfire])) { SetMenuTitle(hVoteMenu, "Voteff Off"); } @@ -85,34 +85,32 @@ DisplayVoteFFMenu(client) } } -public AdminMenu_VoteFF(Handle:topmenu, - TopMenuAction:action, - TopMenuObject:object_id, - param, - String:buffer[], - maxlength) +public void AdminMenu_VoteFF(Handle topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength) { - if (action == TopMenuAction_DisplayOption) + switch (action) { - Format(buffer, maxlength, "%T", "Vote FF", param); - } - else if (action == TopMenuAction_SelectOption) - { - DisplayVoteFFMenu(param); - } - else if (action == TopMenuAction_DrawOption) - { - /* disable this option if a vote is already running */ - buffer[0] = !Internal_IsNewVoteAllowed() ? ITEMDRAW_IGNORE : ITEMDRAW_DEFAULT; + case TopMenuAction_DisplayOption: + { + Format(buffer, maxlength, "%T", "Vote FF", param); + } + case TopMenuAction_SelectOption: + { + DisplayVoteFFMenu(param); + } + case TopMenuAction_DrawOption: + { + /* disable this option if a vote is already running */ + buffer[0] = !Internal_IsNewVoteAllowed() ? ITEMDRAW_IGNORE : ITEMDRAW_DEFAULT; + } } } -public Action:Command_VoteFF(client, args) +public Action Command_VoteFF(int client, int args) { if (args > 0) { CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] Usage: sm_voteff"); - return Plugin_Handled; + return Plugin_Handled; } DisplayVoteFFMenu(client); diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp b/addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp index 04ce41a..6872366 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp @@ -32,7 +32,7 @@ * Version: $Id$ */ -DisplayVoteGravityMenu(client,count,String:items[5][]) +void DisplayVoteGravityMenu(int client, int count, char items[5][64]) { LogAction(client, -1, "\"%L\" initiated a gravity vote.", client); CShowActivity2(client, "[{lightgreen}NativeVotes\x01] ", "%t", "Initiated Vote Gravity"); @@ -41,18 +41,18 @@ DisplayVoteGravityMenu(client,count,String:items[5][]) if (g_NativeVotes && (count == 1 || NativeVotes_IsVoteTypeSupported(NativeVotesType_Custom_Mult)) ) { - new Handle:hVoteMenu; + Handle hVoteMenu; if (count == 1) { strcopy(g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[]), items[0]); - hVoteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Custom_YesNo, MenuAction:MENU_ACTIONS_ALL); + hVoteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Custom_YesNo, view_as(MENU_ACTIONS_ALL)); NativeVotes_SetTitle(hVoteMenu, "Change Gravity To"); // No details for custom votes } else { - hVoteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Custom_Mult, MenuAction:MENU_ACTIONS_ALL); + hVoteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Custom_Mult, view_as(MENU_ACTIONS_ALL)); NativeVotes_SetTitle(hVoteMenu, "Gravity Vote"); for (new i = 0; i < count; i++) { @@ -63,7 +63,7 @@ DisplayVoteGravityMenu(client,count,String:items[5][]) } else { - new Handle:hVoteMenu = CreateMenu(Handler_VoteCallback, MenuAction:MENU_ACTIONS_ALL); + Handle hVoteMenu = CreateMenu(Handler_VoteCallback, view_as(MENU_ACTIONS_ALL)); if (count == 1) { @@ -89,12 +89,7 @@ DisplayVoteGravityMenu(client,count,String:items[5][]) } } -public AdminMenu_VoteGravity(Handle:topmenu, - TopMenuAction:action, - TopMenuObject:object_id, - param, - String:buffer[], - maxlength) +public void AdminMenu_VoteGravity(Handle topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength) { if (action == TopMenuAction_DisplayOption) { @@ -103,22 +98,22 @@ public AdminMenu_VoteGravity(Handle:topmenu, else if (action == TopMenuAction_SelectOption) { /* Might need a better way of selecting the list of pre-defined gravity choices */ - new String:items[5][5] ={"200","400","800","1600","3200"}; + char items[5][5] ={"200","400","800","1600","3200"}; DisplayVoteGravityMenu(param,5, items); } else if (action == TopMenuAction_DrawOption) - { + { /* disable this option if a vote is already running */ buffer[0] = !Internal_IsNewVoteAllowed() ? ITEMDRAW_IGNORE : ITEMDRAW_DEFAULT; } } -public Action:Command_VoteGravity(client, args) +public Action Command_VoteGravity(int client, int args) { if (args < 1) { CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] Usage: sm_votegravity [amount2] ... [amount5]"); - return Plugin_Handled; + return Plugin_Handled; } if (Internal_IsVoteInProgress()) @@ -132,33 +127,33 @@ public Action:Command_VoteGravity(client, args) return Plugin_Handled; } - decl String:text[256]; + char text[256]; GetCmdArgString(text, sizeof(text)); - - decl String:items[5][64]; - new count; - new len, pos; + + char items[5][64]; + int count; + int len, pos; while (pos != -1 && count < 5) - { + { pos = BreakString(text[len], items[count], sizeof(items[])); - decl Float:temp; + float temp; if (StringToFloatEx(items[count], temp) == 0) { CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Invalid Amount"); return Plugin_Handled; - } + } count++; if (pos != -1) { len += pos; - } + } } DisplayVoteGravityMenu(client, count, items); - return Plugin_Handled; + return Plugin_Handled; } \ No newline at end of file diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp b/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp index 5129dbe..eb76a9d 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp @@ -1,4 +1,4 @@ - /** +/** * vim: set ts=4 : * ============================================================================= * NativeVotes Fun Votes Plugin @@ -32,7 +32,7 @@ * Version: $Id$ */ -DisplayVoteSlayMenu(client, target, String:name[]) +void DisplayVoteSlayMenu(int client, int target, char name[MAX_NAME_LENGTH]) { if (!IsPlayerAlive(target)) { @@ -49,7 +49,7 @@ DisplayVoteSlayMenu(client, target, String:name[]) Format(playerName, sizeof(playerName), "{#%06X}%N\x01", color, target); } else { - Format(playerName, sizeof(playerName), "%s%N\x01", GetClientTeam(target) == 2 ? "{red}" : GetClientTeam(target) == 3 ? "{blue}" : "{grey}", target); + Format(playerName, sizeof(playerName), "{teamcolor}%N\x01", target); } // Use 'playerName' in printed messages, but keep g_voteInfo[VOTE_NAME] for vote panels GetClientName(target, g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[])); @@ -61,13 +61,13 @@ DisplayVoteSlayMenu(client, target, String:name[]) if (g_NativeVotes) { - new Handle:hVoteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Custom_YesNo, MenuAction:MENU_ACTIONS_ALL); + Handle hVoteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Custom_YesNo, view_as(MENU_ACTIONS_ALL)); NativeVotes_SetTitle(hVoteMenu, "Voteslay Player"); NativeVotes_DisplayToAll(hVoteMenu, 20); } else { - new Handle:hVoteMenu = CreateMenu(Handler_VoteCallback, MenuAction:MENU_ACTIONS_ALL); + Handle hVoteMenu = CreateMenu(Handler_VoteCallback, view_as(MENU_ACTIONS_ALL)); SetMenuTitle(hVoteMenu, "Voteslay Player"); AddMenuItem(hVoteMenu, VOTE_YES, "Yes"); AddMenuItem(hVoteMenu, VOTE_NO, "No"); @@ -76,11 +76,10 @@ DisplayVoteSlayMenu(client, target, String:name[]) } } -DisplaySlayTargetMenu(client) +void DisplaySlayTargetMenu(int client) { - new Handle:menu = CreateMenu(MenuHandler_Slay); - - decl String:title[100]; + Handle menu = CreateMenu(MenuHandler_Slay); + char title[100]; Format(title, sizeof(title), "%T:", "Slay vote", client); SetMenuTitle(menu, title); SetMenuExitBackButton(menu, true); @@ -90,95 +89,97 @@ DisplaySlayTargetMenu(client) DisplayMenu(menu, client, MENU_TIME_FOREVER); } -public AdminMenu_VoteSlay(Handle:topmenu, - TopMenuAction:action, - TopMenuObject:object_id, - param, - String:buffer[], - maxlength) +public void AdminMenu_VoteSlay(Handle topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength) { - if (action == TopMenuAction_DisplayOption) - { - Format(buffer, maxlength, "%T", "Slay vote", param); - } - else if (action == TopMenuAction_SelectOption) + switch (action) { - DisplaySlayTargetMenu(param); - } - else if (action == TopMenuAction_DrawOption) - { - /* disable this option if a vote is already running */ - buffer[0] = !Internal_IsNewVoteAllowed() ? ITEMDRAW_IGNORE : ITEMDRAW_DEFAULT; + case TopMenuAction_DisplayOption: + { + Format(buffer, maxlength, "%T", "Slay vote", param); + } + case TopMenuAction_SelectOption: + { + DisplaySlayTargetMenu(param); + } + case TopMenuAction_DrawOption: + { + /* disable this option if a vote is already running */ + buffer[0] = !Internal_IsNewVoteAllowed() ? ITEMDRAW_IGNORE : ITEMDRAW_DEFAULT; + } } } -public MenuHandler_Slay(Handle:menu, MenuAction:action, param1, param2) +public int MenuHandler_Slay(Handle menu, MenuAction action, int param1, int param2) { - if (action == MenuAction_End) - { - CloseHandle(menu); - } - else if (action == MenuAction_Cancel) + switch (action) { - if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE) + case MenuAction_End: { - DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory); + CloseHandle(menu); } - } - else if (action == MenuAction_Select) - { - decl String:info[32], String:name[32]; - new userid, target; - - GetMenuItem(menu, param2, info, sizeof(info), _, name, sizeof(name)); - userid = StringToInt(info); - - if ((target = GetClientOfUserId(userid)) == 0) - { - PrintToChat(param1, "[SM] %t", "Player no longer available"); - } - else if (!CanUserTarget(param1, target)) - { - PrintToChat(param1, "[SM] %t", "Unable to target"); - } - else if (!IsPlayerAlive(target)) + case MenuAction_Cancel: { - PrintToChat(param1, "[SM] %t", "Player has since died"); + if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE) + { + DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory); + } } - else + case MenuAction_Select: { - DisplayVoteSlayMenu(param1, target, name); + char info[32], name[32]; + int userid, target; + GetMenuItem(menu, param2, info, sizeof(info), _, name, sizeof(name)); + userid = StringToInt(info); + + if ((target = GetClientOfUserId(userid)) == 0) + { + PrintToChat(param1, "[SM] %t", "Player no longer available"); + } + else if (!CanUserTarget(param1, target)) + { + PrintToChat(param1, "[SM] %t", "Unable to target"); + } + else if (!IsPlayerAlive(target)) + { + PrintToChat(param1, "[SM] %t", "Player has since died"); + } + else + { + DisplayVoteSlayMenu(param1, target, name); + } } } + + return Plugin_Continue; } -public Action:Command_VoteSlay(client, args) +public Action Command_VoteSlay(int client, int args) { if (args < 1) { CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] Usage: sm_voteslay "); - return Plugin_Handled; + return Plugin_Handled; } if (Internal_IsVoteInProgress()) { CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Vote in Progress"); return Plugin_Handled; - } + } if (!TestVoteDelay(client)) { return Plugin_Handled; } - decl String:text[256], String:arg[64]; + char text[256], arg[64]; GetCmdArgString(text, sizeof(text)); BreakString(text, arg, sizeof(arg)); - decl String:target_name[MAX_TARGET_LENGTH]; - decl target_list[MAXPLAYERS], target_count, bool:tn_is_ml; - + char target_name[MAX_TARGET_LENGTH]; + int target_list[MAXPLAYERS], target_count; + bool tn_is_ml; if ((target_count = ProcessTargetString( arg, client, @@ -196,4 +197,4 @@ public Action:Command_VoteSlay(client, args) DisplayVoteSlayMenu(client, target_list[0], arg); return Plugin_Handled; -} +} \ No newline at end of file diff --git a/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp b/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp index b416e78..dbf6751 100644 --- a/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp +++ b/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp @@ -41,7 +41,7 @@ #pragma semicolon 1 #pragma newdecls required -#define VERSION "25w52a" +#define VERSION "26w02a" ConVar g_Cvar_Votes; ConVar g_Cvar_KickVote; @@ -52,7 +52,7 @@ bool g_bRegistered = false; bool g_bMapActive = false; public Plugin myinfo = { - name = "NativeVotes Kick Vote Immunity", + name = "[NativeVotes] Kick Vote Immunity", author = "Powerlord", description = "Causes TF2 kick votes to fail against people who the current user can't target.", version = VERSION, diff --git a/addons/sourcemod/scripting/nativevotes_mapchooser.sp b/addons/sourcemod/scripting/nativevotes_mapchooser.sp index fdc7bd8..3e0c84f 100644 --- a/addons/sourcemod/scripting/nativevotes_mapchooser.sp +++ b/addons/sourcemod/scripting/nativevotes_mapchooser.sp @@ -45,11 +45,11 @@ #pragma semicolon 1 #pragma newdecls required -#define VERSION "25w52a" +#define VERSION "26w02a" public Plugin myinfo = { - name = "NativeVotes MapChooser", + name = "[NativeVotes] MapChooser", author = "AlliedModders LLC and Powerlord", description = "Automated Map Voting", version = VERSION, @@ -302,7 +302,7 @@ public void OnMapEnd() g_VoteTimer = null; g_RetryTimer = null; - char map[PLATFORM_MAX_PATH]; + char map[96]; GetCurrentMap(map, sizeof(map)); g_OldMapList.PushString(map); @@ -321,7 +321,7 @@ public void OnClientDisconnect(int client) return; } - char oldmap[PLATFORM_MAX_PATH]; + char oldmap[96]; g_NominateList.GetString(index, oldmap, sizeof(oldmap)); Call_StartForward(g_NominationsResetForward); Call_PushString(oldmap); @@ -340,7 +340,7 @@ public Action Command_SetNextmap(int client, int args) return Plugin_Handled; } - char map[PLATFORM_MAX_PATH]; + char map[96]; char displayName[PLATFORM_MAX_PATH]; GetCmdArg(1, map, sizeof(map)); @@ -640,7 +640,7 @@ void InitiateVote(MapChange when, ArrayList inputlist=null) * like sm_mapvote from the adminmenu in the future. */ - char map[PLATFORM_MAX_PATH]; + char map[96]; /* No input given - User our internal nominations and maplist */ if (inputlist == null) @@ -834,7 +834,7 @@ public void Handler_NV_VoteFinishedGeneric(NativeVote menu, NativeVotes_FixResults(num_clients, client_indexes, client_votes, num_items, item_indexes, item_votes, client_info, item_info); - char map[PLATFORM_MAX_PATH]; + char map[96]; char displayName[PLATFORM_MAX_PATH]; menu.GetItem(item_indexes[0], map, sizeof(map), displayName, sizeof(displayName)); @@ -848,7 +848,7 @@ public void Handler_VoteFinishedGeneric(Menu menu, int num_items, const int[][] item_info) { - char map[PLATFORM_MAX_PATH]; + char map[96]; char displayName[PLATFORM_MAX_PATH]; menu.GetItem(item_info[0][VOTEINFO_ITEM_INDEX], map, sizeof(map), _, displayName, sizeof(displayName)); @@ -1022,7 +1022,7 @@ public void Handler_NV_MapVoteFinished(NativeVote menu, // New in 1.5.1, used to fix runoff not working properly public Action Timer_NV_Runoff(Handle timer, DataPack data) { - char map[PLATFORM_MAX_PATH]; + char map[96]; char info[PLATFORM_MAX_PATH]; g_VoteNative = new NativeVote(Handler_NV_MapVoteMenu, NativeVotesType_NextLevelMult, NATIVEVOTES_ACTIONS_DEFAULT | MenuAction_DisplayItem); @@ -1061,7 +1061,7 @@ public void Handler_MapVoteFinished(Menu menu, g_VoteMenu.SetTitle("Runoff Vote Nextmap"); g_VoteMenu.VoteResultCallback = Handler_VoteFinishedGeneric; - char map[PLATFORM_MAX_PATH]; + char map[96]; char info1[PLATFORM_MAX_PATH]; char info2[PLATFORM_MAX_PATH]; @@ -1112,7 +1112,7 @@ public int Handler_MapVoteMenu(Menu menu, MenuAction action, int param1, int par { if (menu.ItemCount - 1 == param2) { - char map[PLATFORM_MAX_PATH], buffer[255]; + char map[96], buffer[255]; menu.GetItem(param2, map, sizeof(map)); if (strcmp(map, VOTE_EXTEND, false) == 0) { @@ -1133,7 +1133,7 @@ public int Handler_MapVoteMenu(Menu menu, MenuAction action, int param1, int par if (param1 == VoteCancel_NoVotes && g_Cvar_NoVoteMode.BoolValue) { int count = menu.ItemCount; - char map[PLATFORM_MAX_PATH]; + char map[96]; menu.GetItem(0, map, sizeof(map)); // Make sure the first map in the menu isn't one of the special items. @@ -1181,7 +1181,7 @@ public int Handler_NV_MapVoteMenu(NativeVote menu, MenuAction action, int param1 { if (menu.ItemCount - 1 == param2) { - char map[PLATFORM_MAX_PATH], buffer[255]; + char map[96], buffer[255]; menu.GetItem(param2, map, sizeof(map)); if (strcmp(map, VOTE_EXTEND, false) == 0) { @@ -1202,7 +1202,7 @@ public int Handler_NV_MapVoteMenu(NativeVote menu, MenuAction action, int param1 if (param1 == VoteCancel_NoVotes && g_Cvar_NoVoteMode.BoolValue) { int count = menu.ItemCount; - char map[PLATFORM_MAX_PATH]; + char map[96]; char displayName[PLATFORM_MAX_PATH]; menu.GetItem(0, map, sizeof(map)); @@ -1248,7 +1248,7 @@ public Action Timer_ChangeMap(Handle hTimer, DataPack dp) { g_ChangeMapInProgress = false; - char map[PLATFORM_MAX_PATH]; + char map[96]; if (dp == null) { @@ -1285,7 +1285,7 @@ void CreateNextVote() { g_NextMapList.Clear(); - char map[PLATFORM_MAX_PATH]; + char map[96]; // tempMaps is a resolved map list ArrayList tempMaps = new ArrayList(ByteCountToCells(PLATFORM_MAX_PATH)); @@ -1351,7 +1351,7 @@ NominateResult InternalNominateMap(char[] map, bool force, int owner) /* Look to replace an existing nomination by this client - Nominations made with owner = 0 aren't replaced */ if (owner && ((index = g_NominateOwners.FindValue(owner)) != -1)) { - char oldmap[PLATFORM_MAX_PATH]; + char oldmap[96]; g_NominateList.GetString(index, oldmap, sizeof(oldmap)); Call_StartForward(g_NominationsResetForward); Call_PushString(oldmap); @@ -1393,7 +1393,7 @@ NominateResult InternalNominateMap(char[] map, bool force, int owner) while (g_NominateList.Length > g_Cvar_IncludeMaps.IntValue) { - char oldmap[PLATFORM_MAX_PATH]; + char oldmap[96]; g_NominateList.GetString(0, oldmap, sizeof(oldmap)); Call_StartForward(g_NominationsResetForward); Call_PushString(oldmap); @@ -1430,7 +1430,7 @@ bool InternalRemoveNominationByMap(char[] map) { for (int i = 0; i < g_NominateList.Length; i++) { - char oldmap[PLATFORM_MAX_PATH]; + char oldmap[96]; g_NominateList.GetString(i, oldmap, sizeof(oldmap)); if(strcmp(map, oldmap, false) == 0) @@ -1473,7 +1473,7 @@ bool InternalRemoveNominationByOwner(int owner) if (owner && ((index = g_NominateOwners.FindValue(owner)) != -1)) { - char oldmap[PLATFORM_MAX_PATH]; + char oldmap[96]; g_NominateList.GetString(index, oldmap, sizeof(oldmap)); Call_StartForward(g_NominationsResetForward); @@ -1534,7 +1534,7 @@ public int Native_GetExcludeMapList(Handle plugin, int numParams) return; } int size = g_OldMapList.Length; - char map[PLATFORM_MAX_PATH]; + char map[96]; for (int i=0; i= g_VotesNeeded && g_RTVAllowed ) { - if (g_Cvar_RTVPostVoteAction.IntValue == 1 && HasEndOfMapVoteFinished()) + if (g_ConVars[postvoteaction].IntValue == 1 && HasEndOfMapVoteFinished()) { return; } @@ -268,7 +272,7 @@ public Action Command_ForceRTV(int client, int args) } -void RTVUndoMenuHandler(Menu menu, MenuAction action, int client, int param2) +void MenuHandler_UndoRTV(Menu menu, MenuAction action, int client, int param2) { if (action == MenuAction_Select) { @@ -283,20 +287,18 @@ void RTVUndoMenuHandler(Menu menu, MenuAction action, int client, int param2) Format(name, sizeof(name), "{#%06X}%N\x01", color, client); } else { - Format(name, sizeof(name), "%s%N\x01", GetClientTeam(client) == 2 ? "{red}" : GetClientTeam(client) == 3 ? "{blue}" : "{grey}", client); + Format(name, sizeof(name), "{teamcolor}%N\x01", client); } - g_Voted[client] = false; if (g_Votes > 0) g_Votes--; - //CReplyToCommand(client, "[{lightgreen}Rock The Vote\x01] %t", "Cancelled Vote"); - CPrintToChatAll("[{lightgreen}Rock The Vote\x01] %s: %t", name, "Cancelled Vote"); + CPrintToChatAllEx(client, "[{lightgreen}Rock The Vote\x01] %s: %t", name, "Cancelled Vote"); } } } delete menu; } -void ShowRTVUndoMenu(int client) +void UndoRTV(int client) { char yes[64], no[64]; Format(yes, sizeof(yes), "%T", "Yes", client); @@ -305,7 +307,7 @@ void ShowRTVUndoMenu(int client) char title[128]; Format(title, sizeof(title), "%T", "Cancel vote", client); - Menu menu = new Menu(RTVUndoMenuHandler); + Menu menu = new Menu(MenuHandler_UndoRTV); menu.SetTitle(title); menu.AddItem("yes", yes); menu.AddItem("no", no); @@ -315,7 +317,7 @@ void ShowRTVUndoMenu(int client) void AttemptRTV(int client, bool isVoteMenu=false) { - if (!g_RTVAllowed || (g_Cvar_RTVPostVoteAction.IntValue == 1 && HasEndOfMapVoteFinished())) + if (!g_RTVAllowed || (g_ConVars[postvoteaction].IntValue == 1 && HasEndOfMapVoteFinished())) { CReplyToCommand(client, "[{lightgreen}Rock The Vote\x01] %t", "RTV Not Allowed"); if (isVoteMenu && g_NativeVotes) @@ -340,13 +342,13 @@ void AttemptRTV(int client, bool isVoteMenu=false) return; } - if (!CanMapChooserStartVote()) + if (!CanMapChooserStartVote() && NativeVotes_IsVoteInProgress()) { - CReplyToCommand(client, "[{lightgreen}Rock The Vote\x01] %t", "RTV Started"); + CReplyToCommandEx(client, client, "[{lightgreen}Rock The Vote\x01] %t", "RTV Started"); return; } - if (GetClientCount(true) < g_Cvar_MinPlayers.IntValue) + if (GetClientCount(true) < g_ConVars[minplayers].IntValue) { CReplyToCommand(client, "[{lightgreen}Rock The Vote\x01] %t", "Minimal Players Not Met"); if (isVoteMenu && g_NativeVotes) @@ -358,7 +360,7 @@ void AttemptRTV(int client, bool isVoteMenu=false) if (g_Voted[client]) { - ShowRTVUndoMenu(client); + UndoRTV(client); return; } @@ -369,13 +371,13 @@ void AttemptRTV(int client, bool isVoteMenu=false) Format(name, sizeof(name), "{#%06X}%N\x01", color, client); } else { - Format(name, sizeof(name), "%s%N\x01", GetClientTeam(client) == 2 ? "{red}" : GetClientTeam(client) == 3 ? "{blue}" : "{grey}", client); + Format(name, sizeof(name), "{teamcolor}%N\x01", client); } g_Votes++; g_Voted[client] = true; - CPrintToChatAll("[{lightgreen}Rock The Vote\x01] %t", "RTV Requested", name, g_Votes, g_VotesNeeded); + CPrintToChatAllEx(client, "[{lightgreen}Rock The Vote\x01] %t", "RTV Requested", name, g_Votes, g_VotesNeeded); if (g_Votes >= g_VotesNeeded) { @@ -400,7 +402,7 @@ void StartRTV() if (EndOfMapVoteEnabled() && HasEndOfMapVoteFinished()) { /* Change right now then */ - char map[PLATFORM_MAX_PATH]; + char map[96]; if (GetNextMap(map, sizeof(map))) { GetMapDisplayName(map, map, sizeof(map)); @@ -419,15 +421,15 @@ void StartRTV() if (CanMapChooserStartVote()) { - MapChange when = view_as(g_Cvar_ChangeTime.IntValue); + MapChange when = view_as(g_ConVars[changetime].IntValue); InitiateMapChooserVote(when); ResetRTV(); g_RTVAllowed = false; - g_RTVTime = GetTime() + g_Cvar_Interval.IntValue; + g_RTVTime = GetTime() + g_ConVars[interval].IntValue; - CreateTimer(g_Cvar_Interval.FloatValue, Timer_DelayRTV, _, TIMER_FLAG_NO_MAPCHANGE); + CreateTimer(g_ConVars[interval].FloatValue, Timer_DelayRTV, _, TIMER_FLAG_NO_MAPCHANGE); } } @@ -447,7 +449,7 @@ public Action Timer_ChangeMap(Handle hTimer) LogMessage("RTV changing map manually"); - char map[PLATFORM_MAX_PATH]; + char map[96]; if (GetNextMap(map, sizeof(map))) { ForceChangeLevel(map, "RTV after mapvote"); diff --git a/addons/sourcemod/scripting/nativevotes_voterp.sp b/addons/sourcemod/scripting/nativevotes_voterp.sp index 284e46d..b79fe16 100644 --- a/addons/sourcemod/scripting/nativevotes_voterp.sp +++ b/addons/sourcemod/scripting/nativevotes_voterp.sp @@ -14,41 +14,43 @@ #pragma semicolon 1 #pragma newdecls required -#define VERSION "25w52a" +#define VERSION "26w02a" public Plugin myinfo = { - name = "NativeVotes Medieval Auto-RP", + name = "[NativeVotes] Medieval Auto-RP", author = "Heapons", description = "Provides Medieval Auto-RP voting.", version = VERSION, url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; -ConVar g_Cvar_Medieval; -ConVar g_Cvar_MedievalAutoRP; -ConVar g_Cvar_VoteDuration; +enum +{ + tf_medieval, + tf_medieval_autorp, + vote_duration, + + MAX_CONVARS +} + +ConVar g_ConVars[MAX_CONVARS]; public void OnPluginStart() { - if (GetEngineVersion() != Engine_TF2) + g_ConVars[tf_medieval] = FindConVar("tf_medieval"); + g_ConVars[tf_medieval_autorp] = FindConVar("tf_medieval_autorp"); + if (GetEngineVersion() != Engine_TF2 || !g_ConVars[tf_medieval] || !g_ConVars[tf_medieval_autorp]) { SetFailState("This plugin is for Team Fortress 2 only."); } - g_Cvar_Medieval = FindConVar("tf_medieval"); - g_Cvar_MedievalAutoRP = FindConVar("tf_medieval_autorp"); - if (g_Cvar_MedievalAutoRP == null) - { - SetFailState("Could not find tf_medieval_autorp cvar."); - } - - g_Cvar_VoteDuration = CreateConVar("sm_voterp_voteduration", "20", "Specifies how long the rp vote should be available for.", _, true, 5.0); + g_ConVars[vote_duration] = CreateConVar("sm_voterp_voteduration", "20", "Specifies how long the rp vote should be available for.", _, true, 5.0); RegConsoleCmd("sm_voterp", Command_VoteRP, "Vote to enable/disable tf_medieval_autorp"); } -public Action Command_VoteRP(int client, int args) +Action Command_VoteRP(int client, int args) { if (!client || !IsClientInGame(client)) { @@ -61,27 +63,29 @@ public Action Command_VoteRP(int client, int args) return Plugin_Handled; } - bool isMedieval = g_Cvar_Medieval.BoolValue || GameRules_GetProp("m_bPlayingMedieval") || FindEntityByClassname(-1, "tf_logic_medieval") != -1; + bool isMedieval = g_ConVars[tf_medieval].BoolValue || GameRules_GetProp("m_bPlayingMedieval") || FindEntityByClassname(-1, "tf_logic_medieval") != -1; if (!isMedieval) { PrintToChat(client, "[{lightgreen}NativeVotes\x01] This vote is only available in Medieval Mode."); return Plugin_Handled; } - bool enabled = g_Cvar_MedievalAutoRP.BoolValue; + bool enabled = g_ConVars[tf_medieval_autorp].BoolValue; char title[64]; Format(title, sizeof(title), "Turn Medieval Auto-RP %s?", enabled ? "off" : "on"); NativeVote vote = NativeVotes_Create(VoteHandler, NativeVotesType_Custom_YesNo, MENU_ACTIONS_ALL); + NativeVotes_SetTitle(vote, title); NativeVotes_SetInitiator(vote, client); NativeVotes_AddItem(vote, enabled ? "0" : "1", enabled ? "Disable" : "Enable"); NativeVotes_AddItem(vote, enabled ? "1" : "0", enabled ? "Enable" : "Disable"); - NativeVotes_DisplayToAll(vote, g_Cvar_VoteDuration.IntValue); + NativeVotes_DisplayToAll(vote, g_ConVars[vote_duration].IntValue); + return Plugin_Handled; } -public int VoteHandler(NativeVote vote, MenuAction action, int param1, int param2) +int VoteHandler(NativeVote vote, MenuAction action, int param1, int param2) { switch (action) { @@ -91,7 +95,7 @@ public int VoteHandler(NativeVote vote, MenuAction action, int param1, int param char info[8]; NativeVotes_GetItem(vote, item, info, sizeof(info)); int value = StringToInt(info); - g_Cvar_MedievalAutoRP.SetInt(value); + g_ConVars[tf_medieval_autorp].SetInt(value); NativeVotes_DisplayPass(vote, "tf_medieval_autorp set to %d", value); } case MenuAction_End: @@ -99,5 +103,5 @@ public int VoteHandler(NativeVote vote, MenuAction action, int param1, int param NativeVotes_Close(vote); } } - return 0; + return Plugin_Continue; } From bc3db5ac31335bc14a099f1edeabf7c87e307b17 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Fri, 9 Jan 2026 19:32:28 +0100 Subject: [PATCH 015/128] Fix variable names --- addons/sourcemod/scripting/nativevotes_nominations.sp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_nominations.sp b/addons/sourcemod/scripting/nativevotes_nominations.sp index 595a8fa..aab8eb2 100644 --- a/addons/sourcemod/scripting/nativevotes_nominations.sp +++ b/addons/sourcemod/scripting/nativevotes_nominations.sp @@ -171,7 +171,7 @@ public void OnNominationRemoved(const char[] map, int owner) { int status; - char resolvedmap[96]; + char resolvedMap[96]; FindMap(map, resolvedMap, sizeof(resolvedMap)); /* Is the map in our list? */ @@ -198,7 +198,7 @@ public Action Command_Addmap(int client, int args) } char mapname[96]; - char resolvedmap[96]; + char resolvedMap[96]; GetCmdArg(1, mapname, sizeof(mapname)); if (FindMap(mapname, resolvedMap, sizeof(resolvedMap)) == FindMap_NotFound) @@ -487,7 +487,7 @@ void BuildMapMenu() char map[96]; ArrayList excludeMaps; - char currentmap[96]; + char currentMap[96]; if (g_Cvar_ExcludeOld.BoolValue) { From bfb48cd018429bb657a1e611e20242b0441c46e4 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Fri, 9 Jan 2026 22:01:05 +0100 Subject: [PATCH 016/128] Tidy up --- .../scripting/nativevotes_basecommands.sp | 36 +++++++++---------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_basecommands.sp b/addons/sourcemod/scripting/nativevotes_basecommands.sp index 06ccd41..9bd6ae6 100644 --- a/addons/sourcemod/scripting/nativevotes_basecommands.sp +++ b/addons/sourcemod/scripting/nativevotes_basecommands.sp @@ -99,31 +99,29 @@ public Action Command_CancelVote(int client, const char[] command, int argc) } } -public void AdminMenu_CancelVote(Handle topmenu, - TopMenuAction action, - TopMenuObject object_id, - int param, - char[] buffer, - int maxlength) +public void AdminMenu_CancelVote(Handle topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength) { - if (action == TopMenuAction_DisplayOption) + switch (action) { - Format(buffer, maxlength, "%T", "Cancel vote", param); - } - else if (action == TopMenuAction_SelectOption) - { - PerformCancelVote(param); - RedisplayAdminMenu(topmenu, param); - } - else if (action == TopMenuAction_DrawOption) - { - buffer[0] = NativeVotes_IsVoteInProgress() ? ITEMDRAW_DEFAULT : ITEMDRAW_IGNORE; + case TopMenuAction_DisplayOption: + { + Format(buffer, maxlength, "%T", "Cancel vote", param); + } + case TopMenuAction_SelectOption: + { + PerformCancelVote(param); + RedisplayAdminMenu(topmenu, param); + } + case TopMenuAction_DrawOption: + { + buffer[0] = NativeVotes_IsVoteInProgress() ? ITEMDRAW_DEFAULT : ITEMDRAW_IGNORE; + } } } public Action Command_ReVote(int client, const char[] command, int argc) { - if (client == 0) + if (client <= 0) { return Plugin_Continue; } @@ -185,4 +183,4 @@ public void OnLibraryRemoved(const char[] name) { hTopMenu = null; } -} +} \ No newline at end of file From 0243ae1e68bfa9567dfa19043392f1a351d8375e Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Fri, 9 Jan 2026 22:02:19 +0100 Subject: [PATCH 017/128] Use multicolors functions --- .../nativevotes_basevotes/voteban.sp | 4 +- .../nativevotes_basevotes/votekick.sp | 52 ++++++++++--------- .../nativevotes_basevotes/votemap.sp | 35 +++++++------ .../nativevotes_funvotes/voteburn.sp | 6 +-- .../nativevotes_funvotes/voteslay.sp | 6 +-- .../sourcemod/scripting/nativevotes_voterp.sp | 4 +- 6 files changed, 57 insertions(+), 50 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_basevotes/voteban.sp b/addons/sourcemod/scripting/nativevotes_basevotes/voteban.sp index 1f7d4ec..a717ca4 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes/voteban.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes/voteban.sp @@ -124,11 +124,11 @@ public int MenuHandler_Ban(Handle menu, MenuAction action, int param1, int param userid = StringToInt(info); if ((target = GetClientOfUserId(userid)) == 0) { - PrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Player no longer available"); + CPrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Player no longer available"); } else if (!CanUserTarget(param1, target)) { - PrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Unable to target"); + CPrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Unable to target"); } else { diff --git a/addons/sourcemod/scripting/nativevotes_basevotes/votekick.sp b/addons/sourcemod/scripting/nativevotes_basevotes/votekick.sp index a7038af..f764ffd 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes/votekick.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes/votekick.sp @@ -102,38 +102,42 @@ public void AdminMenu_VoteKick(Handle topmenu, TopMenuAction action, TopMenuObje public int MenuHandler_Kick(Handle menu, MenuAction action, int param1, int param2) { - if (action == MenuAction_End) + switch (action) { - CloseHandle(menu); - } - else if (action == MenuAction_Cancel) - { - if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE) + case MenuAction_End: { - DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory); + CloseHandle(menu); } - } - else if (action == MenuAction_Select) - { - char info[32], name[32]; - int userid, target; - GetMenuItem(menu, param2, info, sizeof(info), _, name, sizeof(name)); - userid = StringToInt(info); - if ((target = GetClientOfUserId(userid)) == 0) + case MenuAction_Cancel: { - PrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Player no longer available"); + if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE) + { + DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory); + } } - else if (!CanUserTarget(param1, target)) + case MenuAction_Select: { - PrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Unable to target"); - } - else - { - g_voteArg[0] = '\0'; - DisplayVoteKickMenu(param1, target); + char info[32], name[32]; + int userid, target; + GetMenuItem(menu, param2, info, sizeof(info), _, name, sizeof(name)); + userid = StringToInt(info); + if ((target = GetClientOfUserId(userid)) == 0) + { + CPrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Player no longer available"); + } + else if (!CanUserTarget(param1, target)) + { + CPrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Unable to target"); + } + else + { + g_voteArg[0] = '\0'; + DisplayVoteKickMenu(param1, target); + } } } - return 0; + + return Plugin_Continue; } public Action Command_Votekick(int client, int args) diff --git a/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp b/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp index 75e67c0..5f8e175 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp @@ -218,27 +218,30 @@ public int MenuHandler_Map(Handle menu, MenuAction action, int param1, int param public void AdminMenu_VoteMap(Handle topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength) { - if (action == TopMenuAction_DisplayOption) - { - Format(buffer, maxlength, "%T", "Map vote", param); - } - else if (action == TopMenuAction_SelectOption) + switch (action) { - if (!g_VoteMapInUse) + case TopMenuAction_DisplayOption: { - ResetMenu(); - g_VoteMapInUse = true; - DisplayMenu(g_MapList, param, MENU_TIME_FOREVER); + Format(buffer, maxlength, "%T", "Map vote", param); } - else + case TopMenuAction_SelectOption: { - PrintToChat(param, "[{lightgreen}NativeVotes\x01] %T", "Map Vote In Use", param); + if (!g_VoteMapInUse) + { + ResetMenu(); + g_VoteMapInUse = true; + DisplayMenu(g_MapList, param, MENU_TIME_FOREVER); + } + else + { + CPrintToChat(param, "[{lightgreen}NativeVotes\x01] %T", "Map Vote In Use", param); + } + } + case TopMenuAction_DrawOption: + { + /* disable this option if a vote is already running, theres no maps listed or someone else has already acessed this menu */ + buffer[0] = (!IsNewVoteAllowed() || g_mapCount < 1 || g_VoteMapInUse) ? ITEMDRAW_IGNORE : ITEMDRAW_DEFAULT; } - } - else if (action == TopMenuAction_DrawOption) - { - /* disable this option if a vote is already running, theres no maps listed or someone else has already acessed this menu */ - buffer[0] = (!IsNewVoteAllowed() || g_mapCount < 1 || g_VoteMapInUse) ? ITEMDRAW_IGNORE : ITEMDRAW_DEFAULT; } } diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp b/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp index a496e27..546d463 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp @@ -132,15 +132,15 @@ public int MenuHandler_Burn(Handle menu, MenuAction action, int param1, int para userid = StringToInt(info); if ((target = GetClientOfUserId(userid)) == 0) { - PrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Player no longer available"); + CPrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Player no longer available"); } else if (!CanUserTarget(param1, target)) { - PrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Unable to target"); + CPrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Unable to target"); } else if (!IsPlayerAlive(target)) { - PrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Player has since died"); + CPrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Player has since died"); } else { diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp b/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp index eb76a9d..a844acb 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp @@ -133,15 +133,15 @@ public int MenuHandler_Slay(Handle menu, MenuAction action, int param1, int para if ((target = GetClientOfUserId(userid)) == 0) { - PrintToChat(param1, "[SM] %t", "Player no longer available"); + CPrintToChat(param1, "[SM] %t", "Player no longer available"); } else if (!CanUserTarget(param1, target)) { - PrintToChat(param1, "[SM] %t", "Unable to target"); + CPrintToChat(param1, "[SM] %t", "Unable to target"); } else if (!IsPlayerAlive(target)) { - PrintToChat(param1, "[SM] %t", "Player has since died"); + CPrintToChat(param1, "[SM] %t", "Player has since died"); } else { diff --git a/addons/sourcemod/scripting/nativevotes_voterp.sp b/addons/sourcemod/scripting/nativevotes_voterp.sp index b79fe16..99f4a6f 100644 --- a/addons/sourcemod/scripting/nativevotes_voterp.sp +++ b/addons/sourcemod/scripting/nativevotes_voterp.sp @@ -59,14 +59,14 @@ Action Command_VoteRP(int client, int args) if (NativeVotes_IsVoteInProgress()) { - PrintToChat(client, "[{lightgreen}NativeVotes\x01] A vote is already in progress."); + CPrintToChat(client, "[{lightgreen}NativeVotes\x01] A vote is already in progress."); return Plugin_Handled; } bool isMedieval = g_ConVars[tf_medieval].BoolValue || GameRules_GetProp("m_bPlayingMedieval") || FindEntityByClassname(-1, "tf_logic_medieval") != -1; if (!isMedieval) { - PrintToChat(client, "[{lightgreen}NativeVotes\x01] This vote is only available in Medieval Mode."); + CPrintToChat(client, "[{lightgreen}NativeVotes\x01] This vote is only available in Medieval Mode."); return Plugin_Handled; } From bae888d0175032a51d24b7435a52de4717e696c8 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sat, 10 Jan 2026 00:56:03 +0100 Subject: [PATCH 018/128] Add support for sv_vote_bots_allowed --- addons/sourcemod/scripting/nativevotes.sp | 76 +++-- .../sourcemod/scripting/nativevotes/game.sp | 306 +++--------------- 2 files changed, 95 insertions(+), 287 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes.sp b/addons/sourcemod/scripting/nativevotes.sp index b605ff6..0b3371c 100644 --- a/addons/sourcemod/scripting/nativevotes.sp +++ b/addons/sourcemod/scripting/nativevotes.sp @@ -80,11 +80,19 @@ int g_NextVote = 0; //---------------------------------------------------------------------------- // CVars -ConVar g_Cvar_VoteHintbox; -ConVar g_Cvar_VoteChat; -ConVar g_Cvar_VoteConsole; -ConVar g_Cvar_VoteClientConsole; -ConVar g_Cvar_VoteDelay; +enum +{ + progress_hintbox, + progress_chat, + progress_console, + progress_client_console, + vote_delay, + bots_allowed, + + MAX_CONVARS +} + +ConVar g_ConVars[MAX_CONVARS]; //---------------------------------------------------------------------------- // Used to track current vote data @@ -157,7 +165,6 @@ public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max if (!Game_IsGameSupported(engineName, sizeof(engineName))) { Format(error, err_max, "Unsupported game: %s", engineName); - //strcopy(error, err_max, "Unsupported game"); return APLRes_Failure; } @@ -252,16 +259,16 @@ public void OnPluginStart() CreateConVar("nativevotes_version", VERSION, "NativeVotes API version", FCVAR_DONTRECORD | FCVAR_NOTIFY); - g_Cvar_VoteHintbox = CreateConVar("nativevotes_progress_hintbox", "0", "Show current vote progress in a hint box", FCVAR_NONE, true, 0.0, true, 1.0); - g_Cvar_VoteChat = CreateConVar("nativevotes_progress_chat", "0", "Show current vote progress as chat messages", FCVAR_NONE, true, 0.0, true, 1.0); - g_Cvar_VoteConsole = CreateConVar("nativevotes_progress_console", "0", "Show current vote progress as console messages", FCVAR_NONE, true, 0.0, true, 1.0); - g_Cvar_VoteClientConsole = CreateConVar("nativevotes_progress_client_console", "0", "Show current vote progress as console messages to clients", FCVAR_NONE, true, 0.0, true, 1.0); - g_Cvar_VoteDelay = CreateConVar("nativevotes_vote_delay", "30", "Sets the recommended time in between public votes", FCVAR_NONE, true, 0.0); - + g_ConVars[progress_hintbox] = CreateConVar("nativevotes_progress_hintbox", "0", "Show current vote progress in a hint box", FCVAR_NONE, true, 0.0, true, 1.0); + g_ConVars[progress_chat] = CreateConVar("nativevotes_progress_chat", "0", "Show current vote progress as chat messages", FCVAR_NONE, true, 0.0, true, 1.0); + g_ConVars[progress_console] = CreateConVar("nativevotes_progress_console", "0", "Show current vote progress as console messages", FCVAR_NONE, true, 0.0, true, 1.0); + g_ConVars[progress_client_console] = CreateConVar("nativevotes_progress_client_console", "0", "Show current vote progress as console messages to clients", FCVAR_NONE, true, 0.0, true, 1.0); + g_ConVars[vote_delay] = CreateConVar("nativevotes_vote_delay", "30", "Sets the recommended time in between public votes", FCVAR_NONE, true, 0.0); + g_ConVars[bots_allowed] = FindConVar("sv_vote_bots_allowed"); + Game_InitializeCvars(); - HookConVarChange(g_Cvar_VoteDelay, OnVoteDelayChange); - + HookConVarChange(g_ConVars[vote_delay], OnVoteDelayChange); AddCommandListener(Command_Vote, "vote"); // All games, command listeners aren't case sensitive sv_vote_holder_may_vote_no = FindConVar("sv_vote_holder_may_vote_no"); @@ -718,7 +725,7 @@ void OnVoteSelect(NativeVote vote, int client, int item) Game_UpdateVoteCounts(g_hVotes, g_TotalClients); - if (g_Cvar_VoteChat.BoolValue || g_Cvar_VoteConsole.BoolValue || g_Cvar_VoteClientConsole.BoolValue) + if (g_ConVars[progress_chat].BoolValue || g_ConVars[progress_console].BoolValue || g_ConVars[progress_clientconsole].BoolValue) { char choice[128]; char name[MAX_NAME_LENGTH+1]; @@ -726,12 +733,12 @@ void OnVoteSelect(NativeVote vote, int client, int item) GetClientName(client, name, MAX_NAME_LENGTH); - if (g_Cvar_VoteConsole.BoolValue) + if (g_ConVars[progress_console].BoolValue) { PrintToServer("[%s] %T", LOGTAG, "Voted For", LANG_SERVER, name, choice); } - if (g_Cvar_VoteChat.BoolValue || g_Cvar_VoteClientConsole.BoolValue) + if (g_ConVars[progress_chat].BoolValue || g_ConVars[progress_clientconsole].BoolValue) { char phrase[30]; @@ -744,12 +751,12 @@ void OnVoteSelect(NativeVote vote, int client, int item) strcopy(phrase, sizeof(phrase), "Voted For"); } - if (g_Cvar_VoteChat.BoolValue) + if (g_ConVars[progress_chat].BoolValue) { CPrintToChatAll("[%s] %t", LOGTAG, phrase, name, choice); } - if (g_Cvar_VoteClientConsole.BoolValue) + if (g_ConVars[progress_clientconsole].BoolValue) { for (int i = 1; i <= MaxClients; i++) { @@ -1000,7 +1007,7 @@ bool:SendResultCallback(Handle:vote, num_votes, num_items, const votes[][]) void DrawHintProgress() { - if (!g_Cvar_VoteHintbox.BoolValue) + if (!g_ConVars[progress_hintbox].BoolValue) { return; } @@ -1019,7 +1026,7 @@ void DrawHintProgress() void BuildVoteLeaders() { - if (g_NumVotes == 0 || !g_Cvar_VoteHintbox.BoolValue) + if (g_NumVotes == 0 || !g_ConVars[progress_hintbox].BoolValue) { return; } @@ -1075,7 +1082,7 @@ void DecrementPlayerCount() void EndVoting() { - int voteDelay = g_Cvar_VoteDelay.IntValue; + int voteDelay = g_ConVars[vote_delay].IntValue; if (voteDelay < 1) { g_NextVote = 0; @@ -1145,7 +1152,7 @@ bool StartVote(NativeVote vote, int num_clients, int[] clients, int max_time, in /* Due to hibernating servers, we no longer use GameTime, but instead standard timestamps. */ - int voteDelay = g_Cvar_VoteDelay.IntValue; + int voteDelay = g_ConVars[vote_delay].IntValue; if (voteDelay < 1) { g_NextVote = 0; @@ -1296,6 +1303,23 @@ void StartVoting() Game_VoteYes(initiator); } } + + // sv_vote_bots_allowed + if (g_ConVars[allow_bots].BoolValue) + { + for (int i = 1; i <= MaxClients; i++) + { + if (!IsFakeClient(i)) + { + continue; + } + + if (Internal_IsClientInVotePool(i)) + { + Game_VoteRandom(i); + } + } + } } public Action DisplayTimer(Handle timer) @@ -2017,9 +2041,9 @@ public int Native_RedrawClientVote(Handle plugin, int numParams) if (!Internal_IsVoteInProgress()) { - // When revoting in TF2, NativeVotes_IsVoteInProgress always gets skipped because of Game_IsVoteInProgress() - // TF2s vote controller will stay alive a few seconds after the vote is complete - // If one tries to revote right as a vote completes, it will throw an error + // When revoting in TF2, NativeVotes_IsVoteInProgress always gets skipped because of Game_IsVoteInProgress() + // TF2's vote controller will stay alive a few seconds after the vote is complete + // If one tries to revote right as a vote completes, it will throw an error LogError("No vote is in progress"); return false; } diff --git a/addons/sourcemod/scripting/nativevotes/game.sp b/addons/sourcemod/scripting/nativevotes/game.sp index bb2a66d..54aaa72 100644 --- a/addons/sourcemod/scripting/nativevotes/game.sp +++ b/addons/sourcemod/scripting/nativevotes/game.sp @@ -68,15 +68,15 @@ #define L4D_VOTE_CHANGELEVEL_PASSED "#L4D_vote_passed_chapter_change" // User vote to return to lobby. -#define L4D_VOTE_RETURNTOLOBBY_START "#L4D_vote_return_to_lobby" +#define L4D_VOTE_RETURNTOLOBBY_START "#L4D_vote_return_to_lobby" #define L4D_VOTE_RETURNTOLOBBY_PASSED "#L4D_vote_passed_return_to_lobby" // User vote to change difficulty. #define L4D_VOTE_CHANGEDIFFICULTY_START "#L4D_vote_change_difficulty" -#define L4D_VOTE_CHANGEDIFFICULTY_PASSED "#L4D_vote_passed_change_difficulty" +#define L4D_VOTE_CHANGEDIFFICULTY_PASSED "#L4D_vote_passed_change_difficulty" // While not a vote string, it works just as well. -#define L4D_VOTE_CUSTOM "#L4D_TargetID_Player" +#define L4D_VOTE_CUSTOM "#L4D_TargetID_Player" //---------------------------------------------------------------------------- // L4D2 @@ -95,7 +95,7 @@ #define TF2CSGO_VOTE_STRING_RESTART "RestartGame" #define TF2CSGO_VOTE_STRING_CHANGELEVEL "ChangeLevel" #define TF2CSGO_VOTE_STRING_NEXTLEVEL "NextLevel" -#define TF2CSGO_VOTE_STRING_SCRAMBLE "ScrambleTeams" +#define TF2CSGO_VOTE_STRING_SCRAMBLE "ScrambleTeams" //---------------------------------------------------------------------------- // TF2 @@ -121,16 +121,16 @@ #define TF2_VOTE_MENU_SCRAMBLE "#Vote_ScrambleTeams" #define TF2_VOTE_MENU_CHANGEMISSION "#Vote_ChangeMission" #define TF2_VOTE_MENU_ETERNAWEEN "#Vote_Eternaween" -#define TF2_VOTE_MENU_AUTOBALANCE_ON "#Vote_TeamAutoBalance_Enable" +#define TF2_VOTE_MENU_AUTOBALANCE_ON "#Vote_TeamAutoBalance_Enable" #define TF2_VOTE_MENU_AUTOBALANCE_OFF "#Vote_TeamAutoBalance_Disable" #define TF2_VOTE_MENU_CLASSLIMIT_ON "#Vote_ClassLimit_Enable" -#define TF2_VOTE_MENU_CLASSLIMIT_OFF "#Vote_ClassLimit_Disable" +#define TF2_VOTE_MENU_CLASSLIMIT_OFF "#Vote_ClassLimit_Disable" #define TF2_VOTE_MENU_EXTEND "#Vote_ExtendLevel" // User vote to kick user. #define TF2_VOTE_KICK_IDLE_START "#TF_vote_kick_player_idle" -#define TF2_VOTE_KICK_SCAMMING_START "#TF_vote_kick_player_scamming" -#define TF2_VOTE_KICK_CHEATING_START "#TF_vote_kick_player_cheating" +#define TF2_VOTE_KICK_SCAMMING_START "#TF_vote_kick_player_scamming" +#define TF2_VOTE_KICK_CHEATING_START "#TF_vote_kick_player_cheating" #define TF2_VOTE_KICK_START "#TF_vote_kick_player_other" #define TF2_VOTE_KICK_PASSED "#TF_vote_passed_kick_player" @@ -145,16 +145,17 @@ // User vote to change next level. #define TF2_VOTE_NEXTLEVEL_SINGLE_START "#TF_vote_nextlevel" #define TF2_VOTE_NEXTLEVEL_MULTIPLE_START "#TF_vote_nextlevel_choices" // Started by server -#define TF2_VOTE_NEXTLEVEL_EXTEND_PASSED "#TF_vote_passed_nextlevel_extend" // Also used for extend vote +#define TF2_VOTE_NEXTLEVEL_EXTEND_PASSED "#TF_vote_passed_nextlevel_extend" // Also used for extend vote #define TF2_VOTE_NEXTLEVEL_PASSED "#TF_vote_passed_nextlevel" -// User vote to scramble teams. Can be immediate or end of round. +// User vote to scramble teams. +// Can be immediate or end of round. #define TF2_VOTE_SCRAMBLE_IMMEDIATE_START "#TF_vote_scramble_teams" -#define TF2_VOTE_SCRAMBLE_ROUNDEND_START "#TF_vote_should_scramble_round" +#define TF2_VOTE_SCRAMBLE_ROUNDEND_START "#TF_vote_should_scramble_round" #define TF2_VOTE_SCRAMBLE_PASSED "#TF_vote_passed_scramble_teams" // User vote to change MvM mission -#define TF2_VOTE_CHANGEMISSION_START "#TF_vote_changechallenge" +#define TF2_VOTE_CHANGEMISSION_START "#TF_vote_changechallenge" #define TF2_VOTE_CHANGEMISSION_PASSED "#TF_vote_passed_changechallenge" // User vote for eternaween @@ -185,7 +186,7 @@ #define TF2_VOTE_EXTEND_START "#TF_vote_extendlevel" // While not a vote string, it works just as well. -#define TF2_VOTE_CUSTOM "#TF_playerid_noteam" +#define TF2_VOTE_CUSTOM "#TF_playerid_noteam" // TF2 (and SDK2013?) VoteFail / CallVoteFail reasons enum @@ -416,7 +417,6 @@ void Game_InitializeCvars() { g_Cvar_Votes_Enabled = FindConVar("sv_allow_votes"); } - case Engine_TF2: { g_Cvar_Votes_Enabled = FindConVar("sv_allow_votes"); @@ -453,7 +453,6 @@ NativeVotesKickType Game_GetKickType(const char[] param1, int &target) target = StringToInt(param1); kickType = NativeVotesKickType_Generic; } - case Engine_TF2: { char params[2][20]; @@ -520,7 +519,6 @@ int Game_ParseVote(const char[] option) { item = L4DL4D2_ParseVote(option); } - case Engine_CSGO: { item = CSGO_ParseVote(option); @@ -543,7 +541,6 @@ int Game_GetMaxItems() { return L4DL4D2_COUNT; } - case Engine_CSGO, Engine_TF2: { return TF2CSGO_COUNT; @@ -563,12 +560,10 @@ bool Game_CheckVoteType(NativeVotesType type) { returnVal = L4D_CheckVoteType(type); } - case Engine_Left4Dead2: { returnVal = L4D2_CheckVoteType(type); } - case Engine_CSGO: { returnVal = CSGO_CheckVoteType(type); @@ -593,17 +588,14 @@ bool Game_CheckVotePassType(NativeVotesPassType type) { returnVal = L4D_CheckVotePassType(type); } - case Engine_Left4Dead2: { returnVal = L4D2_CheckVotePassType(type); } - case Engine_CSGO: { returnVal = CSGO_CheckVotePassType(type); } - case Engine_TF2: { returnVal = TF2_CheckVotePassType(type); @@ -639,12 +631,10 @@ bool Game_DisplayVote(NativeVote vote, int[] clients, int num_clients) { L4D_DisplayVote(vote, num_clients); } - case Engine_Left4Dead2: { L4D2_DisplayVote(vote, clients, num_clients); } - case Engine_CSGO, Engine_TF2: { TF2CSGO_DisplayVote(vote, clients, num_clients); @@ -691,18 +681,15 @@ void Game_DisplayRawVoteFail(int[] clients, int numClients, NativeVotesFailType { L4D_VoteFail(team); } - case Engine_Left4Dead2: { L4D2_VoteFail(clients, numClients, team); } - case Engine_CSGO: { int reasonType = VoteFailTypeToInt(reason); CSGO_VoteFail(clients, numClients, reasonType, team); } - case Engine_TF2: { int reasonType = VoteFailTypeToInt(reason); @@ -740,11 +727,9 @@ void Game_DisplayRawVotePass(NativeVotesPassType passType, int team, int client= if (client <= 0) { L4DL4D2_VotePassToTranslation(passType, translation, sizeof(translation)); - L4D_VotePass(translation, details, team); } } - case Engine_Left4Dead2: { L4DL4D2_VotePassToTranslation(passType, translation, sizeof(translation)); @@ -755,25 +740,21 @@ void Game_DisplayRawVotePass(NativeVotesPassType passType, int team, int client= { L4D2_VotePass(translation, L4D2_VOTE_ALLTALK_ENABLE, team, client); } - case NativeVotesPass_AlltalkOff: { L4D2_VotePass(translation, L4D2_VOTE_ALLTALK_DISABLE, team, client); } - default: { L4D2_VotePass(translation, details, team, client); } } } - case Engine_CSGO: { CSGO_VotePassToTranslation(passType, translation, sizeof(translation)); CSGO_VotePass(translation, details, team, client); } - case Engine_TF2: { TF2_VotePassToTranslation(passType, translation, sizeof(translation)); @@ -801,17 +782,14 @@ void Game_DisplayRawVotePassCustom(const char[] translation, int team, int clien { ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes_DisplayPassCustom is not supported on L4D"); } - case Engine_Left4Dead2: { L4D2_VotePass(L4D_VOTE_CUSTOM, translation, team, client); } - case Engine_CSGO: { CSGO_VotePass(CSGO_VOTE_CUSTOM, translation, team, client); } - case Engine_TF2: { TF2_VotePass(TF2_VOTE_CUSTOM, translation, team, client); @@ -835,12 +813,10 @@ void Game_DisplayCallVoteFail(int client, NativeVotesCallFailType reason, int ti { L4DL4D2_CallVoteFail(client, reasonType); } - case Engine_CSGO: { CSGO_CallVoteFail(client, reasonType, time); } - case Engine_TF2: { TF2_CallVoteFail(client, reasonType, time); @@ -863,7 +839,6 @@ void Game_ClientSelectedItem(NativeVote vote, int client, int item) { L4DL4D2_ClientSelectedItem(client, item); } - case Engine_CSGO: { CSGO_ClientSelectedItem(vote, client, item); @@ -877,7 +852,6 @@ void Game_ClientSelectedItem(NativeVote vote, int client, int item) { L4D_ClientSelectedItem(vote, client, item); } - case Engine_Left4Dead2: { L4D2_ClientSelectedItem(client, item); @@ -894,7 +868,6 @@ void Game_UpdateVoteCounts(ArrayList hVoteCounts, int totalClients) { L4DL4D2_UpdateVoteCounts(hVoteCounts, totalClients); } - case Engine_CSGO, Engine_TF2: { TF2CSGO_UpdateVoteCounts(hVoteCounts); @@ -906,26 +879,26 @@ void Game_DisplayVoteSetup(int client, ArrayList hVoteTypes) { switch (g_EngineVersion) { + /* case Engine_Left4Dead: { - //L4D_DisplayVoteSetup(client, voteTypes); + L4D_DisplayVoteSetup(client, voteTypes); } - case Engine_Left4Dead2: { - //L4D2_DisplayVoteSetup(client, voteTypes); + L4D2_DisplayVoteSetup(client, voteTypes); } - + */ case Engine_TF2: { TF2_DisplayVoteSetup(client, hVoteTypes); } - + /* case Engine_CSGO: { - //CSGO_DisplayVoteSetup(client, hVoteTypes); + CSGO_DisplayVoteSetup(client, hVoteTypes); } - + */ } } @@ -938,12 +911,10 @@ stock void Game_VoteTypeToTranslation(NativeVotesType voteType, char[] translati { L4DL4D2_VoteTypeToTranslation(voteType, translation, maxlength); } - case Engine_CSGO: { CSGO_VoteTypeToTranslation(voteType, translation, maxlength); } - case Engine_TF2: { TF2_VoteTypeToTranslation(voteType, translation, maxlength); @@ -959,7 +930,6 @@ stock void Game_UpdateClientCount(int num_clients) { L4DL4D2_UpdateClientCount(num_clients); } - case Engine_CSGO, Engine_TF2: { TF2CSGO_UpdateClientCount(num_clients); @@ -975,7 +945,6 @@ public Action Game_ResetVote(Handle timer) { L4DL4D2_ResetVote(); } - case Engine_CSGO, Engine_TF2: { TF2CSGO_ResetVote(); @@ -992,7 +961,6 @@ void Game_VoteYes(int client) { FakeClientCommand(client, "Vote Yes"); } - case Engine_CSGO, Engine_TF2: { FakeClientCommand(client, "vote option1"); @@ -1008,7 +976,6 @@ void Game_VoteNo(int client) { FakeClientCommand(client, "Vote No"); } - case Engine_CSGO, Engine_TF2: { FakeClientCommand(client, "vote option2"); @@ -1016,6 +983,30 @@ void Game_VoteNo(int client) } } +void Game_VoteRandom(int client) +{ + switch (g_EngineVersion) + { + case Engine_Left4Dead, Engine_Left4Dead2: + { + int random = GetRandomInt(0, 1); + if (random == 0) + { + FakeClientCommand(client, "Vote Yes"); + } + else + { + FakeClientCommand(client, "Vote No"); + } + } + case Engine_CSGO, Engine_TF2: + { + int random = GetRandomInt(1, 5); + FakeClientCommand(client, "vote option%d", random); + } + } +} + bool Game_IsVoteInProgress() { switch (g_EngineVersion) @@ -1024,7 +1015,6 @@ bool Game_IsVoteInProgress() { return L4DL4D2_IsVoteInProgress(); } - case Engine_CSGO, Engine_TF2: { return TF2CSGO_IsVoteInProgress(); @@ -1177,112 +1167,90 @@ static NativeVotesPassType VoteTypeToVotePass(NativeVotesType voteType) { passType = NativeVotesPass_Custom; } - case NativeVotesType_ChgCampaign: { passType = NativeVotesPass_ChgCampaign; } - case NativeVotesType_ChgDifficulty: { passType = NativeVotesPass_ChgDifficulty; } - case NativeVotesType_ReturnToLobby: { passType = NativeVotesPass_ReturnToLobby; } - case NativeVotesType_AlltalkOn: { passType = NativeVotesPass_AlltalkOn; } - case NativeVotesType_AlltalkOff: { passType = NativeVotesPass_AlltalkOff; } - case NativeVotesType_Restart: { passType = NativeVotesPass_Restart; } - case NativeVotesType_Kick, NativeVotesType_KickIdle, NativeVotesType_KickScamming, NativeVotesType_KickCheating: { passType = NativeVotesPass_Kick; } - case NativeVotesType_ChgLevel: { passType = NativeVotesPass_ChgLevel; } - case NativeVotesType_NextLevel, NativeVotesType_NextLevelMult: { passType = NativeVotesPass_NextLevel; } - case NativeVotesType_ScrambleNow, NativeVotesType_ScrambleEnd: { passType = NativeVotesPass_Scramble; } - case NativeVotesType_ChgMission: { passType = NativeVotesPass_ChgMission; } - case NativeVotesType_SwapTeams: { passType = NativeVotesPass_SwapTeams; } - case NativeVotesType_Surrender: { passType = NativeVotesPass_Surrender; } - case NativeVotesType_Rematch: { passType = NativeVotesPass_Rematch; } - case NativeVotesType_Continue: { passType = NativeVotesPass_Continue; } - case NativeVotesType_StartRound: { passType = NativeVotesPass_StartRound; } - case NativeVotesType_Eternaween: { passType = NativeVotesPass_Eternaween; } - case NativeVotesType_AutoBalanceOn: { passType = NativeVotesPass_AutoBalanceOn; } - case NativeVotesType_AutoBalanceOff: { passType = NativeVotesPass_AutoBalanceOff; } - case NativeVotesType_ClassLimitsOn: { passType = NativeVotesPass_ClassLimitsOn; } - case NativeVotesType_ClassLimitsOff: { passType = NativeVotesPass_ClassLimitsOff; } - case NativeVotesType_Extend: { passType = NativeVotesPass_Extend; @@ -1315,19 +1283,16 @@ static int VoteCallFailTypeToInt(NativeVotesCallFailType failType) { return VOTE_FAILED_GENERIC; } - case NativeVotesCallFail_Loading: { return VOTE_FAILED_TRANSITIONING_PLAYERS; } - case NativeVotesCallFail_Recent: { return VOTE_FAILED_FAILED_RECENTLY; } } } - case Engine_TF2: { switch (failType) @@ -1336,94 +1301,76 @@ static int VoteCallFailTypeToInt(NativeVotesCallFailType failType) { return VOTE_FAILED_GENERIC; } - case NativeVotesCallFail_Loading: { return VOTE_FAILED_TRANSITIONING_PLAYERS; } - case NativeVotesCallFail_Recent: { return VOTE_FAILED_FAILED_RECENTLY; } - case NativeVotesCallFail_Disabled: { return VOTE_FAILED_ISSUE_DISABLED; } - case NativeVotesCallFail_MapNotFound: { return VOTE_FAILED_MAP_NOT_FOUND; } - case NativeVotesCallFail_SpecifyMap: { return VOTE_FAILED_MAP_NAME_REQUIRED; } - case NativeVotesCallFail_Failed: { return VOTE_FAILED_FAILED_RECENTLY; } - case NativeVotesCallFail_WrongTeam: { return VOTE_FAILED_TEAM_CANT_CALL; } - case NativeVotesCallFail_Waiting: { return VOTE_FAILED_WAITINGFORPLAYERS; } - case NativeVotesCallFail_PlayerNotFound: { return VOTE_FAILED_PLAYERNOTFOUND; } - case NativeVotesCallFail_CantKickAdmin: { return VOTE_FAILED_CANNOT_KICK_ADMIN; } - case NativeVotesCallFail_ScramblePending: { return VOTE_FAILED_SCRAMBLE_IN_PROGRESS; } - case NativeVotesCallFail_Spectators: { return VOTE_FAILED_SPECTATOR; } - case NativeVotesCallFail_LevelSet: { return VOTE_FAILED_NEXTLEVEL_SET; } - case NativeVotesCallFail_MapNotValid: { return VOTE_FAILED_MAP_NOT_VALID; } - case NativeVotesCallFail_KickTime: { return VOTE_FAILED_CANNOT_KICK_FOR_TIME; } - case NativeVotesCallFail_KickDuringRound: { return VOTE_FAILED_CANNOT_KICK_DURING_ROUND; } - case NativeVotesCallFail_AlreadyActive: { return VOTE_FAILED_MODIFICATION_ALREADY_ACTIVE; } } } - case Engine_CSGO: { switch (failType) @@ -1432,137 +1379,110 @@ static int VoteCallFailTypeToInt(NativeVotesCallFailType failType) { return CSGO_VOTE_FAILED_GENERIC; } - case NativeVotesCallFail_Loading: { return CSGO_VOTE_FAILED_TRANSITIONING_PLAYERS; } - case NativeVotesCallFail_Recent: { return CSGO_VOTE_FAILED_RATE_EXCEEDED; } - case NativeVotesCallFail_Disabled: { return CSGO_VOTE_FAILED_ISSUE_DISABLED; } - case NativeVotesCallFail_MapNotFound: { return CSGO_VOTE_FAILED_MAP_NOT_FOUND; } - case NativeVotesCallFail_SpecifyMap: { return CSGO_VOTE_FAILED_MAP_NAME_REQUIRED; } - case NativeVotesCallFail_Failed: { return CSGO_VOTE_FAILED_FAILED_RECENTLY; } - case NativeVotesCallFail_WrongTeam: { return CSGO_VOTE_FAILED_TEAM_CANT_CALL; } - case NativeVotesCallFail_Warmup: { return CSGO_VOTE_FAILED_WARMUP; } - case NativeVotesCallFail_PlayerNotFound: { return CSGO_VOTE_FAILED_PLAYERNOTFOUND; } - case NativeVotesCallFail_CantKickAdmin: { return CSGO_VOTE_FAILED_CANNOT_KICK_ADMIN; } - case NativeVotesCallFail_ScramblePending: { return CSGO_VOTE_FAILED_SCRAMBLE_IN_PROGRESS; } - case NativeVotesCallFail_Spectators: { return CSGO_VOTE_FAILED_SPECTATOR; } - case NativeVotesCallFail_LevelSet: { return CSGO_VOTE_FAILED_NEXTLEVEL_SET; } - case NativeVotesCallFail_KickFailed: { return CSGO_VOTE_FAILED_FAILED_RECENTLY_KICK; } - case NativeVotesCallFail_MapFailed: { return CSGO_VOTE_FAILED_FAILED_RECENTLY_MAP; } - case NativeVotesCallFail_SwapFailed: { return CSGO_VOTE_FAILED_FAILED_RECENTLY_SWAP; } - case NativeVotesCallFail_ScrambleFailed: { return CSGO_VOTE_FAILED_FAILED_RECENTLY_SCRAMBLE; } - case NativeVotesCallFail_RestartFailed: { return CSGO_VOTE_FAILED_FAILED_RECENTLY_RESTART; } - case NativeVotesCallFail_SwapPending: { return CSGO_VOTE_FAILED_SWAP_IN_PROGRESS; } - case NativeVotesCallFail_Unknown2: { return CSGO_VOTE_FAILED_UNKNOWN1; } - case NativeVotesCallFail_CantSurrender: { return CSGO_VOTE_FAILED_SURRENDER_ABANDON; } - case NativeVotesCallFail_Unknown3: { return CSGO_VOTE_FAILED_UNKNOWN2; } - case NativeVotesCallFail_MatchPaused: { return CSGO_VOTE_FAILED_PAUSED; } - case NativeVotesCallFail_NotPaused: { return CSGO_VOTE_FAILED_NOT_PAUSED; } - case NativeVotesCallFail_NotWarmup: { return CSGO_VOTE_FAILED_NOT_WARMUP; } - case NativeVotesCallFail_MinPlayers: { return CSGO_VOTE_FAILED_MIN_PLAYERS; } - case NativeVotesCallFail_RoundEnded: { return CSGO_VOTE_FAILED_ROUND_ENDED; @@ -1582,87 +1502,70 @@ static void GetEngineVersionName(EngineVersion version, char[] printName, int ma { strcopy(printName, maxlength, "Unknown"); } - case Engine_Original: { strcopy(printName, maxlength, "Original"); } - case Engine_SourceSDK2006: { strcopy(printName, maxlength, "Source SDK 2006"); } - case Engine_SourceSDK2007: { strcopy(printName, maxlength, "Source SDK 2007"); } - case Engine_Left4Dead: { strcopy(printName, maxlength, "Left 4 Dead "); } - case Engine_DarkMessiah: { strcopy(printName, maxlength, "Dark Messiah"); } - case Engine_Left4Dead2: { strcopy(printName, maxlength, "Left 4 Dead 2"); } - case Engine_AlienSwarm: { strcopy(printName, maxlength, "Alien Swarm"); } - case Engine_BloodyGoodTime: { strcopy(printName, maxlength, "Bloody Good Time"); } - case Engine_EYE: { strcopy(printName, maxlength, "E.Y.E. Divine Cybermancy"); } - case Engine_Portal2: { strcopy(printName, maxlength, "Portal 2"); } - case Engine_CSGO: { strcopy(printName, maxlength, "Counter-Strike: Global Offensive"); } - case Engine_CSS: { strcopy(printName, maxlength, "Counter-Strike: Source"); } - case Engine_DOTA: { strcopy(printName, maxlength, "DOTA 2"); } - case Engine_HL2DM: { strcopy(printName, maxlength, "Half-Life 2: Deathmatch"); } - case Engine_DODS: { strcopy(printName, maxlength, "Day of Defeat: Source"); } - case Engine_TF2: { strcopy(printName, maxlength, "Team Fortress 2"); } - case Engine_NuclearDawn: { strcopy(printName, maxlength, "Nuclear Dawn"); @@ -1755,32 +1658,26 @@ static void L4DL4D2_VoteTypeToTranslation(NativeVotesType voteType, char[] trans { strcopy(translation, maxlength, L4D_VOTE_CHANGECAMPAIGN_START); } - case NativeVotesType_ChgDifficulty: { strcopy(translation, maxlength, L4D_VOTE_CHANGEDIFFICULTY_START); } - case NativeVotesType_ReturnToLobby: { strcopy(translation, maxlength, L4D_VOTE_RETURNTOLOBBY_START); } - case NativeVotesType_AlltalkOn, NativeVotesType_AlltalkOff: { strcopy(translation, maxlength, L4D2_VOTE_ALLTALK_START); } - case NativeVotesType_Restart: { strcopy(translation, maxlength, L4D_VOTE_RESTART_START); } - case NativeVotesType_Kick: { strcopy(translation, maxlength, L4D_VOTE_KICK_START); } - case NativeVotesType_ChgLevel: { strcopy(translation, maxlength, L4D_VOTE_CHANGELEVEL_START); @@ -1801,37 +1698,30 @@ static void L4DL4D2_VotePassToTranslation(NativeVotesPassType passType, char[] t { strcopy(translation, maxlength, L4D_VOTE_CUSTOM); } - case NativeVotesPass_ChgCampaign: { strcopy(translation, maxlength, L4D_VOTE_CHANGECAMPAIGN_PASSED); } - case NativeVotesPass_ChgDifficulty: { strcopy(translation, maxlength, L4D_VOTE_CHANGEDIFFICULTY_PASSED); } - case NativeVotesPass_ReturnToLobby: { strcopy(translation, maxlength, L4D_VOTE_RETURNTOLOBBY_PASSED); } - case NativeVotesPass_AlltalkOn, NativeVotesPass_AlltalkOff: { strcopy(translation, maxlength, L4D2_VOTE_ALLTALK_PASSED); } - case NativeVotesPass_Restart: { strcopy(translation, maxlength, L4D_VOTE_RESTART_PASSED); } - case NativeVotesPass_Kick: { strcopy(translation, maxlength, L4D_VOTE_KICK_PASSED); } - case NativeVotesPass_ChgLevel: { strcopy(translation, maxlength, L4D_VOTE_CHANGELEVEL_PASSED); @@ -2014,12 +1904,10 @@ static void L4D2_DisplayVote(NativeVote vote, int[] clients, int num_clients) { strcopy(details, MAX_VOTE_DETAILS_LENGTH, L4D2_VOTE_ALLTALK_ENABLE); } - case NativeVotesType_AlltalkOff: { strcopy(details, MAX_VOTE_DETAILS_LENGTH, L4D2_VOTE_ALLTALK_DISABLE); } - case NativeVotesType_Custom_YesNo, NativeVotesType_Custom_Mult: { Data_GetTitle(vote, details, MAX_VOTE_DETAILS_LENGTH); @@ -2293,7 +2181,6 @@ static void TF2CSGO_DisplayVote(NativeVote vote, int[] clients, int num_clients) bYesNo = CSGO_VoteTypeToTranslation(voteType, translation, sizeof(translation)); CSGO_VoteTypeToVoteOtherTeamString(voteType, otherTeamString, sizeof(otherTeamString)); } - case Engine_TF2: { bYesNo = TF2_VoteTypeToTranslation(voteType, translation, sizeof(translation)); @@ -2699,7 +2586,6 @@ static bool TF2_CheckVoteType(NativeVotesType voteType) { return true; } - case NativeVotesType_Custom_Mult, NativeVotesType_NextLevelMult: { return true; @@ -2736,94 +2622,76 @@ static bool TF2_VoteTypeToTranslation(NativeVotesType voteType, char[] translati strcopy(translation, maxlength, TF2_VOTE_CUSTOM); bYesNo = false; } - case NativeVotesType_Restart: { strcopy(translation, maxlength, TF2_VOTE_RESTART_START); } - case NativeVotesType_Kick: { strcopy(translation, maxlength, TF2_VOTE_KICK_START); } - case NativeVotesType_KickIdle: { strcopy(translation, maxlength, TF2_VOTE_KICK_IDLE_START); } - case NativeVotesType_KickScamming: { strcopy(translation, maxlength, TF2_VOTE_KICK_SCAMMING_START); } - case NativeVotesType_KickCheating: { strcopy(translation, maxlength, TF2_VOTE_KICK_CHEATING_START); } - case NativeVotesType_ChgLevel: { strcopy(translation, maxlength, TF2_VOTE_CHANGELEVEL_START); } - case NativeVotesType_NextLevel: { strcopy(translation, maxlength, TF2_VOTE_NEXTLEVEL_SINGLE_START); } - case NativeVotesType_NextLevelMult: { strcopy(translation, maxlength, TF2_VOTE_NEXTLEVEL_MULTIPLE_START); bYesNo = false; } - case NativeVotesType_ScrambleNow: { strcopy(translation, maxlength, TF2_VOTE_SCRAMBLE_IMMEDIATE_START); } - case NativeVotesType_ScrambleEnd: { strcopy(translation, maxlength, TF2_VOTE_SCRAMBLE_ROUNDEND_START); } - case NativeVotesType_ChgMission: { strcopy(translation, maxlength, TF2_VOTE_CHANGEMISSION_START); } - case NativeVotesType_StartRound: { strcopy(translation, maxlength, TF2_VOTE_ROUND_START); } - case NativeVotesType_Eternaween: { strcopy(translation, maxlength, TF2_VOTE_ETERNAWEEN_START); } - case NativeVotesType_AutoBalanceOn: { strcopy(translation, maxlength, TF2_VOTE_AUTOBALANCE_ENABLE_START); } - case NativeVotesType_AutoBalanceOff: { strcopy(translation, maxlength, TF2_VOTE_AUTOBALANCE_DISABLE_START); } - case NativeVotesType_ClassLimitsOn: { strcopy(translation, maxlength, TF2_VOTE_CLASSLIMITS_ENABLE_START); } - case NativeVotesType_ClassLimitsOff: { strcopy(translation, maxlength, TF2_VOTE_CLASSLIMITS_DISABLE_START); } - case NativeVotesType_Extend: { strcopy(translation, maxlength, TF2_VOTE_EXTEND_START); @@ -2846,27 +2714,22 @@ static void TF2_VotePassToTranslation(NativeVotesPassType passType, char[] trans { strcopy(translation, maxlength, TF2_VOTE_RESTART_PASSED); } - case NativeVotesPass_Kick: { strcopy(translation, maxlength, TF2_VOTE_KICK_PASSED); } - case NativeVotesPass_ChgLevel: { strcopy(translation, maxlength, TF2_VOTE_CHANGELEVEL_PASSED); } - case NativeVotesPass_NextLevel: { strcopy(translation, maxlength, TF2_VOTE_NEXTLEVEL_PASSED); } - case NativeVotesPass_Extend: { strcopy(translation, maxlength, TF2_VOTE_NEXTLEVEL_EXTEND_PASSED); } - case NativeVotesPass_Scramble: { strcopy(translation, maxlength, TF2_VOTE_SCRAMBLE_PASSED); @@ -2876,32 +2739,26 @@ static void TF2_VotePassToTranslation(NativeVotesPassType passType, char[] trans { strcopy(translation, maxlength, TF2_VOTE_CHANGEMISSION_PASSED); } - case NativeVotesPass_StartRound: { strcopy(translation, maxlength, TF2_VOTE_ROUND_PASSED); } - case NativeVotesPass_Eternaween: { strcopy(translation, maxlength, TF2_VOTE_ETERNAWEEN_PASSED); } - case NativeVotesPass_AutoBalanceOn: { strcopy(translation, maxlength, TF2_VOTE_AUTOBALANCE_ENABLE_PASSED); } - case NativeVotesPass_AutoBalanceOff: { strcopy(translation, maxlength, TF2_VOTE_AUTOBALANCE_DISABLE_PASSED); } - case NativeVotesPass_ClassLimitsOn: { strcopy(translation, maxlength, TF2_VOTE_CLASSLIMITS_ENABLE_PASSED); } - case NativeVotesPass_ClassLimitsOff: { strcopy(translation, maxlength, TF2_VOTE_CLASSLIMITS_DISABLE_PASSED); @@ -2999,7 +2856,6 @@ static bool CSGO_CheckVoteType(NativeVotesType voteType) { return true; } - case NativeVotesType_Custom_Mult, NativeVotesType_NextLevelMult: { // Until/unless Valve fixes their menu code, this is false. @@ -3037,68 +2893,55 @@ static bool CSGO_VoteTypeToTranslation(NativeVotesType voteType, char[] translat strcopy(translation, maxlength, CSGO_VOTE_CUSTOM); bYesNo = false; } - case NativeVotesType_Restart: { strcopy(translation, maxlength, CSGO_VOTE_RESTART_START); } - case NativeVotesType_Kick: { strcopy(translation, maxlength, CSGO_VOTE_KICK_START); } - case NativeVotesType_KickIdle: { strcopy(translation, maxlength, CSGO_VOTE_KICK_IDLE_START); } - case NativeVotesType_KickScamming: { strcopy(translation, maxlength, CSGO_VOTE_KICK_SCAMMING_START); } - case NativeVotesType_KickCheating: { strcopy(translation, maxlength, CSGO_VOTE_KICK_CHEATING_START); } - case NativeVotesType_ChgLevel: { strcopy(translation, maxlength, CSGO_VOTE_CHANGELEVEL_START); } - case NativeVotesType_NextLevel: { strcopy(translation, maxlength, CSGO_VOTE_NEXTLEVEL_SINGLE_START); } - case NativeVotesType_NextLevelMult: { strcopy(translation, maxlength, CSGO_VOTE_NEXTLEVEL_MULTIPLE_START); bYesNo = false; } - case NativeVotesType_ScrambleNow: { strcopy(translation, maxlength, CSGO_VOTE_SCRAMBLE_START); } - case NativeVotesType_SwapTeams: { strcopy(translation, maxlength, CSGO_VOTE_SWAPTEAMS_START); } - case NativeVotesType_Surrender: { strcopy(translation, maxlength, CSGO_VOTE_SURRENDER_START); } - case NativeVotesType_Rematch: { strcopy(translation, maxlength, CSGO_VOTE_REMATCH_START); } - case NativeVotesType_Continue: { strcopy(translation, maxlength, CSGO_VOTE_CONTINUE_START); @@ -3121,47 +2964,38 @@ static void CSGO_VotePassToTranslation(NativeVotesPassType passType, char[] tran { strcopy(translation, maxlength, CSGO_VOTE_RESTART_PASSED); } - case NativeVotesPass_Kick: { strcopy(translation, maxlength, CSGO_VOTE_KICK_PASSED); } - case NativeVotesPass_ChgLevel: { strcopy(translation, maxlength, CSGO_VOTE_CHANGELEVEL_PASSED); } - case NativeVotesPass_NextLevel: { strcopy(translation, maxlength, CSGO_VOTE_NEXTLEVEL_PASSED); } - case NativeVotesPass_Extend: { strcopy(translation, maxlength, CSGO_VOTE_NEXTLEVEL_EXTEND_PASSED); } - case NativeVotesPass_Scramble: { strcopy(translation, maxlength, CSGO_VOTE_SCRAMBLE_PASSED); } - case NativeVotesPass_SwapTeams: { strcopy(translation, maxlength, CSGO_VOTE_SWAPTEAMS_PASSED); } - case NativeVotesPass_Surrender: { strcopy(translation, maxlength, CSGO_VOTE_SURRENDER_PASSED); } - case NativeVotesPass_Rematch: { strcopy(translation, maxlength, CSGO_VOTE_REMATCH_PASSED); } - case NativeVotesPass_Continue: { strcopy(translation, maxlength, CSGO_VOTE_CONTINUE_PASSED); @@ -3182,12 +3016,10 @@ static void CSGO_VoteTypeToVoteOtherTeamString(NativeVotesType voteType, char[] { strcopy(otherTeamString, maxlength, CSGO_VOTE_KICK_OTHERTEAM); } - case NativeVotesType_Surrender: { strcopy(otherTeamString, maxlength, CSGO_VOTE_SURRENDER_OTHERTEAM); } - case NativeVotesType_Continue: { strcopy(otherTeamString, maxlength, CSGO_VOTE_CONTINUE_OTHERTEAM); @@ -3211,27 +3043,22 @@ static stock int TF2CSGO_GetVoteType(NativeVotesType voteType) { valveVoteType = ValveVote_Restart; } - case NativeVotesType_Custom_Mult, NativeVotesType_NextLevel, NativeVotesType_NextLevelMult: { valveVoteType = ValveVote_NextLevel; } - case NativeVotesType_Kick, NativeVotesType_KickIdle, NativeVotesType_KickScamming, NativeVotesType_KickCheating: { valveVoteType = ValveVote_Kick; } - case NativeVotesType_ChgLevel: { valveVoteType = ValveVote_ChangeLevel; } - case NativeVotesType_ScrambleNow, NativeVotesType_ScrambleEnd: { valveVoteType = ValveVote_Scramble; } - case NativeVotesType_SwapTeams: { valveVoteType = ValveVote_SwapTeams; @@ -3253,61 +3080,51 @@ static stock bool TF2_VoteTypeToVoteString(NativeVotesType voteType, char[] vote strcopy(voteString, maxlength, TF2CSGO_VOTE_STRING_KICK); valid = true; } - case NativeVotesType_ChgLevel: { strcopy(voteString, maxlength, TF2CSGO_VOTE_STRING_CHANGELEVEL); valid = true; } - case NativeVotesType_NextLevel: { strcopy(voteString, maxlength, TF2CSGO_VOTE_STRING_NEXTLEVEL); valid = true; } - case NativeVotesType_Restart: { strcopy(voteString, maxlength, TF2CSGO_VOTE_STRING_RESTART); valid = true; } - case NativeVotesType_ScrambleEnd, NativeVotesType_ScrambleNow: { strcopy(voteString, maxlength, TF2CSGO_VOTE_STRING_SCRAMBLE); valid = true; } - case NativeVotesType_Eternaween: { strcopy(voteString, maxlength, TF2_VOTE_STRING_ETERNAWEEN); valid = true; } - case NativeVotesType_AutoBalanceOn: { strcopy(voteString, maxlength, TF2_VOTE_STRING_AUTOBALANCE); valid = true; } - case NativeVotesType_AutoBalanceOff: { strcopy(voteString, maxlength, TF2_VOTE_STRING_AUTOBALANCE); valid = true; } - case NativeVotesType_ClassLimitsOn: { strcopy(voteString, maxlength, TF2_VOTE_STRING_CLASSLIMIT); valid = true; } - case NativeVotesType_ClassLimitsOff: { strcopy(voteString, maxlength, TF2_VOTE_STRING_CLASSLIMIT); valid = true; } - case NativeVotesType_Extend: { strcopy(voteString, maxlength, TF2_VOTE_STRING_EXTEND); @@ -3381,42 +3198,34 @@ static stock NativeVotesOverride TF2_VoteTypeToVoteOverride(NativeVotesType vote { overrideType = NativeVotesOverride_Kick; } - case NativeVotesType_ChgLevel: { overrideType = NativeVotesOverride_ChgLevel; } - case NativeVotesType_NextLevel: { overrideType = NativeVotesOverride_NextLevel; } - case NativeVotesType_Restart: { overrideType = NativeVotesOverride_Restart; } - case NativeVotesType_ScrambleEnd, NativeVotesType_ScrambleNow: { overrideType = NativeVotesOverride_Scramble; } - case NativeVotesType_Eternaween: { overrideType = NativeVotesOverride_Eternaween; } - case NativeVotesType_AutoBalanceOn, NativeVotesType_AutoBalanceOff: { overrideType = NativeVotesOverride_AutoBalance; } - case NativeVotesType_ClassLimitsOn, NativeVotesType_ClassLimitsOff: { overrideType = NativeVotesOverride_ClassLimits; } - case NativeVotesType_Extend: { overrideType = NativeVotesOverride_Extend; @@ -3436,47 +3245,38 @@ static stock NativeVotesType TF2_VoteOverrideToVoteType(NativeVotesOverride over { voteType = NativeVotesType_Restart; } - case NativeVotesOverride_Kick: { voteType = NativeVotesType_Kick; } - case NativeVotesOverride_ChgLevel: { voteType = NativeVotesType_ChgLevel; } - case NativeVotesOverride_NextLevel: { voteType = NativeVotesType_NextLevel; } - case NativeVotesOverride_Scramble: { voteType = NativeVotesType_ScrambleNow; } - case NativeVotesOverride_ChgMission: { voteType = NativeVotesType_ChgMission; } - case NativeVotesOverride_Eternaween: { voteType = NativeVotesType_Eternaween; } - case NativeVotesOverride_AutoBalance: { voteType = NativeVotesType_AutoBalanceOn; } - case NativeVotesOverride_ClassLimits: { voteType = NativeVotesType_ClassLimitsOn; } - case NativeVotesOverride_Extend: { voteType = NativeVotesType_Extend; @@ -3548,49 +3348,41 @@ static stock bool TF2_OverrideTypeToVoteString(NativeVotesOverride overrideType, strcopy(voteString, maxlength, TF2CSGO_VOTE_STRING_KICK); valid = true; } - case NativeVotesOverride_ChgLevel: { strcopy(voteString, maxlength, TF2CSGO_VOTE_STRING_CHANGELEVEL); valid = true; } - case NativeVotesOverride_NextLevel: { strcopy(voteString, maxlength, TF2CSGO_VOTE_STRING_NEXTLEVEL); valid = true; } - case NativeVotesOverride_Restart: { strcopy(voteString, maxlength, TF2CSGO_VOTE_STRING_RESTART); valid = true; } - case NativeVotesOverride_Scramble: { strcopy(voteString, maxlength, TF2CSGO_VOTE_STRING_SCRAMBLE); valid = true; } - case NativeVotesOverride_Eternaween: { strcopy(voteString, maxlength, TF2_VOTE_STRING_ETERNAWEEN); valid = true; } - case NativeVotesOverride_AutoBalance: { strcopy(voteString, maxlength, TF2_VOTE_STRING_AUTOBALANCE); valid = true; } - case NativeVotesOverride_ClassLimits: { strcopy(voteString, maxlength, TF2_VOTE_STRING_CLASSLIMIT); valid = true; } - case NativeVotesOverride_Extend: { strcopy(voteString, maxlength, TF2_VOTE_STRING_EXTEND); @@ -3615,37 +3407,31 @@ static stock bool TF2_OverrideTypeToTranslationString(NativeVotesOverride overri strcopy(translationString, maxlength, TF2_VOTE_MENU_KICK); valid = true; } - case NativeVotesOverride_ChgLevel: { strcopy(translationString, maxlength, TF2_VOTE_MENU_CHANGELEVEL); valid = true; } - case NativeVotesOverride_NextLevel: { strcopy(translationString, maxlength, TF2_VOTE_MENU_NEXTLEVEL); valid = true; } - case NativeVotesOverride_Restart: { strcopy(translationString, maxlength, TF2_VOTE_MENU_RESTART); valid = true; } - case NativeVotesOverride_Scramble: { strcopy(translationString, maxlength, TF2_VOTE_MENU_SCRAMBLE); valid = true; } - case NativeVotesOverride_Eternaween: { strcopy(translationString, maxlength, TF2_VOTE_MENU_ETERNAWEEN); valid = true; } - case NativeVotesOverride_AutoBalance: { if (GetConVarBool(g_Cvar_AutoBalance)) @@ -3658,7 +3444,6 @@ static stock bool TF2_OverrideTypeToTranslationString(NativeVotesOverride overri } valid = true; } - case NativeVotesOverride_ClassLimits: { if (GetConVarInt(g_Cvar_ClassLimit)) @@ -3671,7 +3456,6 @@ static stock bool TF2_OverrideTypeToTranslationString(NativeVotesOverride overri } valid = true; } - case NativeVotesOverride_Extend: { strcopy(translationString, maxlength, TF2_VOTE_MENU_EXTEND); From c6f1319ff1312d7d5d3dc6df4ddbc161244a7998 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sat, 10 Jan 2026 02:36:13 +0100 Subject: [PATCH 019/128] Nomations & RTV Tweaks - Attempting to nominate without specifying the map will open the built-in vote menu. - Attempting to call a "Change Level" vote will nominate said map before attempting a rtv. --- .../scripting/include/nativevotes.inc | 2 +- addons/sourcemod/scripting/nativevotes.sp | 31 +----- .../sourcemod/scripting/nativevotes/game.sp | 24 ---- .../scripting/nativevotes_basecommands.sp | 8 +- .../scripting/nativevotes_basevotes.sp | 2 +- .../scripting/nativevotes_funvotes.sp | 16 +-- .../nativevotes_kickvote_immunity.sp | 2 +- .../scripting/nativevotes_mapchooser.sp | 2 +- .../scripting/nativevotes_nominations.sp | 103 ++++++++++-------- .../scripting/nativevotes_rockthevote.sp | 16 ++- .../sourcemod/scripting/nativevotes_voterp.sp | 4 +- 11 files changed, 93 insertions(+), 117 deletions(-) diff --git a/addons/sourcemod/scripting/include/nativevotes.inc b/addons/sourcemod/scripting/include/nativevotes.inc index db3fdb3..4707ba1 100644 --- a/addons/sourcemod/scripting/include/nativevotes.inc +++ b/addons/sourcemod/scripting/include/nativevotes.inc @@ -1396,7 +1396,7 @@ typedef NativeVotes_CallVoteVisCheck = function Action (int client, NativeVotesO * Are vote commands supported by this game? * * @return true if they are supported, false if not. - * As of this writing, only TF2 will return true + * As of this writing, only TF2 will return true */ native bool NativeVotes_AreVoteCommandsSupported(); diff --git a/addons/sourcemod/scripting/nativevotes.sp b/addons/sourcemod/scripting/nativevotes.sp index 0b3371c..4949e33 100644 --- a/addons/sourcemod/scripting/nativevotes.sp +++ b/addons/sourcemod/scripting/nativevotes.sp @@ -46,7 +46,7 @@ EngineVersion g_EngineVersion = Engine_Unknown; #include "nativevotes/data-keyvalues.sp" -#define VERSION "26w02a" +#define VERSION "26w02b" #define LOGTAG "NV" @@ -87,10 +87,9 @@ enum progress_console, progress_client_console, vote_delay, - bots_allowed, MAX_CONVARS -} +}; ConVar g_ConVars[MAX_CONVARS]; @@ -264,11 +263,10 @@ public void OnPluginStart() g_ConVars[progress_console] = CreateConVar("nativevotes_progress_console", "0", "Show current vote progress as console messages", FCVAR_NONE, true, 0.0, true, 1.0); g_ConVars[progress_client_console] = CreateConVar("nativevotes_progress_client_console", "0", "Show current vote progress as console messages to clients", FCVAR_NONE, true, 0.0, true, 1.0); g_ConVars[vote_delay] = CreateConVar("nativevotes_vote_delay", "30", "Sets the recommended time in between public votes", FCVAR_NONE, true, 0.0); - g_ConVars[bots_allowed] = FindConVar("sv_vote_bots_allowed"); + g_ConVars[vote_delay].AddChangeHook(OnVoteDelayChange); Game_InitializeCvars(); - HookConVarChange(g_ConVars[vote_delay], OnVoteDelayChange); AddCommandListener(Command_Vote, "vote"); // All games, command listeners aren't case sensitive sv_vote_holder_may_vote_no = FindConVar("sv_vote_holder_may_vote_no"); @@ -725,7 +723,7 @@ void OnVoteSelect(NativeVote vote, int client, int item) Game_UpdateVoteCounts(g_hVotes, g_TotalClients); - if (g_ConVars[progress_chat].BoolValue || g_ConVars[progress_console].BoolValue || g_ConVars[progress_clientconsole].BoolValue) + if (g_ConVars[progress_chat].BoolValue || g_ConVars[progress_console].BoolValue || g_ConVars[progress_client_console].BoolValue) { char choice[128]; char name[MAX_NAME_LENGTH+1]; @@ -738,7 +736,7 @@ void OnVoteSelect(NativeVote vote, int client, int item) PrintToServer("[%s] %T", LOGTAG, "Voted For", LANG_SERVER, name, choice); } - if (g_ConVars[progress_chat].BoolValue || g_ConVars[progress_clientconsole].BoolValue) + if (g_ConVars[progress_chat].BoolValue || g_ConVars[progress_client_console].BoolValue) { char phrase[30]; @@ -756,7 +754,7 @@ void OnVoteSelect(NativeVote vote, int client, int item) CPrintToChatAll("[%s] %t", LOGTAG, phrase, name, choice); } - if (g_ConVars[progress_clientconsole].BoolValue) + if (g_ConVars[progress_client_console].BoolValue) { for (int i = 1; i <= MaxClients; i++) { @@ -1303,23 +1301,6 @@ void StartVoting() Game_VoteYes(initiator); } } - - // sv_vote_bots_allowed - if (g_ConVars[allow_bots].BoolValue) - { - for (int i = 1; i <= MaxClients; i++) - { - if (!IsFakeClient(i)) - { - continue; - } - - if (Internal_IsClientInVotePool(i)) - { - Game_VoteRandom(i); - } - } - } } public Action DisplayTimer(Handle timer) diff --git a/addons/sourcemod/scripting/nativevotes/game.sp b/addons/sourcemod/scripting/nativevotes/game.sp index 54aaa72..0a064a0 100644 --- a/addons/sourcemod/scripting/nativevotes/game.sp +++ b/addons/sourcemod/scripting/nativevotes/game.sp @@ -983,30 +983,6 @@ void Game_VoteNo(int client) } } -void Game_VoteRandom(int client) -{ - switch (g_EngineVersion) - { - case Engine_Left4Dead, Engine_Left4Dead2: - { - int random = GetRandomInt(0, 1); - if (random == 0) - { - FakeClientCommand(client, "Vote Yes"); - } - else - { - FakeClientCommand(client, "Vote No"); - } - } - case Engine_CSGO, Engine_TF2: - { - int random = GetRandomInt(1, 5); - FakeClientCommand(client, "vote option%d", random); - } - } -} - bool Game_IsVoteInProgress() { switch (g_EngineVersion) diff --git a/addons/sourcemod/scripting/nativevotes_basecommands.sp b/addons/sourcemod/scripting/nativevotes_basecommands.sp index 9bd6ae6..54debbd 100644 --- a/addons/sourcemod/scripting/nativevotes_basecommands.sp +++ b/addons/sourcemod/scripting/nativevotes_basecommands.sp @@ -38,7 +38,7 @@ #undef REQUIRE_PLUGIN #include -#define VERSION "26w02a" +#define VERSION "26w02b" #pragma newdecls required #pragma semicolon 1 @@ -69,7 +69,7 @@ bool PerformCancelVote(int client) return false; } - CShowActivity2(client, "[{lightgreen}NativeVotes{default}] ", "%t", "Cancelled Vote"); + CShowActivity2(client, "[{lightgreen}NativeVotes\x01] ", "%t", "Cancelled Vote"); NativeVotes_Cancel(); return true; @@ -139,7 +139,7 @@ public Action Command_ReVote(int client, const char[] command, int argc) return Plugin_Continue; } - CReplyToCommand(client, "[{lightgreen}NativeVotes{default}] %t", "Cannot participate in vote"); + CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Cannot participate in vote"); return Plugin_Stop; } @@ -149,7 +149,7 @@ public Action Command_ReVote(int client, const char[] command, int argc) } else if (!IsVoteInProgress()) { - CReplyToCommand(client, "[{lightgreen}NativeVotes{default}] %t", "Cannot change vote"); + CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Cannot change vote"); return Plugin_Stop; } diff --git a/addons/sourcemod/scripting/nativevotes_basevotes.sp b/addons/sourcemod/scripting/nativevotes_basevotes.sp index 776c9a8..c07c3a6 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes.sp @@ -40,7 +40,7 @@ #include #include -#define VERSION "26w02a" +#define VERSION "26w02b" public Plugin:myinfo = { diff --git a/addons/sourcemod/scripting/nativevotes_funvotes.sp b/addons/sourcemod/scripting/nativevotes_funvotes.sp index d8edd16..3e0c62e 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes.sp @@ -42,7 +42,7 @@ #include #include -#define VERSION "26w02a" +#define VERSION "26w02b" public Plugin myinfo = { @@ -122,14 +122,14 @@ public void OnPluginStart() RegAdminCmd("sm_votealltalk", Command_VoteAlltalk, ADMFLAG_VOTE, "sm_votealltalk"); RegAdminCmd("sm_voteff", Command_VoteFF, ADMFLAG_VOTE, "sm_voteff"); - g_ConVars[sv_gravity] = FindConVar("sv_gravity"); - g_ConVars[sv_alltalk] = FindConVar("sv_alltalk"); + g_ConVars[sv_gravity] = FindConVar("sv_gravity"); + g_ConVars[sv_alltalk] = FindConVar("sv_alltalk"); g_ConVars[mp_friendlyfire] = FindConVar("mp_friendlyfire"); - g_ConVars[vote_gravity] = CreateConVar("sm_vote_gravity", "0.60", "Percent required for successful gravity vote.", 0, true, 0.05, true, 1.0); - g_ConVars[vote_burn] = CreateConVar("sm_vote_burn", "0.60", "Percent required for successful burn vote.", 0, true, 0.05, true, 1.0); - g_ConVars[vote_slay] = CreateConVar("sm_vote_slay", "0.60", "Percent required for successful slay vote.", 0, true, 0.05, true, 1.0); - g_ConVars[vote_alltalk] = CreateConVar("sm_vote_alltalk", "0.60", "Percent required for successful alltalk vote.", 0, true, 0.05, true, 1.0); - g_ConVars[vote_ff] = CreateConVar("sm_vote_ff", "0.60", "Percent required for successful friendly fire vote.", 0, true, 0.05, true, 1.0); + g_ConVars[vote_gravity] = CreateConVar("sm_vote_gravity", "0.60", "Percent required for successful gravity vote.", 0, true, 0.05, true, 1.0); + g_ConVars[vote_burn] = CreateConVar("sm_vote_burn", "0.60", "Percent required for successful burn vote.", 0, true, 0.05, true, 1.0); + g_ConVars[vote_slay] = CreateConVar("sm_vote_slay", "0.60", "Percent required for successful slay vote.", 0, true, 0.05, true, 1.0); + g_ConVars[vote_alltalk] = CreateConVar("sm_vote_alltalk", "0.60", "Percent required for successful alltalk vote.", 0, true, 0.05, true, 1.0); + g_ConVars[vote_ff] = CreateConVar("sm_vote_ff", "0.60", "Percent required for successful friendly fire vote.", 0, true, 0.05, true, 1.0); CreateConVar("nativevotes_funvotes_version", VERSION, "NativeVotes Fun Votes version", FCVAR_PLUGIN|FCVAR_NOTIFY|FCVAR_DONTRECORD|FCVAR_SPONLY); } diff --git a/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp b/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp index dbf6751..ede361b 100644 --- a/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp +++ b/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp @@ -41,7 +41,7 @@ #pragma semicolon 1 #pragma newdecls required -#define VERSION "26w02a" +#define VERSION "26w02b" ConVar g_Cvar_Votes; ConVar g_Cvar_KickVote; diff --git a/addons/sourcemod/scripting/nativevotes_mapchooser.sp b/addons/sourcemod/scripting/nativevotes_mapchooser.sp index 3e0c84f..c2b4b2f 100644 --- a/addons/sourcemod/scripting/nativevotes_mapchooser.sp +++ b/addons/sourcemod/scripting/nativevotes_mapchooser.sp @@ -45,7 +45,7 @@ #pragma semicolon 1 #pragma newdecls required -#define VERSION "26w02a" +#define VERSION "26w02b" public Plugin myinfo = { diff --git a/addons/sourcemod/scripting/nativevotes_nominations.sp b/addons/sourcemod/scripting/nativevotes_nominations.sp index aab8eb2..368905a 100644 --- a/addons/sourcemod/scripting/nativevotes_nominations.sp +++ b/addons/sourcemod/scripting/nativevotes_nominations.sp @@ -43,7 +43,7 @@ #pragma semicolon 1 #pragma newdecls required -#define VERSION "26w02a" +#define VERSION "26w02b" public Plugin myinfo = { @@ -54,21 +54,29 @@ public Plugin myinfo = url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; -ConVar g_Cvar_ExcludeOld; -ConVar g_Cvar_ExcludeCurrent; -ConVar g_Cvar_MaxMatches; +enum +{ + excludeold, + excludecurrent, + maxmatches, + //allow_workshop, + + MAX_CONVARS +} + +ConVar g_ConVars[MAX_CONVARS]; Menu g_MapMenu = null; ArrayList g_MapList = null; int g_mapFileSerial = -1; -#define MAPSTATUS_ENABLED (1<<0) -#define MAPSTATUS_DISABLED (1<<1) -#define MAPSTATUS_EXCLUDE_CURRENT (1<<2) -#define MAPSTATUS_EXCLUDE_PREVIOUS (1<<3) -#define MAPSTATUS_EXCLUDE_NOMINATED (1<<4) +#define MAPSTATUS_ENABLED (1<<0) +#define MAPSTATUS_DISABLED (1<<1) +#define MAPSTATUS_EXCLUDE_CURRENT (1<<2) +#define MAPSTATUS_EXCLUDE_PREVIOUS (1<<3) +#define MAPSTATUS_EXCLUDE_NOMINATED (1<<4) -StringMap g_mapTrie = null; +StringMap g_MapTrie = null; // NativeVotes bool g_NativeVotes; @@ -87,16 +95,17 @@ public void OnPluginStart() CreateConVar("nativevotes_nominations_version", VERSION, "NativeVotes Nominations version", FCVAR_DONTRECORD|FCVAR_NOTIFY|FCVAR_SPONLY); - g_Cvar_ExcludeOld = CreateConVar("sm_nominate_excludeold", "1", "Specifies if the current map should be excluded from the Nominations list", 0, true, 0.00, true, 1.0); - g_Cvar_ExcludeCurrent = CreateConVar("sm_nominate_excludecurrent", "1", "Specifies if the MapChooser excluded maps should also be excluded from Nominations", 0, true, 0.00, true, 1.0); - g_Cvar_MaxMatches = CreateConVar("sm_nominate_maxfound", "0", "Maximum number of nomination matches to add to the menu. 0 = infinite.", _, true, 0.0); - + g_ConVars[excludeold] = CreateConVar("sm_nominate_excludeold", "1", "Specifies if the current map should be excluded from the Nominations list", 0, true, 0.0, true, 1.0); + g_ConVars[excludecurrent] = CreateConVar("sm_nominate_excludecurrent", "1", "Specifies if the MapChooser excluded maps should also be excluded from Nominations", 0, true, 0.0, true, 1.0); + g_ConVars[maxmatches] = CreateConVar("sm_nominate_maxfound", "0", "Maximum number of nomination matches to add to the menu. 0 = infinite.", _, true, 0.0); + //g_ConVars[allow_workshop] = CreateConVar("sm_nominate_allow_workshop", "0", "Specifies if unlisted workshop maps should be allowed to be nominated", 0, true, 0.0, true, 1.0); + RegConsoleCmd("sm_nominate", Command_Nominate); RegAdminCmd("sm_nominate_addmap", Command_Addmap, ADMFLAG_CHANGEMAP, "sm_nominate_addmap - Forces a map to be on the next mapvote."); - RegAdminCmd("sm_reload_nominations", Cmd_ReloadNominations, ADMFLAG_RCON, "Reload the nomination map cycle in-place"); - - g_mapTrie = new StringMap(); + RegAdminCmd("sm_reload_nominations", Command_ReloadNominations, ADMFLAG_RCON, "Reload the nomination map cycle in-place"); + + g_MapTrie = new StringMap(); } public void OnPluginEnd() @@ -175,7 +184,7 @@ public void OnNominationRemoved(const char[] map, int owner) FindMap(map, resolvedMap, sizeof(resolvedMap)); /* Is the map in our list? */ - if (!g_mapTrie.GetValue(resolvedMap, status)) + if (!g_MapTrie.GetValue(resolvedMap, status)) { return; } @@ -186,7 +195,7 @@ public void OnNominationRemoved(const char[] map, int owner) return; } - g_mapTrie.SetValue(resolvedMap, MAPSTATUS_ENABLED); + g_MapTrie.SetValue(resolvedMap, MAPSTATUS_ENABLED); } public Action Command_Addmap(int client, int args) @@ -213,7 +222,7 @@ public Action Command_Addmap(int client, int args) Format(displayName, sizeof(displayName), "\x05%s\x01", displayName); int status; - if (!g_mapTrie.GetValue(resolvedMap, status)) + if (!g_MapTrie.GetValue(resolvedMap, status)) { CReplyToCommand(client, "[{lightgreen}Nominations\x01] %t", "Map Not In Pool", displayName); return Plugin_Handled; @@ -228,10 +237,8 @@ public Action Command_Addmap(int client, int args) return Plugin_Handled; } - - - g_mapTrie.SetValue(resolvedMap, MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_NOMINATED); - + + g_MapTrie.SetValue(resolvedMap, MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_NOMINATED); CReplyToCommand(client, "[{lightgreen}Nominations\x01] %t", "Map Inserted", displayName); LogAction(client, -1, "\"%L\" inserted map \"%s\".", client, mapname); @@ -239,7 +246,7 @@ public Action Command_Addmap(int client, int args) return Plugin_Handled; } -Action Cmd_ReloadNominations(int client, int args) +Action Command_ReloadNominations(int client, int args) { OnConfigsExecuted(); return Plugin_Handled; @@ -256,7 +263,7 @@ public void OnClientSayCommand_Post(int client, const char[] command, const char { ReplySource old = SetCmdReplySource(SM_REPLY_TO_CHAT); - OpenNominationMenu(client); + Command_Nominate(client, 0); SetCmdReplySource(old); } @@ -273,10 +280,18 @@ public Action Command_Nominate(int client, int args) if (args == 0) { - OpenNominationMenu(client); - return Plugin_Handled; + // https://github.com/ValveSoftware/source-sdk-2013/blob/7191ecc418e28974de8be3a863eebb16b974a7ef/src/game/server/tf/tf_voteissues.h#L116 + if (g_NativeVotes && NativeVotes_AreVoteCommandsSupported()) + { + FakeClientCommand(client, "callvote"); + } + else + { + OpenNominationMenu(client); + } + return Plugin_Handled; } - + char mapname[96]; GetCmdArg(1, mapname, sizeof(mapname)); @@ -295,7 +310,7 @@ public Action Command_Nominate(int client, int args) // Get the result and nominate it g_MapList.GetString(results.Get(0), mapResult, sizeof(mapResult)); AttemptNominate(client, mapResult, sizeof(mapResult), false); -} + } else if (matches > 1) { if (source == SM_REPLY_TO_CONSOLE) @@ -340,7 +355,7 @@ int FindMatchingMaps(ArrayList mapList, ArrayList results, const char[] input) int matches = 0; char map[96]; - int maxmatches = g_Cvar_MaxMatches.IntValue; + int maxmatches = g_ConVars[maxmatches].IntValue; for (int i = 0; i < map_count; i++) { @@ -381,7 +396,7 @@ void AttemptNominate(int client, const char[] map, int size, bool isVoteMenu) Format(displayName, sizeof(displayName), "\x05%s\x01", displayName); int status; - if (!g_mapTrie.GetValue(mapname, status)) + if (!g_MapTrie.GetValue(mapname, status)) { CReplyToCommand(client, "[{lightgreen}Nominations\x01] %t", "Map Not In Pool", displayName); if (isVoteMenu && g_NativeVotes) @@ -449,7 +464,7 @@ void AttemptNominate(int client, const char[] map, int size, bool isVoteMenu) /* Map was nominated! - Disable the menu item and update the trie */ - g_mapTrie.SetValue(mapname, MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_NOMINATED); + g_MapTrie.SetValue(mapname, MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_NOMINATED); char name[MAX_NAME_LENGTH]; int r, g, b, a, color; GetEntityRenderColor(client, r, g, b, a); @@ -480,7 +495,7 @@ void BuildMapMenu() { delete g_MapMenu; - g_mapTrie.Clear(); + g_MapTrie.Clear(); g_MapMenu = new Menu(MenuHandler_MapSelect, MENU_ACTIONS_DEFAULT|MenuAction_DrawItem|MenuAction_DisplayItem); @@ -489,13 +504,13 @@ void BuildMapMenu() ArrayList excludeMaps; char currentMap[96]; - if (g_Cvar_ExcludeOld.BoolValue) + if (g_ConVars[excludeold].BoolValue) { excludeMaps = new ArrayList(ByteCountToCells(PLATFORM_MAX_PATH)); GetExcludeMapList(excludeMaps); } - if (g_Cvar_ExcludeCurrent.BoolValue) + if (g_ConVars[excludecurrent].BoolValue) { GetCurrentMap(currentMap, sizeof(currentMap)); } @@ -511,7 +526,7 @@ void BuildMapMenu() char displayName[PLATFORM_MAX_PATH]; GetMapDisplayName(map, displayName, sizeof(displayName)); - if (g_Cvar_ExcludeCurrent.BoolValue) + if (g_ConVars[excludecurrent].BoolValue) { if (StrEqual(map, currentMap)) { @@ -520,7 +535,7 @@ void BuildMapMenu() } /* Dont bother with this check if the current map check passed */ - if (g_Cvar_ExcludeOld.BoolValue && status == MAPSTATUS_ENABLED) + if (g_ConVars[excludeold].BoolValue && status == MAPSTATUS_ENABLED) { if (excludeMaps.FindString(map) != -1) { @@ -529,7 +544,7 @@ void BuildMapMenu() } g_MapMenu.AddItem(map, displayName); - g_mapTrie.SetValue(map, status); + g_MapTrie.SetValue(map, status); } g_MapMenu.ExitButton = true; @@ -554,7 +569,7 @@ public int MenuHandler_MapSelect(Menu menu, MenuAction action, int param1, int p menu.GetItem(param2, map, sizeof(map)); int status; - if (!g_mapTrie.GetValue(map, status)) + if (!g_MapTrie.GetValue(map, status)) { LogError("Menu selection of item not in trie. Major logic problem somewhere."); return ITEMDRAW_DEFAULT; @@ -574,7 +589,7 @@ public int MenuHandler_MapSelect(Menu menu, MenuAction action, int param1, int p int status; - if (!g_mapTrie.GetValue(mapname, status)) + if (!g_MapTrie.GetValue(mapname, status)) { LogError("Menu selection of item not in trie. Major logic problem somewhere."); return 0; @@ -689,7 +704,7 @@ public Action NativeVotes_OverrideMaps(StringMap mapList) BuildMapMenu(); } - if (g_mapTrie.Size == 0) + if (g_MapTrie.Size == 0) { LogMessage("No maps loaded."); return Plugin_Continue; @@ -698,7 +713,7 @@ public Action NativeVotes_OverrideMaps(StringMap mapList) // We don't care about the current list, replace it with our own mapList.Clear(); - StringMapSnapshot snapshot = g_mapTrie.Snapshot(); + StringMapSnapshot snapshot = g_MapTrie.Snapshot(); int length = snapshot.Length; for (int i = 0; i < length; i++) @@ -709,7 +724,7 @@ public Action NativeVotes_OverrideMaps(StringMap mapList) int flags; - if (!g_mapTrie.GetValue(map, flags) || flags & MAPSTATUS_DISABLED == MAPSTATUS_DISABLED) + if (!g_MapTrie.GetValue(map, flags) || flags & MAPSTATUS_DISABLED == MAPSTATUS_DISABLED) { continue; } diff --git a/addons/sourcemod/scripting/nativevotes_rockthevote.sp b/addons/sourcemod/scripting/nativevotes_rockthevote.sp index f44d08e..751156c 100644 --- a/addons/sourcemod/scripting/nativevotes_rockthevote.sp +++ b/addons/sourcemod/scripting/nativevotes_rockthevote.sp @@ -50,7 +50,7 @@ #pragma semicolon 1 #pragma newdecls required -#define VERSION "26w02a" +#define VERSION "26w02b" public Plugin myinfo = { @@ -232,7 +232,7 @@ public void OnClientDisconnect(int client) public void OnClientSayCommand_Post(int client, const char[] command, const char[] sArgs) { - if (!client || IsChatTrigger()) + if (client <= 0 || IsChatTrigger()) { return; } @@ -241,7 +241,7 @@ public void OnClientSayCommand_Post(int client, const char[] command, const char { ReplySource old = SetCmdReplySource(SM_REPLY_TO_CHAT); - AttemptRTV(client); + Command_RTV(client, 0); SetCmdReplySource(old); } @@ -249,7 +249,7 @@ public void OnClientSayCommand_Post(int client, const char[] command, const char public Action Command_RTV(int client, int args) { - if (!client) + if (client <= 0) { return Plugin_Handled; } @@ -271,7 +271,6 @@ public Action Command_ForceRTV(int client, int args) return Plugin_Handled; } - void MenuHandler_UndoRTV(Menu menu, MenuAction action, int client, int param2) { if (action == MenuAction_Select) @@ -484,7 +483,7 @@ void RemoveVoteHandler() public Action Menu_RTV(int client, NativeVotesOverride overrideType, const char[] voteArgument) { - if (!client || NativeVotes_IsVoteInProgress()) + if (client <= 0 || NativeVotes_IsVoteInProgress()) { return Plugin_Handled; } @@ -497,6 +496,11 @@ public Action Menu_RTV(int client, NativeVotesOverride overrideType, const char[ ReplySource old = SetCmdReplySource(SM_REPLY_TO_CHAT); + // awful hack, but whatever + char mapname[96]; + strcopy(mapname, sizeof(mapname), voteArgument); + FakeClientCommand(client, "sm_nominate %s", voteArgument); + AttemptRTV(client, true); SetCmdReplySource(old); diff --git a/addons/sourcemod/scripting/nativevotes_voterp.sp b/addons/sourcemod/scripting/nativevotes_voterp.sp index 99f4a6f..c81a895 100644 --- a/addons/sourcemod/scripting/nativevotes_voterp.sp +++ b/addons/sourcemod/scripting/nativevotes_voterp.sp @@ -14,7 +14,7 @@ #pragma semicolon 1 #pragma newdecls required -#define VERSION "26w02a" +#define VERSION "26w02b" public Plugin myinfo = { @@ -47,7 +47,7 @@ public void OnPluginStart() g_ConVars[vote_duration] = CreateConVar("sm_voterp_voteduration", "20", "Specifies how long the rp vote should be available for.", _, true, 5.0); - RegConsoleCmd("sm_voterp", Command_VoteRP, "Vote to enable/disable tf_medieval_autorp"); + RegConsoleCmd("sm_voterp", Command_VoteRP, "Vote to toggle 'tf_medieval_autorp'."); } Action Command_VoteRP(int client, int args) From b0ed687eefc323c1bd3fa63ae2f368208d19ae0d Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sat, 10 Jan 2026 14:31:57 +0100 Subject: [PATCH 020/128] Better support for TF2-based mods --- addons/sourcemod/scripting/nativevotes.sp | 2 +- .../sourcemod/scripting/nativevotes/game.sp | 36 ++- .../scripting/nativevotes_basecommands.sp | 2 +- .../scripting/nativevotes_basevotes.sp | 2 +- .../scripting/nativevotes_funvotes.sp | 2 +- .../nativevotes_kickvote_immunity.sp | 2 +- .../scripting/nativevotes_mapchooser.sp | 275 ++++++++++-------- .../scripting/nativevotes_nominations.sp | 17 +- .../scripting/nativevotes_rockthevote.sp | 2 +- .../sourcemod/scripting/nativevotes_voterp.sp | 2 +- 10 files changed, 188 insertions(+), 154 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes.sp b/addons/sourcemod/scripting/nativevotes.sp index 4949e33..8fae593 100644 --- a/addons/sourcemod/scripting/nativevotes.sp +++ b/addons/sourcemod/scripting/nativevotes.sp @@ -46,7 +46,7 @@ EngineVersion g_EngineVersion = Engine_Unknown; #include "nativevotes/data-keyvalues.sp" -#define VERSION "26w02b" +#define VERSION "26w02c" #define LOGTAG "NV" diff --git a/addons/sourcemod/scripting/nativevotes/game.sp b/addons/sourcemod/scripting/nativevotes/game.sp index 0a064a0..5f4f349 100644 --- a/addons/sourcemod/scripting/nativevotes/game.sp +++ b/addons/sourcemod/scripting/nativevotes/game.sp @@ -229,10 +229,10 @@ enum // User vote to change maps. #define CSGO_VOTE_CHANGELEVEL_START "#SFUI_vote_changelevel" -#define CSGO_VOTE_CHANGELEVEL_PASSED "#SFUI_vote_passed_changelevel" +#define CSGO_VOTE_CHANGELEVEL_PASSED "#SFUI_vote_passed_changelevel" // User vote to change next level. -#define CSGO_VOTE_NEXTLEVEL_SINGLE_START "#SFUI_vote_nextlevel" +#define CSGO_VOTE_NEXTLEVEL_SINGLE_START "#SFUI_vote_nextlevel" #define CSGO_VOTE_NEXTLEVEL_MULTIPLE_START "#SFUI_vote_nextlevel_choices" // Started by server #define CSGO_VOTE_NEXTLEVEL_EXTEND_PASSED "#SFUI_vote_passed_nextlevel_extend" #define CSGO_VOTE_NEXTLEVEL_PASSED "#SFUI_vote_passed_nextlevel" @@ -388,14 +388,26 @@ bool Game_IsGameSupported(char[] engineName="", int maxlength=0) GetGameFolderName(gameDir, sizeof(gameDir)); if (!StrEqual(gameDir, "tf")) { - isTF2SDKModHack = true; - } - - // Fix for TF2 sourcemods - // @note Regarding the 2025 SDK: https://github.com/alliedmodders/metamod-source/commit/dc41559c7905072feec38b6d45ea14c05da3b855 - if (g_EngineVersion == Engine_HL2DM || (g_EngineVersion == Engine_SDK2013 && (StrEqual(gameDir, "open_fortress") || StrEqual(gameDir, "tf2classic") || StrEqual(gameDir, "pf2")))) - { - g_EngineVersion = Engine_TF2; + // Band-aid for old TF2 sourcemods + if (g_EngineVersion == Engine_SDK2013 && (StrEqual(gameDir, "open_fortress") || StrEqual(gameDir, "tf2classic") || StrEqual(gameDir, "pf2"))) + { + isTF2SDKModHack = true; + g_EngineVersion = Engine_TF2; + } + // More robust way to detect TF2 SDK (2025) mods + else + { + KeyValues kv = new KeyValues("GameInfo"); + if (!kv.ImportFromFile("gameinfo.txt")) + { + delete kv; + } + else if (kv.GetNum("DependsOnAppID") == 440) + { + g_EngineVersion = Engine_TF2; + delete kv; + } + } } switch (g_EngineVersion) @@ -847,7 +859,7 @@ void Game_ClientSelectedItem(NativeVote vote, int client, int item) { TF2_ClientSelectedItem(vote, client, item); } -/* + /* case Engine_Left4Dead: { L4D_ClientSelectedItem(vote, client, item); @@ -856,7 +868,7 @@ void Game_ClientSelectedItem(NativeVote vote, int client, int item) { L4D2_ClientSelectedItem(client, item); } -*/ + */ } } diff --git a/addons/sourcemod/scripting/nativevotes_basecommands.sp b/addons/sourcemod/scripting/nativevotes_basecommands.sp index 54debbd..92e7679 100644 --- a/addons/sourcemod/scripting/nativevotes_basecommands.sp +++ b/addons/sourcemod/scripting/nativevotes_basecommands.sp @@ -38,7 +38,7 @@ #undef REQUIRE_PLUGIN #include -#define VERSION "26w02b" +#define VERSION "26w02c" #pragma newdecls required #pragma semicolon 1 diff --git a/addons/sourcemod/scripting/nativevotes_basevotes.sp b/addons/sourcemod/scripting/nativevotes_basevotes.sp index c07c3a6..809ac05 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes.sp @@ -40,7 +40,7 @@ #include #include -#define VERSION "26w02b" +#define VERSION "26w02c" public Plugin:myinfo = { diff --git a/addons/sourcemod/scripting/nativevotes_funvotes.sp b/addons/sourcemod/scripting/nativevotes_funvotes.sp index 3e0c62e..cb85247 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes.sp @@ -42,7 +42,7 @@ #include #include -#define VERSION "26w02b" +#define VERSION "26w02c" public Plugin myinfo = { diff --git a/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp b/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp index ede361b..62c4d66 100644 --- a/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp +++ b/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp @@ -41,7 +41,7 @@ #pragma semicolon 1 #pragma newdecls required -#define VERSION "26w02b" +#define VERSION "26w02c" ConVar g_Cvar_Votes; ConVar g_Cvar_KickVote; diff --git a/addons/sourcemod/scripting/nativevotes_mapchooser.sp b/addons/sourcemod/scripting/nativevotes_mapchooser.sp index c2b4b2f..85ba554 100644 --- a/addons/sourcemod/scripting/nativevotes_mapchooser.sp +++ b/addons/sourcemod/scripting/nativevotes_mapchooser.sp @@ -45,7 +45,7 @@ #pragma semicolon 1 #pragma newdecls required -#define VERSION "26w02b" +#define VERSION "26w02c" public Plugin myinfo = { @@ -56,28 +56,36 @@ public Plugin myinfo = url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; -/* Valve ConVars */ -ConVar g_Cvar_Winlimit; -ConVar g_Cvar_Maxrounds; -ConVar g_Cvar_Fraglimit; -ConVar g_Cvar_Bonusroundtime; - -/* Plugin ConVars */ -ConVar g_Cvar_StartTime; -ConVar g_Cvar_StartRounds; -ConVar g_Cvar_StartFrags; -ConVar g_Cvar_ExtendTimeStep; -ConVar g_Cvar_ExtendRoundStep; -ConVar g_Cvar_ExtendFragStep; -ConVar g_Cvar_ExcludeMaps; -ConVar g_Cvar_IncludeMaps; -ConVar g_Cvar_NoVoteMode; -ConVar g_Cvar_Extend; -ConVar g_Cvar_DontChange; -ConVar g_Cvar_EndOfMapVote; -ConVar g_Cvar_VoteDuration; -ConVar g_Cvar_RunOff; -ConVar g_Cvar_RunOffPercent; +/* ConVars */ +enum +{ + /* Valve ConVars */ + mp_winlimit, + mp_maxrounds, + mp_fraglimit, + mp_bonusroundtime, + + /* Plugin ConVars */ + mapvote_endvote, + mapvote_start, + mapvote_startround, + mapvote_startfrags, + extendmap_timestep, + extendmap_roundstep, + extendmap_fragstep, + mapvote_exclude, + mapvote_include, + mapvote_novote, + mapvote_extend, + mapvote_dontchange, + mapvote_voteduration, + mapvote_runoff, + mapvote_runoffpercent, + + MAX_CONVARS +} + +ConVar g_ConVars[MAX_CONVARS]; Handle g_VoteTimer = null; Handle g_RetryTimer = null; @@ -129,55 +137,79 @@ public void OnPluginStart() g_NominateOwners = new ArrayList(); g_OldMapList = new ArrayList(arraySize); g_NextMapList = new ArrayList(arraySize); + CreateConVar("nativevotes_mapchooser_version", VERSION, "NativeVotes MapChooser version", FCVAR_DONTRECORD|FCVAR_NOTIFY|FCVAR_SPONLY); - g_Cvar_EndOfMapVote = CreateConVar("sm_mapvote_endvote", "1", "Specifies if MapChooser should run an end of map vote", _, true, 0.0, true, 1.0); - - g_Cvar_StartTime = CreateConVar("sm_mapvote_start", "3.0", "Specifies when to start the vote based on time remaining.", _, true, 1.0); - g_Cvar_StartRounds = CreateConVar("sm_mapvote_startround", "2.0", "Specifies when to start the vote based on rounds remaining. Use 0 on TF2 to start vote during bonus round time", _, true, 0.0); - g_Cvar_StartFrags = CreateConVar("sm_mapvote_startfrags", "5.0", "Specifies when to start the vote base on frags remaining.", _, true, 1.0); - g_Cvar_ExtendTimeStep = CreateConVar("sm_extendmap_timestep", "15", "Specifies how much many more minutes each extension makes", _, true, 5.0); - g_Cvar_ExtendRoundStep = CreateConVar("sm_extendmap_roundstep", "5", "Specifies how many more rounds each extension makes", _, true, 1.0); - g_Cvar_ExtendFragStep = CreateConVar("sm_extendmap_fragstep", "10", "Specifies how many more frags are allowed when map is extended.", _, true, 5.0); - g_Cvar_ExcludeMaps = CreateConVar("sm_mapvote_exclude", "5", "Specifies how many past maps to exclude from the vote.", _, true, 0.0); - g_Cvar_IncludeMaps = CreateConVar("sm_mapvote_include", "5", "Specifies how many maps to include in the vote.", _, true, 2.0, true, 6.0); - g_Cvar_NoVoteMode = CreateConVar("sm_mapvote_novote", "1", "Specifies whether or not MapChooser should pick a map if no votes are received.", _, true, 0.0, true, 1.0); - g_Cvar_Extend = CreateConVar("sm_mapvote_extend", "0", "Number of extensions allowed each map.", _, true, 0.0); - g_Cvar_DontChange = CreateConVar("sm_mapvote_dontchange", "1", "Specifies if a 'Don't Change' option should be added to early votes", _, true, 0.0); - g_Cvar_VoteDuration = CreateConVar("sm_mapvote_voteduration", "20", "Specifies how long the mapvote should be available for.", _, true, 5.0); - g_Cvar_RunOff = CreateConVar("sm_mapvote_runoff", "0", "Hold run of votes if winning choice is less than a certain margin", _, true, 0.0, true, 1.0); - g_Cvar_RunOffPercent = CreateConVar("sm_mapvote_runoffpercent", "50", "If winning choice has less than this percent of votes, hold a runoff", _, true, 0.0, true, 100.0); + g_ConVars[mapvote_endvote] = CreateConVar("sm_mapvote_endvote", "1", "Specifies if MapChooser should run an end of map vote", _, true, 0.0, true, 1.0); + g_ConVars[mapvote_start] = CreateConVar("sm_mapvote_start", "3.0", "Specifies when to start the vote based on time remaining.", _, true, 1.0); + g_ConVars[mapvote_startround] = CreateConVar("sm_mapvote_startround", "2.0", "Specifies when to start the vote based on rounds remaining. Use 0 on TF2 to start vote during bonus round time", _, true, 0.0); + g_ConVars[mapvote_startfrags] = CreateConVar("sm_mapvote_startfrags", "5.0", "Specifies when to start the vote base on frags remaining.", _, true, 1.0); + g_ConVars[extendmap_timestep] = CreateConVar("sm_extendmap_timestep", "15", "Specifies how much many more minutes each extension makes", _, true, 5.0); + g_ConVars[extendmap_roundstep] = CreateConVar("sm_extendmap_roundstep", "5", "Specifies how many more rounds each extension makes", _, true, 1.0); + g_ConVars[extendmap_fragstep] = CreateConVar("sm_extendmap_fragstep", "10", "Specifies how many more frags are allowed when map is extended.", _, true, 5.0); + g_ConVars[mapvote_exclude] = CreateConVar("sm_mapvote_exclude", "5", "Specifies how many past maps to exclude from the vote.", _, true, 0.0); + g_ConVars[mapvote_include] = CreateConVar("sm_mapvote_include", "5", "Specifies how many maps to include in the vote.", _, true, 2.0, true, 6.0); + g_ConVars[mapvote_novote] = CreateConVar("sm_mapvote_novote", "1", "Specifies whether or not MapChooser should pick a map if no votes are received.", _, true, 0.0, true, 1.0); + g_ConVars[mapvote_extend] = CreateConVar("sm_mapvote_extend", "0", "Number of extensions allowed each map.", _, true, 0.0); + g_ConVars[mapvote_dontchange] = CreateConVar("sm_mapvote_dontchange", "1", "Specifies if a 'Don't Change' option should be added to early votes", _, true, 0.0); + g_ConVars[mapvote_voteduration] = CreateConVar("sm_mapvote_voteduration", "20", "Specifies how long the mapvote should be available for.", _, true, 5.0); + g_ConVars[mapvote_runoff] = CreateConVar("sm_mapvote_runoff", "0", "Hold run of votes if winning choice is less than a certain margin", _, true, 0.0, true, 1.0); + g_ConVars[mapvote_runoffpercent] = CreateConVar("sm_mapvote_runoffpercent", "50", "If winning choice has less than this percent of votes, hold a runoff", _, true, 0.0, true, 100.0); RegAdminCmd("sm_mapvote", Command_Mapvote, ADMFLAG_CHANGEMAP, "sm_mapvote - Forces MapChooser to attempt to run a map vote now."); RegAdminCmd("sm_setnextmap", Command_SetNextmap, ADMFLAG_CHANGEMAP, "sm_setnextmap "); - g_Cvar_Winlimit = FindConVar("mp_winlimit"); - g_Cvar_Maxrounds = FindConVar("mp_maxrounds"); - g_Cvar_Fraglimit = FindConVar("mp_fraglimit"); - g_Cvar_Bonusroundtime = FindConVar("mp_bonusroundtime"); + g_ConVars[mp_winlimit] = FindConVar("mp_winlimit"); + g_ConVars[mp_maxrounds] = FindConVar("mp_maxrounds"); + g_ConVars[mp_fraglimit] = FindConVar("mp_fraglimit"); + g_ConVars[mp_bonusroundtime] = FindConVar("mp_bonusroundtime"); - if (g_Cvar_Winlimit || g_Cvar_Maxrounds) + if (g_ConVars[mp_winlimit] || g_ConVars[mp_maxrounds]) { - char folder[64]; - GetGameFolderName(folder, sizeof(folder)); - - if (strcmp(folder, "tf") == 0) - { - HookEvent("teamplay_win_panel", Event_TeamPlayWinPanel); - HookEvent("teamplay_restart_round", Event_TFRestartRound); - HookEvent("arena_win_panel", Event_TeamPlayWinPanel); - } - else if (strcmp(folder, "nucleardawn") == 0) + int engine = GetEngineVersion(); + + KeyValues kv = new KeyValues("GameInfo"); + if (!kv.ImportFromFile("gameinfo.txt")) + { + delete kv; + } + else if (kv.GetNum("DependsOnAppID") == 440) { - HookEvent("round_win", Event_RoundEnd); + engine = Engine_TF2; + delete kv; } - else + + switch (engine) { - HookEvent("round_end", Event_RoundEnd); + case Engine_TF2: + { + HookEvent("teamplay_win_panel", Event_TeamplayWinPanel); + HookEvent("teamplay_restart_round", Event_TeamplayRestartRound); + HookEvent("arena_win_panel", Event_TeamplayWinPanel); + } + case Engine_SDK2013: + { + char folder[64]; + GetGameFolderName(folder, sizeof(folder)); + if (strcmp(folder, "tf2classic") == 0 || strcmp(folder, "open_fortress") == 0 || strcmp(folder, "pf2") == 0) + { + HookEvent("teamplay_win_panel", Event_TeamplayWinPanel); + HookEvent("teamplay_restart_round", Event_TeamplayRestartRound); + HookEvent("arena_win_panel", Event_TeamplayWinPanel); + } + } + case Engine_NuclearDawn: + { + HookEvent("round_win", Event_RoundEnd); + } + default: + { + HookEvent("round_end", Event_RoundEnd); + } } } - if (g_Cvar_Fraglimit) + if (g_ConVars[mp_fraglimit]) { HookEvent("player_death", Event_PlayerDeath); } @@ -186,9 +218,9 @@ public void OnPluginStart() //Change the mp_bonusroundtime max so that we have time to display the vote //If you display a vote during bonus time good defaults are 17 vote duration and 19 mp_bonustime - if (g_Cvar_Bonusroundtime) + if (g_ConVars[mp_bonusroundtime]) { - g_Cvar_Bonusroundtime.SetBounds(ConVarBound_Upper, true, 30.0); + g_ConVars[mp_bonusroundtime].SetBounds(ConVarBound_Upper, true, 30.0); } g_NominationsResetForward = CreateGlobalForward("OnNominationRemoved", ET_Ignore, Param_String, Param_Cell); @@ -283,9 +315,9 @@ public void OnConfigsExecuted() /* Check if mapchooser will attempt to start mapvote during bonus round time - TF2 Only */ - if (g_Cvar_Bonusroundtime && !g_Cvar_StartRounds.IntValue) + if (g_ConVars[mp_bonusroundtime] && !g_ConVars[mapvote_startround].IntValue) { - if (g_Cvar_Bonusroundtime.FloatValue <= g_Cvar_VoteDuration.FloatValue) + if (g_ConVars[mp_bonusroundtime].FloatValue <= g_ConVars[mapvote_voteduration].FloatValue) { LogMessage("Warning - Bonus Round Time shorter than Vote Time. Votes during bonus round may not have time to complete"); } @@ -306,7 +338,7 @@ public void OnMapEnd() GetCurrentMap(map, sizeof(map)); g_OldMapList.PushString(map); - while (g_OldMapList.Length > g_Cvar_ExcludeMaps.IntValue) + while (g_OldMapList.Length > g_ConVars[mapvote_exclude].IntValue) { g_OldMapList.Erase(0); } @@ -375,8 +407,8 @@ void SetupTimeleftTimer() int time; if (GetMapTimeLeft(time) && time > 0) { - int startTime = g_Cvar_StartTime.IntValue * 60; - if (time - startTime < 0 && g_Cvar_EndOfMapVote.BoolValue && !g_MapVoteCompleted && !g_HasVoteStarted) + int startTime = g_ConVars[mapvote_start].IntValue * 60; + if (time - startTime < 0 && g_ConVars[mapvote_endvote].BoolValue && !g_MapVoteCompleted && !g_HasVoteStarted) { InitiateVote(MapChange_MapEnd, null); } @@ -410,7 +442,7 @@ public Action Timer_StartMapVote(Handle timer, DataPack data) g_VoteTimer = null; } - if (!g_MapList.Length || !g_Cvar_EndOfMapVote.BoolValue || g_MapVoteCompleted || g_HasVoteStarted) + if (!g_MapList.Length || !g_ConVars[mapvote_endvote].BoolValue || g_MapVoteCompleted || g_HasVoteStarted) { return Plugin_Stop; } @@ -423,13 +455,13 @@ public Action Timer_StartMapVote(Handle timer, DataPack data) return Plugin_Stop; } -public void Event_TFRestartRound(Event event, const char[] name, bool dontBroadcast) +public void Event_TeamplayRestartRound(Event event, const char[] name, bool dontBroadcast) { /* Game got restarted - reset our round count tracking */ g_TotalRounds = 0; } -public void Event_TeamPlayWinPanel(Event event, const char[] name, bool dontBroadcast) +public void Event_TeamplayWinPanel(Event event, const char[] name, bool dontBroadcast) { if (g_ChangeMapAtRoundEnd) { @@ -445,7 +477,7 @@ public void Event_TeamPlayWinPanel(Event event, const char[] name, bool dontBroa { g_TotalRounds++; - if (!g_MapList.Length || g_HasVoteStarted || g_MapVoteCompleted || !g_Cvar_EndOfMapVote.BoolValue) + if (!g_MapList.Length || g_HasVoteStarted || g_MapVoteCompleted || !g_ConVars[mapvote_endvote].BoolValue) { return; } @@ -491,7 +523,7 @@ public void Event_RoundEnd(Event event, const char[] name, bool dontBroadcast) winner = event.GetInt("winner"); } - if (winner == 0 || winner == 1 || !g_Cvar_EndOfMapVote.BoolValue) + if (winner == 0 || winner == 1 || !g_ConVars[mapvote_endvote].BoolValue) { return; } @@ -516,12 +548,12 @@ public void Event_RoundEnd(Event event, const char[] name, bool dontBroadcast) public void CheckWinLimit(int winner_score) { - if (g_Cvar_Winlimit) + if (g_ConVars[mp_winlimit]) { - int winlimit = g_Cvar_Winlimit.IntValue; + int winlimit = g_ConVars[mp_winlimit].IntValue; if (winlimit) { - if (winner_score >= (winlimit - g_Cvar_StartRounds.IntValue)) + if (winner_score >= (winlimit - g_ConVars[mapvote_startround].IntValue)) { InitiateVote(MapChange_MapEnd, null); } @@ -531,12 +563,12 @@ public void CheckWinLimit(int winner_score) public void CheckMaxRounds(int roundcount) { - if (g_Cvar_Maxrounds) + if (g_ConVars[mp_maxrounds]) { - int maxrounds = g_Cvar_Maxrounds.IntValue; + int maxrounds = g_ConVars[mp_maxrounds].IntValue; if (maxrounds) { - if (roundcount >= (maxrounds - g_Cvar_StartRounds.IntValue)) + if (roundcount >= (maxrounds - g_ConVars[mapvote_startround].IntValue)) { InitiateVote(MapChange_MapEnd, null); } @@ -546,12 +578,12 @@ public void CheckMaxRounds(int roundcount) public void Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast) { - if (!g_MapList.Length || !g_Cvar_Fraglimit || g_HasVoteStarted) + if (!g_MapList.Length || !g_ConVars[mp_fraglimit] || g_HasVoteStarted) { return; } - if (!g_Cvar_Fraglimit.IntValue || !g_Cvar_EndOfMapVote.BoolValue) + if (!g_ConVars[mp_fraglimit].IntValue || !g_ConVars[mapvote_endvote].BoolValue) { return; } @@ -568,7 +600,7 @@ public void Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast return; } - if (GetClientFrags(fragger) >= (g_Cvar_Fraglimit.IntValue - g_Cvar_StartFrags.IntValue)) + if (GetClientFrags(fragger) >= (g_ConVars[mp_fraglimit].IntValue - g_ConVars[mapvote_startfrags].IntValue)) { InitiateVote(MapChange_MapEnd, null); } @@ -646,7 +678,7 @@ void InitiateVote(MapChange when, ArrayList inputlist=null) if (inputlist == null) { int nominateCount = g_NominateList.Length; - int voteSize = g_Cvar_IncludeMaps.IntValue; + int voteSize = g_ConVars[mapvote_include].IntValue; // New in 1.5.1 to fix missing extend vote if (g_NativeVotes) @@ -656,7 +688,7 @@ void InitiateVote(MapChange when, ArrayList inputlist=null) voteSize = NativeVotes_GetMaxItems(); } - if (g_Cvar_Extend.IntValue && g_Extends < g_Cvar_Extend.IntValue) + if (g_ConVars[mapvote_extend].IntValue && g_Extends < g_ConVars[mapvote_extend].IntValue) { voteSize--; } @@ -765,7 +797,7 @@ void InitiateVote(MapChange when, ArrayList inputlist=null) } /* Do we add any special items? */ - if ((when == MapChange_Instant || when == MapChange_RoundEnd) && g_Cvar_DontChange.BoolValue) + if ((when == MapChange_Instant || when == MapChange_RoundEnd) && g_ConVars[mapvote_dontchange].BoolValue) { if (g_NativeVotes) { @@ -776,7 +808,7 @@ void InitiateVote(MapChange when, ArrayList inputlist=null) g_VoteMenu.AddItem(VOTE_DONTCHANGE, "Don't Change"); } } - else if (g_Cvar_Extend.BoolValue && g_Extends < g_Cvar_Extend.IntValue) + else if (g_ConVars[mapvote_extend].BoolValue && g_Extends < g_ConVars[mapvote_extend].IntValue) { if (g_NativeVotes) { @@ -804,7 +836,7 @@ void InitiateVote(MapChange when, ArrayList inputlist=null) return; } - int voteDuration = g_Cvar_VoteDuration.IntValue; + int voteDuration = g_ConVars[mapvote_voteduration].IntValue; if (g_NativeVotes) { @@ -873,34 +905,34 @@ public void Handler_VoteFinishedGenericShared(const char[] map, { if (time > 0) { - ExtendMapTimeLimit(g_Cvar_ExtendTimeStep.IntValue * 60); + ExtendMapTimeLimit(g_ConVars[extendmap_timestep].IntValue * 60); } } - if (g_Cvar_Winlimit) + if (g_ConVars[mp_winlimit]) { - int winlimit = g_Cvar_Winlimit.IntValue; + int winlimit = g_ConVars[mp_winlimit].IntValue; if (winlimit) { - g_Cvar_Winlimit.IntValue = winlimit + g_Cvar_ExtendRoundStep.IntValue; + g_ConVars[mp_winlimit].IntValue = winlimit + g_ConVars[extendmap_roundstep].IntValue; } } - if (g_Cvar_Maxrounds) + if (g_ConVars[mp_maxrounds]) { - int maxrounds = g_Cvar_Maxrounds.IntValue; + int maxrounds = g_ConVars[mp_maxrounds].IntValue; if (maxrounds) { - g_Cvar_Maxrounds.IntValue = maxrounds + g_Cvar_ExtendRoundStep.IntValue; + g_ConVars[mp_maxrounds].IntValue = maxrounds + g_ConVars[extendmap_roundstep].IntValue; } } - if (g_Cvar_Fraglimit) + if (g_ConVars[mp_fraglimit]) { - int fraglimit = g_Cvar_Fraglimit.IntValue; + int fraglimit = g_ConVars[mp_fraglimit].IntValue; if (fraglimit) { - g_Cvar_Fraglimit.IntValue = fraglimit + g_Cvar_ExtendFragStep.IntValue; + g_ConVars[mp_fraglimit].IntValue = fraglimit + g_ConVars[extendmap_fragstep].IntValue; } } @@ -964,19 +996,12 @@ public void Handler_VoteFinishedGenericShared(const char[] map, } } -public void Handler_NV_MapVoteFinished(NativeVote menu, - int num_votes, - int num_clients, - const int[] client_indexes, - const int[] client_votes, - int num_items, - const int[] item_indexes, - const int[] item_votes) +public void Handler_NV_MapVoteFinished(NativeVote menu, int num_votes, int num_clients, const int[] client_indexes, const int[] client_votes, int num_items, const int[] item_indexes, const int[] item_votes) { - if (g_Cvar_RunOff.BoolValue && num_items > 1) + if (g_ConVars[mapvote_runoff].BoolValue && num_items > 1) { float winningvotes = float(item_votes[0]); - float required = num_votes * (g_Cvar_RunOffPercent.FloatValue / 100.0); + float required = num_votes * (g_ConVars[mapvote_runoffpercent].FloatValue / 100.0); if (winningvotes < required) { @@ -1009,7 +1034,7 @@ public void Handler_NV_MapVoteFinished(NativeVote menu, float map2percent = float(item_votes[1])/ float(num_votes) * 100; - CPrintToChatAll("[{lightgreen}MapChooser\x01] %t", "Starting Runoff", g_Cvar_RunOffPercent.FloatValue, info1, map1percent, info2, map2percent); + CPrintToChatAll("[{lightgreen}MapChooser\x01] %t", "Starting Runoff", g_ConVars[mapvote_runoffpercent].FloatValue, info1, map1percent, info2, map2percent); LogMessage("Voting for next map was indecisive, beginning runoff vote"); return; @@ -1036,23 +1061,18 @@ public Action Timer_NV_Runoff(Handle timer, DataPack data) data.ReadString(info, sizeof(info)); g_VoteNative.AddItem(map, info); - int voteDuration = g_Cvar_VoteDuration.IntValue; + int voteDuration = g_ConVars[mapvote_voteduration].IntValue; g_VoteNative.DisplayVoteToAll(voteDuration); return Plugin_Continue; } -public void Handler_MapVoteFinished(Menu menu, - int num_votes, - int num_clients, - const int[][] client_info, - int num_items, - const int[][] item_info) +public void Handler_MapVoteFinished(Menu menu, int num_votes, int num_clients, const int[][] client_info, int num_items, const int[][] item_info) { - if (g_Cvar_RunOff.BoolValue && num_items > 1) + if (g_ConVars[mapvote_runoff].BoolValue && num_items > 1) { float winningvotes = float(item_info[0][VOTEINFO_ITEM_VOTES]); - float required = num_votes * (g_Cvar_RunOffPercent.FloatValue / 100.0); + float required = num_votes * (g_ConVars[mapvote_runoffpercent].FloatValue / 100.0); if (winningvotes < required) { @@ -1070,7 +1090,7 @@ public void Handler_MapVoteFinished(Menu menu, menu.GetItem(item_info[1][VOTEINFO_ITEM_INDEX], map, sizeof(map), _, info2, sizeof(info2)); g_VoteMenu.AddItem(map, info2); - int voteDuration = g_Cvar_VoteDuration.IntValue; + int voteDuration = g_ConVars[mapvote_voteduration].IntValue; g_VoteMenu.ExitButton = false; g_VoteMenu.DisplayVoteToAll(voteDuration); @@ -1078,8 +1098,7 @@ public void Handler_MapVoteFinished(Menu menu, float map1percent = float(item_info[0][VOTEINFO_ITEM_VOTES])/ float(num_votes) * 100; float map2percent = float(item_info[1][VOTEINFO_ITEM_VOTES])/ float(num_votes) * 100; - - CPrintToChatAll("[{lightgreen}MapChooser\x01] %t", "Starting Runoff", g_Cvar_RunOffPercent.FloatValue, info1, map1percent, info2, map2percent); + CPrintToChatAll("[{lightgreen}MapChooser\x01] %t", "Starting Runoff", g_ConVars[mapvote_runoffpercent].FloatValue, info1, map1percent, info2, map2percent); LogMessage("Voting for next map was indecisive, beginning runoff vote"); return; @@ -1102,7 +1121,7 @@ public int Handler_MapVoteMenu(Menu menu, MenuAction action, int param1, int par case MenuAction_Display: { char buffer[255]; - Format(buffer, sizeof(buffer), "[{lightgreen}MapChooser\x01] %t", "Vote Nextmap", param1); + Format(buffer, sizeof(buffer), "%t", "Vote Nextmap", param1); Panel panel = view_as(param2); panel.SetTitle(buffer); @@ -1130,7 +1149,7 @@ public int Handler_MapVoteMenu(Menu menu, MenuAction action, int param1, int par case MenuAction_VoteCancel: { // If we receive 0 votes, pick at random. - if (param1 == VoteCancel_NoVotes && g_Cvar_NoVoteMode.BoolValue) + if (param1 == VoteCancel_NoVotes && g_ConVars[mapvote_novote].BoolValue) { int count = menu.ItemCount; char map[96]; @@ -1199,7 +1218,7 @@ public int Handler_NV_MapVoteMenu(NativeVote menu, MenuAction action, int param1 case MenuAction_VoteCancel: { // If we receive 0 votes, pick at random. - if (param1 == VoteCancel_NoVotes && g_Cvar_NoVoteMode.BoolValue) + if (param1 == VoteCancel_NoVotes && g_ConVars[mapvote_novote].BoolValue) { int count = menu.ItemCount; char map[96]; @@ -1302,7 +1321,7 @@ void CreateNextVote() GetCurrentMap(map, sizeof(map)); RemoveStringFromArray(tempMaps, map); - if (g_Cvar_ExcludeMaps.IntValue && tempMaps.Length > g_Cvar_ExcludeMaps.IntValue) + if (g_ConVars[mapvote_exclude].IntValue && tempMaps.Length > g_ConVars[mapvote_exclude].IntValue) { for (int i = 0; i < g_OldMapList.Length; i++) { @@ -1311,7 +1330,7 @@ void CreateNextVote() } } - int limit = (g_Cvar_IncludeMaps.IntValue < tempMaps.Length ? g_Cvar_IncludeMaps.IntValue : tempMaps.Length); + int limit = (g_ConVars[mapvote_include].IntValue < tempMaps.Length ? g_ConVars[mapvote_include].IntValue : tempMaps.Length); for (int i = 0; i < limit; i++) { int b = GetRandomInt(0, tempMaps.Length - 1); @@ -1368,19 +1387,19 @@ NominateResult InternalNominateMap(char[] map, bool force, int owner) { maxIncludes = NativeVotes_GetMaxItems(); - if (g_Cvar_IncludeMaps.IntValue < maxIncludes) + if (g_ConVars[mapvote_include].IntValue < maxIncludes) { - maxIncludes = g_Cvar_IncludeMaps.IntValue; + maxIncludes = g_ConVars[mapvote_include].IntValue; } - if (g_Cvar_Extend.BoolValue && g_Extends < g_Cvar_Extend.IntValue) + if (g_ConVars[mapvote_extend].BoolValue && g_Extends < g_ConVars[mapvote_extend].IntValue) { maxIncludes--; } } else { - maxIncludes = g_Cvar_IncludeMaps.IntValue; + maxIncludes = g_ConVars[mapvote_include].IntValue; } if (g_NominateList.Length >= maxIncludes && !force) @@ -1391,7 +1410,7 @@ NominateResult InternalNominateMap(char[] map, bool force, int owner) g_NominateList.PushString(map); g_NominateOwners.Push(owner); - while (g_NominateList.Length > g_Cvar_IncludeMaps.IntValue) + while (g_NominateList.Length > g_ConVars[mapvote_include].IntValue) { char oldmap[96]; g_NominateList.GetString(0, oldmap, sizeof(oldmap)); @@ -1521,7 +1540,7 @@ public int Native_CheckVoteDone(Handle plugin, int numParams) /* native bool EndOfMapVoteEnabled(); */ public int Native_EndOfMapVoteEnabled(Handle plugin, int numParams) { - return g_Cvar_EndOfMapVote.BoolValue; + return g_ConVars[mapvote_endvote].BoolValue; } /* native void GetExcludeMapList(ArrayList array); */ diff --git a/addons/sourcemod/scripting/nativevotes_nominations.sp b/addons/sourcemod/scripting/nativevotes_nominations.sp index 368905a..5003970 100644 --- a/addons/sourcemod/scripting/nativevotes_nominations.sp +++ b/addons/sourcemod/scripting/nativevotes_nominations.sp @@ -43,7 +43,7 @@ #pragma semicolon 1 #pragma newdecls required -#define VERSION "26w02b" +#define VERSION "26w02c" public Plugin myinfo = { @@ -61,6 +61,8 @@ enum maxmatches, //allow_workshop, + nextlevel_allowed, + MAX_CONVARS } @@ -95,10 +97,12 @@ public void OnPluginStart() CreateConVar("nativevotes_nominations_version", VERSION, "NativeVotes Nominations version", FCVAR_DONTRECORD|FCVAR_NOTIFY|FCVAR_SPONLY); - g_ConVars[excludeold] = CreateConVar("sm_nominate_excludeold", "1", "Specifies if the current map should be excluded from the Nominations list", 0, true, 0.0, true, 1.0); - g_ConVars[excludecurrent] = CreateConVar("sm_nominate_excludecurrent", "1", "Specifies if the MapChooser excluded maps should also be excluded from Nominations", 0, true, 0.0, true, 1.0); - g_ConVars[maxmatches] = CreateConVar("sm_nominate_maxfound", "0", "Maximum number of nomination matches to add to the menu. 0 = infinite.", _, true, 0.0); - //g_ConVars[allow_workshop] = CreateConVar("sm_nominate_allow_workshop", "0", "Specifies if unlisted workshop maps should be allowed to be nominated", 0, true, 0.0, true, 1.0); + g_ConVars[excludeold] = CreateConVar("sm_nominate_excludeold", "1", "Specifies if the current map should be excluded from the Nominations list", 0, true, 0.0, true, 1.0); + g_ConVars[excludecurrent] = CreateConVar("sm_nominate_excludecurrent", "1", "Specifies if the MapChooser excluded maps should also be excluded from Nominations", 0, true, 0.0, true, 1.0); + g_ConVars[maxmatches] = CreateConVar("sm_nominate_maxfound", "0", "Maximum number of nomination matches to add to the menu. 0 = infinite.", _, true, 0.0); + //g_ConVars[allow_workshop] = CreateConVar("sm_nominate_allow_workshop", "0", "Specifies if unlisted workshop maps should be allowed to be nominated", 0, true, 0.0, true, 1.0); + + g_ConVars[nextlevel_allowed] = FindConVar("sv_vote_issue_nextlevel_allowed"); RegConsoleCmd("sm_nominate", Command_Nominate); @@ -280,8 +284,7 @@ public Action Command_Nominate(int client, int args) if (args == 0) { - // https://github.com/ValveSoftware/source-sdk-2013/blob/7191ecc418e28974de8be3a863eebb16b974a7ef/src/game/server/tf/tf_voteissues.h#L116 - if (g_NativeVotes && NativeVotes_AreVoteCommandsSupported()) + if (g_NativeVotes && NativeVotes_AreVoteCommandsSupported() && g_ConVars[nextlevel_allowed].BoolValue) { FakeClientCommand(client, "callvote"); } diff --git a/addons/sourcemod/scripting/nativevotes_rockthevote.sp b/addons/sourcemod/scripting/nativevotes_rockthevote.sp index 751156c..0809d90 100644 --- a/addons/sourcemod/scripting/nativevotes_rockthevote.sp +++ b/addons/sourcemod/scripting/nativevotes_rockthevote.sp @@ -50,7 +50,7 @@ #pragma semicolon 1 #pragma newdecls required -#define VERSION "26w02b" +#define VERSION "26w02c" public Plugin myinfo = { diff --git a/addons/sourcemod/scripting/nativevotes_voterp.sp b/addons/sourcemod/scripting/nativevotes_voterp.sp index c81a895..696422a 100644 --- a/addons/sourcemod/scripting/nativevotes_voterp.sp +++ b/addons/sourcemod/scripting/nativevotes_voterp.sp @@ -14,7 +14,7 @@ #pragma semicolon 1 #pragma newdecls required -#define VERSION "26w02b" +#define VERSION "26w02c" public Plugin myinfo = { From 3df2b1b0cddd168ad79856490c1cd8bd68e8d5b1 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sat, 10 Jan 2026 18:58:01 +0100 Subject: [PATCH 021/128] Add sm_resetrtv --- .../scripting/nativevotes_rockthevote.sp | 61 +++++++++++-------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_rockthevote.sp b/addons/sourcemod/scripting/nativevotes_rockthevote.sp index 0809d90..b1b2b5f 100644 --- a/addons/sourcemod/scripting/nativevotes_rockthevote.sp +++ b/addons/sourcemod/scripting/nativevotes_rockthevote.sp @@ -50,14 +50,12 @@ #pragma semicolon 1 #pragma newdecls required -#define VERSION "26w02c" - public Plugin myinfo = { name = "[NativeVotes] Rock The Vote", author = "AlliedModders LLC and Powerlord", description = "Provides RTV Map Voting", - version = VERSION, + version = "26w02d", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -103,6 +101,7 @@ public void OnPluginStart() RegConsoleCmd("sm_rtv", Command_RTV); RegAdminCmd("sm_forcertv", Command_ForceRTV, ADMFLAG_CHANGEMAP); + RegAdminCmd("sm_resetrtv", Command_ResetRTV, ADMFLAG_CHANGEMAP); AutoExecConfig(true, "rtv"); @@ -271,6 +270,32 @@ public Action Command_ForceRTV(int client, int args) return Plugin_Handled; } +public Action Command_ResetRTV(int client, int args) +{ + ResetRTV(); + + CPrintToChatAll("[{lightgreen}Rock The Vote\x01] %t", "Cancelled Vote"); + + return Plugin_Handled; +} + +void UndoRTV(int client) +{ + char yes[64], no[64]; + Format(yes, sizeof(yes), "%T", "Yes", client); + Format(no, sizeof(no), "%T", "No", client); + + char title[128]; + Format(title, sizeof(title), "%T", "Cancel vote", client); + + Menu menu = new Menu(MenuHandler_UndoRTV); + menu.SetTitle(title); + menu.AddItem("yes", yes); + menu.AddItem("no", no); + menu.ExitButton = false; + menu.Display(client, MENU_TIME_FOREVER); +} + void MenuHandler_UndoRTV(Menu menu, MenuAction action, int client, int param2) { if (action == MenuAction_Select) @@ -282,12 +307,15 @@ void MenuHandler_UndoRTV(Menu menu, MenuAction action, int client, int param2) char name[MAX_NAME_LENGTH]; int r, g, b, a, color; GetEntityRenderColor(client, r, g, b, a); color = (r << 16) | (g << 8) | b; - if (color != 0xFFFFFF) { + if (color != 0xFFFFFF) + { Format(name, sizeof(name), "{#%06X}%N\x01", color, client); } - else { + else + { Format(name, sizeof(name), "{teamcolor}%N\x01", client); } + g_Voted[client] = false; if (g_Votes > 0) g_Votes--; CPrintToChatAllEx(client, "[{lightgreen}Rock The Vote\x01] %s: %t", name, "Cancelled Vote"); @@ -297,23 +325,6 @@ void MenuHandler_UndoRTV(Menu menu, MenuAction action, int client, int param2) delete menu; } -void UndoRTV(int client) -{ - char yes[64], no[64]; - Format(yes, sizeof(yes), "%T", "Yes", client); - Format(no, sizeof(no), "%T", "No", client); - - char title[128]; - Format(title, sizeof(title), "%T", "Cancel vote", client); - - Menu menu = new Menu(MenuHandler_UndoRTV); - menu.SetTitle(title); - menu.AddItem("yes", yes); - menu.AddItem("no", no); - menu.ExitButton = false; - menu.Display(client, MENU_TIME_FOREVER); -} - void AttemptRTV(int client, bool isVoteMenu=false) { if (!g_RTVAllowed || (g_ConVars[postvoteaction].IntValue == 1 && HasEndOfMapVoteFinished())) @@ -366,10 +377,12 @@ void AttemptRTV(int client, bool isVoteMenu=false) char name[MAX_NAME_LENGTH]; int r, g, b, a, color; GetEntityRenderColor(client, r, g, b, a); color = (r << 16) | (g << 8) | b; - if (color != 0xFFFFFF) { + if (color != 0xFFFFFF) + { Format(name, sizeof(name), "{#%06X}%N\x01", color, client); } - else { + else + { Format(name, sizeof(name), "{teamcolor}%N\x01", client); } From 71b8cbc5904b983a00b5408b592257ebba5ce08d Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sat, 10 Jan 2026 18:58:23 +0100 Subject: [PATCH 022/128] Bump up version --- .gitignore | 2 ++ .../scripting/include/nativevotes.inc | 2 -- addons/sourcemod/scripting/nativevotes.sp | 25 +++++++++++++------ .../sourcemod/scripting/nativevotes/game.sp | 4 --- .../scripting/nativevotes_basecommands.sp | 3 +-- .../scripting/nativevotes_basevotes.sp | 6 ++--- .../scripting/nativevotes_funvotes.sp | 6 ++--- .../nativevotes_kickvote_immunity.sp | 5 +--- .../scripting/nativevotes_mapchooser.sp | 8 ++---- .../scripting/nativevotes_nominations.sp | 7 ++---- .../sourcemod/scripting/nativevotes_voterp.sp | 4 +-- 11 files changed, 30 insertions(+), 42 deletions(-) diff --git a/.gitignore b/.gitignore index 38bf2bb..8cf9970 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ *.log *.smx +.* + admin.inc adminmenu.inc adt_array.inc diff --git a/addons/sourcemod/scripting/include/nativevotes.inc b/addons/sourcemod/scripting/include/nativevotes.inc index 4707ba1..b951b3e 100644 --- a/addons/sourcemod/scripting/include/nativevotes.inc +++ b/addons/sourcemod/scripting/include/nativevotes.inc @@ -33,8 +33,6 @@ #include #include -// NativeVotes 1.1 series - #if defined _nativevotes_included #endinput #endif diff --git a/addons/sourcemod/scripting/nativevotes.sp b/addons/sourcemod/scripting/nativevotes.sp index 8fae593..ca72899 100644 --- a/addons/sourcemod/scripting/nativevotes.sp +++ b/addons/sourcemod/scripting/nativevotes.sp @@ -46,8 +46,6 @@ EngineVersion g_EngineVersion = Engine_Unknown; #include "nativevotes/data-keyvalues.sp" -#define VERSION "26w02c" - #define LOGTAG "NV" #define MAX_VOTE_DETAILS_LENGTH 64 // From SourceSDK2013's shareddefs.h @@ -154,7 +152,7 @@ public Plugin myinfo = name = "NativeVotes", author = "Powerlord", description = "Voting API to use the game's native vote panels. Compatible with L4D, L4D2, TF2, and CS:GO.", - version = VERSION, + version = "26w02d", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" } @@ -256,8 +254,6 @@ public void OnPluginStart() LoadTranslations("core.phrases"); LoadTranslations("nativevotes.phrases.txt"); - CreateConVar("nativevotes_version", VERSION, "NativeVotes API version", FCVAR_DONTRECORD | FCVAR_NOTIFY); - g_ConVars[progress_hintbox] = CreateConVar("nativevotes_progress_hintbox", "0", "Show current vote progress in a hint box", FCVAR_NONE, true, 0.0, true, 1.0); g_ConVars[progress_chat] = CreateConVar("nativevotes_progress_chat", "0", "Show current vote progress as chat messages", FCVAR_NONE, true, 0.0, true, 1.0); g_ConVars[progress_console] = CreateConVar("nativevotes_progress_console", "0", "Show current vote progress as console messages", FCVAR_NONE, true, 0.0, true, 1.0); @@ -299,6 +295,7 @@ public void OnMapStart() delete g_MapOverrides; g_OverridesSet = false; + g_NextVote = 0; } public Action Timer_RetryCallVote(Handle timer, any data) @@ -721,6 +718,7 @@ void OnVoteSelect(NativeVote vote, int client, int item) g_hVotes.Set(item, g_hVotes.Get(item) + 1); g_NumVotes++; + Game_UpdateVoteCounts(g_hVotes, g_TotalClients); if (g_ConVars[progress_chat].BoolValue || g_ConVars[progress_console].BoolValue || g_ConVars[progress_client_console].BoolValue) @@ -739,7 +737,7 @@ void OnVoteSelect(NativeVote vote, int client, int item) if (g_ConVars[progress_chat].BoolValue || g_ConVars[progress_client_console].BoolValue) { char phrase[30]; - + if (g_bRevoting[client]) { strcopy(phrase, sizeof(phrase), "Changed Vote"); @@ -751,7 +749,18 @@ void OnVoteSelect(NativeVote vote, int client, int item) if (g_ConVars[progress_chat].BoolValue) { - CPrintToChatAll("[%s] %t", LOGTAG, phrase, name, choice); + int r, g, b, a, color; + GetEntityRenderColor(client, r, g, b, a); + color = (r << 16) | (g << 8) | b; + if (color != 0xFFFFFF) + { + Format(name, sizeof(name), "{#%06X}%N\x01", color, client); + } + else + { + Format(name, sizeof(name), "{teamcolor}%N\x01", client); + } + CPrintToChatAllEx(client, "[%s] %t", LOGTAG, phrase, name, choice); } if (g_ConVars[progress_client_console].BoolValue) @@ -2382,7 +2391,7 @@ public int Native_SetTarget(Handle plugin, int numParams) } int userid; - char steamId[20]; + char steamId[MAX_AUTHID_LENGTH]; if (!client) { diff --git a/addons/sourcemod/scripting/nativevotes/game.sp b/addons/sourcemod/scripting/nativevotes/game.sp index 5f4f349..402b337 100644 --- a/addons/sourcemod/scripting/nativevotes/game.sp +++ b/addons/sourcemod/scripting/nativevotes/game.sp @@ -580,7 +580,6 @@ bool Game_CheckVoteType(NativeVotesType type) { returnVal = CSGO_CheckVoteType(type); } - case Engine_TF2: { returnVal = TF2_CheckVoteType(type); @@ -1714,7 +1713,6 @@ static void L4DL4D2_VotePassToTranslation(NativeVotesPassType passType, char[] t { strcopy(translation, maxlength, L4D_VOTE_CHANGELEVEL_PASSED); } - default: { strcopy(translation, maxlength, L4D_VOTE_CUSTOM); @@ -2155,7 +2153,6 @@ static void TF2CSGO_DisplayVote(NativeVote vote, int[] clients, int num_clients) Data_GetTitle(vote, details, MAX_VOTE_DETAILS_LENGTH); bCustom = true; } - default: { Data_GetDetails(vote, details, MAX_VOTE_DETAILS_LENGTH); @@ -2640,7 +2637,6 @@ static bool TF2_VoteTypeToTranslation(NativeVotesType voteType, char[] translati } case NativeVotesType_NextLevelMult: { - strcopy(translation, maxlength, TF2_VOTE_NEXTLEVEL_MULTIPLE_START); bYesNo = false; } diff --git a/addons/sourcemod/scripting/nativevotes_basecommands.sp b/addons/sourcemod/scripting/nativevotes_basecommands.sp index 92e7679..298d0e9 100644 --- a/addons/sourcemod/scripting/nativevotes_basecommands.sp +++ b/addons/sourcemod/scripting/nativevotes_basecommands.sp @@ -38,7 +38,6 @@ #undef REQUIRE_PLUGIN #include -#define VERSION "26w02c" #pragma newdecls required #pragma semicolon 1 @@ -49,7 +48,7 @@ public Plugin myinfo = name = "[NativeVotes] Basic Commands", author = "Powerlord and AlliedModders LLC", description = "Revote and Cancel support for NativeVotes", - version = VERSION, + version = "26w02d", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" } diff --git a/addons/sourcemod/scripting/nativevotes_basevotes.sp b/addons/sourcemod/scripting/nativevotes_basevotes.sp index 809ac05..67c071e 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes.sp @@ -40,14 +40,13 @@ #include #include -#define VERSION "26w02c" public Plugin:myinfo = { name = "[NativeVotes] Basic Votes", author = "Powerlord and AlliedModders LLC", description = "NativeVotes Basic Vote Commands", - version = VERSION, + version = "26w02d", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -117,8 +116,7 @@ public void OnPluginStart() g_ConVars[map] = CreateConVar("sm_vote_map", "0.60", "percent required for successful map vote.", 0, true, 0.05, true, 1.0); g_ConVars[kick] = CreateConVar("sm_vote_kick", "0.60", "percent required for successful kick vote.", 0, true, 0.05, true, 1.0); g_ConVars[ban] = CreateConVar("sm_vote_ban", "0.60", "percent required for successful ban vote.", 0, true, 0.05, true, 1.0); - CreateConVar("nativevotes_basevotes_version", VERSION, "NativeVotes Basic Votes version", FCVAR_PLUGIN|FCVAR_NOTIFY|FCVAR_DONTRECORD|FCVAR_SPONLY); - + g_SelectedMaps = CreateArray(PLATFORM_MAX_PATH); g_MapList = CreateMenu(MenuHandler_Map, MenuAction_DrawItem|MenuAction_Display); diff --git a/addons/sourcemod/scripting/nativevotes_funvotes.sp b/addons/sourcemod/scripting/nativevotes_funvotes.sp index cb85247..1ea43bd 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes.sp @@ -42,14 +42,13 @@ #include #include -#define VERSION "26w02c" public Plugin myinfo = { name = "[NativeVotes] Fun Votes", author = "Powerlord and AlliedModders LLC", description = "NativeVotes Fun Vote Commands", - version = VERSION, + version = "26w02d", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -130,8 +129,7 @@ public void OnPluginStart() g_ConVars[vote_slay] = CreateConVar("sm_vote_slay", "0.60", "Percent required for successful slay vote.", 0, true, 0.05, true, 1.0); g_ConVars[vote_alltalk] = CreateConVar("sm_vote_alltalk", "0.60", "Percent required for successful alltalk vote.", 0, true, 0.05, true, 1.0); g_ConVars[vote_ff] = CreateConVar("sm_vote_ff", "0.60", "Percent required for successful friendly fire vote.", 0, true, 0.05, true, 1.0); - CreateConVar("nativevotes_funvotes_version", VERSION, "NativeVotes Fun Votes version", FCVAR_PLUGIN|FCVAR_NOTIFY|FCVAR_DONTRECORD|FCVAR_SPONLY); -} + } public void OnAllPluginsLoaded() { diff --git a/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp b/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp index 62c4d66..29c3646 100644 --- a/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp +++ b/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp @@ -41,8 +41,6 @@ #pragma semicolon 1 #pragma newdecls required -#define VERSION "26w02c" - ConVar g_Cvar_Votes; ConVar g_Cvar_KickVote; ConVar g_Cvar_KickVoteMvM; @@ -70,8 +68,7 @@ public void OnPluginStart() HookConVarChange(g_Cvar_KickVoteMvM, Cvar_CheckEnable); LoadTranslations("common.phrases"); - CreateConVar("nativevotes_kickvote_immunity_version", VERSION, "NativeVotes Kickvote Immunity version", FCVAR_NOTIFY|FCVAR_DONTRECORD|FCVAR_SPONLY); -} + } public void OnAllPluginsLoaded() { diff --git a/addons/sourcemod/scripting/nativevotes_mapchooser.sp b/addons/sourcemod/scripting/nativevotes_mapchooser.sp index 85ba554..e1abdf9 100644 --- a/addons/sourcemod/scripting/nativevotes_mapchooser.sp +++ b/addons/sourcemod/scripting/nativevotes_mapchooser.sp @@ -45,14 +45,12 @@ #pragma semicolon 1 #pragma newdecls required -#define VERSION "26w02c" - public Plugin myinfo = { name = "[NativeVotes] MapChooser", author = "AlliedModders LLC and Powerlord", description = "Automated Map Voting", - version = VERSION, + version = "26w02d", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -138,9 +136,7 @@ public void OnPluginStart() g_OldMapList = new ArrayList(arraySize); g_NextMapList = new ArrayList(arraySize); - CreateConVar("nativevotes_mapchooser_version", VERSION, "NativeVotes MapChooser version", FCVAR_DONTRECORD|FCVAR_NOTIFY|FCVAR_SPONLY); - - g_ConVars[mapvote_endvote] = CreateConVar("sm_mapvote_endvote", "1", "Specifies if MapChooser should run an end of map vote", _, true, 0.0, true, 1.0); + g_ConVars[mapvote_endvote] = CreateConVar("sm_mapvote_endvote", "1", "Specifies if MapChooser should run an end of map vote", _, true, 0.0, true, 1.0); g_ConVars[mapvote_start] = CreateConVar("sm_mapvote_start", "3.0", "Specifies when to start the vote based on time remaining.", _, true, 1.0); g_ConVars[mapvote_startround] = CreateConVar("sm_mapvote_startround", "2.0", "Specifies when to start the vote based on rounds remaining. Use 0 on TF2 to start vote during bonus round time", _, true, 0.0); g_ConVars[mapvote_startfrags] = CreateConVar("sm_mapvote_startfrags", "5.0", "Specifies when to start the vote base on frags remaining.", _, true, 1.0); diff --git a/addons/sourcemod/scripting/nativevotes_nominations.sp b/addons/sourcemod/scripting/nativevotes_nominations.sp index 5003970..a868648 100644 --- a/addons/sourcemod/scripting/nativevotes_nominations.sp +++ b/addons/sourcemod/scripting/nativevotes_nominations.sp @@ -43,14 +43,12 @@ #pragma semicolon 1 #pragma newdecls required -#define VERSION "26w02c" - public Plugin myinfo = { name = "[NativeVotes] Map Nominations", author = "AlliedModders LLC and Powerlord", description = "Provides Map Nominations", - version = VERSION, + version = "26w02d", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -95,8 +93,7 @@ public void OnPluginStart() int arraySize = ByteCountToCells(PLATFORM_MAX_PATH); g_MapList = new ArrayList(arraySize); - CreateConVar("nativevotes_nominations_version", VERSION, "NativeVotes Nominations version", FCVAR_DONTRECORD|FCVAR_NOTIFY|FCVAR_SPONLY); - + g_ConVars[excludeold] = CreateConVar("sm_nominate_excludeold", "1", "Specifies if the current map should be excluded from the Nominations list", 0, true, 0.0, true, 1.0); g_ConVars[excludecurrent] = CreateConVar("sm_nominate_excludecurrent", "1", "Specifies if the MapChooser excluded maps should also be excluded from Nominations", 0, true, 0.0, true, 1.0); g_ConVars[maxmatches] = CreateConVar("sm_nominate_maxfound", "0", "Maximum number of nomination matches to add to the menu. 0 = infinite.", _, true, 0.0); diff --git a/addons/sourcemod/scripting/nativevotes_voterp.sp b/addons/sourcemod/scripting/nativevotes_voterp.sp index 696422a..b08d243 100644 --- a/addons/sourcemod/scripting/nativevotes_voterp.sp +++ b/addons/sourcemod/scripting/nativevotes_voterp.sp @@ -14,14 +14,12 @@ #pragma semicolon 1 #pragma newdecls required -#define VERSION "26w02c" - public Plugin myinfo = { name = "[NativeVotes] Medieval Auto-RP", author = "Heapons", description = "Provides Medieval Auto-RP voting.", - version = VERSION, + version = "26w02d", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; From 7212699c26f5dc99e5eb918e23c3492811febac3 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sun, 11 Jan 2026 00:28:10 +0100 Subject: [PATCH 023/128] Chat and versioning fixes --- addons/sourcemod/scripting/nativevotes.sp | 2 +- .../scripting/nativevotes_basecommands.sp | 2 +- .../scripting/nativevotes_basevotes.sp | 25 +++++++++++++------ .../scripting/nativevotes_funvotes.sp | 25 +++++++++++++------ .../nativevotes_kickvote_immunity.sp | 2 +- .../scripting/nativevotes_mapchooser.sp | 2 +- .../scripting/nativevotes_nominations.sp | 8 +++--- .../scripting/nativevotes_rockthevote.sp | 2 +- .../sourcemod/scripting/nativevotes_voterp.sp | 2 +- 9 files changed, 46 insertions(+), 24 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes.sp b/addons/sourcemod/scripting/nativevotes.sp index ca72899..30eb2e4 100644 --- a/addons/sourcemod/scripting/nativevotes.sp +++ b/addons/sourcemod/scripting/nativevotes.sp @@ -152,7 +152,7 @@ public Plugin myinfo = name = "NativeVotes", author = "Powerlord", description = "Voting API to use the game's native vote panels. Compatible with L4D, L4D2, TF2, and CS:GO.", - version = "26w02d", + version = "26w02e", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" } diff --git a/addons/sourcemod/scripting/nativevotes_basecommands.sp b/addons/sourcemod/scripting/nativevotes_basecommands.sp index 298d0e9..1b6ffcd 100644 --- a/addons/sourcemod/scripting/nativevotes_basecommands.sp +++ b/addons/sourcemod/scripting/nativevotes_basecommands.sp @@ -48,7 +48,7 @@ public Plugin myinfo = name = "[NativeVotes] Basic Commands", author = "Powerlord and AlliedModders LLC", description = "Revote and Cancel support for NativeVotes", - version = "26w02d", + version = "26w02e", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" } diff --git a/addons/sourcemod/scripting/nativevotes_basevotes.sp b/addons/sourcemod/scripting/nativevotes_basevotes.sp index 67c071e..aa9dac7 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes.sp @@ -46,7 +46,7 @@ public Plugin:myinfo = name = "[NativeVotes] Basic Votes", author = "Powerlord and AlliedModders LLC", description = "NativeVotes Basic Vote Commands", - version = "26w02d", + version = "26w02e", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -392,7 +392,21 @@ public Handler_VoteCallback(Handle:menu, MenuAction:action, param1, param2) else { CPrintToChatAll("%t", "Vote Successful", RoundToNearest(100.0*percent), totalVotes); - + + int r, g, b, a, color; + GetEntityRenderColor(client, r, g, b, a); + color = (r << 16) | (g << 8) | b; + if (color != 0xFFFFFF) + { + Format(g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[VOTE_NAME]), "{#%06X}%N\x01", color, client); + } + else + { + Format(g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[VOTE_NAME]), "{teamcolor}%N\x01", client); + } + + int client = GetClientOfUserId(g_voteClient[VOTE_USERID]); + switch (g_voteType) { case question: @@ -413,7 +427,6 @@ public Handler_VoteCallback(Handle:menu, MenuAction:action, param1, param2) CPrintToChatAll("%t", "Vote End", g_voteArg, item); } } - case map: { LogAction(-1, -1, "Changing map to %s due to vote.", item); @@ -422,7 +435,6 @@ public Handler_VoteCallback(Handle:menu, MenuAction:action, param1, param2) CreateDataTimer(5.0, Timer_ChangeMap, dp); WritePackString(dp, item); } - case kick: { if (g_voteArg[0] == '\0') @@ -432,14 +444,13 @@ public Handler_VoteCallback(Handle:menu, MenuAction:action, param1, param2) if (GetClientOfUserId(g_voteClient[VOTE_USERID]) > 0 && !IsClientInKickQueue(g_voteClient[VOTE_CLIENTID])) { - CPrintToChatAll("%t", "Kicked target", "_s", g_voteInfo[VOTE_NAME]); + CPrintToChatAllEx(client, "%t", "Kicked target", "_s", g_voteInfo[VOTE_NAME]); LogAction(-1, g_voteClient[VOTE_CLIENTID], "Vote kick successful, kicked \"%L\" (reason \"%s\")", g_voteClient[VOTE_CLIENTID], g_voteArg); KickClient(g_voteClient[VOTE_CLIENTID], "%s", g_voteArg); } } - case ban: { if (g_voteArg[0] == '\0') @@ -449,7 +460,7 @@ public Handler_VoteCallback(Handle:menu, MenuAction:action, param1, param2) if (GetClientOfUserId(g_voteClient[VOTE_USERID]) > 0) { - CPrintToChatAll("%t", "Banned player", g_voteInfo[VOTE_NAME], 30); + CPrintToChatAllEx(client, "%t", "Banned player", g_voteInfo[VOTE_NAME], 30); LogAction(-1, g_voteClient[VOTE_CLIENTID], "Vote ban successful, banned \"%L\" (minutes \"30\") (reason \"%s\")", g_voteClient[VOTE_CLIENTID], g_voteArg); BanClient(g_voteClient[VOTE_CLIENTID], diff --git a/addons/sourcemod/scripting/nativevotes_funvotes.sp b/addons/sourcemod/scripting/nativevotes_funvotes.sp index 1ea43bd..a13e68d 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes.sp @@ -42,13 +42,12 @@ #include #include - public Plugin myinfo = { name = "[NativeVotes] Fun Votes", author = "Powerlord and AlliedModders LLC", description = "NativeVotes Fun Vote Commands", - version = "26w02d", + version = "26w02e", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -322,6 +321,20 @@ public int Handler_VoteCallback(Handle menu, MenuAction action, int param1, int { CPrintToChatAll("%t", "Vote Successful", RoundToNearest(100.0*percent), totalVotes); + int r, g, b, a, color; + GetEntityRenderColor(client, r, g, b, a); + color = (r << 16) | (g << 8) | b; + if (color != 0xFFFFFF) + { + Format(g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[VOTE_NAME]), "{#%06X}%N\x01", color, client); + } + else + { + Format(g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[VOTE_NAME]), "{teamcolor}%N\x01", client); + } + + int client = GetClientOfUserId(g_voteClient[VOTE_USERID]); + switch (g_voteType) { case gravity: @@ -330,31 +343,27 @@ public int Handler_VoteCallback(Handle menu, MenuAction action, int param1, int LogAction(-1, -1, "Changing gravity to %s due to vote.", item); SetConVarInt(g_ConVars[sv_gravity], StringToInt(item)); } - case burn: { - CPrintToChatAll("%t", "Set target on fire", "_s", g_voteInfo[VOTE_NAME]); + CPrintToChatAllEx(client, "%t", "Set target on fire", "_s", g_voteInfo[VOTE_NAME]); LogAction(-1, g_voteClient[VOTE_CLIENTID], "Vote burn successful, igniting \"%L\"", g_voteClient[VOTE_CLIENTID]); IgniteEntity(g_voteClient[VOTE_CLIENTID], 19.8); } - case slay: { - CPrintToChatAll("%t", "Slayed player", g_voteInfo[VOTE_NAME]); + CPrintToChatAllEx(client, "%t", "Slayed player", "_s", g_voteInfo[VOTE_NAME]); LogAction(-1, g_voteClient[VOTE_CLIENTID], "Vote slay successful, slaying \"%L\"", g_voteClient[VOTE_CLIENTID]); ExtinguishEntity(g_voteClient[VOTE_CLIENTID]); ForcePlayerSuicide(g_voteClient[VOTE_CLIENTID]); } - case alltalk: { CPrintToChatAll("%t", "Cvar changed", "sv_alltalk", (GetConVarBool(g_ConVars[sv_alltalk]) ? "0" : "1")); LogAction(-1, -1, "Changing alltalk to %s due to vote.", (GetConVarBool(g_ConVars[sv_alltalk]) ? "0" : "1")); SetConVarBool(g_ConVars[sv_alltalk], !GetConVarBool(g_ConVars[sv_alltalk])); } - case ff: { CPrintToChatAll("%t", "Cvar changed", "mp_friendlyfire", (GetConVarBool(g_ConVars[mp_friendlyfire]) ? "0" : "1")); diff --git a/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp b/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp index 29c3646..9085ca0 100644 --- a/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp +++ b/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp @@ -53,7 +53,7 @@ public Plugin myinfo = { name = "[NativeVotes] Kick Vote Immunity", author = "Powerlord", description = "Causes TF2 kick votes to fail against people who the current user can't target.", - version = VERSION, + version = "25w02e", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_mapchooser.sp b/addons/sourcemod/scripting/nativevotes_mapchooser.sp index e1abdf9..4e574cd 100644 --- a/addons/sourcemod/scripting/nativevotes_mapchooser.sp +++ b/addons/sourcemod/scripting/nativevotes_mapchooser.sp @@ -50,7 +50,7 @@ public Plugin myinfo = name = "[NativeVotes] MapChooser", author = "AlliedModders LLC and Powerlord", description = "Automated Map Voting", - version = "26w02d", + version = "26w02e", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_nominations.sp b/addons/sourcemod/scripting/nativevotes_nominations.sp index a868648..3d343c8 100644 --- a/addons/sourcemod/scripting/nativevotes_nominations.sp +++ b/addons/sourcemod/scripting/nativevotes_nominations.sp @@ -48,7 +48,7 @@ public Plugin myinfo = name = "[NativeVotes] Map Nominations", author = "AlliedModders LLC and Powerlord", description = "Provides Map Nominations", - version = "26w02d", + version = "26w02e", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -469,10 +469,12 @@ void AttemptNominate(int client, const char[] map, int size, bool isVoteMenu) char name[MAX_NAME_LENGTH]; int r, g, b, a, color; GetEntityRenderColor(client, r, g, b, a); color = (r << 16) | (g << 8) | b; - if (color != 0xFFFFFF) { + if (color != 0xFFFFFF) + { Format(name, sizeof(name), "{#%06X}%N\x01", color, client); } - else { + else + { Format(name, sizeof(name), "{teamcolor}%N\x01", client); } diff --git a/addons/sourcemod/scripting/nativevotes_rockthevote.sp b/addons/sourcemod/scripting/nativevotes_rockthevote.sp index b1b2b5f..82590ad 100644 --- a/addons/sourcemod/scripting/nativevotes_rockthevote.sp +++ b/addons/sourcemod/scripting/nativevotes_rockthevote.sp @@ -55,7 +55,7 @@ public Plugin myinfo = name = "[NativeVotes] Rock The Vote", author = "AlliedModders LLC and Powerlord", description = "Provides RTV Map Voting", - version = "26w02d", + version = "26w02e", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_voterp.sp b/addons/sourcemod/scripting/nativevotes_voterp.sp index b08d243..0c79c56 100644 --- a/addons/sourcemod/scripting/nativevotes_voterp.sp +++ b/addons/sourcemod/scripting/nativevotes_voterp.sp @@ -19,7 +19,7 @@ public Plugin myinfo = name = "[NativeVotes] Medieval Auto-RP", author = "Heapons", description = "Provides Medieval Auto-RP voting.", - version = "26w02d", + version = "26w02e", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; From 7056f0a5bcd0ead01226ca318e466ebdc86abc71 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sun, 11 Jan 2026 11:01:05 +0100 Subject: [PATCH 024/128] Fix compile (again) --- addons/sourcemod/scripting/nativevotes.sp | 4 ++-- addons/sourcemod/scripting/nativevotes/game.sp | 13 +++++++++++++ .../sourcemod/scripting/nativevotes_basecommands.sp | 2 +- addons/sourcemod/scripting/nativevotes_basevotes.sp | 5 ++--- .../scripting/nativevotes_basevotes/voteban.sp | 4 ++-- .../scripting/nativevotes_basevotes/votekick.sp | 10 +++++----- addons/sourcemod/scripting/nativevotes_funvotes.sp | 5 ++--- .../scripting/nativevotes_funvotes/voteburn.sp | 4 ++-- .../scripting/nativevotes_funvotes/voteslay.sp | 3 ++- .../sourcemod/scripting/nativevotes_mapchooser.sp | 2 +- .../sourcemod/scripting/nativevotes_nominations.sp | 2 +- .../sourcemod/scripting/nativevotes_rockthevote.sp | 2 +- addons/sourcemod/scripting/nativevotes_voterp.sp | 2 +- 13 files changed, 35 insertions(+), 23 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes.sp b/addons/sourcemod/scripting/nativevotes.sp index 30eb2e4..cc6939b 100644 --- a/addons/sourcemod/scripting/nativevotes.sp +++ b/addons/sourcemod/scripting/nativevotes.sp @@ -152,7 +152,7 @@ public Plugin myinfo = name = "NativeVotes", author = "Powerlord", description = "Voting API to use the game's native vote panels. Compatible with L4D, L4D2, TF2, and CS:GO.", - version = "26w02e", + version = "26w02f", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" } @@ -262,7 +262,7 @@ public void OnPluginStart() g_ConVars[vote_delay].AddChangeHook(OnVoteDelayChange); Game_InitializeCvars(); - + AddCommandListener(Command_Vote, "vote"); // All games, command listeners aren't case sensitive sv_vote_holder_may_vote_no = FindConVar("sv_vote_holder_may_vote_no"); diff --git a/addons/sourcemod/scripting/nativevotes/game.sp b/addons/sourcemod/scripting/nativevotes/game.sp index 402b337..065f1b3 100644 --- a/addons/sourcemod/scripting/nativevotes/game.sp +++ b/addons/sourcemod/scripting/nativevotes/game.sp @@ -2101,6 +2101,17 @@ static void TF2CSGO_UpdateVoteCounts(ArrayList votes) { SetEntProp(g_VoteController, Prop_Send, "m_nVoteOptionCount", votes.Get(i), 4, i); } + + Event changeEvent = CreateEvent("vote_changed"); + for (int i = 0; i < size; i++) + { + char szOption[16]; + Format(szOption, sizeof(szOption), "vote_option%d", i+1); + changeEvent.SetInt(szOption, votes.Get(i)); + } + changeEvent.SetInt("voteidx", s_nNativeVoteIdx); + changeEvent.SetInt("potentialVotes", GetEntProp(g_VoteController, Prop_Send, "m_nPotentialVotes")); + changeEvent.Fire(); } } @@ -2288,6 +2299,7 @@ static void TF2CSGO_DisplayVote(NativeVote vote, int[] clients, int num_clients) protoStart.SetBool("is_yes_no_vote", bYesNo); protoStart.SetString("other_team_str", otherTeamString); protoStart.SetInt("vote_type", voteIndex); + //protoStart.SetInt("target", Data_GetTarget(vote)); } else { @@ -2309,6 +2321,7 @@ static void TF2CSGO_DisplayVote(NativeVote vote, int[] clients, int num_clients) bfStart.WriteString(details); } bfStart.WriteBool(bYesNo); + //bfStart.WriteByte(target); } EndMessage(); diff --git a/addons/sourcemod/scripting/nativevotes_basecommands.sp b/addons/sourcemod/scripting/nativevotes_basecommands.sp index 1b6ffcd..7b17885 100644 --- a/addons/sourcemod/scripting/nativevotes_basecommands.sp +++ b/addons/sourcemod/scripting/nativevotes_basecommands.sp @@ -48,7 +48,7 @@ public Plugin myinfo = name = "[NativeVotes] Basic Commands", author = "Powerlord and AlliedModders LLC", description = "Revote and Cancel support for NativeVotes", - version = "26w02e", + version = "26w02f", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" } diff --git a/addons/sourcemod/scripting/nativevotes_basevotes.sp b/addons/sourcemod/scripting/nativevotes_basevotes.sp index aa9dac7..c3c5805 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes.sp @@ -46,7 +46,7 @@ public Plugin:myinfo = name = "[NativeVotes] Basic Votes", author = "Powerlord and AlliedModders LLC", description = "NativeVotes Basic Vote Commands", - version = "26w02e", + version = "26w02f", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -393,6 +393,7 @@ public Handler_VoteCallback(Handle:menu, MenuAction:action, param1, param2) { CPrintToChatAll("%t", "Vote Successful", RoundToNearest(100.0*percent), totalVotes); + int client = GetClientOfUserId(g_voteClient[VOTE_USERID]); int r, g, b, a, color; GetEntityRenderColor(client, r, g, b, a); color = (r << 16) | (g << 8) | b; @@ -405,8 +406,6 @@ public Handler_VoteCallback(Handle:menu, MenuAction:action, param1, param2) Format(g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[VOTE_NAME]), "{teamcolor}%N\x01", client); } - int client = GetClientOfUserId(g_voteClient[VOTE_USERID]); - switch (g_voteType) { case question: diff --git a/addons/sourcemod/scripting/nativevotes_basevotes/voteban.sp b/addons/sourcemod/scripting/nativevotes_basevotes/voteban.sp index a717ca4..e6aa3fe 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes/voteban.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes/voteban.sp @@ -53,14 +53,14 @@ void DisplayVoteBanMenu(int client, int target) LogAction(client, target, "\"%L\" initiated a ban vote against %s", client, playerName); CShowActivity2(client, "[{lightgreen}NativeVotes\x01] ", "%t", "Initiated Vote Ban", playerName); - g_voteType = voteType:ban; + g_voteType = ban; if (g_NativeVotes) { Handle voteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Custom_YesNo, view_as(MENU_ACTIONS_ALL)); NativeVotes_SetTitle(voteMenu, "Voteban Player"); + NativeVotes_SetTarget(voteMenu, target); // Doesn't work for some reason(?) NativeVotes_DisplayToAll(voteMenu, 20); - NativeVotes_SetTarget(voteMenu, target); } else { diff --git a/addons/sourcemod/scripting/nativevotes_basevotes/votekick.sp b/addons/sourcemod/scripting/nativevotes_basevotes/votekick.sp index f764ffd..6ff6903 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes/votekick.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes/votekick.sp @@ -41,24 +41,24 @@ void DisplayVoteKickMenu(int client, int target) GetEntityRenderColor(target, r, g, b, a); color = (r << 16) | (g << 8) | b; if (color != 0xFFFFFF) { - Format(playerName, sizeof(playerName), "{#%06X}%N\x01", color, target); + Format(playerName, sizeof(playerName), "{#%06X}%N\x01", color, g_voteClient[VOTE_CLIENTID]); } else { - Format(playerName, sizeof(playerName), "{teamcolor}%N\x01", target); + Format(playerName, sizeof(playerName), "{teamcolor}%N\x01", g_voteClient[VOTE_CLIENTID]); } GetClientName(target, g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[])); - LogAction(client, target, "\"%L\" initiated a kick vote against %N", client, target); + LogAction(client, target, "\"%L\" initiated a kick vote against %N", client, g_voteClient[VOTE_CLIENTID]); CShowActivity(client, "%t", "Initiated Vote Kick", playerName); - g_voteType = voteType:kick; + g_voteType = kick; if (g_NativeVotes) { Handle voteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Kick, view_as(MENU_ACTIONS_ALL)); // No title, builtin type - NativeVotes_SetTarget(voteMenu, target); + NativeVotes_SetTarget(voteMenu, target); // Doesn't work for some reason(?) NativeVotes_DisplayToAll(voteMenu, 20); } else diff --git a/addons/sourcemod/scripting/nativevotes_funvotes.sp b/addons/sourcemod/scripting/nativevotes_funvotes.sp index a13e68d..d697932 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes.sp @@ -47,7 +47,7 @@ public Plugin myinfo = name = "[NativeVotes] Fun Votes", author = "Powerlord and AlliedModders LLC", description = "NativeVotes Fun Vote Commands", - version = "26w02e", + version = "26w02f", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -321,6 +321,7 @@ public int Handler_VoteCallback(Handle menu, MenuAction action, int param1, int { CPrintToChatAll("%t", "Vote Successful", RoundToNearest(100.0*percent), totalVotes); + int client = GetClientOfUserId(g_voteClient[VOTE_USERID]); int r, g, b, a, color; GetEntityRenderColor(client, r, g, b, a); color = (r << 16) | (g << 8) | b; @@ -333,8 +334,6 @@ public int Handler_VoteCallback(Handle menu, MenuAction action, int param1, int Format(g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[VOTE_NAME]), "{teamcolor}%N\x01", client); } - int client = GetClientOfUserId(g_voteClient[VOTE_USERID]); - switch (g_voteType) { case gravity: diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp b/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp index 546d463..b24af07 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp @@ -57,13 +57,13 @@ void DisplayVoteBurnMenu(int client, int target, char name[MAX_NAME_LENGTH]) LogAction(client, target, "\"%L\" initiated a burn vote against %s", client, playerName); CShowActivity2(client, "[{lightgreen}NativeVotes\x01] ", "%t", "Initiated Vote Burn", playerName); - g_voteType = voteType:burn; + g_voteType = burn; if (g_NativeVotes) { Handle hVoteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Custom_YesNo, view_as(MENU_ACTIONS_ALL)); NativeVotes_SetTitle(hVoteMenu, "Voteburn player"); - NativeVotes_SetTarget(hVoteMenu, target); + NativeVotes_SetTarget(hVoteMenu, target); // Doesn't work for some reason(?) NativeVotes_DisplayToAll(hVoteMenu, 20); } else diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp b/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp index a844acb..2351d09 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp @@ -57,12 +57,13 @@ void DisplayVoteSlayMenu(int client, int target, char name[MAX_NAME_LENGTH]) LogAction(client, target, "\"%L\" initiated a slay vote against %s", client, playerName); CShowActivity2(client, "[{lightgreen}NativeVotes\x01] ", "%t", "Initiated Vote Slay", playerName); - g_voteType = voteType:slay; + g_voteType = slay; if (g_NativeVotes) { Handle hVoteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Custom_YesNo, view_as(MENU_ACTIONS_ALL)); NativeVotes_SetTitle(hVoteMenu, "Voteslay Player"); + NativeVotes_SetTarget(hVoteMenu, target); // Doesn't work for some reason(?) NativeVotes_DisplayToAll(hVoteMenu, 20); } else diff --git a/addons/sourcemod/scripting/nativevotes_mapchooser.sp b/addons/sourcemod/scripting/nativevotes_mapchooser.sp index 4e574cd..2245cbd 100644 --- a/addons/sourcemod/scripting/nativevotes_mapchooser.sp +++ b/addons/sourcemod/scripting/nativevotes_mapchooser.sp @@ -50,7 +50,7 @@ public Plugin myinfo = name = "[NativeVotes] MapChooser", author = "AlliedModders LLC and Powerlord", description = "Automated Map Voting", - version = "26w02e", + version = "26w02f", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_nominations.sp b/addons/sourcemod/scripting/nativevotes_nominations.sp index 3d343c8..f08cd53 100644 --- a/addons/sourcemod/scripting/nativevotes_nominations.sp +++ b/addons/sourcemod/scripting/nativevotes_nominations.sp @@ -48,7 +48,7 @@ public Plugin myinfo = name = "[NativeVotes] Map Nominations", author = "AlliedModders LLC and Powerlord", description = "Provides Map Nominations", - version = "26w02e", + version = "26w02f", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_rockthevote.sp b/addons/sourcemod/scripting/nativevotes_rockthevote.sp index 82590ad..2a3d5ee 100644 --- a/addons/sourcemod/scripting/nativevotes_rockthevote.sp +++ b/addons/sourcemod/scripting/nativevotes_rockthevote.sp @@ -55,7 +55,7 @@ public Plugin myinfo = name = "[NativeVotes] Rock The Vote", author = "AlliedModders LLC and Powerlord", description = "Provides RTV Map Voting", - version = "26w02e", + version = "26w02f", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_voterp.sp b/addons/sourcemod/scripting/nativevotes_voterp.sp index 0c79c56..0dd3460 100644 --- a/addons/sourcemod/scripting/nativevotes_voterp.sp +++ b/addons/sourcemod/scripting/nativevotes_voterp.sp @@ -19,7 +19,7 @@ public Plugin myinfo = name = "[NativeVotes] Medieval Auto-RP", author = "Heapons", description = "Provides Medieval Auto-RP voting.", - version = "26w02e", + version = "26w02f", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; From f10cb61cb78759cbb3978469b0e39d1374ea66a6 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sun, 11 Jan 2026 18:33:12 +0100 Subject: [PATCH 025/128] Bump version --- addons/sourcemod/scripting/nativevotes.sp | 2 +- .../scripting/nativevotes_basecommands.sp | 2 +- .../sourcemod/scripting/nativevotes_basevotes.sp | 2 +- .../scripting/nativevotes_basevotes/voteban.sp | 8 +++++--- .../scripting/nativevotes_basevotes/votekick.sp | 14 ++++++++------ addons/sourcemod/scripting/nativevotes_funvotes.sp | 2 +- .../scripting/nativevotes_funvotes/voteburn.sp | 8 +++++--- .../scripting/nativevotes_funvotes/voteslay.sp | 10 ++++++---- .../sourcemod/scripting/nativevotes_mapchooser.sp | 2 +- .../sourcemod/scripting/nativevotes_nominations.sp | 2 +- .../sourcemod/scripting/nativevotes_rockthevote.sp | 2 +- addons/sourcemod/scripting/nativevotes_voterp.sp | 2 +- 12 files changed, 32 insertions(+), 24 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes.sp b/addons/sourcemod/scripting/nativevotes.sp index cc6939b..9d79c03 100644 --- a/addons/sourcemod/scripting/nativevotes.sp +++ b/addons/sourcemod/scripting/nativevotes.sp @@ -152,7 +152,7 @@ public Plugin myinfo = name = "NativeVotes", author = "Powerlord", description = "Voting API to use the game's native vote panels. Compatible with L4D, L4D2, TF2, and CS:GO.", - version = "26w02f", + version = "26w02g", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" } diff --git a/addons/sourcemod/scripting/nativevotes_basecommands.sp b/addons/sourcemod/scripting/nativevotes_basecommands.sp index 7b17885..d068809 100644 --- a/addons/sourcemod/scripting/nativevotes_basecommands.sp +++ b/addons/sourcemod/scripting/nativevotes_basecommands.sp @@ -48,7 +48,7 @@ public Plugin myinfo = name = "[NativeVotes] Basic Commands", author = "Powerlord and AlliedModders LLC", description = "Revote and Cancel support for NativeVotes", - version = "26w02f", + version = "26w02g", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" } diff --git a/addons/sourcemod/scripting/nativevotes_basevotes.sp b/addons/sourcemod/scripting/nativevotes_basevotes.sp index c3c5805..649cd91 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes.sp @@ -46,7 +46,7 @@ public Plugin:myinfo = name = "[NativeVotes] Basic Votes", author = "Powerlord and AlliedModders LLC", description = "NativeVotes Basic Vote Commands", - version = "26w02f", + version = "26w02g", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_basevotes/voteban.sp b/addons/sourcemod/scripting/nativevotes_basevotes/voteban.sp index e6aa3fe..e6311b9 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes/voteban.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes/voteban.sp @@ -40,10 +40,12 @@ void DisplayVoteBanMenu(int client, int target) char playerName[MAX_NAME_LENGTH]; int r, g, b, a, color; GetEntityRenderColor(target, r, g, b, a); color = (r << 16) | (g << 8) | b; - if (color != 0xFFFFFF) { + if (color != 0xFFFFFF) + { Format(playerName, sizeof(playerName), "{#%06X}%N\x01", color, target); } - else { + else + { Format(playerName, sizeof(playerName), "{teamcolor}%N\x01", target); } @@ -59,7 +61,7 @@ void DisplayVoteBanMenu(int client, int target) { Handle voteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Custom_YesNo, view_as(MENU_ACTIONS_ALL)); NativeVotes_SetTitle(voteMenu, "Voteban Player"); - NativeVotes_SetTarget(voteMenu, target); // Doesn't work for some reason(?) + NativeVotes_SetTarget(voteMenu, target); NativeVotes_DisplayToAll(voteMenu, 20); } else diff --git a/addons/sourcemod/scripting/nativevotes_basevotes/votekick.sp b/addons/sourcemod/scripting/nativevotes_basevotes/votekick.sp index 6ff6903..d62a1f4 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes/votekick.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes/votekick.sp @@ -40,16 +40,18 @@ void DisplayVoteKickMenu(int client, int target) char playerName[MAX_NAME_LENGTH]; int r, g, b, a, color; GetEntityRenderColor(target, r, g, b, a); color = (r << 16) | (g << 8) | b; - if (color != 0xFFFFFF) { - Format(playerName, sizeof(playerName), "{#%06X}%N\x01", color, g_voteClient[VOTE_CLIENTID]); + if (color != 0xFFFFFF) + { + Format(playerName, sizeof(playerName), "{#%06X}%N\x01", color, target); } - else { - Format(playerName, sizeof(playerName), "{teamcolor}%N\x01", g_voteClient[VOTE_CLIENTID]); + else + { + Format(playerName, sizeof(playerName), "{teamcolor}%N\x01", target); } GetClientName(target, g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[])); - LogAction(client, target, "\"%L\" initiated a kick vote against %N", client, g_voteClient[VOTE_CLIENTID]); + LogAction(client, target, "\"%L\" initiated a kick vote against %N", client, target); CShowActivity(client, "%t", "Initiated Vote Kick", playerName); g_voteType = kick; @@ -58,7 +60,7 @@ void DisplayVoteKickMenu(int client, int target) { Handle voteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Kick, view_as(MENU_ACTIONS_ALL)); // No title, builtin type - NativeVotes_SetTarget(voteMenu, target); // Doesn't work for some reason(?) + NativeVotes_SetTarget(voteMenu, target); NativeVotes_DisplayToAll(voteMenu, 20); } else diff --git a/addons/sourcemod/scripting/nativevotes_funvotes.sp b/addons/sourcemod/scripting/nativevotes_funvotes.sp index d697932..e6550ee 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes.sp @@ -47,7 +47,7 @@ public Plugin myinfo = name = "[NativeVotes] Fun Votes", author = "Powerlord and AlliedModders LLC", description = "NativeVotes Fun Vote Commands", - version = "26w02f", + version = "26w02g", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp b/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp index b24af07..0712bb3 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp @@ -45,10 +45,12 @@ void DisplayVoteBurnMenu(int client, int target, char name[MAX_NAME_LENGTH]) char playerName[MAX_NAME_LENGTH]; int r, g, b, a, color; GetEntityRenderColor(target, r, g, b, a); color = (r << 16) | (g << 8) | b; - if (color != 0xFFFFFF) { + if (color != 0xFFFFFF) + { Format(playerName, sizeof(playerName), "{#%06X}%N\x01", color, target); } - else { + else + { Format(playerName, sizeof(playerName), "{teamcolor}%N\x01", target); } @@ -63,7 +65,7 @@ void DisplayVoteBurnMenu(int client, int target, char name[MAX_NAME_LENGTH]) { Handle hVoteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Custom_YesNo, view_as(MENU_ACTIONS_ALL)); NativeVotes_SetTitle(hVoteMenu, "Voteburn player"); - NativeVotes_SetTarget(hVoteMenu, target); // Doesn't work for some reason(?) + NativeVotes_SetTarget(hVoteMenu, target); NativeVotes_DisplayToAll(hVoteMenu, 20); } else diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp b/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp index 2351d09..2415c7b 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp @@ -45,13 +45,15 @@ void DisplayVoteSlayMenu(int client, int target, char name[MAX_NAME_LENGTH]) char playerName[MAX_NAME_LENGTH]; int r, g, b, a, color; GetEntityRenderColor(target, r, g, b, a); color = (r << 16) | (g << 8) | b; - if (color != 0xFFFFFF) { + if (color != 0xFFFFFF) + { Format(playerName, sizeof(playerName), "{#%06X}%N\x01", color, target); } - else { + else + { Format(playerName, sizeof(playerName), "{teamcolor}%N\x01", target); } - // Use 'playerName' in printed messages, but keep g_voteInfo[VOTE_NAME] for vote panels + GetClientName(target, g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[])); LogAction(client, target, "\"%L\" initiated a slay vote against %s", client, playerName); @@ -63,7 +65,7 @@ void DisplayVoteSlayMenu(int client, int target, char name[MAX_NAME_LENGTH]) { Handle hVoteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Custom_YesNo, view_as(MENU_ACTIONS_ALL)); NativeVotes_SetTitle(hVoteMenu, "Voteslay Player"); - NativeVotes_SetTarget(hVoteMenu, target); // Doesn't work for some reason(?) + NativeVotes_SetTarget(hVoteMenu, target); NativeVotes_DisplayToAll(hVoteMenu, 20); } else diff --git a/addons/sourcemod/scripting/nativevotes_mapchooser.sp b/addons/sourcemod/scripting/nativevotes_mapchooser.sp index 2245cbd..80825a7 100644 --- a/addons/sourcemod/scripting/nativevotes_mapchooser.sp +++ b/addons/sourcemod/scripting/nativevotes_mapchooser.sp @@ -50,7 +50,7 @@ public Plugin myinfo = name = "[NativeVotes] MapChooser", author = "AlliedModders LLC and Powerlord", description = "Automated Map Voting", - version = "26w02f", + version = "26w02g", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_nominations.sp b/addons/sourcemod/scripting/nativevotes_nominations.sp index f08cd53..134815f 100644 --- a/addons/sourcemod/scripting/nativevotes_nominations.sp +++ b/addons/sourcemod/scripting/nativevotes_nominations.sp @@ -48,7 +48,7 @@ public Plugin myinfo = name = "[NativeVotes] Map Nominations", author = "AlliedModders LLC and Powerlord", description = "Provides Map Nominations", - version = "26w02f", + version = "26w02g", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_rockthevote.sp b/addons/sourcemod/scripting/nativevotes_rockthevote.sp index 2a3d5ee..b7021c4 100644 --- a/addons/sourcemod/scripting/nativevotes_rockthevote.sp +++ b/addons/sourcemod/scripting/nativevotes_rockthevote.sp @@ -55,7 +55,7 @@ public Plugin myinfo = name = "[NativeVotes] Rock The Vote", author = "AlliedModders LLC and Powerlord", description = "Provides RTV Map Voting", - version = "26w02f", + version = "26w02g", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_voterp.sp b/addons/sourcemod/scripting/nativevotes_voterp.sp index 0dd3460..bad0a5c 100644 --- a/addons/sourcemod/scripting/nativevotes_voterp.sp +++ b/addons/sourcemod/scripting/nativevotes_voterp.sp @@ -19,7 +19,7 @@ public Plugin myinfo = name = "[NativeVotes] Medieval Auto-RP", author = "Heapons", description = "Provides Medieval Auto-RP voting.", - version = "26w02f", + version = "26w02g", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; From 4c96a65fc09fa6d1858baa2e40490ef93cc5358a Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sun, 11 Jan 2026 18:35:24 +0100 Subject: [PATCH 026/128] Fix player targets - If you set NativeVotes_SetTarget(), the player avatar will be shown. --- addons/sourcemod/scripting/nativevotes/game.sp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes/game.sp b/addons/sourcemod/scripting/nativevotes/game.sp index 065f1b3..b01db34 100644 --- a/addons/sourcemod/scripting/nativevotes/game.sp +++ b/addons/sourcemod/scripting/nativevotes/game.sp @@ -2299,7 +2299,6 @@ static void TF2CSGO_DisplayVote(NativeVote vote, int[] clients, int num_clients) protoStart.SetBool("is_yes_no_vote", bYesNo); protoStart.SetString("other_team_str", otherTeamString); protoStart.SetInt("vote_type", voteIndex); - //protoStart.SetInt("target", Data_GetTarget(vote)); } else { @@ -2309,7 +2308,6 @@ static void TF2CSGO_DisplayVote(NativeVote vote, int[] clients, int num_clients) { bfStart.WriteNum(s_nNativeVoteIdx); } - bfStart.WriteByte(Data_GetInitiator(vote)); bfStart.WriteString(translation); if (bCustom && changeTitle == Plugin_Changed) @@ -2320,8 +2318,11 @@ static void TF2CSGO_DisplayVote(NativeVote vote, int[] clients, int num_clients) { bfStart.WriteString(details); } - bfStart.WriteBool(bYesNo); - //bfStart.WriteByte(target); + bfStart.WriteByte(bYesNo); + if (!isTF2SDKModHack) + { + bfStart.WriteByte(Data_GetTarget(vote) - 1); + } } EndMessage(); From 441bf950e86a38b53667975f3c65e840a0a054a9 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sun, 11 Jan 2026 20:07:21 +0100 Subject: [PATCH 027/128] MVM Fix --- addons/sourcemod/scripting/nativevotes/game.sp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes/game.sp b/addons/sourcemod/scripting/nativevotes/game.sp index b01db34..7f56a43 100644 --- a/addons/sourcemod/scripting/nativevotes/game.sp +++ b/addons/sourcemod/scripting/nativevotes/game.sp @@ -2254,7 +2254,7 @@ static void TF2CSGO_DisplayVote(NativeVote vote, int[] clients, int num_clients) int holder = vote.Initiator; // required to allow the initiator to vote on their own issue // ValveSoftware/Source-1-Games#3934 - if (sv_vote_holder_may_vote_no && holder <= MaxClients && IsClientConnected(holder)) + if (sv_vote_holder_may_vote_no && holder <= MaxClients && IsClientConnected(holder)) && !IsFakeClient(holder) { sv_vote_holder_may_vote_no.ReplicateToClient(holder, "1"); } @@ -2319,7 +2319,7 @@ static void TF2CSGO_DisplayVote(NativeVote vote, int[] clients, int num_clients) bfStart.WriteString(details); } bfStart.WriteByte(bYesNo); - if (!isTF2SDKModHack) + if (!isTF2SDKModHack && !IsFakeClient(clients[i])) { bfStart.WriteByte(Data_GetTarget(vote) - 1); } From 7cf448fc67e41822194823374853c6a10c9b953c Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sun, 11 Jan 2026 20:08:40 +0100 Subject: [PATCH 028/128] Fix brackets https://github.com/sapphonie/sourcemod-nativevotes-updated/issues/45 --- addons/sourcemod/scripting/nativevotes/game.sp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/sourcemod/scripting/nativevotes/game.sp b/addons/sourcemod/scripting/nativevotes/game.sp index 7f56a43..c943fcc 100644 --- a/addons/sourcemod/scripting/nativevotes/game.sp +++ b/addons/sourcemod/scripting/nativevotes/game.sp @@ -2254,7 +2254,7 @@ static void TF2CSGO_DisplayVote(NativeVote vote, int[] clients, int num_clients) int holder = vote.Initiator; // required to allow the initiator to vote on their own issue // ValveSoftware/Source-1-Games#3934 - if (sv_vote_holder_may_vote_no && holder <= MaxClients && IsClientConnected(holder)) && !IsFakeClient(holder) + if (sv_vote_holder_may_vote_no && holder <= MaxClients && IsClientConnected(holder) && !IsFakeClient(holder)) { sv_vote_holder_may_vote_no.ReplicateToClient(holder, "1"); } From fcdfea3a14b96e2e7cf6bfc4a2713c8b4f12bf8d Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sun, 11 Jan 2026 22:08:58 +0100 Subject: [PATCH 029/128] Bump version --- addons/sourcemod/scripting/nativevotes.sp | 2 +- addons/sourcemod/scripting/nativevotes_basecommands.sp | 2 +- addons/sourcemod/scripting/nativevotes_basevotes.sp | 2 +- addons/sourcemod/scripting/nativevotes_funvotes.sp | 2 +- addons/sourcemod/scripting/nativevotes_nominations.sp | 2 +- addons/sourcemod/scripting/nativevotes_rockthevote.sp | 2 +- addons/sourcemod/scripting/nativevotes_voterp.sp | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes.sp b/addons/sourcemod/scripting/nativevotes.sp index 9d79c03..aa41e47 100644 --- a/addons/sourcemod/scripting/nativevotes.sp +++ b/addons/sourcemod/scripting/nativevotes.sp @@ -152,7 +152,7 @@ public Plugin myinfo = name = "NativeVotes", author = "Powerlord", description = "Voting API to use the game's native vote panels. Compatible with L4D, L4D2, TF2, and CS:GO.", - version = "26w02g", + version = "26w02h", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" } diff --git a/addons/sourcemod/scripting/nativevotes_basecommands.sp b/addons/sourcemod/scripting/nativevotes_basecommands.sp index d068809..c0bb16c 100644 --- a/addons/sourcemod/scripting/nativevotes_basecommands.sp +++ b/addons/sourcemod/scripting/nativevotes_basecommands.sp @@ -48,7 +48,7 @@ public Plugin myinfo = name = "[NativeVotes] Basic Commands", author = "Powerlord and AlliedModders LLC", description = "Revote and Cancel support for NativeVotes", - version = "26w02g", + version = "26w02h", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" } diff --git a/addons/sourcemod/scripting/nativevotes_basevotes.sp b/addons/sourcemod/scripting/nativevotes_basevotes.sp index 649cd91..077bc34 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes.sp @@ -46,7 +46,7 @@ public Plugin:myinfo = name = "[NativeVotes] Basic Votes", author = "Powerlord and AlliedModders LLC", description = "NativeVotes Basic Vote Commands", - version = "26w02g", + version = "26w02h", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_funvotes.sp b/addons/sourcemod/scripting/nativevotes_funvotes.sp index e6550ee..a5c8a60 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes.sp @@ -47,7 +47,7 @@ public Plugin myinfo = name = "[NativeVotes] Fun Votes", author = "Powerlord and AlliedModders LLC", description = "NativeVotes Fun Vote Commands", - version = "26w02g", + version = "26w02h", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_nominations.sp b/addons/sourcemod/scripting/nativevotes_nominations.sp index 134815f..8fd8173 100644 --- a/addons/sourcemod/scripting/nativevotes_nominations.sp +++ b/addons/sourcemod/scripting/nativevotes_nominations.sp @@ -48,7 +48,7 @@ public Plugin myinfo = name = "[NativeVotes] Map Nominations", author = "AlliedModders LLC and Powerlord", description = "Provides Map Nominations", - version = "26w02g", + version = "26w02h", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_rockthevote.sp b/addons/sourcemod/scripting/nativevotes_rockthevote.sp index b7021c4..969a656 100644 --- a/addons/sourcemod/scripting/nativevotes_rockthevote.sp +++ b/addons/sourcemod/scripting/nativevotes_rockthevote.sp @@ -55,7 +55,7 @@ public Plugin myinfo = name = "[NativeVotes] Rock The Vote", author = "AlliedModders LLC and Powerlord", description = "Provides RTV Map Voting", - version = "26w02g", + version = "26w02h", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_voterp.sp b/addons/sourcemod/scripting/nativevotes_voterp.sp index bad0a5c..94c6f0a 100644 --- a/addons/sourcemod/scripting/nativevotes_voterp.sp +++ b/addons/sourcemod/scripting/nativevotes_voterp.sp @@ -19,7 +19,7 @@ public Plugin myinfo = name = "[NativeVotes] Medieval Auto-RP", author = "Heapons", description = "Provides Medieval Auto-RP voting.", - version = "26w02g", + version = "26w02h", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; From b78da3b9cbe0ace382512345fb487a287269f7f5 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sun, 11 Jan 2026 22:11:09 +0100 Subject: [PATCH 030/128] Don't use color tags needlessly --- .../scripting/nativevotes_mapchooser.sp | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_mapchooser.sp b/addons/sourcemod/scripting/nativevotes_mapchooser.sp index 80825a7..6d4f671 100644 --- a/addons/sourcemod/scripting/nativevotes_mapchooser.sp +++ b/addons/sourcemod/scripting/nativevotes_mapchooser.sp @@ -50,7 +50,7 @@ public Plugin myinfo = name = "[NativeVotes] MapChooser", author = "AlliedModders LLC and Powerlord", description = "Automated Map Voting", - version = "26w02g", + version = "26w02h", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -136,7 +136,7 @@ public void OnPluginStart() g_OldMapList = new ArrayList(arraySize); g_NextMapList = new ArrayList(arraySize); - g_ConVars[mapvote_endvote] = CreateConVar("sm_mapvote_endvote", "1", "Specifies if MapChooser should run an end of map vote", _, true, 0.0, true, 1.0); + g_ConVars[mapvote_endvote] = CreateConVar("sm_mapvote_endvote", "1", "Specifies if MapChooser should run an end of map vote", _, true, 0.0, true, 1.0); g_ConVars[mapvote_start] = CreateConVar("sm_mapvote_start", "3.0", "Specifies when to start the vote based on time remaining.", _, true, 1.0); g_ConVars[mapvote_startround] = CreateConVar("sm_mapvote_startround", "2.0", "Specifies when to start the vote based on rounds remaining. Use 0 on TF2 to start vote during bonus round time", _, true, 0.0); g_ConVars[mapvote_startfrags] = CreateConVar("sm_mapvote_startfrags", "5.0", "Specifies when to start the vote base on frags remaining.", _, true, 1.0); @@ -364,12 +364,12 @@ public Action Command_SetNextmap(int client, int args) { if (args < 1) { - CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] Usage: sm_setnextmap "); + CReplyToCommand(client, "[{lightgreen}MapChooser\x01] Usage: sm_setnextmap "); return Plugin_Handled; } char map[96]; - char displayName[PLATFORM_MAX_PATH]; + char displayName[64]; GetCmdArg(1, map, sizeof(map)); if (FindMap(map, displayName, sizeof(displayName)) == FindMap_NotFound) @@ -381,7 +381,7 @@ public Action Command_SetNextmap(int client, int args) GetMapDisplayName(displayName, displayName, sizeof(displayName)); Format(displayName, sizeof(displayName), "\x05%s\x01", displayName); - CShowActivity(client, "[{lightgreen}MapChooser\x01] %t", "Changed Next Map", displayName); + CShowActivity(client, "%t", "Changed Next Map", displayName); LogAction(client, -1, "\"%L\" changed nextmap to \"%s\"", client, map); SetNextMap(map); @@ -695,10 +695,9 @@ void InitiateVote(MapChange when, ArrayList inputlist=null) for (int i=0; i Date: Wed, 14 Jan 2026 12:44:07 +0100 Subject: [PATCH 031/128] Fix MVM error on older TF2 mods --- .../sourcemod/scripting/nativevotes/game.sp | 323 +++++++++--------- 1 file changed, 165 insertions(+), 158 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes/game.sp b/addons/sourcemod/scripting/nativevotes/game.sp index c943fcc..65fb8b6 100644 --- a/addons/sourcemod/scripting/nativevotes/game.sp +++ b/addons/sourcemod/scripting/nativevotes/game.sp @@ -1605,19 +1605,19 @@ static void L4DL4D2_ClientSelectedItem(int client, int item) static void L4DL4D2_UpdateVoteCounts(ArrayList votes, int totalClients) { - int yesVotes = votes.Get(NATIVEVOTES_VOTE_YES); - int noVotes = votes.Get(NATIVEVOTES_VOTE_NO); - Event changeEvent = CreateEvent("vote_changed"); - changeEvent.SetInt("yesVotes", yesVotes); - changeEvent.SetInt("noVotes", noVotes); - changeEvent.SetInt("potentialVotes", totalClients); - changeEvent.Fire(); - - if (CheckVoteController()) - { - SetEntProp(g_VoteController, Prop_Send, "m_votesYes", yesVotes); - SetEntProp(g_VoteController, Prop_Send, "m_votesNo", noVotes); - } + int yesVotes = votes.Get(NATIVEVOTES_VOTE_YES); + int noVotes = votes.Get(NATIVEVOTES_VOTE_NO); + Event changeEvent = CreateEvent("vote_changed"); + changeEvent.SetInt("yesVotes", yesVotes); + changeEvent.SetInt("noVotes", noVotes); + changeEvent.SetInt("potentialVotes", totalClients); + changeEvent.Fire(); + + if (CheckVoteController()) + { + SetEntProp(g_VoteController, Prop_Send, "m_votesYes", yesVotes); + SetEntProp(g_VoteController, Prop_Send, "m_votesNo", noVotes); + } } static stock void L4DL4D2_UpdateClientCount(int num_clients) @@ -1985,9 +1985,15 @@ static bool L4D2_CheckVoteType(NativeVotesType voteType) { switch(voteType) { - case NativeVotesType_Custom_YesNo, NativeVotesType_ChgCampaign, NativeVotesType_ChgDifficulty, - NativeVotesType_ReturnToLobby, NativeVotesType_AlltalkOn, NativeVotesType_AlltalkOff, - NativeVotesType_Restart, NativeVotesType_Kick, NativeVotesType_ChgLevel: + case NativeVotesType_Custom_YesNo, + NativeVotesType_ChgCampaign, + NativeVotesType_ChgDifficulty, + NativeVotesType_ReturnToLobby, + NativeVotesType_AlltalkOn, + NativeVotesType_AlltalkOff, + NativeVotesType_Restart, + NativeVotesType_Kick, + NativeVotesType_ChgLevel: { return true; } @@ -2000,9 +2006,13 @@ static bool L4D2_CheckVotePassType(NativeVotesPassType passType) { switch(passType) { - case NativeVotesPass_Custom, NativeVotesPass_ChgCampaign, NativeVotesPass_ChgDifficulty, - NativeVotesPass_ReturnToLobby, NativeVotesPass_AlltalkOn, NativeVotesPass_AlltalkOff, - NativeVotesPass_Restart, NativeVotesPass_Kick, NativeVotesPass_ChgLevel: + case NativeVotesPass_Custom, + NativeVotesPass_ChgCampaign, + NativeVotesPass_ChgDifficulty, + NativeVotesPass_ReturnToLobby, + NativeVotesPass_Restart, + NativeVotesPass_Kick, + NativeVotesPass_ChgLevel: { return true; } @@ -2097,17 +2107,14 @@ static void TF2CSGO_UpdateVoteCounts(ArrayList votes) if (CheckVoteController()) { int size = votes.Length; + Event changeEvent = CreateEvent("vote_changed"); for (int i = 0; i < size; i++) { SetEntProp(g_VoteController, Prop_Send, "m_nVoteOptionCount", votes.Get(i), 4, i); - } - Event changeEvent = CreateEvent("vote_changed"); - for (int i = 0; i < size; i++) - { - char szOption[16]; - Format(szOption, sizeof(szOption), "vote_option%d", i+1); - changeEvent.SetInt(szOption, votes.Get(i)); + char szVoteOption[13]; + Format(szVoteOption, sizeof(szVoteOption), "vote_option%d", i+1); + changeEvent.SetInt(szVoteOption, votes.Get(i)); } changeEvent.SetInt("voteidx", s_nNativeVoteIdx); changeEvent.SetInt("potentialVotes", GetEntProp(g_VoteController, Prop_Send, "m_nPotentialVotes")); @@ -2166,42 +2173,42 @@ static void TF2CSGO_DisplayVote(NativeVote vote, int[] clients, int num_clients) } default: { - Data_GetDetails(vote, details, MAX_VOTE_DETAILS_LENGTH); - } - } + Data_GetDetails(vote, details, MAX_VOTE_DETAILS_LENGTH); + } + } - switch(g_EngineVersion) - { - case Engine_CSGO: - { - bYesNo = CSGO_VoteTypeToTranslation(voteType, translation, sizeof(translation)); - CSGO_VoteTypeToVoteOtherTeamString(voteType, otherTeamString, sizeof(otherTeamString)); - } - case Engine_TF2: - { - bYesNo = TF2_VoteTypeToTranslation(voteType, translation, sizeof(translation)); - } - } + switch(g_EngineVersion) + { + case Engine_CSGO: + { + bYesNo = CSGO_VoteTypeToTranslation(voteType, translation, sizeof(translation)); + CSGO_VoteTypeToVoteOtherTeamString(voteType, otherTeamString, sizeof(otherTeamString)); + } + case Engine_TF2: + { + bYesNo = TF2_VoteTypeToTranslation(voteType, translation, sizeof(translation)); + } + } - int team = Data_GetTeam(vote); + int team = Data_GetTeam(vote); - // Moved to mimic SourceSDK2013's server/vote_controller.cpp - if (CheckVoteController()) - { - SetEntProp(g_VoteController, Prop_Send, "m_bIsYesNoVote", bYesNo); + // Moved to mimic SourceSDK2013's server/vote_controller.cpp + if (CheckVoteController()) + { + SetEntProp(g_VoteController, Prop_Send, "m_bIsYesNoVote", bYesNo); - // CSGO gets very cranky if you try setting this - if (g_EngineVersion == Engine_TF2) - SetEntProp(g_VoteController, Prop_Send, "m_iActiveIssueIndex", voteIndex); + // CSGO gets very cranky if you try setting this + if (g_EngineVersion == Engine_TF2) + SetEntProp(g_VoteController, Prop_Send, "m_iActiveIssueIndex", voteIndex); - SetEntProp(g_VoteController, Prop_Send, "m_iOnlyTeamToVote", team); - for (int i = 0; i < 5; i++) - { - SetEntProp(g_VoteController, Prop_Send, "m_nVoteOptionCount", 0, _, i); - } - - if (!isTF2SDKModHack) - { + SetEntProp(g_VoteController, Prop_Send, "m_iOnlyTeamToVote", team); + for (int i = 0; i < 5; i++) + { + SetEntProp(g_VoteController, Prop_Send, "m_nVoteOptionCount", 0, _, i); + } + + if (!isTF2SDKModHack) + { // TODO(UPDATE): M-M-M-MULTIVOTE // we need unique vote indices; HUD elements for previous votes aren't cleaned up (?) // the game implements this as `this->m_nVoteIdx = s_nVoteIdx++` @@ -2211,124 +2218,124 @@ static void TF2CSGO_DisplayVote(NativeVote vote, int[] clients, int num_clients) PrintToServer("Starting vote index: %d (controller: %d)", s_nNativeVoteIdx, GetEntProp(g_VoteController, Prop_Send, "m_nVoteIdx")); #endif SetEntProp(g_VoteController, Prop_Send, "m_nVoteIdx", s_nNativeVoteIdx + 1); // TODO(UPDATE) - } - } + } + } // According to Source SDK 2013, vote_options is only sent for a multiple choice vote. // As of 2015-09-28, vote_options is sent for all votes in TF2 despite Yes/No being // translated in the UI itself. // As of 2016-07-10, we send this only when we're doing Yes/No votes as we handle // multiple choice votes separately later. - if (bYesNo) - { - int itemCount = Data_GetItemCount(vote); + if (bYesNo) + { + int itemCount = Data_GetItemCount(vote); - Event optionsEvent = CreateEvent("vote_options"); + Event optionsEvent = CreateEvent("vote_options"); - for (int i = 0; i < itemCount; i++) - { - char option[8]; - Format(option, sizeof(option), "%s%d", TF2CSGO_VOTE_PREFIX, i+1); - char display[TRANSLATION_LENGTH]; - Data_GetItemDisplay(vote, i, display, sizeof(display)); + for (int i = 0; i < itemCount; i++) + { + char option[8]; + Format(option, sizeof(option), "%s%d", TF2CSGO_VOTE_PREFIX, i+1); + char display[TRANSLATION_LENGTH]; + Data_GetItemDisplay(vote, i, display, sizeof(display)); - optionsEvent.SetString(option, display); - } - optionsEvent.SetInt("count", itemCount); - if (!isTF2SDKModHack) - { - optionsEvent.SetInt("voteidx", s_nNativeVoteIdx); // TODO(UPDATE) - } - optionsEvent.Fire(); - } + optionsEvent.SetString(option, display); + } + optionsEvent.SetInt("count", itemCount); + if (!isTF2SDKModHack) + { + optionsEvent.SetInt("voteidx", s_nNativeVoteIdx); // TODO(UPDATE) + } + optionsEvent.Fire(); + } - // Moved to mimic SourceSDK2013's server/vote_controller.cpp - // For whatever reason, while the other props are set first, this one's set after the vote_options event - if (CheckVoteController()) - { - SetEntProp(g_VoteController, Prop_Send, "m_nPotentialVotes", num_clients); - } + // Moved to mimic SourceSDK2013's server/vote_controller.cpp + // For whatever reason, while the other props are set first, this one's set after the vote_options event + if (CheckVoteController()) + { + SetEntProp(g_VoteController, Prop_Send, "m_nPotentialVotes", num_clients); + } - if (!isTF2SDKModHack) - { - int holder = vote.Initiator; - // required to allow the initiator to vote on their own issue - // ValveSoftware/Source-1-Games#3934 - if (sv_vote_holder_may_vote_no && holder <= MaxClients && IsClientConnected(holder) && !IsFakeClient(holder)) - { - sv_vote_holder_may_vote_no.ReplicateToClient(holder, "1"); - } - } + if (!isTF2SDKModHack) + { + int holder = vote.Initiator; + // required to allow the initiator to vote on their own issue + // ValveSoftware/Source-1-Games#3934 + if (sv_vote_holder_may_vote_no && holder <= MaxClients && IsClientConnected(holder) && !IsFakeClient(holder)) + { + sv_vote_holder_may_vote_no.ReplicateToClient(holder, "1"); + } + } - MenuAction actions = Data_GetActions(vote); + MenuAction actions = Data_GetActions(vote); - for (int i = 0; i < num_clients; ++i) - { - g_newMenuTitle[0] = '\0'; + for (int i = 0; i < num_clients; ++i) + { + g_newMenuTitle[0] = '\0'; - Action changeTitle = Plugin_Continue; - if (bCustom && actions & MenuAction_Display) - { - g_curDisplayClient = clients[i]; - changeTitle = DoAction(vote, MenuAction_Display, clients[i], 0); - } + Action changeTitle = Plugin_Continue; + if (bCustom && actions & MenuAction_Display) + { + g_curDisplayClient = clients[i]; + changeTitle = DoAction(vote, MenuAction_Display, clients[i], 0); + } - g_curDisplayClient = 0; + g_curDisplayClient = 0; - if (!bYesNo) - { - TF2CSGO_SendOptionsToClient(vote, clients[i]); - } + if (!bYesNo) + { + TF2CSGO_SendOptionsToClient(vote, clients[i]); + } - Handle voteStart = StartMessageOne("VoteStart", clients[i], USERMSG_RELIABLE); + Handle voteStart = StartMessageOne("VoteStart", clients[i], USERMSG_RELIABLE); - if(g_bUserBuf) - { - Protobuf protoStart = UserMessageToProtobuf(voteStart); - protoStart.SetInt("team", team); - protoStart.SetInt("ent_idx", Data_GetInitiator(vote)); - protoStart.SetString("disp_str", translation); - if (bCustom && changeTitle == Plugin_Changed) - { - protoStart.SetString("details_str", g_newMenuTitle); - } - else - { - protoStart.SetString("details_str", details); - } - protoStart.SetBool("is_yes_no_vote", bYesNo); - protoStart.SetString("other_team_str", otherTeamString); - protoStart.SetInt("vote_type", voteIndex); - } - else - { - BfWrite bfStart = UserMessageToBfWrite(voteStart); - bfStart.WriteByte(team); - if (!isTF2SDKModHack) - { - bfStart.WriteNum(s_nNativeVoteIdx); - } - bfStart.WriteByte(Data_GetInitiator(vote)); - bfStart.WriteString(translation); - if (bCustom && changeTitle == Plugin_Changed) - { - bfStart.WriteString(g_newMenuTitle); - } - else - { - bfStart.WriteString(details); - } - bfStart.WriteByte(bYesNo); - if (!isTF2SDKModHack && !IsFakeClient(clients[i])) - { - bfStart.WriteByte(Data_GetTarget(vote) - 1); - } - } + if(g_bUserBuf) + { + Protobuf protoStart = UserMessageToProtobuf(voteStart); + protoStart.SetInt("team", team); + protoStart.SetInt("ent_idx", Data_GetInitiator(vote)); + protoStart.SetString("disp_str", translation); + if (bCustom && changeTitle == Plugin_Changed) + { + protoStart.SetString("details_str", g_newMenuTitle); + } + else + { + protoStart.SetString("details_str", details); + } + protoStart.SetBool("is_yes_no_vote", bYesNo); + protoStart.SetString("other_team_str", otherTeamString); + protoStart.SetInt("vote_type", voteIndex); + } + else + { + BfWrite bfStart = UserMessageToBfWrite(voteStart); + bfStart.WriteByte(team); + if (!isTF2SDKModHack) + { + bfStart.WriteNum(s_nNativeVoteIdx); + } + bfStart.WriteByte(Data_GetInitiator(vote)); + bfStart.WriteString(translation); + if (bCustom && changeTitle == Plugin_Changed) + { + bfStart.WriteString(g_newMenuTitle); + } + else + { + bfStart.WriteString(details); + } + bfStart.WriteByte(bYesNo); + if (!isTF2SDKModHack && !IsFakeClient(clients[i])) + { + bfStart.WriteByte(Data_GetTarget(vote) - 1); + } + } - EndMessage(); - } + EndMessage(); + } - g_curDisplayClient = 0; + g_curDisplayClient = 0; } static void TF2CSGO_SendOptionsToClient(NativeVote vote, int client) @@ -2532,7 +2539,7 @@ static void TF2_DisplayVoteSetup(int client, ArrayList hVoteTypes) static void TF2CSGO_ResetVote() { if (CheckVoteController()) - { + { if (g_EngineVersion == Engine_TF2) { SetEntProp(g_VoteController, Prop_Send, "m_iActiveIssueIndex", INVALID_ISSUE); @@ -2545,7 +2552,7 @@ static void TF2CSGO_ResetVote() for (int i = 0; i < 5; i++) { SetEntProp(g_VoteController, Prop_Send, "m_nVoteOptionCount", 0, _, i); - } + } SetEntProp(g_VoteController, Prop_Send, "m_nPotentialVotes", 0); if (g_EngineVersion == Engine_TF2) { @@ -2777,7 +2784,7 @@ static void TF2_AddDefaultVotes(ArrayList hVoteTypes, bool bHideDisabledVotes) return; } - if (GameRules_GetProp("m_bPlayingMannVsMachine")) + if (g_EngineVersion == Engine_TF2 && GameRules_GetProp("m_bPlayingMannVsMachine")) { // Default MvM vote types From 8c5f49c646db07c3f645db80cf769b602cc99b25 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Wed, 14 Jan 2026 12:54:30 +0100 Subject: [PATCH 032/128] Bump version --- README.md | 35 +++++--- addons/sourcemod/scripting/nativevotes.sp | 4 +- .../scripting/nativevotes_basecommands.sp | 4 +- .../scripting/nativevotes_basevotes.sp | 19 ++++- .../nativevotes_basevotes/voteban.sp | 13 +-- .../nativevotes_basevotes/votekick.sp | 13 +-- .../scripting/nativevotes_funvotes.sp | 37 ++++---- .../nativevotes_funvotes/voteburn.sp | 15 +--- .../nativevotes_funvotes/voteslay.sp | 15 +--- .../nativevotes_kickvote_immunity.sp | 6 +- .../scripting/nativevotes_mapchooser.sp | 6 +- .../scripting/nativevotes_nominations.sp | 79 ++++++++--------- .../scripting/nativevotes_rockthevote.sp | 85 +++++++++---------- .../sourcemod/scripting/nativevotes_voterp.sp | 14 +-- 14 files changed, 164 insertions(+), 181 deletions(-) diff --git a/README.md b/README.md index 1a004b5..735cea5 100644 --- a/README.md +++ b/README.md @@ -5,20 +5,31 @@ This fork aims to expand upon [ **Team Fortress 2**](https://store.steampowered.com/app/440)‼ If it doesn't work in any other game, open an [issue](https://github.com/Heapons/sourcemod-nativevotes-updated/issues/new). -# To-Do (Wishlist...?) +# [Differences](https://github.com/sapphonie/sourcemod-nativevotes-updated/compare/master...Heapons:sourcemod-nativevotes-updated:master) ## General -- [x] Include [NativeVotes BaseVotes and FunVotes](https://github.com/powerlord/sourcemod-nativevotes-basevotes) in this repository. -- [ ] Chat tweaks. - - [x] Team-colored player names. - - [x] Highlight map names. - - [ ] Parse workshop map titles. -- [x] Update [Nominations](https://github.com/Heapons/sourcemod-nativevotes-updated/blob/master/addons/sourcemod/scripting/nativevotes_nominations.sp) and [Rock The Vote](https://github.com/Heapons/sourcemod-nativevotes-updated/blob/master/addons/sourcemod/scripting/nativevotes_rockthevote.sp) to match with the latest [Sourcemod](https://github.com/alliedmodders/sourcemod/tree/master/plugins) version. -- [x] Add [`sm_voterp`](https://github.com/Heapons/sourcemod-nativevotes-updated/blob/master/addons/sourcemod/scripting/nativevotes_voterp.sp). +- Include [NativeVotes BaseVotes and FunVotes](https://github.com/powerlord/sourcemod-nativevotes-basevotes) in this repository. + - Show player avatar on vote panel (if the game supports it). + - Applies to: [voteban](https://github.com/Heapons/sourcemod-nativevotes-updated/blob/master/addons/sourcemod/scripting/nativevotes_basevotes/voteban.sp), [votekick](https://github.com/Heapons/sourcemod-nativevotes-updated/blob/master/addons/sourcemod/scripting/nativevotes_basevotes/votekick.sp), [voteburn](https://github.com/Heapons/sourcemod-nativevotes-updated/blob/master/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp), [voteslay](https://github.com/Heapons/sourcemod-nativevotes-updated/blob/master/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp). +- Chat tweaks. + - Team-colored player names. + - Highlight map names. +- Update [Nominations](https://github.com/Heapons/sourcemod-nativevotes-updated/blob/master/addons/sourcemod/scripting/nativevotes_nominations.sp) and [Rock The Vote](https://github.com/Heapons/sourcemod-nativevotes-updated/blob/master/addons/sourcemod/scripting/nativevotes_rockthevote.sp) to be on par with the latest [Sourcemod](https://github.com/alliedmodders/sourcemod/tree/master/plugins) version. + +### Team Fortress 2 +- Fixed ✔️/❌ vote counts. +- Add [`sm_voterp`](https://github.com/Heapons/sourcemod-nativevotes-updated/blob/master/addons/sourcemod/scripting/nativevotes_voterp.sp). - Controls `tf_medieval_autorp` cvar. + ## [Rock The Vote](https://github.com/Heapons/sourcemod-nativevotes-updated/blob/master/addons/sourcemod/scripting/nativevotes_rockthevote.sp) -- [x] Add `sm_forcertv` for admins. -- [x] Allow players to retract their rock-the-vote. +- Admin commands. + - `sm_forcertv`. + - Force a RTV. + - `sm_resetrtv`. + - Reset RTV counts. +- Allow players to retract their rock-the-vote. - Execute `sm_rtv` again to undo. + ## [Nominations](https://github.com/Heapons/sourcemod-nativevotes-updated/blob/master/addons/sourcemod/scripting/nativevotes_nominations.sp) -- [ ] Download and nominate workshop maps. - - Inspired by [[TF2] Workshop Vote](https://forums.alliedmods.net/showthread.php?p=2717878). +### Team Fortress 2 +- Attempting to nominate with no argument will open **Vote Setup** (`callvote`). +- Support partial map name matches. \ No newline at end of file diff --git a/addons/sourcemod/scripting/nativevotes.sp b/addons/sourcemod/scripting/nativevotes.sp index aa41e47..9aaeef2 100644 --- a/addons/sourcemod/scripting/nativevotes.sp +++ b/addons/sourcemod/scripting/nativevotes.sp @@ -152,7 +152,7 @@ public Plugin myinfo = name = "NativeVotes", author = "Powerlord", description = "Voting API to use the game's native vote panels. Compatible with L4D, L4D2, TF2, and CS:GO.", - version = "26w02h", + version = "26w03a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" } @@ -1014,7 +1014,7 @@ bool:SendResultCallback(Handle:vote, num_votes, num_items, const votes[][]) void DrawHintProgress() { - if (!g_ConVars[progress_hintbox].BoolValue) + if (!g_ConVars[progress_hintbox].BoolValue || NativeVotes_GetType(g_hCurVote) == NativeVotesType_Custom_YesNo) { return; } diff --git a/addons/sourcemod/scripting/nativevotes_basecommands.sp b/addons/sourcemod/scripting/nativevotes_basecommands.sp index c0bb16c..1b9cf2c 100644 --- a/addons/sourcemod/scripting/nativevotes_basecommands.sp +++ b/addons/sourcemod/scripting/nativevotes_basecommands.sp @@ -45,10 +45,10 @@ TopMenu hTopMenu; public Plugin myinfo = { - name = "[NativeVotes] Basic Commands", + name = "NativeVotes | Basic Commands", author = "Powerlord and AlliedModders LLC", description = "Revote and Cancel support for NativeVotes", - version = "26w02h", + version = "26w03a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" } diff --git a/addons/sourcemod/scripting/nativevotes_basevotes.sp b/addons/sourcemod/scripting/nativevotes_basevotes.sp index 077bc34..bd7c05e 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes.sp @@ -43,10 +43,10 @@ public Plugin:myinfo = { - name = "[NativeVotes] Basic Votes", + name = "NativeVotes | Basic Votes", author = "Powerlord and AlliedModders LLC", description = "NativeVotes Basic Vote Commands", - version = "26w02h", + version = "26w03a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -729,3 +729,18 @@ bool Internal_IsNewVoteAllowed() } return IsNewVoteAllowed(); } + +void GetPlayerName(int client, char[] name, int maxlen) +{ + int r, g, b, a, color; + GetEntityRenderColor(client, r, g, b, a); + color = (r << 16) | (g << 8) | b; + if (color != 0xFFFFFF) + { + Format(name, maxlen, "{#%06X}%N\x01", color, client); + } + else + { + Format(name, maxlen, "{teamcolor}%N\x01", client); + } +} \ No newline at end of file diff --git a/addons/sourcemod/scripting/nativevotes_basevotes/voteban.sp b/addons/sourcemod/scripting/nativevotes_basevotes/voteban.sp index e6311b9..b5451ec 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes/voteban.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes/voteban.sp @@ -37,17 +37,8 @@ void DisplayVoteBanMenu(int client, int target) g_voteClient[VOTE_CLIENTID] = target; g_voteClient[VOTE_USERID] = GetClientUserId(target); - char playerName[MAX_NAME_LENGTH]; int r, g, b, a, color; - GetEntityRenderColor(target, r, g, b, a); - color = (r << 16) | (g << 8) | b; - if (color != 0xFFFFFF) - { - Format(playerName, sizeof(playerName), "{#%06X}%N\x01", color, target); - } - else - { - Format(playerName, sizeof(playerName), "{teamcolor}%N\x01", target); - } + char playerName[MAX_NAME_LENGTH]; + GetPlayerName(target, playerName, sizeof(playerName)); GetClientName(target, g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[])); GetClientIP(target, g_voteInfo[VOTE_IP], sizeof(g_voteInfo[])); diff --git a/addons/sourcemod/scripting/nativevotes_basevotes/votekick.sp b/addons/sourcemod/scripting/nativevotes_basevotes/votekick.sp index d62a1f4..cf7b210 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes/votekick.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes/votekick.sp @@ -37,17 +37,8 @@ void DisplayVoteKickMenu(int client, int target) g_voteClient[VOTE_CLIENTID] = target; g_voteClient[VOTE_USERID] = GetClientUserId(target); - char playerName[MAX_NAME_LENGTH]; int r, g, b, a, color; - GetEntityRenderColor(target, r, g, b, a); - color = (r << 16) | (g << 8) | b; - if (color != 0xFFFFFF) - { - Format(playerName, sizeof(playerName), "{#%06X}%N\x01", color, target); - } - else - { - Format(playerName, sizeof(playerName), "{teamcolor}%N\x01", target); - } + char playerName[MAX_NAME_LENGTH]; + GetPlayerName(target, playerName, sizeof(playerName)); GetClientName(target, g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[])); diff --git a/addons/sourcemod/scripting/nativevotes_funvotes.sp b/addons/sourcemod/scripting/nativevotes_funvotes.sp index a5c8a60..006f049 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes.sp @@ -44,10 +44,10 @@ public Plugin myinfo = { - name = "[NativeVotes] Fun Votes", + name = "NativeVotes | Fun Votes", author = "Powerlord and AlliedModders LLC", description = "NativeVotes Fun Vote Commands", - version = "26w02h", + version = "26w03a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -321,18 +321,8 @@ public int Handler_VoteCallback(Handle menu, MenuAction action, int param1, int { CPrintToChatAll("%t", "Vote Successful", RoundToNearest(100.0*percent), totalVotes); - int client = GetClientOfUserId(g_voteClient[VOTE_USERID]); - int r, g, b, a, color; - GetEntityRenderColor(client, r, g, b, a); - color = (r << 16) | (g << 8) | b; - if (color != 0xFFFFFF) - { - Format(g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[VOTE_NAME]), "{#%06X}%N\x01", color, client); - } - else - { - Format(g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[VOTE_NAME]), "{teamcolor}%N\x01", client); - } + char name[MAX_NAME_LENGTH]; + GetPlayerName(g_voteClient[VOTE_CLIENTID], name, sizeof(name)); switch (g_voteType) { @@ -344,14 +334,14 @@ public int Handler_VoteCallback(Handle menu, MenuAction action, int param1, int } case burn: { - CPrintToChatAllEx(client, "%t", "Set target on fire", "_s", g_voteInfo[VOTE_NAME]); + CPrintToChatAllEx(g_voteClient[VOTE_CLIENTID], "%t", "Set target on fire", "_s", name); LogAction(-1, g_voteClient[VOTE_CLIENTID], "Vote burn successful, igniting \"%L\"", g_voteClient[VOTE_CLIENTID]); IgniteEntity(g_voteClient[VOTE_CLIENTID], 19.8); } case slay: { - CPrintToChatAllEx(client, "%t", "Slayed player", "_s", g_voteInfo[VOTE_NAME]); + CPrintToChatAllEx(g_voteClient[VOTE_CLIENTID], "%t", "Slayed player", "_s", name); LogAction(-1, g_voteClient[VOTE_CLIENTID], "Vote slay successful, slaying \"%L\"", g_voteClient[VOTE_CLIENTID]); ExtinguishEntity(g_voteClient[VOTE_CLIENTID]); @@ -560,3 +550,18 @@ bool Internal_IsNewVoteAllowed() return IsNewVoteAllowed(); } + +void GetPlayerName(int client, char[] name, int maxlen) +{ + int r, g, b, a, color; + GetEntityRenderColor(client, r, g, b, a); + color = (r << 16) | (g << 8) | b; + if (color != 0xFFFFFF) + { + Format(name, maxlen, "{#%06X}%N\x01", color, client); + } + else + { + Format(name, maxlen, "{teamcolor}%N\x01", client); + } +} \ No newline at end of file diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp b/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp index 0712bb3..6b9fdbb 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp @@ -42,21 +42,12 @@ void DisplayVoteBurnMenu(int client, int target, char name[MAX_NAME_LENGTH]) g_voteClient[VOTE_CLIENTID] = target; - char playerName[MAX_NAME_LENGTH]; int r, g, b, a, color; - GetEntityRenderColor(target, r, g, b, a); - color = (r << 16) | (g << 8) | b; - if (color != 0xFFFFFF) - { - Format(playerName, sizeof(playerName), "{#%06X}%N\x01", color, target); - } - else - { - Format(playerName, sizeof(playerName), "{teamcolor}%N\x01", target); - } + char playerName[MAX_NAME_LENGTH]; + GetPlayerName(target, playerName, sizeof(playerName)); GetClientName(target, g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[])); - LogAction(client, target, "\"%L\" initiated a burn vote against %s", client, playerName); + LogAction(client, target, "\"%L\" initiated a burn vote against %N", client, target); CShowActivity2(client, "[{lightgreen}NativeVotes\x01] ", "%t", "Initiated Vote Burn", playerName); g_voteType = burn; diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp b/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp index 2415c7b..3af1e51 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp @@ -42,21 +42,12 @@ void DisplayVoteSlayMenu(int client, int target, char name[MAX_NAME_LENGTH]) g_voteClient[VOTE_CLIENTID] = target; - char playerName[MAX_NAME_LENGTH]; int r, g, b, a, color; - GetEntityRenderColor(target, r, g, b, a); - color = (r << 16) | (g << 8) | b; - if (color != 0xFFFFFF) - { - Format(playerName, sizeof(playerName), "{#%06X}%N\x01", color, target); - } - else - { - Format(playerName, sizeof(playerName), "{teamcolor}%N\x01", target); - } + char playerName[MAX_NAME_LENGTH]; + GetPlayerName(target, playerName, sizeof(playerName)); GetClientName(target, g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[])); - LogAction(client, target, "\"%L\" initiated a slay vote against %s", client, playerName); + LogAction(client, target, "\"%L\" initiated a slay vote against %N", client, target); CShowActivity2(client, "[{lightgreen}NativeVotes\x01] ", "%t", "Initiated Vote Slay", playerName); g_voteType = slay; diff --git a/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp b/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp index 9085ca0..1f01691 100644 --- a/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp +++ b/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp @@ -50,11 +50,11 @@ bool g_bRegistered = false; bool g_bMapActive = false; public Plugin myinfo = { - name = "[NativeVotes] Kick Vote Immunity", + name = "NativeVotes | Kick Vote Immunity", author = "Powerlord", description = "Causes TF2 kick votes to fail against people who the current user can't target.", - version = "25w02e", - url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" + version = "26w03a", + url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; public void OnPluginStart() diff --git a/addons/sourcemod/scripting/nativevotes_mapchooser.sp b/addons/sourcemod/scripting/nativevotes_mapchooser.sp index 6d4f671..bed1291 100644 --- a/addons/sourcemod/scripting/nativevotes_mapchooser.sp +++ b/addons/sourcemod/scripting/nativevotes_mapchooser.sp @@ -47,10 +47,10 @@ public Plugin myinfo = { - name = "[NativeVotes] MapChooser", + name = "NativeVotes | MapChooser", author = "AlliedModders LLC and Powerlord", description = "Automated Map Voting", - version = "26w02h", + version = "26w03a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -1216,7 +1216,7 @@ public int Handler_NV_MapVoteMenu(NativeVote menu, MenuAction action, int param1 { int count = menu.ItemCount; char map[96]; - char displayName[PLATFORM_MAX_PATH]; + char displayName[64]; menu.GetItem(0, map, sizeof(map)); // Make sure the first map in the menu isn't one of the special items. diff --git a/addons/sourcemod/scripting/nativevotes_nominations.sp b/addons/sourcemod/scripting/nativevotes_nominations.sp index 8fd8173..804c18f 100644 --- a/addons/sourcemod/scripting/nativevotes_nominations.sp +++ b/addons/sourcemod/scripting/nativevotes_nominations.sp @@ -45,10 +45,10 @@ public Plugin myinfo = { - name = "[NativeVotes] Map Nominations", + name = "NativeVotes | Map Nominations", author = "AlliedModders LLC and Powerlord", description = "Provides Map Nominations", - version = "26w02h", + version = "26w03a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -57,7 +57,7 @@ enum excludeold, excludecurrent, maxmatches, - //allow_workshop, + allow_workshop, nextlevel_allowed, @@ -92,12 +92,11 @@ public void OnPluginStart() int arraySize = ByteCountToCells(PLATFORM_MAX_PATH); g_MapList = new ArrayList(arraySize); - - + g_ConVars[excludeold] = CreateConVar("sm_nominate_excludeold", "1", "Specifies if the current map should be excluded from the Nominations list", 0, true, 0.0, true, 1.0); g_ConVars[excludecurrent] = CreateConVar("sm_nominate_excludecurrent", "1", "Specifies if the MapChooser excluded maps should also be excluded from Nominations", 0, true, 0.0, true, 1.0); g_ConVars[maxmatches] = CreateConVar("sm_nominate_maxfound", "0", "Maximum number of nomination matches to add to the menu. 0 = infinite.", _, true, 0.0); - //g_ConVars[allow_workshop] = CreateConVar("sm_nominate_allow_workshop", "0", "Specifies if unlisted workshop maps should be allowed to be nominated", 0, true, 0.0, true, 1.0); + g_ConVars[allow_workshop] = CreateConVar("sm_nominate_allow_workshop", "0", "Specifies if unlisted workshop maps can be nominated", 0, true, 0.0, true, 1.0); g_ConVars[nextlevel_allowed] = FindConVar("sv_vote_issue_nextlevel_allowed"); @@ -133,8 +132,8 @@ public void OnAllPluginsLoaded() } g_NativeVotes = LibraryExists(LIBRARY) && - GetFeatureStatus(FeatureType_Native, "NativeVotes_AreVoteCommandsSupported") == FeatureStatus_Available && - NativeVotes_AreVoteCommandsSupported(); + GetFeatureStatus(FeatureType_Native, "NativeVotes_AreVoteCommandsSupported") == FeatureStatus_Available && + NativeVotes_AreVoteCommandsSupported(); if (g_NativeVotes) RegisterVoteHandler(); @@ -142,7 +141,7 @@ public void OnAllPluginsLoaded() public void OnLibraryAdded(const char[] name) { - if (StrEqual(name, LIBRARY, false) && + if (StrEqual(name, "nativevotes", false) && GetFeatureStatus(FeatureType_Native, "NativeVotes_AreVoteCommandsSupported") == FeatureStatus_Available && NativeVotes_AreVoteCommandsSupported()) { @@ -153,7 +152,7 @@ public void OnLibraryAdded(const char[] name) public void OnLibraryRemoved(const char[] name) { - if (StrEqual(name, LIBRARY, false)) + if (StrEqual(name, "nativevotes", false)) { g_NativeVotes = false; RemoveVoteHandler(); @@ -162,11 +161,7 @@ public void OnLibraryRemoved(const char[] name) public void OnConfigsExecuted() { - if (ReadMapList(g_MapList, - g_mapFileSerial, - "nominations", - MAPLIST_FLAG_CLEARARRAY|MAPLIST_FLAG_MAPSFOLDER) - == null) + if (ReadMapList(g_MapList, g_mapFileSerial, "nominations", MAPLIST_FLAG_CLEARARRAY|MAPLIST_FLAG_MAPSFOLDER) == null) { if (g_mapFileSerial == -1) { @@ -294,7 +289,7 @@ public Action Command_Nominate(int client, int args) char mapname[96]; GetCmdArg(1, mapname, sizeof(mapname)); - + ArrayList results = new ArrayList(); int matches = FindMatchingMaps(g_MapList, results, mapname); @@ -392,20 +387,20 @@ void AttemptNominate(int client, const char[] map, int size, bool isVoteMenu) } char displayName[PLATFORM_MAX_PATH]; - GetMapDisplayName(mapname, displayName, sizeof(displayName)); + GetMapDisplayName(mapname, displayName, sizeof(displayName)); Format(displayName, sizeof(displayName), "\x05%s\x01", displayName); int status; if (!g_MapTrie.GetValue(mapname, status)) - { - CReplyToCommand(client, "[{lightgreen}Nominations\x01] %t", "Map Not In Pool", displayName); - if (isVoteMenu && g_NativeVotes) { - NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_MapNotValid); - } - return; + CReplyToCommand(client, "[{lightgreen}Nominations\x01] %t", "Map Not In Pool", displayName); + if (isVoteMenu && g_NativeVotes) + { + NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_MapNotValid); + } + return; } - + if ((status & MAPSTATUS_DISABLED) == MAPSTATUS_DISABLED) { if ((status & MAPSTATUS_EXCLUDE_CURRENT) == MAPSTATUS_EXCLUDE_CURRENT) @@ -439,7 +434,7 @@ void AttemptNominate(int client, const char[] map, int size, bool isVoteMenu) } NominateResult result = NominateMap(mapname, false, client); - + if (result > Nominate_Replaced) { if (result == Nominate_AlreadyInVote) @@ -461,22 +456,13 @@ void AttemptNominate(int client, const char[] map, int size, bool isVoteMenu) return; } - + /* Map was nominated! - Disable the menu item and update the trie */ - g_MapTrie.SetValue(mapname, MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_NOMINATED); - - char name[MAX_NAME_LENGTH]; int r, g, b, a, color; - GetEntityRenderColor(client, r, g, b, a); - color = (r << 16) | (g << 8) | b; - if (color != 0xFFFFFF) - { - Format(name, sizeof(name), "{#%06X}%N\x01", color, client); - } - else - { - Format(name, sizeof(name), "{teamcolor}%N\x01", client); - } + g_MapTrie.SetValue(mapname, MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_NOMINATED); + + char name[MAX_NAME_LENGTH]; + GetPlayerName(client, name, sizeof(name)); if (result == Nominate_Added) { CPrintToChatAllEx(client, "[{lightgreen}Nominations\x01] %t", "Map Nominated", name, displayName); @@ -746,4 +732,19 @@ public Action NativeVotes_OverrideMaps(StringMap mapList) } return Plugin_Continue; +} + +void GetPlayerName(int client, char[] name, int maxlen) +{ + int r, g, b, a, color; + GetEntityRenderColor(client, r, g, b, a); + color = (r << 16) | (g << 8) | b; + if (color != 0xFFFFFF) + { + Format(name, maxlen, "{#%06X}%N\x01", color, client); + } + else + { + Format(name, maxlen, "{teamcolor}%N\x01", client); + } } \ No newline at end of file diff --git a/addons/sourcemod/scripting/nativevotes_rockthevote.sp b/addons/sourcemod/scripting/nativevotes_rockthevote.sp index 969a656..caaa6e6 100644 --- a/addons/sourcemod/scripting/nativevotes_rockthevote.sp +++ b/addons/sourcemod/scripting/nativevotes_rockthevote.sp @@ -52,10 +52,10 @@ public Plugin myinfo = { - name = "[NativeVotes] Rock The Vote", + name = "NativeVotes | Rock The Vote", author = "AlliedModders LLC and Powerlord", description = "Provides RTV Map Voting", - version = "26w02h", + version = "26w03a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -92,14 +92,15 @@ public void OnPluginStart() LoadTranslations("common.phrases"); LoadTranslations("rockthevote.phrases"); - g_ConVars[needed] = CreateConVar("sm_rtv_needed", "0.60", "Percentage of players needed to rockthevote (Def 60%)", 0, true, 0.05, true, 1.0); - g_ConVars[minplayers] = CreateConVar("sm_rtv_minplayers", "0", "Number of players required before RTV will be enabled.", 0, true, 0.0, true, float(MAXPLAYERS)); - g_ConVars[initialdelay] = CreateConVar("sm_rtv_initialdelay", "30.0", "Time (in seconds) before first RTV can be held", 0, true, 0.00); - g_ConVars[interval] = CreateConVar("sm_rtv_interval", "240.0", "Time (in seconds) after a failed RTV before another can be held", 0, true, 0.00); - g_ConVars[changetime] = CreateConVar("sm_rtv_changetime", "0", "When to change the map after a succesful RTV: 0 - Instant, 1 - RoundEnd, 2 - MapEnd", _, true, 0.0, true, 2.0); + g_ConVars[needed] = CreateConVar("sm_rtv_needed", "0.60", "Percentage of players needed to rockthevote (Def 60%)", 0, true, 0.05, true, 1.0); + g_ConVars[minplayers] = CreateConVar("sm_rtv_minplayers", "0", "Number of players required before RTV will be enabled.", 0, true, 0.0, true, float(MAXPLAYERS)); + g_ConVars[initialdelay] = CreateConVar("sm_rtv_initialdelay", "30.0", "Time (in seconds) before first RTV can be held", 0, true, 0.00); + g_ConVars[interval] = CreateConVar("sm_rtv_interval", "240.0", "Time (in seconds) after a failed RTV before another can be held", 0, true, 0.00); + g_ConVars[changetime] = CreateConVar("sm_rtv_changetime", "0", "When to change the map after a succesful RTV: 0 - Instant, 1 - RoundEnd, 2 - MapEnd", _, true, 0.0, true, 2.0); g_ConVars[postvoteaction] = CreateConVar("sm_rtv_postvoteaction", "0", "What to do with RTV's after a mapvote has completed. 0 - Allow, success = instant change, 1 - Deny", _, true, 0.0, true, 1.0); RegConsoleCmd("sm_rtv", Command_RTV); + RegConsoleCmd("sm_rockthevote", Command_RTV); RegAdminCmd("sm_forcertv", Command_ForceRTV, ADMFLAG_CHANGEMAP); RegAdminCmd("sm_resetrtv", Command_ResetRTV, ADMFLAG_CHANGEMAP); @@ -108,7 +109,7 @@ public void OnPluginStart() OnMapEnd(); /* Handle late load */ - for (int i=1; i<=MaxClients; i++) + for (int i = 1; i <= MaxClients; i++) { if (IsClientConnected(i)) { @@ -304,18 +305,9 @@ void MenuHandler_UndoRTV(Menu menu, MenuAction action, int client, int param2) { if (g_Voted[client]) { - char name[MAX_NAME_LENGTH]; int r, g, b, a, color; - GetEntityRenderColor(client, r, g, b, a); - color = (r << 16) | (g << 8) | b; - if (color != 0xFFFFFF) - { - Format(name, sizeof(name), "{#%06X}%N\x01", color, client); - } - else - { - Format(name, sizeof(name), "{teamcolor}%N\x01", client); - } - + char name[MAX_NAME_LENGTH]; + GetPlayerName(client, name, sizeof(name)); + g_Voted[client] = false; if (g_Votes > 0) g_Votes--; CPrintToChatAllEx(client, "[{lightgreen}Rock The Vote\x01] %s: %t", name, "Cancelled Vote"); @@ -327,7 +319,13 @@ void MenuHandler_UndoRTV(Menu menu, MenuAction action, int client, int param2) void AttemptRTV(int client, bool isVoteMenu=false) { - if (!g_RTVAllowed || (g_ConVars[postvoteaction].IntValue == 1 && HasEndOfMapVoteFinished())) + if (g_Voted[client]) + { + UndoRTV(client); + return; + } + + if (!g_RTVAllowed || (g_ConVars[postvoteaction].IntValue == 1 && HasEndOfMapVoteFinished())) { CReplyToCommand(client, "[{lightgreen}Rock The Vote\x01] %t", "RTV Not Allowed"); if (isVoteMenu && g_NativeVotes) @@ -354,7 +352,7 @@ void AttemptRTV(int client, bool isVoteMenu=false) if (!CanMapChooserStartVote() && NativeVotes_IsVoteInProgress()) { - CReplyToCommandEx(client, client, "[{lightgreen}Rock The Vote\x01] %t", "RTV Started"); + CReplyToCommand(client, "[{lightgreen}Rock The Vote\x01] %t", "RTV Started"); return; } @@ -368,24 +366,9 @@ void AttemptRTV(int client, bool isVoteMenu=false) return; } - if (g_Voted[client]) - { - UndoRTV(client); - return; - } - - char name[MAX_NAME_LENGTH]; int r, g, b, a, color; - GetEntityRenderColor(client, r, g, b, a); - color = (r << 16) | (g << 8) | b; - if (color != 0xFFFFFF) - { - Format(name, sizeof(name), "{#%06X}%N\x01", color, client); - } - else - { - Format(name, sizeof(name), "{teamcolor}%N\x01", client); - } - + char name[MAX_NAME_LENGTH]; + GetPlayerName(client, name, sizeof(name)); + g_Votes++; g_Voted[client] = true; @@ -449,7 +432,7 @@ void ResetRTV() { g_Votes = 0; - for (int i=1; i<=MAXPLAYERS; i++) + for (int i = 1; i <= MaxClients; i++) { g_Voted[i] = false; } @@ -508,10 +491,7 @@ public Action Menu_RTV(int client, NativeVotesOverride overrideType, const char[ } ReplySource old = SetCmdReplySource(SM_REPLY_TO_CHAT); - - // awful hack, but whatever - char mapname[96]; - strcopy(mapname, sizeof(mapname), voteArgument); + FakeClientCommand(client, "sm_nominate %s", voteArgument); AttemptRTV(client, true); @@ -520,3 +500,18 @@ public Action Menu_RTV(int client, NativeVotesOverride overrideType, const char[ return Plugin_Handled; } + +void GetPlayerName(int client, char[] name, int maxlen) +{ + int r, g, b, a, color; + GetEntityRenderColor(client, r, g, b, a); + color = (r << 16) | (g << 8) | b; + if (color != 0xFFFFFF) + { + Format(name, maxlen, "{#%06X}%N\x01", color, client); + } + else + { + Format(name, maxlen, "{teamcolor}%N\x01", client); + } +} \ No newline at end of file diff --git a/addons/sourcemod/scripting/nativevotes_voterp.sp b/addons/sourcemod/scripting/nativevotes_voterp.sp index 94c6f0a..fee43d3 100644 --- a/addons/sourcemod/scripting/nativevotes_voterp.sp +++ b/addons/sourcemod/scripting/nativevotes_voterp.sp @@ -1,11 +1,3 @@ -/** - * vim: set ts=4 : - * ============================================================================= - * NativeVotes: sm_voterp - Vote to enable/disable tf_medieval_autorp - * Only works in TF2. - * ============================================================================= - */ - #include #include #include @@ -16,10 +8,10 @@ public Plugin myinfo = { - name = "[NativeVotes] Medieval Auto-RP", + name = "NativeVotes | Medieval Auto-RP", author = "Heapons", description = "Provides Medieval Auto-RP voting.", - version = "26w02h", + version = "26w03a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -102,4 +94,4 @@ int VoteHandler(NativeVote vote, MenuAction action, int param1, int param2) } } return Plugin_Continue; -} +} \ No newline at end of file From 0f1b5b50356f3f7b521b2a9864a442d304072a5a Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Thu, 22 Jan 2026 19:03:06 +0100 Subject: [PATCH 033/128] Bump version --- addons/sourcemod/scripting/nativevotes_basecommands.sp | 2 +- addons/sourcemod/scripting/nativevotes_basevotes.sp | 2 +- addons/sourcemod/scripting/nativevotes_funvotes.sp | 2 +- addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_basecommands.sp b/addons/sourcemod/scripting/nativevotes_basecommands.sp index 1b9cf2c..0075c64 100644 --- a/addons/sourcemod/scripting/nativevotes_basecommands.sp +++ b/addons/sourcemod/scripting/nativevotes_basecommands.sp @@ -48,7 +48,7 @@ public Plugin myinfo = name = "NativeVotes | Basic Commands", author = "Powerlord and AlliedModders LLC", description = "Revote and Cancel support for NativeVotes", - version = "26w03a", + version = "26w04a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" } diff --git a/addons/sourcemod/scripting/nativevotes_basevotes.sp b/addons/sourcemod/scripting/nativevotes_basevotes.sp index bd7c05e..5510b19 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes.sp @@ -46,7 +46,7 @@ public Plugin:myinfo = name = "NativeVotes | Basic Votes", author = "Powerlord and AlliedModders LLC", description = "NativeVotes Basic Vote Commands", - version = "26w03a", + version = "26w04a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_funvotes.sp b/addons/sourcemod/scripting/nativevotes_funvotes.sp index 006f049..ab4591a 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes.sp @@ -47,7 +47,7 @@ public Plugin myinfo = name = "NativeVotes | Fun Votes", author = "Powerlord and AlliedModders LLC", description = "NativeVotes Fun Vote Commands", - version = "26w03a", + version = "26w04a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp b/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp index 1f01691..f7fa5e5 100644 --- a/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp +++ b/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp @@ -53,7 +53,7 @@ public Plugin myinfo = { name = "NativeVotes | Kick Vote Immunity", author = "Powerlord", description = "Causes TF2 kick votes to fail against people who the current user can't target.", - version = "26w03a", + version = "26w04a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; From d5bd3a093c807d2daab67a619d8a6398bbd40f33 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Thu, 22 Jan 2026 19:41:35 +0100 Subject: [PATCH 034/128] Auto-generated mapcycles & workshop map cleanups --- .../scripting/nativevotes_mapchooser.sp | 284 ++++++++++++------ 1 file changed, 194 insertions(+), 90 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_mapchooser.sp b/addons/sourcemod/scripting/nativevotes_mapchooser.sp index bed1291..a217158 100644 --- a/addons/sourcemod/scripting/nativevotes_mapchooser.sp +++ b/addons/sourcemod/scripting/nativevotes_mapchooser.sp @@ -37,6 +37,7 @@ #include #include #include +#include #undef REQUIRE_PLUGIN #include @@ -50,7 +51,7 @@ public Plugin myinfo = name = "NativeVotes | MapChooser", author = "AlliedModders LLC and Powerlord", description = "Automated Map Voting", - version = "26w03a", + version = "26w04a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -79,6 +80,9 @@ enum mapvote_voteduration, mapvote_runoff, mapvote_runoffpercent, + mapcycle_auto, + mapcycle_exclude, + workshop_cleanup, MAX_CONVARS } @@ -117,18 +121,31 @@ Handle g_MapVoteStartedForward = null; #define MAXTEAMS 10 int g_winCount[MAXTEAMS]; -#define VOTE_EXTEND "##extend##" +#define VOTE_EXTEND "##extend##" #define VOTE_DONTCHANGE "##dontchange##" // NativeVotes bool g_NativeVotes; -#define LIBRARY "nativevotes" public void OnPluginStart() { LoadTranslations("mapchooser.phrases"); LoadTranslations("common.phrases"); + + KeyValues kv = new KeyValues("GameInfo"); + kv.ImportFromFile("gameinfo.txt"); + + char gameDir[128]; + GetGameFolderName(gameDir, sizeof(gameDir)); + EngineVersion engine = GetEngineVersion(); + if (!StrEqual(gameDir, "tf") && + (kv.GetNum("DependsOnAppID") == 440 || + (engine == Engine_SDK2013 && FileExists("resource/tf.ttf")))) + { + engine = Engine_TF2; + } + int arraySize = ByteCountToCells(PLATFORM_MAX_PATH); g_MapList = new ArrayList(arraySize); g_NominateList = new ArrayList(arraySize); @@ -138,7 +155,7 @@ public void OnPluginStart() g_ConVars[mapvote_endvote] = CreateConVar("sm_mapvote_endvote", "1", "Specifies if MapChooser should run an end of map vote", _, true, 0.0, true, 1.0); g_ConVars[mapvote_start] = CreateConVar("sm_mapvote_start", "3.0", "Specifies when to start the vote based on time remaining.", _, true, 1.0); - g_ConVars[mapvote_startround] = CreateConVar("sm_mapvote_startround", "2.0", "Specifies when to start the vote based on rounds remaining. Use 0 on TF2 to start vote during bonus round time", _, true, 0.0); + g_ConVars[mapvote_startround] = CreateConVar("sm_mapvote_startround", "2.0", "Specifies when to start the vote based on rounds remaining. Use '0' on TF2 to start vote during bonus round time", _, true, 0.0); g_ConVars[mapvote_startfrags] = CreateConVar("sm_mapvote_startfrags", "5.0", "Specifies when to start the vote base on frags remaining.", _, true, 1.0); g_ConVars[extendmap_timestep] = CreateConVar("sm_extendmap_timestep", "15", "Specifies how much many more minutes each extension makes", _, true, 5.0); g_ConVars[extendmap_roundstep] = CreateConVar("sm_extendmap_roundstep", "5", "Specifies how many more rounds each extension makes", _, true, 1.0); @@ -147,13 +164,20 @@ public void OnPluginStart() g_ConVars[mapvote_include] = CreateConVar("sm_mapvote_include", "5", "Specifies how many maps to include in the vote.", _, true, 2.0, true, 6.0); g_ConVars[mapvote_novote] = CreateConVar("sm_mapvote_novote", "1", "Specifies whether or not MapChooser should pick a map if no votes are received.", _, true, 0.0, true, 1.0); g_ConVars[mapvote_extend] = CreateConVar("sm_mapvote_extend", "0", "Number of extensions allowed each map.", _, true, 0.0); - g_ConVars[mapvote_dontchange] = CreateConVar("sm_mapvote_dontchange", "1", "Specifies if a 'Don't Change' option should be added to early votes", _, true, 0.0); + g_ConVars[mapvote_dontchange] = CreateConVar("sm_mapvote_dontchange", "1", "Specifies if a 'Don't Change' option should be added to early votes", _, true, 0.0, true, 1.0); g_ConVars[mapvote_voteduration] = CreateConVar("sm_mapvote_voteduration", "20", "Specifies how long the mapvote should be available for.", _, true, 5.0); g_ConVars[mapvote_runoff] = CreateConVar("sm_mapvote_runoff", "0", "Hold run of votes if winning choice is less than a certain margin", _, true, 0.0, true, 1.0); g_ConVars[mapvote_runoffpercent] = CreateConVar("sm_mapvote_runoffpercent", "50", "If winning choice has less than this percent of votes, hold a runoff", _, true, 0.0, true, 100.0); - - RegAdminCmd("sm_mapvote", Command_Mapvote, ADMFLAG_CHANGEMAP, "sm_mapvote - Forces MapChooser to attempt to run a map vote now."); - RegAdminCmd("sm_setnextmap", Command_SetNextmap, ADMFLAG_CHANGEMAP, "sm_setnextmap "); + g_ConVars[mapcycle_auto] = CreateConVar("sm_mapcycle_auto", "0", "Specifies whether or not to automatically populate the maps list.", _, true, 0.0, true, 1.0); + g_ConVars[mapcycle_exclude] = CreateConVar("sm_mapcycle_exclude", ".*itemtest.*|background01|^tr.*$", "Specifies which maps shouldn't be automatically added. Defined with a regex pattern."); + if (engine != Engine_SDK2013 && engine == Engine_TF2) + { + g_ConVars[workshop_cleanup] = CreateConVar("sm_mapvote_workshop_cleanup", "0", "Specifies whether or not to automatically workshop maps on map change", _, true, 0.0, true, 1.0); + } + + RegAdminCmd("sm_mapvote", Command_MapVote, ADMFLAG_CHANGEMAP, "Forces MapChooser to attempt to run a map vote now."); + RegAdminCmd("sm_setnextmap", Command_SetNextMap, ADMFLAG_CHANGEMAP, "sm_setnextmap "); + RegAdminCmd("sm_reloadmaplist", Command_ReloadMapList, ADMFLAG_CHANGEMAP, "Re-populates the mapcycle file."); g_ConVars[mp_winlimit] = FindConVar("mp_winlimit"); g_ConVars[mp_maxrounds] = FindConVar("mp_maxrounds"); @@ -162,19 +186,6 @@ public void OnPluginStart() if (g_ConVars[mp_winlimit] || g_ConVars[mp_maxrounds]) { - int engine = GetEngineVersion(); - - KeyValues kv = new KeyValues("GameInfo"); - if (!kv.ImportFromFile("gameinfo.txt")) - { - delete kv; - } - else if (kv.GetNum("DependsOnAppID") == 440) - { - engine = Engine_TF2; - delete kv; - } - switch (engine) { case Engine_TF2: @@ -183,17 +194,6 @@ public void OnPluginStart() HookEvent("teamplay_restart_round", Event_TeamplayRestartRound); HookEvent("arena_win_panel", Event_TeamplayWinPanel); } - case Engine_SDK2013: - { - char folder[64]; - GetGameFolderName(folder, sizeof(folder)); - if (strcmp(folder, "tf2classic") == 0 || strcmp(folder, "open_fortress") == 0 || strcmp(folder, "pf2") == 0) - { - HookEvent("teamplay_win_panel", Event_TeamplayWinPanel); - HookEvent("teamplay_restart_round", Event_TeamplayRestartRound); - HookEvent("arena_win_panel", Event_TeamplayWinPanel); - } - } case Engine_NuclearDawn: { HookEvent("round_win", Event_RoundEnd); @@ -212,8 +212,8 @@ public void OnPluginStart() AutoExecConfig(true, "mapchooser"); - //Change the mp_bonusroundtime max so that we have time to display the vote - //If you display a vote during bonus time good defaults are 17 vote duration and 19 mp_bonustime + // Change the mp_bonusroundtime max so that we have time to display the vote + // If you display a vote during bonus time good defaults are 17 vote duration and 19 mp_bonustime if (g_ConVars[mp_bonusroundtime]) { g_ConVars[mp_bonusroundtime].SetBounds(ConVarBound_Upper, true, 30.0); @@ -258,12 +258,12 @@ public void OnAllPluginsLoaded() } } - g_NativeVotes = LibraryExists(LIBRARY) && NativeVotes_IsVoteTypeSupported(NativeVotesType_NextLevelMult); + g_NativeVotes = LibraryExists("nativevotes") && NativeVotes_IsVoteTypeSupported(NativeVotesType_NextLevelMult); } public void OnLibraryAdded(const char[] name) { - if (StrEqual(name, LIBRARY, false) && NativeVotes_IsVoteTypeSupported(NativeVotesType_NextLevelMult)) + if (StrEqual(name, "nativevotes", false) && NativeVotes_IsVoteTypeSupported(NativeVotesType_NextLevelMult)) { g_NativeVotes = true; } @@ -271,20 +271,28 @@ public void OnLibraryAdded(const char[] name) public void OnLibraryRemoved(const char[] name) { - if (StrEqual(name, LIBRARY, false)) + if (StrEqual(name, "nativevotes", false)) { g_NativeVotes = false; } } +public void OnMapInit() +{ + if (g_ConVars[mapcycle_auto].BoolValue) + { + PopulateMapList(); + } + + if (g_ConVars[workshop_cleanup].BoolValue) + { + CleanupWorkshopMaps(); + } +} + public void OnConfigsExecuted() { - if (ReadMapList(g_MapList, - g_mapFileSerial, - "mapchooser", - MAPLIST_FLAG_CLEARARRAY|MAPLIST_FLAG_MAPSFOLDER) - != null) - + if (ReadMapList(g_MapList, g_mapFileSerial, "mapchooser", MAPLIST_FLAG_CLEARARRAY|MAPLIST_FLAG_MAPSFOLDER) != null) { if (g_mapFileSerial == -1) { @@ -304,7 +312,7 @@ public void OnConfigsExecuted() g_NominateList.Clear(); g_NominateOwners.Clear(); - for (int i=0; i= voteSize ? voteSize : nominateCount; - for (int i=0; i g_ConVars[mapvote_include].IntValue) { - char oldmap[96]; + char oldmap[PLATFORM_MAX_PATH]; g_NominateList.GetString(0, oldmap, sizeof(oldmap)); Call_StartForward(g_NominationsResetForward); Call_PushString(oldmap); @@ -1443,7 +1439,7 @@ bool InternalRemoveNominationByMap(char[] map) { for (int i = 0; i < g_NominateList.Length; i++) { - char oldmap[96]; + char oldmap[PLATFORM_MAX_PATH]; g_NominateList.GetString(i, oldmap, sizeof(oldmap)); if(strcmp(map, oldmap, false) == 0) @@ -1486,7 +1482,7 @@ bool InternalRemoveNominationByOwner(int owner) if (owner && ((index = g_NominateOwners.FindValue(owner)) != -1)) { - char oldmap[96]; + char oldmap[PLATFORM_MAX_PATH]; g_NominateList.GetString(index, oldmap, sizeof(oldmap)); Call_StartForward(g_NominationsResetForward); @@ -1547,9 +1543,9 @@ public int Native_GetExcludeMapList(Handle plugin, int numParams) return; } int size = g_OldMapList.Length; - char map[96]; + char map[PLATFORM_MAX_PATH]; - for (int i=0; i= 1) + continue; + + WriteFileLine(mapcycleFile, "%s", mapName); + } + delete dir; + delete regex; + CloseHandle(mapcycleFile); +} + + +void CleanupWorkshopMaps() +{ + KeyValues kv = new KeyValues("GameInfo"); + int appid; + if (kv.ImportFromFile("gameinfo.txt") && kv.JumpToKey("FileSystem")) + { + appid = kv.GetNum("SteamAppId"); + } + delete kv; + + char workshopDir[PLATFORM_MAX_PATH]; + Format(workshopDir, sizeof(workshopDir), "../steamapps/workshop/content/%d", appid); + + DirectoryListing dir = OpenDirectory(workshopDir); + if (dir != null) + { + char ugcid[PLATFORM_MAX_PATH]; + FileType type; + char path[PLATFORM_MAX_PATH]; + + while (dir.GetNext(ugcid, sizeof(ugcid), type)) + { + if (type != FileType_Directory) + continue; + + Format(path, sizeof(path), "%s/%s", workshopDir, ugcid); + DirectoryListing ugcDir = OpenDirectory(path); + if (ugcDir != null) + { + char file[PLATFORM_MAX_PATH]; + FileType fileType; + char filePath[PLATFORM_MAX_PATH]; + while (ugcDir.GetNext(file, sizeof(file), fileType)) + { + if (fileType == FileType_File) + { + Format(filePath, sizeof(filePath), "%s/%s", path, file); + DeleteFile(filePath); + } + } + delete ugcDir; + } + RemoveDir(path); + } + delete dir; + } + RemoveDir(workshopDir); } \ No newline at end of file From fe89bc1a5b4c38121643b469aafbe4243933f5d7 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Thu, 22 Jan 2026 19:41:50 +0100 Subject: [PATCH 035/128] Toggle callvote/menu --- .../sourcemod/scripting/nativevotes_nominations.sp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_nominations.sp b/addons/sourcemod/scripting/nativevotes_nominations.sp index 804c18f..466a564 100644 --- a/addons/sourcemod/scripting/nativevotes_nominations.sp +++ b/addons/sourcemod/scripting/nativevotes_nominations.sp @@ -48,7 +48,7 @@ public Plugin myinfo = name = "NativeVotes | Map Nominations", author = "AlliedModders LLC and Powerlord", description = "Provides Map Nominations", - version = "26w03a", + version = "26w04a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -58,6 +58,7 @@ enum excludecurrent, maxmatches, allow_workshop, + use_callvote, nextlevel_allowed, @@ -93,10 +94,11 @@ public void OnPluginStart() int arraySize = ByteCountToCells(PLATFORM_MAX_PATH); g_MapList = new ArrayList(arraySize); - g_ConVars[excludeold] = CreateConVar("sm_nominate_excludeold", "1", "Specifies if the current map should be excluded from the Nominations list", 0, true, 0.0, true, 1.0); - g_ConVars[excludecurrent] = CreateConVar("sm_nominate_excludecurrent", "1", "Specifies if the MapChooser excluded maps should also be excluded from Nominations", 0, true, 0.0, true, 1.0); + g_ConVars[excludeold] = CreateConVar("sm_nominate_excludeold", "1", "Specifies if the current map should be excluded from the Nominations list", _, true, 0.0, true, 1.0); + g_ConVars[excludecurrent] = CreateConVar("sm_nominate_excludecurrent", "1", "Specifies if the MapChooser excluded maps should also be excluded from Nominations", _, true, 0.0, true, 1.0); g_ConVars[maxmatches] = CreateConVar("sm_nominate_maxfound", "0", "Maximum number of nomination matches to add to the menu. 0 = infinite.", _, true, 0.0); - g_ConVars[allow_workshop] = CreateConVar("sm_nominate_allow_workshop", "0", "Specifies if unlisted workshop maps can be nominated", 0, true, 0.0, true, 1.0); + //g_ConVars[allow_workshop] = CreateConVar("sm_nominate_allow_workshop", "0", "Specifies if unlisted workshop maps can be nominated", _, true, 0.0, true, 1.0); + g_ConVars[use_callvote] = CreateConVar("sm_nominate_use_callvote", "1", "Specifies the command to use to open the nomination vote menu", _, true, 0.0, true, 1.0); g_ConVars[nextlevel_allowed] = FindConVar("sv_vote_issue_nextlevel_allowed"); @@ -276,7 +278,7 @@ public Action Command_Nominate(int client, int args) if (args == 0) { - if (g_NativeVotes && NativeVotes_AreVoteCommandsSupported() && g_ConVars[nextlevel_allowed].BoolValue) + if (g_NativeVotes && NativeVotes_AreVoteCommandsSupported() && g_ConVars[use_callvote].BoolValue && g_ConVars[nextlevel_allowed].BoolValue) { FakeClientCommand(client, "callvote"); } From 77efc5ebc6313d6e6757d20c8153d379de4d42f6 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Thu, 22 Jan 2026 19:42:12 +0100 Subject: [PATCH 036/128] Minor tweaks --- .../scripting/nativevotes_rockthevote.sp | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_rockthevote.sp b/addons/sourcemod/scripting/nativevotes_rockthevote.sp index caaa6e6..17296ad 100644 --- a/addons/sourcemod/scripting/nativevotes_rockthevote.sp +++ b/addons/sourcemod/scripting/nativevotes_rockthevote.sp @@ -55,7 +55,7 @@ public Plugin myinfo = name = "NativeVotes | Rock The Vote", author = "AlliedModders LLC and Powerlord", description = "Provides RTV Map Voting", - version = "26w03a", + version = "26w04a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -232,7 +232,7 @@ public void OnClientDisconnect(int client) public void OnClientSayCommand_Post(int client, const char[] command, const char[] sArgs) { - if (client <= 0 || IsChatTrigger()) + if (!client || IsChatTrigger()) { return; } @@ -249,7 +249,7 @@ public void OnClientSayCommand_Post(int client, const char[] command, const char public Action Command_RTV(int client, int args) { - if (client <= 0) + if (!client) { return Plugin_Handled; } @@ -319,12 +319,6 @@ void MenuHandler_UndoRTV(Menu menu, MenuAction action, int client, int param2) void AttemptRTV(int client, bool isVoteMenu=false) { - if (g_Voted[client]) - { - UndoRTV(client); - return; - } - if (!g_RTVAllowed || (g_ConVars[postvoteaction].IntValue == 1 && HasEndOfMapVoteFinished())) { CReplyToCommand(client, "[{lightgreen}Rock The Vote\x01] %t", "RTV Not Allowed"); @@ -366,6 +360,12 @@ void AttemptRTV(int client, bool isVoteMenu=false) return; } + if (g_Voted[client]) + { + UndoRTV(client); + return; + } + char name[MAX_NAME_LENGTH]; GetPlayerName(client, name, sizeof(name)); @@ -397,7 +397,7 @@ void StartRTV() if (EndOfMapVoteEnabled() && HasEndOfMapVoteFinished()) { /* Change right now then */ - char map[96]; + char map[PLATFORM_MAX_PATH]; if (GetNextMap(map, sizeof(map))) { GetMapDisplayName(map, map, sizeof(map)); @@ -432,7 +432,7 @@ void ResetRTV() { g_Votes = 0; - for (int i = 1; i <= MaxClients; i++) + for (int i = 1; i <= MAXPLAYERS; i++) { g_Voted[i] = false; } @@ -444,7 +444,7 @@ public Action Timer_ChangeMap(Handle hTimer) LogMessage("RTV changing map manually"); - char map[96]; + char map[PLATFORM_MAX_PATH]; if (GetNextMap(map, sizeof(map))) { ForceChangeLevel(map, "RTV after mapvote"); From 4538b803de7076b413b7002ffc2e3aad59ce2125 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Thu, 22 Jan 2026 19:42:26 +0100 Subject: [PATCH 037/128] Bump version --- addons/sourcemod/scripting/nativevotes_voterp.sp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/sourcemod/scripting/nativevotes_voterp.sp b/addons/sourcemod/scripting/nativevotes_voterp.sp index fee43d3..15e8d1c 100644 --- a/addons/sourcemod/scripting/nativevotes_voterp.sp +++ b/addons/sourcemod/scripting/nativevotes_voterp.sp @@ -11,7 +11,7 @@ public Plugin myinfo = name = "NativeVotes | Medieval Auto-RP", author = "Heapons", description = "Provides Medieval Auto-RP voting.", - version = "26w03a", + version = "26w04a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; From cf2589b4ae211af36e1638071a6b34e2a52b332b Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Thu, 22 Jan 2026 19:42:52 +0100 Subject: [PATCH 038/128] Fix MVM --- addons/sourcemod/scripting/nativevotes.sp | 41 +-- .../sourcemod/scripting/nativevotes/game.sp | 346 +++++++++--------- 2 files changed, 189 insertions(+), 198 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes.sp b/addons/sourcemod/scripting/nativevotes.sp index 9aaeef2..e8a5174 100644 --- a/addons/sourcemod/scripting/nativevotes.sp +++ b/addons/sourcemod/scripting/nativevotes.sp @@ -39,8 +39,8 @@ #pragma semicolon 1 #pragma newdecls required -#include "include/nativevotes.inc" -#include "include/implodeexplode.inc" +#include +#include EngineVersion g_EngineVersion = Engine_Unknown; @@ -121,8 +121,6 @@ ConVar sv_vote_holder_may_vote_no; // Map list stuffs -#define STRINGTABLE_NAME "ServerMapCycle" -#define STRINGTABLE_ITEM "ServerMapCycle" //#define MAP_STRING_CACHE_SIZE PLATFORM_MAX_PATH * 256 // Forward @@ -152,7 +150,7 @@ public Plugin myinfo = name = "NativeVotes", author = "Powerlord", description = "Voting API to use the game's native vote panels. Compatible with L4D, L4D2, TF2, and CS:GO.", - version = "26w03a", + version = "26w04a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" } @@ -313,8 +311,15 @@ public Action Timer_RetryCallVote(Handle timer, any data) public void ProcessMapList() { - int stringTableIndex = FindStringTable(STRINGTABLE_NAME); - int stringIndex = FindStringIndex(stringTableIndex, STRINGTABLE_ITEM); + char stringTableName[64] = "ServerMapCycle", stringTableItem[64] = "ServerMapCycle"; + if (FindSendPropInfo("CTFGameRulesProxy", "m_bPlayingMannVsMachine") > 0 && GameRules_GetProp("m_bPlayingMannVsMachine")) + { + Format(stringTableName, sizeof(stringTableName), "%sMvM", stringTableName); + Format(stringTableItem, sizeof(stringTableItem), "%sMvM", stringTableItem); + } + + int stringTableIndex = FindStringTable(stringTableName); + int stringIndex = FindStringIndex(stringTableIndex, stringTableItem); StringMap overrideList = new StringMap(); @@ -550,7 +555,6 @@ public Action Command_CallVote(int client, const char[] command, int argc) GetClientName(targetClient, argument, sizeof(argument)); } - case NativeVotesType_ChgLevel, NativeVotesType_NextLevel: { if (g_MapOverrides == null) @@ -564,8 +568,7 @@ public Action Command_CallVote(int client, const char[] command, int argc) GetCmdArg(2, map, sizeof(map)); g_MapOverrides.GetString(map, argument, sizeof(argument)); } - } - + } default: { GetCmdArg(2, argument, sizeof(argument)); @@ -1420,9 +1423,7 @@ bool Internal_IsVoteInProgress() bool Internal_IsClientInVotePool(int client) { - if (client < 1 - || client > MaxClients - || g_hCurVote == null) + if (client < 1 || client > MaxClients || g_hCurVote == null) { return false; } @@ -1456,8 +1457,7 @@ bool Internal_RedrawToClient(int client, bool revotes) //Game_DisplayVoteFail(g_hCurVote, NativeVotesFail_Generic, client); // No, display a vote pass screen because that's nicer and we can customize it. - // Note: This isn't inside the earlier if because some players have had issues where the display - // doesn't always appear the first time. + // @note This isn't inside the earlier if because some players have had issues where the display doesn't always appear the first time. char revotePhrase[128]; Format(revotePhrase, sizeof(revotePhrase), "%T", "NativeVotes Revote", client); Game_DisplayVotePassCustom(g_hCurVote, revotePhrase, client); @@ -1605,13 +1605,13 @@ public int Native_Close(Handle plugin, int numParams) CancelVoting(); g_hCurVote = null; -/* + /* if (g_hVoteTimer != INVALID_HANDLE) { KillTimer(g_hVoteTimer); g_hVoteTimer = INVALID_HANDLE; } -*/ + */ } // This bit is necessary because the Forward system appears to not remove these when the forward Handle is closed @@ -1756,12 +1756,7 @@ public int Native_RemoveAllItems(Handle plugin, int numParams) Data_RemoveAllItems(vote); } -// native bool:NativeVotes_GetItem(Handle:vote, -// position, -// String:infoBuf[], -// infoBufLen, -// String:dispBuf[]="", -// dispBufLen=0); +// native bool NativeVotes_GetItem(Handle vote, int position, const char[] infoBuf, int infoBufLen, const char[] dispBuf = "", int dispBufLen = 0); public int Native_GetItem(Handle plugin, int numParams) { NativeVote vote = GetNativeCell(1); diff --git a/addons/sourcemod/scripting/nativevotes/game.sp b/addons/sourcemod/scripting/nativevotes/game.sp index 65fb8b6..aa11f2e 100644 --- a/addons/sourcemod/scripting/nativevotes/game.sp +++ b/addons/sourcemod/scripting/nativevotes/game.sp @@ -388,25 +388,19 @@ bool Game_IsGameSupported(char[] engineName="", int maxlength=0) GetGameFolderName(gameDir, sizeof(gameDir)); if (!StrEqual(gameDir, "tf")) { - // Band-aid for old TF2 sourcemods - if (g_EngineVersion == Engine_SDK2013 && (StrEqual(gameDir, "open_fortress") || StrEqual(gameDir, "tf2classic") || StrEqual(gameDir, "pf2"))) + if (g_EngineVersion == Engine_SDK2013 && FileExists("resource/tf.ttf")) { - isTF2SDKModHack = true; g_EngineVersion = Engine_TF2; + isTF2SDKModHack = true; } - // More robust way to detect TF2 SDK (2025) mods else { - KeyValues kv = new KeyValues("GameInfo"); - if (!kv.ImportFromFile("gameinfo.txt")) - { - delete kv; - } - else if (kv.GetNum("DependsOnAppID") == 440) + KeyValues kv = new KeyValues("GameInfo"); + if (kv.ImportFromFile("gameinfo.txt") && kv.GetNum("DependsOnAppID") == 440) { g_EngineVersion = Engine_TF2; - delete kv; } + delete kv; } } @@ -1924,7 +1918,7 @@ static void L4D2_DisplayVote(NativeVote vote, int[] clients, int num_clients) if (bCustom && actions & MenuAction_Display) { g_curDisplayClient = clients[i]; - changeTitle = view_as(DoAction(vote, MenuAction_Display, clients[i], 0)); + changeTitle = DoAction(vote, MenuAction_Display, clients[i], 0); } g_curDisplayClient = 0; @@ -2116,7 +2110,10 @@ static void TF2CSGO_UpdateVoteCounts(ArrayList votes) Format(szVoteOption, sizeof(szVoteOption), "vote_option%d", i+1); changeEvent.SetInt(szVoteOption, votes.Get(i)); } - changeEvent.SetInt("voteidx", s_nNativeVoteIdx); + if (!isTF2SDKModHack) + { + changeEvent.SetInt("voteidx", s_nNativeVoteIdx); + } changeEvent.SetInt("potentialVotes", GetEntProp(g_VoteController, Prop_Send, "m_nPotentialVotes")); changeEvent.Fire(); } @@ -2174,41 +2171,44 @@ static void TF2CSGO_DisplayVote(NativeVote vote, int[] clients, int num_clients) default: { Data_GetDetails(vote, details, MAX_VOTE_DETAILS_LENGTH); - } - } + } + } - switch(g_EngineVersion) - { - case Engine_CSGO: - { - bYesNo = CSGO_VoteTypeToTranslation(voteType, translation, sizeof(translation)); - CSGO_VoteTypeToVoteOtherTeamString(voteType, otherTeamString, sizeof(otherTeamString)); - } - case Engine_TF2: - { - bYesNo = TF2_VoteTypeToTranslation(voteType, translation, sizeof(translation)); - } - } + switch(g_EngineVersion) + { + case Engine_CSGO: + { + bYesNo = CSGO_VoteTypeToTranslation(voteType, translation, sizeof(translation)); + CSGO_VoteTypeToVoteOtherTeamString(voteType, otherTeamString, sizeof(otherTeamString)); + } + case Engine_TF2: + { + bYesNo = TF2_VoteTypeToTranslation(voteType, translation, sizeof(translation)); + } + } - int team = Data_GetTeam(vote); + int team = Data_GetTeam(vote); - // Moved to mimic SourceSDK2013's server/vote_controller.cpp - if (CheckVoteController()) - { - SetEntProp(g_VoteController, Prop_Send, "m_bIsYesNoVote", bYesNo); + // Moved to mimic SourceSDK2013's server/vote_controller.cpp + if (CheckVoteController()) + { + SetEntProp(g_VoteController, Prop_Send, "m_bIsYesNoVote", bYesNo); - // CSGO gets very cranky if you try setting this - if (g_EngineVersion == Engine_TF2) - SetEntProp(g_VoteController, Prop_Send, "m_iActiveIssueIndex", voteIndex); - - SetEntProp(g_VoteController, Prop_Send, "m_iOnlyTeamToVote", team); - for (int i = 0; i < 5; i++) - { - SetEntProp(g_VoteController, Prop_Send, "m_nVoteOptionCount", 0, _, i); - } + // CSGO gets very cranky if you try setting this + if (g_EngineVersion == Engine_TF2) + { + SetEntProp(g_VoteController, Prop_Send, "m_iActiveIssueIndex", voteIndex); + } - if (!isTF2SDKModHack) - { + SetEntProp(g_VoteController, Prop_Send, "m_iOnlyTeamToVote", team); + + for (int i = 0; i < 5; i++) + { + SetEntProp(g_VoteController, Prop_Send, "m_nVoteOptionCount", 0, _, i); + } + + if (!isTF2SDKModHack) + { // TODO(UPDATE): M-M-M-MULTIVOTE // we need unique vote indices; HUD elements for previous votes aren't cleaned up (?) // the game implements this as `this->m_nVoteIdx = s_nVoteIdx++` @@ -2216,126 +2216,126 @@ static void TF2CSGO_DisplayVote(NativeVote vote, int[] clients, int num_clients) #if defined LOG PrintToServer("Starting vote index: %d (controller: %d)", s_nNativeVoteIdx, GetEntProp(g_VoteController, Prop_Send, "m_nVoteIdx")); -#endif +#endif SetEntProp(g_VoteController, Prop_Send, "m_nVoteIdx", s_nNativeVoteIdx + 1); // TODO(UPDATE) - } - } + } + } // According to Source SDK 2013, vote_options is only sent for a multiple choice vote. // As of 2015-09-28, vote_options is sent for all votes in TF2 despite Yes/No being // translated in the UI itself. // As of 2016-07-10, we send this only when we're doing Yes/No votes as we handle // multiple choice votes separately later. - if (bYesNo) - { - int itemCount = Data_GetItemCount(vote); + if (bYesNo) + { + int itemCount = Data_GetItemCount(vote); - Event optionsEvent = CreateEvent("vote_options"); + Event optionsEvent = CreateEvent("vote_options"); - for (int i = 0; i < itemCount; i++) - { - char option[8]; - Format(option, sizeof(option), "%s%d", TF2CSGO_VOTE_PREFIX, i+1); - char display[TRANSLATION_LENGTH]; - Data_GetItemDisplay(vote, i, display, sizeof(display)); + for (int i = 0; i < itemCount; i++) + { + char option[8]; + Format(option, sizeof(option), "%s%d", TF2CSGO_VOTE_PREFIX, i+1); + char display[TRANSLATION_LENGTH]; + Data_GetItemDisplay(vote, i, display, sizeof(display)); - optionsEvent.SetString(option, display); - } - optionsEvent.SetInt("count", itemCount); - if (!isTF2SDKModHack) - { - optionsEvent.SetInt("voteidx", s_nNativeVoteIdx); // TODO(UPDATE) - } - optionsEvent.Fire(); - } + optionsEvent.SetString(option, display); + } + optionsEvent.SetInt("count", itemCount); + if (!isTF2SDKModHack) + { + optionsEvent.SetInt("voteidx", s_nNativeVoteIdx); // TODO(UPDATE) + } + optionsEvent.Fire(); + } - // Moved to mimic SourceSDK2013's server/vote_controller.cpp - // For whatever reason, while the other props are set first, this one's set after the vote_options event - if (CheckVoteController()) - { - SetEntProp(g_VoteController, Prop_Send, "m_nPotentialVotes", num_clients); - } + // Moved to mimic SourceSDK2013's server/vote_controller.cpp + // For whatever reason, while the other props are set first, this one's set after the vote_options event + if (CheckVoteController()) + { + SetEntProp(g_VoteController, Prop_Send, "m_nPotentialVotes", num_clients); + } - if (!isTF2SDKModHack) - { - int holder = vote.Initiator; - // required to allow the initiator to vote on their own issue - // ValveSoftware/Source-1-Games#3934 - if (sv_vote_holder_may_vote_no && holder <= MaxClients && IsClientConnected(holder) && !IsFakeClient(holder)) - { - sv_vote_holder_may_vote_no.ReplicateToClient(holder, "1"); - } - } + if (!isTF2SDKModHack) + { + int holder = vote.Initiator; + // required to allow the initiator to vote on their own issue + // ValveSoftware/Source-1-Games#3934 + if (sv_vote_holder_may_vote_no && holder <= MaxClients && IsClientConnected(holder) && !IsFakeClient(holder)) + { + sv_vote_holder_may_vote_no.ReplicateToClient(holder, "1"); + } + } - MenuAction actions = Data_GetActions(vote); + MenuAction actions = Data_GetActions(vote); - for (int i = 0; i < num_clients; ++i) - { - g_newMenuTitle[0] = '\0'; + for (int i = 0; i < num_clients; ++i) + { + g_newMenuTitle[0] = '\0'; - Action changeTitle = Plugin_Continue; - if (bCustom && actions & MenuAction_Display) - { - g_curDisplayClient = clients[i]; - changeTitle = DoAction(vote, MenuAction_Display, clients[i], 0); - } + Action changeTitle = Plugin_Continue; + if (bCustom && actions & MenuAction_Display) + { + g_curDisplayClient = clients[i]; + changeTitle = DoAction(vote, MenuAction_Display, clients[i], 0); + } - g_curDisplayClient = 0; + g_curDisplayClient = 0; - if (!bYesNo) - { - TF2CSGO_SendOptionsToClient(vote, clients[i]); - } + if (!bYesNo) + { + TF2CSGO_SendOptionsToClient(vote, clients[i]); + } - Handle voteStart = StartMessageOne("VoteStart", clients[i], USERMSG_RELIABLE); - - if(g_bUserBuf) - { - Protobuf protoStart = UserMessageToProtobuf(voteStart); - protoStart.SetInt("team", team); - protoStart.SetInt("ent_idx", Data_GetInitiator(vote)); - protoStart.SetString("disp_str", translation); - if (bCustom && changeTitle == Plugin_Changed) - { - protoStart.SetString("details_str", g_newMenuTitle); - } - else - { - protoStart.SetString("details_str", details); - } - protoStart.SetBool("is_yes_no_vote", bYesNo); - protoStart.SetString("other_team_str", otherTeamString); - protoStart.SetInt("vote_type", voteIndex); - } - else - { - BfWrite bfStart = UserMessageToBfWrite(voteStart); - bfStart.WriteByte(team); - if (!isTF2SDKModHack) - { - bfStart.WriteNum(s_nNativeVoteIdx); - } - bfStart.WriteByte(Data_GetInitiator(vote)); - bfStart.WriteString(translation); - if (bCustom && changeTitle == Plugin_Changed) - { - bfStart.WriteString(g_newMenuTitle); - } - else - { - bfStart.WriteString(details); - } - bfStart.WriteByte(bYesNo); - if (!isTF2SDKModHack && !IsFakeClient(clients[i])) - { - bfStart.WriteByte(Data_GetTarget(vote) - 1); - } - } + Handle voteStart = StartMessageOne("VoteStart", clients[i], USERMSG_RELIABLE); - EndMessage(); + if(g_bUserBuf) + { + Protobuf protoStart = UserMessageToProtobuf(voteStart); + protoStart.SetInt("team", team); + protoStart.SetInt("ent_idx", Data_GetInitiator(vote)); + protoStart.SetString("disp_str", translation); + if (bCustom && changeTitle == Plugin_Changed) + { + protoStart.SetString("details_str", g_newMenuTitle); + } + else + { + protoStart.SetString("details_str", details); + } + protoStart.SetBool("is_yes_no_vote", bYesNo); + protoStart.SetString("other_team_str", otherTeamString); + protoStart.SetInt("vote_type", voteIndex); + } + else + { + BfWrite bfStart = UserMessageToBfWrite(voteStart); + bfStart.WriteByte(team); + if (!isTF2SDKModHack) + { + bfStart.WriteNum(s_nNativeVoteIdx); + } + bfStart.WriteByte(Data_GetInitiator(vote)); + bfStart.WriteString(translation); + if (bCustom && changeTitle == Plugin_Changed) + { + bfStart.WriteString(g_newMenuTitle); + } + else + { + bfStart.WriteString(details); + } + bfStart.WriteByte(bYesNo); + if (!isTF2SDKModHack && !IsFakeClient(clients[i])) + { + bfStart.WriteByte(Data_GetTarget(vote) - 1); } + } + + EndMessage(); + } - g_curDisplayClient = 0; + g_curDisplayClient = 0; } static void TF2CSGO_SendOptionsToClient(NativeVote vote, int client) @@ -2542,26 +2542,18 @@ static void TF2CSGO_ResetVote() { if (g_EngineVersion == Engine_TF2) { - SetEntProp(g_VoteController, Prop_Send, "m_iActiveIssueIndex", INVALID_ISSUE); if (!isTF2SDKModHack) { - SetEntProp(g_VoteController, Prop_Send, "m_nVoteIdx", -1); // TODO(UPDATE) + SetEntProp(g_VoteController, Prop_Send, "m_nVoteIdx", -1); } + SetEntProp(g_VoteController, Prop_Send, "m_iActiveIssueIndex", INVALID_ISSUE); } - + SetEntProp(g_VoteController, Prop_Send, "m_iOnlyTeamToVote", g_EngineVersion == Engine_TF2 ? NATIVEVOTES_TF2_ALL_TEAMS : NATIVEVOTES_ALL_TEAMS); for (int i = 0; i < 5; i++) { SetEntProp(g_VoteController, Prop_Send, "m_nVoteOptionCount", 0, _, i); - } - SetEntProp(g_VoteController, Prop_Send, "m_nPotentialVotes", 0); - if (g_EngineVersion == Engine_TF2) - { - SetEntProp(g_VoteController, Prop_Send, "m_iOnlyTeamToVote", NATIVEVOTES_TF2_ALL_TEAMS); - } - else - { - SetEntProp(g_VoteController, Prop_Send, "m_iOnlyTeamToVote", NATIVEVOTES_ALL_TEAMS); } + SetEntProp(g_VoteController, Prop_Send, "m_nPotentialVotes", 0); SetEntProp(g_VoteController, Prop_Send, "m_bIsYesNoVote", true); } } @@ -2701,7 +2693,6 @@ static bool TF2_VoteTypeToTranslation(NativeVotesType voteType, char[] translati { strcopy(translation, maxlength, TF2_VOTE_EXTEND_START); } - default: { strcopy(translation, maxlength, TF2_VOTE_CUSTOM); @@ -2739,7 +2730,6 @@ static void TF2_VotePassToTranslation(NativeVotesPassType passType, char[] trans { strcopy(translation, maxlength, TF2_VOTE_SCRAMBLE_PASSED); } - case NativeVotesPass_ChgMission: { strcopy(translation, maxlength, TF2_VOTE_CHANGEMISSION_PASSED); @@ -2768,7 +2758,6 @@ static void TF2_VotePassToTranslation(NativeVotesPassType passType, char[] trans { strcopy(translation, maxlength, TF2_VOTE_CLASSLIMITS_DISABLE_PASSED); } - default: { strcopy(translation, maxlength, TF2_VOTE_CUSTOM); @@ -2778,13 +2767,14 @@ static void TF2_VotePassToTranslation(NativeVotesPassType passType, char[] trans static void TF2_AddDefaultVotes(ArrayList hVoteTypes, bool bHideDisabledVotes) { - int globalEnable = g_Cvar_Votes_Enabled.BoolValue; + bool globalEnable = g_Cvar_Votes_Enabled.BoolValue; if (!globalEnable && bHideDisabledVotes) { return; } - if (g_EngineVersion == Engine_TF2 && GameRules_GetProp("m_bPlayingMannVsMachine")) + // Not every TF2 mod supports MVM + if (FindSendPropInfo("CTFGameRulesProxy", "m_bPlayingMannVsMachine") > 0 && GameRules_GetProp("m_bPlayingMannVsMachine")) { // Default MvM vote types @@ -2951,7 +2941,6 @@ static bool CSGO_VoteTypeToTranslation(NativeVotesType voteType, char[] translat { strcopy(translation, maxlength, CSGO_VOTE_CONTINUE_START); } - default: { strcopy(translation, maxlength, CSGO_VOTE_CUSTOM); @@ -3005,7 +2994,6 @@ static void CSGO_VotePassToTranslation(NativeVotesPassType passType, char[] tran { strcopy(translation, maxlength, CSGO_VOTE_CONTINUE_PASSED); } - default: { strcopy(translation, maxlength, CSGO_VOTE_CUSTOM); @@ -3029,7 +3017,6 @@ static void CSGO_VoteTypeToVoteOtherTeamString(NativeVotesType voteType, char[] { strcopy(otherTeamString, maxlength, CSGO_VOTE_CONTINUE_OTHERTEAM); } - default: { strcopy(otherTeamString, maxlength, CSGO_VOTE_UNIMPLEMENTED_OTHERTEAM); @@ -3141,7 +3128,7 @@ static stock bool TF2_VoteTypeToVoteString(NativeVotesType voteType, char[] vote static stock NativeVotesType TF2_VoteStringToVoteType(const char[] voteString) { - NativeVotesType voteType = NativeVotesType_None; + NativeVotesType voteType; if (StrEqual(voteString, TF2CSGO_VOTE_STRING_KICK, false)) { @@ -3169,13 +3156,11 @@ static stock NativeVotesType TF2_VoteStringToVoteType(const char[] voteString) } else if (StrEqual(voteString, TF2_VOTE_STRING_AUTOBALANCE, false)) { - voteType = g_Cvar_AutoBalance.BoolValue ? - NativeVotesType_AutoBalanceOff : NativeVotesType_AutoBalanceOn; + voteType = g_Cvar_AutoBalance.BoolValue ? NativeVotesType_AutoBalanceOff : NativeVotesType_AutoBalanceOn; } else if (StrEqual(voteString, TF2_VOTE_STRING_CLASSLIMIT, false)) { - voteType = g_Cvar_ClassLimit.IntValue ? - NativeVotesType_ClassLimitsOff : NativeVotesType_ClassLimitsOn; + voteType = g_Cvar_ClassLimit.IntValue ? NativeVotesType_ClassLimitsOff : NativeVotesType_ClassLimitsOn; } else if (StrEqual(voteString, TF2_VOTE_STRING_EXTEND, false)) { @@ -3185,9 +3170,9 @@ static stock NativeVotesType TF2_VoteStringToVoteType(const char[] voteString) { voteType = NativeVotesType_ChgMission; } - else if (StrEqual(voteString, TF2_VOTE_STRING_CHANGEMISSION, false)) + else { - voteType = NativeVotesType_ChgMission; + voteType = NativeVotesType_None; } return voteType; @@ -3195,7 +3180,7 @@ static stock NativeVotesType TF2_VoteStringToVoteType(const char[] voteString) static stock NativeVotesOverride TF2_VoteTypeToVoteOverride(NativeVotesType voteType) { - NativeVotesOverride overrideType = NativeVotesOverride_None; + NativeVotesOverride overrideType; switch (voteType) { @@ -3235,6 +3220,10 @@ static stock NativeVotesOverride TF2_VoteTypeToVoteOverride(NativeVotesType vote { overrideType = NativeVotesOverride_Extend; } + default: + { + overrideType = NativeVotesOverride_None; + } } return overrideType; @@ -3242,8 +3231,8 @@ static stock NativeVotesOverride TF2_VoteTypeToVoteOverride(NativeVotesType vote static stock NativeVotesType TF2_VoteOverrideToVoteType(NativeVotesOverride overrideType) { - NativeVotesType voteType = NativeVotesType_None; - + NativeVotesType voteType; + switch (overrideType) { case NativeVotesOverride_Restart: @@ -3285,7 +3274,11 @@ static stock NativeVotesType TF2_VoteOverrideToVoteType(NativeVotesOverride over case NativeVotesOverride_Extend: { voteType = NativeVotesType_Extend; - } + } + default: + { + voteType = NativeVotesType_None; + } } return voteType; @@ -3293,7 +3286,7 @@ static stock NativeVotesType TF2_VoteOverrideToVoteType(NativeVotesOverride over static stock NativeVotesOverride TF2_VoteStringToVoteOverride(const char[] voteString) { - NativeVotesOverride overrideType = NativeVotesOverride_None; + NativeVotesOverride overrideType; if (StrEqual(voteString, TF2CSGO_VOTE_STRING_KICK, false)) { @@ -3335,6 +3328,10 @@ static stock NativeVotesOverride TF2_VoteStringToVoteOverride(const char[] voteS { overrideType = NativeVotesOverride_ChgMission; } + else + { + overrideType = NativeVotesOverride_None; + } #if defined LOG LogMessage("Resolved \"%s\" to %d", voteString, overrideType); @@ -3392,7 +3389,6 @@ static stock bool TF2_OverrideTypeToVoteString(NativeVotesOverride overrideType, { strcopy(voteString, maxlength, TF2_VOTE_STRING_EXTEND); } - case NativeVotesOverride_ChgMission: { strcopy(voteString, maxlength, TF2_VOTE_STRING_CHANGEMISSION); From fbd0a326e142faafe5353d7cfbe7f45b2478dd20 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Thu, 22 Jan 2026 20:02:57 +0100 Subject: [PATCH 039/128] Fix cvar description --- addons/sourcemod/scripting/nativevotes_nominations.sp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/sourcemod/scripting/nativevotes_nominations.sp b/addons/sourcemod/scripting/nativevotes_nominations.sp index 466a564..e84cc21 100644 --- a/addons/sourcemod/scripting/nativevotes_nominations.sp +++ b/addons/sourcemod/scripting/nativevotes_nominations.sp @@ -98,7 +98,7 @@ public void OnPluginStart() g_ConVars[excludecurrent] = CreateConVar("sm_nominate_excludecurrent", "1", "Specifies if the MapChooser excluded maps should also be excluded from Nominations", _, true, 0.0, true, 1.0); g_ConVars[maxmatches] = CreateConVar("sm_nominate_maxfound", "0", "Maximum number of nomination matches to add to the menu. 0 = infinite.", _, true, 0.0); //g_ConVars[allow_workshop] = CreateConVar("sm_nominate_allow_workshop", "0", "Specifies if unlisted workshop maps can be nominated", _, true, 0.0, true, 1.0); - g_ConVars[use_callvote] = CreateConVar("sm_nominate_use_callvote", "1", "Specifies the command to use to open the nomination vote menu", _, true, 0.0, true, 1.0); + g_ConVars[use_callvote] = CreateConVar("sm_nominate_use_callvote", "1", "Specifies whether to execute callvote when nominating without specifying the map.", _, true, 0.0, true, 1.0); g_ConVars[nextlevel_allowed] = FindConVar("sv_vote_issue_nextlevel_allowed"); From 804825328664fa17e0197e2ecbb3b80b0875d159 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Thu, 22 Jan 2026 20:10:44 +0100 Subject: [PATCH 040/128] Update README.md --- README.md | 46 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 735cea5..7c3e35b 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ This fork aims to expand upon [0 - Allow (success = instant change), 1 - Deny| ## [Nominations](https://github.com/Heapons/sourcemod-nativevotes-updated/blob/master/addons/sourcemod/scripting/nativevotes_nominations.sp) ### Team Fortress 2 - Attempting to nominate with no argument will open **Vote Setup** (`callvote`). -- Support partial map name matches. \ No newline at end of file + - Can be toggled with `sm_nominate_use_callvote`. +- Support partial map name matches. +--- +|Name|Default Value|Description| +|-|-|-| +|`sm_nominate_excludeold`|`1`|Specifies if the current map should be excluded from the Nominations list| +|`sm_nominate_excludecurrent`|`1`|Specifies if the MapChooser excluded maps should also be excluded from Nominations| +|`sm_nominate_maxfound`|`0`|Maximum number of nomination matches to add to the menu.
0 = infinite| +|`sm_nominate_use_callvote`|`1`|Specifies whether to execute callvote when nominating without specifying the map.| + +## [MapChooser](https://github.com/Heapons/sourcemod-nativevotes-updated/blob/master/addons/sourcemod/scripting/nativevotes_mapchooser.sp) +- Automatically generate mapcycle files. + - Can be refreshed with `sm_reloadmaplist`. +- **[TF2]** Clean up workshop maps to reduce disk size. +--- +|Name|Default Value|Description| +|-|-|-| +|`sm_mapvote_endvote`|`1`|Specifies if MapChooser should run an end of map vote| +|`sm_mapvote_start`|`3.0`|Specifies when to start the vote based on time remaining (in minutes)| +|`sm_mapvote_startround`|`2.0`|Specifies when to start the vote based on rounds remaining| +|`sm_mapvote_startfrags`|`5.0`|Specifies when to start the vote based on frags remaining| +|`sm_extendmap_timestep`|`15`|Specifies how many more minutes each extension makes| +|`sm_extendmap_roundstep`|`5`|Specifies how many more rounds each extension makes| +|`sm_extendmap_fragstep`|`10`|Specifies how many more frags are allowed when map is extended| +|`sm_mapvote_exclude`|`5`|Specifies how many past maps to exclude from the vote| +|`sm_mapvote_include`|`5`|Specifies how many maps to include in the vote| +|`sm_mapvote_novote`|`1`|Specifies whether MapChooser should pick a map if no votes are received| +|`sm_mapvote_extend`|`0`|Number of extensions allowed each map| +|`sm_mapvote_dontchange`|`1`|Specifies if a 'Don't Change' option should be added to early votes| +|`sm_mapvote_voteduration`|`20`|Specifies how long the mapvote should be available for (in seconds)| +|`sm_mapvote_runoff`|`0`|Hold runoff votes if winning choice is less than a certain margin| +|`sm_mapvote_runoffpercent`|`50`|If winning choice has less than this percent of votes, hold a runoff| +|`sm_mapcycle_auto`|`0`|Specifies whether to automatically populate the maps list| +|`sm_mapcycle_exclude`|`.*itemtest.*\|background01\|^tr.*$`|Specifies which maps shouldn't be automatically added (regex pattern)| +|`sm_mapvote_workshop_cleanup`|`0`|Specifies whether to automatically cleanup workshop maps on map change
[ **Team Fortress 2**](https://store.steampowered.com/app/440) (or its mods) only| \ No newline at end of file From 49f775be83fb51f133c5a2c0047d290fc7ce805c Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Thu, 22 Jan 2026 20:30:14 +0100 Subject: [PATCH 041/128] Fix cvars --- README.md | 2 +- addons/sourcemod/scripting/nativevotes_mapchooser.sp | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 7c3e35b..2d5327b 100644 --- a/README.md +++ b/README.md @@ -76,4 +76,4 @@ This fork aims to expand upon [ **Team Fortress 2**](https://store.steampowered.com/app/440) (or its mods) only| \ No newline at end of file +|`sm_workshop_map_cleanup`|`0`|Specifies whether to automatically cleanup workshop maps on map change
[ **Team Fortress 2**](https://store.steampowered.com/app/440) (or its mods) only| \ No newline at end of file diff --git a/addons/sourcemod/scripting/nativevotes_mapchooser.sp b/addons/sourcemod/scripting/nativevotes_mapchooser.sp index a217158..637fd59 100644 --- a/addons/sourcemod/scripting/nativevotes_mapchooser.sp +++ b/addons/sourcemod/scripting/nativevotes_mapchooser.sp @@ -82,6 +82,7 @@ enum mapvote_runoffpercent, mapcycle_auto, mapcycle_exclude, + workshop_maplist, workshop_cleanup, MAX_CONVARS @@ -169,10 +170,11 @@ public void OnPluginStart() g_ConVars[mapvote_runoff] = CreateConVar("sm_mapvote_runoff", "0", "Hold run of votes if winning choice is less than a certain margin", _, true, 0.0, true, 1.0); g_ConVars[mapvote_runoffpercent] = CreateConVar("sm_mapvote_runoffpercent", "50", "If winning choice has less than this percent of votes, hold a runoff", _, true, 0.0, true, 100.0); g_ConVars[mapcycle_auto] = CreateConVar("sm_mapcycle_auto", "0", "Specifies whether or not to automatically populate the maps list.", _, true, 0.0, true, 1.0); - g_ConVars[mapcycle_exclude] = CreateConVar("sm_mapcycle_exclude", ".*itemtest.*|background01|^tr.*$", "Specifies which maps shouldn't be automatically added. Defined with a regex pattern."); + g_ConVars[mapcycle_exclude] = CreateConVar("sm_mapcycle_exclude", ".*itemtest.*|background01|^tr.*$", "Specifies which maps shouldn't be automatically added with a regex pattern."); if (engine != Engine_SDK2013 && engine == Engine_TF2) { - g_ConVars[workshop_cleanup] = CreateConVar("sm_mapvote_workshop_cleanup", "0", "Specifies whether or not to automatically workshop maps on map change", _, true, 0.0, true, 1.0); + g_ConVars[workshop_maplist] = CreateConVar("sm_workshop_map_collection", "", "Specifies the file containing the workshop maps to include in the map list", _, true, 0.0, true, 1.0); + g_ConVars[workshop_cleanup] = CreateConVar("sm_workshop_map_cleanup", "0", "Specifies whether or not to automatically workshop maps on map change", _, true, 0.0, true, 1.0); } RegAdminCmd("sm_mapvote", Command_MapVote, ADMFLAG_CHANGEMAP, "Forces MapChooser to attempt to run a map vote now."); @@ -1592,7 +1594,8 @@ void PopulateMapList() char mapcycle_generated[PLATFORM_MAX_PATH]; Format(mapcycle_generated, sizeof(mapcycle_generated), "%s/mapcycle_generated.txt", configsPath); - Handle mapcycleFile = OpenFile(mapcycle_generated, "wt"); + + File mapcycleFile = OpenFile(mapcycle_generated, "wt"); if (mapcycleFile != null) { mapcycleFile = OpenFile(mapcycle_generated, "a"); @@ -1632,7 +1635,7 @@ void PopulateMapList() if (regex.Match(mapName) >= 1) continue; - WriteFileLine(mapcycleFile, "%s", mapName); + mapcycleFile.WriteLine("%s", mapName); } delete dir; delete regex; From 6ea8150d79d25d990b2eecd4e2ca123534bbb6db Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Wed, 28 Jan 2026 14:43:14 +0100 Subject: [PATCH 042/128] Update variables And bumped the version. --- addons/sourcemod/scripting/nativevotes_basecommands.sp | 9 ++++++++- addons/sourcemod/scripting/nativevotes_basevotes.sp | 4 ++-- .../sourcemod/scripting/nativevotes_basevotes/votemap.sp | 2 +- addons/sourcemod/scripting/nativevotes_funvotes.sp | 2 +- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_basecommands.sp b/addons/sourcemod/scripting/nativevotes_basecommands.sp index 0075c64..3808db1 100644 --- a/addons/sourcemod/scripting/nativevotes_basecommands.sp +++ b/addons/sourcemod/scripting/nativevotes_basecommands.sp @@ -48,7 +48,7 @@ public Plugin myinfo = name = "NativeVotes | Basic Commands", author = "Powerlord and AlliedModders LLC", description = "Revote and Cancel support for NativeVotes", - version = "26w04a", + version = "26w05a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" } @@ -120,6 +120,13 @@ public void AdminMenu_CancelVote(Handle topmenu, TopMenuAction action, TopMenuOb public Action Command_ReVote(int client, const char[] command, int argc) { +/* + if (FindSendPropInfo("CVoteController", "m_nVoteIdx") > 0) + { + LogError("[NativeVotes] Re-voting is buggy in Team Fortress 2 and SDK 2025 mods."); + return Plugin_Continue; + } +*/ if (client <= 0) { return Plugin_Continue; diff --git a/addons/sourcemod/scripting/nativevotes_basevotes.sp b/addons/sourcemod/scripting/nativevotes_basevotes.sp index 5510b19..2d7c5c5 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes.sp @@ -46,7 +46,7 @@ public Plugin:myinfo = name = "NativeVotes | Basic Votes", author = "Powerlord and AlliedModders LLC", description = "NativeVotes Basic Vote Commands", - version = "26w04a", + version = "26w05a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -693,7 +693,7 @@ bool TestVoteDelay(int client) public Action Timer_ChangeMap(Handle timer, DataPack dp) { - char mapname[96]; + char mapname[PLATFORM_MAX_PATH]; ResetPack(dp); ReadPackString(dp, mapname, sizeof(mapname)); diff --git a/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp b/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp index 5f8e175..6fc77d2 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp @@ -316,7 +316,7 @@ int LoadMapList(Handle menu) RemoveAllMenuItems(menu); - char map_name[96]; + char map_name[PLATFORM_MAX_PATH]; int map_count = GetArraySize(g_map_array); for (int i = 0; i < map_count; i++) diff --git a/addons/sourcemod/scripting/nativevotes_funvotes.sp b/addons/sourcemod/scripting/nativevotes_funvotes.sp index ab4591a..7f02d18 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes.sp @@ -47,7 +47,7 @@ public Plugin myinfo = name = "NativeVotes | Fun Votes", author = "Powerlord and AlliedModders LLC", description = "NativeVotes Fun Vote Commands", - version = "26w04a", + version = "26w05a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; From f24c6117c4edda5c2b68dc2f87cb31d41bc6584c Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Wed, 28 Jan 2026 15:06:41 +0100 Subject: [PATCH 043/128] Update variables --- .../nativevotes_kickvote_immunity.sp | 50 +++++++++++-------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp b/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp index f7fa5e5..1e952ac 100644 --- a/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp +++ b/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp @@ -41,9 +41,16 @@ #pragma semicolon 1 #pragma newdecls required -ConVar g_Cvar_Votes; -ConVar g_Cvar_KickVote; -ConVar g_Cvar_KickVoteMvM; +enum +{ + sv_allow_votes, + sv_vote_issue_kick_allowed, + sv_vote_issue_kick_allowed_mvm, + + MAX_CONVARS +} + +ConVar g_ConVars[MAX_CONVARS]; bool g_bRegistered = false; @@ -53,22 +60,26 @@ public Plugin myinfo = { name = "NativeVotes | Kick Vote Immunity", author = "Powerlord", description = "Causes TF2 kick votes to fail against people who the current user can't target.", - version = "26w04a", + version = "26w05a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; public void OnPluginStart() { - g_Cvar_Votes = FindConVar("sv_allow_votes"); - g_Cvar_KickVote = FindConVar("sv_vote_issue_kick_allowed"); - g_Cvar_KickVoteMvM = FindConVar("sv_vote_issue_kick_allowed_mvm"); + g_ConVars[sv_allow_votes] = FindConVar("sv_allow_votes"); + g_ConVars[sv_vote_issue_kick_allowed] = FindConVar("sv_vote_issue_kick_allowed"); + g_ConVars[sv_vote_issue_kick_allowed_mvm] = FindConVar("sv_vote_issue_kick_allowed_mvm"); - HookConVarChange(g_Cvar_Votes, Cvar_CheckEnable); - HookConVarChange(g_Cvar_KickVote, Cvar_CheckEnable); - HookConVarChange(g_Cvar_KickVoteMvM, Cvar_CheckEnable); + for (int i = 0; i < MAX_CONVARS; i++) + { + if (g_ConVars[i] != null) + { + g_ConVars[i].AddChangeHook(ConVar_CheckEnable); + } + } LoadTranslations("common.phrases"); - } +} public void OnAllPluginsLoaded() { @@ -96,8 +107,7 @@ public void OnPluginEnd() void RegisterVoteCommand() { - if (!g_bRegistered && - NativeVotes_AreVoteCommandsSupported()) + if (!g_bRegistered && NativeVotes_AreVoteCommandsSupported()) { NativeVotes_RegisterVoteCommand(NativeVotesOverride_Kick, KickVoteHandler); g_bRegistered = true; @@ -113,7 +123,7 @@ void UnregisterVoteCommand() } } -public void Cvar_CheckEnable(ConVar convar, const char[] oldValue, const char[] newValue) +public void ConVar_CheckEnable(ConVar convar, const char[] oldValue, const char[] newValue) { if (!g_bMapActive) return; @@ -126,9 +136,9 @@ void CheckStatus() bool bIsMvM = IsMvM(); if (g_bRegistered) { - if (!g_Cvar_Votes.BoolValue || - (bIsMvM && !g_Cvar_KickVoteMvM.BoolValue) || - (!bIsMvM && !g_Cvar_KickVote.BoolValue) + if (!g_ConVars[sv_allow_votes].BoolValue || + (bIsMvM && !g_ConVars[sv_vote_issue_kick_allowed_mvm].BoolValue) || + (!bIsMvM && !g_ConVars[sv_vote_issue_kick_allowed].BoolValue) ) { LogMessage("Disabling."); @@ -137,9 +147,9 @@ void CheckStatus() } else { - if (g_Cvar_Votes.BoolValue && - ((bIsMvM && g_Cvar_KickVoteMvM.BoolValue) || - (!bIsMvM && g_Cvar_KickVote.BoolValue)) + if (g_ConVars[sv_allow_votes].BoolValue && + ((bIsMvM && g_ConVars[sv_vote_issue_kick_allowed_mvm].BoolValue) || + (!bIsMvM && g_ConVars[sv_vote_issue_kick_allowed].BoolValue)) ) { LogMessage("Enabling."); From a4f6eb9df612de12b39911cbb62f4867f0f607bc Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Wed, 28 Jan 2026 15:07:22 +0100 Subject: [PATCH 044/128] Tidy up --- addons/sourcemod/scripting/nativevotes.sp | 150 +++++++++--------- .../scripting/nativevotes_nominations.sp | 67 ++++---- 2 files changed, 100 insertions(+), 117 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes.sp b/addons/sourcemod/scripting/nativevotes.sp index e8a5174..82c049a 100644 --- a/addons/sourcemod/scripting/nativevotes.sp +++ b/addons/sourcemod/scripting/nativevotes.sp @@ -46,7 +46,7 @@ EngineVersion g_EngineVersion = Engine_Unknown; #include "nativevotes/data-keyvalues.sp" -#define LOGTAG "NV" +#define LOGTAG "NativeVotes" #define MAX_VOTE_DETAILS_LENGTH 64 // From SourceSDK2013's shareddefs.h #define TRANSLATION_LENGTH 192 @@ -150,7 +150,7 @@ public Plugin myinfo = name = "NativeVotes", author = "Powerlord", description = "Voting API to use the game's native vote panels. Compatible with L4D, L4D2, TF2, and CS:GO.", - version = "26w04a", + version = "26w05a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" } @@ -311,18 +311,15 @@ public Action Timer_RetryCallVote(Handle timer, any data) public void ProcessMapList() { - char stringTableName[64] = "ServerMapCycle", stringTableItem[64] = "ServerMapCycle"; + char stringTable[64] = "ServerMapCycle"; if (FindSendPropInfo("CTFGameRulesProxy", "m_bPlayingMannVsMachine") > 0 && GameRules_GetProp("m_bPlayingMannVsMachine")) { - Format(stringTableName, sizeof(stringTableName), "%sMvM", stringTableName); - Format(stringTableItem, sizeof(stringTableItem), "%sMvM", stringTableItem); + Format(stringTable, sizeof(stringTable), "%sMvM", stringTable); } - int stringTableIndex = FindStringTable(stringTableName); - int stringIndex = FindStringIndex(stringTableIndex, stringTableItem); + int stringTableIndex = FindStringTable(stringTable); + int stringIndex = FindStringIndex(stringTableIndex, stringTable); - StringMap overrideList = new StringMap(); - // Maplist resets every map int length = GetStringTableDataLength(stringTableIndex, stringIndex); char[] mapData = new char[length]; @@ -331,6 +328,7 @@ public void ProcessMapList() // We'll get an extra blank entry if we don't do this TrimString(mapData); + StringMap overrideList = new StringMap(); ExplodeStringToStringMap(mapData, "\n", overrideList, PLATFORM_MAX_PATH, ImplodePart_Key); Action mapResult = Plugin_Continue; @@ -338,30 +336,29 @@ public void ProcessMapList() Call_PushCell(overrideList); Call_Finish(mapResult); - if (mapResult == Plugin_Changed && overrideList.Size > 0) + if (mapResult != Plugin_Changed || overrideList.Size <= 0) { + delete overrideList; + return; + } + #if defined LOG - LogMessage("Overriding map list with %d maps", overrideList.Size); + LogMessage("Overriding map list with %d maps", overrideList.Size); #endif - g_MapOverrides = overrideList; - - int maxLength = GetStringMapImplodeSize(overrideList, 1, ImplodePart_Key); - - char[] newMapData = new char[maxLength]; - int newLength = ImplodeStringMapToString(overrideList, "\n", newMapData, maxLength, ImplodePart_Key); - if (newLength < maxLength && newMapData[newLength] != '\n') - { - // do this to avoid a StrCat - newLength += strcopy(newMapData[newLength], maxLength, "\n") + 1; - } + g_MapOverrides = overrideList; + + int maxLength = GetStringMapImplodeSize(overrideList, 1, ImplodePart_Key); - SetStringTableData(stringTableIndex, stringIndex, newMapData, newLength); - } - else + char[] newMapData = new char[maxLength]; + int newLength = ImplodeStringMapToString(overrideList, "\n", newMapData, maxLength, ImplodePart_Key); + if (newLength < maxLength && newMapData[newLength] != '\n') { - delete overrideList; + // do this to avoid a StrCat + newLength += strcopy(newMapData[newLength], maxLength, "\n") + 1; } + + SetStringTableData(stringTableIndex, stringIndex, newMapData, newLength); } public void OnClientDisconnect_Post(int client) @@ -502,7 +499,7 @@ public Action Command_CallVote(int client, const char[] command, int argc) (overrideType == NativeVotesOverride_ChgLevel || overrideType == NativeVotesOverride_NextLevel)) { - char map[96]; + char map[PLATFORM_MAX_PATH]; GetCmdArg(2, map, sizeof(map)); g_MapOverrides.GetString(map, argument, sizeof(argument)); @@ -563,7 +560,7 @@ public Action Command_CallVote(int client, const char[] command, int argc) } else { - char map[96]; + char map[PLATFORM_MAX_PATH]; GetCmdArg(2, map, sizeof(map)); g_MapOverrides.GetString(map, argument, sizeof(argument)); @@ -720,7 +717,6 @@ void OnVoteSelect(NativeVote vote, int client, int item) g_ClientVotes[client] = item; g_hVotes.Set(item, g_hVotes.Get(item) + 1); g_NumVotes++; - Game_UpdateVoteCounts(g_hVotes, g_TotalClients); @@ -1409,7 +1405,7 @@ void Internal_Reset(bool cancel=false) { delete g_hDisplayTimer; } - + if (!cancel) { CreateTimer(5.0, Game_ResetVote, TIMER_FLAG_NO_MAPCHANGE); @@ -1546,7 +1542,7 @@ void PerformVisChecks(int client, ArrayList hVoteTypes) //---------------------------------------------------------------------------- // Natives -// native bool:NativeVotes_IsVoteTypeSupported(NativeVotesType:voteType); +// native bool NativeVotes_IsVoteTypeSupported(NativeVotesType voteType); public int Native_IsVoteTypeSupported(Handle plugin, int numParams) { NativeVotesType type = GetNativeCell(1); @@ -1554,8 +1550,7 @@ public int Native_IsVoteTypeSupported(Handle plugin, int numParams) return Game_CheckVoteType(type); } -// native Handle:NativeVotes_Create(MenuHandler:handler, NativeVotesType:voteType, -// MenuAction:actions=NATIVEVOTES_ACTIONS_DEFAULT); +// native Handle NativeVotes_Create(MenuHandler handler, NativeVotesType voteType, MenuAction actions = NATIVEVOTES_ACTIONS_DEFAULT); public int Native_Create(Handle plugin, int numParams) { Function handler = GetNativeFunction(1); @@ -1590,7 +1585,7 @@ public int Native_Create(Handle plugin, int numParams) return view_as(vote); } -// native Handle:NativeVotes_Close(Handle:vote); +// native Handle NativeVotes_Close(Handle vote); public int Native_Close(Handle plugin, int numParams) { NativeVote vote = GetNativeCell(1); @@ -1626,7 +1621,7 @@ public int Native_Close(Handle plugin, int numParams) Data_CloseVote(vote); } -// native bool:NativeVotes_Display(Handle:vote, clients[], numClients, time, flags=0); +// native bool NativeVotes_Display(Handle vote, int clients[], int numClients, int time, int flags=0); public int Native_Display(Handle plugin, int numParams) { if (Internal_IsVoteInProgress()) @@ -1662,7 +1657,7 @@ public int Native_Display(Handle plugin, int numParams) } -// native bool:NativeVotes_AddItem(Handle:vote, const String:info[], const String:display[]); +// native bool NativeVotes_AddItem(Handle vote, const char[] info, const char[] display); public int Native_AddItem(Handle plugin, int numParams) { NativeVote vote = GetNativeCell(1); @@ -1687,7 +1682,7 @@ public int Native_AddItem(Handle plugin, int numParams) return Data_AddItem(vote, info, display); } -// native bool:NativeVotes_InsertItem(Handle:vote, position, const String:info[], const String:display[]); +// native bool NativeVotes_InsertItem(Handle vote, int position, const char[] info, const char[] display); public int Native_InsertItem(Handle plugin, int numParams) { NativeVote vote = GetNativeCell(1); @@ -1721,7 +1716,7 @@ public int Native_InsertItem(Handle plugin, int numParams) } -// native bool:NativeVotes_RemoveItem(Handle:vote, position); +// native bool NativeVotes_RemoveItem(Handle vote, int position); public int Native_RemoveItem(Handle plugin, int numParams) { NativeVote vote = GetNativeCell(1); @@ -1743,7 +1738,7 @@ public int Native_RemoveItem(Handle plugin, int numParams) return Data_RemoveItem(vote, position); } -// native NativeVotes_RemoveAllItems(Handle:vote); +// native NativeVotes_RemoveAllItems(Handle vote); public int Native_RemoveAllItems(Handle plugin, int numParams) { NativeVote vote = GetNativeCell(1); @@ -1785,7 +1780,7 @@ public int Native_GetItem(Handle plugin, int numParams) } } -// native NativeVotes_GetItemCount(Handle:vote); +// native NativeVotes_GetItemCount(Handle vote); public int Native_GetItemCount(Handle plugin, int numParams) { NativeVote vote = GetNativeCell(1); @@ -1798,7 +1793,7 @@ public int Native_GetItemCount(Handle plugin, int numParams) return Data_GetItemCount(vote); } -// native NativeVotes_GetDetails(Handle:vote, String:buffer[], maxlength); +// native NativeVotes_GetDetails(Handle vote, const char[] buffer, int maxlength); public int Native_GetDetails(Handle plugin, int numParams) { NativeVote vote = GetNativeCell(1); @@ -1817,7 +1812,7 @@ public int Native_GetDetails(Handle plugin, int numParams) SetNativeString(2, details, len); } -// native NativeVotes_SetDetails(Handle:vote, String:fmt[], any:...); +// native NativeVotes_SetDetails(Handle vote, const char[] fmt, any:...); public int Native_SetDetails(Handle plugin, int numParams) { NativeVote vote = GetNativeCell(1); @@ -1835,7 +1830,7 @@ public int Native_SetDetails(Handle plugin, int numParams) Data_SetDetails(vote, details); } -// native NativeVotes_GetDetails(Handle:vote, String:buffer[], maxlength); +// native NativeVotes_GetDetails(Handle vote, const char[] buffer, int maxlength); public int Native_GetTitle(Handle plugin, int numParams) { NativeVote vote = GetNativeCell(1); @@ -1854,7 +1849,7 @@ public int Native_GetTitle(Handle plugin, int numParams) SetNativeString(2, title, len); } -// native NativeVotes_SetTitle(Handle:vote, String:fmt[], any:...); +// native NativeVotes_SetTitle(Handle vote, const char[] fmt, any:...); public int Native_SetTitle(Handle plugin, int numParams) { NativeVote vote = GetNativeCell(1); @@ -1872,7 +1867,7 @@ public int Native_SetTitle(Handle plugin, int numParams) Data_SetTitle(vote, details); } -// native bool:NativeVotes_IsVoteInProgress(); +// native bool NativeVotes_IsVoteInProgress(); public int Native_IsVoteInProgress(Handle plugin, int numParams) { return Internal_IsVoteInProgress() || Game_IsVoteInProgress(); @@ -1884,7 +1879,7 @@ public int Native_GetMaxItems(Handle plugin, int numParams) return Game_GetMaxItems(); } -// native NativeVotes_GetOptionFlags(Handle:vote); +// native NativeVotes_GetOptionFlags(Handle vote); public int Native_GetOptionFlags(Handle plugin, int numParams) { NativeVote vote = GetNativeCell(1); @@ -1897,7 +1892,7 @@ public int Native_GetOptionFlags(Handle plugin, int numParams) return Data_GetFlags(vote); } -// native NativeVotes_SetOptionFlags(Handle:vote, flags); +// native NativeVotes_SetOptionFlags(Handle vote, flags); public int Native_SetOptionFlags(Handle plugin, int numParams) { NativeVote vote = GetNativeCell(1); @@ -1954,7 +1949,7 @@ public int Native_Cancel(Handle plugin, int numParams) CancelVoting(); } -// native NativeVotes_SetResultCallback(Handle:vote, NativeVotes_VoteHandler:callback); +// native NativeVotes_SetResultCallback(Handle vote, NativeVotes_VoteHandler callback); public int Native_SetResultCallback(Handle plugin, int numParams) { NativeVote vote = GetNativeCell(1); @@ -1993,7 +1988,7 @@ public int Native_CheckVoteDelay(Handle plugin, int numParams) return (g_NextVote - curTime); } -// native bool:NativeVotes_IsClientInVotePool(client); +// native bool NativeVotes_IsClientInVotePool(int client); public int Native_IsClientInVotePool(Handle plugin, int numParams) { int client = GetNativeCell(1); @@ -2013,7 +2008,7 @@ public int Native_IsClientInVotePool(Handle plugin, int numParams) return Internal_IsClientInVotePool(client); } -// native bool:NativeVotes_RedrawClientVote(client, bool:revotes=true); +// native bool NativeVotes_RedrawClientVote(int client, bool revotes = true); public int Native_RedrawClientVote(Handle plugin, int numParams) { int client = GetNativeCell(1); @@ -2048,7 +2043,7 @@ public int Native_RedrawClientVote(Handle plugin, int numParams) return Internal_RedrawToClient(client, revote); } -// native NativeVotesType:NativeVotes_GetType(Handle:vote); +// native NativeVotesType NativeVotes_GetType(Handle vote); public int Native_GetType(Handle plugin, int numParams) { NativeVote vote = GetNativeCell(1); @@ -2061,7 +2056,7 @@ public int Native_GetType(Handle plugin, int numParams) return view_as(Data_GetType(vote)); } -// native NativeVotes_GetTeam(Handle:vote); +// native NativeVotes_GetTeam(Handle vote); public int Native_GetTeam(Handle plugin, int numParams) { NativeVote vote = GetNativeCell(1); @@ -2075,7 +2070,7 @@ public int Native_GetTeam(Handle plugin, int numParams) } -// native NativeVotes_SetTeam(Handle:vote, team); +// native NativeVotes_SetTeam(Handle vote, int team); public int Native_SetTeam(Handle plugin, int numParams) { NativeVote vote = GetNativeCell(1); @@ -2103,7 +2098,7 @@ public int Native_SetTeam(Handle plugin, int numParams) Data_SetTeam(vote, team); } -// native NativeVotes_GetInitiator(Handle:vote); +// native NativeVotes_GetInitiator(Handle vote); public int Native_GetInitiator(Handle plugin, int numParams) { NativeVote vote = GetNativeCell(1); @@ -2116,7 +2111,7 @@ public int Native_GetInitiator(Handle plugin, int numParams) return Data_GetInitiator(vote); } -// native NativeVotes_SetInitiator(Handle:vote, client); +// native int NativeVotes_SetInitiator(Handle vote, int client); public int Native_SetInitiator(Handle plugin, int numParams) { NativeVote vote = GetNativeCell(1); @@ -2130,7 +2125,7 @@ public int Native_SetInitiator(Handle plugin, int numParams) Data_SetInitiator(vote, initiator); } -// native NativeVotes_DisplayPass(Handle:vote, const String:details[]=""); +// native NativeVotes_DisplayPass(Handle vote, const char[] details = ""); public int Native_DisplayPass(Handle plugin, int numParams) { NativeVote vote = GetNativeCell(1); @@ -2154,7 +2149,7 @@ public int Native_DisplayPass(Handle plugin, int numParams) } -// native NativeVotes_DisplayPassCustomToOne(Handle:vote, client, const String:format[], any:...); +// native NativeVotes_DisplayPassCustomToOne(Handle vote, client, const char[] format, any:...); public int Native_DisplayPassCustomToOne(Handle plugin, int numParams) { NativeVote vote = GetNativeCell(1); @@ -2174,7 +2169,7 @@ public int Native_DisplayPassCustomToOne(Handle plugin, int numParams) Game_DisplayVotePassCustom(vote, translation, client); } -// native NativeVotes_DisplayPassEx(Handle:vote, NativeVotesPassType:passType, const String:details[]=""); +// native NativeVotes_DisplayPassEx(Handle vote, NativeVotesPassType passType, const char[] details = ""); public int Native_DisplayPassEx(Handle plugin, int numParams) { NativeVote vote = GetNativeCell(1); @@ -2231,7 +2226,7 @@ public Native_DisplayRawPass(Handle:plugin, numParams) } */ -// native NativeVotes_DisplayRawPassToOne(client, NativeVotesPassType:passType, const String:details[]="", team=NATIVEVOTES_ALL_TEAMS); +// native NativeVotes_DisplayRawPassToOne(int client, NativeVotesPassType passType, const char[] details = "", int team = NATIVEVOTES_ALL_TEAMS); public int Native_DisplayRawPassToOne(Handle plugin, int numParams) { int client = GetNativeCell(1); @@ -2262,8 +2257,7 @@ public int Native_DisplayRawPassToOne(Handle plugin, int numParams) Game_DisplayRawVotePass(passType, team, client); } } - -// native NativeVotes_DisplayRawPassCustomToOne(client, team, const String:format[], any:...); +// native NativeVotes_DisplayRawPassCustomToOne(int client, int team, const char[] format, any:...); public int Native_DisplayRawPassCustomToOne(Handle plugin, int numParams) { int client = GetNativeCell(1); @@ -2277,7 +2271,7 @@ public int Native_DisplayRawPassCustomToOne(Handle plugin, int numParams) Game_DisplayRawVotePassCustom(translation, team, client); } -// native NativeVotes_DisplayFail(Handle:vote, NativeVotesFailType:reason=NativeVotesFail_Generic); +// native NativeVotes_DisplayFail(Handle vote, NativeVotesFailType reason = NativeVotesFail_Generic); public int Native_DisplayFail(Handle plugin, int numParams) { NativeVote vote = GetNativeCell(1); @@ -2292,7 +2286,7 @@ public int Native_DisplayFail(Handle plugin, int numParams) Game_DisplayVoteFail(vote, reason); } -// native NativeVotes_DisplayRawFail(NativeVotesFailType:reason=NativeVotesFail_Generic, team=NATIVEVOTES_ALL_TEAMS); +// native NativeVotes_DisplayRawFail(NativeVotesFailType reason = NativeVotesFail_Generic, int team = NATIVEVOTES_ALL_TEAMS); public int Native_DisplayRawFail(Handle plugin, int numParams) { int size = GetNativeCell(2); @@ -2311,15 +2305,15 @@ public int Native_DisplayRawFail(Handle plugin, int numParams) Game_DisplayRawVoteFail(clients, size, reason, team); } -// native NativeVotes_DisplayRawFailToOne(client, NativeVotesFailType:reason=NativeVotesFail_Generic, team=NATIVEVOTES_ALL_TEAMS); +// native NativeVotes_DisplayRawFailToOne(int client, NativeVotesFailType reason = NativeVotesFail_Generic, int team = NATIVEVOTES_ALL_TEAMS); /* -public Native_DisplayRawFailToOne(Handle:plugin, numParams) +public Native_DisplayRawFailToOne(Handle plugin, int numParams) { - new client = GetNativeCell(1); + int client = GetNativeCell(1); - new NativeVotesFailType:reason = NativeVotesFailType:GetNativeCell(2); + NativeVotesFailType reason = view_as(GetNativeCell(2)); - new team = GetNativeCell(3); + int team = GetNativeCell(3); if (g_EngineVersion == Engine_TF2 && team == NATIVEVOTES_ALL_TEAMS) { @@ -2329,7 +2323,7 @@ public Native_DisplayRawFailToOne(Handle:plugin, numParams) Game_DisplayRawVoteFail(reason, team, client); } */ -// native NativeVotes_GetTarget(Handle:vote); +// native NativeVotes_GetTarget(Handle vote); public int Native_GetTarget(Handle plugin, int numParams) { NativeVote vote = GetNativeCell(1); @@ -2350,14 +2344,14 @@ public int Native_GetTarget(Handle plugin, int numParams) return GetClientOfUserId(target); } -// native NativeVotes_GetTargetSteam(Handle:vote, String:buffer[], maxlength); +// native NativeVotes_GetTargetSteam(Handle vote, const char[] buffer, maxlength); public int Native_GetTargetSteam(Handle plugin, int numParams) { NativeVote vote = GetNativeCell(1); if (vote == null) { ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return; + return 0; } int size = GetNativeCell(3); @@ -2367,7 +2361,7 @@ public int Native_GetTargetSteam(Handle plugin, int numParams) Data_GetTargetSteam(vote, steamId, size); } -// native NativeVotes_SetTarget(Handle:vote, client, bool:setDetails=true); +// native NativeVotes_SetTarget(Handle vote, int client, bool setDetails = true); public int Native_SetTarget(Handle plugin, int numParams) { NativeVote vote = GetNativeCell(1); @@ -2421,13 +2415,13 @@ public int Native_SetTarget(Handle plugin, int numParams) } } -// native bool:NativeVotes_AreVoteCommandsSupported(); +// native bool NativeVotes_AreVoteCommandsSupported(); public int Native_AreVoteCommandsSupported(Handle plugin, int numParams) { return Game_AreVoteCommandsSupported(); } -// native NativeVotes_RegisterVoteCommand(NativeVotesOverride:overrideType, NativeVotes_CallVoteHandler:callHandler, NativeVotes_CallVoteVisCheck:visHandler=INVALID_FUNCTION); +// native NativeVotes_RegisterVoteCommand(NativeVotesOverride overrideType, NativeVotes_CallVoteHandler callHandler, NativeVotes_CallVoteVisCheck visHandler = INVALID_FUNCTION); public int Native_RegisterVoteCommand(Handle plugin, int numParams) { NativeVotesOverride overrideType = GetNativeCell(1); @@ -2456,7 +2450,7 @@ public int Native_RegisterVoteCommand(Handle plugin, int numParams) } } -// native NativeVotes_UnregisterVoteCommand(NativeVotesOverride:overrideType, NativeVotes_CallVoteHandler:callHandler, NativeVotes_CallVoteVisCheck:visHandler=INVALID_FUNCTION); +// native NativeVotes_UnregisterVoteCommand(NativeVotesOverride overrideType, NativeVotes_CallVoteHandler callHandler, NativeVotes_CallVoteVisCheck visHandler=INVALID_FUNCTION); public int Native_UnregisterVoteCommand(Handle plugin, int numParams) { NativeVotesOverride overrideType = GetNativeCell(1); @@ -2483,7 +2477,7 @@ public int Native_UnregisterVoteCommand(Handle plugin, int numParams) } } -// native NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFailType:reason, time=0); +// native NativeVotes_DisplayCallVoteFail(int client, NativeVotesCallFailType reason, int time=0); public int Native_DisplayCallVoteFail(Handle plugin, int numParams) { int client = GetNativeCell(1); @@ -2501,7 +2495,7 @@ public int Native_DisplayCallVoteFail(Handle plugin, int numParams) Game_DisplayCallVoteFail(client, reason, time); } -// native Action:NativeVotes_RedrawVoteTitle(const String:text[]); +// native Action NativeVotes_RedrawVoteTitle(const char[] text); public int Native_RedrawVoteTitle(Handle plugin, int numParams) { if (!g_curDisplayClient) @@ -2520,7 +2514,7 @@ public int Native_RedrawVoteTitle(Handle plugin, int numParams) return view_as(Plugin_Changed); } -// native Action:NativeVotes_RedrawVoteItem(const String:text[]); +// native Action NativeVotes_RedrawVoteItem(const char[] text); public int Native_RedrawVoteItem(Handle plugin, int numParams) { if (!g_curItemClient) diff --git a/addons/sourcemod/scripting/nativevotes_nominations.sp b/addons/sourcemod/scripting/nativevotes_nominations.sp index e84cc21..02435a9 100644 --- a/addons/sourcemod/scripting/nativevotes_nominations.sp +++ b/addons/sourcemod/scripting/nativevotes_nominations.sp @@ -48,7 +48,7 @@ public Plugin myinfo = name = "NativeVotes | Map Nominations", author = "AlliedModders LLC and Powerlord", description = "Provides Map Nominations", - version = "26w04a", + version = "26w05a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -91,13 +91,12 @@ public void OnPluginStart() LoadTranslations("common.phrases"); LoadTranslations("nominations.phrases"); - int arraySize = ByteCountToCells(PLATFORM_MAX_PATH); - g_MapList = new ArrayList(arraySize); + g_MapList = new ArrayList(ByteCountToCells(PLATFORM_MAX_PATH)); g_ConVars[excludeold] = CreateConVar("sm_nominate_excludeold", "1", "Specifies if the current map should be excluded from the Nominations list", _, true, 0.0, true, 1.0); g_ConVars[excludecurrent] = CreateConVar("sm_nominate_excludecurrent", "1", "Specifies if the MapChooser excluded maps should also be excluded from Nominations", _, true, 0.0, true, 1.0); g_ConVars[maxmatches] = CreateConVar("sm_nominate_maxfound", "0", "Maximum number of nomination matches to add to the menu. 0 = infinite.", _, true, 0.0); - //g_ConVars[allow_workshop] = CreateConVar("sm_nominate_allow_workshop", "0", "Specifies if unlisted workshop maps can be nominated", _, true, 0.0, true, 1.0); + //g_ConVars[allow_workshop] = CreateConVar("sm_nominate_allow_workshop", "0", "Specifies if unlisted workshop maps can be nominated", _, true, 0.0, true, 1.0); g_ConVars[use_callvote] = CreateConVar("sm_nominate_use_callvote", "1", "Specifies whether to execute callvote when nominating without specifying the map.", _, true, 0.0, true, 1.0); g_ConVars[nextlevel_allowed] = FindConVar("sv_vote_issue_nextlevel_allowed"); @@ -178,7 +177,7 @@ public void OnNominationRemoved(const char[] map, int owner) { int status; - char resolvedMap[96]; + char resolvedMap[PLATFORM_MAX_PATH]; FindMap(map, resolvedMap, sizeof(resolvedMap)); /* Is the map in our list? */ @@ -204,8 +203,7 @@ public Action Command_Addmap(int client, int args) return Plugin_Handled; } - char mapname[96]; - char resolvedMap[96]; + char mapname[PLATFORM_MAX_PATH], resolvedMap[PLATFORM_MAX_PATH]; GetCmdArg(1, mapname, sizeof(mapname)); if (FindMap(mapname, resolvedMap, sizeof(resolvedMap)) == FindMap_NotFound) @@ -252,7 +250,7 @@ Action Command_ReloadNominations(int client, int args) public void OnClientSayCommand_Post(int client, const char[] command, const char[] sArgs) { - if (!client) + if (!client || IsChatTrigger()) { return; } @@ -289,7 +287,7 @@ public Action Command_Nominate(int client, int args) return Plugin_Handled; } - char mapname[96]; + char mapname[PLATFORM_MAX_PATH]; GetCmdArg(1, mapname, sizeof(mapname)); ArrayList results = new ArrayList(); @@ -350,7 +348,7 @@ int FindMatchingMaps(ArrayList mapList, ArrayList results, const char[] input) } int matches = 0; - char map[96]; + char map[PLATFORM_MAX_PATH]; int maxmatches = g_ConVars[maxmatches].IntValue; @@ -374,8 +372,7 @@ int FindMatchingMaps(ArrayList mapList, ArrayList results, const char[] input) void AttemptNominate(int client, const char[] map, int size, bool isVoteMenu) { - char mapname[96]; - + char mapname[PLATFORM_MAX_PATH]; if (FindMap(map, mapname, size) == FindMap_NotFound) { // We couldn't resolve the map entry to a filename, so... @@ -389,7 +386,7 @@ void AttemptNominate(int client, const char[] map, int size, bool isVoteMenu) } char displayName[PLATFORM_MAX_PATH]; - GetMapDisplayName(mapname, displayName, sizeof(displayName)); + GetMapDisplayName(mapname, displayName, sizeof(displayName)); Format(displayName, sizeof(displayName), "\x05%s\x01", displayName); int status; @@ -460,15 +457,17 @@ void AttemptNominate(int client, const char[] map, int size, bool isVoteMenu) } /* Map was nominated! - Disable the menu item and update the trie */ - - g_MapTrie.SetValue(mapname, MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_NOMINATED); + g_MapTrie.SetValue(mapname, MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_NOMINATED); - char name[MAX_NAME_LENGTH]; - GetPlayerName(client, name, sizeof(name)); + char name[MAX_NAME_LENGTH]; + GetPlayerName(client, name, sizeof(name)); - if (result == Nominate_Added) { + if (result == Nominate_Added) + { CPrintToChatAllEx(client, "[{lightgreen}Nominations\x01] %t", "Map Nominated", name, displayName); - } else { + } + else + { CReplyToCommandEx(client, client, "[{lightgreen}Nominations\x01] %t", "Map Nominated", name, displayName); } @@ -489,10 +488,10 @@ void BuildMapMenu() g_MapMenu = new Menu(MenuHandler_MapSelect, MENU_ACTIONS_DEFAULT|MenuAction_DrawItem|MenuAction_DisplayItem); - char map[96]; + char map[PLATFORM_MAX_PATH]; ArrayList excludeMaps; - char currentMap[96]; + char currentMap[PLATFORM_MAX_PATH]; if (g_ConVars[excludeold].BoolValue) { @@ -548,14 +547,14 @@ public int MenuHandler_MapSelect(Menu menu, MenuAction action, int param1, int p { case MenuAction_Select: { - char mapname[96]; + char mapname[PLATFORM_MAX_PATH]; // Get the map name and attempt to nominate it menu.GetItem(param2, mapname, sizeof(mapname)); AttemptNominate(param1, mapname, sizeof(mapname), false); } case MenuAction_DrawItem: { - char map[96]; + char map[PLATFORM_MAX_PATH]; menu.GetItem(param2, map, sizeof(map)); int status; @@ -574,7 +573,7 @@ public int MenuHandler_MapSelect(Menu menu, MenuAction action, int param1, int p } case MenuAction_DisplayItem: { - char mapname[96]; + char mapname[PLATFORM_MAX_PATH]; menu.GetItem(param2, mapname, sizeof(mapname)); int status; @@ -616,8 +615,7 @@ public int MenuHandler_MapSelect(Menu menu, MenuAction action, int param1, int p if (menu != g_MapMenu) { delete menu; - } - + } } } return 0; @@ -676,11 +674,7 @@ public Action Menu_Nominate(int client, NativeVotesOverride overrideType, const ReplySource old = SetCmdReplySource(SM_REPLY_TO_CHAT); - // awful hack, but whatever - char mapname[96]; - strcopy(mapname, sizeof(mapname), voteArgument); - - AttemptNominate(client, mapname, sizeof(mapname), true); + AttemptNominate(client, voteArgument, PLATFORM_MAX_PATH, true); SetCmdReplySource(old); @@ -721,19 +715,14 @@ public Action NativeVotes_OverrideMaps(StringMap mapList) char displayName[PLATFORM_MAX_PATH]; GetMapDisplayName(map, displayName, sizeof(displayName)); - + // Display name first mapList.SetString(displayName, map); } delete snapshot; - - if (mapList.Size > 0) - { - return Plugin_Changed; - } - - return Plugin_Continue; + + return mapList.Size > 0 ? Plugin_Changed : Plugin_Continue; } void GetPlayerName(int client, char[] name, int maxlen) From 79c0a3481d7ef503070823e559af0b05307d89b9 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Wed, 28 Jan 2026 15:07:47 +0100 Subject: [PATCH 045/128] Attempt to fix erronous "RTV has already started" nagging --- addons/sourcemod/scripting/nativevotes_rockthevote.sp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_rockthevote.sp b/addons/sourcemod/scripting/nativevotes_rockthevote.sp index 17296ad..0823103 100644 --- a/addons/sourcemod/scripting/nativevotes_rockthevote.sp +++ b/addons/sourcemod/scripting/nativevotes_rockthevote.sp @@ -55,7 +55,7 @@ public Plugin myinfo = name = "NativeVotes | Rock The Vote", author = "AlliedModders LLC and Powerlord", description = "Provides RTV Map Voting", - version = "26w04a", + version = "26w05a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -190,6 +190,11 @@ public void OnMapEnd() public void OnConfigsExecuted() { + if (g_ConVars[initialdelay].FloatValue <= 0.0) + { + g_RTVAllowed = true; + return; + } CreateTimer(g_ConVars[initialdelay].FloatValue, Timer_DelayRTV, _, TIMER_FLAG_NO_MAPCHANGE); } @@ -343,8 +348,8 @@ void AttemptRTV(int client, bool isVoteMenu=false) } return; } - - if (!CanMapChooserStartVote() && NativeVotes_IsVoteInProgress()) + + if (!CanMapChooserStartVote()) { CReplyToCommand(client, "[{lightgreen}Rock The Vote\x01] %t", "RTV Started"); return; From f2290c44979c6a14c49a8d558b051432f08592cb Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Wed, 28 Jan 2026 15:11:03 +0100 Subject: [PATCH 046/128] Auto Maplist builder --- .../scripting/nativevotes_mapchooser.sp | 3470 +++++++++-------- 1 file changed, 1778 insertions(+), 1692 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_mapchooser.sp b/addons/sourcemod/scripting/nativevotes_mapchooser.sp index 637fd59..a417dae 100644 --- a/addons/sourcemod/scripting/nativevotes_mapchooser.sp +++ b/addons/sourcemod/scripting/nativevotes_mapchooser.sp @@ -1,1693 +1,1779 @@ -/** - * vim: set ts=4 : - * ============================================================================= - * SourceMod NativeVotes Mapchooser Plugin - * Creates a map vote at appropriate times, setting sm_nextmap to the winning - * vote - * Updated with NativeVotes support - * - * NativeVotes (C)2011-2016 Ross Bemrose (Powerlord). All rights reserved. - * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved. - * ============================================================================= - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, version 3.0, as published by the - * Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - * - * As a special exception, AlliedModders LLC gives you permission to link the - * code of this program (as well as its derivative works) to "Half-Life 2," the - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software - * by the Valve Corporation. You must obey the GNU General Public License in - * all respects for all other code used. Additionally, AlliedModders LLC grants - * this exception to all derivative works. AlliedModders LLC defines further - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), - * or . - * - * Version: $Id$ - */ - -#include -#include -#include -#include - -#undef REQUIRE_PLUGIN -#include -#define REQUIRE_PLUGIN - -#pragma semicolon 1 -#pragma newdecls required - -public Plugin myinfo = -{ - name = "NativeVotes | MapChooser", - author = "AlliedModders LLC and Powerlord", - description = "Automated Map Voting", - version = "26w04a", - url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" -}; - -/* ConVars */ -enum -{ - /* Valve ConVars */ - mp_winlimit, - mp_maxrounds, - mp_fraglimit, - mp_bonusroundtime, - - /* Plugin ConVars */ - mapvote_endvote, - mapvote_start, - mapvote_startround, - mapvote_startfrags, - extendmap_timestep, - extendmap_roundstep, - extendmap_fragstep, - mapvote_exclude, - mapvote_include, - mapvote_novote, - mapvote_extend, - mapvote_dontchange, - mapvote_voteduration, - mapvote_runoff, - mapvote_runoffpercent, - mapcycle_auto, - mapcycle_exclude, - workshop_maplist, - workshop_cleanup, - - MAX_CONVARS -} - -ConVar g_ConVars[MAX_CONVARS]; - -Handle g_VoteTimer = null; -Handle g_RetryTimer = null; - -// g_MapList stores unresolved names so we can resolve them after every map change in the workshop updates. -// g_OldMapList and g_NextMapList are resolved. g_NominateList depends on the nominations implementation. -/* Data Handles */ -ArrayList g_MapList; -ArrayList g_NominateList; -ArrayList g_NominateOwners; -ArrayList g_OldMapList; -ArrayList g_NextMapList; -Menu g_VoteMenu; -NativeVote g_VoteNative; - -int g_Extends; -int g_TotalRounds; -bool g_HasVoteStarted; -bool g_WaitingForVote; -bool g_MapVoteCompleted; -bool g_ChangeMapAtRoundEnd; -bool g_ChangeMapInProgress; -int g_mapFileSerial = -1; - -MapChange g_ChangeTime; - -Handle g_NominationsResetForward = null; -Handle g_MapVoteStartedForward = null; - -/* Upper bound of how many team there could be */ -#define MAXTEAMS 10 -int g_winCount[MAXTEAMS]; - -#define VOTE_EXTEND "##extend##" -#define VOTE_DONTCHANGE "##dontchange##" - -// NativeVotes -bool g_NativeVotes; - -public void OnPluginStart() -{ - LoadTranslations("mapchooser.phrases"); - LoadTranslations("common.phrases"); - - KeyValues kv = new KeyValues("GameInfo"); - kv.ImportFromFile("gameinfo.txt"); - - char gameDir[128]; - GetGameFolderName(gameDir, sizeof(gameDir)); - - EngineVersion engine = GetEngineVersion(); - if (!StrEqual(gameDir, "tf") && - (kv.GetNum("DependsOnAppID") == 440 || - (engine == Engine_SDK2013 && FileExists("resource/tf.ttf")))) - { - engine = Engine_TF2; - } - - int arraySize = ByteCountToCells(PLATFORM_MAX_PATH); - g_MapList = new ArrayList(arraySize); - g_NominateList = new ArrayList(arraySize); - g_NominateOwners = new ArrayList(); - g_OldMapList = new ArrayList(arraySize); - g_NextMapList = new ArrayList(arraySize); - - g_ConVars[mapvote_endvote] = CreateConVar("sm_mapvote_endvote", "1", "Specifies if MapChooser should run an end of map vote", _, true, 0.0, true, 1.0); - g_ConVars[mapvote_start] = CreateConVar("sm_mapvote_start", "3.0", "Specifies when to start the vote based on time remaining.", _, true, 1.0); - g_ConVars[mapvote_startround] = CreateConVar("sm_mapvote_startround", "2.0", "Specifies when to start the vote based on rounds remaining. Use '0' on TF2 to start vote during bonus round time", _, true, 0.0); - g_ConVars[mapvote_startfrags] = CreateConVar("sm_mapvote_startfrags", "5.0", "Specifies when to start the vote base on frags remaining.", _, true, 1.0); - g_ConVars[extendmap_timestep] = CreateConVar("sm_extendmap_timestep", "15", "Specifies how much many more minutes each extension makes", _, true, 5.0); - g_ConVars[extendmap_roundstep] = CreateConVar("sm_extendmap_roundstep", "5", "Specifies how many more rounds each extension makes", _, true, 1.0); - g_ConVars[extendmap_fragstep] = CreateConVar("sm_extendmap_fragstep", "10", "Specifies how many more frags are allowed when map is extended.", _, true, 5.0); - g_ConVars[mapvote_exclude] = CreateConVar("sm_mapvote_exclude", "5", "Specifies how many past maps to exclude from the vote.", _, true, 0.0); - g_ConVars[mapvote_include] = CreateConVar("sm_mapvote_include", "5", "Specifies how many maps to include in the vote.", _, true, 2.0, true, 6.0); - g_ConVars[mapvote_novote] = CreateConVar("sm_mapvote_novote", "1", "Specifies whether or not MapChooser should pick a map if no votes are received.", _, true, 0.0, true, 1.0); - g_ConVars[mapvote_extend] = CreateConVar("sm_mapvote_extend", "0", "Number of extensions allowed each map.", _, true, 0.0); - g_ConVars[mapvote_dontchange] = CreateConVar("sm_mapvote_dontchange", "1", "Specifies if a 'Don't Change' option should be added to early votes", _, true, 0.0, true, 1.0); - g_ConVars[mapvote_voteduration] = CreateConVar("sm_mapvote_voteduration", "20", "Specifies how long the mapvote should be available for.", _, true, 5.0); - g_ConVars[mapvote_runoff] = CreateConVar("sm_mapvote_runoff", "0", "Hold run of votes if winning choice is less than a certain margin", _, true, 0.0, true, 1.0); - g_ConVars[mapvote_runoffpercent] = CreateConVar("sm_mapvote_runoffpercent", "50", "If winning choice has less than this percent of votes, hold a runoff", _, true, 0.0, true, 100.0); - g_ConVars[mapcycle_auto] = CreateConVar("sm_mapcycle_auto", "0", "Specifies whether or not to automatically populate the maps list.", _, true, 0.0, true, 1.0); - g_ConVars[mapcycle_exclude] = CreateConVar("sm_mapcycle_exclude", ".*itemtest.*|background01|^tr.*$", "Specifies which maps shouldn't be automatically added with a regex pattern."); - if (engine != Engine_SDK2013 && engine == Engine_TF2) - { - g_ConVars[workshop_maplist] = CreateConVar("sm_workshop_map_collection", "", "Specifies the file containing the workshop maps to include in the map list", _, true, 0.0, true, 1.0); - g_ConVars[workshop_cleanup] = CreateConVar("sm_workshop_map_cleanup", "0", "Specifies whether or not to automatically workshop maps on map change", _, true, 0.0, true, 1.0); - } - - RegAdminCmd("sm_mapvote", Command_MapVote, ADMFLAG_CHANGEMAP, "Forces MapChooser to attempt to run a map vote now."); - RegAdminCmd("sm_setnextmap", Command_SetNextMap, ADMFLAG_CHANGEMAP, "sm_setnextmap "); - RegAdminCmd("sm_reloadmaplist", Command_ReloadMapList, ADMFLAG_CHANGEMAP, "Re-populates the mapcycle file."); - - g_ConVars[mp_winlimit] = FindConVar("mp_winlimit"); - g_ConVars[mp_maxrounds] = FindConVar("mp_maxrounds"); - g_ConVars[mp_fraglimit] = FindConVar("mp_fraglimit"); - g_ConVars[mp_bonusroundtime] = FindConVar("mp_bonusroundtime"); - - if (g_ConVars[mp_winlimit] || g_ConVars[mp_maxrounds]) - { - switch (engine) - { - case Engine_TF2: - { - HookEvent("teamplay_win_panel", Event_TeamplayWinPanel); - HookEvent("teamplay_restart_round", Event_TeamplayRestartRound); - HookEvent("arena_win_panel", Event_TeamplayWinPanel); - } - case Engine_NuclearDawn: - { - HookEvent("round_win", Event_RoundEnd); - } - default: - { - HookEvent("round_end", Event_RoundEnd); - } - } - } - - if (g_ConVars[mp_fraglimit]) - { - HookEvent("player_death", Event_PlayerDeath); - } - - AutoExecConfig(true, "mapchooser"); - - // Change the mp_bonusroundtime max so that we have time to display the vote - // If you display a vote during bonus time good defaults are 17 vote duration and 19 mp_bonustime - if (g_ConVars[mp_bonusroundtime]) - { - g_ConVars[mp_bonusroundtime].SetBounds(ConVarBound_Upper, true, 30.0); - } - - g_NominationsResetForward = CreateGlobalForward("OnNominationRemoved", ET_Ignore, Param_String, Param_Cell); - g_MapVoteStartedForward = CreateGlobalForward("OnMapVoteStarted", ET_Ignore); -} - -public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) -{ - RegPluginLibrary("mapchooser"); - - CreateNative("NominateMap", Native_NominateMap); - CreateNative("RemoveNominationByMap", Native_RemoveNominationByMap); - CreateNative("RemoveNominationByOwner", Native_RemoveNominationByOwner); - CreateNative("InitiateMapChooserVote", Native_InitiateVote); - CreateNative("CanMapChooserStartVote", Native_CanVoteStart); - CreateNative("HasEndOfMapVoteFinished", Native_CheckVoteDone); - CreateNative("GetExcludeMapList", Native_GetExcludeMapList); - CreateNative("GetNominatedMapList", Native_GetNominatedMapList); - CreateNative("EndOfMapVoteEnabled", Native_EndOfMapVoteEnabled); - - return APLRes_Success; -} - -public void OnAllPluginsLoaded() -{ - if (FindPluginByFile("mapchooser.smx") != null) - { - LogMessage("Unloading mapchooser to prevent conflicts..."); - ServerCommand("sm plugins unload mapchooser"); - - char oldPath[PLATFORM_MAX_PATH]; - char newPath[PLATFORM_MAX_PATH]; - - BuildPath(Path_SM, oldPath, sizeof(oldPath), "plugins/mapchooser.smx"); - BuildPath(Path_SM, newPath, sizeof(newPath), "plugins/disabled/mapchooser.smx"); - if (RenameFile(newPath, oldPath)) - { - LogMessage("Moving mapchooser to disabled."); - } - } - - g_NativeVotes = LibraryExists("nativevotes") && NativeVotes_IsVoteTypeSupported(NativeVotesType_NextLevelMult); -} - -public void OnLibraryAdded(const char[] name) -{ - if (StrEqual(name, "nativevotes", false) && NativeVotes_IsVoteTypeSupported(NativeVotesType_NextLevelMult)) - { - g_NativeVotes = true; - } -} - -public void OnLibraryRemoved(const char[] name) -{ - if (StrEqual(name, "nativevotes", false)) - { - g_NativeVotes = false; - } -} - -public void OnMapInit() -{ - if (g_ConVars[mapcycle_auto].BoolValue) - { - PopulateMapList(); - } - - if (g_ConVars[workshop_cleanup].BoolValue) - { - CleanupWorkshopMaps(); - } -} - -public void OnConfigsExecuted() -{ - if (ReadMapList(g_MapList, g_mapFileSerial, "mapchooser", MAPLIST_FLAG_CLEARARRAY|MAPLIST_FLAG_MAPSFOLDER) != null) - { - if (g_mapFileSerial == -1) - { - LogError("Unable to create a valid map list."); - } - } - - CreateNextVote(); - SetupTimeleftTimer(); - - g_TotalRounds = 0; - - g_Extends = 0; - - g_MapVoteCompleted = false; - - g_NominateList.Clear(); - g_NominateOwners.Clear(); - - for (int i = 0; i < MAXTEAMS; i++) - { - g_winCount[i] = 0; - } - - - /* Check if mapchooser will attempt to start mapvote during bonus round time - TF2 Only */ - if (g_ConVars[mp_bonusroundtime] && !g_ConVars[mapvote_startround].IntValue) - { - if (g_ConVars[mp_bonusroundtime].FloatValue <= g_ConVars[mapvote_voteduration].FloatValue) - { - LogMessage("Warning - Bonus Round Time shorter than Vote Time. Votes during bonus round may not have time to complete"); - } - } -} - -public void OnMapEnd() -{ - g_HasVoteStarted = false; - g_WaitingForVote = false; - g_ChangeMapAtRoundEnd = false; - g_ChangeMapInProgress = false; - - g_VoteTimer = null; - g_RetryTimer = null; - - char map[PLATFORM_MAX_PATH]; - GetCurrentMap(map, sizeof(map)); - g_OldMapList.PushString(map); - - while (g_OldMapList.Length > g_ConVars[mapvote_exclude].IntValue) - { - g_OldMapList.Erase(0); - } -} - -public void OnClientDisconnect(int client) -{ - int index = g_NominateOwners.FindValue(client); - - if (index == -1) - { - return; - } - - char oldmap[PLATFORM_MAX_PATH]; - g_NominateList.GetString(index, oldmap, sizeof(oldmap)); - Call_StartForward(g_NominationsResetForward); - Call_PushString(oldmap); - Call_PushCell(g_NominateOwners.Get(index)); - Call_Finish(); - - g_NominateOwners.Erase(index); - g_NominateList.Erase(index); -} - -public Action Command_SetNextMap(int client, int args) -{ - if (args < 1) - { - CReplyToCommand(client, "[{lightgreen}MapChooser\x01] Usage: sm_setnextmap "); - return Plugin_Handled; - } - - char map[PLATFORM_MAX_PATH], displayName[PLATFORM_MAX_PATH]; - GetCmdArg(1, map, sizeof(map)); - - if (FindMap(map, displayName, sizeof(displayName)) == FindMap_NotFound) - { - CReplyToCommand(client, "[{lightgreen}MapChooser\x01] %t", "Map was not found", map); - return Plugin_Handled; - } - - GetMapDisplayName(displayName, displayName, sizeof(displayName)); - Format(displayName, sizeof(displayName), "\x05%s\x01", displayName); - - CShowActivity(client, "%t", "Changed Next Map", displayName); - LogAction(client, -1, "\"%L\" changed nextmap to \"%s\"", client, map); - - SetNextMap(map); - g_MapVoteCompleted = true; - - return Plugin_Handled; -} - -public void OnMapTimeLeftChanged() -{ - if (g_MapList.Length) - { - SetupTimeleftTimer(); - } -} - -void SetupTimeleftTimer() -{ - int time; - if (GetMapTimeLeft(time) && time > 0) - { - int startTime = g_ConVars[mapvote_start].IntValue * 60; - if (time - startTime < 0 && g_ConVars[mapvote_endvote].BoolValue && !g_MapVoteCompleted && !g_HasVoteStarted) - { - InitiateVote(MapChange_MapEnd, null); - } - else - { - if (g_VoteTimer != null) - { - KillTimer(g_VoteTimer); - g_VoteTimer = null; - } - - //g_VoteTimer = CreateTimer(float(time - startTime), Timer_StartMapVote, _, TIMER_FLAG_NO_MAPCHANGE); - DataPack data; - g_VoteTimer = CreateDataTimer(float(time - startTime), Timer_StartMapVote, data, TIMER_FLAG_NO_MAPCHANGE); - data.WriteCell(MapChange_MapEnd); - data.WriteCell(INVALID_HANDLE); - data.Reset(); - } - } -} - -public Action Timer_StartMapVote(Handle timer, DataPack data) -{ - if (timer == g_RetryTimer) - { - g_WaitingForVote = false; - g_RetryTimer = null; - } - else - { - g_VoteTimer = null; - } - - if (!g_MapList.Length || !g_ConVars[mapvote_endvote].BoolValue || g_MapVoteCompleted || g_HasVoteStarted) - { - return Plugin_Stop; - } - - MapChange mapChange = view_as(data.ReadCell()); - ArrayList hndl = view_as(data.ReadCell()); - - InitiateVote(mapChange, hndl); - - return Plugin_Stop; -} - -public void Event_TeamplayRestartRound(Event event, const char[] name, bool dontBroadcast) -{ - /* Game got restarted - reset our round count tracking */ - g_TotalRounds = 0; -} - -public void Event_TeamplayWinPanel(Event event, const char[] name, bool dontBroadcast) -{ - if (g_ChangeMapAtRoundEnd) - { - g_ChangeMapAtRoundEnd = false; - CreateTimer(2.0, Timer_ChangeMap, INVALID_HANDLE, TIMER_FLAG_NO_MAPCHANGE); - g_ChangeMapInProgress = true; - } - - int bluescore = event.GetInt("blue_score"); - int redscore = event.GetInt("red_score"); - - if (event.GetInt("round_complete") == 1 || StrEqual(name, "arena_win_panel")) - { - g_TotalRounds++; - - if (!g_MapList.Length || g_HasVoteStarted || g_MapVoteCompleted || !g_ConVars[mapvote_endvote].BoolValue) - { - return; - } - - CheckMaxRounds(g_TotalRounds); - - switch(event.GetInt("winning_team")) - { - case 3: - { - CheckWinLimit(bluescore); - } - case 2: - { - CheckWinLimit(redscore); - } - //We need to do nothing on winning_team == 0 this indicates stalemate. - default: - { - return; - } - } - } -} -/* You ask, why don't you just use team_score event? And I answer... Because CSS doesn't. */ -public void Event_RoundEnd(Event event, const char[] name, bool dontBroadcast) -{ - if (g_ChangeMapAtRoundEnd) - { - g_ChangeMapAtRoundEnd = false; - CreateTimer(2.0, Timer_ChangeMap, INVALID_HANDLE, TIMER_FLAG_NO_MAPCHANGE); - g_ChangeMapInProgress = true; - } - - int winner; - if (strcmp(name, "round_win") == 0) - { - // Nuclear Dawn - winner = event.GetInt("team"); - } - else - { - winner = event.GetInt("winner"); - } - - if (winner == 0 || winner == 1 || !g_ConVars[mapvote_endvote].BoolValue) - { - return; - } - - if (winner >= MAXTEAMS) - { - SetFailState("Mod exceed maximum team count - Please file a bug report."); - } - - g_TotalRounds++; - - g_winCount[winner]++; - - if (!g_MapList.Length || g_HasVoteStarted || g_MapVoteCompleted) - { - return; - } - - CheckWinLimit(g_winCount[winner]); - CheckMaxRounds(g_TotalRounds); -} - -public void CheckWinLimit(int winner_score) -{ - if (g_ConVars[mp_winlimit]) - { - int winlimit = g_ConVars[mp_winlimit].IntValue; - if (winlimit) - { - if (winner_score >= (winlimit - g_ConVars[mapvote_startround].IntValue)) - { - InitiateVote(MapChange_MapEnd, null); - } - } - } -} - -public void CheckMaxRounds(int roundcount) -{ - if (g_ConVars[mp_maxrounds]) - { - int maxrounds = g_ConVars[mp_maxrounds].IntValue; - if (maxrounds) - { - if (roundcount >= (maxrounds - g_ConVars[mapvote_startround].IntValue)) - { - InitiateVote(MapChange_MapEnd, null); - } - } - } -} - -public void Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast) -{ - if (!g_MapList.Length || !g_ConVars[mp_fraglimit] || g_HasVoteStarted) - { - return; - } - - if (!g_ConVars[mp_fraglimit].IntValue || !g_ConVars[mapvote_endvote].BoolValue) - { - return; - } - - if (g_MapVoteCompleted) - { - return; - } - - int fragger = GetClientOfUserId(event.GetInt("attacker")); - - if (!fragger) - { - return; - } - - if (GetClientFrags(fragger) >= (g_ConVars[mp_fraglimit].IntValue - g_ConVars[mapvote_startfrags].IntValue)) - { - InitiateVote(MapChange_MapEnd, null); - } -} - -public Action Command_MapVote(int client, int args) -{ - InitiateVote(MapChange_MapEnd, null); - - return Plugin_Handled; -} - -public Action Command_ReloadMapList(int client, int args) -{ - PopulateMapList(); -} - -/** - * Starts a new map vote - * - * @param when When the resulting map change should occur. - * @param inputlist Optional list of maps to use for the vote, otherwise an internal list of nominations + random maps will be used. - * @param noSpecials Block special vote options like extend/nochange (upgrade this to bitflags instead?) - */ -void InitiateVote(MapChange when, ArrayList inputlist=null) -{ - g_WaitingForVote = true; - - if ((g_NativeVotes && NativeVotes_IsVoteInProgress()) || (!g_NativeVotes && IsVoteInProgress())) - //if (IsVoteInProgress()) - { - // Can't start a vote, try again in 5 seconds. - //g_RetryTimer = CreateTimer(5.0, Timer_StartMapVote, _, TIMER_FLAG_NO_MAPCHANGE); - - DataPack data; - g_RetryTimer = CreateDataTimer(5.0, Timer_StartMapVote, data, TIMER_FLAG_NO_MAPCHANGE); - data.WriteCell(when); - data.WriteCell(inputlist); - data.Reset(); - return; - } - - /* If the main map vote has completed (and chosen result) and its currently changing (not a delayed change) we block further attempts */ - if (g_MapVoteCompleted && g_ChangeMapInProgress) - { - return; - } - - g_ChangeTime = when; - - g_WaitingForVote = false; - - g_HasVoteStarted = true; - if (g_NativeVotes) - { - g_VoteNative = new NativeVote(Handler_NV_MapVoteMenu, NativeVotesType_NextLevelMult, NATIVEVOTES_ACTIONS_DEFAULT | MenuAction_DisplayItem); - g_VoteNative.VoteResultCallback = Handler_NV_MapVoteFinished; - } - else - { - g_VoteMenu = new Menu(Handler_MapVoteMenu, MENU_ACTIONS_ALL); - g_VoteMenu.SetTitle("Vote Nextmap"); - g_VoteMenu.VoteResultCallback = Handler_MapVoteFinished; - } - - /* Call OnMapVoteStarted() Forward */ - Call_StartForward(g_MapVoteStartedForward); - Call_Finish(); - - /** - * TODO: Make a proper decision on when to clear the nominations list. - * Currently it clears when used, and stays if an external list is provided. - * Is this the right thing to do? External lists will probably come from places - * like sm_mapvote from the adminmenu in the future. - */ - - char map[PLATFORM_MAX_PATH]; - - /* No input given - User our internal nominations and maplist */ - if (inputlist == null) - { - int nominateCount = g_NominateList.Length; - int voteSize = g_ConVars[mapvote_include].IntValue; - - // New in 1.5.1 to fix missing extend vote - if (g_NativeVotes) - { - if (voteSize > NativeVotes_GetMaxItems()) - { - voteSize = NativeVotes_GetMaxItems(); - } - - if (g_ConVars[mapvote_extend].IntValue && g_Extends < g_ConVars[mapvote_extend].IntValue) - { - voteSize--; - } - } - - /* Smaller of the two - It should be impossible for nominations to exceed the size though (cvar changed mid-map?) */ - int nominationsToAdd = nominateCount >= voteSize ? voteSize : nominateCount; - - for (int i = 0; i < nominationsToAdd; i++) - { - char displayName[PLATFORM_MAX_PATH]; - g_NominateList.GetString(i, map, sizeof(map)); - GetMapDisplayName(map, displayName, sizeof(displayName)); - - if (g_NativeVotes) - { - g_VoteNative.AddItem(map, displayName); - } - else - { - g_VoteMenu.AddItem(map, displayName); - } - - RemoveStringFromArray(g_NextMapList, map); - - /* Notify Nominations that this map is now free */ - Call_StartForward(g_NominationsResetForward); - Call_PushString(map); - Call_PushCell(g_NominateOwners.Get(i)); - Call_Finish(); - } - - /* Clear out the rest of the nominations array */ - for (int i = nominationsToAdd; i < nominateCount; i++) - { - g_NominateList.GetString(i, map, sizeof(map)); - /* These maps shouldn't be excluded from the vote as they weren't really nominated at all */ - - /* Notify Nominations that this map is now free */ - Call_StartForward(g_NominationsResetForward); - Call_PushString(map); - Call_PushCell(g_NominateOwners.Get(i)); - Call_Finish(); - } - - /* There should currently be 'nominationsToAdd' unique maps in the vote */ - - int i = nominationsToAdd; - int count = 0; - int availableMaps = g_NextMapList.Length; - - while (i < voteSize) - { - if (count >= availableMaps) - { - //Run out of maps, this will have to do. - break; - } - - g_NextMapList.GetString(count, map, sizeof(map)); - count++; - - /* Insert the map and increment our count */ - char displayName[PLATFORM_MAX_PATH]; - GetMapDisplayName(map, displayName, sizeof(displayName)); - if (g_NativeVotes) - { - g_VoteNative.AddItem(map, displayName); - } - else - { - g_VoteMenu.AddItem(map, displayName); - } - i++; - } - - /* Wipe out our nominations list - Nominations have already been informed of this */ - g_NominateOwners.Clear(); - g_NominateList.Clear(); - } - else //We were given a list of maps to start the vote with - { - int size = inputlist.Length; - - for (int i = 0; i < size; i++) - { - inputlist.GetString(i, map, sizeof(map)); - - if (IsMapValid(map)) - { - char displayName[PLATFORM_MAX_PATH]; - GetMapDisplayName(map, displayName, sizeof(displayName)); - if (g_NativeVotes) - { - g_VoteNative.AddItem(map, displayName); - } - else - { - g_VoteMenu.AddItem(map, displayName); - } - } - } - } - - /* Do we add any special items? */ - if ((when == MapChange_Instant || when == MapChange_RoundEnd) && g_ConVars[mapvote_dontchange].BoolValue) - { - if (g_NativeVotes) - { - g_VoteNative.AddItem(VOTE_DONTCHANGE, "Don't Change"); - } - else - { - g_VoteMenu.AddItem(VOTE_DONTCHANGE, "Don't Change"); - } - } - else if (g_ConVars[mapvote_extend].BoolValue && g_Extends < g_ConVars[mapvote_extend].IntValue) - { - if (g_NativeVotes) - { - g_VoteNative.AddItem(VOTE_EXTEND, "Extend Map"); - } - else - { - g_VoteMenu.AddItem(VOTE_EXTEND, "Extend Map"); - } - } - - /* There are no maps we could vote for. Don't show anything. */ - if (g_NativeVotes && g_VoteNative.ItemCount == 0) - { - g_HasVoteStarted = false; - g_VoteNative.Close(); - g_VoteNative = null; - return; - } - else if (!g_NativeVotes && g_VoteMenu.ItemCount == 0) - { - g_HasVoteStarted = false; - delete g_VoteMenu; - g_VoteMenu = null; - return; - } - - int voteDuration = g_ConVars[mapvote_voteduration].IntValue; - - if (g_NativeVotes) - { - g_VoteNative.DisplayVoteToAll(voteDuration); - } - else - { - g_VoteMenu.ExitButton = false; - g_VoteMenu.DisplayVoteToAll(voteDuration); - } - - LogAction(-1, -1, "Voting for next map has started."); - CPrintToChatAll("[{lightgreen}MapChooser\x01] %t", "Nextmap Voting Started"); -} - -public void Handler_NV_VoteFinishedGeneric(NativeVote menu, int num_votes, int num_clients, const int[] client_indexes, const int[] client_votes, int num_items, const int[] item_indexes, const int[] item_votes) -{ - int[][] client_info = new int[num_clients][2]; - int[][] item_info = new int[num_items][2]; - - NativeVotes_FixResults(num_clients, client_indexes, client_votes, num_items, item_indexes, item_votes, client_info, item_info); - - char map[PLATFORM_MAX_PATH]; - char displayName[PLATFORM_MAX_PATH]; - menu.GetItem(item_indexes[0], map, sizeof(map), displayName, sizeof(displayName)); - - Handler_VoteFinishedGenericShared(map, displayName, num_votes, num_clients, client_info, num_items, item_info, true); -} - -public void Handler_VoteFinishedGeneric(Menu menu, int num_votes, int num_clients, const int[][] client_info, int num_items, const int[][] item_info) -{ - char map[PLATFORM_MAX_PATH]; - char displayName[PLATFORM_MAX_PATH]; - menu.GetItem(item_info[0][VOTEINFO_ITEM_INDEX], map, sizeof(map), _, displayName, sizeof(displayName)); - - Handler_VoteFinishedGenericShared(map, displayName, num_votes, num_clients, client_info, num_items, item_info, false); -} - -public void Handler_VoteFinishedGenericShared(const char[] map, - const char[] displayName, - int num_votes, - int num_clients, - const int[][] client_info, - int num_items, - const int[][] item_info, - bool isNativeVotes) -{ - if (strcmp(map, VOTE_EXTEND, false) == 0) - { - g_Extends++; - - int time; - if (GetMapTimeLimit(time)) - { - if (time > 0) - { - ExtendMapTimeLimit(g_ConVars[extendmap_timestep].IntValue * 60); - } - } - - if (g_ConVars[mp_winlimit]) - { - int winlimit = g_ConVars[mp_winlimit].IntValue; - if (winlimit) - { - g_ConVars[mp_winlimit].IntValue = winlimit + g_ConVars[extendmap_roundstep].IntValue; - } - } - - if (g_ConVars[mp_maxrounds]) - { - int maxrounds = g_ConVars[mp_maxrounds].IntValue; - if (maxrounds) - { - g_ConVars[mp_maxrounds].IntValue = maxrounds + g_ConVars[extendmap_roundstep].IntValue; - } - } - - if (g_ConVars[mp_fraglimit]) - { - int fraglimit = g_ConVars[mp_fraglimit].IntValue; - if (fraglimit) - { - g_ConVars[mp_fraglimit].IntValue = fraglimit + g_ConVars[extendmap_fragstep].IntValue; - } - } - - CPrintToChatAll("[{lightgreen}MapChooser\x01] %t", "Current Map Extended", RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes); - LogAction(-1, -1, "Voting for next map has finished. The current map has been extended."); - - if (isNativeVotes) - { - g_VoteNative.DisplayPassEx(NativeVotesPass_Extend); - } - - // We extended, so we'll have to vote again. - g_HasVoteStarted = false; - CreateNextVote(); - SetupTimeleftTimer(); - - } - else if (strcmp(map, VOTE_DONTCHANGE, false) == 0) - { - CPrintToChatAll("[{lightgreen}MapChooser\x01] %t", "Current Map Stays", RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes); - LogAction(-1, -1, "Voting for next map has finished. 'No Change' was the winner"); - - if (isNativeVotes) - { - g_VoteNative.DisplayPassEx(NativeVotesPass_Extend); - } - - g_HasVoteStarted = false; - CreateNextVote(); - SetupTimeleftTimer(); - } - else - { - if (g_ChangeTime == MapChange_MapEnd) - { - SetNextMap(map); - } - else if (g_ChangeTime == MapChange_Instant) - { - DataPack data; - CreateDataTimer(2.0, Timer_ChangeMap, data); - data.WriteString(map); - g_ChangeMapInProgress = false; - } - else // MapChange_RoundEnd - { - SetNextMap(map); - g_ChangeMapAtRoundEnd = true; - } - - g_HasVoteStarted = false; - g_MapVoteCompleted = true; - - if (isNativeVotes) - { - g_VoteNative.DisplayPass(displayName); - } - - CPrintToChatAll("[{lightgreen}MapChooser\x01] %t", "Nextmap Voting Finished", displayName, RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes); - LogAction(-1, -1, "Voting for next map has finished. Nextmap: %s.", map); - } -} - -public void Handler_NV_MapVoteFinished(NativeVote menu, int num_votes, int num_clients, const int[] client_indexes, const int[] client_votes, int num_items, const int[] item_indexes, const int[] item_votes) -{ - if (g_ConVars[mapvote_runoff].BoolValue && num_items > 1) - { - float winningvotes = float(item_votes[0]); - float required = num_votes * (g_ConVars[mapvote_runoffpercent].FloatValue / 100.0); - - if (winningvotes < required) - { - //Added in 1.5.1 - menu.DisplayFail(NativeVotesFail_NotEnoughVotes); - - /* Insufficient Winning margin - Lets do a runoff */ - - char map1[PLATFORM_MAX_PATH]; - char map2[PLATFORM_MAX_PATH]; - char info1[PLATFORM_MAX_PATH]; - char info2[PLATFORM_MAX_PATH]; - - DataPack data; - - menu.GetItem(item_indexes[0], map1, sizeof(map1), info1, sizeof(info1)); - menu.GetItem(item_indexes[1], map2, sizeof(map2), info2, sizeof(info2)); - - CreateDataTimer(3.0, Timer_NV_Runoff, data, TIMER_FLAG_NO_MAPCHANGE); - - data.WriteString(map1); - data.WriteString(info1); - data.WriteString(map2); - data.WriteString(info2); - - data.Reset(); - - /* Notify */ - float map1percent = float(item_votes[0])/ float(num_votes) * 100; - float map2percent = float(item_votes[1])/ float(num_votes) * 100; - - - CPrintToChatAll("[{lightgreen}MapChooser\x01] %t", "Starting Runoff", g_ConVars[mapvote_runoffpercent].FloatValue, info1, map1percent, info2, map2percent); - LogMessage("Voting for next map was indecisive, beginning runoff vote"); - - return; - } - } - - Handler_NV_VoteFinishedGeneric(menu, num_votes, num_clients, client_indexes, client_votes, num_items, item_indexes, item_votes); -} - -// New in 1.5.1, used to fix runoff not working properly -public Action Timer_NV_Runoff(Handle timer, DataPack data) -{ - char map[96]; - char info[PLATFORM_MAX_PATH]; - - g_VoteNative = new NativeVote(Handler_NV_MapVoteMenu, NativeVotesType_NextLevelMult, NATIVEVOTES_ACTIONS_DEFAULT | MenuAction_DisplayItem); - g_VoteNative.VoteResultCallback = Handler_NV_VoteFinishedGeneric; - - data.ReadString(map, sizeof(map)); - data.ReadString(info, sizeof(info)); - g_VoteNative.AddItem(map, info); - - data.ReadString(map, sizeof(map)); - data.ReadString(info, sizeof(info)); - g_VoteNative.AddItem(map, info); - - int voteDuration = g_ConVars[mapvote_voteduration].IntValue; - g_VoteNative.DisplayVoteToAll(voteDuration); - - return Plugin_Continue; -} - -public void Handler_MapVoteFinished(Menu menu, int num_votes, int num_clients, const int[][] client_info, int num_items, const int[][] item_info) -{ - if (g_ConVars[mapvote_runoff].BoolValue && num_items > 1) - { - float winningvotes = float(item_info[0][VOTEINFO_ITEM_VOTES]); - float required = num_votes * (g_ConVars[mapvote_runoffpercent].FloatValue / 100.0); - - if (winningvotes < required) - { - /* Insufficient Winning margin - Lets do a runoff */ - g_VoteMenu = new Menu(Handler_MapVoteMenu, MENU_ACTIONS_ALL); - g_VoteMenu.SetTitle("Runoff Vote Nextmap"); - g_VoteMenu.VoteResultCallback = Handler_VoteFinishedGeneric; - - char map[PLATFORM_MAX_PATH], info1[PLATFORM_MAX_PATH], info2[PLATFORM_MAX_PATH]; - - menu.GetItem(item_info[0][VOTEINFO_ITEM_INDEX], map, sizeof(map), _, info1, sizeof(info1)); - g_VoteMenu.AddItem(map, info1); - menu.GetItem(item_info[1][VOTEINFO_ITEM_INDEX], map, sizeof(map), _, info2, sizeof(info2)); - g_VoteMenu.AddItem(map, info2); - - int voteDuration = g_ConVars[mapvote_voteduration].IntValue; - g_VoteMenu.ExitButton = false; - g_VoteMenu.DisplayVoteToAll(voteDuration); - - /* Notify */ - float map1percent = float(item_info[0][VOTEINFO_ITEM_VOTES])/ float(num_votes) * 100; - float map2percent = float(item_info[1][VOTEINFO_ITEM_VOTES])/ float(num_votes) * 100; - - CPrintToChatAll("[{lightgreen}MapChooser\x01] %t", "Starting Runoff", g_ConVars[mapvote_runoffpercent].FloatValue, info1, map1percent, info2, map2percent); - LogMessage("Voting for next map was indecisive, beginning runoff vote"); - - return; - } - } - - Handler_VoteFinishedGeneric(menu, num_votes, num_clients, client_info, num_items, item_info); -} - -public int Handler_MapVoteMenu(Menu menu, MenuAction action, int param1, int param2) -{ - switch (action) - { - case MenuAction_End: - { - g_VoteMenu = null; - delete menu; - } - - case MenuAction_Display: - { - char buffer[255]; - Format(buffer, sizeof(buffer), "%t", "Vote Nextmap", param1); - - Panel panel = view_as(param2); - panel.SetTitle(buffer); - } - - case MenuAction_DisplayItem: - { - if (menu.ItemCount - 1 == param2) - { - char map[PLATFORM_MAX_PATH], buffer[255]; - menu.GetItem(param2, map, sizeof(map)); - if (strcmp(map, VOTE_EXTEND, false) == 0) - { - Format(buffer, sizeof(buffer), "%t", "Extend Map", param1); - return RedrawMenuItem(buffer); - } - else if (strcmp(map, VOTE_DONTCHANGE, false) == 0) - { - Format(buffer, sizeof(buffer), "%t", "Dont Change", param1); - return RedrawMenuItem(buffer); - } - } - } - - case MenuAction_VoteCancel: - { - // If we receive 0 votes, pick at random. - if (param1 == VoteCancel_NoVotes && g_ConVars[mapvote_novote].BoolValue) - { - int count = menu.ItemCount; - char map[PLATFORM_MAX_PATH]; - menu.GetItem(0, map, sizeof(map)); - - // Make sure the first map in the menu isn't one of the special items. - // This would mean there are no real maps in the menu, because the special items are added after all maps. Don't do anything if that's the case. - if (strcmp(map, VOTE_EXTEND, false) != 0 && strcmp(map, VOTE_DONTCHANGE, false) != 0) - { - // Get a random map from the list. - int item = GetRandomInt(0, count - 1); - menu.GetItem(item, map, sizeof(map)); - - // Make sure it's not one of the special items. - while (strcmp(map, VOTE_EXTEND, false) == 0 || strcmp(map, VOTE_DONTCHANGE, false) == 0) - { - item = GetRandomInt(0, count - 1); - menu.GetItem(item, map, sizeof(map)); - } - - SetNextMap(map); - g_MapVoteCompleted = true; - } - } - else - { - // We were actually cancelled. I guess we do nothing. - } - - g_HasVoteStarted = false; - } - } - - return 0; -} - -public int Handler_NV_MapVoteMenu(NativeVote menu, MenuAction action, int param1, int param2) -{ - switch (action) - { - case MenuAction_End: - { - g_VoteMenu = null; - menu.Close(); - } - - case MenuAction_DisplayItem: - { - if (menu.ItemCount - 1 == param2) - { - char map[PLATFORM_MAX_PATH], buffer[255]; - menu.GetItem(param2, map, sizeof(map)); - if (strcmp(map, VOTE_EXTEND, false) == 0) - { - Format(buffer, sizeof(buffer), "%t", "Extend Map", param1); - return view_as(NativeVotes_RedrawVoteItem(buffer)); - } - else if (strcmp(map, VOTE_DONTCHANGE, false) == 0) - { - Format(buffer, sizeof(buffer), "%t", "Dont Change", param1); - return view_as(NativeVotes_RedrawVoteItem(buffer)); - } - } - } - - case MenuAction_VoteCancel: - { - // If we receive 0 votes, pick at random. - if (param1 == VoteCancel_NoVotes && g_ConVars[mapvote_novote].BoolValue) - { - int count = menu.ItemCount; - char map[PLATFORM_MAX_PATH], displayName[PLATFORM_MAX_PATH]; - menu.GetItem(0, map, sizeof(map)); - - // Make sure the first map in the menu isn't one of the special items. - // This would mean there are no real maps in the menu, because the special items are added after all maps. Don't do anything if that's the case. - if (strcmp(map, VOTE_EXTEND, false) != 0 && strcmp(map, VOTE_DONTCHANGE, false) != 0) - { - // Get a random map from the list. - int item = GetRandomInt(0, count - 1); - menu.GetItem(item, map, sizeof(map), displayName, sizeof(displayName)); - - // Make sure it's not one of the special items. - while (strcmp(map, VOTE_EXTEND, false) == 0 || strcmp(map, VOTE_DONTCHANGE, false) == 0) - { - item = GetRandomInt(0, count - 1); - menu.GetItem(item, map, sizeof(map), displayName, sizeof(displayName)); - } - - SetNextMap(map); - g_MapVoteCompleted = true; - menu.DisplayPass(displayName); - } - } - else if (param1 == VoteCancel_NoVotes) - { - // We didn't have enough votes. Display the note enough votes fail message. - menu.DisplayFail(NativeVotesFail_NotEnoughVotes); - } - else - { - // We were actually cancelled. Display the generic fail message - menu.DisplayFail(NativeVotesFail_Generic); - } - - g_HasVoteStarted = false; - } - } - - return 0; -} - -public Action Timer_ChangeMap(Handle hTimer, DataPack dp) -{ - g_ChangeMapInProgress = false; - - char map[PLATFORM_MAX_PATH]; - - if (dp == null) - { - if (!GetNextMap(map, sizeof(map))) - { - //No passed map and no set nextmap. fail! - return Plugin_Stop; - } - } - else - { - dp.Reset(); - dp.ReadString(map, sizeof(map)); - } - - ForceChangeLevel(map, "Map Vote"); - - return Plugin_Stop; -} - -bool RemoveStringFromArray(ArrayList array, char[] str) -{ - int index = array.FindString(str); - if (index != -1) - { - array.Erase(index); - return true; - } - - return false; -} - -void CreateNextVote() -{ - g_NextMapList.Clear(); - - char map[PLATFORM_MAX_PATH]; - // tempMaps is a resolved map list - ArrayList tempMaps = new ArrayList(ByteCountToCells(PLATFORM_MAX_PATH)); - - for (int i = 0; i < g_MapList.Length; i++) - { - g_MapList.GetString(i, map, sizeof(map)); - if (FindMap(map, map, sizeof(map)) != FindMap_NotFound) - { - tempMaps.PushString(map); - } - } - - //GetCurrentMap always returns a resolved map - GetCurrentMap(map, sizeof(map)); - RemoveStringFromArray(tempMaps, map); - - if (g_ConVars[mapvote_exclude].IntValue && tempMaps.Length > g_ConVars[mapvote_exclude].IntValue) - { - for (int i = 0; i < g_OldMapList.Length; i++) - { - g_OldMapList.GetString(i, map, sizeof(map)); - RemoveStringFromArray(tempMaps, map); - } - } - - int limit = (g_ConVars[mapvote_include].IntValue < tempMaps.Length ? g_ConVars[mapvote_include].IntValue : tempMaps.Length); - for (int i = 0; i < limit; i++) - { - int b = GetRandomInt(0, tempMaps.Length - 1); - tempMaps.GetString(b, map, sizeof(map)); - g_NextMapList.PushString(map); - tempMaps.Erase(b); - } - - delete tempMaps; -} - -bool CanVoteStart() -{ - if (g_WaitingForVote || g_HasVoteStarted) - { - return false; - } - - return true; -} - -NominateResult InternalNominateMap(char[] map, bool force, int owner) -{ - if (!IsMapValid(map)) - { - return Nominate_InvalidMap; - } - - /* Map already in the vote */ - if (g_NominateList.FindString(map) != -1) - { - return Nominate_AlreadyInVote; - } - - int index; - - /* Look to replace an existing nomination by this client - Nominations made with owner = 0 aren't replaced */ - if (owner && ((index = g_NominateOwners.FindValue(owner)) != -1)) - { - char oldmap[PLATFORM_MAX_PATH]; - g_NominateList.GetString(index, oldmap, sizeof(oldmap)); - Call_StartForward(g_NominationsResetForward); - Call_PushString(oldmap); - Call_PushCell(owner); - Call_Finish(); - - g_NominateList.SetString(index, map); - return Nominate_Replaced; - } - - /* Too many nominated maps. */ - int maxIncludes = 0; - if (g_NativeVotes) - { - maxIncludes = NativeVotes_GetMaxItems(); - - if (g_ConVars[mapvote_include].IntValue < maxIncludes) - { - maxIncludes = g_ConVars[mapvote_include].IntValue; - } - - if (g_ConVars[mapvote_extend].BoolValue && g_Extends < g_ConVars[mapvote_extend].IntValue) - { - maxIncludes--; - } - } - else - { - maxIncludes = g_ConVars[mapvote_include].IntValue; - } - - if (g_NominateList.Length >= maxIncludes && !force) - { - return Nominate_VoteFull; - } - - g_NominateList.PushString(map); - g_NominateOwners.Push(owner); - - while (g_NominateList.Length > g_ConVars[mapvote_include].IntValue) - { - char oldmap[PLATFORM_MAX_PATH]; - g_NominateList.GetString(0, oldmap, sizeof(oldmap)); - Call_StartForward(g_NominationsResetForward); - Call_PushString(oldmap); - Call_PushCell(g_NominateOwners.Get(0)); - Call_Finish(); - - g_NominateList.Erase(0); - g_NominateOwners.Erase(0); - } - - return Nominate_Added; -} - -/* Add natives to allow nominate and initiate vote to be call */ - -/* native NominateResult NominateMap(const char[] map, bool force, int owner); */ -public int Native_NominateMap(Handle plugin, int numParams) -{ - int len; - GetNativeStringLength(1, len); - - if (len <= 0) - { - return false; - } - - char[] map = new char[len+1]; - GetNativeString(1, map, len+1); - - return view_as(InternalNominateMap(map, GetNativeCell(2), GetNativeCell(3))); -} - -bool InternalRemoveNominationByMap(char[] map) -{ - for (int i = 0; i < g_NominateList.Length; i++) - { - char oldmap[PLATFORM_MAX_PATH]; - g_NominateList.GetString(i, oldmap, sizeof(oldmap)); - - if(strcmp(map, oldmap, false) == 0) - { - Call_StartForward(g_NominationsResetForward); - Call_PushString(oldmap); - Call_PushCell(g_NominateOwners.Get(i)); - Call_Finish(); - - g_NominateList.Erase(i); - g_NominateOwners.Erase(i); - - return true; - } - } - - return false; -} - -/* native bool RemoveNominationByMap(const char[] map); */ -public int Native_RemoveNominationByMap(Handle plugin, int numParams) -{ - int len; - GetNativeStringLength(1, len); - - if (len <= 0) - { - return false; - } - - char[] map = new char[len+1]; - GetNativeString(1, map, len+1); - - return InternalRemoveNominationByMap(map); -} - -bool InternalRemoveNominationByOwner(int owner) -{ - int index; - - if (owner && ((index = g_NominateOwners.FindValue(owner)) != -1)) - { - char oldmap[PLATFORM_MAX_PATH]; - g_NominateList.GetString(index, oldmap, sizeof(oldmap)); - - Call_StartForward(g_NominationsResetForward); - Call_PushString(oldmap); - Call_PushCell(owner); - Call_Finish(); - - g_NominateList.Erase(index); - g_NominateOwners.Erase(index); - - return true; - } - - return false; -} - -/* native bool RemoveNominationByOwner(int owner); */ -public int Native_RemoveNominationByOwner(Handle plugin, int numParams) -{ - return InternalRemoveNominationByOwner(GetNativeCell(1)); -} - -/* native void InitiateMapChooserVote(MapChange when, ArrayList inputarray=null); */ -public int Native_InitiateVote(Handle plugin, int numParams) -{ - MapChange when = view_as(GetNativeCell(1)); - ArrayList inputarray = view_as(GetNativeCell(2)); - - LogAction(-1, -1, "Starting map vote because outside request"); - InitiateVote(when, inputarray); -} - -/* native bool CanMapChooserStartVote(); */ -public int Native_CanVoteStart(Handle plugin, int numParams) -{ - return CanVoteStart(); -} - -/* native bool HasEndOfMapVoteFinished(); */ -public int Native_CheckVoteDone(Handle plugin, int numParams) -{ - return g_MapVoteCompleted; -} - -/* native bool EndOfMapVoteEnabled(); */ -public int Native_EndOfMapVoteEnabled(Handle plugin, int numParams) -{ - return g_ConVars[mapvote_endvote].BoolValue; -} - -/* native void GetExcludeMapList(ArrayList array); */ -public int Native_GetExcludeMapList(Handle plugin, int numParams) -{ - ArrayList array = view_as(GetNativeCell(1)); - - if (array == null) - { - return; - } - int size = g_OldMapList.Length; - char map[PLATFORM_MAX_PATH]; - - for (int i = 0; i < size; i++) - { - g_OldMapList.GetString(i, map, sizeof(map)); - array.PushString(map); - } - - return; -} - -/* native void GetNominatedMapList(ArrayList maparray, ArrayList ownerarray = null); */ -public int Native_GetNominatedMapList(Handle plugin, int numParams) -{ - ArrayList maparray = view_as(GetNativeCell(1)); - ArrayList ownerarray = view_as(GetNativeCell(2)); - - if (maparray == null) - return; - - char map[PLATFORM_MAX_PATH]; - - for (int i = 0; i < g_NominateList.Length; i++) - { - g_NominateList.GetString(i, map, sizeof(map)); - maparray.PushString(map); - - // If the optional parameter for an owner list was passed, then we need to fill that out as well - if(ownerarray != null) - { - int index = g_NominateOwners.Get(i); - ownerarray.Push(index); - } - } - - return; -} - -void PopulateMapList() -{ - char configsPath[PLATFORM_MAX_PATH]; - BuildPath(Path_SM, configsPath, sizeof(configsPath), "configs/nativevotes"); - if (!DirExists(configsPath)) - { - CreateDirectory(configsPath); - } - - char mapcycle_generated[PLATFORM_MAX_PATH]; - Format(mapcycle_generated, sizeof(mapcycle_generated), "%s/mapcycle_generated.txt", configsPath); - - File mapcycleFile = OpenFile(mapcycle_generated, "wt"); - if (mapcycleFile != null) - { - mapcycleFile = OpenFile(mapcycle_generated, "a"); - } - else - { - return; - } - - char excludePattern[256]; - g_ConVars[mapcycle_exclude].GetString(excludePattern, sizeof(excludePattern)); - Regex regex = new Regex(excludePattern); - - DirectoryListing dir = OpenDirectory("maps"); - if (dir == null) - { - delete regex; - CloseHandle(mapcycleFile); - return; - } - - char mapName[PLATFORM_MAX_PATH]; - FileType type; - int len; - - while (dir.GetNext(mapName, sizeof(mapName), type)) - { - if (type != FileType_File) - continue; - - len = strlen(mapName); - if (len <= 4 || !StrEqual(mapName[len-4], ".bsp", false)) - continue; - - mapName[len-4] = '\0'; - - if (regex.Match(mapName) >= 1) - continue; - - mapcycleFile.WriteLine("%s", mapName); - } - delete dir; - delete regex; - CloseHandle(mapcycleFile); -} - - -void CleanupWorkshopMaps() -{ - KeyValues kv = new KeyValues("GameInfo"); - int appid; - if (kv.ImportFromFile("gameinfo.txt") && kv.JumpToKey("FileSystem")) - { - appid = kv.GetNum("SteamAppId"); - } - delete kv; - - char workshopDir[PLATFORM_MAX_PATH]; - Format(workshopDir, sizeof(workshopDir), "../steamapps/workshop/content/%d", appid); - - DirectoryListing dir = OpenDirectory(workshopDir); - if (dir != null) - { - char ugcid[PLATFORM_MAX_PATH]; - FileType type; - char path[PLATFORM_MAX_PATH]; - - while (dir.GetNext(ugcid, sizeof(ugcid), type)) - { - if (type != FileType_Directory) - continue; - - Format(path, sizeof(path), "%s/%s", workshopDir, ugcid); - DirectoryListing ugcDir = OpenDirectory(path); - if (ugcDir != null) - { - char file[PLATFORM_MAX_PATH]; - FileType fileType; - char filePath[PLATFORM_MAX_PATH]; - while (ugcDir.GetNext(file, sizeof(file), fileType)) - { - if (fileType == FileType_File) - { - Format(filePath, sizeof(filePath), "%s/%s", path, file); - DeleteFile(filePath); - } - } - delete ugcDir; - } - RemoveDir(path); - } - delete dir; - } - RemoveDir(workshopDir); +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod NativeVotes Mapchooser Plugin + * Creates a map vote at appropriate times, setting sm_nextmap to the winning + * vote + * Updated with NativeVotes support + * + * NativeVotes (C)2011-2016 Ross Bemrose (Powerlord). All rights reserved. + * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#include +#include +#include +#include + +#undef REQUIRE_PLUGIN +#include +#define REQUIRE_PLUGIN + +#undef REQUIRE_EXTENSIONS +#include +#define REQUIRE_EXTENSIONS + +#pragma semicolon 1 +#pragma newdecls required + +public Plugin myinfo = +{ + name = "NativeVotes | MapChooser", + author = "AlliedModders LLC and Powerlord", + description = "Automated Map Voting", + version = "26w05a", + url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" +}; + +/* ConVars */ +enum +{ + /* Valve ConVars */ + mp_winlimit, + mp_maxrounds, + mp_fraglimit, + mp_bonusroundtime, + mapcyclefile, + + /* Plugin ConVars */ + mapvote_endvote, + mapvote_start, + mapvote_startround, + mapvote_startfrags, + extendmap_timestep, + extendmap_roundstep, + extendmap_fragstep, + mapvote_exclude, + mapvote_include, + mapvote_novote, + mapvote_extend, + mapvote_dontchange, + mapvote_voteduration, + mapvote_runoff, + mapvote_runoffpercent, + mapcycle_auto, + mapcycle_exclude, + workshop_map_collection, + workshop_cleanup, + + MAX_CONVARS +} + +ConVar g_ConVars[MAX_CONVARS]; + +Handle g_VoteTimer = null; +Handle g_RetryTimer = null; + +// g_MapList stores unresolved names so we can resolve them after every map change in the workshop updates. +// g_OldMapList and g_NextMapList are resolved. g_NominateList depends on the nominations implementation. +/* Data Handles */ +ArrayList g_MapList; +ArrayList g_NominateList; +ArrayList g_NominateOwners; +ArrayList g_OldMapList; +ArrayList g_NextMapList; +Menu g_VoteMenu; +NativeVote g_VoteNative; + +int g_Extends; +int g_TotalRounds; +bool g_HasVoteStarted; +bool g_WaitingForVote; +bool g_MapVoteCompleted; +bool g_ChangeMapAtRoundEnd; +bool g_ChangeMapInProgress; +int g_mapFileSerial = -1; + +MapChange g_ChangeTime; + +Handle g_NominationsResetForward = null; +Handle g_MapVoteStartedForward = null; + +/* Upper bound of how many team there could be */ +#define MAX_TEAMS 10 +int g_winCount[MAX_TEAMS]; + +#define VOTE_EXTEND "##extend##" +#define VOTE_DONTCHANGE "##dontchange##" + +// Libraries +bool g_NativeVotes; +bool g_RestInPawn; + +public void OnPluginStart() +{ + LoadTranslations("mapchooser.phrases"); + LoadTranslations("common.phrases"); + + KeyValues kv = new KeyValues("GameInfo"); + kv.ImportFromFile("gameinfo.txt"); + + char gameDir[128]; + GetGameFolderName(gameDir, sizeof(gameDir)); + + EngineVersion engine = GetEngineVersion(); + if (!StrEqual(gameDir, "tf") && + (kv.GetNum("DependsOnAppID") == 440 || + (engine == Engine_SDK2013 && FileExists("resource/tf.ttf")))) + { + engine = Engine_TF2; + } + + int arraySize = ByteCountToCells(PLATFORM_MAX_PATH); + g_MapList = new ArrayList(arraySize); + g_NominateList = new ArrayList(arraySize); + g_NominateOwners = new ArrayList(); + g_OldMapList = new ArrayList(arraySize); + g_NextMapList = new ArrayList(arraySize); + + g_ConVars[mapvote_endvote] = CreateConVar("sm_mapvote_endvote", "1", "Specifies if MapChooser should run an end of map vote.", _, true, 0.0, true, 1.0); + g_ConVars[mapvote_start] = CreateConVar("sm_mapvote_start", "3.0", "Specifies when to start the vote based on time remaining.", _, true, 1.0); + g_ConVars[mapvote_startround] = CreateConVar("sm_mapvote_startround", "2.0", "Specifies when to start the vote based on rounds remaining. Use '0' on TF2 to start vote during bonus round time", _, true, 0.0); + g_ConVars[mapvote_startfrags] = CreateConVar("sm_mapvote_startfrags", "5.0", "Specifies when to start the vote base on frags remaining.", _, true, 1.0); + g_ConVars[extendmap_timestep] = CreateConVar("sm_extendmap_timestep", "15", "Specifies how much many more minutes each extension makes.", _, true, 5.0); + g_ConVars[extendmap_roundstep] = CreateConVar("sm_extendmap_roundstep", "5", "Specifies how many more rounds each extension makes.", _, true, 1.0); + g_ConVars[extendmap_fragstep] = CreateConVar("sm_extendmap_fragstep", "10", "Specifies how many more frags are allowed when map is extended.", _, true, 5.0); + g_ConVars[mapvote_exclude] = CreateConVar("sm_mapvote_exclude", "5", "Specifies how many past maps to exclude from the vote.", _, true, 0.0); + g_ConVars[mapvote_include] = CreateConVar("sm_mapvote_include", "5", "Specifies how many maps to include in the vote.", _, true, 2.0, true, 6.0); + g_ConVars[mapvote_novote] = CreateConVar("sm_mapvote_novote", "1", "Specifies whether or not MapChooser should pick a map if no votes are received.", _, true, 0.0, true, 1.0); + g_ConVars[mapvote_extend] = CreateConVar("sm_mapvote_extend", "0", "Number of extensions allowed each map.", _, true, 0.0); + g_ConVars[mapvote_dontchange] = CreateConVar("sm_mapvote_dontchange", "1", "Specifies if a 'Don't Change' option should be added to early votes.", _, true, 0.0, true, 1.0); + g_ConVars[mapvote_voteduration] = CreateConVar("sm_mapvote_voteduration", "20", "Specifies how long the mapvote should be available for.", _, true, 5.0); + g_ConVars[mapvote_runoff] = CreateConVar("sm_mapvote_runoff", "0", "Hold run of votes if winning choice is less than a certain margin.", _, true, 0.0, true, 1.0); + g_ConVars[mapvote_runoffpercent] = CreateConVar("sm_mapvote_runoffpercent", "50", "If winning choice has less than this percent of votes, hold a runoff.", _, true, 0.0, true, 100.0); + g_ConVars[mapcycle_auto] = CreateConVar("sm_mapcycle_auto", "0", "Specifies whether or not to automatically populate the maps list.", _, true, 0.0, true, 1.0); + g_ConVars[mapcycle_exclude] = CreateConVar("sm_mapcycle_exclude", ".*itemtest.*|background01|^tr.*$", "Specifies which maps shouldn't be automatically added with a regex pattern."); + if (engine != Engine_SDK2013 && engine == Engine_TF2) + { + g_ConVars[workshop_map_collection] = CreateConVar("sm_workshop_map_collection", "", "Specifies the workshop collection to fetch the maps from."); + g_ConVars[workshop_cleanup] = CreateConVar("sm_workshop_map_cleanup", "0", "Specifies whether or not to automatically workshop maps on map change.", _, true, 0.0, true, 1.0); + } + + RegAdminCmd("sm_mapvote", Command_MapVote, ADMFLAG_CHANGEMAP, "Forces MapChooser to attempt to run a map vote now."); + RegAdminCmd("sm_setnextmap", Command_SetNextMap, ADMFLAG_CHANGEMAP, "sm_setnextmap "); + + g_ConVars[mp_winlimit] = FindConVar("mp_winlimit"); + g_ConVars[mp_maxrounds] = FindConVar("mp_maxrounds"); + g_ConVars[mp_fraglimit] = FindConVar("mp_fraglimit"); + g_ConVars[mp_bonusroundtime] = FindConVar("mp_bonusroundtime"); + g_ConVars[mapcyclefile] = FindConVar("mapcyclefile"); + + if (g_ConVars[mp_winlimit] || g_ConVars[mp_maxrounds]) + { + switch (engine) + { + case Engine_TF2: + { + HookEvent("teamplay_win_panel", Event_TeamplayWinPanel); + HookEvent("teamplay_restart_round", Event_TeamplayRestartRound); + HookEvent("arena_win_panel", Event_TeamplayWinPanel); + } + case Engine_NuclearDawn: + { + HookEvent("round_win", Event_RoundEnd); + } + default: + { + HookEvent("round_end", Event_RoundEnd); + } + } + } + + if (g_ConVars[mp_fraglimit]) + { + HookEvent("player_death", Event_PlayerDeath); + } + + AutoExecConfig(true, "mapchooser"); + + // Change the mp_bonusroundtime max so that we have time to display the vote + // If you display a vote during bonus time good defaults are 17 vote duration and 19 mp_bonustime + if (g_ConVars[mp_bonusroundtime]) + { + g_ConVars[mp_bonusroundtime].SetBounds(ConVarBound_Upper, true, 30.0); + } + + g_NominationsResetForward = CreateGlobalForward("OnNominationRemoved", ET_Ignore, Param_String, Param_Cell); + g_MapVoteStartedForward = CreateGlobalForward("OnMapVoteStarted", ET_Ignore); +} + +public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) +{ + RegPluginLibrary("mapchooser"); + + CreateNative("NominateMap", Native_NominateMap); + CreateNative("RemoveNominationByMap", Native_RemoveNominationByMap); + CreateNative("RemoveNominationByOwner", Native_RemoveNominationByOwner); + CreateNative("InitiateMapChooserVote", Native_InitiateVote); + CreateNative("CanMapChooserStartVote", Native_CanVoteStart); + CreateNative("HasEndOfMapVoteFinished", Native_CheckVoteDone); + CreateNative("GetExcludeMapList", Native_GetExcludeMapList); + CreateNative("GetNominatedMapList", Native_GetNominatedMapList); + CreateNative("EndOfMapVoteEnabled", Native_EndOfMapVoteEnabled); + + // Why doesn't RIP ext already set these as optional?? + MarkNativeAsOptional("HTTPRequest.HTTPRequest"); + MarkNativeAsOptional("HTTPRequest.AppendFormParam"); + MarkNativeAsOptional("HTTPRequest.PostForm"); + MarkNativeAsOptional("HTTPResponse.Status.get"); + MarkNativeAsOptional("HTTPResponse.Data.get"); + MarkNativeAsOptional("JSONObject.Get"); + MarkNativeAsOptional("JSONObject.GetInt"); + MarkNativeAsOptional("JSONObject.GetString"); + MarkNativeAsOptional("JSONArray.Get"); + MarkNativeAsOptional("JSONArray.Length.get"); + + return APLRes_Success; +} + +public void OnAllPluginsLoaded() +{ + if (FindPluginByFile("mapchooser.smx") != null) + { + LogMessage("Unloading mapchooser to prevent conflicts..."); + ServerCommand("sm plugins unload mapchooser"); + + char oldPath[PLATFORM_MAX_PATH]; + char newPath[PLATFORM_MAX_PATH]; + + BuildPath(Path_SM, oldPath, sizeof(oldPath), "plugins/mapchooser.smx"); + BuildPath(Path_SM, newPath, sizeof(newPath), "plugins/disabled/mapchooser.smx"); + if (RenameFile(newPath, oldPath)) + { + LogMessage("Moving mapchooser to disabled."); + } + } + + g_NativeVotes = LibraryExists("nativevotes") && NativeVotes_IsVoteTypeSupported(NativeVotesType_NextLevelMult); + g_RestInPawn = GetExtensionFileStatus("rip.ext") == 1; +} + +public void OnLibraryAdded(const char[] name) +{ + if (StrEqual(name, "nativevotes", false) && NativeVotes_IsVoteTypeSupported(NativeVotesType_NextLevelMult)) + { + g_NativeVotes = true; + } +} + +public void OnLibraryRemoved(const char[] name) +{ + if (StrEqual(name, "nativevotes", false)) + { + g_NativeVotes = false; + } + else if (StrEqual(name, "rip.ext", false)) + { + g_RestInPawn = false; + } +} + +public void OnConfigsExecuted() +{ + if (g_ConVars[workshop_cleanup].BoolValue) + { + CleanupWorkshopMaps(); + } + + if (g_ConVars[mapcycle_auto].BoolValue) + { + PopulateMapList(); + } + + if (ReadMapList(g_MapList, g_mapFileSerial, "mapchooser", MAPLIST_FLAG_CLEARARRAY|MAPLIST_FLAG_MAPSFOLDER) != null) + { + if (g_mapFileSerial == -1) + { + LogError("Unable to create a valid map list."); + } + } + + CreateNextVote(); + SetupTimeleftTimer(); + + g_TotalRounds = 0; + + g_Extends = 0; + + g_MapVoteCompleted = false; + + g_NominateList.Clear(); + g_NominateOwners.Clear(); + + for (int i = 0; i < MAX_TEAMS; i++) + { + g_winCount[i] = 0; + } + + /* Check if mapchooser will attempt to start mapvote during bonus round time - TF2 Only */ + if (g_ConVars[mp_bonusroundtime] && !g_ConVars[mapvote_startround].IntValue) + { + if (g_ConVars[mp_bonusroundtime].FloatValue <= g_ConVars[mapvote_voteduration].FloatValue) + { + LogMessage("Warning - Bonus Round Time shorter than Vote Time. Votes during bonus round may not have time to complete"); + } + } +} + +public void OnMapEnd() +{ + g_HasVoteStarted = false; + g_WaitingForVote = false; + g_ChangeMapAtRoundEnd = false; + g_ChangeMapInProgress = false; + + g_VoteTimer = null; + g_RetryTimer = null; + + char map[PLATFORM_MAX_PATH]; + GetCurrentMap(map, sizeof(map)); + g_OldMapList.PushString(map); + + while (g_OldMapList.Length > g_ConVars[mapvote_exclude].IntValue) + { + g_OldMapList.Erase(0); + } +} + +public void OnClientDisconnect(int client) +{ + int index = g_NominateOwners.FindValue(client); + + if (index == -1) + { + return; + } + + char oldmap[PLATFORM_MAX_PATH]; + g_NominateList.GetString(index, oldmap, sizeof(oldmap)); + Call_StartForward(g_NominationsResetForward); + Call_PushString(oldmap); + Call_PushCell(g_NominateOwners.Get(index)); + Call_Finish(); + + g_NominateOwners.Erase(index); + g_NominateList.Erase(index); +} + +public Action Command_SetNextMap(int client, int args) +{ + if (args < 1) + { + CReplyToCommand(client, "[{lightgreen}MapChooser\x01] Usage: sm_setnextmap "); + return Plugin_Handled; + } + + char map[PLATFORM_MAX_PATH], displayName[PLATFORM_MAX_PATH]; + GetCmdArg(1, map, sizeof(map)); + + if (FindMap(map, displayName, sizeof(displayName)) == FindMap_NotFound) + { + CReplyToCommand(client, "[{lightgreen}MapChooser\x01] %t", "Map was not found", map); + return Plugin_Handled; + } + + GetMapDisplayName(displayName, displayName, sizeof(displayName)); + Format(displayName, sizeof(displayName), "\x05%s\x01", displayName); + + CShowActivity(client, "%t", "Changed Next Map", displayName); + LogAction(client, -1, "\"%L\" changed nextmap to \"%s\"", client, map); + + SetNextMap(map); + g_MapVoteCompleted = true; + + return Plugin_Handled; +} + +public void OnMapTimeLeftChanged() +{ + if (g_MapList.Length) + { + SetupTimeleftTimer(); + } +} + +void SetupTimeleftTimer() +{ + int time; + if (GetMapTimeLeft(time) && time > 0) + { + int startTime = g_ConVars[mapvote_start].IntValue * 60; + if (time - startTime < 0 && g_ConVars[mapvote_endvote].BoolValue && !g_MapVoteCompleted && !g_HasVoteStarted) + { + InitiateVote(MapChange_MapEnd, null); + } + else + { + if (g_VoteTimer != null) + { + KillTimer(g_VoteTimer); + g_VoteTimer = null; + } + + //g_VoteTimer = CreateTimer(float(time - startTime), Timer_StartMapVote, _, TIMER_FLAG_NO_MAPCHANGE); + DataPack data; + g_VoteTimer = CreateDataTimer(float(time - startTime), Timer_StartMapVote, data, TIMER_FLAG_NO_MAPCHANGE); + data.WriteCell(MapChange_MapEnd); + data.WriteCell(INVALID_HANDLE); + data.Reset(); + } + } +} + +public Action Timer_StartMapVote(Handle timer, DataPack data) +{ + if (timer == g_RetryTimer) + { + g_WaitingForVote = false; + g_RetryTimer = null; + } + else + { + g_VoteTimer = null; + } + + if (!g_MapList.Length || !g_ConVars[mapvote_endvote].BoolValue || g_MapVoteCompleted || g_HasVoteStarted) + { + return Plugin_Stop; + } + + MapChange mapChange = view_as(data.ReadCell()); + ArrayList hndl = view_as(data.ReadCell()); + + InitiateVote(mapChange, hndl); + + return Plugin_Stop; +} + +public void Event_TeamplayRestartRound(Event event, const char[] name, bool dontBroadcast) +{ + /* Game got restarted - reset our round count tracking */ + g_TotalRounds = 0; +} + +public void Event_TeamplayWinPanel(Event event, const char[] name, bool dontBroadcast) +{ + if (g_ChangeMapAtRoundEnd) + { + g_ChangeMapAtRoundEnd = false; + CreateTimer(2.0, Timer_ChangeMap, INVALID_HANDLE, TIMER_FLAG_NO_MAPCHANGE); + g_ChangeMapInProgress = true; + } + + int bluescore = event.GetInt("blue_score"); + int redscore = event.GetInt("red_score"); + + if (event.GetInt("round_complete") == 1 || StrEqual(name, "arena_win_panel")) + { + g_TotalRounds++; + + if (!g_MapList.Length || g_HasVoteStarted || g_MapVoteCompleted || !g_ConVars[mapvote_endvote].BoolValue) + { + return; + } + + CheckMaxRounds(g_TotalRounds); + + switch(event.GetInt("winning_team")) + { + case 3: + { + CheckWinLimit(bluescore); + } + case 2: + { + CheckWinLimit(redscore); + } + //We need to do nothing on winning_team == 0 this indicates stalemate. + default: + { + return; + } + } + } +} +/* You ask, why don't you just use team_score event? And I answer... Because CSS doesn't. */ +public void Event_RoundEnd(Event event, const char[] name, bool dontBroadcast) +{ + if (g_ChangeMapAtRoundEnd) + { + g_ChangeMapAtRoundEnd = false; + CreateTimer(2.0, Timer_ChangeMap, INVALID_HANDLE, TIMER_FLAG_NO_MAPCHANGE); + g_ChangeMapInProgress = true; + } + + int winner; + if (strcmp(name, "round_win") == 0) + { + // Nuclear Dawn + winner = event.GetInt("team"); + } + else + { + winner = event.GetInt("winner"); + } + + if (winner == 0 || winner == 1 || !g_ConVars[mapvote_endvote].BoolValue) + { + return; + } + + if (winner >= MAX_TEAMS) + { + SetFailState("Mod exceed maximum team count - Please file a bug report."); + } + + g_TotalRounds++; + + g_winCount[winner]++; + + if (!g_MapList.Length || g_HasVoteStarted || g_MapVoteCompleted) + { + return; + } + + CheckWinLimit(g_winCount[winner]); + CheckMaxRounds(g_TotalRounds); +} + +public void CheckWinLimit(int winner_score) +{ + if (g_ConVars[mp_winlimit]) + { + int winlimit = g_ConVars[mp_winlimit].IntValue; + if (winlimit) + { + if (winner_score >= (winlimit - g_ConVars[mapvote_startround].IntValue)) + { + InitiateVote(MapChange_MapEnd, null); + } + } + } +} + +public void CheckMaxRounds(int roundcount) +{ + if (g_ConVars[mp_maxrounds]) + { + int maxrounds = g_ConVars[mp_maxrounds].IntValue; + if (maxrounds) + { + if (roundcount >= (maxrounds - g_ConVars[mapvote_startround].IntValue)) + { + InitiateVote(MapChange_MapEnd, null); + } + } + } +} + +public void Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast) +{ + if (!g_MapList.Length || !g_ConVars[mp_fraglimit] || g_HasVoteStarted) + { + return; + } + + if (!g_ConVars[mp_fraglimit].IntValue || !g_ConVars[mapvote_endvote].BoolValue) + { + return; + } + + if (g_MapVoteCompleted) + { + return; + } + + int fragger = GetClientOfUserId(event.GetInt("attacker")); + + if (!fragger) + { + return; + } + + if (GetClientFrags(fragger) >= (g_ConVars[mp_fraglimit].IntValue - g_ConVars[mapvote_startfrags].IntValue)) + { + InitiateVote(MapChange_MapEnd, null); + } +} + +public Action Command_MapVote(int client, int args) +{ + InitiateVote(MapChange_MapEnd, null); + + return Plugin_Handled; +} + +/** + * Starts a new map vote + * + * @param when When the resulting map change should occur. + * @param inputlist Optional list of maps to use for the vote, otherwise an internal list of nominations + random maps will be used. + * @param noSpecials Block special vote options like extend/nochange (upgrade this to bitflags instead?) + */ +void InitiateVote(MapChange when, ArrayList inputlist=null) +{ + g_WaitingForVote = true; + + if ((g_NativeVotes && NativeVotes_IsVoteInProgress()) || (!g_NativeVotes && IsVoteInProgress())) + //if (IsVoteInProgress()) + { + // Can't start a vote, try again in 5 seconds. + //g_RetryTimer = CreateTimer(5.0, Timer_StartMapVote, _, TIMER_FLAG_NO_MAPCHANGE); + + DataPack data; + g_RetryTimer = CreateDataTimer(5.0, Timer_StartMapVote, data, TIMER_FLAG_NO_MAPCHANGE); + data.WriteCell(when); + data.WriteCell(inputlist); + data.Reset(); + return; + } + + /* If the main map vote has completed (and chosen result) and its currently changing (not a delayed change) we block further attempts */ + if (g_MapVoteCompleted && g_ChangeMapInProgress) + { + return; + } + + g_ChangeTime = when; + + g_WaitingForVote = false; + + g_HasVoteStarted = true; + if (g_NativeVotes) + { + g_VoteNative = new NativeVote(Handler_NV_MapVoteMenu, NativeVotesType_NextLevelMult, NATIVEVOTES_ACTIONS_DEFAULT | MenuAction_DisplayItem); + g_VoteNative.VoteResultCallback = Handler_NV_MapVoteFinished; + } + else + { + g_VoteMenu = new Menu(Handler_MapVoteMenu, MENU_ACTIONS_ALL); + g_VoteMenu.SetTitle("Vote Nextmap"); + g_VoteMenu.VoteResultCallback = Handler_MapVoteFinished; + } + + /* Call OnMapVoteStarted() Forward */ + Call_StartForward(g_MapVoteStartedForward); + Call_Finish(); + + /** + * TODO: Make a proper decision on when to clear the nominations list. + * Currently it clears when used, and stays if an external list is provided. + * Is this the right thing to do? External lists will probably come from places + * like sm_mapvote from the adminmenu in the future. + */ + + char map[PLATFORM_MAX_PATH]; + + /* No input given - User our internal nominations and maplist */ + if (inputlist == null) + { + int nominateCount = g_NominateList.Length; + int voteSize = g_ConVars[mapvote_include].IntValue; + + // New in 1.5.1 to fix missing extend vote + if (g_NativeVotes) + { + if (voteSize > NativeVotes_GetMaxItems()) + { + voteSize = NativeVotes_GetMaxItems(); + } + + if (g_ConVars[mapvote_extend].IntValue && g_Extends < g_ConVars[mapvote_extend].IntValue) + { + voteSize--; + } + } + + /* Smaller of the two - It should be impossible for nominations to exceed the size though (cvar changed mid-map?) */ + int nominationsToAdd = nominateCount >= voteSize ? voteSize : nominateCount; + + for (int i = 0; i < nominationsToAdd; i++) + { + char displayName[PLATFORM_MAX_PATH]; + g_NominateList.GetString(i, map, sizeof(map)); + GetMapDisplayName(map, displayName, sizeof(displayName)); + + if (g_NativeVotes) + { + g_VoteNative.AddItem(map, displayName); + } + else + { + g_VoteMenu.AddItem(map, displayName); + } + + RemoveStringFromArray(g_NextMapList, map); + + /* Notify Nominations that this map is now free */ + Call_StartForward(g_NominationsResetForward); + Call_PushString(map); + Call_PushCell(g_NominateOwners.Get(i)); + Call_Finish(); + } + + /* Clear out the rest of the nominations array */ + for (int i = nominationsToAdd; i < nominateCount; i++) + { + g_NominateList.GetString(i, map, sizeof(map)); + /* These maps shouldn't be excluded from the vote as they weren't really nominated at all */ + + /* Notify Nominations that this map is now free */ + Call_StartForward(g_NominationsResetForward); + Call_PushString(map); + Call_PushCell(g_NominateOwners.Get(i)); + Call_Finish(); + } + + /* There should currently be 'nominationsToAdd' unique maps in the vote */ + + int i = nominationsToAdd; + int count = 0; + int availableMaps = g_NextMapList.Length; + + while (i < voteSize) + { + if (count >= availableMaps) + { + //Run out of maps, this will have to do. + break; + } + + g_NextMapList.GetString(count, map, sizeof(map)); + count++; + + /* Insert the map and increment our count */ + char displayName[PLATFORM_MAX_PATH]; + GetMapDisplayName(map, displayName, sizeof(displayName)); + if (g_NativeVotes) + { + g_VoteNative.AddItem(map, displayName); + } + else + { + g_VoteMenu.AddItem(map, displayName); + } + i++; + } + + /* Wipe out our nominations list - Nominations have already been informed of this */ + g_NominateOwners.Clear(); + g_NominateList.Clear(); + } + else //We were given a list of maps to start the vote with + { + int size = inputlist.Length; + + for (int i = 0; i < size; i++) + { + inputlist.GetString(i, map, sizeof(map)); + + if (IsMapValid(map)) + { + char displayName[PLATFORM_MAX_PATH]; + GetMapDisplayName(map, displayName, sizeof(displayName)); + if (g_NativeVotes) + { + g_VoteNative.AddItem(map, displayName); + } + else + { + g_VoteMenu.AddItem(map, displayName); + } + } + } + } + + /* Do we add any special items? */ + if ((when == MapChange_Instant || when == MapChange_RoundEnd) && g_ConVars[mapvote_dontchange].BoolValue) + { + if (g_NativeVotes) + { + g_VoteNative.AddItem(VOTE_DONTCHANGE, "Don't Change"); + } + else + { + g_VoteMenu.AddItem(VOTE_DONTCHANGE, "Don't Change"); + } + } + else if (g_ConVars[mapvote_extend].BoolValue && g_Extends < g_ConVars[mapvote_extend].IntValue) + { + if (g_NativeVotes) + { + g_VoteNative.AddItem(VOTE_EXTEND, "Extend Map"); + } + else + { + g_VoteMenu.AddItem(VOTE_EXTEND, "Extend Map"); + } + } + + /* There are no maps we could vote for. Don't show anything. */ + if (g_NativeVotes && g_VoteNative.ItemCount == 0) + { + g_HasVoteStarted = false; + g_VoteNative.Close(); + return; + } + else if (!g_NativeVotes && g_VoteMenu.ItemCount == 0) + { + g_HasVoteStarted = false; + delete g_VoteMenu; + return; + } + + int voteDuration = g_ConVars[mapvote_voteduration].IntValue; + + if (g_NativeVotes) + { + g_VoteNative.DisplayVoteToAll(voteDuration); + } + else + { + g_VoteMenu.ExitButton = false; + g_VoteMenu.DisplayVoteToAll(voteDuration); + } + + LogAction(-1, -1, "Voting for next map has started."); + CPrintToChatAll("[{lightgreen}MapChooser\x01] %t", "Nextmap Voting Started"); +} + +public void Handler_NV_VoteFinishedGeneric(NativeVote menu, int num_votes, int num_clients, const int[] client_indexes, const int[] client_votes, int num_items, const int[] item_indexes, const int[] item_votes) +{ + int[][] client_info = new int[num_clients][2]; + int[][] item_info = new int[num_items][2]; + + NativeVotes_FixResults(num_clients, client_indexes, client_votes, num_items, item_indexes, item_votes, client_info, item_info); + + char map[PLATFORM_MAX_PATH]; + char displayName[PLATFORM_MAX_PATH]; + menu.GetItem(item_indexes[0], map, sizeof(map), displayName, sizeof(displayName)); + + Handler_VoteFinishedGenericShared(map, displayName, num_votes, num_clients, client_info, num_items, item_info, true); +} + +public void Handler_VoteFinishedGeneric(Menu menu, int num_votes, int num_clients, const int[][] client_info, int num_items, const int[][] item_info) +{ + char map[PLATFORM_MAX_PATH]; + char displayName[PLATFORM_MAX_PATH]; + menu.GetItem(item_info[0][VOTEINFO_ITEM_INDEX], map, sizeof(map), _, displayName, sizeof(displayName)); + + Handler_VoteFinishedGenericShared(map, displayName, num_votes, num_clients, client_info, num_items, item_info, false); +} + +public void Handler_VoteFinishedGenericShared(const char[] map, const char[] displayName, int num_votes, int num_clients, const int[][] client_info, int num_items, const int[][] item_info, bool isNativeVotes) +{ + if (strcmp(map, VOTE_EXTEND, false) == 0) + { + g_Extends++; + + int time; + if (GetMapTimeLimit(time)) + { + if (time > 0) + { + ExtendMapTimeLimit(g_ConVars[extendmap_timestep].IntValue * 60); + } + } + + if (g_ConVars[mp_winlimit]) + { + int winlimit = g_ConVars[mp_winlimit].IntValue; + if (winlimit) + { + g_ConVars[mp_winlimit].IntValue = winlimit + g_ConVars[extendmap_roundstep].IntValue; + } + } + + if (g_ConVars[mp_maxrounds]) + { + int maxrounds = g_ConVars[mp_maxrounds].IntValue; + if (maxrounds) + { + g_ConVars[mp_maxrounds].IntValue = maxrounds + g_ConVars[extendmap_roundstep].IntValue; + } + } + + if (g_ConVars[mp_fraglimit]) + { + int fraglimit = g_ConVars[mp_fraglimit].IntValue; + if (fraglimit) + { + g_ConVars[mp_fraglimit].IntValue = fraglimit + g_ConVars[extendmap_fragstep].IntValue; + } + } + + CPrintToChatAll("[{lightgreen}MapChooser\x01] %t", "Current Map Extended", RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes); + LogAction(-1, -1, "Voting for next map has finished. The current map has been extended."); + + if (isNativeVotes) + { + g_VoteNative.DisplayPassEx(NativeVotesPass_Extend); + } + + // We extended, so we'll have to vote again. + g_HasVoteStarted = false; + CreateNextVote(); + SetupTimeleftTimer(); + + } + else if (strcmp(map, VOTE_DONTCHANGE, false) == 0) + { + CPrintToChatAll("[{lightgreen}MapChooser\x01] %t", "Current Map Stays", RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes); + LogAction(-1, -1, "Voting for next map has finished. 'No Change' was the winner"); + + if (isNativeVotes) + { + g_VoteNative.DisplayPassEx(NativeVotesPass_Extend); + } + + g_HasVoteStarted = false; + CreateNextVote(); + SetupTimeleftTimer(); + } + else + { + if (g_ChangeTime == MapChange_MapEnd) + { + SetNextMap(map); + } + else if (g_ChangeTime == MapChange_Instant) + { + DataPack data; + CreateDataTimer(2.0, Timer_ChangeMap, data); + data.WriteString(map); + g_ChangeMapInProgress = false; + } + else // MapChange_RoundEnd + { + SetNextMap(map); + g_ChangeMapAtRoundEnd = true; + } + + g_HasVoteStarted = false; + g_MapVoteCompleted = true; + + if (isNativeVotes) + { + g_VoteNative.DisplayPass(displayName); + } + + Format(displayName, sizeof(displayName), "\x05%s\x01", displayName); + CPrintToChatAll("[{lightgreen}MapChooser\x01] %t", "Nextmap Voting Finished", displayName, RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes); + LogAction(-1, -1, "Voting for next map has finished. Nextmap: %s.", map); + } +} + +public void Handler_NV_MapVoteFinished(NativeVote menu, int num_votes, int num_clients, const int[] client_indexes, const int[] client_votes, int num_items, const int[] item_indexes, const int[] item_votes) +{ + if (g_ConVars[mapvote_runoff].BoolValue && num_items > 1) + { + float winningvotes = float(item_votes[0]); + float required = num_votes * (g_ConVars[mapvote_runoffpercent].FloatValue / 100.0); + + if (winningvotes < required) + { + //Added in 1.5.1 + menu.DisplayFail(NativeVotesFail_NotEnoughVotes); + + /* Insufficient Winning margin - Lets do a runoff */ + + char map1[PLATFORM_MAX_PATH]; + char map2[PLATFORM_MAX_PATH]; + char info1[PLATFORM_MAX_PATH]; + char info2[PLATFORM_MAX_PATH]; + + DataPack data; + + menu.GetItem(item_indexes[0], map1, sizeof(map1), info1, sizeof(info1)); + menu.GetItem(item_indexes[1], map2, sizeof(map2), info2, sizeof(info2)); + + CreateDataTimer(3.0, Timer_NV_Runoff, data, TIMER_FLAG_NO_MAPCHANGE); + + data.WriteString(map1); + data.WriteString(info1); + data.WriteString(map2); + data.WriteString(info2); + + data.Reset(); + + /* Notify */ + float map1percent = float(item_votes[0])/ float(num_votes) * 100; + float map2percent = float(item_votes[1])/ float(num_votes) * 100; + + + CPrintToChatAll("[{lightgreen}MapChooser\x01] %t", "Starting Runoff", g_ConVars[mapvote_runoffpercent].FloatValue, info1, map1percent, info2, map2percent); + LogMessage("Voting for next map was indecisive, beginning runoff vote"); + + return; + } + } + + Handler_NV_VoteFinishedGeneric(menu, num_votes, num_clients, client_indexes, client_votes, num_items, item_indexes, item_votes); +} + +// New in 1.5.1, used to fix runoff not working properly +public Action Timer_NV_Runoff(Handle timer, DataPack data) +{ + char map[PLATFORM_MAX_PATH], info[PLATFORM_MAX_PATH]; + + g_VoteNative = new NativeVote(Handler_NV_MapVoteMenu, NativeVotesType_NextLevelMult, NATIVEVOTES_ACTIONS_DEFAULT | MenuAction_DisplayItem); + g_VoteNative.VoteResultCallback = Handler_NV_VoteFinishedGeneric; + + data.ReadString(map, sizeof(map)); + data.ReadString(info, sizeof(info)); + g_VoteNative.AddItem(map, info); + + data.ReadString(map, sizeof(map)); + data.ReadString(info, sizeof(info)); + g_VoteNative.AddItem(map, info); + + int voteDuration = g_ConVars[mapvote_voteduration].IntValue; + g_VoteNative.DisplayVoteToAll(voteDuration); + + return Plugin_Continue; +} + +public void Handler_MapVoteFinished(Menu menu, int num_votes, int num_clients, const int[][] client_info, int num_items, const int[][] item_info) +{ + if (g_ConVars[mapvote_runoff].BoolValue && num_items > 1) + { + float winningvotes = float(item_info[0][VOTEINFO_ITEM_VOTES]); + float required = num_votes * (g_ConVars[mapvote_runoffpercent].FloatValue / 100.0); + + if (winningvotes < required) + { + /* Insufficient Winning margin - Lets do a runoff */ + g_VoteMenu = new Menu(Handler_MapVoteMenu, MENU_ACTIONS_ALL); + g_VoteMenu.SetTitle("Runoff Vote Nextmap"); + g_VoteMenu.VoteResultCallback = Handler_VoteFinishedGeneric; + + char map[PLATFORM_MAX_PATH], info1[PLATFORM_MAX_PATH], info2[PLATFORM_MAX_PATH]; + + menu.GetItem(item_info[0][VOTEINFO_ITEM_INDEX], map, sizeof(map), _, info1, sizeof(info1)); + g_VoteMenu.AddItem(map, info1); + menu.GetItem(item_info[1][VOTEINFO_ITEM_INDEX], map, sizeof(map), _, info2, sizeof(info2)); + g_VoteMenu.AddItem(map, info2); + + int voteDuration = g_ConVars[mapvote_voteduration].IntValue; + g_VoteMenu.ExitButton = false; + g_VoteMenu.DisplayVoteToAll(voteDuration); + + /* Notify */ + float map1percent = float(item_info[0][VOTEINFO_ITEM_VOTES])/ float(num_votes) * 100; + float map2percent = float(item_info[1][VOTEINFO_ITEM_VOTES])/ float(num_votes) * 100; + + CPrintToChatAll("[{lightgreen}MapChooser\x01] %t", "Starting Runoff", g_ConVars[mapvote_runoffpercent].FloatValue, info1, map1percent, info2, map2percent); + LogMessage("Voting for next map was indecisive, beginning runoff vote"); + + return; + } + } + + Handler_VoteFinishedGeneric(menu, num_votes, num_clients, client_info, num_items, item_info); +} + +public int Handler_MapVoteMenu(Menu menu, MenuAction action, int param1, int param2) +{ + switch (action) + { + case MenuAction_End: + { + g_VoteMenu = null; + delete menu; + } + + case MenuAction_Display: + { + char buffer[255]; + Format(buffer, sizeof(buffer), "%t", "Vote Nextmap", param1); + + Panel panel = view_as(param2); + panel.SetTitle(buffer); + } + + case MenuAction_DisplayItem: + { + if (menu.ItemCount - 1 == param2) + { + char map[PLATFORM_MAX_PATH], buffer[255]; + menu.GetItem(param2, map, sizeof(map)); + if (strcmp(map, VOTE_EXTEND, false) == 0) + { + Format(buffer, sizeof(buffer), "%t", "Extend Map", param1); + return RedrawMenuItem(buffer); + } + else if (strcmp(map, VOTE_DONTCHANGE, false) == 0) + { + Format(buffer, sizeof(buffer), "%t", "Dont Change", param1); + return RedrawMenuItem(buffer); + } + } + } + + case MenuAction_VoteCancel: + { + // If we receive 0 votes, pick at random. + if (param1 == VoteCancel_NoVotes && g_ConVars[mapvote_novote].BoolValue) + { + int count = menu.ItemCount; + char map[PLATFORM_MAX_PATH]; + menu.GetItem(0, map, sizeof(map)); + + // Make sure the first map in the menu isn't one of the special items. + // This would mean there are no real maps in the menu, because the special items are added after all maps. Don't do anything if that's the case. + if (strcmp(map, VOTE_EXTEND, false) != 0 && strcmp(map, VOTE_DONTCHANGE, false) != 0) + { + // Get a random map from the list. + int item = GetRandomInt(0, count - 1); + menu.GetItem(item, map, sizeof(map)); + + // Make sure it's not one of the special items. + while (strcmp(map, VOTE_EXTEND, false) == 0 || strcmp(map, VOTE_DONTCHANGE, false) == 0) + { + item = GetRandomInt(0, count - 1); + menu.GetItem(item, map, sizeof(map)); + } + + SetNextMap(map); + g_MapVoteCompleted = true; + } + } + + g_HasVoteStarted = false; + } + } + + return 0; +} + +public int Handler_NV_MapVoteMenu(NativeVote menu, MenuAction action, int param1, int param2) +{ + switch (action) + { + case MenuAction_End: + { + g_VoteMenu = null; + menu.Close(); + } + + case MenuAction_DisplayItem: + { + if (menu.ItemCount - 1 == param2) + { + char map[PLATFORM_MAX_PATH], buffer[255]; + menu.GetItem(param2, map, sizeof(map)); + if (strcmp(map, VOTE_EXTEND, false) == 0) + { + Format(buffer, sizeof(buffer), "%t", "Extend Map", param1); + return view_as(NativeVotes_RedrawVoteItem(buffer)); + } + else if (strcmp(map, VOTE_DONTCHANGE, false) == 0) + { + Format(buffer, sizeof(buffer), "%t", "Dont Change", param1); + return view_as(NativeVotes_RedrawVoteItem(buffer)); + } + } + } + + case MenuAction_VoteCancel: + { + // If we receive 0 votes, pick at random. + if (param1 == VoteCancel_NoVotes && g_ConVars[mapvote_novote].BoolValue) + { + int count = menu.ItemCount; + char map[PLATFORM_MAX_PATH], displayName[PLATFORM_MAX_PATH]; + menu.GetItem(0, map, sizeof(map)); + + // Make sure the first map in the menu isn't one of the special items. + // This would mean there are no real maps in the menu, because the special items are added after all maps. Don't do anything if that's the case. + if (strcmp(map, VOTE_EXTEND, false) != 0 && strcmp(map, VOTE_DONTCHANGE, false) != 0) + { + // Get a random map from the list. + int item = GetRandomInt(0, count - 1); + menu.GetItem(item, map, sizeof(map), displayName, sizeof(displayName)); + + // Make sure it's not one of the special items. + while (strcmp(map, VOTE_EXTEND, false) == 0 || strcmp(map, VOTE_DONTCHANGE, false) == 0) + { + item = GetRandomInt(0, count - 1); + menu.GetItem(item, map, sizeof(map), displayName, sizeof(displayName)); + } + + SetNextMap(map); + g_MapVoteCompleted = true; + menu.DisplayPass(displayName); + } + } + else if (param1 == VoteCancel_NoVotes) + { + // We didn't have enough votes. Display the note enough votes fail message. + menu.DisplayFail(NativeVotesFail_NotEnoughVotes); + } + else + { + // We were actually cancelled. Display the generic fail message + menu.DisplayFail(NativeVotesFail_Generic); + } + + g_HasVoteStarted = false; + } + } + + return 0; +} + +public Action Timer_ChangeMap(Handle hTimer, DataPack dp) +{ + g_ChangeMapInProgress = false; + + char map[PLATFORM_MAX_PATH]; + + if (dp == null) + { + if (!GetNextMap(map, sizeof(map))) + { + //No passed map and no set nextmap. fail! + return Plugin_Stop; + } + } + else + { + dp.Reset(); + dp.ReadString(map, sizeof(map)); + } + + ForceChangeLevel(map, "Map Vote"); + + return Plugin_Stop; +} + +bool RemoveStringFromArray(ArrayList array, char[] str) +{ + int index = array.FindString(str); + if (index != -1) + { + array.Erase(index); + return true; + } + + return false; +} + +void CreateNextVote() +{ + g_NextMapList.Clear(); + + char map[PLATFORM_MAX_PATH]; + // tempMaps is a resolved map list + ArrayList tempMaps = new ArrayList(ByteCountToCells(PLATFORM_MAX_PATH)); + + for (int i = 0; i < g_MapList.Length; i++) + { + g_MapList.GetString(i, map, sizeof(map)); + if (FindMap(map, map, sizeof(map)) != FindMap_NotFound) + { + tempMaps.PushString(map); + } + } + + //GetCurrentMap always returns a resolved map + GetCurrentMap(map, sizeof(map)); + RemoveStringFromArray(tempMaps, map); + + if (g_ConVars[mapvote_exclude].IntValue && tempMaps.Length > g_ConVars[mapvote_exclude].IntValue) + { + for (int i = 0; i < g_OldMapList.Length; i++) + { + g_OldMapList.GetString(i, map, sizeof(map)); + RemoveStringFromArray(tempMaps, map); + } + } + + int limit = (g_ConVars[mapvote_include].IntValue < tempMaps.Length ? g_ConVars[mapvote_include].IntValue : tempMaps.Length); + for (int i = 0; i < limit; i++) + { + int b = GetRandomInt(0, tempMaps.Length - 1); + tempMaps.GetString(b, map, sizeof(map)); + g_NextMapList.PushString(map); + tempMaps.Erase(b); + } + + delete tempMaps; +} + +bool CanVoteStart() +{ + return !(g_WaitingForVote || g_HasVoteStarted); +} + +NominateResult InternalNominateMap(char[] map, bool force, int owner) +{ + if (!IsMapValid(map)) + { + return Nominate_InvalidMap; + } + + /* Map already in the vote */ + if (g_NominateList.FindString(map) != -1) + { + return Nominate_AlreadyInVote; + } + + int index; + + /* Look to replace an existing nomination by this client - Nominations made with owner = 0 aren't replaced */ + if (owner && ((index = g_NominateOwners.FindValue(owner)) != -1)) + { + char oldmap[PLATFORM_MAX_PATH]; + g_NominateList.GetString(index, oldmap, sizeof(oldmap)); + Call_StartForward(g_NominationsResetForward); + Call_PushString(oldmap); + Call_PushCell(owner); + Call_Finish(); + + g_NominateList.SetString(index, map); + return Nominate_Replaced; + } + + /* Too many nominated maps. */ + int maxIncludes = 0; + if (g_NativeVotes) + { + maxIncludes = NativeVotes_GetMaxItems(); + + if (g_ConVars[mapvote_include].IntValue < maxIncludes) + { + maxIncludes = g_ConVars[mapvote_include].IntValue; + } + + if (g_ConVars[mapvote_extend].BoolValue && g_Extends < g_ConVars[mapvote_extend].IntValue) + { + maxIncludes--; + } + } + else + { + maxIncludes = g_ConVars[mapvote_include].IntValue; + } + + if (g_NominateList.Length >= maxIncludes && !force) + { + return Nominate_VoteFull; + } + + g_NominateList.PushString(map); + g_NominateOwners.Push(owner); + + while (g_NominateList.Length > g_ConVars[mapvote_include].IntValue) + { + char oldmap[PLATFORM_MAX_PATH]; + g_NominateList.GetString(0, oldmap, sizeof(oldmap)); + Call_StartForward(g_NominationsResetForward); + Call_PushString(oldmap); + Call_PushCell(g_NominateOwners.Get(0)); + Call_Finish(); + + g_NominateList.Erase(0); + g_NominateOwners.Erase(0); + } + + return Nominate_Added; +} + +/* Add natives to allow nominate and initiate vote to be call */ + +/* native NominateResult NominateMap(const char[] map, bool force, int owner); */ +public int Native_NominateMap(Handle plugin, int numParams) +{ + int len; + GetNativeStringLength(1, len); + + if (len <= 0) + { + return false; + } + + char[] map = new char[len+1]; + GetNativeString(1, map, len+1); + + return view_as(InternalNominateMap(map, GetNativeCell(2), GetNativeCell(3))); +} + +bool InternalRemoveNominationByMap(char[] map) +{ + for (int i = 0; i < g_NominateList.Length; i++) + { + char oldmap[PLATFORM_MAX_PATH]; + g_NominateList.GetString(i, oldmap, sizeof(oldmap)); + + if(strcmp(map, oldmap, false) == 0) + { + Call_StartForward(g_NominationsResetForward); + Call_PushString(oldmap); + Call_PushCell(g_NominateOwners.Get(i)); + Call_Finish(); + + g_NominateList.Erase(i); + g_NominateOwners.Erase(i); + + return true; + } + } + + return false; +} + +/* native bool RemoveNominationByMap(const char[] map); */ +public int Native_RemoveNominationByMap(Handle plugin, int numParams) +{ + int len; + GetNativeStringLength(1, len); + + if (len <= 0) + { + return false; + } + + char[] map = new char[len+1]; + GetNativeString(1, map, len+1); + + return InternalRemoveNominationByMap(map); +} + +bool InternalRemoveNominationByOwner(int owner) +{ + int index; + + if (owner && ((index = g_NominateOwners.FindValue(owner)) != -1)) + { + char oldmap[PLATFORM_MAX_PATH]; + g_NominateList.GetString(index, oldmap, sizeof(oldmap)); + + Call_StartForward(g_NominationsResetForward); + Call_PushString(oldmap); + Call_PushCell(owner); + Call_Finish(); + + g_NominateList.Erase(index); + g_NominateOwners.Erase(index); + + return true; + } + + return false; +} + +/* native bool RemoveNominationByOwner(int owner); */ +public int Native_RemoveNominationByOwner(Handle plugin, int numParams) +{ + return InternalRemoveNominationByOwner(GetNativeCell(1)); +} + +/* native void InitiateMapChooserVote(MapChange when, ArrayList inputarray=null); */ +public int Native_InitiateVote(Handle plugin, int numParams) +{ + MapChange when = view_as(GetNativeCell(1)); + ArrayList inputarray = view_as(GetNativeCell(2)); + + LogAction(-1, -1, "Starting map vote because outside request"); + InitiateVote(when, inputarray); + + return 0; +} + +/* native bool CanMapChooserStartVote(); */ +public int Native_CanVoteStart(Handle plugin, int numParams) +{ + return CanVoteStart(); +} + +/* native bool HasEndOfMapVoteFinished(); */ +public int Native_CheckVoteDone(Handle plugin, int numParams) +{ + return g_MapVoteCompleted; +} + +/* native bool EndOfMapVoteEnabled(); */ +public int Native_EndOfMapVoteEnabled(Handle plugin, int numParams) +{ + return g_ConVars[mapvote_endvote].BoolValue; +} + +/* native void GetExcludeMapList(ArrayList array); */ +public int Native_GetExcludeMapList(Handle plugin, int numParams) +{ + ArrayList array = view_as(GetNativeCell(1)); + + if (array == null) + { + return 0; + } + int size = g_OldMapList.Length; + char map[PLATFORM_MAX_PATH]; + + for (int i = 0; i < size; i++) + { + g_OldMapList.GetString(i, map, sizeof(map)); + array.PushString(map); + } + + return 0; +} + +/* native void GetNominatedMapList(ArrayList maparray, ArrayList ownerarray = null); */ +public int Native_GetNominatedMapList(Handle plugin, int numParams) +{ + ArrayList maparray = view_as(GetNativeCell(1)); + ArrayList ownerarray = view_as(GetNativeCell(2)); + + if (maparray == null) + return 0; + + char map[PLATFORM_MAX_PATH]; + + for (int i = 0; i < g_NominateList.Length; i++) + { + g_NominateList.GetString(i, map, sizeof(map)); + maparray.PushString(map); + + // If the optional parameter for an owner list was passed, then we need to fill that out as well + if(ownerarray != null) + { + int index = g_NominateOwners.Get(i); + ownerarray.Push(index); + } + } + + return 0; +} + +void PopulateMapList() +{ + char mapcycleFile[PLATFORM_MAX_PATH]; + g_ConVars[mapcyclefile].GetString(mapcycleFile, sizeof(mapcycleFile)); + Format(mapcycleFile, sizeof(mapcycleFile), "cfg/%s", mapcycleFile); + + File file = OpenFile(mapcycleFile, "w"); + if (file == null) + { + return; + } + + char excludePattern[512]; + g_ConVars[mapcycle_exclude].GetString(excludePattern, sizeof(excludePattern)); + Regex regex = new Regex(excludePattern); + + DirectoryListing dir = OpenDirectory("maps"); + if (dir == null) + { + delete regex; + CloseHandle(file); + return; + } + + char mapName[PLATFORM_MAX_PATH]; + FileType type; + int len; + + file.WriteLine("// Generated with NativeVotes MapChooser."); + file.WriteLine("// https://github.com/Heapons/sourcemod-nativevotes-updated"); + + // FastDL + while (dir.GetNext(mapName, sizeof(mapName), type)) + { + if (type != FileType_File) + continue; + + len = strlen(mapName); + if (len <= 4 || !StrEqual(mapName[len-4], ".bsp", false)) + continue; + + mapName[len-4] = '\0'; + + if (regex.Match(mapName) >= 1) + continue; + + file.WriteLine("%s", mapName); + } + delete dir; + delete regex; + + // Workshop + char workshopCollection[64]; + g_ConVars[workshop_map_collection].GetString(workshopCollection, sizeof(workshopCollection)); + if (g_RestInPawn && workshopCollection[0] != '\0') + { + HTTPRequest req = new HTTPRequest("https://api.steampowered.com/ISteamRemoteStorage/GetCollectionDetails/v1/"); + req.AppendFormParam("collectioncount", "1"); + req.AppendFormParam("publishedfileids[0]", workshopCollection); + req.PostForm(HTTPResponse_GetCollectionDetails, file); + } + else + { + CloseHandle(file); + } +} + +void HTTPResponse_GetCollectionDetails(HTTPResponse response, File file) +{ + if (response.Status != HTTPStatus_OK || response.Data == null) + { + CloseHandle(file); + return; + } + + JSONObject root = view_as(response.Data); + if (root == null) + { + CloseHandle(file); + return; + } + + JSONObject responseObj = view_as(root.Get("response")); + if (responseObj == null) + { + delete root; + CloseHandle(file); + return; + } + + JSONArray collectionDetailsArray = view_as(responseObj.Get("collectiondetails")); + if (collectionDetailsArray == null || collectionDetailsArray.Length <= 0) + { + delete responseObj; + delete root; + CloseHandle(file); + return; + } + + JSONObject collectionDetails = view_as(collectionDetailsArray.Get(0)); + if (collectionDetails == null) + { + delete collectionDetailsArray; + delete responseObj; + delete root; + CloseHandle(file); + return; + } + + JSONArray children = view_as(collectionDetails.Get("children")); + if (children != null) + { + ConVar sig_etc_workshop_map_fix = FindConVar("sig_etc_workshop_map_fix"); + bool workshopMapFix = sig_etc_workshop_map_fix != null ? sig_etc_workshop_map_fix.BoolValue : false; + for (int i = 0; i < children.Length; i++) + { + JSONObject child = view_as(children.Get(i)); + if (child == null) + { + continue; + } + + if (child.GetInt("filetype") == 0) // Map + { + char publishedfileid[64]; + if (child.GetString("publishedfileid", publishedfileid, sizeof(publishedfileid))) + { + if (workshopMapFix) + { + ServerCommand("tf_workshop_map_sync %s", publishedfileid); + } + else + { + file.WriteLine("workshop/%s", publishedfileid); + } + } + } + delete child; + } + delete children; + } + delete collectionDetails; + delete collectionDetailsArray; + delete responseObj; + delete root; + CloseHandle(file); +} + +void CleanupWorkshopMaps() +{ + KeyValues kv = new KeyValues("GameInfo"); + int appid; + if (kv.ImportFromFile("gameinfo.txt") && kv.JumpToKey("FileSystem")) + { + appid = kv.GetNum("SteamAppId"); + } + delete kv; + + char workshopDir[PLATFORM_MAX_PATH]; + Format(workshopDir, sizeof(workshopDir), "../steamapps/workshop/content/%d", appid); + + DirectoryListing dir = OpenDirectory(workshopDir); + if (dir != null) + { + char ugcid[PLATFORM_MAX_PATH]; + FileType type; + char path[PLATFORM_MAX_PATH]; + + while (dir.GetNext(ugcid, sizeof(ugcid), type)) + { + if (type != FileType_Directory) + continue; + + Format(path, sizeof(path), "%s/%s", workshopDir, ugcid); + DirectoryListing ugcDir = OpenDirectory(path); + if (ugcDir != null) + { + char file[PLATFORM_MAX_PATH]; + FileType fileType; + char filePath[PLATFORM_MAX_PATH]; + while (ugcDir.GetNext(file, sizeof(file), fileType)) + { + if (fileType == FileType_File) + { + Format(filePath, sizeof(filePath), "%s/%s", path, file); + DeleteFile(filePath); + } + } + delete ugcDir; + } + RemoveDir(path); + } + delete dir; + } + RemoveDir(workshopDir); } \ No newline at end of file From 5a6dcc3a513b3396a667a95215ffabe9410169ef Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Wed, 28 Jan 2026 15:11:26 +0100 Subject: [PATCH 047/128] Remove TF2 extensions requirement --- addons/sourcemod/scripting/nativevotes_voterp.sp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_voterp.sp b/addons/sourcemod/scripting/nativevotes_voterp.sp index 15e8d1c..500bb33 100644 --- a/addons/sourcemod/scripting/nativevotes_voterp.sp +++ b/addons/sourcemod/scripting/nativevotes_voterp.sp @@ -1,6 +1,5 @@ #include #include -#include #include #pragma semicolon 1 @@ -11,7 +10,7 @@ public Plugin myinfo = name = "NativeVotes | Medieval Auto-RP", author = "Heapons", description = "Provides Medieval Auto-RP voting.", - version = "26w04a", + version = "26w05a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -30,9 +29,9 @@ public void OnPluginStart() { g_ConVars[tf_medieval] = FindConVar("tf_medieval"); g_ConVars[tf_medieval_autorp] = FindConVar("tf_medieval_autorp"); - if (GetEngineVersion() != Engine_TF2 || !g_ConVars[tf_medieval] || !g_ConVars[tf_medieval_autorp]) + if (FindSendPropInfo("CTFGameRulesProxy", "m_bPlayingMedieval") <= 0) { - SetFailState("This plugin is for Team Fortress 2 only."); + SetFailState("This game doesn't support Medieval Mode."); } g_ConVars[vote_duration] = CreateConVar("sm_voterp_voteduration", "20", "Specifies how long the rp vote should be available for.", _, true, 5.0); From ea35e2245a9e781af5560dde2f44ba5ce5e325d4 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Wed, 28 Jan 2026 15:12:10 +0100 Subject: [PATCH 048/128] Fix vote sounds not being correctly played in multi-choice votes https: //github.com/Heapons/sourcemod-nativevotes-updated/issues/2#issuecomment-3810616332 Co-Authored-By: Benoist <14257866+kenzzer@users.noreply.github.com> --- .../sourcemod/scripting/nativevotes/game.sp | 265 +++++++++--------- 1 file changed, 125 insertions(+), 140 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes/game.sp b/addons/sourcemod/scripting/nativevotes/game.sp index aa11f2e..3d59a63 100644 --- a/addons/sourcemod/scripting/nativevotes/game.sp +++ b/addons/sourcemod/scripting/nativevotes/game.sp @@ -365,12 +365,12 @@ static ConVar g_Cvar_HideDisabledIssues; /** * TODO(UPDATE): For now we only support one vote from NativeVotes at a time. * - * Ideally we peek and poke at the game's `s_nVoteIdx` and increment it accordingly, but we also + * Ideally we peek and poke at the game's `s_nVoteIdx` and decrement it accordingly, but we also * have to store an internal list of active votes. + * + * Thank you @Kenzzer‼ */ -static int s_nNativeVoteIdx = 0; - -bool isTF2SDKModHack = false; +static int s_nNativeVoteIdx = 0x7FFFFFFF; bool Game_IsGameSupported(char[] engineName="", int maxlength=0) { @@ -391,7 +391,6 @@ bool Game_IsGameSupported(char[] engineName="", int maxlength=0) if (g_EngineVersion == Engine_SDK2013 && FileExists("resource/tf.ttf")) { g_EngineVersion = Engine_TF2; - isTF2SDKModHack = true; } else { @@ -421,26 +420,26 @@ void Game_InitializeCvars() { case Engine_Left4Dead, Engine_Left4Dead2, Engine_CSGO: { - g_Cvar_Votes_Enabled = FindConVar("sv_allow_votes"); + g_Cvar_Votes_Enabled = FindConVar("sv_allow_votes"); } case Engine_TF2: { - g_Cvar_Votes_Enabled = FindConVar("sv_allow_votes"); - g_Cvar_VoteKick_Enabled = FindConVar("sv_vote_issue_kick_allowed"); - g_Cvar_MvM_VoteKick_Enabled = FindConVar("sv_vote_issue_kick_allowed_mvm"); - g_Cvar_VoteNextLevel_Enabled = FindConVar("sv_vote_issue_nextlevel_allowed"); - g_Cvar_VoteChangeLevel_Enabled = FindConVar("sv_vote_issue_changelevel_allowed"); + g_Cvar_Votes_Enabled = FindConVar("sv_allow_votes"); + g_Cvar_VoteKick_Enabled = FindConVar("sv_vote_issue_kick_allowed"); + g_Cvar_MvM_VoteKick_Enabled = FindConVar("sv_vote_issue_kick_allowed_mvm"); + g_Cvar_VoteNextLevel_Enabled = FindConVar("sv_vote_issue_nextlevel_allowed"); + g_Cvar_VoteChangeLevel_Enabled = FindConVar("sv_vote_issue_changelevel_allowed"); g_Cvar_MvM_VoteChangeLevel_Enabled = FindConVar("sv_vote_issue_changelevel_allowed_mvm"); - g_Cvar_VoteRestart_Enabled = FindConVar("sv_vote_issue_restart_game_allowed"); - g_Cvar_MvM_VoteRestart_Enabled = FindConVar("sv_vote_issue_restart_game_allowed_mvm"); - g_Cvar_VoteScramble_Enabled = FindConVar("sv_vote_issue_scramble_teams_allowed"); - g_Cvar_MvM_VoteChallenge_Enabled = FindConVar("sv_vote_issue_mvm_challenge_allowed"); - g_Cvar_VoteAutoBalance_Enabled = FindConVar("sv_vote_issue_autobalance_allowed"); - g_Cvar_VoteClassLimits_Enabled = FindConVar("sv_vote_issue_classlimits_allowed"); + g_Cvar_VoteRestart_Enabled = FindConVar("sv_vote_issue_restart_game_allowed"); + g_Cvar_MvM_VoteRestart_Enabled = FindConVar("sv_vote_issue_restart_game_allowed_mvm"); + g_Cvar_VoteScramble_Enabled = FindConVar("sv_vote_issue_scramble_teams_allowed"); + g_Cvar_MvM_VoteChallenge_Enabled = FindConVar("sv_vote_issue_mvm_challenge_allowed"); + g_Cvar_VoteAutoBalance_Enabled = FindConVar("sv_vote_issue_autobalance_allowed"); + g_Cvar_VoteClassLimits_Enabled = FindConVar("sv_vote_issue_classlimits_allowed"); g_Cvar_MvM_VoteClassLimits_Enabled = FindConVar("sv_vote_issue_classlimits_allowed_mvm"); - g_Cvar_VoteExtend_Enabled = FindConVar("sv_vote_issue_extendlevel_allowed"); + g_Cvar_VoteExtend_Enabled = FindConVar("sv_vote_issue_extendlevel_allowed"); - g_Cvar_ClassLimit = FindConVar("tf_classlimit"); + g_Cvar_ClassLimit = FindConVar("tf_classlimit"); g_Cvar_AutoBalance = FindConVar("mp_autoteambalance"); g_Cvar_HideDisabledIssues = FindConVar("sv_vote_ui_hide_disabled_issues"); @@ -852,16 +851,6 @@ void Game_ClientSelectedItem(NativeVote vote, int client, int item) { TF2_ClientSelectedItem(vote, client, item); } - /* - case Engine_Left4Dead: - { - L4D_ClientSelectedItem(vote, client, item); - } - case Engine_Left4Dead2: - { - L4D2_ClientSelectedItem(client, item); - } - */ } } @@ -884,26 +873,10 @@ void Game_DisplayVoteSetup(int client, ArrayList hVoteTypes) { switch (g_EngineVersion) { - /* - case Engine_Left4Dead: - { - L4D_DisplayVoteSetup(client, voteTypes); - } - case Engine_Left4Dead2: - { - L4D2_DisplayVoteSetup(client, voteTypes); - } - */ case Engine_TF2: { TF2_DisplayVoteSetup(client, hVoteTypes); } - /* - case Engine_CSGO: - { - CSGO_DisplayVoteSetup(client, hVoteTypes); - } - */ } } @@ -1236,7 +1209,6 @@ static NativeVotesPassType VoteTypeToVotePass(NativeVotesType voteType) { passType = NativeVotesPass_Extend; } - default: { passType = NativeVotesPass_Custom; @@ -1537,7 +1509,17 @@ static void GetEngineVersionName(EngineVersion version, char[] printName, int ma } case Engine_HL2DM: { - strcopy(printName, maxlength, "Half-Life 2: Deathmatch"); + char gameDir[32]; + GetGameFolderName(gameDir, sizeof(gameDir)); + if (StrEqual(gameDir, "hl2mp")) + { + strcopy(printName, maxlength, "Half-Life 2: Deathmatch"); + } + else + { + // https://github.com/alliedmodders/metamod-source/pull/210 + strcopy(printName, maxlength, "Source SDK 2025"); + } } case Engine_DODS: { @@ -1551,7 +1533,38 @@ static void GetEngineVersionName(EngineVersion version, char[] printName, int ma { strcopy(printName, maxlength, "Nuclear Dawn"); } - + case Engine_SDK2013: + { + strcopy(printName, maxlength , "Source SDK 2013"); + } + case Engine_Blade: + { + strcopy(printName, maxlength , "Blade Symphony"); + } + case Engine_Insurgency: + { + strcopy(printName, maxlength , "Insurgency (2013 Retail version)"); + } + case Engine_Contagion: + { + strcopy(printName, maxlength , "Contagion"); + } + case Engine_BlackMesa: + { + strcopy(printName, maxlength , "Black Mesa Multiplayer"); + } + case Engine_DOI: + { + strcopy(printName, maxlength , "Day of Infamy"); + } + case Engine_PVKII: + { + strcopy(printName, maxlength , "Pirates, Vikings, and Knights II"); + } + case Engine_MCV: + { + strcopy(printName, maxlength , "Military Conflict: Vietnam"); + } default: { strcopy(printName, maxlength, "Not listed"); @@ -1663,7 +1676,6 @@ static void L4DL4D2_VoteTypeToTranslation(NativeVotesType voteType, char[] trans { strcopy(translation, maxlength, L4D_VOTE_CHANGELEVEL_START); } - default: { strcopy(translation, maxlength, L4D_VOTE_CUSTOM); @@ -1836,9 +1848,13 @@ static bool L4D_CheckVoteType(NativeVotesType voteType) { switch(voteType) { - case NativeVotesType_Custom_YesNo, NativeVotesType_ChgCampaign, NativeVotesType_ChgDifficulty, - NativeVotesType_ReturnToLobby, NativeVotesType_Restart, NativeVotesType_Kick, - NativeVotesType_ChgLevel: + case NativeVotesType_Custom_YesNo, + NativeVotesType_ChgCampaign, + NativeVotesType_ChgDifficulty, + NativeVotesType_ReturnToLobby, + NativeVotesType_Restart, + NativeVotesType_Kick, + NativeVotesType_ChgLevel: { return true; } @@ -1851,9 +1867,13 @@ static bool L4D_CheckVotePassType(NativeVotesPassType passType) { switch(passType) { - case NativeVotesPass_Custom, NativeVotesPass_ChgCampaign, NativeVotesPass_ChgDifficulty, - NativeVotesPass_ReturnToLobby, NativeVotesPass_Restart, NativeVotesPass_Kick, - NativeVotesPass_ChgLevel: + case NativeVotesPass_Custom, + NativeVotesPass_ChgCampaign, + NativeVotesPass_ChgDifficulty, + NativeVotesPass_ReturnToLobby, + NativeVotesPass_Restart, + NativeVotesPass_Kick, + NativeVotesPass_ChgLevel: { return true; } @@ -1952,7 +1972,7 @@ static void L4D2_DisplayVote(NativeVote vote, int[] clients, int num_clients) static void L4D2_VotePass(const char[] translation, const char[] details, int team, int client=0) { BfWrite votePass; - if (client <= 0) + if (!client) { votePass = UserMessageToBfWrite(StartMessageAll("VotePass", USERMSG_RELIABLE)); } @@ -2032,22 +2052,9 @@ static int CSGO_ParseVote(const char[] option) return StringToInt(option[6]) - 1; } -// NATIVEVOTES_VOTE_INVALID means parse failed -static int TF2_SDKHACK_ParseVote(const char[] option) -{ - // currently sdk2013 tf mods match csgo voting but if that ever changes - // this func will have to be properly written - return CSGO_ParseVote(option); -} - // NATIVEVOTES_VOTE_INVALID means parse failed static int TF2_ParseVote(const char[] option) { - if (isTF2SDKModHack) - { - return TF2_SDKHACK_ParseVote(option); - } - // the update on 2022-06-22 changed the params passed to the `vote` command // previously it was a single string "optionN", where N was the option to be selected // now it's two arguments "X optionN", where X is the vote index being acted on @@ -2087,10 +2094,7 @@ static void TF2_ClientSelectedItem(NativeVote vote, int client, int item) Event castEvent = CreateEvent("vote_cast"); castEvent.SetInt("team", Data_GetTeam(vote)); - if (!isTF2SDKModHack) - { - castEvent.SetInt("voteidx", s_nNativeVoteIdx); // TODO(UPDATE): this was added in 2022-06-22 - figure out what the client voted for - } + castEvent.SetInt("voteidx", s_nNativeVoteIdx); castEvent.SetInt("entityid", client); castEvent.SetInt("vote_option", item); castEvent.Fire(); @@ -2106,14 +2110,11 @@ static void TF2CSGO_UpdateVoteCounts(ArrayList votes) { SetEntProp(g_VoteController, Prop_Send, "m_nVoteOptionCount", votes.Get(i), 4, i); - char szVoteOption[13]; - Format(szVoteOption, sizeof(szVoteOption), "vote_option%d", i+1); + char szVoteOption[13]; + Format(szVoteOption, sizeof(szVoteOption), "vote_option%d", i+1); changeEvent.SetInt(szVoteOption, votes.Get(i)); } - if (!isTF2SDKModHack) - { - changeEvent.SetInt("voteidx", s_nNativeVoteIdx); - } + changeEvent.SetInt("voteidx", s_nNativeVoteIdx); changeEvent.SetInt("potentialVotes", GetEntProp(g_VoteController, Prop_Send, "m_nPotentialVotes")); changeEvent.Fire(); } @@ -2170,7 +2171,7 @@ static void TF2CSGO_DisplayVote(NativeVote vote, int[] clients, int num_clients) } default: { - Data_GetDetails(vote, details, MAX_VOTE_DETAILS_LENGTH); + Data_GetDetails(vote, details, MAX_VOTE_DETAILS_LENGTH); } } @@ -2207,18 +2208,7 @@ static void TF2CSGO_DisplayVote(NativeVote vote, int[] clients, int num_clients) SetEntProp(g_VoteController, Prop_Send, "m_nVoteOptionCount", 0, _, i); } - if (!isTF2SDKModHack) - { - // TODO(UPDATE): M-M-M-MULTIVOTE - // we need unique vote indices; HUD elements for previous votes aren't cleaned up (?) - // the game implements this as `this->m_nVoteIdx = s_nVoteIdx++` - s_nNativeVoteIdx = GetEntProp(g_VoteController, Prop_Send, "m_nVoteIdx"); - -#if defined LOG - PrintToServer("Starting vote index: %d (controller: %d)", s_nNativeVoteIdx, GetEntProp(g_VoteController, Prop_Send, "m_nVoteIdx")); -#endif - SetEntProp(g_VoteController, Prop_Send, "m_nVoteIdx", s_nNativeVoteIdx + 1); // TODO(UPDATE) - } + SetEntProp(g_VoteController, Prop_Send, "m_nVoteIdx", --s_nNativeVoteIdx); } // According to Source SDK 2013, vote_options is only sent for a multiple choice vote. @@ -2242,10 +2232,7 @@ static void TF2CSGO_DisplayVote(NativeVote vote, int[] clients, int num_clients) optionsEvent.SetString(option, display); } optionsEvent.SetInt("count", itemCount); - if (!isTF2SDKModHack) - { - optionsEvent.SetInt("voteidx", s_nNativeVoteIdx); // TODO(UPDATE) - } + optionsEvent.SetInt("voteidx", s_nNativeVoteIdx); optionsEvent.Fire(); } @@ -2256,15 +2243,12 @@ static void TF2CSGO_DisplayVote(NativeVote vote, int[] clients, int num_clients) SetEntProp(g_VoteController, Prop_Send, "m_nPotentialVotes", num_clients); } - if (!isTF2SDKModHack) + int holder = vote.Initiator; + // required to allow the initiator to vote on their own issue + // https://github.com/ValveSoftware/Source-1-Games/issues/3934 + if (sv_vote_holder_may_vote_no && holder <= MaxClients && IsClientConnected(holder) && !IsFakeClient(holder)) { - int holder = vote.Initiator; - // required to allow the initiator to vote on their own issue - // ValveSoftware/Source-1-Games#3934 - if (sv_vote_holder_may_vote_no && holder <= MaxClients && IsClientConnected(holder) && !IsFakeClient(holder)) - { - sv_vote_holder_may_vote_no.ReplicateToClient(holder, "1"); - } + sv_vote_holder_may_vote_no.ReplicateToClient(holder, "1"); } MenuAction actions = Data_GetActions(vote); @@ -2289,7 +2273,7 @@ static void TF2CSGO_DisplayVote(NativeVote vote, int[] clients, int num_clients) Handle voteStart = StartMessageOne("VoteStart", clients[i], USERMSG_RELIABLE); - if(g_bUserBuf) + if (g_bUserBuf) { Protobuf protoStart = UserMessageToProtobuf(voteStart); protoStart.SetInt("team", team); @@ -2311,10 +2295,7 @@ static void TF2CSGO_DisplayVote(NativeVote vote, int[] clients, int num_clients) { BfWrite bfStart = UserMessageToBfWrite(voteStart); bfStart.WriteByte(team); - if (!isTF2SDKModHack) - { - bfStart.WriteNum(s_nNativeVoteIdx); - } + bfStart.WriteNum(s_nNativeVoteIdx); bfStart.WriteByte(Data_GetInitiator(vote)); bfStart.WriteString(translation); if (bCustom && changeTitle == Plugin_Changed) @@ -2326,7 +2307,7 @@ static void TF2CSGO_DisplayVote(NativeVote vote, int[] clients, int num_clients) bfStart.WriteString(details); } bfStart.WriteByte(bYesNo); - if (!isTF2SDKModHack && !IsFakeClient(clients[i])) + if (!IsFakeClient(clients[i])) { bfStart.WriteByte(Data_GetTarget(vote) - 1); } @@ -2334,7 +2315,7 @@ static void TF2CSGO_DisplayVote(NativeVote vote, int[] clients, int num_clients) EndMessage(); } - + g_curDisplayClient = 0; } @@ -2388,10 +2369,7 @@ static void TF2CSGO_SendOptionsToClient(NativeVote vote, int client) optionsEvent.SetString(option, display); } optionsEvent.SetInt("count", itemCount); - if (!isTF2SDKModHack) - { - optionsEvent.SetInt("voteidx", s_nNativeVoteIdx); // TODO(UPDATE) - } + optionsEvent.SetInt("voteidx", s_nNativeVoteIdx); optionsEvent.FireToClient(client); // FireToClient does not close the handle, so we call Cancel() to do that for us. optionsEvent.Cancel(); @@ -2432,10 +2410,7 @@ static void TF2_VotePass(const char[] translation, const char[] details, int tea } votePass.WriteByte(team); - if (!isTF2SDKModHack) - { - votePass.WriteNum(s_nNativeVoteIdx); - } + votePass.WriteNum(s_nNativeVoteIdx); votePass.WriteString(translation); votePass.WriteString(details); @@ -2457,10 +2432,7 @@ static void TF2_VoteFail(int[] clients, int numClients, int reason, int team) BfWrite voteFailed = UserMessageToBfWrite(StartMessage("VoteFailed", clients, numClients, USERMSG_RELIABLE)); voteFailed.WriteByte(team); - if (!isTF2SDKModHack) - { - voteFailed.WriteNum(s_nNativeVoteIdx); // TODO(UPDATE) - } + voteFailed.WriteNum(s_nNativeVoteIdx); voteFailed.WriteByte(reason); EndMessage(); @@ -2500,8 +2472,7 @@ stock static void CSGO_DisplayVoteSetup(int client, ArrayList hVoteTypes) hVoteTypes.GetArray(i, voteData.CallVoteList_VoteType); Game_OverrideTypeToVoteString(voteData.CallVoteList_VoteType, voteIssue, sizeof(voteIssue)); - - + voteSetup.AddString("potential_issues", voteIssue); } @@ -2527,7 +2498,7 @@ static void TF2_DisplayVoteSetup(int client, ArrayList hVoteTypes) char translation[128]; Game_OverrideTypeToTranslationString(voteData.CallVoteList_VoteType, translation, sizeof(translation)); - + voteSetup.WriteString(voteIssue); voteSetup.WriteString(translation); voteSetup.WriteByte(voteData.CallVoteList_VoteEnabled); @@ -2542,10 +2513,7 @@ static void TF2CSGO_ResetVote() { if (g_EngineVersion == Engine_TF2) { - if (!isTF2SDKModHack) - { - SetEntProp(g_VoteController, Prop_Send, "m_nVoteIdx", -1); - } + SetEntProp(g_VoteController, Prop_Send, "m_nVoteIdx", -1); SetEntProp(g_VoteController, Prop_Send, "m_iActiveIssueIndex", INVALID_ISSUE); } SetEntProp(g_VoteController, Prop_Send, "m_iOnlyTeamToVote", g_EngineVersion == Engine_TF2 ? NATIVEVOTES_TF2_ALL_TEAMS : NATIVEVOTES_ALL_TEAMS); @@ -2597,11 +2565,20 @@ static bool TF2_CheckVotePassType(NativeVotesPassType passType) { switch(passType) { - case NativeVotesPass_Custom, NativeVotesPass_Restart, NativeVotesPass_ChgLevel, - NativeVotesPass_Kick, NativeVotesPass_NextLevel, NativeVotesPass_Extend, - NativeVotesPass_Scramble, NativeVotesPass_ChgMission, NativeVotesPass_StartRound, NativeVotesPass_Eternaween, - NativeVotesPass_AutoBalanceOn, NativeVotesPass_AutoBalanceOff, - NativeVotesPass_ClassLimitsOn, NativeVotesPass_ClassLimitsOff: + case NativeVotesPass_Custom, + NativeVotesPass_Restart, + NativeVotesPass_ChgLevel, + NativeVotesPass_Kick, + NativeVotesPass_NextLevel, + NativeVotesPass_Extend, + NativeVotesPass_Scramble, + NativeVotesPass_ChgMission, + NativeVotesPass_StartRound, + NativeVotesPass_Eternaween, + NativeVotesPass_AutoBalanceOn, + NativeVotesPass_AutoBalanceOff, + NativeVotesPass_ClassLimitsOn, + NativeVotesPass_ClassLimitsOff: { return true; } @@ -2773,7 +2750,6 @@ static void TF2_AddDefaultVotes(ArrayList hVoteTypes, bool bHideDisabledVotes) return; } - // Not every TF2 mod supports MVM if (FindSendPropInfo("CTFGameRulesProxy", "m_bPlayingMannVsMachine") > 0 && GameRules_GetProp("m_bPlayingMannVsMachine")) { // Default MvM vote types @@ -2844,10 +2820,19 @@ static bool CSGO_CheckVoteType(NativeVotesType voteType) { switch(voteType) { - case NativeVotesType_Custom_YesNo, NativeVotesType_Restart, - NativeVotesType_Kick, NativeVotesType_KickIdle, NativeVotesType_KickScamming, NativeVotesType_KickCheating, - NativeVotesType_ChgLevel, NativeVotesType_NextLevel, NativeVotesType_ScrambleNow, NativeVotesType_SwapTeams, - NativeVotesType_Surrender, NativeVotesType_Rematch, NativeVotesType_Continue: + case NativeVotesType_Custom_YesNo, + NativeVotesType_Restart, + NativeVotesType_Kick, + NativeVotesType_KickIdle, + NativeVotesType_KickScamming, + NativeVotesType_KickCheating, + NativeVotesType_ChgLevel, + NativeVotesType_NextLevel, + NativeVotesType_ScrambleNow, + NativeVotesType_SwapTeams, + NativeVotesType_Surrender, + NativeVotesType_Rematch, + NativeVotesType_Continue: { return true; } From 0626ad90cdc10f2258bce02593e233dee6132a3d Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Wed, 28 Jan 2026 15:23:39 +0100 Subject: [PATCH 049/128] Update README.md Co-Authored-By: Benoist <14257866+kenzzer@users.noreply.github.com> --- README.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2d5327b..c15426b 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ This fork aims to expand upon [[ **Team Fortress 2**](https://store.steampowered.com/app/440) (or its mods) only| \ No newline at end of file +|`sm_mapcycle_auto`|`0`|Specifies whether to automatically populate the maps list.| +|`sm_mapcycle_exclude`|`.*itemtest.*\|background01\|^tr.*$`|Specifies which maps shouldn't be automatically added (regex pattern).| +|`sm_workshop_map_collection`|""|Specifies the workshop collection to fetch the maps from.
[ **Team Fortress 2**](https://store.steampowered.com/app/440) (or its mods) only| +|`sm_workshop_map_cleanup`|`0`|Specifies whether to automatically cleanup workshop maps on map change
[ **Team Fortress 2**](https://store.steampowered.com/app/440) (or its mods) only| + +> [!WARNING] +> There's currently a bug where leaving `sm_mapcycle_auto` enabled at all times will make all plugins unable to find [`mapcyclefile`](https://developer.valvesoftware.com/wiki/Mapcycle.txt). But if you're still going to leave it on for whatever reason, [`sm_reload_nominations`](https://github.com/Heapons/sourcemod-nativevotes-updated/blob/master/addons/sourcemod/scripting/nativevotes_nominations.sp#L247-L251) works as a temporary fix. \ No newline at end of file From 32b6af3d9b8ee9f3f367f503518bd12d564aabcc Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Wed, 28 Jan 2026 15:32:28 +0100 Subject: [PATCH 050/128] Fix compile errors --- addons/sourcemod/scripting/nativevotes_mapchooser.sp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_mapchooser.sp b/addons/sourcemod/scripting/nativevotes_mapchooser.sp index a417dae..678bf98 100644 --- a/addons/sourcemod/scripting/nativevotes_mapchooser.sp +++ b/addons/sourcemod/scripting/nativevotes_mapchooser.sp @@ -991,8 +991,9 @@ public void Handler_VoteFinishedGenericShared(const char[] map, const char[] dis g_VoteNative.DisplayPass(displayName); } - Format(displayName, sizeof(displayName), "\x05%s\x01", displayName); - CPrintToChatAll("[{lightgreen}MapChooser\x01] %t", "Nextmap Voting Finished", displayName, RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes); + char formattedName[PLATFORM_MAX_PATH]; + Format(formattedName, sizeof(formattedName), "\x05%s\x01", displayName); + CPrintToChatAll("[{lightgreen}MapChooser\x01] %t", "Nextmap Voting Finished", formattedName, RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes); LogAction(-1, -1, "Voting for next map has finished. Nextmap: %s.", map); } } From 00cfa2ae27c4d0566485577317ada6f798021a4e Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Wed, 28 Jan 2026 15:37:21 +0100 Subject: [PATCH 051/128] Update workflow --- .github/workflows/nativevotes.yml | 12 + .gitignore | 3 +- .../scripting/include/multicolors.inc | 460 -------- .../scripting/include/multicolors/colors.inc | 897 ---------------- .../include/multicolors/morecolors.inc | 992 ------------------ 5 files changed, 14 insertions(+), 2350 deletions(-) delete mode 100644 addons/sourcemod/scripting/include/multicolors.inc delete mode 100644 addons/sourcemod/scripting/include/multicolors/colors.inc delete mode 100644 addons/sourcemod/scripting/include/multicolors/morecolors.inc diff --git a/.github/workflows/nativevotes.yml b/.github/workflows/nativevotes.yml index 133b678..f266dbd 100644 --- a/.github/workflows/nativevotes.yml +++ b/.github/workflows/nativevotes.yml @@ -29,6 +29,18 @@ jobs: with: submodules: true + - name: Download dependencies + shell: bash + run: | + mkdir -p addons/sourcemod/scripting/include/ripext + curl -o addons/sourcemod/scripting/include/ripext.inc https://github.com/ErikMinekus/sm-ripext/raw/refs/heads/main/pawn/scripting/include/ripext.inc + curl -o addons/sourcemod/scripting/include/ripext/http.inc https://github.com/ErikMinekus/sm-ripext/raw/refs/heads/main/pawn/scripting/include/ripext/http.inc + curl -o addons/sourcemod/scripting/include/ripext/json.inc https://github.com/ErikMinekus/sm-ripext/raw/refs/heads/main/pawn/scripting/include/ripext/json.inc + mkdir -p addons/sourcemod/scripting/include/multicolors + curl -o addons/sourcemod/scripting/include/multicolors.inc https://raw.githubusercontent.com/Bara/Multi-Colors/refs/heads/master/addons/sourcemod/scripting/include/multicolors.inc + curl -o addons/sourcemod/scripting/include/multicolors/colors.inc https://raw.githubusercontent.com/Bara/Multi-Colors/refs/heads/master/addons/sourcemod/scripting/include/multicolors/colors.inc + curl -o addons/sourcemod/scripting/include/multicolors/morecolors.inc https://raw.githubusercontent.com/Bara/Multi-Colors/refs/heads/master/addons/sourcemod/scripting/include/multicolors/morecolors.inc + - name: Setup SourcePawn 1.13 Compiler uses: rumblefrog/setup-sp@master with: diff --git a/.gitignore b/.gitignore index 8cf9970..452dfdb 100644 --- a/.gitignore +++ b/.gitignore @@ -79,4 +79,5 @@ version.inc virtual_address.inc steam* -ripext* \ No newline at end of file +ripext* +multicolors* \ No newline at end of file diff --git a/addons/sourcemod/scripting/include/multicolors.inc b/addons/sourcemod/scripting/include/multicolors.inc deleted file mode 100644 index e6612a7..0000000 --- a/addons/sourcemod/scripting/include/multicolors.inc +++ /dev/null @@ -1,460 +0,0 @@ -#if defined _multicolors_included - #endinput -#endif -#define _multicolors_included - -#define MuCo_VERSION "2.1.2" - -#include -#include - -/* -* -* Credits: -* - Popoklopsi -* - Powerlord -* - exvel -* - Dr. McKay -* -* Based on stamm-colors -* - https://github.com/popoklopsi/Stamm/blob/master/include/stamm/stamm-colors.inc -* -*/ - - - -#define PREFIX_MAX_LENGTH 64 -#define PREFIX_SEPARATOR "{default} " - -/* Global var to check whether colors are fixed or not */ -static bool g_bCFixColors; - -static char g_sCPrefix[PREFIX_MAX_LENGTH]; - -/** - * Add a chat prefix before all chat msg - * - * @param sPrefix Prefix - */ -stock void CSetPrefix(const char[] sPrefix, any ...) { - if (!sPrefix[0]) { - return; - } - - SetGlobalTransTarget(LANG_SERVER); - VFormat(g_sCPrefix, sizeof(g_sCPrefix) - strlen(PREFIX_SEPARATOR), sPrefix, 2); - - // Add ending space - Format(g_sCPrefix, sizeof(g_sCPrefix), "%s%s", g_sCPrefix, PREFIX_SEPARATOR); -} - -/** - * Add a chat prefix before all chat msg - * - * @param sPrefix Prefix - */ -stock void CClearPrefix() { - g_sCPrefix[0] = '\0'; -} - -/** - * Writes a message to a client with the correct stock for the game. - * - * @param client Client index. - * @param message Message (formatting rules). - * - * @error If the client is not connected an error will be thrown. - */ -stock void CPrintToChat(int client, const char[] message, any ...) { - char buffer[MAX_MESSAGE_LENGTH]; - SetGlobalTransTarget(client); - VFormat(buffer, sizeof(buffer), message, 3); - - if (!g_bCFixColors) { - CFixColors(); - } - - if (!IsSource2009()) { - C_PrintToChat(client, "%s%s", g_sCPrefix, buffer); - } - else { - MC_PrintToChat(client, "%s%s", g_sCPrefix, buffer); - } -} - -/** - * Prints a message to all clients in the chat area. - * Supports color tags. - * - * @param client Client index. - * @param message Message (formatting rules) - */ -stock void CPrintToChatAll(const char[] message, any ...) { - if (!g_bCFixColors) { - CFixColors(); - } - - char buffer[MAX_BUFFER_LENGTH]; - if (!IsSource2009()) { - for (int i = 1; i <= MaxClients; ++i) { - if (IsClientInGame(i) && !IsFakeClient(i) && !C_SkipList[i]) { - SetGlobalTransTarget(i); - VFormat(buffer, sizeof(buffer), message, 2); - - C_PrintToChat(i, "%s", buffer); - } - - C_SkipList[i] = false; - } - } - else { - MC_CheckTrie(); - - char buffer2[MAX_BUFFER_LENGTH]; - - for (int i = 1; i <= MaxClients; ++i) { - if (!IsClientInGame(i) || MC_SkipList[i]) { - MC_SkipList[i] = false; - continue; - } - - SetGlobalTransTarget(i); - Format(buffer, sizeof(buffer), "\x01%s", message); - VFormat(buffer2, sizeof(buffer2), buffer, 2); - - MC_ReplaceColorCodes(buffer2); - MC_SendMessage(i, buffer2); - } - } -} - -/** - * Writes a message to all of a client's observers. - * - * @param target Client index. - * @param message Message (formatting rules). - */ -stock void CPrintToChatObservers(int target, const char[] message, any ...) { - char buffer[MAX_MESSAGE_LENGTH]; - SetGlobalTransTarget(LANG_SERVER); - VFormat(buffer, sizeof(buffer), message, 3); - - if (!g_bCFixColors) { - CFixColors(); - } - - for (int client = 1; client <= MaxClients; client++) { - if (IsClientInGame(client) && !IsPlayerAlive(client) && !IsFakeClient(client)) { - int observee = GetEntPropEnt(client, Prop_Send, "m_hObserverTarget"); - int ObserverMode = GetEntProp(client, Prop_Send, "m_iObserverMode"); - - if (observee == target && (ObserverMode == 4 || ObserverMode == 5)) { - CPrintToChat(client, buffer); - } - } - } -} - -/** - * Writes a message to a client with the correct stock for the game. - * - * @param client Client index. - * @param author Author index. - * @param message Message (formatting rules). - * - * @error If the client is not connected an error will be thrown. - */ -stock void CPrintToChatEx(int client, int author, const char[] message, any ...) { - char buffer[MAX_MESSAGE_LENGTH]; - SetGlobalTransTarget(client); - VFormat(buffer, sizeof(buffer), message, 4); - - if (!g_bCFixColors) { - CFixColors(); - } - - if (!IsSource2009()) { - C_PrintToChatEx(client, author, "%s%s", g_sCPrefix, buffer); - } - else { - MC_PrintToChatEx(client, author, "%s%s", g_sCPrefix, buffer); - } -} - -/** - * Writes a message to all clients with the correct stock for the game. - * - * @param author Author index. - * @param message Message (formatting rules). - */ -stock void CPrintToChatAllEx(int author, const char[] message, any ...) { - char buffer[MAX_MESSAGE_LENGTH]; - SetGlobalTransTarget(LANG_SERVER); - VFormat(buffer, sizeof(buffer), message, 3); - - if (!g_bCFixColors) { - CFixColors(); - } - - if (!IsSource2009()) { - C_PrintToChatAllEx(author, "%s%s", g_sCPrefix, buffer); - } - else { - MC_PrintToChatAllEx(author, "%s%s", g_sCPrefix, buffer); - } -} - -/** - * Writes a message to all of a client's observers with the correct - * game stock. - * - * @param target Client index. - * @param message Message (formatting rules). - */ -stock void CPrintToChatObserversEx(int target, const char[] message, any ...) { - char buffer[MAX_MESSAGE_LENGTH]; - SetGlobalTransTarget(LANG_SERVER); - VFormat(buffer, sizeof(buffer), message, 3); - - if (!g_bCFixColors) { - CFixColors(); - } - - for (int client = 1; client <= MaxClients; client++) { - if (IsClientInGame(client) && !IsPlayerAlive(client) && !IsFakeClient(client)) { - int observee = GetEntPropEnt(client, Prop_Send, "m_hObserverTarget"); - int ObserverMode = GetEntProp(client, Prop_Send, "m_iObserverMode"); - - if (observee == target && (ObserverMode == 4 || ObserverMode == 5)) { - CPrintToChatEx(client, target, buffer); - } - } - } -} - -/** - * Replies to a command with colors - * - * @param client Client to reply to - * @param message Message (formatting rules) - */ -stock void CReplyToCommand(int client, const char[] message, any ...) { - char buffer[MAX_MESSAGE_LENGTH]; - SetGlobalTransTarget(client); - VFormat(buffer, sizeof(buffer), message, 3); - - if (!g_bCFixColors) { - CFixColors(); - } - - if (!IsSource2009()) { - C_ReplyToCommand(client, "%s%s", g_sCPrefix, buffer); - } - else { - MC_ReplyToCommand(client, "%s%s", g_sCPrefix, buffer); - } -} - -/** - * Replies to a command with colors - * - * @param client Client to reply to - * @param author Client to use for {teamcolor} - * @param message Message (formatting rules) - */ -stock void CReplyToCommandEx(int client, int author, const char[] message, any ...) { - char buffer[MAX_MESSAGE_LENGTH]; - SetGlobalTransTarget(client); - VFormat(buffer, sizeof(buffer), message, 4); - - if (!g_bCFixColors) { - CFixColors(); - } - - if (!IsSource2009()) { - C_ReplyToCommandEx(client, author, "%s%s", g_sCPrefix, buffer); - } - else { - MC_ReplyToCommandEx(client, author, "%s%s", g_sCPrefix, buffer); - } -} - -/** - * Remove all tags and print to server - * - * @param message Message (formatting rules) - */ -stock void CPrintToServer(const char[] message, any ...) { - char buffer[MAX_MESSAGE_LENGTH]; - char prefixBuffer[PREFIX_MAX_LENGTH]; - SetGlobalTransTarget(LANG_SERVER); - VFormat(buffer, sizeof(buffer), message, 2); - strcopy(prefixBuffer, sizeof(prefixBuffer), g_sCPrefix); - - if (!g_bCFixColors) { - CFixColors(); - } - - CRemoveTags(buffer, sizeof(buffer)); - CRemoveTags(prefixBuffer, sizeof(prefixBuffer)); - - PrintToServer("%s%s", prefixBuffer, buffer); -} - -/** - * Displays usage of an admin command to users depending on the - * setting of the sm_show_activity cvar. - * - * This version does not display a message to the originating client - * if used from chat triggers or menus. If manual replies are used - * for these cases, then this function will suffice. Otherwise, - * CShowActivity2() is slightly more useful. - * Supports color tags. - * - * @param client Client index doing the action, or 0 for server. - * @param format Formatting rules. - * @param ... Variable number of format parameters. - * - * @error - */ -stock void CShowActivity(int author, const char[] message, any ...) { - char buffer[MAX_MESSAGE_LENGTH]; - SetGlobalTransTarget(LANG_SERVER); - VFormat(buffer, sizeof(buffer), message, 3); - - if (!g_bCFixColors) { - CFixColors(); - } - - if (!IsSource2009()) { - C_ShowActivity(author, "%s", buffer); - } - else { - MC_ShowActivity(author, "%s", buffer); - } -} - -/** - * Same as C_ShowActivity(), except the tag parameter is used instead of "[SM] " (note that you must supply any spacing). - * Supports color tags. - * - * @param client Client index doing the action, or 0 for server. - * @param tags Tag to display with. - * @param format Formatting rules. - * @param ... Variable number of format parameters. - * - * @error - */ -stock void CShowActivityEx(int author, const char[] tag, const char[] message, any ...) { - char buffer[MAX_MESSAGE_LENGTH]; - SetGlobalTransTarget(LANG_SERVER); - VFormat(buffer, sizeof(buffer), message, 4); - - if (!g_bCFixColors) { - CFixColors(); - } - - if (!IsSource2009()) { - C_ShowActivityEx(author, tag, "%s", buffer); - } - else { - MC_ShowActivityEx(author, tag, "%s", buffer); - } -} - -/** - * Displays usage of an admin command to users depending on the setting of the sm_show_activity cvar. - * All users receive a message in their chat text, except for the originating client, - * who receives the message based on the current ReplySource. - * Supports color tags. - * - * @param client Client index doing the action, or 0 for server. - * @param tags Tag to prepend to the message. - * @param format Formatting rules. - * @param ... Variable number of format parameters. - * - * @error - */ - stock void CShowActivity2(int author, const char[] tag, const char[] message, any ...) { - char buffer[MAX_MESSAGE_LENGTH]; - SetGlobalTransTarget(LANG_SERVER); - VFormat(buffer, sizeof(buffer), message, 4); - - if (!g_bCFixColors) { - CFixColors(); - } - - if (!IsSource2009()) { - C_ShowActivity2(author, tag, "%s", buffer); - } - else { - MC_ShowActivity2(author, tag, "%s", buffer); - } -} - -/** - * Replaces color tags in a string with color codes - * - * @param message String. - * @param maxlength Maximum length of the string buffer. - */ -stock void CFormatColor(char[] message, int maxlength, int author = -1) { - if (!g_bCFixColors) { - CFixColors(); - } - - if (!IsSource2009()) { - if (author == 0) { - author = -1; - } - - C_Format(message, maxlength, author); - } - else { - if (author == -1) { - author = 0; - } - - MC_ReplaceColorCodes(message, author, false, maxlength); - } -} - -/** - * Removes color tags from a message - * - * @param message Message to remove tags from - * @param maxlen Maximum buffer length - */ -stock void CRemoveTags(char[] message, int maxlen) { - if (!IsSource2009()) { - C_RemoveTags(message, maxlen); - } - else { - MC_RemoveTags(message, maxlen); - } -} - -/** - * Fixes missing Lightgreen color. - */ -stock void CFixColors() { - g_bCFixColors = true; - - // Replace lightgreen if not exists - if (!C_ColorAllowed(Color_Lightgreen)) { - if (C_ColorAllowed(Color_Lime)) { - C_ReplaceColor(Color_Lightgreen, Color_Lime); - } - else if (C_ColorAllowed(Color_Olive)) { - C_ReplaceColor(Color_Lightgreen, Color_Olive); - } - } -} - -stock bool IsSource2009() { - return (GetEngineVersion() == Engine_CSS - || GetEngineVersion() == Engine_HL2DM - || GetEngineVersion() == Engine_DODS - || GetEngineVersion() == Engine_TF2 - || GetEngineVersion() == Engine_SDK2013); -} diff --git a/addons/sourcemod/scripting/include/multicolors/colors.inc b/addons/sourcemod/scripting/include/multicolors/colors.inc deleted file mode 100644 index 65c3f1c..0000000 --- a/addons/sourcemod/scripting/include/multicolors/colors.inc +++ /dev/null @@ -1,897 +0,0 @@ -/************************************************************************** - * * - * Colored Chat Functions * - * Author: exvel, Editor: Popoklopsi, Powerlord, Bara * - * Version: 2.0.0-MC * - * * - **************************************************************************/ - - -#if defined _colors_included - #endinput -#endif -#define _colors_included - -#define MAX_MESSAGE_LENGTH 250 -#define MAX_COLORS 18 - -#define SERVER_INDEX 0 -#define NO_INDEX -1 -#define NO_PLAYER -2 - -enum C_Colors { - Color_Default = 0, - Color_Darkred, - Color_Green, - Color_Lightgreen, - Color_Red, - Color_Blue, - Color_Olive, - Color_Lime, - Color_Lightred, - Color_Purple, - Color_Grey, - Color_Yellow, - Color_Orange, - Color_Bluegrey, - Color_Lightblue, - Color_Darkblue, - Color_Grey2, - Color_Orchid, - Color_Lightred2 -} - -/* C_Colors' properties */ -char C_Tag[][] = {"{default}", "{darkred}", "{green}", "{lightgreen}", "{red}", "{blue}", "{olive}", "{lime}", "{lightred}", "{purple}", "{grey}", "{yellow}", "{orange}", "{bluegrey}", "{lightblue}", "{darkblue}", "{grey2}", "{orchid}", "{lightred2}"}; -char C_TagCode[][] = {"\x01", "\x02", "\x04", "\x03", "\x03", "\x03", "\x05", "\x06", "\x07", "\x03", "\x08", "\x09", "\x10", "\x0A", "\x0B", "\x0C", "\x0D", "\x0E", "\x0F"}; -bool C_TagReqSayText2[] = {false, false, false, true, true, true, false, false, false, false, false, false, false, false, false, false, false, false, false}; -bool C_EventIsHooked; -bool C_SkipList[MAXPLAYERS+1]; - -/* Game default profile */ -bool C_Profile_Colors[] = {true, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}; -int C_Profile_TeamIndex[] = {NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX}; -bool C_Profile_SayText2; - -static ConVar sm_show_activity; - -/** - * Prints a message to a specific client in the chat area. - * Supports color tags. - * - * @param client Client index. - * @param szMessage Message (formatting rules). - * @return No return - * - * On error/Errors: If the client is not connected an error will be thrown. - */ -stock void C_PrintToChat(int client, const char[] szMessage, any ...) { - if (client <= 0 || client > MaxClients) { - ThrowError("Invalid client index %d", client); - } - - if (!IsClientInGame(client)) { - ThrowError("Client %d is not in game", client); - } - - char szBuffer[MAX_MESSAGE_LENGTH]; - char szCMessage[MAX_MESSAGE_LENGTH]; - - SetGlobalTransTarget(client); - - Format(szBuffer, sizeof(szBuffer), "\x01%s", szMessage); - VFormat(szCMessage, sizeof(szCMessage), szBuffer, 3); - - int index = C_Format(szCMessage, sizeof(szCMessage)); - - if (index == NO_INDEX) { - PrintToChat(client, "%s", szCMessage); - } - else { - C_SayText2(client, index, szCMessage); - } -} - -/** - * Reples to a message in a command. A client index of 0 will use PrintToServer(). - * If the command was from the console, PrintToConsole() is used. If the command was from chat, C_PrintToChat() is used. - * Supports color tags. - * - * @param client Client index, or 0 for server. - * @param szMessage Formatting rules. - * @param ... Variable number of format parameters. - * @return No return - * - * On error/Errors: If the client is not connected or invalid. - */ -stock void C_ReplyToCommand(int client, const char[] szMessage, any ...) { - char szCMessage[MAX_MESSAGE_LENGTH]; - SetGlobalTransTarget(client); - VFormat(szCMessage, sizeof(szCMessage), szMessage, 3); - - if (client == 0) { - C_RemoveTags(szCMessage, sizeof(szCMessage)); - PrintToServer("%s", szCMessage); - } - else if (GetCmdReplySource() == SM_REPLY_TO_CONSOLE) { - C_RemoveTags(szCMessage, sizeof(szCMessage)); - PrintToConsole(client, "%s", szCMessage); - } - else { - C_PrintToChat(client, "%s", szCMessage); - } -} - -/** - * Reples to a message in a command. A client index of 0 will use PrintToServer(). - * If the command was from the console, PrintToConsole() is used. If the command was from chat, C_PrintToChat() is used. - * Supports color tags. - * - * @param client Client index, or 0 for server. - * @param author Author index whose color will be used for teamcolor tag. - * @param szMessage Formatting rules. - * @param ... Variable number of format parameters. - * @return No return - * - * On error/Errors: If the client is not connected or invalid. - */ -stock void C_ReplyToCommandEx(int client, int author, const char[] szMessage, any ...) { - char szCMessage[MAX_MESSAGE_LENGTH]; - SetGlobalTransTarget(client); - VFormat(szCMessage, sizeof(szCMessage), szMessage, 4); - - if (client == 0) { - C_RemoveTags(szCMessage, sizeof(szCMessage)); - PrintToServer("%s", szCMessage); - } - else if (GetCmdReplySource() == SM_REPLY_TO_CONSOLE) { - C_RemoveTags(szCMessage, sizeof(szCMessage)); - PrintToConsole(client, "%s", szCMessage); - } - else { - C_PrintToChatEx(client, author, "%s", szCMessage); - } -} - -/** - * Prints a message to all clients in the chat area. - * Supports color tags. - * - * @param client Client index. - * @param szMessage Message (formatting rules) - * @return No return - */ -stock void C_PrintToChatAll(const char[] szMessage, any ...) { - char szBuffer[MAX_MESSAGE_LENGTH]; - - for (int i = 1; i <= MaxClients; ++i) { - if (IsClientInGame(i) && !IsFakeClient(i) && !C_SkipList[i]) { - SetGlobalTransTarget(i); - VFormat(szBuffer, sizeof(szBuffer), szMessage, 2); - - C_PrintToChat(i, "%s", szBuffer); - } - - C_SkipList[i] = false; - } -} - -/** - * Prints a message to a specific client in the chat area. - * Supports color tags and teamcolor tag. - * - * @param client Client index. - * @param author Author index whose color will be used for teamcolor tag. - * @param szMessage Message (formatting rules). - * @return No return - * - * On error/Errors: If the client or author are not connected an error will be thrown. - */ -stock void C_PrintToChatEx(int client, int author, const char[] szMessage, any ...) { - if (client <= 0 || client > MaxClients) { - ThrowError("Invalid client index %d", client); - } - - if (!IsClientInGame(client)) { - ThrowError("Client %d is not in game", client); - } - - if (author < 0 || author > MaxClients) { - ThrowError("Invalid client index %d", author); - } - - char szBuffer[MAX_MESSAGE_LENGTH]; - char szCMessage[MAX_MESSAGE_LENGTH]; - - SetGlobalTransTarget(client); - - Format(szBuffer, sizeof(szBuffer), "\x01%s", szMessage); - VFormat(szCMessage, sizeof(szCMessage), szBuffer, 4); - - int index = C_Format(szCMessage, sizeof(szCMessage), author); - - if (index == NO_INDEX) { - PrintToChat(client, "%s", szCMessage); - } - else { - C_SayText2(client, author, szCMessage); - } -} - -/** - * Prints a message to all clients in the chat area. - * Supports color tags and teamcolor tag. - * - * @param author Author index whos color will be used for teamcolor tag. - * @param szMessage Message (formatting rules). - * @return No return - * - * On error/Errors: If the author is not connected an error will be thrown. - */ -stock void C_PrintToChatAllEx(int author, const char[] szMessage, any ...) { - if (author < 0 || author > MaxClients) { - ThrowError("Invalid client index %d", author); - } - - if (!IsClientInGame(author)) { - ThrowError("Client %d is not in game", author); - } - - char szBuffer[MAX_MESSAGE_LENGTH]; - - for (int i = 1; i <= MaxClients; ++i) { - if (IsClientInGame(i) && !IsFakeClient(i) && !C_SkipList[i]) { - SetGlobalTransTarget(i); - VFormat(szBuffer, sizeof(szBuffer), szMessage, 3); - - C_PrintToChatEx(i, author, "%s", szBuffer); - } - - C_SkipList[i] = false; - } -} - -/** - * Removes color tags from the string. - * - * @param szMessage String. - * @return No return - */ -stock void C_RemoveTags(char[] szMessage, int maxlength) { - for (int i = 0; i < MAX_COLORS; i++) { - ReplaceString(szMessage, maxlength, C_Tag[i], "", false); - } - - ReplaceString(szMessage, maxlength, "{teamcolor}", "", false); -} - -/** - * Checks whether a color is allowed or not - * - * @param tag Color Tag. - * @return True when color is supported, otherwise false - */ -stock bool C_ColorAllowed(C_Colors color) { - if (!C_EventIsHooked) { - C_SetupProfile(); - - C_EventIsHooked = true; - } - - return C_Profile_Colors[color]; -} - -/** - * Replace the color with another color - * Handle with care! - * - * @param color color to replace. - * @param newColor color to replace with. - * @noreturn - */ -stock void C_ReplaceColor(C_Colors color, C_Colors newColor) { - if (!C_EventIsHooked) { - C_SetupProfile(); - - C_EventIsHooked = true; - } - - C_Profile_Colors[color] = C_Profile_Colors[newColor]; - C_Profile_TeamIndex[color] = C_Profile_TeamIndex[newColor]; - - C_TagReqSayText2[color] = C_TagReqSayText2[newColor]; - Format(C_TagCode[color], sizeof(C_TagCode[]), C_TagCode[newColor]); -} - -/** - * This function should only be used right in front of - * C_PrintToChatAll or C_PrintToChatAllEx and it tells - * to those funcions to skip specified client when printing - * message to all clients. After message is printed client will - * no more be skipped. - * - * @param client Client index - * @return No return - */ -stock void C_SkipNextClient(int client) { - if (client <= 0 || client > MaxClients) { - ThrowError("Invalid client index %d", client); - } - - C_SkipList[client] = true; -} - -/** - * Replaces color tags in a string with color codes - * - * @param szMessage String. - * @param maxlength Maximum length of the string buffer. - * @return Client index that can be used for SayText2 author index - * - * On error/Errors: If there is more then one team color is used an error will be thrown. - */ -stock int C_Format(char[] szMessage, int maxlength, int author = NO_INDEX) { - /* Hook event for auto profile setup on map start */ - if (!C_EventIsHooked) { - C_SetupProfile(); - HookEvent("server_spawn", C_Event_MapStart, EventHookMode_PostNoCopy); - - C_EventIsHooked = true; - } - - int iRandomPlayer = NO_INDEX; - - // On CS:GO set invisible precolor - if (GetEngineVersion() == Engine_CSGO) { - Format(szMessage, maxlength, " %s", szMessage); - } - - /* If author was specified replace {teamcolor} tag */ - if (author != NO_INDEX) { - if (C_Profile_SayText2) { - ReplaceString(szMessage, maxlength, "{teamcolor}", "\x03", false); - - iRandomPlayer = author; - } - /* If saytext2 is not supported by game replace {teamcolor} with green tag */ - else { - ReplaceString(szMessage, maxlength, "{teamcolor}", C_TagCode[Color_Green], false); - } - } - else { - ReplaceString(szMessage, maxlength, "{teamcolor}", "", false); - } - - /* For other color tags we need a loop */ - for (int i = 0; i < MAX_COLORS; i++) { - /* If tag not found - skip */ - if (StrContains(szMessage, C_Tag[i], false) == -1) { - continue; - } - - /* If tag is not supported by game replace it with green tag */ - else if (!C_Profile_Colors[i]) { - ReplaceString(szMessage, maxlength, C_Tag[i], C_TagCode[Color_Green], false); - } - - /* If tag doesn't need saytext2 simply replace */ - else if (!C_TagReqSayText2[i]) { - ReplaceString(szMessage, maxlength, C_Tag[i], C_TagCode[i], false); - } - - /* Tag needs saytext2 */ - else { - /* If saytext2 is not supported by game replace tag with green tag */ - if (!C_Profile_SayText2) { - ReplaceString(szMessage, maxlength, C_Tag[i], C_TagCode[Color_Green], false); - } - - /* Game supports saytext2 */ - else { - /* If random player for tag wasn't specified replace tag and find player */ - if (iRandomPlayer == NO_INDEX) { - /* Searching for valid client for tag */ - iRandomPlayer = C_FindRandomPlayerByTeam(C_Profile_TeamIndex[i]); - - /* If player not found replace tag with green color tag */ - if (iRandomPlayer == NO_PLAYER) { - ReplaceString(szMessage, maxlength, C_Tag[i], C_TagCode[Color_Green], false); - } - - /* If player was found simply replace */ - else { - ReplaceString(szMessage, maxlength, C_Tag[i], C_TagCode[i], false); - } - - } - /* If found another team color tag throw error */ - else { - //ReplaceString(szMessage, maxlength, C_Tag[i], ""); - ThrowError("Using two team colors in one message is not allowed"); - } - } - } - } - - return iRandomPlayer; -} - -/** - * Finds a random player with specified team - * - * @param color_team Client team. - * @return Client index or NO_PLAYER if no player found - */ -stock int C_FindRandomPlayerByTeam(int color_team) { - if (color_team == SERVER_INDEX) { - return 0; - } - - int[] players = new int[MaxClients]; - int count; - - for (int i = 1; i <= MaxClients; ++i) { - if (IsClientInGame(i) && GetClientTeam(i) == color_team) { - players[count++] = i; - } - } - - if (count) { - return players[GetRandomInt(0, count-1)]; - } - - return NO_PLAYER; -} - -/** - * Sends a SayText2 usermessage to a client - * - * @param szMessage Client index - * @param maxlength Author index - * @param szMessage Message - * @return No return. - */ -stock void C_SayText2(int client, int author, const char[] szMessage) { - Handle hBuffer = StartMessageOne("SayText2", client, USERMSG_RELIABLE|USERMSG_BLOCKHOOKS); - - if (GetFeatureStatus(FeatureType_Native, "GetUserMessageType") == FeatureStatus_Available && GetUserMessageType() == UM_Protobuf) { - Protobuf pb = UserMessageToProtobuf(hBuffer); - pb.SetInt("ent_idx", author); - pb.SetBool("chat", true); - pb.SetString("msg_name", szMessage); - pb.AddString("params", ""); - pb.AddString("params", ""); - pb.AddString("params", ""); - pb.AddString("params", ""); - } - else { - BfWrite bf = UserMessageToBfWrite(hBuffer); - bf.WriteByte(author); - bf.WriteByte(true); - bf.WriteString(szMessage); - } - - EndMessage(); -} - -/** - * Creates game color profile - * This function must be edited if you want to add more games support - * - * @return No return. - */ -stock void C_SetupProfile() { - EngineVersion engine = GetEngineVersion(); - - if (engine == Engine_CSS) { - C_Profile_Colors[Color_Lightgreen] = true; - C_Profile_Colors[Color_Red] = true; - C_Profile_Colors[Color_Blue] = true; - C_Profile_Colors[Color_Olive] = true; - C_Profile_TeamIndex[Color_Lightgreen] = SERVER_INDEX; - C_Profile_TeamIndex[Color_Red] = 2; - C_Profile_TeamIndex[Color_Blue] = 3; - C_Profile_SayText2 = true; - } - else if (engine == Engine_CSGO) { - C_Profile_Colors[Color_Red] = true; - C_Profile_Colors[Color_Blue] = true; - C_Profile_Colors[Color_Olive] = true; - C_Profile_Colors[Color_Darkred] = true; - C_Profile_Colors[Color_Lime] = true; - C_Profile_Colors[Color_Lightred] = true; - C_Profile_Colors[Color_Purple] = true; - C_Profile_Colors[Color_Grey] = true; - C_Profile_Colors[Color_Yellow] = true; - C_Profile_Colors[Color_Orange] = true; - C_Profile_Colors[Color_Bluegrey] = true; - C_Profile_Colors[Color_Lightblue] = true; - C_Profile_Colors[Color_Darkblue] = true; - C_Profile_Colors[Color_Grey2] = true; - C_Profile_Colors[Color_Orchid] = true; - C_Profile_Colors[Color_Lightred2] = true; - C_Profile_TeamIndex[Color_Red] = 2; - C_Profile_TeamIndex[Color_Blue] = 3; - C_Profile_SayText2 = true; - } - else if (engine == Engine_TF2) { - C_Profile_Colors[Color_Lightgreen] = true; - C_Profile_Colors[Color_Red] = true; - C_Profile_Colors[Color_Blue] = true; - C_Profile_Colors[Color_Olive] = true; - C_Profile_TeamIndex[Color_Lightgreen] = SERVER_INDEX; - C_Profile_TeamIndex[Color_Red] = 2; - C_Profile_TeamIndex[Color_Blue] = 3; - C_Profile_SayText2 = true; - } - else if (engine == Engine_Left4Dead || engine == Engine_Left4Dead2) { - C_Profile_Colors[Color_Lightgreen] = true; - C_Profile_Colors[Color_Red] = true; - C_Profile_Colors[Color_Blue] = true; - C_Profile_Colors[Color_Olive] = true; - C_Profile_TeamIndex[Color_Lightgreen] = SERVER_INDEX; - C_Profile_TeamIndex[Color_Red] = 3; - C_Profile_TeamIndex[Color_Blue] = 2; - C_Profile_SayText2 = true; - } - else if (engine == Engine_HL2DM) { - /* hl2mp profile is based on mp_teamplay convar */ - if (GetConVarBool(FindConVar("mp_teamplay"))) { - C_Profile_Colors[Color_Red] = true; - C_Profile_Colors[Color_Blue] = true; - C_Profile_Colors[Color_Olive] = true; - C_Profile_TeamIndex[Color_Red] = 3; - C_Profile_TeamIndex[Color_Blue] = 2; - C_Profile_SayText2 = true; - } - else { - C_Profile_SayText2 = false; - C_Profile_Colors[Color_Olive] = true; - } - } - else if (engine == Engine_DODS) { - C_Profile_Colors[Color_Olive] = true; - C_Profile_SayText2 = false; - } - /* Profile for other games */ - else { - if (GetUserMessageId("SayText2") == INVALID_MESSAGE_ID) { - C_Profile_SayText2 = false; - } - else { - C_Profile_Colors[Color_Red] = true; - C_Profile_Colors[Color_Blue] = true; - C_Profile_TeamIndex[Color_Red] = 2; - C_Profile_TeamIndex[Color_Blue] = 3; - C_Profile_SayText2 = true; - } - } -} - -public void C_Event_MapStart(Event event, const char[] name, bool dontBroadcast) { - C_SetupProfile(); - - for (int i = 1; i <= MaxClients; ++i) { - C_SkipList[i] = false; - } -} - -/** - * Displays usage of an admin command to users depending on the - * setting of the sm_show_activity cvar. - * - * This version does not display a message to the originating client - * if used from chat triggers or menus. If manual replies are used - * for these cases, then this function will suffice. Otherwise, - * C_ShowActivity2() is slightly more useful. - * Supports color tags. - * - * @param client Client index doing the action, or 0 for server. - * @param format Formatting rules. - * @param ... Variable number of format parameters. - * @noreturn - * @error - */ -stock int C_ShowActivity(int client, const char[] format, any ...) { - if (sm_show_activity == null) { - sm_show_activity = FindConVar("sm_show_activity"); - } - - char tag[] = "[SM] "; - - char szBuffer[MAX_MESSAGE_LENGTH]; - //char szCMessage[MAX_MESSAGE_LENGTH]; - int value = sm_show_activity.IntValue; - ReplySource replyto = GetCmdReplySource(); - - char name[MAX_NAME_LENGTH] = "Console"; - char sign[MAX_NAME_LENGTH] = "ADMIN"; - bool display_in_chat = false; - if (client != 0) { - if (client < 0 || client > MaxClients || !IsClientConnected(client)) { - ThrowError("Client index %d is invalid", client); - } - - GetClientName(client, name, sizeof(name)); - AdminId id = GetUserAdmin(client); - if (id == INVALID_ADMIN_ID || !GetAdminFlag(id, Admin_Generic, Access_Effective)) { - sign = "PLAYER"; - } - - /* Display the message to the client? */ - if (replyto == SM_REPLY_TO_CONSOLE) { - SetGlobalTransTarget(client); - VFormat(szBuffer, sizeof(szBuffer), format, 3); - - C_RemoveTags(szBuffer, sizeof(szBuffer)); - PrintToConsole(client, "%s%s\n", tag, szBuffer); - display_in_chat = true; - } - } - else { - SetGlobalTransTarget(LANG_SERVER); - VFormat(szBuffer, sizeof(szBuffer), format, 3); - - C_RemoveTags(szBuffer, sizeof(szBuffer)); - PrintToServer("%s%s\n", tag, szBuffer); - } - - if (!value) { - return 1; - } - - for (int i = 1; i <= MaxClients; ++i) { - if (!IsClientInGame(i) || IsFakeClient(i) || (display_in_chat && i == client)) { - continue; - } - - AdminId id = GetUserAdmin(i); - SetGlobalTransTarget(i); - if (id == INVALID_ADMIN_ID || !GetAdminFlag(id, Admin_Generic, Access_Effective)) { - /* Treat this as a normal user. */ - if ((value & 1) | (value & 2)) { - char newsign[MAX_NAME_LENGTH]; - - if ((value & 2) || (i == client)) { - newsign = name; - } - else { - newsign = sign; - } - - VFormat(szBuffer, sizeof(szBuffer), format, 3); - - C_PrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer); - } - } - else { - /* Treat this as an admin user */ - bool is_root = GetAdminFlag(id, Admin_Root, Access_Effective); - if ((value & 4) || (value & 8) || ((value & 16) && is_root)) { - char newsign[MAX_NAME_LENGTH]; - - if ((value & 8) || ((value & 16) && is_root) || (i == client)) { - newsign = name; - } - else { - newsign = sign; - } - - VFormat(szBuffer, sizeof(szBuffer), format, 3); - - C_PrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer); - } - } - } - - return 1; -} - -/** - * Same as C_ShowActivity(), except the tag parameter is used instead of "[SM] " (note that you must supply any spacing). - * Supports color tags. - * - * @param client Client index doing the action, or 0 for server. - * @param tags Tag to display with. - * @param format Formatting rules. - * @param ... Variable number of format parameters. - * @noreturn - * @error - */ -stock int C_ShowActivityEx(int client, const char[] tag, const char[] format, any ...) { - if (sm_show_activity == null) { - sm_show_activity = FindConVar("sm_show_activity"); - } - - char szBuffer[MAX_MESSAGE_LENGTH]; - //char szCMessage[MAX_MESSAGE_LENGTH]; - int value = sm_show_activity.IntValue; - ReplySource replyto = GetCmdReplySource(); - - char name[MAX_NAME_LENGTH] = "Console"; - char sign[MAX_NAME_LENGTH] = "ADMIN"; - bool display_in_chat = false; - if (client != 0) { - if (client < 0 || client > MaxClients || !IsClientConnected(client)) { - ThrowError("Client index %d is invalid", client); - } - - GetClientName(client, name, sizeof(name)); - AdminId id = GetUserAdmin(client); - if (id == INVALID_ADMIN_ID || !GetAdminFlag(id, Admin_Generic, Access_Effective)) { - sign = "PLAYER"; - } - - /* Display the message to the client? */ - if (replyto == SM_REPLY_TO_CONSOLE) { - SetGlobalTransTarget(client); - VFormat(szBuffer, sizeof(szBuffer), format, 4); - - C_RemoveTags(szBuffer, sizeof(szBuffer)); - PrintToConsole(client, "%s%s\n", tag, szBuffer); - display_in_chat = true; - } - } - else { - SetGlobalTransTarget(LANG_SERVER); - VFormat(szBuffer, sizeof(szBuffer), format, 4); - - C_RemoveTags(szBuffer, sizeof(szBuffer)); - PrintToServer("%s%s\n", tag, szBuffer); - } - - if (!value) { - return 1; - } - - for (int i = 1; i <= MaxClients; ++i) { - if (!IsClientInGame(i) || IsFakeClient(i) || (display_in_chat && i == client)) { - continue; - } - - AdminId id = GetUserAdmin(i); - SetGlobalTransTarget(i); - if (id == INVALID_ADMIN_ID || !GetAdminFlag(id, Admin_Generic, Access_Effective)) { - /* Treat this as a normal user. */ - if ((value & 1) | (value & 2)) { - char newsign[MAX_NAME_LENGTH]; - - if ((value & 2) || (i == client)) { - newsign = name; - } - else { - newsign = sign; - } - - VFormat(szBuffer, sizeof(szBuffer), format, 4); - - C_PrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer); - } - } - else { - /* Treat this as an admin user */ - bool is_root = GetAdminFlag(id, Admin_Root, Access_Effective); - if ((value & 4) || (value & 8) || ((value & 16) && is_root)) { - char newsign[MAX_NAME_LENGTH]; - - if ((value & 8) || ((value & 16) && is_root) || (i == client)) { - newsign = name; - } - else { - newsign = sign; - } - - VFormat(szBuffer, sizeof(szBuffer), format, 4); - - C_PrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer); - } - } - } - - return 1; -} - -/** - * Displays usage of an admin command to users depending on the setting of the sm_show_activity cvar. - * All users receive a message in their chat text, except for the originating client, - * who receives the message based on the current ReplySource. - * Supports color tags. - * - * @param client Client index doing the action, or 0 for server. - * @param tags Tag to prepend to the message. - * @param format Formatting rules. - * @param ... Variable number of format parameters. - * @noreturn - * @error - */ -stock int C_ShowActivity2(int client, const char[] tag, const char[] format, any ...) { - if (sm_show_activity == null) { - sm_show_activity = FindConVar("sm_show_activity"); - } - - char szBuffer[MAX_MESSAGE_LENGTH]; - //char szCMessage[MAX_MESSAGE_LENGTH]; - int value = sm_show_activity.IntValue; - // ReplySource replyto = GetCmdReplySource(); - - char name[MAX_NAME_LENGTH] = "Console"; - char sign[MAX_NAME_LENGTH] = "ADMIN"; - if (client != 0) { - if (client < 0 || client > MaxClients || !IsClientConnected(client)) { - ThrowError("Client index %d is invalid", client); - } - - GetClientName(client, name, sizeof(name)); - AdminId id = GetUserAdmin(client); - if (id == INVALID_ADMIN_ID || !GetAdminFlag(id, Admin_Generic, Access_Effective)) { - sign = "PLAYER"; - } - - SetGlobalTransTarget(client); - VFormat(szBuffer, sizeof(szBuffer), format, 4); - - /* We don't display directly to the console because the chat text - * simply gets added to the console, so we don't want it to print - * twice. - */ - C_PrintToChatEx(client, client, "%s%s", tag, szBuffer); - } - else { - SetGlobalTransTarget(LANG_SERVER); - VFormat(szBuffer, sizeof(szBuffer), format, 4); - - C_RemoveTags(szBuffer, sizeof(szBuffer)); - PrintToServer("%s%s\n", tag, szBuffer); - } - - if (!value) { - return 1; - } - - for (int i = 1; i <= MaxClients; ++i) { - if (!IsClientInGame(i) || IsFakeClient(i) || i == client) { - continue; - } - - AdminId id = GetUserAdmin(i); - SetGlobalTransTarget(i); - if (id == INVALID_ADMIN_ID - || !GetAdminFlag(id, Admin_Generic, Access_Effective)) { - /* Treat this as a normal user. */ - if ((value & 1) | (value & 2)) { - char newsign[MAX_NAME_LENGTH]; - - if ((value & 2)) { - newsign = name; - } - else { - newsign = sign; - } - - VFormat(szBuffer, sizeof(szBuffer), format, 4); - - C_PrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer); - } - } - else { - /* Treat this as an admin user */ - bool is_root = GetAdminFlag(id, Admin_Root, Access_Effective); - if ((value & 4) || (value & 8) || ((value & 16) && is_root)) { - char newsign[MAX_NAME_LENGTH]; - - if ((value & 8) || ((value & 16) && is_root)) { - newsign = name; - } - else { - newsign = sign; - } - - VFormat(szBuffer, sizeof(szBuffer), format, 4); - - C_PrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer); - } - } - } - - return 1; -} diff --git a/addons/sourcemod/scripting/include/multicolors/morecolors.inc b/addons/sourcemod/scripting/include/multicolors/morecolors.inc deleted file mode 100644 index 2020b60..0000000 --- a/addons/sourcemod/scripting/include/multicolors/morecolors.inc +++ /dev/null @@ -1,992 +0,0 @@ -// MOAR COLORS -// By Dr. McKay -// Inspired by: https://forums.alliedmods.net/showthread.php?t=96831 - -#if defined _more_colors_included - #endinput -#endif -#define _more_colors_included - -#pragma newdecls optional -#include - -#define MORE_COLORS_VERSION "2.0.0-MC" -#define MC_MAX_MESSAGE_LENGTH 256 -#define MAX_BUFFER_LENGTH (MC_MAX_MESSAGE_LENGTH * 4) - -#define MCOLOR_RED 0xFF4040 -#define MCOLOR_BLUE 0x99CCFF -#define MCOLOR_GRAY 0xCCCCCC -#define MCOLOR_GREEN 0x3EFF3E - -#define MC_GAME_DODS 0 - -bool MC_SkipList[MAXPLAYERS+1]; -StringMap MC_Trie; -int MC_TeamColors[][] = {{0xCCCCCC, 0x4D7942, 0xFF4040}}; // Multi-dimensional array for games that don't support SayText2. First index is the game index (as defined by the GAME_ defines), second index is team. 0 = spectator, 1 = team1, 2 = team2 - -static ConVar sm_show_activity; - -/** - * Prints a message to a specific client in the chat area. - * Supports color tags. - * - * @param client Client index. - * @param message Message (formatting rules). - * - * On error/Errors: If the client is not connected an error will be thrown. - */ -stock void MC_PrintToChat(int client, const char[] message, any ...) { - MC_CheckTrie(); - - if (client <= 0 || client > MaxClients) { - ThrowError("Invalid client index %i", client); - } - - if (!IsClientInGame(client)) { - ThrowError("Client %i is not in game", client); - } - - char buffer[MAX_BUFFER_LENGTH]; - char buffer2[MAX_BUFFER_LENGTH]; - - SetGlobalTransTarget(client); - Format(buffer, sizeof(buffer), "\x01%s", message); - VFormat(buffer2, sizeof(buffer2), buffer, 3); - - MC_ReplaceColorCodes(buffer2); - MC_SendMessage(client, buffer2); -} - -/** - * Prints a message to all clients in the chat area. - * Supports color tags. - * - * @param client Client index. - * @param message Message (formatting rules). - */ -stock void MC_PrintToChatAll(const char[] message, any ...) { - MC_CheckTrie(); - - char buffer[MAX_BUFFER_LENGTH], buffer2[MAX_BUFFER_LENGTH]; - - for (int i = 1; i <= MaxClients; ++i) { - if (!IsClientInGame(i) || MC_SkipList[i]) { - MC_SkipList[i] = false; - continue; - } - - SetGlobalTransTarget(i); - Format(buffer, sizeof(buffer), "\x01%s", message); - VFormat(buffer2, sizeof(buffer2), buffer, 2); - - MC_ReplaceColorCodes(buffer2); - MC_SendMessage(i, buffer2); - } -} - -/** - * Prints a message to a specific client in the chat area. - * Supports color tags and teamcolor tag. - * - * @param client Client index. - * @param author Author index whose color will be used for teamcolor tag. - * @param message Message (formatting rules). - * - * On error/Errors: If the client or author are not connected an error will be thrown - */ -stock void MC_PrintToChatEx(int client, int author, const char[] message, any ...) { - MC_CheckTrie(); - - if (client <= 0 || client > MaxClients) { - ThrowError("Invalid client index %i", client); - } - - if (!IsClientInGame(client)) { - ThrowError("Client %i is not in game", client); - } - - if (author <= 0 || author > MaxClients) { - ThrowError("Invalid client index %i", author); - } - - if (!IsClientInGame(author)) { - ThrowError("Client %i is not in game", author); - } - - char buffer[MAX_BUFFER_LENGTH], buffer2[MAX_BUFFER_LENGTH]; - SetGlobalTransTarget(client); - Format(buffer, sizeof(buffer), "\x01%s", message); - VFormat(buffer2, sizeof(buffer2), buffer, 4); - MC_ReplaceColorCodes(buffer2, author); - MC_SendMessage(client, buffer2, author); -} - -/** - * Prints a message to all clients in the chat area. - * Supports color tags and teamcolor tag. - * - * @param author Author index whose color will be used for teamcolor tag. - * @param message Message (formatting rules). - * - * On error/Errors: If the author is not connected an error will be thrown. - */ -stock void MC_PrintToChatAllEx(int author, const char[] message, any ...) { - MC_CheckTrie(); - - if (author <= 0 || author > MaxClients) { - ThrowError("Invalid client index %i", author); - } - - if (!IsClientInGame(author)) { - ThrowError("Client %i is not in game", author); - } - - char buffer[MAX_BUFFER_LENGTH]; - char buffer2[MAX_BUFFER_LENGTH]; - - for (int i = 1; i <= MaxClients; ++i) { - if (!IsClientInGame(i) || MC_SkipList[i]) { - MC_SkipList[i] = false; - continue; - } - - SetGlobalTransTarget(i); - Format(buffer, sizeof(buffer), "\x01%s", message); - VFormat(buffer2, sizeof(buffer2), buffer, 3); - - MC_ReplaceColorCodes(buffer2, author); - MC_SendMessage(i, buffer2, author); - } -} - -/** - * Sends a SayText2 usermessage - * - * @param client Client to send usermessage to - * @param message Message to send - */ -stock void MC_SendMessage(int client, const char[] message, int author = 0) { - if (author == 0) { - author = client; - } - - char buffer[MC_MAX_MESSAGE_LENGTH]; - strcopy(buffer, sizeof(buffer), message); - - UserMsg index = GetUserMessageId("SayText2"); - if (index == INVALID_MESSAGE_ID) { - if (GetEngineVersion() == Engine_DODS) { - int team = GetClientTeam(author); - if (team == 0) { - ReplaceString(buffer, sizeof(buffer), "\x03", "\x04", false); // Unassigned gets green - } - else { - char temp[16]; - Format(temp, sizeof(temp), "\x07%06X", MC_TeamColors[MC_GAME_DODS][team - 1]); - ReplaceString(buffer, sizeof(buffer), "\x03", temp, false); - } - } - - PrintToChat(client, "%s", buffer); - return; - } - - Handle buf = StartMessageOne("SayText2", client, USERMSG_RELIABLE|USERMSG_BLOCKHOOKS); - if (GetFeatureStatus(FeatureType_Native, "GetUserMessageType") == FeatureStatus_Available && GetUserMessageType() == UM_Protobuf) { - Protobuf pb = UserMessageToProtobuf(buf); - pb.SetInt("ent_idx", author); - pb.SetBool("chat", true); - pb.SetString("msg_name", buffer); - pb.AddString("params", ""); - pb.AddString("params", ""); - pb.AddString("params", ""); - pb.AddString("params", ""); - } - else { - BfWrite bf = UserMessageToBfWrite(buf); - bf.WriteByte(author); // Message author - bf.WriteByte(true); // Chat message - bf.WriteString(buffer); // Message text - } - - EndMessage(); -} - -/** - * This function should only be used right in front of - * MC_PrintToChatAll or MC_PrintToChatAllEx. It causes those functions - * to skip the specified client when printing the message. - * After printing the message, the client will no longer be skipped. - * - * @param client Client index - */ -stock void MC_SkipNextClient(int client) { - if (client <= 0 || client > MaxClients) { - ThrowError("Invalid client index %i", client); - } - - MC_SkipList[client] = true; -} - -/** - * Checks if the colors trie is initialized and initializes it if it's not (used internally) - * - * @return No return - */ -stock void MC_CheckTrie() { - if (MC_Trie == null) { - MC_Trie = MC_InitColorTrie(); - } -} - -/** - * Replaces color tags in a string with color codes (used internally by MC_PrintToChat, MC_PrintToChatAll, MC_PrintToChatEx, and MC_PrintToChatAllEx - * - * @param buffer String. - * @param author Optional client index to use for {teamcolor} tags, or 0 for none - * @param removeTags Optional boolean value to determine whether we're replacing tags with colors, or just removing tags, used by MC_RemoveTags - * @param maxlen Optional value for max buffer length, used by MC_RemoveTags - * - * On error/Errors: If the client index passed for author is invalid or not in game. - */ -stock void MC_ReplaceColorCodes(char[] buffer, int author = 0, bool removeTags = false, int maxlen = MAX_BUFFER_LENGTH) { - MC_CheckTrie(); - if (!removeTags) { - ReplaceString(buffer, maxlen, "{default}", "\x01", false); - } - else { - ReplaceString(buffer, maxlen, "{default}", "", false); - ReplaceString(buffer, maxlen, "{teamcolor}", "", false); - } - - if (author != 0 && !removeTags) { - if (author < 0 || author > MaxClients) { - ThrowError("Invalid client index %i", author); - } - - if (!IsClientInGame(author)) { - ThrowError("Client %i is not in game", author); - } - - ReplaceString(buffer, maxlen, "{teamcolor}", "\x03", false); - } - - int cursor = 0; - int value; - char tag[32], buff[32]; - char[] output = new char[maxlen]; - - strcopy(output, maxlen, buffer); - // Since the string's size is going to be changing, output will hold the replaced string and we'll search buffer - - Regex regex = new Regex("{[#a-zA-Z0-9]+}"); - for (int i = 0; i < 1000; i++) { // The RegEx extension is quite flaky, so we have to loop here :/. This loop is supposed to be infinite and broken by return, but conditions have been added to be safe. - if (regex.Match(buffer[cursor]) < 1) { - delete regex; - strcopy(buffer, maxlen, output); - return; - } - - regex.GetSubString(0, tag, sizeof(tag)); - MC_StrToLower(tag); - cursor = StrContains(buffer[cursor], tag, false) + cursor + 1; - strcopy(buff, sizeof(buff), tag); - ReplaceString(buff, sizeof(buff), "{", ""); - ReplaceString(buff, sizeof(buff), "}", ""); - - if (buff[0] == '#') { - if (strlen(buff) == 7) { - Format(buff, sizeof(buff), "\x07%s", buff[1]); - } - else if (strlen(buff) == 9) { - Format(buff, sizeof(buff), "\x08%s", buff[1]); - } - else { - continue; - } - - if (removeTags) { - ReplaceString(output, maxlen, tag, "", false); - } - else { - ReplaceString(output, maxlen, tag, buff, false); - } - } - else if (!MC_Trie.GetValue(buff, value)) { - continue; - } - - if (removeTags) { - ReplaceString(output, maxlen, tag, "", false); - } - else { - Format(buff, sizeof(buff), "\x07%06X", value); - ReplaceString(output, maxlen, tag, buff, false); - } - } - LogError("[MORE COLORS] Infinite loop broken."); -} - -/** - * Gets a part of a string - * - * @param input String to get the part from - * @param output Buffer to write to - * @param maxlen Max length of output buffer - * @param start Position to start at - * @param numChars Number of characters to return, or 0 for the end of the string - */ -stock void CSubString(const char[] input, char[] output, int maxlen, int start, int numChars = 0) { - int i = 0; - for (;;) { - if (i == maxlen - 1 || i >= numChars || input[start + i] == '\0') { - output[i] = '\0'; - return; - } - - output[i] = input[start + i]; - i++; - } -} - -/** - * Converts a string to lowercase - * - * @param buffer String to convert - */ -stock void MC_StrToLower(char[] buffer) { - int len = strlen(buffer); - for (int i = 0; i < len; i++) { - buffer[i] = CharToLower(buffer[i]); - } -} - -/** - * Adds a color to the colors trie - * - * @param name Color name, without braces - * @param color Hexadecimal representation of the color (0xRRGGBB) - * @return True if color was added successfully, false if a color already exists with that name - */ -stock bool MC_AddColor(const char[] name, int color) { - MC_CheckTrie(); - - int value; - - if (MC_Trie.GetValue(name, value)) { - return false; - } - - char newName[64]; - strcopy(newName, sizeof(newName), name); - - MC_StrToLower(newName); - MC_Trie.SetValue(newName, color); - return true; -} - -/** - * Removes color tags from a message - * - * @param message Message to remove tags from - * @param maxlen Maximum buffer length - */ -stock void MC_RemoveTags(char[] message, int maxlen) { - MC_ReplaceColorCodes(message, 0, true, maxlen); -} - -/** - * Replies to a command with colors - * - * @param client Client to reply to - * @param message Message (formatting rules) - */ -stock void MC_ReplyToCommand(int client, const char[] message, any ...) { - char buffer[MAX_BUFFER_LENGTH]; - SetGlobalTransTarget(client); - VFormat(buffer, sizeof(buffer), message, 3); - - if (client == 0) { - MC_RemoveTags(buffer, sizeof(buffer)); - PrintToServer("%s", buffer); - } - - if (GetCmdReplySource() == SM_REPLY_TO_CONSOLE) { - MC_RemoveTags(buffer, sizeof(buffer)); - PrintToConsole(client, "%s", buffer); - } - else { - MC_PrintToChat(client, "%s", buffer); - } -} - -/** - * Replies to a command with colors - * - * @param client Client to reply to - * @param author Client to use for {teamcolor} - * @param message Message (formatting rules) - */ -stock void MC_ReplyToCommandEx(int client, int author, const char[] message, any ...) { - char buffer[MAX_BUFFER_LENGTH]; - SetGlobalTransTarget(client); - VFormat(buffer, sizeof(buffer), message, 4); - - if (client == 0) { - MC_RemoveTags(buffer, sizeof(buffer)); - PrintToServer("%s", buffer); - } - - if (GetCmdReplySource() == SM_REPLY_TO_CONSOLE) { - MC_RemoveTags(buffer, sizeof(buffer)); - PrintToConsole(client, "%s", buffer); - } - else { - MC_PrintToChatEx(client, author, "%s", buffer); - } -} - -/** - * Displays usage of an admin command to users depending on the - * setting of the sm_show_activity cvar. - * - * This version does not display a message to the originating client - * if used from chat triggers or menus. If manual replies are used - * for these cases, then this function will suffice. Otherwise, - * MC_ShowActivity2() is slightly more useful. - * Supports color tags. - * - * @param client Client index doing the action, or 0 for server. - * @param format Formatting rules. - * @param ... Variable number of format parameters. - * @error - */ -stock int MC_ShowActivity(int client, const char[] format, any ...) { - if (sm_show_activity == null) { - sm_show_activity = FindConVar("sm_show_activity"); - } - - char tag[] = "[SM] "; - - char szBuffer[MC_MAX_MESSAGE_LENGTH]; - //char szCMessage[MC_MAX_MESSAGE_LENGTH]; - int value = sm_show_activity.IntValue; - ReplySource replyto = GetCmdReplySource(); - - char name[MAX_NAME_LENGTH] = "Console"; - char sign[MAX_NAME_LENGTH] = "ADMIN"; - bool display_in_chat = false; - if (client != 0) { - if (client < 0 || client > MaxClients || !IsClientConnected(client)) - ThrowError("Client index %d is invalid", client); - - GetClientName(client, name, sizeof(name)); - AdminId id = GetUserAdmin(client); - if (id == INVALID_ADMIN_ID || !id.HasFlag(Admin_Generic, Access_Effective)) { - sign = "PLAYER"; - } - - /* Display the message to the client? */ - if (replyto == SM_REPLY_TO_CONSOLE) { - SetGlobalTransTarget(client); - VFormat(szBuffer, sizeof(szBuffer), format, 3); - - MC_RemoveTags(szBuffer, sizeof(szBuffer)); - PrintToConsole(client, "%s%s\n", tag, szBuffer); - display_in_chat = true; - } - } - else { - SetGlobalTransTarget(LANG_SERVER); - VFormat(szBuffer, sizeof(szBuffer), format, 3); - - MC_RemoveTags(szBuffer, sizeof(szBuffer)); - PrintToServer("%s%s\n", tag, szBuffer); - } - - if (!value) { - return 1; - } - - for (int i = 1; i <= MaxClients; ++i) { - if (!IsClientInGame(i) || IsFakeClient(i) || (display_in_chat && i == client)) { - continue; - } - - AdminId id = GetUserAdmin(i); - SetGlobalTransTarget(i); - if (id == INVALID_ADMIN_ID || !id.HasFlag(Admin_Generic, Access_Effective)) { - /* Treat this as a normal user. */ - if ((value & 1) | (value & 2)) { - char newsign[MAX_NAME_LENGTH]; - - if ((value & 2) || (i == client)) { - newsign = name; - } - else { - newsign = sign; - } - - VFormat(szBuffer, sizeof(szBuffer), format, 3); - - MC_PrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer); - } - } - else { - /* Treat this as an admin user */ - bool is_root = id.HasFlag(Admin_Root, Access_Effective); - if ((value & 4) || (value & 8) || ((value & 16) && is_root)) { - char newsign[MAX_NAME_LENGTH]; - - if ((value & 8) || ((value & 16) && is_root) || (i == client)) { - newsign = name; - } - else { - newsign = sign; - } - - VFormat(szBuffer, sizeof(szBuffer), format, 3); - - MC_PrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer); - } - } - } - - return 1; -} - -/** - * Same as MC_ShowActivity(), except the tag parameter is used instead of "[SM] " (note that you must supply any spacing). - * Supports color tags. - * - * @param client Client index doing the action, or 0 for server. - * @param tags Tag to display with. - * @param format Formatting rules. - * @param ... Variable number of format parameters. - * @error - */ -stock int MC_ShowActivityEx(int client, const char[] tag, const char[] format, any ...) { - if (sm_show_activity == null) { - sm_show_activity = FindConVar("sm_show_activity"); - } - - char szBuffer[MC_MAX_MESSAGE_LENGTH]; - //char szCMessage[MC_MAX_MESSAGE_LENGTH]; - int value = sm_show_activity.IntValue; - ReplySource replyto = GetCmdReplySource(); - - char name[MAX_NAME_LENGTH] = "Console"; - char sign[MAX_NAME_LENGTH] = "ADMIN"; - bool display_in_chat = false; - if (client != 0) { - if (client < 0 || client > MaxClients || !IsClientConnected(client)) { - ThrowError("Client index %d is invalid", client); - } - - GetClientName(client, name, sizeof(name)); - AdminId id = GetUserAdmin(client); - if (id == INVALID_ADMIN_ID || !id.HasFlag(Admin_Generic, Access_Effective)) { - sign = "PLAYER"; - } - - /* Display the message to the client? */ - if (replyto == SM_REPLY_TO_CONSOLE) { - SetGlobalTransTarget(client); - VFormat(szBuffer, sizeof(szBuffer), format, 4); - - MC_RemoveTags(szBuffer, sizeof(szBuffer)); - PrintToConsole(client, "%s%s\n", tag, szBuffer); - display_in_chat = true; - } - } - else { - SetGlobalTransTarget(LANG_SERVER); - VFormat(szBuffer, sizeof(szBuffer), format, 4); - - MC_RemoveTags(szBuffer, sizeof(szBuffer)); - PrintToServer("%s%s\n", tag, szBuffer); - } - - if (!value) { - return 1; - } - - for (int i = 1; i <= MaxClients; ++i) { - if (!IsClientInGame(i) || IsFakeClient(i) || (display_in_chat && i == client)) { - continue; - } - - AdminId id = GetUserAdmin(i); - SetGlobalTransTarget(i); - if (id == INVALID_ADMIN_ID || !id.HasFlag(Admin_Generic, Access_Effective)) { - /* Treat this as a normal user. */ - if ((value & 1) | (value & 2)) { - char newsign[MAX_NAME_LENGTH]; - - if ((value & 2) || (i == client)) { - newsign = name; - } - else { - newsign = sign; - } - - VFormat(szBuffer, sizeof(szBuffer), format, 4); - - MC_PrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer); - } - } - else { - /* Treat this as an admin user */ - bool is_root = id.HasFlag(Admin_Root, Access_Effective); - if ((value & 4) || (value & 8) || ((value & 16) && is_root)) { - char newsign[MAX_NAME_LENGTH]; - - if ((value & 8) || ((value & 16) && is_root) || (i == client)) { - newsign = name; - } - else { - newsign = sign; - } - - VFormat(szBuffer, sizeof(szBuffer), format, 4); - - MC_PrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer); - } - } - } - - return 1; -} - -/** - * Displays usage of an admin command to users depending on the setting of the sm_show_activity cvar. - * All users receive a message in their chat text, except for the originating client, - * who receives the message based on the current ReplySource. - * Supports color tags. - * - * @param client Client index doing the action, or 0 for server. - * @param tags Tag to prepend to the message. - * @param format Formatting rules. - * @param ... Variable number of format parameters. - * @error - */ -stock int MC_ShowActivity2(int client, const char[] tag, const char[] format, any ...) { - if (sm_show_activity == null) { - sm_show_activity = FindConVar("sm_show_activity"); - } - - char szBuffer[MC_MAX_MESSAGE_LENGTH]; - //char szCMessage[MC_MAX_MESSAGE_LENGTH]; - int value = sm_show_activity.IntValue; - // ReplySource replyto = GetCmdReplySource(); - - char name[MAX_NAME_LENGTH] = "Console"; - char sign[MAX_NAME_LENGTH] = "ADMIN"; - - if (client != 0) { - if (client < 0 || client > MaxClients || !IsClientConnected(client)) { - ThrowError("Client index %d is invalid", client); - } - - GetClientName(client, name, sizeof(name)); - - AdminId id = GetUserAdmin(client); - if (id == INVALID_ADMIN_ID || !id.HasFlag(Admin_Generic, Access_Effective)) { - sign = "PLAYER"; - } - - SetGlobalTransTarget(client); - VFormat(szBuffer, sizeof(szBuffer), format, 4); - - /* We don't display directly to the console because the chat text - * simply gets added to the console, so we don't want it to print - * twice. - */ - MC_PrintToChatEx(client, client, "%s%s", tag, szBuffer); - } - else { - SetGlobalTransTarget(LANG_SERVER); - VFormat(szBuffer, sizeof(szBuffer), format, 4); - - MC_RemoveTags(szBuffer, sizeof(szBuffer)); - PrintToServer("%s%s\n", tag, szBuffer); - } - - if (!value) { - return 1; - } - - for (int i = 1; i <= MaxClients; ++i) { - if (!IsClientInGame(i) || IsFakeClient(i) || i == client) { - continue; - } - - AdminId id = GetUserAdmin(i); - SetGlobalTransTarget(i); - if (id == INVALID_ADMIN_ID || !id.HasFlag(Admin_Generic, Access_Effective)) { - /* Treat this as a normal user. */ - if ((value & 1) | (value & 2)) { - char newsign[MAX_NAME_LENGTH]; - - if ((value & 2)) { - newsign = name; - } - else { - newsign = sign; - } - - VFormat(szBuffer, sizeof(szBuffer), format, 4); - - MC_PrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer); - } - } - else { - /* Treat this as an admin user */ - bool is_root = id.HasFlag(Admin_Root, Access_Effective); - if ((value & 4) || (value & 8) || ((value & 16) && is_root)) { - char newsign[MAX_NAME_LENGTH]; - - - if ((value & 8) || ((value & 16) && is_root)) { - newsign = name; - } - else { - newsign = sign; - } - - VFormat(szBuffer, sizeof(szBuffer), format, 4); - - MC_PrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer); - } - } - } - - return 1; -} - -/** - * Determines whether a color name exists - * - * @param color The color name to check - * @return True if the color exists, false otherwise - */ -stock bool CColorExists(const char[] color) { - MC_CheckTrie(); - int temp; - return MC_Trie.GetValue(color, temp); -} - -/** - * Returns the hexadecimal representation of a client's team color (will NOT initialize the trie) - * - * @param client Client to get the team color for - * @return Client's team color in hexadecimal, or green if unknown - * On error/Errors: If the client index passed is invalid or not in game. - */ -stock int CGetTeamColor(int client) { - if (client <= 0 || client > MaxClients) { - ThrowError("Invalid client index %i", client); - } - - if (!IsClientInGame(client)) { - ThrowError("Client %i is not in game", client); - } - - int value; - switch(GetClientTeam(client)) { - case 1: { - value = MCOLOR_GRAY; - } - case 2: { - value = MCOLOR_RED; - } - case 3: { - value = MCOLOR_BLUE; - } - default: { - value = MCOLOR_GREEN; - } - } - - return value; -} - -stock StringMap MC_InitColorTrie() { - StringMap hTrie = new StringMap(); - hTrie.SetValue("aliceblue", 0xF0F8FF); - hTrie.SetValue("allies", 0x4D7942); // same as Allies team in DoD:S - hTrie.SetValue("ancient", 0xEB4B4B); // same as Ancient item rarity in Dota 2 - hTrie.SetValue("antiquewhite", 0xFAEBD7); - hTrie.SetValue("aqua", 0x00FFFF); - hTrie.SetValue("aquamarine", 0x7FFFD4); - hTrie.SetValue("arcana", 0xADE55C); // same as Arcana item rarity in Dota 2 - hTrie.SetValue("axis", 0xFF4040); // same as Axis team in DoD:S - hTrie.SetValue("azure", 0x007FFF); - hTrie.SetValue("beige", 0xF5F5DC); - hTrie.SetValue("bisque", 0xFFE4C4); - hTrie.SetValue("black", 0x000000); - hTrie.SetValue("blanchedalmond", 0xFFEBCD); - hTrie.SetValue("blue", 0x99CCFF); // same as BLU/Counter-Terrorist team color - hTrie.SetValue("blueviolet", 0x8A2BE2); - hTrie.SetValue("brown", 0xA52A2A); - hTrie.SetValue("burlywood", 0xDEB887); - hTrie.SetValue("cadetblue", 0x5F9EA0); - hTrie.SetValue("chartreuse", 0x7FFF00); - hTrie.SetValue("chocolate", 0xD2691E); - hTrie.SetValue("collectors", 0xAA0000); // same as Collector's item quality in TF2 - hTrie.SetValue("common", 0xB0C3D9); // same as Common item rarity in Dota 2 - hTrie.SetValue("community", 0x70B04A); // same as Community item quality in TF2 - hTrie.SetValue("coral", 0xFF7F50); - hTrie.SetValue("cornflowerblue", 0x6495ED); - hTrie.SetValue("cornsilk", 0xFFF8DC); - hTrie.SetValue("corrupted", 0xA32C2E); // same as Corrupted item quality in Dota 2 - hTrie.SetValue("crimson", 0xDC143C); - hTrie.SetValue("cyan", 0x00FFFF); - hTrie.SetValue("darkblue", 0x00008B); - hTrie.SetValue("darkcyan", 0x008B8B); - hTrie.SetValue("darkgoldenrod", 0xB8860B); - hTrie.SetValue("darkgray", 0xA9A9A9); - hTrie.SetValue("darkgrey", 0xA9A9A9); - hTrie.SetValue("darkgreen", 0x006400); - hTrie.SetValue("darkkhaki", 0xBDB76B); - hTrie.SetValue("darkmagenta", 0x8B008B); - hTrie.SetValue("darkolivegreen", 0x556B2F); - hTrie.SetValue("darkorange", 0xFF8C00); - hTrie.SetValue("darkorchid", 0x9932CC); - hTrie.SetValue("darkred", 0x8B0000); - hTrie.SetValue("darksalmon", 0xE9967A); - hTrie.SetValue("darkseagreen", 0x8FBC8F); - hTrie.SetValue("darkslateblue", 0x483D8B); - hTrie.SetValue("darkslategray", 0x2F4F4F); - hTrie.SetValue("darkslategrey", 0x2F4F4F); - hTrie.SetValue("darkturquoise", 0x00CED1); - hTrie.SetValue("darkviolet", 0x9400D3); - hTrie.SetValue("deeppink", 0xFF1493); - hTrie.SetValue("deepskyblue", 0x00BFFF); - hTrie.SetValue("dimgray", 0x696969); - hTrie.SetValue("dimgrey", 0x696969); - hTrie.SetValue("dodgerblue", 0x1E90FF); - hTrie.SetValue("exalted", 0xCCCCCD); // same as Exalted item quality in Dota 2 - hTrie.SetValue("firebrick", 0xB22222); - hTrie.SetValue("floralwhite", 0xFFFAF0); - hTrie.SetValue("forestgreen", 0x228B22); - hTrie.SetValue("frozen", 0x4983B3); // same as Frozen item quality in Dota 2 - hTrie.SetValue("fuchsia", 0xFF00FF); - hTrie.SetValue("fullblue", 0x0000FF); - hTrie.SetValue("fullred", 0xFF0000); - hTrie.SetValue("gainsboro", 0xDCDCDC); - hTrie.SetValue("genuine", 0x4D7455); // same as Genuine item quality in TF2 - hTrie.SetValue("ghostwhite", 0xF8F8FF); - hTrie.SetValue("gold", 0xFFD700); - hTrie.SetValue("goldenrod", 0xDAA520); - hTrie.SetValue("gray", 0xCCCCCC); // same as spectator team color - hTrie.SetValue("grey", 0xCCCCCC); - hTrie.SetValue("green", 0x3EFF3E); - hTrie.SetValue("greenyellow", 0xADFF2F); - hTrie.SetValue("haunted", 0x38F3AB); // same as Haunted item quality in TF2 - hTrie.SetValue("honeydew", 0xF0FFF0); - hTrie.SetValue("hotpink", 0xFF69B4); - hTrie.SetValue("immortal", 0xE4AE33); // same as Immortal item rarity in Dota 2 - hTrie.SetValue("indianred", 0xCD5C5C); - hTrie.SetValue("indigo", 0x4B0082); - hTrie.SetValue("ivory", 0xFFFFF0); - hTrie.SetValue("khaki", 0xF0E68C); - hTrie.SetValue("lavender", 0xE6E6FA); - hTrie.SetValue("lavenderblush", 0xFFF0F5); - hTrie.SetValue("lawngreen", 0x7CFC00); - hTrie.SetValue("legendary", 0xD32CE6); // same as Legendary item rarity in Dota 2 - hTrie.SetValue("lemonchiffon", 0xFFFACD); - hTrie.SetValue("lightblue", 0xADD8E6); - hTrie.SetValue("lightcoral", 0xF08080); - hTrie.SetValue("lightcyan", 0xE0FFFF); - hTrie.SetValue("lightgoldenrodyellow", 0xFAFAD2); - hTrie.SetValue("lightgray", 0xD3D3D3); - hTrie.SetValue("lightgrey", 0xD3D3D3); - hTrie.SetValue("lightgreen", 0x99FF99); - hTrie.SetValue("lightpink", 0xFFB6C1); - hTrie.SetValue("lightsalmon", 0xFFA07A); - hTrie.SetValue("lightseagreen", 0x20B2AA); - hTrie.SetValue("lightskyblue", 0x87CEFA); - hTrie.SetValue("lightslategray", 0x778899); - hTrie.SetValue("lightslategrey", 0x778899); - hTrie.SetValue("lightsteelblue", 0xB0C4DE); - hTrie.SetValue("lightyellow", 0xFFFFE0); - hTrie.SetValue("lime", 0x00FF00); - hTrie.SetValue("limegreen", 0x32CD32); - hTrie.SetValue("linen", 0xFAF0E6); - hTrie.SetValue("magenta", 0xFF00FF); - hTrie.SetValue("maroon", 0x800000); - hTrie.SetValue("mediumaquamarine", 0x66CDAA); - hTrie.SetValue("mediumblue", 0x0000CD); - hTrie.SetValue("mediumorchid", 0xBA55D3); - hTrie.SetValue("mediumpurple", 0x9370D8); - hTrie.SetValue("mediumseagreen", 0x3CB371); - hTrie.SetValue("mediumslateblue", 0x7B68EE); - hTrie.SetValue("mediumspringgreen", 0x00FA9A); - hTrie.SetValue("mediumturquoise", 0x48D1CC); - hTrie.SetValue("mediumvioletred", 0xC71585); - hTrie.SetValue("midnightblue", 0x191970); - hTrie.SetValue("mintcream", 0xF5FFFA); - hTrie.SetValue("mistyrose", 0xFFE4E1); - hTrie.SetValue("moccasin", 0xFFE4B5); - hTrie.SetValue("mythical", 0x8847FF); // same as Mythical item rarity in Dota 2 - hTrie.SetValue("navajowhite", 0xFFDEAD); - hTrie.SetValue("navy", 0x000080); - hTrie.SetValue("normal", 0xB2B2B2); // same as Normal item quality in TF2 - hTrie.SetValue("oldlace", 0xFDF5E6); - hTrie.SetValue("olive", 0x9EC34F); - hTrie.SetValue("olivedrab", 0x6B8E23); - hTrie.SetValue("orange", 0xFFA500); - hTrie.SetValue("orangered", 0xFF4500); - hTrie.SetValue("orchid", 0xDA70D6); - hTrie.SetValue("palegoldenrod", 0xEEE8AA); - hTrie.SetValue("palegreen", 0x98FB98); - hTrie.SetValue("paleturquoise", 0xAFEEEE); - hTrie.SetValue("palevioletred", 0xD87093); - hTrie.SetValue("papayawhip", 0xFFEFD5); - hTrie.SetValue("peachpuff", 0xFFDAB9); - hTrie.SetValue("peru", 0xCD853F); - hTrie.SetValue("pink", 0xFFC0CB); - hTrie.SetValue("plum", 0xDDA0DD); - hTrie.SetValue("powderblue", 0xB0E0E6); - hTrie.SetValue("purple", 0x800080); - hTrie.SetValue("rare", 0x4B69FF); // same as Rare item rarity in Dota 2 - hTrie.SetValue("red", 0xFF4040); // same as RED/Terrorist team color - hTrie.SetValue("rosybrown", 0xBC8F8F); - hTrie.SetValue("royalblue", 0x4169E1); - hTrie.SetValue("saddlebrown", 0x8B4513); - hTrie.SetValue("salmon", 0xFA8072); - hTrie.SetValue("sandybrown", 0xF4A460); - hTrie.SetValue("seagreen", 0x2E8B57); - hTrie.SetValue("seashell", 0xFFF5EE); - hTrie.SetValue("selfmade", 0x70B04A); // same as Self-Made item quality in TF2 - hTrie.SetValue("sienna", 0xA0522D); - hTrie.SetValue("silver", 0xC0C0C0); - hTrie.SetValue("skyblue", 0x87CEEB); - hTrie.SetValue("slateblue", 0x6A5ACD); - hTrie.SetValue("slategray", 0x708090); - hTrie.SetValue("slategrey", 0x708090); - hTrie.SetValue("snow", 0xFFFAFA); - hTrie.SetValue("springgreen", 0x00FF7F); - hTrie.SetValue("steelblue", 0x4682B4); - hTrie.SetValue("strange", 0xCF6A32); // same as Strange item quality in TF2 - hTrie.SetValue("tan", 0xD2B48C); - hTrie.SetValue("teal", 0x008080); - hTrie.SetValue("thistle", 0xD8BFD8); - hTrie.SetValue("tomato", 0xFF6347); - hTrie.SetValue("turquoise", 0x40E0D0); - hTrie.SetValue("uncommon", 0xB0C3D9); // same as Uncommon item rarity in Dota 2 - hTrie.SetValue("unique", 0xFFD700); // same as Unique item quality in TF2 - hTrie.SetValue("unusual", 0x8650AC); // same as Unusual item quality in TF2 - hTrie.SetValue("valve", 0xA50F79); // same as Valve item quality in TF2 - hTrie.SetValue("vintage", 0x476291); // same as Vintage item quality in TF2 - hTrie.SetValue("violet", 0xEE82EE); - hTrie.SetValue("wheat", 0xF5DEB3); - hTrie.SetValue("white", 0xFFFFFF); - hTrie.SetValue("whitesmoke", 0xF5F5F5); - hTrie.SetValue("yellow", 0xFFFF00); - hTrie.SetValue("yellowgreen", 0x9ACD32); - - return hTrie; -} From 4025b2f81aa7b7c35689c4f570f46e23bb68389b Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Wed, 28 Jan 2026 15:41:18 +0100 Subject: [PATCH 052/128] Update workflow --- .github/workflows/nativevotes.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/nativevotes.yml b/.github/workflows/nativevotes.yml index f266dbd..44a36ba 100644 --- a/.github/workflows/nativevotes.yml +++ b/.github/workflows/nativevotes.yml @@ -33,13 +33,13 @@ jobs: shell: bash run: | mkdir -p addons/sourcemod/scripting/include/ripext - curl -o addons/sourcemod/scripting/include/ripext.inc https://github.com/ErikMinekus/sm-ripext/raw/refs/heads/main/pawn/scripting/include/ripext.inc - curl -o addons/sourcemod/scripting/include/ripext/http.inc https://github.com/ErikMinekus/sm-ripext/raw/refs/heads/main/pawn/scripting/include/ripext/http.inc - curl -o addons/sourcemod/scripting/include/ripext/json.inc https://github.com/ErikMinekus/sm-ripext/raw/refs/heads/main/pawn/scripting/include/ripext/json.inc + curl -o addons/sourcemod/scripting/include/ripext.inc https://raw.githubusercontent.com/ErikMinekus/sm-ripext/main/pawn/scripting/include/ripext.inc + curl -o addons/sourcemod/scripting/include/ripext/http.inc https://raw.githubusercontent.com/ErikMinekus/sm-ripext/main/pawn/scripting/include/ripext/http.inc + curl -o addons/sourcemod/scripting/include/ripext/json.inc https://raw.githubusercontent.com/ErikMinekus/sm-ripext/main/pawn/scripting/include/ripext/json.inc mkdir -p addons/sourcemod/scripting/include/multicolors - curl -o addons/sourcemod/scripting/include/multicolors.inc https://raw.githubusercontent.com/Bara/Multi-Colors/refs/heads/master/addons/sourcemod/scripting/include/multicolors.inc - curl -o addons/sourcemod/scripting/include/multicolors/colors.inc https://raw.githubusercontent.com/Bara/Multi-Colors/refs/heads/master/addons/sourcemod/scripting/include/multicolors/colors.inc - curl -o addons/sourcemod/scripting/include/multicolors/morecolors.inc https://raw.githubusercontent.com/Bara/Multi-Colors/refs/heads/master/addons/sourcemod/scripting/include/multicolors/morecolors.inc + curl -o addons/sourcemod/scripting/include/multicolors.inc https://raw.githubusercontent.com/Bara/Multi-Colors/master/addons/sourcemod/scripting/include/multicolors.inc + curl -o addons/sourcemod/scripting/include/multicolors/colors.inc https://raw.githubusercontent.com/Bara/Multi-Colors/master/addons/sourcemod/scripting/include/multicolors/colors.inc + curl -o addons/sourcemod/scripting/include/multicolors/morecolors.inc https://raw.githubusercontent.com/Bara/Multi-Colors/master/addons/sourcemod/scripting/include/multicolors/morecolors.inc - name: Setup SourcePawn 1.13 Compiler uses: rumblefrog/setup-sp@master From da70901ddfee2ce9b1ce29c92f174d67c98e5e2d Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Wed, 28 Jan 2026 16:36:56 +0100 Subject: [PATCH 053/128] Update README.md --- README.md | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index c15426b..3065cf3 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,9 @@ # / NativeVotes — Continued! This fork aims to expand upon [ sapphonie](https://github.com/sapphonie)['s work](https://github.com/sapphonie/sourcemod-nativevotes-updated). +> [!NOTE] +> If you want to benefit from this repository's features without NativeVotes, you may safely unload `nativevotes.smx`. Modules will simply fall back to Radio Menus. + > [!WARNING] > This plugin has only been tested in [ **Team Fortress 2**](https://store.steampowered.com/app/440)‼ If it doesn't work in any other game, open an [issue](https://github.com/Heapons/sourcemod-nativevotes-updated/issues/new). @@ -15,11 +18,13 @@ This fork aims to expand upon [ Team Fortress 2 +- Fixes. + - ✔️/❌ vote counts. + - Proper sounds get played upon selecting an item in a **multi-choice** (≤5 options max.) vote. - Add [`sm_voterp`](https://github.com/Heapons/sourcemod-nativevotes-updated/blob/master/addons/sourcemod/scripting/nativevotes_voterp.sp). - Controls `tf_medieval_autorp` cvar. -- MVM Support. +- Map lists in **Vote Setup** now work in **Mann Vs. Machine** as well. ## [Rock The Vote](https://github.com/Heapons/sourcemod-nativevotes-updated/blob/master/addons/sourcemod/scripting/nativevotes_rockthevote.sp) - Admin commands. @@ -43,7 +48,7 @@ This fork aims to expand upon [ Clean up workshop maps to reduce disk size. +- **[OPTIONAL]** Automatically generate the mapcycle file (requires [Rest In Pawn](https://github.com/srcdslab/sm-ext-ripext)). + - Import Workshop map collections. +> [!WARNING] +> There's currently a bug where leaving `sm_mapcycle_auto` enabled at all times will make all plugins unable to find [`mapcyclefile`](https://developer.valvesoftware.com/wiki/Mapcycle.txt). But if you're still going to leave it on for whatever reason, `sm_reload_nominations` works as a temporary fix (or alternatively, toggle it on then off). + --- |Name|Default Value|Description| |-|-|-| @@ -77,7 +87,4 @@ This fork aims to expand upon [[ **Team Fortress 2**](https://store.steampowered.com/app/440) (or its mods) only| -|`sm_workshop_map_cleanup`|`0`|Specifies whether to automatically cleanup workshop maps on map change
[ **Team Fortress 2**](https://store.steampowered.com/app/440) (or its mods) only| - -> [!WARNING] -> There's currently a bug where leaving `sm_mapcycle_auto` enabled at all times will make all plugins unable to find [`mapcyclefile`](https://developer.valvesoftware.com/wiki/Mapcycle.txt). But if you're still going to leave it on for whatever reason, [`sm_reload_nominations`](https://github.com/Heapons/sourcemod-nativevotes-updated/blob/master/addons/sourcemod/scripting/nativevotes_nominations.sp#L247-L251) works as a temporary fix. \ No newline at end of file +|`sm_workshop_map_cleanup`|`0`|Specifies whether to automatically cleanup workshop maps on map change
[ **Team Fortress 2**](https://store.steampowered.com/app/440) (or its mods) only| \ No newline at end of file From dc390b0c58319c61b9d8f5d1644a4058056317c4 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Fri, 30 Jan 2026 23:09:45 +0100 Subject: [PATCH 054/128] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3065cf3..3498f37 100644 --- a/README.md +++ b/README.md @@ -61,8 +61,8 @@ This fork aims to expand upon [ Clean up workshop maps to reduce disk size. -- **[OPTIONAL]** Automatically generate the mapcycle file (requires [Rest In Pawn](https://github.com/srcdslab/sm-ext-ripext)). - - Import Workshop map collections. +- **[OPTIONAL]** Automatically generate the mapcycle file. + - Import Workshop map collections (requires [Rest In Pawn](https://github.com/srcdslab/sm-ext-ripext) in order to use that feature). > [!WARNING] > There's currently a bug where leaving `sm_mapcycle_auto` enabled at all times will make all plugins unable to find [`mapcyclefile`](https://developer.valvesoftware.com/wiki/Mapcycle.txt). But if you're still going to leave it on for whatever reason, `sm_reload_nominations` works as a temporary fix (or alternatively, toggle it on then off). @@ -87,4 +87,4 @@ This fork aims to expand upon [[ **Team Fortress 2**](https://store.steampowered.com/app/440) (or its mods) only| -|`sm_workshop_map_cleanup`|`0`|Specifies whether to automatically cleanup workshop maps on map change
[ **Team Fortress 2**](https://store.steampowered.com/app/440) (or its mods) only| \ No newline at end of file +|`sm_workshop_map_cleanup`|`0`|Specifies whether to automatically cleanup workshop maps on map change
[ **Team Fortress 2**](https://store.steampowered.com/app/440) (or its mods) only| From 953e097f42cf89d0c921c9a5cb47dc0a7564d7ae Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sat, 31 Jan 2026 01:40:09 +0100 Subject: [PATCH 055/128] Revert commit ea35e22 https://github.com/Heapons/sourcemod-nativevotes-updated/issues/2#issuecomment-3825973400 --- addons/sourcemod/scripting/nativevotes.sp | 5062 ++++++------ .../sourcemod/scripting/nativevotes/game.sp | 6962 +++++++++-------- 2 files changed, 6016 insertions(+), 6008 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes.sp b/addons/sourcemod/scripting/nativevotes.sp index 82c049a..436c9ce 100644 --- a/addons/sourcemod/scripting/nativevotes.sp +++ b/addons/sourcemod/scripting/nativevotes.sp @@ -1,2532 +1,2532 @@ -/** - * vim: set ts=4 : - * ============================================================================= - * NativeVotes - * NativeVotes is a voting API plugin for L4D, L4D2, TF2, and CS:GO. - * Based on the SourceMod voting API - * - * NativeVotes (C) 2011-2015 Ross Bemrose (Powerlord). All rights reserved. - * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved. - * ============================================================================= - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, version 3.0, as published by the - * Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - * - * As a special exception, AlliedModders LLC gives you permission to link the - * code of this program (as well as its derivative works) to "Half-Life 2," the - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software - * by the Valve Corporation. You must obey the GNU General Public License in - * all respects for all other code used. Additionally, AlliedModders LLC grants - * this exception to all derivative works. AlliedModders LLC defines further - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), - * or . - * - * Version: $Id$ - */ - -#include -#include - -#pragma semicolon 1 -#pragma newdecls required - -#include -#include - -EngineVersion g_EngineVersion = Engine_Unknown; - -#include "nativevotes/data-keyvalues.sp" - -#define LOGTAG "NativeVotes" - -#define MAX_VOTE_DETAILS_LENGTH 64 // From SourceSDK2013's shareddefs.h -#define TRANSLATION_LENGTH 192 - -#define VOTE_DELAY_TIME 3.0 - -// SourceMod uses these internally, so... we do too. -#define VOTE_NOT_VOTING -2 -#define VOTE_PENDING -1 - -#define MAX_VOTE_ISSUES 20 -#define VOTE_STRING_SIZE 32 - -//---------------------------------------------------------------------------- -// These values are swapped from their NativeVotes equivalent -#define L4D2_VOTE_YES_INDEX 1 -#define L4D2_VOTE_NO_INDEX 0 - -#define L4DL4D2_COUNT 2 -#define TF2CSGO_COUNT 5 - -#define MAX_CALLVOTE_SIZE 128 - -//#define LOG - -//---------------------------------------------------------------------------- -// Global Variables -int g_NextVote = 0; - -//---------------------------------------------------------------------------- -// CVars -enum -{ - progress_hintbox, - progress_chat, - progress_console, - progress_client_console, - vote_delay, - - MAX_CONVARS -}; - -ConVar g_ConVars[MAX_CONVARS]; - -//---------------------------------------------------------------------------- -// Used to track current vote data -//Handle g_hVoteTimer; -Handle g_hDisplayTimer; - -int g_Clients; -int g_TotalClients; -int g_Items; -ArrayList g_hVotes; -NativeVote g_hCurVote; -int g_curDisplayClient = 0; -char g_newMenuTitle[TRANSLATION_LENGTH]; -int g_curItemClient = 0; -char g_newMenuItem[TRANSLATION_LENGTH]; - -bool g_bStarted; -bool g_bCancelled; -int g_NumVotes; -int g_VoteTime; -int g_VoteFlags; -float g_fStartTime; -int g_TimeLeft; -int g_ClientVotes[MAXPLAYERS+1]; -bool g_bRevoting[MAXPLAYERS+1]; -char g_LeaderList[1024]; - -ConVar sv_vote_holder_may_vote_no; - -// Map list stuffs - -//#define MAP_STRING_CACHE_SIZE PLATFORM_MAX_PATH * 256 - -// Forward -Handle g_OverrideMaps; -StringMap g_MapOverrides; -bool g_OverridesSet; -bool g_OverrideNextCallVote[MAXPLAYERS + 1]; - -enum struct CallVoteForwards -{ - Handle CallVote_Forward; - Handle CallVote_Vis; -} - -enum struct CallVoteListData -{ - NativeVotesOverride CallVoteList_VoteType; - bool CallVoteList_VoteEnabled; -} - -CallVoteForwards g_CallVotes[NativeVotesOverride_Count]; - -#include "nativevotes/game.sp" - -public Plugin myinfo = -{ - name = "NativeVotes", - author = "Powerlord", - description = "Voting API to use the game's native vote panels. Compatible with L4D, L4D2, TF2, and CS:GO.", - version = "26w05a", - url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" -} - -public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) -{ - char engineName[64]; - if (!Game_IsGameSupported(engineName, sizeof(engineName))) - { - Format(error, err_max, "Unsupported game: %s", engineName); - return APLRes_Failure; - } - - CreateNative("NativeVotes_IsVoteTypeSupported", Native_IsVoteTypeSupported); - CreateNative("NativeVotes_Create", Native_Create); - CreateNative("NativeVotes_Close", Native_Close); - CreateNative("NativeVotes_Display", Native_Display); - CreateNative("NativeVotes_AddItem", Native_AddItem); - CreateNative("NativeVotes_InsertItem", Native_InsertItem); - CreateNative("NativeVotes_RemoveItem", Native_RemoveItem); - CreateNative("NativeVotes_RemoveAllItems", Native_RemoveAllItems); - CreateNative("NativeVotes_GetItem", Native_GetItem); - CreateNative("NativeVotes_GetItemCount", Native_GetItemCount); - CreateNative("NativeVotes_SetDetails", Native_SetDetails); - CreateNative("NativeVotes_GetDetails", Native_GetDetails); - CreateNative("NativeVotes_SetTitle", Native_SetTitle); - CreateNative("NativeVotes_GetTitle", Native_GetTitle); - CreateNative("NativeVotes_SetTarget", Native_SetTarget); - CreateNative("NativeVotes_GetTarget", Native_GetTarget); - CreateNative("NativeVotes_GetTargetSteam", Native_GetTargetSteam); - CreateNative("NativeVotes_IsVoteInProgress", Native_IsVoteInProgress); - CreateNative("NativeVotes_GetMaxItems", Native_GetMaxItems); - CreateNative("NativeVotes_SetOptionFlags", Native_SetOptionFlags); - CreateNative("NativeVotes_GetOptionFlags", Native_GetOptionFlags); - CreateNative("NativeVotes_SetNoVoteButton", Native_SetNoVoteButton); - CreateNative("NativeVotes_Cancel", Native_Cancel); - CreateNative("NativeVotes_SetResultCallback", Native_SetResultCallback); - CreateNative("NativeVotes_CheckVoteDelay", Native_CheckVoteDelay); - CreateNative("NativeVotes_IsClientInVotePool", Native_IsClientInVotePool); - CreateNative("NativeVotes_RedrawClientVote", Native_RedrawClientVote); - CreateNative("NativeVotes_GetType", Native_GetType); - CreateNative("NativeVotes_SetTeam", Native_SetTeam); - CreateNative("NativeVotes_GetTeam", Native_GetTeam); - CreateNative("NativeVotes_SetInitiator", Native_SetInitiator); - CreateNative("NativeVotes_GetInitiator", Native_GetInitiator); - CreateNative("NativeVotes_DisplayPass", Native_DisplayPass); - CreateNative("NativeVotes_DisplayPassCustomToOne", Native_DisplayPassCustomToOne); - CreateNative("NativeVotes_DisplayPassEx", Native_DisplayPassEx); - //CreateNative("NativeVotes_DisplayRawPass", Native_DisplayRawPass); - CreateNative("NativeVotes_DisplayRawPassToOne", Native_DisplayRawPassToOne); - CreateNative("NativeVotes_DisplayRawPassCustomToOne", Native_DisplayRawPassCustomToOne); - CreateNative("NativeVotes_DisplayFail", Native_DisplayFail); - CreateNative("NativeVotes_DisplayRawFail", Native_DisplayRawFail); - //CreateNative("NativeVotes_DisplayRawFailToOne", Native_DisplayRawFailToOne); - CreateNative("NativeVotes_AreVoteCommandsSupported", Native_AreVoteCommandsSupported); - CreateNative("NativeVotes_RegisterVoteCommand", Native_RegisterVoteCommand); - CreateNative("NativeVotes_UnregisterVoteCommand", Native_UnregisterVoteCommand); - CreateNative("NativeVotes_DisplayCallVoteFail", Native_DisplayCallVoteFail); - CreateNative("NativeVotes_RedrawVoteTitle", Native_RedrawVoteTitle); - CreateNative("NativeVotes_RedrawVoteItem", Native_RedrawVoteItem); - - // Transitional syntax support - CreateNative("NativeVote.NativeVote", Native_Create); - CreateNative("NativeVote.Close", Native_Close); - CreateNative("NativeVote.AddItem", Native_AddItem); - CreateNative("NativeVote.InsertItem", Native_InsertItem); - CreateNative("NativeVote.RemoveItem", Native_RemoveItem); - CreateNative("NativeVote.RemoveAllItems", Native_RemoveAllItems); - CreateNative("NativeVote.GetItem", Native_GetItem); - CreateNative("NativeVote.SetDetails", Native_SetDetails); - CreateNative("NativeVote.GetDetails", Native_GetDetails); - CreateNative("NativeVote.SetTitle", Native_SetTitle); - CreateNative("NativeVote.GetTitle", Native_GetTitle); - CreateNative("NativeVote.SetTarget", Native_SetTarget); - CreateNative("NativeVote.GetTarget", Native_GetTarget); - CreateNative("NativeVote.GetTargetSteam", Native_GetTargetSteam); - CreateNative("NativeVote.DisplayVote", Native_Display); - CreateNative("NativeVote.DisplayPass", Native_DisplayPass); - CreateNative("NativeVote.DisplayPassCustomToOne", Native_DisplayPassCustomToOne); - CreateNative("NativeVote.DisplayPassEx", Native_DisplayPassEx); - CreateNative("NativeVote.DisplayFail", Native_DisplayFail); - CreateNative("NativeVote.OptionFlags.set", Native_SetOptionFlags); - CreateNative("NativeVote.OptionFlags.get", Native_GetOptionFlags); - CreateNative("NativeVote.NoVoteButton.set", Native_SetNoVoteButton); - CreateNative("NativeVote.VoteResultCallback.set", Native_SetResultCallback); - CreateNative("NativeVote.ItemCount.get", Native_GetItemCount); - CreateNative("NativeVote.VoteType.get", Native_GetType); - CreateNative("NativeVote.Team.set", Native_SetTeam); - CreateNative("NativeVote.Team.get", Native_GetTeam); - CreateNative("NativeVote.Initiator.set", Native_SetInitiator); - CreateNative("NativeVote.Initiator.get", Native_GetInitiator); - - RegPluginLibrary("nativevotes"); - - return APLRes_Success; -} - -public void OnPluginStart() -{ - LoadTranslations("core.phrases"); - LoadTranslations("nativevotes.phrases.txt"); - - g_ConVars[progress_hintbox] = CreateConVar("nativevotes_progress_hintbox", "0", "Show current vote progress in a hint box", FCVAR_NONE, true, 0.0, true, 1.0); - g_ConVars[progress_chat] = CreateConVar("nativevotes_progress_chat", "0", "Show current vote progress as chat messages", FCVAR_NONE, true, 0.0, true, 1.0); - g_ConVars[progress_console] = CreateConVar("nativevotes_progress_console", "0", "Show current vote progress as console messages", FCVAR_NONE, true, 0.0, true, 1.0); - g_ConVars[progress_client_console] = CreateConVar("nativevotes_progress_client_console", "0", "Show current vote progress as console messages to clients", FCVAR_NONE, true, 0.0, true, 1.0); - g_ConVars[vote_delay] = CreateConVar("nativevotes_vote_delay", "30", "Sets the recommended time in between public votes", FCVAR_NONE, true, 0.0); - g_ConVars[vote_delay].AddChangeHook(OnVoteDelayChange); - - Game_InitializeCvars(); - - AddCommandListener(Command_Vote, "vote"); // All games, command listeners aren't case sensitive - - sv_vote_holder_may_vote_no = FindConVar("sv_vote_holder_may_vote_no"); - - // The new version of the CallVote system is TF2 only - if (Game_AreVoteCommandsSupported()) - { - AddCommandListener(Command_CallVote, "callvote"); - - // None is type 0, which has no overrides - // As of 2015-09-28, there are 10 votes for a total of 20 private forwards created here. - for (int i = 1; i < sizeof(g_CallVotes); i++) - { - g_CallVotes[i].CallVote_Forward = CreateForward(ET_Hook, Param_Cell, Param_Cell, Param_String, Param_Cell, Param_Cell); - g_CallVotes[i].CallVote_Vis = CreateForward(ET_Hook, Param_Cell, Param_Cell); - } - - g_OverrideMaps = CreateGlobalForward("NativeVotes_OverrideMaps", ET_Hook, Param_Cell); - } - - g_hVotes = new ArrayList(1, Game_GetMaxItems()); - - AutoExecConfig(true, "nativevotes"); -} - -public void OnMapStart() -{ - // Map list stuffs - if (g_MapOverrides != null) - delete g_MapOverrides; - - g_OverridesSet = false; - g_NextVote = 0; -} - -public Action Timer_RetryCallVote(Handle timer, any data) -{ - int client = GetClientOfUserId(data); - - if (client == 0) - { - return Plugin_Stop; - } - - FakeClientCommand(client, "callvote"); - return Plugin_Stop; -} - -public void ProcessMapList() -{ - char stringTable[64] = "ServerMapCycle"; - if (FindSendPropInfo("CTFGameRulesProxy", "m_bPlayingMannVsMachine") > 0 && GameRules_GetProp("m_bPlayingMannVsMachine")) - { - Format(stringTable, sizeof(stringTable), "%sMvM", stringTable); - } - - int stringTableIndex = FindStringTable(stringTable); - int stringIndex = FindStringIndex(stringTableIndex, stringTable); - - // Maplist resets every map - int length = GetStringTableDataLength(stringTableIndex, stringIndex); - char[] mapData = new char[length]; - GetStringTableData(stringTableIndex, stringIndex, mapData, length); - - // We'll get an extra blank entry if we don't do this - TrimString(mapData); - - StringMap overrideList = new StringMap(); - ExplodeStringToStringMap(mapData, "\n", overrideList, PLATFORM_MAX_PATH, ImplodePart_Key); - - Action mapResult = Plugin_Continue; - Call_StartForward(g_OverrideMaps); - Call_PushCell(overrideList); - Call_Finish(mapResult); - - if (mapResult != Plugin_Changed || overrideList.Size <= 0) - { - delete overrideList; - return; - } - -#if defined LOG - LogMessage("Overriding map list with %d maps", overrideList.Size); -#endif - - g_MapOverrides = overrideList; - - int maxLength = GetStringMapImplodeSize(overrideList, 1, ImplodePart_Key); - - char[] newMapData = new char[maxLength]; - int newLength = ImplodeStringMapToString(overrideList, "\n", newMapData, maxLength, ImplodePart_Key); - if (newLength < maxLength && newMapData[newLength] != '\n') - { - // do this to avoid a StrCat - newLength += strcopy(newMapData[newLength], maxLength, "\n") + 1; - } - - SetStringTableData(stringTableIndex, stringIndex, newMapData, newLength); -} - -public void OnClientDisconnect_Post(int client) -{ - if (!Internal_IsVoteInProgress() || !Internal_IsClientInVotePool(client)) - { - return; - } - - /* Wipe out their vote if they had one. We have to make sure the - * newly connected client is not allowed to vote. - */ - int item = g_ClientVotes[client]; - if (item >= VOTE_PENDING) - { - if (item > VOTE_PENDING) - { - g_hVotes.Set(item, g_hVotes.Get(item) - 1); - } - - g_ClientVotes[client] = VOTE_NOT_VOTING; - } - - CancelClientVote(g_hCurVote, client, MenuCancel_Disconnected); -} - -void CancelClientVote(NativeVote vote, int client, int reason) -{ - OnCancel(vote, client, reason); - OnClientEnd(); -} - -public Action Command_CallVote(int client, const char[] command, int argc) -{ - if (g_OverrideNextCallVote[client]) - { - g_OverrideNextCallVote[client] = false; - return Plugin_Continue; - } - - if (Internal_IsVoteInProgress() || Game_IsVoteInProgress() || !IsClientInGame(client)) - { - return Plugin_Handled; - } - - Action result = Plugin_Continue; - - switch (argc) - { - // No args means that we need to return a CallVoteSetup usermessage - case 0: - { - if (!g_OverridesSet && Game_AreVoteCommandsSupported()) - { - g_OverridesSet = true; - ProcessMapList(); - -#if defined LOG - LogMessage("Delaying to allow stringtable time to network"); -#endif - // Force it to reissue the callvote command so that the map list gets networked - CreateTimer(0.1, Timer_RetryCallVote, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE); - return Plugin_Handled; - } - - ArrayList hVoteTypes = new ArrayList(sizeof(CallVoteListData)); // Stores arrays of CallVoteListData - - Game_AddDefaultVotes(hVoteTypes); - - // Add our overridden votes to the system - bool overridesPresent = false; - for (int i = 1; i < sizeof(g_CallVotes); i++) - { - if (GetForwardFunctionCount(g_CallVotes[i].CallVote_Forward) > 0) - { -#if defined LOG - LogMessage("Found overrides for vote type: %d", i); -#endif - overridesPresent = true; - CallVoteListData voteType; - - int pos = FindVoteInArray(hVoteTypes, view_as(i)); - if (pos > -1) - { - hVoteTypes.GetArray(pos, voteType, sizeof(CallVoteListData)); - voteType.CallVoteList_VoteEnabled = true; -#if defined LOG - LogMessage("Forcing vote type to visible: %d", i); -#endif - hVoteTypes.SetArray(pos, voteType); - } - else - { -#if defined LOG - LogMessage("Creating override for vote type: %d", i); -#endif - voteType.CallVoteList_VoteType = view_as(i); - voteType.CallVoteList_VoteEnabled = true; - hVoteTypes.PushArray(voteType); - } - } - } - - if (overridesPresent) - { - PerformVisChecks(client, hVoteTypes); -#if defined LOG - LogMessage("Overriding VoteSetup message"); -#endif - Game_DisplayVoteSetup(client, hVoteTypes); - delete hVoteTypes; - return Plugin_Handled; - } - else - { - delete hVoteTypes; - return Plugin_Continue; - } - } - - default: - { - char voteCommand[VOTE_STRING_SIZE]; - GetCmdArg(1, voteCommand, VOTE_STRING_SIZE); - -#if defined LOG - LogMessage("User is attempting to call %s", voteCommand); -#endif - - NativeVotesOverride overrideType = Game_VoteStringToVoteOverride(voteCommand); - - char argument[PLATFORM_MAX_PATH]; - - if (GetForwardFunctionCount(g_CallVotes[overrideType].CallVote_Forward) == 0) - { - - if (g_MapOverrides != null && - (overrideType == NativeVotesOverride_ChgLevel || - overrideType == NativeVotesOverride_NextLevel)) - { - char map[PLATFORM_MAX_PATH]; - - GetCmdArg(2, map, sizeof(map)); - g_MapOverrides.GetString(map, argument, sizeof(argument)); - - g_OverrideNextCallVote[client] = true; - FakeClientCommandEx(client, "callvote %s %s", voteCommand, argument); - return Plugin_Handled; - } - -#if defined LOG - LogMessage("We don't have a handler for %s, passing back to server", voteCommand); -#endif - return Plugin_Continue; - } - - // Vis checks are done here just in case something went wrong and the vote option was shown to a person it shouldn't be. -#if defined LOG - LogMessage("Calling visForward for %s", voteCommand); -#endif - Call_StartForward(g_CallVotes[overrideType].CallVote_Vis); - Call_PushCell(client); - Call_PushCell(overrideType); - Call_Finish(result); - if (result >= Plugin_Handled) - { - return result; - } - - NativeVotesType voteType = Game_VoteStringToVoteType(voteCommand); - - int target = 0; - - NativeVotesKickType kickType = NativeVotesKickType_None; - - switch (voteType) - { - case NativeVotesType_Kick: - { - char param1[20]; - GetCmdArg(2, param1, sizeof(param1)); - - kickType = Game_GetKickType(param1, target); - - int targetClient = GetClientOfUserId(target); - - if (targetClient < 1 || targetClient > MaxClients || !IsClientInGame(targetClient)) - { - return Plugin_Continue; - } - - GetClientName(targetClient, argument, sizeof(argument)); - } - case NativeVotesType_ChgLevel, NativeVotesType_NextLevel: - { - if (g_MapOverrides == null) - { - GetCmdArg(2, argument, sizeof(argument)); - } - else - { - char map[PLATFORM_MAX_PATH]; - - GetCmdArg(2, map, sizeof(map)); - g_MapOverrides.GetString(map, argument, sizeof(argument)); - } - } - default: - { - GetCmdArg(2, argument, sizeof(argument)); - } - } - -#if defined LOG - LogMessage("Calling callVoteForward for %s", voteCommand); -#endif - - Call_StartForward(g_CallVotes[overrideType].CallVote_Forward); - Call_PushCell(client); - Call_PushCell(overrideType); - Call_PushString(argument); - Call_PushCell(kickType); - Call_PushCell(target); - Call_Finish(result); - } - } - - // Default to continue if we're not processing things - return result; - -} - -stock int FindVoteInArray(ArrayList myArray, NativeVotesOverride value) -{ - int size = myArray.Length; - for (int i = 0; i < size; i++) - { - CallVoteListData voteData; - myArray.GetArray(i, voteData, sizeof(CallVoteListData)); - - if (voteData.CallVoteList_VoteType == value) - { - return i; - } - } - - return -1; -} - -stock bool IsVoteEnabled(ArrayList myArray, NativeVotesOverride value) -{ - int pos = FindVoteInArray(myArray, value); - if (pos > -1) - { - CallVoteListData voteType; - myArray.GetArray(pos, voteType, sizeof(CallVoteListData)); - if (voteType.CallVoteList_VoteEnabled) - return true; - } - return false; -} - -public void OnVoteDelayChange(ConVar convar, const char[] oldValue, const char[] newValue) -{ - /* See if the new vote delay isn't something we need to account for */ - if (convar.IntValue < 1) - { - g_NextVote = 0; - return; - } - - /* If there was never a last vote, ignore this change */ - if (g_NextVote <= 0) - { - return; - } - - /* Subtract the original value, then add the new one. */ - g_NextVote -= StringToInt(oldValue); - g_NextVote += StringToInt(newValue); -} - -public void OnMapEnd() -{ - if (g_hCurVote != null) - { - // Cancel the ongoing vote, but don't close the handle, as the other plugins may still re-use it - CancelVoting(); - //OnVoteCancel(g_hCurVote, VoteCancel_Generic); - g_hCurVote = null; - } - - if (g_hDisplayTimer != null) - { - delete g_hDisplayTimer; - } - -// g_hVoteTimer = INVALID_HANDLE; -} - -public Action Command_Vote(int client, const char[] command, int argc) -{ -#if defined LOG - char voteString[128]; - GetCmdArgString(voteString, sizeof(voteString)); - LogMessage("Client %N ran a vote command: %s", client, voteString); -#endif - - // If we're not running a vote, return the vote control back to the server - if (!Internal_IsVoteInProgress() || g_ClientVotes[client] != VOTE_PENDING) - { - return Plugin_Continue; - } - - char option[64]; - GetCmdArgString(option, sizeof(option)); - - int item = Game_ParseVote(option); - - // Make sure we don't go out of bounds on the vote - if (item == NATIVEVOTES_VOTE_INVALID || item > g_Items) - { - return Plugin_Handled; - } - - bool cancel; - - if (Data_GetFlags(g_hCurVote) & MENUFLAG_BUTTON_NOVOTE && item == 0) - { - cancel = true; - } - - if (cancel) - { - OnCancel(g_hCurVote, client, MenuCancel_Exit); - } - else - { - OnVoteSelect(g_hCurVote, client, item); - } - - OnClientEnd(); - - return Plugin_Handled; -} - -void OnVoteSelect(NativeVote vote, int client, int item) -{ - if (Internal_IsVoteInProgress() && g_ClientVotes[client] == VOTE_PENDING) - { - /* Check by our item count, NOT the vote array size */ - if (item < g_Items) - { - Game_ClientSelectedItem(vote, client, item); - - g_ClientVotes[client] = item; - g_hVotes.Set(item, g_hVotes.Get(item) + 1); - g_NumVotes++; - - Game_UpdateVoteCounts(g_hVotes, g_TotalClients); - - if (g_ConVars[progress_chat].BoolValue || g_ConVars[progress_console].BoolValue || g_ConVars[progress_client_console].BoolValue) - { - char choice[128]; - char name[MAX_NAME_LENGTH+1]; - Data_GetItemDisplay(vote, item, choice, sizeof(choice)); - - GetClientName(client, name, MAX_NAME_LENGTH); - - if (g_ConVars[progress_console].BoolValue) - { - PrintToServer("[%s] %T", LOGTAG, "Voted For", LANG_SERVER, name, choice); - } - - if (g_ConVars[progress_chat].BoolValue || g_ConVars[progress_client_console].BoolValue) - { - char phrase[30]; - - if (g_bRevoting[client]) - { - strcopy(phrase, sizeof(phrase), "Changed Vote"); - } - else - { - strcopy(phrase, sizeof(phrase), "Voted For"); - } - - if (g_ConVars[progress_chat].BoolValue) - { - int r, g, b, a, color; - GetEntityRenderColor(client, r, g, b, a); - color = (r << 16) | (g << 8) | b; - if (color != 0xFFFFFF) - { - Format(name, sizeof(name), "{#%06X}%N\x01", color, client); - } - else - { - Format(name, sizeof(name), "{teamcolor}%N\x01", client); - } - CPrintToChatAllEx(client, "[%s] %t", LOGTAG, phrase, name, choice); - } - - if (g_ConVars[progress_client_console].BoolValue) - { - for (int i = 1; i <= MaxClients; i++) - { - if (IsClientInGame(i) && !IsFakeClient(i)) - { - PrintToConsole(i, "[%s] %t", LOGTAG, phrase, name, choice); - } - } - } - } - } - - BuildVoteLeaders(); - DrawHintProgress(); - - OnSelect(g_hCurVote, client, item); - } - } -} - -//MenuAction_Select -void OnSelect(NativeVote vote, int client, int item) -{ - MenuAction actions = Data_GetActions(vote); - if (actions & MenuAction_Select) - { - DoAction(vote, MenuAction_Select, client, item); - } -} - -//MenuAction_End -void OnEnd(NativeVote vote, int item) -{ - // Always called - DoAction(vote, MenuAction_End, item, 0); -} - - -stock void OnVoteEnd(NativeVote vote, int item) -{ - // Always called - DoAction(vote, MenuAction_VoteEnd, item, 0); -} - -void OnVoteStart(NativeVote vote) -{ - // Fire both Start and VoteStart in the other plugin. - - MenuAction actions = Data_GetActions(vote); - if (actions & MenuAction_Start) - { - DoAction(vote, MenuAction_Start, 0, 0); - } - - // Always called - DoAction(vote, MenuAction_VoteStart, 0, 0); -} - -void OnVoteCancel(NativeVote vote, int reason) -{ - // Always called - DoAction(vote, MenuAction_VoteCancel, reason, 0); -} - -void OnCancel(NativeVote vote, int client, int reason) -{ - DoAction(vote, MenuAction_Cancel, client, reason); -} - -void OnClientEnd() -{ - DecrementPlayerCount(); -} - -Action DoAction(NativeVote vote, MenuAction action, int param1, int param2, Action def_res = Plugin_Continue) -{ - Action res = def_res; - - Handle handler = CloneHandle(Data_GetHandler(vote)); -#if defined LOG - LogMessage("Calling Menu forward for vote: %d, handler: %d, action: %d, param1: %d, param2: %d", vote, handler, action, param1, param2); -#endif - Call_StartForward(handler); - Call_PushCell(vote); - Call_PushCell(action); - Call_PushCell(param1); - Call_PushCell(param2); - Call_Finish(res); - delete handler; - return res; -} - -void OnVoteResults(NativeVote vote, const int[][] votes, int num_votes, int item_count, const int[][] client_list, int num_clients) -{ - Handle resultsHandler = Data_GetResultCallback(vote); - - if (resultsHandler == null || !GetForwardFunctionCount(resultsHandler)) - { - /* Call MenuAction_VoteEnd instead. See if there are any extra winners. */ - int num_items = 1; - for (int i = 1; i < num_votes; i++) - { - if (votes[i][VOTEINFO_ITEM_VOTES] != votes[0][VOTEINFO_ITEM_VOTES]) - { - break; - } - num_items++; - } - - /* See if we need to pick a random winner. */ - int winning_item; - if (num_items > 1) - { - /* Yes, we do */ - winning_item = GetRandomInt(0, num_items - 1); - winning_item = votes[winning_item][VOTEINFO_ITEM_INDEX]; - } - else - { - /* No, take the first */ - winning_item = votes[0][VOTEINFO_ITEM_INDEX]; - } - - int winning_votes = votes[0][VOTEINFO_ITEM_VOTES]; - - DoAction(vote, MenuAction_VoteEnd, winning_item, (num_votes << 16) | (winning_votes & 0xFFFF)); - } - else - { - // This code is quite different than its C++ version, as we're reversing the logic previously done - - int[] client_indexes = new int[num_clients]; - int[] client_items = new int[num_clients]; - int[] vote_items = new int[item_count]; - int[] vote_votes = new int[item_count]; - - /* First array */ - for (int i = 0; i < item_count; i++) - { - vote_items[i] = votes[i][VOTEINFO_ITEM_INDEX]; - vote_votes[i] = votes[i][VOTEINFO_ITEM_VOTES]; - } - - /* Second array */ - for (int i = 0; i < num_clients; i++) - { - client_indexes[i] = client_list[i][VOTEINFO_CLIENT_INDEX]; - client_items[i] = client_list[i][VOTEINFO_CLIENT_ITEM]; - } - -#if defined LOG - LogMessage("Calling results forward for vote: %d, num_votes: %d, num_clients: %d, item_count: %d", vote, num_votes, num_clients, item_count); -#endif - - Call_StartForward(resultsHandler); - Call_PushCell(vote); - Call_PushCell(num_votes); - Call_PushCell(num_clients); - Call_PushArray(client_indexes, num_clients); - Call_PushArray(client_items, num_clients); - Call_PushCell(item_count); - Call_PushArray(vote_items, item_count); - Call_PushArray(vote_votes, item_count); - Call_Finish(); - } -} - -/* -VoteEnd(Handle:vote) -{ - if (g_NumVotes == 0) - { - // Fire VoteCancel in the other plugin - OnVoteCancel(vote, VoteCancel_NoVotes); - } - else - { - new num_items; - new num_votes; - - new slots = Game_GetMaxItems(); - new votes[slots][2]; - - Internal_GetResults(votes, slots); - - if (!SendResultCallback(vote, num_votes, num_items, votes)) - { - Handle handler = Data_GetHandler(g_hCurVote); - - Call_StartForward(handler); - Call_PushCell(g_CurVote); - Call_PushCell(MenuAction_VoteEnd); - Call_PushCell(votes[0][VOTEINFO_ITEM_INDEX]); - Call_PushCell(0); - Call_Finish(); - } - } - -} - -bool:SendResultCallback(Handle:vote, num_votes, num_items, const votes[][]) -{ - Handle voteResults = Data_GetResultCallback(g_CurVote); - if (GetForwardFunctionCount(voteResults) == 0) - { - return false; - } - - // This block is present because we can't pass 2D arrays to other plugins' functions - new item_indexes[]; - new item_votes[]; - - for (int i = 0, i < num_items; i++) - { - item_indexes[i] = votes[i][VOTEINFO_ITEM_INDEX]; - item_votes[i] = votes[i][VOTEINFO_ITEM_VOTES]; - } - - // Client block - new client_indexes[MaxClients]; - new client_votes[MaxClients]; - - new num_clients; - for (new i = 1; i <= MaxClients; i++) - { - if (g_ClientVotes[i] > VOTE_PENDING) - { - client_indexes[num_clients] = i; - client_votes[num_clients] = g_ClientVotes[i]; - num_clients++; - } - } - - Call_StartForward(voteResults); - Call_PushCell(_:vote); - Call_PushCell(num_votes); - Call_PushCell(num_clients); - Call_PushArray(client_indexes, num_clients); - Call_PushArray(client_votes, num_clients); - Call_PushCell(num_items); - Call_PushArray(item_indexes, num_items); - Call_PushArray(item_votes, num_items); - Call_Finish(); - - return true; -} -*/ - -void DrawHintProgress() -{ - if (!g_ConVars[progress_hintbox].BoolValue || NativeVotes_GetType(g_hCurVote) == NativeVotesType_Custom_YesNo) - { - return; - } - - float timeRemaining = (g_fStartTime + g_VoteTime) - GetGameTime(); - - if (timeRemaining < 0.0) - { - timeRemaining = 0.0; - } - - int iTimeRemaining = RoundFloat(timeRemaining); - - PrintHintTextToAll("%t%s", "Vote Count", g_NumVotes, g_TotalClients, iTimeRemaining, g_LeaderList); -} - -void BuildVoteLeaders() -{ - if (g_NumVotes == 0 || !g_ConVars[progress_hintbox].BoolValue) - { - return; - } - - // Since we can't have structs, we get "struct" with this instead - - int slots = Game_GetMaxItems(); - int[][] votes = new int[slots][2]; - - int num_items = Internal_GetResults(votes); - - /* Take the top 3 (if applicable) and draw them */ - g_LeaderList[0] = '\0'; - - for (int i = 0; i < num_items && i < 3; i++) - { - int cur_item = votes[i][VOTEINFO_ITEM_INDEX]; - char choice[256]; - Data_GetItemDisplay(g_hCurVote, cur_item, choice, sizeof(choice)); - Format(g_LeaderList, sizeof(g_LeaderList), "%s\n%i. %s: (%i)", g_LeaderList, i+1, choice, votes[i][VOTEINFO_ITEM_VOTES]); - } - -} - -public int SortVoteItems(int[] a, int[] b, const int[][] array, Handle hndl) -{ - if (b[VOTEINFO_ITEM_VOTES] == a[VOTEINFO_ITEM_VOTES]) - { - return 0; - } - else if (b[VOTEINFO_ITEM_VOTES] > a[VOTEINFO_ITEM_VOTES]) - { - return 1; - } - else - { - return -1; - } -} - -void DecrementPlayerCount() -{ - g_Clients--; - - // The vote is running and we have no clients left, so end the vote. - if (g_bStarted && g_Clients == 0) - { - EndVoting(); - } - -} - - -void EndVoting() -{ - int voteDelay = g_ConVars[vote_delay].IntValue; - if (voteDelay < 1) - { - g_NextVote = 0; - } - else - { - g_NextVote = GetTime() + voteDelay; - } - - if (g_hDisplayTimer != null) - { - delete g_hDisplayTimer; - } - - if (g_bCancelled) - { - /* If we were cancelled, don't bother tabulating anything. - * Reset just in case someone tries to redraw, which means - * we need to save our states. - */ - NativeVote vote = g_hCurVote; - Internal_Reset(true); - OnVoteCancel(vote, VoteCancel_Generic); - OnEnd(vote, MenuEnd_VotingCancelled); - return; - } - - int slots = Game_GetMaxItems(); - int[][] votes = new int[slots][2]; - int num_votes; - int num_items = Internal_GetResults(votes, num_votes); - - if (!num_votes) - { - NativeVote vote = g_hCurVote; - Internal_Reset(); - OnVoteCancel(vote, VoteCancel_NoVotes); - OnEnd(vote, MenuEnd_VotingCancelled); - return; - } - - int[][] client_list = new int[MaxClients][2]; - int num_clients = Internal_GetClients(client_list); - - /* Save states, then clear what we've saved. - * This makes us re-entrant, which is always the safe way to go. - */ - NativeVote vote = g_hCurVote; - Internal_Reset(); - -#if defined LOG - LogMessage("Voting done"); -#endif - - /* Send vote info */ - OnVoteResults(vote, votes, num_votes, num_items, client_list, num_clients); - OnEnd(vote, MenuEnd_VotingDone); -} - -bool StartVote(NativeVote vote, int num_clients, int[] clients, int max_time, int flags) -{ - if (!InitializeVoting(vote, max_time, flags)) - { - return false; - } - - /* Due to hibernating servers, we no longer use GameTime, but instead standard timestamps. - */ - - int voteDelay = g_ConVars[vote_delay].IntValue; - if (voteDelay < 1) - { - g_NextVote = 0; - } - else - { - /* This little trick break for infinite votes! - * However, we just ignore that since those 1) shouldn't exist and - * 2) people must be checking IsVoteInProgress() beforehand anyway. - */ - g_NextVote = GetTime() + voteDelay + max_time; - } - - g_fStartTime = GetGameTime(); - g_VoteTime = max_time; - g_TimeLeft = max_time; - - int clientCount = 0; - - for (int i = 0; i < num_clients; ++i) - { - if (clients[i] < 1 || clients[i] > MaxClients) - { - continue; - } - - g_ClientVotes[clients[i]] = VOTE_PENDING; - clientCount++; - } - - g_Clients = clientCount; - - Game_UpdateVoteCounts(g_hVotes, clientCount); - - DoClientVote(vote, clients, num_clients); - - StartVoting(); - - DrawHintProgress(); - - return true; -} - -bool DoClientVote(NativeVote vote, int[] clients, int num_clients) -{ - int totalPlayers = 0; - int[] realClients = new int[MaxClients+1]; - - for (int i = 0; i < num_clients; ++i) - { - if (clients[i] < 1 || clients[i] > MaxClients || !IsClientInGame(clients[i]) || IsFakeClient(clients[i])) - { - continue; - } - - realClients[totalPlayers++] = clients[i]; - } - - if (totalPlayers > 0) - { - Game_DisplayVote(vote, realClients, totalPlayers); - return true; - } - else - { - return false; - } -} - -bool InitializeVoting(NativeVote vote, int time, int flags) -{ - if (Internal_IsVoteInProgress()) - { - return false; - } - - Internal_Reset(); - - /* Mark all clients as not voting */ - for (int i = 1; i <= MaxClients; ++i) - { - g_ClientVotes[i] = VOTE_NOT_VOTING; - g_bRevoting[i] = false; - } - - g_Items = Data_GetItemCount(vote); - - // Clear all items - for (int i = 0; i < g_hVotes.Length; ++i) - { - g_hVotes.Set(i, 0); - } - - g_hCurVote = vote; - g_VoteTime = time; - g_VoteFlags = flags; - - return true; -} - -void StartVoting() -{ - if (g_hCurVote == null) - { - return; - } - - g_bStarted = true; - - OnVoteStart(g_hCurVote); - - g_hDisplayTimer = CreateTimer(1.0, DisplayTimer, _, TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE); - - g_TotalClients = g_Clients; - - /* By now we know how many clients were set. - * If there are none, we should end IMMEDIATELY. - */ - if (g_Clients == 0) - { - EndVoting(); - return; - } - - // Kick targets automatically vote no if they're in the pool - NativeVotesType voteType = Data_GetType(g_hCurVote); - - switch (voteType) - { - case NativeVotesType_Kick, NativeVotesType_KickCheating, NativeVotesType_KickIdle, NativeVotesType_KickScamming: - { - int target = Data_GetTarget(g_hCurVote); - - if (target > 0 && target <= MaxClients && IsClientConnected(target) && Internal_IsClientInVotePool(target)) - { - Game_VoteNo(target); - } - } - } - - // Initiators always vote yes when they're in the pool. - if (voteType != NativeVotesType_Custom_Mult && voteType != NativeVotesType_NextLevelMult) - { - int initiator = Data_GetInitiator(g_hCurVote); - - if (initiator > 0 && initiator <= MaxClients && IsClientConnected(initiator) && Internal_IsClientInVotePool(initiator)) - { - Game_VoteYes(initiator); - } - } -} - -public Action DisplayTimer(Handle timer) -{ - if (!Internal_IsVoteInProgress() || g_TimeLeft <= 0) - { - if (g_hDisplayTimer != null) - { - g_hDisplayTimer = null; - EndVoting(); - } - return Plugin_Stop; - } - DrawHintProgress(); - --g_TimeLeft; - return Plugin_Continue; -} - -int Internal_GetResults(int[][] votes, int &num_votes=0) -{ - if (!Internal_IsVoteInProgress()) - { - return 0; - } - - // Since we can't have structs, we get "struct" with this instead - int num_items; - - num_votes = 0; - - for (int i = 0; i < g_Items; i++) - { - int voteCount = g_hVotes.Get(i); - if (voteCount > 0) - { - votes[num_items][VOTEINFO_ITEM_INDEX] = i; - votes[num_items][VOTEINFO_ITEM_VOTES] = voteCount; - num_votes += voteCount; - num_items++; - } - } - - /* Sort the item list descending like we promised */ - SortCustom2D(votes, num_items, SortVoteItems); - - return num_items; -} - -int Internal_GetClients(int[][] client_vote) -{ - if (!Internal_IsVoteInProgress()) - { - return 0; - } - - /* Build the client list */ - int num_clients; - - for (int i = 1; i <= MaxClients; i++) - { - if (g_ClientVotes[i] >= VOTE_PENDING) - { - client_vote[num_clients][VOTEINFO_CLIENT_INDEX] = i; - client_vote[num_clients][VOTEINFO_CLIENT_ITEM] = g_ClientVotes[i]; - num_clients++; - } - } - - return num_clients; -} - -bool Internal_IsCancelling() -{ - return g_bCancelled; -} - -stock NativeVote Internal_GetCurrentVote() -{ - return g_hCurVote; -} - -void Internal_Reset(bool cancel=false) -{ - g_Clients = 0; - g_Items = 0; - g_bStarted = false; - g_hCurVote = null; - g_NumVotes = 0; - g_bCancelled = false; - g_LeaderList[0] = '\0'; - g_TotalClients = 0; - - if (g_hDisplayTimer != null) - { - delete g_hDisplayTimer; - } - - if (!cancel) - { - CreateTimer(5.0, Game_ResetVote, TIMER_FLAG_NO_MAPCHANGE); - } -} - -bool Internal_IsVoteInProgress() -{ - return (g_hCurVote != INVALID_HANDLE); -} - -bool Internal_IsClientInVotePool(int client) -{ - if (client < 1 || client > MaxClients || g_hCurVote == null) - { - return false; - } - - return (g_ClientVotes[client] > VOTE_NOT_VOTING); -} - -bool Internal_RedrawToClient(int client, bool revotes) -{ - if (!Internal_IsVoteInProgress() || !Internal_IsClientInVotePool(client)) - { - return false; - } - - if (g_ClientVotes[client] >= 0) - { - if ((g_VoteFlags & VOTEFLAG_NO_REVOTES) || !revotes || g_VoteTime <= VOTE_DELAY_TIME) - { - return false; - } - - g_Clients++; - SetArrayCell(g_hVotes, g_ClientVotes[client], GetArrayCell(g_hVotes, g_ClientVotes[client]) - 1); - g_ClientVotes[client] = VOTE_PENDING; - g_bRevoting[client] = true; - g_NumVotes--; - Game_UpdateVoteCounts(g_hVotes, g_TotalClients); - } - - // Display the vote fail screen for a few seconds - //Game_DisplayVoteFail(g_hCurVote, NativeVotesFail_Generic, client); - - // No, display a vote pass screen because that's nicer and we can customize it. - // @note This isn't inside the earlier if because some players have had issues where the display doesn't always appear the first time. - char revotePhrase[128]; - Format(revotePhrase, sizeof(revotePhrase), "%T", "NativeVotes Revote", client); - Game_DisplayVotePassCustom(g_hCurVote, revotePhrase, client); - - DataPack data; - - CreateDataTimer(VOTE_DELAY_TIME, RedrawTimer, data, TIMER_FLAG_NO_MAPCHANGE); - data.WriteCell(GetClientUserId(client)); - data.WriteCell(view_as(g_hCurVote)); - - return true; -} - -public Action RedrawTimer(Handle timer, DataPack data) -{ - if (g_hCurVote == null) - { - return Plugin_Stop; - } - - ResetPack(data); - int client = GetClientOfUserId(data.ReadCell()); - if (client == 0) - { - return Plugin_Stop; - } - - NativeVote vote = view_as(data.ReadCell()); - - if (Internal_IsVoteInProgress() && !Internal_IsCancelling() && vote == g_hCurVote) - { - Game_DisplayVoteToOne(vote, client); - } - - return Plugin_Stop; -} - -void CancelVoting() -{ - if (g_bCancelled || g_hCurVote == INVALID_HANDLE) - { - return; - } - - g_bCancelled = true; - - EndVoting(); -} - -void PerformVisChecks(int client, ArrayList hVoteTypes) -{ - // Iterate backwards so we can safely remove items - for (int i = hVoteTypes.Length - 1; i >= 0; i--) - { - CallVoteListData voteData; - hVoteTypes.GetArray(i, voteData, sizeof(CallVoteListData)); - - Action hide = Plugin_Continue; - -#if defined LOG - LogMessage("Checking visibility forward for %d: %d", voteData.CallVoteList_VoteType, g_CallVotes[voteData.CallVoteList_VoteType].CallVote_Vis); -#endif - Call_StartForward(g_CallVotes[voteData.CallVoteList_VoteType].CallVote_Vis); - Call_PushCell(client); - Call_PushCell(voteData.CallVoteList_VoteType); - Call_Finish(hide); - if (hide >= Plugin_Handled) - { -#if defined LOG - LogMessage("Hiding vote type %d", voteData.CallVoteList_VoteType); -#endif - if (Game_AreDisabledIssuesHidden()) - { - // Since we hide disabled issues, remove it from the arraylist - RemoveFromArray(hVoteTypes, i); - } - else - { - // Arrays are pass by ref, so this should update the one inside the ArrayList - voteData.CallVoteList_VoteEnabled = false; - } - } - } -} - -//---------------------------------------------------------------------------- -// Natives - -// native bool NativeVotes_IsVoteTypeSupported(NativeVotesType voteType); -public int Native_IsVoteTypeSupported(Handle plugin, int numParams) -{ - NativeVotesType type = GetNativeCell(1); - - return Game_CheckVoteType(type); -} - -// native Handle NativeVotes_Create(MenuHandler handler, NativeVotesType voteType, MenuAction actions = NATIVEVOTES_ACTIONS_DEFAULT); -public int Native_Create(Handle plugin, int numParams) -{ - Function handler = GetNativeFunction(1); - NativeVotesType voteType = GetNativeCell(2); - MenuAction actions = GetNativeCell(3); - - if (handler == INVALID_FUNCTION) - { - ThrowNativeError(SP_ERROR_NATIVE, "Menuhandler is invalid"); - } - - NativeVote vote; - if (Game_CheckVoteType(voteType)) - { - vote = Data_CreateVote(voteType, actions); - } - else - { - return view_as(INVALID_HANDLE); - } - - if (voteType != NativeVotesType_NextLevelMult && voteType != NativeVotesType_Custom_Mult) - { - Data_AddItem(vote, "yes", "Yes"); - Data_AddItem(vote, "no", "No"); - } - - Handle menuForward = Data_GetHandler(vote); - - AddToForward(menuForward, plugin, handler); - - return view_as(vote); -} - -// native Handle NativeVotes_Close(Handle vote); -public int Native_Close(Handle plugin, int numParams) -{ - NativeVote vote = GetNativeCell(1); - - if (vote == null) - { - return; - } - - if (g_hCurVote == vote) - { - CancelVoting(); - g_hCurVote = null; - - /* - if (g_hVoteTimer != INVALID_HANDLE) - { - KillTimer(g_hVoteTimer); - g_hVoteTimer = INVALID_HANDLE; - } - */ - } - - // This bit is necessary because the Forward system appears to not remove these when the forward Handle is closed - // This was necessary in SM 1.5.x, but has a REALLY high probability of crashing in SM 1.6, plus is no longer needed - //Handle menuForward = Data_GetHandler(vote); - //RemoveAllFromForward(menuForward, plugin); - - //Handle voteResults = Data_GetResultCallback(vote); - //RemoveAllFromForward(voteResults, plugin); - - // Do the datatype-specific close operations - Data_CloseVote(vote); -} - -// native bool NativeVotes_Display(Handle vote, int clients[], int numClients, int time, int flags=0); -public int Native_Display(Handle plugin, int numParams) -{ - if (Internal_IsVoteInProgress()) - { - ThrowNativeError(SP_ERROR_NATIVE, "A vote is already in progress"); - } - - NativeVote vote = GetNativeCell(1); - if (vote == null) - { - ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return false; - } - - int count = GetNativeCell(3); - int[] clients = new int[count]; - GetNativeArray(2, clients, count); - - // Note: Only one flag exists: VOTEFLAG_NO_REVOTES - int flags = 0; - - if (numParams >= 5) - { - flags = GetNativeCell(5); - } - - if (!StartVote(vote, count, clients, GetNativeCell(4), flags)) - { - return 0; - } - - return 1; - -} - -// native bool NativeVotes_AddItem(Handle vote, const char[] info, const char[] display); -public int Native_AddItem(Handle plugin, int numParams) -{ - NativeVote vote = GetNativeCell(1); - if (vote == null) - { - ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return false; - } - - NativeVotesType voteType = Data_GetType(vote); - - if (voteType != NativeVotesType_NextLevelMult && voteType != NativeVotesType_Custom_Mult) - { - return false; - } - - char info[256]; - char display[256]; - GetNativeString(2, info, sizeof(info)); - GetNativeString(3, display, sizeof(display)); - - return Data_AddItem(vote, info, display); -} - -// native bool NativeVotes_InsertItem(Handle vote, int position, const char[] info, const char[] display); -public int Native_InsertItem(Handle plugin, int numParams) -{ - NativeVote vote = GetNativeCell(1); - if (vote == null) - { - ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return false; - } - - NativeVotesType voteType = Data_GetType(vote); - - if (voteType != NativeVotesType_NextLevelMult && voteType != NativeVotesType_Custom_Mult) - { - return false; - } - - int position = GetNativeCell(2); - - if (position < 0) - { - ThrowNativeError(SP_ERROR_NATIVE, "Vote index can't be negative: %d", position); - return false; - } - - char info[256]; - char display[256]; - GetNativeString(3, info, sizeof(info)); - GetNativeString(4, display, sizeof(display)); - - return Data_InsertItem(vote, position, info, display); - -} - -// native bool NativeVotes_RemoveItem(Handle vote, int position); -public int Native_RemoveItem(Handle plugin, int numParams) -{ - NativeVote vote = GetNativeCell(1); - if (vote == null) - { - ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return false; - } - - NativeVotesType voteType = Data_GetType(vote); - - if (voteType != NativeVotesType_NextLevelMult && voteType != NativeVotesType_Custom_Mult) - { - return false; - } - - int position = GetNativeCell(2); - - return Data_RemoveItem(vote, position); -} - -// native NativeVotes_RemoveAllItems(Handle vote); -public int Native_RemoveAllItems(Handle plugin, int numParams) -{ - NativeVote vote = GetNativeCell(1); - if (vote == null) - { - ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return; - } - - Data_RemoveAllItems(vote); -} - -// native bool NativeVotes_GetItem(Handle vote, int position, const char[] infoBuf, int infoBufLen, const char[] dispBuf = "", int dispBufLen = 0); -public int Native_GetItem(Handle plugin, int numParams) -{ - NativeVote vote = GetNativeCell(1); - if (vote == null) - { - ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return; - } - - int position = GetNativeCell(2); - - int infoLength = GetNativeCell(4); - char[] info = new char[infoLength]; - Data_GetItemInfo(vote, position, info, infoLength); - SetNativeString(3, info, infoLength); - - if (numParams >= 6) - { - int displayLength = GetNativeCell(6); - if (displayLength > 0) - { - char[] display = new char[displayLength]; - Data_GetItemDisplay(vote, position, display, displayLength); - SetNativeString(5, display, displayLength); - } - } -} - -// native NativeVotes_GetItemCount(Handle vote); -public int Native_GetItemCount(Handle plugin, int numParams) -{ - NativeVote vote = GetNativeCell(1); - if (vote == null) - { - ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return 0; - } - - return Data_GetItemCount(vote); -} - -// native NativeVotes_GetDetails(Handle vote, const char[] buffer, int maxlength); -public int Native_GetDetails(Handle plugin, int numParams) -{ - NativeVote vote = GetNativeCell(1); - if (vote == null) - { - ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return; - } - - int len = GetNativeCell(3); - - char[] details = new char[len]; - - Data_GetDetails(vote, details, len); - - SetNativeString(2, details, len); -} - -// native NativeVotes_SetDetails(Handle vote, const char[] fmt, any:...); -public int Native_SetDetails(Handle plugin, int numParams) -{ - NativeVote vote = GetNativeCell(1); - if (vote == null) - { - ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return; - } - - char details[MAX_VOTE_DETAILS_LENGTH]; - - //SetGlobalTransTarget(LANG_SERVER); - FormatNativeString(0, 2, 3, sizeof(details), _, details); - - Data_SetDetails(vote, details); -} - -// native NativeVotes_GetDetails(Handle vote, const char[] buffer, int maxlength); -public int Native_GetTitle(Handle plugin, int numParams) -{ - NativeVote vote = GetNativeCell(1); - if (vote == null) - { - ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return; - } - - int len = GetNativeCell(3); - - char[] title = new char[len]; - - Data_GetTitle(vote, title, len); - - SetNativeString(2, title, len); -} - -// native NativeVotes_SetTitle(Handle vote, const char[] fmt, any:...); -public int Native_SetTitle(Handle plugin, int numParams) -{ - NativeVote vote = GetNativeCell(1); - if (vote == null) - { - ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return; - } - - char details[MAX_VOTE_DETAILS_LENGTH]; - - //SetGlobalTransTarget(LANG_SERVER); - FormatNativeString(0, 2, 3, sizeof(details), _, details); - - Data_SetTitle(vote, details); -} - -// native bool NativeVotes_IsVoteInProgress(); -public int Native_IsVoteInProgress(Handle plugin, int numParams) -{ - return Internal_IsVoteInProgress() || Game_IsVoteInProgress(); -} - -// native NativeVotes_GetMaxItems(); -public int Native_GetMaxItems(Handle plugin, int numParams) -{ - return Game_GetMaxItems(); -} - -// native NativeVotes_GetOptionFlags(Handle vote); -public int Native_GetOptionFlags(Handle plugin, int numParams) -{ - NativeVote vote = GetNativeCell(1); - if (vote == null) - { - ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return 0; - } - - return Data_GetFlags(vote); -} - -// native NativeVotes_SetOptionFlags(Handle vote, flags); -public int Native_SetOptionFlags(Handle plugin, int numParams) -{ - NativeVote vote = GetNativeCell(1); - if (vote == null) - { - ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return; - } - - int flags = GetNativeCell(2); - - // This is an ORed group of flags to strip the ones we don't support - flags &= (MENUFLAG_BUTTON_NOVOTE); - - Data_SetFlags(vote, flags); -} - -// native bool NativeVotes_SetNoVoteButton(Handle vote, bool button); -public int Native_SetNoVoteButton(Handle plugin, int numParams) -{ - NativeVote vote = GetNativeCell(1); - if (vote == null) - { - return ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - } - - int flags = Data_GetFlags(vote); - - if (GetNativeCell(2)) - { - flags |= MENUFLAG_BUTTON_NOVOTE; - } - else - { - flags &= ~MENUFLAG_BUTTON_NOVOTE; - } - - Data_SetFlags(vote, flags); - - int newflags = Data_GetFlags(vote); - - return (flags == newflags); -} - -// native NativeVotes_Cancel(); -public int Native_Cancel(Handle plugin, int numParams) -{ - if (!Internal_IsVoteInProgress()) - { - ThrowNativeError(SP_ERROR_NATIVE, "No vote is in progress"); - return; - } - - CancelVoting(); -} - -// native NativeVotes_SetResultCallback(Handle vote, NativeVotes_VoteHandler callback); -public int Native_SetResultCallback(Handle plugin, int numParams) -{ - NativeVote vote = GetNativeCell(1); - if (vote == null) - { - ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return; - } - - Function handler = GetNativeFunction(2); - - if (handler == INVALID_FUNCTION) - { - ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes_VoteHandler is invalid"); - return; - } - - Handle voteResults = Data_GetResultCallback(vote); - - RemoveAllFromForward(voteResults, plugin); - if (!AddToForward(voteResults, plugin, handler)) - { - ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes_VoteHandler cannot be added to forward"); - } -} - -// native NativeVotes_CheckVoteDelay(); -public int Native_CheckVoteDelay(Handle plugin, int numParams) -{ - int curTime = GetTime(); - if (g_NextVote <= curTime) - { - return 0; - } - - return (g_NextVote - curTime); -} - -// native bool NativeVotes_IsClientInVotePool(int client); -public int Native_IsClientInVotePool(Handle plugin, int numParams) -{ - int client = GetNativeCell(1); - - if (!client || client > MaxClients) - { - ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index %d", client); - return false; - } - - if (!Internal_IsVoteInProgress()) - { - ThrowNativeError(SP_ERROR_NATIVE, "No vote is in progress"); - return false; - } - - return Internal_IsClientInVotePool(client); -} - -// native bool NativeVotes_RedrawClientVote(int client, bool revotes = true); -public int Native_RedrawClientVote(Handle plugin, int numParams) -{ - int client = GetNativeCell(1); - - if (client < 1 || client > MaxClients || !IsClientConnected(client)) - { - ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index %d", client); - return false; - } - - if (!Internal_IsVoteInProgress()) - { - // When revoting in TF2, NativeVotes_IsVoteInProgress always gets skipped because of Game_IsVoteInProgress() - // TF2's vote controller will stay alive a few seconds after the vote is complete - // If one tries to revote right as a vote completes, it will throw an error - LogError("No vote is in progress"); - return false; - } - - if (!Internal_IsClientInVotePool(client)) - { - ThrowNativeError(SP_ERROR_NATIVE, "Client is not in the voting pool"); - return false; - } - - bool revote = true; - if (numParams >= 2 && !GetNativeCell(2)) - { - revote = false; - } - - return Internal_RedrawToClient(client, revote); -} - -// native NativeVotesType NativeVotes_GetType(Handle vote); -public int Native_GetType(Handle plugin, int numParams) -{ - NativeVote vote = GetNativeCell(1); - if (vote == null) - { - ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return 0; - } - - return view_as(Data_GetType(vote)); -} - -// native NativeVotes_GetTeam(Handle vote); -public int Native_GetTeam(Handle plugin, int numParams) -{ - NativeVote vote = GetNativeCell(1); - if (vote == null) - { - ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return NATIVEVOTES_ALL_TEAMS; - } - - return Data_GetTeam(vote); - -} - -// native NativeVotes_SetTeam(Handle vote, int team); -public int Native_SetTeam(Handle plugin, int numParams) -{ - NativeVote vote = GetNativeCell(1); - if (vote == null) - { - ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return; - } - - int team = GetNativeCell(2); - - // Teams are numbered starting with 0 - // Currently 4 is the maximum (Unassigned, Spectator, Team 1, Team 2) - if (team >= GetTeamCount()) - { - ThrowNativeError(SP_ERROR_NATIVE, "Team %d is invalid", team); - return; - } - - if (g_EngineVersion == Engine_TF2 && team == NATIVEVOTES_ALL_TEAMS) - { - team = NATIVEVOTES_TF2_ALL_TEAMS; - } - - Data_SetTeam(vote, team); -} - -// native NativeVotes_GetInitiator(Handle vote); -public int Native_GetInitiator(Handle plugin, int numParams) -{ - NativeVote vote = GetNativeCell(1); - if (vote == null) - { - ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return NATIVEVOTES_SERVER_INDEX; - } - - return Data_GetInitiator(vote); -} - -// native int NativeVotes_SetInitiator(Handle vote, int client); -public int Native_SetInitiator(Handle plugin, int numParams) -{ - NativeVote vote = GetNativeCell(1); - if (vote == null) - { - ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return; - } - - int initiator = GetNativeCell(2); - Data_SetInitiator(vote, initiator); -} - -// native NativeVotes_DisplayPass(Handle vote, const char[] details = ""); -public int Native_DisplayPass(Handle plugin, int numParams) -{ - NativeVote vote = GetNativeCell(1); - if (vote == null) - { - ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return; - } - - if (numParams >= 2) - { - char winner[TRANSLATION_LENGTH]; - FormatNativeString(0, 2, 3, sizeof(winner), _, winner); - - Game_DisplayVotePass(vote, winner); - } - else - { - Game_DisplayVotePass(vote); - } - -} - -// native NativeVotes_DisplayPassCustomToOne(Handle vote, client, const char[] format, any:...); -public int Native_DisplayPassCustomToOne(Handle plugin, int numParams) -{ - NativeVote vote = GetNativeCell(1); - if (vote == null) - { - ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return; - } - - int client = GetNativeCell(2); - - char translation[TRANSLATION_LENGTH]; - - SetGlobalTransTarget(client); - FormatNativeString(0, 3, 4, TRANSLATION_LENGTH, _, translation); - - Game_DisplayVotePassCustom(vote, translation, client); -} - -// native NativeVotes_DisplayPassEx(Handle vote, NativeVotesPassType passType, const char[] details = ""); -public int Native_DisplayPassEx(Handle plugin, int numParams) -{ - NativeVote vote = GetNativeCell(1); - if (vote == null) - { - ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return; - } - - NativeVotesPassType passType = view_as(GetNativeCell(2)); - - if (!Game_CheckVotePassType(passType)) - { - ThrowNativeError(SP_ERROR_NATIVE, "Invalid vote pass type: %d", passType); - } - - if (numParams >= 3) - { - char winner[TRANSLATION_LENGTH]; - //SetGlobalTransTarget(LANG_SERVER); - FormatNativeString(0, 3, 4, sizeof(winner), _, winner); - - Game_DisplayVotePassEx(vote, passType, winner); - } - else - { - Game_DisplayVotePassEx(vote, passType); - } -} - -// native NativeVotes_DisplayRawPass(NativeVotesPassType:passType, const String:details[]="", team=NATIVEVOTES_ALL_TEAMS); -/* -public Native_DisplayRawPass(Handle:plugin, numParams) -{ - new NativeVotesPassType:passType = NativeVotesPassType:GetNativeCell(1); - - if (!Game_CheckVotePassType(passType)) - { - ThrowNativeError(SP_ERROR_NATIVE, "Invalid vote pass type: %d", passType); - } - - new len; - GetNativeStringLength(2, len); - new String:winner[len+1]; - GetNativeString(2, winner, len+1); - new team = GetNativeCell(3); - - if (g_EngineVersion == Engine_TF2 && team == NATIVEVOTES_ALL_TEAMS) - { - team = NATIVEVOTES_TF2_ALL_TEAMS; - } - - Game_DisplayRawVotePass(passType, winner, team); -} -*/ - -// native NativeVotes_DisplayRawPassToOne(int client, NativeVotesPassType passType, const char[] details = "", int team = NATIVEVOTES_ALL_TEAMS); -public int Native_DisplayRawPassToOne(Handle plugin, int numParams) -{ - int client = GetNativeCell(1); - NativeVotesPassType passType = view_as(GetNativeCell(2)); - - if (!Game_CheckVotePassType(passType)) - { - ThrowNativeError(SP_ERROR_NATIVE, "Invalid vote pass type: %d", passType); - } - - int team = GetNativeCell(3); - - if (g_EngineVersion == Engine_TF2 && team == NATIVEVOTES_ALL_TEAMS) - { - team = NATIVEVOTES_TF2_ALL_TEAMS; - } - - if (numParams >= 4) - { - char winner[TRANSLATION_LENGTH]; - SetGlobalTransTarget(client); - FormatNativeString(0, 4, 5, sizeof(winner), _, winner); - - Game_DisplayRawVotePass(passType, team, client, winner); - } - else - { - Game_DisplayRawVotePass(passType, team, client); - } -} -// native NativeVotes_DisplayRawPassCustomToOne(int client, int team, const char[] format, any:...); -public int Native_DisplayRawPassCustomToOne(Handle plugin, int numParams) -{ - int client = GetNativeCell(1); - int team = GetNativeCell(2); - - char translation[TRANSLATION_LENGTH]; - - SetGlobalTransTarget(client); - FormatNativeString(0, 3, 4, TRANSLATION_LENGTH, _, translation); - - Game_DisplayRawVotePassCustom(translation, team, client); -} - -// native NativeVotes_DisplayFail(Handle vote, NativeVotesFailType reason = NativeVotesFail_Generic); -public int Native_DisplayFail(Handle plugin, int numParams) -{ - NativeVote vote = GetNativeCell(1); - if (vote == null) - { - ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return; - } - - NativeVotesFailType reason = view_as(GetNativeCell(2)); - - Game_DisplayVoteFail(vote, reason); -} - -// native NativeVotes_DisplayRawFail(NativeVotesFailType reason = NativeVotesFail_Generic, int team = NATIVEVOTES_ALL_TEAMS); -public int Native_DisplayRawFail(Handle plugin, int numParams) -{ - int size = GetNativeCell(2); - int[] clients = new int[size]; - GetNativeArray(1, clients, size); - - NativeVotesFailType reason = view_as(GetNativeCell(3)); - - int team = GetNativeCell(4); - - if (g_EngineVersion == Engine_TF2 && team == NATIVEVOTES_ALL_TEAMS) - { - team = NATIVEVOTES_TF2_ALL_TEAMS; - } - - Game_DisplayRawVoteFail(clients, size, reason, team); -} - -// native NativeVotes_DisplayRawFailToOne(int client, NativeVotesFailType reason = NativeVotesFail_Generic, int team = NATIVEVOTES_ALL_TEAMS); -/* -public Native_DisplayRawFailToOne(Handle plugin, int numParams) -{ - int client = GetNativeCell(1); - - NativeVotesFailType reason = view_as(GetNativeCell(2)); - - int team = GetNativeCell(3); - - if (g_EngineVersion == Engine_TF2 && team == NATIVEVOTES_ALL_TEAMS) - { - team = NATIVEVOTES_TF2_ALL_TEAMS; - } - - Game_DisplayRawVoteFail(reason, team, client); -} -*/ -// native NativeVotes_GetTarget(Handle vote); -public int Native_GetTarget(Handle plugin, int numParams) -{ - NativeVote vote = GetNativeCell(1); - if (vote == null) - { - ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return 0; - } - - int target = Data_GetTarget(vote); - - if (target == 0) - { - // No target was set, return -1 - return -1; - } - - return GetClientOfUserId(target); -} - -// native NativeVotes_GetTargetSteam(Handle vote, const char[] buffer, maxlength); -public int Native_GetTargetSteam(Handle plugin, int numParams) -{ - NativeVote vote = GetNativeCell(1); - if (vote == null) - { - ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return 0; - } - - int size = GetNativeCell(3); - char[] steamId = new char[size]; - GetNativeString(2, steamId, size); - - Data_GetTargetSteam(vote, steamId, size); -} - -// native NativeVotes_SetTarget(Handle vote, int client, bool setDetails = true); -public int Native_SetTarget(Handle plugin, int numParams) -{ - NativeVote vote = GetNativeCell(1); - if (vote == null) - { - ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return; - } - - int client = GetNativeCell(2); - - if (client < 1 || client > MaxClients || !IsClientConnected(client)) - { - ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index %d", client); - return; - } - - int userid; - char steamId[MAX_AUTHID_LENGTH]; - - if (!client) - { - userid = 0; - steamId = ""; - } - else - { - userid = GetClientUserId(client); - if (!GetClientAuthId(client, AuthId_Steam2, steamId, sizeof(steamId))) - { - steamId = ""; - } - } - - Data_SetTarget(vote, userid); - Data_SetTargetSteam(vote, steamId); - - bool changeDetails = GetNativeCell(3); - if (changeDetails) - { - char name[MAX_NAME_LENGTH+1]; - if (client > 0) - { - GetClientName(client, name, MAX_NAME_LENGTH); - Data_SetDetails(vote, name); - } - else - { - Data_SetDetails(vote, ""); - } - } -} - -// native bool NativeVotes_AreVoteCommandsSupported(); -public int Native_AreVoteCommandsSupported(Handle plugin, int numParams) -{ - return Game_AreVoteCommandsSupported(); -} - -// native NativeVotes_RegisterVoteCommand(NativeVotesOverride overrideType, NativeVotes_CallVoteHandler callHandler, NativeVotes_CallVoteVisCheck visHandler = INVALID_FUNCTION); -public int Native_RegisterVoteCommand(Handle plugin, int numParams) -{ - NativeVotesOverride overrideType = GetNativeCell(1); - Function callVoteHandler = GetNativeFunction(2); - Function visHandler = GetNativeFunction(3); - - // This tosses an error so use the simplified version - // if (view_as(overrideType) > sizeof(g_CallVotes)) - if (overrideType > NativeVotesOverride_Count) - { - ThrowNativeError(SP_ERROR_NATIVE, "Override Type %d is not supported by this version of NativeVotes", overrideType); - return; - } - - if (callVoteHandler == INVALID_FUNCTION) - { - ThrowNativeError(SP_ERROR_NATIVE, "CallVoteHandler function was invalid"); - return; - } - - AddToForward(g_CallVotes[overrideType].CallVote_Forward, plugin, callVoteHandler); - - if (visHandler != INVALID_FUNCTION) - { - AddToForward(g_CallVotes[overrideType].CallVote_Vis, plugin, visHandler); - } -} - -// native NativeVotes_UnregisterVoteCommand(NativeVotesOverride overrideType, NativeVotes_CallVoteHandler callHandler, NativeVotes_CallVoteVisCheck visHandler=INVALID_FUNCTION); -public int Native_UnregisterVoteCommand(Handle plugin, int numParams) -{ - NativeVotesOverride overrideType = GetNativeCell(1); - Function callVoteHandler = GetNativeFunction(2); - Function visHandler = GetNativeFunction(3); - - if (overrideType > NativeVotesOverride_Count) - { - ThrowNativeError(SP_ERROR_NATIVE, "Override Type %d is not supported by this version of NativeVotes", overrideType); - return; - } - - if (callVoteHandler == INVALID_FUNCTION) - { - ThrowNativeError(SP_ERROR_NATIVE, "CallVoteHandler function was invalid"); - return; - } - - RemoveFromForward(g_CallVotes[overrideType].CallVote_Forward, plugin, callVoteHandler); - - if (visHandler != INVALID_FUNCTION) - { - RemoveFromForward(g_CallVotes[overrideType].CallVote_Vis, plugin, visHandler); - } -} - -// native NativeVotes_DisplayCallVoteFail(int client, NativeVotesCallFailType reason, int time=0); -public int Native_DisplayCallVoteFail(Handle plugin, int numParams) -{ - int client = GetNativeCell(1); - - if (client < 1 || client > MaxClients || !IsClientConnected(client)) - { - ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index %d", client); - return; - } - - NativeVotesCallFailType reason = GetNativeCell(2); - - int time = GetNativeCell(3); - - Game_DisplayCallVoteFail(client, reason, time); -} - -// native Action NativeVotes_RedrawVoteTitle(const char[] text); -public int Native_RedrawVoteTitle(Handle plugin, int numParams) -{ - if (!g_curDisplayClient) - { - ThrowNativeError(SP_ERROR_NATIVE, "You can only call this once from a MenuAction_Display callback"); - } - - NativeVotesType voteType = Data_GetType(g_hCurVote); - - if (voteType != NativeVotesType_Custom_Mult && voteType != NativeVotesType_Custom_YesNo) - { - return view_as(Plugin_Continue); - } - - GetNativeString(1, g_newMenuTitle, TRANSLATION_LENGTH); - return view_as(Plugin_Changed); -} - -// native Action NativeVotes_RedrawVoteItem(const char[] text); -public int Native_RedrawVoteItem(Handle plugin, int numParams) -{ - if (!g_curItemClient) - { - ThrowNativeError(SP_ERROR_NATIVE, "You can only call this once from a MenuAction_DisplayItem callback"); - } - - if (Game_GetMaxItems() == L4DL4D2_COUNT) - { - return view_as(Plugin_Continue); - } - - GetNativeString(1, g_newMenuItem, TRANSLATION_LENGTH); - return view_as(Plugin_Changed); +/** + * vim: set ts=4 : + * ============================================================================= + * NativeVotes + * NativeVotes is a voting API plugin for L4D, L4D2, TF2, and CS:GO. + * Based on the SourceMod voting API + * + * NativeVotes (C) 2011-2015 Ross Bemrose (Powerlord). All rights reserved. + * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#include +#include + +#pragma semicolon 1 +#pragma newdecls required + +#include +#include + +EngineVersion g_EngineVersion = Engine_Unknown; + +#include "nativevotes/data-keyvalues.sp" + +#define LOGTAG "NativeVotes" + +#define MAX_VOTE_DETAILS_LENGTH 64 // From SourceSDK2013's shareddefs.h +#define TRANSLATION_LENGTH 192 + +#define VOTE_DELAY_TIME 3.0 + +// SourceMod uses these internally, so... we do too. +#define VOTE_NOT_VOTING -2 +#define VOTE_PENDING -1 + +#define MAX_VOTE_ISSUES 20 +#define VOTE_STRING_SIZE 32 + +//---------------------------------------------------------------------------- +// These values are swapped from their NativeVotes equivalent +#define L4D2_VOTE_YES_INDEX 1 +#define L4D2_VOTE_NO_INDEX 0 + +#define L4DL4D2_COUNT 2 +#define TF2CSGO_COUNT 5 + +#define MAX_CALLVOTE_SIZE 128 + +//#define LOG + +//---------------------------------------------------------------------------- +// Global Variables +int g_NextVote = 0; + +//---------------------------------------------------------------------------- +// CVars +enum +{ + progress_hintbox, + progress_chat, + progress_console, + progress_client_console, + vote_delay, + + MAX_CONVARS +}; + +ConVar g_ConVars[MAX_CONVARS]; + +//---------------------------------------------------------------------------- +// Used to track current vote data +//Handle g_hVoteTimer; +Handle g_hDisplayTimer; + +int g_Clients; +int g_TotalClients; +int g_Items; +ArrayList g_hVotes; +NativeVote g_hCurVote; +int g_curDisplayClient = 0; +char g_newMenuTitle[TRANSLATION_LENGTH]; +int g_curItemClient = 0; +char g_newMenuItem[TRANSLATION_LENGTH]; + +bool g_bStarted; +bool g_bCancelled; +int g_NumVotes; +int g_VoteTime; +int g_VoteFlags; +float g_fStartTime; +int g_TimeLeft; +int g_ClientVotes[MAXPLAYERS+1]; +bool g_bRevoting[MAXPLAYERS+1]; +char g_LeaderList[1024]; + +ConVar sv_vote_holder_may_vote_no; + +// Map list stuffs + +//#define MAP_STRING_CACHE_SIZE PLATFORM_MAX_PATH * 256 + +// Forward +Handle g_OverrideMaps; +StringMap g_MapOverrides; +bool g_OverridesSet; +bool g_OverrideNextCallVote[MAXPLAYERS + 1]; + +enum struct CallVoteForwards +{ + Handle CallVote_Forward; + Handle CallVote_Vis; +} + +enum struct CallVoteListData +{ + NativeVotesOverride CallVoteList_VoteType; + bool CallVoteList_VoteEnabled; +} + +CallVoteForwards g_CallVotes[NativeVotesOverride_Count]; + +#include "nativevotes/game.sp" + +public Plugin myinfo = +{ + name = "NativeVotes", + author = "Powerlord", + description = "Voting API to use the game's native vote panels. Compatible with L4D, L4D2, TF2, and CS:GO.", + version = "26w05b", + url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" +} + +public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) +{ + char engineName[64]; + if (!Game_IsGameSupported(engineName, sizeof(engineName))) + { + Format(error, err_max, "Unsupported game: %s", engineName); + return APLRes_Failure; + } + + CreateNative("NativeVotes_IsVoteTypeSupported", Native_IsVoteTypeSupported); + CreateNative("NativeVotes_Create", Native_Create); + CreateNative("NativeVotes_Close", Native_Close); + CreateNative("NativeVotes_Display", Native_Display); + CreateNative("NativeVotes_AddItem", Native_AddItem); + CreateNative("NativeVotes_InsertItem", Native_InsertItem); + CreateNative("NativeVotes_RemoveItem", Native_RemoveItem); + CreateNative("NativeVotes_RemoveAllItems", Native_RemoveAllItems); + CreateNative("NativeVotes_GetItem", Native_GetItem); + CreateNative("NativeVotes_GetItemCount", Native_GetItemCount); + CreateNative("NativeVotes_SetDetails", Native_SetDetails); + CreateNative("NativeVotes_GetDetails", Native_GetDetails); + CreateNative("NativeVotes_SetTitle", Native_SetTitle); + CreateNative("NativeVotes_GetTitle", Native_GetTitle); + CreateNative("NativeVotes_SetTarget", Native_SetTarget); + CreateNative("NativeVotes_GetTarget", Native_GetTarget); + CreateNative("NativeVotes_GetTargetSteam", Native_GetTargetSteam); + CreateNative("NativeVotes_IsVoteInProgress", Native_IsVoteInProgress); + CreateNative("NativeVotes_GetMaxItems", Native_GetMaxItems); + CreateNative("NativeVotes_SetOptionFlags", Native_SetOptionFlags); + CreateNative("NativeVotes_GetOptionFlags", Native_GetOptionFlags); + CreateNative("NativeVotes_SetNoVoteButton", Native_SetNoVoteButton); + CreateNative("NativeVotes_Cancel", Native_Cancel); + CreateNative("NativeVotes_SetResultCallback", Native_SetResultCallback); + CreateNative("NativeVotes_CheckVoteDelay", Native_CheckVoteDelay); + CreateNative("NativeVotes_IsClientInVotePool", Native_IsClientInVotePool); + CreateNative("NativeVotes_RedrawClientVote", Native_RedrawClientVote); + CreateNative("NativeVotes_GetType", Native_GetType); + CreateNative("NativeVotes_SetTeam", Native_SetTeam); + CreateNative("NativeVotes_GetTeam", Native_GetTeam); + CreateNative("NativeVotes_SetInitiator", Native_SetInitiator); + CreateNative("NativeVotes_GetInitiator", Native_GetInitiator); + CreateNative("NativeVotes_DisplayPass", Native_DisplayPass); + CreateNative("NativeVotes_DisplayPassCustomToOne", Native_DisplayPassCustomToOne); + CreateNative("NativeVotes_DisplayPassEx", Native_DisplayPassEx); + //CreateNative("NativeVotes_DisplayRawPass", Native_DisplayRawPass); + CreateNative("NativeVotes_DisplayRawPassToOne", Native_DisplayRawPassToOne); + CreateNative("NativeVotes_DisplayRawPassCustomToOne", Native_DisplayRawPassCustomToOne); + CreateNative("NativeVotes_DisplayFail", Native_DisplayFail); + CreateNative("NativeVotes_DisplayRawFail", Native_DisplayRawFail); + //CreateNative("NativeVotes_DisplayRawFailToOne", Native_DisplayRawFailToOne); + CreateNative("NativeVotes_AreVoteCommandsSupported", Native_AreVoteCommandsSupported); + CreateNative("NativeVotes_RegisterVoteCommand", Native_RegisterVoteCommand); + CreateNative("NativeVotes_UnregisterVoteCommand", Native_UnregisterVoteCommand); + CreateNative("NativeVotes_DisplayCallVoteFail", Native_DisplayCallVoteFail); + CreateNative("NativeVotes_RedrawVoteTitle", Native_RedrawVoteTitle); + CreateNative("NativeVotes_RedrawVoteItem", Native_RedrawVoteItem); + + // Transitional syntax support + CreateNative("NativeVote.NativeVote", Native_Create); + CreateNative("NativeVote.Close", Native_Close); + CreateNative("NativeVote.AddItem", Native_AddItem); + CreateNative("NativeVote.InsertItem", Native_InsertItem); + CreateNative("NativeVote.RemoveItem", Native_RemoveItem); + CreateNative("NativeVote.RemoveAllItems", Native_RemoveAllItems); + CreateNative("NativeVote.GetItem", Native_GetItem); + CreateNative("NativeVote.SetDetails", Native_SetDetails); + CreateNative("NativeVote.GetDetails", Native_GetDetails); + CreateNative("NativeVote.SetTitle", Native_SetTitle); + CreateNative("NativeVote.GetTitle", Native_GetTitle); + CreateNative("NativeVote.SetTarget", Native_SetTarget); + CreateNative("NativeVote.GetTarget", Native_GetTarget); + CreateNative("NativeVote.GetTargetSteam", Native_GetTargetSteam); + CreateNative("NativeVote.DisplayVote", Native_Display); + CreateNative("NativeVote.DisplayPass", Native_DisplayPass); + CreateNative("NativeVote.DisplayPassCustomToOne", Native_DisplayPassCustomToOne); + CreateNative("NativeVote.DisplayPassEx", Native_DisplayPassEx); + CreateNative("NativeVote.DisplayFail", Native_DisplayFail); + CreateNative("NativeVote.OptionFlags.set", Native_SetOptionFlags); + CreateNative("NativeVote.OptionFlags.get", Native_GetOptionFlags); + CreateNative("NativeVote.NoVoteButton.set", Native_SetNoVoteButton); + CreateNative("NativeVote.VoteResultCallback.set", Native_SetResultCallback); + CreateNative("NativeVote.ItemCount.get", Native_GetItemCount); + CreateNative("NativeVote.VoteType.get", Native_GetType); + CreateNative("NativeVote.Team.set", Native_SetTeam); + CreateNative("NativeVote.Team.get", Native_GetTeam); + CreateNative("NativeVote.Initiator.set", Native_SetInitiator); + CreateNative("NativeVote.Initiator.get", Native_GetInitiator); + + RegPluginLibrary("nativevotes"); + + return APLRes_Success; +} + +public void OnPluginStart() +{ + LoadTranslations("core.phrases"); + LoadTranslations("nativevotes.phrases.txt"); + + g_ConVars[progress_hintbox] = CreateConVar("nativevotes_progress_hintbox", "0", "Show current vote progress in a hint box", FCVAR_NONE, true, 0.0, true, 1.0); + g_ConVars[progress_chat] = CreateConVar("nativevotes_progress_chat", "0", "Show current vote progress as chat messages", FCVAR_NONE, true, 0.0, true, 1.0); + g_ConVars[progress_console] = CreateConVar("nativevotes_progress_console", "0", "Show current vote progress as console messages", FCVAR_NONE, true, 0.0, true, 1.0); + g_ConVars[progress_client_console] = CreateConVar("nativevotes_progress_client_console", "0", "Show current vote progress as console messages to clients", FCVAR_NONE, true, 0.0, true, 1.0); + g_ConVars[vote_delay] = CreateConVar("nativevotes_vote_delay", "30", "Sets the recommended time in between public votes", FCVAR_NONE, true, 0.0); + g_ConVars[vote_delay].AddChangeHook(OnVoteDelayChange); + + Game_InitializeCvars(); + + AddCommandListener(Command_Vote, "vote"); // All games, command listeners aren't case sensitive + + sv_vote_holder_may_vote_no = FindConVar("sv_vote_holder_may_vote_no"); + + // The new version of the CallVote system is TF2 only + if (Game_AreVoteCommandsSupported()) + { + AddCommandListener(Command_CallVote, "callvote"); + + // None is type 0, which has no overrides + // As of 2015-09-28, there are 10 votes for a total of 20 private forwards created here. + for (int i = 1; i < sizeof(g_CallVotes); i++) + { + g_CallVotes[i].CallVote_Forward = CreateForward(ET_Hook, Param_Cell, Param_Cell, Param_String, Param_Cell, Param_Cell); + g_CallVotes[i].CallVote_Vis = CreateForward(ET_Hook, Param_Cell, Param_Cell); + } + + g_OverrideMaps = CreateGlobalForward("NativeVotes_OverrideMaps", ET_Hook, Param_Cell); + } + + g_hVotes = new ArrayList(1, Game_GetMaxItems()); + + AutoExecConfig(true, "nativevotes"); +} + +public void OnMapStart() +{ + // Map list stuffs + if (g_MapOverrides != null) + delete g_MapOverrides; + + g_OverridesSet = false; + g_NextVote = 0; +} + +public Action Timer_RetryCallVote(Handle timer, any data) +{ + int client = GetClientOfUserId(data); + + if (client == 0) + { + return Plugin_Stop; + } + + FakeClientCommand(client, "callvote"); + return Plugin_Stop; +} + +public void ProcessMapList() +{ + char stringTable[64] = "ServerMapCycle"; + if (FindSendPropInfo("CTFGameRulesProxy", "m_bPlayingMannVsMachine") > 0 && GameRules_GetProp("m_bPlayingMannVsMachine")) + { + Format(stringTable, sizeof(stringTable), "%sMvM", stringTable); + } + + int stringTableIndex = FindStringTable(stringTable); + int stringIndex = FindStringIndex(stringTableIndex, stringTable); + + // Maplist resets every map + int length = GetStringTableDataLength(stringTableIndex, stringIndex); + char[] mapData = new char[length]; + GetStringTableData(stringTableIndex, stringIndex, mapData, length); + + // We'll get an extra blank entry if we don't do this + TrimString(mapData); + + StringMap overrideList = new StringMap(); + ExplodeStringToStringMap(mapData, "\n", overrideList, PLATFORM_MAX_PATH, ImplodePart_Key); + + Action mapResult = Plugin_Continue; + Call_StartForward(g_OverrideMaps); + Call_PushCell(overrideList); + Call_Finish(mapResult); + + if (mapResult != Plugin_Changed || overrideList.Size <= 0) + { + delete overrideList; + return; + } + +#if defined LOG + LogMessage("Overriding map list with %d maps", overrideList.Size); +#endif + + g_MapOverrides = overrideList; + + int maxLength = GetStringMapImplodeSize(overrideList, 1, ImplodePart_Key); + + char[] newMapData = new char[maxLength]; + int newLength = ImplodeStringMapToString(overrideList, "\n", newMapData, maxLength, ImplodePart_Key); + if (newLength < maxLength && newMapData[newLength] != '\n') + { + // do this to avoid a StrCat + newLength += strcopy(newMapData[newLength], maxLength, "\n") + 1; + } + + SetStringTableData(stringTableIndex, stringIndex, newMapData, newLength); +} + +public void OnClientDisconnect_Post(int client) +{ + if (!Internal_IsVoteInProgress() || !Internal_IsClientInVotePool(client)) + { + return; + } + + /* Wipe out their vote if they had one. We have to make sure the + * newly connected client is not allowed to vote. + */ + int item = g_ClientVotes[client]; + if (item >= VOTE_PENDING) + { + if (item > VOTE_PENDING) + { + g_hVotes.Set(item, g_hVotes.Get(item) - 1); + } + + g_ClientVotes[client] = VOTE_NOT_VOTING; + } + + CancelClientVote(g_hCurVote, client, MenuCancel_Disconnected); +} + +void CancelClientVote(NativeVote vote, int client, int reason) +{ + OnCancel(vote, client, reason); + OnClientEnd(); +} + +public Action Command_CallVote(int client, const char[] command, int argc) +{ + if (g_OverrideNextCallVote[client]) + { + g_OverrideNextCallVote[client] = false; + return Plugin_Continue; + } + + if (Internal_IsVoteInProgress() || Game_IsVoteInProgress() || !IsClientInGame(client)) + { + return Plugin_Handled; + } + + Action result = Plugin_Continue; + + switch (argc) + { + // No args means that we need to return a CallVoteSetup usermessage + case 0: + { + if (!g_OverridesSet && Game_AreVoteCommandsSupported()) + { + g_OverridesSet = true; + ProcessMapList(); + +#if defined LOG + LogMessage("Delaying to allow stringtable time to network"); +#endif + // Force it to reissue the callvote command so that the map list gets networked + CreateTimer(0.1, Timer_RetryCallVote, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE); + return Plugin_Handled; + } + + ArrayList hVoteTypes = new ArrayList(sizeof(CallVoteListData)); // Stores arrays of CallVoteListData + + Game_AddDefaultVotes(hVoteTypes); + + // Add our overridden votes to the system + bool overridesPresent = false; + for (int i = 1; i < sizeof(g_CallVotes); i++) + { + if (GetForwardFunctionCount(g_CallVotes[i].CallVote_Forward) > 0) + { +#if defined LOG + LogMessage("Found overrides for vote type: %d", i); +#endif + overridesPresent = true; + CallVoteListData voteType; + + int pos = FindVoteInArray(hVoteTypes, view_as(i)); + if (pos > -1) + { + hVoteTypes.GetArray(pos, voteType, sizeof(CallVoteListData)); + voteType.CallVoteList_VoteEnabled = true; +#if defined LOG + LogMessage("Forcing vote type to visible: %d", i); +#endif + hVoteTypes.SetArray(pos, voteType); + } + else + { +#if defined LOG + LogMessage("Creating override for vote type: %d", i); +#endif + voteType.CallVoteList_VoteType = view_as(i); + voteType.CallVoteList_VoteEnabled = true; + hVoteTypes.PushArray(voteType); + } + } + } + + if (overridesPresent) + { + PerformVisChecks(client, hVoteTypes); +#if defined LOG + LogMessage("Overriding VoteSetup message"); +#endif + Game_DisplayVoteSetup(client, hVoteTypes); + delete hVoteTypes; + return Plugin_Handled; + } + else + { + delete hVoteTypes; + return Plugin_Continue; + } + } + + default: + { + char voteCommand[VOTE_STRING_SIZE]; + GetCmdArg(1, voteCommand, VOTE_STRING_SIZE); + +#if defined LOG + LogMessage("User is attempting to call %s", voteCommand); +#endif + + NativeVotesOverride overrideType = Game_VoteStringToVoteOverride(voteCommand); + + char argument[PLATFORM_MAX_PATH]; + + if (GetForwardFunctionCount(g_CallVotes[overrideType].CallVote_Forward) == 0) + { + + if (g_MapOverrides != null && + (overrideType == NativeVotesOverride_ChgLevel || + overrideType == NativeVotesOverride_NextLevel)) + { + char map[PLATFORM_MAX_PATH]; + + GetCmdArg(2, map, sizeof(map)); + g_MapOverrides.GetString(map, argument, sizeof(argument)); + + g_OverrideNextCallVote[client] = true; + FakeClientCommandEx(client, "callvote %s %s", voteCommand, argument); + return Plugin_Handled; + } + +#if defined LOG + LogMessage("We don't have a handler for %s, passing back to server", voteCommand); +#endif + return Plugin_Continue; + } + + // Vis checks are done here just in case something went wrong and the vote option was shown to a person it shouldn't be. +#if defined LOG + LogMessage("Calling visForward for %s", voteCommand); +#endif + Call_StartForward(g_CallVotes[overrideType].CallVote_Vis); + Call_PushCell(client); + Call_PushCell(overrideType); + Call_Finish(result); + if (result >= Plugin_Handled) + { + return result; + } + + NativeVotesType voteType = Game_VoteStringToVoteType(voteCommand); + + int target = 0; + + NativeVotesKickType kickType = NativeVotesKickType_None; + + switch (voteType) + { + case NativeVotesType_Kick: + { + char param1[20]; + GetCmdArg(2, param1, sizeof(param1)); + + kickType = Game_GetKickType(param1, target); + + int targetClient = GetClientOfUserId(target); + + if (targetClient < 1 || targetClient > MaxClients || !IsClientInGame(targetClient)) + { + return Plugin_Continue; + } + + GetClientName(targetClient, argument, sizeof(argument)); + } + case NativeVotesType_ChgLevel, NativeVotesType_NextLevel: + { + if (g_MapOverrides == null) + { + GetCmdArg(2, argument, sizeof(argument)); + } + else + { + char map[PLATFORM_MAX_PATH]; + + GetCmdArg(2, map, sizeof(map)); + g_MapOverrides.GetString(map, argument, sizeof(argument)); + } + } + default: + { + GetCmdArg(2, argument, sizeof(argument)); + } + } + +#if defined LOG + LogMessage("Calling callVoteForward for %s", voteCommand); +#endif + + Call_StartForward(g_CallVotes[overrideType].CallVote_Forward); + Call_PushCell(client); + Call_PushCell(overrideType); + Call_PushString(argument); + Call_PushCell(kickType); + Call_PushCell(target); + Call_Finish(result); + } + } + + // Default to continue if we're not processing things + return result; + +} + +stock int FindVoteInArray(ArrayList myArray, NativeVotesOverride value) +{ + int size = myArray.Length; + for (int i = 0; i < size; i++) + { + CallVoteListData voteData; + myArray.GetArray(i, voteData, sizeof(CallVoteListData)); + + if (voteData.CallVoteList_VoteType == value) + { + return i; + } + } + + return -1; +} + +stock bool IsVoteEnabled(ArrayList myArray, NativeVotesOverride value) +{ + int pos = FindVoteInArray(myArray, value); + if (pos > -1) + { + CallVoteListData voteType; + myArray.GetArray(pos, voteType, sizeof(CallVoteListData)); + if (voteType.CallVoteList_VoteEnabled) + return true; + } + return false; +} + +public void OnVoteDelayChange(ConVar convar, const char[] oldValue, const char[] newValue) +{ + /* See if the new vote delay isn't something we need to account for */ + if (convar.IntValue < 1) + { + g_NextVote = 0; + return; + } + + /* If there was never a last vote, ignore this change */ + if (g_NextVote <= 0) + { + return; + } + + /* Subtract the original value, then add the new one. */ + g_NextVote -= StringToInt(oldValue); + g_NextVote += StringToInt(newValue); +} + +public void OnMapEnd() +{ + if (g_hCurVote != null) + { + // Cancel the ongoing vote, but don't close the handle, as the other plugins may still re-use it + CancelVoting(); + //OnVoteCancel(g_hCurVote, VoteCancel_Generic); + g_hCurVote = null; + } + + if (g_hDisplayTimer != null) + { + delete g_hDisplayTimer; + } + +// g_hVoteTimer = INVALID_HANDLE; +} + +public Action Command_Vote(int client, const char[] command, int argc) +{ +#if defined LOG + char voteString[128]; + GetCmdArgString(voteString, sizeof(voteString)); + LogMessage("Client %N ran a vote command: %s", client, voteString); +#endif + + // If we're not running a vote, return the vote control back to the server + if (!Internal_IsVoteInProgress() || g_ClientVotes[client] != VOTE_PENDING) + { + return Plugin_Continue; + } + + char option[64]; + GetCmdArgString(option, sizeof(option)); + + int item = Game_ParseVote(option); + + // Make sure we don't go out of bounds on the vote + if (item == NATIVEVOTES_VOTE_INVALID || item > g_Items) + { + return Plugin_Handled; + } + + bool cancel; + + if (Data_GetFlags(g_hCurVote) & MENUFLAG_BUTTON_NOVOTE && item == 0) + { + cancel = true; + } + + if (cancel) + { + OnCancel(g_hCurVote, client, MenuCancel_Exit); + } + else + { + OnVoteSelect(g_hCurVote, client, item); + } + + OnClientEnd(); + + return Plugin_Handled; +} + +void OnVoteSelect(NativeVote vote, int client, int item) +{ + if (Internal_IsVoteInProgress() && g_ClientVotes[client] == VOTE_PENDING) + { + /* Check by our item count, NOT the vote array size */ + if (item < g_Items) + { + Game_ClientSelectedItem(vote, client, item); + + g_ClientVotes[client] = item; + g_hVotes.Set(item, g_hVotes.Get(item) + 1); + g_NumVotes++; + + Game_UpdateVoteCounts(g_hVotes, g_TotalClients); + + if (g_ConVars[progress_chat].BoolValue || g_ConVars[progress_console].BoolValue || g_ConVars[progress_client_console].BoolValue) + { + char choice[128]; + char name[MAX_NAME_LENGTH+1]; + Data_GetItemDisplay(vote, item, choice, sizeof(choice)); + + GetClientName(client, name, MAX_NAME_LENGTH); + + if (g_ConVars[progress_console].BoolValue) + { + PrintToServer("[%s] %T", LOGTAG, "Voted For", LANG_SERVER, name, choice); + } + + if (g_ConVars[progress_chat].BoolValue || g_ConVars[progress_client_console].BoolValue) + { + char phrase[30]; + + if (g_bRevoting[client]) + { + strcopy(phrase, sizeof(phrase), "Changed Vote"); + } + else + { + strcopy(phrase, sizeof(phrase), "Voted For"); + } + + if (g_ConVars[progress_chat].BoolValue) + { + int r, g, b, a, color; + GetEntityRenderColor(client, r, g, b, a); + color = (r << 16) | (g << 8) | b; + if (color != 0xFFFFFF) + { + Format(name, sizeof(name), "{#%06X}%N\x01", color, client); + } + else + { + Format(name, sizeof(name), "{teamcolor}%N\x01", client); + } + CPrintToChatAllEx(client, "[%s] %t", LOGTAG, phrase, name, choice); + } + + if (g_ConVars[progress_client_console].BoolValue) + { + for (int i = 1; i <= MaxClients; i++) + { + if (IsClientInGame(i) && !IsFakeClient(i)) + { + PrintToConsole(i, "[%s] %t", LOGTAG, phrase, name, choice); + } + } + } + } + } + + BuildVoteLeaders(); + DrawHintProgress(); + + OnSelect(g_hCurVote, client, item); + } + } +} + +//MenuAction_Select +void OnSelect(NativeVote vote, int client, int item) +{ + MenuAction actions = Data_GetActions(vote); + if (actions & MenuAction_Select) + { + DoAction(vote, MenuAction_Select, client, item); + } +} + +//MenuAction_End +void OnEnd(NativeVote vote, int item) +{ + // Always called + DoAction(vote, MenuAction_End, item, 0); +} + + +stock void OnVoteEnd(NativeVote vote, int item) +{ + // Always called + DoAction(vote, MenuAction_VoteEnd, item, 0); +} + +void OnVoteStart(NativeVote vote) +{ + // Fire both Start and VoteStart in the other plugin. + + MenuAction actions = Data_GetActions(vote); + if (actions & MenuAction_Start) + { + DoAction(vote, MenuAction_Start, 0, 0); + } + + // Always called + DoAction(vote, MenuAction_VoteStart, 0, 0); +} + +void OnVoteCancel(NativeVote vote, int reason) +{ + // Always called + DoAction(vote, MenuAction_VoteCancel, reason, 0); +} + +void OnCancel(NativeVote vote, int client, int reason) +{ + DoAction(vote, MenuAction_Cancel, client, reason); +} + +void OnClientEnd() +{ + DecrementPlayerCount(); +} + +Action DoAction(NativeVote vote, MenuAction action, int param1, int param2, Action def_res = Plugin_Continue) +{ + Action res = def_res; + + Handle handler = CloneHandle(Data_GetHandler(vote)); +#if defined LOG + LogMessage("Calling Menu forward for vote: %d, handler: %d, action: %d, param1: %d, param2: %d", vote, handler, action, param1, param2); +#endif + Call_StartForward(handler); + Call_PushCell(vote); + Call_PushCell(action); + Call_PushCell(param1); + Call_PushCell(param2); + Call_Finish(res); + delete handler; + return res; +} + +void OnVoteResults(NativeVote vote, const int[][] votes, int num_votes, int item_count, const int[][] client_list, int num_clients) +{ + Handle resultsHandler = Data_GetResultCallback(vote); + + if (resultsHandler == null || !GetForwardFunctionCount(resultsHandler)) + { + /* Call MenuAction_VoteEnd instead. See if there are any extra winners. */ + int num_items = 1; + for (int i = 1; i < num_votes; i++) + { + if (votes[i][VOTEINFO_ITEM_VOTES] != votes[0][VOTEINFO_ITEM_VOTES]) + { + break; + } + num_items++; + } + + /* See if we need to pick a random winner. */ + int winning_item; + if (num_items > 1) + { + /* Yes, we do */ + winning_item = GetRandomInt(0, num_items - 1); + winning_item = votes[winning_item][VOTEINFO_ITEM_INDEX]; + } + else + { + /* No, take the first */ + winning_item = votes[0][VOTEINFO_ITEM_INDEX]; + } + + int winning_votes = votes[0][VOTEINFO_ITEM_VOTES]; + + DoAction(vote, MenuAction_VoteEnd, winning_item, (num_votes << 16) | (winning_votes & 0xFFFF)); + } + else + { + // This code is quite different than its C++ version, as we're reversing the logic previously done + + int[] client_indexes = new int[num_clients]; + int[] client_items = new int[num_clients]; + int[] vote_items = new int[item_count]; + int[] vote_votes = new int[item_count]; + + /* First array */ + for (int i = 0; i < item_count; i++) + { + vote_items[i] = votes[i][VOTEINFO_ITEM_INDEX]; + vote_votes[i] = votes[i][VOTEINFO_ITEM_VOTES]; + } + + /* Second array */ + for (int i = 0; i < num_clients; i++) + { + client_indexes[i] = client_list[i][VOTEINFO_CLIENT_INDEX]; + client_items[i] = client_list[i][VOTEINFO_CLIENT_ITEM]; + } + +#if defined LOG + LogMessage("Calling results forward for vote: %d, num_votes: %d, num_clients: %d, item_count: %d", vote, num_votes, num_clients, item_count); +#endif + + Call_StartForward(resultsHandler); + Call_PushCell(vote); + Call_PushCell(num_votes); + Call_PushCell(num_clients); + Call_PushArray(client_indexes, num_clients); + Call_PushArray(client_items, num_clients); + Call_PushCell(item_count); + Call_PushArray(vote_items, item_count); + Call_PushArray(vote_votes, item_count); + Call_Finish(); + } +} + +/* +VoteEnd(Handle:vote) +{ + if (g_NumVotes == 0) + { + // Fire VoteCancel in the other plugin + OnVoteCancel(vote, VoteCancel_NoVotes); + } + else + { + new num_items; + new num_votes; + + new slots = Game_GetMaxItems(); + new votes[slots][2]; + + Internal_GetResults(votes, slots); + + if (!SendResultCallback(vote, num_votes, num_items, votes)) + { + Handle handler = Data_GetHandler(g_hCurVote); + + Call_StartForward(handler); + Call_PushCell(g_CurVote); + Call_PushCell(MenuAction_VoteEnd); + Call_PushCell(votes[0][VOTEINFO_ITEM_INDEX]); + Call_PushCell(0); + Call_Finish(); + } + } + +} + +bool:SendResultCallback(Handle:vote, num_votes, num_items, const votes[][]) +{ + Handle voteResults = Data_GetResultCallback(g_CurVote); + if (GetForwardFunctionCount(voteResults) == 0) + { + return false; + } + + // This block is present because we can't pass 2D arrays to other plugins' functions + new item_indexes[]; + new item_votes[]; + + for (int i = 0, i < num_items; i++) + { + item_indexes[i] = votes[i][VOTEINFO_ITEM_INDEX]; + item_votes[i] = votes[i][VOTEINFO_ITEM_VOTES]; + } + + // Client block + new client_indexes[MaxClients]; + new client_votes[MaxClients]; + + new num_clients; + for (new i = 1; i <= MaxClients; i++) + { + if (g_ClientVotes[i] > VOTE_PENDING) + { + client_indexes[num_clients] = i; + client_votes[num_clients] = g_ClientVotes[i]; + num_clients++; + } + } + + Call_StartForward(voteResults); + Call_PushCell(_:vote); + Call_PushCell(num_votes); + Call_PushCell(num_clients); + Call_PushArray(client_indexes, num_clients); + Call_PushArray(client_votes, num_clients); + Call_PushCell(num_items); + Call_PushArray(item_indexes, num_items); + Call_PushArray(item_votes, num_items); + Call_Finish(); + + return true; +} +*/ + +void DrawHintProgress() +{ + if (!g_ConVars[progress_hintbox].BoolValue || NativeVotes_GetType(g_hCurVote) == NativeVotesType_Custom_YesNo) + { + return; + } + + float timeRemaining = (g_fStartTime + g_VoteTime) - GetGameTime(); + + if (timeRemaining < 0.0) + { + timeRemaining = 0.0; + } + + int iTimeRemaining = RoundFloat(timeRemaining); + + PrintHintTextToAll("%t%s", "Vote Count", g_NumVotes, g_TotalClients, iTimeRemaining, g_LeaderList); +} + +void BuildVoteLeaders() +{ + if (g_NumVotes == 0 || !g_ConVars[progress_hintbox].BoolValue) + { + return; + } + + // Since we can't have structs, we get "struct" with this instead + + int slots = Game_GetMaxItems(); + int[][] votes = new int[slots][2]; + + int num_items = Internal_GetResults(votes); + + /* Take the top 3 (if applicable) and draw them */ + g_LeaderList[0] = '\0'; + + for (int i = 0; i < num_items && i < 3; i++) + { + int cur_item = votes[i][VOTEINFO_ITEM_INDEX]; + char choice[256]; + Data_GetItemDisplay(g_hCurVote, cur_item, choice, sizeof(choice)); + Format(g_LeaderList, sizeof(g_LeaderList), "%s\n%i. %s: (%i)", g_LeaderList, i+1, choice, votes[i][VOTEINFO_ITEM_VOTES]); + } + +} + +public int SortVoteItems(int[] a, int[] b, const int[][] array, Handle hndl) +{ + if (b[VOTEINFO_ITEM_VOTES] == a[VOTEINFO_ITEM_VOTES]) + { + return 0; + } + else if (b[VOTEINFO_ITEM_VOTES] > a[VOTEINFO_ITEM_VOTES]) + { + return 1; + } + else + { + return -1; + } +} + +void DecrementPlayerCount() +{ + g_Clients--; + + // The vote is running and we have no clients left, so end the vote. + if (g_bStarted && g_Clients == 0) + { + EndVoting(); + } + +} + + +void EndVoting() +{ + int voteDelay = g_ConVars[vote_delay].IntValue; + if (voteDelay < 1) + { + g_NextVote = 0; + } + else + { + g_NextVote = GetTime() + voteDelay; + } + + if (g_hDisplayTimer != null) + { + delete g_hDisplayTimer; + } + + if (g_bCancelled) + { + /* If we were cancelled, don't bother tabulating anything. + * Reset just in case someone tries to redraw, which means + * we need to save our states. + */ + NativeVote vote = g_hCurVote; + Internal_Reset(true); + OnVoteCancel(vote, VoteCancel_Generic); + OnEnd(vote, MenuEnd_VotingCancelled); + return; + } + + int slots = Game_GetMaxItems(); + int[][] votes = new int[slots][2]; + int num_votes; + int num_items = Internal_GetResults(votes, num_votes); + + if (!num_votes) + { + NativeVote vote = g_hCurVote; + Internal_Reset(); + OnVoteCancel(vote, VoteCancel_NoVotes); + OnEnd(vote, MenuEnd_VotingCancelled); + return; + } + + int[][] client_list = new int[MaxClients][2]; + int num_clients = Internal_GetClients(client_list); + + /* Save states, then clear what we've saved. + * This makes us re-entrant, which is always the safe way to go. + */ + NativeVote vote = g_hCurVote; + Internal_Reset(); + +#if defined LOG + LogMessage("Voting done"); +#endif + + /* Send vote info */ + OnVoteResults(vote, votes, num_votes, num_items, client_list, num_clients); + OnEnd(vote, MenuEnd_VotingDone); +} + +bool StartVote(NativeVote vote, int num_clients, int[] clients, int max_time, int flags) +{ + if (!InitializeVoting(vote, max_time, flags)) + { + return false; + } + + /* Due to hibernating servers, we no longer use GameTime, but instead standard timestamps. + */ + + int voteDelay = g_ConVars[vote_delay].IntValue; + if (voteDelay < 1) + { + g_NextVote = 0; + } + else + { + /* This little trick break for infinite votes! + * However, we just ignore that since those 1) shouldn't exist and + * 2) people must be checking IsVoteInProgress() beforehand anyway. + */ + g_NextVote = GetTime() + voteDelay + max_time; + } + + g_fStartTime = GetGameTime(); + g_VoteTime = max_time; + g_TimeLeft = max_time; + + int clientCount = 0; + + for (int i = 0; i < num_clients; ++i) + { + if (clients[i] < 1 || clients[i] > MaxClients) + { + continue; + } + + g_ClientVotes[clients[i]] = VOTE_PENDING; + clientCount++; + } + + g_Clients = clientCount; + + Game_UpdateVoteCounts(g_hVotes, clientCount); + + DoClientVote(vote, clients, num_clients); + + StartVoting(); + + DrawHintProgress(); + + return true; +} + +bool DoClientVote(NativeVote vote, int[] clients, int num_clients) +{ + int totalPlayers = 0; + int[] realClients = new int[MaxClients+1]; + + for (int i = 0; i < num_clients; ++i) + { + if (clients[i] < 1 || clients[i] > MaxClients || !IsClientInGame(clients[i]) || IsFakeClient(clients[i])) + { + continue; + } + + realClients[totalPlayers++] = clients[i]; + } + + if (totalPlayers > 0) + { + Game_DisplayVote(vote, realClients, totalPlayers); + return true; + } + else + { + return false; + } +} + +bool InitializeVoting(NativeVote vote, int time, int flags) +{ + if (Internal_IsVoteInProgress()) + { + return false; + } + + Internal_Reset(); + + /* Mark all clients as not voting */ + for (int i = 1; i <= MaxClients; ++i) + { + g_ClientVotes[i] = VOTE_NOT_VOTING; + g_bRevoting[i] = false; + } + + g_Items = Data_GetItemCount(vote); + + // Clear all items + for (int i = 0; i < g_hVotes.Length; ++i) + { + g_hVotes.Set(i, 0); + } + + g_hCurVote = vote; + g_VoteTime = time; + g_VoteFlags = flags; + + return true; +} + +void StartVoting() +{ + if (g_hCurVote == null) + { + return; + } + + g_bStarted = true; + + OnVoteStart(g_hCurVote); + + g_hDisplayTimer = CreateTimer(1.0, DisplayTimer, _, TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE); + + g_TotalClients = g_Clients; + + /* By now we know how many clients were set. + * If there are none, we should end IMMEDIATELY. + */ + if (g_Clients == 0) + { + EndVoting(); + return; + } + + // Kick targets automatically vote no if they're in the pool + NativeVotesType voteType = Data_GetType(g_hCurVote); + + switch (voteType) + { + case NativeVotesType_Kick, NativeVotesType_KickCheating, NativeVotesType_KickIdle, NativeVotesType_KickScamming: + { + int target = Data_GetTarget(g_hCurVote); + + if (target > 0 && target <= MaxClients && IsClientConnected(target) && Internal_IsClientInVotePool(target)) + { + Game_VoteNo(target); + } + } + } + + // Initiators always vote yes when they're in the pool. + if (voteType != NativeVotesType_Custom_Mult && voteType != NativeVotesType_NextLevelMult) + { + int initiator = Data_GetInitiator(g_hCurVote); + + if (initiator > 0 && initiator <= MaxClients && IsClientConnected(initiator) && Internal_IsClientInVotePool(initiator)) + { + Game_VoteYes(initiator); + } + } +} + +public Action DisplayTimer(Handle timer) +{ + if (!Internal_IsVoteInProgress() || g_TimeLeft <= 0) + { + if (g_hDisplayTimer != null) + { + g_hDisplayTimer = null; + EndVoting(); + } + return Plugin_Stop; + } + DrawHintProgress(); + --g_TimeLeft; + return Plugin_Continue; +} + +int Internal_GetResults(int[][] votes, int &num_votes=0) +{ + if (!Internal_IsVoteInProgress()) + { + return 0; + } + + // Since we can't have structs, we get "struct" with this instead + int num_items; + + num_votes = 0; + + for (int i = 0; i < g_Items; i++) + { + int voteCount = g_hVotes.Get(i); + if (voteCount > 0) + { + votes[num_items][VOTEINFO_ITEM_INDEX] = i; + votes[num_items][VOTEINFO_ITEM_VOTES] = voteCount; + num_votes += voteCount; + num_items++; + } + } + + /* Sort the item list descending like we promised */ + SortCustom2D(votes, num_items, SortVoteItems); + + return num_items; +} + +int Internal_GetClients(int[][] client_vote) +{ + if (!Internal_IsVoteInProgress()) + { + return 0; + } + + /* Build the client list */ + int num_clients; + + for (int i = 1; i <= MaxClients; i++) + { + if (g_ClientVotes[i] >= VOTE_PENDING) + { + client_vote[num_clients][VOTEINFO_CLIENT_INDEX] = i; + client_vote[num_clients][VOTEINFO_CLIENT_ITEM] = g_ClientVotes[i]; + num_clients++; + } + } + + return num_clients; +} + +bool Internal_IsCancelling() +{ + return g_bCancelled; +} + +stock NativeVote Internal_GetCurrentVote() +{ + return g_hCurVote; +} + +void Internal_Reset(bool cancel=false) +{ + g_Clients = 0; + g_Items = 0; + g_bStarted = false; + g_hCurVote = null; + g_NumVotes = 0; + g_bCancelled = false; + g_LeaderList[0] = '\0'; + g_TotalClients = 0; + + if (g_hDisplayTimer != null) + { + delete g_hDisplayTimer; + } + + if (!cancel) + { + CreateTimer(5.0, Game_ResetVote, TIMER_FLAG_NO_MAPCHANGE); + } +} + +bool Internal_IsVoteInProgress() +{ + return (g_hCurVote != INVALID_HANDLE); +} + +bool Internal_IsClientInVotePool(int client) +{ + if (client < 1 || client > MaxClients || g_hCurVote == null) + { + return false; + } + + return (g_ClientVotes[client] > VOTE_NOT_VOTING); +} + +bool Internal_RedrawToClient(int client, bool revotes) +{ + if (!Internal_IsVoteInProgress() || !Internal_IsClientInVotePool(client)) + { + return false; + } + + if (g_ClientVotes[client] >= 0) + { + if ((g_VoteFlags & VOTEFLAG_NO_REVOTES) || !revotes || g_VoteTime <= VOTE_DELAY_TIME) + { + return false; + } + + g_Clients++; + SetArrayCell(g_hVotes, g_ClientVotes[client], GetArrayCell(g_hVotes, g_ClientVotes[client]) - 1); + g_ClientVotes[client] = VOTE_PENDING; + g_bRevoting[client] = true; + g_NumVotes--; + Game_UpdateVoteCounts(g_hVotes, g_TotalClients); + } + + // Display the vote fail screen for a few seconds + //Game_DisplayVoteFail(g_hCurVote, NativeVotesFail_Generic, client); + + // No, display a vote pass screen because that's nicer and we can customize it. + // @note This isn't inside the earlier if because some players have had issues where the display doesn't always appear the first time. + char revotePhrase[128]; + Format(revotePhrase, sizeof(revotePhrase), "%T", "NativeVotes Revote", client); + Game_DisplayVotePassCustom(g_hCurVote, revotePhrase, client); + + DataPack data; + + CreateDataTimer(VOTE_DELAY_TIME, RedrawTimer, data, TIMER_FLAG_NO_MAPCHANGE); + data.WriteCell(GetClientUserId(client)); + data.WriteCell(view_as(g_hCurVote)); + + return true; +} + +public Action RedrawTimer(Handle timer, DataPack data) +{ + if (g_hCurVote == null) + { + return Plugin_Stop; + } + + ResetPack(data); + int client = GetClientOfUserId(data.ReadCell()); + if (client == 0) + { + return Plugin_Stop; + } + + NativeVote vote = view_as(data.ReadCell()); + + if (Internal_IsVoteInProgress() && !Internal_IsCancelling() && vote == g_hCurVote) + { + Game_DisplayVoteToOne(vote, client); + } + + return Plugin_Stop; +} + +void CancelVoting() +{ + if (g_bCancelled || g_hCurVote == INVALID_HANDLE) + { + return; + } + + g_bCancelled = true; + + EndVoting(); +} + +void PerformVisChecks(int client, ArrayList hVoteTypes) +{ + // Iterate backwards so we can safely remove items + for (int i = hVoteTypes.Length - 1; i >= 0; i--) + { + CallVoteListData voteData; + hVoteTypes.GetArray(i, voteData, sizeof(CallVoteListData)); + + Action hide = Plugin_Continue; + +#if defined LOG + LogMessage("Checking visibility forward for %d: %d", voteData.CallVoteList_VoteType, g_CallVotes[voteData.CallVoteList_VoteType].CallVote_Vis); +#endif + Call_StartForward(g_CallVotes[voteData.CallVoteList_VoteType].CallVote_Vis); + Call_PushCell(client); + Call_PushCell(voteData.CallVoteList_VoteType); + Call_Finish(hide); + if (hide >= Plugin_Handled) + { +#if defined LOG + LogMessage("Hiding vote type %d", voteData.CallVoteList_VoteType); +#endif + if (Game_AreDisabledIssuesHidden()) + { + // Since we hide disabled issues, remove it from the arraylist + RemoveFromArray(hVoteTypes, i); + } + else + { + // Arrays are pass by ref, so this should update the one inside the ArrayList + voteData.CallVoteList_VoteEnabled = false; + } + } + } +} + +//---------------------------------------------------------------------------- +// Natives + +// native bool NativeVotes_IsVoteTypeSupported(NativeVotesType voteType); +public int Native_IsVoteTypeSupported(Handle plugin, int numParams) +{ + NativeVotesType type = GetNativeCell(1); + + return Game_CheckVoteType(type); +} + +// native Handle NativeVotes_Create(MenuHandler handler, NativeVotesType voteType, MenuAction actions = NATIVEVOTES_ACTIONS_DEFAULT); +public int Native_Create(Handle plugin, int numParams) +{ + Function handler = GetNativeFunction(1); + NativeVotesType voteType = GetNativeCell(2); + MenuAction actions = GetNativeCell(3); + + if (handler == INVALID_FUNCTION) + { + ThrowNativeError(SP_ERROR_NATIVE, "Menuhandler is invalid"); + } + + NativeVote vote; + if (Game_CheckVoteType(voteType)) + { + vote = Data_CreateVote(voteType, actions); + } + else + { + return view_as(INVALID_HANDLE); + } + + if (voteType != NativeVotesType_NextLevelMult && voteType != NativeVotesType_Custom_Mult) + { + Data_AddItem(vote, "yes", "Yes"); + Data_AddItem(vote, "no", "No"); + } + + Handle menuForward = Data_GetHandler(vote); + + AddToForward(menuForward, plugin, handler); + + return view_as(vote); +} + +// native Handle NativeVotes_Close(Handle vote); +public int Native_Close(Handle plugin, int numParams) +{ + NativeVote vote = GetNativeCell(1); + + if (vote == null) + { + return; + } + + if (g_hCurVote == vote) + { + CancelVoting(); + g_hCurVote = null; + + /* + if (g_hVoteTimer != INVALID_HANDLE) + { + KillTimer(g_hVoteTimer); + g_hVoteTimer = INVALID_HANDLE; + } + */ + } + + // This bit is necessary because the Forward system appears to not remove these when the forward Handle is closed + // This was necessary in SM 1.5.x, but has a REALLY high probability of crashing in SM 1.6, plus is no longer needed + //Handle menuForward = Data_GetHandler(vote); + //RemoveAllFromForward(menuForward, plugin); + + //Handle voteResults = Data_GetResultCallback(vote); + //RemoveAllFromForward(voteResults, plugin); + + // Do the datatype-specific close operations + Data_CloseVote(vote); +} + +// native bool NativeVotes_Display(Handle vote, int clients[], int numClients, int time, int flags=0); +public int Native_Display(Handle plugin, int numParams) +{ + if (Internal_IsVoteInProgress()) + { + ThrowNativeError(SP_ERROR_NATIVE, "A vote is already in progress"); + } + + NativeVote vote = GetNativeCell(1); + if (vote == null) + { + ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); + return false; + } + + int count = GetNativeCell(3); + int[] clients = new int[count]; + GetNativeArray(2, clients, count); + + // Note: Only one flag exists: VOTEFLAG_NO_REVOTES + int flags = 0; + + if (numParams >= 5) + { + flags = GetNativeCell(5); + } + + if (!StartVote(vote, count, clients, GetNativeCell(4), flags)) + { + return 0; + } + + return 1; + +} + +// native bool NativeVotes_AddItem(Handle vote, const char[] info, const char[] display); +public int Native_AddItem(Handle plugin, int numParams) +{ + NativeVote vote = GetNativeCell(1); + if (vote == null) + { + ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); + return false; + } + + NativeVotesType voteType = Data_GetType(vote); + + if (voteType != NativeVotesType_NextLevelMult && voteType != NativeVotesType_Custom_Mult) + { + return false; + } + + char info[256]; + char display[256]; + GetNativeString(2, info, sizeof(info)); + GetNativeString(3, display, sizeof(display)); + + return Data_AddItem(vote, info, display); +} + +// native bool NativeVotes_InsertItem(Handle vote, int position, const char[] info, const char[] display); +public int Native_InsertItem(Handle plugin, int numParams) +{ + NativeVote vote = GetNativeCell(1); + if (vote == null) + { + ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); + return false; + } + + NativeVotesType voteType = Data_GetType(vote); + + if (voteType != NativeVotesType_NextLevelMult && voteType != NativeVotesType_Custom_Mult) + { + return false; + } + + int position = GetNativeCell(2); + + if (position < 0) + { + ThrowNativeError(SP_ERROR_NATIVE, "Vote index can't be negative: %d", position); + return false; + } + + char info[256]; + char display[256]; + GetNativeString(3, info, sizeof(info)); + GetNativeString(4, display, sizeof(display)); + + return Data_InsertItem(vote, position, info, display); + +} + +// native bool NativeVotes_RemoveItem(Handle vote, int position); +public int Native_RemoveItem(Handle plugin, int numParams) +{ + NativeVote vote = GetNativeCell(1); + if (vote == null) + { + ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); + return false; + } + + NativeVotesType voteType = Data_GetType(vote); + + if (voteType != NativeVotesType_NextLevelMult && voteType != NativeVotesType_Custom_Mult) + { + return false; + } + + int position = GetNativeCell(2); + + return Data_RemoveItem(vote, position); +} + +// native NativeVotes_RemoveAllItems(Handle vote); +public int Native_RemoveAllItems(Handle plugin, int numParams) +{ + NativeVote vote = GetNativeCell(1); + if (vote == null) + { + ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); + return; + } + + Data_RemoveAllItems(vote); +} + +// native bool NativeVotes_GetItem(Handle vote, int position, const char[] infoBuf, int infoBufLen, const char[] dispBuf = "", int dispBufLen = 0); +public int Native_GetItem(Handle plugin, int numParams) +{ + NativeVote vote = GetNativeCell(1); + if (vote == null) + { + ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); + return; + } + + int position = GetNativeCell(2); + + int infoLength = GetNativeCell(4); + char[] info = new char[infoLength]; + Data_GetItemInfo(vote, position, info, infoLength); + SetNativeString(3, info, infoLength); + + if (numParams >= 6) + { + int displayLength = GetNativeCell(6); + if (displayLength > 0) + { + char[] display = new char[displayLength]; + Data_GetItemDisplay(vote, position, display, displayLength); + SetNativeString(5, display, displayLength); + } + } +} + +// native NativeVotes_GetItemCount(Handle vote); +public int Native_GetItemCount(Handle plugin, int numParams) +{ + NativeVote vote = GetNativeCell(1); + if (vote == null) + { + ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); + return 0; + } + + return Data_GetItemCount(vote); +} + +// native NativeVotes_GetDetails(Handle vote, const char[] buffer, int maxlength); +public int Native_GetDetails(Handle plugin, int numParams) +{ + NativeVote vote = GetNativeCell(1); + if (vote == null) + { + ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); + return; + } + + int len = GetNativeCell(3); + + char[] details = new char[len]; + + Data_GetDetails(vote, details, len); + + SetNativeString(2, details, len); +} + +// native NativeVotes_SetDetails(Handle vote, const char[] fmt, any:...); +public int Native_SetDetails(Handle plugin, int numParams) +{ + NativeVote vote = GetNativeCell(1); + if (vote == null) + { + ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); + return; + } + + char details[MAX_VOTE_DETAILS_LENGTH]; + + //SetGlobalTransTarget(LANG_SERVER); + FormatNativeString(0, 2, 3, sizeof(details), _, details); + + Data_SetDetails(vote, details); +} + +// native NativeVotes_GetDetails(Handle vote, const char[] buffer, int maxlength); +public int Native_GetTitle(Handle plugin, int numParams) +{ + NativeVote vote = GetNativeCell(1); + if (vote == null) + { + ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); + return; + } + + int len = GetNativeCell(3); + + char[] title = new char[len]; + + Data_GetTitle(vote, title, len); + + SetNativeString(2, title, len); +} + +// native NativeVotes_SetTitle(Handle vote, const char[] fmt, any:...); +public int Native_SetTitle(Handle plugin, int numParams) +{ + NativeVote vote = GetNativeCell(1); + if (vote == null) + { + ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); + return; + } + + char details[MAX_VOTE_DETAILS_LENGTH]; + + //SetGlobalTransTarget(LANG_SERVER); + FormatNativeString(0, 2, 3, sizeof(details), _, details); + + Data_SetTitle(vote, details); +} + +// native bool NativeVotes_IsVoteInProgress(); +public int Native_IsVoteInProgress(Handle plugin, int numParams) +{ + return Internal_IsVoteInProgress() || Game_IsVoteInProgress(); +} + +// native NativeVotes_GetMaxItems(); +public int Native_GetMaxItems(Handle plugin, int numParams) +{ + return Game_GetMaxItems(); +} + +// native NativeVotes_GetOptionFlags(Handle vote); +public int Native_GetOptionFlags(Handle plugin, int numParams) +{ + NativeVote vote = GetNativeCell(1); + if (vote == null) + { + ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); + return 0; + } + + return Data_GetFlags(vote); +} + +// native NativeVotes_SetOptionFlags(Handle vote, flags); +public int Native_SetOptionFlags(Handle plugin, int numParams) +{ + NativeVote vote = GetNativeCell(1); + if (vote == null) + { + ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); + return; + } + + int flags = GetNativeCell(2); + + // This is an ORed group of flags to strip the ones we don't support + flags &= (MENUFLAG_BUTTON_NOVOTE); + + Data_SetFlags(vote, flags); +} + +// native bool NativeVotes_SetNoVoteButton(Handle vote, bool button); +public int Native_SetNoVoteButton(Handle plugin, int numParams) +{ + NativeVote vote = GetNativeCell(1); + if (vote == null) + { + return ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); + } + + int flags = Data_GetFlags(vote); + + if (GetNativeCell(2)) + { + flags |= MENUFLAG_BUTTON_NOVOTE; + } + else + { + flags &= ~MENUFLAG_BUTTON_NOVOTE; + } + + Data_SetFlags(vote, flags); + + int newflags = Data_GetFlags(vote); + + return (flags == newflags); +} + +// native NativeVotes_Cancel(); +public int Native_Cancel(Handle plugin, int numParams) +{ + if (!Internal_IsVoteInProgress()) + { + ThrowNativeError(SP_ERROR_NATIVE, "No vote is in progress"); + return; + } + + CancelVoting(); +} + +// native NativeVotes_SetResultCallback(Handle vote, NativeVotes_VoteHandler callback); +public int Native_SetResultCallback(Handle plugin, int numParams) +{ + NativeVote vote = GetNativeCell(1); + if (vote == null) + { + ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); + return; + } + + Function handler = GetNativeFunction(2); + + if (handler == INVALID_FUNCTION) + { + ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes_VoteHandler is invalid"); + return; + } + + Handle voteResults = Data_GetResultCallback(vote); + + RemoveAllFromForward(voteResults, plugin); + if (!AddToForward(voteResults, plugin, handler)) + { + ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes_VoteHandler cannot be added to forward"); + } +} + +// native NativeVotes_CheckVoteDelay(); +public int Native_CheckVoteDelay(Handle plugin, int numParams) +{ + int curTime = GetTime(); + if (g_NextVote <= curTime) + { + return 0; + } + + return (g_NextVote - curTime); +} + +// native bool NativeVotes_IsClientInVotePool(int client); +public int Native_IsClientInVotePool(Handle plugin, int numParams) +{ + int client = GetNativeCell(1); + + if (!client || client > MaxClients) + { + ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index %d", client); + return false; + } + + if (!Internal_IsVoteInProgress()) + { + ThrowNativeError(SP_ERROR_NATIVE, "No vote is in progress"); + return false; + } + + return Internal_IsClientInVotePool(client); +} + +// native bool NativeVotes_RedrawClientVote(int client, bool revotes = true); +public int Native_RedrawClientVote(Handle plugin, int numParams) +{ + int client = GetNativeCell(1); + + if (client < 1 || client > MaxClients || !IsClientConnected(client)) + { + ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index %d", client); + return false; + } + + if (!Internal_IsVoteInProgress()) + { + // When revoting in TF2, NativeVotes_IsVoteInProgress always gets skipped because of Game_IsVoteInProgress() + // TF2's vote controller will stay alive a few seconds after the vote is complete + // If one tries to revote right as a vote completes, it will throw an error + LogError("No vote is in progress"); + return false; + } + + if (!Internal_IsClientInVotePool(client)) + { + ThrowNativeError(SP_ERROR_NATIVE, "Client is not in the voting pool"); + return false; + } + + bool revote = true; + if (numParams >= 2 && !GetNativeCell(2)) + { + revote = false; + } + + return Internal_RedrawToClient(client, revote); +} + +// native NativeVotesType NativeVotes_GetType(Handle vote); +public int Native_GetType(Handle plugin, int numParams) +{ + NativeVote vote = GetNativeCell(1); + if (vote == null) + { + ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); + return 0; + } + + return view_as(Data_GetType(vote)); +} + +// native NativeVotes_GetTeam(Handle vote); +public int Native_GetTeam(Handle plugin, int numParams) +{ + NativeVote vote = GetNativeCell(1); + if (vote == null) + { + ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); + return NATIVEVOTES_ALL_TEAMS; + } + + return Data_GetTeam(vote); + +} + +// native NativeVotes_SetTeam(Handle vote, int team); +public int Native_SetTeam(Handle plugin, int numParams) +{ + NativeVote vote = GetNativeCell(1); + if (vote == null) + { + ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); + return; + } + + int team = GetNativeCell(2); + + // Teams are numbered starting with 0 + // Currently 4 is the maximum (Unassigned, Spectator, Team 1, Team 2) + if (team >= GetTeamCount()) + { + ThrowNativeError(SP_ERROR_NATIVE, "Team %d is invalid", team); + return; + } + + if (g_EngineVersion == Engine_TF2 && team == NATIVEVOTES_ALL_TEAMS) + { + team = NATIVEVOTES_TF2_ALL_TEAMS; + } + + Data_SetTeam(vote, team); +} + +// native NativeVotes_GetInitiator(Handle vote); +public int Native_GetInitiator(Handle plugin, int numParams) +{ + NativeVote vote = GetNativeCell(1); + if (vote == null) + { + ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); + return NATIVEVOTES_SERVER_INDEX; + } + + return Data_GetInitiator(vote); +} + +// native int NativeVotes_SetInitiator(Handle vote, int client); +public int Native_SetInitiator(Handle plugin, int numParams) +{ + NativeVote vote = GetNativeCell(1); + if (vote == null) + { + ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); + return; + } + + int initiator = GetNativeCell(2); + Data_SetInitiator(vote, initiator); +} + +// native NativeVotes_DisplayPass(Handle vote, const char[] details = ""); +public int Native_DisplayPass(Handle plugin, int numParams) +{ + NativeVote vote = GetNativeCell(1); + if (vote == null) + { + ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); + return; + } + + if (numParams >= 2) + { + char winner[TRANSLATION_LENGTH]; + FormatNativeString(0, 2, 3, sizeof(winner), _, winner); + + Game_DisplayVotePass(vote, winner); + } + else + { + Game_DisplayVotePass(vote); + } + +} + +// native NativeVotes_DisplayPassCustomToOne(Handle vote, client, const char[] format, any:...); +public int Native_DisplayPassCustomToOne(Handle plugin, int numParams) +{ + NativeVote vote = GetNativeCell(1); + if (vote == null) + { + ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); + return; + } + + int client = GetNativeCell(2); + + char translation[TRANSLATION_LENGTH]; + + SetGlobalTransTarget(client); + FormatNativeString(0, 3, 4, TRANSLATION_LENGTH, _, translation); + + Game_DisplayVotePassCustom(vote, translation, client); +} + +// native NativeVotes_DisplayPassEx(Handle vote, NativeVotesPassType passType, const char[] details = ""); +public int Native_DisplayPassEx(Handle plugin, int numParams) +{ + NativeVote vote = GetNativeCell(1); + if (vote == null) + { + ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); + return; + } + + NativeVotesPassType passType = view_as(GetNativeCell(2)); + + if (!Game_CheckVotePassType(passType)) + { + ThrowNativeError(SP_ERROR_NATIVE, "Invalid vote pass type: %d", passType); + } + + if (numParams >= 3) + { + char winner[TRANSLATION_LENGTH]; + //SetGlobalTransTarget(LANG_SERVER); + FormatNativeString(0, 3, 4, sizeof(winner), _, winner); + + Game_DisplayVotePassEx(vote, passType, winner); + } + else + { + Game_DisplayVotePassEx(vote, passType); + } +} + +// native NativeVotes_DisplayRawPass(NativeVotesPassType:passType, const String:details[]="", team=NATIVEVOTES_ALL_TEAMS); +/* +public Native_DisplayRawPass(Handle:plugin, numParams) +{ + new NativeVotesPassType:passType = NativeVotesPassType:GetNativeCell(1); + + if (!Game_CheckVotePassType(passType)) + { + ThrowNativeError(SP_ERROR_NATIVE, "Invalid vote pass type: %d", passType); + } + + new len; + GetNativeStringLength(2, len); + new String:winner[len+1]; + GetNativeString(2, winner, len+1); + new team = GetNativeCell(3); + + if (g_EngineVersion == Engine_TF2 && team == NATIVEVOTES_ALL_TEAMS) + { + team = NATIVEVOTES_TF2_ALL_TEAMS; + } + + Game_DisplayRawVotePass(passType, winner, team); +} +*/ + +// native NativeVotes_DisplayRawPassToOne(int client, NativeVotesPassType passType, const char[] details = "", int team = NATIVEVOTES_ALL_TEAMS); +public int Native_DisplayRawPassToOne(Handle plugin, int numParams) +{ + int client = GetNativeCell(1); + NativeVotesPassType passType = view_as(GetNativeCell(2)); + + if (!Game_CheckVotePassType(passType)) + { + ThrowNativeError(SP_ERROR_NATIVE, "Invalid vote pass type: %d", passType); + } + + int team = GetNativeCell(3); + + if (g_EngineVersion == Engine_TF2 && team == NATIVEVOTES_ALL_TEAMS) + { + team = NATIVEVOTES_TF2_ALL_TEAMS; + } + + if (numParams >= 4) + { + char winner[TRANSLATION_LENGTH]; + SetGlobalTransTarget(client); + FormatNativeString(0, 4, 5, sizeof(winner), _, winner); + + Game_DisplayRawVotePass(passType, team, client, winner); + } + else + { + Game_DisplayRawVotePass(passType, team, client); + } +} +// native NativeVotes_DisplayRawPassCustomToOne(int client, int team, const char[] format, any:...); +public int Native_DisplayRawPassCustomToOne(Handle plugin, int numParams) +{ + int client = GetNativeCell(1); + int team = GetNativeCell(2); + + char translation[TRANSLATION_LENGTH]; + + SetGlobalTransTarget(client); + FormatNativeString(0, 3, 4, TRANSLATION_LENGTH, _, translation); + + Game_DisplayRawVotePassCustom(translation, team, client); +} + +// native NativeVotes_DisplayFail(Handle vote, NativeVotesFailType reason = NativeVotesFail_Generic); +public int Native_DisplayFail(Handle plugin, int numParams) +{ + NativeVote vote = GetNativeCell(1); + if (vote == null) + { + ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); + return; + } + + NativeVotesFailType reason = view_as(GetNativeCell(2)); + + Game_DisplayVoteFail(vote, reason); +} + +// native NativeVotes_DisplayRawFail(NativeVotesFailType reason = NativeVotesFail_Generic, int team = NATIVEVOTES_ALL_TEAMS); +public int Native_DisplayRawFail(Handle plugin, int numParams) +{ + int size = GetNativeCell(2); + int[] clients = new int[size]; + GetNativeArray(1, clients, size); + + NativeVotesFailType reason = view_as(GetNativeCell(3)); + + int team = GetNativeCell(4); + + if (g_EngineVersion == Engine_TF2 && team == NATIVEVOTES_ALL_TEAMS) + { + team = NATIVEVOTES_TF2_ALL_TEAMS; + } + + Game_DisplayRawVoteFail(clients, size, reason, team); +} + +// native NativeVotes_DisplayRawFailToOne(int client, NativeVotesFailType reason = NativeVotesFail_Generic, int team = NATIVEVOTES_ALL_TEAMS); +/* +public Native_DisplayRawFailToOne(Handle plugin, int numParams) +{ + int client = GetNativeCell(1); + + NativeVotesFailType reason = view_as(GetNativeCell(2)); + + int team = GetNativeCell(3); + + if (g_EngineVersion == Engine_TF2 && team == NATIVEVOTES_ALL_TEAMS) + { + team = NATIVEVOTES_TF2_ALL_TEAMS; + } + + Game_DisplayRawVoteFail(reason, team, client); +} +*/ +// native NativeVotes_GetTarget(Handle vote); +public int Native_GetTarget(Handle plugin, int numParams) +{ + NativeVote vote = GetNativeCell(1); + if (vote == null) + { + ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); + return 0; + } + + int target = Data_GetTarget(vote); + + if (target == 0) + { + // No target was set, return -1 + return -1; + } + + return GetClientOfUserId(target); +} + +// native NativeVotes_GetTargetSteam(Handle vote, const char[] buffer, maxlength); +public int Native_GetTargetSteam(Handle plugin, int numParams) +{ + NativeVote vote = GetNativeCell(1); + if (vote == null) + { + ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); + return 0; + } + + int size = GetNativeCell(3); + char[] steamId = new char[size]; + GetNativeString(2, steamId, size); + + Data_GetTargetSteam(vote, steamId, size); +} + +// native NativeVotes_SetTarget(Handle vote, int client, bool setDetails = true); +public int Native_SetTarget(Handle plugin, int numParams) +{ + NativeVote vote = GetNativeCell(1); + if (vote == null) + { + ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); + return; + } + + int client = GetNativeCell(2); + + if (client < 1 || client > MaxClients || !IsClientConnected(client)) + { + ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index %d", client); + return; + } + + int userid; + char steamId[MAX_AUTHID_LENGTH]; + + if (!client) + { + userid = 0; + steamId = ""; + } + else + { + userid = GetClientUserId(client); + if (!GetClientAuthId(client, AuthId_Steam2, steamId, sizeof(steamId))) + { + steamId = ""; + } + } + + Data_SetTarget(vote, userid); + Data_SetTargetSteam(vote, steamId); + + bool changeDetails = GetNativeCell(3); + if (changeDetails) + { + char name[MAX_NAME_LENGTH+1]; + if (client > 0) + { + GetClientName(client, name, MAX_NAME_LENGTH); + Data_SetDetails(vote, name); + } + else + { + Data_SetDetails(vote, ""); + } + } +} + +// native bool NativeVotes_AreVoteCommandsSupported(); +public int Native_AreVoteCommandsSupported(Handle plugin, int numParams) +{ + return Game_AreVoteCommandsSupported(); +} + +// native NativeVotes_RegisterVoteCommand(NativeVotesOverride overrideType, NativeVotes_CallVoteHandler callHandler, NativeVotes_CallVoteVisCheck visHandler = INVALID_FUNCTION); +public int Native_RegisterVoteCommand(Handle plugin, int numParams) +{ + NativeVotesOverride overrideType = GetNativeCell(1); + Function callVoteHandler = GetNativeFunction(2); + Function visHandler = GetNativeFunction(3); + + // This tosses an error so use the simplified version + // if (view_as(overrideType) > sizeof(g_CallVotes)) + if (overrideType > NativeVotesOverride_Count) + { + ThrowNativeError(SP_ERROR_NATIVE, "Override Type %d is not supported by this version of NativeVotes", overrideType); + return; + } + + if (callVoteHandler == INVALID_FUNCTION) + { + ThrowNativeError(SP_ERROR_NATIVE, "CallVoteHandler function was invalid"); + return; + } + + AddToForward(g_CallVotes[overrideType].CallVote_Forward, plugin, callVoteHandler); + + if (visHandler != INVALID_FUNCTION) + { + AddToForward(g_CallVotes[overrideType].CallVote_Vis, plugin, visHandler); + } +} + +// native NativeVotes_UnregisterVoteCommand(NativeVotesOverride overrideType, NativeVotes_CallVoteHandler callHandler, NativeVotes_CallVoteVisCheck visHandler=INVALID_FUNCTION); +public int Native_UnregisterVoteCommand(Handle plugin, int numParams) +{ + NativeVotesOverride overrideType = GetNativeCell(1); + Function callVoteHandler = GetNativeFunction(2); + Function visHandler = GetNativeFunction(3); + + if (overrideType > NativeVotesOverride_Count) + { + ThrowNativeError(SP_ERROR_NATIVE, "Override Type %d is not supported by this version of NativeVotes", overrideType); + return; + } + + if (callVoteHandler == INVALID_FUNCTION) + { + ThrowNativeError(SP_ERROR_NATIVE, "CallVoteHandler function was invalid"); + return; + } + + RemoveFromForward(g_CallVotes[overrideType].CallVote_Forward, plugin, callVoteHandler); + + if (visHandler != INVALID_FUNCTION) + { + RemoveFromForward(g_CallVotes[overrideType].CallVote_Vis, plugin, visHandler); + } +} + +// native NativeVotes_DisplayCallVoteFail(int client, NativeVotesCallFailType reason, int time=0); +public int Native_DisplayCallVoteFail(Handle plugin, int numParams) +{ + int client = GetNativeCell(1); + + if (client < 1 || client > MaxClients || !IsClientConnected(client)) + { + ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index %d", client); + return; + } + + NativeVotesCallFailType reason = GetNativeCell(2); + + int time = GetNativeCell(3); + + Game_DisplayCallVoteFail(client, reason, time); +} + +// native Action NativeVotes_RedrawVoteTitle(const char[] text); +public int Native_RedrawVoteTitle(Handle plugin, int numParams) +{ + if (!g_curDisplayClient) + { + ThrowNativeError(SP_ERROR_NATIVE, "You can only call this once from a MenuAction_Display callback"); + } + + NativeVotesType voteType = Data_GetType(g_hCurVote); + + if (voteType != NativeVotesType_Custom_Mult && voteType != NativeVotesType_Custom_YesNo) + { + return view_as(Plugin_Continue); + } + + GetNativeString(1, g_newMenuTitle, TRANSLATION_LENGTH); + return view_as(Plugin_Changed); +} + +// native Action NativeVotes_RedrawVoteItem(const char[] text); +public int Native_RedrawVoteItem(Handle plugin, int numParams) +{ + if (!g_curItemClient) + { + ThrowNativeError(SP_ERROR_NATIVE, "You can only call this once from a MenuAction_DisplayItem callback"); + } + + if (Game_GetMaxItems() == L4DL4D2_COUNT) + { + return view_as(Plugin_Continue); + } + + GetNativeString(1, g_newMenuItem, TRANSLATION_LENGTH); + return view_as(Plugin_Changed); } \ No newline at end of file diff --git a/addons/sourcemod/scripting/nativevotes/game.sp b/addons/sourcemod/scripting/nativevotes/game.sp index 3d59a63..5f11905 100644 --- a/addons/sourcemod/scripting/nativevotes/game.sp +++ b/addons/sourcemod/scripting/nativevotes/game.sp @@ -1,3478 +1,3486 @@ -/** - * vim: set ts=4 : - * ============================================================================= - * NativeVotes - * NativeVotes is a voting API plugin for L4D, L4D2, TF2, and CS:GO. - * Based on the SourceMod voting API - * - * NativeVotes (C) 2011-2014 Ross Bemrose (Powerlord). All rights reserved. - * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved. - * ============================================================================= - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, version 3.0, as published by the - * Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - * - * As a special exception, AlliedModders LLC gives you permission to link the - * code of this program (as well as its derivative works) to "Half-Life 2," the - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software - * by the Valve Corporation. You must obey the GNU General Public License in - * all respects for all other code used. Additionally, AlliedModders LLC grants - * this exception to all derivative works. AlliedModders LLC defines further - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), - * or . - * - * Version: $Id$ - */ -#if defined _nativevotes_game_included - #endinput -#endif - -#define _nativevotes_game_included - -#include - -#define L4DL4D2_COUNT 2 -#define TF2CSGO_COUNT 5 - -#define INVALID_ISSUE -1 - -//---------------------------------------------------------------------------- -// Translation strings - -//---------------------------------------------------------------------------- -// L4D/L4D2 - -#define L4DL4D2_VOTE_YES_STRING "Yes" -#define L4DL4D2_VOTE_NO_STRING "No" - -#define L4D_VOTE_KICK_START "#L4D_vote_kick_player" -#define L4D_VOTE_KICK_PASSED "#L4D_vote_passed_kick_player" - -// User vote to restart map. -#define L4D_VOTE_RESTART_START "#L4D_vote_restart_game" -#define L4D_VOTE_RESTART_PASSED "#L4D_vote_passed_restart_game" - -// User vote to change maps. -#define L4D_VOTE_CHANGECAMPAIGN_START "#L4D_vote_mission_change" -#define L4D_VOTE_CHANGECAMPAIGN_PASSED "#L4D_vote_passed_mission_change" -#define L4D_VOTE_CHANGELEVEL_START "#L4D_vote_chapter_change" -#define L4D_VOTE_CHANGELEVEL_PASSED "#L4D_vote_passed_chapter_change" - -// User vote to return to lobby. -#define L4D_VOTE_RETURNTOLOBBY_START "#L4D_vote_return_to_lobby" -#define L4D_VOTE_RETURNTOLOBBY_PASSED "#L4D_vote_passed_return_to_lobby" - -// User vote to change difficulty. -#define L4D_VOTE_CHANGEDIFFICULTY_START "#L4D_vote_change_difficulty" -#define L4D_VOTE_CHANGEDIFFICULTY_PASSED "#L4D_vote_passed_change_difficulty" - -// While not a vote string, it works just as well. -#define L4D_VOTE_CUSTOM "#L4D_TargetID_Player" - -//---------------------------------------------------------------------------- -// L4D2 - -// User vote to change alltalk. -#define L4D2_VOTE_ALLTALK_START "#L4D_vote_alltalk_change" -#define L4D2_VOTE_ALLTALK_PASSED "#L4D_vote_passed_alltalk_change" -#define L4D2_VOTE_ALLTALK_ENABLE "#L4D_vote_alltalk_enable" -#define L4D2_VOTE_ALLTALK_DISABLE "#L4D_vote_alltalk_disable" - -//---------------------------------------------------------------------------- -// TF2/CSGO -#define TF2CSGO_VOTE_PREFIX "option" - -#define TF2CSGO_VOTE_STRING_KICK "Kick" -#define TF2CSGO_VOTE_STRING_RESTART "RestartGame" -#define TF2CSGO_VOTE_STRING_CHANGELEVEL "ChangeLevel" -#define TF2CSGO_VOTE_STRING_NEXTLEVEL "NextLevel" -#define TF2CSGO_VOTE_STRING_SCRAMBLE "ScrambleTeams" - -//---------------------------------------------------------------------------- -// TF2 - -#define TF2_VOTE_STRING_CHANGEMISSION "ChangeMission" - -// This one doesn't actually exist in the normal vote menu, but it has a translation string for it. -// Thus, this is the name we will use for it internally -#define TF2_VOTE_STRING_ETERNAWEEN "Eternaween" - -// These are toggles, but the on and off versions are identical -#define TF2_VOTE_STRING_AUTOBALANCE "TeamAutoBalance" -#define TF2_VOTE_STRING_CLASSLIMIT "ClassLimits" - -// New as of 2015-09-24 -#define TF2_VOTE_STRING_EXTEND "ExtendLevel" - -// Menu items for votes -#define TF2_VOTE_MENU_RESTART "#Vote_RestartGame" -#define TF2_VOTE_MENU_KICK "#Vote_Kick" -#define TF2_VOTE_MENU_CHANGELEVEL "#Vote_ChangeLevel" -#define TF2_VOTE_MENU_NEXTLEVEL "#Vote_NextLevel" -#define TF2_VOTE_MENU_SCRAMBLE "#Vote_ScrambleTeams" -#define TF2_VOTE_MENU_CHANGEMISSION "#Vote_ChangeMission" -#define TF2_VOTE_MENU_ETERNAWEEN "#Vote_Eternaween" -#define TF2_VOTE_MENU_AUTOBALANCE_ON "#Vote_TeamAutoBalance_Enable" -#define TF2_VOTE_MENU_AUTOBALANCE_OFF "#Vote_TeamAutoBalance_Disable" -#define TF2_VOTE_MENU_CLASSLIMIT_ON "#Vote_ClassLimit_Enable" -#define TF2_VOTE_MENU_CLASSLIMIT_OFF "#Vote_ClassLimit_Disable" -#define TF2_VOTE_MENU_EXTEND "#Vote_ExtendLevel" - -// User vote to kick user. -#define TF2_VOTE_KICK_IDLE_START "#TF_vote_kick_player_idle" -#define TF2_VOTE_KICK_SCAMMING_START "#TF_vote_kick_player_scamming" -#define TF2_VOTE_KICK_CHEATING_START "#TF_vote_kick_player_cheating" -#define TF2_VOTE_KICK_START "#TF_vote_kick_player_other" -#define TF2_VOTE_KICK_PASSED "#TF_vote_passed_kick_player" - -// User vote to restart map. -#define TF2_VOTE_RESTART_START "#TF_vote_restart_game" -#define TF2_VOTE_RESTART_PASSED "#TF_vote_passed_restart_game" - -// User vote to change maps. -#define TF2_VOTE_CHANGELEVEL_START "#TF_vote_changelevel" -#define TF2_VOTE_CHANGELEVEL_PASSED "#TF_vote_passed_changelevel" - -// User vote to change next level. -#define TF2_VOTE_NEXTLEVEL_SINGLE_START "#TF_vote_nextlevel" -#define TF2_VOTE_NEXTLEVEL_MULTIPLE_START "#TF_vote_nextlevel_choices" // Started by server -#define TF2_VOTE_NEXTLEVEL_EXTEND_PASSED "#TF_vote_passed_nextlevel_extend" // Also used for extend vote -#define TF2_VOTE_NEXTLEVEL_PASSED "#TF_vote_passed_nextlevel" - -// User vote to scramble teams. -// Can be immediate or end of round. -#define TF2_VOTE_SCRAMBLE_IMMEDIATE_START "#TF_vote_scramble_teams" -#define TF2_VOTE_SCRAMBLE_ROUNDEND_START "#TF_vote_should_scramble_round" -#define TF2_VOTE_SCRAMBLE_PASSED "#TF_vote_passed_scramble_teams" - -// User vote to change MvM mission -#define TF2_VOTE_CHANGEMISSION_START "#TF_vote_changechallenge" -#define TF2_VOTE_CHANGEMISSION_PASSED "#TF_vote_passed_changechallenge" - -// User vote for eternaween -#define TF2_VOTE_ETERNAWEEN_START "#TF_vote_eternaween" -#define TF2_VOTE_ETERNAWEEN_PASSED "#TF_vote_passed_eternaween" - -// User vote to start round -#define TF2_VOTE_ROUND_START "#TF_vote_td_start_round" -#define TF2_VOTE_ROUND_PASSED "#TF_vote_passed_td_start_round" - -// User vote to enable autobalance -#define TF2_VOTE_AUTOBALANCE_ENABLE_START "#TF_vote_autobalance_enable" -#define TF2_VOTE_AUTOBALANCE_ENABLE_PASSED "#TF_vote_passed_autobalance_enable" - -// User vote to disable autobalance -#define TF2_VOTE_AUTOBALANCE_DISABLE_START "#TF_vote_autobalance_disable" -#define TF2_VOTE_AUTOBALANCE_DISABLE_PASSED "#TF_vote_passed_autobalance_disable" - -// User vote to enable classlimits -#define TF2_VOTE_CLASSLIMITS_ENABLE_START "#TF_vote_classlimits_enable" -#define TF2_VOTE_CLASSLIMITS_ENABLE_PASSED "#TF_vote_passed_classlimits_enable" - -// User vote to disable classlimits -#define TF2_VOTE_CLASSLIMITS_DISABLE_START "#TF_vote_classlimits_disable" -#define TF2_VOTE_CLASSLIMITS_DISABLE_PASSED "#TF_vote_passed_classlimits_disable" - -// Use vote to extend map. -#define TF2_VOTE_EXTEND_START "#TF_vote_extendlevel" - -// While not a vote string, it works just as well. -#define TF2_VOTE_CUSTOM "#TF_playerid_noteam" - -// TF2 (and SDK2013?) VoteFail / CallVoteFail reasons -enum -{ - VOTE_FAILED_GENERIC, - VOTE_FAILED_TRANSITIONING_PLAYERS, - VOTE_FAILED_RATE_EXCEEDED, - VOTE_FAILED_YES_MUST_EXCEED_NO, - VOTE_FAILED_QUORUM_FAILURE, - VOTE_FAILED_ISSUE_DISABLED, - VOTE_FAILED_MAP_NOT_FOUND, - VOTE_FAILED_MAP_NAME_REQUIRED, - VOTE_FAILED_FAILED_RECENTLY, - VOTE_FAILED_TEAM_CANT_CALL, - VOTE_FAILED_WAITINGFORPLAYERS, - VOTE_FAILED_PLAYERNOTFOUND, - VOTE_FAILED_CANNOT_KICK_ADMIN, - VOTE_FAILED_SCRAMBLE_IN_PROGRESS, - VOTE_FAILED_SPECTATOR, - VOTE_FAILED_NEXTLEVEL_SET, - VOTE_FAILED_MAP_NOT_VALID, - VOTE_FAILED_CANNOT_KICK_FOR_TIME, - VOTE_FAILED_CANNOT_KICK_DURING_ROUND, - VOTE_FAILED_MODIFICATION_ALREADY_ACTIVE, -} - -//---------------------------------------------------------------------------- -// CSGO -// User vote to kick user. -#define CSGO_VOTE_KICK_IDLE_START "#SFUI_vote_kick_player_idle" -#define CSGO_VOTE_KICK_SCAMMING_START "#SFUI_vote_kick_player_scamming" -#define CSGO_VOTE_KICK_CHEATING_START "#SFUI_vote_kick_player_cheating" -#define CSGO_VOTE_KICK_START "#SFUI_vote_kick_player_other" -#define CSGO_VOTE_KICK_OTHERTEAM "#SFUI_otherteam_vote_kick_player" -#define CSGO_VOTE_KICK_PASSED "#SFUI_vote_passed_kick_player" - -// User vote to restart map. -#define CSGO_VOTE_RESTART_START "#SFUI_vote_restart_game" -#define CSGO_VOTE_RESTART_PASSED "#SFUI_vote_passed_restart_game" - -// User vote to change maps. -#define CSGO_VOTE_CHANGELEVEL_START "#SFUI_vote_changelevel" -#define CSGO_VOTE_CHANGELEVEL_PASSED "#SFUI_vote_passed_changelevel" - -// User vote to change next level. -#define CSGO_VOTE_NEXTLEVEL_SINGLE_START "#SFUI_vote_nextlevel" -#define CSGO_VOTE_NEXTLEVEL_MULTIPLE_START "#SFUI_vote_nextlevel_choices" // Started by server -#define CSGO_VOTE_NEXTLEVEL_EXTEND_PASSED "#SFUI_vote_passed_nextlevel_extend" -#define CSGO_VOTE_NEXTLEVEL_PASSED "#SFUI_vote_passed_nextlevel" - -// User vote to scramble teams. -#define CSGO_VOTE_SCRAMBLE_START "#SFUI_vote_scramble_teams" -#define CSGO_VOTE_SCRAMBLE_PASSED "#SFUI_vote_passed_scramble_teams" - -// User vote to swap teams. -#define CSGO_VOTE_SWAPTEAMS_START "#SFUI_vote_swap_teams" -#define CSGO_VOTE_SWAPTEAMS_PASSED "#SFUI_vote_passed_swap_teams" - -// User vote to surrender -#define CSGO_VOTE_SURRENDER_START "#SFUI_vote_surrender" -#define CSGO_VOTE_SURRENDER_OTHERTEAM "#SFUI_otherteam_vote_continue_or_surrender" -#define CSGO_VOTE_SURRENDER_PASSED "#SFUI_vote_passed_surrender" - -// User vote to rematch -#define CSGO_VOTE_REMATCH_START "#SFUI_vote_rematch" -#define CSGO_VOTE_REMATCH_PASSED "#SFUI_vote_passed_rematch" - -// User vote to continue match with bots -#define CSGO_VOTE_CONTINUE_START "#SFUI_vote_continue" -#define CSGO_VOTE_CONTINUE_OTHERTEAM "#SFUI_otherteam_vote_continue_or_surrender" -#define CSGO_VOTE_CONTINUE_PASSED "#SFUI_vote_passed_continue" - -// User vote to pause game -#define CSGO_VOTE_PAUSE_START "#SFUI_Vote_pause_match" -#define CSGO_VOTE_PAUSE_PASSED "#SFUI_vote_passed_pause_match" - -// User vote to unpause game -#define CSGO_VOTE_UNPAUSE_START "#SFUI_Vote_unpause_match" -#define CSGO_VOTE_UNPAUSE_PASSED "#SFUI_vote_passed_unpause_match" - -// User vote to load backups -#define CSGO_VOTE_LOADBACKUP_START "#SFUI_Vote_loadbackup" -#define CSGO_VOTE_LOADBACKUP_PASSED "#SFUI_vote_passed_loadbackup" - -// User vote to start match -#define CSGO_VOTE_READY_START "#SFUI_Vote_ready_for_match" -#define CSGO_VOTE_READY_PASSED "#SFUI_vote_passed_ready_for_match" - -// User vote to delay match start -#define CSGO_VOTE_NOTREADY_START "#SFUI_Vote_not_ready_for_match" -#define CSGO_VOTE_NOTREADY_PASSED "#SFUI_vote_passed_not_ready_for_match" - -// User vote to start round -#define CSGO_VOTE_ROUND_START "#SFUI_vote_td_start_round" -#define CSGO_VOTE_ROUND_PASSED "#SFUI_vote_passed_td_start_round" - -#define CSGO_VOTE_UNIMPLEMENTED_OTHERTEAM "#SFUI_otherteam_vote_unimplemented" - -// While not a vote string, it works just as well. -#define CSGO_VOTE_CUSTOM "#SFUI_Scoreboard_NormalPlayer" - -// CSGO VoteFail / CallVoteFail reasons -enum -{ - CSGO_VOTE_FAILED_GENERIC, - CSGO_VOTE_FAILED_TRANSITIONING_PLAYERS, - CSGO_VOTE_FAILED_RATE_EXCEEDED, - CSGO_VOTE_FAILED_YES_MUST_EXCEED_NO, - CSGO_VOTE_FAILED_QUORUM_FAILURE, - CSGO_VOTE_FAILED_ISSUE_DISABLED, - CSGO_VOTE_FAILED_MAP_NOT_FOUND, - CSGO_VOTE_FAILED_MAP_NAME_REQUIRED, - CSGO_VOTE_FAILED_FAILED_RECENTLY, - CSGO_VOTE_FAILED_FAILED_RECENTLY_KICK, - CSGO_VOTE_FAILED_FAILED_RECENTLY_MAP, - CSGO_VOTE_FAILED_FAILED_RECENTLY_SWAP, - CSGO_VOTE_FAILED_FAILED_RECENTLY_SCRAMBLE, - CSGO_VOTE_FAILED_FAILED_RECENTLY_RESTART, - CSGO_VOTE_FAILED_TEAM_CANT_CALL, - CSGO_VOTE_FAILED_WARMUP, - CSGO_VOTE_FAILED_PLAYERNOTFOUND, - CSGO_VOTE_FAILED_CANNOT_KICK_ADMIN, - CSGO_VOTE_FAILED_SCRAMBLE_IN_PROGRESS, - CSGO_VOTE_FAILED_SWAP_IN_PROGRESS, - CSGO_VOTE_FAILED_SPECTATOR, - CSGO_VOTE_FAILED_NEXTLEVEL_SET, - CSGO_VOTE_FAILED_UNKNOWN1, - CSGO_VOTE_FAILED_SURRENDER_ABANDON, - CSGO_VOTE_FAILED_UNKNOWN2, - CSGO_VOTE_FAILED_PAUSED, - CSGO_VOTE_FAILED_NOT_PAUSED, - CSGO_VOTE_FAILED_NOT_WARMUP, - CSGO_VOTE_FAILED_MIN_PLAYERS, - CSGO_VOTE_FAILED_ROUND_ENDED, -} - - -//---------------------------------------------------------------------------- -// Generic functions -// - -// This is deprecated in NativeVotes 1.1 -enum -{ - ValveVote_Kick = 0, - ValveVote_Restart = 1, - ValveVote_ChangeLevel = 2, - ValveVote_NextLevel = 3, - ValveVote_Scramble = 4, - ValveVote_SwapTeams = 5, -} - -static int g_VoteController = -1; -static bool g_bUserBuf = false; - -static ConVar g_Cvar_Votes_Enabled; -static ConVar g_Cvar_VoteKick_Enabled; -static ConVar g_Cvar_MvM_VoteKick_Enabled; -static ConVar g_Cvar_VoteNextLevel_Enabled; -static ConVar g_Cvar_VoteChangeLevel_Enabled; -static ConVar g_Cvar_MvM_VoteChangeLevel_Enabled; -static ConVar g_Cvar_VoteRestart_Enabled; -static ConVar g_Cvar_MvM_VoteRestart_Enabled; -static ConVar g_Cvar_VoteScramble_Enabled; -static ConVar g_Cvar_MvM_VoteChallenge_Enabled; -static ConVar g_Cvar_VoteAutoBalance_Enabled; -static ConVar g_Cvar_VoteClassLimits_Enabled; -static ConVar g_Cvar_MvM_VoteClassLimits_Enabled; -static ConVar g_Cvar_VoteExtend_Enabled; - -static ConVar g_Cvar_ClassLimit; -static ConVar g_Cvar_AutoBalance; - -static ConVar g_Cvar_HideDisabledIssues; - -/** - * TODO(UPDATE): For now we only support one vote from NativeVotes at a time. - * - * Ideally we peek and poke at the game's `s_nVoteIdx` and decrement it accordingly, but we also - * have to store an internal list of active votes. - * - * Thank you @Kenzzer‼ - */ -static int s_nNativeVoteIdx = 0x7FFFFFFF; - -bool Game_IsGameSupported(char[] engineName="", int maxlength=0) -{ - g_EngineVersion = GetEngineVersion(); - - g_bUserBuf = GetFeatureStatus(FeatureType_Native, "GetUserMessageType") == FeatureStatus_Available && GetUserMessageType() == UM_Protobuf; - - //LogMessage("Detected Engine version: %d", g_EngineVersion); - if (maxlength > 0) - { - GetEngineVersionName(g_EngineVersion, engineName, maxlength); - } - - char gameDir[128]; - GetGameFolderName(gameDir, sizeof(gameDir)); - if (!StrEqual(gameDir, "tf")) - { - if (g_EngineVersion == Engine_SDK2013 && FileExists("resource/tf.ttf")) - { - g_EngineVersion = Engine_TF2; - } - else - { - KeyValues kv = new KeyValues("GameInfo"); - if (kv.ImportFromFile("gameinfo.txt") && kv.GetNum("DependsOnAppID") == 440) - { - g_EngineVersion = Engine_TF2; - } - delete kv; - } - } - - switch (g_EngineVersion) - { - case Engine_Left4Dead, Engine_Left4Dead2, Engine_CSGO, Engine_TF2: - { - return true; - } - } - - return false; -} - -void Game_InitializeCvars() -{ - switch (g_EngineVersion) - { - case Engine_Left4Dead, Engine_Left4Dead2, Engine_CSGO: - { - g_Cvar_Votes_Enabled = FindConVar("sv_allow_votes"); - } - case Engine_TF2: - { - g_Cvar_Votes_Enabled = FindConVar("sv_allow_votes"); - g_Cvar_VoteKick_Enabled = FindConVar("sv_vote_issue_kick_allowed"); - g_Cvar_MvM_VoteKick_Enabled = FindConVar("sv_vote_issue_kick_allowed_mvm"); - g_Cvar_VoteNextLevel_Enabled = FindConVar("sv_vote_issue_nextlevel_allowed"); - g_Cvar_VoteChangeLevel_Enabled = FindConVar("sv_vote_issue_changelevel_allowed"); - g_Cvar_MvM_VoteChangeLevel_Enabled = FindConVar("sv_vote_issue_changelevel_allowed_mvm"); - g_Cvar_VoteRestart_Enabled = FindConVar("sv_vote_issue_restart_game_allowed"); - g_Cvar_MvM_VoteRestart_Enabled = FindConVar("sv_vote_issue_restart_game_allowed_mvm"); - g_Cvar_VoteScramble_Enabled = FindConVar("sv_vote_issue_scramble_teams_allowed"); - g_Cvar_MvM_VoteChallenge_Enabled = FindConVar("sv_vote_issue_mvm_challenge_allowed"); - g_Cvar_VoteAutoBalance_Enabled = FindConVar("sv_vote_issue_autobalance_allowed"); - g_Cvar_VoteClassLimits_Enabled = FindConVar("sv_vote_issue_classlimits_allowed"); - g_Cvar_MvM_VoteClassLimits_Enabled = FindConVar("sv_vote_issue_classlimits_allowed_mvm"); - g_Cvar_VoteExtend_Enabled = FindConVar("sv_vote_issue_extendlevel_allowed"); - - g_Cvar_ClassLimit = FindConVar("tf_classlimit"); - g_Cvar_AutoBalance = FindConVar("mp_autoteambalance"); - - g_Cvar_HideDisabledIssues = FindConVar("sv_vote_ui_hide_disabled_issues"); - } - } -} - -NativeVotesKickType Game_GetKickType(const char[] param1, int &target) -{ - NativeVotesKickType kickType; - - switch(g_EngineVersion) - { - case Engine_Left4Dead, Engine_Left4Dead2, Engine_CSGO: - { - target = StringToInt(param1); - kickType = NativeVotesKickType_Generic; - } - case Engine_TF2: - { - char params[2][20]; - ExplodeString(param1, " ", params, sizeof(params), sizeof(params[])); - - target = StringToInt(params[0]); - - if (StrEqual(params[1], "cheating", false)) - { - kickType = NativeVotesKickType_Cheating; - } - else if (StrEqual(params[1], "idle", false)) - { - kickType = NativeVotesKickType_Idle; - } - else if (StrEqual(params[1], "scamming", false)) - { - kickType = NativeVotesKickType_Scamming; - } - else - { - kickType = NativeVotesKickType_Generic; - } - } - } - return kickType; -} - -bool CheckVoteController() -{ - int entity = INVALID_ENT_REFERENCE; - if (g_VoteController != -1) - { - entity = EntRefToEntIndex(g_VoteController); - } - - if (entity == INVALID_ENT_REFERENCE) - { - entity = FindEntityByClassname(-1, "vote_controller"); - if (entity == -1) - { - //LogError("Could not find Vote Controller."); - return false; - } - - g_VoteController = EntIndexToEntRef(entity); - } - return true; -} - -// All logic for choosing a game-specific function should happen here. -// There should be one per function in the game shared and specific sections -int Game_ParseVote(const char[] option) -{ - int item = NATIVEVOTES_VOTE_INVALID; - -#if defined LOG - LogMessage("Parsing vote option %s", option); -#endif - - switch(g_EngineVersion) - { - case Engine_Left4Dead, Engine_Left4Dead2: - { - item = L4DL4D2_ParseVote(option); - } - case Engine_CSGO: - { - item = CSGO_ParseVote(option); - } - case Engine_TF2: - { - item = TF2_ParseVote(option); - } - } - - return item; - -} - -int Game_GetMaxItems() -{ - switch (g_EngineVersion) - { - case Engine_Left4Dead, Engine_Left4Dead2: - { - return L4DL4D2_COUNT; - } - case Engine_CSGO, Engine_TF2: - { - return TF2CSGO_COUNT; - } - } - - return 0; // Here to prevent warnings -} - -bool Game_CheckVoteType(NativeVotesType type) -{ - bool returnVal = false; - - switch(g_EngineVersion) - { - case Engine_Left4Dead: - { - returnVal = L4D_CheckVoteType(type); - } - case Engine_Left4Dead2: - { - returnVal = L4D2_CheckVoteType(type); - } - case Engine_CSGO: - { - returnVal = CSGO_CheckVoteType(type); - } - case Engine_TF2: - { - returnVal = TF2_CheckVoteType(type); - } - } - - return returnVal; -} - -bool Game_CheckVotePassType(NativeVotesPassType type) -{ - bool returnVal = false; - - switch(g_EngineVersion) - { - case Engine_Left4Dead: - { - returnVal = L4D_CheckVotePassType(type); - } - case Engine_Left4Dead2: - { - returnVal = L4D2_CheckVotePassType(type); - } - case Engine_CSGO: - { - returnVal = CSGO_CheckVotePassType(type); - } - case Engine_TF2: - { - returnVal = TF2_CheckVotePassType(type); - } - } - - return returnVal; -} - -bool Game_DisplayVoteToOne(NativeVote vote, int client) -{ - if (g_bCancelled) - { - return false; - } - - int clients[1]; - clients[0] = client; - - return Game_DisplayVote(vote, clients, 1); -} - -bool Game_DisplayVote(NativeVote vote, int[] clients, int num_clients) -{ - if (g_bCancelled) - { - return false; - } - - switch(g_EngineVersion) - { - case Engine_Left4Dead: - { - L4D_DisplayVote(vote, num_clients); - } - case Engine_Left4Dead2: - { - L4D2_DisplayVote(vote, clients, num_clients); - } - case Engine_CSGO, Engine_TF2: - { - TF2CSGO_DisplayVote(vote, clients, num_clients); - } - } - - -#if defined LOG - char details[MAX_VOTE_DETAILS_LENGTH]; - char translation[TRANSLATION_LENGTH]; - - NativeVotesType voteType = Data_GetType(vote); - Data_GetDetails(vote, details, sizeof(details)); - Game_VoteTypeToTranslation(voteType, translation, sizeof(translation)); - - LogMessage("Displaying vote: type: %d, translation: \"%s\", details: \"%s\"", voteType, translation, details); -#endif - - return true; -} - -void Game_DisplayVoteFail(NativeVote vote, NativeVotesFailType reason) -{ - int team = Data_GetTeam(vote); - - int total = 0; - int[] players = new int[MaxClients]; - - for (int i = 1; i <= MaxClients; i++) - { - if (!IsClientInGame(i) || IsFakeClient(i)) - continue; - players[total++] = i; - } - - Game_DisplayRawVoteFail(players, total, reason, team); -} - -void Game_DisplayRawVoteFail(int[] clients, int numClients, NativeVotesFailType reason, int team) -{ - switch(g_EngineVersion) - { - case Engine_Left4Dead: - { - L4D_VoteFail(team); - } - case Engine_Left4Dead2: - { - L4D2_VoteFail(clients, numClients, team); - } - case Engine_CSGO: - { - int reasonType = VoteFailTypeToInt(reason); - CSGO_VoteFail(clients, numClients, reasonType, team); - } - case Engine_TF2: - { - int reasonType = VoteFailTypeToInt(reason); - TF2_VoteFail(clients, numClients, reasonType, team); - } - } - -#if defined LOG - LogMessage("Vote Failed to %d client(s): \"%d\"", numClients, reason); -#endif -} - -void Game_DisplayVotePass(NativeVote vote, const char[] details="", int client=0) -{ - NativeVotesPassType passType = VoteTypeToVotePass(Data_GetType(vote)); - - Game_DisplayVotePassEx(vote, passType, details, client); -} - -void Game_DisplayVotePassEx(NativeVote vote, NativeVotesPassType passType, const char[] details="", int client=0) -{ - int team = Data_GetTeam(vote); - - Game_DisplayRawVotePass(passType, team, client, details); -} - -void Game_DisplayRawVotePass(NativeVotesPassType passType, int team, int client=0, const char[] details="") -{ - char translation[TRANSLATION_LENGTH]; - - switch (g_EngineVersion) - { - case Engine_Left4Dead: - { - if (client <= 0) - { - L4DL4D2_VotePassToTranslation(passType, translation, sizeof(translation)); - L4D_VotePass(translation, details, team); - } - } - case Engine_Left4Dead2: - { - L4DL4D2_VotePassToTranslation(passType, translation, sizeof(translation)); - - switch (passType) - { - case NativeVotesPass_AlltalkOn: - { - L4D2_VotePass(translation, L4D2_VOTE_ALLTALK_ENABLE, team, client); - } - case NativeVotesPass_AlltalkOff: - { - L4D2_VotePass(translation, L4D2_VOTE_ALLTALK_DISABLE, team, client); - } - default: - { - L4D2_VotePass(translation, details, team, client); - } - } - } - case Engine_CSGO: - { - CSGO_VotePassToTranslation(passType, translation, sizeof(translation)); - CSGO_VotePass(translation, details, team, client); - } - case Engine_TF2: - { - TF2_VotePassToTranslation(passType, translation, sizeof(translation)); - TF2_VotePass(translation, details, team, client); - } - } - -#if defined LOG - if (client != 0) - LogMessage("Vote Passed: \"%s\", \"%s\"", translation, details); -#endif -} - -void Game_DisplayVotePassCustom(NativeVote vote, const char[] translation, int client) -{ - int team = Data_GetTeam(vote); - Game_DisplayRawVotePassCustom(translation, team, client); -} - -void Game_DisplayRawVotePassCustom(const char[] translation, int team, int client) -{ - switch (g_EngineVersion) - { - case Engine_Left4Dead: - { - ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes_DisplayPassCustom is not supported on L4D"); - } - case Engine_Left4Dead2: - { - L4D2_VotePass(L4D_VOTE_CUSTOM, translation, team, client); - } - case Engine_CSGO: - { - CSGO_VotePass(CSGO_VOTE_CUSTOM, translation, team, client); - } - case Engine_TF2: - { - TF2_VotePass(TF2_VOTE_CUSTOM, translation, team, client); - } - } - -#if defined LOG - if (client != 0) - LogMessage("Vote Passed Custom: \"%s\"", translation); -#endif -} - -void Game_DisplayCallVoteFail(int client, NativeVotesCallFailType reason, int time) -{ - - int reasonType = VoteCallFailTypeToInt(reason); - - switch (g_EngineVersion) - { - case Engine_Left4Dead, Engine_Left4Dead2: - { - L4DL4D2_CallVoteFail(client, reasonType); - } - case Engine_CSGO: - { - CSGO_CallVoteFail(client, reasonType, time); - } - case Engine_TF2: - { - TF2_CallVoteFail(client, reasonType, time); - } - } - -#if defined LOG - LogMessage("Call vote failed: client: %N, reason: %d, time: %d", client, reason, time); -#endif -} - -void Game_ClientSelectedItem(NativeVote vote, int client, int item) -{ -#if defined LOG - LogMessage("Client %N selected item %d", client, item); -#endif - switch(g_EngineVersion) - { - case Engine_Left4Dead, Engine_Left4Dead2: - { - L4DL4D2_ClientSelectedItem(client, item); - } - case Engine_CSGO: - { - CSGO_ClientSelectedItem(vote, client, item); - } - case Engine_TF2: - { - TF2_ClientSelectedItem(vote, client, item); - } - } -} - -void Game_UpdateVoteCounts(ArrayList hVoteCounts, int totalClients) -{ - switch(g_EngineVersion) - { - case Engine_Left4Dead, Engine_Left4Dead2: - { - L4DL4D2_UpdateVoteCounts(hVoteCounts, totalClients); - } - case Engine_CSGO, Engine_TF2: - { - TF2CSGO_UpdateVoteCounts(hVoteCounts); - } - } -} - -void Game_DisplayVoteSetup(int client, ArrayList hVoteTypes) -{ - switch (g_EngineVersion) - { - case Engine_TF2: - { - TF2_DisplayVoteSetup(client, hVoteTypes); - } - } -} - -// stock because at the moment it's only used in logging code which isn't always compiled. -stock void Game_VoteTypeToTranslation(NativeVotesType voteType, char[] translation, int maxlength) -{ - switch(g_EngineVersion) - { - case Engine_Left4Dead, Engine_Left4Dead2: - { - L4DL4D2_VoteTypeToTranslation(voteType, translation, maxlength); - } - case Engine_CSGO: - { - CSGO_VoteTypeToTranslation(voteType, translation, maxlength); - } - case Engine_TF2: - { - TF2_VoteTypeToTranslation(voteType, translation, maxlength); - } - } -} - -stock void Game_UpdateClientCount(int num_clients) -{ - switch(g_EngineVersion) - { - case Engine_Left4Dead, Engine_Left4Dead2: - { - L4DL4D2_UpdateClientCount(num_clients); - } - case Engine_CSGO, Engine_TF2: - { - TF2CSGO_UpdateClientCount(num_clients); - } - } -} - -public Action Game_ResetVote(Handle timer) -{ - switch(g_EngineVersion) - { - case Engine_Left4Dead, Engine_Left4Dead2: - { - L4DL4D2_ResetVote(); - } - case Engine_CSGO, Engine_TF2: - { - TF2CSGO_ResetVote(); - } - } - return Plugin_Continue; -} - -void Game_VoteYes(int client) -{ - switch (g_EngineVersion) - { - case Engine_Left4Dead, Engine_Left4Dead2: - { - FakeClientCommand(client, "Vote Yes"); - } - case Engine_CSGO, Engine_TF2: - { - FakeClientCommand(client, "vote option1"); - } - } -} - -void Game_VoteNo(int client) -{ - switch (g_EngineVersion) - { - case Engine_Left4Dead, Engine_Left4Dead2: - { - FakeClientCommand(client, "Vote No"); - } - case Engine_CSGO, Engine_TF2: - { - FakeClientCommand(client, "vote option2"); - } - } -} - -bool Game_IsVoteInProgress() -{ - switch (g_EngineVersion) - { - case Engine_Left4Dead, Engine_Left4Dead2: - { - return L4DL4D2_IsVoteInProgress(); - } - case Engine_CSGO, Engine_TF2: - { - return TF2CSGO_IsVoteInProgress(); - } - } - - return false; -} - -bool Game_AreVoteCommandsSupported() -{ - switch (g_EngineVersion) - { - case Engine_TF2: - { - return true; - } - } - - return false; -} - -stock bool Game_VoteTypeToVoteString(NativeVotesType voteType, char[] voteString, int maxlength) -{ - switch (g_EngineVersion) - { - case Engine_TF2: - { - return TF2_VoteTypeToVoteString(voteType, voteString, maxlength); - } - } - - return false; -} - -stock NativeVotesType Game_VoteStringToVoteType(char[] voteString) -{ - switch (g_EngineVersion) - { - case Engine_TF2: - { - return TF2_VoteStringToVoteType(voteString); - } - } - - return NativeVotesType_None; -} - -stock NativeVotesOverride Game_VoteTypeToVoteOverride(NativeVotesType voteType) -{ - switch (g_EngineVersion) - { - case Engine_TF2: - { - return TF2_VoteTypeToVoteOverride(voteType); - } - } - - return NativeVotesOverride_None; -} - -stock NativeVotesType Game_VoteOverrideToVoteType(NativeVotesOverride overrideType) -{ - switch (g_EngineVersion) - { - case Engine_TF2: - { - return TF2_VoteOverrideToVoteType(overrideType); - } - } - - return NativeVotesType_None; -} - -stock NativeVotesOverride Game_VoteStringToVoteOverride(const char[] voteString) -{ - switch (g_EngineVersion) - { - case Engine_TF2: - { - return TF2_VoteStringToVoteOverride(voteString); - } - } - - return NativeVotesOverride_None; -} - -stock bool Game_OverrideTypeToVoteString(NativeVotesOverride overrideType, char[] voteString, int maxlength) -{ - switch (g_EngineVersion) - { - case Engine_TF2: - { - return TF2_OverrideTypeToVoteString(overrideType, voteString, maxlength); - } - } - - return false; -} - -stock bool Game_OverrideTypeToTranslationString(NativeVotesOverride overrideType, char[] translationString, int maxlength) -{ - switch (g_EngineVersion) - { - case Engine_TF2: - { - return TF2_OverrideTypeToTranslationString(overrideType, translationString, maxlength); - } - } - - return false; -} - -void Game_AddDefaultVotes(ArrayList hVoteTypes) -{ - switch (g_EngineVersion) - { - case Engine_TF2: - { - TF2_AddDefaultVotes(hVoteTypes, Game_AreDisabledIssuesHidden()); - } - } -} - -bool Game_AreDisabledIssuesHidden() -{ - switch (g_EngineVersion) - { - case Engine_TF2: - { - return g_Cvar_HideDisabledIssues.BoolValue; - } - } - - return true; -} - -// All games shared functions - -//---------------------------------------------------------------------------- -// Data functions - -static NativeVotesPassType VoteTypeToVotePass(NativeVotesType voteType) -{ - NativeVotesPassType passType = NativeVotesPass_None; - - switch(voteType) - { - case NativeVotesType_Custom_YesNo, NativeVotesType_Custom_Mult: - { - passType = NativeVotesPass_Custom; - } - case NativeVotesType_ChgCampaign: - { - passType = NativeVotesPass_ChgCampaign; - } - case NativeVotesType_ChgDifficulty: - { - passType = NativeVotesPass_ChgDifficulty; - } - case NativeVotesType_ReturnToLobby: - { - passType = NativeVotesPass_ReturnToLobby; - } - case NativeVotesType_AlltalkOn: - { - passType = NativeVotesPass_AlltalkOn; - } - case NativeVotesType_AlltalkOff: - { - passType = NativeVotesPass_AlltalkOff; - } - case NativeVotesType_Restart: - { - passType = NativeVotesPass_Restart; - } - case NativeVotesType_Kick, NativeVotesType_KickIdle, NativeVotesType_KickScamming, NativeVotesType_KickCheating: - { - passType = NativeVotesPass_Kick; - } - case NativeVotesType_ChgLevel: - { - passType = NativeVotesPass_ChgLevel; - } - case NativeVotesType_NextLevel, NativeVotesType_NextLevelMult: - { - passType = NativeVotesPass_NextLevel; - } - case NativeVotesType_ScrambleNow, NativeVotesType_ScrambleEnd: - { - passType = NativeVotesPass_Scramble; - } - case NativeVotesType_ChgMission: - { - passType = NativeVotesPass_ChgMission; - } - case NativeVotesType_SwapTeams: - { - passType = NativeVotesPass_SwapTeams; - } - case NativeVotesType_Surrender: - { - passType = NativeVotesPass_Surrender; - } - case NativeVotesType_Rematch: - { - passType = NativeVotesPass_Rematch; - } - case NativeVotesType_Continue: - { - passType = NativeVotesPass_Continue; - } - case NativeVotesType_StartRound: - { - passType = NativeVotesPass_StartRound; - } - case NativeVotesType_Eternaween: - { - passType = NativeVotesPass_Eternaween; - } - case NativeVotesType_AutoBalanceOn: - { - passType = NativeVotesPass_AutoBalanceOn; - } - case NativeVotesType_AutoBalanceOff: - { - passType = NativeVotesPass_AutoBalanceOff; - } - case NativeVotesType_ClassLimitsOn: - { - passType = NativeVotesPass_ClassLimitsOn; - } - case NativeVotesType_ClassLimitsOff: - { - passType = NativeVotesPass_ClassLimitsOff; - } - case NativeVotesType_Extend: - { - passType = NativeVotesPass_Extend; - } - default: - { - passType = NativeVotesPass_Custom; - } - } - - return passType; -} - -// In case we find more types later -static int VoteFailTypeToInt(NativeVotesFailType failType) -{ - return view_as(failType); -} - -static int VoteCallFailTypeToInt(NativeVotesCallFailType failType) -{ - switch (g_EngineVersion) - { - case Engine_Left4Dead, Engine_Left4Dead2: - { - switch (failType) - { - case NativeVotesCallFail_Generic: - { - return VOTE_FAILED_GENERIC; - } - case NativeVotesCallFail_Loading: - { - return VOTE_FAILED_TRANSITIONING_PLAYERS; - } - case NativeVotesCallFail_Recent: - { - return VOTE_FAILED_FAILED_RECENTLY; - } - } - } - case Engine_TF2: - { - switch (failType) - { - case NativeVotesCallFail_Generic: - { - return VOTE_FAILED_GENERIC; - } - case NativeVotesCallFail_Loading: - { - return VOTE_FAILED_TRANSITIONING_PLAYERS; - } - case NativeVotesCallFail_Recent: - { - return VOTE_FAILED_FAILED_RECENTLY; - } - case NativeVotesCallFail_Disabled: - { - return VOTE_FAILED_ISSUE_DISABLED; - } - case NativeVotesCallFail_MapNotFound: - { - return VOTE_FAILED_MAP_NOT_FOUND; - } - case NativeVotesCallFail_SpecifyMap: - { - return VOTE_FAILED_MAP_NAME_REQUIRED; - } - case NativeVotesCallFail_Failed: - { - return VOTE_FAILED_FAILED_RECENTLY; - } - case NativeVotesCallFail_WrongTeam: - { - return VOTE_FAILED_TEAM_CANT_CALL; - } - case NativeVotesCallFail_Waiting: - { - return VOTE_FAILED_WAITINGFORPLAYERS; - } - case NativeVotesCallFail_PlayerNotFound: - { - return VOTE_FAILED_PLAYERNOTFOUND; - } - case NativeVotesCallFail_CantKickAdmin: - { - return VOTE_FAILED_CANNOT_KICK_ADMIN; - } - case NativeVotesCallFail_ScramblePending: - { - return VOTE_FAILED_SCRAMBLE_IN_PROGRESS; - } - case NativeVotesCallFail_Spectators: - { - return VOTE_FAILED_SPECTATOR; - } - case NativeVotesCallFail_LevelSet: - { - return VOTE_FAILED_NEXTLEVEL_SET; - } - case NativeVotesCallFail_MapNotValid: - { - return VOTE_FAILED_MAP_NOT_VALID; - } - case NativeVotesCallFail_KickTime: - { - return VOTE_FAILED_CANNOT_KICK_FOR_TIME; - } - case NativeVotesCallFail_KickDuringRound: - { - return VOTE_FAILED_CANNOT_KICK_DURING_ROUND; - } - case NativeVotesCallFail_AlreadyActive: - { - return VOTE_FAILED_MODIFICATION_ALREADY_ACTIVE; - } - } - } - case Engine_CSGO: - { - switch (failType) - { - case NativeVotesCallFail_Generic: - { - return CSGO_VOTE_FAILED_GENERIC; - } - case NativeVotesCallFail_Loading: - { - return CSGO_VOTE_FAILED_TRANSITIONING_PLAYERS; - } - case NativeVotesCallFail_Recent: - { - return CSGO_VOTE_FAILED_RATE_EXCEEDED; - } - case NativeVotesCallFail_Disabled: - { - return CSGO_VOTE_FAILED_ISSUE_DISABLED; - } - case NativeVotesCallFail_MapNotFound: - { - return CSGO_VOTE_FAILED_MAP_NOT_FOUND; - } - case NativeVotesCallFail_SpecifyMap: - { - return CSGO_VOTE_FAILED_MAP_NAME_REQUIRED; - } - case NativeVotesCallFail_Failed: - { - return CSGO_VOTE_FAILED_FAILED_RECENTLY; - } - case NativeVotesCallFail_WrongTeam: - { - return CSGO_VOTE_FAILED_TEAM_CANT_CALL; - } - case NativeVotesCallFail_Warmup: - { - return CSGO_VOTE_FAILED_WARMUP; - } - case NativeVotesCallFail_PlayerNotFound: - { - return CSGO_VOTE_FAILED_PLAYERNOTFOUND; - } - case NativeVotesCallFail_CantKickAdmin: - { - return CSGO_VOTE_FAILED_CANNOT_KICK_ADMIN; - } - case NativeVotesCallFail_ScramblePending: - { - return CSGO_VOTE_FAILED_SCRAMBLE_IN_PROGRESS; - } - case NativeVotesCallFail_Spectators: - { - return CSGO_VOTE_FAILED_SPECTATOR; - } - case NativeVotesCallFail_LevelSet: - { - return CSGO_VOTE_FAILED_NEXTLEVEL_SET; - } - case NativeVotesCallFail_KickFailed: - { - return CSGO_VOTE_FAILED_FAILED_RECENTLY_KICK; - } - case NativeVotesCallFail_MapFailed: - { - return CSGO_VOTE_FAILED_FAILED_RECENTLY_MAP; - } - case NativeVotesCallFail_SwapFailed: - { - return CSGO_VOTE_FAILED_FAILED_RECENTLY_SWAP; - } - case NativeVotesCallFail_ScrambleFailed: - { - return CSGO_VOTE_FAILED_FAILED_RECENTLY_SCRAMBLE; - } - case NativeVotesCallFail_RestartFailed: - { - return CSGO_VOTE_FAILED_FAILED_RECENTLY_RESTART; - } - case NativeVotesCallFail_SwapPending: - { - return CSGO_VOTE_FAILED_SWAP_IN_PROGRESS; - } - case NativeVotesCallFail_Unknown2: - { - return CSGO_VOTE_FAILED_UNKNOWN1; - } - case NativeVotesCallFail_CantSurrender: - { - return CSGO_VOTE_FAILED_SURRENDER_ABANDON; - } - case NativeVotesCallFail_Unknown3: - { - return CSGO_VOTE_FAILED_UNKNOWN2; - } - case NativeVotesCallFail_MatchPaused: - { - return CSGO_VOTE_FAILED_PAUSED; - } - case NativeVotesCallFail_NotPaused: - { - return CSGO_VOTE_FAILED_NOT_PAUSED; - } - case NativeVotesCallFail_NotWarmup: - { - return CSGO_VOTE_FAILED_NOT_WARMUP; - } - case NativeVotesCallFail_MinPlayers: - { - return CSGO_VOTE_FAILED_MIN_PLAYERS; - } - case NativeVotesCallFail_RoundEnded: - { - return CSGO_VOTE_FAILED_ROUND_ENDED; - } - } - } - } - - return VOTE_FAILED_GENERIC; -} - -static void GetEngineVersionName(EngineVersion version, char[] printName, int maxlength) -{ - switch (version) - { - case Engine_Unknown: - { - strcopy(printName, maxlength, "Unknown"); - } - case Engine_Original: - { - strcopy(printName, maxlength, "Original"); - } - case Engine_SourceSDK2006: - { - strcopy(printName, maxlength, "Source SDK 2006"); - } - case Engine_SourceSDK2007: - { - strcopy(printName, maxlength, "Source SDK 2007"); - } - case Engine_Left4Dead: - { - strcopy(printName, maxlength, "Left 4 Dead "); - } - case Engine_DarkMessiah: - { - strcopy(printName, maxlength, "Dark Messiah"); - } - case Engine_Left4Dead2: - { - strcopy(printName, maxlength, "Left 4 Dead 2"); - } - case Engine_AlienSwarm: - { - strcopy(printName, maxlength, "Alien Swarm"); - } - case Engine_BloodyGoodTime: - { - strcopy(printName, maxlength, "Bloody Good Time"); - } - case Engine_EYE: - { - strcopy(printName, maxlength, "E.Y.E. Divine Cybermancy"); - } - case Engine_Portal2: - { - strcopy(printName, maxlength, "Portal 2"); - } - case Engine_CSGO: - { - strcopy(printName, maxlength, "Counter-Strike: Global Offensive"); - } - case Engine_CSS: - { - strcopy(printName, maxlength, "Counter-Strike: Source"); - } - case Engine_DOTA: - { - strcopy(printName, maxlength, "DOTA 2"); - } - case Engine_HL2DM: - { - char gameDir[32]; - GetGameFolderName(gameDir, sizeof(gameDir)); - if (StrEqual(gameDir, "hl2mp")) - { - strcopy(printName, maxlength, "Half-Life 2: Deathmatch"); - } - else - { - // https://github.com/alliedmodders/metamod-source/pull/210 - strcopy(printName, maxlength, "Source SDK 2025"); - } - } - case Engine_DODS: - { - strcopy(printName, maxlength, "Day of Defeat: Source"); - } - case Engine_TF2: - { - strcopy(printName, maxlength, "Team Fortress 2"); - } - case Engine_NuclearDawn: - { - strcopy(printName, maxlength, "Nuclear Dawn"); - } - case Engine_SDK2013: - { - strcopy(printName, maxlength , "Source SDK 2013"); - } - case Engine_Blade: - { - strcopy(printName, maxlength , "Blade Symphony"); - } - case Engine_Insurgency: - { - strcopy(printName, maxlength , "Insurgency (2013 Retail version)"); - } - case Engine_Contagion: - { - strcopy(printName, maxlength , "Contagion"); - } - case Engine_BlackMesa: - { - strcopy(printName, maxlength , "Black Mesa Multiplayer"); - } - case Engine_DOI: - { - strcopy(printName, maxlength , "Day of Infamy"); - } - case Engine_PVKII: - { - strcopy(printName, maxlength , "Pirates, Vikings, and Knights II"); - } - case Engine_MCV: - { - strcopy(printName, maxlength , "Military Conflict: Vietnam"); - } - default: - { - strcopy(printName, maxlength, "Not listed"); - } - } -} - - - -//---------------------------------------------------------------------------- -// L4D/L4D2 shared functions - -// NATIVEVOTES_VOTE_INVALID means parse failed -static int L4DL4D2_ParseVote(const char[] option) -{ - if (StrEqual(option, "Yes", false)) - { - return NATIVEVOTES_VOTE_YES; - } - else if (StrEqual(option, "No", false)) - { - return NATIVEVOTES_VOTE_NO; - } - - return NATIVEVOTES_VOTE_INVALID; -} - -static void L4DL4D2_ClientSelectedItem(int client, int item) -{ - int choice; - - if (item == NATIVEVOTES_VOTE_NO) - { - choice = 0; - } - else if (item == NATIVEVOTES_VOTE_YES) - { - choice = 1; - } - - BfWrite voteCast = UserMessageToBfWrite(StartMessageOne("VoteRegistered", client, USERMSG_RELIABLE)); - voteCast.WriteByte(choice); - EndMessage(); -} - -static void L4DL4D2_UpdateVoteCounts(ArrayList votes, int totalClients) -{ - int yesVotes = votes.Get(NATIVEVOTES_VOTE_YES); - int noVotes = votes.Get(NATIVEVOTES_VOTE_NO); - Event changeEvent = CreateEvent("vote_changed"); - changeEvent.SetInt("yesVotes", yesVotes); - changeEvent.SetInt("noVotes", noVotes); - changeEvent.SetInt("potentialVotes", totalClients); - changeEvent.Fire(); - - if (CheckVoteController()) - { - SetEntProp(g_VoteController, Prop_Send, "m_votesYes", yesVotes); - SetEntProp(g_VoteController, Prop_Send, "m_votesNo", noVotes); - } -} - -static stock void L4DL4D2_UpdateClientCount(int num_clients) -{ - if (CheckVoteController()) - { - SetEntProp(g_VoteController, Prop_Send, "m_potentialVotes", num_clients); - } -} - -static void L4DL4D2_CallVoteFail(int client, int reason) -{ - BfWrite callVoteFail = UserMessageToBfWrite(StartMessageOne("CallVoteFailed", client, USERMSG_RELIABLE)); - - callVoteFail.WriteByte(reason); - - EndMessage(); -} - -static void L4DL4D2_VoteTypeToTranslation(NativeVotesType voteType, char[] translation, int maxlength) -{ - switch(voteType) - { - case NativeVotesType_ChgCampaign: - { - strcopy(translation, maxlength, L4D_VOTE_CHANGECAMPAIGN_START); - } - case NativeVotesType_ChgDifficulty: - { - strcopy(translation, maxlength, L4D_VOTE_CHANGEDIFFICULTY_START); - } - case NativeVotesType_ReturnToLobby: - { - strcopy(translation, maxlength, L4D_VOTE_RETURNTOLOBBY_START); - } - case NativeVotesType_AlltalkOn, NativeVotesType_AlltalkOff: - { - strcopy(translation, maxlength, L4D2_VOTE_ALLTALK_START); - } - case NativeVotesType_Restart: - { - strcopy(translation, maxlength, L4D_VOTE_RESTART_START); - } - case NativeVotesType_Kick: - { - strcopy(translation, maxlength, L4D_VOTE_KICK_START); - } - case NativeVotesType_ChgLevel: - { - strcopy(translation, maxlength, L4D_VOTE_CHANGELEVEL_START); - } - default: - { - strcopy(translation, maxlength, L4D_VOTE_CUSTOM); - } - } -} - -static void L4DL4D2_VotePassToTranslation(NativeVotesPassType passType, char[] translation, int maxlength) -{ - switch(passType) - { - case NativeVotesPass_Custom: - { - strcopy(translation, maxlength, L4D_VOTE_CUSTOM); - } - case NativeVotesPass_ChgCampaign: - { - strcopy(translation, maxlength, L4D_VOTE_CHANGECAMPAIGN_PASSED); - } - case NativeVotesPass_ChgDifficulty: - { - strcopy(translation, maxlength, L4D_VOTE_CHANGEDIFFICULTY_PASSED); - } - case NativeVotesPass_ReturnToLobby: - { - strcopy(translation, maxlength, L4D_VOTE_RETURNTOLOBBY_PASSED); - } - case NativeVotesPass_AlltalkOn, NativeVotesPass_AlltalkOff: - { - strcopy(translation, maxlength, L4D2_VOTE_ALLTALK_PASSED); - } - case NativeVotesPass_Restart: - { - strcopy(translation, maxlength, L4D_VOTE_RESTART_PASSED); - } - case NativeVotesPass_Kick: - { - strcopy(translation, maxlength, L4D_VOTE_KICK_PASSED); - } - case NativeVotesPass_ChgLevel: - { - strcopy(translation, maxlength, L4D_VOTE_CHANGELEVEL_PASSED); - } - default: - { - strcopy(translation, maxlength, L4D_VOTE_CUSTOM); - } - } -} - -static void L4DL4D2_ResetVote() -{ - if (CheckVoteController()) - { - SetEntProp(g_VoteController, Prop_Send, "m_activeIssueIndex", INVALID_ISSUE); - SetEntProp(g_VoteController, Prop_Send, "m_votesYes", 0); - SetEntProp(g_VoteController, Prop_Send, "m_votesNo", 0); - SetEntProp(g_VoteController, Prop_Send, "m_potentialVotes", 0); - SetEntProp(g_VoteController, Prop_Send, "m_onlyTeamToVote", NATIVEVOTES_ALL_TEAMS); - } -} - -static bool L4DL4D2_IsVoteInProgress() -{ - if (CheckVoteController()) - { - return (GetEntProp(g_VoteController, Prop_Send, "m_activeIssueIndex") > INVALID_ISSUE); - } - return false; -} - -//---------------------------------------------------------------------------- -// L4D functions - -/* -static L4D_ClientSelectedItem(Handle:vote, client, item) -{ - if (item > NATIVEVOTES_VOTE_INVALID && item <= Game_GetMaxItems()) - { - Handle castEvent; - - switch (item) - { - case NATIVEVOTES_VOTE_YES: - { - castEvent = CreateEvent("vote_cast_no"); - } - - case NATIVEVOTES_VOTE_NO: - { - castEvent = CreateEvent("vote_cast_yes"); - } - - default: - { - return; - } - } - - if (castEvent != INVALID_HANDLE) - { - SetEventInt(castEvent, "team", Data_GetTeam(vote)); - SetEventInt(castEvent, "entityid", client); - FireEvent(castEvent); - } - - } -} -*/ - -static void L4D_DisplayVote(NativeVote vote, int num_clients) -{ - char translation[TRANSLATION_LENGTH]; - - NativeVotesType voteType = Data_GetType(vote); - - L4DL4D2_VoteTypeToTranslation(voteType, translation, sizeof(translation)); - - char details[MAX_VOTE_DETAILS_LENGTH]; - Data_GetDetails(vote, details, MAX_VOTE_DETAILS_LENGTH); - - int team = Data_GetTeam(vote); - - if (CheckVoteController()) - { - // TODO: Need to look these values up - SetEntProp(g_VoteController, Prop_Send, "m_activeIssueIndex", 0); // For now, set to 0 to block in-game votes - SetEntProp(g_VoteController, Prop_Send, "m_onlyTeamToVote", team); - SetEntProp(g_VoteController, Prop_Send, "m_votesYes", 0); - SetEntProp(g_VoteController, Prop_Send, "m_votesNo", 0); - SetEntProp(g_VoteController, Prop_Send, "m_potentialVotes", num_clients); - } - - Event voteStart = CreateEvent("vote_started"); - voteStart.SetInt("team", team); - voteStart.SetInt("initiator", Data_GetInitiator(vote)); - voteStart.SetString("issue", translation); - voteStart.SetString("param1", details); - voteStart.Fire(); - -} - -static void L4D_VoteEnded() -{ - Event endEvent = CreateEvent("vote_ended"); - endEvent.Fire(); -} - -static void L4D_VotePass(const char[] translation, const char[] details, int team) -{ - L4D_VoteEnded(); - - Event passEvent = CreateEvent("vote_passed"); - passEvent.SetString("details", translation); - passEvent.SetString("param1", details); - passEvent.SetInt("team", team); - passEvent.Fire(); -} - -static void L4D_VoteFail(int team) -{ - L4D_VoteEnded(); - - Event failEvent = CreateEvent("vote_failed"); - failEvent.SetInt("team", team); - failEvent.Fire(); -} - -static bool L4D_CheckVoteType(NativeVotesType voteType) -{ - switch(voteType) - { - case NativeVotesType_Custom_YesNo, - NativeVotesType_ChgCampaign, - NativeVotesType_ChgDifficulty, - NativeVotesType_ReturnToLobby, - NativeVotesType_Restart, - NativeVotesType_Kick, - NativeVotesType_ChgLevel: - { - return true; - } - } - - return false; -} - -static bool L4D_CheckVotePassType(NativeVotesPassType passType) -{ - switch(passType) - { - case NativeVotesPass_Custom, - NativeVotesPass_ChgCampaign, - NativeVotesPass_ChgDifficulty, - NativeVotesPass_ReturnToLobby, - NativeVotesPass_Restart, - NativeVotesPass_Kick, - NativeVotesPass_ChgLevel: - { - return true; - } - } - - return false; -} - -//---------------------------------------------------------------------------- -// L4D2 functions - -static void L4D2_DisplayVote(NativeVote vote, int[] clients, int num_clients) -{ - char translation[TRANSLATION_LENGTH]; - - NativeVotesType voteType = Data_GetType(vote); - - L4DL4D2_VoteTypeToTranslation(voteType, translation, sizeof(translation)); - - char details[MAX_VOTE_DETAILS_LENGTH]; - - int team = Data_GetTeam(vote); - bool bCustom = false; - - switch (voteType) - { - case NativeVotesType_AlltalkOn: - { - strcopy(details, MAX_VOTE_DETAILS_LENGTH, L4D2_VOTE_ALLTALK_ENABLE); - } - case NativeVotesType_AlltalkOff: - { - strcopy(details, MAX_VOTE_DETAILS_LENGTH, L4D2_VOTE_ALLTALK_DISABLE); - } - case NativeVotesType_Custom_YesNo, NativeVotesType_Custom_Mult: - { - Data_GetTitle(vote, details, MAX_VOTE_DETAILS_LENGTH); - bCustom = true; - } - - default: - { - Data_GetDetails(vote, details, MAX_VOTE_DETAILS_LENGTH); - } - } - - int initiator = Data_GetInitiator(vote); - char initiatorName[MAX_NAME_LENGTH]; - - if (initiator != NATIVEVOTES_SERVER_INDEX && initiator > 0 && initiator <= MaxClients && IsClientInGame(initiator)) - { - GetClientName(initiator, initiatorName, MAX_NAME_LENGTH); - } - - for (int i = 0; i < num_clients; ++i) - { - g_newMenuTitle[0] = '\0'; - - MenuAction actions = Data_GetActions(vote); - - Action changeTitle = Plugin_Continue; - if (bCustom && actions & MenuAction_Display) - { - g_curDisplayClient = clients[i]; - changeTitle = DoAction(vote, MenuAction_Display, clients[i], 0); - } - - g_curDisplayClient = 0; - - BfWrite voteStart = UserMessageToBfWrite(StartMessageOne("VoteStart", clients[i], USERMSG_RELIABLE)); - voteStart.WriteByte(team); - voteStart.WriteByte(initiator); - voteStart.WriteString(translation); - if (changeTitle == Plugin_Changed) - { - voteStart.WriteString(g_newMenuTitle); - } - else - { - voteStart.WriteString(details); - } - voteStart.WriteString(initiatorName); - EndMessage(); - } - - if (CheckVoteController()) - { - SetEntProp(g_VoteController, Prop_Send, "m_onlyTeamToVote", team); - SetEntProp(g_VoteController, Prop_Send, "m_votesYes", 0); - SetEntProp(g_VoteController, Prop_Send, "m_votesNo", 0); - SetEntProp(g_VoteController, Prop_Send, "m_potentialVotes", num_clients); - SetEntProp(g_VoteController, Prop_Send, "m_activeIssueIndex", 0); // Set to 0 to block ingame votes - } -} - -static void L4D2_VotePass(const char[] translation, const char[] details, int team, int client=0) -{ - BfWrite votePass; - if (!client) - { - votePass = UserMessageToBfWrite(StartMessageAll("VotePass", USERMSG_RELIABLE)); - } - else - { - votePass = UserMessageToBfWrite(StartMessageOne("VotePass", client, USERMSG_RELIABLE)); - } - - votePass.WriteByte(team); - votePass.WriteString(translation); - votePass.WriteString(details); - EndMessage(); -} - -static void L4D2_VoteFail(int[] clients, int numClients, int team) -{ - BfWrite voteFailed = UserMessageToBfWrite(StartMessage("VoteFail", clients, numClients, USERMSG_RELIABLE)); - - voteFailed.WriteByte(team); - EndMessage(); -} - -static bool L4D2_CheckVoteType(NativeVotesType voteType) -{ - switch(voteType) - { - case NativeVotesType_Custom_YesNo, - NativeVotesType_ChgCampaign, - NativeVotesType_ChgDifficulty, - NativeVotesType_ReturnToLobby, - NativeVotesType_AlltalkOn, - NativeVotesType_AlltalkOff, - NativeVotesType_Restart, - NativeVotesType_Kick, - NativeVotesType_ChgLevel: - { - return true; - } - } - - return false; -} - -static bool L4D2_CheckVotePassType(NativeVotesPassType passType) -{ - switch(passType) - { - case NativeVotesPass_Custom, - NativeVotesPass_ChgCampaign, - NativeVotesPass_ChgDifficulty, - NativeVotesPass_ReturnToLobby, - NativeVotesPass_Restart, - NativeVotesPass_Kick, - NativeVotesPass_ChgLevel: - { - return true; - } - } - - return false; -} - -//---------------------------------------------------------------------------- -// TF2/CSGO shared functions - -// TF2 and CSGO functions are still together in case Valve moves TF2 to protobufs. - -// NATIVEVOTES_VOTE_INVALID means parse failed -static int CSGO_ParseVote(const char[] option) -{ - // option1 <-- 7 characters exactly - if (strlen(option) != 7) - { - return NATIVEVOTES_VOTE_INVALID; - } - - return StringToInt(option[6]) - 1; -} - -// NATIVEVOTES_VOTE_INVALID means parse failed -static int TF2_ParseVote(const char[] option) -{ - // the update on 2022-06-22 changed the params passed to the `vote` command - // previously it was a single string "optionN", where N was the option to be selected - // now it's two arguments "X optionN", where X is the vote index being acted on - - if (strlen(option) == 0 || GetCmdArgs() != 2) - { - return NATIVEVOTES_VOTE_INVALID; - } - - // int voteidx = GetCmdArgInt(1); - - char voteOption[16]; - GetCmdArg(2, voteOption, sizeof(voteOption)); - - // option1 <-- 7 characters exactly - // voteOption's last character should be numeric - if (strlen(voteOption) != 7 || !IsCharNumeric(voteOption[6])) - { - return NATIVEVOTES_VOTE_INVALID; - } - - return StringToInt(voteOption[6]) - 1; -} - -static void CSGO_ClientSelectedItem(NativeVote vote, int client, int item) -{ - Event castEvent = CreateEvent("vote_cast"); - - castEvent.SetInt("team", Data_GetTeam(vote)); - castEvent.SetInt("entityid", client); - castEvent.SetInt("vote_option", item); - castEvent.Fire(); -} - -static void TF2_ClientSelectedItem(NativeVote vote, int client, int item) -{ - Event castEvent = CreateEvent("vote_cast"); - - castEvent.SetInt("team", Data_GetTeam(vote)); - castEvent.SetInt("voteidx", s_nNativeVoteIdx); - castEvent.SetInt("entityid", client); - castEvent.SetInt("vote_option", item); - castEvent.Fire(); -} - -static void TF2CSGO_UpdateVoteCounts(ArrayList votes) -{ - if (CheckVoteController()) - { - int size = votes.Length; - Event changeEvent = CreateEvent("vote_changed"); - for (int i = 0; i < size; i++) - { - SetEntProp(g_VoteController, Prop_Send, "m_nVoteOptionCount", votes.Get(i), 4, i); - - char szVoteOption[13]; - Format(szVoteOption, sizeof(szVoteOption), "vote_option%d", i+1); - changeEvent.SetInt(szVoteOption, votes.Get(i)); - } - changeEvent.SetInt("voteidx", s_nNativeVoteIdx); - changeEvent.SetInt("potentialVotes", GetEntProp(g_VoteController, Prop_Send, "m_nPotentialVotes")); - changeEvent.Fire(); - } -} - -static stock void TF2CSGO_UpdateClientCount(int num_clients) -{ - if (CheckVoteController()) - { - SetEntProp(g_VoteController, Prop_Send, "m_nPotentialVotes", num_clients); - } -} - -static void TF2CSGO_DisplayVote(NativeVote vote, int[] clients, int num_clients) -{ - NativeVotesType voteType = Data_GetType(vote); - - // Added for novote support - bool bNoVoteButton = (Data_GetFlags(vote) & MENUFLAG_BUTTON_NOVOTE) == MENUFLAG_BUTTON_NOVOTE; - - if (bNoVoteButton) - { - int max = Game_GetMaxItems(); - if (Data_GetItemCount(vote) == max) - { - // item must be removed before No Vote is added to prevent it from being blocked - Data_RemoveItem(vote, max-1); - } - - char display[TRANSLATION_LENGTH]; - Format(display, sizeof(display), "%T", "No Vote", LANG_SERVER); - - Data_InsertItem(vote, 0, "No Vote", display); - } - - char translation[TRANSLATION_LENGTH]; - char otherTeamString[TRANSLATION_LENGTH]; - bool bYesNo = true; - bool bCustom = false; - - char details[MAX_VOTE_DETAILS_LENGTH]; - - // voteIndex is used by the CVoteController, which we're not using. - // -1 means no vote in progress, so any other value should work. - //int voteIndex = TF2CSGO_GetVoteType(voteType); - int voteIndex = 0; - - switch (voteType) - { - case NativeVotesType_Custom_YesNo, NativeVotesType_Custom_Mult: - { - Data_GetTitle(vote, details, MAX_VOTE_DETAILS_LENGTH); - bCustom = true; - } - default: - { - Data_GetDetails(vote, details, MAX_VOTE_DETAILS_LENGTH); - } - } - - switch(g_EngineVersion) - { - case Engine_CSGO: - { - bYesNo = CSGO_VoteTypeToTranslation(voteType, translation, sizeof(translation)); - CSGO_VoteTypeToVoteOtherTeamString(voteType, otherTeamString, sizeof(otherTeamString)); - } - case Engine_TF2: - { - bYesNo = TF2_VoteTypeToTranslation(voteType, translation, sizeof(translation)); - } - } - - int team = Data_GetTeam(vote); - - // Moved to mimic SourceSDK2013's server/vote_controller.cpp - if (CheckVoteController()) - { - SetEntProp(g_VoteController, Prop_Send, "m_bIsYesNoVote", bYesNo); - - // CSGO gets very cranky if you try setting this - if (g_EngineVersion == Engine_TF2) - { - SetEntProp(g_VoteController, Prop_Send, "m_iActiveIssueIndex", voteIndex); - } - - SetEntProp(g_VoteController, Prop_Send, "m_iOnlyTeamToVote", team); - - for (int i = 0; i < 5; i++) - { - SetEntProp(g_VoteController, Prop_Send, "m_nVoteOptionCount", 0, _, i); - } - - SetEntProp(g_VoteController, Prop_Send, "m_nVoteIdx", --s_nNativeVoteIdx); - } - - // According to Source SDK 2013, vote_options is only sent for a multiple choice vote. - // As of 2015-09-28, vote_options is sent for all votes in TF2 despite Yes/No being - // translated in the UI itself. - // As of 2016-07-10, we send this only when we're doing Yes/No votes as we handle - // multiple choice votes separately later. - if (bYesNo) - { - int itemCount = Data_GetItemCount(vote); - - Event optionsEvent = CreateEvent("vote_options"); - - for (int i = 0; i < itemCount; i++) - { - char option[8]; - Format(option, sizeof(option), "%s%d", TF2CSGO_VOTE_PREFIX, i+1); - char display[TRANSLATION_LENGTH]; - Data_GetItemDisplay(vote, i, display, sizeof(display)); - - optionsEvent.SetString(option, display); - } - optionsEvent.SetInt("count", itemCount); - optionsEvent.SetInt("voteidx", s_nNativeVoteIdx); - optionsEvent.Fire(); - } - - // Moved to mimic SourceSDK2013's server/vote_controller.cpp - // For whatever reason, while the other props are set first, this one's set after the vote_options event - if (CheckVoteController()) - { - SetEntProp(g_VoteController, Prop_Send, "m_nPotentialVotes", num_clients); - } - - int holder = vote.Initiator; - // required to allow the initiator to vote on their own issue - // https://github.com/ValveSoftware/Source-1-Games/issues/3934 - if (sv_vote_holder_may_vote_no && holder <= MaxClients && IsClientConnected(holder) && !IsFakeClient(holder)) - { - sv_vote_holder_may_vote_no.ReplicateToClient(holder, "1"); - } - - MenuAction actions = Data_GetActions(vote); - - for (int i = 0; i < num_clients; ++i) - { - g_newMenuTitle[0] = '\0'; - - Action changeTitle = Plugin_Continue; - if (bCustom && actions & MenuAction_Display) - { - g_curDisplayClient = clients[i]; - changeTitle = DoAction(vote, MenuAction_Display, clients[i], 0); - } - - g_curDisplayClient = 0; - - if (!bYesNo) - { - TF2CSGO_SendOptionsToClient(vote, clients[i]); - } - - Handle voteStart = StartMessageOne("VoteStart", clients[i], USERMSG_RELIABLE); - - if (g_bUserBuf) - { - Protobuf protoStart = UserMessageToProtobuf(voteStart); - protoStart.SetInt("team", team); - protoStart.SetInt("ent_idx", Data_GetInitiator(vote)); - protoStart.SetString("disp_str", translation); - if (bCustom && changeTitle == Plugin_Changed) - { - protoStart.SetString("details_str", g_newMenuTitle); - } - else - { - protoStart.SetString("details_str", details); - } - protoStart.SetBool("is_yes_no_vote", bYesNo); - protoStart.SetString("other_team_str", otherTeamString); - protoStart.SetInt("vote_type", voteIndex); - } - else - { - BfWrite bfStart = UserMessageToBfWrite(voteStart); - bfStart.WriteByte(team); - bfStart.WriteNum(s_nNativeVoteIdx); - bfStart.WriteByte(Data_GetInitiator(vote)); - bfStart.WriteString(translation); - if (bCustom && changeTitle == Plugin_Changed) - { - bfStart.WriteString(g_newMenuTitle); - } - else - { - bfStart.WriteString(details); - } - bfStart.WriteByte(bYesNo); - if (!IsFakeClient(clients[i])) - { - bfStart.WriteByte(Data_GetTarget(vote) - 1); - } - } - - EndMessage(); - } - - g_curDisplayClient = 0; -} - -static void TF2CSGO_SendOptionsToClient(NativeVote vote, int client) -{ - Event optionsEvent = CreateEvent("vote_options"); - - MenuAction actions = Data_GetActions(vote); - bool bNoVoteButton = (Data_GetFlags(vote) & MENUFLAG_BUTTON_NOVOTE) == MENUFLAG_BUTTON_NOVOTE; - - int start = 0; - - if (bNoVoteButton) - { - start = 1; - char option[8]; - Format(option, sizeof(option), "%s1", TF2CSGO_VOTE_PREFIX); - - char display[TRANSLATION_LENGTH]; - Format(display, sizeof(display), "%T", "No Vote", client); - optionsEvent.SetString(option, display); - } - - int itemCount = Data_GetItemCount(vote); - - for (int i = start; i < itemCount; i++) - { - Action changeItem = Plugin_Continue; - - if (actions & MenuAction_DisplayItem) - { - g_curItemClient = client; - g_newMenuItem[0] = '\0'; - - changeItem = DoAction(vote, MenuAction_DisplayItem, client, i); - g_curItemClient = 0; - } - - char option[8]; - Format(option, sizeof(option), "%s%d", TF2CSGO_VOTE_PREFIX, i+1); - char display[TRANSLATION_LENGTH]; - - if (changeItem == Plugin_Changed) - { - strcopy(display, TRANSLATION_LENGTH, g_newMenuItem); - } - else - { - Data_GetItemDisplay(vote, i, display, sizeof(display)); - } - optionsEvent.SetString(option, display); - } - optionsEvent.SetInt("count", itemCount); - optionsEvent.SetInt("voteidx", s_nNativeVoteIdx); - optionsEvent.FireToClient(client); - // FireToClient does not close the handle, so we call Cancel() to do that for us. - optionsEvent.Cancel(); -} - -static void CSGO_VotePass(const char[] translation, const char[] details, int team, int client=0) -{ - Protobuf votePass = null; - - if (client <= 0) - { - votePass = UserMessageToProtobuf(StartMessageAll("VotePass", USERMSG_RELIABLE)); - } - else - { - votePass = UserMessageToProtobuf(StartMessageOne("VotePass", client, USERMSG_RELIABLE)); - } - - votePass.SetInt("team", team); - votePass.SetString("disp_str", translation); - votePass.SetString("details_str", details); - votePass.SetInt("vote_type", 0); // Unknown, need to check values - - EndMessage(); -} - -static void TF2_VotePass(const char[] translation, const char[] details, int team, int client=0) -{ - BfWrite votePass = null; - - if (client <= 0) - { - votePass = UserMessageToBfWrite(StartMessageAll("VotePass", USERMSG_RELIABLE)); - } - else - { - votePass = UserMessageToBfWrite(StartMessageOne("VotePass", client, USERMSG_RELIABLE)); - } - - votePass.WriteByte(team); - votePass.WriteNum(s_nNativeVoteIdx); - votePass.WriteString(translation); - votePass.WriteString(details); - - EndMessage(); -} - -static void CSGO_VoteFail(int[] clients, int numClients, int reason, int team) -{ - Protobuf voteFailed = UserMessageToProtobuf(StartMessage("VoteFailed", clients, numClients, USERMSG_RELIABLE)); - - voteFailed.SetInt("team", team); - voteFailed.SetInt("reason", reason); - - EndMessage(); -} - -static void TF2_VoteFail(int[] clients, int numClients, int reason, int team) -{ - BfWrite voteFailed = UserMessageToBfWrite(StartMessage("VoteFailed", clients, numClients, USERMSG_RELIABLE)); - - voteFailed.WriteByte(team); - voteFailed.WriteNum(s_nNativeVoteIdx); - voteFailed.WriteByte(reason); - - EndMessage(); -} - -static void CSGO_CallVoteFail(int client, int reason, int time) -{ - Protobuf callVoteFail = UserMessageToProtobuf(StartMessageOne("CallVoteFailed", client, USERMSG_RELIABLE)); - - callVoteFail.SetInt("reason", reason); - callVoteFail.SetInt("time", time); - - EndMessage(); -} - -static void TF2_CallVoteFail(int client, int reason, int time) -{ - BfWrite callVoteFail = UserMessageToBfWrite(StartMessageOne("CallVoteFailed", client, USERMSG_RELIABLE)); - - callVoteFail.WriteByte(reason); - callVoteFail.WriteShort(time); - - EndMessage(); -} - -stock static void CSGO_DisplayVoteSetup(int client, ArrayList hVoteTypes) -{ - int count = hVoteTypes.Length; - - Protobuf voteSetup = UserMessageToProtobuf(StartMessageOne("VoteSetup", client, USERMSG_RELIABLE)); - - for (int i = 0; i < count; ++i) - { - char voteIssue[128]; - - CallVoteListData voteData; - hVoteTypes.GetArray(i, voteData.CallVoteList_VoteType); - - Game_OverrideTypeToVoteString(voteData.CallVoteList_VoteType, voteIssue, sizeof(voteIssue)); - - voteSetup.AddString("potential_issues", voteIssue); - } - - EndMessage(); -} - -static void TF2_DisplayVoteSetup(int client, ArrayList hVoteTypes) -{ - int count = hVoteTypes.Length; - - BfWrite voteSetup = UserMessageToBfWrite(StartMessageOne("VoteSetup", client, USERMSG_RELIABLE)); - - voteSetup.WriteByte(count); - - for (int i = 0; i < count; ++i) - { - char voteIssue[128]; - - CallVoteListData voteData; - hVoteTypes.GetArray(i, voteData, sizeof(CallVoteListData)); - - Game_OverrideTypeToVoteString(voteData.CallVoteList_VoteType, voteIssue, sizeof(voteIssue)); - - char translation[128]; - Game_OverrideTypeToTranslationString(voteData.CallVoteList_VoteType, translation, sizeof(translation)); - - voteSetup.WriteString(voteIssue); - voteSetup.WriteString(translation); - voteSetup.WriteByte(voteData.CallVoteList_VoteEnabled); - } - - EndMessage(); -} - -static void TF2CSGO_ResetVote() -{ - if (CheckVoteController()) - { - if (g_EngineVersion == Engine_TF2) - { - SetEntProp(g_VoteController, Prop_Send, "m_nVoteIdx", -1); - SetEntProp(g_VoteController, Prop_Send, "m_iActiveIssueIndex", INVALID_ISSUE); - } - SetEntProp(g_VoteController, Prop_Send, "m_iOnlyTeamToVote", g_EngineVersion == Engine_TF2 ? NATIVEVOTES_TF2_ALL_TEAMS : NATIVEVOTES_ALL_TEAMS); - for (int i = 0; i < 5; i++) - { - SetEntProp(g_VoteController, Prop_Send, "m_nVoteOptionCount", 0, _, i); - } - SetEntProp(g_VoteController, Prop_Send, "m_nPotentialVotes", 0); - SetEntProp(g_VoteController, Prop_Send, "m_bIsYesNoVote", true); - } -} - -static bool TF2CSGO_IsVoteInProgress() -{ - if (CheckVoteController()) - { - return (GetEntProp(g_VoteController, Prop_Send, "m_iActiveIssueIndex") != INVALID_ISSUE); - } - return false; -} - - -//---------------------------------------------------------------------------- -// TF2 functions - -static bool TF2_CheckVoteType(NativeVotesType voteType) -{ - switch(voteType) - { - case NativeVotesType_Custom_YesNo, NativeVotesType_Restart, - NativeVotesType_Kick, NativeVotesType_KickIdle, NativeVotesType_KickScamming, NativeVotesType_KickCheating, - NativeVotesType_ChgLevel, NativeVotesType_NextLevel, NativeVotesType_ScrambleNow, NativeVotesType_ScrambleEnd, - NativeVotesType_ChgMission, NativeVotesType_StartRound, NativeVotesType_Eternaween, - NativeVotesType_AutoBalanceOn, NativeVotesType_AutoBalanceOff, - NativeVotesType_ClassLimitsOn, NativeVotesType_ClassLimitsOff, NativeVotesType_Extend: - { - return true; - } - case NativeVotesType_Custom_Mult, NativeVotesType_NextLevelMult: - { - return true; - } - } - - return false; -} - -static bool TF2_CheckVotePassType(NativeVotesPassType passType) -{ - switch(passType) - { - case NativeVotesPass_Custom, - NativeVotesPass_Restart, - NativeVotesPass_ChgLevel, - NativeVotesPass_Kick, - NativeVotesPass_NextLevel, - NativeVotesPass_Extend, - NativeVotesPass_Scramble, - NativeVotesPass_ChgMission, - NativeVotesPass_StartRound, - NativeVotesPass_Eternaween, - NativeVotesPass_AutoBalanceOn, - NativeVotesPass_AutoBalanceOff, - NativeVotesPass_ClassLimitsOn, - NativeVotesPass_ClassLimitsOff: - { - return true; - } - } - - return false; -} - -static bool TF2_VoteTypeToTranslation(NativeVotesType voteType, char[] translation, int maxlength) -{ - bool bYesNo = true; - switch(voteType) - { - case NativeVotesType_Custom_Mult: - { - strcopy(translation, maxlength, TF2_VOTE_CUSTOM); - bYesNo = false; - } - case NativeVotesType_Restart: - { - strcopy(translation, maxlength, TF2_VOTE_RESTART_START); - } - case NativeVotesType_Kick: - { - strcopy(translation, maxlength, TF2_VOTE_KICK_START); - } - case NativeVotesType_KickIdle: - { - strcopy(translation, maxlength, TF2_VOTE_KICK_IDLE_START); - } - case NativeVotesType_KickScamming: - { - strcopy(translation, maxlength, TF2_VOTE_KICK_SCAMMING_START); - } - case NativeVotesType_KickCheating: - { - strcopy(translation, maxlength, TF2_VOTE_KICK_CHEATING_START); - } - case NativeVotesType_ChgLevel: - { - strcopy(translation, maxlength, TF2_VOTE_CHANGELEVEL_START); - } - case NativeVotesType_NextLevel: - { - strcopy(translation, maxlength, TF2_VOTE_NEXTLEVEL_SINGLE_START); - } - case NativeVotesType_NextLevelMult: - { - strcopy(translation, maxlength, TF2_VOTE_NEXTLEVEL_MULTIPLE_START); - bYesNo = false; - } - case NativeVotesType_ScrambleNow: - { - strcopy(translation, maxlength, TF2_VOTE_SCRAMBLE_IMMEDIATE_START); - } - case NativeVotesType_ScrambleEnd: - { - strcopy(translation, maxlength, TF2_VOTE_SCRAMBLE_ROUNDEND_START); - } - case NativeVotesType_ChgMission: - { - strcopy(translation, maxlength, TF2_VOTE_CHANGEMISSION_START); - } - case NativeVotesType_StartRound: - { - strcopy(translation, maxlength, TF2_VOTE_ROUND_START); - } - case NativeVotesType_Eternaween: - { - strcopy(translation, maxlength, TF2_VOTE_ETERNAWEEN_START); - } - case NativeVotesType_AutoBalanceOn: - { - strcopy(translation, maxlength, TF2_VOTE_AUTOBALANCE_ENABLE_START); - } - case NativeVotesType_AutoBalanceOff: - { - strcopy(translation, maxlength, TF2_VOTE_AUTOBALANCE_DISABLE_START); - } - case NativeVotesType_ClassLimitsOn: - { - strcopy(translation, maxlength, TF2_VOTE_CLASSLIMITS_ENABLE_START); - } - case NativeVotesType_ClassLimitsOff: - { - strcopy(translation, maxlength, TF2_VOTE_CLASSLIMITS_DISABLE_START); - } - case NativeVotesType_Extend: - { - strcopy(translation, maxlength, TF2_VOTE_EXTEND_START); - } - default: - { - strcopy(translation, maxlength, TF2_VOTE_CUSTOM); - } - } - - return bYesNo; -} - -static void TF2_VotePassToTranslation(NativeVotesPassType passType, char[] translation, int maxlength) -{ - switch(passType) - { - case NativeVotesPass_Restart: - { - strcopy(translation, maxlength, TF2_VOTE_RESTART_PASSED); - } - case NativeVotesPass_Kick: - { - strcopy(translation, maxlength, TF2_VOTE_KICK_PASSED); - } - case NativeVotesPass_ChgLevel: - { - strcopy(translation, maxlength, TF2_VOTE_CHANGELEVEL_PASSED); - } - case NativeVotesPass_NextLevel: - { - strcopy(translation, maxlength, TF2_VOTE_NEXTLEVEL_PASSED); - } - case NativeVotesPass_Extend: - { - strcopy(translation, maxlength, TF2_VOTE_NEXTLEVEL_EXTEND_PASSED); - } - case NativeVotesPass_Scramble: - { - strcopy(translation, maxlength, TF2_VOTE_SCRAMBLE_PASSED); - } - case NativeVotesPass_ChgMission: - { - strcopy(translation, maxlength, TF2_VOTE_CHANGEMISSION_PASSED); - } - case NativeVotesPass_StartRound: - { - strcopy(translation, maxlength, TF2_VOTE_ROUND_PASSED); - } - case NativeVotesPass_Eternaween: - { - strcopy(translation, maxlength, TF2_VOTE_ETERNAWEEN_PASSED); - } - case NativeVotesPass_AutoBalanceOn: - { - strcopy(translation, maxlength, TF2_VOTE_AUTOBALANCE_ENABLE_PASSED); - } - case NativeVotesPass_AutoBalanceOff: - { - strcopy(translation, maxlength, TF2_VOTE_AUTOBALANCE_DISABLE_PASSED); - } - case NativeVotesPass_ClassLimitsOn: - { - strcopy(translation, maxlength, TF2_VOTE_CLASSLIMITS_ENABLE_PASSED); - } - case NativeVotesPass_ClassLimitsOff: - { - strcopy(translation, maxlength, TF2_VOTE_CLASSLIMITS_DISABLE_PASSED); - } - default: - { - strcopy(translation, maxlength, TF2_VOTE_CUSTOM); - } - } -} - -static void TF2_AddDefaultVotes(ArrayList hVoteTypes, bool bHideDisabledVotes) -{ - bool globalEnable = g_Cvar_Votes_Enabled.BoolValue; - if (!globalEnable && bHideDisabledVotes) - { - return; - } - - if (FindSendPropInfo("CTFGameRulesProxy", "m_bPlayingMannVsMachine") > 0 && GameRules_GetProp("m_bPlayingMannVsMachine")) - { - // Default MvM vote types - - // Kick - VoteTypeSet(hVoteTypes, bHideDisabledVotes, NativeVotesOverride_Kick, globalEnable && g_Cvar_MvM_VoteKick_Enabled.BoolValue); - - // Restart - VoteTypeSet(hVoteTypes, bHideDisabledVotes, NativeVotesOverride_Restart, globalEnable && g_Cvar_MvM_VoteRestart_Enabled.BoolValue); - - // ChgLevel - VoteTypeSet(hVoteTypes, bHideDisabledVotes, NativeVotesOverride_ChgLevel, globalEnable && g_Cvar_MvM_VoteChangeLevel_Enabled.BoolValue); - - // ChgMission - VoteTypeSet(hVoteTypes, bHideDisabledVotes, NativeVotesOverride_ChgMission, globalEnable && g_Cvar_MvM_VoteChallenge_Enabled.BoolValue); - - // ClassLimits - VoteTypeSet(hVoteTypes, bHideDisabledVotes, NativeVotesOverride_ClassLimits, globalEnable && g_Cvar_MvM_VoteClassLimits_Enabled.BoolValue); - } - else - { - // Default vote types - - // Kick - VoteTypeSet(hVoteTypes, bHideDisabledVotes, NativeVotesOverride_Kick, globalEnable && g_Cvar_VoteKick_Enabled.BoolValue); - - // Restart - VoteTypeSet(hVoteTypes, bHideDisabledVotes, NativeVotesOverride_Restart, globalEnable && g_Cvar_VoteRestart_Enabled.BoolValue); - - // ChgLevel - VoteTypeSet(hVoteTypes, bHideDisabledVotes, NativeVotesOverride_ChgLevel, globalEnable && g_Cvar_VoteChangeLevel_Enabled.BoolValue); - - // NextLevel - VoteTypeSet(hVoteTypes, bHideDisabledVotes, NativeVotesOverride_NextLevel, globalEnable && g_Cvar_VoteNextLevel_Enabled.BoolValue); - - // Scramble - VoteTypeSet(hVoteTypes, bHideDisabledVotes, NativeVotesOverride_Scramble, globalEnable && g_Cvar_VoteScramble_Enabled.BoolValue); - - // ClassLimits - VoteTypeSet(hVoteTypes, bHideDisabledVotes, NativeVotesOverride_ClassLimits, globalEnable && g_Cvar_VoteClassLimits_Enabled.BoolValue); - - // AutoBalance - VoteTypeSet(hVoteTypes, bHideDisabledVotes, NativeVotesOverride_AutoBalance, globalEnable && g_Cvar_VoteAutoBalance_Enabled.BoolValue); - - // Extend - VoteTypeSet(hVoteTypes, bHideDisabledVotes, NativeVotesOverride_Extend, globalEnable && g_Cvar_VoteExtend_Enabled.BoolValue); - } - -} - -static void VoteTypeSet(ArrayList hVoteTypes, bool bHideDisabledVotes, NativeVotesOverride voteType, bool bEnabled) -{ - CallVoteListData voteList; - - if (bEnabled || !bHideDisabledVotes) - { - voteList.CallVoteList_VoteType = voteType; - voteList.CallVoteList_VoteEnabled = bEnabled; - - hVoteTypes.PushArray(voteList); - } -} - -//---------------------------------------------------------------------------- -// CSGO functions - -static bool CSGO_CheckVoteType(NativeVotesType voteType) -{ - switch(voteType) - { - case NativeVotesType_Custom_YesNo, - NativeVotesType_Restart, - NativeVotesType_Kick, - NativeVotesType_KickIdle, - NativeVotesType_KickScamming, - NativeVotesType_KickCheating, - NativeVotesType_ChgLevel, - NativeVotesType_NextLevel, - NativeVotesType_ScrambleNow, - NativeVotesType_SwapTeams, - NativeVotesType_Surrender, - NativeVotesType_Rematch, - NativeVotesType_Continue: - { - return true; - } - case NativeVotesType_Custom_Mult, NativeVotesType_NextLevelMult: - { - // Until/unless Valve fixes their menu code, this is false. - return false; - } - - } - - return false; -} - -static bool CSGO_CheckVotePassType(NativeVotesPassType passType) -{ - switch(passType) - { - case NativeVotesPass_Custom, NativeVotesPass_Restart, NativeVotesPass_Kick, - NativeVotesPass_ChgLevel, NativeVotesPass_NextLevel, NativeVotesPass_Extend, - NativeVotesPass_Scramble, NativeVotesPass_SwapTeams, NativeVotesPass_Surrender, - NativeVotesPass_Rematch, NativeVotesPass_Continue: - { - return true; - } - } - - return false; -} - -static bool CSGO_VoteTypeToTranslation(NativeVotesType voteType, char[] translation, int maxlength) -{ - bool bYesNo = true; - switch(voteType) - { - case NativeVotesType_Custom_Mult: - { - strcopy(translation, maxlength, CSGO_VOTE_CUSTOM); - bYesNo = false; - } - case NativeVotesType_Restart: - { - strcopy(translation, maxlength, CSGO_VOTE_RESTART_START); - } - case NativeVotesType_Kick: - { - strcopy(translation, maxlength, CSGO_VOTE_KICK_START); - } - case NativeVotesType_KickIdle: - { - strcopy(translation, maxlength, CSGO_VOTE_KICK_IDLE_START); - } - case NativeVotesType_KickScamming: - { - strcopy(translation, maxlength, CSGO_VOTE_KICK_SCAMMING_START); - } - case NativeVotesType_KickCheating: - { - strcopy(translation, maxlength, CSGO_VOTE_KICK_CHEATING_START); - } - case NativeVotesType_ChgLevel: - { - strcopy(translation, maxlength, CSGO_VOTE_CHANGELEVEL_START); - } - case NativeVotesType_NextLevel: - { - strcopy(translation, maxlength, CSGO_VOTE_NEXTLEVEL_SINGLE_START); - } - case NativeVotesType_NextLevelMult: - { - strcopy(translation, maxlength, CSGO_VOTE_NEXTLEVEL_MULTIPLE_START); - bYesNo = false; - } - case NativeVotesType_ScrambleNow: - { - strcopy(translation, maxlength, CSGO_VOTE_SCRAMBLE_START); - } - case NativeVotesType_SwapTeams: - { - strcopy(translation, maxlength, CSGO_VOTE_SWAPTEAMS_START); - } - case NativeVotesType_Surrender: - { - strcopy(translation, maxlength, CSGO_VOTE_SURRENDER_START); - } - case NativeVotesType_Rematch: - { - strcopy(translation, maxlength, CSGO_VOTE_REMATCH_START); - } - case NativeVotesType_Continue: - { - strcopy(translation, maxlength, CSGO_VOTE_CONTINUE_START); - } - default: - { - strcopy(translation, maxlength, CSGO_VOTE_CUSTOM); - } - } - - return bYesNo; -} - -static void CSGO_VotePassToTranslation(NativeVotesPassType passType, char[] translation, int maxlength) -{ - switch(passType) - { - case NativeVotesPass_Restart: - { - strcopy(translation, maxlength, CSGO_VOTE_RESTART_PASSED); - } - case NativeVotesPass_Kick: - { - strcopy(translation, maxlength, CSGO_VOTE_KICK_PASSED); - } - case NativeVotesPass_ChgLevel: - { - strcopy(translation, maxlength, CSGO_VOTE_CHANGELEVEL_PASSED); - } - case NativeVotesPass_NextLevel: - { - strcopy(translation, maxlength, CSGO_VOTE_NEXTLEVEL_PASSED); - } - case NativeVotesPass_Extend: - { - strcopy(translation, maxlength, CSGO_VOTE_NEXTLEVEL_EXTEND_PASSED); - } - case NativeVotesPass_Scramble: - { - strcopy(translation, maxlength, CSGO_VOTE_SCRAMBLE_PASSED); - } - case NativeVotesPass_SwapTeams: - { - strcopy(translation, maxlength, CSGO_VOTE_SWAPTEAMS_PASSED); - } - case NativeVotesPass_Surrender: - { - strcopy(translation, maxlength, CSGO_VOTE_SURRENDER_PASSED); - } - case NativeVotesPass_Rematch: - { - strcopy(translation, maxlength, CSGO_VOTE_REMATCH_PASSED); - } - case NativeVotesPass_Continue: - { - strcopy(translation, maxlength, CSGO_VOTE_CONTINUE_PASSED); - } - default: - { - strcopy(translation, maxlength, CSGO_VOTE_CUSTOM); - } - } -} - -static void CSGO_VoteTypeToVoteOtherTeamString(NativeVotesType voteType, char[] otherTeamString, int maxlength) -{ - switch(voteType) - { - case NativeVotesType_Kick: - { - strcopy(otherTeamString, maxlength, CSGO_VOTE_KICK_OTHERTEAM); - } - case NativeVotesType_Surrender: - { - strcopy(otherTeamString, maxlength, CSGO_VOTE_SURRENDER_OTHERTEAM); - } - case NativeVotesType_Continue: - { - strcopy(otherTeamString, maxlength, CSGO_VOTE_CONTINUE_OTHERTEAM); - } - default: - { - strcopy(otherTeamString, maxlength, CSGO_VOTE_UNIMPLEMENTED_OTHERTEAM); - } - } - -} - -static stock int TF2CSGO_GetVoteType(NativeVotesType voteType) -{ - int valveVoteType = ValveVote_Restart; - - switch (voteType) - { - case NativeVotesType_Custom_YesNo, NativeVotesType_Restart: - { - valveVoteType = ValveVote_Restart; - } - case NativeVotesType_Custom_Mult, NativeVotesType_NextLevel, NativeVotesType_NextLevelMult: - { - valveVoteType = ValveVote_NextLevel; - } - case NativeVotesType_Kick, NativeVotesType_KickIdle, NativeVotesType_KickScamming, NativeVotesType_KickCheating: - { - valveVoteType = ValveVote_Kick; - } - case NativeVotesType_ChgLevel: - { - valveVoteType = ValveVote_ChangeLevel; - } - case NativeVotesType_ScrambleNow, NativeVotesType_ScrambleEnd: - { - valveVoteType = ValveVote_Scramble; - } - case NativeVotesType_SwapTeams: - { - valveVoteType = ValveVote_SwapTeams; - } - } - - return valveVoteType; -} - -// The stocks below are used by the vote override system -// Not all are used by the plugin -static stock bool TF2_VoteTypeToVoteString(NativeVotesType voteType, char[] voteString, int maxlength) -{ - bool valid = false; - switch(voteType) - { - case NativeVotesType_Kick, NativeVotesType_KickCheating, NativeVotesType_KickIdle, NativeVotesType_KickScamming: - { - strcopy(voteString, maxlength, TF2CSGO_VOTE_STRING_KICK); - valid = true; - } - case NativeVotesType_ChgLevel: - { - strcopy(voteString, maxlength, TF2CSGO_VOTE_STRING_CHANGELEVEL); - valid = true; - } - case NativeVotesType_NextLevel: - { - strcopy(voteString, maxlength, TF2CSGO_VOTE_STRING_NEXTLEVEL); - valid = true; - } - case NativeVotesType_Restart: - { - strcopy(voteString, maxlength, TF2CSGO_VOTE_STRING_RESTART); - valid = true; - } - case NativeVotesType_ScrambleEnd, NativeVotesType_ScrambleNow: - { - strcopy(voteString, maxlength, TF2CSGO_VOTE_STRING_SCRAMBLE); - valid = true; - } - case NativeVotesType_Eternaween: - { - strcopy(voteString, maxlength, TF2_VOTE_STRING_ETERNAWEEN); - valid = true; - } - case NativeVotesType_AutoBalanceOn: - { - strcopy(voteString, maxlength, TF2_VOTE_STRING_AUTOBALANCE); - valid = true; - } - case NativeVotesType_AutoBalanceOff: - { - strcopy(voteString, maxlength, TF2_VOTE_STRING_AUTOBALANCE); - valid = true; - } - case NativeVotesType_ClassLimitsOn: - { - strcopy(voteString, maxlength, TF2_VOTE_STRING_CLASSLIMIT); - valid = true; - } - case NativeVotesType_ClassLimitsOff: - { - strcopy(voteString, maxlength, TF2_VOTE_STRING_CLASSLIMIT); - valid = true; - } - case NativeVotesType_Extend: - { - strcopy(voteString, maxlength, TF2_VOTE_STRING_EXTEND); - } - } - - return valid; -} - -static stock NativeVotesType TF2_VoteStringToVoteType(const char[] voteString) -{ - NativeVotesType voteType; - - if (StrEqual(voteString, TF2CSGO_VOTE_STRING_KICK, false)) - { - voteType = NativeVotesType_Kick; - } - else if (StrEqual(voteString, TF2CSGO_VOTE_STRING_CHANGELEVEL, false)) - { - voteType = NativeVotesType_ChgLevel; - } - else if (StrEqual(voteString, TF2CSGO_VOTE_STRING_NEXTLEVEL, false)) - { - voteType = NativeVotesType_NextLevel; - } - else if (StrEqual(voteString, TF2CSGO_VOTE_STRING_RESTART, false)) - { - voteType = NativeVotesType_Restart; - } - else if (StrEqual(voteString, TF2CSGO_VOTE_STRING_SCRAMBLE, false)) - { - voteType = NativeVotesType_ScrambleNow; - } - else if (StrEqual(voteString, TF2_VOTE_STRING_ETERNAWEEN, false)) - { - voteType = NativeVotesType_Eternaween; - } - else if (StrEqual(voteString, TF2_VOTE_STRING_AUTOBALANCE, false)) - { - voteType = g_Cvar_AutoBalance.BoolValue ? NativeVotesType_AutoBalanceOff : NativeVotesType_AutoBalanceOn; - } - else if (StrEqual(voteString, TF2_VOTE_STRING_CLASSLIMIT, false)) - { - voteType = g_Cvar_ClassLimit.IntValue ? NativeVotesType_ClassLimitsOff : NativeVotesType_ClassLimitsOn; - } - else if (StrEqual(voteString, TF2_VOTE_STRING_EXTEND, false)) - { - voteType = NativeVotesType_Extend; - } - else if (StrEqual(voteString, TF2_VOTE_STRING_CHANGEMISSION, false)) - { - voteType = NativeVotesType_ChgMission; - } - else - { - voteType = NativeVotesType_None; - } - - return voteType; -} - -static stock NativeVotesOverride TF2_VoteTypeToVoteOverride(NativeVotesType voteType) -{ - NativeVotesOverride overrideType; - - switch (voteType) - { - case NativeVotesType_Kick, NativeVotesType_KickCheating, NativeVotesType_KickIdle, NativeVotesType_KickScamming: - { - overrideType = NativeVotesOverride_Kick; - } - case NativeVotesType_ChgLevel: - { - overrideType = NativeVotesOverride_ChgLevel; - } - case NativeVotesType_NextLevel: - { - overrideType = NativeVotesOverride_NextLevel; - } - case NativeVotesType_Restart: - { - overrideType = NativeVotesOverride_Restart; - } - case NativeVotesType_ScrambleEnd, NativeVotesType_ScrambleNow: - { - overrideType = NativeVotesOverride_Scramble; - } - case NativeVotesType_Eternaween: - { - overrideType = NativeVotesOverride_Eternaween; - } - case NativeVotesType_AutoBalanceOn, NativeVotesType_AutoBalanceOff: - { - overrideType = NativeVotesOverride_AutoBalance; - } - case NativeVotesType_ClassLimitsOn, NativeVotesType_ClassLimitsOff: - { - overrideType = NativeVotesOverride_ClassLimits; - } - case NativeVotesType_Extend: - { - overrideType = NativeVotesOverride_Extend; - } - default: - { - overrideType = NativeVotesOverride_None; - } - } - - return overrideType; -} - -static stock NativeVotesType TF2_VoteOverrideToVoteType(NativeVotesOverride overrideType) -{ - NativeVotesType voteType; - - switch (overrideType) - { - case NativeVotesOverride_Restart: - { - voteType = NativeVotesType_Restart; - } - case NativeVotesOverride_Kick: - { - voteType = NativeVotesType_Kick; - } - case NativeVotesOverride_ChgLevel: - { - voteType = NativeVotesType_ChgLevel; - } - case NativeVotesOverride_NextLevel: - { - voteType = NativeVotesType_NextLevel; - } - case NativeVotesOverride_Scramble: - { - voteType = NativeVotesType_ScrambleNow; - } - case NativeVotesOverride_ChgMission: - { - voteType = NativeVotesType_ChgMission; - } - case NativeVotesOverride_Eternaween: - { - voteType = NativeVotesType_Eternaween; - } - case NativeVotesOverride_AutoBalance: - { - voteType = NativeVotesType_AutoBalanceOn; - } - case NativeVotesOverride_ClassLimits: - { - voteType = NativeVotesType_ClassLimitsOn; - } - case NativeVotesOverride_Extend: - { - voteType = NativeVotesType_Extend; - } - default: - { - voteType = NativeVotesType_None; - } - } - - return voteType; -} - -static stock NativeVotesOverride TF2_VoteStringToVoteOverride(const char[] voteString) -{ - NativeVotesOverride overrideType; - - if (StrEqual(voteString, TF2CSGO_VOTE_STRING_KICK, false)) - { - overrideType = NativeVotesOverride_Kick; - } - else if (StrEqual(voteString, TF2CSGO_VOTE_STRING_CHANGELEVEL, false)) - { - overrideType = NativeVotesOverride_ChgLevel; - } - else if (StrEqual(voteString, TF2CSGO_VOTE_STRING_NEXTLEVEL, false)) - { - overrideType = NativeVotesOverride_NextLevel; - } - else if (StrEqual(voteString, TF2CSGO_VOTE_STRING_RESTART, false)) - { - overrideType = NativeVotesOverride_Restart; - } - else if (StrEqual(voteString, TF2CSGO_VOTE_STRING_SCRAMBLE, false)) - { - overrideType = NativeVotesOverride_Scramble; - } - else if (StrEqual(voteString, TF2_VOTE_STRING_ETERNAWEEN, false)) - { - overrideType = NativeVotesOverride_Eternaween; - } - else if (StrEqual(voteString, TF2_VOTE_STRING_AUTOBALANCE, false)) - { - overrideType = NativeVotesOverride_AutoBalance; - } - else if (StrEqual(voteString, TF2_VOTE_STRING_CLASSLIMIT, false)) - { - overrideType = NativeVotesOverride_ClassLimits; - } - else if (StrEqual(voteString, TF2_VOTE_STRING_EXTEND, false)) - { - overrideType = NativeVotesOverride_Extend; - } - else if (StrEqual(voteString, TF2_VOTE_STRING_CHANGEMISSION, false)) - { - overrideType = NativeVotesOverride_ChgMission; - } - else - { - overrideType = NativeVotesOverride_None; - } - -#if defined LOG - LogMessage("Resolved \"%s\" to %d", voteString, overrideType); -#endif - - return overrideType; -} - -static stock bool TF2_OverrideTypeToVoteString(NativeVotesOverride overrideType, char[] voteString, int maxlength) -{ - bool valid = false; - switch(overrideType) - { - case NativeVotesOverride_Kick: - { - strcopy(voteString, maxlength, TF2CSGO_VOTE_STRING_KICK); - valid = true; - } - case NativeVotesOverride_ChgLevel: - { - strcopy(voteString, maxlength, TF2CSGO_VOTE_STRING_CHANGELEVEL); - valid = true; - } - case NativeVotesOverride_NextLevel: - { - strcopy(voteString, maxlength, TF2CSGO_VOTE_STRING_NEXTLEVEL); - valid = true; - } - case NativeVotesOverride_Restart: - { - strcopy(voteString, maxlength, TF2CSGO_VOTE_STRING_RESTART); - valid = true; - } - case NativeVotesOverride_Scramble: - { - strcopy(voteString, maxlength, TF2CSGO_VOTE_STRING_SCRAMBLE); - valid = true; - } - case NativeVotesOverride_Eternaween: - { - strcopy(voteString, maxlength, TF2_VOTE_STRING_ETERNAWEEN); - valid = true; - } - case NativeVotesOverride_AutoBalance: - { - strcopy(voteString, maxlength, TF2_VOTE_STRING_AUTOBALANCE); - valid = true; - } - case NativeVotesOverride_ClassLimits: - { - strcopy(voteString, maxlength, TF2_VOTE_STRING_CLASSLIMIT); - valid = true; - } - case NativeVotesOverride_Extend: - { - strcopy(voteString, maxlength, TF2_VOTE_STRING_EXTEND); - } - case NativeVotesOverride_ChgMission: - { - strcopy(voteString, maxlength, TF2_VOTE_STRING_CHANGEMISSION); - } - } - - return valid; -} - -static stock bool TF2_OverrideTypeToTranslationString(NativeVotesOverride overrideType, char[] translationString, int maxlength) -{ - bool valid = false; - switch(overrideType) - { - case NativeVotesOverride_Kick: - { - strcopy(translationString, maxlength, TF2_VOTE_MENU_KICK); - valid = true; - } - case NativeVotesOverride_ChgLevel: - { - strcopy(translationString, maxlength, TF2_VOTE_MENU_CHANGELEVEL); - valid = true; - } - case NativeVotesOverride_NextLevel: - { - strcopy(translationString, maxlength, TF2_VOTE_MENU_NEXTLEVEL); - valid = true; - } - case NativeVotesOverride_Restart: - { - strcopy(translationString, maxlength, TF2_VOTE_MENU_RESTART); - valid = true; - } - case NativeVotesOverride_Scramble: - { - strcopy(translationString, maxlength, TF2_VOTE_MENU_SCRAMBLE); - valid = true; - } - case NativeVotesOverride_Eternaween: - { - strcopy(translationString, maxlength, TF2_VOTE_MENU_ETERNAWEEN); - valid = true; - } - case NativeVotesOverride_AutoBalance: - { - if (GetConVarBool(g_Cvar_AutoBalance)) - { - strcopy(translationString, maxlength, TF2_VOTE_MENU_AUTOBALANCE_OFF); - } - else - { - strcopy(translationString, maxlength, TF2_VOTE_MENU_AUTOBALANCE_ON); - } - valid = true; - } - case NativeVotesOverride_ClassLimits: - { - if (GetConVarInt(g_Cvar_ClassLimit)) - { - strcopy(translationString, maxlength, TF2_VOTE_MENU_CLASSLIMIT_OFF); - } - else - { - strcopy(translationString, maxlength, TF2_VOTE_MENU_CLASSLIMIT_ON); - } - valid = true; - } - case NativeVotesOverride_Extend: - { - strcopy(translationString, maxlength, TF2_VOTE_MENU_EXTEND); - } - } - - return valid; -} - -stock bool Game_IsVoteTypeCustom(NativeVotesType voteType) -{ - switch(voteType) - { - case NativeVotesType_Custom_YesNo, NativeVotesType_Custom_Mult: - { - return true; - } - } - - return false; -} - -stock bool Game_IsVoteTypeYesNo(NativeVotesType voteType) -{ - switch(voteType) - { - case NativeVotesType_Custom_Mult, NativeVotesType_NextLevelMult: - { - return false; - } - } - - return true; +/** + * vim: set ts=4 : + * ============================================================================= + * NativeVotes + * NativeVotes is a voting API plugin for L4D, L4D2, TF2, and CS:GO. + * Based on the SourceMod voting API + * + * NativeVotes (C) 2011-2014 Ross Bemrose (Powerlord). All rights reserved. + * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ +#if defined _nativevotes_game_included + #endinput +#endif + +#define _nativevotes_game_included + +#include + +#define L4DL4D2_COUNT 2 +#define TF2CSGO_COUNT 5 + +#define INVALID_ISSUE -1 + +//---------------------------------------------------------------------------- +// Translation strings + +//---------------------------------------------------------------------------- +// L4D/L4D2 + +#define L4DL4D2_VOTE_YES_STRING "Yes" +#define L4DL4D2_VOTE_NO_STRING "No" + +#define L4D_VOTE_KICK_START "#L4D_vote_kick_player" +#define L4D_VOTE_KICK_PASSED "#L4D_vote_passed_kick_player" + +// User vote to restart map. +#define L4D_VOTE_RESTART_START "#L4D_vote_restart_game" +#define L4D_VOTE_RESTART_PASSED "#L4D_vote_passed_restart_game" + +// User vote to change maps. +#define L4D_VOTE_CHANGECAMPAIGN_START "#L4D_vote_mission_change" +#define L4D_VOTE_CHANGECAMPAIGN_PASSED "#L4D_vote_passed_mission_change" +#define L4D_VOTE_CHANGELEVEL_START "#L4D_vote_chapter_change" +#define L4D_VOTE_CHANGELEVEL_PASSED "#L4D_vote_passed_chapter_change" + +// User vote to return to lobby. +#define L4D_VOTE_RETURNTOLOBBY_START "#L4D_vote_return_to_lobby" +#define L4D_VOTE_RETURNTOLOBBY_PASSED "#L4D_vote_passed_return_to_lobby" + +// User vote to change difficulty. +#define L4D_VOTE_CHANGEDIFFICULTY_START "#L4D_vote_change_difficulty" +#define L4D_VOTE_CHANGEDIFFICULTY_PASSED "#L4D_vote_passed_change_difficulty" + +// While not a vote string, it works just as well. +#define L4D_VOTE_CUSTOM "#L4D_TargetID_Player" + +//---------------------------------------------------------------------------- +// L4D2 + +// User vote to change alltalk. +#define L4D2_VOTE_ALLTALK_START "#L4D_vote_alltalk_change" +#define L4D2_VOTE_ALLTALK_PASSED "#L4D_vote_passed_alltalk_change" +#define L4D2_VOTE_ALLTALK_ENABLE "#L4D_vote_alltalk_enable" +#define L4D2_VOTE_ALLTALK_DISABLE "#L4D_vote_alltalk_disable" + +//---------------------------------------------------------------------------- +// TF2/CSGO +#define TF2CSGO_VOTE_PREFIX "option" + +#define TF2CSGO_VOTE_STRING_KICK "Kick" +#define TF2CSGO_VOTE_STRING_RESTART "RestartGame" +#define TF2CSGO_VOTE_STRING_CHANGELEVEL "ChangeLevel" +#define TF2CSGO_VOTE_STRING_NEXTLEVEL "NextLevel" +#define TF2CSGO_VOTE_STRING_SCRAMBLE "ScrambleTeams" + +//---------------------------------------------------------------------------- +// TF2 + +#define TF2_VOTE_STRING_CHANGEMISSION "ChangeMission" + +// This one doesn't actually exist in the normal vote menu, but it has a translation string for it. +// Thus, this is the name we will use for it internally +#define TF2_VOTE_STRING_ETERNAWEEN "Eternaween" + +// These are toggles, but the on and off versions are identical +#define TF2_VOTE_STRING_AUTOBALANCE "TeamAutoBalance" +#define TF2_VOTE_STRING_CLASSLIMIT "ClassLimits" + +// New as of 2015-09-24 +#define TF2_VOTE_STRING_EXTEND "ExtendLevel" + +// Menu items for votes +#define TF2_VOTE_MENU_RESTART "#Vote_RestartGame" +#define TF2_VOTE_MENU_KICK "#Vote_Kick" +#define TF2_VOTE_MENU_CHANGELEVEL "#Vote_ChangeLevel" +#define TF2_VOTE_MENU_NEXTLEVEL "#Vote_NextLevel" +#define TF2_VOTE_MENU_SCRAMBLE "#Vote_ScrambleTeams" +#define TF2_VOTE_MENU_CHANGEMISSION "#Vote_ChangeMission" +#define TF2_VOTE_MENU_ETERNAWEEN "#Vote_Eternaween" +#define TF2_VOTE_MENU_AUTOBALANCE_ON "#Vote_TeamAutoBalance_Enable" +#define TF2_VOTE_MENU_AUTOBALANCE_OFF "#Vote_TeamAutoBalance_Disable" +#define TF2_VOTE_MENU_CLASSLIMIT_ON "#Vote_ClassLimit_Enable" +#define TF2_VOTE_MENU_CLASSLIMIT_OFF "#Vote_ClassLimit_Disable" +#define TF2_VOTE_MENU_EXTEND "#Vote_ExtendLevel" + +// User vote to kick user. +#define TF2_VOTE_KICK_IDLE_START "#TF_vote_kick_player_idle" +#define TF2_VOTE_KICK_SCAMMING_START "#TF_vote_kick_player_scamming" +#define TF2_VOTE_KICK_CHEATING_START "#TF_vote_kick_player_cheating" +#define TF2_VOTE_KICK_START "#TF_vote_kick_player_other" +#define TF2_VOTE_KICK_PASSED "#TF_vote_passed_kick_player" + +// User vote to restart map. +#define TF2_VOTE_RESTART_START "#TF_vote_restart_game" +#define TF2_VOTE_RESTART_PASSED "#TF_vote_passed_restart_game" + +// User vote to change maps. +#define TF2_VOTE_CHANGELEVEL_START "#TF_vote_changelevel" +#define TF2_VOTE_CHANGELEVEL_PASSED "#TF_vote_passed_changelevel" + +// User vote to change next level. +#define TF2_VOTE_NEXTLEVEL_SINGLE_START "#TF_vote_nextlevel" +#define TF2_VOTE_NEXTLEVEL_MULTIPLE_START "#TF_vote_nextlevel_choices" // Started by server +#define TF2_VOTE_NEXTLEVEL_EXTEND_PASSED "#TF_vote_passed_nextlevel_extend" // Also used for extend vote +#define TF2_VOTE_NEXTLEVEL_PASSED "#TF_vote_passed_nextlevel" + +// User vote to scramble teams. +// Can be immediate or end of round. +#define TF2_VOTE_SCRAMBLE_IMMEDIATE_START "#TF_vote_scramble_teams" +#define TF2_VOTE_SCRAMBLE_ROUNDEND_START "#TF_vote_should_scramble_round" +#define TF2_VOTE_SCRAMBLE_PASSED "#TF_vote_passed_scramble_teams" + +// User vote to change MvM mission +#define TF2_VOTE_CHANGEMISSION_START "#TF_vote_changechallenge" +#define TF2_VOTE_CHANGEMISSION_PASSED "#TF_vote_passed_changechallenge" + +// User vote for eternaween +#define TF2_VOTE_ETERNAWEEN_START "#TF_vote_eternaween" +#define TF2_VOTE_ETERNAWEEN_PASSED "#TF_vote_passed_eternaween" + +// User vote to start round +#define TF2_VOTE_ROUND_START "#TF_vote_td_start_round" +#define TF2_VOTE_ROUND_PASSED "#TF_vote_passed_td_start_round" + +// User vote to enable autobalance +#define TF2_VOTE_AUTOBALANCE_ENABLE_START "#TF_vote_autobalance_enable" +#define TF2_VOTE_AUTOBALANCE_ENABLE_PASSED "#TF_vote_passed_autobalance_enable" + +// User vote to disable autobalance +#define TF2_VOTE_AUTOBALANCE_DISABLE_START "#TF_vote_autobalance_disable" +#define TF2_VOTE_AUTOBALANCE_DISABLE_PASSED "#TF_vote_passed_autobalance_disable" + +// User vote to enable classlimits +#define TF2_VOTE_CLASSLIMITS_ENABLE_START "#TF_vote_classlimits_enable" +#define TF2_VOTE_CLASSLIMITS_ENABLE_PASSED "#TF_vote_passed_classlimits_enable" + +// User vote to disable classlimits +#define TF2_VOTE_CLASSLIMITS_DISABLE_START "#TF_vote_classlimits_disable" +#define TF2_VOTE_CLASSLIMITS_DISABLE_PASSED "#TF_vote_passed_classlimits_disable" + +// Use vote to extend map. +#define TF2_VOTE_EXTEND_START "#TF_vote_extendlevel" + +// While not a vote string, it works just as well. +#define TF2_VOTE_CUSTOM "#TF_playerid_noteam" + +// TF2 (and SDK2013?) VoteFail / CallVoteFail reasons +enum +{ + VOTE_FAILED_GENERIC, + VOTE_FAILED_TRANSITIONING_PLAYERS, + VOTE_FAILED_RATE_EXCEEDED, + VOTE_FAILED_YES_MUST_EXCEED_NO, + VOTE_FAILED_QUORUM_FAILURE, + VOTE_FAILED_ISSUE_DISABLED, + VOTE_FAILED_MAP_NOT_FOUND, + VOTE_FAILED_MAP_NAME_REQUIRED, + VOTE_FAILED_FAILED_RECENTLY, + VOTE_FAILED_TEAM_CANT_CALL, + VOTE_FAILED_WAITINGFORPLAYERS, + VOTE_FAILED_PLAYERNOTFOUND, + VOTE_FAILED_CANNOT_KICK_ADMIN, + VOTE_FAILED_SCRAMBLE_IN_PROGRESS, + VOTE_FAILED_SPECTATOR, + VOTE_FAILED_NEXTLEVEL_SET, + VOTE_FAILED_MAP_NOT_VALID, + VOTE_FAILED_CANNOT_KICK_FOR_TIME, + VOTE_FAILED_CANNOT_KICK_DURING_ROUND, + VOTE_FAILED_MODIFICATION_ALREADY_ACTIVE, +} + +//---------------------------------------------------------------------------- +// CSGO +// User vote to kick user. +#define CSGO_VOTE_KICK_IDLE_START "#SFUI_vote_kick_player_idle" +#define CSGO_VOTE_KICK_SCAMMING_START "#SFUI_vote_kick_player_scamming" +#define CSGO_VOTE_KICK_CHEATING_START "#SFUI_vote_kick_player_cheating" +#define CSGO_VOTE_KICK_START "#SFUI_vote_kick_player_other" +#define CSGO_VOTE_KICK_OTHERTEAM "#SFUI_otherteam_vote_kick_player" +#define CSGO_VOTE_KICK_PASSED "#SFUI_vote_passed_kick_player" + +// User vote to restart map. +#define CSGO_VOTE_RESTART_START "#SFUI_vote_restart_game" +#define CSGO_VOTE_RESTART_PASSED "#SFUI_vote_passed_restart_game" + +// User vote to change maps. +#define CSGO_VOTE_CHANGELEVEL_START "#SFUI_vote_changelevel" +#define CSGO_VOTE_CHANGELEVEL_PASSED "#SFUI_vote_passed_changelevel" + +// User vote to change next level. +#define CSGO_VOTE_NEXTLEVEL_SINGLE_START "#SFUI_vote_nextlevel" +#define CSGO_VOTE_NEXTLEVEL_MULTIPLE_START "#SFUI_vote_nextlevel_choices" // Started by server +#define CSGO_VOTE_NEXTLEVEL_EXTEND_PASSED "#SFUI_vote_passed_nextlevel_extend" +#define CSGO_VOTE_NEXTLEVEL_PASSED "#SFUI_vote_passed_nextlevel" + +// User vote to scramble teams. +#define CSGO_VOTE_SCRAMBLE_START "#SFUI_vote_scramble_teams" +#define CSGO_VOTE_SCRAMBLE_PASSED "#SFUI_vote_passed_scramble_teams" + +// User vote to swap teams. +#define CSGO_VOTE_SWAPTEAMS_START "#SFUI_vote_swap_teams" +#define CSGO_VOTE_SWAPTEAMS_PASSED "#SFUI_vote_passed_swap_teams" + +// User vote to surrender +#define CSGO_VOTE_SURRENDER_START "#SFUI_vote_surrender" +#define CSGO_VOTE_SURRENDER_OTHERTEAM "#SFUI_otherteam_vote_continue_or_surrender" +#define CSGO_VOTE_SURRENDER_PASSED "#SFUI_vote_passed_surrender" + +// User vote to rematch +#define CSGO_VOTE_REMATCH_START "#SFUI_vote_rematch" +#define CSGO_VOTE_REMATCH_PASSED "#SFUI_vote_passed_rematch" + +// User vote to continue match with bots +#define CSGO_VOTE_CONTINUE_START "#SFUI_vote_continue" +#define CSGO_VOTE_CONTINUE_OTHERTEAM "#SFUI_otherteam_vote_continue_or_surrender" +#define CSGO_VOTE_CONTINUE_PASSED "#SFUI_vote_passed_continue" + +// User vote to pause game +#define CSGO_VOTE_PAUSE_START "#SFUI_Vote_pause_match" +#define CSGO_VOTE_PAUSE_PASSED "#SFUI_vote_passed_pause_match" + +// User vote to unpause game +#define CSGO_VOTE_UNPAUSE_START "#SFUI_Vote_unpause_match" +#define CSGO_VOTE_UNPAUSE_PASSED "#SFUI_vote_passed_unpause_match" + +// User vote to load backups +#define CSGO_VOTE_LOADBACKUP_START "#SFUI_Vote_loadbackup" +#define CSGO_VOTE_LOADBACKUP_PASSED "#SFUI_vote_passed_loadbackup" + +// User vote to start match +#define CSGO_VOTE_READY_START "#SFUI_Vote_ready_for_match" +#define CSGO_VOTE_READY_PASSED "#SFUI_vote_passed_ready_for_match" + +// User vote to delay match start +#define CSGO_VOTE_NOTREADY_START "#SFUI_Vote_not_ready_for_match" +#define CSGO_VOTE_NOTREADY_PASSED "#SFUI_vote_passed_not_ready_for_match" + +// User vote to start round +#define CSGO_VOTE_ROUND_START "#SFUI_vote_td_start_round" +#define CSGO_VOTE_ROUND_PASSED "#SFUI_vote_passed_td_start_round" + +#define CSGO_VOTE_UNIMPLEMENTED_OTHERTEAM "#SFUI_otherteam_vote_unimplemented" + +// While not a vote string, it works just as well. +#define CSGO_VOTE_CUSTOM "#SFUI_Scoreboard_NormalPlayer" + +// CSGO VoteFail / CallVoteFail reasons +enum +{ + CSGO_VOTE_FAILED_GENERIC, + CSGO_VOTE_FAILED_TRANSITIONING_PLAYERS, + CSGO_VOTE_FAILED_RATE_EXCEEDED, + CSGO_VOTE_FAILED_YES_MUST_EXCEED_NO, + CSGO_VOTE_FAILED_QUORUM_FAILURE, + CSGO_VOTE_FAILED_ISSUE_DISABLED, + CSGO_VOTE_FAILED_MAP_NOT_FOUND, + CSGO_VOTE_FAILED_MAP_NAME_REQUIRED, + CSGO_VOTE_FAILED_FAILED_RECENTLY, + CSGO_VOTE_FAILED_FAILED_RECENTLY_KICK, + CSGO_VOTE_FAILED_FAILED_RECENTLY_MAP, + CSGO_VOTE_FAILED_FAILED_RECENTLY_SWAP, + CSGO_VOTE_FAILED_FAILED_RECENTLY_SCRAMBLE, + CSGO_VOTE_FAILED_FAILED_RECENTLY_RESTART, + CSGO_VOTE_FAILED_TEAM_CANT_CALL, + CSGO_VOTE_FAILED_WARMUP, + CSGO_VOTE_FAILED_PLAYERNOTFOUND, + CSGO_VOTE_FAILED_CANNOT_KICK_ADMIN, + CSGO_VOTE_FAILED_SCRAMBLE_IN_PROGRESS, + CSGO_VOTE_FAILED_SWAP_IN_PROGRESS, + CSGO_VOTE_FAILED_SPECTATOR, + CSGO_VOTE_FAILED_NEXTLEVEL_SET, + CSGO_VOTE_FAILED_UNKNOWN1, + CSGO_VOTE_FAILED_SURRENDER_ABANDON, + CSGO_VOTE_FAILED_UNKNOWN2, + CSGO_VOTE_FAILED_PAUSED, + CSGO_VOTE_FAILED_NOT_PAUSED, + CSGO_VOTE_FAILED_NOT_WARMUP, + CSGO_VOTE_FAILED_MIN_PLAYERS, + CSGO_VOTE_FAILED_ROUND_ENDED, +} + + +//---------------------------------------------------------------------------- +// Generic functions +// + +// This is deprecated in NativeVotes 1.1 +enum +{ + ValveVote_Kick = 0, + ValveVote_Restart = 1, + ValveVote_ChangeLevel = 2, + ValveVote_NextLevel = 3, + ValveVote_Scramble = 4, + ValveVote_SwapTeams = 5, +} + +static int g_VoteController = -1; +static bool g_bUserBuf = false; + +static ConVar g_Cvar_Votes_Enabled; +static ConVar g_Cvar_VoteKick_Enabled; +static ConVar g_Cvar_MvM_VoteKick_Enabled; +static ConVar g_Cvar_VoteNextLevel_Enabled; +static ConVar g_Cvar_VoteChangeLevel_Enabled; +static ConVar g_Cvar_MvM_VoteChangeLevel_Enabled; +static ConVar g_Cvar_VoteRestart_Enabled; +static ConVar g_Cvar_MvM_VoteRestart_Enabled; +static ConVar g_Cvar_VoteScramble_Enabled; +static ConVar g_Cvar_MvM_VoteChallenge_Enabled; +static ConVar g_Cvar_VoteAutoBalance_Enabled; +static ConVar g_Cvar_VoteClassLimits_Enabled; +static ConVar g_Cvar_MvM_VoteClassLimits_Enabled; +static ConVar g_Cvar_VoteExtend_Enabled; + +static ConVar g_Cvar_ClassLimit; +static ConVar g_Cvar_AutoBalance; + +static ConVar g_Cvar_HideDisabledIssues; + +/** + * TODO(UPDATE): For now we only support one vote from NativeVotes at a time. + * + * Ideally we peek and poke at the game's `s_nVoteIdx` and increment it accordingly, but we also + * have to store an internal list of active votes. + * + * TODO: https://github.com/Heapons/sourcemod-nativevotes-updated/issues/2#issuecomment-3825973400 + */ +static int s_nNativeVoteIdx = 0; + +bool Game_IsGameSupported(char[] engineName="", int maxlength=0) +{ + g_EngineVersion = GetEngineVersion(); + + g_bUserBuf = GetFeatureStatus(FeatureType_Native, "GetUserMessageType") == FeatureStatus_Available && GetUserMessageType() == UM_Protobuf; + + //LogMessage("Detected Engine version: %d", g_EngineVersion); + if (maxlength > 0) + { + GetEngineVersionName(g_EngineVersion, engineName, maxlength); + } + + char gameDir[128]; + GetGameFolderName(gameDir, sizeof(gameDir)); + if (!StrEqual(gameDir, "tf")) + { + if (g_EngineVersion == Engine_SDK2013 && FileExists("resource/tf.ttf")) + { + g_EngineVersion = Engine_TF2; + } + else + { + KeyValues kv = new KeyValues("GameInfo"); + if (kv.ImportFromFile("gameinfo.txt") && kv.GetNum("DependsOnAppID") == 440) + { + g_EngineVersion = Engine_TF2; + } + delete kv; + } + } + + switch (g_EngineVersion) + { + case Engine_Left4Dead, Engine_Left4Dead2, Engine_CSGO, Engine_TF2: + { + return true; + } + } + + return false; +} + +void Game_InitializeCvars() +{ + switch (g_EngineVersion) + { + case Engine_Left4Dead, Engine_Left4Dead2, Engine_CSGO: + { + g_Cvar_Votes_Enabled = FindConVar("sv_allow_votes"); + } + case Engine_TF2: + { + g_Cvar_Votes_Enabled = FindConVar("sv_allow_votes"); + g_Cvar_VoteKick_Enabled = FindConVar("sv_vote_issue_kick_allowed"); + g_Cvar_MvM_VoteKick_Enabled = FindConVar("sv_vote_issue_kick_allowed_mvm"); + g_Cvar_VoteNextLevel_Enabled = FindConVar("sv_vote_issue_nextlevel_allowed"); + g_Cvar_VoteChangeLevel_Enabled = FindConVar("sv_vote_issue_changelevel_allowed"); + g_Cvar_MvM_VoteChangeLevel_Enabled = FindConVar("sv_vote_issue_changelevel_allowed_mvm"); + g_Cvar_VoteRestart_Enabled = FindConVar("sv_vote_issue_restart_game_allowed"); + g_Cvar_MvM_VoteRestart_Enabled = FindConVar("sv_vote_issue_restart_game_allowed_mvm"); + g_Cvar_VoteScramble_Enabled = FindConVar("sv_vote_issue_scramble_teams_allowed"); + g_Cvar_MvM_VoteChallenge_Enabled = FindConVar("sv_vote_issue_mvm_challenge_allowed"); + g_Cvar_VoteAutoBalance_Enabled = FindConVar("sv_vote_issue_autobalance_allowed"); + g_Cvar_VoteClassLimits_Enabled = FindConVar("sv_vote_issue_classlimits_allowed"); + g_Cvar_MvM_VoteClassLimits_Enabled = FindConVar("sv_vote_issue_classlimits_allowed_mvm"); + g_Cvar_VoteExtend_Enabled = FindConVar("sv_vote_issue_extendlevel_allowed"); + + g_Cvar_ClassLimit = FindConVar("tf_classlimit"); + g_Cvar_AutoBalance = FindConVar("mp_autoteambalance"); + + g_Cvar_HideDisabledIssues = FindConVar("sv_vote_ui_hide_disabled_issues"); + } + } +} + +NativeVotesKickType Game_GetKickType(const char[] param1, int &target) +{ + NativeVotesKickType kickType; + + switch(g_EngineVersion) + { + case Engine_Left4Dead, Engine_Left4Dead2, Engine_CSGO: + { + target = StringToInt(param1); + kickType = NativeVotesKickType_Generic; + } + case Engine_TF2: + { + char params[2][20]; + ExplodeString(param1, " ", params, sizeof(params), sizeof(params[])); + + target = StringToInt(params[0]); + + if (StrEqual(params[1], "cheating", false)) + { + kickType = NativeVotesKickType_Cheating; + } + else if (StrEqual(params[1], "idle", false)) + { + kickType = NativeVotesKickType_Idle; + } + else if (StrEqual(params[1], "scamming", false)) + { + kickType = NativeVotesKickType_Scamming; + } + else + { + kickType = NativeVotesKickType_Generic; + } + } + } + return kickType; +} + +bool CheckVoteController() +{ + int entity = INVALID_ENT_REFERENCE; + if (g_VoteController != -1) + { + entity = EntRefToEntIndex(g_VoteController); + } + + if (entity == INVALID_ENT_REFERENCE) + { + entity = FindEntityByClassname(-1, "vote_controller"); + if (entity == -1) + { + //LogError("Could not find Vote Controller."); + return false; + } + + g_VoteController = EntIndexToEntRef(entity); + } + return true; +} + +// All logic for choosing a game-specific function should happen here. +// There should be one per function in the game shared and specific sections +int Game_ParseVote(const char[] option) +{ + int item = NATIVEVOTES_VOTE_INVALID; + +#if defined LOG + LogMessage("Parsing vote option %s", option); +#endif + + switch(g_EngineVersion) + { + case Engine_Left4Dead, Engine_Left4Dead2: + { + item = L4DL4D2_ParseVote(option); + } + case Engine_CSGO: + { + item = CSGO_ParseVote(option); + } + case Engine_TF2: + { + item = TF2_ParseVote(option); + } + } + + return item; + +} + +int Game_GetMaxItems() +{ + switch (g_EngineVersion) + { + case Engine_Left4Dead, Engine_Left4Dead2: + { + return L4DL4D2_COUNT; + } + case Engine_CSGO, Engine_TF2: + { + return TF2CSGO_COUNT; + } + } + + return 0; // Here to prevent warnings +} + +bool Game_CheckVoteType(NativeVotesType type) +{ + bool returnVal = false; + + switch(g_EngineVersion) + { + case Engine_Left4Dead: + { + returnVal = L4D_CheckVoteType(type); + } + case Engine_Left4Dead2: + { + returnVal = L4D2_CheckVoteType(type); + } + case Engine_CSGO: + { + returnVal = CSGO_CheckVoteType(type); + } + case Engine_TF2: + { + returnVal = TF2_CheckVoteType(type); + } + } + + return returnVal; +} + +bool Game_CheckVotePassType(NativeVotesPassType type) +{ + bool returnVal = false; + + switch(g_EngineVersion) + { + case Engine_Left4Dead: + { + returnVal = L4D_CheckVotePassType(type); + } + case Engine_Left4Dead2: + { + returnVal = L4D2_CheckVotePassType(type); + } + case Engine_CSGO: + { + returnVal = CSGO_CheckVotePassType(type); + } + case Engine_TF2: + { + returnVal = TF2_CheckVotePassType(type); + } + } + + return returnVal; +} + +bool Game_DisplayVoteToOne(NativeVote vote, int client) +{ + if (g_bCancelled) + { + return false; + } + + int clients[1]; + clients[0] = client; + + return Game_DisplayVote(vote, clients, 1); +} + +bool Game_DisplayVote(NativeVote vote, int[] clients, int num_clients) +{ + if (g_bCancelled) + { + return false; + } + + switch(g_EngineVersion) + { + case Engine_Left4Dead: + { + L4D_DisplayVote(vote, num_clients); + } + case Engine_Left4Dead2: + { + L4D2_DisplayVote(vote, clients, num_clients); + } + case Engine_CSGO, Engine_TF2: + { + TF2CSGO_DisplayVote(vote, clients, num_clients); + } + } + + +#if defined LOG + char details[MAX_VOTE_DETAILS_LENGTH]; + char translation[TRANSLATION_LENGTH]; + + NativeVotesType voteType = Data_GetType(vote); + Data_GetDetails(vote, details, sizeof(details)); + Game_VoteTypeToTranslation(voteType, translation, sizeof(translation)); + + LogMessage("Displaying vote: type: %d, translation: \"%s\", details: \"%s\"", voteType, translation, details); +#endif + + return true; +} + +void Game_DisplayVoteFail(NativeVote vote, NativeVotesFailType reason) +{ + int team = Data_GetTeam(vote); + + int total = 0; + int[] players = new int[MaxClients]; + + for (int i = 1; i <= MaxClients; i++) + { + if (!IsClientInGame(i) || IsFakeClient(i)) + continue; + players[total++] = i; + } + + Game_DisplayRawVoteFail(players, total, reason, team); +} + +void Game_DisplayRawVoteFail(int[] clients, int numClients, NativeVotesFailType reason, int team) +{ + switch(g_EngineVersion) + { + case Engine_Left4Dead: + { + L4D_VoteFail(team); + } + case Engine_Left4Dead2: + { + L4D2_VoteFail(clients, numClients, team); + } + case Engine_CSGO: + { + int reasonType = VoteFailTypeToInt(reason); + CSGO_VoteFail(clients, numClients, reasonType, team); + } + case Engine_TF2: + { + int reasonType = VoteFailTypeToInt(reason); + TF2_VoteFail(clients, numClients, reasonType, team); + } + } + +#if defined LOG + LogMessage("Vote Failed to %d client(s): \"%d\"", numClients, reason); +#endif +} + +void Game_DisplayVotePass(NativeVote vote, const char[] details="", int client=0) +{ + NativeVotesPassType passType = VoteTypeToVotePass(Data_GetType(vote)); + + Game_DisplayVotePassEx(vote, passType, details, client); +} + +void Game_DisplayVotePassEx(NativeVote vote, NativeVotesPassType passType, const char[] details="", int client=0) +{ + int team = Data_GetTeam(vote); + + Game_DisplayRawVotePass(passType, team, client, details); +} + +void Game_DisplayRawVotePass(NativeVotesPassType passType, int team, int client=0, const char[] details="") +{ + char translation[TRANSLATION_LENGTH]; + + switch (g_EngineVersion) + { + case Engine_Left4Dead: + { + if (client <= 0) + { + L4DL4D2_VotePassToTranslation(passType, translation, sizeof(translation)); + L4D_VotePass(translation, details, team); + } + } + case Engine_Left4Dead2: + { + L4DL4D2_VotePassToTranslation(passType, translation, sizeof(translation)); + + switch (passType) + { + case NativeVotesPass_AlltalkOn: + { + L4D2_VotePass(translation, L4D2_VOTE_ALLTALK_ENABLE, team, client); + } + case NativeVotesPass_AlltalkOff: + { + L4D2_VotePass(translation, L4D2_VOTE_ALLTALK_DISABLE, team, client); + } + default: + { + L4D2_VotePass(translation, details, team, client); + } + } + } + case Engine_CSGO: + { + CSGO_VotePassToTranslation(passType, translation, sizeof(translation)); + CSGO_VotePass(translation, details, team, client); + } + case Engine_TF2: + { + TF2_VotePassToTranslation(passType, translation, sizeof(translation)); + TF2_VotePass(translation, details, team, client); + } + } + +#if defined LOG + if (client != 0) + LogMessage("Vote Passed: \"%s\", \"%s\"", translation, details); +#endif +} + +void Game_DisplayVotePassCustom(NativeVote vote, const char[] translation, int client) +{ + int team = Data_GetTeam(vote); + Game_DisplayRawVotePassCustom(translation, team, client); +} + +void Game_DisplayRawVotePassCustom(const char[] translation, int team, int client) +{ + switch (g_EngineVersion) + { + case Engine_Left4Dead: + { + ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes_DisplayPassCustom is not supported on L4D"); + } + case Engine_Left4Dead2: + { + L4D2_VotePass(L4D_VOTE_CUSTOM, translation, team, client); + } + case Engine_CSGO: + { + CSGO_VotePass(CSGO_VOTE_CUSTOM, translation, team, client); + } + case Engine_TF2: + { + TF2_VotePass(TF2_VOTE_CUSTOM, translation, team, client); + } + } + +#if defined LOG + if (client != 0) + LogMessage("Vote Passed Custom: \"%s\"", translation); +#endif +} + +void Game_DisplayCallVoteFail(int client, NativeVotesCallFailType reason, int time) +{ + + int reasonType = VoteCallFailTypeToInt(reason); + + switch (g_EngineVersion) + { + case Engine_Left4Dead, Engine_Left4Dead2: + { + L4DL4D2_CallVoteFail(client, reasonType); + } + case Engine_CSGO: + { + CSGO_CallVoteFail(client, reasonType, time); + } + case Engine_TF2: + { + TF2_CallVoteFail(client, reasonType, time); + } + } + +#if defined LOG + LogMessage("Call vote failed: client: %N, reason: %d, time: %d", client, reason, time); +#endif +} + +void Game_ClientSelectedItem(NativeVote vote, int client, int item) +{ +#if defined LOG + LogMessage("Client %N selected item %d", client, item); +#endif + switch(g_EngineVersion) + { + case Engine_Left4Dead, Engine_Left4Dead2: + { + L4DL4D2_ClientSelectedItem(client, item); + } + case Engine_CSGO: + { + CSGO_ClientSelectedItem(vote, client, item); + } + case Engine_TF2: + { + TF2_ClientSelectedItem(vote, client, item); + } + } +} + +void Game_UpdateVoteCounts(ArrayList hVoteCounts, int totalClients) +{ + switch(g_EngineVersion) + { + case Engine_Left4Dead, Engine_Left4Dead2: + { + L4DL4D2_UpdateVoteCounts(hVoteCounts, totalClients); + } + case Engine_CSGO, Engine_TF2: + { + TF2CSGO_UpdateVoteCounts(hVoteCounts); + } + } +} + +void Game_DisplayVoteSetup(int client, ArrayList hVoteTypes) +{ + switch (g_EngineVersion) + { + case Engine_TF2: + { + TF2_DisplayVoteSetup(client, hVoteTypes); + } + } +} + +// stock because at the moment it's only used in logging code which isn't always compiled. +stock void Game_VoteTypeToTranslation(NativeVotesType voteType, char[] translation, int maxlength) +{ + switch(g_EngineVersion) + { + case Engine_Left4Dead, Engine_Left4Dead2: + { + L4DL4D2_VoteTypeToTranslation(voteType, translation, maxlength); + } + case Engine_CSGO: + { + CSGO_VoteTypeToTranslation(voteType, translation, maxlength); + } + case Engine_TF2: + { + TF2_VoteTypeToTranslation(voteType, translation, maxlength); + } + } +} + +stock void Game_UpdateClientCount(int num_clients) +{ + switch(g_EngineVersion) + { + case Engine_Left4Dead, Engine_Left4Dead2: + { + L4DL4D2_UpdateClientCount(num_clients); + } + case Engine_CSGO, Engine_TF2: + { + TF2CSGO_UpdateClientCount(num_clients); + } + } +} + +public Action Game_ResetVote(Handle timer) +{ + switch(g_EngineVersion) + { + case Engine_Left4Dead, Engine_Left4Dead2: + { + L4DL4D2_ResetVote(); + } + case Engine_CSGO, Engine_TF2: + { + TF2CSGO_ResetVote(); + } + } + return Plugin_Continue; +} + +void Game_VoteYes(int client) +{ + switch (g_EngineVersion) + { + case Engine_Left4Dead, Engine_Left4Dead2: + { + FakeClientCommand(client, "Vote Yes"); + } + case Engine_CSGO, Engine_TF2: + { + FakeClientCommand(client, "vote option1"); + } + } +} + +void Game_VoteNo(int client) +{ + switch (g_EngineVersion) + { + case Engine_Left4Dead, Engine_Left4Dead2: + { + FakeClientCommand(client, "Vote No"); + } + case Engine_CSGO, Engine_TF2: + { + FakeClientCommand(client, "vote option2"); + } + } +} + +bool Game_IsVoteInProgress() +{ + switch (g_EngineVersion) + { + case Engine_Left4Dead, Engine_Left4Dead2: + { + return L4DL4D2_IsVoteInProgress(); + } + case Engine_CSGO, Engine_TF2: + { + return TF2CSGO_IsVoteInProgress(); + } + } + + return false; +} + +bool Game_AreVoteCommandsSupported() +{ + switch (g_EngineVersion) + { + case Engine_TF2: + { + return true; + } + } + + return false; +} + +stock bool Game_VoteTypeToVoteString(NativeVotesType voteType, char[] voteString, int maxlength) +{ + switch (g_EngineVersion) + { + case Engine_TF2: + { + return TF2_VoteTypeToVoteString(voteType, voteString, maxlength); + } + } + + return false; +} + +stock NativeVotesType Game_VoteStringToVoteType(char[] voteString) +{ + switch (g_EngineVersion) + { + case Engine_TF2: + { + return TF2_VoteStringToVoteType(voteString); + } + } + + return NativeVotesType_None; +} + +stock NativeVotesOverride Game_VoteTypeToVoteOverride(NativeVotesType voteType) +{ + switch (g_EngineVersion) + { + case Engine_TF2: + { + return TF2_VoteTypeToVoteOverride(voteType); + } + } + + return NativeVotesOverride_None; +} + +stock NativeVotesType Game_VoteOverrideToVoteType(NativeVotesOverride overrideType) +{ + switch (g_EngineVersion) + { + case Engine_TF2: + { + return TF2_VoteOverrideToVoteType(overrideType); + } + } + + return NativeVotesType_None; +} + +stock NativeVotesOverride Game_VoteStringToVoteOverride(const char[] voteString) +{ + switch (g_EngineVersion) + { + case Engine_TF2: + { + return TF2_VoteStringToVoteOverride(voteString); + } + } + + return NativeVotesOverride_None; +} + +stock bool Game_OverrideTypeToVoteString(NativeVotesOverride overrideType, char[] voteString, int maxlength) +{ + switch (g_EngineVersion) + { + case Engine_TF2: + { + return TF2_OverrideTypeToVoteString(overrideType, voteString, maxlength); + } + } + + return false; +} + +stock bool Game_OverrideTypeToTranslationString(NativeVotesOverride overrideType, char[] translationString, int maxlength) +{ + switch (g_EngineVersion) + { + case Engine_TF2: + { + return TF2_OverrideTypeToTranslationString(overrideType, translationString, maxlength); + } + } + + return false; +} + +void Game_AddDefaultVotes(ArrayList hVoteTypes) +{ + switch (g_EngineVersion) + { + case Engine_TF2: + { + TF2_AddDefaultVotes(hVoteTypes, Game_AreDisabledIssuesHidden()); + } + } +} + +bool Game_AreDisabledIssuesHidden() +{ + switch (g_EngineVersion) + { + case Engine_TF2: + { + return g_Cvar_HideDisabledIssues.BoolValue; + } + } + + return true; +} + +// All games shared functions + +//---------------------------------------------------------------------------- +// Data functions + +static NativeVotesPassType VoteTypeToVotePass(NativeVotesType voteType) +{ + NativeVotesPassType passType = NativeVotesPass_None; + + switch(voteType) + { + case NativeVotesType_Custom_YesNo, NativeVotesType_Custom_Mult: + { + passType = NativeVotesPass_Custom; + } + case NativeVotesType_ChgCampaign: + { + passType = NativeVotesPass_ChgCampaign; + } + case NativeVotesType_ChgDifficulty: + { + passType = NativeVotesPass_ChgDifficulty; + } + case NativeVotesType_ReturnToLobby: + { + passType = NativeVotesPass_ReturnToLobby; + } + case NativeVotesType_AlltalkOn: + { + passType = NativeVotesPass_AlltalkOn; + } + case NativeVotesType_AlltalkOff: + { + passType = NativeVotesPass_AlltalkOff; + } + case NativeVotesType_Restart: + { + passType = NativeVotesPass_Restart; + } + case NativeVotesType_Kick, NativeVotesType_KickIdle, NativeVotesType_KickScamming, NativeVotesType_KickCheating: + { + passType = NativeVotesPass_Kick; + } + case NativeVotesType_ChgLevel: + { + passType = NativeVotesPass_ChgLevel; + } + case NativeVotesType_NextLevel, NativeVotesType_NextLevelMult: + { + passType = NativeVotesPass_NextLevel; + } + case NativeVotesType_ScrambleNow, NativeVotesType_ScrambleEnd: + { + passType = NativeVotesPass_Scramble; + } + case NativeVotesType_ChgMission: + { + passType = NativeVotesPass_ChgMission; + } + case NativeVotesType_SwapTeams: + { + passType = NativeVotesPass_SwapTeams; + } + case NativeVotesType_Surrender: + { + passType = NativeVotesPass_Surrender; + } + case NativeVotesType_Rematch: + { + passType = NativeVotesPass_Rematch; + } + case NativeVotesType_Continue: + { + passType = NativeVotesPass_Continue; + } + case NativeVotesType_StartRound: + { + passType = NativeVotesPass_StartRound; + } + case NativeVotesType_Eternaween: + { + passType = NativeVotesPass_Eternaween; + } + case NativeVotesType_AutoBalanceOn: + { + passType = NativeVotesPass_AutoBalanceOn; + } + case NativeVotesType_AutoBalanceOff: + { + passType = NativeVotesPass_AutoBalanceOff; + } + case NativeVotesType_ClassLimitsOn: + { + passType = NativeVotesPass_ClassLimitsOn; + } + case NativeVotesType_ClassLimitsOff: + { + passType = NativeVotesPass_ClassLimitsOff; + } + case NativeVotesType_Extend: + { + passType = NativeVotesPass_Extend; + } + default: + { + passType = NativeVotesPass_Custom; + } + } + + return passType; +} + +// In case we find more types later +static int VoteFailTypeToInt(NativeVotesFailType failType) +{ + return view_as(failType); +} + +static int VoteCallFailTypeToInt(NativeVotesCallFailType failType) +{ + switch (g_EngineVersion) + { + case Engine_Left4Dead, Engine_Left4Dead2: + { + switch (failType) + { + case NativeVotesCallFail_Generic: + { + return VOTE_FAILED_GENERIC; + } + case NativeVotesCallFail_Loading: + { + return VOTE_FAILED_TRANSITIONING_PLAYERS; + } + case NativeVotesCallFail_Recent: + { + return VOTE_FAILED_FAILED_RECENTLY; + } + } + } + case Engine_TF2: + { + switch (failType) + { + case NativeVotesCallFail_Generic: + { + return VOTE_FAILED_GENERIC; + } + case NativeVotesCallFail_Loading: + { + return VOTE_FAILED_TRANSITIONING_PLAYERS; + } + case NativeVotesCallFail_Recent: + { + return VOTE_FAILED_FAILED_RECENTLY; + } + case NativeVotesCallFail_Disabled: + { + return VOTE_FAILED_ISSUE_DISABLED; + } + case NativeVotesCallFail_MapNotFound: + { + return VOTE_FAILED_MAP_NOT_FOUND; + } + case NativeVotesCallFail_SpecifyMap: + { + return VOTE_FAILED_MAP_NAME_REQUIRED; + } + case NativeVotesCallFail_Failed: + { + return VOTE_FAILED_FAILED_RECENTLY; + } + case NativeVotesCallFail_WrongTeam: + { + return VOTE_FAILED_TEAM_CANT_CALL; + } + case NativeVotesCallFail_Waiting: + { + return VOTE_FAILED_WAITINGFORPLAYERS; + } + case NativeVotesCallFail_PlayerNotFound: + { + return VOTE_FAILED_PLAYERNOTFOUND; + } + case NativeVotesCallFail_CantKickAdmin: + { + return VOTE_FAILED_CANNOT_KICK_ADMIN; + } + case NativeVotesCallFail_ScramblePending: + { + return VOTE_FAILED_SCRAMBLE_IN_PROGRESS; + } + case NativeVotesCallFail_Spectators: + { + return VOTE_FAILED_SPECTATOR; + } + case NativeVotesCallFail_LevelSet: + { + return VOTE_FAILED_NEXTLEVEL_SET; + } + case NativeVotesCallFail_MapNotValid: + { + return VOTE_FAILED_MAP_NOT_VALID; + } + case NativeVotesCallFail_KickTime: + { + return VOTE_FAILED_CANNOT_KICK_FOR_TIME; + } + case NativeVotesCallFail_KickDuringRound: + { + return VOTE_FAILED_CANNOT_KICK_DURING_ROUND; + } + case NativeVotesCallFail_AlreadyActive: + { + return VOTE_FAILED_MODIFICATION_ALREADY_ACTIVE; + } + } + } + case Engine_CSGO: + { + switch (failType) + { + case NativeVotesCallFail_Generic: + { + return CSGO_VOTE_FAILED_GENERIC; + } + case NativeVotesCallFail_Loading: + { + return CSGO_VOTE_FAILED_TRANSITIONING_PLAYERS; + } + case NativeVotesCallFail_Recent: + { + return CSGO_VOTE_FAILED_RATE_EXCEEDED; + } + case NativeVotesCallFail_Disabled: + { + return CSGO_VOTE_FAILED_ISSUE_DISABLED; + } + case NativeVotesCallFail_MapNotFound: + { + return CSGO_VOTE_FAILED_MAP_NOT_FOUND; + } + case NativeVotesCallFail_SpecifyMap: + { + return CSGO_VOTE_FAILED_MAP_NAME_REQUIRED; + } + case NativeVotesCallFail_Failed: + { + return CSGO_VOTE_FAILED_FAILED_RECENTLY; + } + case NativeVotesCallFail_WrongTeam: + { + return CSGO_VOTE_FAILED_TEAM_CANT_CALL; + } + case NativeVotesCallFail_Warmup: + { + return CSGO_VOTE_FAILED_WARMUP; + } + case NativeVotesCallFail_PlayerNotFound: + { + return CSGO_VOTE_FAILED_PLAYERNOTFOUND; + } + case NativeVotesCallFail_CantKickAdmin: + { + return CSGO_VOTE_FAILED_CANNOT_KICK_ADMIN; + } + case NativeVotesCallFail_ScramblePending: + { + return CSGO_VOTE_FAILED_SCRAMBLE_IN_PROGRESS; + } + case NativeVotesCallFail_Spectators: + { + return CSGO_VOTE_FAILED_SPECTATOR; + } + case NativeVotesCallFail_LevelSet: + { + return CSGO_VOTE_FAILED_NEXTLEVEL_SET; + } + case NativeVotesCallFail_KickFailed: + { + return CSGO_VOTE_FAILED_FAILED_RECENTLY_KICK; + } + case NativeVotesCallFail_MapFailed: + { + return CSGO_VOTE_FAILED_FAILED_RECENTLY_MAP; + } + case NativeVotesCallFail_SwapFailed: + { + return CSGO_VOTE_FAILED_FAILED_RECENTLY_SWAP; + } + case NativeVotesCallFail_ScrambleFailed: + { + return CSGO_VOTE_FAILED_FAILED_RECENTLY_SCRAMBLE; + } + case NativeVotesCallFail_RestartFailed: + { + return CSGO_VOTE_FAILED_FAILED_RECENTLY_RESTART; + } + case NativeVotesCallFail_SwapPending: + { + return CSGO_VOTE_FAILED_SWAP_IN_PROGRESS; + } + case NativeVotesCallFail_Unknown2: + { + return CSGO_VOTE_FAILED_UNKNOWN1; + } + case NativeVotesCallFail_CantSurrender: + { + return CSGO_VOTE_FAILED_SURRENDER_ABANDON; + } + case NativeVotesCallFail_Unknown3: + { + return CSGO_VOTE_FAILED_UNKNOWN2; + } + case NativeVotesCallFail_MatchPaused: + { + return CSGO_VOTE_FAILED_PAUSED; + } + case NativeVotesCallFail_NotPaused: + { + return CSGO_VOTE_FAILED_NOT_PAUSED; + } + case NativeVotesCallFail_NotWarmup: + { + return CSGO_VOTE_FAILED_NOT_WARMUP; + } + case NativeVotesCallFail_MinPlayers: + { + return CSGO_VOTE_FAILED_MIN_PLAYERS; + } + case NativeVotesCallFail_RoundEnded: + { + return CSGO_VOTE_FAILED_ROUND_ENDED; + } + } + } + } + + return VOTE_FAILED_GENERIC; +} + +static void GetEngineVersionName(EngineVersion version, char[] printName, int maxlength) +{ + switch (version) + { + case Engine_Unknown: + { + strcopy(printName, maxlength, "Unknown"); + } + case Engine_Original: + { + strcopy(printName, maxlength, "Original"); + } + case Engine_SourceSDK2006: + { + strcopy(printName, maxlength, "Source SDK 2006"); + } + case Engine_SourceSDK2007: + { + strcopy(printName, maxlength, "Source SDK 2007"); + } + case Engine_Left4Dead: + { + strcopy(printName, maxlength, "Left 4 Dead "); + } + case Engine_DarkMessiah: + { + strcopy(printName, maxlength, "Dark Messiah"); + } + case Engine_Left4Dead2: + { + strcopy(printName, maxlength, "Left 4 Dead 2"); + } + case Engine_AlienSwarm: + { + strcopy(printName, maxlength, "Alien Swarm"); + } + case Engine_BloodyGoodTime: + { + strcopy(printName, maxlength, "Bloody Good Time"); + } + case Engine_EYE: + { + strcopy(printName, maxlength, "E.Y.E. Divine Cybermancy"); + } + case Engine_Portal2: + { + strcopy(printName, maxlength, "Portal 2"); + } + case Engine_CSGO: + { + strcopy(printName, maxlength, "Counter-Strike: Global Offensive"); + } + case Engine_CSS: + { + strcopy(printName, maxlength, "Counter-Strike: Source"); + } + case Engine_DOTA: + { + strcopy(printName, maxlength, "DOTA 2"); + } + case Engine_HL2DM: + { + char gameDir[32]; + GetGameFolderName(gameDir, sizeof(gameDir)); + if (StrEqual(gameDir, "hl2mp")) + { + strcopy(printName, maxlength, "Half-Life 2: Deathmatch"); + } + else + { + // https://github.com/alliedmodders/metamod-source/pull/210 + strcopy(printName, maxlength, "Source SDK 2025"); + } + } + case Engine_DODS: + { + strcopy(printName, maxlength, "Day of Defeat: Source"); + } + case Engine_TF2: + { + strcopy(printName, maxlength, "Team Fortress 2"); + } + case Engine_NuclearDawn: + { + strcopy(printName, maxlength, "Nuclear Dawn"); + } + case Engine_SDK2013: + { + strcopy(printName, maxlength , "Source SDK 2013"); + } + case Engine_Blade: + { + strcopy(printName, maxlength , "Blade Symphony"); + } + case Engine_Insurgency: + { + strcopy(printName, maxlength , "Insurgency (2013 Retail version)"); + } + case Engine_Contagion: + { + strcopy(printName, maxlength , "Contagion"); + } + case Engine_BlackMesa: + { + strcopy(printName, maxlength , "Black Mesa Multiplayer"); + } + case Engine_DOI: + { + strcopy(printName, maxlength , "Day of Infamy"); + } + case Engine_PVKII: + { + strcopy(printName, maxlength , "Pirates, Vikings, and Knights II"); + } + case Engine_MCV: + { + strcopy(printName, maxlength , "Military Conflict: Vietnam"); + } + default: + { + strcopy(printName, maxlength, "Not listed"); + } + } +} + + + +//---------------------------------------------------------------------------- +// L4D/L4D2 shared functions + +// NATIVEVOTES_VOTE_INVALID means parse failed +static int L4DL4D2_ParseVote(const char[] option) +{ + if (StrEqual(option, "Yes", false)) + { + return NATIVEVOTES_VOTE_YES; + } + else if (StrEqual(option, "No", false)) + { + return NATIVEVOTES_VOTE_NO; + } + + return NATIVEVOTES_VOTE_INVALID; +} + +static void L4DL4D2_ClientSelectedItem(int client, int item) +{ + int choice; + + if (item == NATIVEVOTES_VOTE_NO) + { + choice = 0; + } + else if (item == NATIVEVOTES_VOTE_YES) + { + choice = 1; + } + + BfWrite voteCast = UserMessageToBfWrite(StartMessageOne("VoteRegistered", client, USERMSG_RELIABLE)); + voteCast.WriteByte(choice); + EndMessage(); +} + +static void L4DL4D2_UpdateVoteCounts(ArrayList votes, int totalClients) +{ + int yesVotes = votes.Get(NATIVEVOTES_VOTE_YES); + int noVotes = votes.Get(NATIVEVOTES_VOTE_NO); + Event changeEvent = CreateEvent("vote_changed"); + changeEvent.SetInt("yesVotes", yesVotes); + changeEvent.SetInt("noVotes", noVotes); + changeEvent.SetInt("potentialVotes", totalClients); + changeEvent.Fire(); + + if (CheckVoteController()) + { + SetEntProp(g_VoteController, Prop_Send, "m_votesYes", yesVotes); + SetEntProp(g_VoteController, Prop_Send, "m_votesNo", noVotes); + } +} + +static stock void L4DL4D2_UpdateClientCount(int num_clients) +{ + if (CheckVoteController()) + { + SetEntProp(g_VoteController, Prop_Send, "m_potentialVotes", num_clients); + } +} + +static void L4DL4D2_CallVoteFail(int client, int reason) +{ + BfWrite callVoteFail = UserMessageToBfWrite(StartMessageOne("CallVoteFailed", client, USERMSG_RELIABLE)); + + callVoteFail.WriteByte(reason); + + EndMessage(); +} + +static void L4DL4D2_VoteTypeToTranslation(NativeVotesType voteType, char[] translation, int maxlength) +{ + switch(voteType) + { + case NativeVotesType_ChgCampaign: + { + strcopy(translation, maxlength, L4D_VOTE_CHANGECAMPAIGN_START); + } + case NativeVotesType_ChgDifficulty: + { + strcopy(translation, maxlength, L4D_VOTE_CHANGEDIFFICULTY_START); + } + case NativeVotesType_ReturnToLobby: + { + strcopy(translation, maxlength, L4D_VOTE_RETURNTOLOBBY_START); + } + case NativeVotesType_AlltalkOn, NativeVotesType_AlltalkOff: + { + strcopy(translation, maxlength, L4D2_VOTE_ALLTALK_START); + } + case NativeVotesType_Restart: + { + strcopy(translation, maxlength, L4D_VOTE_RESTART_START); + } + case NativeVotesType_Kick: + { + strcopy(translation, maxlength, L4D_VOTE_KICK_START); + } + case NativeVotesType_ChgLevel: + { + strcopy(translation, maxlength, L4D_VOTE_CHANGELEVEL_START); + } + default: + { + strcopy(translation, maxlength, L4D_VOTE_CUSTOM); + } + } +} + +static void L4DL4D2_VotePassToTranslation(NativeVotesPassType passType, char[] translation, int maxlength) +{ + switch(passType) + { + case NativeVotesPass_Custom: + { + strcopy(translation, maxlength, L4D_VOTE_CUSTOM); + } + case NativeVotesPass_ChgCampaign: + { + strcopy(translation, maxlength, L4D_VOTE_CHANGECAMPAIGN_PASSED); + } + case NativeVotesPass_ChgDifficulty: + { + strcopy(translation, maxlength, L4D_VOTE_CHANGEDIFFICULTY_PASSED); + } + case NativeVotesPass_ReturnToLobby: + { + strcopy(translation, maxlength, L4D_VOTE_RETURNTOLOBBY_PASSED); + } + case NativeVotesPass_AlltalkOn, NativeVotesPass_AlltalkOff: + { + strcopy(translation, maxlength, L4D2_VOTE_ALLTALK_PASSED); + } + case NativeVotesPass_Restart: + { + strcopy(translation, maxlength, L4D_VOTE_RESTART_PASSED); + } + case NativeVotesPass_Kick: + { + strcopy(translation, maxlength, L4D_VOTE_KICK_PASSED); + } + case NativeVotesPass_ChgLevel: + { + strcopy(translation, maxlength, L4D_VOTE_CHANGELEVEL_PASSED); + } + default: + { + strcopy(translation, maxlength, L4D_VOTE_CUSTOM); + } + } +} + +static void L4DL4D2_ResetVote() +{ + if (CheckVoteController()) + { + SetEntProp(g_VoteController, Prop_Send, "m_activeIssueIndex", INVALID_ISSUE); + SetEntProp(g_VoteController, Prop_Send, "m_votesYes", 0); + SetEntProp(g_VoteController, Prop_Send, "m_votesNo", 0); + SetEntProp(g_VoteController, Prop_Send, "m_potentialVotes", 0); + SetEntProp(g_VoteController, Prop_Send, "m_onlyTeamToVote", NATIVEVOTES_ALL_TEAMS); + } +} + +static bool L4DL4D2_IsVoteInProgress() +{ + if (CheckVoteController()) + { + return (GetEntProp(g_VoteController, Prop_Send, "m_activeIssueIndex") > INVALID_ISSUE); + } + return false; +} + +//---------------------------------------------------------------------------- +// L4D functions + +/* +static L4D_ClientSelectedItem(Handle:vote, client, item) +{ + if (item > NATIVEVOTES_VOTE_INVALID && item <= Game_GetMaxItems()) + { + Handle castEvent; + + switch (item) + { + case NATIVEVOTES_VOTE_YES: + { + castEvent = CreateEvent("vote_cast_no"); + } + + case NATIVEVOTES_VOTE_NO: + { + castEvent = CreateEvent("vote_cast_yes"); + } + + default: + { + return; + } + } + + if (castEvent != INVALID_HANDLE) + { + SetEventInt(castEvent, "team", Data_GetTeam(vote)); + SetEventInt(castEvent, "entityid", client); + FireEvent(castEvent); + } + + } +} +*/ + +static void L4D_DisplayVote(NativeVote vote, int num_clients) +{ + char translation[TRANSLATION_LENGTH]; + + NativeVotesType voteType = Data_GetType(vote); + + L4DL4D2_VoteTypeToTranslation(voteType, translation, sizeof(translation)); + + char details[MAX_VOTE_DETAILS_LENGTH]; + Data_GetDetails(vote, details, MAX_VOTE_DETAILS_LENGTH); + + int team = Data_GetTeam(vote); + + if (CheckVoteController()) + { + // TODO: Need to look these values up + SetEntProp(g_VoteController, Prop_Send, "m_activeIssueIndex", 0); // For now, set to 0 to block in-game votes + SetEntProp(g_VoteController, Prop_Send, "m_onlyTeamToVote", team); + SetEntProp(g_VoteController, Prop_Send, "m_votesYes", 0); + SetEntProp(g_VoteController, Prop_Send, "m_votesNo", 0); + SetEntProp(g_VoteController, Prop_Send, "m_potentialVotes", num_clients); + } + + Event voteStart = CreateEvent("vote_started"); + voteStart.SetInt("team", team); + voteStart.SetInt("initiator", Data_GetInitiator(vote)); + voteStart.SetString("issue", translation); + voteStart.SetString("param1", details); + voteStart.Fire(); + +} + +static void L4D_VoteEnded() +{ + Event endEvent = CreateEvent("vote_ended"); + endEvent.Fire(); +} + +static void L4D_VotePass(const char[] translation, const char[] details, int team) +{ + L4D_VoteEnded(); + + Event passEvent = CreateEvent("vote_passed"); + passEvent.SetString("details", translation); + passEvent.SetString("param1", details); + passEvent.SetInt("team", team); + passEvent.Fire(); +} + +static void L4D_VoteFail(int team) +{ + L4D_VoteEnded(); + + Event failEvent = CreateEvent("vote_failed"); + failEvent.SetInt("team", team); + failEvent.Fire(); +} + +static bool L4D_CheckVoteType(NativeVotesType voteType) +{ + switch(voteType) + { + case NativeVotesType_Custom_YesNo, + NativeVotesType_ChgCampaign, + NativeVotesType_ChgDifficulty, + NativeVotesType_ReturnToLobby, + NativeVotesType_Restart, + NativeVotesType_Kick, + NativeVotesType_ChgLevel: + { + return true; + } + } + + return false; +} + +static bool L4D_CheckVotePassType(NativeVotesPassType passType) +{ + switch(passType) + { + case NativeVotesPass_Custom, + NativeVotesPass_ChgCampaign, + NativeVotesPass_ChgDifficulty, + NativeVotesPass_ReturnToLobby, + NativeVotesPass_Restart, + NativeVotesPass_Kick, + NativeVotesPass_ChgLevel: + { + return true; + } + } + + return false; +} + +//---------------------------------------------------------------------------- +// L4D2 functions + +static void L4D2_DisplayVote(NativeVote vote, int[] clients, int num_clients) +{ + char translation[TRANSLATION_LENGTH]; + + NativeVotesType voteType = Data_GetType(vote); + + L4DL4D2_VoteTypeToTranslation(voteType, translation, sizeof(translation)); + + char details[MAX_VOTE_DETAILS_LENGTH]; + + int team = Data_GetTeam(vote); + bool bCustom = false; + + switch (voteType) + { + case NativeVotesType_AlltalkOn: + { + strcopy(details, MAX_VOTE_DETAILS_LENGTH, L4D2_VOTE_ALLTALK_ENABLE); + } + case NativeVotesType_AlltalkOff: + { + strcopy(details, MAX_VOTE_DETAILS_LENGTH, L4D2_VOTE_ALLTALK_DISABLE); + } + case NativeVotesType_Custom_YesNo, NativeVotesType_Custom_Mult: + { + Data_GetTitle(vote, details, MAX_VOTE_DETAILS_LENGTH); + bCustom = true; + } + + default: + { + Data_GetDetails(vote, details, MAX_VOTE_DETAILS_LENGTH); + } + } + + int initiator = Data_GetInitiator(vote); + char initiatorName[MAX_NAME_LENGTH]; + + if (initiator != NATIVEVOTES_SERVER_INDEX && initiator > 0 && initiator <= MaxClients && IsClientInGame(initiator)) + { + GetClientName(initiator, initiatorName, MAX_NAME_LENGTH); + } + + for (int i = 0; i < num_clients; ++i) + { + g_newMenuTitle[0] = '\0'; + + MenuAction actions = Data_GetActions(vote); + + Action changeTitle = Plugin_Continue; + if (bCustom && actions & MenuAction_Display) + { + g_curDisplayClient = clients[i]; + changeTitle = DoAction(vote, MenuAction_Display, clients[i], 0); + } + + g_curDisplayClient = 0; + + BfWrite voteStart = UserMessageToBfWrite(StartMessageOne("VoteStart", clients[i], USERMSG_RELIABLE)); + voteStart.WriteByte(team); + voteStart.WriteByte(initiator); + voteStart.WriteString(translation); + if (changeTitle == Plugin_Changed) + { + voteStart.WriteString(g_newMenuTitle); + } + else + { + voteStart.WriteString(details); + } + voteStart.WriteString(initiatorName); + EndMessage(); + } + + if (CheckVoteController()) + { + SetEntProp(g_VoteController, Prop_Send, "m_onlyTeamToVote", team); + SetEntProp(g_VoteController, Prop_Send, "m_votesYes", 0); + SetEntProp(g_VoteController, Prop_Send, "m_votesNo", 0); + SetEntProp(g_VoteController, Prop_Send, "m_potentialVotes", num_clients); + SetEntProp(g_VoteController, Prop_Send, "m_activeIssueIndex", 0); // Set to 0 to block ingame votes + } +} + +static void L4D2_VotePass(const char[] translation, const char[] details, int team, int client=0) +{ + BfWrite votePass; + if (!client) + { + votePass = UserMessageToBfWrite(StartMessageAll("VotePass", USERMSG_RELIABLE)); + } + else + { + votePass = UserMessageToBfWrite(StartMessageOne("VotePass", client, USERMSG_RELIABLE)); + } + + votePass.WriteByte(team); + votePass.WriteString(translation); + votePass.WriteString(details); + EndMessage(); +} + +static void L4D2_VoteFail(int[] clients, int numClients, int team) +{ + BfWrite voteFailed = UserMessageToBfWrite(StartMessage("VoteFail", clients, numClients, USERMSG_RELIABLE)); + + voteFailed.WriteByte(team); + EndMessage(); +} + +static bool L4D2_CheckVoteType(NativeVotesType voteType) +{ + switch(voteType) + { + case NativeVotesType_Custom_YesNo, + NativeVotesType_ChgCampaign, + NativeVotesType_ChgDifficulty, + NativeVotesType_ReturnToLobby, + NativeVotesType_AlltalkOn, + NativeVotesType_AlltalkOff, + NativeVotesType_Restart, + NativeVotesType_Kick, + NativeVotesType_ChgLevel: + { + return true; + } + } + + return false; +} + +static bool L4D2_CheckVotePassType(NativeVotesPassType passType) +{ + switch(passType) + { + case NativeVotesPass_Custom, + NativeVotesPass_ChgCampaign, + NativeVotesPass_ChgDifficulty, + NativeVotesPass_ReturnToLobby, + NativeVotesPass_Restart, + NativeVotesPass_Kick, + NativeVotesPass_ChgLevel: + { + return true; + } + } + + return false; +} + +//---------------------------------------------------------------------------- +// TF2/CSGO shared functions + +// TF2 and CSGO functions are still together in case Valve moves TF2 to protobufs. + +// NATIVEVOTES_VOTE_INVALID means parse failed +static int CSGO_ParseVote(const char[] option) +{ + // option1 <-- 7 characters exactly + if (strlen(option) != 7) + { + return NATIVEVOTES_VOTE_INVALID; + } + + return StringToInt(option[6]) - 1; +} + +// NATIVEVOTES_VOTE_INVALID means parse failed +static int TF2_ParseVote(const char[] option) +{ + // the update on 2022-06-22 changed the params passed to the `vote` command + // previously it was a single string "optionN", where N was the option to be selected + // now it's two arguments "X optionN", where X is the vote index being acted on + + if (strlen(option) == 0 || GetCmdArgs() != 2) + { + return NATIVEVOTES_VOTE_INVALID; + } + + // int voteidx = GetCmdArgInt(1); + + char voteOption[16]; + GetCmdArg(2, voteOption, sizeof(voteOption)); + + // option1 <-- 7 characters exactly + // voteOption's last character should be numeric + if (strlen(voteOption) != 7 || !IsCharNumeric(voteOption[6])) + { + return NATIVEVOTES_VOTE_INVALID; + } + + return StringToInt(voteOption[6]) - 1; +} + +static void CSGO_ClientSelectedItem(NativeVote vote, int client, int item) +{ + Event castEvent = CreateEvent("vote_cast"); + + castEvent.SetInt("team", Data_GetTeam(vote)); + castEvent.SetInt("entityid", client); + castEvent.SetInt("vote_option", item); + castEvent.Fire(); +} + +static void TF2_ClientSelectedItem(NativeVote vote, int client, int item) +{ + Event castEvent = CreateEvent("vote_cast"); + + castEvent.SetInt("team", Data_GetTeam(vote)); + castEvent.SetInt("voteidx", s_nNativeVoteIdx); + castEvent.SetInt("entityid", client); + castEvent.SetInt("vote_option", item); + castEvent.Fire(); +} + +static void TF2CSGO_UpdateVoteCounts(ArrayList votes) +{ + if (CheckVoteController()) + { + int size = votes.Length; + Event changeEvent = CreateEvent("vote_changed"); + for (int i = 0; i < size; i++) + { + SetEntProp(g_VoteController, Prop_Send, "m_nVoteOptionCount", votes.Get(i), 4, i); + + char szVoteOption[13]; + Format(szVoteOption, sizeof(szVoteOption), "vote_option%d", i+1); + changeEvent.SetInt(szVoteOption, votes.Get(i)); + } + changeEvent.SetInt("voteidx", s_nNativeVoteIdx); + changeEvent.SetInt("potentialVotes", GetEntProp(g_VoteController, Prop_Send, "m_nPotentialVotes")); + changeEvent.Fire(); + } +} + +static stock void TF2CSGO_UpdateClientCount(int num_clients) +{ + if (CheckVoteController()) + { + SetEntProp(g_VoteController, Prop_Send, "m_nPotentialVotes", num_clients); + } +} + +static void TF2CSGO_DisplayVote(NativeVote vote, int[] clients, int num_clients) +{ + NativeVotesType voteType = Data_GetType(vote); + + // Added for novote support + bool bNoVoteButton = (Data_GetFlags(vote) & MENUFLAG_BUTTON_NOVOTE) == MENUFLAG_BUTTON_NOVOTE; + + if (bNoVoteButton) + { + int max = Game_GetMaxItems(); + if (Data_GetItemCount(vote) == max) + { + // item must be removed before No Vote is added to prevent it from being blocked + Data_RemoveItem(vote, max-1); + } + + char display[TRANSLATION_LENGTH]; + Format(display, sizeof(display), "%T", "No Vote", LANG_SERVER); + + Data_InsertItem(vote, 0, "No Vote", display); + } + + char translation[TRANSLATION_LENGTH]; + char otherTeamString[TRANSLATION_LENGTH]; + bool bYesNo = true; + bool bCustom = false; + + char details[MAX_VOTE_DETAILS_LENGTH]; + + // voteIndex is used by the CVoteController, which we're not using. + // -1 means no vote in progress, so any other value should work. + //int voteIndex = TF2CSGO_GetVoteType(voteType); + int voteIndex = 0; + + switch (voteType) + { + case NativeVotesType_Custom_YesNo, NativeVotesType_Custom_Mult: + { + Data_GetTitle(vote, details, MAX_VOTE_DETAILS_LENGTH); + bCustom = true; + } + default: + { + Data_GetDetails(vote, details, MAX_VOTE_DETAILS_LENGTH); + } + } + + switch(g_EngineVersion) + { + case Engine_CSGO: + { + bYesNo = CSGO_VoteTypeToTranslation(voteType, translation, sizeof(translation)); + CSGO_VoteTypeToVoteOtherTeamString(voteType, otherTeamString, sizeof(otherTeamString)); + } + case Engine_TF2: + { + bYesNo = TF2_VoteTypeToTranslation(voteType, translation, sizeof(translation)); + } + } + + int team = Data_GetTeam(vote); + + // Moved to mimic SourceSDK2013's server/vote_controller.cpp + if (CheckVoteController()) + { + SetEntProp(g_VoteController, Prop_Send, "m_bIsYesNoVote", bYesNo); + + // CSGO gets very cranky if you try setting this + if (g_EngineVersion == Engine_TF2) + { + SetEntProp(g_VoteController, Prop_Send, "m_iActiveIssueIndex", voteIndex); + } + + SetEntProp(g_VoteController, Prop_Send, "m_iOnlyTeamToVote", team); + + for (int i = 0; i < TF2CSGO_COUNT; i++) + { + SetEntProp(g_VoteController, Prop_Send, "m_nVoteOptionCount", 0, _, i); + } + + // TODO(UPDATE): M-M-M-MULTIVOTE + // we need unique vote indices; HUD elements for previous votes aren't cleaned up (?) + // the game implements this as `this->m_nVoteIdx = s_nVoteIdx++` + s_nNativeVoteIdx = GetEntProp(g_VoteController, Prop_Send, "m_nVoteIdx"); + + #if defined LOG + PrintToServer("Starting vote index: %d (controller: %d)", s_nNativeVoteIdx, GetEntProp(g_VoteController, Prop_Send, "m_nVoteIdx")); + #endif + SetEntProp(g_VoteController, Prop_Send, "m_nVoteIdx", s_nNativeVoteIdx + 1); // TODO(UPDATE) + } + + // According to Source SDK 2013, vote_options is only sent for a multiple choice vote. + // As of 2015-09-28, vote_options is sent for all votes in TF2 despite Yes/No being + // translated in the UI itself. + // As of 2016-07-10, we send this only when we're doing Yes/No votes as we handle + // multiple choice votes separately later. + if (bYesNo) + { + int itemCount = Data_GetItemCount(vote); + + Event optionsEvent = CreateEvent("vote_options"); + + for (int i = 0; i < itemCount; i++) + { + char option[8]; + Format(option, sizeof(option), "%s%d", TF2CSGO_VOTE_PREFIX, i+1); + char display[TRANSLATION_LENGTH]; + Data_GetItemDisplay(vote, i, display, sizeof(display)); + + optionsEvent.SetString(option, display); + } + optionsEvent.SetInt("count", itemCount); + optionsEvent.SetInt("voteidx", s_nNativeVoteIdx); + optionsEvent.Fire(); + } + + // Moved to mimic SourceSDK2013's server/vote_controller.cpp + // For whatever reason, while the other props are set first, this one's set after the vote_options event + if (CheckVoteController()) + { + SetEntProp(g_VoteController, Prop_Send, "m_nPotentialVotes", num_clients); + } + + int holder = vote.Initiator; + // required to allow the initiator to vote on their own issue + // https://github.com/ValveSoftware/Source-1-Games/issues/3934 + if (sv_vote_holder_may_vote_no && holder <= MaxClients && IsClientConnected(holder) && !IsFakeClient(holder)) + { + sv_vote_holder_may_vote_no.ReplicateToClient(holder, "1"); + } + + MenuAction actions = Data_GetActions(vote); + + for (int i = 0; i < num_clients; ++i) + { + g_newMenuTitle[0] = '\0'; + + Action changeTitle = Plugin_Continue; + if (bCustom && actions & MenuAction_Display) + { + g_curDisplayClient = clients[i]; + changeTitle = DoAction(vote, MenuAction_Display, clients[i], 0); + } + + g_curDisplayClient = 0; + + if (!bYesNo) + { + TF2CSGO_SendOptionsToClient(vote, clients[i]); + } + + Handle voteStart = StartMessageOne("VoteStart", clients[i], USERMSG_RELIABLE); + + if (g_bUserBuf) + { + Protobuf protoStart = UserMessageToProtobuf(voteStart); + protoStart.SetInt("team", team); + protoStart.SetInt("ent_idx", Data_GetInitiator(vote)); + protoStart.SetString("disp_str", translation); + if (bCustom && changeTitle == Plugin_Changed) + { + protoStart.SetString("details_str", g_newMenuTitle); + } + else + { + protoStart.SetString("details_str", details); + } + protoStart.SetBool("is_yes_no_vote", bYesNo); + protoStart.SetString("other_team_str", otherTeamString); + protoStart.SetInt("vote_type", voteIndex); + } + else + { + BfWrite bfStart = UserMessageToBfWrite(voteStart); + bfStart.WriteByte(team); + bfStart.WriteNum(s_nNativeVoteIdx); + bfStart.WriteByte(Data_GetInitiator(vote)); + bfStart.WriteString(translation); + if (bCustom && changeTitle == Plugin_Changed) + { + bfStart.WriteString(g_newMenuTitle); + } + else + { + bfStart.WriteString(details); + } + bfStart.WriteByte(bYesNo); + if (!IsFakeClient(clients[i])) + { + bfStart.WriteByte(Data_GetTarget(vote) - 1); + } + } + + EndMessage(); + } + + g_curDisplayClient = 0; +} + +static void TF2CSGO_SendOptionsToClient(NativeVote vote, int client) +{ + Event optionsEvent = CreateEvent("vote_options"); + + MenuAction actions = Data_GetActions(vote); + bool bNoVoteButton = (Data_GetFlags(vote) & MENUFLAG_BUTTON_NOVOTE) == MENUFLAG_BUTTON_NOVOTE; + + int start = 0; + + if (bNoVoteButton) + { + start = 1; + char option[8]; + Format(option, sizeof(option), "%s1", TF2CSGO_VOTE_PREFIX); + + char display[TRANSLATION_LENGTH]; + Format(display, sizeof(display), "%T", "No Vote", client); + optionsEvent.SetString(option, display); + } + + int itemCount = Data_GetItemCount(vote); + + for (int i = start; i < itemCount; i++) + { + Action changeItem = Plugin_Continue; + + if (actions & MenuAction_DisplayItem) + { + g_curItemClient = client; + g_newMenuItem[0] = '\0'; + + changeItem = DoAction(vote, MenuAction_DisplayItem, client, i); + g_curItemClient = 0; + } + + char option[8]; + Format(option, sizeof(option), "%s%d", TF2CSGO_VOTE_PREFIX, i+1); + char display[TRANSLATION_LENGTH]; + + if (changeItem == Plugin_Changed) + { + strcopy(display, TRANSLATION_LENGTH, g_newMenuItem); + } + else + { + Data_GetItemDisplay(vote, i, display, sizeof(display)); + } + optionsEvent.SetString(option, display); + } + optionsEvent.SetInt("count", itemCount); + optionsEvent.SetInt("voteidx", s_nNativeVoteIdx); + optionsEvent.FireToClient(client); + // FireToClient does not close the handle, so we call Cancel() to do that for us. + optionsEvent.Cancel(); +} + +static void CSGO_VotePass(const char[] translation, const char[] details, int team, int client=0) +{ + Protobuf votePass = null; + + if (client <= 0) + { + votePass = UserMessageToProtobuf(StartMessageAll("VotePass", USERMSG_RELIABLE)); + } + else + { + votePass = UserMessageToProtobuf(StartMessageOne("VotePass", client, USERMSG_RELIABLE)); + } + + votePass.SetInt("team", team); + votePass.SetString("disp_str", translation); + votePass.SetString("details_str", details); + votePass.SetInt("vote_type", 0); // Unknown, need to check values + + EndMessage(); +} + +static void TF2_VotePass(const char[] translation, const char[] details, int team, int client=0) +{ + BfWrite votePass = null; + + if (client <= 0) + { + votePass = UserMessageToBfWrite(StartMessageAll("VotePass", USERMSG_RELIABLE)); + } + else + { + votePass = UserMessageToBfWrite(StartMessageOne("VotePass", client, USERMSG_RELIABLE)); + } + + votePass.WriteByte(team); + votePass.WriteNum(s_nNativeVoteIdx); + votePass.WriteString(translation); + votePass.WriteString(details); + + EndMessage(); +} + +static void CSGO_VoteFail(int[] clients, int numClients, int reason, int team) +{ + Protobuf voteFailed = UserMessageToProtobuf(StartMessage("VoteFailed", clients, numClients, USERMSG_RELIABLE)); + + voteFailed.SetInt("team", team); + voteFailed.SetInt("reason", reason); + + EndMessage(); +} + +static void TF2_VoteFail(int[] clients, int numClients, int reason, int team) +{ + BfWrite voteFailed = UserMessageToBfWrite(StartMessage("VoteFailed", clients, numClients, USERMSG_RELIABLE)); + + voteFailed.WriteByte(team); + voteFailed.WriteNum(s_nNativeVoteIdx); + voteFailed.WriteByte(reason); + + EndMessage(); +} + +static void CSGO_CallVoteFail(int client, int reason, int time) +{ + Protobuf callVoteFail = UserMessageToProtobuf(StartMessageOne("CallVoteFailed", client, USERMSG_RELIABLE)); + + callVoteFail.SetInt("reason", reason); + callVoteFail.SetInt("time", time); + + EndMessage(); +} + +static void TF2_CallVoteFail(int client, int reason, int time) +{ + BfWrite callVoteFail = UserMessageToBfWrite(StartMessageOne("CallVoteFailed", client, USERMSG_RELIABLE)); + + callVoteFail.WriteByte(reason); + callVoteFail.WriteShort(time); + + EndMessage(); +} + +stock static void CSGO_DisplayVoteSetup(int client, ArrayList hVoteTypes) +{ + int count = hVoteTypes.Length; + + Protobuf voteSetup = UserMessageToProtobuf(StartMessageOne("VoteSetup", client, USERMSG_RELIABLE)); + + for (int i = 0; i < count; ++i) + { + char voteIssue[128]; + + CallVoteListData voteData; + hVoteTypes.GetArray(i, voteData.CallVoteList_VoteType); + + Game_OverrideTypeToVoteString(voteData.CallVoteList_VoteType, voteIssue, sizeof(voteIssue)); + + voteSetup.AddString("potential_issues", voteIssue); + } + + EndMessage(); +} + +static void TF2_DisplayVoteSetup(int client, ArrayList hVoteTypes) +{ + int count = hVoteTypes.Length; + + BfWrite voteSetup = UserMessageToBfWrite(StartMessageOne("VoteSetup", client, USERMSG_RELIABLE)); + + voteSetup.WriteByte(count); + + for (int i = 0; i < count; ++i) + { + char voteIssue[128]; + + CallVoteListData voteData; + hVoteTypes.GetArray(i, voteData, sizeof(CallVoteListData)); + + Game_OverrideTypeToVoteString(voteData.CallVoteList_VoteType, voteIssue, sizeof(voteIssue)); + + char translation[128]; + Game_OverrideTypeToTranslationString(voteData.CallVoteList_VoteType, translation, sizeof(translation)); + + voteSetup.WriteString(voteIssue); + voteSetup.WriteString(translation); + voteSetup.WriteByte(voteData.CallVoteList_VoteEnabled); + } + + EndMessage(); +} + +static void TF2CSGO_ResetVote() +{ + if (CheckVoteController()) + { + if (g_EngineVersion == Engine_TF2) + { + SetEntProp(g_VoteController, Prop_Send, "m_nVoteIdx", -1); + SetEntProp(g_VoteController, Prop_Send, "m_iActiveIssueIndex", INVALID_ISSUE); + } + SetEntProp(g_VoteController, Prop_Send, "m_iOnlyTeamToVote", g_EngineVersion == Engine_TF2 ? NATIVEVOTES_TF2_ALL_TEAMS : NATIVEVOTES_ALL_TEAMS); + for (int i = 0; i < 5; i++) + { + SetEntProp(g_VoteController, Prop_Send, "m_nVoteOptionCount", 0, _, i); + } + SetEntProp(g_VoteController, Prop_Send, "m_nPotentialVotes", 0); + SetEntProp(g_VoteController, Prop_Send, "m_bIsYesNoVote", true); + } +} + +static bool TF2CSGO_IsVoteInProgress() +{ + if (CheckVoteController()) + { + return (GetEntProp(g_VoteController, Prop_Send, "m_iActiveIssueIndex") != INVALID_ISSUE); + } + return false; +} + + +//---------------------------------------------------------------------------- +// TF2 functions + +static bool TF2_CheckVoteType(NativeVotesType voteType) +{ + switch(voteType) + { + case NativeVotesType_Custom_YesNo, NativeVotesType_Restart, + NativeVotesType_Kick, NativeVotesType_KickIdle, NativeVotesType_KickScamming, NativeVotesType_KickCheating, + NativeVotesType_ChgLevel, NativeVotesType_NextLevel, NativeVotesType_ScrambleNow, NativeVotesType_ScrambleEnd, + NativeVotesType_ChgMission, NativeVotesType_StartRound, NativeVotesType_Eternaween, + NativeVotesType_AutoBalanceOn, NativeVotesType_AutoBalanceOff, + NativeVotesType_ClassLimitsOn, NativeVotesType_ClassLimitsOff, NativeVotesType_Extend: + { + return true; + } + case NativeVotesType_Custom_Mult, NativeVotesType_NextLevelMult: + { + return true; + } + } + + return false; +} + +static bool TF2_CheckVotePassType(NativeVotesPassType passType) +{ + switch(passType) + { + case NativeVotesPass_Custom, + NativeVotesPass_Restart, + NativeVotesPass_ChgLevel, + NativeVotesPass_Kick, + NativeVotesPass_NextLevel, + NativeVotesPass_Extend, + NativeVotesPass_Scramble, + NativeVotesPass_ChgMission, + NativeVotesPass_StartRound, + NativeVotesPass_Eternaween, + NativeVotesPass_AutoBalanceOn, + NativeVotesPass_AutoBalanceOff, + NativeVotesPass_ClassLimitsOn, + NativeVotesPass_ClassLimitsOff: + { + return true; + } + } + + return false; +} + +static bool TF2_VoteTypeToTranslation(NativeVotesType voteType, char[] translation, int maxlength) +{ + bool bYesNo = true; + switch(voteType) + { + case NativeVotesType_Custom_Mult: + { + strcopy(translation, maxlength, TF2_VOTE_CUSTOM); + bYesNo = false; + } + case NativeVotesType_Restart: + { + strcopy(translation, maxlength, TF2_VOTE_RESTART_START); + } + case NativeVotesType_Kick: + { + strcopy(translation, maxlength, TF2_VOTE_KICK_START); + } + case NativeVotesType_KickIdle: + { + strcopy(translation, maxlength, TF2_VOTE_KICK_IDLE_START); + } + case NativeVotesType_KickScamming: + { + strcopy(translation, maxlength, TF2_VOTE_KICK_SCAMMING_START); + } + case NativeVotesType_KickCheating: + { + strcopy(translation, maxlength, TF2_VOTE_KICK_CHEATING_START); + } + case NativeVotesType_ChgLevel: + { + strcopy(translation, maxlength, TF2_VOTE_CHANGELEVEL_START); + } + case NativeVotesType_NextLevel: + { + strcopy(translation, maxlength, TF2_VOTE_NEXTLEVEL_SINGLE_START); + } + case NativeVotesType_NextLevelMult: + { + strcopy(translation, maxlength, TF2_VOTE_NEXTLEVEL_MULTIPLE_START); + bYesNo = false; + } + case NativeVotesType_ScrambleNow: + { + strcopy(translation, maxlength, TF2_VOTE_SCRAMBLE_IMMEDIATE_START); + } + case NativeVotesType_ScrambleEnd: + { + strcopy(translation, maxlength, TF2_VOTE_SCRAMBLE_ROUNDEND_START); + } + case NativeVotesType_ChgMission: + { + strcopy(translation, maxlength, TF2_VOTE_CHANGEMISSION_START); + } + case NativeVotesType_StartRound: + { + strcopy(translation, maxlength, TF2_VOTE_ROUND_START); + } + case NativeVotesType_Eternaween: + { + strcopy(translation, maxlength, TF2_VOTE_ETERNAWEEN_START); + } + case NativeVotesType_AutoBalanceOn: + { + strcopy(translation, maxlength, TF2_VOTE_AUTOBALANCE_ENABLE_START); + } + case NativeVotesType_AutoBalanceOff: + { + strcopy(translation, maxlength, TF2_VOTE_AUTOBALANCE_DISABLE_START); + } + case NativeVotesType_ClassLimitsOn: + { + strcopy(translation, maxlength, TF2_VOTE_CLASSLIMITS_ENABLE_START); + } + case NativeVotesType_ClassLimitsOff: + { + strcopy(translation, maxlength, TF2_VOTE_CLASSLIMITS_DISABLE_START); + } + case NativeVotesType_Extend: + { + strcopy(translation, maxlength, TF2_VOTE_EXTEND_START); + } + default: + { + strcopy(translation, maxlength, TF2_VOTE_CUSTOM); + } + } + + return bYesNo; +} + +static void TF2_VotePassToTranslation(NativeVotesPassType passType, char[] translation, int maxlength) +{ + switch(passType) + { + case NativeVotesPass_Restart: + { + strcopy(translation, maxlength, TF2_VOTE_RESTART_PASSED); + } + case NativeVotesPass_Kick: + { + strcopy(translation, maxlength, TF2_VOTE_KICK_PASSED); + } + case NativeVotesPass_ChgLevel: + { + strcopy(translation, maxlength, TF2_VOTE_CHANGELEVEL_PASSED); + } + case NativeVotesPass_NextLevel: + { + strcopy(translation, maxlength, TF2_VOTE_NEXTLEVEL_PASSED); + } + case NativeVotesPass_Extend: + { + strcopy(translation, maxlength, TF2_VOTE_NEXTLEVEL_EXTEND_PASSED); + } + case NativeVotesPass_Scramble: + { + strcopy(translation, maxlength, TF2_VOTE_SCRAMBLE_PASSED); + } + case NativeVotesPass_ChgMission: + { + strcopy(translation, maxlength, TF2_VOTE_CHANGEMISSION_PASSED); + } + case NativeVotesPass_StartRound: + { + strcopy(translation, maxlength, TF2_VOTE_ROUND_PASSED); + } + case NativeVotesPass_Eternaween: + { + strcopy(translation, maxlength, TF2_VOTE_ETERNAWEEN_PASSED); + } + case NativeVotesPass_AutoBalanceOn: + { + strcopy(translation, maxlength, TF2_VOTE_AUTOBALANCE_ENABLE_PASSED); + } + case NativeVotesPass_AutoBalanceOff: + { + strcopy(translation, maxlength, TF2_VOTE_AUTOBALANCE_DISABLE_PASSED); + } + case NativeVotesPass_ClassLimitsOn: + { + strcopy(translation, maxlength, TF2_VOTE_CLASSLIMITS_ENABLE_PASSED); + } + case NativeVotesPass_ClassLimitsOff: + { + strcopy(translation, maxlength, TF2_VOTE_CLASSLIMITS_DISABLE_PASSED); + } + default: + { + strcopy(translation, maxlength, TF2_VOTE_CUSTOM); + } + } +} + +static void TF2_AddDefaultVotes(ArrayList hVoteTypes, bool bHideDisabledVotes) +{ + bool globalEnable = g_Cvar_Votes_Enabled.BoolValue; + if (!globalEnable && bHideDisabledVotes) + { + return; + } + + if (FindSendPropInfo("CTFGameRulesProxy", "m_bPlayingMannVsMachine") > 0 && GameRules_GetProp("m_bPlayingMannVsMachine")) + { + // Default MvM vote types + + // Kick + VoteTypeSet(hVoteTypes, bHideDisabledVotes, NativeVotesOverride_Kick, globalEnable && g_Cvar_MvM_VoteKick_Enabled.BoolValue); + + // Restart + VoteTypeSet(hVoteTypes, bHideDisabledVotes, NativeVotesOverride_Restart, globalEnable && g_Cvar_MvM_VoteRestart_Enabled.BoolValue); + + // ChgLevel + VoteTypeSet(hVoteTypes, bHideDisabledVotes, NativeVotesOverride_ChgLevel, globalEnable && g_Cvar_MvM_VoteChangeLevel_Enabled.BoolValue); + + // ChgMission + VoteTypeSet(hVoteTypes, bHideDisabledVotes, NativeVotesOverride_ChgMission, globalEnable && g_Cvar_MvM_VoteChallenge_Enabled.BoolValue); + + // ClassLimits + VoteTypeSet(hVoteTypes, bHideDisabledVotes, NativeVotesOverride_ClassLimits, globalEnable && g_Cvar_MvM_VoteClassLimits_Enabled.BoolValue); + } + else + { + // Default vote types + + // Kick + VoteTypeSet(hVoteTypes, bHideDisabledVotes, NativeVotesOverride_Kick, globalEnable && g_Cvar_VoteKick_Enabled.BoolValue); + + // Restart + VoteTypeSet(hVoteTypes, bHideDisabledVotes, NativeVotesOverride_Restart, globalEnable && g_Cvar_VoteRestart_Enabled.BoolValue); + + // ChgLevel + VoteTypeSet(hVoteTypes, bHideDisabledVotes, NativeVotesOverride_ChgLevel, globalEnable && g_Cvar_VoteChangeLevel_Enabled.BoolValue); + + // NextLevel + VoteTypeSet(hVoteTypes, bHideDisabledVotes, NativeVotesOverride_NextLevel, globalEnable && g_Cvar_VoteNextLevel_Enabled.BoolValue); + + // Scramble + VoteTypeSet(hVoteTypes, bHideDisabledVotes, NativeVotesOverride_Scramble, globalEnable && g_Cvar_VoteScramble_Enabled.BoolValue); + + // ClassLimits + VoteTypeSet(hVoteTypes, bHideDisabledVotes, NativeVotesOverride_ClassLimits, globalEnable && g_Cvar_VoteClassLimits_Enabled.BoolValue); + + // AutoBalance + VoteTypeSet(hVoteTypes, bHideDisabledVotes, NativeVotesOverride_AutoBalance, globalEnable && g_Cvar_VoteAutoBalance_Enabled.BoolValue); + + // Extend + VoteTypeSet(hVoteTypes, bHideDisabledVotes, NativeVotesOverride_Extend, globalEnable && g_Cvar_VoteExtend_Enabled.BoolValue); + } + +} + +static void VoteTypeSet(ArrayList hVoteTypes, bool bHideDisabledVotes, NativeVotesOverride voteType, bool bEnabled) +{ + CallVoteListData voteList; + + if (bEnabled || !bHideDisabledVotes) + { + voteList.CallVoteList_VoteType = voteType; + voteList.CallVoteList_VoteEnabled = bEnabled; + + hVoteTypes.PushArray(voteList); + } +} + +//---------------------------------------------------------------------------- +// CSGO functions + +static bool CSGO_CheckVoteType(NativeVotesType voteType) +{ + switch(voteType) + { + case NativeVotesType_Custom_YesNo, + NativeVotesType_Restart, + NativeVotesType_Kick, + NativeVotesType_KickIdle, + NativeVotesType_KickScamming, + NativeVotesType_KickCheating, + NativeVotesType_ChgLevel, + NativeVotesType_NextLevel, + NativeVotesType_ScrambleNow, + NativeVotesType_SwapTeams, + NativeVotesType_Surrender, + NativeVotesType_Rematch, + NativeVotesType_Continue: + { + return true; + } + case NativeVotesType_Custom_Mult, NativeVotesType_NextLevelMult: + { + // Until/unless Valve fixes their menu code, this is false. + return false; + } + + } + + return false; +} + +static bool CSGO_CheckVotePassType(NativeVotesPassType passType) +{ + switch(passType) + { + case NativeVotesPass_Custom, NativeVotesPass_Restart, NativeVotesPass_Kick, + NativeVotesPass_ChgLevel, NativeVotesPass_NextLevel, NativeVotesPass_Extend, + NativeVotesPass_Scramble, NativeVotesPass_SwapTeams, NativeVotesPass_Surrender, + NativeVotesPass_Rematch, NativeVotesPass_Continue: + { + return true; + } + } + + return false; +} + +static bool CSGO_VoteTypeToTranslation(NativeVotesType voteType, char[] translation, int maxlength) +{ + bool bYesNo = true; + switch(voteType) + { + case NativeVotesType_Custom_Mult: + { + strcopy(translation, maxlength, CSGO_VOTE_CUSTOM); + bYesNo = false; + } + case NativeVotesType_Restart: + { + strcopy(translation, maxlength, CSGO_VOTE_RESTART_START); + } + case NativeVotesType_Kick: + { + strcopy(translation, maxlength, CSGO_VOTE_KICK_START); + } + case NativeVotesType_KickIdle: + { + strcopy(translation, maxlength, CSGO_VOTE_KICK_IDLE_START); + } + case NativeVotesType_KickScamming: + { + strcopy(translation, maxlength, CSGO_VOTE_KICK_SCAMMING_START); + } + case NativeVotesType_KickCheating: + { + strcopy(translation, maxlength, CSGO_VOTE_KICK_CHEATING_START); + } + case NativeVotesType_ChgLevel: + { + strcopy(translation, maxlength, CSGO_VOTE_CHANGELEVEL_START); + } + case NativeVotesType_NextLevel: + { + strcopy(translation, maxlength, CSGO_VOTE_NEXTLEVEL_SINGLE_START); + } + case NativeVotesType_NextLevelMult: + { + strcopy(translation, maxlength, CSGO_VOTE_NEXTLEVEL_MULTIPLE_START); + bYesNo = false; + } + case NativeVotesType_ScrambleNow: + { + strcopy(translation, maxlength, CSGO_VOTE_SCRAMBLE_START); + } + case NativeVotesType_SwapTeams: + { + strcopy(translation, maxlength, CSGO_VOTE_SWAPTEAMS_START); + } + case NativeVotesType_Surrender: + { + strcopy(translation, maxlength, CSGO_VOTE_SURRENDER_START); + } + case NativeVotesType_Rematch: + { + strcopy(translation, maxlength, CSGO_VOTE_REMATCH_START); + } + case NativeVotesType_Continue: + { + strcopy(translation, maxlength, CSGO_VOTE_CONTINUE_START); + } + default: + { + strcopy(translation, maxlength, CSGO_VOTE_CUSTOM); + } + } + + return bYesNo; +} + +static void CSGO_VotePassToTranslation(NativeVotesPassType passType, char[] translation, int maxlength) +{ + switch(passType) + { + case NativeVotesPass_Restart: + { + strcopy(translation, maxlength, CSGO_VOTE_RESTART_PASSED); + } + case NativeVotesPass_Kick: + { + strcopy(translation, maxlength, CSGO_VOTE_KICK_PASSED); + } + case NativeVotesPass_ChgLevel: + { + strcopy(translation, maxlength, CSGO_VOTE_CHANGELEVEL_PASSED); + } + case NativeVotesPass_NextLevel: + { + strcopy(translation, maxlength, CSGO_VOTE_NEXTLEVEL_PASSED); + } + case NativeVotesPass_Extend: + { + strcopy(translation, maxlength, CSGO_VOTE_NEXTLEVEL_EXTEND_PASSED); + } + case NativeVotesPass_Scramble: + { + strcopy(translation, maxlength, CSGO_VOTE_SCRAMBLE_PASSED); + } + case NativeVotesPass_SwapTeams: + { + strcopy(translation, maxlength, CSGO_VOTE_SWAPTEAMS_PASSED); + } + case NativeVotesPass_Surrender: + { + strcopy(translation, maxlength, CSGO_VOTE_SURRENDER_PASSED); + } + case NativeVotesPass_Rematch: + { + strcopy(translation, maxlength, CSGO_VOTE_REMATCH_PASSED); + } + case NativeVotesPass_Continue: + { + strcopy(translation, maxlength, CSGO_VOTE_CONTINUE_PASSED); + } + default: + { + strcopy(translation, maxlength, CSGO_VOTE_CUSTOM); + } + } +} + +static void CSGO_VoteTypeToVoteOtherTeamString(NativeVotesType voteType, char[] otherTeamString, int maxlength) +{ + switch(voteType) + { + case NativeVotesType_Kick: + { + strcopy(otherTeamString, maxlength, CSGO_VOTE_KICK_OTHERTEAM); + } + case NativeVotesType_Surrender: + { + strcopy(otherTeamString, maxlength, CSGO_VOTE_SURRENDER_OTHERTEAM); + } + case NativeVotesType_Continue: + { + strcopy(otherTeamString, maxlength, CSGO_VOTE_CONTINUE_OTHERTEAM); + } + default: + { + strcopy(otherTeamString, maxlength, CSGO_VOTE_UNIMPLEMENTED_OTHERTEAM); + } + } + +} + +static stock int TF2CSGO_GetVoteType(NativeVotesType voteType) +{ + int valveVoteType = ValveVote_Restart; + + switch (voteType) + { + case NativeVotesType_Custom_YesNo, NativeVotesType_Restart: + { + valveVoteType = ValveVote_Restart; + } + case NativeVotesType_Custom_Mult, NativeVotesType_NextLevel, NativeVotesType_NextLevelMult: + { + valveVoteType = ValveVote_NextLevel; + } + case NativeVotesType_Kick, NativeVotesType_KickIdle, NativeVotesType_KickScamming, NativeVotesType_KickCheating: + { + valveVoteType = ValveVote_Kick; + } + case NativeVotesType_ChgLevel: + { + valveVoteType = ValveVote_ChangeLevel; + } + case NativeVotesType_ScrambleNow, NativeVotesType_ScrambleEnd: + { + valveVoteType = ValveVote_Scramble; + } + case NativeVotesType_SwapTeams: + { + valveVoteType = ValveVote_SwapTeams; + } + } + + return valveVoteType; +} + +// The stocks below are used by the vote override system +// Not all are used by the plugin +static stock bool TF2_VoteTypeToVoteString(NativeVotesType voteType, char[] voteString, int maxlength) +{ + bool valid = false; + switch(voteType) + { + case NativeVotesType_Kick, NativeVotesType_KickCheating, NativeVotesType_KickIdle, NativeVotesType_KickScamming: + { + strcopy(voteString, maxlength, TF2CSGO_VOTE_STRING_KICK); + valid = true; + } + case NativeVotesType_ChgLevel: + { + strcopy(voteString, maxlength, TF2CSGO_VOTE_STRING_CHANGELEVEL); + valid = true; + } + case NativeVotesType_NextLevel: + { + strcopy(voteString, maxlength, TF2CSGO_VOTE_STRING_NEXTLEVEL); + valid = true; + } + case NativeVotesType_Restart: + { + strcopy(voteString, maxlength, TF2CSGO_VOTE_STRING_RESTART); + valid = true; + } + case NativeVotesType_ScrambleEnd, NativeVotesType_ScrambleNow: + { + strcopy(voteString, maxlength, TF2CSGO_VOTE_STRING_SCRAMBLE); + valid = true; + } + case NativeVotesType_Eternaween: + { + strcopy(voteString, maxlength, TF2_VOTE_STRING_ETERNAWEEN); + valid = true; + } + case NativeVotesType_AutoBalanceOn: + { + strcopy(voteString, maxlength, TF2_VOTE_STRING_AUTOBALANCE); + valid = true; + } + case NativeVotesType_AutoBalanceOff: + { + strcopy(voteString, maxlength, TF2_VOTE_STRING_AUTOBALANCE); + valid = true; + } + case NativeVotesType_ClassLimitsOn: + { + strcopy(voteString, maxlength, TF2_VOTE_STRING_CLASSLIMIT); + valid = true; + } + case NativeVotesType_ClassLimitsOff: + { + strcopy(voteString, maxlength, TF2_VOTE_STRING_CLASSLIMIT); + valid = true; + } + case NativeVotesType_Extend: + { + strcopy(voteString, maxlength, TF2_VOTE_STRING_EXTEND); + } + } + + return valid; +} + +static stock NativeVotesType TF2_VoteStringToVoteType(const char[] voteString) +{ + NativeVotesType voteType; + + if (StrEqual(voteString, TF2CSGO_VOTE_STRING_KICK, false)) + { + voteType = NativeVotesType_Kick; + } + else if (StrEqual(voteString, TF2CSGO_VOTE_STRING_CHANGELEVEL, false)) + { + voteType = NativeVotesType_ChgLevel; + } + else if (StrEqual(voteString, TF2CSGO_VOTE_STRING_NEXTLEVEL, false)) + { + voteType = NativeVotesType_NextLevel; + } + else if (StrEqual(voteString, TF2CSGO_VOTE_STRING_RESTART, false)) + { + voteType = NativeVotesType_Restart; + } + else if (StrEqual(voteString, TF2CSGO_VOTE_STRING_SCRAMBLE, false)) + { + voteType = NativeVotesType_ScrambleNow; + } + else if (StrEqual(voteString, TF2_VOTE_STRING_ETERNAWEEN, false)) + { + voteType = NativeVotesType_Eternaween; + } + else if (StrEqual(voteString, TF2_VOTE_STRING_AUTOBALANCE, false)) + { + voteType = g_Cvar_AutoBalance.BoolValue ? NativeVotesType_AutoBalanceOff : NativeVotesType_AutoBalanceOn; + } + else if (StrEqual(voteString, TF2_VOTE_STRING_CLASSLIMIT, false)) + { + voteType = g_Cvar_ClassLimit.IntValue ? NativeVotesType_ClassLimitsOff : NativeVotesType_ClassLimitsOn; + } + else if (StrEqual(voteString, TF2_VOTE_STRING_EXTEND, false)) + { + voteType = NativeVotesType_Extend; + } + else if (StrEqual(voteString, TF2_VOTE_STRING_CHANGEMISSION, false)) + { + voteType = NativeVotesType_ChgMission; + } + else + { + voteType = NativeVotesType_None; + } + + return voteType; +} + +static stock NativeVotesOverride TF2_VoteTypeToVoteOverride(NativeVotesType voteType) +{ + NativeVotesOverride overrideType; + + switch (voteType) + { + case NativeVotesType_Kick, NativeVotesType_KickCheating, NativeVotesType_KickIdle, NativeVotesType_KickScamming: + { + overrideType = NativeVotesOverride_Kick; + } + case NativeVotesType_ChgLevel: + { + overrideType = NativeVotesOverride_ChgLevel; + } + case NativeVotesType_NextLevel: + { + overrideType = NativeVotesOverride_NextLevel; + } + case NativeVotesType_Restart: + { + overrideType = NativeVotesOverride_Restart; + } + case NativeVotesType_ScrambleEnd, NativeVotesType_ScrambleNow: + { + overrideType = NativeVotesOverride_Scramble; + } + case NativeVotesType_Eternaween: + { + overrideType = NativeVotesOverride_Eternaween; + } + case NativeVotesType_AutoBalanceOn, NativeVotesType_AutoBalanceOff: + { + overrideType = NativeVotesOverride_AutoBalance; + } + case NativeVotesType_ClassLimitsOn, NativeVotesType_ClassLimitsOff: + { + overrideType = NativeVotesOverride_ClassLimits; + } + case NativeVotesType_Extend: + { + overrideType = NativeVotesOverride_Extend; + } + default: + { + overrideType = NativeVotesOverride_None; + } + } + + return overrideType; +} + +static stock NativeVotesType TF2_VoteOverrideToVoteType(NativeVotesOverride overrideType) +{ + NativeVotesType voteType; + + switch (overrideType) + { + case NativeVotesOverride_Restart: + { + voteType = NativeVotesType_Restart; + } + case NativeVotesOverride_Kick: + { + voteType = NativeVotesType_Kick; + } + case NativeVotesOverride_ChgLevel: + { + voteType = NativeVotesType_ChgLevel; + } + case NativeVotesOverride_NextLevel: + { + voteType = NativeVotesType_NextLevel; + } + case NativeVotesOverride_Scramble: + { + voteType = NativeVotesType_ScrambleNow; + } + case NativeVotesOverride_ChgMission: + { + voteType = NativeVotesType_ChgMission; + } + case NativeVotesOverride_Eternaween: + { + voteType = NativeVotesType_Eternaween; + } + case NativeVotesOverride_AutoBalance: + { + voteType = NativeVotesType_AutoBalanceOn; + } + case NativeVotesOverride_ClassLimits: + { + voteType = NativeVotesType_ClassLimitsOn; + } + case NativeVotesOverride_Extend: + { + voteType = NativeVotesType_Extend; + } + default: + { + voteType = NativeVotesType_None; + } + } + + return voteType; +} + +static stock NativeVotesOverride TF2_VoteStringToVoteOverride(const char[] voteString) +{ + NativeVotesOverride overrideType; + + if (StrEqual(voteString, TF2CSGO_VOTE_STRING_KICK, false)) + { + overrideType = NativeVotesOverride_Kick; + } + else if (StrEqual(voteString, TF2CSGO_VOTE_STRING_CHANGELEVEL, false)) + { + overrideType = NativeVotesOverride_ChgLevel; + } + else if (StrEqual(voteString, TF2CSGO_VOTE_STRING_NEXTLEVEL, false)) + { + overrideType = NativeVotesOverride_NextLevel; + } + else if (StrEqual(voteString, TF2CSGO_VOTE_STRING_RESTART, false)) + { + overrideType = NativeVotesOverride_Restart; + } + else if (StrEqual(voteString, TF2CSGO_VOTE_STRING_SCRAMBLE, false)) + { + overrideType = NativeVotesOverride_Scramble; + } + else if (StrEqual(voteString, TF2_VOTE_STRING_ETERNAWEEN, false)) + { + overrideType = NativeVotesOverride_Eternaween; + } + else if (StrEqual(voteString, TF2_VOTE_STRING_AUTOBALANCE, false)) + { + overrideType = NativeVotesOverride_AutoBalance; + } + else if (StrEqual(voteString, TF2_VOTE_STRING_CLASSLIMIT, false)) + { + overrideType = NativeVotesOverride_ClassLimits; + } + else if (StrEqual(voteString, TF2_VOTE_STRING_EXTEND, false)) + { + overrideType = NativeVotesOverride_Extend; + } + else if (StrEqual(voteString, TF2_VOTE_STRING_CHANGEMISSION, false)) + { + overrideType = NativeVotesOverride_ChgMission; + } + else + { + overrideType = NativeVotesOverride_None; + } + +#if defined LOG + LogMessage("Resolved \"%s\" to %d", voteString, overrideType); +#endif + + return overrideType; +} + +static stock bool TF2_OverrideTypeToVoteString(NativeVotesOverride overrideType, char[] voteString, int maxlength) +{ + bool valid = false; + switch(overrideType) + { + case NativeVotesOverride_Kick: + { + strcopy(voteString, maxlength, TF2CSGO_VOTE_STRING_KICK); + valid = true; + } + case NativeVotesOverride_ChgLevel: + { + strcopy(voteString, maxlength, TF2CSGO_VOTE_STRING_CHANGELEVEL); + valid = true; + } + case NativeVotesOverride_NextLevel: + { + strcopy(voteString, maxlength, TF2CSGO_VOTE_STRING_NEXTLEVEL); + valid = true; + } + case NativeVotesOverride_Restart: + { + strcopy(voteString, maxlength, TF2CSGO_VOTE_STRING_RESTART); + valid = true; + } + case NativeVotesOverride_Scramble: + { + strcopy(voteString, maxlength, TF2CSGO_VOTE_STRING_SCRAMBLE); + valid = true; + } + case NativeVotesOverride_Eternaween: + { + strcopy(voteString, maxlength, TF2_VOTE_STRING_ETERNAWEEN); + valid = true; + } + case NativeVotesOverride_AutoBalance: + { + strcopy(voteString, maxlength, TF2_VOTE_STRING_AUTOBALANCE); + valid = true; + } + case NativeVotesOverride_ClassLimits: + { + strcopy(voteString, maxlength, TF2_VOTE_STRING_CLASSLIMIT); + valid = true; + } + case NativeVotesOverride_Extend: + { + strcopy(voteString, maxlength, TF2_VOTE_STRING_EXTEND); + } + case NativeVotesOverride_ChgMission: + { + strcopy(voteString, maxlength, TF2_VOTE_STRING_CHANGEMISSION); + } + } + + return valid; +} + +static stock bool TF2_OverrideTypeToTranslationString(NativeVotesOverride overrideType, char[] translationString, int maxlength) +{ + bool valid = false; + switch(overrideType) + { + case NativeVotesOverride_Kick: + { + strcopy(translationString, maxlength, TF2_VOTE_MENU_KICK); + valid = true; + } + case NativeVotesOverride_ChgLevel: + { + strcopy(translationString, maxlength, TF2_VOTE_MENU_CHANGELEVEL); + valid = true; + } + case NativeVotesOverride_NextLevel: + { + strcopy(translationString, maxlength, TF2_VOTE_MENU_NEXTLEVEL); + valid = true; + } + case NativeVotesOverride_Restart: + { + strcopy(translationString, maxlength, TF2_VOTE_MENU_RESTART); + valid = true; + } + case NativeVotesOverride_Scramble: + { + strcopy(translationString, maxlength, TF2_VOTE_MENU_SCRAMBLE); + valid = true; + } + case NativeVotesOverride_Eternaween: + { + strcopy(translationString, maxlength, TF2_VOTE_MENU_ETERNAWEEN); + valid = true; + } + case NativeVotesOverride_AutoBalance: + { + if (GetConVarBool(g_Cvar_AutoBalance)) + { + strcopy(translationString, maxlength, TF2_VOTE_MENU_AUTOBALANCE_OFF); + } + else + { + strcopy(translationString, maxlength, TF2_VOTE_MENU_AUTOBALANCE_ON); + } + valid = true; + } + case NativeVotesOverride_ClassLimits: + { + if (GetConVarInt(g_Cvar_ClassLimit)) + { + strcopy(translationString, maxlength, TF2_VOTE_MENU_CLASSLIMIT_OFF); + } + else + { + strcopy(translationString, maxlength, TF2_VOTE_MENU_CLASSLIMIT_ON); + } + valid = true; + } + case NativeVotesOverride_Extend: + { + strcopy(translationString, maxlength, TF2_VOTE_MENU_EXTEND); + } + } + + return valid; +} + +stock bool Game_IsVoteTypeCustom(NativeVotesType voteType) +{ + switch(voteType) + { + case NativeVotesType_Custom_YesNo, NativeVotesType_Custom_Mult: + { + return true; + } + } + + return false; +} + +stock bool Game_IsVoteTypeYesNo(NativeVotesType voteType) +{ + switch(voteType) + { + case NativeVotesType_Custom_Mult, NativeVotesType_NextLevelMult: + { + return false; + } + } + + return true; } \ No newline at end of file From d2af24e27ae8125da219f756f33ff8dcc2a11d1c Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sun, 1 Feb 2026 16:37:57 +0100 Subject: [PATCH 056/128] Fix "RTV already started" messages --- addons/sourcemod/scripting/nativevotes.sp | 21 +++++++++++++------ .../sourcemod/scripting/nativevotes/game.sp | 2 ++ .../scripting/nativevotes_basecommands.sp | 2 +- .../scripting/nativevotes_basevotes.sp | 2 +- .../scripting/nativevotes_funvotes.sp | 2 +- .../nativevotes_kickvote_immunity.sp | 2 +- .../scripting/nativevotes_mapchooser.sp | 2 +- .../scripting/nativevotes_nominations.sp | 2 +- .../scripting/nativevotes_rockthevote.sp | 4 ++-- .../sourcemod/scripting/nativevotes_voterp.sp | 2 +- 10 files changed, 26 insertions(+), 15 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes.sp b/addons/sourcemod/scripting/nativevotes.sp index 436c9ce..4ce8a44 100644 --- a/addons/sourcemod/scripting/nativevotes.sp +++ b/addons/sourcemod/scripting/nativevotes.sp @@ -93,7 +93,7 @@ ConVar g_ConVars[MAX_CONVARS]; //---------------------------------------------------------------------------- // Used to track current vote data -//Handle g_hVoteTimer; +Handle g_hVoteTimer; Handle g_hDisplayTimer; int g_Clients; @@ -150,7 +150,7 @@ public Plugin myinfo = name = "NativeVotes", author = "Powerlord", description = "Voting API to use the game's native vote panels. Compatible with L4D, L4D2, TF2, and CS:GO.", - version = "26w05b", + version = "26w06a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" } @@ -653,7 +653,8 @@ public void OnMapEnd() if (g_hDisplayTimer != null) { - delete g_hDisplayTimer; + KillTimer(g_hDisplayTimer); + g_hDisplayTimer = null; } // g_hVoteTimer = INVALID_HANDLE; @@ -1100,7 +1101,8 @@ void EndVoting() if (g_hDisplayTimer != null) { - delete g_hDisplayTimer; + KillTimer(g_hDisplayTimer); + g_hDisplayTimer = null; } if (g_bCancelled) @@ -1235,6 +1237,12 @@ bool InitializeVoting(NativeVote vote, int time, int flags) return false; } + if (g_hVoteTimer != null) + { + //KillTimer(g_hVoteTimer); + g_hVoteTimer = null; + } + Internal_Reset(); /* Mark all clients as not voting */ @@ -1403,12 +1411,13 @@ void Internal_Reset(bool cancel=false) if (g_hDisplayTimer != null) { - delete g_hDisplayTimer; + KillTimer(g_hDisplayTimer); + g_hDisplayTimer = null; } if (!cancel) { - CreateTimer(5.0, Game_ResetVote, TIMER_FLAG_NO_MAPCHANGE); + g_hVoteTimer = CreateTimer(5.0, Game_ResetVote, TIMER_FLAG_NO_MAPCHANGE); } } diff --git a/addons/sourcemod/scripting/nativevotes/game.sp b/addons/sourcemod/scripting/nativevotes/game.sp index 5f11905..2c0ede5 100644 --- a/addons/sourcemod/scripting/nativevotes/game.sp +++ b/addons/sourcemod/scripting/nativevotes/game.sp @@ -917,6 +917,8 @@ stock void Game_UpdateClientCount(int num_clients) public Action Game_ResetVote(Handle timer) { + g_hVoteTimer = null; + switch(g_EngineVersion) { case Engine_Left4Dead, Engine_Left4Dead2: diff --git a/addons/sourcemod/scripting/nativevotes_basecommands.sp b/addons/sourcemod/scripting/nativevotes_basecommands.sp index 3808db1..3edcdaf 100644 --- a/addons/sourcemod/scripting/nativevotes_basecommands.sp +++ b/addons/sourcemod/scripting/nativevotes_basecommands.sp @@ -48,7 +48,7 @@ public Plugin myinfo = name = "NativeVotes | Basic Commands", author = "Powerlord and AlliedModders LLC", description = "Revote and Cancel support for NativeVotes", - version = "26w05a", + version = "26w06a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" } diff --git a/addons/sourcemod/scripting/nativevotes_basevotes.sp b/addons/sourcemod/scripting/nativevotes_basevotes.sp index 2d7c5c5..8656d34 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes.sp @@ -46,7 +46,7 @@ public Plugin:myinfo = name = "NativeVotes | Basic Votes", author = "Powerlord and AlliedModders LLC", description = "NativeVotes Basic Vote Commands", - version = "26w05a", + version = "26w06a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_funvotes.sp b/addons/sourcemod/scripting/nativevotes_funvotes.sp index 7f02d18..4c111ec 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes.sp @@ -47,7 +47,7 @@ public Plugin myinfo = name = "NativeVotes | Fun Votes", author = "Powerlord and AlliedModders LLC", description = "NativeVotes Fun Vote Commands", - version = "26w05a", + version = "26w06a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp b/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp index 1e952ac..cbb0df1 100644 --- a/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp +++ b/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp @@ -60,7 +60,7 @@ public Plugin myinfo = { name = "NativeVotes | Kick Vote Immunity", author = "Powerlord", description = "Causes TF2 kick votes to fail against people who the current user can't target.", - version = "26w05a", + version = "26w06a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_mapchooser.sp b/addons/sourcemod/scripting/nativevotes_mapchooser.sp index 678bf98..a694fda 100644 --- a/addons/sourcemod/scripting/nativevotes_mapchooser.sp +++ b/addons/sourcemod/scripting/nativevotes_mapchooser.sp @@ -55,7 +55,7 @@ public Plugin myinfo = name = "NativeVotes | MapChooser", author = "AlliedModders LLC and Powerlord", description = "Automated Map Voting", - version = "26w05a", + version = "26w06a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_nominations.sp b/addons/sourcemod/scripting/nativevotes_nominations.sp index 02435a9..63a437d 100644 --- a/addons/sourcemod/scripting/nativevotes_nominations.sp +++ b/addons/sourcemod/scripting/nativevotes_nominations.sp @@ -48,7 +48,7 @@ public Plugin myinfo = name = "NativeVotes | Map Nominations", author = "AlliedModders LLC and Powerlord", description = "Provides Map Nominations", - version = "26w05a", + version = "26w06a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_rockthevote.sp b/addons/sourcemod/scripting/nativevotes_rockthevote.sp index 0823103..81b2903 100644 --- a/addons/sourcemod/scripting/nativevotes_rockthevote.sp +++ b/addons/sourcemod/scripting/nativevotes_rockthevote.sp @@ -55,7 +55,7 @@ public Plugin myinfo = name = "NativeVotes | Rock The Vote", author = "AlliedModders LLC and Powerlord", description = "Provides RTV Map Voting", - version = "26w05a", + version = "26w06a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -349,7 +349,7 @@ void AttemptRTV(int client, bool isVoteMenu=false) return; } - if (!CanMapChooserStartVote()) + if (!CanMapChooserStartVote() && NativeVotes_IsVoteInProgress()) { CReplyToCommand(client, "[{lightgreen}Rock The Vote\x01] %t", "RTV Started"); return; diff --git a/addons/sourcemod/scripting/nativevotes_voterp.sp b/addons/sourcemod/scripting/nativevotes_voterp.sp index 500bb33..f5fcd94 100644 --- a/addons/sourcemod/scripting/nativevotes_voterp.sp +++ b/addons/sourcemod/scripting/nativevotes_voterp.sp @@ -10,7 +10,7 @@ public Plugin myinfo = name = "NativeVotes | Medieval Auto-RP", author = "Heapons", description = "Provides Medieval Auto-RP voting.", - version = "26w05a", + version = "26w06a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; From 1926d87e95e6bf1ec2b5ec0b7a77a15ebf050a5e Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Mon, 2 Feb 2026 22:46:12 +0100 Subject: [PATCH 057/128] Bump version --- addons/sourcemod/scripting/nativevotes.sp | 2 +- addons/sourcemod/scripting/nativevotes_basevotes.sp | 2 +- addons/sourcemod/scripting/nativevotes_funvotes.sp | 2 +- addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp | 2 +- addons/sourcemod/scripting/nativevotes_rockthevote.sp | 2 +- addons/sourcemod/scripting/nativevotes_voterp.sp | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes.sp b/addons/sourcemod/scripting/nativevotes.sp index 4ce8a44..d4b5670 100644 --- a/addons/sourcemod/scripting/nativevotes.sp +++ b/addons/sourcemod/scripting/nativevotes.sp @@ -150,7 +150,7 @@ public Plugin myinfo = name = "NativeVotes", author = "Powerlord", description = "Voting API to use the game's native vote panels. Compatible with L4D, L4D2, TF2, and CS:GO.", - version = "26w06a", + version = "26w06b", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" } diff --git a/addons/sourcemod/scripting/nativevotes_basevotes.sp b/addons/sourcemod/scripting/nativevotes_basevotes.sp index 8656d34..54c99d1 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes.sp @@ -46,7 +46,7 @@ public Plugin:myinfo = name = "NativeVotes | Basic Votes", author = "Powerlord and AlliedModders LLC", description = "NativeVotes Basic Vote Commands", - version = "26w06a", + version = "26w06b", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_funvotes.sp b/addons/sourcemod/scripting/nativevotes_funvotes.sp index 4c111ec..a3bd09c 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes.sp @@ -47,7 +47,7 @@ public Plugin myinfo = name = "NativeVotes | Fun Votes", author = "Powerlord and AlliedModders LLC", description = "NativeVotes Fun Vote Commands", - version = "26w06a", + version = "26w06b", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp b/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp index cbb0df1..aa6b24a 100644 --- a/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp +++ b/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp @@ -60,7 +60,7 @@ public Plugin myinfo = { name = "NativeVotes | Kick Vote Immunity", author = "Powerlord", description = "Causes TF2 kick votes to fail against people who the current user can't target.", - version = "26w06a", + version = "26w06b", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_rockthevote.sp b/addons/sourcemod/scripting/nativevotes_rockthevote.sp index 81b2903..d6757c7 100644 --- a/addons/sourcemod/scripting/nativevotes_rockthevote.sp +++ b/addons/sourcemod/scripting/nativevotes_rockthevote.sp @@ -55,7 +55,7 @@ public Plugin myinfo = name = "NativeVotes | Rock The Vote", author = "AlliedModders LLC and Powerlord", description = "Provides RTV Map Voting", - version = "26w06a", + version = "26w06b", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_voterp.sp b/addons/sourcemod/scripting/nativevotes_voterp.sp index f5fcd94..4671b7b 100644 --- a/addons/sourcemod/scripting/nativevotes_voterp.sp +++ b/addons/sourcemod/scripting/nativevotes_voterp.sp @@ -10,7 +10,7 @@ public Plugin myinfo = name = "NativeVotes | Medieval Auto-RP", author = "Heapons", description = "Provides Medieval Auto-RP voting.", - version = "26w06a", + version = "26w06b", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; From 3caf179d075d5a8bf632f6454fa06e69cabb64e4 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Mon, 2 Feb 2026 22:50:17 +0100 Subject: [PATCH 058/128] Add sm_callvote --- .../scripting/nativevotes_basecommands.sp | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/addons/sourcemod/scripting/nativevotes_basecommands.sp b/addons/sourcemod/scripting/nativevotes_basecommands.sp index 3edcdaf..4b10482 100644 --- a/addons/sourcemod/scripting/nativevotes_basecommands.sp +++ b/addons/sourcemod/scripting/nativevotes_basecommands.sp @@ -48,7 +48,7 @@ public Plugin myinfo = name = "NativeVotes | Basic Commands", author = "Powerlord and AlliedModders LLC", description = "Revote and Cancel support for NativeVotes", - version = "26w06a", + version = "26w06b", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" } @@ -59,6 +59,7 @@ public void OnPluginStart() AddCommandListener(Command_CancelVote, "sm_cancelvote"); AddCommandListener(Command_ReVote, "sm_revote"); + RegConsoleCmd("sm_callvote", Command_CallVote, "Start a vote on an issue."); } bool PerformCancelVote(int client) @@ -74,6 +75,23 @@ bool PerformCancelVote(int client) return true; } +public void OnClientSayCommand_Post(int client, const char[] command, const char[] sArgs) +{ + if (!client || IsChatTrigger()) + { + return; + } + + if (strcmp(sArgs, "callvote", false) == 0) + { + ReplySource old = SetCmdReplySource(SM_REPLY_TO_CHAT); + + Command_CallVote(client, 0); + + SetCmdReplySource(old); + } +} + public Action Command_CancelVote(int client, const char[] command, int argc) { if (!CheckCommandAccess(client, "sm_cancelvote", ADMFLAG_VOTE)) @@ -162,6 +180,18 @@ public Action Command_ReVote(int client, const char[] command, int argc) return Plugin_Continue; } +public Action Command_CallVote(int client, int args) +{ + if (client <= 0) + { + return Plugin_Continue; + } + + FakeClientCommand(client, "callvote"); + + return Plugin_Continue; +} + public void OnAdminMenuReady(Handle aTopMenu) { TopMenu topmenu = TopMenu.FromHandle(aTopMenu); From 51e05f2cbb2f3b0e5751f9c3d20a5039a86518a4 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Mon, 2 Feb 2026 22:50:35 +0100 Subject: [PATCH 059/128] Remove sm_nominate_use_callvote --- .../sourcemod/scripting/nativevotes_nominations.sp | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_nominations.sp b/addons/sourcemod/scripting/nativevotes_nominations.sp index 63a437d..b9ff2e2 100644 --- a/addons/sourcemod/scripting/nativevotes_nominations.sp +++ b/addons/sourcemod/scripting/nativevotes_nominations.sp @@ -48,7 +48,7 @@ public Plugin myinfo = name = "NativeVotes | Map Nominations", author = "AlliedModders LLC and Powerlord", description = "Provides Map Nominations", - version = "26w06a", + version = "26w06b", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -58,7 +58,6 @@ enum excludecurrent, maxmatches, allow_workshop, - use_callvote, nextlevel_allowed, @@ -97,7 +96,6 @@ public void OnPluginStart() g_ConVars[excludecurrent] = CreateConVar("sm_nominate_excludecurrent", "1", "Specifies if the MapChooser excluded maps should also be excluded from Nominations", _, true, 0.0, true, 1.0); g_ConVars[maxmatches] = CreateConVar("sm_nominate_maxfound", "0", "Maximum number of nomination matches to add to the menu. 0 = infinite.", _, true, 0.0); //g_ConVars[allow_workshop] = CreateConVar("sm_nominate_allow_workshop", "0", "Specifies if unlisted workshop maps can be nominated", _, true, 0.0, true, 1.0); - g_ConVars[use_callvote] = CreateConVar("sm_nominate_use_callvote", "1", "Specifies whether to execute callvote when nominating without specifying the map.", _, true, 0.0, true, 1.0); g_ConVars[nextlevel_allowed] = FindConVar("sv_vote_issue_nextlevel_allowed"); @@ -276,14 +274,7 @@ public Action Command_Nominate(int client, int args) if (args == 0) { - if (g_NativeVotes && NativeVotes_AreVoteCommandsSupported() && g_ConVars[use_callvote].BoolValue && g_ConVars[nextlevel_allowed].BoolValue) - { - FakeClientCommand(client, "callvote"); - } - else - { - OpenNominationMenu(client); - } + OpenNominationMenu(client); return Plugin_Handled; } From 2294381f76eed2e89d379baec26848bfb6db11e7 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Mon, 2 Feb 2026 22:51:11 +0100 Subject: [PATCH 060/128] Fix mapcycle file not being found when automatically generated --- .../scripting/nativevotes_mapchooser.sp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_mapchooser.sp b/addons/sourcemod/scripting/nativevotes_mapchooser.sp index a694fda..cb36237 100644 --- a/addons/sourcemod/scripting/nativevotes_mapchooser.sp +++ b/addons/sourcemod/scripting/nativevotes_mapchooser.sp @@ -55,7 +55,7 @@ public Plugin myinfo = name = "NativeVotes | MapChooser", author = "AlliedModders LLC and Powerlord", description = "Automated Map Voting", - version = "26w06a", + version = "26w06b", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -176,7 +176,7 @@ public void OnPluginStart() g_ConVars[mapvote_runoff] = CreateConVar("sm_mapvote_runoff", "0", "Hold run of votes if winning choice is less than a certain margin.", _, true, 0.0, true, 1.0); g_ConVars[mapvote_runoffpercent] = CreateConVar("sm_mapvote_runoffpercent", "50", "If winning choice has less than this percent of votes, hold a runoff.", _, true, 0.0, true, 100.0); g_ConVars[mapcycle_auto] = CreateConVar("sm_mapcycle_auto", "0", "Specifies whether or not to automatically populate the maps list.", _, true, 0.0, true, 1.0); - g_ConVars[mapcycle_exclude] = CreateConVar("sm_mapcycle_exclude", ".*itemtest.*|background01|^tr.*$", "Specifies which maps shouldn't be automatically added with a regex pattern."); + g_ConVars[mapcycle_exclude] = CreateConVar("sm_mapcycle_exclude", ".*test.*|background01|^tr.*$", "Specifies which maps shouldn't be automatically added with a regex pattern."); if (engine != Engine_SDK2013 && engine == Engine_TF2) { g_ConVars[workshop_map_collection] = CreateConVar("sm_workshop_map_collection", "", "Specifies the workshop collection to fetch the maps from."); @@ -1647,6 +1647,7 @@ void PopulateMapList() { CloseHandle(file); } + RequestFrame(RequestFrame_ReadMapList); } void HTTPResponse_GetCollectionDetails(HTTPResponse response, File file) @@ -1777,4 +1778,15 @@ void CleanupWorkshopMaps() delete dir; } RemoveDir(workshopDir); +} + +void RequestFrame_ReadMapList() +{ + if (ReadMapList(g_MapList, g_mapFileSerial, "mapchooser", MAPLIST_FLAG_CLEARARRAY|MAPLIST_FLAG_MAPSFOLDER) != null) + { + if (g_mapFileSerial == -1) + { + LogError("Unable to create a valid map list."); + } + } } \ No newline at end of file From 94937c32801ff1b65f101571b40f75acad1a6e43 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Mon, 2 Feb 2026 22:51:27 +0100 Subject: [PATCH 061/128] Fix Change VIP vote issue not showing up --- .../scripting/include/nativevotes.inc | 3 + .../sourcemod/scripting/nativevotes/game.sp | 94 +++++++++++++++++-- 2 files changed, 87 insertions(+), 10 deletions(-) diff --git a/addons/sourcemod/scripting/include/nativevotes.inc b/addons/sourcemod/scripting/include/nativevotes.inc index b951b3e..320ea6f 100644 --- a/addons/sourcemod/scripting/include/nativevotes.inc +++ b/addons/sourcemod/scripting/include/nativevotes.inc @@ -113,6 +113,7 @@ enum NativeVotesType NativeVotesType_ClassLimitsOn, /**< TF2: Yes/No, details are ignored */ NativeVotesType_ClassLimitsOff, /**< TF2: Yes/No, details are ignored */ NativeVotesType_Extend, /**< TF2: Yes/No, details are ignored */ + NativeVotesType_ChgCivilian, /**< TF2 Classified: Yes/No, target is player userid, details are auto-set by target */ }; enum NativeVotesPassType @@ -141,6 +142,7 @@ enum NativeVotesPassType NativeVotesPass_AutoBalanceOff, /**< TF2: Yes/No, details are ignored */ NativeVotesPass_ClassLimitsOn, /**< TF2: Yes/No, details are ignored */ NativeVotesPass_ClassLimitsOff, /**< TF2: Yes/No, details are ignored */ + NativeVotesPass_ChgCivilian, /**< TF2 Classified: Yes/No, target is player userid, details are auto-set by target */ }; /** @@ -237,6 +239,7 @@ enum NativeVotesOverride NativeVotesOverride_AutoBalance, NativeVotesOverride_ClassLimits, NativeVotesOverride_Extend, + NativeVotesOverride_ChgCivilian, NativeVotesOverride_Count, } diff --git a/addons/sourcemod/scripting/nativevotes/game.sp b/addons/sourcemod/scripting/nativevotes/game.sp index 2c0ede5..d5cc8ca 100644 --- a/addons/sourcemod/scripting/nativevotes/game.sp +++ b/addons/sourcemod/scripting/nativevotes/game.sp @@ -213,6 +213,17 @@ enum VOTE_FAILED_MODIFICATION_ALREADY_ACTIVE, } +//---------------------------------------------------------------------------- +// TF2 Classified + +#define TF2CLASSIFIED_VOTE_STRING_CHANGE_CIVILIAN "ChangeCivilian" + +#define TF2CLASSIFIED_VOTE_MENU_CHANGE_CIVILIAN "#Vote_ChangeCivilian" + +#define TF2CLASSIFIED_VOTE_CHANGE_CIVILIAN_START "#TF_vote_change_civilian" +#define TF2CLASSIFIED_VOTE_CHANGE_CIVILIAN_CALLER "#TF_vote_change_civilian_caller" +#define TF2CLASSIFIED_VOTE_CHANGE_CIVILIAN_PASSED "#TF_vote_passed_change_civilian" + //---------------------------------------------------------------------------- // CSGO // User vote to kick user. @@ -362,6 +373,8 @@ static ConVar g_Cvar_AutoBalance; static ConVar g_Cvar_HideDisabledIssues; +static ConVar g_Cvar_ChangeCivilian_Enabled; + /** * TODO(UPDATE): For now we only support one vote from NativeVotes at a time. * @@ -443,6 +456,9 @@ void Game_InitializeCvars() g_Cvar_AutoBalance = FindConVar("mp_autoteambalance"); g_Cvar_HideDisabledIssues = FindConVar("sv_vote_ui_hide_disabled_issues"); + + // Team Fortress 2 Classified + g_Cvar_ChangeCivilian_Enabled = FindConVar("tf2c_vote_issue_change_civilian_allowed"); } } } @@ -1211,6 +1227,10 @@ static NativeVotesPassType VoteTypeToVotePass(NativeVotesType voteType) { passType = NativeVotesPass_Extend; } + case NativeVotesType_ChgCivilian: + { + passType = NativeVotesPass_ChgCivilian; + } default: { passType = NativeVotesPass_Custom; @@ -2553,12 +2573,25 @@ static bool TF2_CheckVoteType(NativeVotesType voteType) { switch(voteType) { - case NativeVotesType_Custom_YesNo, NativeVotesType_Restart, - NativeVotesType_Kick, NativeVotesType_KickIdle, NativeVotesType_KickScamming, NativeVotesType_KickCheating, - NativeVotesType_ChgLevel, NativeVotesType_NextLevel, NativeVotesType_ScrambleNow, NativeVotesType_ScrambleEnd, - NativeVotesType_ChgMission, NativeVotesType_StartRound, NativeVotesType_Eternaween, - NativeVotesType_AutoBalanceOn, NativeVotesType_AutoBalanceOff, - NativeVotesType_ClassLimitsOn, NativeVotesType_ClassLimitsOff, NativeVotesType_Extend: + case NativeVotesType_Custom_YesNo, + NativeVotesType_Restart, + NativeVotesType_Kick, + NativeVotesType_KickIdle, + NativeVotesType_KickScamming, + NativeVotesType_KickCheating, + NativeVotesType_ChgLevel, + NativeVotesType_NextLevel, + NativeVotesType_ScrambleNow, + NativeVotesType_ScrambleEnd, + NativeVotesType_ChgMission, + NativeVotesType_StartRound, + NativeVotesType_Eternaween, + NativeVotesType_AutoBalanceOn, + NativeVotesType_AutoBalanceOff, + NativeVotesType_ClassLimitsOn, + NativeVotesType_ClassLimitsOff, + NativeVotesType_Extend, + NativeVotesType_ChgCivilian: { return true; } @@ -2588,7 +2621,8 @@ static bool TF2_CheckVotePassType(NativeVotesPassType passType) NativeVotesPass_AutoBalanceOn, NativeVotesPass_AutoBalanceOff, NativeVotesPass_ClassLimitsOn, - NativeVotesPass_ClassLimitsOff: + NativeVotesPass_ClassLimitsOff, + NativeVotesPass_ChgCivilian: { return true; } @@ -2680,6 +2714,10 @@ static bool TF2_VoteTypeToTranslation(NativeVotesType voteType, char[] translati { strcopy(translation, maxlength, TF2_VOTE_EXTEND_START); } + case NativeVotesType_ChgCivilian: + { + strcopy(translation, maxlength, TF2CLASSIFIED_VOTE_CHANGE_CIVILIAN_START); + } default: { strcopy(translation, maxlength, TF2_VOTE_CUSTOM); @@ -2745,6 +2783,10 @@ static void TF2_VotePassToTranslation(NativeVotesPassType passType, char[] trans { strcopy(translation, maxlength, TF2_VOTE_CLASSLIMITS_DISABLE_PASSED); } + case NativeVotesPass_ChgCivilian: + { + strcopy(translation, maxlength, TF2CLASSIFIED_VOTE_CHANGE_CIVILIAN_PASSED); + } default: { strcopy(translation, maxlength, TF2_VOTE_CUSTOM); @@ -2806,6 +2848,10 @@ static void TF2_AddDefaultVotes(ArrayList hVoteTypes, bool bHideDisabledVotes) // Extend VoteTypeSet(hVoteTypes, bHideDisabledVotes, NativeVotesOverride_Extend, globalEnable && g_Cvar_VoteExtend_Enabled.BoolValue); + + /* Team Fortress 2 Classified */ + // ChangeCivilian + VoteTypeSet(hVoteTypes, bHideDisabledVotes, NativeVotesOverride_ChgCivilian, globalEnable && g_Cvar_ChangeCivilian_Enabled.BoolValue); } } @@ -3165,6 +3211,10 @@ static stock NativeVotesType TF2_VoteStringToVoteType(const char[] voteString) { voteType = NativeVotesType_ChgMission; } + else if (StrEqual(voteString, TF2CLASSIFIED_VOTE_STRING_CHANGE_CIVILIAN, false)) + { + voteType = NativeVotesType_ChgCivilian; + } else { voteType = NativeVotesType_None; @@ -3179,7 +3229,7 @@ static stock NativeVotesOverride TF2_VoteTypeToVoteOverride(NativeVotesType vote switch (voteType) { - case NativeVotesType_Kick, NativeVotesType_KickCheating, NativeVotesType_KickIdle, NativeVotesType_KickScamming: + case NativeVotesType_Kick, NativeVotesType_KickCheating, NativeVotesType_KickIdle, NativeVotesType_KickScamming: { overrideType = NativeVotesOverride_Kick; } @@ -3203,11 +3253,13 @@ static stock NativeVotesOverride TF2_VoteTypeToVoteOverride(NativeVotesType vote { overrideType = NativeVotesOverride_Eternaween; } - case NativeVotesType_AutoBalanceOn, NativeVotesType_AutoBalanceOff: + case NativeVotesType_AutoBalanceOn, + NativeVotesType_AutoBalanceOff: { overrideType = NativeVotesOverride_AutoBalance; } - case NativeVotesType_ClassLimitsOn, NativeVotesType_ClassLimitsOff: + case NativeVotesType_ClassLimitsOn, + NativeVotesType_ClassLimitsOff: { overrideType = NativeVotesOverride_ClassLimits; } @@ -3215,6 +3267,10 @@ static stock NativeVotesOverride TF2_VoteTypeToVoteOverride(NativeVotesType vote { overrideType = NativeVotesOverride_Extend; } + case NativeVotesType_ChgCivilian: + { + overrideType = NativeVotesOverride_ChgCivilian; + } default: { overrideType = NativeVotesOverride_None; @@ -3270,6 +3326,10 @@ static stock NativeVotesType TF2_VoteOverrideToVoteType(NativeVotesOverride over { voteType = NativeVotesType_Extend; } + case NativeVotesOverride_ChgCivilian: + { + voteType = NativeVotesType_ChgCivilian; + } default: { voteType = NativeVotesType_None; @@ -3323,6 +3383,10 @@ static stock NativeVotesOverride TF2_VoteStringToVoteOverride(const char[] voteS { overrideType = NativeVotesOverride_ChgMission; } + else if (StrEqual(voteString, TF2CLASSIFIED_VOTE_STRING_CHANGE_CIVILIAN, false)) + { + overrideType = NativeVotesOverride_ChgCivilian; + } else { overrideType = NativeVotesOverride_None; @@ -3388,6 +3452,11 @@ static stock bool TF2_OverrideTypeToVoteString(NativeVotesOverride overrideType, { strcopy(voteString, maxlength, TF2_VOTE_STRING_CHANGEMISSION); } + case NativeVotesOverride_ChgCivilian: + { + strcopy(voteString, maxlength, TF2CLASSIFIED_VOTE_STRING_CHANGE_CIVILIAN); + valid = true; + } } return valid; @@ -3456,6 +3525,11 @@ static stock bool TF2_OverrideTypeToTranslationString(NativeVotesOverride overri { strcopy(translationString, maxlength, TF2_VOTE_MENU_EXTEND); } + case NativeVotesOverride_ChgCivilian: + { + strcopy(translationString, maxlength, TF2CLASSIFIED_VOTE_MENU_CHANGE_CIVILIAN); + valid = true; + } } return valid; From 162f7f30f44981fe5597062ea5539ae6764a0161 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Mon, 2 Feb 2026 22:51:34 +0100 Subject: [PATCH 062/128] Update README.md --- README.md | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 3498f37..9799c9b 100644 --- a/README.md +++ b/README.md @@ -19,12 +19,15 @@ This fork aims to expand upon [ Team Fortress 2 -- Fixes. +- Fixes: - ✔️/❌ vote counts. - - Proper sounds get played upon selecting an item in a **multi-choice** (≤5 options max.) vote. + - Proper sounds get played upon selecting an item in a **multi-choice** (≤5 options) vote. + - Temporarily reverted. See: https://github.com/Heapons/sourcemod-nativevotes-updated/issues/2#issuecomment-3825973400. + - **Change VIP** vote issue now shows up again. - Add [`sm_voterp`](https://github.com/Heapons/sourcemod-nativevotes-updated/blob/master/addons/sourcemod/scripting/nativevotes_voterp.sp). - Controls `tf_medieval_autorp` cvar. -- Map lists in **Vote Setup** now work in **Mann Vs. Machine** as well. +- Maplists in **Vote Setup** now work in **Mann Vs. Machine** as well. +- Added `sm_callvote` command and `callvote` chat trigger to open **Vote Setup**. ## [Rock The Vote](https://github.com/Heapons/sourcemod-nativevotes-updated/blob/master/addons/sourcemod/scripting/nativevotes_rockthevote.sp) - Admin commands. @@ -45,9 +48,6 @@ This fork aims to expand upon [0 = infinite| -|`sm_nominate_use_callvote`|`1`|Specifies whether to execute callvote when nominating without specifying the map.| ## [MapChooser](https://github.com/Heapons/sourcemod-nativevotes-updated/blob/master/addons/sourcemod/scripting/nativevotes_mapchooser.sp) - Automatically generate mapcycle files. - Can be refreshed with `sm_reloadmaplist`. - Clean up workshop maps to reduce disk size. -- **[OPTIONAL]** Automatically generate the mapcycle file. - - Import Workshop map collections (requires [Rest In Pawn](https://github.com/srcdslab/sm-ext-ripext) in order to use that feature). -> [!WARNING] -> There's currently a bug where leaving `sm_mapcycle_auto` enabled at all times will make all plugins unable to find [`mapcyclefile`](https://developer.valvesoftware.com/wiki/Mapcycle.txt). But if you're still going to leave it on for whatever reason, `sm_reload_nominations` works as a temporary fix (or alternatively, toggle it on then off). +- Automatically generate the mapcycle file. + - Import Workshop map collections (requires [Rest In Pawn](https://github.com/srcdslab/sm-ext-ripext) in order to use that feature). --- |Name|Default Value|Description| @@ -85,6 +82,6 @@ This fork aims to expand upon [[ **Team Fortress 2**](https://store.steampowered.com/app/440) (or its mods) only| -|`sm_workshop_map_cleanup`|`0`|Specifies whether to automatically cleanup workshop maps on map change
[ **Team Fortress 2**](https://store.steampowered.com/app/440) (or its mods) only| +|`sm_mapcycle_exclude`|`.*test.*\|background01\|^tr.*$`|Specifies which maps shouldn't be automatically added (regex pattern).| +| `sm_workshop_map_collection`|` `|Specifies the workshop collection to fetch the maps from.| +| `sm_workshop_map_cleanup`|`0`|Specifies whether to automatically cleanup workshop maps on map change| From fc120315495f32bfcdc1d777e4417836e350de1e Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Mon, 2 Feb 2026 22:53:41 +0100 Subject: [PATCH 063/128] Run workflow on latest Ubuntu version --- .github/workflows/nativevotes.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/nativevotes.yml b/.github/workflows/nativevotes.yml index 44a36ba..c541f3d 100644 --- a/.github/workflows/nativevotes.yml +++ b/.github/workflows/nativevotes.yml @@ -19,7 +19,7 @@ on: jobs: run: name: Run action - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest # skip build on '[ci skip]' if: "!contains(github.event.head_commit.message, '[ci skip]')" From 11ea44d5663f07edb0c982826fc01daa873a4a58 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Wed, 4 Feb 2026 16:28:26 +0100 Subject: [PATCH 064/128] Scramble Teams #4 https://github.com/Heapons/sourcemod-nativevotes-updated/issues/4 --- .../scripting/nativevotes_votescramble.sp | 489 ++++++++++++++++++ .../translations/votescramble.phrases.txt | 39 ++ 2 files changed, 528 insertions(+) create mode 100644 addons/sourcemod/scripting/nativevotes_votescramble.sp create mode 100644 addons/sourcemod/translations/votescramble.phrases.txt diff --git a/addons/sourcemod/scripting/nativevotes_votescramble.sp b/addons/sourcemod/scripting/nativevotes_votescramble.sp new file mode 100644 index 0000000..3442bbc --- /dev/null +++ b/addons/sourcemod/scripting/nativevotes_votescramble.sp @@ -0,0 +1,489 @@ +#include +#include +#include + +#undef REQUIRE_PLUGIN +#include +#define REQUIRE_PLUGIN + +public Plugin myinfo = +{ + name = "NativeVotes | Scramble Teams", + author = "Heapons", + description = "Provides Scramble Teams Voting", + version = "26w06c", + url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" +}; + +enum +{ + needed, + minplayers, + initialdelay, + interval, + restart_round, + restart_timelimit, + enforce_timelimit, + + MAX_CONVARS +} + +ConVar g_ConVars[MAX_CONVARS]; + +bool g_ScrambleAllowed = false; +int g_Voters = 0; +int g_Votes = 0; +int g_VotesNeeded = 0; +bool g_Voted[MAXPLAYERS+1] = {false, ...}; +bool g_NativeVotes; +bool g_RegisteredScramble = false; +int g_ScrambleTime = 0; + +public void OnPluginStart() +{ + LoadTranslations("common.phrases"); + LoadTranslations("rockthevote.phrases"); + LoadTranslations("votescramble.phrases"); + + g_ConVars[needed] = CreateConVar("sm_scrambleteams_needed", "0.60", "Percentage of players needed to scramble teams (Def 60%)", 0, true, 0.05, true, 1.0); + g_ConVars[minplayers] = CreateConVar("sm_scrambleteams_minplayers", "0", "Number of players required before scramble will be enabled.", 0, true, 0.0, true, float(MAXPLAYERS)); + g_ConVars[initialdelay] = CreateConVar("sm_scrambleteams_initialdelay", "30.0", "Time (in seconds) before first scramble can be held", 0, true, 0.00); + g_ConVars[interval] = CreateConVar("sm_scrambleteams_interval", "240.0", "Time (in seconds) after a failed scramble before another can be held", 0, true, 0.00); + g_ConVars[restart_round] = CreateConVar("sm_scrambleteams_restart_round", "0", "Whether to restart the round after scrambling teams", 0, true, 0.0, true, 1.0); + g_ConVars[restart_timelimit] = CreateConVar("sm_scrambleteams_restart_timelimit", "0", "Whether to restart the timelimit after scrambling teams", 0, true, 0.0, true, 1.0); + g_ConVars[enforce_timelimit] = CreateConVar("sm_scrambleteams_enforce_timelimit", "0", "Whether to end the game as soon as the timelimit runs out.", 0, true, 0.0, true, 1.0); + + RegConsoleCmd("sm_votescramble", Command_VoteScramble); + RegConsoleCmd("sm_scramble", Command_VoteScramble); + + RegAdminCmd("sm_forcescramble", Command_ForceScramble, ADMFLAG_CHANGEMAP); + RegAdminCmd("sm_resetscramble", Command_ResetScramble, ADMFLAG_CHANGEMAP); + + int flags = GetCommandFlags("mp_scrambleteams"); + if (flags & FCVAR_CHEAT) + { + SetCommandFlags("mp_scrambleteams", flags & ~FCVAR_CHEAT); + } + + AutoExecConfig(true, "votescramble"); + + OnMapEnd(); + + for (int i = 1; i <= MaxClients; i++) + { + if (IsClientConnected(i)) + { + OnClientConnected(i); + } + } +} + +public void OnPluginEnd() +{ + RemoveVoteHandler(); +} + +public void OnAllPluginsLoaded() +{ + g_NativeVotes = LibraryExists("nativevotes") && + GetFeatureStatus(FeatureType_Native, "NativeVotes_AreVoteCommandsSupported") == FeatureStatus_Available && + NativeVotes_AreVoteCommandsSupported(); + + if (g_NativeVotes) + RegisterVoteHandler(); +} + +public void OnLibraryAdded(const char[] name) +{ + if (StrEqual(name, "nativevotes", false) && + GetFeatureStatus(FeatureType_Native, "NativeVotes_AreVoteCommandsSupported") == FeatureStatus_Available && + NativeVotes_AreVoteCommandsSupported()) + { + g_NativeVotes = true; + RegisterVoteHandler(); + } +} + +public void OnLibraryRemoved(const char[] name) +{ + if (StrEqual(name, "nativevotes", false)) + { + g_NativeVotes = false; + RemoveVoteHandler(); + } +} + +public void OnMapEnd() +{ + g_ScrambleAllowed = false; + g_Voters = 0; + g_Votes = 0; + g_VotesNeeded = 0; +} + +public void OnConfigsExecuted() +{ + if (g_ConVars[initialdelay].FloatValue <= 0.0) + { + g_ScrambleAllowed = true; + return; + } + CreateTimer(g_ConVars[initialdelay].FloatValue, Timer_DelayScramble, _, TIMER_FLAG_NO_MAPCHANGE); +} + +public void OnClientConnected(int client) +{ + if (!IsFakeClient(client)) + { + g_Voters++; + g_VotesNeeded = RoundToCeil(float(g_Voters) * g_ConVars[needed].FloatValue); + } +} + +public void OnClientDisconnect(int client) +{ + if (g_Voted[client]) + { + g_Votes--; + g_Voted[client] = false; + } + + if (!IsFakeClient(client)) + { + g_Voters--; + g_VotesNeeded = RoundToCeil(float(g_Voters) * g_ConVars[needed].FloatValue); + } + + if (g_Votes && + g_Voters && + g_Votes >= g_VotesNeeded && + g_ScrambleAllowed) + { + if (g_NativeVotes && NativeVotes_IsVoteInProgress()) + { + return; + } + + StartScramble(); + } +} + +public void Event_TeamplayRoundStart(Event event, const char[] name, bool dontBroadcast) +{ + // Ends game mid-round once mp_timelimit runs out + if (g_ConVars[enforce_timelimit].BoolValue) + { + int entity = FindEntityByClassname(-1, "tf_gamerules"); + if (entity != -1) + { + SetVariantBool(true); + AcceptEntityInput(entity, "SetStalemateOnTimelimit", 0, 0); + } + } + // Prevents map reset after scrambling teams + if (!g_ConVars[restart_timelimit].BoolValue) + { + GameRules_SetPropFloat("m_flMapResetTime", 0.0); + } +} + +public Action Command_VoteScramble(int client, int args) +{ + if (!client) + return Plugin_Handled; + + AttemptScramble(client); + return Plugin_Handled; +} + +public Action Command_ForceScramble(int client, int args) +{ + if (!g_ScrambleAllowed) + g_ScrambleAllowed = true; + + StartScramble(); + return Plugin_Handled; +} + +public Action Command_ResetScramble(int client, int args) +{ + ResetScramble(); + CPrintToChatAll("[{lightgreen}Scramble Teams\x01] %t", "Cancelled Vote"); + return Plugin_Handled; +} + +void UndoScramble(int client) +{ + char yes[64], no[64]; + Format(yes, sizeof(yes), "%T", "Yes", client); + Format(no, sizeof(no), "%T", "No", client); + + char title[128]; + Format(title, sizeof(title), "%T", "Cancel vote", client); + + Menu menu = new Menu(MenuHandler_UndoScramble); + menu.SetTitle(title); + menu.AddItem("yes", yes); + menu.AddItem("no", no); + menu.ExitButton = false; + menu.Display(client, MENU_TIME_FOREVER); +} + +void MenuHandler_UndoScramble(Menu menu, MenuAction action, int client, int param2) +{ + switch (action) + { + case MenuAction_Select: + { + if (param2 == 0) // Yes + { + if (g_Voted[client]) + { + char name[MAX_NAME_LENGTH]; + GetPlayerName(client, name, sizeof(name)); + + g_Voted[client] = false; + if (g_Votes > 0) g_Votes--; + CPrintToChatAllEx(client, "[{lightgreen}Scramble Teams\x01] %s: %t", name, "Cancelled Vote"); + } + } + } + case MenuAction_End: + { + delete menu; + } + } +} + +void AttemptScramble(int client, bool isVoteMenu=false) +{ + if (!g_ScrambleAllowed) + { + CReplyToCommand(client, "[{lightgreen}Scramble Teams\x01] %t", "Scramble Not Allowed"); + if (isVoteMenu && g_NativeVotes) + { + int timeleft = g_ScrambleTime - GetTime(); + if (timeleft > 0) + { + NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_Failed, timeleft); + } + else + { + NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_Generic); + } + } + return; + } + + if (g_NativeVotes && NativeVotes_IsVoteInProgress()) + { + CReplyToCommand(client, "[{lightgreen}Scramble Teams\x01] %t", "Vote in Progress"); + return; + } + + if (GetClientCount(true) < g_ConVars[minplayers].IntValue) + { + CReplyToCommand(client, "[{lightgreen}Scramble Teams\x01] %t", "Minimal Players Not Met"); + if (isVoteMenu && g_NativeVotes) + { + NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_Loading); + } + return; + } + + if (g_Voted[client]) + { + UndoScramble(client); + return; + } + + char name[MAX_NAME_LENGTH]; + GetPlayerName(client, name, sizeof(name)); + + g_Votes++; + g_Voted[client] = true; + + CPrintToChatAllEx(client, "[{lightgreen}Scramble Teams\x01] %t", "Scramble Requested", name, g_Votes, g_VotesNeeded); + + if (g_Votes >= g_VotesNeeded) + { + StartScramble(); + } +} + +void StartScramble() +{ + char yes[64], no[64]; + Format(yes, sizeof(yes), "%T", "Yes", LANG_SERVER); + Format(no, sizeof(no), "%T", "No", LANG_SERVER); + if (g_NativeVotes) + { + NativeVote vote = NativeVotes_Create(ScrambleVoteHandler, NativeVotesType_ScrambleNow); + NativeVotes_SetTitle(vote, "%t", "Scramble Teams"); + NativeVotes_AddItem(vote, "yes", yes); + NativeVotes_AddItem(vote, "no", no); + NativeVotes_DisplayToAll(vote, 20); + NativeVotes_SetResultCallback(vote, ScrambleVoteResult); + } + else + { + Menu menu = new Menu(MenuHandler_Scramble); + menu.SetTitle("%t", "Scramble Teams"); + menu.AddItem("yes", yes); + menu.AddItem("no", no); + menu.ExitButton = false; + for (int i = 1; i <= MaxClients; i++) + { + if (IsClientInGame(i) && !IsFakeClient(i)) + { + menu.Display(i, 20); + } + } + } + ResetScramble(); + g_ScrambleAllowed = false; + + g_ScrambleTime = GetTime() + g_ConVars[interval].IntValue; + CreateTimer(g_ConVars[interval].FloatValue, Timer_DelayScramble, _, TIMER_FLAG_NO_MAPCHANGE); +} + +void ResetScramble() +{ + g_Votes = 0; + for (int i = 1; i <= MAXPLAYERS; i++) + { + g_Voted[i] = false; + } +} + +void RegisterVoteHandler() +{ + if (!g_NativeVotes) + return; + + if (!g_RegisteredScramble) + { + NativeVotes_RegisterVoteCommand(NativeVotesOverride_Scramble, Menu_ScrambleTeams); + g_RegisteredScramble = true; + } +} + +void RemoveVoteHandler() +{ + if (g_RegisteredScramble) + { + if (g_NativeVotes) + NativeVotes_UnregisterVoteCommand(NativeVotesOverride_Scramble, Menu_ScrambleTeams); + g_RegisteredScramble = false; + } +} + +public Action Menu_ScrambleTeams(int client, NativeVotesOverride overrideType, const char[] voteArgument) +{ + if (client <= 0 || NativeVotes_IsVoteInProgress()) + return Plugin_Stop; + + AttemptScramble(client, true); + return Plugin_Stop; +} + +int ScrambleVoteHandler(NativeVote vote, MenuAction action, int param1, int param2) +{ + if (action == MenuAction_VoteEnd) + { + // handled by result callback + } + else if (action == MenuAction_End) + { + NativeVotes_Close(vote); + } + return 0; +} + +void ScrambleVoteResult(NativeVote vote, int num_votes, int num_clients, const int[] client_indexes, const int[] client_votes, int num_items, const int[] item_indexes, const int[] item_votes) +{ + int yesVotes = 0; + int noVotes = 0; + + for (int i = 0; i < num_items; i++) + { + if (item_indexes[i] == 0) + { + yesVotes = item_votes[i]; + } + else if (item_indexes[i] == 1) + { + noVotes = item_votes[i]; + } + } + + if (yesVotes > noVotes) + { + NativeVotes_DisplayPassEx(vote, NativeVotesPass_Scramble); + + CPrintToChatAll("[{lightgreen}Scramble Teams\x01] %t", "Scrambling Teams"); + ServerCommand("mp_scrambleteams"); + if (g_ConVars[restart_round].BoolValue) + { + ServerCommand("mp_restartgame 5"); + } + } + else if (yesVotes == 0 && noVotes == 0) + { + NativeVotes_DisplayFail(vote, NativeVotesFail_NotEnoughVotes); + CPrintToChatAll("[{lightgreen}Scramble Teams\x01] %t", "No Votes"); + } + else + { + NativeVotes_DisplayFail(vote, NativeVotesFail_Loses); + } +} + +int MenuHandler_Scramble(Menu menu, MenuAction action, int client, int param2) +{ + switch (action) + { + case MenuAction_Select: + { + if (param2 == 0) + { + CPrintToChatAll("[{lightgreen}Scramble Teams\x01] %t", "Scrambling Teams"); + ServerCommand("mp_scrambleteams"); + if (g_ConVars[restart_round].BoolValue) + { + ServerCommand("mp_restartgame 5"); + } + } + else + { + CPrintToChatAll("[{lightgreen}Scramble Teams\x01] %t", "No Votes"); + } + } + case MenuAction_End: + { + delete menu; + } + } + return 0; +} + +public Action Timer_DelayScramble(Handle timer) +{ + g_ScrambleAllowed = true; + return Plugin_Continue; +} + +void GetPlayerName(int client, char[] name, int maxlen) +{ + int r, g, b, a, color; + GetEntityRenderColor(client, r, g, b, a); + color = (r << 16) | (g << 8) | b; + if (color != 0xFFFFFF) + { + Format(name, maxlen, "{#%06X}%N\x01", color, client); + } + else + { + Format(name, maxlen, "{teamcolor}%N\x01", client); + } +} \ No newline at end of file diff --git a/addons/sourcemod/translations/votescramble.phrases.txt b/addons/sourcemod/translations/votescramble.phrases.txt new file mode 100644 index 0000000..4c38d7e --- /dev/null +++ b/addons/sourcemod/translations/votescramble.phrases.txt @@ -0,0 +1,39 @@ +"Phrases" +{ + "Scramble Teams" + { + "en" "Scramble Teams:" + } + + "Scramble Not Allowed" + { + "en" "Scramble is not allowed yet." + } + + "Already Voted" + { + "#format" "{1:d},{2:d}" + "en" "You have already voted to scramble teams. ({1} votes, {2} required)" + } + + "Scramble Requested" + { + "#format" "{1:s},{2:d},{3:d}" + "en" "{1} wants to scramble teams. ({2} votes, {3} required)" + } + + "Scramble Vote Ready" + { + "en" "Scrambling Teams!" + } + + "No Votes" + { + "en" "No votes received for Scramble Teams, keeping current team compositions." + } + + "Scrambling Teams" + { + "en" "Scrambling Teams! Vote has spoken!" + } +} \ No newline at end of file From 00786d7a6a01ff8aa8b8ba5c74264e0de8c7a5a3 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Wed, 4 Feb 2026 16:29:36 +0100 Subject: [PATCH 065/128] Only display "Change VIP" vote issue on VIP(R) maps. --- addons/sourcemod/scripting/nativevotes.sp | 15 +++++++-- .../sourcemod/scripting/nativevotes/game.sp | 31 +++++++------------ 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes.sp b/addons/sourcemod/scripting/nativevotes.sp index d4b5670..55b1928 100644 --- a/addons/sourcemod/scripting/nativevotes.sp +++ b/addons/sourcemod/scripting/nativevotes.sp @@ -43,6 +43,9 @@ #include EngineVersion g_EngineVersion = Engine_Unknown; +int g_AppID = 0; + +#define APP_TF2CLASSIFIED 3545060 #include "nativevotes/data-keyvalues.sp" @@ -150,7 +153,7 @@ public Plugin myinfo = name = "NativeVotes", author = "Powerlord", description = "Voting API to use the game's native vote panels. Compatible with L4D, L4D2, TF2, and CS:GO.", - version = "26w06b", + version = "26w06c", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" } @@ -162,6 +165,13 @@ public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max Format(error, err_max, "Unsupported game: %s", engineName); return APLRes_Failure; } + + KeyValues kv = new KeyValues("GameInfo"); + if (kv.ImportFromFile("gameinfo.txt") && kv.JumpToKey("FileSystem")) + { + g_AppID = kv.GetNum("SteamAppId"); + } + delete kv; CreateNative("NativeVotes_IsVoteTypeSupported", Native_IsVoteTypeSupported); CreateNative("NativeVotes_Create", Native_Create); @@ -1282,7 +1292,8 @@ void StartVoting() g_TotalClients = g_Clients; - /* By now we know how many clients were set. + /* + * By now we know how many clients were set. * If there are none, we should end IMMEDIATELY. */ if (g_Clients == 0) diff --git a/addons/sourcemod/scripting/nativevotes/game.sp b/addons/sourcemod/scripting/nativevotes/game.sp index d5cc8ca..8385f46 100644 --- a/addons/sourcemod/scripting/nativevotes/game.sp +++ b/addons/sourcemod/scripting/nativevotes/game.sp @@ -2848,12 +2848,17 @@ static void TF2_AddDefaultVotes(ArrayList hVoteTypes, bool bHideDisabledVotes) // Extend VoteTypeSet(hVoteTypes, bHideDisabledVotes, NativeVotesOverride_Extend, globalEnable && g_Cvar_VoteExtend_Enabled.BoolValue); + } - /* Team Fortress 2 Classified */ - // ChangeCivilian - VoteTypeSet(hVoteTypes, bHideDisabledVotes, NativeVotesOverride_ChgCivilian, globalEnable && g_Cvar_ChangeCivilian_Enabled.BoolValue); + switch (g_AppID) + { + case APP_TF2CLASSIFIED: + { + /* ChangeCivilian */ + int entity = FindEntityByClassname(INVALID_ENT_REFERENCE, "tf2c_logic_vip"); + VoteTypeSet(hVoteTypes, bHideDisabledVotes, NativeVotesOverride_ChgCivilian, globalEnable && g_Cvar_ChangeCivilian_Enabled.BoolValue && entity != INVALID_ENT_REFERENCE); + } } - } static void VoteTypeSet(ArrayList hVoteTypes, bool bHideDisabledVotes, NativeVotesOverride voteType, bool bEnabled) @@ -3499,26 +3504,12 @@ static stock bool TF2_OverrideTypeToTranslationString(NativeVotesOverride overri } case NativeVotesOverride_AutoBalance: { - if (GetConVarBool(g_Cvar_AutoBalance)) - { - strcopy(translationString, maxlength, TF2_VOTE_MENU_AUTOBALANCE_OFF); - } - else - { - strcopy(translationString, maxlength, TF2_VOTE_MENU_AUTOBALANCE_ON); - } + strcopy(translationString, maxlength, GetConVarBool(g_Cvar_AutoBalance) ? TF2_VOTE_MENU_AUTOBALANCE_OFF : TF2_VOTE_MENU_AUTOBALANCE_ON); valid = true; } case NativeVotesOverride_ClassLimits: { - if (GetConVarInt(g_Cvar_ClassLimit)) - { - strcopy(translationString, maxlength, TF2_VOTE_MENU_CLASSLIMIT_OFF); - } - else - { - strcopy(translationString, maxlength, TF2_VOTE_MENU_CLASSLIMIT_ON); - } + strcopy(translationString, maxlength, GetConVarInt(g_Cvar_ClassLimit) ? TF2_VOTE_MENU_CLASSLIMIT_OFF : TF2_VOTE_MENU_CLASSLIMIT_ON); valid = true; } case NativeVotesOverride_Extend: From df854257fff1a75a170cf2c0557a491ed4eeaec5 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Wed, 4 Feb 2026 16:36:25 +0100 Subject: [PATCH 066/128] sm_vote tweaks Allow multi-choice votes with only 1 or 2 options. --- .../scripting/nativevotes_basevotes.sp | 110 +++++++----------- 1 file changed, 39 insertions(+), 71 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_basevotes.sp b/addons/sourcemod/scripting/nativevotes_basevotes.sp index 54c99d1..37dfe96 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes.sp @@ -46,7 +46,7 @@ public Plugin:myinfo = name = "NativeVotes | Basic Votes", author = "Powerlord and AlliedModders LLC", description = "NativeVotes Basic Vote Commands", - version = "26w06b", + version = "26w06c", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -103,15 +103,7 @@ public void OnPluginStart() RegAdminCmd("sm_votemap", Command_Votemap, ADMFLAG_VOTE|ADMFLAG_CHANGEMAP, "sm_votemap [mapname2] ... [mapname5] "); RegAdminCmd("sm_votekick", Command_Votekick, ADMFLAG_VOTE|ADMFLAG_KICK, "sm_votekick [reason]"); RegAdminCmd("sm_voteban", Command_Voteban, ADMFLAG_VOTE|ADMFLAG_BAN, "sm_voteban [reason]"); - RegAdminCmd("sm_vote", Command_Vote, ADMFLAG_VOTE, "sm_vote [Answer1] [Answer2] ... [Answer5]"); - - /* - g_Cvar_Show = FindConVar("sm_vote_show"); - if (g_Cvar_Show == INVALID_HANDLE) - { - g_Cvar_Show = CreateConVar("sm_vote_show", "1", "Show player's votes? Default on.", 0, true, 0.0, true, 1.0); - } - */ + RegAdminCmd("sm_vote", Command_Vote, ADMFLAG_VOTE, "sm_vote [option1] [option2] ... [option5]"); g_ConVars[map] = CreateConVar("sm_vote_map", "0.60", "percent required for successful map vote.", 0, true, 0.05, true, 1.0); g_ConVars[kick] = CreateConVar("sm_vote_kick", "0.60", "percent required for successful kick vote.", 0, true, 0.05, true, 1.0); @@ -233,8 +225,8 @@ public Action Command_Vote(int client, int args) { if (args < 1) { - CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] Usage: sm_vote <question> [Answer1] [Answer2] ... [Answer5]"); - return Plugin_Handled; + CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] Usage: sm_vote <title> [option1] [option2] ... [option5]"); + return Plugin_Handled; } if (Internal_IsVoteInProgress()) @@ -252,7 +244,7 @@ public Action Command_Vote(int client, int args) GetCmdArgString(text, sizeof(text)); char answers[5][64]; - int answerCount; + int answerCount = 0; int len = BreakString(text, g_voteArg, sizeof(g_voteArg)); int pos = len; @@ -271,21 +263,18 @@ public Action Command_Vote(int client, int args) g_voteType = question; Handle voteMenu; - if (g_NativeVotes && (answerCount < 2 || NativeVotes_IsVoteTypeSupported(NativeVotesType_Custom_Mult)) ) + if (g_NativeVotes) { - NativeVotesType nVoteType = answerCount < 2 ? NativeVotesType_Custom_YesNo : NativeVotesType_Custom_Mult; - voteMenu = NativeVotes_Create(Handler_NativeVoteCallback, nVoteType, view_as<MenuAction>(MENU_ACTIONS_ALL)); - NativeVotes_SetTitle(voteMenu, g_voteArg); - - if (answerCount >= 2) - { - for (int i = 0; i < answerCount; i++) - { - NativeVotes_AddItem(voteMenu, answers[i], answers[i]); - } - } - - //NativeVotes_SetInitiator(voteMenu, client); + NativeVotesType nVoteType = answerCount == 0 ? NativeVotesType_Custom_YesNo : NativeVotesType_Custom_Mult; + if (nVoteType == NativeVotesType_Custom_Mult) + { + for (int i = 0; i < answerCount; i++) + { + NativeVotes_AddItem(voteMenu, answers[i], answers[i]); + } + } + voteMenu = NativeVotes_Create(Handler_NativeVoteCallback, nVoteType, view_as<MenuAction>(MENU_ACTIONS_ALL)); + NativeVotes_SetTitle(voteMenu, g_voteArg); NativeVotes_DisplayToAll(voteMenu, 20); } else @@ -293,16 +282,19 @@ public Action Command_Vote(int client, int args) voteMenu = CreateMenu(Handler_VoteCallback, view_as<MenuAction>(MENU_ACTIONS_ALL)); SetMenuTitle(voteMenu, "%s?", g_voteArg); - if (answerCount < 2) + switch (answerCount) { - AddMenuItem(voteMenu, VOTE_YES, "Yes"); - AddMenuItem(voteMenu, VOTE_NO, "No"); - } - else - { - for (int i = 0; i < answerCount; i++) + case 0: + { + AddMenuItem(voteMenu, VOTE_YES, "Yes"); + AddMenuItem(voteMenu, VOTE_NO, "No"); + } + default: { - AddMenuItem(voteMenu, answers[i], answers[i]); + for (int i = 0; i < answerCount; i++) + { + AddMenuItem(voteMenu, answers[i], answers[i]); + } } } @@ -318,39 +310,35 @@ public Handler_VoteCallback(Handle:menu, MenuAction:action, param1, param2) { case MenuAction_End: { - //VoteMenuClose(); CloseHandle(menu); } - case MenuAction_Display: { if (g_voteType != voteType:question) { - decl String:title[64]; + char title[64]; GetMenuTitle(menu, title, sizeof(title)); - decl String:buffer[255]; + char buffer[255]; Format(buffer, sizeof(buffer), "%T", title, param1, g_voteInfo[VOTE_NAME]); Handle panel = Handle:param2; SetPanelTitle(panel, buffer); } } - case MenuAction_DisplayItem: { - decl String:display[64]; + char display[64]; GetMenuItem(menu, param2, "", 0, _, display, sizeof(display)); if (strcmp(display, "No") == 0 || strcmp(display, "Yes") == 0) { - decl String:buffer[255]; + char buffer[255]; Format(buffer, sizeof(buffer), "%T", display, param1); return RedrawMenuItem(buffer); } } - case MenuAction_VoteCancel: { if (param1 == VoteCancel_NoVotes) @@ -358,11 +346,11 @@ public Handler_VoteCallback(Handle:menu, MenuAction:action, param1, param2) CPrintToChatAll("%t", "No Votes Cast"); } } - case MenuAction_VoteEnd: { - decl String:item[64], String:display[64]; - new Float:percent, Float:limit, votes, totalVotes; + char item[64], display[64]; + float percent, limit; + int votes, totalVotes; GetMenuVoteInfo(param2, votes, totalVotes); GetMenuItem(menu, param1, item, sizeof(item), _, display, sizeof(display)); @@ -493,10 +481,10 @@ public Handler_NativeVoteCallback(Handle:menu, MenuAction:action, param1, param2 new NativeVotesType:nVoteType = NativeVotes_GetType(menu); if (g_voteType != voteType:question && (nVoteType == NativeVotesType_Custom_YesNo || nVoteType == NativeVotesType_Custom_Mult)) { - decl String:title[64]; + char title[64]; NativeVotes_GetTitle(menu, title, sizeof(title)); - decl String:buffer[255]; + char buffer[255]; Format(buffer, sizeof(buffer), "%T", title, param1, g_voteInfo[VOTE_NAME]); return _:NativeVotes_RedrawVoteTitle(buffer); @@ -518,8 +506,9 @@ public Handler_NativeVoteCallback(Handle:menu, MenuAction:action, param1, param2 case MenuAction_VoteEnd: { - decl String:item[64], String:display[64]; - new Float:percent, Float:limit, votes, totalVotes; + char item[64], display[64]; + float percent, limit; + int votes, totalVotes;; new NativeVotesType:nVoteType = NativeVotes_GetType(menu); @@ -639,27 +628,6 @@ public Handler_NativeVoteCallback(Handle:menu, MenuAction:action, param1, param2 return Plugin_Continue; } -/* -VoteSelect(Handle:menu, param1, param2 = 0) -{ - if (GetConVarInt(g_Cvar_VoteShow) == 1) - { - decl String:voter[64], String:junk[64], String:choice[64]; - GetClientName(param1, voter, sizeof(voter)); - GetMenuItem(menu, param2, junk, sizeof(junk), _, choice, sizeof(choice)); - CPrintToChatAll("%t", "Vote Select", LANG_SERVER, voter, choice); - } -} -*/ - -/* -VoteMenuClose() -{ - CloseHandle(g_hVoteMenu); - g_hVoteMenu = INVALID_HANDLE; -} -*/ - float GetVotePercent(int votes, int totalVotes) { return float(votes) / float(totalVotes); From 903cbcb941c271ae7ac0c3664a649d72b64d0039 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Wed, 4 Feb 2026 16:36:40 +0100 Subject: [PATCH 067/128] Make NativeVotes optional --- .../sourcemod/scripting/nativevotes_voterp.sp | 77 ++++++++++++++++--- 1 file changed, 68 insertions(+), 9 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_voterp.sp b/addons/sourcemod/scripting/nativevotes_voterp.sp index 4671b7b..a4ccc51 100644 --- a/addons/sourcemod/scripting/nativevotes_voterp.sp +++ b/addons/sourcemod/scripting/nativevotes_voterp.sp @@ -1,6 +1,10 @@ #include <sourcemod> +#include <multicolors> #include <sdktools> + +#undef REQUIRE_PLUGIN #include <nativevotes> +#define REQUIRE_PLUGIN #pragma semicolon 1 #pragma newdecls required @@ -10,7 +14,7 @@ public Plugin myinfo = name = "NativeVotes | Medieval Auto-RP", author = "Heapons", description = "Provides Medieval Auto-RP voting.", - version = "26w06b", + version = "26w06c", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -25,6 +29,8 @@ enum ConVar g_ConVars[MAX_CONVARS]; +bool g_NativeVotes; + public void OnPluginStart() { g_ConVars[tf_medieval] = FindConVar("tf_medieval"); @@ -39,6 +45,48 @@ public void OnPluginStart() RegConsoleCmd("sm_voterp", Command_VoteRP, "Vote to toggle 'tf_medieval_autorp'."); } +public void OnAllPluginsLoaded() +{ + g_NativeVotes = LibraryExists("nativevotes"); +} + +public void OnLibraryAdded(const char[] name) +{ + if (StrEqual(name, "nativevotes", false)) + { + g_NativeVotes = true; + } +} + +public void OnLibraryRemoved(const char[] name) +{ + if (StrEqual(name, "nativevotes", false)) + { + g_NativeVotes = false; + } +} + +int MenuHandler_VoteRP(Menu menu, MenuAction action, int param1, int param2) +{ + switch (action) + { + case MenuAction_VoteEnd: + { + int item = param1; + char info[8]; + menu.GetItem(item, info, sizeof(info)); + int value = StringToInt(info); + g_ConVars[tf_medieval_autorp].SetInt(value); + CPrintToChatAll("[{lightgreen}NativeVotes\x01] tf_medieval_autorp set to %d", value); + } + case MenuAction_End: + { + delete menu; + } + } + return 0; +} + Action Command_VoteRP(int client, int args) { if (!client || !IsClientInGame(client)) @@ -46,7 +94,7 @@ Action Command_VoteRP(int client, int args) return Plugin_Handled; } - if (NativeVotes_IsVoteInProgress()) + if ((g_NativeVotes && NativeVotes_IsVoteInProgress()) || (!g_NativeVotes && IsVoteInProgress())) { CPrintToChat(client, "[{lightgreen}NativeVotes\x01] A vote is already in progress."); return Plugin_Handled; @@ -63,13 +111,24 @@ Action Command_VoteRP(int client, int args) char title[64]; Format(title, sizeof(title), "Turn Medieval Auto-RP %s?", enabled ? "off" : "on"); - NativeVote vote = NativeVotes_Create(VoteHandler, NativeVotesType_Custom_YesNo, MENU_ACTIONS_ALL); - - NativeVotes_SetTitle(vote, title); - NativeVotes_SetInitiator(vote, client); - NativeVotes_AddItem(vote, enabled ? "0" : "1", enabled ? "Disable" : "Enable"); - NativeVotes_AddItem(vote, enabled ? "1" : "0", enabled ? "Enable" : "Disable"); - NativeVotes_DisplayToAll(vote, g_ConVars[vote_duration].IntValue); + if (g_NativeVotes) + { + NativeVote vote = NativeVotes_Create(VoteHandler, NativeVotesType_Custom_YesNo, MENU_ACTIONS_ALL); + NativeVotes_SetTitle(vote, title); + NativeVotes_SetInitiator(vote, client); + NativeVotes_AddItem(vote, enabled ? "0" : "1", enabled ? "Disable" : "Enable"); + NativeVotes_AddItem(vote, enabled ? "1" : "0", enabled ? "Enable" : "Disable"); + NativeVotes_DisplayToAll(vote, g_ConVars[vote_duration].IntValue); + } + else + { + Menu menu = new Menu(MenuHandler_VoteRP); + menu.SetTitle(title); + menu.AddItem(enabled ? "0" : "1", enabled ? "Disable" : "Enable"); + menu.AddItem(enabled ? "1" : "0", enabled ? "Enable" : "Disable"); + menu.ExitButton = false; + menu.DisplayVoteToAll(g_ConVars[vote_duration].IntValue); + } return Plugin_Handled; } From 1aa17c379405c4b9cf17752906d99061d3414938 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Wed, 4 Feb 2026 16:38:15 +0100 Subject: [PATCH 068/128] Fix Arrays --- .../scripting/nativevotes_mapchooser.sp | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_mapchooser.sp b/addons/sourcemod/scripting/nativevotes_mapchooser.sp index cb36237..9de3fc6 100644 --- a/addons/sourcemod/scripting/nativevotes_mapchooser.sp +++ b/addons/sourcemod/scripting/nativevotes_mapchooser.sp @@ -55,7 +55,7 @@ public Plugin myinfo = name = "NativeVotes | MapChooser", author = "AlliedModders LLC and Powerlord", description = "Automated Map Voting", - version = "26w06b", + version = "26w06c", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -698,7 +698,7 @@ void InitiateVote(MapChange when, ArrayList inputlist=null) char map[PLATFORM_MAX_PATH]; - /* No input given - User our internal nominations and maplist */ + /* No input given - Use our internal nominations and maplist */ if (inputlist == null) { int nominateCount = g_NominateList.Length; @@ -707,17 +707,26 @@ void InitiateVote(MapChange when, ArrayList inputlist=null) // New in 1.5.1 to fix missing extend vote if (g_NativeVotes) { - if (voteSize > NativeVotes_GetMaxItems()) + int maxItems = NativeVotes_GetMaxItems(); + if (maxItems < 1) { - voteSize = NativeVotes_GetMaxItems(); + voteSize = 1; } - + else if (voteSize > maxItems) + { + voteSize = maxItems; + } + if (g_ConVars[mapvote_extend].IntValue && g_Extends < g_ConVars[mapvote_extend].IntValue) { voteSize--; } + + if (voteSize < 1) + { + voteSize = 1; + } } - /* Smaller of the two - It should be impossible for nominations to exceed the size though (cvar changed mid-map?) */ int nominationsToAdd = nominateCount >= voteSize ? voteSize : nominateCount; From 03eaa90d22182e08b3506004e4c9969be99114a5 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Wed, 4 Feb 2026 16:38:30 +0100 Subject: [PATCH 069/128] Bump version --- .../scripting/nativevotes_basecommands.sp | 2 +- .../scripting/nativevotes_funvotes.sp | 2 +- .../nativevotes_kickvote_immunity.sp | 2 +- .../scripting/nativevotes_nominations.sp | 2 +- .../scripting/nativevotes_rockthevote.sp | 26 ++++++++++++------- 5 files changed, 20 insertions(+), 14 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_basecommands.sp b/addons/sourcemod/scripting/nativevotes_basecommands.sp index 4b10482..bc1cdce 100644 --- a/addons/sourcemod/scripting/nativevotes_basecommands.sp +++ b/addons/sourcemod/scripting/nativevotes_basecommands.sp @@ -48,7 +48,7 @@ public Plugin myinfo = name = "NativeVotes | Basic Commands", author = "Powerlord and AlliedModders LLC", description = "Revote and Cancel support for NativeVotes", - version = "26w06b", + version = "26w06c", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" } diff --git a/addons/sourcemod/scripting/nativevotes_funvotes.sp b/addons/sourcemod/scripting/nativevotes_funvotes.sp index a3bd09c..120649f 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes.sp @@ -47,7 +47,7 @@ public Plugin myinfo = name = "NativeVotes | Fun Votes", author = "Powerlord and AlliedModders LLC", description = "NativeVotes Fun Vote Commands", - version = "26w06b", + version = "26w06c", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp b/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp index aa6b24a..2620218 100644 --- a/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp +++ b/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp @@ -60,7 +60,7 @@ public Plugin myinfo = { name = "NativeVotes | Kick Vote Immunity", author = "Powerlord", description = "Causes TF2 kick votes to fail against people who the current user can't target.", - version = "26w06b", + version = "26w06c", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_nominations.sp b/addons/sourcemod/scripting/nativevotes_nominations.sp index b9ff2e2..b5565d9 100644 --- a/addons/sourcemod/scripting/nativevotes_nominations.sp +++ b/addons/sourcemod/scripting/nativevotes_nominations.sp @@ -48,7 +48,7 @@ public Plugin myinfo = name = "NativeVotes | Map Nominations", author = "AlliedModders LLC and Powerlord", description = "Provides Map Nominations", - version = "26w06b", + version = "26w06c", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_rockthevote.sp b/addons/sourcemod/scripting/nativevotes_rockthevote.sp index d6757c7..b9b222b 100644 --- a/addons/sourcemod/scripting/nativevotes_rockthevote.sp +++ b/addons/sourcemod/scripting/nativevotes_rockthevote.sp @@ -55,7 +55,7 @@ public Plugin myinfo = name = "NativeVotes | Rock The Vote", author = "AlliedModders LLC and Powerlord", description = "Provides RTV Map Voting", - version = "26w06b", + version = "26w06c", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -304,22 +304,28 @@ void UndoRTV(int client) void MenuHandler_UndoRTV(Menu menu, MenuAction action, int client, int param2) { - if (action == MenuAction_Select) + switch (action) { - if (param2 == 0) // Yes + case MenuAction_Select: { - if (g_Voted[client]) + if (param2 == 0) // Yes { - char name[MAX_NAME_LENGTH]; - GetPlayerName(client, name, sizeof(name)); + if (g_Voted[client]) + { + char name[MAX_NAME_LENGTH]; + GetPlayerName(client, name, sizeof(name)); - g_Voted[client] = false; - if (g_Votes > 0) g_Votes--; - CPrintToChatAllEx(client, "[{lightgreen}Rock The Vote\x01] %s: %t", name, "Cancelled Vote"); + g_Voted[client] = false; + if (g_Votes > 0) g_Votes--; + CPrintToChatAllEx(client, "[{lightgreen}Rock The Vote\x01] %s: %t", name, "Cancelled Vote"); + } } } + case MenuAction_End: + { + delete menu; + } } - delete menu; } void AttemptRTV(int client, bool isVoteMenu=false) From a9608387b33437054b3fe9d258d541b6b1ca5b7a Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Wed, 4 Feb 2026 16:47:17 +0100 Subject: [PATCH 070/128] Update README.md --- README.md | 30 ++++++++++++++++++- .../scripting/nativevotes_basevotes.sp | 2 +- .../scripting/nativevotes_votescramble.sp | 6 ---- 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 9799c9b..4fa8a27 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ This fork aims to expand upon [<img src="https://avatars.githubusercontent.com/u - Added `sm_callvote` command and `callvote` chat trigger to open **Vote Setup**. ## [Rock The Vote](https://github.com/Heapons/sourcemod-nativevotes-updated/blob/master/addons/sourcemod/scripting/nativevotes_rockthevote.sp) -- Admin commands. +- Admin commands: - `sm_forcertv`. - Force a RTV. - `sm_resetrtv`. @@ -85,3 +85,31 @@ This fork aims to expand upon [<img src="https://avatars.githubusercontent.com/u |`sm_mapcycle_exclude`|`.*test.*\|background01\|^tr.*$`|Specifies which maps shouldn't be automatically added (regex pattern).| |<img src="https://cdn.fastly.steamstatic.com/steamcommunity/public/images/apps/440/033bdd91842b6aca0633ee1e5f3e6b82f2e8962f.ico" width="16" height="16" style="vertical-align: text-bottom;"> `sm_workshop_map_collection`|` `|Specifies the workshop collection to fetch the maps from.| |<img src="https://cdn.fastly.steamstatic.com/steamcommunity/public/images/apps/440/033bdd91842b6aca0633ee1e5f3e6b82f2e8962f.ico" width="16" height="16" style="vertical-align: text-bottom;"> `sm_workshop_map_cleanup`|`0`|Specifies whether to automatically cleanup workshop maps on map change| + +## [Scramble Teams](https://github.com/Heapons/sourcemod-nativevotes-updated/blob/master/addons/sourcemod/scripting/nativevotes_votescramble.sp) +> This is a module that makes Scramble Team votes behave like Rock The Vote. + +- Player commands: + - `sm_votescramble` (alias: `sm_scramble`). + - Attempt and request a Scramble Teams vote. +- Admin commands: + - `sm_forcescramble`. + - Force a scramble teams vote. + - `sm_resetscramble`. + - Reset scramble teams counts. +- Allow players to retract their scramble teams vote. + - Execute `sm_votescramble` or `sm_scramble` again to undo. + +> [!NOTE] +> It overrides built-in **Scramble Teams** vote issue. + +--- +|Name|Default Value|Description| +|-|-|-| +|`sm_scrambleteams_needed`|`0.60`|Percentage of players needed to scramble teams| +|`sm_scrambleteams_minplayers`|`0`|Number of players required before scramble will be enabled| +|`sm_scrambleteams_initialdelay`|`30.0`|Time (in seconds) before first scramble can be held| +|`sm_scrambleteams_interval`|`240.0`|Time (in seconds) after a failed scramble before another can be held| +|`sm_scrambleteams_restart_round`|`0`|Whether to restart the round after scrambling teams| +|`sm_scrambleteams_restart_timelimit`|`0`|Whether to restart the timelimit after scrambling teams| +|`sm_scrambleteams_enforce_timelimit`|`0`|Whether to end the game as soon as the timelimit runs out| diff --git a/addons/sourcemod/scripting/nativevotes_basevotes.sp b/addons/sourcemod/scripting/nativevotes_basevotes.sp index 37dfe96..4a02057 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes.sp @@ -508,7 +508,7 @@ public Handler_NativeVoteCallback(Handle:menu, MenuAction:action, param1, param2 { char item[64], display[64]; float percent, limit; - int votes, totalVotes;; + int votes, totalVotes; new NativeVotesType:nVoteType = NativeVotes_GetType(menu); diff --git a/addons/sourcemod/scripting/nativevotes_votescramble.sp b/addons/sourcemod/scripting/nativevotes_votescramble.sp index 3442bbc..e7b746e 100644 --- a/addons/sourcemod/scripting/nativevotes_votescramble.sp +++ b/addons/sourcemod/scripting/nativevotes_votescramble.sp @@ -59,12 +59,6 @@ public void OnPluginStart() RegAdminCmd("sm_forcescramble", Command_ForceScramble, ADMFLAG_CHANGEMAP); RegAdminCmd("sm_resetscramble", Command_ResetScramble, ADMFLAG_CHANGEMAP); - int flags = GetCommandFlags("mp_scrambleteams"); - if (flags & FCVAR_CHEAT) - { - SetCommandFlags("mp_scrambleteams", flags & ~FCVAR_CHEAT); - } - AutoExecConfig(true, "votescramble"); OnMapEnd(); From 340de35f6e92356533765aa907a9805df598a1f3 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Wed, 4 Feb 2026 17:11:10 +0100 Subject: [PATCH 071/128] Update README.md --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 4fa8a27..58fed25 100644 --- a/README.md +++ b/README.md @@ -57,8 +57,6 @@ This fork aims to expand upon [<img src="https://avatars.githubusercontent.com/u |`sm_nominate_maxfound`|`0`|Maximum number of nomination matches to add to the menu.<br>0 = infinite| ## [MapChooser](https://github.com/Heapons/sourcemod-nativevotes-updated/blob/master/addons/sourcemod/scripting/nativevotes_mapchooser.sp) -- Automatically generate mapcycle files. - - Can be refreshed with `sm_reloadmaplist`. - <img src="https://cdn.fastly.steamstatic.com/steamcommunity/public/images/apps/440/033bdd91842b6aca0633ee1e5f3e6b82f2e8962f.ico" width="16" height="16" style="vertical-align: text-bottom;"> Clean up workshop maps to reduce disk size. - Automatically generate the mapcycle file. - <img src="https://cdn.fastly.steamstatic.com/steamcommunity/public/images/apps/440/033bdd91842b6aca0633ee1e5f3e6b82f2e8962f.ico" width="16" height="16" style="vertical-align: text-bottom;"> Import Workshop map collections (requires [Rest In Pawn](https://github.com/srcdslab/sm-ext-ripext) in order to use that feature). From f72889e75bfd0baae4d79d013d2b0c8d16cad7f1 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Wed, 4 Feb 2026 17:47:26 +0100 Subject: [PATCH 072/128] Minor tweaks --- .../scripting/nativevotes_mapchooser.sp | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_mapchooser.sp b/addons/sourcemod/scripting/nativevotes_mapchooser.sp index 9de3fc6..1227d0a 100644 --- a/addons/sourcemod/scripting/nativevotes_mapchooser.sp +++ b/addons/sourcemod/scripting/nativevotes_mapchooser.sp @@ -55,7 +55,7 @@ public Plugin myinfo = name = "NativeVotes | MapChooser", author = "AlliedModders LLC and Powerlord", description = "Automated Map Voting", - version = "26w06c", + version = "26w06d", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -393,7 +393,7 @@ public Action Command_SetNextMap(int client, int args) { if (args < 1) { - CReplyToCommand(client, "[{lightgreen}MapChooser\x01] Usage: sm_setnextmap <map>"); + CReplyToCommand(client, "[{olive}MapChooser\x01] Usage: sm_setnextmap <map>"); return Plugin_Handled; } @@ -402,15 +402,18 @@ public Action Command_SetNextMap(int client, int args) if (FindMap(map, displayName, sizeof(displayName)) == FindMap_NotFound) { - CReplyToCommand(client, "[{lightgreen}MapChooser\x01] %t", "Map was not found", map); + CReplyToCommand(client, "[{olive}MapChooser\x01] %t", "Map was not found", map); return Plugin_Handled; } GetMapDisplayName(displayName, displayName, sizeof(displayName)); Format(displayName, sizeof(displayName), "\x05%s\x01", displayName); - CShowActivity(client, "%t", "Changed Next Map", displayName); - LogAction(client, -1, "\"%L\" changed nextmap to \"%s\"", client, map); + CShowActivity2(client, "[{olive}MapChooser\x01] ", "%t", "Changed Next Map", displayName); + if (client > 0) + { + LogAction(client, -1, "\"%L\" changed nextmap to \"%s\"", client, map); + } SetNextMap(map); g_MapVoteCompleted = true; @@ -877,7 +880,7 @@ void InitiateVote(MapChange when, ArrayList inputlist=null) } LogAction(-1, -1, "Voting for next map has started."); - CPrintToChatAll("[{lightgreen}MapChooser\x01] %t", "Nextmap Voting Started"); + CPrintToChatAll("[{olive}MapChooser\x01] %t", "Nextmap Voting Started"); } public void Handler_NV_VoteFinishedGeneric(NativeVote menu, int num_votes, int num_clients, const int[] client_indexes, const int[] client_votes, int num_items, const int[] item_indexes, const int[] item_votes) @@ -945,7 +948,7 @@ public void Handler_VoteFinishedGenericShared(const char[] map, const char[] dis } } - CPrintToChatAll("[{lightgreen}MapChooser\x01] %t", "Current Map Extended", RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes); + CPrintToChatAll("[{olive}MapChooser\x01] %t", "Current Map Extended", RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes); LogAction(-1, -1, "Voting for next map has finished. The current map has been extended."); if (isNativeVotes) @@ -961,7 +964,7 @@ public void Handler_VoteFinishedGenericShared(const char[] map, const char[] dis } else if (strcmp(map, VOTE_DONTCHANGE, false) == 0) { - CPrintToChatAll("[{lightgreen}MapChooser\x01] %t", "Current Map Stays", RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes); + CPrintToChatAll("[{olive}MapChooser\x01] %t", "Current Map Stays", RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes); LogAction(-1, -1, "Voting for next map has finished. 'No Change' was the winner"); if (isNativeVotes) @@ -1002,7 +1005,7 @@ public void Handler_VoteFinishedGenericShared(const char[] map, const char[] dis char formattedName[PLATFORM_MAX_PATH]; Format(formattedName, sizeof(formattedName), "\x05%s\x01", displayName); - CPrintToChatAll("[{lightgreen}MapChooser\x01] %t", "Nextmap Voting Finished", formattedName, RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes); + CPrintToChatAll("[{olive}MapChooser\x01] %t", "Nextmap Voting Finished", formattedName, RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes); LogAction(-1, -1, "Voting for next map has finished. Nextmap: %s.", map); } } @@ -1045,7 +1048,7 @@ public void Handler_NV_MapVoteFinished(NativeVote menu, int num_votes, int num_c float map2percent = float(item_votes[1])/ float(num_votes) * 100; - CPrintToChatAll("[{lightgreen}MapChooser\x01] %t", "Starting Runoff", g_ConVars[mapvote_runoffpercent].FloatValue, info1, map1percent, info2, map2percent); + CPrintToChatAll("[{olive}MapChooser\x01] %t", "Starting Runoff", g_ConVars[mapvote_runoffpercent].FloatValue, info1, map1percent, info2, map2percent); LogMessage("Voting for next map was indecisive, beginning runoff vote"); return; @@ -1106,7 +1109,7 @@ public void Handler_MapVoteFinished(Menu menu, int num_votes, int num_clients, c float map1percent = float(item_info[0][VOTEINFO_ITEM_VOTES])/ float(num_votes) * 100; float map2percent = float(item_info[1][VOTEINFO_ITEM_VOTES])/ float(num_votes) * 100; - CPrintToChatAll("[{lightgreen}MapChooser\x01] %t", "Starting Runoff", g_ConVars[mapvote_runoffpercent].FloatValue, info1, map1percent, info2, map2percent); + CPrintToChatAll("[{olive}MapChooser\x01] %t", "Starting Runoff", g_ConVars[mapvote_runoffpercent].FloatValue, info1, map1percent, info2, map2percent); LogMessage("Voting for next map was indecisive, beginning runoff vote"); return; From 66ee3054f1bb37e46eb81add2d5e3f3f4c1b2157 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Wed, 4 Feb 2026 17:47:34 +0100 Subject: [PATCH 073/128] Bump version --- .../scripting/nativevotes_basecommands.sp | 6 ++-- .../scripting/nativevotes_basevotes.sp | 4 +-- .../nativevotes_basevotes/voteban.sp | 10 +++---- .../nativevotes_basevotes/votekick.sp | 8 ++--- .../nativevotes_basevotes/votemap.sp | 10 +++---- .../nativevotes_funvotes/votealltalk.sp | 6 ++-- .../nativevotes_funvotes/voteburn.sp | 14 ++++----- .../scripting/nativevotes_funvotes/voteff.sp | 6 ++-- .../nativevotes_funvotes/votegravity.sp | 8 ++--- .../nativevotes_funvotes/voteslay.sp | 8 ++--- .../scripting/nativevotes_nominations.sp | 30 +++++++++---------- .../scripting/nativevotes_rockthevote.sp | 14 ++++----- .../sourcemod/scripting/nativevotes_voterp.sp | 6 ++-- .../scripting/nativevotes_votescramble.sp | 20 ++++++------- 14 files changed, 75 insertions(+), 75 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_basecommands.sp b/addons/sourcemod/scripting/nativevotes_basecommands.sp index bc1cdce..b64fb8b 100644 --- a/addons/sourcemod/scripting/nativevotes_basecommands.sp +++ b/addons/sourcemod/scripting/nativevotes_basecommands.sp @@ -69,7 +69,7 @@ bool PerformCancelVote(int client) return false; } - CShowActivity2(client, "[{lightgreen}NativeVotes\x01] ", "%t", "Cancelled Vote"); + CShowActivity2(client, "[{olive}NativeVotes\x01] ", "%t", "Cancelled Vote"); NativeVotes_Cancel(); return true; @@ -163,7 +163,7 @@ public Action Command_ReVote(int client, const char[] command, int argc) return Plugin_Continue; } - CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Cannot participate in vote"); + CReplyToCommand(client, "[{olive}NativeVotes\x01] %t", "Cannot participate in vote"); return Plugin_Stop; } @@ -173,7 +173,7 @@ public Action Command_ReVote(int client, const char[] command, int argc) } else if (!IsVoteInProgress()) { - CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Cannot change vote"); + CReplyToCommand(client, "[{olive}NativeVotes\x01] %t", "Cannot change vote"); return Plugin_Stop; } diff --git a/addons/sourcemod/scripting/nativevotes_basevotes.sp b/addons/sourcemod/scripting/nativevotes_basevotes.sp index 4a02057..95a930f 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes.sp @@ -225,7 +225,7 @@ public Action Command_Vote(int client, int args) { if (args < 1) { - CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] Usage: sm_vote <title> [option1] [option2] ... [option5]"); + CReplyToCommand(client, "[{olive}NativeVotes\x01] Usage: sm_vote <title> [option1] [option2] ... [option5]"); return Plugin_Handled; } @@ -259,7 +259,7 @@ public Action Command_Vote(int client, int args) } } LogAction(client, -1, "\"%L\" initiated a generic vote.", client); - CShowActivity2(client, "[{lightgreen}NativeVotes\x01] ", "%t", "Initiate Vote", g_voteArg); + CShowActivity2(client, "[{olive}NativeVotes\x01] ", "%t", "Initiate Vote", g_voteArg); g_voteType = question; Handle voteMenu; diff --git a/addons/sourcemod/scripting/nativevotes_basevotes/voteban.sp b/addons/sourcemod/scripting/nativevotes_basevotes/voteban.sp index b5451ec..1b593fb 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes/voteban.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes/voteban.sp @@ -44,7 +44,7 @@ void DisplayVoteBanMenu(int client, int target) GetClientIP(target, g_voteInfo[VOTE_IP], sizeof(g_voteInfo[])); LogAction(client, target, "\"%L\" initiated a ban vote against %s", client, playerName); - CShowActivity2(client, "[{lightgreen}NativeVotes\x01] ", "%t", "Initiated Vote Ban", playerName); + CShowActivity2(client, "[{olive}NativeVotes\x01] ", "%t", "Initiated Vote Ban", playerName); g_voteType = ban; @@ -117,11 +117,11 @@ public int MenuHandler_Ban(Handle menu, MenuAction action, int param1, int param userid = StringToInt(info); if ((target = GetClientOfUserId(userid)) == 0) { - CPrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Player no longer available"); + CPrintToChat(param1, "[{olive}NativeVotes\x01] %t", "Player no longer available"); } else if (!CanUserTarget(param1, target)) { - CPrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Unable to target"); + CPrintToChat(param1, "[{olive}NativeVotes\x01] %t", "Unable to target"); } else { @@ -138,13 +138,13 @@ public Action Command_Voteban(int client, int args) { if (args < 1) { - CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] Usage: sm_voteban <player> [reason]"); + CReplyToCommand(client, "[{olive}NativeVotes\x01] Usage: sm_voteban <player> [reason]"); return Plugin_Handled; } if (Internal_IsVoteInProgress()) { - CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Vote in Progress"); + CReplyToCommand(client, "[{olive}NativeVotes\x01] %t", "Vote in Progress"); return Plugin_Handled; } diff --git a/addons/sourcemod/scripting/nativevotes_basevotes/votekick.sp b/addons/sourcemod/scripting/nativevotes_basevotes/votekick.sp index cf7b210..b55616f 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes/votekick.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes/votekick.sp @@ -116,11 +116,11 @@ public int MenuHandler_Kick(Handle menu, MenuAction action, int param1, int para userid = StringToInt(info); if ((target = GetClientOfUserId(userid)) == 0) { - CPrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Player no longer available"); + CPrintToChat(param1, "[{olive}NativeVotes\x01] %t", "Player no longer available"); } else if (!CanUserTarget(param1, target)) { - CPrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Unable to target"); + CPrintToChat(param1, "[{olive}NativeVotes\x01] %t", "Unable to target"); } else { @@ -137,12 +137,12 @@ public Action Command_Votekick(int client, int args) { if (args < 1) { - CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] Usage: sm_votekick <player> [reason]"); + CReplyToCommand(client, "[{olive}NativeVotes\x01] Usage: sm_votekick <player> [reason]"); return Plugin_Handled; } if (Internal_IsVoteInProgress()) { - CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Vote in Progress"); + CReplyToCommand(client, "[{olive}NativeVotes\x01] %t", "Vote in Progress"); return Plugin_Handled; } if (!TestVoteDelay(client)) diff --git a/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp b/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp index 6fc77d2..e1db37b 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp @@ -41,7 +41,7 @@ bool g_VoteMapInUse; void DisplayVoteMapMenu(int client, int mapCount, char maps[5][PLATFORM_MAX_PATH]) { LogAction(client, -1, "\"%L\" initiated a map vote.", client); - CShowActivity2(client, "[{lightgreen}NativeVotes\x01] ", "%t", "Initiated Vote Map"); + CShowActivity2(client, "[{olive}NativeVotes\x01] ", "%t", "Initiated Vote Map"); g_voteType = map; @@ -234,7 +234,7 @@ public void AdminMenu_VoteMap(Handle topmenu, TopMenuAction action, TopMenuObjec } else { - CPrintToChat(param, "[{lightgreen}NativeVotes\x01] %T", "Map Vote In Use", param); + CPrintToChat(param, "[{olive}NativeVotes\x01] %T", "Map Vote In Use", param); } } case TopMenuAction_DrawOption: @@ -249,13 +249,13 @@ public Action Command_Votemap(int client, int args) { if (args < 1) { - CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] Usage: sm_votemap <mapname> [mapname2] ... [mapname5]"); + CReplyToCommand(client, "[{olive}NativeVotes\x01] Usage: sm_votemap <mapname> [mapname2] ... [mapname5]"); return Plugin_Handled; } if (Internal_IsVoteInProgress()) { - CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Vote in Progress"); + CReplyToCommand(client, "[{olive}NativeVotes\x01] %t", "Vote in Progress"); return Plugin_Handled; } @@ -277,7 +277,7 @@ public Action Command_Votemap(int client, int args) if (!IsMapValid(maps[mapCount])) { - CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Map was not found", maps[mapCount]); + CReplyToCommand(client, "[{olive}NativeVotes\x01] %t", "Map was not found", maps[mapCount]); return Plugin_Handled; } diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp b/addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp index 39ba6c2..07e3a8d 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp @@ -36,7 +36,7 @@ void DisplayVoteAllTalkMenu(int client) { if (Internal_IsVoteInProgress()) { - CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Vote in Progress"); + CReplyToCommand(client, "[{olive}NativeVotes\x01] %t", "Vote in Progress"); return; } @@ -46,7 +46,7 @@ void DisplayVoteAllTalkMenu(int client) } LogAction(client, -1, "\"%L\" initiated an alltalk vote.", client); - CShowActivity2(client, "[{lightgreen}NativeVotes\x01] ", "%t", "Initiated Vote Alltalk"); + CShowActivity2(client, "[{olive}NativeVotes\x01] ", "%t", "Initiated Vote Alltalk"); g_voteType = voteType:alltalk; g_voteInfo[VOTE_NAME][0] = '\0'; @@ -128,7 +128,7 @@ public Action Command_VoteAlltalk(int client, int args) { if (args > 0) { - CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] Usage: sm_votealltalk"); + CReplyToCommand(client, "[{olive}NativeVotes\x01] Usage: sm_votealltalk"); return Plugin_Handled; } diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp b/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp index 6b9fdbb..28458c5 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp @@ -36,7 +36,7 @@ void DisplayVoteBurnMenu(int client, int target, char name[MAX_NAME_LENGTH]) { if (!IsPlayerAlive(target)) { - CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Cannot be performed on dead", name); + CReplyToCommand(client, "[{olive}NativeVotes\x01] %t", "Cannot be performed on dead", name); return; } @@ -48,7 +48,7 @@ void DisplayVoteBurnMenu(int client, int target, char name[MAX_NAME_LENGTH]) GetClientName(target, g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[])); LogAction(client, target, "\"%L\" initiated a burn vote against %N", client, target); - CShowActivity2(client, "[{lightgreen}NativeVotes\x01] ", "%t", "Initiated Vote Burn", playerName); + CShowActivity2(client, "[{olive}NativeVotes\x01] ", "%t", "Initiated Vote Burn", playerName); g_voteType = burn; @@ -125,15 +125,15 @@ public int MenuHandler_Burn(Handle menu, MenuAction action, int param1, int para userid = StringToInt(info); if ((target = GetClientOfUserId(userid)) == 0) { - CPrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Player no longer available"); + CPrintToChat(param1, "[{olive}NativeVotes\x01] %t", "Player no longer available"); } else if (!CanUserTarget(param1, target)) { - CPrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Unable to target"); + CPrintToChat(param1, "[{olive}NativeVotes\x01] %t", "Unable to target"); } else if (!IsPlayerAlive(target)) { - CPrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Player has since died"); + CPrintToChat(param1, "[{olive}NativeVotes\x01] %t", "Player has since died"); } else { @@ -149,13 +149,13 @@ public Action Command_VoteBurn(int client, int args) { if (args < 1) { - CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] Usage: sm_voteburn <player>"); + CReplyToCommand(client, "[{olive}NativeVotes\x01] Usage: sm_voteburn <player>"); return Plugin_Handled; } if (Internal_IsVoteInProgress()) { - CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Vote in Progress"); + CReplyToCommand(client, "[{olive}NativeVotes\x01] %t", "Vote in Progress"); return Plugin_Handled; } diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp b/addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp index a081290..c50ff3f 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp @@ -36,7 +36,7 @@ void DisplayVoteFFMenu(int client) { if (Internal_IsVoteInProgress()) { - CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Vote in Progress"); + CReplyToCommand(client, "[{olive}NativeVotes\x01] %t", "Vote in Progress"); return; } @@ -46,7 +46,7 @@ void DisplayVoteFFMenu(int client) } LogAction(client, -1, "\"%L\" initiated a friendly fire vote.", client); - CShowActivity2(client, "[{lightgreen}NativeVotes\x01] ", "%t", "Initiated Vote FF"); + CShowActivity2(client, "[{olive}NativeVotes\x01] ", "%t", "Initiated Vote FF"); g_voteType = voteType:ff; g_voteInfo[VOTE_NAME][0] = '\0'; @@ -109,7 +109,7 @@ public Action Command_VoteFF(int client, int args) { if (args > 0) { - CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] Usage: sm_voteff"); + CReplyToCommand(client, "[{olive}NativeVotes\x01] Usage: sm_voteff"); return Plugin_Handled; } diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp b/addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp index 6872366..39b45e3 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp @@ -35,7 +35,7 @@ void DisplayVoteGravityMenu(int client, int count, char items[5][64]) { LogAction(client, -1, "\"%L\" initiated a gravity vote.", client); - CShowActivity2(client, "[{lightgreen}NativeVotes\x01] ", "%t", "Initiated Vote Gravity"); + CShowActivity2(client, "[{olive}NativeVotes\x01] ", "%t", "Initiated Vote Gravity"); g_voteType = voteType:gravity; @@ -112,13 +112,13 @@ public Action Command_VoteGravity(int client, int args) { if (args < 1) { - CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] Usage: sm_votegravity <amount> [amount2] ... [amount5]"); + CReplyToCommand(client, "[{olive}NativeVotes\x01] Usage: sm_votegravity <amount> [amount2] ... [amount5]"); return Plugin_Handled; } if (Internal_IsVoteInProgress()) { - CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Vote in Progress"); + CReplyToCommand(client, "[{olive}NativeVotes\x01] %t", "Vote in Progress"); return Plugin_Handled; } @@ -141,7 +141,7 @@ public Action Command_VoteGravity(int client, int args) float temp; if (StringToFloatEx(items[count], temp) == 0) { - CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Invalid Amount"); + CReplyToCommand(client, "[{olive}NativeVotes\x01] %t", "Invalid Amount"); return Plugin_Handled; } diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp b/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp index 3af1e51..a11eceb 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp @@ -36,7 +36,7 @@ void DisplayVoteSlayMenu(int client, int target, char name[MAX_NAME_LENGTH]) { if (!IsPlayerAlive(target)) { - CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Cannot be performed on dead", name); + CReplyToCommand(client, "[{olive}NativeVotes\x01] %t", "Cannot be performed on dead", name); return; } @@ -48,7 +48,7 @@ void DisplayVoteSlayMenu(int client, int target, char name[MAX_NAME_LENGTH]) GetClientName(target, g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[])); LogAction(client, target, "\"%L\" initiated a slay vote against %N", client, target); - CShowActivity2(client, "[{lightgreen}NativeVotes\x01] ", "%t", "Initiated Vote Slay", playerName); + CShowActivity2(client, "[{olive}NativeVotes\x01] ", "%t", "Initiated Vote Slay", playerName); g_voteType = slay; @@ -151,13 +151,13 @@ public Action Command_VoteSlay(int client, int args) { if (args < 1) { - CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] Usage: sm_voteslay <player>"); + CReplyToCommand(client, "[{olive}NativeVotes\x01] Usage: sm_voteslay <player>"); return Plugin_Handled; } if (Internal_IsVoteInProgress()) { - CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Vote in Progress"); + CReplyToCommand(client, "[{olive}NativeVotes\x01] %t", "Vote in Progress"); return Plugin_Handled; } diff --git a/addons/sourcemod/scripting/nativevotes_nominations.sp b/addons/sourcemod/scripting/nativevotes_nominations.sp index b5565d9..5a3c0a6 100644 --- a/addons/sourcemod/scripting/nativevotes_nominations.sp +++ b/addons/sourcemod/scripting/nativevotes_nominations.sp @@ -197,7 +197,7 @@ public Action Command_Addmap(int client, int args) { if (args < 1) { - CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] Usage: sm_nominate_addmap <mapname>"); + CReplyToCommand(client, "[{olive}NativeVotes\x01] Usage: sm_nominate_addmap <mapname>"); return Plugin_Handled; } @@ -207,7 +207,7 @@ public Action Command_Addmap(int client, int args) if (FindMap(mapname, resolvedMap, sizeof(resolvedMap)) == FindMap_NotFound) { // We couldn't resolve the map entry to a filename, so... - CReplyToCommand(client, "[{lightgreen}Nominations\x01] %t", "Map was not found", mapname); + CReplyToCommand(client, "[{olive}Nominations\x01] %t", "Map was not found", mapname); return Plugin_Handled; } @@ -218,7 +218,7 @@ public Action Command_Addmap(int client, int args) int status; if (!g_MapTrie.GetValue(resolvedMap, status)) { - CReplyToCommand(client, "[{lightgreen}Nominations\x01] %t", "Map Not In Pool", displayName); + CReplyToCommand(client, "[{olive}Nominations\x01] %t", "Map Not In Pool", displayName); return Plugin_Handled; } @@ -227,14 +227,14 @@ public Action Command_Addmap(int client, int args) if (result > Nominate_Replaced) { /* We assume already in vote is the casue because the maplist does a Map Validity check and we forced, so it can't be full */ - CReplyToCommand(client, "[{lightgreen}Nominations\x01] %t", "Map Already In Vote", displayName); + CReplyToCommand(client, "[{olive}Nominations\x01] %t", "Map Already In Vote", displayName); return Plugin_Handled; } g_MapTrie.SetValue(resolvedMap, MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_NOMINATED); - CReplyToCommand(client, "[{lightgreen}Nominations\x01] %t", "Map Inserted", displayName); + CReplyToCommand(client, "[{olive}Nominations\x01] %t", "Map Inserted", displayName); LogAction(client, -1, "\"%L\" inserted map \"%s\".", client, mapname); return Plugin_Handled; @@ -288,7 +288,7 @@ public Action Command_Nominate(int client, int args) if (matches <= 0) { - CReplyToCommand(client, "[{lightgreen}Nominations\x01] %t", "Map was not found", mapname); + CReplyToCommand(client, "[{olive}Nominations\x01] %t", "Map was not found", mapname); } // One result else if (matches == 1) @@ -367,7 +367,7 @@ void AttemptNominate(int client, const char[] map, int size, bool isVoteMenu) if (FindMap(map, mapname, size) == FindMap_NotFound) { // We couldn't resolve the map entry to a filename, so... - CReplyToCommand(client, "[{lightgreen}Nominations\x01] %t", "Map was not found", mapname); + CReplyToCommand(client, "[{olive}Nominations\x01] %t", "Map was not found", mapname); if (isVoteMenu && g_NativeVotes) { @@ -383,7 +383,7 @@ void AttemptNominate(int client, const char[] map, int size, bool isVoteMenu) int status; if (!g_MapTrie.GetValue(mapname, status)) { - CReplyToCommand(client, "[{lightgreen}Nominations\x01] %t", "Map Not In Pool", displayName); + CReplyToCommand(client, "[{olive}Nominations\x01] %t", "Map Not In Pool", displayName); if (isVoteMenu && g_NativeVotes) { NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_MapNotValid); @@ -399,7 +399,7 @@ void AttemptNominate(int client, const char[] map, int size, bool isVoteMenu) { NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_MapNotValid); } - CReplyToCommand(client, "[{lightgreen}Nominations\x01] %t", "Can't Nominate Current Map"); + CReplyToCommand(client, "[{olive}Nominations\x01] %t", "Can't Nominate Current Map"); } if ((status & MAPSTATUS_EXCLUDE_PREVIOUS) == MAPSTATUS_EXCLUDE_PREVIOUS) @@ -408,7 +408,7 @@ void AttemptNominate(int client, const char[] map, int size, bool isVoteMenu) { NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_MapNotValid); } - CReplyToCommand(client, "[{lightgreen}Nominations\x01] %t", "Map in Exclude List"); + CReplyToCommand(client, "[{olive}Nominations\x01] %t", "Map in Exclude List"); } if ((status & MAPSTATUS_EXCLUDE_NOMINATED) == MAPSTATUS_EXCLUDE_NOMINATED) @@ -417,7 +417,7 @@ void AttemptNominate(int client, const char[] map, int size, bool isVoteMenu) { NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_MapNotValid); } - CReplyToCommand(client, "[{lightgreen}Nominations\x01] %t", "Map Already Nominated"); + CReplyToCommand(client, "[{olive}Nominations\x01] %t", "Map Already Nominated"); } return; @@ -433,7 +433,7 @@ void AttemptNominate(int client, const char[] map, int size, bool isVoteMenu) { NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_MapNotValid); } - CReplyToCommand(client, "[{lightgreen}Nominations\x01] %t", "Map Already In Vote", displayName); + CReplyToCommand(client, "[{olive}Nominations\x01] %t", "Map Already In Vote", displayName); } else { @@ -441,7 +441,7 @@ void AttemptNominate(int client, const char[] map, int size, bool isVoteMenu) { NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_MapNotValid); } - CReplyToCommand(client, "[{lightgreen}Nominations\x01] %t", "Max Nominations"); + CReplyToCommand(client, "[{olive}Nominations\x01] %t", "Max Nominations"); } return; @@ -455,11 +455,11 @@ void AttemptNominate(int client, const char[] map, int size, bool isVoteMenu) if (result == Nominate_Added) { - CPrintToChatAllEx(client, "[{lightgreen}Nominations\x01] %t", "Map Nominated", name, displayName); + CPrintToChatAllEx(client, "[{olive}Nominations\x01] %t", "Map Nominated", name, displayName); } else { - CReplyToCommandEx(client, client, "[{lightgreen}Nominations\x01] %t", "Map Nominated", name, displayName); + CReplyToCommandEx(client, client, "[{olive}Nominations\x01] %t", "Map Nominated", name, displayName); } return; diff --git a/addons/sourcemod/scripting/nativevotes_rockthevote.sp b/addons/sourcemod/scripting/nativevotes_rockthevote.sp index b9b222b..5bec600 100644 --- a/addons/sourcemod/scripting/nativevotes_rockthevote.sp +++ b/addons/sourcemod/scripting/nativevotes_rockthevote.sp @@ -280,7 +280,7 @@ public Action Command_ResetRTV(int client, int args) { ResetRTV(); - CPrintToChatAll("[{lightgreen}Rock The Vote\x01] %t", "Cancelled Vote"); + CPrintToChatAll("[{olive}Rock The Vote\x01] %t", "Cancelled Vote"); return Plugin_Handled; } @@ -317,7 +317,7 @@ void MenuHandler_UndoRTV(Menu menu, MenuAction action, int client, int param2) g_Voted[client] = false; if (g_Votes > 0) g_Votes--; - CPrintToChatAllEx(client, "[{lightgreen}Rock The Vote\x01] %s: %t", name, "Cancelled Vote"); + CPrintToChatAllEx(client, "[{olive}Rock The Vote\x01] %s: %t", name, "Cancelled Vote"); } } } @@ -332,7 +332,7 @@ void AttemptRTV(int client, bool isVoteMenu=false) { if (!g_RTVAllowed || (g_ConVars[postvoteaction].IntValue == 1 && HasEndOfMapVoteFinished())) { - CReplyToCommand(client, "[{lightgreen}Rock The Vote\x01] %t", "RTV Not Allowed"); + CReplyToCommand(client, "[{olive}Rock The Vote\x01] %t", "RTV Not Allowed"); if (isVoteMenu && g_NativeVotes) { if (g_Warmup) @@ -357,13 +357,13 @@ void AttemptRTV(int client, bool isVoteMenu=false) if (!CanMapChooserStartVote() && NativeVotes_IsVoteInProgress()) { - CReplyToCommand(client, "[{lightgreen}Rock The Vote\x01] %t", "RTV Started"); + CReplyToCommand(client, "[{olive}Rock The Vote\x01] %t", "RTV Started"); return; } if (GetClientCount(true) < g_ConVars[minplayers].IntValue) { - CReplyToCommand(client, "[{lightgreen}Rock The Vote\x01] %t", "Minimal Players Not Met"); + CReplyToCommand(client, "[{olive}Rock The Vote\x01] %t", "Minimal Players Not Met"); if (isVoteMenu && g_NativeVotes) { NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_Loading); @@ -383,7 +383,7 @@ void AttemptRTV(int client, bool isVoteMenu=false) g_Votes++; g_Voted[client] = true; - CPrintToChatAllEx(client, "[{lightgreen}Rock The Vote\x01] %t", "RTV Requested", name, g_Votes, g_VotesNeeded); + CPrintToChatAllEx(client, "[{olive}Rock The Vote\x01] %t", "RTV Requested", name, g_Votes, g_VotesNeeded); if (g_Votes >= g_VotesNeeded) { @@ -414,7 +414,7 @@ void StartRTV() GetMapDisplayName(map, map, sizeof(map)); Format(map, sizeof(map), "\x05%s\x01", map); - CPrintToChatAll("[{lightgreen}Rock The Vote\x01] %t", "Changing Maps", map); + CPrintToChatAll("[{olive}Rock The Vote\x01] %t", "Changing Maps", map); CreateTimer(5.0, Timer_ChangeMap, _, TIMER_FLAG_NO_MAPCHANGE); g_InChange = true; diff --git a/addons/sourcemod/scripting/nativevotes_voterp.sp b/addons/sourcemod/scripting/nativevotes_voterp.sp index a4ccc51..a4717ee 100644 --- a/addons/sourcemod/scripting/nativevotes_voterp.sp +++ b/addons/sourcemod/scripting/nativevotes_voterp.sp @@ -77,7 +77,7 @@ int MenuHandler_VoteRP(Menu menu, MenuAction action, int param1, int param2) menu.GetItem(item, info, sizeof(info)); int value = StringToInt(info); g_ConVars[tf_medieval_autorp].SetInt(value); - CPrintToChatAll("[{lightgreen}NativeVotes\x01] tf_medieval_autorp set to %d", value); + CPrintToChatAll("[{olive}NativeVotes\x01] tf_medieval_autorp set to %d", value); } case MenuAction_End: { @@ -96,14 +96,14 @@ Action Command_VoteRP(int client, int args) if ((g_NativeVotes && NativeVotes_IsVoteInProgress()) || (!g_NativeVotes && IsVoteInProgress())) { - CPrintToChat(client, "[{lightgreen}NativeVotes\x01] A vote is already in progress."); + CPrintToChat(client, "[{olive}NativeVotes\x01] A vote is already in progress."); return Plugin_Handled; } bool isMedieval = g_ConVars[tf_medieval].BoolValue || GameRules_GetProp("m_bPlayingMedieval") || FindEntityByClassname(-1, "tf_logic_medieval") != -1; if (!isMedieval) { - CPrintToChat(client, "[{lightgreen}NativeVotes\x01] This vote is only available in Medieval Mode."); + CPrintToChat(client, "[{olive}NativeVotes\x01] This vote is only available in Medieval Mode."); return Plugin_Handled; } diff --git a/addons/sourcemod/scripting/nativevotes_votescramble.sp b/addons/sourcemod/scripting/nativevotes_votescramble.sp index e7b746e..52f5c81 100644 --- a/addons/sourcemod/scripting/nativevotes_votescramble.sp +++ b/addons/sourcemod/scripting/nativevotes_votescramble.sp @@ -202,7 +202,7 @@ public Action Command_ForceScramble(int client, int args) public Action Command_ResetScramble(int client, int args) { ResetScramble(); - CPrintToChatAll("[{lightgreen}Scramble Teams\x01] %t", "Cancelled Vote"); + CPrintToChatAll("[{olive}Scramble Teams\x01] %t", "Cancelled Vote"); return Plugin_Handled; } @@ -238,7 +238,7 @@ void MenuHandler_UndoScramble(Menu menu, MenuAction action, int client, int para g_Voted[client] = false; if (g_Votes > 0) g_Votes--; - CPrintToChatAllEx(client, "[{lightgreen}Scramble Teams\x01] %s: %t", name, "Cancelled Vote"); + CPrintToChatAllEx(client, "[{olive}Scramble Teams\x01] %s: %t", name, "Cancelled Vote"); } } } @@ -253,7 +253,7 @@ void AttemptScramble(int client, bool isVoteMenu=false) { if (!g_ScrambleAllowed) { - CReplyToCommand(client, "[{lightgreen}Scramble Teams\x01] %t", "Scramble Not Allowed"); + CReplyToCommand(client, "[{olive}Scramble Teams\x01] %t", "Scramble Not Allowed"); if (isVoteMenu && g_NativeVotes) { int timeleft = g_ScrambleTime - GetTime(); @@ -271,13 +271,13 @@ void AttemptScramble(int client, bool isVoteMenu=false) if (g_NativeVotes && NativeVotes_IsVoteInProgress()) { - CReplyToCommand(client, "[{lightgreen}Scramble Teams\x01] %t", "Vote in Progress"); + CReplyToCommand(client, "[{olive}Scramble Teams\x01] %t", "Vote in Progress"); return; } if (GetClientCount(true) < g_ConVars[minplayers].IntValue) { - CReplyToCommand(client, "[{lightgreen}Scramble Teams\x01] %t", "Minimal Players Not Met"); + CReplyToCommand(client, "[{olive}Scramble Teams\x01] %t", "Minimal Players Not Met"); if (isVoteMenu && g_NativeVotes) { NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_Loading); @@ -297,7 +297,7 @@ void AttemptScramble(int client, bool isVoteMenu=false) g_Votes++; g_Voted[client] = true; - CPrintToChatAllEx(client, "[{lightgreen}Scramble Teams\x01] %t", "Scramble Requested", name, g_Votes, g_VotesNeeded); + CPrintToChatAllEx(client, "[{olive}Scramble Teams\x01] %t", "Scramble Requested", name, g_Votes, g_VotesNeeded); if (g_Votes >= g_VotesNeeded) { @@ -415,7 +415,7 @@ void ScrambleVoteResult(NativeVote vote, int num_votes, int num_clients, const i { NativeVotes_DisplayPassEx(vote, NativeVotesPass_Scramble); - CPrintToChatAll("[{lightgreen}Scramble Teams\x01] %t", "Scrambling Teams"); + CPrintToChatAll("[{olive}Scramble Teams\x01] %t", "Scrambling Teams"); ServerCommand("mp_scrambleteams"); if (g_ConVars[restart_round].BoolValue) { @@ -425,7 +425,7 @@ void ScrambleVoteResult(NativeVote vote, int num_votes, int num_clients, const i else if (yesVotes == 0 && noVotes == 0) { NativeVotes_DisplayFail(vote, NativeVotesFail_NotEnoughVotes); - CPrintToChatAll("[{lightgreen}Scramble Teams\x01] %t", "No Votes"); + CPrintToChatAll("[{olive}Scramble Teams\x01] %t", "No Votes"); } else { @@ -441,7 +441,7 @@ int MenuHandler_Scramble(Menu menu, MenuAction action, int client, int param2) { if (param2 == 0) { - CPrintToChatAll("[{lightgreen}Scramble Teams\x01] %t", "Scrambling Teams"); + CPrintToChatAll("[{olive}Scramble Teams\x01] %t", "Scrambling Teams"); ServerCommand("mp_scrambleteams"); if (g_ConVars[restart_round].BoolValue) { @@ -450,7 +450,7 @@ int MenuHandler_Scramble(Menu menu, MenuAction action, int client, int param2) } else { - CPrintToChatAll("[{lightgreen}Scramble Teams\x01] %t", "No Votes"); + CPrintToChatAll("[{olive}Scramble Teams\x01] %t", "No Votes"); } } case MenuAction_End: From ef7244540e693050215f2bcfbeaea19734d2432e Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Wed, 4 Feb 2026 19:33:21 +0100 Subject: [PATCH 074/128] Fix multi-votes --- .../scripting/nativevotes_basevotes.sp | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_basevotes.sp b/addons/sourcemod/scripting/nativevotes_basevotes.sp index 95a930f..8372f6e 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes.sp @@ -46,7 +46,7 @@ public Plugin:myinfo = name = "NativeVotes | Basic Votes", author = "Powerlord and AlliedModders LLC", description = "NativeVotes Basic Vote Commands", - version = "26w06c", + version = "26w06d", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -266,6 +266,7 @@ public Action Command_Vote(int client, int args) if (g_NativeVotes) { NativeVotesType nVoteType = answerCount == 0 ? NativeVotesType_Custom_YesNo : NativeVotesType_Custom_Mult; + voteMenu = NativeVotes_Create(Handler_NativeVoteCallback, nVoteType, view_as<MenuAction>(MENU_ACTIONS_ALL)); if (nVoteType == NativeVotesType_Custom_Mult) { for (int i = 0; i < answerCount; i++) @@ -273,28 +274,24 @@ public Action Command_Vote(int client, int args) NativeVotes_AddItem(voteMenu, answers[i], answers[i]); } } - voteMenu = NativeVotes_Create(Handler_NativeVoteCallback, nVoteType, view_as<MenuAction>(MENU_ACTIONS_ALL)); NativeVotes_SetTitle(voteMenu, g_voteArg); NativeVotes_DisplayToAll(voteMenu, 20); } else { voteMenu = CreateMenu(Handler_VoteCallback, view_as<MenuAction>(MENU_ACTIONS_ALL)); - SetMenuTitle(voteMenu, "%s?", g_voteArg); + SetMenuTitle(voteMenu, g_voteArg); - switch (answerCount) + if (answerCount == 0) { - case 0: - { - AddMenuItem(voteMenu, VOTE_YES, "Yes"); - AddMenuItem(voteMenu, VOTE_NO, "No"); - } - default: + AddMenuItem(voteMenu, VOTE_YES, "Yes"); + AddMenuItem(voteMenu, VOTE_NO, "No"); + } + else + { + for (int i = 0; i < answerCount; i++) { - for (int i = 0; i < answerCount; i++) - { - AddMenuItem(voteMenu, answers[i], answers[i]); - } + AddMenuItem(voteMenu, answers[i], answers[i]); } } From 0185ad3c16505462fe97bbeccbc8772cb6b13535 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Wed, 4 Feb 2026 19:33:36 +0100 Subject: [PATCH 075/128] Use mp_match_end_at_timelimit --- .../scripting/nativevotes_votescramble.sp | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_votescramble.sp b/addons/sourcemod/scripting/nativevotes_votescramble.sp index 52f5c81..7699c43 100644 --- a/addons/sourcemod/scripting/nativevotes_votescramble.sp +++ b/addons/sourcemod/scripting/nativevotes_votescramble.sp @@ -11,7 +11,7 @@ public Plugin myinfo = name = "NativeVotes | Scramble Teams", author = "Heapons", description = "Provides Scramble Teams Voting", - version = "26w06c", + version = "26w06d", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -23,7 +23,7 @@ enum interval, restart_round, restart_timelimit, - enforce_timelimit, + mp_match_end_at_timelimit, MAX_CONVARS } @@ -51,7 +51,7 @@ public void OnPluginStart() g_ConVars[interval] = CreateConVar("sm_scrambleteams_interval", "240.0", "Time (in seconds) after a failed scramble before another can be held", 0, true, 0.00); g_ConVars[restart_round] = CreateConVar("sm_scrambleteams_restart_round", "0", "Whether to restart the round after scrambling teams", 0, true, 0.0, true, 1.0); g_ConVars[restart_timelimit] = CreateConVar("sm_scrambleteams_restart_timelimit", "0", "Whether to restart the timelimit after scrambling teams", 0, true, 0.0, true, 1.0); - g_ConVars[enforce_timelimit] = CreateConVar("sm_scrambleteams_enforce_timelimit", "0", "Whether to end the game as soon as the timelimit runs out.", 0, true, 0.0, true, 1.0); + g_ConVars[mp_match_end_at_timelimit] = FindConVar("mp_match_end_at_timelimit"); RegConsoleCmd("sm_votescramble", Command_VoteScramble); RegConsoleCmd("sm_scramble", Command_VoteScramble); @@ -165,15 +165,12 @@ public void OnClientDisconnect(int client) public void Event_TeamplayRoundStart(Event event, const char[] name, bool dontBroadcast) { // Ends game mid-round once mp_timelimit runs out - if (g_ConVars[enforce_timelimit].BoolValue) - { - int entity = FindEntityByClassname(-1, "tf_gamerules"); - if (entity != -1) - { - SetVariantBool(true); - AcceptEntityInput(entity, "SetStalemateOnTimelimit", 0, 0); - } - } + int entity = FindEntityByClassname(-1, "tf_gamerules"); + if (entity != -1) + { + SetVariantBool(g_ConVars[mp_match_end_at_timelimit].BoolValue); + AcceptEntityInput(entity, "SetStalemateOnTimelimit", 0, 0); + } // Prevents map reset after scrambling teams if (!g_ConVars[restart_timelimit].BoolValue) { From 60afc701f5706ba1c4fe081ba55ed3bb3e42da52 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Wed, 4 Feb 2026 19:33:51 +0100 Subject: [PATCH 076/128] Bump version --- addons/sourcemod/scripting/nativevotes.sp | 2 +- addons/sourcemod/scripting/nativevotes_basecommands.sp | 2 +- addons/sourcemod/scripting/nativevotes_funvotes.sp | 2 +- addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp | 2 +- addons/sourcemod/scripting/nativevotes_nominations.sp | 2 +- addons/sourcemod/scripting/nativevotes_rockthevote.sp | 2 +- addons/sourcemod/scripting/nativevotes_voterp.sp | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes.sp b/addons/sourcemod/scripting/nativevotes.sp index 55b1928..378f83a 100644 --- a/addons/sourcemod/scripting/nativevotes.sp +++ b/addons/sourcemod/scripting/nativevotes.sp @@ -153,7 +153,7 @@ public Plugin myinfo = name = "NativeVotes", author = "Powerlord", description = "Voting API to use the game's native vote panels. Compatible with L4D, L4D2, TF2, and CS:GO.", - version = "26w06c", + version = "26w06d", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" } diff --git a/addons/sourcemod/scripting/nativevotes_basecommands.sp b/addons/sourcemod/scripting/nativevotes_basecommands.sp index b64fb8b..64a523f 100644 --- a/addons/sourcemod/scripting/nativevotes_basecommands.sp +++ b/addons/sourcemod/scripting/nativevotes_basecommands.sp @@ -48,7 +48,7 @@ public Plugin myinfo = name = "NativeVotes | Basic Commands", author = "Powerlord and AlliedModders LLC", description = "Revote and Cancel support for NativeVotes", - version = "26w06c", + version = "26w06d", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" } diff --git a/addons/sourcemod/scripting/nativevotes_funvotes.sp b/addons/sourcemod/scripting/nativevotes_funvotes.sp index 120649f..585e543 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes.sp @@ -47,7 +47,7 @@ public Plugin myinfo = name = "NativeVotes | Fun Votes", author = "Powerlord and AlliedModders LLC", description = "NativeVotes Fun Vote Commands", - version = "26w06c", + version = "26w06d", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp b/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp index 2620218..3240ef4 100644 --- a/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp +++ b/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp @@ -60,7 +60,7 @@ public Plugin myinfo = { name = "NativeVotes | Kick Vote Immunity", author = "Powerlord", description = "Causes TF2 kick votes to fail against people who the current user can't target.", - version = "26w06c", + version = "26w06d", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_nominations.sp b/addons/sourcemod/scripting/nativevotes_nominations.sp index 5a3c0a6..eafb9a3 100644 --- a/addons/sourcemod/scripting/nativevotes_nominations.sp +++ b/addons/sourcemod/scripting/nativevotes_nominations.sp @@ -48,7 +48,7 @@ public Plugin myinfo = name = "NativeVotes | Map Nominations", author = "AlliedModders LLC and Powerlord", description = "Provides Map Nominations", - version = "26w06c", + version = "26w06d", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_rockthevote.sp b/addons/sourcemod/scripting/nativevotes_rockthevote.sp index 5bec600..e1542e9 100644 --- a/addons/sourcemod/scripting/nativevotes_rockthevote.sp +++ b/addons/sourcemod/scripting/nativevotes_rockthevote.sp @@ -55,7 +55,7 @@ public Plugin myinfo = name = "NativeVotes | Rock The Vote", author = "AlliedModders LLC and Powerlord", description = "Provides RTV Map Voting", - version = "26w06c", + version = "26w06d", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_voterp.sp b/addons/sourcemod/scripting/nativevotes_voterp.sp index a4717ee..b8b78d5 100644 --- a/addons/sourcemod/scripting/nativevotes_voterp.sp +++ b/addons/sourcemod/scripting/nativevotes_voterp.sp @@ -14,7 +14,7 @@ public Plugin myinfo = name = "NativeVotes | Medieval Auto-RP", author = "Heapons", description = "Provides Medieval Auto-RP voting.", - version = "26w06c", + version = "26w06d", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; From c5aae5c110b9c114405b50b0bde9cab9bdeea0c4 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sat, 7 Feb 2026 10:52:09 +0100 Subject: [PATCH 077/128] Fix map reset time --- README.md | 3 +- .../scripting/nativevotes_votescramble.sp | 40 +++++++++---------- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 58fed25..644ab3b 100644 --- a/README.md +++ b/README.md @@ -109,5 +109,4 @@ This fork aims to expand upon [<img src="https://avatars.githubusercontent.com/u |`sm_scrambleteams_initialdelay`|`30.0`|Time (in seconds) before first scramble can be held| |`sm_scrambleteams_interval`|`240.0`|Time (in seconds) after a failed scramble before another can be held| |`sm_scrambleteams_restart_round`|`0`|Whether to restart the round after scrambling teams| -|`sm_scrambleteams_restart_timelimit`|`0`|Whether to restart the timelimit after scrambling teams| -|`sm_scrambleteams_enforce_timelimit`|`0`|Whether to end the game as soon as the timelimit runs out| +|`sm_scrambleteams_restart_timelimit`|`-1.0`|Override map timer after scrambling teams (leave it to '-1.0' for default behaviour)| \ No newline at end of file diff --git a/addons/sourcemod/scripting/nativevotes_votescramble.sp b/addons/sourcemod/scripting/nativevotes_votescramble.sp index 7699c43..e5d9b8a 100644 --- a/addons/sourcemod/scripting/nativevotes_votescramble.sp +++ b/addons/sourcemod/scripting/nativevotes_votescramble.sp @@ -10,8 +10,8 @@ public Plugin myinfo = { name = "NativeVotes | Scramble Teams", author = "Heapons", - description = "Provides Scramble Teams Voting", - version = "26w06d", + description = "Provides RTV-style Scramble Teams Voting", + version = "26w06e", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -45,12 +45,12 @@ public void OnPluginStart() LoadTranslations("rockthevote.phrases"); LoadTranslations("votescramble.phrases"); - g_ConVars[needed] = CreateConVar("sm_scrambleteams_needed", "0.60", "Percentage of players needed to scramble teams (Def 60%)", 0, true, 0.05, true, 1.0); - g_ConVars[minplayers] = CreateConVar("sm_scrambleteams_minplayers", "0", "Number of players required before scramble will be enabled.", 0, true, 0.0, true, float(MAXPLAYERS)); - g_ConVars[initialdelay] = CreateConVar("sm_scrambleteams_initialdelay", "30.0", "Time (in seconds) before first scramble can be held", 0, true, 0.00); - g_ConVars[interval] = CreateConVar("sm_scrambleteams_interval", "240.0", "Time (in seconds) after a failed scramble before another can be held", 0, true, 0.00); - g_ConVars[restart_round] = CreateConVar("sm_scrambleteams_restart_round", "0", "Whether to restart the round after scrambling teams", 0, true, 0.0, true, 1.0); - g_ConVars[restart_timelimit] = CreateConVar("sm_scrambleteams_restart_timelimit", "0", "Whether to restart the timelimit after scrambling teams", 0, true, 0.0, true, 1.0); + g_ConVars[needed] = CreateConVar("sm_scrambleteams_needed", "0.60", "Percentage of players needed to scramble teams (Def 60%)", 0, true, 0.05, true, 1.0); + g_ConVars[minplayers] = CreateConVar("sm_scrambleteams_minplayers", "0", "Number of players required before scramble will be enabled.", 0, true, 0.0, true, float(MAXPLAYERS)); + g_ConVars[initialdelay] = CreateConVar("sm_scrambleteams_initialdelay", "30.0", "Time (in seconds) before first scramble can be held", 0, true, 0.00); + g_ConVars[interval] = CreateConVar("sm_scrambleteams_interval", "240.0", "Time (in seconds) after a failed scramble before another can be held", 0, true, 0.00); + g_ConVars[restart_round] = CreateConVar("sm_scrambleteams_restart_round", "0", "Whether to restart the round after scrambling teams", 0, true, 0.0, true, 1.0); + g_ConVars[restart_timelimit] = CreateConVar("sm_scrambleteams_restart_timelimit", "-1.0", "Override map timer after scrambling teams (leave it to '-1.0' for default behaviour)", 0, true, -1.0); g_ConVars[mp_match_end_at_timelimit] = FindConVar("mp_match_end_at_timelimit"); RegConsoleCmd("sm_votescramble", Command_VoteScramble); @@ -172,9 +172,9 @@ public void Event_TeamplayRoundStart(Event event, const char[] name, bool dontBr AcceptEntityInput(entity, "SetStalemateOnTimelimit", 0, 0); } // Prevents map reset after scrambling teams - if (!g_ConVars[restart_timelimit].BoolValue) + if (g_ConVars[restart_timelimit].IntValue >= 0) { - GameRules_SetPropFloat("m_flMapResetTime", 0.0); + GameRules_SetPropFloat("m_flMapResetTime", g_ConVars[restart_timelimit].FloatValue); } } @@ -199,7 +199,7 @@ public Action Command_ForceScramble(int client, int args) public Action Command_ResetScramble(int client, int args) { ResetScramble(); - CPrintToChatAll("[{olive}Scramble Teams\x01] %t", "Cancelled Vote"); + CPrintToChatAll("[\x04Scramble Teams\x01] %t", "Cancelled Vote"); return Plugin_Handled; } @@ -235,7 +235,7 @@ void MenuHandler_UndoScramble(Menu menu, MenuAction action, int client, int para g_Voted[client] = false; if (g_Votes > 0) g_Votes--; - CPrintToChatAllEx(client, "[{olive}Scramble Teams\x01] %s: %t", name, "Cancelled Vote"); + CPrintToChatAllEx(client, "[\x04Scramble Teams\x01] %s: %t", name, "Cancelled Vote"); } } } @@ -250,7 +250,7 @@ void AttemptScramble(int client, bool isVoteMenu=false) { if (!g_ScrambleAllowed) { - CReplyToCommand(client, "[{olive}Scramble Teams\x01] %t", "Scramble Not Allowed"); + CReplyToCommand(client, "[\x04Scramble Teams\x01] %t", "Scramble Not Allowed"); if (isVoteMenu && g_NativeVotes) { int timeleft = g_ScrambleTime - GetTime(); @@ -268,13 +268,13 @@ void AttemptScramble(int client, bool isVoteMenu=false) if (g_NativeVotes && NativeVotes_IsVoteInProgress()) { - CReplyToCommand(client, "[{olive}Scramble Teams\x01] %t", "Vote in Progress"); + CReplyToCommand(client, "[\x04Scramble Teams\x01] %t", "Vote in Progress"); return; } if (GetClientCount(true) < g_ConVars[minplayers].IntValue) { - CReplyToCommand(client, "[{olive}Scramble Teams\x01] %t", "Minimal Players Not Met"); + CReplyToCommand(client, "[\x04Scramble Teams\x01] %t", "Minimal Players Not Met"); if (isVoteMenu && g_NativeVotes) { NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_Loading); @@ -294,7 +294,7 @@ void AttemptScramble(int client, bool isVoteMenu=false) g_Votes++; g_Voted[client] = true; - CPrintToChatAllEx(client, "[{olive}Scramble Teams\x01] %t", "Scramble Requested", name, g_Votes, g_VotesNeeded); + CPrintToChatAllEx(client, "[\x04Scramble Teams\x01] %t", "Scramble Requested", name, g_Votes, g_VotesNeeded); if (g_Votes >= g_VotesNeeded) { @@ -412,7 +412,7 @@ void ScrambleVoteResult(NativeVote vote, int num_votes, int num_clients, const i { NativeVotes_DisplayPassEx(vote, NativeVotesPass_Scramble); - CPrintToChatAll("[{olive}Scramble Teams\x01] %t", "Scrambling Teams"); + CPrintToChatAll("[\x04Scramble Teams\x01] %t", "Scrambling Teams"); ServerCommand("mp_scrambleteams"); if (g_ConVars[restart_round].BoolValue) { @@ -422,7 +422,7 @@ void ScrambleVoteResult(NativeVote vote, int num_votes, int num_clients, const i else if (yesVotes == 0 && noVotes == 0) { NativeVotes_DisplayFail(vote, NativeVotesFail_NotEnoughVotes); - CPrintToChatAll("[{olive}Scramble Teams\x01] %t", "No Votes"); + CPrintToChatAll("[\x04Scramble Teams\x01] %t", "No Votes"); } else { @@ -438,7 +438,7 @@ int MenuHandler_Scramble(Menu menu, MenuAction action, int client, int param2) { if (param2 == 0) { - CPrintToChatAll("[{olive}Scramble Teams\x01] %t", "Scrambling Teams"); + CPrintToChatAll("[\x04Scramble Teams\x01] %t", "Scrambling Teams"); ServerCommand("mp_scrambleteams"); if (g_ConVars[restart_round].BoolValue) { @@ -447,7 +447,7 @@ int MenuHandler_Scramble(Menu menu, MenuAction action, int client, int param2) } else { - CPrintToChatAll("[{olive}Scramble Teams\x01] %t", "No Votes"); + CPrintToChatAll("[\x04Scramble Teams\x01] %t", "No Votes"); } } case MenuAction_End: From 40353526e34e216a1da77230db982ac6b951ba59 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sat, 7 Feb 2026 10:52:31 +0100 Subject: [PATCH 078/128] Bump version --- addons/sourcemod/scripting/nativevotes.sp | 2 +- .../scripting/nativevotes_basecommands.sp | 8 ++-- .../scripting/nativevotes_basevotes.sp | 6 +-- .../nativevotes_basevotes/voteban.sp | 10 ++--- .../nativevotes_basevotes/votekick.sp | 8 ++-- .../nativevotes_basevotes/votemap.sp | 10 ++--- .../scripting/nativevotes_funvotes.sp | 2 +- .../nativevotes_funvotes/votealltalk.sp | 6 +-- .../nativevotes_funvotes/voteburn.sp | 14 +++---- .../scripting/nativevotes_funvotes/voteff.sp | 6 +-- .../nativevotes_funvotes/votegravity.sp | 8 ++-- .../nativevotes_funvotes/voteslay.sp | 8 ++-- .../nativevotes_kickvote_immunity.sp | 2 +- .../scripting/nativevotes_mapchooser.sp | 42 +++++++++---------- .../scripting/nativevotes_nominations.sp | 32 +++++++------- .../scripting/nativevotes_rockthevote.sp | 16 +++---- .../sourcemod/scripting/nativevotes_voterp.sp | 8 ++-- 17 files changed, 93 insertions(+), 95 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes.sp b/addons/sourcemod/scripting/nativevotes.sp index 378f83a..bb5b551 100644 --- a/addons/sourcemod/scripting/nativevotes.sp +++ b/addons/sourcemod/scripting/nativevotes.sp @@ -153,7 +153,7 @@ public Plugin myinfo = name = "NativeVotes", author = "Powerlord", description = "Voting API to use the game's native vote panels. Compatible with L4D, L4D2, TF2, and CS:GO.", - version = "26w06d", + version = "26w06e", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" } diff --git a/addons/sourcemod/scripting/nativevotes_basecommands.sp b/addons/sourcemod/scripting/nativevotes_basecommands.sp index 64a523f..4346769 100644 --- a/addons/sourcemod/scripting/nativevotes_basecommands.sp +++ b/addons/sourcemod/scripting/nativevotes_basecommands.sp @@ -48,7 +48,7 @@ public Plugin myinfo = name = "NativeVotes | Basic Commands", author = "Powerlord and AlliedModders LLC", description = "Revote and Cancel support for NativeVotes", - version = "26w06d", + version = "26w06e", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" } @@ -69,7 +69,7 @@ bool PerformCancelVote(int client) return false; } - CShowActivity2(client, "[{olive}NativeVotes\x01] ", "%t", "Cancelled Vote"); + CShowActivity2(client, "[\x04NativeVotes\x01] ", "%t", "Cancelled Vote"); NativeVotes_Cancel(); return true; @@ -163,7 +163,7 @@ public Action Command_ReVote(int client, const char[] command, int argc) return Plugin_Continue; } - CReplyToCommand(client, "[{olive}NativeVotes\x01] %t", "Cannot participate in vote"); + CReplyToCommand(client, "[\x04NativeVotes\x01] %t", "Cannot participate in vote"); return Plugin_Stop; } @@ -173,7 +173,7 @@ public Action Command_ReVote(int client, const char[] command, int argc) } else if (!IsVoteInProgress()) { - CReplyToCommand(client, "[{olive}NativeVotes\x01] %t", "Cannot change vote"); + CReplyToCommand(client, "[\x04NativeVotes\x01] %t", "Cannot change vote"); return Plugin_Stop; } diff --git a/addons/sourcemod/scripting/nativevotes_basevotes.sp b/addons/sourcemod/scripting/nativevotes_basevotes.sp index 8372f6e..5546348 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes.sp @@ -46,7 +46,7 @@ public Plugin:myinfo = name = "NativeVotes | Basic Votes", author = "Powerlord and AlliedModders LLC", description = "NativeVotes Basic Vote Commands", - version = "26w06d", + version = "26w06e", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -225,7 +225,7 @@ public Action Command_Vote(int client, int args) { if (args < 1) { - CReplyToCommand(client, "[{olive}NativeVotes\x01] Usage: sm_vote <title> [option1] [option2] ... [option5]"); + CReplyToCommand(client, "[\x04NativeVotes\x01] Usage: sm_vote <title> [option1] [option2] ... [option5]"); return Plugin_Handled; } @@ -259,7 +259,7 @@ public Action Command_Vote(int client, int args) } } LogAction(client, -1, "\"%L\" initiated a generic vote.", client); - CShowActivity2(client, "[{olive}NativeVotes\x01] ", "%t", "Initiate Vote", g_voteArg); + CShowActivity2(client, "[\x04NativeVotes\x01] ", "%t", "Initiate Vote", g_voteArg); g_voteType = question; Handle voteMenu; diff --git a/addons/sourcemod/scripting/nativevotes_basevotes/voteban.sp b/addons/sourcemod/scripting/nativevotes_basevotes/voteban.sp index 1b593fb..f2c76d3 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes/voteban.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes/voteban.sp @@ -44,7 +44,7 @@ void DisplayVoteBanMenu(int client, int target) GetClientIP(target, g_voteInfo[VOTE_IP], sizeof(g_voteInfo[])); LogAction(client, target, "\"%L\" initiated a ban vote against %s", client, playerName); - CShowActivity2(client, "[{olive}NativeVotes\x01] ", "%t", "Initiated Vote Ban", playerName); + CShowActivity2(client, "[\x04NativeVotes\x01] ", "%t", "Initiated Vote Ban", playerName); g_voteType = ban; @@ -117,11 +117,11 @@ public int MenuHandler_Ban(Handle menu, MenuAction action, int param1, int param userid = StringToInt(info); if ((target = GetClientOfUserId(userid)) == 0) { - CPrintToChat(param1, "[{olive}NativeVotes\x01] %t", "Player no longer available"); + CPrintToChat(param1, "[\x04NativeVotes\x01] %t", "Player no longer available"); } else if (!CanUserTarget(param1, target)) { - CPrintToChat(param1, "[{olive}NativeVotes\x01] %t", "Unable to target"); + CPrintToChat(param1, "[\x04NativeVotes\x01] %t", "Unable to target"); } else { @@ -138,13 +138,13 @@ public Action Command_Voteban(int client, int args) { if (args < 1) { - CReplyToCommand(client, "[{olive}NativeVotes\x01] Usage: sm_voteban <player> [reason]"); + CReplyToCommand(client, "[\x04NativeVotes\x01] Usage: sm_voteban <player> [reason]"); return Plugin_Handled; } if (Internal_IsVoteInProgress()) { - CReplyToCommand(client, "[{olive}NativeVotes\x01] %t", "Vote in Progress"); + CReplyToCommand(client, "[\x04NativeVotes\x01] %t", "Vote in Progress"); return Plugin_Handled; } diff --git a/addons/sourcemod/scripting/nativevotes_basevotes/votekick.sp b/addons/sourcemod/scripting/nativevotes_basevotes/votekick.sp index b55616f..31bb222 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes/votekick.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes/votekick.sp @@ -116,11 +116,11 @@ public int MenuHandler_Kick(Handle menu, MenuAction action, int param1, int para userid = StringToInt(info); if ((target = GetClientOfUserId(userid)) == 0) { - CPrintToChat(param1, "[{olive}NativeVotes\x01] %t", "Player no longer available"); + CPrintToChat(param1, "[\x04NativeVotes\x01] %t", "Player no longer available"); } else if (!CanUserTarget(param1, target)) { - CPrintToChat(param1, "[{olive}NativeVotes\x01] %t", "Unable to target"); + CPrintToChat(param1, "[\x04NativeVotes\x01] %t", "Unable to target"); } else { @@ -137,12 +137,12 @@ public Action Command_Votekick(int client, int args) { if (args < 1) { - CReplyToCommand(client, "[{olive}NativeVotes\x01] Usage: sm_votekick <player> [reason]"); + CReplyToCommand(client, "[\x04NativeVotes\x01] Usage: sm_votekick <player> [reason]"); return Plugin_Handled; } if (Internal_IsVoteInProgress()) { - CReplyToCommand(client, "[{olive}NativeVotes\x01] %t", "Vote in Progress"); + CReplyToCommand(client, "[\x04NativeVotes\x01] %t", "Vote in Progress"); return Plugin_Handled; } if (!TestVoteDelay(client)) diff --git a/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp b/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp index e1db37b..abdbf86 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp @@ -41,7 +41,7 @@ bool g_VoteMapInUse; void DisplayVoteMapMenu(int client, int mapCount, char maps[5][PLATFORM_MAX_PATH]) { LogAction(client, -1, "\"%L\" initiated a map vote.", client); - CShowActivity2(client, "[{olive}NativeVotes\x01] ", "%t", "Initiated Vote Map"); + CShowActivity2(client, "[\x04NativeVotes\x01] ", "%t", "Initiated Vote Map"); g_voteType = map; @@ -234,7 +234,7 @@ public void AdminMenu_VoteMap(Handle topmenu, TopMenuAction action, TopMenuObjec } else { - CPrintToChat(param, "[{olive}NativeVotes\x01] %T", "Map Vote In Use", param); + CPrintToChat(param, "[\x04NativeVotes\x01] %T", "Map Vote In Use", param); } } case TopMenuAction_DrawOption: @@ -249,13 +249,13 @@ public Action Command_Votemap(int client, int args) { if (args < 1) { - CReplyToCommand(client, "[{olive}NativeVotes\x01] Usage: sm_votemap <mapname> [mapname2] ... [mapname5]"); + CReplyToCommand(client, "[\x04NativeVotes\x01] Usage: sm_votemap <mapname> [mapname2] ... [mapname5]"); return Plugin_Handled; } if (Internal_IsVoteInProgress()) { - CReplyToCommand(client, "[{olive}NativeVotes\x01] %t", "Vote in Progress"); + CReplyToCommand(client, "[\x04NativeVotes\x01] %t", "Vote in Progress"); return Plugin_Handled; } @@ -277,7 +277,7 @@ public Action Command_Votemap(int client, int args) if (!IsMapValid(maps[mapCount])) { - CReplyToCommand(client, "[{olive}NativeVotes\x01] %t", "Map was not found", maps[mapCount]); + CReplyToCommand(client, "[\x04NativeVotes\x01] %t", "Map was not found", maps[mapCount]); return Plugin_Handled; } diff --git a/addons/sourcemod/scripting/nativevotes_funvotes.sp b/addons/sourcemod/scripting/nativevotes_funvotes.sp index 585e543..e72471a 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes.sp @@ -47,7 +47,7 @@ public Plugin myinfo = name = "NativeVotes | Fun Votes", author = "Powerlord and AlliedModders LLC", description = "NativeVotes Fun Vote Commands", - version = "26w06d", + version = "26w06e", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp b/addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp index 07e3a8d..28fba20 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp @@ -36,7 +36,7 @@ void DisplayVoteAllTalkMenu(int client) { if (Internal_IsVoteInProgress()) { - CReplyToCommand(client, "[{olive}NativeVotes\x01] %t", "Vote in Progress"); + CReplyToCommand(client, "[\x04NativeVotes\x01] %t", "Vote in Progress"); return; } @@ -46,7 +46,7 @@ void DisplayVoteAllTalkMenu(int client) } LogAction(client, -1, "\"%L\" initiated an alltalk vote.", client); - CShowActivity2(client, "[{olive}NativeVotes\x01] ", "%t", "Initiated Vote Alltalk"); + CShowActivity2(client, "[\x04NativeVotes\x01] ", "%t", "Initiated Vote Alltalk"); g_voteType = voteType:alltalk; g_voteInfo[VOTE_NAME][0] = '\0'; @@ -128,7 +128,7 @@ public Action Command_VoteAlltalk(int client, int args) { if (args > 0) { - CReplyToCommand(client, "[{olive}NativeVotes\x01] Usage: sm_votealltalk"); + CReplyToCommand(client, "[\x04NativeVotes\x01] Usage: sm_votealltalk"); return Plugin_Handled; } diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp b/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp index 28458c5..39f803f 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp @@ -36,7 +36,7 @@ void DisplayVoteBurnMenu(int client, int target, char name[MAX_NAME_LENGTH]) { if (!IsPlayerAlive(target)) { - CReplyToCommand(client, "[{olive}NativeVotes\x01] %t", "Cannot be performed on dead", name); + CReplyToCommand(client, "[\x04NativeVotes\x01] %t", "Cannot be performed on dead", name); return; } @@ -48,7 +48,7 @@ void DisplayVoteBurnMenu(int client, int target, char name[MAX_NAME_LENGTH]) GetClientName(target, g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[])); LogAction(client, target, "\"%L\" initiated a burn vote against %N", client, target); - CShowActivity2(client, "[{olive}NativeVotes\x01] ", "%t", "Initiated Vote Burn", playerName); + CShowActivity2(client, "[\x04NativeVotes\x01] ", "%t", "Initiated Vote Burn", playerName); g_voteType = burn; @@ -125,15 +125,15 @@ public int MenuHandler_Burn(Handle menu, MenuAction action, int param1, int para userid = StringToInt(info); if ((target = GetClientOfUserId(userid)) == 0) { - CPrintToChat(param1, "[{olive}NativeVotes\x01] %t", "Player no longer available"); + CPrintToChat(param1, "[\x04NativeVotes\x01] %t", "Player no longer available"); } else if (!CanUserTarget(param1, target)) { - CPrintToChat(param1, "[{olive}NativeVotes\x01] %t", "Unable to target"); + CPrintToChat(param1, "[\x04NativeVotes\x01] %t", "Unable to target"); } else if (!IsPlayerAlive(target)) { - CPrintToChat(param1, "[{olive}NativeVotes\x01] %t", "Player has since died"); + CPrintToChat(param1, "[\x04NativeVotes\x01] %t", "Player has since died"); } else { @@ -149,13 +149,13 @@ public Action Command_VoteBurn(int client, int args) { if (args < 1) { - CReplyToCommand(client, "[{olive}NativeVotes\x01] Usage: sm_voteburn <player>"); + CReplyToCommand(client, "[\x04NativeVotes\x01] Usage: sm_voteburn <player>"); return Plugin_Handled; } if (Internal_IsVoteInProgress()) { - CReplyToCommand(client, "[{olive}NativeVotes\x01] %t", "Vote in Progress"); + CReplyToCommand(client, "[\x04NativeVotes\x01] %t", "Vote in Progress"); return Plugin_Handled; } diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp b/addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp index c50ff3f..f7911a8 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp @@ -36,7 +36,7 @@ void DisplayVoteFFMenu(int client) { if (Internal_IsVoteInProgress()) { - CReplyToCommand(client, "[{olive}NativeVotes\x01] %t", "Vote in Progress"); + CReplyToCommand(client, "[\x04NativeVotes\x01] %t", "Vote in Progress"); return; } @@ -46,7 +46,7 @@ void DisplayVoteFFMenu(int client) } LogAction(client, -1, "\"%L\" initiated a friendly fire vote.", client); - CShowActivity2(client, "[{olive}NativeVotes\x01] ", "%t", "Initiated Vote FF"); + CShowActivity2(client, "[\x04NativeVotes\x01] ", "%t", "Initiated Vote FF"); g_voteType = voteType:ff; g_voteInfo[VOTE_NAME][0] = '\0'; @@ -109,7 +109,7 @@ public Action Command_VoteFF(int client, int args) { if (args > 0) { - CReplyToCommand(client, "[{olive}NativeVotes\x01] Usage: sm_voteff"); + CReplyToCommand(client, "[\x04NativeVotes\x01] Usage: sm_voteff"); return Plugin_Handled; } diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp b/addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp index 39b45e3..1b6591a 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp @@ -35,7 +35,7 @@ void DisplayVoteGravityMenu(int client, int count, char items[5][64]) { LogAction(client, -1, "\"%L\" initiated a gravity vote.", client); - CShowActivity2(client, "[{olive}NativeVotes\x01] ", "%t", "Initiated Vote Gravity"); + CShowActivity2(client, "[\x04NativeVotes\x01] ", "%t", "Initiated Vote Gravity"); g_voteType = voteType:gravity; @@ -112,13 +112,13 @@ public Action Command_VoteGravity(int client, int args) { if (args < 1) { - CReplyToCommand(client, "[{olive}NativeVotes\x01] Usage: sm_votegravity <amount> [amount2] ... [amount5]"); + CReplyToCommand(client, "[\x04NativeVotes\x01] Usage: sm_votegravity <amount> [amount2] ... [amount5]"); return Plugin_Handled; } if (Internal_IsVoteInProgress()) { - CReplyToCommand(client, "[{olive}NativeVotes\x01] %t", "Vote in Progress"); + CReplyToCommand(client, "[\x04NativeVotes\x01] %t", "Vote in Progress"); return Plugin_Handled; } @@ -141,7 +141,7 @@ public Action Command_VoteGravity(int client, int args) float temp; if (StringToFloatEx(items[count], temp) == 0) { - CReplyToCommand(client, "[{olive}NativeVotes\x01] %t", "Invalid Amount"); + CReplyToCommand(client, "[\x04NativeVotes\x01] %t", "Invalid Amount"); return Plugin_Handled; } diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp b/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp index a11eceb..6233136 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp @@ -36,7 +36,7 @@ void DisplayVoteSlayMenu(int client, int target, char name[MAX_NAME_LENGTH]) { if (!IsPlayerAlive(target)) { - CReplyToCommand(client, "[{olive}NativeVotes\x01] %t", "Cannot be performed on dead", name); + CReplyToCommand(client, "[\x04NativeVotes\x01] %t", "Cannot be performed on dead", name); return; } @@ -48,7 +48,7 @@ void DisplayVoteSlayMenu(int client, int target, char name[MAX_NAME_LENGTH]) GetClientName(target, g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[])); LogAction(client, target, "\"%L\" initiated a slay vote against %N", client, target); - CShowActivity2(client, "[{olive}NativeVotes\x01] ", "%t", "Initiated Vote Slay", playerName); + CShowActivity2(client, "[\x04NativeVotes\x01] ", "%t", "Initiated Vote Slay", playerName); g_voteType = slay; @@ -151,13 +151,13 @@ public Action Command_VoteSlay(int client, int args) { if (args < 1) { - CReplyToCommand(client, "[{olive}NativeVotes\x01] Usage: sm_voteslay <player>"); + CReplyToCommand(client, "[\x04NativeVotes\x01] Usage: sm_voteslay <player>"); return Plugin_Handled; } if (Internal_IsVoteInProgress()) { - CReplyToCommand(client, "[{olive}NativeVotes\x01] %t", "Vote in Progress"); + CReplyToCommand(client, "[\x04NativeVotes\x01] %t", "Vote in Progress"); return Plugin_Handled; } diff --git a/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp b/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp index 3240ef4..fb8a975 100644 --- a/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp +++ b/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp @@ -60,7 +60,7 @@ public Plugin myinfo = { name = "NativeVotes | Kick Vote Immunity", author = "Powerlord", description = "Causes TF2 kick votes to fail against people who the current user can't target.", - version = "26w06d", + version = "26w06e", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_mapchooser.sp b/addons/sourcemod/scripting/nativevotes_mapchooser.sp index 1227d0a..7cbbc76 100644 --- a/addons/sourcemod/scripting/nativevotes_mapchooser.sp +++ b/addons/sourcemod/scripting/nativevotes_mapchooser.sp @@ -55,7 +55,7 @@ public Plugin myinfo = name = "NativeVotes | MapChooser", author = "AlliedModders LLC and Powerlord", description = "Automated Map Voting", - version = "26w06d", + version = "26w06e", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -117,6 +117,7 @@ bool g_MapVoteCompleted; bool g_ChangeMapAtRoundEnd; bool g_ChangeMapInProgress; int g_mapFileSerial = -1; +int g_AppID; MapChange g_ChangeTime; @@ -153,6 +154,13 @@ public void OnPluginStart() engine = Engine_TF2; } + if (kv.JumpToKey("FileSystem")) + { + g_AppID = kv.GetNum("SteamAppId"); + } + + delete kv; + int arraySize = ByteCountToCells(PLATFORM_MAX_PATH); g_MapList = new ArrayList(arraySize); g_NominateList = new ArrayList(arraySize); @@ -393,7 +401,7 @@ public Action Command_SetNextMap(int client, int args) { if (args < 1) { - CReplyToCommand(client, "[{olive}MapChooser\x01] Usage: sm_setnextmap <map>"); + CReplyToCommand(client, "[\x04MapChooser\x01] Usage: sm_setnextmap <map>"); return Plugin_Handled; } @@ -402,14 +410,14 @@ public Action Command_SetNextMap(int client, int args) if (FindMap(map, displayName, sizeof(displayName)) == FindMap_NotFound) { - CReplyToCommand(client, "[{olive}MapChooser\x01] %t", "Map was not found", map); + CReplyToCommand(client, "[\x04MapChooser\x01] %t", "Map was not found", map); return Plugin_Handled; } GetMapDisplayName(displayName, displayName, sizeof(displayName)); Format(displayName, sizeof(displayName), "\x05%s\x01", displayName); - CShowActivity2(client, "[{olive}MapChooser\x01] ", "%t", "Changed Next Map", displayName); + CShowActivity2(client, "[\x04MapChooser\x01] ", "%t", "Changed Next Map", displayName); if (client > 0) { LogAction(client, -1, "\"%L\" changed nextmap to \"%s\"", client, map); @@ -880,7 +888,7 @@ void InitiateVote(MapChange when, ArrayList inputlist=null) } LogAction(-1, -1, "Voting for next map has started."); - CPrintToChatAll("[{olive}MapChooser\x01] %t", "Nextmap Voting Started"); + CPrintToChatAll("[\x04MapChooser\x01] %t", "Nextmap Voting Started"); } public void Handler_NV_VoteFinishedGeneric(NativeVote menu, int num_votes, int num_clients, const int[] client_indexes, const int[] client_votes, int num_items, const int[] item_indexes, const int[] item_votes) @@ -948,7 +956,7 @@ public void Handler_VoteFinishedGenericShared(const char[] map, const char[] dis } } - CPrintToChatAll("[{olive}MapChooser\x01] %t", "Current Map Extended", RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes); + CPrintToChatAll("[\x04MapChooser\x01] %t", "Current Map Extended", RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes); LogAction(-1, -1, "Voting for next map has finished. The current map has been extended."); if (isNativeVotes) @@ -964,7 +972,7 @@ public void Handler_VoteFinishedGenericShared(const char[] map, const char[] dis } else if (strcmp(map, VOTE_DONTCHANGE, false) == 0) { - CPrintToChatAll("[{olive}MapChooser\x01] %t", "Current Map Stays", RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes); + CPrintToChatAll("[\x04MapChooser\x01] %t", "Current Map Stays", RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes); LogAction(-1, -1, "Voting for next map has finished. 'No Change' was the winner"); if (isNativeVotes) @@ -1005,7 +1013,7 @@ public void Handler_VoteFinishedGenericShared(const char[] map, const char[] dis char formattedName[PLATFORM_MAX_PATH]; Format(formattedName, sizeof(formattedName), "\x05%s\x01", displayName); - CPrintToChatAll("[{olive}MapChooser\x01] %t", "Nextmap Voting Finished", formattedName, RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes); + CPrintToChatAll("[\x04MapChooser\x01] %t", "Nextmap Voting Finished", formattedName, RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes); LogAction(-1, -1, "Voting for next map has finished. Nextmap: %s.", map); } } @@ -1048,7 +1056,7 @@ public void Handler_NV_MapVoteFinished(NativeVote menu, int num_votes, int num_c float map2percent = float(item_votes[1])/ float(num_votes) * 100; - CPrintToChatAll("[{olive}MapChooser\x01] %t", "Starting Runoff", g_ConVars[mapvote_runoffpercent].FloatValue, info1, map1percent, info2, map2percent); + CPrintToChatAll("[\x04MapChooser\x01] %t", "Starting Runoff", g_ConVars[mapvote_runoffpercent].FloatValue, info1, map1percent, info2, map2percent); LogMessage("Voting for next map was indecisive, beginning runoff vote"); return; @@ -1109,7 +1117,7 @@ public void Handler_MapVoteFinished(Menu menu, int num_votes, int num_clients, c float map1percent = float(item_info[0][VOTEINFO_ITEM_VOTES])/ float(num_votes) * 100; float map2percent = float(item_info[1][VOTEINFO_ITEM_VOTES])/ float(num_votes) * 100; - CPrintToChatAll("[{olive}MapChooser\x01] %t", "Starting Runoff", g_ConVars[mapvote_runoffpercent].FloatValue, info1, map1percent, info2, map2percent); + CPrintToChatAll("[\x04MapChooser\x01] %t", "Starting Runoff", g_ConVars[mapvote_runoffpercent].FloatValue, info1, map1percent, info2, map2percent); LogMessage("Voting for next map was indecisive, beginning runoff vote"); return; @@ -1745,24 +1753,14 @@ void HTTPResponse_GetCollectionDetails(HTTPResponse response, File file) void CleanupWorkshopMaps() { - KeyValues kv = new KeyValues("GameInfo"); - int appid; - if (kv.ImportFromFile("gameinfo.txt") && kv.JumpToKey("FileSystem")) - { - appid = kv.GetNum("SteamAppId"); - } - delete kv; - char workshopDir[PLATFORM_MAX_PATH]; - Format(workshopDir, sizeof(workshopDir), "../steamapps/workshop/content/%d", appid); + Format(workshopDir, sizeof(workshopDir), "../steamapps/workshop/content/%d", g_AppID); DirectoryListing dir = OpenDirectory(workshopDir); if (dir != null) { - char ugcid[PLATFORM_MAX_PATH]; + char ugcid[PLATFORM_MAX_PATH], path[PLATFORM_MAX_PATH]; FileType type; - char path[PLATFORM_MAX_PATH]; - while (dir.GetNext(ugcid, sizeof(ugcid), type)) { if (type != FileType_Directory) diff --git a/addons/sourcemod/scripting/nativevotes_nominations.sp b/addons/sourcemod/scripting/nativevotes_nominations.sp index eafb9a3..fec659a 100644 --- a/addons/sourcemod/scripting/nativevotes_nominations.sp +++ b/addons/sourcemod/scripting/nativevotes_nominations.sp @@ -48,7 +48,7 @@ public Plugin myinfo = name = "NativeVotes | Map Nominations", author = "AlliedModders LLC and Powerlord", description = "Provides Map Nominations", - version = "26w06d", + version = "26w06e", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -197,7 +197,7 @@ public Action Command_Addmap(int client, int args) { if (args < 1) { - CReplyToCommand(client, "[{olive}NativeVotes\x01] Usage: sm_nominate_addmap <mapname>"); + CReplyToCommand(client, "[\x04NativeVotes\x01] Usage: sm_nominate_addmap <mapname>"); return Plugin_Handled; } @@ -207,7 +207,7 @@ public Action Command_Addmap(int client, int args) if (FindMap(mapname, resolvedMap, sizeof(resolvedMap)) == FindMap_NotFound) { // We couldn't resolve the map entry to a filename, so... - CReplyToCommand(client, "[{olive}Nominations\x01] %t", "Map was not found", mapname); + CReplyToCommand(client, "[\x04Nominations\x01] %t", "Map was not found", mapname); return Plugin_Handled; } @@ -218,7 +218,7 @@ public Action Command_Addmap(int client, int args) int status; if (!g_MapTrie.GetValue(resolvedMap, status)) { - CReplyToCommand(client, "[{olive}Nominations\x01] %t", "Map Not In Pool", displayName); + CReplyToCommand(client, "[\x04Nominations\x01] %t", "Map Not In Pool", displayName); return Plugin_Handled; } @@ -227,14 +227,14 @@ public Action Command_Addmap(int client, int args) if (result > Nominate_Replaced) { /* We assume already in vote is the casue because the maplist does a Map Validity check and we forced, so it can't be full */ - CReplyToCommand(client, "[{olive}Nominations\x01] %t", "Map Already In Vote", displayName); + CReplyToCommand(client, "[\x04Nominations\x01] %t", "Map Already In Vote", displayName); return Plugin_Handled; } g_MapTrie.SetValue(resolvedMap, MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_NOMINATED); - CReplyToCommand(client, "[{olive}Nominations\x01] %t", "Map Inserted", displayName); + CReplyToCommand(client, "[\x04Nominations\x01] %t", "Map Inserted", displayName); LogAction(client, -1, "\"%L\" inserted map \"%s\".", client, mapname); return Plugin_Handled; @@ -288,7 +288,7 @@ public Action Command_Nominate(int client, int args) if (matches <= 0) { - CReplyToCommand(client, "[{olive}Nominations\x01] %t", "Map was not found", mapname); + CReplyToCommand(client, "[\x04Nominations\x01] %t", "Map was not found", mapname); } // One result else if (matches == 1) @@ -367,7 +367,7 @@ void AttemptNominate(int client, const char[] map, int size, bool isVoteMenu) if (FindMap(map, mapname, size) == FindMap_NotFound) { // We couldn't resolve the map entry to a filename, so... - CReplyToCommand(client, "[{olive}Nominations\x01] %t", "Map was not found", mapname); + CReplyToCommand(client, "[\x04Nominations\x01] %t", "Map was not found", mapname); if (isVoteMenu && g_NativeVotes) { @@ -383,7 +383,7 @@ void AttemptNominate(int client, const char[] map, int size, bool isVoteMenu) int status; if (!g_MapTrie.GetValue(mapname, status)) { - CReplyToCommand(client, "[{olive}Nominations\x01] %t", "Map Not In Pool", displayName); + CReplyToCommand(client, "[\x04Nominations\x01] %t", "Map Not In Pool", displayName); if (isVoteMenu && g_NativeVotes) { NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_MapNotValid); @@ -399,7 +399,7 @@ void AttemptNominate(int client, const char[] map, int size, bool isVoteMenu) { NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_MapNotValid); } - CReplyToCommand(client, "[{olive}Nominations\x01] %t", "Can't Nominate Current Map"); + CReplyToCommand(client, "[\x04Nominations\x01] %t", "Can't Nominate Current Map"); } if ((status & MAPSTATUS_EXCLUDE_PREVIOUS) == MAPSTATUS_EXCLUDE_PREVIOUS) @@ -408,7 +408,7 @@ void AttemptNominate(int client, const char[] map, int size, bool isVoteMenu) { NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_MapNotValid); } - CReplyToCommand(client, "[{olive}Nominations\x01] %t", "Map in Exclude List"); + CReplyToCommand(client, "[\x04Nominations\x01] %t", "Map in Exclude List"); } if ((status & MAPSTATUS_EXCLUDE_NOMINATED) == MAPSTATUS_EXCLUDE_NOMINATED) @@ -417,7 +417,7 @@ void AttemptNominate(int client, const char[] map, int size, bool isVoteMenu) { NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_MapNotValid); } - CReplyToCommand(client, "[{olive}Nominations\x01] %t", "Map Already Nominated"); + CReplyToCommand(client, "[\x04Nominations\x01] %t", "Map Already Nominated"); } return; @@ -433,7 +433,7 @@ void AttemptNominate(int client, const char[] map, int size, bool isVoteMenu) { NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_MapNotValid); } - CReplyToCommand(client, "[{olive}Nominations\x01] %t", "Map Already In Vote", displayName); + CReplyToCommand(client, "[\x04Nominations\x01] %t", "Map Already In Vote", displayName); } else { @@ -441,7 +441,7 @@ void AttemptNominate(int client, const char[] map, int size, bool isVoteMenu) { NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_MapNotValid); } - CReplyToCommand(client, "[{olive}Nominations\x01] %t", "Max Nominations"); + CReplyToCommand(client, "[\x04Nominations\x01] %t", "Max Nominations"); } return; @@ -455,11 +455,11 @@ void AttemptNominate(int client, const char[] map, int size, bool isVoteMenu) if (result == Nominate_Added) { - CPrintToChatAllEx(client, "[{olive}Nominations\x01] %t", "Map Nominated", name, displayName); + CPrintToChatAllEx(client, "[\x04Nominations\x01] %t", "Map Nominated", name, displayName); } else { - CReplyToCommandEx(client, client, "[{olive}Nominations\x01] %t", "Map Nominated", name, displayName); + CReplyToCommandEx(client, client, "[\x04Nominations\x01] %t", "Map Nominated", name, displayName); } return; diff --git a/addons/sourcemod/scripting/nativevotes_rockthevote.sp b/addons/sourcemod/scripting/nativevotes_rockthevote.sp index e1542e9..f7d7876 100644 --- a/addons/sourcemod/scripting/nativevotes_rockthevote.sp +++ b/addons/sourcemod/scripting/nativevotes_rockthevote.sp @@ -55,7 +55,7 @@ public Plugin myinfo = name = "NativeVotes | Rock The Vote", author = "AlliedModders LLC and Powerlord", description = "Provides RTV Map Voting", - version = "26w06d", + version = "26w06e", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -280,7 +280,7 @@ public Action Command_ResetRTV(int client, int args) { ResetRTV(); - CPrintToChatAll("[{olive}Rock The Vote\x01] %t", "Cancelled Vote"); + CPrintToChatAll("[\x04Rock The Vote\x01] %t", "Cancelled Vote"); return Plugin_Handled; } @@ -317,7 +317,7 @@ void MenuHandler_UndoRTV(Menu menu, MenuAction action, int client, int param2) g_Voted[client] = false; if (g_Votes > 0) g_Votes--; - CPrintToChatAllEx(client, "[{olive}Rock The Vote\x01] %s: %t", name, "Cancelled Vote"); + CPrintToChatAllEx(client, "[\x04Rock The Vote\x01] %s: %t", name, "Cancelled Vote"); } } } @@ -332,7 +332,7 @@ void AttemptRTV(int client, bool isVoteMenu=false) { if (!g_RTVAllowed || (g_ConVars[postvoteaction].IntValue == 1 && HasEndOfMapVoteFinished())) { - CReplyToCommand(client, "[{olive}Rock The Vote\x01] %t", "RTV Not Allowed"); + CReplyToCommand(client, "[\x04Rock The Vote\x01] %t", "RTV Not Allowed"); if (isVoteMenu && g_NativeVotes) { if (g_Warmup) @@ -357,13 +357,13 @@ void AttemptRTV(int client, bool isVoteMenu=false) if (!CanMapChooserStartVote() && NativeVotes_IsVoteInProgress()) { - CReplyToCommand(client, "[{olive}Rock The Vote\x01] %t", "RTV Started"); + CReplyToCommand(client, "[\x04Rock The Vote\x01] %t", "RTV Started"); return; } if (GetClientCount(true) < g_ConVars[minplayers].IntValue) { - CReplyToCommand(client, "[{olive}Rock The Vote\x01] %t", "Minimal Players Not Met"); + CReplyToCommand(client, "[\x04Rock The Vote\x01] %t", "Minimal Players Not Met"); if (isVoteMenu && g_NativeVotes) { NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_Loading); @@ -383,7 +383,7 @@ void AttemptRTV(int client, bool isVoteMenu=false) g_Votes++; g_Voted[client] = true; - CPrintToChatAllEx(client, "[{olive}Rock The Vote\x01] %t", "RTV Requested", name, g_Votes, g_VotesNeeded); + CPrintToChatAllEx(client, "[\x04Rock The Vote\x01] %t", "RTV Requested", name, g_Votes, g_VotesNeeded); if (g_Votes >= g_VotesNeeded) { @@ -414,7 +414,7 @@ void StartRTV() GetMapDisplayName(map, map, sizeof(map)); Format(map, sizeof(map), "\x05%s\x01", map); - CPrintToChatAll("[{olive}Rock The Vote\x01] %t", "Changing Maps", map); + CPrintToChatAll("[\x04Rock The Vote\x01] %t", "Changing Maps", map); CreateTimer(5.0, Timer_ChangeMap, _, TIMER_FLAG_NO_MAPCHANGE); g_InChange = true; diff --git a/addons/sourcemod/scripting/nativevotes_voterp.sp b/addons/sourcemod/scripting/nativevotes_voterp.sp index b8b78d5..7a21ce1 100644 --- a/addons/sourcemod/scripting/nativevotes_voterp.sp +++ b/addons/sourcemod/scripting/nativevotes_voterp.sp @@ -14,7 +14,7 @@ public Plugin myinfo = name = "NativeVotes | Medieval Auto-RP", author = "Heapons", description = "Provides Medieval Auto-RP voting.", - version = "26w06d", + version = "26w06e", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -77,7 +77,7 @@ int MenuHandler_VoteRP(Menu menu, MenuAction action, int param1, int param2) menu.GetItem(item, info, sizeof(info)); int value = StringToInt(info); g_ConVars[tf_medieval_autorp].SetInt(value); - CPrintToChatAll("[{olive}NativeVotes\x01] tf_medieval_autorp set to %d", value); + CPrintToChatAll("[\x04NativeVotes\x01] tf_medieval_autorp set to %d", value); } case MenuAction_End: { @@ -96,14 +96,14 @@ Action Command_VoteRP(int client, int args) if ((g_NativeVotes && NativeVotes_IsVoteInProgress()) || (!g_NativeVotes && IsVoteInProgress())) { - CPrintToChat(client, "[{olive}NativeVotes\x01] A vote is already in progress."); + CPrintToChat(client, "[\x04NativeVotes\x01] A vote is already in progress."); return Plugin_Handled; } bool isMedieval = g_ConVars[tf_medieval].BoolValue || GameRules_GetProp("m_bPlayingMedieval") || FindEntityByClassname(-1, "tf_logic_medieval") != -1; if (!isMedieval) { - CPrintToChat(client, "[{olive}NativeVotes\x01] This vote is only available in Medieval Mode."); + CPrintToChat(client, "[\x04NativeVotes\x01] This vote is only available in Medieval Mode."); return Plugin_Handled; } From c0bf96f9412a33ebaaa17c273ecb0f0998071f0b Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sat, 7 Feb 2026 10:53:58 +0100 Subject: [PATCH 079/128] Fix oversight --- addons/sourcemod/scripting/nativevotes_votescramble.sp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/sourcemod/scripting/nativevotes_votescramble.sp b/addons/sourcemod/scripting/nativevotes_votescramble.sp index e5d9b8a..fb6bd53 100644 --- a/addons/sourcemod/scripting/nativevotes_votescramble.sp +++ b/addons/sourcemod/scripting/nativevotes_votescramble.sp @@ -172,7 +172,7 @@ public void Event_TeamplayRoundStart(Event event, const char[] name, bool dontBr AcceptEntityInput(entity, "SetStalemateOnTimelimit", 0, 0); } // Prevents map reset after scrambling teams - if (g_ConVars[restart_timelimit].IntValue >= 0) + if (g_ConVars[restart_timelimit].FloatValue >= 0.0) { GameRules_SetPropFloat("m_flMapResetTime", g_ConVars[restart_timelimit].FloatValue); } From 89dbb00c7126a2e36c6945abed51de6cf4618116 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sat, 7 Feb 2026 10:59:30 +0100 Subject: [PATCH 080/128] Add chat trigger --- .../scripting/nativevotes_votescramble.sp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/addons/sourcemod/scripting/nativevotes_votescramble.sp b/addons/sourcemod/scripting/nativevotes_votescramble.sp index fb6bd53..8408c59 100644 --- a/addons/sourcemod/scripting/nativevotes_votescramble.sp +++ b/addons/sourcemod/scripting/nativevotes_votescramble.sp @@ -162,6 +162,23 @@ public void OnClientDisconnect(int client) } } +public void OnClientSayCommand_Post(int client, const char[] command, const char[] sArgs) +{ + if (!client || IsChatTrigger()) + { + return; + } + + if (strcmp(sArgs, "scramble", false) == 0) + { + ReplySource old = SetCmdReplySource(SM_REPLY_TO_CHAT); + + Command_VoteScramble(client, 0); + + SetCmdReplySource(old); + } +} + public void Event_TeamplayRoundStart(Event event, const char[] name, bool dontBroadcast) { // Ends game mid-round once mp_timelimit runs out From a28351902e69241805f6d2ecda39f32113bcdb94 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sat, 7 Feb 2026 10:59:50 +0100 Subject: [PATCH 081/128] Bump version --- addons/sourcemod/scripting/nativevotes.sp | 2 +- addons/sourcemod/scripting/nativevotes_basecommands.sp | 2 +- addons/sourcemod/scripting/nativevotes_basevotes.sp | 2 +- addons/sourcemod/scripting/nativevotes_funvotes.sp | 2 +- addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp | 2 +- addons/sourcemod/scripting/nativevotes_mapchooser.sp | 2 +- addons/sourcemod/scripting/nativevotes_nominations.sp | 2 +- addons/sourcemod/scripting/nativevotes_rockthevote.sp | 2 +- addons/sourcemod/scripting/nativevotes_voterp.sp | 2 +- addons/sourcemod/scripting/nativevotes_votescramble.sp | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes.sp b/addons/sourcemod/scripting/nativevotes.sp index bb5b551..6b53714 100644 --- a/addons/sourcemod/scripting/nativevotes.sp +++ b/addons/sourcemod/scripting/nativevotes.sp @@ -153,7 +153,7 @@ public Plugin myinfo = name = "NativeVotes", author = "Powerlord", description = "Voting API to use the game's native vote panels. Compatible with L4D, L4D2, TF2, and CS:GO.", - version = "26w06e", + version = "26w06f", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" } diff --git a/addons/sourcemod/scripting/nativevotes_basecommands.sp b/addons/sourcemod/scripting/nativevotes_basecommands.sp index 4346769..a09873b 100644 --- a/addons/sourcemod/scripting/nativevotes_basecommands.sp +++ b/addons/sourcemod/scripting/nativevotes_basecommands.sp @@ -48,7 +48,7 @@ public Plugin myinfo = name = "NativeVotes | Basic Commands", author = "Powerlord and AlliedModders LLC", description = "Revote and Cancel support for NativeVotes", - version = "26w06e", + version = "26w06f", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" } diff --git a/addons/sourcemod/scripting/nativevotes_basevotes.sp b/addons/sourcemod/scripting/nativevotes_basevotes.sp index 5546348..fddc449 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes.sp @@ -46,7 +46,7 @@ public Plugin:myinfo = name = "NativeVotes | Basic Votes", author = "Powerlord and AlliedModders LLC", description = "NativeVotes Basic Vote Commands", - version = "26w06e", + version = "26w06f", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_funvotes.sp b/addons/sourcemod/scripting/nativevotes_funvotes.sp index e72471a..c31bd44 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes.sp @@ -47,7 +47,7 @@ public Plugin myinfo = name = "NativeVotes | Fun Votes", author = "Powerlord and AlliedModders LLC", description = "NativeVotes Fun Vote Commands", - version = "26w06e", + version = "26w06f", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp b/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp index fb8a975..2fe621e 100644 --- a/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp +++ b/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp @@ -60,7 +60,7 @@ public Plugin myinfo = { name = "NativeVotes | Kick Vote Immunity", author = "Powerlord", description = "Causes TF2 kick votes to fail against people who the current user can't target.", - version = "26w06e", + version = "26w06f", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_mapchooser.sp b/addons/sourcemod/scripting/nativevotes_mapchooser.sp index 7cbbc76..5b2052c 100644 --- a/addons/sourcemod/scripting/nativevotes_mapchooser.sp +++ b/addons/sourcemod/scripting/nativevotes_mapchooser.sp @@ -55,7 +55,7 @@ public Plugin myinfo = name = "NativeVotes | MapChooser", author = "AlliedModders LLC and Powerlord", description = "Automated Map Voting", - version = "26w06e", + version = "26w06f", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_nominations.sp b/addons/sourcemod/scripting/nativevotes_nominations.sp index fec659a..c0b82c9 100644 --- a/addons/sourcemod/scripting/nativevotes_nominations.sp +++ b/addons/sourcemod/scripting/nativevotes_nominations.sp @@ -48,7 +48,7 @@ public Plugin myinfo = name = "NativeVotes | Map Nominations", author = "AlliedModders LLC and Powerlord", description = "Provides Map Nominations", - version = "26w06e", + version = "26w06f", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_rockthevote.sp b/addons/sourcemod/scripting/nativevotes_rockthevote.sp index f7d7876..39e8c11 100644 --- a/addons/sourcemod/scripting/nativevotes_rockthevote.sp +++ b/addons/sourcemod/scripting/nativevotes_rockthevote.sp @@ -55,7 +55,7 @@ public Plugin myinfo = name = "NativeVotes | Rock The Vote", author = "AlliedModders LLC and Powerlord", description = "Provides RTV Map Voting", - version = "26w06e", + version = "26w06f", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_voterp.sp b/addons/sourcemod/scripting/nativevotes_voterp.sp index 7a21ce1..99cd530 100644 --- a/addons/sourcemod/scripting/nativevotes_voterp.sp +++ b/addons/sourcemod/scripting/nativevotes_voterp.sp @@ -14,7 +14,7 @@ public Plugin myinfo = name = "NativeVotes | Medieval Auto-RP", author = "Heapons", description = "Provides Medieval Auto-RP voting.", - version = "26w06e", + version = "26w06f", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_votescramble.sp b/addons/sourcemod/scripting/nativevotes_votescramble.sp index 8408c59..733c9c4 100644 --- a/addons/sourcemod/scripting/nativevotes_votescramble.sp +++ b/addons/sourcemod/scripting/nativevotes_votescramble.sp @@ -11,7 +11,7 @@ public Plugin myinfo = name = "NativeVotes | Scramble Teams", author = "Heapons", description = "Provides RTV-style Scramble Teams Voting", - version = "26w06e", + version = "26w06f", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; From 72ab09bbfd7364f1d4edb9a0b7d83df0124acc95 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sun, 8 Feb 2026 01:12:25 +0100 Subject: [PATCH 082/128] Instruction hint displays *Only on Team Fortress 2 Classified --- addons/sourcemod/scripting/nativevotes.sp | 226 ++++++++++++++++++++-- 1 file changed, 209 insertions(+), 17 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes.sp b/addons/sourcemod/scripting/nativevotes.sp index 6b53714..2c4ba4c 100644 --- a/addons/sourcemod/scripting/nativevotes.sp +++ b/addons/sourcemod/scripting/nativevotes.sp @@ -87,6 +87,7 @@ enum progress_chat, progress_console, progress_client_console, + progress_hintcaption, vote_delay, MAX_CONVARS @@ -118,7 +119,7 @@ float g_fStartTime; int g_TimeLeft; int g_ClientVotes[MAXPLAYERS+1]; bool g_bRevoting[MAXPLAYERS+1]; -char g_LeaderList[1024]; +int g_HintCaptionEnt = -1; ConVar sv_vote_holder_may_vote_no; @@ -262,12 +263,19 @@ public void OnPluginStart() LoadTranslations("core.phrases"); LoadTranslations("nativevotes.phrases.txt"); - g_ConVars[progress_hintbox] = CreateConVar("nativevotes_progress_hintbox", "0", "Show current vote progress in a hint box", FCVAR_NONE, true, 0.0, true, 1.0); - g_ConVars[progress_chat] = CreateConVar("nativevotes_progress_chat", "0", "Show current vote progress as chat messages", FCVAR_NONE, true, 0.0, true, 1.0); - g_ConVars[progress_console] = CreateConVar("nativevotes_progress_console", "0", "Show current vote progress as console messages", FCVAR_NONE, true, 0.0, true, 1.0); - g_ConVars[progress_client_console] = CreateConVar("nativevotes_progress_client_console", "0", "Show current vote progress as console messages to clients", FCVAR_NONE, true, 0.0, true, 1.0); - g_ConVars[vote_delay] = CreateConVar("nativevotes_vote_delay", "30", "Sets the recommended time in between public votes", FCVAR_NONE, true, 0.0); + g_ConVars[progress_hintbox] = CreateConVar("nativevotes_progress_hintbox", "0", "Show current vote progress in a hint box", _, true, 0.0, true, 1.0); + g_ConVars[progress_chat] = CreateConVar("nativevotes_progress_chat", "0", "Show current vote progress as chat messages", _, true, 0.0, true, 1.0); + g_ConVars[progress_console] = CreateConVar("nativevotes_progress_console", "0", "Show current vote progress as console messages", _, true, 0.0, true, 1.0); + g_ConVars[progress_client_console] = CreateConVar("nativevotes_progress_client_console", "0", "Show current vote progress as console messages to clients", _, true, 0.0, true, 1.0); + g_ConVars[vote_delay] = CreateConVar("nativevotes_vote_delay", "0", "Sets the recommended time in between public votes", _, true, 0.0); g_ConVars[vote_delay].AddChangeHook(OnVoteDelayChange); + switch (g_AppID) + { + case APP_TF2CLASSIFIED: + { + g_ConVars[progress_hintcaption] = CreateConVar("nativevotes_progress_hintcaption", "0", "Show current vote progress in a instructor hint caption", _, true, 0.0, true, 1.0); + } + } Game_InitializeCvars(); @@ -667,6 +675,12 @@ public void OnMapEnd() g_hDisplayTimer = null; } + if (g_HintCaptionEnt != -1 && IsValidEntity(g_HintCaptionEnt)) + { + RemoveEdict(g_HintCaptionEnt); + g_HintCaptionEnt = -1; + } + // g_hVoteTimer = INVALID_HANDLE; } @@ -786,7 +800,6 @@ void OnVoteSelect(NativeVote vote, int client, int item) } } - BuildVoteLeaders(); DrawHintProgress(); OnSelect(g_hCurVote, client, item); @@ -1024,7 +1037,9 @@ bool:SendResultCallback(Handle:vote, num_votes, num_items, const votes[][]) void DrawHintProgress() { - if (!g_ConVars[progress_hintbox].BoolValue || NativeVotes_GetType(g_hCurVote) == NativeVotesType_Custom_YesNo) + bool showHintbox = g_ConVars[progress_hintbox].BoolValue; + bool showHintCaption = g_ConVars[progress_hintcaption] != null && g_ConVars[progress_hintcaption].BoolValue; + if (!showHintbox && !showHintCaption) { return; } @@ -1038,12 +1053,68 @@ void DrawHintProgress() int iTimeRemaining = RoundFloat(timeRemaining); - PrintHintTextToAll("%t%s", "Vote Count", g_NumVotes, g_TotalClients, iTimeRemaining, g_LeaderList); + NativeVotesType voteType = Data_GetType(g_hCurVote); + bool isMultVote = (voteType == NativeVotesType_Custom_Mult || voteType == NativeVotesType_NextLevelMult); + char leaderList[256]; + if (isMultVote) + { + BuildVoteLeaders(leaderList, sizeof(leaderList)); + } + else + { + leaderList[0] = '\0'; + } + + if (showHintbox) + { + if (isMultVote) + { + PrintHintTextToAll("%t%s", "Vote Count", g_NumVotes, g_TotalClients, iTimeRemaining, leaderList); + } + else + { + PrintHintTextToAll("%t", "Vote Count", g_NumVotes, g_TotalClients, iTimeRemaining); + } + } + + if (showHintCaption) + { + char baseText[TRANSLATION_LENGTH]; + Format(baseText, sizeof(baseText), "%t", "Vote Count", g_NumVotes, g_TotalClients, iTimeRemaining); + if (isMultVote && leaderList[0] != '\0') + { + int baseLen = strlen(baseText); + int maxLeaderLen = 256 - baseLen - 1; + if (maxLeaderLen < 1) + { + ShowHintCaption(baseText); + } + else + { + char[] cappedLeaders = new char[maxLeaderLen + 1]; + BuildVoteLeaders(cappedLeaders, maxLeaderLen + 1); + char caption[256]; + Format(caption, sizeof(caption), "%s\n%s", baseText, cappedLeaders); + ShowHintCaption(caption); + } + } + else + { + ShowHintCaption(baseText); + } + } } -void BuildVoteLeaders() +void BuildVoteLeaders(char[] leaderList, int leaderListSize) { - if (g_NumVotes == 0 || !g_ConVars[progress_hintbox].BoolValue) + if (leaderListSize <= 1) + { + return; + } + + leaderList[0] = '\0'; + + if (g_NumVotes == 0) { return; } @@ -1055,15 +1126,50 @@ void BuildVoteLeaders() int num_items = Internal_GetResults(votes); - /* Take the top 3 (if applicable) and draw them */ - g_LeaderList[0] = '\0'; - - for (int i = 0; i < num_items && i < 3; i++) + int limit = leaderListSize - 1; + + /* Build the full leaderboard (bounded by buffer size) */ + for (int i = 0; i < num_items; i++) { int cur_item = votes[i][VOTEINFO_ITEM_INDEX]; char choice[256]; Data_GetItemDisplay(g_hCurVote, cur_item, choice, sizeof(choice)); - Format(g_LeaderList, sizeof(g_LeaderList), "%s\n%i. %s: (%i)", g_LeaderList, i+1, choice, votes[i][VOTEINFO_ITEM_VOTES]); + char line[256]; + Format(line, sizeof(line), "%i. %s: (%i)", i + 1, choice, votes[i][VOTEINFO_ITEM_VOTES]); + int used = strlen(leaderList); + int sepLen = (used == 0) ? 0 : 1; + int lineLen = strlen(line); + if (used + sepLen + lineLen > limit) + { + int remaining = num_items - i; + if (remaining > 0) + { + char moreLine[64]; + Format(moreLine, sizeof(moreLine), "... (+%d more)", remaining); + int moreLen = strlen(moreLine); + if (used + sepLen + moreLen <= limit) + { + if (used == 0) + { + Format(leaderList, limit + 1, "%s", moreLine); + } + else + { + Format(leaderList, limit + 1, "%s\n%s", leaderList, moreLine); + } + } + } + break; + } + + if (used == 0) + { + Format(leaderList, limit + 1, "%s", line); + } + else + { + Format(leaderList, limit + 1, "%s\n%s", leaderList, line); + } } } @@ -1099,6 +1205,8 @@ void DecrementPlayerCount() void EndVoting() { + EndHintCaption(); + int voteDelay = g_ConVars[vote_delay].IntValue; if (voteDelay < 1) { @@ -1417,7 +1525,6 @@ void Internal_Reset(bool cancel=false) g_hCurVote = null; g_NumVotes = 0; g_bCancelled = false; - g_LeaderList[0] = '\0'; g_TotalClients = 0; if (g_hDisplayTimer != null) @@ -2549,4 +2656,89 @@ public int Native_RedrawVoteItem(Handle plugin, int numParams) GetNativeString(1, g_newMenuItem, TRANSLATION_LENGTH); return view_as<int>(Plugin_Changed); +} + +void SetupInstructorHint() +{ + if (g_HintCaptionEnt != -1 && IsValidEntity(g_HintCaptionEnt)) + { + return; + } + + g_HintCaptionEnt = CreateEntityByName("env_instructor_hint"); + if (g_HintCaptionEnt == -1) + { + return; + } + + DispatchKeyValue(g_HintCaptionEnt, "hint_replace_key", "nativevotes_progress"); + DispatchKeyValue(g_HintCaptionEnt, "hint_static", "1"); + DispatchKeyValue(g_HintCaptionEnt, "hint_allow_nodraw_target", "1"); + DispatchKeyValue(g_HintCaptionEnt, "hint_nooffscreen", "1"); + DispatchKeyValue(g_HintCaptionEnt, "hint_forcecaption", "1"); + DispatchKeyValue(g_HintCaptionEnt, "hint_timeout", "2"); + DispatchKeyValue(g_HintCaptionEnt, "hint_icon_onscreen", "tf2c_clock"); + DispatchKeyValue(g_HintCaptionEnt, "hint_icon_offscreen", "tf2c_clock"); + DispatchSpawn(g_HintCaptionEnt); +} + +void ShowHintCaption(const char[] caption) +{ + if (g_ConVars[progress_hintcaption] == null || !g_ConVars[progress_hintcaption].BoolValue) + { + return; + } + + SetupInstructorHint(); + if (g_HintCaptionEnt == -1 || !IsValidEntity(g_HintCaptionEnt)) + { + return; + } + + DispatchKeyValue(g_HintCaptionEnt, "hint_caption", caption); + DispatchKeyValue(g_HintCaptionEnt, "hint_activator_caption", caption); + bool sent = false; + for (int i = 1; i <= MaxClients; i++) + { + if (IsClientInGame(i) && !IsFakeClient(i)) + { + SetVariantString(caption); + AcceptEntityInput(g_HintCaptionEnt, "SetCaption", i, i); + AcceptEntityInput(g_HintCaptionEnt, "ShowHint", i, i); + sent = true; + } + } + + if (!sent) + { + return; + } +} + +void EndHintCaption() +{ + if (g_ConVars[progress_hintcaption] == null || !g_ConVars[progress_hintcaption].BoolValue) + { + return; + } + + if (g_HintCaptionEnt == -1 || !IsValidEntity(g_HintCaptionEnt)) + { + return; + } + + bool sent = false; + for (int i = 1; i <= MaxClients; i++) + { + if (IsClientInGame(i) && !IsFakeClient(i)) + { + AcceptEntityInput(g_HintCaptionEnt, "EndHint", i, i); + sent = true; + } + } + + if (!sent) + { + return; + } } \ No newline at end of file From fd0dede1ce97eeccd1b7eb09e25ad2129106b2a2 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sun, 8 Feb 2026 01:14:45 +0100 Subject: [PATCH 083/128] Brought back "You already voted" feedback --- .../scripting/nativevotes_rockthevote.sp | 17 +++++++------ .../scripting/nativevotes_votescramble.sp | 25 +++++++++++-------- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_rockthevote.sp b/addons/sourcemod/scripting/nativevotes_rockthevote.sp index 39e8c11..181b31c 100644 --- a/addons/sourcemod/scripting/nativevotes_rockthevote.sp +++ b/addons/sourcemod/scripting/nativevotes_rockthevote.sp @@ -50,6 +50,8 @@ #pragma semicolon 1 #pragma newdecls required +#define PLUGIN_PREFIX "[\x04Rock The Vote\x01]" + public Plugin myinfo = { name = "NativeVotes | Rock The Vote", @@ -280,7 +282,7 @@ public Action Command_ResetRTV(int client, int args) { ResetRTV(); - CPrintToChatAll("[\x04Rock The Vote\x01] %t", "Cancelled Vote"); + CPrintToChatAll(PLUGIN_PREFIX ... " %t", "Cancelled Vote"); return Plugin_Handled; } @@ -317,7 +319,7 @@ void MenuHandler_UndoRTV(Menu menu, MenuAction action, int client, int param2) g_Voted[client] = false; if (g_Votes > 0) g_Votes--; - CPrintToChatAllEx(client, "[\x04Rock The Vote\x01] %s: %t", name, "Cancelled Vote"); + CPrintToChatAllEx(client, PLUGIN_PREFIX ... " %s: %t", name, "Cancelled Vote"); } } } @@ -332,7 +334,7 @@ void AttemptRTV(int client, bool isVoteMenu=false) { if (!g_RTVAllowed || (g_ConVars[postvoteaction].IntValue == 1 && HasEndOfMapVoteFinished())) { - CReplyToCommand(client, "[\x04Rock The Vote\x01] %t", "RTV Not Allowed"); + CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "RTV Not Allowed"); if (isVoteMenu && g_NativeVotes) { if (g_Warmup) @@ -357,13 +359,13 @@ void AttemptRTV(int client, bool isVoteMenu=false) if (!CanMapChooserStartVote() && NativeVotes_IsVoteInProgress()) { - CReplyToCommand(client, "[\x04Rock The Vote\x01] %t", "RTV Started"); + CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "RTV Started"); return; } if (GetClientCount(true) < g_ConVars[minplayers].IntValue) { - CReplyToCommand(client, "[\x04Rock The Vote\x01] %t", "Minimal Players Not Met"); + CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Minimal Players Not Met"); if (isVoteMenu && g_NativeVotes) { NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_Loading); @@ -373,6 +375,7 @@ void AttemptRTV(int client, bool isVoteMenu=false) if (g_Voted[client]) { + CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Already Voted", g_Votes, g_VotesNeeded); UndoRTV(client); return; } @@ -383,7 +386,7 @@ void AttemptRTV(int client, bool isVoteMenu=false) g_Votes++; g_Voted[client] = true; - CPrintToChatAllEx(client, "[\x04Rock The Vote\x01] %t", "RTV Requested", name, g_Votes, g_VotesNeeded); + CPrintToChatAllEx(client, PLUGIN_PREFIX ... " %t", "RTV Requested", name, g_Votes, g_VotesNeeded); if (g_Votes >= g_VotesNeeded) { @@ -414,7 +417,7 @@ void StartRTV() GetMapDisplayName(map, map, sizeof(map)); Format(map, sizeof(map), "\x05%s\x01", map); - CPrintToChatAll("[\x04Rock The Vote\x01] %t", "Changing Maps", map); + CPrintToChatAll(PLUGIN_PREFIX ... " %t", "Changing Maps", map); CreateTimer(5.0, Timer_ChangeMap, _, TIMER_FLAG_NO_MAPCHANGE); g_InChange = true; diff --git a/addons/sourcemod/scripting/nativevotes_votescramble.sp b/addons/sourcemod/scripting/nativevotes_votescramble.sp index 733c9c4..54720ee 100644 --- a/addons/sourcemod/scripting/nativevotes_votescramble.sp +++ b/addons/sourcemod/scripting/nativevotes_votescramble.sp @@ -6,6 +6,8 @@ #include <nativevotes> #define REQUIRE_PLUGIN +#define PLUGIN_PREFIX "[\x04Scramble Teams\x01]" + public Plugin myinfo = { name = "NativeVotes | Scramble Teams", @@ -24,6 +26,7 @@ enum restart_round, restart_timelimit, mp_match_end_at_timelimit, + mp_timelimit, MAX_CONVARS } @@ -52,6 +55,7 @@ public void OnPluginStart() g_ConVars[restart_round] = CreateConVar("sm_scrambleteams_restart_round", "0", "Whether to restart the round after scrambling teams", 0, true, 0.0, true, 1.0); g_ConVars[restart_timelimit] = CreateConVar("sm_scrambleteams_restart_timelimit", "-1.0", "Override map timer after scrambling teams (leave it to '-1.0' for default behaviour)", 0, true, -1.0); g_ConVars[mp_match_end_at_timelimit] = FindConVar("mp_match_end_at_timelimit"); + g_ConVars[mp_timelimit] = FindConVar("mp_timelimit"); RegConsoleCmd("sm_votescramble", Command_VoteScramble); RegConsoleCmd("sm_scramble", Command_VoteScramble); @@ -216,7 +220,7 @@ public Action Command_ForceScramble(int client, int args) public Action Command_ResetScramble(int client, int args) { ResetScramble(); - CPrintToChatAll("[\x04Scramble Teams\x01] %t", "Cancelled Vote"); + CPrintToChatAll(PLUGIN_PREFIX ... " %t", "Cancelled Vote"); return Plugin_Handled; } @@ -252,7 +256,7 @@ void MenuHandler_UndoScramble(Menu menu, MenuAction action, int client, int para g_Voted[client] = false; if (g_Votes > 0) g_Votes--; - CPrintToChatAllEx(client, "[\x04Scramble Teams\x01] %s: %t", name, "Cancelled Vote"); + CPrintToChatAllEx(client, PLUGIN_PREFIX ... " %s: %t", name, "Cancelled Vote"); } } } @@ -267,7 +271,7 @@ void AttemptScramble(int client, bool isVoteMenu=false) { if (!g_ScrambleAllowed) { - CReplyToCommand(client, "[\x04Scramble Teams\x01] %t", "Scramble Not Allowed"); + CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Scramble Not Allowed"); if (isVoteMenu && g_NativeVotes) { int timeleft = g_ScrambleTime - GetTime(); @@ -285,13 +289,13 @@ void AttemptScramble(int client, bool isVoteMenu=false) if (g_NativeVotes && NativeVotes_IsVoteInProgress()) { - CReplyToCommand(client, "[\x04Scramble Teams\x01] %t", "Vote in Progress"); + CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Vote in Progress"); return; } if (GetClientCount(true) < g_ConVars[minplayers].IntValue) { - CReplyToCommand(client, "[\x04Scramble Teams\x01] %t", "Minimal Players Not Met"); + CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Minimal Players Not Met"); if (isVoteMenu && g_NativeVotes) { NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_Loading); @@ -301,6 +305,7 @@ void AttemptScramble(int client, bool isVoteMenu=false) if (g_Voted[client]) { + CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Already Voted", g_Votes, g_VotesNeeded); UndoScramble(client); return; } @@ -311,7 +316,7 @@ void AttemptScramble(int client, bool isVoteMenu=false) g_Votes++; g_Voted[client] = true; - CPrintToChatAllEx(client, "[\x04Scramble Teams\x01] %t", "Scramble Requested", name, g_Votes, g_VotesNeeded); + CPrintToChatAllEx(client, PLUGIN_PREFIX ... " %t", "Scramble Requested", name, g_Votes, g_VotesNeeded); if (g_Votes >= g_VotesNeeded) { @@ -429,7 +434,7 @@ void ScrambleVoteResult(NativeVote vote, int num_votes, int num_clients, const i { NativeVotes_DisplayPassEx(vote, NativeVotesPass_Scramble); - CPrintToChatAll("[\x04Scramble Teams\x01] %t", "Scrambling Teams"); + CPrintToChatAll(PLUGIN_PREFIX ... " %t", "Scrambling Teams"); ServerCommand("mp_scrambleteams"); if (g_ConVars[restart_round].BoolValue) { @@ -439,7 +444,7 @@ void ScrambleVoteResult(NativeVote vote, int num_votes, int num_clients, const i else if (yesVotes == 0 && noVotes == 0) { NativeVotes_DisplayFail(vote, NativeVotesFail_NotEnoughVotes); - CPrintToChatAll("[\x04Scramble Teams\x01] %t", "No Votes"); + CPrintToChatAll(PLUGIN_PREFIX ... " %t", "No Votes"); } else { @@ -455,7 +460,7 @@ int MenuHandler_Scramble(Menu menu, MenuAction action, int client, int param2) { if (param2 == 0) { - CPrintToChatAll("[\x04Scramble Teams\x01] %t", "Scrambling Teams"); + CPrintToChatAll(PLUGIN_PREFIX ... " %t", "Scrambling Teams"); ServerCommand("mp_scrambleteams"); if (g_ConVars[restart_round].BoolValue) { @@ -464,7 +469,7 @@ int MenuHandler_Scramble(Menu menu, MenuAction action, int client, int param2) } else { - CPrintToChatAll("[\x04Scramble Teams\x01] %t", "No Votes"); + CPrintToChatAll(PLUGIN_PREFIX ... " %t", "No Votes"); } } case MenuAction_End: From 839484a6f061ee51e8f65de68132ef28b1e60888 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sun, 8 Feb 2026 01:16:14 +0100 Subject: [PATCH 084/128] Bump version --- addons/sourcemod/scripting/nativevotes.sp | 2 +- .../scripting/nativevotes_basecommands.sp | 8 ++-- .../scripting/nativevotes_basevotes.sp | 5 ++- .../scripting/nativevotes_funvotes.sp | 2 +- .../nativevotes_kickvote_immunity.sp | 2 +- .../scripting/nativevotes_mapchooser.sp | 45 ++++++++++--------- .../scripting/nativevotes_nominations.sp | 34 +++++++------- .../scripting/nativevotes_rockthevote.sp | 2 +- .../sourcemod/scripting/nativevotes_voterp.sp | 10 +++-- .../scripting/nativevotes_votescramble.sp | 2 +- 10 files changed, 62 insertions(+), 50 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes.sp b/addons/sourcemod/scripting/nativevotes.sp index 2c4ba4c..960dd73 100644 --- a/addons/sourcemod/scripting/nativevotes.sp +++ b/addons/sourcemod/scripting/nativevotes.sp @@ -154,7 +154,7 @@ public Plugin myinfo = name = "NativeVotes", author = "Powerlord", description = "Voting API to use the game's native vote panels. Compatible with L4D, L4D2, TF2, and CS:GO.", - version = "26w06f", + version = "26w06g", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" } diff --git a/addons/sourcemod/scripting/nativevotes_basecommands.sp b/addons/sourcemod/scripting/nativevotes_basecommands.sp index a09873b..88a1bd5 100644 --- a/addons/sourcemod/scripting/nativevotes_basecommands.sp +++ b/addons/sourcemod/scripting/nativevotes_basecommands.sp @@ -41,6 +41,8 @@ #pragma newdecls required #pragma semicolon 1 +#define PLUGIN_PREFIX "[\x04NativeVotes\x01]" + TopMenu hTopMenu; public Plugin myinfo = @@ -48,7 +50,7 @@ public Plugin myinfo = name = "NativeVotes | Basic Commands", author = "Powerlord and AlliedModders LLC", description = "Revote and Cancel support for NativeVotes", - version = "26w06f", + version = "26w06g", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" } @@ -163,7 +165,7 @@ public Action Command_ReVote(int client, const char[] command, int argc) return Plugin_Continue; } - CReplyToCommand(client, "[\x04NativeVotes\x01] %t", "Cannot participate in vote"); + CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Cannot participate in vote"); return Plugin_Stop; } @@ -173,7 +175,7 @@ public Action Command_ReVote(int client, const char[] command, int argc) } else if (!IsVoteInProgress()) { - CReplyToCommand(client, "[\x04NativeVotes\x01] %t", "Cannot change vote"); + CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Cannot change vote"); return Plugin_Stop; } diff --git a/addons/sourcemod/scripting/nativevotes_basevotes.sp b/addons/sourcemod/scripting/nativevotes_basevotes.sp index fddc449..e47f78e 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes.sp @@ -40,13 +40,14 @@ #include <adminmenu> #include <nativevotes> +#define PLUGIN_PREFIX "[\x04NativeVotes\x01]" public Plugin:myinfo = { name = "NativeVotes | Basic Votes", author = "Powerlord and AlliedModders LLC", description = "NativeVotes Basic Vote Commands", - version = "26w06f", + version = "26w06g", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -225,7 +226,7 @@ public Action Command_Vote(int client, int args) { if (args < 1) { - CReplyToCommand(client, "[\x04NativeVotes\x01] Usage: sm_vote <title> [option1] [option2] ... [option5]"); + CReplyToCommand(client, PLUGIN_PREFIX ... " Usage: sm_vote <title> [option1] [option2] ... [option5]"); return Plugin_Handled; } diff --git a/addons/sourcemod/scripting/nativevotes_funvotes.sp b/addons/sourcemod/scripting/nativevotes_funvotes.sp index c31bd44..233d5f5 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes.sp @@ -47,7 +47,7 @@ public Plugin myinfo = name = "NativeVotes | Fun Votes", author = "Powerlord and AlliedModders LLC", description = "NativeVotes Fun Vote Commands", - version = "26w06f", + version = "26w06g", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp b/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp index 2fe621e..028eb34 100644 --- a/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp +++ b/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp @@ -60,7 +60,7 @@ public Plugin myinfo = { name = "NativeVotes | Kick Vote Immunity", author = "Powerlord", description = "Causes TF2 kick votes to fail against people who the current user can't target.", - version = "26w06f", + version = "26w06g", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_mapchooser.sp b/addons/sourcemod/scripting/nativevotes_mapchooser.sp index 5b2052c..cc8f2e1 100644 --- a/addons/sourcemod/scripting/nativevotes_mapchooser.sp +++ b/addons/sourcemod/scripting/nativevotes_mapchooser.sp @@ -50,12 +50,14 @@ #pragma semicolon 1 #pragma newdecls required +#define PLUGIN_PREFIX "[\x04MapChooser\x01]" + public Plugin myinfo = { name = "NativeVotes | MapChooser", author = "AlliedModders LLC and Powerlord", description = "Automated Map Voting", - version = "26w06f", + version = "26w06g", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -401,7 +403,7 @@ public Action Command_SetNextMap(int client, int args) { if (args < 1) { - CReplyToCommand(client, "[\x04MapChooser\x01] Usage: sm_setnextmap <map>"); + CReplyToCommand(client, PLUGIN_PREFIX ... " Usage: sm_setnextmap <map>"); return Plugin_Handled; } @@ -410,7 +412,7 @@ public Action Command_SetNextMap(int client, int args) if (FindMap(map, displayName, sizeof(displayName)) == FindMap_NotFound) { - CReplyToCommand(client, "[\x04MapChooser\x01] %t", "Map was not found", map); + CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Map was not found", map); return Plugin_Handled; } @@ -888,7 +890,7 @@ void InitiateVote(MapChange when, ArrayList inputlist=null) } LogAction(-1, -1, "Voting for next map has started."); - CPrintToChatAll("[\x04MapChooser\x01] %t", "Nextmap Voting Started"); + CPrintToChatAll(PLUGIN_PREFIX ... " %t", "Nextmap Voting Started"); } public void Handler_NV_VoteFinishedGeneric(NativeVote menu, int num_votes, int num_clients, const int[] client_indexes, const int[] client_votes, int num_items, const int[] item_indexes, const int[] item_votes) @@ -956,7 +958,7 @@ public void Handler_VoteFinishedGenericShared(const char[] map, const char[] dis } } - CPrintToChatAll("[\x04MapChooser\x01] %t", "Current Map Extended", RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes); + CPrintToChatAll(PLUGIN_PREFIX ... " %t", "Current Map Extended", RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes); LogAction(-1, -1, "Voting for next map has finished. The current map has been extended."); if (isNativeVotes) @@ -972,7 +974,7 @@ public void Handler_VoteFinishedGenericShared(const char[] map, const char[] dis } else if (strcmp(map, VOTE_DONTCHANGE, false) == 0) { - CPrintToChatAll("[\x04MapChooser\x01] %t", "Current Map Stays", RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes); + CPrintToChatAll(PLUGIN_PREFIX ... " %t", "Current Map Stays", RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes); LogAction(-1, -1, "Voting for next map has finished. 'No Change' was the winner"); if (isNativeVotes) @@ -1013,7 +1015,7 @@ public void Handler_VoteFinishedGenericShared(const char[] map, const char[] dis char formattedName[PLATFORM_MAX_PATH]; Format(formattedName, sizeof(formattedName), "\x05%s\x01", displayName); - CPrintToChatAll("[\x04MapChooser\x01] %t", "Nextmap Voting Finished", formattedName, RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes); + CPrintToChatAll(PLUGIN_PREFIX ... " %t", "Nextmap Voting Finished", formattedName, RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes); LogAction(-1, -1, "Voting for next map has finished. Nextmap: %s.", map); } } @@ -1640,7 +1642,7 @@ void PopulateMapList() continue; len = strlen(mapName); - if (len <= 4 || !StrEqual(mapName[len-4], ".bsp", false)) + if (len <= 4 || !StrEqual(mapName[len-4], ".bsp")) continue; mapName[len-4] = '\0'; @@ -1667,7 +1669,21 @@ void PopulateMapList() { CloseHandle(file); } - RequestFrame(RequestFrame_ReadMapList); + + CreateTimer(3.0, Timer_ReadMapList, _, TIMER_FLAG_NO_MAPCHANGE|TIMER_REPEAT); +} + +public Action Timer_ReadMapList(Handle timer, any data) +{ + if (ReadMapList(g_MapList, g_mapFileSerial, "mapchooser", MAPLIST_FLAG_CLEARARRAY|MAPLIST_FLAG_MAPSFOLDER) != null) + { + if (g_mapFileSerial == -1) + { + LogError("Unable to create a valid map list."); + return Plugin_Continue; + } + } + return Plugin_Stop; } void HTTPResponse_GetCollectionDetails(HTTPResponse response, File file) @@ -1788,15 +1804,4 @@ void CleanupWorkshopMaps() delete dir; } RemoveDir(workshopDir); -} - -void RequestFrame_ReadMapList() -{ - if (ReadMapList(g_MapList, g_mapFileSerial, "mapchooser", MAPLIST_FLAG_CLEARARRAY|MAPLIST_FLAG_MAPSFOLDER) != null) - { - if (g_mapFileSerial == -1) - { - LogError("Unable to create a valid map list."); - } - } } \ No newline at end of file diff --git a/addons/sourcemod/scripting/nativevotes_nominations.sp b/addons/sourcemod/scripting/nativevotes_nominations.sp index c0b82c9..e2bc5bf 100644 --- a/addons/sourcemod/scripting/nativevotes_nominations.sp +++ b/addons/sourcemod/scripting/nativevotes_nominations.sp @@ -43,12 +43,14 @@ #pragma semicolon 1 #pragma newdecls required +#define PLUGIN_PREFIX "[\x04Nominations\x01]" + public Plugin myinfo = { name = "NativeVotes | Map Nominations", author = "AlliedModders LLC and Powerlord", description = "Provides Map Nominations", - version = "26w06f", + version = "26w06g", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -197,7 +199,7 @@ public Action Command_Addmap(int client, int args) { if (args < 1) { - CReplyToCommand(client, "[\x04NativeVotes\x01] Usage: sm_nominate_addmap <mapname>"); + CReplyToCommand(client, PLUGIN_PREFIX ... " Usage: sm_nominate_addmap <mapname>"); return Plugin_Handled; } @@ -207,7 +209,7 @@ public Action Command_Addmap(int client, int args) if (FindMap(mapname, resolvedMap, sizeof(resolvedMap)) == FindMap_NotFound) { // We couldn't resolve the map entry to a filename, so... - CReplyToCommand(client, "[\x04Nominations\x01] %t", "Map was not found", mapname); + CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Map was not found", mapname); return Plugin_Handled; } @@ -218,7 +220,7 @@ public Action Command_Addmap(int client, int args) int status; if (!g_MapTrie.GetValue(resolvedMap, status)) { - CReplyToCommand(client, "[\x04Nominations\x01] %t", "Map Not In Pool", displayName); + CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Map Not In Pool", displayName); return Plugin_Handled; } @@ -227,14 +229,14 @@ public Action Command_Addmap(int client, int args) if (result > Nominate_Replaced) { /* We assume already in vote is the casue because the maplist does a Map Validity check and we forced, so it can't be full */ - CReplyToCommand(client, "[\x04Nominations\x01] %t", "Map Already In Vote", displayName); + CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Map Already In Vote", displayName); return Plugin_Handled; } g_MapTrie.SetValue(resolvedMap, MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_NOMINATED); - CReplyToCommand(client, "[\x04Nominations\x01] %t", "Map Inserted", displayName); + CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Map Inserted", displayName); LogAction(client, -1, "\"%L\" inserted map \"%s\".", client, mapname); return Plugin_Handled; @@ -288,7 +290,7 @@ public Action Command_Nominate(int client, int args) if (matches <= 0) { - CReplyToCommand(client, "[\x04Nominations\x01] %t", "Map was not found", mapname); + CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Map was not found", mapname); } // One result else if (matches == 1) @@ -367,7 +369,7 @@ void AttemptNominate(int client, const char[] map, int size, bool isVoteMenu) if (FindMap(map, mapname, size) == FindMap_NotFound) { // We couldn't resolve the map entry to a filename, so... - CReplyToCommand(client, "[\x04Nominations\x01] %t", "Map was not found", mapname); + CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Map was not found", mapname); if (isVoteMenu && g_NativeVotes) { @@ -383,7 +385,7 @@ void AttemptNominate(int client, const char[] map, int size, bool isVoteMenu) int status; if (!g_MapTrie.GetValue(mapname, status)) { - CReplyToCommand(client, "[\x04Nominations\x01] %t", "Map Not In Pool", displayName); + CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Map Not In Pool", displayName); if (isVoteMenu && g_NativeVotes) { NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_MapNotValid); @@ -399,7 +401,7 @@ void AttemptNominate(int client, const char[] map, int size, bool isVoteMenu) { NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_MapNotValid); } - CReplyToCommand(client, "[\x04Nominations\x01] %t", "Can't Nominate Current Map"); + CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Can't Nominate Current Map"); } if ((status & MAPSTATUS_EXCLUDE_PREVIOUS) == MAPSTATUS_EXCLUDE_PREVIOUS) @@ -408,7 +410,7 @@ void AttemptNominate(int client, const char[] map, int size, bool isVoteMenu) { NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_MapNotValid); } - CReplyToCommand(client, "[\x04Nominations\x01] %t", "Map in Exclude List"); + CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Map in Exclude List"); } if ((status & MAPSTATUS_EXCLUDE_NOMINATED) == MAPSTATUS_EXCLUDE_NOMINATED) @@ -417,7 +419,7 @@ void AttemptNominate(int client, const char[] map, int size, bool isVoteMenu) { NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_MapNotValid); } - CReplyToCommand(client, "[\x04Nominations\x01] %t", "Map Already Nominated"); + CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Map Already Nominated"); } return; @@ -433,7 +435,7 @@ void AttemptNominate(int client, const char[] map, int size, bool isVoteMenu) { NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_MapNotValid); } - CReplyToCommand(client, "[\x04Nominations\x01] %t", "Map Already In Vote", displayName); + CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Map Already In Vote", displayName); } else { @@ -441,7 +443,7 @@ void AttemptNominate(int client, const char[] map, int size, bool isVoteMenu) { NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_MapNotValid); } - CReplyToCommand(client, "[\x04Nominations\x01] %t", "Max Nominations"); + CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Max Nominations"); } return; @@ -455,11 +457,11 @@ void AttemptNominate(int client, const char[] map, int size, bool isVoteMenu) if (result == Nominate_Added) { - CPrintToChatAllEx(client, "[\x04Nominations\x01] %t", "Map Nominated", name, displayName); + CPrintToChatAllEx(client, PLUGIN_PREFIX ... " %t", "Map Nominated", name, displayName); } else { - CReplyToCommandEx(client, client, "[\x04Nominations\x01] %t", "Map Nominated", name, displayName); + CReplyToCommandEx(client, client, PLUGIN_PREFIX ... " %t", "Map Nominated", name, displayName); } return; diff --git a/addons/sourcemod/scripting/nativevotes_rockthevote.sp b/addons/sourcemod/scripting/nativevotes_rockthevote.sp index 181b31c..0b876ef 100644 --- a/addons/sourcemod/scripting/nativevotes_rockthevote.sp +++ b/addons/sourcemod/scripting/nativevotes_rockthevote.sp @@ -57,7 +57,7 @@ public Plugin myinfo = name = "NativeVotes | Rock The Vote", author = "AlliedModders LLC and Powerlord", description = "Provides RTV Map Voting", - version = "26w06f", + version = "26w06g", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_voterp.sp b/addons/sourcemod/scripting/nativevotes_voterp.sp index 99cd530..07e9d40 100644 --- a/addons/sourcemod/scripting/nativevotes_voterp.sp +++ b/addons/sourcemod/scripting/nativevotes_voterp.sp @@ -9,12 +9,14 @@ #pragma semicolon 1 #pragma newdecls required +#define PLUGIN_PREFIX "[\x04NativeVotes\x01]" + public Plugin myinfo = { name = "NativeVotes | Medieval Auto-RP", author = "Heapons", description = "Provides Medieval Auto-RP voting.", - version = "26w06f", + version = "26w06g", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -77,7 +79,7 @@ int MenuHandler_VoteRP(Menu menu, MenuAction action, int param1, int param2) menu.GetItem(item, info, sizeof(info)); int value = StringToInt(info); g_ConVars[tf_medieval_autorp].SetInt(value); - CPrintToChatAll("[\x04NativeVotes\x01] tf_medieval_autorp set to %d", value); + CPrintToChatAll(PLUGIN_PREFIX ... " tf_medieval_autorp set to %d", value); } case MenuAction_End: { @@ -96,14 +98,14 @@ Action Command_VoteRP(int client, int args) if ((g_NativeVotes && NativeVotes_IsVoteInProgress()) || (!g_NativeVotes && IsVoteInProgress())) { - CPrintToChat(client, "[\x04NativeVotes\x01] A vote is already in progress."); + CPrintToChat(client, PLUGIN_PREFIX ... " A vote is already in progress."); return Plugin_Handled; } bool isMedieval = g_ConVars[tf_medieval].BoolValue || GameRules_GetProp("m_bPlayingMedieval") || FindEntityByClassname(-1, "tf_logic_medieval") != -1; if (!isMedieval) { - CPrintToChat(client, "[\x04NativeVotes\x01] This vote is only available in Medieval Mode."); + CPrintToChat(client, PLUGIN_PREFIX ... " This vote is only available in Medieval Mode."); return Plugin_Handled; } diff --git a/addons/sourcemod/scripting/nativevotes_votescramble.sp b/addons/sourcemod/scripting/nativevotes_votescramble.sp index 54720ee..e4c7267 100644 --- a/addons/sourcemod/scripting/nativevotes_votescramble.sp +++ b/addons/sourcemod/scripting/nativevotes_votescramble.sp @@ -13,7 +13,7 @@ public Plugin myinfo = name = "NativeVotes | Scramble Teams", author = "Heapons", description = "Provides RTV-style Scramble Teams Voting", - version = "26w06f", + version = "26w06g", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; From 78559e3dce8f05253f0835c7c5a7bcd7dea8361c Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sun, 8 Feb 2026 01:21:01 +0100 Subject: [PATCH 085/128] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 644ab3b..9100b7f 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,8 @@ This fork aims to expand upon [<img src="https://avatars.githubusercontent.com/u - Team-colored player names. - Highlight map names. - Update [Nominations](https://github.com/Heapons/sourcemod-nativevotes-updated/blob/master/addons/sourcemod/scripting/nativevotes_nominations.sp) and [Rock The Vote](https://github.com/Heapons/sourcemod-nativevotes-updated/blob/master/addons/sourcemod/scripting/nativevotes_rockthevote.sp) to be on par with the latest [Sourcemod](https://github.com/alliedmodders/sourcemod/tree/master/plugins) version. +- Vote Progress HUD: + - <img src="https://shared.fastly.steamstatic.com/community_assets/images/apps/3545060/08607ace82bfb52cf8993efe88c2ef00fa25c96f.ico" width="16" height="16" style="vertical-align: text-bottom;"> Added `nativevotes_progress_hintcaption` convar to use the style of tutorial hints. ## <img src="https://cdn.fastly.steamstatic.com/steamcommunity/public/images/apps/440/033bdd91842b6aca0633ee1e5f3e6b82f2e8962f.ico" width="24" height="24" style="vertical-align: text-bottom;"> Team Fortress 2 - Fixes: From e4d464972056827a9d259b19522fe42a12f9c82b Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sun, 8 Feb 2026 01:59:34 +0100 Subject: [PATCH 086/128] Hotfix --- addons/sourcemod/scripting/nativevotes.sp | 34 +++++++++++++---------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes.sp b/addons/sourcemod/scripting/nativevotes.sp index 960dd73..5a6c1ae 100644 --- a/addons/sourcemod/scripting/nativevotes.sp +++ b/addons/sourcemod/scripting/nativevotes.sp @@ -154,7 +154,7 @@ public Plugin myinfo = name = "NativeVotes", author = "Powerlord", description = "Voting API to use the game's native vote panels. Compatible with L4D, L4D2, TF2, and CS:GO.", - version = "26w06g", + version = "26w06h", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" } @@ -2658,7 +2658,7 @@ public int Native_RedrawVoteItem(Handle plugin, int numParams) return view_as<int>(Plugin_Changed); } -void SetupInstructorHint() +void EnsureHintCaptionEntity() { if (g_HintCaptionEnt != -1 && IsValidEntity(g_HintCaptionEnt)) { @@ -2689,30 +2689,32 @@ void ShowHintCaption(const char[] caption) return; } - SetupInstructorHint(); + EnsureHintCaptionEntity(); if (g_HintCaptionEnt == -1 || !IsValidEntity(g_HintCaptionEnt)) { return; } - DispatchKeyValue(g_HintCaptionEnt, "hint_caption", caption); - DispatchKeyValue(g_HintCaptionEnt, "hint_activator_caption", caption); - bool sent = false; + int activator = -1; for (int i = 1; i <= MaxClients; i++) { if (IsClientInGame(i) && !IsFakeClient(i)) { - SetVariantString(caption); - AcceptEntityInput(g_HintCaptionEnt, "SetCaption", i, i); - AcceptEntityInput(g_HintCaptionEnt, "ShowHint", i, i); - sent = true; + activator = i; + break; } } - if (!sent) + if (activator == -1) { return; } + + DispatchKeyValue(g_HintCaptionEnt, "hint_caption", caption); + DispatchKeyValue(g_HintCaptionEnt, "hint_activator_caption", caption); + SetVariantString(caption); + AcceptEntityInput(g_HintCaptionEnt, "SetCaption", activator); + AcceptEntityInput(g_HintCaptionEnt, "ShowHint", activator); } void EndHintCaption() @@ -2727,18 +2729,20 @@ void EndHintCaption() return; } - bool sent = false; + int activator = -1; for (int i = 1; i <= MaxClients; i++) { if (IsClientInGame(i) && !IsFakeClient(i)) { - AcceptEntityInput(g_HintCaptionEnt, "EndHint", i, i); - sent = true; + activator = i; + break; } } - if (!sent) + if (activator == -1) { return; } + + AcceptEntityInput(g_HintCaptionEnt, "EndHint", activator); } \ No newline at end of file From 42404304c21dc3229674b7a6af61de388fc13fa1 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sun, 8 Feb 2026 01:59:50 +0100 Subject: [PATCH 087/128] Bump version --- addons/sourcemod/scripting/nativevotes_basecommands.sp | 2 +- addons/sourcemod/scripting/nativevotes_basevotes.sp | 2 +- addons/sourcemod/scripting/nativevotes_funvotes.sp | 2 +- addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp | 2 +- addons/sourcemod/scripting/nativevotes_mapchooser.sp | 2 +- addons/sourcemod/scripting/nativevotes_nominations.sp | 2 +- addons/sourcemod/scripting/nativevotes_rockthevote.sp | 2 +- addons/sourcemod/scripting/nativevotes_voterp.sp | 2 +- addons/sourcemod/scripting/nativevotes_votescramble.sp | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_basecommands.sp b/addons/sourcemod/scripting/nativevotes_basecommands.sp index 88a1bd5..ccca3e0 100644 --- a/addons/sourcemod/scripting/nativevotes_basecommands.sp +++ b/addons/sourcemod/scripting/nativevotes_basecommands.sp @@ -50,7 +50,7 @@ public Plugin myinfo = name = "NativeVotes | Basic Commands", author = "Powerlord and AlliedModders LLC", description = "Revote and Cancel support for NativeVotes", - version = "26w06g", + version = "26w06h", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" } diff --git a/addons/sourcemod/scripting/nativevotes_basevotes.sp b/addons/sourcemod/scripting/nativevotes_basevotes.sp index e47f78e..4c58550 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes.sp @@ -47,7 +47,7 @@ public Plugin:myinfo = name = "NativeVotes | Basic Votes", author = "Powerlord and AlliedModders LLC", description = "NativeVotes Basic Vote Commands", - version = "26w06g", + version = "26w06h", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_funvotes.sp b/addons/sourcemod/scripting/nativevotes_funvotes.sp index 233d5f5..447269e 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes.sp @@ -47,7 +47,7 @@ public Plugin myinfo = name = "NativeVotes | Fun Votes", author = "Powerlord and AlliedModders LLC", description = "NativeVotes Fun Vote Commands", - version = "26w06g", + version = "26w06h", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp b/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp index 028eb34..1e1b097 100644 --- a/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp +++ b/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp @@ -60,7 +60,7 @@ public Plugin myinfo = { name = "NativeVotes | Kick Vote Immunity", author = "Powerlord", description = "Causes TF2 kick votes to fail against people who the current user can't target.", - version = "26w06g", + version = "26w06h", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_mapchooser.sp b/addons/sourcemod/scripting/nativevotes_mapchooser.sp index cc8f2e1..e2f69b6 100644 --- a/addons/sourcemod/scripting/nativevotes_mapchooser.sp +++ b/addons/sourcemod/scripting/nativevotes_mapchooser.sp @@ -57,7 +57,7 @@ public Plugin myinfo = name = "NativeVotes | MapChooser", author = "AlliedModders LLC and Powerlord", description = "Automated Map Voting", - version = "26w06g", + version = "26w06h", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_nominations.sp b/addons/sourcemod/scripting/nativevotes_nominations.sp index e2bc5bf..3403af9 100644 --- a/addons/sourcemod/scripting/nativevotes_nominations.sp +++ b/addons/sourcemod/scripting/nativevotes_nominations.sp @@ -50,7 +50,7 @@ public Plugin myinfo = name = "NativeVotes | Map Nominations", author = "AlliedModders LLC and Powerlord", description = "Provides Map Nominations", - version = "26w06g", + version = "26w06h", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_rockthevote.sp b/addons/sourcemod/scripting/nativevotes_rockthevote.sp index 0b876ef..a9329ed 100644 --- a/addons/sourcemod/scripting/nativevotes_rockthevote.sp +++ b/addons/sourcemod/scripting/nativevotes_rockthevote.sp @@ -57,7 +57,7 @@ public Plugin myinfo = name = "NativeVotes | Rock The Vote", author = "AlliedModders LLC and Powerlord", description = "Provides RTV Map Voting", - version = "26w06g", + version = "26w06h", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_voterp.sp b/addons/sourcemod/scripting/nativevotes_voterp.sp index 07e9d40..1f809a2 100644 --- a/addons/sourcemod/scripting/nativevotes_voterp.sp +++ b/addons/sourcemod/scripting/nativevotes_voterp.sp @@ -16,7 +16,7 @@ public Plugin myinfo = name = "NativeVotes | Medieval Auto-RP", author = "Heapons", description = "Provides Medieval Auto-RP voting.", - version = "26w06g", + version = "26w06h", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_votescramble.sp b/addons/sourcemod/scripting/nativevotes_votescramble.sp index e4c7267..bc61c79 100644 --- a/addons/sourcemod/scripting/nativevotes_votescramble.sp +++ b/addons/sourcemod/scripting/nativevotes_votescramble.sp @@ -13,7 +13,7 @@ public Plugin myinfo = name = "NativeVotes | Scramble Teams", author = "Heapons", description = "Provides RTV-style Scramble Teams Voting", - version = "26w06g", + version = "26w06h", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; From c55a5bddf4d2eda0b6ed387a333f8e36c58c42d3 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sun, 8 Feb 2026 02:50:32 +0100 Subject: [PATCH 088/128] Use server lang for hint captions --- addons/sourcemod/scripting/nativevotes.sp | 40 +++-------------------- 1 file changed, 5 insertions(+), 35 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes.sp b/addons/sourcemod/scripting/nativevotes.sp index 5a6c1ae..3fb9c66 100644 --- a/addons/sourcemod/scripting/nativevotes.sp +++ b/addons/sourcemod/scripting/nativevotes.sp @@ -1080,7 +1080,7 @@ void DrawHintProgress() if (showHintCaption) { char baseText[TRANSLATION_LENGTH]; - Format(baseText, sizeof(baseText), "%t", "Vote Count", g_NumVotes, g_TotalClients, iTimeRemaining); + Format(baseText, sizeof(baseText), "%T", "Vote Count", LANG_SERVER, g_NumVotes, g_TotalClients, iTimeRemaining); if (isMultVote && leaderList[0] != '\0') { int baseLen = strlen(baseText); @@ -2676,7 +2676,7 @@ void EnsureHintCaptionEntity() DispatchKeyValue(g_HintCaptionEnt, "hint_allow_nodraw_target", "1"); DispatchKeyValue(g_HintCaptionEnt, "hint_nooffscreen", "1"); DispatchKeyValue(g_HintCaptionEnt, "hint_forcecaption", "1"); - DispatchKeyValue(g_HintCaptionEnt, "hint_timeout", "2"); + DispatchKeyValue(g_HintCaptionEnt, "hint_timeout", "4"); DispatchKeyValue(g_HintCaptionEnt, "hint_icon_onscreen", "tf2c_clock"); DispatchKeyValue(g_HintCaptionEnt, "hint_icon_offscreen", "tf2c_clock"); DispatchSpawn(g_HintCaptionEnt); @@ -2695,26 +2695,11 @@ void ShowHintCaption(const char[] caption) return; } - int activator = -1; - for (int i = 1; i <= MaxClients; i++) - { - if (IsClientInGame(i) && !IsFakeClient(i)) - { - activator = i; - break; - } - } - - if (activator == -1) - { - return; - } - DispatchKeyValue(g_HintCaptionEnt, "hint_caption", caption); DispatchKeyValue(g_HintCaptionEnt, "hint_activator_caption", caption); SetVariantString(caption); - AcceptEntityInput(g_HintCaptionEnt, "SetCaption", activator); - AcceptEntityInput(g_HintCaptionEnt, "ShowHint", activator); + AcceptEntityInput(g_HintCaptionEnt, "SetCaption"); + AcceptEntityInput(g_HintCaptionEnt, "ShowHint"); } void EndHintCaption() @@ -2729,20 +2714,5 @@ void EndHintCaption() return; } - int activator = -1; - for (int i = 1; i <= MaxClients; i++) - { - if (IsClientInGame(i) && !IsFakeClient(i)) - { - activator = i; - break; - } - } - - if (activator == -1) - { - return; - } - - AcceptEntityInput(g_HintCaptionEnt, "EndHint", activator); + AcceptEntityInput(g_HintCaptionEnt, "EndHint"); } \ No newline at end of file From fcd6dad5a247d11650a4bc69ebb99121cd74895a Mon Sep 17 00:00:00 2001 From: megascatterbomb <megascatterbomb@gmail.com> Date: Sun, 8 Feb 2026 20:53:09 +1300 Subject: [PATCH 089/128] fix scramble timer resets for real --- .../scripting/nativevotes_votescramble.sp | 60 +++++++++++-------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_votescramble.sp b/addons/sourcemod/scripting/nativevotes_votescramble.sp index bc61c79..9b30d5d 100644 --- a/addons/sourcemod/scripting/nativevotes_votescramble.sp +++ b/addons/sourcemod/scripting/nativevotes_votescramble.sp @@ -23,8 +23,7 @@ enum minplayers, initialdelay, interval, - restart_round, - restart_timelimit, + full_reset, mp_match_end_at_timelimit, mp_timelimit, @@ -41,6 +40,7 @@ bool g_Voted[MAXPLAYERS+1] = {false, ...}; bool g_NativeVotes; bool g_RegisteredScramble = false; int g_ScrambleTime = 0; +float g_MapResetTime = 0; public void OnPluginStart() { @@ -48,12 +48,27 @@ public void OnPluginStart() LoadTranslations("rockthevote.phrases"); LoadTranslations("votescramble.phrases"); + KeyValues kv = new KeyValues("GameInfo"); + kv.ImportFromFile("gameinfo.txt"); + + char gameDir[128]; + GetGameFolderName(gameDir, sizeof(gameDir)); + + EngineVersion engine = GetEngineVersion(); + if (!StrEqual(gameDir, "tf") && + (kv.GetNum("DependsOnAppID") == 440 || + (engine == Engine_SDK2013 && FileExists("resource/tf.ttf")))) + { + engine = Engine_TF2; + } + + delete kv; + g_ConVars[needed] = CreateConVar("sm_scrambleteams_needed", "0.60", "Percentage of players needed to scramble teams (Def 60%)", 0, true, 0.05, true, 1.0); g_ConVars[minplayers] = CreateConVar("sm_scrambleteams_minplayers", "0", "Number of players required before scramble will be enabled.", 0, true, 0.0, true, float(MAXPLAYERS)); g_ConVars[initialdelay] = CreateConVar("sm_scrambleteams_initialdelay", "30.0", "Time (in seconds) before first scramble can be held", 0, true, 0.00); g_ConVars[interval] = CreateConVar("sm_scrambleteams_interval", "240.0", "Time (in seconds) after a failed scramble before another can be held", 0, true, 0.00); - g_ConVars[restart_round] = CreateConVar("sm_scrambleteams_restart_round", "0", "Whether to restart the round after scrambling teams", 0, true, 0.0, true, 1.0); - g_ConVars[restart_timelimit] = CreateConVar("sm_scrambleteams_restart_timelimit", "-1.0", "Override map timer after scrambling teams (leave it to '-1.0' for default behaviour)", 0, true, -1.0); + g_ConVars[full_reset] = CreateConVar("sm_scrambleteams_full_reset", "1", "Whether time/rounds played should reset after a scramble is triggered ", 0, true, 0.0, true, 1.0); g_ConVars[mp_match_end_at_timelimit] = FindConVar("mp_match_end_at_timelimit"); g_ConVars[mp_timelimit] = FindConVar("mp_timelimit"); @@ -74,6 +89,14 @@ public void OnPluginStart() OnClientConnected(i); } } + + switch (engine) + { + case Engine_TF2: + { + HookEvent("teamplay_round_start", Event_TeamplayRoundStart); + } + } } public void OnPluginEnd() @@ -185,17 +208,10 @@ public void OnClientSayCommand_Post(int client, const char[] command, const char public void Event_TeamplayRoundStart(Event event, const char[] name, bool dontBroadcast) { - // Ends game mid-round once mp_timelimit runs out - int entity = FindEntityByClassname(-1, "tf_gamerules"); - if (entity != -1) - { - SetVariantBool(g_ConVars[mp_match_end_at_timelimit].BoolValue); - AcceptEntityInput(entity, "SetStalemateOnTimelimit", 0, 0); - } - // Prevents map reset after scrambling teams - if (g_ConVars[restart_timelimit].FloatValue >= 0.0) + CreateTimer(g_ConVars[initialdelay].FloatValue, Timer_DelayScramble, _, TIMER_FLAG_NO_MAPCHANGE); + if (!g_ConVars[full_reset].BoolValue) { - GameRules_SetPropFloat("m_flMapResetTime", g_ConVars[restart_timelimit].FloatValue); + GameRules_SetPropFloat("m_flMapResetTime", g_MapResetTime); } } @@ -433,13 +449,11 @@ void ScrambleVoteResult(NativeVote vote, int num_votes, int num_clients, const i if (yesVotes > noVotes) { NativeVotes_DisplayPassEx(vote, NativeVotesPass_Scramble); - CPrintToChatAll(PLUGIN_PREFIX ... " %t", "Scrambling Teams"); - ServerCommand("mp_scrambleteams"); - if (g_ConVars[restart_round].BoolValue) - { - ServerCommand("mp_restartgame 5"); - } + + // mp_scrambleteams 2 preserves rounds played for mp_maxrounds. + ServerCommand(g_ConVars[full_reset].BoolValue ? "mp_scrambleteams" : "mp_scrambleteams 2"); + g_MapResetTime = GameRules_GetPropFloat("m_flMapResetTime"); } else if (yesVotes == 0 && noVotes == 0) { @@ -461,11 +475,7 @@ int MenuHandler_Scramble(Menu menu, MenuAction action, int client, int param2) if (param2 == 0) { CPrintToChatAll(PLUGIN_PREFIX ... " %t", "Scrambling Teams"); - ServerCommand("mp_scrambleteams"); - if (g_ConVars[restart_round].BoolValue) - { - ServerCommand("mp_restartgame 5"); - } + ServerCommand(g_ConVars[full_reset].BoolValue ? "mp_scrambleteams" : "mp_scrambleteams 2"); } else { From eb2388741a5137ac42d633905abe085452169db3 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sun, 8 Feb 2026 11:45:11 +0100 Subject: [PATCH 090/128] Update README.md --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 9100b7f..e52520a 100644 --- a/README.md +++ b/README.md @@ -110,5 +110,4 @@ This fork aims to expand upon [<img src="https://avatars.githubusercontent.com/u |`sm_scrambleteams_minplayers`|`0`|Number of players required before scramble will be enabled| |`sm_scrambleteams_initialdelay`|`30.0`|Time (in seconds) before first scramble can be held| |`sm_scrambleteams_interval`|`240.0`|Time (in seconds) after a failed scramble before another can be held| -|`sm_scrambleteams_restart_round`|`0`|Whether to restart the round after scrambling teams| -|`sm_scrambleteams_restart_timelimit`|`-1.0`|Override map timer after scrambling teams (leave it to '-1.0' for default behaviour)| \ No newline at end of file +|`sm_scrambleteams_full_reset`|`1`|Whether time/rounds played should reset after a scramble is triggered| \ No newline at end of file From 7ed42a45defbfbf21cef945928de748d2a7d9ef0 Mon Sep 17 00:00:00 2001 From: megascatterbomb <megascatterbomb@gmail.com> Date: Sat, 14 Feb 2026 12:47:46 +1300 Subject: [PATCH 091/128] fix mp_maxrounds setting, mapchooser reads m_nRoundsPlayed directly --- .../scripting/nativevotes_mapchooser.sp | 23 +++++-------------- .../scripting/nativevotes_votescramble.sp | 11 ++++++++- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_mapchooser.sp b/addons/sourcemod/scripting/nativevotes_mapchooser.sp index e2f69b6..f171784 100644 --- a/addons/sourcemod/scripting/nativevotes_mapchooser.sp +++ b/addons/sourcemod/scripting/nativevotes_mapchooser.sp @@ -36,6 +36,7 @@ #include <sourcemod> #include <mapchooser> +#include <sdktools> #include <nextmap> #include <regex> @@ -112,7 +113,6 @@ Menu g_VoteMenu; NativeVote g_VoteNative; int g_Extends; -int g_TotalRounds; bool g_HasVoteStarted; bool g_WaitingForVote; bool g_MapVoteCompleted; @@ -209,7 +209,6 @@ public void OnPluginStart() case Engine_TF2: { HookEvent("teamplay_win_panel", Event_TeamplayWinPanel); - HookEvent("teamplay_restart_round", Event_TeamplayRestartRound); HookEvent("arena_win_panel", Event_TeamplayWinPanel); } case Engine_NuclearDawn: @@ -334,9 +333,7 @@ public void OnConfigsExecuted() CreateNextVote(); SetupTimeleftTimer(); - - g_TotalRounds = 0; - + g_Extends = 0; g_MapVoteCompleted = false; @@ -492,12 +489,6 @@ public Action Timer_StartMapVote(Handle timer, DataPack data) return Plugin_Stop; } -public void Event_TeamplayRestartRound(Event event, const char[] name, bool dontBroadcast) -{ - /* Game got restarted - reset our round count tracking */ - g_TotalRounds = 0; -} - public void Event_TeamplayWinPanel(Event event, const char[] name, bool dontBroadcast) { if (g_ChangeMapAtRoundEnd) @@ -512,14 +503,13 @@ public void Event_TeamplayWinPanel(Event event, const char[] name, bool dontBroa if (event.GetInt("round_complete") == 1 || StrEqual(name, "arena_win_panel")) { - g_TotalRounds++; if (!g_MapList.Length || g_HasVoteStarted || g_MapVoteCompleted || !g_ConVars[mapvote_endvote].BoolValue) { return; } - CheckMaxRounds(g_TotalRounds); + CheckMaxRounds(); switch(event.GetInt("winning_team")) { @@ -569,8 +559,6 @@ public void Event_RoundEnd(Event event, const char[] name, bool dontBroadcast) { SetFailState("Mod exceed maximum team count - Please file a bug report."); } - - g_TotalRounds++; g_winCount[winner]++; @@ -580,7 +568,7 @@ public void Event_RoundEnd(Event event, const char[] name, bool dontBroadcast) } CheckWinLimit(g_winCount[winner]); - CheckMaxRounds(g_TotalRounds); + CheckMaxRounds(); } public void CheckWinLimit(int winner_score) @@ -598,10 +586,11 @@ public void CheckWinLimit(int winner_score) } } -public void CheckMaxRounds(int roundcount) +public void CheckMaxRounds() { if (g_ConVars[mp_maxrounds]) { + int roundcount = GameRules_GetProp("m_nRoundsPlayed"); int maxrounds = g_ConVars[mp_maxrounds].IntValue; if (maxrounds) { diff --git a/addons/sourcemod/scripting/nativevotes_votescramble.sp b/addons/sourcemod/scripting/nativevotes_votescramble.sp index 9b30d5d..a9cf3b2 100644 --- a/addons/sourcemod/scripting/nativevotes_votescramble.sp +++ b/addons/sourcemod/scripting/nativevotes_votescramble.sp @@ -41,6 +41,7 @@ bool g_NativeVotes; bool g_RegisteredScramble = false; int g_ScrambleTime = 0; float g_MapResetTime = 0; +int g_SetRoundsPlayedTo = -1; public void OnPluginStart() { @@ -212,7 +213,14 @@ public void Event_TeamplayRoundStart(Event event, const char[] name, bool dontBr if (!g_ConVars[full_reset].BoolValue) { GameRules_SetPropFloat("m_flMapResetTime", g_MapResetTime); + + // If this isn't -1 we know our plugin just triggered a scramble, gotta change it back + if (g_SetRoundsPlayedTo >= 0) + { + GameRules_SetProp("m_nRoundsPlayed", g_SetRoundsPlayedTo); + } } + g_SetRoundsPlayedTo = -1; } public Action Command_VoteScramble(int client, int args) @@ -451,9 +459,10 @@ void ScrambleVoteResult(NativeVote vote, int num_votes, int num_clients, const i NativeVotes_DisplayPassEx(vote, NativeVotesPass_Scramble); CPrintToChatAll(PLUGIN_PREFIX ... " %t", "Scrambling Teams"); - // mp_scrambleteams 2 preserves rounds played for mp_maxrounds. + // mp_scrambleteams 2 SHOULD preserve rounds played for mp_maxrounds, but doesn't appear to at the moment. ServerCommand(g_ConVars[full_reset].BoolValue ? "mp_scrambleteams" : "mp_scrambleteams 2"); g_MapResetTime = GameRules_GetPropFloat("m_flMapResetTime"); + g_SetRoundsPlayedTo = GameRules_GetProp("m_nRoundsPlayed"); } else if (yesVotes == 0 && noVotes == 0) { From eaf449e98a4d7193dc71e6cb6663c39681d7d324 Mon Sep 17 00:00:00 2001 From: megascatterbomb <megascatterbomb@gmail.com> Date: Sat, 14 Feb 2026 13:10:46 +1300 Subject: [PATCH 092/128] cVar to uncap nominations and pick subset at random --- .../scripting/nativevotes_mapchooser.sp | 47 +++++++++++++++---- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_mapchooser.sp b/addons/sourcemod/scripting/nativevotes_mapchooser.sp index e2f69b6..d5e07dd 100644 --- a/addons/sourcemod/scripting/nativevotes_mapchooser.sp +++ b/addons/sourcemod/scripting/nativevotes_mapchooser.sp @@ -89,6 +89,7 @@ enum mapvote_runoffpercent, mapcycle_auto, mapcycle_exclude, + mapvote_shuffle_nominations, workshop_map_collection, workshop_cleanup, @@ -187,6 +188,8 @@ public void OnPluginStart() g_ConVars[mapvote_runoffpercent] = CreateConVar("sm_mapvote_runoffpercent", "50", "If winning choice has less than this percent of votes, hold a runoff.", _, true, 0.0, true, 100.0); g_ConVars[mapcycle_auto] = CreateConVar("sm_mapcycle_auto", "0", "Specifies whether or not to automatically populate the maps list.", _, true, 0.0, true, 1.0); g_ConVars[mapcycle_exclude] = CreateConVar("sm_mapcycle_exclude", ".*test.*|background01|^tr.*$", "Specifies which maps shouldn't be automatically added with a regex pattern."); + g_ConVars[mapvote_shuffle_nominations] = CreateConVar("sm_mapvote_shuffle_nominations", "0", "If set, allows infinite nominations and picks a random subset to appear in the vote.", _, true, 0.0, true, 1.0); + if (engine != Engine_SDK2013 && engine == Engine_TF2) { g_ConVars[workshop_map_collection] = CreateConVar("sm_workshop_map_collection", "", "Specifies the workshop collection to fetch the maps from."); @@ -650,6 +653,17 @@ public Action Command_MapVote(int client, int args) return Plugin_Handled; } +void ShuffleNominations() +{ + int lengthList = g_NominateList.Length; + for (int i = lengthList - 1; i > 0; --i) + { + int j = GetRandomInt(0, i); + g_NominateList.SwapAt(i, j); + g_NominateOwners.SwapAt(i, j); + } +} + /** * Starts a new map vote * @@ -742,6 +756,11 @@ void InitiateVote(MapChange when, ArrayList inputlist=null) } /* Smaller of the two - It should be impossible for nominations to exceed the size though (cvar changed mid-map?) */ int nominationsToAdd = nominateCount >= voteSize ? voteSize : nominateCount; + + if(g_ConVars[mapvote_shuffle_nominations].BoolValue && nominateCount > voteSize) + { + ShuffleNominations(); + } for (int i = 0; i < nominationsToAdd; i++) { @@ -1412,6 +1431,11 @@ NominateResult InternalNominateMap(char[] map, bool force, int owner) { maxIncludes = g_ConVars[mapvote_include].IntValue; } + + if (!g_ConVars[mapvote_shuffle_nominations].BoolValue && g_NominateList.Length >= maxIncludes && !force) + { + return Nominate_VoteFull; + } if (g_NominateList.Length >= maxIncludes && !force) { @@ -1421,17 +1445,20 @@ NominateResult InternalNominateMap(char[] map, bool force, int owner) g_NominateList.PushString(map); g_NominateOwners.Push(owner); - while (g_NominateList.Length > g_ConVars[mapvote_include].IntValue) + if (!g_ConVars[mapvote_shuffle_nominations].BoolValue) { - char oldmap[PLATFORM_MAX_PATH]; - g_NominateList.GetString(0, oldmap, sizeof(oldmap)); - Call_StartForward(g_NominationsResetForward); - Call_PushString(oldmap); - Call_PushCell(g_NominateOwners.Get(0)); - Call_Finish(); - - g_NominateList.Erase(0); - g_NominateOwners.Erase(0); + while (g_NominateList.Length > g_ConVars[mapvote_include].IntValue) + { + char oldmap[PLATFORM_MAX_PATH]; + g_NominateList.GetString(0, oldmap, sizeof(oldmap)); + Call_StartForward(g_NominationsResetForward); + Call_PushString(oldmap); + Call_PushCell(g_NominateOwners.Get(0)); + Call_Finish(); + + g_NominateList.Erase(0); + g_NominateOwners.Erase(0); + } } return Nominate_Added; From 0e65244ceacc765abda54e6a3909edc8e494ab75 Mon Sep 17 00:00:00 2001 From: megascatterbomb <megascatterbomb@gmail.com> Date: Sat, 14 Feb 2026 13:17:14 +1300 Subject: [PATCH 093/128] remove duplicate check, improve cvar ordering --- addons/sourcemod/scripting/nativevotes_mapchooser.sp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_mapchooser.sp b/addons/sourcemod/scripting/nativevotes_mapchooser.sp index d5e07dd..31aea17 100644 --- a/addons/sourcemod/scripting/nativevotes_mapchooser.sp +++ b/addons/sourcemod/scripting/nativevotes_mapchooser.sp @@ -87,9 +87,9 @@ enum mapvote_voteduration, mapvote_runoff, mapvote_runoffpercent, + mapvote_shuffle_nominations, mapcycle_auto, mapcycle_exclude, - mapvote_shuffle_nominations, workshop_map_collection, workshop_cleanup, @@ -186,9 +186,9 @@ public void OnPluginStart() g_ConVars[mapvote_voteduration] = CreateConVar("sm_mapvote_voteduration", "20", "Specifies how long the mapvote should be available for.", _, true, 5.0); g_ConVars[mapvote_runoff] = CreateConVar("sm_mapvote_runoff", "0", "Hold run of votes if winning choice is less than a certain margin.", _, true, 0.0, true, 1.0); g_ConVars[mapvote_runoffpercent] = CreateConVar("sm_mapvote_runoffpercent", "50", "If winning choice has less than this percent of votes, hold a runoff.", _, true, 0.0, true, 100.0); + g_ConVars[mapvote_shuffle_nominations] = CreateConVar("sm_mapvote_shuffle_nominations", "0", "If set, allows infinite nominations and picks a random subset to appear in the vote.", _, true, 0.0, true, 1.0); g_ConVars[mapcycle_auto] = CreateConVar("sm_mapcycle_auto", "0", "Specifies whether or not to automatically populate the maps list.", _, true, 0.0, true, 1.0); g_ConVars[mapcycle_exclude] = CreateConVar("sm_mapcycle_exclude", ".*test.*|background01|^tr.*$", "Specifies which maps shouldn't be automatically added with a regex pattern."); - g_ConVars[mapvote_shuffle_nominations] = CreateConVar("sm_mapvote_shuffle_nominations", "0", "If set, allows infinite nominations and picks a random subset to appear in the vote.", _, true, 0.0, true, 1.0); if (engine != Engine_SDK2013 && engine == Engine_TF2) { @@ -1437,11 +1437,6 @@ NominateResult InternalNominateMap(char[] map, bool force, int owner) return Nominate_VoteFull; } - if (g_NominateList.Length >= maxIncludes && !force) - { - return Nominate_VoteFull; - } - g_NominateList.PushString(map); g_NominateOwners.Push(owner); From 97d9dad1ca2a75f1ef6a2657c45a8c2a05f668ba Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sat, 14 Feb 2026 13:22:51 +0100 Subject: [PATCH 094/128] Tweaks - sm_rtv doesn't attempt to nominate twice anymore. - Fixed instructor hints and hint boxes formatting issues. - Instructor hints are now event-based. - Fixed invalid array sizes(?) --- addons/sourcemod/scripting/nativevotes.sp | 252 ++++++++---------- .../sourcemod/scripting/nativevotes/game.sp | 2 +- .../scripting/nativevotes_mapchooser.sp | 6 +- .../scripting/nativevotes_rockthevote.sp | 8 +- 4 files changed, 113 insertions(+), 155 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes.sp b/addons/sourcemod/scripting/nativevotes.sp index 3fb9c66..4a1b361 100644 --- a/addons/sourcemod/scripting/nativevotes.sp +++ b/addons/sourcemod/scripting/nativevotes.sp @@ -45,8 +45,6 @@ EngineVersion g_EngineVersion = Engine_Unknown; int g_AppID = 0; -#define APP_TF2CLASSIFIED 3545060 - #include "nativevotes/data-keyvalues.sp" #define LOGTAG "NativeVotes" @@ -119,7 +117,6 @@ float g_fStartTime; int g_TimeLeft; int g_ClientVotes[MAXPLAYERS+1]; bool g_bRevoting[MAXPLAYERS+1]; -int g_HintCaptionEnt = -1; ConVar sv_vote_holder_may_vote_no; @@ -154,7 +151,7 @@ public Plugin myinfo = name = "NativeVotes", author = "Powerlord", description = "Voting API to use the game's native vote panels. Compatible with L4D, L4D2, TF2, and CS:GO.", - version = "26w06h", + version = "26w07a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" } @@ -269,12 +266,12 @@ public void OnPluginStart() g_ConVars[progress_client_console] = CreateConVar("nativevotes_progress_client_console", "0", "Show current vote progress as console messages to clients", _, true, 0.0, true, 1.0); g_ConVars[vote_delay] = CreateConVar("nativevotes_vote_delay", "0", "Sets the recommended time in between public votes", _, true, 0.0); g_ConVars[vote_delay].AddChangeHook(OnVoteDelayChange); - switch (g_AppID) + + Event instructor = CreateEvent("instructor_server_hint_create", true); + if (instructor != null) { - case APP_TF2CLASSIFIED: - { - g_ConVars[progress_hintcaption] = CreateConVar("nativevotes_progress_hintcaption", "0", "Show current vote progress in a instructor hint caption", _, true, 0.0, true, 1.0); - } + g_ConVars[progress_hintcaption] = CreateConVar("nativevotes_progress_hintcaption", "0", "Show current vote progress in a instructor hint caption", _, true, 0.0, true, 1.0); + instructor.Cancel(); } Game_InitializeCvars(); @@ -282,7 +279,6 @@ public void OnPluginStart() AddCommandListener(Command_Vote, "vote"); // All games, command listeners aren't case sensitive sv_vote_holder_may_vote_no = FindConVar("sv_vote_holder_may_vote_no"); - // The new version of the CallVote system is TF2 only if (Game_AreVoteCommandsSupported()) { @@ -671,14 +667,7 @@ public void OnMapEnd() if (g_hDisplayTimer != null) { - KillTimer(g_hDisplayTimer); - g_hDisplayTimer = null; - } - - if (g_HintCaptionEnt != -1 && IsValidEntity(g_HintCaptionEnt)) - { - RemoveEdict(g_HintCaptionEnt); - g_HintCaptionEnt = -1; + delete g_hDisplayTimer; } // g_hVoteTimer = INVALID_HANDLE; @@ -1065,43 +1054,49 @@ void DrawHintProgress() leaderList[0] = '\0'; } - if (showHintbox) + for (int client = 1; client <= MaxClients; client++) { - if (isMultVote) - { - PrintHintTextToAll("%t%s", "Vote Count", g_NumVotes, g_TotalClients, iTimeRemaining, leaderList); - } - else + if (!IsClientInGame(client) || IsFakeClient(client)) + continue; + + SetGlobalTransTarget(client); + if (showHintbox) { - PrintHintTextToAll("%t", "Vote Count", g_NumVotes, g_TotalClients, iTimeRemaining); + if (isMultVote) + PrintHintText(client, "%t\n%s", "Vote Count", g_NumVotes, g_TotalClients, iTimeRemaining, leaderList); + else + PrintHintText(client, "%t", "Vote Count", g_NumVotes, g_TotalClients, iTimeRemaining); } - } - - if (showHintCaption) - { - char baseText[TRANSLATION_LENGTH]; - Format(baseText, sizeof(baseText), "%T", "Vote Count", LANG_SERVER, g_NumVotes, g_TotalClients, iTimeRemaining); - if (isMultVote && leaderList[0] != '\0') + if (showHintCaption) { - int baseLen = strlen(baseText); - int maxLeaderLen = 256 - baseLen - 1; - if (maxLeaderLen < 1) + char baseText[TRANSLATION_LENGTH]; + Format(baseText, sizeof(baseText), "%t", "Vote Count", g_NumVotes, g_TotalClients, iTimeRemaining); + if (isMultVote && leaderList[0] != '\0') { - ShowHintCaption(baseText); + int baseLen = strlen(baseText); + int maxLeaderLen = 256 - baseLen - 1; + if (maxLeaderLen < 1) + { + char caption[256]; + Format(caption, sizeof(caption), "%t", "Vote Count", g_NumVotes, g_TotalClients, iTimeRemaining); + SendInstructorHint(client, caption); + } + else + { + char[] cappedLeaders = new char[maxLeaderLen + 1]; + BuildVoteLeaders(cappedLeaders, maxLeaderLen + 1); + char caption[256]; + Format(caption, sizeof(caption), "%s\n%s", baseText, cappedLeaders); + SendInstructorHint(client, caption); + } } else { - char[] cappedLeaders = new char[maxLeaderLen + 1]; - BuildVoteLeaders(cappedLeaders, maxLeaderLen + 1); char caption[256]; - Format(caption, sizeof(caption), "%s\n%s", baseText, cappedLeaders); - ShowHintCaption(caption); + Format(caption, sizeof(caption), "%t", "Vote Count", g_NumVotes, g_TotalClients, iTimeRemaining); + SendInstructorHint(client, caption); } } - else - { - ShowHintCaption(baseText); - } } } @@ -1125,8 +1120,6 @@ void BuildVoteLeaders(char[] leaderList, int leaderListSize) int[][] votes = new int[slots][2]; int num_items = Internal_GetResults(votes); - - int limit = leaderListSize - 1; /* Build the full leaderboard (bounded by buffer size) */ for (int i = 0; i < num_items; i++) @@ -1136,42 +1129,15 @@ void BuildVoteLeaders(char[] leaderList, int leaderListSize) Data_GetItemDisplay(g_hCurVote, cur_item, choice, sizeof(choice)); char line[256]; Format(line, sizeof(line), "%i. %s: (%i)", i + 1, choice, votes[i][VOTEINFO_ITEM_VOTES]); - int used = strlen(leaderList); - int sepLen = (used == 0) ? 0 : 1; - int lineLen = strlen(line); - if (used + sepLen + lineLen > limit) - { - int remaining = num_items - i; - if (remaining > 0) - { - char moreLine[64]; - Format(moreLine, sizeof(moreLine), "... (+%d more)", remaining); - int moreLen = strlen(moreLine); - if (used + sepLen + moreLen <= limit) - { - if (used == 0) - { - Format(leaderList, limit + 1, "%s", moreLine); - } - else - { - Format(leaderList, limit + 1, "%s\n%s", leaderList, moreLine); - } - } - } - break; - } - - if (used == 0) + if (i == 0) { - Format(leaderList, limit + 1, "%s", line); + Format(leaderList, leaderListSize, "%s", line); } else { - Format(leaderList, limit + 1, "%s\n%s", leaderList, line); + Format(leaderList, leaderListSize, "%s\n%s", leaderList, line); } } - } public int SortVoteItems(int[] a, int[] b, const int[][] array, Handle hndl) @@ -1205,7 +1171,12 @@ void DecrementPlayerCount() void EndVoting() { - EndHintCaption(); + for (int client = 1; client <= MaxClients; client++) + { + if (!IsClientInGame(client) || !IsClientConnected(client) || IsFakeClient(client)) + continue; + EndInstructorHint(client); + } int voteDelay = g_ConVars[vote_delay].IntValue; if (voteDelay < 1) @@ -1219,8 +1190,7 @@ void EndVoting() if (g_hDisplayTimer != null) { - KillTimer(g_hDisplayTimer); - g_hDisplayTimer = null; + delete g_hDisplayTimer; } if (g_bCancelled) @@ -1719,7 +1689,7 @@ public int Native_Close(Handle plugin, int numParams) if (vote == null) { - return; + return 0; } if (g_hCurVote == vote) @@ -1760,10 +1730,15 @@ public int Native_Display(Handle plugin, int numParams) if (vote == null) { ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return false; + return 0; } int count = GetNativeCell(3); + if (count <= 0) + { + return 0; + } + int[] clients = new int[count]; GetNativeArray(2, clients, count); @@ -1791,14 +1766,14 @@ public int Native_AddItem(Handle plugin, int numParams) if (vote == null) { ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return false; + return 0; } NativeVotesType voteType = Data_GetType(vote); if (voteType != NativeVotesType_NextLevelMult && voteType != NativeVotesType_Custom_Mult) { - return false; + return 0; } char info[256]; @@ -1816,14 +1791,14 @@ public int Native_InsertItem(Handle plugin, int numParams) if (vote == null) { ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return false; + return 0; } NativeVotesType voteType = Data_GetType(vote); if (voteType != NativeVotesType_NextLevelMult && voteType != NativeVotesType_Custom_Mult) { - return false; + return 0; } int position = GetNativeCell(2); @@ -1831,7 +1806,7 @@ public int Native_InsertItem(Handle plugin, int numParams) if (position < 0) { ThrowNativeError(SP_ERROR_NATIVE, "Vote index can't be negative: %d", position); - return false; + return 0; } char info[256]; @@ -1850,14 +1825,14 @@ public int Native_RemoveItem(Handle plugin, int numParams) if (vote == null) { ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return false; + return 0; } NativeVotesType voteType = Data_GetType(vote); if (voteType != NativeVotesType_NextLevelMult && voteType != NativeVotesType_Custom_Mult) { - return false; + return 0; } int position = GetNativeCell(2); @@ -1872,7 +1847,7 @@ public int Native_RemoveAllItems(Handle plugin, int numParams) if (vote == null) { ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return; + return 0; } Data_RemoveAllItems(vote); @@ -1885,7 +1860,7 @@ public int Native_GetItem(Handle plugin, int numParams) if (vote == null) { ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return; + return 0; } int position = GetNativeCell(2); @@ -1927,7 +1902,7 @@ public int Native_GetDetails(Handle plugin, int numParams) if (vote == null) { ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return; + return 0; } int len = GetNativeCell(3); @@ -1946,12 +1921,11 @@ public int Native_SetDetails(Handle plugin, int numParams) if (vote == null) { ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return; + return 0; } char details[MAX_VOTE_DETAILS_LENGTH]; - //SetGlobalTransTarget(LANG_SERVER); FormatNativeString(0, 2, 3, sizeof(details), _, details); Data_SetDetails(vote, details); @@ -1964,7 +1938,7 @@ public int Native_GetTitle(Handle plugin, int numParams) if (vote == null) { ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return; + return 0; } int len = GetNativeCell(3); @@ -1983,12 +1957,11 @@ public int Native_SetTitle(Handle plugin, int numParams) if (vote == null) { ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return; + return 0; } char details[MAX_VOTE_DETAILS_LENGTH]; - //SetGlobalTransTarget(LANG_SERVER); FormatNativeString(0, 2, 3, sizeof(details), _, details); Data_SetTitle(vote, details); @@ -2026,7 +1999,7 @@ public int Native_SetOptionFlags(Handle plugin, int numParams) if (vote == null) { ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return; + return 0; } int flags = GetNativeCell(2); @@ -2070,7 +2043,7 @@ public int Native_Cancel(Handle plugin, int numParams) if (!Internal_IsVoteInProgress()) { ThrowNativeError(SP_ERROR_NATIVE, "No vote is in progress"); - return; + return 0; } CancelVoting(); @@ -2083,7 +2056,7 @@ public int Native_SetResultCallback(Handle plugin, int numParams) if (vote == null) { ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return; + return 0; } Function handler = GetNativeFunction(2); @@ -2204,7 +2177,7 @@ public int Native_SetTeam(Handle plugin, int numParams) if (vote == null) { ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return; + return 0; } int team = GetNativeCell(2); @@ -2245,7 +2218,7 @@ public int Native_SetInitiator(Handle plugin, int numParams) if (vote == null) { ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return; + return 0; } int initiator = GetNativeCell(2); @@ -2259,7 +2232,7 @@ public int Native_DisplayPass(Handle plugin, int numParams) if (vote == null) { ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return; + return 0; } if (numParams >= 2) @@ -2283,7 +2256,7 @@ public int Native_DisplayPassCustomToOne(Handle plugin, int numParams) if (vote == null) { ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return; + return 0; } int client = GetNativeCell(2); @@ -2303,7 +2276,7 @@ public int Native_DisplayPassEx(Handle plugin, int numParams) if (vote == null) { ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return; + return 0; } NativeVotesPassType passType = view_as<NativeVotesPassType>(GetNativeCell(2)); @@ -2316,7 +2289,6 @@ public int Native_DisplayPassEx(Handle plugin, int numParams) if (numParams >= 3) { char winner[TRANSLATION_LENGTH]; - //SetGlobalTransTarget(LANG_SERVER); FormatNativeString(0, 3, 4, sizeof(winner), _, winner); Game_DisplayVotePassEx(vote, passType, winner); @@ -2405,7 +2377,7 @@ public int Native_DisplayFail(Handle plugin, int numParams) if (vote == null) { ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return; + return 0; } NativeVotesFailType reason = view_as<NativeVotesFailType>(GetNativeCell(2)); @@ -2495,7 +2467,7 @@ public int Native_SetTarget(Handle plugin, int numParams) if (vote == null) { ThrowNativeError(SP_ERROR_NATIVE, "NativeVotes handle %x is invalid", vote); - return; + return 0; } int client = GetNativeCell(2); @@ -2560,7 +2532,7 @@ public int Native_RegisterVoteCommand(Handle plugin, int numParams) if (overrideType > NativeVotesOverride_Count) { ThrowNativeError(SP_ERROR_NATIVE, "Override Type %d is not supported by this version of NativeVotes", overrideType); - return; + return 0; } if (callVoteHandler == INVALID_FUNCTION) @@ -2587,7 +2559,7 @@ public int Native_UnregisterVoteCommand(Handle plugin, int numParams) if (overrideType > NativeVotesOverride_Count) { ThrowNativeError(SP_ERROR_NATIVE, "Override Type %d is not supported by this version of NativeVotes", overrideType); - return; + return 0; } if (callVoteHandler == INVALID_FUNCTION) @@ -2612,7 +2584,7 @@ public int Native_DisplayCallVoteFail(Handle plugin, int numParams) if (client < 1 || client > MaxClients || !IsClientConnected(client)) { ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index %d", client); - return; + return 0; } NativeVotesCallFailType reason = GetNativeCell(2); @@ -2658,61 +2630,49 @@ public int Native_RedrawVoteItem(Handle plugin, int numParams) return view_as<int>(Plugin_Changed); } -void EnsureHintCaptionEntity() -{ - if (g_HintCaptionEnt != -1 && IsValidEntity(g_HintCaptionEnt)) - { - return; - } - - g_HintCaptionEnt = CreateEntityByName("env_instructor_hint"); - if (g_HintCaptionEnt == -1) - { - return; - } - - DispatchKeyValue(g_HintCaptionEnt, "hint_replace_key", "nativevotes_progress"); - DispatchKeyValue(g_HintCaptionEnt, "hint_static", "1"); - DispatchKeyValue(g_HintCaptionEnt, "hint_allow_nodraw_target", "1"); - DispatchKeyValue(g_HintCaptionEnt, "hint_nooffscreen", "1"); - DispatchKeyValue(g_HintCaptionEnt, "hint_forcecaption", "1"); - DispatchKeyValue(g_HintCaptionEnt, "hint_timeout", "4"); - DispatchKeyValue(g_HintCaptionEnt, "hint_icon_onscreen", "tf2c_clock"); - DispatchKeyValue(g_HintCaptionEnt, "hint_icon_offscreen", "tf2c_clock"); - DispatchSpawn(g_HintCaptionEnt); -} - -void ShowHintCaption(const char[] caption) +// Sends a vote progress hint caption to a specific client using game events +void SendInstructorHint(int client, const char[] caption) { if (g_ConVars[progress_hintcaption] == null || !g_ConVars[progress_hintcaption].BoolValue) { return; } - EnsureHintCaptionEntity(); - if (g_HintCaptionEnt == -1 || !IsValidEntity(g_HintCaptionEnt)) + Event event = CreateEvent("instructor_server_hint_create", true); + if (event == null) { return; } - DispatchKeyValue(g_HintCaptionEnt, "hint_caption", caption); - DispatchKeyValue(g_HintCaptionEnt, "hint_activator_caption", caption); - SetVariantString(caption); - AcceptEntityInput(g_HintCaptionEnt, "SetCaption"); - AcceptEntityInput(g_HintCaptionEnt, "ShowHint"); + event.SetString("hint_name", "nativevotes_progress"); + event.SetString("hint_replace_key", "nativevotes_progress"); + event.SetInt("hint_target", client); + event.SetInt("hint_activator_userid", GetClientUserId(client)); + event.SetInt("hint_timeout", 0); + event.SetString("hint_icon_onscreen", "tf2c_clock"); + event.SetString("hint_icon_offscreen", "tf2c_clock"); + event.SetString("hint_activator_caption", caption); + event.SetInt("hint_allow_nodraw_target", 1); + event.SetInt("hint_nooffscreen", 1); + event.SetInt("hint_forcecaption", 1); + + event.FireToClient(client); } -void EndHintCaption() +// Ends the vote progress hint for a specific client +void EndInstructorHint(int client) { if (g_ConVars[progress_hintcaption] == null || !g_ConVars[progress_hintcaption].BoolValue) { return; } - if (g_HintCaptionEnt == -1 || !IsValidEntity(g_HintCaptionEnt)) + Event event = CreateEvent("instructor_server_hint_stop", true); + if (event == null) { return; } + event.SetString("hint_name", "nativevotes_progress"); + event.FireToClient(client); +} - AcceptEntityInput(g_HintCaptionEnt, "EndHint"); -} \ No newline at end of file diff --git a/addons/sourcemod/scripting/nativevotes/game.sp b/addons/sourcemod/scripting/nativevotes/game.sp index 8385f46..7db0d20 100644 --- a/addons/sourcemod/scripting/nativevotes/game.sp +++ b/addons/sourcemod/scripting/nativevotes/game.sp @@ -2852,7 +2852,7 @@ static void TF2_AddDefaultVotes(ArrayList hVoteTypes, bool bHideDisabledVotes) switch (g_AppID) { - case APP_TF2CLASSIFIED: + case 3545060: // Team Fortress 2 Classified { /* ChangeCivilian */ int entity = FindEntityByClassname(INVALID_ENT_REFERENCE, "tf2c_logic_vip"); diff --git a/addons/sourcemod/scripting/nativevotes_mapchooser.sp b/addons/sourcemod/scripting/nativevotes_mapchooser.sp index e2f69b6..dc8c788 100644 --- a/addons/sourcemod/scripting/nativevotes_mapchooser.sp +++ b/addons/sourcemod/scripting/nativevotes_mapchooser.sp @@ -57,7 +57,7 @@ public Plugin myinfo = name = "NativeVotes | MapChooser", author = "AlliedModders LLC and Powerlord", description = "Automated Map Voting", - version = "26w06h", + version = "26w07a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -674,6 +674,8 @@ void InitiateVote(MapChange when, ArrayList inputlist=null) data.Reset(); return; } + + g_WaitingForVote = false; /* If the main map vote has completed (and chosen result) and its currently changing (not a delayed change) we block further attempts */ if (g_MapVoteCompleted && g_ChangeMapInProgress) @@ -682,8 +684,6 @@ void InitiateVote(MapChange when, ArrayList inputlist=null) } g_ChangeTime = when; - - g_WaitingForVote = false; g_HasVoteStarted = true; if (g_NativeVotes) diff --git a/addons/sourcemod/scripting/nativevotes_rockthevote.sp b/addons/sourcemod/scripting/nativevotes_rockthevote.sp index a9329ed..a98cd8e 100644 --- a/addons/sourcemod/scripting/nativevotes_rockthevote.sp +++ b/addons/sourcemod/scripting/nativevotes_rockthevote.sp @@ -57,7 +57,7 @@ public Plugin myinfo = name = "NativeVotes | Rock The Vote", author = "AlliedModders LLC and Powerlord", description = "Provides RTV Map Voting", - version = "26w06h", + version = "26w07a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -357,7 +357,7 @@ void AttemptRTV(int client, bool isVoteMenu=false) return; } - if (!CanMapChooserStartVote() && NativeVotes_IsVoteInProgress()) + if (!CanMapChooserStartVote() || (g_NativeVotes && NativeVotes_IsVoteInProgress()) || (!g_NativeVotes && IsVoteInProgress())) { CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "RTV Started"); return; @@ -493,7 +493,7 @@ void RemoveVoteHandler() public Action Menu_RTV(int client, NativeVotesOverride overrideType, const char[] voteArgument) { - if (client <= 0 || NativeVotes_IsVoteInProgress()) + if (client <= 0 || (g_NativeVotes && NativeVotes_IsVoteInProgress()) || (!g_NativeVotes && IsVoteInProgress())) { return Plugin_Handled; } @@ -506,8 +506,6 @@ public Action Menu_RTV(int client, NativeVotesOverride overrideType, const char[ ReplySource old = SetCmdReplySource(SM_REPLY_TO_CHAT); - FakeClientCommand(client, "sm_nominate %s", voteArgument); - AttemptRTV(client, true); SetCmdReplySource(old); From 129225506261e4bbb73bc42b3bcc7c9256a3a916 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sat, 14 Feb 2026 13:23:42 +0100 Subject: [PATCH 095/128] Bump version --- addons/sourcemod/scripting/nativevotes_basecommands.sp | 2 +- addons/sourcemod/scripting/nativevotes_basevotes.sp | 2 +- addons/sourcemod/scripting/nativevotes_funvotes.sp | 2 +- addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp | 2 +- addons/sourcemod/scripting/nativevotes_nominations.sp | 2 +- addons/sourcemod/scripting/nativevotes_voterp.sp | 2 +- addons/sourcemod/scripting/nativevotes_votescramble.sp | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_basecommands.sp b/addons/sourcemod/scripting/nativevotes_basecommands.sp index ccca3e0..d95ea4c 100644 --- a/addons/sourcemod/scripting/nativevotes_basecommands.sp +++ b/addons/sourcemod/scripting/nativevotes_basecommands.sp @@ -50,7 +50,7 @@ public Plugin myinfo = name = "NativeVotes | Basic Commands", author = "Powerlord and AlliedModders LLC", description = "Revote and Cancel support for NativeVotes", - version = "26w06h", + version = "26w07a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" } diff --git a/addons/sourcemod/scripting/nativevotes_basevotes.sp b/addons/sourcemod/scripting/nativevotes_basevotes.sp index 4c58550..1938753 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes.sp @@ -47,7 +47,7 @@ public Plugin:myinfo = name = "NativeVotes | Basic Votes", author = "Powerlord and AlliedModders LLC", description = "NativeVotes Basic Vote Commands", - version = "26w06h", + version = "26w07a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_funvotes.sp b/addons/sourcemod/scripting/nativevotes_funvotes.sp index 447269e..01f9bdd 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes.sp @@ -47,7 +47,7 @@ public Plugin myinfo = name = "NativeVotes | Fun Votes", author = "Powerlord and AlliedModders LLC", description = "NativeVotes Fun Vote Commands", - version = "26w06h", + version = "26w07a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp b/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp index 1e1b097..cab2155 100644 --- a/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp +++ b/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp @@ -60,7 +60,7 @@ public Plugin myinfo = { name = "NativeVotes | Kick Vote Immunity", author = "Powerlord", description = "Causes TF2 kick votes to fail against people who the current user can't target.", - version = "26w06h", + version = "26w07a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_nominations.sp b/addons/sourcemod/scripting/nativevotes_nominations.sp index 3403af9..b7d7de9 100644 --- a/addons/sourcemod/scripting/nativevotes_nominations.sp +++ b/addons/sourcemod/scripting/nativevotes_nominations.sp @@ -50,7 +50,7 @@ public Plugin myinfo = name = "NativeVotes | Map Nominations", author = "AlliedModders LLC and Powerlord", description = "Provides Map Nominations", - version = "26w06h", + version = "26w07a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_voterp.sp b/addons/sourcemod/scripting/nativevotes_voterp.sp index 1f809a2..417871c 100644 --- a/addons/sourcemod/scripting/nativevotes_voterp.sp +++ b/addons/sourcemod/scripting/nativevotes_voterp.sp @@ -16,7 +16,7 @@ public Plugin myinfo = name = "NativeVotes | Medieval Auto-RP", author = "Heapons", description = "Provides Medieval Auto-RP voting.", - version = "26w06h", + version = "26w07a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_votescramble.sp b/addons/sourcemod/scripting/nativevotes_votescramble.sp index 9b30d5d..8b56706 100644 --- a/addons/sourcemod/scripting/nativevotes_votescramble.sp +++ b/addons/sourcemod/scripting/nativevotes_votescramble.sp @@ -13,7 +13,7 @@ public Plugin myinfo = name = "NativeVotes | Scramble Teams", author = "Heapons", description = "Provides RTV-style Scramble Teams Voting", - version = "26w06h", + version = "26w07a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; From cd4fc1fc7db7e7a6ff96ab0148e51577aac941b0 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sat, 14 Feb 2026 21:00:28 +0100 Subject: [PATCH 096/128] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e52520a..6bafede 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,7 @@ This fork aims to expand upon [<img src="https://avatars.githubusercontent.com/u |`sm_mapvote_voteduration`|`20`|Specifies how long the mapvote should be available for (in seconds)| |`sm_mapvote_runoff`|`0`|Hold runoff votes if winning choice is less than a certain margin| |`sm_mapvote_runoffpercent`|`50`|If winning choice has less than this percent of votes, hold a runoff| +|`sm_mapvote_shuffle_nominations`|`0`|If set, allows infinite nominations and picks a random subset to appear in the vote.| |`sm_mapcycle_auto`|`0`|Specifies whether to automatically populate the maps list.| |`sm_mapcycle_exclude`|`.*test.*\|background01\|^tr.*$`|Specifies which maps shouldn't be automatically added (regex pattern).| |<img src="https://cdn.fastly.steamstatic.com/steamcommunity/public/images/apps/440/033bdd91842b6aca0633ee1e5f3e6b82f2e8962f.ico" width="16" height="16" style="vertical-align: text-bottom;"> `sm_workshop_map_collection`|` `|Specifies the workshop collection to fetch the maps from.| From 2b53aaae5a7b68add4a6b85fa4cbf56c4c1cd28f Mon Sep 17 00:00:00 2001 From: megascatterbomb <megascatterbomb@gmail.com> Date: Sun, 15 Feb 2026 23:03:49 +1300 Subject: [PATCH 097/128] update CTF timers by changing mp_timelimit briefly --- .../scripting/nativevotes_votescramble.sp | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_votescramble.sp b/addons/sourcemod/scripting/nativevotes_votescramble.sp index 21bd141..3144a64 100644 --- a/addons/sourcemod/scripting/nativevotes_votescramble.sp +++ b/addons/sourcemod/scripting/nativevotes_votescramble.sp @@ -40,7 +40,7 @@ bool g_Voted[MAXPLAYERS+1] = {false, ...}; bool g_NativeVotes; bool g_RegisteredScramble = false; int g_ScrambleTime = 0; -float g_MapResetTime = 0; +float g_MapResetTime = -1.0; int g_SetRoundsPlayedTo = -1; public void OnPluginStart() @@ -212,7 +212,27 @@ public void Event_TeamplayRoundStart(Event event, const char[] name, bool dontBr CreateTimer(g_ConVars[initialdelay].FloatValue, Timer_DelayScramble, _, TIMER_FLAG_NO_MAPCHANGE); if (!g_ConVars[full_reset].BoolValue) { - GameRules_SetPropFloat("m_flMapResetTime", g_MapResetTime); + // If this isn't -1 we know our plugin just triggered a scramble, gotta change it back + if (g_MapResetTime >= 0) { + GameRules_SetPropFloat("m_flMapResetTime", g_MapResetTime); + + // Some gamemodes create a team_round_timer that mimics mp_timelimit, we can update these by setting mp_timelimit to 0 and back. + if (g_ConVars[mp_timelimit].IntValue > 0) { + int flags = g_ConVars[mp_timelimit].Flags; + int oldFlags = flags; + int oldValue = g_ConVars[mp_timelimit].IntValue; + + // Don't notify about this cvar change. + flags = flags & ~FCVAR_NOTIFY; + g_ConVars[mp_timelimit].Flags = flags; + g_ConVars[mp_timelimit].IntValue = 0; + + DataPack pack; + CreateDataTimer(0.1, Timer_FixCTFTimer, pack, TIMER_FLAG_NO_MAPCHANGE); + pack.WriteCell(oldValue); + pack.WriteCell(oldFlags); + } + } // If this isn't -1 we know our plugin just triggered a scramble, gotta change it back if (g_SetRoundsPlayedTo >= 0) @@ -221,6 +241,7 @@ public void Event_TeamplayRoundStart(Event event, const char[] name, bool dontBr } } g_SetRoundsPlayedTo = -1; + g_MapResetTime = -1.0; } public Action Command_VoteScramble(int client, int args) @@ -505,6 +526,15 @@ public Action Timer_DelayScramble(Handle timer) return Plugin_Continue; } +public Action Timer_FixCTFTimer(Handle timer, DataPack pack) +{ + pack.Reset(); + int oldValue = pack.ReadCell(); + int oldFlags = pack.ReadCell(); + g_ConVars[mp_timelimit].IntValue = oldValue; + g_ConVars[mp_timelimit].Flags = oldFlags; +} + void GetPlayerName(int client, char[] name, int maxlen) { int r, g, b, a, color; From 332ba5fd9ed994b248544a5d62cfebb3287c99a5 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Fri, 20 Feb 2026 00:28:20 +0100 Subject: [PATCH 098/128] =?UTF-8?q?Hook=20built-in=20kick=20vote=20menu=20?= =?UTF-8?q?+=20SourceBans++=20integration=20"Kick=20Vote=20Immunity"=20mod?= =?UTF-8?q?ule=20is=20now=20deprecated=E2=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/nativevotes.yml | 1 + .gitignore | 3 +- .../scripting/nativevotes_basevotes.sp | 364 +++++++++++++----- 3 files changed, 265 insertions(+), 103 deletions(-) diff --git a/.github/workflows/nativevotes.yml b/.github/workflows/nativevotes.yml index c541f3d..94ee54e 100644 --- a/.github/workflows/nativevotes.yml +++ b/.github/workflows/nativevotes.yml @@ -32,6 +32,7 @@ jobs: - name: Download dependencies shell: bash run: | + curl -o addons/sourcemod/scripting/include/sourcebanspp.inc https://raw.githubusercontent.com/sbpp/sourcebans-pp/refs/heads/php81/game/addons/sourcemod/scripting/include/sourcebanspp.inc mkdir -p addons/sourcemod/scripting/include/ripext curl -o addons/sourcemod/scripting/include/ripext.inc https://raw.githubusercontent.com/ErikMinekus/sm-ripext/main/pawn/scripting/include/ripext.inc curl -o addons/sourcemod/scripting/include/ripext/http.inc https://raw.githubusercontent.com/ErikMinekus/sm-ripext/main/pawn/scripting/include/ripext/http.inc diff --git a/.gitignore b/.gitignore index 452dfdb..9904c40 100644 --- a/.gitignore +++ b/.gitignore @@ -80,4 +80,5 @@ virtual_address.inc steam* ripext* -multicolors* \ No newline at end of file +multicolors* +source* \ No newline at end of file diff --git a/addons/sourcemod/scripting/nativevotes_basevotes.sp b/addons/sourcemod/scripting/nativevotes_basevotes.sp index 1938753..1711704 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes.sp @@ -36,18 +36,21 @@ #pragma semicolon 1 #include <sourcemod> +#include <sdktools> + #undef REQUIRE_PLUGIN #include <adminmenu> #include <nativevotes> +#include <sourcebanspp> #define PLUGIN_PREFIX "[\x04NativeVotes\x01]" -public Plugin:myinfo = +public Plugin myinfo = { name = "NativeVotes | Basic Votes", author = "Powerlord and AlliedModders LLC", description = "NativeVotes Basic Vote Commands", - version = "26w07a", + version = "26w08a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -56,10 +59,25 @@ public Plugin:myinfo = // Handle g_hVoteMenu; -ConVar g_ConVars[question]; +enum +{ + vote_map, + vote_kick, + vote_ban, + vote_question, + + sv_vote_issue_kick_allowed, + sv_vote_issue_kick_allowed_mvm, + sv_vote_kick_ban_duration, + sv_vote_timer_duration, + + MAX_CONVARS +} + +ConVar g_ConVars[MAX_CONVARS]; // ConVar g_Cvar_VoteSay; -enum voteType +enum VoteType { map, kick, @@ -67,26 +85,31 @@ enum voteType question } -voteType g_voteType = question; +VoteType g_VoteType = question; // Menu API does not provide us with a way to pass multiple peices of data with a single // choice, so some globals are used to hold stuff. // #define VOTE_CLIENTID 0 #define VOTE_USERID 1 -int g_voteClient[2]; // Holds the target's client id and user id + +int g_VoteClient[2]; // Holds the target's client id and user id #define VOTE_NAME 0 #define VOTE_AUTHID 1 #define VOTE_IP 2 -char g_voteInfo[3][65]; // Holds the target's name, authid, and IP -char g_voteArg[256]; // Used to hold ban/kick reasons or vote questions +char g_VoteInfo[3][65]; // Holds the target's name, authid, and IP +char g_VoteArg[256]; // Used to hold ban/kick reasons or vote questions -Handle hTopMenu = INVALID_HANDLE; +Handle g_TopMenu = INVALID_HANDLE; -// NativeVotes +bool g_KickVoteRegistered = false; +bool g_isMvM = false; + +// Libraries bool g_NativeVotes; +bool g_SourceBans; //Handle g_Cvar_NativeVotesMenu = INVALID_HANDLE; @@ -106,9 +129,16 @@ public void OnPluginStart() RegAdminCmd("sm_voteban", Command_Voteban, ADMFLAG_VOTE|ADMFLAG_BAN, "sm_voteban <player> [reason]"); RegAdminCmd("sm_vote", Command_Vote, ADMFLAG_VOTE, "sm_vote <title> [option1] [option2] ... [option5]"); - g_ConVars[map] = CreateConVar("sm_vote_map", "0.60", "percent required for successful map vote.", 0, true, 0.05, true, 1.0); - g_ConVars[kick] = CreateConVar("sm_vote_kick", "0.60", "percent required for successful kick vote.", 0, true, 0.05, true, 1.0); - g_ConVars[ban] = CreateConVar("sm_vote_ban", "0.60", "percent required for successful ban vote.", 0, true, 0.05, true, 1.0); + g_ConVars[vote_map] = CreateConVar("sm_vote_map", "0.60", "percent required for successful map vote.", 0, true, 0.05, true, 1.0); + g_ConVars[vote_kick] = CreateConVar("sm_vote_kick", "0.60", "percent required for successful kick vote.", 0, true, 0.05, true, 1.0); + g_ConVars[vote_ban] = CreateConVar("sm_vote_ban", "0.60", "percent required for successful ban vote.", 0, true, 0.05, true, 1.0); + + g_ConVars[sv_vote_issue_kick_allowed] = FindConVar("sv_vote_issue_kick_allowed"); + g_ConVars[sv_vote_issue_kick_allowed].AddChangeHook(ConVar_VoteKickAllowed); + g_ConVars[sv_vote_issue_kick_allowed_mvm] = FindConVar("sv_vote_issue_kick_allowed_mvm"); + g_ConVars[sv_vote_issue_kick_allowed_mvm].AddChangeHook(ConVar_VoteKickAllowed); + g_ConVars[sv_vote_kick_ban_duration] = FindConVar("sv_vote_kick_ban_duration"); + g_ConVars[sv_vote_timer_duration] = FindConVar("sv_vote_timer_duration"); g_SelectedMaps = CreateArray(PLATFORM_MAX_PATH); @@ -121,6 +151,60 @@ public void OnPluginStart() SetMapListCompatBind("sm_votemap menu", mapListPath); } +public void OnMapStart() +{ + g_isMvM = FindSendPropInfo("CTFGameRulesProxy", "m_bPlayingMannVsMachine") > 0 && GameRules_GetProp("m_bPlayingMannVsMachine"); +} + +void ConVar_VoteKickAllowed(ConVar convar, const char[] oldValue, const char[] newValue) +{ + if (!g_NativeVotes) + { + return; + } + + if (g_ConVars[sv_vote_issue_kick_allowed].BoolValue || (g_isMvM && g_ConVars[sv_vote_issue_kick_allowed_mvm].BoolValue)) + { + RegisterKickVoteCommand(); + } + else + { + UnregisterKickVoteCommand(); + } +} + +void RegisterKickVoteCommand() +{ + if (!g_KickVoteRegistered && g_NativeVotes) + { + bool isAllowed = false; + if (g_ConVars[sv_vote_issue_kick_allowed] != null && g_ConVars[sv_vote_issue_kick_allowed_mvm] != null) + { + isAllowed = g_isMvM ? g_ConVars[sv_vote_issue_kick_allowed_mvm].BoolValue : + g_ConVars[sv_vote_issue_kick_allowed].BoolValue; + } + if (isAllowed) + { + NativeVotes_RegisterVoteCommand(NativeVotesOverride_Kick, Menu_Kick); + g_KickVoteRegistered = true; + } + } +} + +void UnregisterKickVoteCommand() +{ + if (g_KickVoteRegistered && g_NativeVotes) + { + NativeVotes_UnregisterVoteCommand(NativeVotesOverride_Kick, Menu_Kick); + g_KickVoteRegistered = false; + } +} + +public void OnPluginEnd() +{ + UnregisterKickVoteCommand(); +} + public void OnAllPluginsLoaded() { if (FindPluginByFile("basevotes.smx") != INVALID_HANDLE) @@ -136,18 +220,19 @@ public void OnAllPluginsLoaded() { LogMessage("Moving basevotes to disabled."); } - - //SetFailState("This plugin replaces basevotes. You cannot run both at once."); + + //SetFailState("This plugin replaces basevotes. You cannot run both at once."); } - + /* Account for late loading */ Handle topmenu; if (LibraryExists("adminmenu") && ((topmenu = GetAdminTopMenu()) != INVALID_HANDLE)) { OnAdminMenuReady(topmenu); } - + g_NativeVotes = LibraryExists("nativevotes") && NativeVotes_IsVoteTypeSupported(NativeVotesType_Custom_YesNo); + g_SourceBans = LibraryExists("sourcebans++"); } public void OnLibraryAdded(const char[] name) @@ -160,6 +245,11 @@ public void OnLibraryAdded(const char[] name) else if (StrEqual(name, "nativevotes") && NativeVotes_IsVoteTypeSupported(NativeVotesType_Custom_YesNo)) { g_NativeVotes = true; + RegisterKickVoteCommand(); + } + else if (StrEqual(name, "sourcebans++")) + { + g_SourceBans = true; } } @@ -167,36 +257,73 @@ public void OnLibraryRemoved(const char[] name) { if (StrEqual(name, "adminmenu")) { - hTopMenu = INVALID_HANDLE; + g_TopMenu = INVALID_HANDLE; } else if (StrEqual(name, "nativevotes")) { g_NativeVotes = false; + UnregisterKickVoteCommand(); + } + else if (StrEqual(name, "sourcebans++")) + { + g_SourceBans = false; } } public void OnConfigsExecuted() { g_mapCount = LoadMapList(g_MapList); + if (g_KickVoteRegistered) + { + UnregisterKickVoteCommand(); + RegisterKickVoteCommand(); + } +} + +public Action Menu_Kick(int client, NativeVotesOverride overrideType, const char[] voteArgument) +{ + int target = 0; + if (strlen(voteArgument) > 0) + { + target = FindTarget(client, voteArgument); + } + + if (!CanUserTarget(client, target)) + { + NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_CantKickAdmin); + CPrintToChat(client, "%t", "Unable to target"); + return Plugin_Stop; + } + + ReplySource old = SetCmdReplySource(SM_REPLY_TO_CHAT); + + if (target > 0) + { + DisplayVoteKickMenu(client, target); + } + + SetCmdReplySource(old); + + return Plugin_Stop; } public void OnAdminMenuReady(Handle topmenu) { /* Block us from being called twice */ - if (topmenu == hTopMenu) + if (topmenu == g_TopMenu) { return; } /* Save the Handle */ - hTopMenu = topmenu; + g_TopMenu = topmenu; /* Build the "Voting Commands" category */ - TopMenuObject voting_commands = FindTopMenuCategory(hTopMenu, ADMINMENU_VOTINGCOMMANDS); + TopMenuObject voting_commands = FindTopMenuCategory(g_TopMenu, ADMINMENU_VOTINGCOMMANDS); if (voting_commands != INVALID_TOPMENUOBJECT) { - AddToTopMenu(hTopMenu, + AddToTopMenu(g_TopMenu, "sm_votekick", TopMenuObject_Item, AdminMenu_VoteKick, @@ -204,7 +331,7 @@ public void OnAdminMenuReady(Handle topmenu) "sm_votekick", ADMFLAG_VOTE|ADMFLAG_KICK); - AddToTopMenu(hTopMenu, + AddToTopMenu(g_TopMenu, "sm_voteban", TopMenuObject_Item, AdminMenu_VoteBan, @@ -212,7 +339,7 @@ public void OnAdminMenuReady(Handle topmenu) "sm_voteban", ADMFLAG_VOTE|ADMFLAG_BAN); - AddToTopMenu(hTopMenu, + AddToTopMenu(g_TopMenu, "sm_votemap", TopMenuObject_Item, AdminMenu_VoteMap, @@ -246,7 +373,7 @@ public Action Command_Vote(int client, int args) char answers[5][64]; int answerCount = 0; - int len = BreakString(text, g_voteArg, sizeof(g_voteArg)); + int len = BreakString(text, g_VoteArg, sizeof(g_VoteArg)); int pos = len; while (args > 1 && pos != -1 && answerCount < 5) @@ -260,8 +387,8 @@ public Action Command_Vote(int client, int args) } } LogAction(client, -1, "\"%L\" initiated a generic vote.", client); - CShowActivity2(client, "[\x04NativeVotes\x01] ", "%t", "Initiate Vote", g_voteArg); - g_voteType = question; + CShowActivity2(client, PLUGIN_PREFIX ... " ", "%t", "Initiate Vote", g_VoteArg); + g_VoteType = question; Handle voteMenu; if (g_NativeVotes) @@ -275,13 +402,13 @@ public Action Command_Vote(int client, int args) NativeVotes_AddItem(voteMenu, answers[i], answers[i]); } } - NativeVotes_SetTitle(voteMenu, g_voteArg); - NativeVotes_DisplayToAll(voteMenu, 20); + NativeVotes_SetTitle(voteMenu, g_VoteArg); + NativeVotes_DisplayToAll(voteMenu, g_ConVars[sv_vote_timer_duration].IntValue); } else { voteMenu = CreateMenu(Handler_VoteCallback, view_as<MenuAction>(MENU_ACTIONS_ALL)); - SetMenuTitle(voteMenu, g_voteArg); + SetMenuTitle(voteMenu, g_VoteArg); if (answerCount == 0) { @@ -297,7 +424,7 @@ public Action Command_Vote(int client, int args) } SetMenuExitButton(voteMenu, false); - VoteMenuToAll(voteMenu, 20); + VoteMenuToAll(voteMenu, g_ConVars[sv_vote_timer_duration].IntValue); } return Plugin_Handled; } @@ -312,13 +439,13 @@ public Handler_VoteCallback(Handle:menu, MenuAction:action, param1, param2) } case MenuAction_Display: { - if (g_voteType != voteType:question) + if (g_VoteType != question) { char title[64]; GetMenuTitle(menu, title, sizeof(title)); char buffer[255]; - Format(buffer, sizeof(buffer), "%T", title, param1, g_voteInfo[VOTE_NAME]); + Format(buffer, sizeof(buffer), "%T", title, param1, g_VoteInfo[VOTE_NAME]); Handle panel = Handle:param2; SetPanelTitle(panel, buffer); @@ -360,17 +487,17 @@ public Handler_VoteCallback(Handle:menu, MenuAction:action, param1, param2) percent = GetVotePercent(votes, totalVotes); - if (g_voteType != voteType:question) + if (g_VoteType != question) { - limit = GetConVarFloat(g_ConVars[g_voteType]); + limit = GetConVarFloat(g_ConVars[g_VoteType]); } - /* :TODO: g_voteClient[userid] needs to be checked */ + /* :TODO: g_VoteClient[userid] needs to be checked */ // A multi-argument vote is "always successful", but have to check if its a Yes/No vote. if ((strcmp(item, VOTE_YES) == 0 && FloatCompare(percent,limit) < 0 && param1 == 0) || (strcmp(item, VOTE_NO) == 0 && param1 == 1)) { - /* :TODO: g_voteClient[userid] should be used here and set to -1 if not applicable. + /* :TODO: g_VoteClient[userid] should be used here and set to -1 if not applicable. */ LogAction(-1, -1, "Vote failed."); CPrintToChatAll("%t", "Vote Failed", RoundToNearest(100.0*limit), RoundToNearest(100.0*percent), totalVotes); @@ -379,37 +506,37 @@ public Handler_VoteCallback(Handle:menu, MenuAction:action, param1, param2) { CPrintToChatAll("%t", "Vote Successful", RoundToNearest(100.0*percent), totalVotes); - int client = GetClientOfUserId(g_voteClient[VOTE_USERID]); + int client = GetClientOfUserId(g_VoteClient[VOTE_USERID]); int r, g, b, a, color; GetEntityRenderColor(client, r, g, b, a); color = (r << 16) | (g << 8) | b; if (color != 0xFFFFFF) { - Format(g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[VOTE_NAME]), "{#%06X}%N\x01", color, client); + Format(g_VoteInfo[VOTE_NAME], sizeof(g_VoteInfo[VOTE_NAME]), "{#%06X}%N\x01", color, client); } else { - Format(g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[VOTE_NAME]), "{teamcolor}%N\x01", client); + Format(g_VoteInfo[VOTE_NAME], sizeof(g_VoteInfo[VOTE_NAME]), "{teamcolor}%N\x01", client); } - switch (g_voteType) + switch (g_VoteType) { case question: { if (strcmp(item, VOTE_NO) == 0 || strcmp(item, VOTE_YES) == 0) { - for (new i = 1; i <= MaxClients; i++) + for (int i = 1; i <= MaxClients; i++) { if (IsClientInGame(i) && !IsFakeClient(i)) { Format(item, sizeof(item), "%T", display, i); - CPrintToChat(i, "%t", "Vote End", g_voteArg, item); + CPrintToChat(i, "%t", "Vote End", g_VoteArg, item); } } } else { - CPrintToChatAll("%t", "Vote End", g_voteArg, item); + CPrintToChatAll("%t", "Vote End", g_VoteArg, item); } } case map: @@ -422,36 +549,47 @@ public Handler_VoteCallback(Handle:menu, MenuAction:action, param1, param2) } case kick: { - if (g_voteArg[0] == '\0') + if (g_VoteArg[0] == '\0') { - strcopy(g_voteArg, sizeof(g_voteArg), "Votekicked"); + strcopy(g_VoteArg, sizeof(g_VoteArg), "Votekicked"); } - if (GetClientOfUserId(g_voteClient[VOTE_USERID]) > 0 && !IsClientInKickQueue(g_voteClient[VOTE_CLIENTID])) + if (GetClientOfUserId(g_VoteClient[VOTE_USERID]) > 0 && !IsClientInKickQueue(g_VoteClient[VOTE_CLIENTID])) { - CPrintToChatAllEx(client, "%t", "Kicked target", "_s", g_voteInfo[VOTE_NAME]); - LogAction(-1, g_voteClient[VOTE_CLIENTID], "Vote kick successful, kicked \"%L\" (reason \"%s\")", g_voteClient[VOTE_CLIENTID], g_voteArg); - - KickClient(g_voteClient[VOTE_CLIENTID], "%s", g_voteArg); + CPrintToChatAllEx(client, "%t", "Kicked target", "_s", g_VoteInfo[VOTE_NAME]); + LogAction(-1, g_VoteClient[VOTE_CLIENTID], "Vote kick successful, kicked \"%L\" (reason \"%s\")", g_VoteClient[VOTE_CLIENTID], g_VoteArg); + + if (g_SourceBans) + { + SBPP_BanPlayer(0, g_VoteClient[VOTE_CLIENTID], g_ConVars[sv_vote_kick_ban_duration].IntValue, g_VoteArg); + } + else if (g_ConVars[sv_vote_kick_ban_duration].IntValue < 0) + { + KickClient(g_VoteClient[VOTE_CLIENTID], g_VoteArg); + } + else + { + BanClient(g_VoteClient[VOTE_CLIENTID], g_ConVars[sv_vote_kick_ban_duration].IntValue, BANFLAG_AUTO, g_VoteArg, "Removed from match by system", "sm_votekick"); + } } } case ban: { - if (g_voteArg[0] == '\0') + if (g_VoteArg[0] == '\0') { - strcopy(g_voteArg, sizeof(g_voteArg), "Votebanned"); + strcopy(g_VoteArg, sizeof(g_VoteArg), "Votebanned"); } - if (GetClientOfUserId(g_voteClient[VOTE_USERID]) > 0) + if (GetClientOfUserId(g_VoteClient[VOTE_USERID]) > 0) { - CPrintToChatAllEx(client, "%t", "Banned player", g_voteInfo[VOTE_NAME], 30); - LogAction(-1, g_voteClient[VOTE_CLIENTID], "Vote ban successful, banned \"%L\" (minutes \"30\") (reason \"%s\")", g_voteClient[VOTE_CLIENTID], g_voteArg); + CPrintToChatAllEx(client, "%t", "Banned player", g_VoteInfo[VOTE_NAME], 30); + LogAction(-1, g_VoteClient[VOTE_CLIENTID], "Vote ban successful, banned \"%L\" (minutes \"30\") (reason \"%s\")", g_VoteClient[VOTE_CLIENTID], g_VoteArg); - BanClient(g_voteClient[VOTE_CLIENTID], + BanClient(g_VoteClient[VOTE_CLIENTID], 30, BANFLAG_AUTO, - g_voteArg, + g_VoteArg, "Banned by vote", "sm_voteban"); } @@ -476,16 +614,16 @@ public Handler_NativeVoteCallback(Handle:menu, MenuAction:action, param1, param2 case MenuAction_Display: { - new NativeVotesType:nVoteType = NativeVotes_GetType(menu); - if (g_voteType != voteType:question && (nVoteType == NativeVotesType_Custom_YesNo || nVoteType == NativeVotesType_Custom_Mult)) + NativeVotesType nVoteType = NativeVotes_GetType(menu); + if (g_VoteType != question && (nVoteType == NativeVotesType_Custom_YesNo || nVoteType == NativeVotesType_Custom_Mult)) { char title[64]; NativeVotes_GetTitle(menu, title, sizeof(title)); char buffer[255]; - Format(buffer, sizeof(buffer), "%T", title, param1, g_voteInfo[VOTE_NAME]); + Format(buffer, sizeof(buffer), "%T", title, param1, g_VoteInfo[VOTE_NAME]); - return _:NativeVotes_RedrawVoteTitle(buffer); + return NativeVotes_RedrawVoteTitle(buffer); } } @@ -507,30 +645,46 @@ public Handler_NativeVoteCallback(Handle:menu, MenuAction:action, param1, param2 char item[64], display[64]; float percent, limit; int votes, totalVotes; - - new NativeVotesType:nVoteType = NativeVotes_GetType(menu); + + NativeVotesType nVoteType = NativeVotes_GetType(menu); NativeVotes_GetInfo(param2, votes, totalVotes); NativeVotes_GetItem(menu, param1, item, sizeof(item), display, sizeof(display)); - + if (nVoteType == NativeVotesType_Custom_YesNo && param1 == NATIVEVOTES_VOTE_NO) { votes = totalVotes - votes; // Reverse the votes to be in relation to the Yes option. } - + percent = GetVotePercent(votes, totalVotes); - - if (g_voteType != voteType:question) + + switch (nVoteType) { - limit = GetConVarFloat(g_ConVars[g_voteType]); + case NativeVotesType_Kick, NativeVotesType_KickIdle, NativeVotesType_KickScamming, NativeVotesType_KickCheating: + { + g_VoteType = kick; + } + case NativeVotesType_ChgLevel: + { + g_VoteType = map; + } + case NativeVotesType_Custom_YesNo, NativeVotesType_Custom_Mult: + { + g_VoteType = question; + } } - - /* :TODO: g_voteClient[userid] needs to be checked */ + + if (g_VoteType != question) + { + limit = GetConVarFloat(g_ConVars[g_VoteType]); + } + + /* :TODO: g_VoteClient[userid] needs to be checked */ // A multi-argument vote is "always successful", but have to check if its a Yes/No vote. if ((nVoteType != NativeVotesType_NextLevelMult && nVoteType != NativeVotesType_Custom_Mult) && ((param1 == NATIVEVOTES_VOTE_YES && FloatCompare(percent,limit) < 0) || (param1 == NATIVEVOTES_VOTE_NO))) { - /* :TODO: g_voteClient[userid] should be used here and set to -1 if not applicable. + /* :TODO: g_VoteClient[userid] should be used here and set to -1 if not applicable. */ NativeVotes_DisplayFail(menu, NativeVotesFail_Loses); LogAction(-1, -1, "Vote failed."); @@ -539,30 +693,28 @@ public Handler_NativeVoteCallback(Handle:menu, MenuAction:action, param1, param2 else { CPrintToChatAll("%t", "Vote Successful", RoundToNearest(100.0*percent), totalVotes); - - switch (g_voteType) + switch (g_VoteType) { case question: { if (nVoteType == NativeVotesType_Custom_YesNo) { - for (new i = 1; i <= MaxClients; i++) + for (int i = 1; i <= MaxClients; i++) { if (IsClientInGame(i) && !IsFakeClient(i)) { Format(item, sizeof(item), "%T", display, i); - CPrintToChat(i, "%t", "Vote End", g_voteArg, item); - NativeVotes_DisplayPassCustomToOne(menu, i, "%t", "Vote End", g_voteArg, item); + CPrintToChat(i, "%t", "Vote End", g_VoteArg, item); + NativeVotes_DisplayPassCustomToOne(menu, i, "%t", "Vote End", g_VoteArg, item); } } } else { - CPrintToChatAll("%t", "Vote End", g_voteArg, item); - NativeVotes_DisplayPassCustom(menu, "%t", "Vote End", g_voteArg, item); + CPrintToChatAll("%t", "Vote End", g_VoteArg, item); + NativeVotes_DisplayPassCustom(menu, "%t", "Vote End", g_VoteArg, item); } } - case map: { if (nVoteType == NativeVotesType_ChgLevel) @@ -578,44 +730,48 @@ public Handler_NativeVoteCallback(Handle:menu, MenuAction:action, param1, param2 CreateDataTimer(5.0, Timer_ChangeMap, dp); WritePackString(dp, item); } - case kick: { - if (g_voteArg[0] == '\0') + if (g_VoteArg[0] == '\0') { - strcopy(g_voteArg, sizeof(g_voteArg), "Votekicked"); + strcopy(g_VoteArg, sizeof(g_VoteArg), "Votekicked"); } - - if (GetClientOfUserId(g_voteClient[VOTE_USERID]) > 0 && !IsClientInKickQueue(g_voteClient[VOTE_CLIENTID])) + if (GetClientOfUserId(g_VoteClient[VOTE_USERID]) > 0 && !IsClientInKickQueue(g_VoteClient[VOTE_CLIENTID])) { - CPrintToChatAll("%t", "Kicked target", "_s", g_voteInfo[VOTE_NAME]); - LogAction(-1, g_voteClient[VOTE_CLIENTID], "Vote kick successful, kicked \"%L\" (reason \"%s\")", g_voteClient[VOTE_CLIENTID], g_voteArg); - - KickClient(g_voteClient[VOTE_CLIENTID], "%s", g_voteArg); - NativeVotes_DisplayPass(menu, g_voteInfo[VOTE_NAME]); + CPrintToChatAll("%t", "Kicked target", "_s", g_VoteInfo[VOTE_NAME]); + LogAction(-1, g_VoteClient[VOTE_CLIENTID], "Vote kick successful, kicked \"%L\" (reason \"%s\")", g_VoteClient[VOTE_CLIENTID], g_VoteArg); + if (g_SourceBans) + { + SBPP_BanPlayer(0, g_VoteClient[VOTE_CLIENTID], g_ConVars[sv_vote_kick_ban_duration].IntValue, g_VoteArg); + } + else if (g_ConVars[sv_vote_kick_ban_duration].IntValue < 0) + { + KickClient(g_VoteClient[VOTE_CLIENTID], "%s", g_VoteArg); + } + else + { + BanClient(g_VoteClient[VOTE_CLIENTID], g_ConVars[sv_vote_kick_ban_duration].IntValue, BANFLAG_AUTO, g_VoteArg, "Kicked by vote", "sm_votekick"); + } + NativeVotes_DisplayPass(menu, g_VoteInfo[VOTE_NAME]); } } - case ban: { - if (g_voteArg[0] == '\0') + if (g_VoteArg[0] == '\0') { - strcopy(g_voteArg, sizeof(g_voteArg), "Votebanned"); + strcopy(g_VoteArg, sizeof(g_VoteArg), "Votebanned"); } - - if (GetClientOfUserId(g_voteClient[VOTE_USERID]) > 0) + if (GetClientOfUserId(g_VoteClient[VOTE_USERID]) > 0) { - CPrintToChatAll("%t", "Banned player", g_voteInfo[VOTE_NAME], 30); - LogAction(-1, g_voteClient[VOTE_CLIENTID], "Vote ban successful, banned \"%L\" (minutes \"30\") (reason \"%s\")", g_voteClient[VOTE_CLIENTID], g_voteArg); - - BanClient(g_voteClient[VOTE_CLIENTID], + CPrintToChatAll("%t", "Banned player", g_VoteInfo[VOTE_NAME], 30); + LogAction(-1, g_VoteClient[VOTE_CLIENTID], "Vote ban successful, banned \"%L\" (minutes \"30\") (reason \"%s\")", g_VoteClient[VOTE_CLIENTID], g_VoteArg); + BanClient(g_VoteClient[VOTE_CLIENTID], 30, BANFLAG_AUTO, - g_voteArg, + g_VoteArg, "Banned by vote", "sm_voteban"); - - NativeVotes_DisplayPassCustom(menu, "%t", "Banned player", g_voteInfo[VOTE_NAME], 30); + NativeVotes_DisplayPassCustom(menu, "%t", "Banned player", g_VoteInfo[VOTE_NAME], 30); } } } @@ -634,6 +790,10 @@ float GetVotePercent(int votes, int totalVotes) bool TestVoteDelay(int client) { int delay = Internal_CheckVoteDelay(); + if (client <= 0 || GetUserFlagBits(client) & ADMFLAG_GENERIC) + { + delay = 0; + } if (delay > 0) { From 65b612bc45e6967018f9d1a5cb79eadd7b80f525 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Fri, 20 Feb 2026 00:30:02 +0100 Subject: [PATCH 099/128] Add support for Fortress Connected --- .../scripting/include/nativevotes.inc | 118 +++++------ addons/sourcemod/scripting/nativevotes.sp | 47 +++-- .../sourcemod/scripting/nativevotes/game.sp | 184 ++++++++++++++---- 3 files changed, 238 insertions(+), 111 deletions(-) diff --git a/addons/sourcemod/scripting/include/nativevotes.inc b/addons/sourcemod/scripting/include/nativevotes.inc index 320ea6f..882cb63 100644 --- a/addons/sourcemod/scripting/include/nativevotes.inc +++ b/addons/sourcemod/scripting/include/nativevotes.inc @@ -83,67 +83,69 @@ MenuAction_DisplayItem Item text is being drawn to the display (param1=client, p */ enum NativeVotesType { - NativeVotesType_None = 0, /**< Special placeholder for callvote with no arguments for NativeVotes_OnCallVote */ - NativeVotesType_Custom_YesNo, /**< Yes/No, details are vote text. */ - NativeVotesType_Custom_Mult, /**< TF2/CS:GO: Multiple-choice, details are vote text. */ - NativeVotesType_ChgCampaign, /**< L4D/L4D2: Yes/No, details are campaign name */ - NativeVotesType_ChgDifficulty, /**< L4D/L4D2: Yes/No, details are difficulty number in L4D/L4D2 */ - NativeVotesType_ReturnToLobby, /**< L4D/L4D2: Yes/No, details are ignored */ - NativeVotesType_AlltalkOn, /**< L4D2: Yes/No, details are ignored (handled internally by extension) */ - NativeVotesType_AlltalkOff, /**< L4D2: Yes/No, details are ignored (handled internally by extension) */ - NativeVotesType_Restart, /**< Yes/No, details are ignored */ - NativeVotesType_Kick, /**< Yes/No, target is player userid, details are auto-set by target */ - NativeVotesType_KickIdle, /**< TF2/CS:GO: Yes/No, target is player userid, details are auto-set by target */ - NativeVotesType_KickScamming, /**< TF2/CS:GO: Yes/No, target is player userid, details are auto-set by target */ - NativeVotesType_KickCheating, /**< TF2/CS:GO: Yes/No, target is player userid, details are auto-set by target */ - NativeVotesType_ChgLevel, /**< Yes/No, details are level number in L4D/L4D2 or map name in TF2 */ - NativeVotesType_NextLevel, /**< TF2/CS:GO: Yes/No, details are map name */ - NativeVotesType_NextLevelMult, /**< TF2/CS:GO: Multiple-choice, details are ignored */ - NativeVotesType_ScrambleNow, /**< TF2/CS:GO: Yes/No, details are ignored */ - NativeVotesType_ScrambleEnd, /**< TF2: Yes/No, details are ignored */ - NativeVotesType_ChgMission, /**< TF2: Yes/No, details are popfile */ - NativeVotesType_SwapTeams, /**< CS:GO: Yes/No, details are ignored */ - NativeVotesType_Surrender, /**< CS:GO: Yes/No, details are ignored */ - NativeVotesType_Rematch, /**< CS:GO: Yes/No, details are ignored */ - NativeVotesType_Continue, /**< CS:GO: Yes/No, details are ignored */ - NativeVotesType_StartRound, /**< TF2: Yes/No, details are ignored */ - NativeVotesType_Eternaween, /**< TF2: Yes/No, details are ignored */ - NativeVotesType_AutoBalanceOn, /**< TF2: Yes/No, details are ignored */ - NativeVotesType_AutoBalanceOff, /**< TF2: Yes/No, details are ignored */ - NativeVotesType_ClassLimitsOn, /**< TF2: Yes/No, details are ignored */ - NativeVotesType_ClassLimitsOff, /**< TF2: Yes/No, details are ignored */ - NativeVotesType_Extend, /**< TF2: Yes/No, details are ignored */ - NativeVotesType_ChgCivilian, /**< TF2 Classified: Yes/No, target is player userid, details are auto-set by target */ + NativeVotesType_None = 0, /**< Special placeholder for callvote with no arguments for NativeVotes_OnCallVote */ + NativeVotesType_Custom_YesNo, /**< Yes/No, details are vote text. */ + NativeVotesType_Custom_Mult, /**< TF2/CS:GO: Multiple-choice, details are vote text. */ + NativeVotesType_ChgCampaign, /**< L4D/L4D2: Yes/No, details are campaign name */ + NativeVotesType_ChgDifficulty, /**< L4D/L4D2/Fortress Connected: Yes/No, details are difficulty number */ + NativeVotesType_ReturnToLobby, /**< L4D/L4D2: Yes/No, details are ignored */ + NativeVotesType_AlltalkOn, /**< L4D2: Yes/No, details are ignored (handled internally by extension) */ + NativeVotesType_AlltalkOff, /**< L4D2: Yes/No, details are ignored (handled internally by extension) */ + NativeVotesType_Restart, /**< Yes/No, details are ignored */ + NativeVotesType_Kick, /**< Yes/No, target is player userid, details are auto-set by target */ + NativeVotesType_KickIdle, /**< TF2/CS:GO: Yes/No, target is player userid, details are auto-set by target */ + NativeVotesType_KickScamming, /**< TF2/CS:GO: Yes/No, target is player userid, details are auto-set by target */ + NativeVotesType_KickCheating, /**< TF2/CS:GO: Yes/No, target is player userid, details are auto-set by target */ + NativeVotesType_ChgLevel, /**< Yes/No, details are level number in L4D/L4D2 or map name in TF2 */ + NativeVotesType_NextLevel, /**< TF2/CS:GO: Yes/No, details are map name */ + NativeVotesType_NextLevelMult, /**< TF2/CS:GO: Multiple-choice, details are ignored */ + NativeVotesType_ScrambleNow, /**< TF2/CS:GO: Yes/No, details are ignored */ + NativeVotesType_ScrambleEnd, /**< TF2: Yes/No, details are ignored */ + NativeVotesType_ChgMission, /**< TF2: Yes/No, details are popfile */ + NativeVotesType_SwapTeams, /**< CS:GO: Yes/No, details are ignored */ + NativeVotesType_Surrender, /**< CS:GO: Yes/No, details are ignored */ + NativeVotesType_Rematch, /**< CS:GO: Yes/No, details are ignored */ + NativeVotesType_Continue, /**< CS:GO: Yes/No, details are ignored */ + NativeVotesType_StartRound, /**< TF2: Yes/No, details are ignored */ + NativeVotesType_Eternaween, /**< TF2: Yes/No, details are ignored */ + NativeVotesType_AutoBalanceOn, /**< TF2: Yes/No, details are ignored */ + NativeVotesType_AutoBalanceOff, /**< TF2: Yes/No, details are ignored */ + NativeVotesType_ClassLimitsOn, /**< TF2: Yes/No, details are ignored */ + NativeVotesType_ClassLimitsOff, /**< TF2: Yes/No, details are ignored */ + NativeVotesType_Extend, /**< TF2: Yes/No, details are ignored */ + NativeVotesType_ChgCivilian, /**< TF2 Classified: Yes/No, target is player userid, details are auto-set by target */ + NativeVotesType_ChgGamemode /**< Fortress Connected: Yes/No, details are gamemode number */ }; enum NativeVotesPassType { - NativeVotesPass_None = 0, /**< Special placeholder for error value */ - NativeVotesPass_Custom, /**< Details are custom pass message */ - NativeVotesPass_ChgCampaign, /**< L4D/L4D2: Details are campaign name */ - NativeVotesPass_ChgDifficulty, /**< L4D/L4D2/TF2: Details are difficulty number in L4D/L4D2 and mission name in TF2 */ - NativeVotesPass_ReturnToLobby, /**< L4D/L4D2: Details are ignored */ - NativeVotesPass_AlltalkOn, /**< L4D2: Details are ignored */ - NativeVotesPass_AlltalkOff, /**< L4D2: Details are ignored */ - NativeVotesPass_Restart, /**< Details are ignored */ - NativeVotesPass_Kick, /**< Details are player name */ - NativeVotesPass_ChgLevel, /**< Details are level number in L4D/L4D2 or map name in TF2/CS:GO */ - NativeVotesPass_NextLevel, /**< TF2/CS:GO: Details are map name */ - NativeVotesPass_Extend, /**< TF2/CS:GO: Details are ignored */ - NativeVotesPass_Scramble, /**< TF2/CS:GO: Details are ignored */ - NativeVotesPass_ChgMission, /**< TF2: Details are popfile */ - NativeVotesPass_SwapTeams, /**< CS:GO: Details are ignored */ - NativeVotesPass_Surrender, /**< CS:GO: Details are ignored */ - NativeVotesPass_Rematch, /**< CS:GO: Details are ignored */ - NativeVotesPass_Continue, /**< CS:GO: Details are ignored */ - NativeVotesPass_StartRound, /**< TF2: Details are ignored */ - NativeVotesPass_Eternaween, /**< TF2: Details are ignored */ - NativeVotesPass_AutoBalanceOn, /**< TF2: Yes/No, details are ignored */ - NativeVotesPass_AutoBalanceOff, /**< TF2: Yes/No, details are ignored */ - NativeVotesPass_ClassLimitsOn, /**< TF2: Yes/No, details are ignored */ - NativeVotesPass_ClassLimitsOff, /**< TF2: Yes/No, details are ignored */ - NativeVotesPass_ChgCivilian, /**< TF2 Classified: Yes/No, target is player userid, details are auto-set by target */ -}; + NativeVotesPass_None = 0, /**< Special placeholder for error value */ + NativeVotesPass_Custom, /**< Details are custom pass message */ + NativeVotesPass_ChgCampaign, /**< L4D/L4D2: Details are campaign name */ + NativeVotesPass_ChgDifficulty, /**< L4D/L4D2/TF2/Fortress Connected: Details are difficulty number in L4D/L4D2/FC and mission name in TF2 */ + NativeVotesPass_ReturnToLobby, /**< L4D/L4D2: Details are ignored */ + NativeVotesPass_AlltalkOn, /**< L4D2: Details are ignored */ + NativeVotesPass_AlltalkOff, /**< L4D2: Details are ignored */ + NativeVotesPass_Restart, /**< Details are ignored */ + NativeVotesPass_Kick, /**< Details are player name */ + NativeVotesPass_ChgLevel, /**< Details are level number in L4D/L4D2 or map name in TF2/CS:GO */ + NativeVotesPass_NextLevel, /**< TF2/CS:GO: Details are map name */ + NativeVotesPass_Extend, /**< TF2/CS:GO: Details are ignored */ + NativeVotesPass_Scramble, /**< TF2/CS:GO: Details are ignored */ + NativeVotesPass_ChgMission, /**< TF2: Details are popfile */ + NativeVotesPass_SwapTeams, /**< CS:GO: Details are ignored */ + NativeVotesPass_Surrender, /**< CS:GO: Details are ignored */ + NativeVotesPass_Rematch, /**< CS:GO: Details are ignored */ + NativeVotesPass_Continue, /**< CS:GO: Details are ignored */ + NativeVotesPass_StartRound, /**< TF2: Details are ignored */ + NativeVotesPass_Eternaween, /**< TF2: Details are ignored */ + NativeVotesPass_AutoBalanceOn, /**< TF2: Yes/No, details are ignored */ + NativeVotesPass_AutoBalanceOff, /**< TF2: Yes/No, details are ignored */ + NativeVotesPass_ClassLimitsOn, /**< TF2: Yes/No, details are ignored */ + NativeVotesPass_ClassLimitsOff, /**< TF2: Yes/No, details are ignored */ + NativeVotesPass_ChgCivilian, /**< TF2 Classified: Yes/No, target is player userid, details are auto-set by target */ + NativeVotesPass_ChgGamemode /**< Fortress Connected: Yes/No, details are gamemode number */ +}; /** * Reasons a vote was canceled. Not used for L4D/L4D2, as they don't care @@ -240,6 +242,8 @@ enum NativeVotesOverride NativeVotesOverride_ClassLimits, NativeVotesOverride_Extend, NativeVotesOverride_ChgCivilian, + NativeVotesOverride_ChgGamemode, + NativeVotesOverride_ChgDifficulty, NativeVotesOverride_Count, } diff --git a/addons/sourcemod/scripting/nativevotes.sp b/addons/sourcemod/scripting/nativevotes.sp index 4a1b361..edd4f49 100644 --- a/addons/sourcemod/scripting/nativevotes.sp +++ b/addons/sourcemod/scripting/nativevotes.sp @@ -86,6 +86,7 @@ enum progress_console, progress_client_console, progress_hintcaption, + progress_hintcaption_icon, vote_delay, MAX_CONVARS @@ -151,7 +152,7 @@ public Plugin myinfo = name = "NativeVotes", author = "Powerlord", description = "Voting API to use the game's native vote panels. Compatible with L4D, L4D2, TF2, and CS:GO.", - version = "26w07a", + version = "26w08a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" } @@ -270,7 +271,8 @@ public void OnPluginStart() Event instructor = CreateEvent("instructor_server_hint_create", true); if (instructor != null) { - g_ConVars[progress_hintcaption] = CreateConVar("nativevotes_progress_hintcaption", "0", "Show current vote progress in a instructor hint caption", _, true, 0.0, true, 1.0); + g_ConVars[progress_hintcaption] = CreateConVar("nativevotes_progress_hintcaption", "0", "Show current vote progress in a instructor hint caption", _, true, 0.0, true, 1.0); + g_ConVars[progress_hintcaption_icon] = CreateConVar("nativevotes_progress_hintcaption_icon", "icon_tip", "Set the icon to use in instructor hint captions during votes"); instructor.Cancel(); } @@ -597,6 +599,10 @@ public Action Command_CallVote(int client, const char[] command, int argc) Call_PushCell(kickType); Call_PushCell(target); Call_Finish(result); + if (result >= Plugin_Handled) + { + return result; + } } } @@ -999,7 +1005,7 @@ bool:SendResultCallback(Handle:vote, num_votes, num_items, const votes[][]) new client_votes[MaxClients]; new num_clients; - for (new i = 1; i <= MaxClients; i++) + for (int i = 1; i <= MaxClients; i++) { if (g_ClientVotes[i] > VOTE_PENDING) { @@ -1044,7 +1050,7 @@ void DrawHintProgress() NativeVotesType voteType = Data_GetType(g_hCurVote); bool isMultVote = (voteType == NativeVotesType_Custom_Mult || voteType == NativeVotesType_NextLevelMult); - char leaderList[256]; + char leaderList[1024]; if (isMultVote) { BuildVoteLeaders(leaderList, sizeof(leaderList)); @@ -1077,7 +1083,7 @@ void DrawHintProgress() int maxLeaderLen = 256 - baseLen - 1; if (maxLeaderLen < 1) { - char caption[256]; + char caption[1024]; Format(caption, sizeof(caption), "%t", "Vote Count", g_NumVotes, g_TotalClients, iTimeRemaining); SendInstructorHint(client, caption); } @@ -1085,14 +1091,14 @@ void DrawHintProgress() { char[] cappedLeaders = new char[maxLeaderLen + 1]; BuildVoteLeaders(cappedLeaders, maxLeaderLen + 1); - char caption[256]; + char caption[1024]; Format(caption, sizeof(caption), "%s\n%s", baseText, cappedLeaders); SendInstructorHint(client, caption); } } else { - char caption[256]; + char caption[1024]; Format(caption, sizeof(caption), "%t", "Vote Count", g_NumVotes, g_TotalClients, iTimeRemaining); SendInstructorHint(client, caption); } @@ -1121,13 +1127,13 @@ void BuildVoteLeaders(char[] leaderList, int leaderListSize) int num_items = Internal_GetResults(votes); - /* Build the full leaderboard (bounded by buffer size) */ + /* Build the full leaderboard */ for (int i = 0; i < num_items; i++) { int cur_item = votes[i][VOTEINFO_ITEM_INDEX]; - char choice[256]; + char choice[1024]; Data_GetItemDisplay(g_hCurVote, cur_item, choice, sizeof(choice)); - char line[256]; + char line[1024]; Format(line, sizeof(line), "%i. %s: (%i)", i + 1, choice, votes[i][VOTEINFO_ITEM_VOTES]); if (i == 0) { @@ -1327,8 +1333,11 @@ bool InitializeVoting(NativeVote vote, int time, int flags) if (g_hVoteTimer != null) { - //KillTimer(g_hVoteTimer); - g_hVoteTimer = null; + delete g_hVoteTimer; + } + if (g_hDisplayTimer != null) + { + delete g_hDisplayTimer; } Internal_Reset(); @@ -1499,8 +1508,7 @@ void Internal_Reset(bool cancel=false) if (g_hDisplayTimer != null) { - KillTimer(g_hDisplayTimer); - g_hDisplayTimer = null; + delete g_hDisplayTimer; } if (!cancel) @@ -1671,6 +1679,7 @@ public int Native_Create(Handle plugin, int numParams) if (voteType != NativeVotesType_NextLevelMult && voteType != NativeVotesType_Custom_Mult) { + SetGlobalTransTarget(LANG_SERVER); Data_AddItem(vote, "yes", "Yes"); Data_AddItem(vote, "no", "No"); } @@ -2246,7 +2255,6 @@ public int Native_DisplayPass(Handle plugin, int numParams) { Game_DisplayVotePass(vote); } - } // native NativeVotes_DisplayPassCustomToOne(Handle vote, client, const char[] format, any:...); @@ -2649,8 +2657,13 @@ void SendInstructorHint(int client, const char[] caption) event.SetInt("hint_target", client); event.SetInt("hint_activator_userid", GetClientUserId(client)); event.SetInt("hint_timeout", 0); - event.SetString("hint_icon_onscreen", "tf2c_clock"); - event.SetString("hint_icon_offscreen", "tf2c_clock"); + char icon[64]; + g_ConVars[progress_hintcaption_icon].GetString(icon, sizeof(icon)); + if (icon[0] != '\0') + { + event.SetString("hint_icon_onscreen", icon); + event.SetString("hint_icon_offscreen", icon); + } event.SetString("hint_activator_caption", caption); event.SetInt("hint_allow_nodraw_target", 1); event.SetInt("hint_nooffscreen", 1); diff --git a/addons/sourcemod/scripting/nativevotes/game.sp b/addons/sourcemod/scripting/nativevotes/game.sp index 7db0d20..f486012 100644 --- a/addons/sourcemod/scripting/nativevotes/game.sp +++ b/addons/sourcemod/scripting/nativevotes/game.sp @@ -224,6 +224,19 @@ enum #define TF2CLASSIFIED_VOTE_CHANGE_CIVILIAN_CALLER "#TF_vote_change_civilian_caller" #define TF2CLASSIFIED_VOTE_CHANGE_CIVILIAN_PASSED "#TF_vote_passed_change_civilian" +//---------------------------------------------------------------------------- +// Fortress Connected + +#define FC_VOTE_STRING_CHANGE_DIFFICULTY "ChangeDifficulty" + +#define FC_VOTE_MENU_CHANGE_DIFFICULTY "#Vote_changedifficulty" +#define FC_VOTE_PASSED_CHANGE_DIFFICULTY "#Vote_passed_changedifficulty" + +#define FC_VOTE_STRING_CHANGE_GAMEMODE "ChangeGamemode" + +#define FC_VOTE_MENU_CHANGE_GAMEMODE "#Vote_changegamemode" +#define FC_VOTE_PASSED_CHANGE_GAMEMODE "#Vote_passed_changegamemode" + //---------------------------------------------------------------------------- // CSGO // User vote to kick user. @@ -373,15 +386,18 @@ static ConVar g_Cvar_AutoBalance; static ConVar g_Cvar_HideDisabledIssues; +// TF2 Classified static ConVar g_Cvar_ChangeCivilian_Enabled; +// Fortress Connected +static ConVar g_Cvar_ChangeDifficulty_Enabled; +static ConVar g_Cvar_ChangeGamemode_Enabled; + /** * TODO(UPDATE): For now we only support one vote from NativeVotes at a time. * * Ideally we peek and poke at the game's `s_nVoteIdx` and increment it accordingly, but we also * have to store an internal list of active votes. - * - * TODO: https://github.com/Heapons/sourcemod-nativevotes-updated/issues/2#issuecomment-3825973400 */ static int s_nNativeVoteIdx = 0; @@ -459,6 +475,10 @@ void Game_InitializeCvars() // Team Fortress 2 Classified g_Cvar_ChangeCivilian_Enabled = FindConVar("tf2c_vote_issue_change_civilian_allowed"); + + // Fortress Connected + g_Cvar_ChangeDifficulty_Enabled = FindConVar("sv_vote_issue_changedifficulty_allowed"); + g_Cvar_ChangeGamemode_Enabled = FindConVar("sv_vote_issue_changegamemode_allowed"); } } } @@ -1231,6 +1251,10 @@ static NativeVotesPassType VoteTypeToVotePass(NativeVotesType voteType) { passType = NativeVotesPass_ChgCivilian; } + case NativeVotesType_ChgGamemode: + { + passType = NativeVotesPass_ChgGamemode; + } default: { passType = NativeVotesPass_Custom; @@ -1465,7 +1489,6 @@ static int VoteCallFailTypeToInt(NativeVotesCallFailType failType) } } } - return VOTE_FAILED_GENERIC; } @@ -1935,7 +1958,6 @@ static void L4D2_DisplayVote(NativeVote vote, int[] clients, int num_clients) Data_GetTitle(vote, details, MAX_VOTE_DETAILS_LENGTH); bCustom = true; } - default: { Data_GetDetails(vote, details, MAX_VOTE_DETAILS_LENGTH); @@ -2197,7 +2219,7 @@ static void TF2CSGO_DisplayVote(NativeVote vote, int[] clients, int num_clients) } } - switch(g_EngineVersion) + switch (g_EngineVersion) { case Engine_CSGO: { @@ -2337,15 +2359,10 @@ static void TF2CSGO_DisplayVote(NativeVote vote, int[] clients, int num_clients) bfStart.WriteString(details); } bfStart.WriteByte(bYesNo); - if (!IsFakeClient(clients[i])) - { - bfStart.WriteByte(Data_GetTarget(vote) - 1); - } + bfStart.WriteByte(Data_GetTarget(vote) - 1); } - EndMessage(); } - g_curDisplayClient = 0; } @@ -2547,7 +2564,7 @@ static void TF2CSGO_ResetVote() SetEntProp(g_VoteController, Prop_Send, "m_iActiveIssueIndex", INVALID_ISSUE); } SetEntProp(g_VoteController, Prop_Send, "m_iOnlyTeamToVote", g_EngineVersion == Engine_TF2 ? NATIVEVOTES_TF2_ALL_TEAMS : NATIVEVOTES_ALL_TEAMS); - for (int i = 0; i < 5; i++) + for (int i = 0; i < TF2CSGO_COUNT; i++) { SetEntProp(g_VoteController, Prop_Send, "m_nVoteOptionCount", 0, _, i); } @@ -2591,7 +2608,9 @@ static bool TF2_CheckVoteType(NativeVotesType voteType) NativeVotesType_ClassLimitsOn, NativeVotesType_ClassLimitsOff, NativeVotesType_Extend, - NativeVotesType_ChgCivilian: + NativeVotesType_ChgCivilian, + NativeVotesType_ChgDifficulty, + NativeVotesType_ChgGamemode: { return true; } @@ -2622,7 +2641,9 @@ static bool TF2_CheckVotePassType(NativeVotesPassType passType) NativeVotesPass_AutoBalanceOff, NativeVotesPass_ClassLimitsOn, NativeVotesPass_ClassLimitsOff, - NativeVotesPass_ChgCivilian: + NativeVotesPass_ChgCivilian, + NativeVotesPass_ChgDifficulty, + NativeVotesPass_ChgGamemode: { return true; } @@ -2718,6 +2739,14 @@ static bool TF2_VoteTypeToTranslation(NativeVotesType voteType, char[] translati { strcopy(translation, maxlength, TF2CLASSIFIED_VOTE_CHANGE_CIVILIAN_START); } + case NativeVotesType_ChgDifficulty: + { + strcopy(translation, maxlength, FC_VOTE_MENU_CHANGE_DIFFICULTY); + } + case NativeVotesType_ChgGamemode: + { + strcopy(translation, maxlength, FC_VOTE_MENU_CHANGE_GAMEMODE); + } default: { strcopy(translation, maxlength, TF2_VOTE_CUSTOM); @@ -2787,6 +2816,14 @@ static void TF2_VotePassToTranslation(NativeVotesPassType passType, char[] trans { strcopy(translation, maxlength, TF2CLASSIFIED_VOTE_CHANGE_CIVILIAN_PASSED); } + case NativeVotesPass_ChgDifficulty: + { + strcopy(translation, maxlength, FC_VOTE_PASSED_CHANGE_DIFFICULTY); + } + case NativeVotesPass_ChgGamemode: + { + strcopy(translation, maxlength, FC_VOTE_PASSED_CHANGE_GAMEMODE); + } default: { strcopy(translation, maxlength, TF2_VOTE_CUSTOM); @@ -2858,6 +2895,14 @@ static void TF2_AddDefaultVotes(ArrayList hVoteTypes, bool bHideDisabledVotes) int entity = FindEntityByClassname(INVALID_ENT_REFERENCE, "tf2c_logic_vip"); VoteTypeSet(hVoteTypes, bHideDisabledVotes, NativeVotesOverride_ChgCivilian, globalEnable && g_Cvar_ChangeCivilian_Enabled.BoolValue && entity != INVALID_ENT_REFERENCE); } + case 3561600: // Fortress Connected + { + /* ChangeDifficulty */ + VoteTypeSet(hVoteTypes, bHideDisabledVotes, NativeVotesOverride_ChgDifficulty, globalEnable && g_Cvar_ChangeDifficulty_Enabled.BoolValue); + + /* ChangeGamemode */ + VoteTypeSet(hVoteTypes, bHideDisabledVotes, NativeVotesOverride_ChgGamemode, globalEnable && g_Cvar_ChangeGamemode_Enabled.BoolValue); + } } } @@ -3167,8 +3212,23 @@ static stock bool TF2_VoteTypeToVoteString(NativeVotesType voteType, char[] vote { strcopy(voteString, maxlength, TF2_VOTE_STRING_EXTEND); } + case NativeVotesType_ChgCivilian: + { + strcopy(voteString, maxlength, TF2CLASSIFIED_VOTE_STRING_CHANGE_CIVILIAN); + valid = true; + } + case NativeVotesType_ChgDifficulty: + { + strcopy(voteString, maxlength, FC_VOTE_STRING_CHANGE_DIFFICULTY); + valid = true; + } + case NativeVotesType_ChgGamemode: + { + strcopy(voteString, maxlength, FC_VOTE_STRING_CHANGE_GAMEMODE); + valid = true; + } } - + return valid; } @@ -3220,6 +3280,14 @@ static stock NativeVotesType TF2_VoteStringToVoteType(const char[] voteString) { voteType = NativeVotesType_ChgCivilian; } + else if (StrEqual(voteString, FC_VOTE_STRING_CHANGE_DIFFICULTY, false)) + { + voteType = NativeVotesType_ChgDifficulty; + } + else if (StrEqual(voteString, FC_VOTE_STRING_CHANGE_GAMEMODE, false)) + { + voteType = NativeVotesType_ChgGamemode; + } else { voteType = NativeVotesType_None; @@ -3234,7 +3302,7 @@ static stock NativeVotesOverride TF2_VoteTypeToVoteOverride(NativeVotesType vote switch (voteType) { - case NativeVotesType_Kick, NativeVotesType_KickCheating, NativeVotesType_KickIdle, NativeVotesType_KickScamming: + case NativeVotesType_Kick, NativeVotesType_KickCheating, NativeVotesType_KickIdle, NativeVotesType_KickScamming: { overrideType = NativeVotesOverride_Kick; } @@ -3258,13 +3326,11 @@ static stock NativeVotesOverride TF2_VoteTypeToVoteOverride(NativeVotesType vote { overrideType = NativeVotesOverride_Eternaween; } - case NativeVotesType_AutoBalanceOn, - NativeVotesType_AutoBalanceOff: + case NativeVotesType_AutoBalanceOn, NativeVotesType_AutoBalanceOff: { overrideType = NativeVotesOverride_AutoBalance; } - case NativeVotesType_ClassLimitsOn, - NativeVotesType_ClassLimitsOff: + case NativeVotesType_ClassLimitsOn, NativeVotesType_ClassLimitsOff: { overrideType = NativeVotesOverride_ClassLimits; } @@ -3276,6 +3342,14 @@ static stock NativeVotesOverride TF2_VoteTypeToVoteOverride(NativeVotesType vote { overrideType = NativeVotesOverride_ChgCivilian; } + case NativeVotesType_ChgDifficulty: + { + overrideType = NativeVotesOverride_ChgDifficulty; + } + case NativeVotesType_ChgGamemode: + { + overrideType = NativeVotesOverride_ChgGamemode; + } default: { overrideType = NativeVotesOverride_None; @@ -3335,6 +3409,14 @@ static stock NativeVotesType TF2_VoteOverrideToVoteType(NativeVotesOverride over { voteType = NativeVotesType_ChgCivilian; } + case NativeVotesOverride_ChgDifficulty: + { + voteType = NativeVotesType_ChgDifficulty; + } + case NativeVotesOverride_ChgGamemode: + { + voteType = NativeVotesType_ChgGamemode; + } default: { voteType = NativeVotesType_None; @@ -3392,6 +3474,14 @@ static stock NativeVotesOverride TF2_VoteStringToVoteOverride(const char[] voteS { overrideType = NativeVotesOverride_ChgCivilian; } + else if (StrEqual(voteString, FC_VOTE_STRING_CHANGE_DIFFICULTY, false)) + { + overrideType = NativeVotesOverride_ChgDifficulty; + } + else if (StrEqual(voteString, FC_VOTE_STRING_CHANGE_GAMEMODE, false)) + { + overrideType = NativeVotesOverride_ChgGamemode; + } else { overrideType = NativeVotesOverride_None; @@ -3449,22 +3539,32 @@ static stock bool TF2_OverrideTypeToVoteString(NativeVotesOverride overrideType, strcopy(voteString, maxlength, TF2_VOTE_STRING_CLASSLIMIT); valid = true; } - case NativeVotesOverride_Extend: - { - strcopy(voteString, maxlength, TF2_VOTE_STRING_EXTEND); - } - case NativeVotesOverride_ChgMission: - { - strcopy(voteString, maxlength, TF2_VOTE_STRING_CHANGEMISSION); - } - case NativeVotesOverride_ChgCivilian: - { - strcopy(voteString, maxlength, TF2CLASSIFIED_VOTE_STRING_CHANGE_CIVILIAN); - valid = true; - } - } - - return valid; + case NativeVotesOverride_Extend: + { + strcopy(voteString, maxlength, TF2_VOTE_STRING_EXTEND); + } + case NativeVotesOverride_ChgMission: + { + strcopy(voteString, maxlength, TF2_VOTE_STRING_CHANGEMISSION); + } + case NativeVotesOverride_ChgCivilian: + { + strcopy(voteString, maxlength, TF2CLASSIFIED_VOTE_STRING_CHANGE_CIVILIAN); + valid = true; + } + case NativeVotesOverride_ChgDifficulty: + { + strcopy(voteString, maxlength, FC_VOTE_STRING_CHANGE_DIFFICULTY); + valid = true; + } + case NativeVotesOverride_ChgGamemode: + { + strcopy(voteString, maxlength, FC_VOTE_STRING_CHANGE_GAMEMODE); + valid = true; + } + } + + return valid; } static stock bool TF2_OverrideTypeToTranslationString(NativeVotesOverride overrideType, char[] translationString, int maxlength) @@ -3521,6 +3621,16 @@ static stock bool TF2_OverrideTypeToTranslationString(NativeVotesOverride overri strcopy(translationString, maxlength, TF2CLASSIFIED_VOTE_MENU_CHANGE_CIVILIAN); valid = true; } + case NativeVotesOverride_ChgDifficulty: + { + strcopy(translationString, maxlength, FC_VOTE_MENU_CHANGE_DIFFICULTY); + valid = true; + } + case NativeVotesOverride_ChgGamemode: + { + strcopy(translationString, maxlength, FC_VOTE_MENU_CHANGE_GAMEMODE); + valid = true; + } } return valid; @@ -3548,6 +3658,6 @@ stock bool Game_IsVoteTypeYesNo(NativeVotesType voteType) return false; } } - + return true; } \ No newline at end of file From 6d4b7dec80fb9cc556fe11e62f4942d55b2473e2 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Fri, 20 Feb 2026 00:52:49 +0100 Subject: [PATCH 100/128] Un-hardcode vote timer by using sv_vote_timer_duration whenever applicable --- .../nativevotes_basevotes/voteban.sp | 34 +++--- .../nativevotes_basevotes/votekick.sp | 30 ++--- .../nativevotes_basevotes/votemap.sp | 30 ++--- .../scripting/nativevotes_funvotes.sp | 111 ++++++++++-------- .../nativevotes_funvotes/votealltalk.sp | 16 +-- .../nativevotes_funvotes/voteburn.sp | 28 ++--- .../scripting/nativevotes_funvotes/voteff.sp | 14 +-- .../nativevotes_funvotes/votegravity.sp | 24 ++-- .../nativevotes_funvotes/voteslay.sp | 28 ++--- .../sourcemod/scripting/nativevotes_voterp.sp | 14 +-- .../scripting/nativevotes_votescramble.sp | 20 ++-- 11 files changed, 177 insertions(+), 172 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_basevotes/voteban.sp b/addons/sourcemod/scripting/nativevotes_basevotes/voteban.sp index f2c76d3..9f40954 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes/voteban.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes/voteban.sp @@ -34,26 +34,26 @@ void DisplayVoteBanMenu(int client, int target) { - g_voteClient[VOTE_CLIENTID] = target; - g_voteClient[VOTE_USERID] = GetClientUserId(target); + g_VoteClient[VOTE_CLIENTID] = target; + g_VoteClient[VOTE_USERID] = GetClientUserId(target); char playerName[MAX_NAME_LENGTH]; GetPlayerName(target, playerName, sizeof(playerName)); - GetClientName(target, g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[])); - GetClientIP(target, g_voteInfo[VOTE_IP], sizeof(g_voteInfo[])); + GetClientName(target, g_VoteInfo[VOTE_NAME], sizeof(g_VoteInfo[])); + GetClientIP(target, g_VoteInfo[VOTE_IP], sizeof(g_VoteInfo[])); LogAction(client, target, "\"%L\" initiated a ban vote against %s", client, playerName); - CShowActivity2(client, "[\x04NativeVotes\x01] ", "%t", "Initiated Vote Ban", playerName); + CShowActivity2(client, PLUGIN_PREFIX ... " ", "%t", "Initiated Vote Ban", playerName); - g_voteType = ban; + g_VoteType = ban; if (g_NativeVotes) { Handle voteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Custom_YesNo, view_as<MenuAction>(MENU_ACTIONS_ALL)); NativeVotes_SetTitle(voteMenu, "Voteban Player"); NativeVotes_SetTarget(voteMenu, target); - NativeVotes_DisplayToAll(voteMenu, 20); + NativeVotes_DisplayToAll(voteMenu, g_ConVars[sv_vote_timer_duration].IntValue); } else { @@ -62,7 +62,7 @@ void DisplayVoteBanMenu(int client, int target) AddMenuItem(voteMenu, VOTE_YES, "Yes"); AddMenuItem(voteMenu, VOTE_NO, "No"); SetMenuExitButton(voteMenu, false); - VoteMenuToAll(voteMenu, 20); + VoteMenuToAll(voteMenu, g_ConVars[sv_vote_timer_duration].IntValue); } } @@ -104,9 +104,9 @@ public int MenuHandler_Ban(Handle menu, MenuAction action, int param1, int param } case MenuAction_Cancel: { - if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE) + if (param2 == MenuCancel_ExitBack && g_TopMenu != INVALID_HANDLE) { - DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory); + DisplayTopMenu(g_TopMenu, param1, TopMenuPosition_LastCategory); } } case MenuAction_Select: @@ -117,15 +117,15 @@ public int MenuHandler_Ban(Handle menu, MenuAction action, int param1, int param userid = StringToInt(info); if ((target = GetClientOfUserId(userid)) == 0) { - CPrintToChat(param1, "[\x04NativeVotes\x01] %t", "Player no longer available"); + CPrintToChat(param1, PLUGIN_PREFIX ... " %t", "Player no longer available"); } else if (!CanUserTarget(param1, target)) { - CPrintToChat(param1, "[\x04NativeVotes\x01] %t", "Unable to target"); + CPrintToChat(param1, PLUGIN_PREFIX ... " %t", "Unable to target"); } else { - g_voteArg[0] = '\0'; + g_VoteArg[0] = '\0'; DisplayVoteBanMenu(param1, target); } } @@ -138,13 +138,13 @@ public Action Command_Voteban(int client, int args) { if (args < 1) { - CReplyToCommand(client, "[\x04NativeVotes\x01] Usage: sm_voteban <player> [reason]"); + CReplyToCommand(client, PLUGIN_PREFIX ... " Usage: sm_voteban <player> [reason]"); return Plugin_Handled; } if (Internal_IsVoteInProgress()) { - CReplyToCommand(client, "[\x04NativeVotes\x01] %t", "Vote in Progress"); + CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Vote in Progress"); return Plugin_Handled; } @@ -158,11 +158,11 @@ public Action Command_Voteban(int client, int args) int len = BreakString(text, arg, sizeof(arg)); if (len != -1) { - strcopy(g_voteArg, sizeof(g_voteArg), text[len]); + strcopy(g_VoteArg, sizeof(g_VoteArg), text[len]); } else { - g_voteArg[0] = '\0'; + g_VoteArg[0] = '\0'; } char target_name[MAX_TARGET_LENGTH]; diff --git a/addons/sourcemod/scripting/nativevotes_basevotes/votekick.sp b/addons/sourcemod/scripting/nativevotes_basevotes/votekick.sp index 31bb222..e14488e 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes/votekick.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes/votekick.sp @@ -34,25 +34,25 @@ void DisplayVoteKickMenu(int client, int target) { - g_voteClient[VOTE_CLIENTID] = target; - g_voteClient[VOTE_USERID] = GetClientUserId(target); + g_VoteClient[VOTE_CLIENTID] = target; + g_VoteClient[VOTE_USERID] = GetClientUserId(target); char playerName[MAX_NAME_LENGTH]; GetPlayerName(target, playerName, sizeof(playerName)); - GetClientName(target, g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[])); + GetClientName(target, g_VoteInfo[VOTE_NAME], sizeof(g_VoteInfo[])); LogAction(client, target, "\"%L\" initiated a kick vote against %N", client, target); CShowActivity(client, "%t", "Initiated Vote Kick", playerName); - g_voteType = kick; + g_VoteType = kick; if (g_NativeVotes) { Handle voteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Kick, view_as<MenuAction>(MENU_ACTIONS_ALL)); // No title, builtin type NativeVotes_SetTarget(voteMenu, target); - NativeVotes_DisplayToAll(voteMenu, 20); + NativeVotes_DisplayToAll(voteMenu, g_ConVars[sv_vote_timer_duration].IntValue); } else { @@ -61,7 +61,7 @@ void DisplayVoteKickMenu(int client, int target) AddMenuItem(voteMenu, VOTE_YES, "Yes"); AddMenuItem(voteMenu, VOTE_NO, "No"); SetMenuExitButton(voteMenu, false); - VoteMenuToAll(voteMenu, 20); + VoteMenuToAll(voteMenu, g_ConVars[sv_vote_timer_duration].IntValue); } } @@ -103,9 +103,9 @@ public int MenuHandler_Kick(Handle menu, MenuAction action, int param1, int para } case MenuAction_Cancel: { - if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE) + if (param2 == MenuCancel_ExitBack && g_TopMenu != INVALID_HANDLE) { - DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory); + DisplayTopMenu(g_TopMenu, param1, TopMenuPosition_LastCategory); } } case MenuAction_Select: @@ -116,15 +116,15 @@ public int MenuHandler_Kick(Handle menu, MenuAction action, int param1, int para userid = StringToInt(info); if ((target = GetClientOfUserId(userid)) == 0) { - CPrintToChat(param1, "[\x04NativeVotes\x01] %t", "Player no longer available"); + CPrintToChat(param1, PLUGIN_PREFIX ... " %t", "Player no longer available"); } else if (!CanUserTarget(param1, target)) { - CPrintToChat(param1, "[\x04NativeVotes\x01] %t", "Unable to target"); + CPrintToChat(param1, PLUGIN_PREFIX ... " %t", "Unable to target"); } else { - g_voteArg[0] = '\0'; + g_VoteArg[0] = '\0'; DisplayVoteKickMenu(param1, target); } } @@ -137,12 +137,12 @@ public Action Command_Votekick(int client, int args) { if (args < 1) { - CReplyToCommand(client, "[\x04NativeVotes\x01] Usage: sm_votekick <player> [reason]"); + CReplyToCommand(client, PLUGIN_PREFIX ... " Usage: sm_votekick <player> [reason]"); return Plugin_Handled; } if (Internal_IsVoteInProgress()) { - CReplyToCommand(client, "[\x04NativeVotes\x01] %t", "Vote in Progress"); + CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Vote in Progress"); return Plugin_Handled; } if (!TestVoteDelay(client)) @@ -159,11 +159,11 @@ public Action Command_Votekick(int client, int args) } if (len != -1) { - strcopy(g_voteArg, sizeof(g_voteArg), text[len]); + strcopy(g_VoteArg, sizeof(g_VoteArg), text[len]); } else { - g_voteArg[0] = '\0'; + g_VoteArg[0] = '\0'; } DisplayVoteKickMenu(client, target); return Plugin_Handled; diff --git a/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp b/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp index abdbf86..a83e8ab 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp @@ -41,9 +41,9 @@ bool g_VoteMapInUse; void DisplayVoteMapMenu(int client, int mapCount, char maps[5][PLATFORM_MAX_PATH]) { LogAction(client, -1, "\"%L\" initiated a map vote.", client); - CShowActivity2(client, "[\x04NativeVotes\x01] ", "%t", "Initiated Vote Map"); + CShowActivity2(client, PLUGIN_PREFIX ... " %t", "Initiated Vote Map"); - g_voteType = map; + g_VoteType = map; char resolvedMaps[5][PLATFORM_MAX_PATH]; char displayNames[5][PLATFORM_MAX_PATH]; @@ -58,7 +58,7 @@ void DisplayVoteMapMenu(int client, int mapCount, char maps[5][PLATFORM_MAX_PATH Handle voteMenu; if (mapCount == 1) { - strcopy(g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[]), displayNames[0]); + strcopy(g_VoteInfo[VOTE_NAME], sizeof(g_VoteInfo[]), displayNames[0]); voteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_ChgLevel, view_as<MenuAction>(MENU_ACTIONS_ALL)); @@ -69,7 +69,7 @@ void DisplayVoteMapMenu(int client, int mapCount, char maps[5][PLATFORM_MAX_PATH { voteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_NextLevelMult, view_as<MenuAction>(MENU_ACTIONS_ALL)); - g_voteInfo[VOTE_NAME][0] = '\0'; + g_VoteInfo[VOTE_NAME][0] = '\0'; // No title, builtin type for (int i = 0; i < mapCount; i++) @@ -77,7 +77,7 @@ void DisplayVoteMapMenu(int client, int mapCount, char maps[5][PLATFORM_MAX_PATH NativeVotes_AddItem(voteMenu, resolvedMaps[i], displayNames[i]); } } - NativeVotes_DisplayToAll(voteMenu, 20); + NativeVotes_DisplayToAll(voteMenu, g_ConVars[sv_vote_timer_duration].IntValue); } else { @@ -85,7 +85,7 @@ void DisplayVoteMapMenu(int client, int mapCount, char maps[5][PLATFORM_MAX_PATH if (mapCount == 1) { - strcopy(g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[]), displayNames[0]); + strcopy(g_VoteInfo[VOTE_NAME], sizeof(g_VoteInfo[]), displayNames[0]); SetMenuTitle(voteMenu, "Change Map To"); AddMenuItem(voteMenu, resolvedMaps[0], "Yes"); @@ -93,7 +93,7 @@ void DisplayVoteMapMenu(int client, int mapCount, char maps[5][PLATFORM_MAX_PATH } else { - g_voteInfo[VOTE_NAME][0] = '\0'; + g_VoteInfo[VOTE_NAME][0] = '\0'; SetMenuTitle(voteMenu, "Map Vote"); for (int i = 0; i < mapCount; i++) @@ -102,7 +102,7 @@ void DisplayVoteMapMenu(int client, int mapCount, char maps[5][PLATFORM_MAX_PATH } } SetMenuExitButton(voteMenu, false); - VoteMenuToAll(voteMenu, 20); + VoteMenuToAll(voteMenu, g_ConVars[sv_vote_timer_duration].IntValue); } } @@ -140,9 +140,9 @@ public int MenuHandler_Confirm(Handle menu, MenuAction action, int param1, int p case MenuAction_Cancel: { ResetMenu(); - if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE) + if (param2 == MenuCancel_ExitBack && g_TopMenu != INVALID_HANDLE) { - DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory); + DisplayTopMenu(g_TopMenu, param1, TopMenuPosition_LastCategory); } } case MenuAction_Select: @@ -167,7 +167,7 @@ public int MenuHandler_Map(Handle menu, MenuAction action, int param1, int param { case MenuAction_Cancel: { - if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE) + if (param2 == MenuCancel_ExitBack && g_TopMenu != INVALID_HANDLE) { ConfirmVote(param1); } @@ -234,7 +234,7 @@ public void AdminMenu_VoteMap(Handle topmenu, TopMenuAction action, TopMenuObjec } else { - CPrintToChat(param, "[\x04NativeVotes\x01] %T", "Map Vote In Use", param); + CPrintToChat(param, PLUGIN_PREFIX ... " %t", "Map Vote In Use", param); } } case TopMenuAction_DrawOption: @@ -249,13 +249,13 @@ public Action Command_Votemap(int client, int args) { if (args < 1) { - CReplyToCommand(client, "[\x04NativeVotes\x01] Usage: sm_votemap <mapname> [mapname2] ... [mapname5]"); + CReplyToCommand(client, PLUGIN_PREFIX ... " Usage: sm_votemap <mapname> [mapname2] ... [mapname5]"); return Plugin_Handled; } if (Internal_IsVoteInProgress()) { - CReplyToCommand(client, "[\x04NativeVotes\x01] %t", "Vote in Progress"); + CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Vote in Progress"); return Plugin_Handled; } @@ -277,7 +277,7 @@ public Action Command_Votemap(int client, int args) if (!IsMapValid(maps[mapCount])) { - CReplyToCommand(client, "[\x04NativeVotes\x01] %t", "Map was not found", maps[mapCount]); + CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Map was not found", maps[mapCount]); return Plugin_Handled; } diff --git a/addons/sourcemod/scripting/nativevotes_funvotes.sp b/addons/sourcemod/scripting/nativevotes_funvotes.sp index 01f9bdd..415c109 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes.sp @@ -42,12 +42,14 @@ #include <adminmenu> #include <nativevotes> +#define PLUGIN_PREFIX "[\x04NativeVotes\x01]" + public Plugin myinfo = { name = "NativeVotes | Fun Votes", author = "Powerlord and AlliedModders LLC", description = "NativeVotes Fun Vote Commands", - version = "26w07a", + version = "26w08a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -61,6 +63,7 @@ enum sv_gravity, sv_alltalk, mp_friendlyfire, + sv_vote_timer_duration, vote_gravity, vote_burn, @@ -73,7 +76,7 @@ enum ConVar g_ConVars[MAX_CONVARS]; -enum voteType +enum VoteType { gravity, burn, @@ -82,21 +85,21 @@ enum voteType ff }; -voteType g_voteType = gravity; +VoteType g_VoteType = gravity; // Menu API does not provide us with a way to pass multiple peices of data with a single // choice, so some globals are used to hold stuff. // #define VOTE_CLIENTID 0 #define VOTE_USERID 1 -int g_voteClient[2]; // Holds the target's client id and user id +int g_VoteClient[2]; // Holds the target's client id and user id #define VOTE_NAME 0 #define VOTE_AUTHID 1 #define VOTE_IP 2 -char g_voteInfo[3][65]; // Holds the target's name, authid, and IP +char g_VoteInfo[3][65]; // Holds the target's name, authid, and IP -Handle hTopMenu; +Handle g_TopMenu; // NativeVotes bool g_NativeVotes; @@ -120,15 +123,16 @@ public void OnPluginStart() RegAdminCmd("sm_votealltalk", Command_VoteAlltalk, ADMFLAG_VOTE, "sm_votealltalk"); RegAdminCmd("sm_voteff", Command_VoteFF, ADMFLAG_VOTE, "sm_voteff"); - g_ConVars[sv_gravity] = FindConVar("sv_gravity"); - g_ConVars[sv_alltalk] = FindConVar("sv_alltalk"); - g_ConVars[mp_friendlyfire] = FindConVar("mp_friendlyfire"); - g_ConVars[vote_gravity] = CreateConVar("sm_vote_gravity", "0.60", "Percent required for successful gravity vote.", 0, true, 0.05, true, 1.0); - g_ConVars[vote_burn] = CreateConVar("sm_vote_burn", "0.60", "Percent required for successful burn vote.", 0, true, 0.05, true, 1.0); - g_ConVars[vote_slay] = CreateConVar("sm_vote_slay", "0.60", "Percent required for successful slay vote.", 0, true, 0.05, true, 1.0); - g_ConVars[vote_alltalk] = CreateConVar("sm_vote_alltalk", "0.60", "Percent required for successful alltalk vote.", 0, true, 0.05, true, 1.0); - g_ConVars[vote_ff] = CreateConVar("sm_vote_ff", "0.60", "Percent required for successful friendly fire vote.", 0, true, 0.05, true, 1.0); - } + g_ConVars[sv_gravity] = FindConVar("sv_gravity"); + g_ConVars[sv_alltalk] = FindConVar("sv_alltalk"); + g_ConVars[mp_friendlyfire] = FindConVar("mp_friendlyfire"); + g_ConVars[sv_vote_timer_duration] = FindConVar("sv_vote_timer_duration"); + g_ConVars[vote_gravity] = CreateConVar("sm_vote_gravity", "0.60", "Percent required for successful gravity vote.", 0, true, 0.05, true, 1.0); + g_ConVars[vote_burn] = CreateConVar("sm_vote_burn", "0.60", "Percent required for successful burn vote.", 0, true, 0.05, true, 1.0); + g_ConVars[vote_slay] = CreateConVar("sm_vote_slay", "0.60", "Percent required for successful slay vote.", 0, true, 0.05, true, 1.0); + g_ConVars[vote_alltalk] = CreateConVar("sm_vote_alltalk", "0.60", "Percent required for successful alltalk vote.", 0, true, 0.05, true, 1.0); + g_ConVars[vote_ff] = CreateConVar("sm_vote_ff", "0.60", "Percent required for successful friendly fire vote.", 0, true, 0.05, true, 1.0); +} public void OnAllPluginsLoaded() { @@ -179,7 +183,7 @@ public void OnLibraryRemoved(const char[] name) { if (StrEqual(name, "adminmenu")) { - hTopMenu = null; + g_TopMenu = null; } else if (StrEqual(name, "nativevotes")) { @@ -190,20 +194,20 @@ public void OnLibraryRemoved(const char[] name) public void OnAdminMenuReady(Handle topmenu) { /* Block us from being called twice */ - if (topmenu == hTopMenu) + if (topmenu == g_TopMenu) { return; } /* Save the Handle */ - hTopMenu = topmenu; + g_TopMenu = topmenu; /* Build the "Voting Commands" category */ - TopMenuObject voting_commands = FindTopMenuCategory(hTopMenu, ADMINMENU_VOTINGCOMMANDS); + TopMenuObject voting_commands = FindTopMenuCategory(g_TopMenu, ADMINMENU_VOTINGCOMMANDS); if (voting_commands != INVALID_TOPMENUOBJECT) { - AddToTopMenu(hTopMenu, + AddToTopMenu(g_TopMenu, "sm_votegravity", TopMenuObject_Item, AdminMenu_VoteGravity, @@ -211,7 +215,7 @@ public void OnAdminMenuReady(Handle topmenu) "sm_votegravity", ADMFLAG_VOTE); - AddToTopMenu(hTopMenu, + AddToTopMenu(g_TopMenu, "sm_voteburn", TopMenuObject_Item, AdminMenu_VoteBurn, @@ -219,7 +223,7 @@ public void OnAdminMenuReady(Handle topmenu) "sm_voteburn", ADMFLAG_VOTE|ADMFLAG_SLAY); - AddToTopMenu(hTopMenu, + AddToTopMenu(g_TopMenu, "sm_voteslay", TopMenuObject_Item, AdminMenu_VoteSlay, @@ -227,7 +231,7 @@ public void OnAdminMenuReady(Handle topmenu) "sm_voteslay", ADMFLAG_VOTE|ADMFLAG_SLAY); - AddToTopMenu(hTopMenu, + AddToTopMenu(g_TopMenu, "sm_votealltalk", TopMenuObject_Item, AdminMenu_VoteAllTalk, @@ -235,7 +239,7 @@ public void OnAdminMenuReady(Handle topmenu) "sm_votealltalk", ADMFLAG_VOTE); - AddToTopMenu(hTopMenu, + AddToTopMenu(g_TopMenu, "sm_voteff", TopMenuObject_Item, AdminMenu_VoteFF, @@ -261,7 +265,7 @@ public int Handler_VoteCallback(Handle menu, MenuAction action, int param1, int GetMenuTitle(menu, title, sizeof(title)); char buffer[255]; - Format(buffer, sizeof(buffer), "%T", title, param1, g_voteInfo[VOTE_NAME]); + Format(buffer, sizeof(buffer), "%T", title, param1, g_VoteInfo[VOTE_NAME]); Handle panel = view_as<Handle>(param2); SetPanelTitle(panel, buffer); @@ -305,14 +309,14 @@ public int Handler_VoteCallback(Handle menu, MenuAction action, int param1, int percent = GetVotePercent(votes, totalVotes); - limit = GetConVarFloat(g_ConVars[g_voteType]); + limit = GetConVarFloat(g_ConVars[g_VoteType]); - // :TODO: g_voteClient[userid] needs to be checked + // :TODO: g_VoteClient[userid] needs to be checked // A multi-argument vote is "always successful", but have to check if its a Yes/No vote. if ((strcmp(item, VOTE_YES) == 0 && FloatCompare(percent,limit) < 0 && param1 == 0) || (strcmp(item, VOTE_NO) == 0 && param1 == 1)) { - /* :TODO: g_voteClient[userid] should be used here and set to -1 if not applicable. + /* :TODO: g_VoteClient[userid] should be used here and set to -1 if not applicable. */ LogAction(-1, -1, "Vote failed."); CPrintToChatAll("%t", "Vote Failed", RoundToNearest(100.0*limit), RoundToNearest(100.0*percent), totalVotes); @@ -322,9 +326,9 @@ public int Handler_VoteCallback(Handle menu, MenuAction action, int param1, int CPrintToChatAll("%t", "Vote Successful", RoundToNearest(100.0*percent), totalVotes); char name[MAX_NAME_LENGTH]; - GetPlayerName(g_voteClient[VOTE_CLIENTID], name, sizeof(name)); + GetPlayerName(g_VoteClient[VOTE_CLIENTID], name, sizeof(name)); - switch (g_voteType) + switch (g_VoteType) { case gravity: { @@ -334,18 +338,18 @@ public int Handler_VoteCallback(Handle menu, MenuAction action, int param1, int } case burn: { - CPrintToChatAllEx(g_voteClient[VOTE_CLIENTID], "%t", "Set target on fire", "_s", name); - LogAction(-1, g_voteClient[VOTE_CLIENTID], "Vote burn successful, igniting \"%L\"", g_voteClient[VOTE_CLIENTID]); + CPrintToChatAllEx(g_VoteClient[VOTE_CLIENTID], "%t", "Set target on fire", "_s", name); + LogAction(-1, g_VoteClient[VOTE_CLIENTID], "Vote burn successful, igniting \"%L\"", g_VoteClient[VOTE_CLIENTID]); - IgniteEntity(g_voteClient[VOTE_CLIENTID], 19.8); + IgniteEntity(g_VoteClient[VOTE_CLIENTID], 19.8); } case slay: { - CPrintToChatAllEx(g_voteClient[VOTE_CLIENTID], "%t", "Slayed player", "_s", name); - LogAction(-1, g_voteClient[VOTE_CLIENTID], "Vote slay successful, slaying \"%L\"", g_voteClient[VOTE_CLIENTID]); + CPrintToChatAllEx(g_VoteClient[VOTE_CLIENTID], "%t", "Slayed player", "_s", name); + LogAction(-1, g_VoteClient[VOTE_CLIENTID], "Vote slay successful, slaying \"%L\"", g_VoteClient[VOTE_CLIENTID]); - ExtinguishEntity(g_voteClient[VOTE_CLIENTID]); - ForcePlayerSuicide(g_voteClient[VOTE_CLIENTID]); + ExtinguishEntity(g_VoteClient[VOTE_CLIENTID]); + ForcePlayerSuicide(g_VoteClient[VOTE_CLIENTID]); } case alltalk: { @@ -385,7 +389,7 @@ public int Handler_NativeVoteCallback(Handle menu, MenuAction action, int param1 NativeVotes_GetTitle(menu, title, sizeof(title)); char buffer[255]; - Format(buffer, sizeof(buffer), "%T", title, param1, g_voteInfo[VOTE_NAME]); + Format(buffer, sizeof(buffer), "%T", title, param1, g_VoteInfo[VOTE_NAME]); return NativeVotes_RedrawVoteTitle(buffer); } @@ -422,14 +426,14 @@ public int Handler_NativeVoteCallback(Handle menu, MenuAction action, int param1 percent = GetVotePercent(votes, totalVotes); - limit = GetConVarFloat(g_ConVars[g_voteType]); + limit = GetConVarFloat(g_ConVars[g_VoteType]); - // :TODO: g_voteClient[userid] needs to be checked + // :TODO: g_VoteClient[userid] needs to be checked // A multi-argument vote is "always successful", but have to check if its a Yes/No vote. if ((nVoteType != NativeVotesType_NextLevelMult && nVoteType != NativeVotesType_Custom_Mult) && ((param1 == NATIVEVOTES_VOTE_YES && FloatCompare(percent,limit) < 0) || (param1 == NATIVEVOTES_VOTE_NO))) { - /* :TODO: g_voteClient[userid] should be used here and set to -1 if not applicable. + /* :TODO: g_VoteClient[userid] should be used here and set to -1 if not applicable. */ NativeVotes_DisplayFail(menu, NativeVotesFail_Loses); LogAction(-1, -1, "Vote failed."); @@ -439,7 +443,7 @@ public int Handler_NativeVoteCallback(Handle menu, MenuAction action, int param1 { CPrintToChatAll("%t", "Vote Successful", RoundToNearest(100.0*percent), totalVotes); - switch (g_voteType) + switch (g_VoteType) { case gravity: { @@ -451,21 +455,21 @@ public int Handler_NativeVoteCallback(Handle menu, MenuAction action, int param1 case burn: { - CPrintToChatAll("%t", "Set target on fire", "_s", g_voteInfo[VOTE_NAME]); - NativeVotes_DisplayPassCustom(menu, "%t", "Set target on fire", "_s", g_voteInfo[VOTE_NAME]); - LogAction(-1, g_voteClient[VOTE_CLIENTID], "Vote burn successful, igniting \"%L\"", g_voteClient[VOTE_CLIENTID]); + CPrintToChatAll("%t", "Set target on fire", "_s", g_VoteInfo[VOTE_NAME]); + NativeVotes_DisplayPassCustom(menu, "%t", "Set target on fire", "_s", g_VoteInfo[VOTE_NAME]); + LogAction(-1, g_VoteClient[VOTE_CLIENTID], "Vote burn successful, igniting \"%L\"", g_VoteClient[VOTE_CLIENTID]); - IgniteEntity(g_voteClient[VOTE_CLIENTID], 19.8); + IgniteEntity(g_VoteClient[VOTE_CLIENTID], 19.8); } case slay: { - CPrintToChatAll("%t", "Slayed player", g_voteInfo[VOTE_NAME]); - NativeVotes_DisplayPassCustom(menu, "%t", "Slayed player", g_voteInfo[VOTE_NAME]); - LogAction(-1, g_voteClient[VOTE_CLIENTID], "Vote slay successful, slaying \"%L\"", g_voteClient[VOTE_CLIENTID]); + CPrintToChatAll("%t", "Slayed player", g_VoteInfo[VOTE_NAME]); + NativeVotes_DisplayPassCustom(menu, "%t", "Slayed player", g_VoteInfo[VOTE_NAME]); + LogAction(-1, g_VoteClient[VOTE_CLIENTID], "Vote slay successful, slaying \"%L\"", g_VoteClient[VOTE_CLIENTID]); - ExtinguishEntity(g_voteClient[VOTE_CLIENTID]); - ForcePlayerSuicide(g_voteClient[VOTE_CLIENTID]); + ExtinguishEntity(g_VoteClient[VOTE_CLIENTID]); + ForcePlayerSuicide(g_VoteClient[VOTE_CLIENTID]); } case alltalk: @@ -499,6 +503,11 @@ float GetVotePercent(int votes, int totalVotes) bool TestVoteDelay(int client) { int delay = Internal_CheckVoteDelay(); + if (client <= 0 || GetUserFlagBits(client) & ADMFLAG_GENERIC) + { + delay = 0; + } + if (delay > 0) { if (delay > 60) diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp b/addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp index 28fba20..0045491 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp @@ -36,7 +36,7 @@ void DisplayVoteAllTalkMenu(int client) { if (Internal_IsVoteInProgress()) { - CReplyToCommand(client, "[\x04NativeVotes\x01] %t", "Vote in Progress"); + CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Vote in Progress"); return; } @@ -46,17 +46,17 @@ void DisplayVoteAllTalkMenu(int client) } LogAction(client, -1, "\"%L\" initiated an alltalk vote.", client); - CShowActivity2(client, "[\x04NativeVotes\x01] ", "%t", "Initiated Vote Alltalk"); + CShowActivity2(client, PLUGIN_PREFIX ... " %t", "Initiated Vote Alltalk"); - g_voteType = voteType:alltalk; - g_voteInfo[VOTE_NAME][0] = '\0'; + g_VoteType = alltalk; + g_VoteInfo[VOTE_NAME][0] = '\0'; if (g_NativeVotes) { Handle hVoteMenu; if (NativeVotes_IsVoteTypeSupported(NativeVotesType_AlltalkOn)) { - new NativeVotesType:nVoteType; + NativeVotesType nVoteType; if (GetConVarBool(g_ConVars[sv_alltalk])) { nVoteType = NativeVotesType_AlltalkOff; @@ -81,7 +81,7 @@ void DisplayVoteAllTalkMenu(int client) } } - NativeVotes_DisplayToAll(hVoteMenu, 20); + NativeVotes_DisplayToAll(hVoteMenu, g_ConVars[sv_vote_timer_duration].IntValue); } else { @@ -99,7 +99,7 @@ void DisplayVoteAllTalkMenu(int client) AddMenuItem(hVoteMenu, VOTE_YES, "Yes"); AddMenuItem(hVoteMenu, VOTE_NO, "No"); SetMenuExitButton(hVoteMenu, false); - VoteMenuToAll(hVoteMenu, 20); + VoteMenuToAll(hVoteMenu, g_ConVars[sv_vote_timer_duration].IntValue); } } @@ -128,7 +128,7 @@ public Action Command_VoteAlltalk(int client, int args) { if (args > 0) { - CReplyToCommand(client, "[\x04NativeVotes\x01] Usage: sm_votealltalk"); + CReplyToCommand(client, PLUGIN_PREFIX ... " Usage: sm_votealltalk"); return Plugin_Handled; } diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp b/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp index 39f803f..c302bbc 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp @@ -36,28 +36,28 @@ void DisplayVoteBurnMenu(int client, int target, char name[MAX_NAME_LENGTH]) { if (!IsPlayerAlive(target)) { - CReplyToCommand(client, "[\x04NativeVotes\x01] %t", "Cannot be performed on dead", name); + CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Cannot be performed on dead", name); return; } - g_voteClient[VOTE_CLIENTID] = target; + g_VoteClient[VOTE_CLIENTID] = target; char playerName[MAX_NAME_LENGTH]; GetPlayerName(target, playerName, sizeof(playerName)); - GetClientName(target, g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[])); + GetClientName(target, g_VoteInfo[VOTE_NAME], sizeof(g_VoteInfo[])); LogAction(client, target, "\"%L\" initiated a burn vote against %N", client, target); - CShowActivity2(client, "[\x04NativeVotes\x01] ", "%t", "Initiated Vote Burn", playerName); + CShowActivity2(client, PLUGIN_PREFIX ... " %t", "Initiated Vote Burn", playerName); - g_voteType = burn; + g_VoteType = burn; if (g_NativeVotes) { Handle hVoteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Custom_YesNo, view_as<MenuAction>(MENU_ACTIONS_ALL)); NativeVotes_SetTitle(hVoteMenu, "Voteburn player"); NativeVotes_SetTarget(hVoteMenu, target); - NativeVotes_DisplayToAll(hVoteMenu, 20); + NativeVotes_DisplayToAll(hVoteMenu, g_ConVars[sv_vote_timer_duration].IntValue); } else { @@ -66,7 +66,7 @@ void DisplayVoteBurnMenu(int client, int target, char name[MAX_NAME_LENGTH]) AddMenuItem(hVoteMenu, VOTE_YES, "Yes"); AddMenuItem(hVoteMenu, VOTE_NO, "No"); SetMenuExitButton(hVoteMenu, false); - VoteMenuToAll(hVoteMenu, 20); + VoteMenuToAll(hVoteMenu, g_ConVars[sv_vote_timer_duration].IntValue); } } @@ -112,9 +112,9 @@ public int MenuHandler_Burn(Handle menu, MenuAction action, int param1, int para } case MenuAction_Cancel: { - if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE) + if (param2 == MenuCancel_ExitBack && g_TopMenu != INVALID_HANDLE) { - DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory); + DisplayTopMenu(g_TopMenu, param1, TopMenuPosition_LastCategory); } } case MenuAction_Select: @@ -125,15 +125,15 @@ public int MenuHandler_Burn(Handle menu, MenuAction action, int param1, int para userid = StringToInt(info); if ((target = GetClientOfUserId(userid)) == 0) { - CPrintToChat(param1, "[\x04NativeVotes\x01] %t", "Player no longer available"); + CPrintToChat(param1, PLUGIN_PREFIX ... " %t", "Player no longer available"); } else if (!CanUserTarget(param1, target)) { - CPrintToChat(param1, "[\x04NativeVotes\x01] %t", "Unable to target"); + CPrintToChat(param1, PLUGIN_PREFIX ... " %t", "Unable to target"); } else if (!IsPlayerAlive(target)) { - CPrintToChat(param1, "[\x04NativeVotes\x01] %t", "Player has since died"); + CPrintToChat(param1, PLUGIN_PREFIX ... " %t", "Player has since died"); } else { @@ -149,13 +149,13 @@ public Action Command_VoteBurn(int client, int args) { if (args < 1) { - CReplyToCommand(client, "[\x04NativeVotes\x01] Usage: sm_voteburn <player>"); + CReplyToCommand(client, PLUGIN_PREFIX ... " Usage: sm_voteburn <player>"); return Plugin_Handled; } if (Internal_IsVoteInProgress()) { - CReplyToCommand(client, "[\x04NativeVotes\x01] %t", "Vote in Progress"); + CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Vote in Progress"); return Plugin_Handled; } diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp b/addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp index f7911a8..fb3a2cc 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp @@ -36,7 +36,7 @@ void DisplayVoteFFMenu(int client) { if (Internal_IsVoteInProgress()) { - CReplyToCommand(client, "[\x04NativeVotes\x01] %t", "Vote in Progress"); + CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Vote in Progress"); return; } @@ -46,10 +46,10 @@ void DisplayVoteFFMenu(int client) } LogAction(client, -1, "\"%L\" initiated a friendly fire vote.", client); - CShowActivity2(client, "[\x04NativeVotes\x01] ", "%t", "Initiated Vote FF"); + CShowActivity2(client, PLUGIN_PREFIX ... " %t", "Initiated Vote FF"); - g_voteType = voteType:ff; - g_voteInfo[VOTE_NAME][0] = '\0'; + g_VoteType = ff; + g_VoteInfo[VOTE_NAME][0] = '\0'; if (g_NativeVotes) { @@ -63,7 +63,7 @@ void DisplayVoteFFMenu(int client) { NativeVotes_SetTitle(hVoteMenu, "Voteff On"); } - NativeVotes_DisplayToAll(hVoteMenu, 20); + NativeVotes_DisplayToAll(hVoteMenu, g_ConVars[sv_vote_timer_duration].IntValue); } else { @@ -81,7 +81,7 @@ void DisplayVoteFFMenu(int client) AddMenuItem(hVoteMenu, VOTE_YES, "Yes"); AddMenuItem(hVoteMenu, VOTE_NO, "No"); SetMenuExitButton(hVoteMenu, false); - VoteMenuToAll(hVoteMenu, 20); + VoteMenuToAll(hVoteMenu, g_ConVars[sv_vote_timer_duration].IntValue); } } @@ -109,7 +109,7 @@ public Action Command_VoteFF(int client, int args) { if (args > 0) { - CReplyToCommand(client, "[\x04NativeVotes\x01] Usage: sm_voteff"); + CReplyToCommand(client, PLUGIN_PREFIX ... " Usage: sm_voteff"); return Plugin_Handled; } diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp b/addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp index 1b6591a..0dcf18a 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp @@ -35,16 +35,16 @@ void DisplayVoteGravityMenu(int client, int count, char items[5][64]) { LogAction(client, -1, "\"%L\" initiated a gravity vote.", client); - CShowActivity2(client, "[\x04NativeVotes\x01] ", "%t", "Initiated Vote Gravity"); + CShowActivity2(client, PLUGIN_PREFIX ... " %t", "Initiated Vote Gravity"); - g_voteType = voteType:gravity; + g_VoteType = gravity; if (g_NativeVotes && (count == 1 || NativeVotes_IsVoteTypeSupported(NativeVotesType_Custom_Mult)) ) { Handle hVoteMenu; if (count == 1) { - strcopy(g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[]), items[0]); + strcopy(g_VoteInfo[VOTE_NAME], sizeof(g_VoteInfo[]), items[0]); hVoteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Custom_YesNo, view_as<MenuAction>(MENU_ACTIONS_ALL)); NativeVotes_SetTitle(hVoteMenu, "Change Gravity To"); @@ -54,12 +54,12 @@ void DisplayVoteGravityMenu(int client, int count, char items[5][64]) { hVoteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Custom_Mult, view_as<MenuAction>(MENU_ACTIONS_ALL)); NativeVotes_SetTitle(hVoteMenu, "Gravity Vote"); - for (new i = 0; i < count; i++) + for (int i = 0; i < count; i++) { NativeVotes_AddItem(hVoteMenu, items[i], items[i]); } } - NativeVotes_DisplayToAll(hVoteMenu, 20); + NativeVotes_DisplayToAll(hVoteMenu, g_ConVars[sv_vote_timer_duration].IntValue); } else { @@ -67,7 +67,7 @@ void DisplayVoteGravityMenu(int client, int count, char items[5][64]) if (count == 1) { - strcopy(g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[]), items[0]); + strcopy(g_VoteInfo[VOTE_NAME], sizeof(g_VoteInfo[]), items[0]); SetMenuTitle(hVoteMenu, "Change Gravity To"); AddMenuItem(hVoteMenu, items[0], "Yes"); @@ -75,17 +75,17 @@ void DisplayVoteGravityMenu(int client, int count, char items[5][64]) } else { - g_voteInfo[VOTE_NAME][0] = '\0'; + g_VoteInfo[VOTE_NAME][0] = '\0'; SetMenuTitle(hVoteMenu, "Gravity Vote"); - for (new i = 0; i < count; i++) + for (int i = 0; i < count; i++) { AddMenuItem(hVoteMenu, items[i], items[i]); } } SetMenuExitButton(hVoteMenu, false); - VoteMenuToAll(hVoteMenu, 20); + VoteMenuToAll(hVoteMenu, g_ConVars[sv_vote_timer_duration].IntValue); } } @@ -112,13 +112,13 @@ public Action Command_VoteGravity(int client, int args) { if (args < 1) { - CReplyToCommand(client, "[\x04NativeVotes\x01] Usage: sm_votegravity <amount> [amount2] ... [amount5]"); + CReplyToCommand(client, PLUGIN_PREFIX ... " Usage: sm_votegravity <amount> [amount2] ... [amount5]"); return Plugin_Handled; } if (Internal_IsVoteInProgress()) { - CReplyToCommand(client, "[\x04NativeVotes\x01] %t", "Vote in Progress"); + CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Vote in Progress"); return Plugin_Handled; } @@ -141,7 +141,7 @@ public Action Command_VoteGravity(int client, int args) float temp; if (StringToFloatEx(items[count], temp) == 0) { - CReplyToCommand(client, "[\x04NativeVotes\x01] %t", "Invalid Amount"); + CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Invalid Amount"); return Plugin_Handled; } diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp b/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp index 6233136..b3ac4c4 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp @@ -36,28 +36,28 @@ void DisplayVoteSlayMenu(int client, int target, char name[MAX_NAME_LENGTH]) { if (!IsPlayerAlive(target)) { - CReplyToCommand(client, "[\x04NativeVotes\x01] %t", "Cannot be performed on dead", name); + CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Cannot be performed on dead", name); return; } - g_voteClient[VOTE_CLIENTID] = target; + g_VoteClient[VOTE_CLIENTID] = target; char playerName[MAX_NAME_LENGTH]; GetPlayerName(target, playerName, sizeof(playerName)); - GetClientName(target, g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[])); + GetClientName(target, g_VoteInfo[VOTE_NAME], sizeof(g_VoteInfo[])); LogAction(client, target, "\"%L\" initiated a slay vote against %N", client, target); - CShowActivity2(client, "[\x04NativeVotes\x01] ", "%t", "Initiated Vote Slay", playerName); + CShowActivity2(client, PLUGIN_PREFIX ... " %t", "Initiated Vote Slay", playerName); - g_voteType = slay; + g_VoteType = slay; if (g_NativeVotes) { Handle hVoteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Custom_YesNo, view_as<MenuAction>(MENU_ACTIONS_ALL)); NativeVotes_SetTitle(hVoteMenu, "Voteslay Player"); NativeVotes_SetTarget(hVoteMenu, target); - NativeVotes_DisplayToAll(hVoteMenu, 20); + NativeVotes_DisplayToAll(hVoteMenu, g_ConVars[sv_vote_timer_duration].IntValue); } else { @@ -66,7 +66,7 @@ void DisplayVoteSlayMenu(int client, int target, char name[MAX_NAME_LENGTH]) AddMenuItem(hVoteMenu, VOTE_YES, "Yes"); AddMenuItem(hVoteMenu, VOTE_NO, "No"); SetMenuExitButton(hVoteMenu, false); - VoteMenuToAll(hVoteMenu, 20); + VoteMenuToAll(hVoteMenu, g_ConVars[sv_vote_timer_duration].IntValue); } } @@ -113,9 +113,9 @@ public int MenuHandler_Slay(Handle menu, MenuAction action, int param1, int para } case MenuAction_Cancel: { - if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE) + if (param2 == MenuCancel_ExitBack && g_TopMenu != INVALID_HANDLE) { - DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory); + DisplayTopMenu(g_TopMenu, param1, TopMenuPosition_LastCategory); } } case MenuAction_Select: @@ -127,15 +127,15 @@ public int MenuHandler_Slay(Handle menu, MenuAction action, int param1, int para if ((target = GetClientOfUserId(userid)) == 0) { - CPrintToChat(param1, "[SM] %t", "Player no longer available"); + CPrintToChat(param1, PLUGIN_PREFIX ... " %t", "Player no longer available"); } else if (!CanUserTarget(param1, target)) { - CPrintToChat(param1, "[SM] %t", "Unable to target"); + CPrintToChat(param1, PLUGIN_PREFIX ... " %t", "Unable to target"); } else if (!IsPlayerAlive(target)) { - CPrintToChat(param1, "[SM] %t", "Player has since died"); + CPrintToChat(param1, PLUGIN_PREFIX ... " %t", "Player has since died"); } else { @@ -151,13 +151,13 @@ public Action Command_VoteSlay(int client, int args) { if (args < 1) { - CReplyToCommand(client, "[\x04NativeVotes\x01] Usage: sm_voteslay <player>"); + CReplyToCommand(client, PLUGIN_PREFIX ... " Usage: sm_voteslay <player>"); return Plugin_Handled; } if (Internal_IsVoteInProgress()) { - CReplyToCommand(client, "[\x04NativeVotes\x01] %t", "Vote in Progress"); + CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Vote in Progress"); return Plugin_Handled; } diff --git a/addons/sourcemod/scripting/nativevotes_voterp.sp b/addons/sourcemod/scripting/nativevotes_voterp.sp index 417871c..03082e5 100644 --- a/addons/sourcemod/scripting/nativevotes_voterp.sp +++ b/addons/sourcemod/scripting/nativevotes_voterp.sp @@ -16,7 +16,7 @@ public Plugin myinfo = name = "NativeVotes | Medieval Auto-RP", author = "Heapons", description = "Provides Medieval Auto-RP voting.", - version = "26w07a", + version = "26w08a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -24,7 +24,7 @@ enum { tf_medieval, tf_medieval_autorp, - vote_duration, + sv_vote_timer_duration, MAX_CONVARS } @@ -42,7 +42,7 @@ public void OnPluginStart() SetFailState("This game doesn't support Medieval Mode."); } - g_ConVars[vote_duration] = CreateConVar("sm_voterp_voteduration", "20", "Specifies how long the rp vote should be available for.", _, true, 5.0); + g_ConVars[sv_vote_timer_duration] = FindConVar("sv_vote_timer_duration"); RegConsoleCmd("sm_voterp", Command_VoteRP, "Vote to toggle 'tf_medieval_autorp'."); } @@ -111,7 +111,7 @@ Action Command_VoteRP(int client, int args) bool enabled = g_ConVars[tf_medieval_autorp].BoolValue; char title[64]; - Format(title, sizeof(title), "Turn Medieval Auto-RP %s?", enabled ? "off" : "on"); + Format(title, sizeof(title), "%s Medieval Mode auto-roleplaying?", enabled ? "Disable" : "Enable"); if (g_NativeVotes) { @@ -120,7 +120,7 @@ Action Command_VoteRP(int client, int args) NativeVotes_SetInitiator(vote, client); NativeVotes_AddItem(vote, enabled ? "0" : "1", enabled ? "Disable" : "Enable"); NativeVotes_AddItem(vote, enabled ? "1" : "0", enabled ? "Enable" : "Disable"); - NativeVotes_DisplayToAll(vote, g_ConVars[vote_duration].IntValue); + NativeVotes_DisplayToAll(vote, g_ConVars[sv_vote_timer_duration].IntValue); } else { @@ -129,7 +129,7 @@ Action Command_VoteRP(int client, int args) menu.AddItem(enabled ? "0" : "1", enabled ? "Disable" : "Enable"); menu.AddItem(enabled ? "1" : "0", enabled ? "Enable" : "Disable"); menu.ExitButton = false; - menu.DisplayVoteToAll(g_ConVars[vote_duration].IntValue); + menu.DisplayVoteToAll(g_ConVars[sv_vote_timer_duration].IntValue); } return Plugin_Handled; @@ -146,7 +146,7 @@ int VoteHandler(NativeVote vote, MenuAction action, int param1, int param2) NativeVotes_GetItem(vote, item, info, sizeof(info)); int value = StringToInt(info); g_ConVars[tf_medieval_autorp].SetInt(value); - NativeVotes_DisplayPass(vote, "tf_medieval_autorp set to %d", value); + NativeVotes_DisplayPass(vote, "Set 'tf_medieval_autorp' to %s...", value ? "true" : "false"); } case MenuAction_End: { diff --git a/addons/sourcemod/scripting/nativevotes_votescramble.sp b/addons/sourcemod/scripting/nativevotes_votescramble.sp index 21bd141..9204620 100644 --- a/addons/sourcemod/scripting/nativevotes_votescramble.sp +++ b/addons/sourcemod/scripting/nativevotes_votescramble.sp @@ -13,7 +13,7 @@ public Plugin myinfo = name = "NativeVotes | Scramble Teams", author = "Heapons", description = "Provides RTV-style Scramble Teams Voting", - version = "26w07a", + version = "26w08a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -26,6 +26,7 @@ enum full_reset, mp_match_end_at_timelimit, mp_timelimit, + sv_vote_timer_duration, MAX_CONVARS } @@ -72,12 +73,13 @@ public void OnPluginStart() g_ConVars[full_reset] = CreateConVar("sm_scrambleteams_full_reset", "1", "Whether time/rounds played should reset after a scramble is triggered ", 0, true, 0.0, true, 1.0); g_ConVars[mp_match_end_at_timelimit] = FindConVar("mp_match_end_at_timelimit"); g_ConVars[mp_timelimit] = FindConVar("mp_timelimit"); + g_ConVars[sv_vote_timer_duration] = FindConVar("sv_vote_timer_duration"); RegConsoleCmd("sm_votescramble", Command_VoteScramble); RegConsoleCmd("sm_scramble", Command_VoteScramble); - RegAdminCmd("sm_forcescramble", Command_ForceScramble, ADMFLAG_CHANGEMAP); - RegAdminCmd("sm_resetscramble", Command_ResetScramble, ADMFLAG_CHANGEMAP); + RegAdminCmd("sm_forcescramble", Command_ForceScramble, ADMFLAG_VOTE); + RegAdminCmd("sm_resetscramble", Command_ResetScramble, ADMFLAG_VOTE); AutoExecConfig(true, "votescramble"); @@ -357,9 +359,7 @@ void StartScramble() { NativeVote vote = NativeVotes_Create(ScrambleVoteHandler, NativeVotesType_ScrambleNow); NativeVotes_SetTitle(vote, "%t", "Scramble Teams"); - NativeVotes_AddItem(vote, "yes", yes); - NativeVotes_AddItem(vote, "no", no); - NativeVotes_DisplayToAll(vote, 20); + NativeVotes_DisplayToAll(vote, g_ConVars[sv_vote_timer_duration].IntValue); NativeVotes_SetResultCallback(vote, ScrambleVoteResult); } else @@ -373,7 +373,7 @@ void StartScramble() { if (IsClientInGame(i) && !IsFakeClient(i)) { - menu.Display(i, 20); + menu.Display(i, g_ConVars[sv_vote_timer_duration].IntValue); } } } @@ -426,11 +426,7 @@ public Action Menu_ScrambleTeams(int client, NativeVotesOverride overrideType, c int ScrambleVoteHandler(NativeVote vote, MenuAction action, int param1, int param2) { - if (action == MenuAction_VoteEnd) - { - // handled by result callback - } - else if (action == MenuAction_End) + if (action == MenuAction_End) { NativeVotes_Close(vote); } From a6b0a9859469b35600e05a393d6fa42148a1d4bf Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Fri, 20 Feb 2026 00:53:34 +0100 Subject: [PATCH 101/128] Bump version --- .../scripting/nativevotes_basecommands.sp | 16 ++++++++-------- .../scripting/nativevotes_mapchooser.sp | 13 ++++++------- .../scripting/nativevotes_nominations.sp | 2 +- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_basecommands.sp b/addons/sourcemod/scripting/nativevotes_basecommands.sp index d95ea4c..ca90b93 100644 --- a/addons/sourcemod/scripting/nativevotes_basecommands.sp +++ b/addons/sourcemod/scripting/nativevotes_basecommands.sp @@ -43,14 +43,14 @@ #define PLUGIN_PREFIX "[\x04NativeVotes\x01]" -TopMenu hTopMenu; +TopMenu g_TopMenu; public Plugin myinfo = { name = "NativeVotes | Basic Commands", author = "Powerlord and AlliedModders LLC", description = "Revote and Cancel support for NativeVotes", - version = "26w07a", + version = "26w08a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" } @@ -71,7 +71,7 @@ bool PerformCancelVote(int client) return false; } - CShowActivity2(client, "[\x04NativeVotes\x01] ", "%t", "Cancelled Vote"); + CShowActivity2(client, PLUGIN_PREFIX ... " ", "%t", "Cancelled Vote"); NativeVotes_Cancel(); return true; @@ -199,19 +199,19 @@ public void OnAdminMenuReady(Handle aTopMenu) TopMenu topmenu = TopMenu.FromHandle(aTopMenu); /* Block us from being called twice */ - if (topmenu == hTopMenu) + if (topmenu == g_TopMenu) { return; } /* Save the Handle */ - hTopMenu = topmenu; + g_TopMenu = topmenu; - TopMenuObject voting_commands = hTopMenu.FindCategory(ADMINMENU_VOTINGCOMMANDS); + TopMenuObject voting_commands = g_TopMenu.FindCategory(ADMINMENU_VOTINGCOMMANDS); if (voting_commands != INVALID_TOPMENUOBJECT) { - hTopMenu.AddItem("sm_cancelvote", AdminMenu_CancelVote, voting_commands, "sm_cancelvote", ADMFLAG_VOTE); + g_TopMenu.AddItem("sm_cancelvote", AdminMenu_CancelVote, voting_commands, "sm_cancelvote", ADMFLAG_VOTE); } } @@ -219,6 +219,6 @@ public void OnLibraryRemoved(const char[] name) { if (strcmp(name, "adminmenu") == 0) { - hTopMenu = null; + g_TopMenu = null; } } \ No newline at end of file diff --git a/addons/sourcemod/scripting/nativevotes_mapchooser.sp b/addons/sourcemod/scripting/nativevotes_mapchooser.sp index 74426e0..1c48802 100644 --- a/addons/sourcemod/scripting/nativevotes_mapchooser.sp +++ b/addons/sourcemod/scripting/nativevotes_mapchooser.sp @@ -58,7 +58,7 @@ public Plugin myinfo = name = "NativeVotes | MapChooser", author = "AlliedModders LLC and Powerlord", description = "Automated Map Voting", - version = "26w07a", + version = "26w08a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -419,7 +419,7 @@ public Action Command_SetNextMap(int client, int args) GetMapDisplayName(displayName, displayName, sizeof(displayName)); Format(displayName, sizeof(displayName), "\x05%s\x01", displayName); - CShowActivity2(client, "[\x04MapChooser\x01] ", "%t", "Changed Next Map", displayName); + CShowActivity2(client, PLUGIN_PREFIX ... " ", "%t", "Changed Next Map", displayName); if (client > 0) { LogAction(client, -1, "\"%L\" changed nextmap to \"%s\"", client, map); @@ -1037,7 +1037,7 @@ public void Handler_NV_MapVoteFinished(NativeVote menu, int num_votes, int num_c if (winningvotes < required) { - //Added in 1.5.1 + //Added in 1.5.1 menu.DisplayFail(NativeVotesFail_NotEnoughVotes); /* Insufficient Winning margin - Lets do a runoff */ @@ -1064,9 +1064,8 @@ public void Handler_NV_MapVoteFinished(NativeVote menu, int num_votes, int num_c /* Notify */ float map1percent = float(item_votes[0])/ float(num_votes) * 100; float map2percent = float(item_votes[1])/ float(num_votes) * 100; - - - CPrintToChatAll("[\x04MapChooser\x01] %t", "Starting Runoff", g_ConVars[mapvote_runoffpercent].FloatValue, info1, map1percent, info2, map2percent); + + CPrintToChatAll(PLUGIN_PREFIX ... " %t", "Starting Runoff", g_ConVars[mapvote_runoffpercent].FloatValue, info1, map1percent, info2, map2percent); LogMessage("Voting for next map was indecisive, beginning runoff vote"); return; @@ -1127,7 +1126,7 @@ public void Handler_MapVoteFinished(Menu menu, int num_votes, int num_clients, c float map1percent = float(item_info[0][VOTEINFO_ITEM_VOTES])/ float(num_votes) * 100; float map2percent = float(item_info[1][VOTEINFO_ITEM_VOTES])/ float(num_votes) * 100; - CPrintToChatAll("[\x04MapChooser\x01] %t", "Starting Runoff", g_ConVars[mapvote_runoffpercent].FloatValue, info1, map1percent, info2, map2percent); + CPrintToChatAll(PLUGIN_PREFIX ... " %t", "Starting Runoff", g_ConVars[mapvote_runoffpercent].FloatValue, info1, map1percent, info2, map2percent); LogMessage("Voting for next map was indecisive, beginning runoff vote"); return; diff --git a/addons/sourcemod/scripting/nativevotes_nominations.sp b/addons/sourcemod/scripting/nativevotes_nominations.sp index b7d7de9..42c68e2 100644 --- a/addons/sourcemod/scripting/nativevotes_nominations.sp +++ b/addons/sourcemod/scripting/nativevotes_nominations.sp @@ -50,7 +50,7 @@ public Plugin myinfo = name = "NativeVotes | Map Nominations", author = "AlliedModders LLC and Powerlord", description = "Provides Map Nominations", - version = "26w07a", + version = "26w08a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; From c30fff63c53bd87219da52bfccf82655d9da1e40 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Fri, 20 Feb 2026 00:53:48 +0100 Subject: [PATCH 102/128] Remove Kickvote Immunity in favor of Base Votes --- .../nativevotes_kickvote_immunity.sp | 176 ------------------ 1 file changed, 176 deletions(-) delete mode 100644 addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp diff --git a/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp b/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp deleted file mode 100644 index cab2155..0000000 --- a/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp +++ /dev/null @@ -1,176 +0,0 @@ -/** - * vim: set ts=4 : - * ============================================================================= - * NativeVotes Kick Vote Immunity - * Causes TF2 kick votes to fail against people who the current user can't target. - * - * Inspired by psychonic's [TF2] Basic Votekick Immunity - * - * NativeVotes Kick Vote Immunity (C)2014 Powerlord (Ross Bemrose). - * All rights reserved. - * ============================================================================= - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, version 3.0, as published by the - * Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. - * - * As a special exception, AlliedModders LLC gives you permission to link the - * code of this program (as well as its derivative works) to "Half-Life 2," the - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software - * by the Valve Corporation. You must obey the GNU General Public License in - * all respects for all other code used. Additionally, AlliedModders LLC grants - * this exception to all derivative works. AlliedModders LLC defines further - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), - * or <http://www.sourcemod.net/license.php>. - * - * Version: $Id$ - */ -#include <sourcemod> -#include <sdktools> - -#include <nativevotes> - -#pragma semicolon 1 -#pragma newdecls required - -enum -{ - sv_allow_votes, - sv_vote_issue_kick_allowed, - sv_vote_issue_kick_allowed_mvm, - - MAX_CONVARS -} - -ConVar g_ConVars[MAX_CONVARS]; - -bool g_bRegistered = false; - -bool g_bMapActive = false; - -public Plugin myinfo = { - name = "NativeVotes | Kick Vote Immunity", - author = "Powerlord", - description = "Causes TF2 kick votes to fail against people who the current user can't target.", - version = "26w07a", - url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" -}; - -public void OnPluginStart() -{ - g_ConVars[sv_allow_votes] = FindConVar("sv_allow_votes"); - g_ConVars[sv_vote_issue_kick_allowed] = FindConVar("sv_vote_issue_kick_allowed"); - g_ConVars[sv_vote_issue_kick_allowed_mvm] = FindConVar("sv_vote_issue_kick_allowed_mvm"); - - for (int i = 0; i < MAX_CONVARS; i++) - { - if (g_ConVars[i] != null) - { - g_ConVars[i].AddChangeHook(ConVar_CheckEnable); - } - } - - LoadTranslations("common.phrases"); -} - -public void OnAllPluginsLoaded() -{ - if (GetFeatureStatus(FeatureType_Native, "NativeVotes_AreVoteCommandsSupported") != FeatureStatus_Available) - { - SetFailState("Requires NativeVotes 1.1 or newer"); - } -} - -public void OnConfigsExecuted() -{ - g_bMapActive = true; - CheckStatus(); -} - -public void OnMapEnd() -{ - g_bMapActive = false; -} - -public void OnPluginEnd() -{ - UnregisterVoteCommand(); -} - -void RegisterVoteCommand() -{ - if (!g_bRegistered && NativeVotes_AreVoteCommandsSupported()) - { - NativeVotes_RegisterVoteCommand(NativeVotesOverride_Kick, KickVoteHandler); - g_bRegistered = true; - } -} - -void UnregisterVoteCommand() -{ - if (g_bRegistered) - { - NativeVotes_UnregisterVoteCommand(NativeVotesOverride_Kick, KickVoteHandler); - g_bRegistered = false; - } -} - -public void ConVar_CheckEnable(ConVar convar, const char[] oldValue, const char[] newValue) -{ - if (!g_bMapActive) - return; - - CheckStatus(); -} - -void CheckStatus() -{ - bool bIsMvM = IsMvM(); - if (g_bRegistered) - { - if (!g_ConVars[sv_allow_votes].BoolValue || - (bIsMvM && !g_ConVars[sv_vote_issue_kick_allowed_mvm].BoolValue) || - (!bIsMvM && !g_ConVars[sv_vote_issue_kick_allowed].BoolValue) - ) - { - LogMessage("Disabling."); - UnregisterVoteCommand(); - } - } - else - { - if (g_ConVars[sv_allow_votes].BoolValue && - ((bIsMvM && g_ConVars[sv_vote_issue_kick_allowed_mvm].BoolValue) || - (!bIsMvM && g_ConVars[sv_vote_issue_kick_allowed].BoolValue)) - ) - { - LogMessage("Enabling."); - RegisterVoteCommand(); - } - } -} - -stock bool IsMvM() -{ - return view_as<bool>(GameRules_GetProp("m_bPlayingMannVsMachine")); -} - -public Action KickVoteHandler(int client, NativeVotesOverride overrideType, const char[] voteArgument, NativeVotesKickType kickType, int target) -{ - if (!CanUserTarget(client, target)) - { - NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_CantKickAdmin); - CPrintToChat(client, "%t", "Unable to target"); - return Plugin_Stop; - } - - return Plugin_Continue; -} \ No newline at end of file From a6178d822d3e64497f76f83e187d7daa908b44fd Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Fri, 20 Feb 2026 00:54:30 +0100 Subject: [PATCH 103/128] Tweaks - Small delay to account for nominations. - Attempt to fix "RTV already started" false positives. --- .../scripting/nativevotes_rockthevote.sp | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_rockthevote.sp b/addons/sourcemod/scripting/nativevotes_rockthevote.sp index a98cd8e..d832774 100644 --- a/addons/sourcemod/scripting/nativevotes_rockthevote.sp +++ b/addons/sourcemod/scripting/nativevotes_rockthevote.sp @@ -57,7 +57,7 @@ public Plugin myinfo = name = "NativeVotes | Rock The Vote", author = "AlliedModders LLC and Powerlord", description = "Provides RTV Map Voting", - version = "26w07a", + version = "26w08a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -103,8 +103,8 @@ public void OnPluginStart() RegConsoleCmd("sm_rtv", Command_RTV); RegConsoleCmd("sm_rockthevote", Command_RTV); - RegAdminCmd("sm_forcertv", Command_ForceRTV, ADMFLAG_CHANGEMAP); - RegAdminCmd("sm_resetrtv", Command_ResetRTV, ADMFLAG_CHANGEMAP); + RegAdminCmd("sm_forcertv", Command_ForceRTV, ADMFLAG_VOTE|ADMFLAG_CHANGEMAP); + RegAdminCmd("sm_resetrtv", Command_ResetRTV, ADMFLAG_VOTE|ADMFLAG_CHANGEMAP); AutoExecConfig(true, "rtv"); @@ -357,7 +357,7 @@ void AttemptRTV(int client, bool isVoteMenu=false) return; } - if (!CanMapChooserStartVote() || (g_NativeVotes && NativeVotes_IsVoteInProgress()) || (!g_NativeVotes && IsVoteInProgress())) + if (!CanMapChooserStartVote()) { CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "RTV Started"); return; @@ -436,8 +436,6 @@ void StartRTV() ResetRTV(); g_RTVAllowed = false; - g_RTVTime = GetTime() + g_ConVars[interval].IntValue; - CreateTimer(g_ConVars[interval].FloatValue, Timer_DelayRTV, _, TIMER_FLAG_NO_MAPCHANGE); } } @@ -491,6 +489,17 @@ void RemoveVoteHandler() } +public Action Timer_Menu_RTV(Handle timer, int client) +{ + ReplySource old = SetCmdReplySource(SM_REPLY_TO_CHAT); + + AttemptRTV(client, true); + + SetCmdReplySource(old); + + return Plugin_Handled; +} + public Action Menu_RTV(int client, NativeVotesOverride overrideType, const char[] voteArgument) { if (client <= 0 || (g_NativeVotes && NativeVotes_IsVoteInProgress()) || (!g_NativeVotes && IsVoteInProgress())) @@ -503,7 +512,13 @@ public Action Menu_RTV(int client, NativeVotesOverride overrideType, const char[ NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_SpecifyMap); return Plugin_Handled; } - + + if (CommandExists("sm_nominate")) + { + CreateTimer(0.1, Timer_Menu_RTV, client); + return Plugin_Handled; + } + ReplySource old = SetCmdReplySource(SM_REPLY_TO_CHAT); AttemptRTV(client, true); From 0f6407997635b33c89ae05cbc0b7090450eb0d25 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Fri, 20 Feb 2026 01:03:57 +0100 Subject: [PATCH 104/128] Update README.md I really should get started on a proper wiki... --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6bafede..a993071 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ This fork aims to expand upon [<img src="https://avatars.githubusercontent.com/u - Highlight map names. - Update [Nominations](https://github.com/Heapons/sourcemod-nativevotes-updated/blob/master/addons/sourcemod/scripting/nativevotes_nominations.sp) and [Rock The Vote](https://github.com/Heapons/sourcemod-nativevotes-updated/blob/master/addons/sourcemod/scripting/nativevotes_rockthevote.sp) to be on par with the latest [Sourcemod](https://github.com/alliedmodders/sourcemod/tree/master/plugins) version. - Vote Progress HUD: - - <img src="https://shared.fastly.steamstatic.com/community_assets/images/apps/3545060/08607ace82bfb52cf8993efe88c2ef00fa25c96f.ico" width="16" height="16" style="vertical-align: text-bottom;"> Added `nativevotes_progress_hintcaption` convar to use the style of tutorial hints. + - <img src="https://developer.valvesoftware.com/w/images/2/2b/Env_instructor_hint_-_icon_info.png" width="16" height="16" style="vertical-align: text-bottom;"> Added `nativevotes_progress_hintcaption` convar to use the style of [game instructor hints](https://developer.valvesoftware.com/wiki/Env_instructor_hint) for supported games. ## <img src="https://cdn.fastly.steamstatic.com/steamcommunity/public/images/apps/440/033bdd91842b6aca0633ee1e5f3e6b82f2e8962f.ico" width="24" height="24" style="vertical-align: text-bottom;"> Team Fortress 2 - Fixes: From 9c8621caf4db84b029ae6f5e898c2abcdeded205 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Fri, 20 Feb 2026 01:24:25 +0100 Subject: [PATCH 105/128] Potential fix for #16 Only RED and BLU teams were accounted for. --- .../scripting/nativevotes_mapchooser.sp | 26 +++++-------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_mapchooser.sp b/addons/sourcemod/scripting/nativevotes_mapchooser.sp index 1c48802..59b070e 100644 --- a/addons/sourcemod/scripting/nativevotes_mapchooser.sp +++ b/addons/sourcemod/scripting/nativevotes_mapchooser.sp @@ -501,34 +501,20 @@ public void Event_TeamplayWinPanel(Event event, const char[] name, bool dontBroa g_ChangeMapInProgress = true; } - int bluescore = event.GetInt("blue_score"); - int redscore = event.GetInt("red_score"); - if (event.GetInt("round_complete") == 1 || StrEqual(name, "arena_win_panel")) { - if (!g_MapList.Length || g_HasVoteStarted || g_MapVoteCompleted || !g_ConVars[mapvote_endvote].BoolValue) { return; } - + CheckMaxRounds(); - - switch(event.GetInt("winning_team")) + + int winning_team = event.GetInt("winning_team"); + if (winning_team > 1 && winning_team < MAX_TEAMS) { - case 3: - { - CheckWinLimit(bluescore); - } - case 2: - { - CheckWinLimit(redscore); - } - //We need to do nothing on winning_team == 0 this indicates stalemate. - default: - { - return; - } + int score = GetTeamScore(winning_team); + CheckWinLimit(score); } } } From 2bb5fe048deb14a49f977f4fe966971d93185773 Mon Sep 17 00:00:00 2001 From: megascatterbomb <megascatterbomb@gmail.com> Date: Sat, 21 Feb 2026 13:35:42 +1300 Subject: [PATCH 106/128] Fix incorrect calls to CShowActivity2 --- addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp | 2 +- addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp | 2 +- addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp | 2 +- addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp | 2 +- addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp | 2 +- addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp b/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp index a83e8ab..e147ed3 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp @@ -41,7 +41,7 @@ bool g_VoteMapInUse; void DisplayVoteMapMenu(int client, int mapCount, char maps[5][PLATFORM_MAX_PATH]) { LogAction(client, -1, "\"%L\" initiated a map vote.", client); - CShowActivity2(client, PLUGIN_PREFIX ... " %t", "Initiated Vote Map"); + CShowActivity2(client, PLUGIN_PREFIX ... " ", "%t", "Initiated Vote Map"); g_VoteType = map; diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp b/addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp index 0045491..054c174 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp @@ -46,7 +46,7 @@ void DisplayVoteAllTalkMenu(int client) } LogAction(client, -1, "\"%L\" initiated an alltalk vote.", client); - CShowActivity2(client, PLUGIN_PREFIX ... " %t", "Initiated Vote Alltalk"); + CShowActivity2(client, PLUGIN_PREFIX ... " ", "%t", "Initiated Vote Alltalk"); g_VoteType = alltalk; g_VoteInfo[VOTE_NAME][0] = '\0'; diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp b/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp index c302bbc..ef0a4e1 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp @@ -48,7 +48,7 @@ void DisplayVoteBurnMenu(int client, int target, char name[MAX_NAME_LENGTH]) GetClientName(target, g_VoteInfo[VOTE_NAME], sizeof(g_VoteInfo[])); LogAction(client, target, "\"%L\" initiated a burn vote against %N", client, target); - CShowActivity2(client, PLUGIN_PREFIX ... " %t", "Initiated Vote Burn", playerName); + CShowActivity2(client, PLUGIN_PREFIX ... " ", "%t", "Initiated Vote Burn", playerName); g_VoteType = burn; diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp b/addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp index fb3a2cc..72527c2 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp @@ -46,7 +46,7 @@ void DisplayVoteFFMenu(int client) } LogAction(client, -1, "\"%L\" initiated a friendly fire vote.", client); - CShowActivity2(client, PLUGIN_PREFIX ... " %t", "Initiated Vote FF"); + CShowActivity2(client, PLUGIN_PREFIX ... " ", "%t", "Initiated Vote FF"); g_VoteType = ff; g_VoteInfo[VOTE_NAME][0] = '\0'; diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp b/addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp index 0dcf18a..3948a58 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp @@ -35,7 +35,7 @@ void DisplayVoteGravityMenu(int client, int count, char items[5][64]) { LogAction(client, -1, "\"%L\" initiated a gravity vote.", client); - CShowActivity2(client, PLUGIN_PREFIX ... " %t", "Initiated Vote Gravity"); + CShowActivity2(client, PLUGIN_PREFIX ... " ", "%t", "Initiated Vote Gravity"); g_VoteType = gravity; diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp b/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp index b3ac4c4..1376a3d 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp @@ -48,7 +48,7 @@ void DisplayVoteSlayMenu(int client, int target, char name[MAX_NAME_LENGTH]) GetClientName(target, g_VoteInfo[VOTE_NAME], sizeof(g_VoteInfo[])); LogAction(client, target, "\"%L\" initiated a slay vote against %N", client, target); - CShowActivity2(client, PLUGIN_PREFIX ... " %t", "Initiated Vote Slay", playerName); + CShowActivity2(client, PLUGIN_PREFIX ... " ", "%t", "Initiated Vote Slay", playerName); g_VoteType = slay; From dfb7d58835df2b819bd3807fc25a67ba590cff06 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sat, 21 Feb 2026 11:25:17 +0100 Subject: [PATCH 107/128] Revert "(Untested) Fix incorrect calls to CShowActivity2" --- addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp | 2 +- addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp | 2 +- addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp | 2 +- addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp | 2 +- addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp | 2 +- addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp b/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp index e147ed3..a83e8ab 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp @@ -41,7 +41,7 @@ bool g_VoteMapInUse; void DisplayVoteMapMenu(int client, int mapCount, char maps[5][PLATFORM_MAX_PATH]) { LogAction(client, -1, "\"%L\" initiated a map vote.", client); - CShowActivity2(client, PLUGIN_PREFIX ... " ", "%t", "Initiated Vote Map"); + CShowActivity2(client, PLUGIN_PREFIX ... " %t", "Initiated Vote Map"); g_VoteType = map; diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp b/addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp index 054c174..0045491 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp @@ -46,7 +46,7 @@ void DisplayVoteAllTalkMenu(int client) } LogAction(client, -1, "\"%L\" initiated an alltalk vote.", client); - CShowActivity2(client, PLUGIN_PREFIX ... " ", "%t", "Initiated Vote Alltalk"); + CShowActivity2(client, PLUGIN_PREFIX ... " %t", "Initiated Vote Alltalk"); g_VoteType = alltalk; g_VoteInfo[VOTE_NAME][0] = '\0'; diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp b/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp index ef0a4e1..c302bbc 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp @@ -48,7 +48,7 @@ void DisplayVoteBurnMenu(int client, int target, char name[MAX_NAME_LENGTH]) GetClientName(target, g_VoteInfo[VOTE_NAME], sizeof(g_VoteInfo[])); LogAction(client, target, "\"%L\" initiated a burn vote against %N", client, target); - CShowActivity2(client, PLUGIN_PREFIX ... " ", "%t", "Initiated Vote Burn", playerName); + CShowActivity2(client, PLUGIN_PREFIX ... " %t", "Initiated Vote Burn", playerName); g_VoteType = burn; diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp b/addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp index 72527c2..fb3a2cc 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp @@ -46,7 +46,7 @@ void DisplayVoteFFMenu(int client) } LogAction(client, -1, "\"%L\" initiated a friendly fire vote.", client); - CShowActivity2(client, PLUGIN_PREFIX ... " ", "%t", "Initiated Vote FF"); + CShowActivity2(client, PLUGIN_PREFIX ... " %t", "Initiated Vote FF"); g_VoteType = ff; g_VoteInfo[VOTE_NAME][0] = '\0'; diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp b/addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp index 3948a58..0dcf18a 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp @@ -35,7 +35,7 @@ void DisplayVoteGravityMenu(int client, int count, char items[5][64]) { LogAction(client, -1, "\"%L\" initiated a gravity vote.", client); - CShowActivity2(client, PLUGIN_PREFIX ... " ", "%t", "Initiated Vote Gravity"); + CShowActivity2(client, PLUGIN_PREFIX ... " %t", "Initiated Vote Gravity"); g_VoteType = gravity; diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp b/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp index 1376a3d..b3ac4c4 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp @@ -48,7 +48,7 @@ void DisplayVoteSlayMenu(int client, int target, char name[MAX_NAME_LENGTH]) GetClientName(target, g_VoteInfo[VOTE_NAME], sizeof(g_VoteInfo[])); LogAction(client, target, "\"%L\" initiated a slay vote against %N", client, target); - CShowActivity2(client, PLUGIN_PREFIX ... " ", "%t", "Initiated Vote Slay", playerName); + CShowActivity2(client, PLUGIN_PREFIX ... " %t", "Initiated Vote Slay", playerName); g_VoteType = slay; From d895081b0f64470403717c655a35a308e4ca934b Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sat, 21 Feb 2026 11:29:52 +0100 Subject: [PATCH 108/128] Revert "Revert "(Untested) Fix incorrect calls to CShowActivity2"" --- addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp | 2 +- addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp | 2 +- addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp | 2 +- addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp | 2 +- addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp | 2 +- addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp b/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp index a83e8ab..e147ed3 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp @@ -41,7 +41,7 @@ bool g_VoteMapInUse; void DisplayVoteMapMenu(int client, int mapCount, char maps[5][PLATFORM_MAX_PATH]) { LogAction(client, -1, "\"%L\" initiated a map vote.", client); - CShowActivity2(client, PLUGIN_PREFIX ... " %t", "Initiated Vote Map"); + CShowActivity2(client, PLUGIN_PREFIX ... " ", "%t", "Initiated Vote Map"); g_VoteType = map; diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp b/addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp index 0045491..054c174 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp @@ -46,7 +46,7 @@ void DisplayVoteAllTalkMenu(int client) } LogAction(client, -1, "\"%L\" initiated an alltalk vote.", client); - CShowActivity2(client, PLUGIN_PREFIX ... " %t", "Initiated Vote Alltalk"); + CShowActivity2(client, PLUGIN_PREFIX ... " ", "%t", "Initiated Vote Alltalk"); g_VoteType = alltalk; g_VoteInfo[VOTE_NAME][0] = '\0'; diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp b/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp index c302bbc..ef0a4e1 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp @@ -48,7 +48,7 @@ void DisplayVoteBurnMenu(int client, int target, char name[MAX_NAME_LENGTH]) GetClientName(target, g_VoteInfo[VOTE_NAME], sizeof(g_VoteInfo[])); LogAction(client, target, "\"%L\" initiated a burn vote against %N", client, target); - CShowActivity2(client, PLUGIN_PREFIX ... " %t", "Initiated Vote Burn", playerName); + CShowActivity2(client, PLUGIN_PREFIX ... " ", "%t", "Initiated Vote Burn", playerName); g_VoteType = burn; diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp b/addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp index fb3a2cc..72527c2 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp @@ -46,7 +46,7 @@ void DisplayVoteFFMenu(int client) } LogAction(client, -1, "\"%L\" initiated a friendly fire vote.", client); - CShowActivity2(client, PLUGIN_PREFIX ... " %t", "Initiated Vote FF"); + CShowActivity2(client, PLUGIN_PREFIX ... " ", "%t", "Initiated Vote FF"); g_VoteType = ff; g_VoteInfo[VOTE_NAME][0] = '\0'; diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp b/addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp index 0dcf18a..3948a58 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp @@ -35,7 +35,7 @@ void DisplayVoteGravityMenu(int client, int count, char items[5][64]) { LogAction(client, -1, "\"%L\" initiated a gravity vote.", client); - CShowActivity2(client, PLUGIN_PREFIX ... " %t", "Initiated Vote Gravity"); + CShowActivity2(client, PLUGIN_PREFIX ... " ", "%t", "Initiated Vote Gravity"); g_VoteType = gravity; diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp b/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp index b3ac4c4..1376a3d 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp @@ -48,7 +48,7 @@ void DisplayVoteSlayMenu(int client, int target, char name[MAX_NAME_LENGTH]) GetClientName(target, g_VoteInfo[VOTE_NAME], sizeof(g_VoteInfo[])); LogAction(client, target, "\"%L\" initiated a slay vote against %N", client, target); - CShowActivity2(client, PLUGIN_PREFIX ... " %t", "Initiated Vote Slay", playerName); + CShowActivity2(client, PLUGIN_PREFIX ... " ", "%t", "Initiated Vote Slay", playerName); g_VoteType = slay; From d268785bbd1e452feabd20ebc30b16942b61d2fa Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Fri, 13 Mar 2026 13:24:42 +0100 Subject: [PATCH 109/128] Fix activity prints --- addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp | 2 +- addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp | 2 +- addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp | 2 +- addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp | 2 +- addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp | 2 +- addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp b/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp index a83e8ab..e147ed3 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp @@ -41,7 +41,7 @@ bool g_VoteMapInUse; void DisplayVoteMapMenu(int client, int mapCount, char maps[5][PLATFORM_MAX_PATH]) { LogAction(client, -1, "\"%L\" initiated a map vote.", client); - CShowActivity2(client, PLUGIN_PREFIX ... " %t", "Initiated Vote Map"); + CShowActivity2(client, PLUGIN_PREFIX ... " ", "%t", "Initiated Vote Map"); g_VoteType = map; diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp b/addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp index 0045491..054c174 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp @@ -46,7 +46,7 @@ void DisplayVoteAllTalkMenu(int client) } LogAction(client, -1, "\"%L\" initiated an alltalk vote.", client); - CShowActivity2(client, PLUGIN_PREFIX ... " %t", "Initiated Vote Alltalk"); + CShowActivity2(client, PLUGIN_PREFIX ... " ", "%t", "Initiated Vote Alltalk"); g_VoteType = alltalk; g_VoteInfo[VOTE_NAME][0] = '\0'; diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp b/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp index c302bbc..ef0a4e1 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp @@ -48,7 +48,7 @@ void DisplayVoteBurnMenu(int client, int target, char name[MAX_NAME_LENGTH]) GetClientName(target, g_VoteInfo[VOTE_NAME], sizeof(g_VoteInfo[])); LogAction(client, target, "\"%L\" initiated a burn vote against %N", client, target); - CShowActivity2(client, PLUGIN_PREFIX ... " %t", "Initiated Vote Burn", playerName); + CShowActivity2(client, PLUGIN_PREFIX ... " ", "%t", "Initiated Vote Burn", playerName); g_VoteType = burn; diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp b/addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp index fb3a2cc..72527c2 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp @@ -46,7 +46,7 @@ void DisplayVoteFFMenu(int client) } LogAction(client, -1, "\"%L\" initiated a friendly fire vote.", client); - CShowActivity2(client, PLUGIN_PREFIX ... " %t", "Initiated Vote FF"); + CShowActivity2(client, PLUGIN_PREFIX ... " ", "%t", "Initiated Vote FF"); g_VoteType = ff; g_VoteInfo[VOTE_NAME][0] = '\0'; diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp b/addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp index 0dcf18a..3948a58 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp @@ -35,7 +35,7 @@ void DisplayVoteGravityMenu(int client, int count, char items[5][64]) { LogAction(client, -1, "\"%L\" initiated a gravity vote.", client); - CShowActivity2(client, PLUGIN_PREFIX ... " %t", "Initiated Vote Gravity"); + CShowActivity2(client, PLUGIN_PREFIX ... " ", "%t", "Initiated Vote Gravity"); g_VoteType = gravity; diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp b/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp index b3ac4c4..1376a3d 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp @@ -48,7 +48,7 @@ void DisplayVoteSlayMenu(int client, int target, char name[MAX_NAME_LENGTH]) GetClientName(target, g_VoteInfo[VOTE_NAME], sizeof(g_VoteInfo[])); LogAction(client, target, "\"%L\" initiated a slay vote against %N", client, target); - CShowActivity2(client, PLUGIN_PREFIX ... " %t", "Initiated Vote Slay", playerName); + CShowActivity2(client, PLUGIN_PREFIX ... " ", "%t", "Initiated Vote Slay", playerName); g_VoteType = slay; From 51e1c1c1464fc1caf6b53a0c8bd46b404cf8a4c4 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Fri, 13 Mar 2026 13:30:16 +0100 Subject: [PATCH 110/128] Use LANG_SERVER for "Extend Map" and "Don't Change" --- addons/sourcemod/scripting/nativevotes_mapchooser.sp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_mapchooser.sp b/addons/sourcemod/scripting/nativevotes_mapchooser.sp index 59b070e..11da09e 100644 --- a/addons/sourcemod/scripting/nativevotes_mapchooser.sp +++ b/addons/sourcemod/scripting/nativevotes_mapchooser.sp @@ -1149,12 +1149,12 @@ public int Handler_MapVoteMenu(Menu menu, MenuAction action, int param1, int par menu.GetItem(param2, map, sizeof(map)); if (strcmp(map, VOTE_EXTEND, false) == 0) { - Format(buffer, sizeof(buffer), "%t", "Extend Map", param1); + Format(buffer, sizeof(buffer), "%T", LANG_SERVER, "Extend Map", param1); return RedrawMenuItem(buffer); } else if (strcmp(map, VOTE_DONTCHANGE, false) == 0) { - Format(buffer, sizeof(buffer), "%t", "Dont Change", param1); + Format(buffer, sizeof(buffer), "%T", LANG_SERVER, "Dont Change", param1); return RedrawMenuItem(buffer); } } @@ -1204,6 +1204,7 @@ public int Handler_NV_MapVoteMenu(NativeVote menu, MenuAction action, int param1 { g_VoteMenu = null; menu.Close(); + g_HasVoteStarted = false; } case MenuAction_DisplayItem: @@ -1214,12 +1215,12 @@ public int Handler_NV_MapVoteMenu(NativeVote menu, MenuAction action, int param1 menu.GetItem(param2, map, sizeof(map)); if (strcmp(map, VOTE_EXTEND, false) == 0) { - Format(buffer, sizeof(buffer), "%t", "Extend Map", param1); + Format(buffer, sizeof(buffer), "%T", LANG_SERVER, "Extend Map", param1); return view_as<int>(NativeVotes_RedrawVoteItem(buffer)); } else if (strcmp(map, VOTE_DONTCHANGE, false) == 0) { - Format(buffer, sizeof(buffer), "%t", "Dont Change", param1); + Format(buffer, sizeof(buffer), "%T", LANG_SERVER, "Dont Change", param1); return view_as<int>(NativeVotes_RedrawVoteItem(buffer)); } } @@ -1265,7 +1266,7 @@ public int Handler_NV_MapVoteMenu(NativeVote menu, MenuAction action, int param1 menu.DisplayFail(NativeVotesFail_Generic); } - g_HasVoteStarted = false; + //g_HasVoteStarted = false; } } From 08329f724e1aa6b738b6de5751f5629dec105437 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Fri, 13 Mar 2026 13:30:51 +0100 Subject: [PATCH 111/128] Fix cvar limits --- addons/sourcemod/scripting/nativevotes_funvotes.sp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_funvotes.sp b/addons/sourcemod/scripting/nativevotes_funvotes.sp index 415c109..bf3b7fb 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes.sp @@ -309,14 +309,15 @@ public int Handler_VoteCallback(Handle menu, MenuAction action, int param1, int percent = GetVotePercent(votes, totalVotes); - limit = GetConVarFloat(g_ConVars[g_VoteType]); + limit = GetConVarFloat(g_ConVars[vote_gravity + view_as<int>(g_VoteType)]); // :TODO: g_VoteClient[userid] needs to be checked // A multi-argument vote is "always successful", but have to check if its a Yes/No vote. if ((strcmp(item, VOTE_YES) == 0 && FloatCompare(percent,limit) < 0 && param1 == 0) || (strcmp(item, VOTE_NO) == 0 && param1 == 1)) { - /* :TODO: g_VoteClient[userid] should be used here and set to -1 if not applicable. + /* + * :TODO: g_VoteClient[userid] should be used here and set to -1 if not applicable. */ LogAction(-1, -1, "Vote failed."); CPrintToChatAll("%t", "Vote Failed", RoundToNearest(100.0*limit), RoundToNearest(100.0*percent), totalVotes); @@ -426,7 +427,7 @@ public int Handler_NativeVoteCallback(Handle menu, MenuAction action, int param1 percent = GetVotePercent(votes, totalVotes); - limit = GetConVarFloat(g_ConVars[g_VoteType]); + limit = GetConVarFloat(g_ConVars[vote_gravity + view_as<int>(g_VoteType)]); // :TODO: g_VoteClient[userid] needs to be checked @@ -452,7 +453,6 @@ public int Handler_NativeVoteCallback(Handle menu, MenuAction action, int param1 LogAction(-1, -1, "Changing gravity to %s due to vote.", item); SetConVarInt(g_ConVars[sv_gravity], StringToInt(item)); } - case burn: { CPrintToChatAll("%t", "Set target on fire", "_s", g_VoteInfo[VOTE_NAME]); @@ -461,7 +461,6 @@ public int Handler_NativeVoteCallback(Handle menu, MenuAction action, int param1 IgniteEntity(g_VoteClient[VOTE_CLIENTID], 19.8); } - case slay: { CPrintToChatAll("%t", "Slayed player", g_VoteInfo[VOTE_NAME]); @@ -471,7 +470,6 @@ public int Handler_NativeVoteCallback(Handle menu, MenuAction action, int param1 ExtinguishEntity(g_VoteClient[VOTE_CLIENTID]); ForcePlayerSuicide(g_VoteClient[VOTE_CLIENTID]); } - case alltalk: { CPrintToChatAll("%t", "Cvar changed", "sv_alltalk", (GetConVarBool(g_ConVars[sv_alltalk]) ? "0" : "1")); @@ -479,7 +477,6 @@ public int Handler_NativeVoteCallback(Handle menu, MenuAction action, int param1 LogAction(-1, -1, "Changing alltalk to %s due to vote.", (GetConVarBool(g_ConVars[sv_alltalk]) ? "0" : "1")); SetConVarBool(g_ConVars[sv_alltalk], !GetConVarBool(g_ConVars[sv_alltalk])); } - case ff: { CPrintToChatAll("%t", "Cvar changed", "mp_friendlyfire", (GetConVarBool(g_ConVars[mp_friendlyfire]) ? "0" : "1")); From 880952393eb5d79f13232fa467177075be256ca0 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Fri, 13 Mar 2026 13:33:12 +0100 Subject: [PATCH 112/128] Bump version --- .../scripting/include/nativevotes.inc | 10 ++-- addons/sourcemod/scripting/nativevotes.sp | 22 +++++--- .../sourcemod/scripting/nativevotes/game.sp | 52 ++++++++++--------- .../scripting/nativevotes_basecommands.sp | 2 +- .../scripting/nativevotes_basevotes.sp | 4 +- .../scripting/nativevotes_funvotes.sp | 2 +- .../scripting/nativevotes_mapchooser.sp | 2 +- .../scripting/nativevotes_nominations.sp | 2 +- .../scripting/nativevotes_rockthevote.sp | 2 +- .../sourcemod/scripting/nativevotes_voterp.sp | 4 +- .../scripting/nativevotes_votescramble.sp | 2 +- 11 files changed, 55 insertions(+), 49 deletions(-) diff --git a/addons/sourcemod/scripting/include/nativevotes.inc b/addons/sourcemod/scripting/include/nativevotes.inc index 882cb63..65be07b 100644 --- a/addons/sourcemod/scripting/include/nativevotes.inc +++ b/addons/sourcemod/scripting/include/nativevotes.inc @@ -244,7 +244,7 @@ enum NativeVotesOverride NativeVotesOverride_ChgCivilian, NativeVotesOverride_ChgGamemode, NativeVotesOverride_ChgDifficulty, - NativeVotesOverride_Count, + NativeVotesOverride_Count } methodmap NativeVote < KeyValues @@ -527,11 +527,11 @@ native bool NativeVotes_IsVoteTypeSupported(NativeVotesType voteType); /** * Creates a new, empty vote. * - * @param handler Function which will receive vote actions. + * @param handler Function which will receive vote actions. * @param voteType Vote type, cannot be changed after set - * @param actions Optionally set which actions to receive. VoteStart, + * @param actions Optionally set which actions to receive. VoteStart, * VoteCancel, VoteEnd, and End will always be received - * regardless of whether they are set or not. They are + * regardless of whether they are set or not. They are * also the only default actions. * @return A new vote Handle on INVALID_HANDLE if a vote type is unsupported by this game. */ @@ -1587,4 +1587,4 @@ public void __pl_nativevotes_SetNTVOptional() MarkNativeAsOptional("NativeVotes_RedrawVoteTitle"); MarkNativeAsOptional("NativeVotes_RedrawVoteItem"); } -#endif +#endif \ No newline at end of file diff --git a/addons/sourcemod/scripting/nativevotes.sp b/addons/sourcemod/scripting/nativevotes.sp index edd4f49..19f9ec4 100644 --- a/addons/sourcemod/scripting/nativevotes.sp +++ b/addons/sourcemod/scripting/nativevotes.sp @@ -152,7 +152,7 @@ public Plugin myinfo = name = "NativeVotes", author = "Powerlord", description = "Voting API to use the game's native vote panels. Compatible with L4D, L4D2, TF2, and CS:GO.", - version = "26w08a", + version = "26w11a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" } @@ -219,7 +219,7 @@ public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max CreateNative("NativeVotes_DisplayCallVoteFail", Native_DisplayCallVoteFail); CreateNative("NativeVotes_RedrawVoteTitle", Native_RedrawVoteTitle); CreateNative("NativeVotes_RedrawVoteItem", Native_RedrawVoteItem); - + // Transitional syntax support CreateNative("NativeVote.NativeVote", Native_Create); CreateNative("NativeVote.Close", Native_Close); @@ -477,7 +477,7 @@ public Action Command_CallVote(int client, const char[] command, int argc) } } } - + if (overridesPresent) { PerformVisChecks(client, hVoteTypes); @@ -1511,8 +1511,14 @@ void Internal_Reset(bool cancel=false) delete g_hDisplayTimer; } + Game_ResetVote(null); + if (!cancel) { + if (g_hVoteTimer != null) + { + delete g_hVoteTimer; + } g_hVoteTimer = CreateTimer(5.0, Game_ResetVote, TIMER_FLAG_NO_MAPCHANGE); } } @@ -2546,7 +2552,7 @@ public int Native_RegisterVoteCommand(Handle plugin, int numParams) if (callVoteHandler == INVALID_FUNCTION) { ThrowNativeError(SP_ERROR_NATIVE, "CallVoteHandler function was invalid"); - return; + return 0; } AddToForward(g_CallVotes[overrideType].CallVote_Forward, plugin, callVoteHandler); @@ -2573,7 +2579,7 @@ public int Native_UnregisterVoteCommand(Handle plugin, int numParams) if (callVoteHandler == INVALID_FUNCTION) { ThrowNativeError(SP_ERROR_NATIVE, "CallVoteHandler function was invalid"); - return; + return 0; } RemoveFromForward(g_CallVotes[overrideType].CallVote_Forward, plugin, callVoteHandler); @@ -2654,7 +2660,7 @@ void SendInstructorHint(int client, const char[] caption) event.SetString("hint_name", "nativevotes_progress"); event.SetString("hint_replace_key", "nativevotes_progress"); - event.SetInt("hint_target", client); + //event.SetInt("hint_target", client); event.SetInt("hint_activator_userid", GetClientUserId(client)); event.SetInt("hint_timeout", 0); char icon[64]; @@ -2668,6 +2674,7 @@ void SendInstructorHint(int client, const char[] caption) event.SetInt("hint_allow_nodraw_target", 1); event.SetInt("hint_nooffscreen", 1); event.SetInt("hint_forcecaption", 1); + event.SetBool("hint_local_player_only", true); event.FireToClient(client); } @@ -2687,5 +2694,4 @@ void EndInstructorHint(int client) } event.SetString("hint_name", "nativevotes_progress"); event.FireToClient(client); -} - +} \ No newline at end of file diff --git a/addons/sourcemod/scripting/nativevotes/game.sp b/addons/sourcemod/scripting/nativevotes/game.sp index f486012..c0bdfc5 100644 --- a/addons/sourcemod/scripting/nativevotes/game.sp +++ b/addons/sourcemod/scripting/nativevotes/game.sp @@ -2531,46 +2531,48 @@ static void TF2_DisplayVoteSetup(int client, ArrayList hVoteTypes) int count = hVoteTypes.Length; BfWrite voteSetup = UserMessageToBfWrite(StartMessageOne("VoteSetup", client, USERMSG_RELIABLE)); - + voteSetup.WriteByte(count); - + for (int i = 0; i < count; ++i) { char voteIssue[128]; CallVoteListData voteData; hVoteTypes.GetArray(i, voteData, sizeof(CallVoteListData)); + + Game_OverrideTypeToVoteString(voteData.CallVoteList_VoteType, voteIssue, sizeof(voteIssue)); - Game_OverrideTypeToVoteString(voteData.CallVoteList_VoteType, voteIssue, sizeof(voteIssue)); - - char translation[128]; - Game_OverrideTypeToTranslationString(voteData.CallVoteList_VoteType, translation, sizeof(translation)); + char translation[128]; + Game_OverrideTypeToTranslationString(voteData.CallVoteList_VoteType, translation, sizeof(translation)); - voteSetup.WriteString(voteIssue); - voteSetup.WriteString(translation); - voteSetup.WriteByte(voteData.CallVoteList_VoteEnabled); + voteSetup.WriteString(voteIssue); + voteSetup.WriteString(translation); + voteSetup.WriteByte(voteData.CallVoteList_VoteEnabled); } - + EndMessage(); } static void TF2CSGO_ResetVote() { - if (CheckVoteController()) - { - if (g_EngineVersion == Engine_TF2) - { - SetEntProp(g_VoteController, Prop_Send, "m_nVoteIdx", -1); - SetEntProp(g_VoteController, Prop_Send, "m_iActiveIssueIndex", INVALID_ISSUE); - } - SetEntProp(g_VoteController, Prop_Send, "m_iOnlyTeamToVote", g_EngineVersion == Engine_TF2 ? NATIVEVOTES_TF2_ALL_TEAMS : NATIVEVOTES_ALL_TEAMS); - for (int i = 0; i < TF2CSGO_COUNT; i++) - { - SetEntProp(g_VoteController, Prop_Send, "m_nVoteOptionCount", 0, _, i); - } - SetEntProp(g_VoteController, Prop_Send, "m_nPotentialVotes", 0); - SetEntProp(g_VoteController, Prop_Send, "m_bIsYesNoVote", true); - } + if (CheckVoteController()) + { + if (g_EngineVersion == Engine_TF2) + { + SetEntProp(g_VoteController, Prop_Send, "m_nVoteIdx", -1); + SetEntProp(g_VoteController, Prop_Send, "m_iActiveIssueIndex", INVALID_ISSUE); + + ChangeEdictState(g_VoteController, GetEntSendPropOffs(g_VoteController, "m_nVoteIdx")); + } + SetEntProp(g_VoteController, Prop_Send, "m_iOnlyTeamToVote", g_EngineVersion == Engine_TF2 ? NATIVEVOTES_TF2_ALL_TEAMS : NATIVEVOTES_ALL_TEAMS); + for (int i = 0; i < TF2CSGO_COUNT; i++) + { + SetEntProp(g_VoteController, Prop_Send, "m_nVoteOptionCount", 0, _, i); + } + SetEntProp(g_VoteController, Prop_Send, "m_nPotentialVotes", 0); + SetEntProp(g_VoteController, Prop_Send, "m_bIsYesNoVote", true); + } } static bool TF2CSGO_IsVoteInProgress() diff --git a/addons/sourcemod/scripting/nativevotes_basecommands.sp b/addons/sourcemod/scripting/nativevotes_basecommands.sp index ca90b93..d00fddd 100644 --- a/addons/sourcemod/scripting/nativevotes_basecommands.sp +++ b/addons/sourcemod/scripting/nativevotes_basecommands.sp @@ -50,7 +50,7 @@ public Plugin myinfo = name = "NativeVotes | Basic Commands", author = "Powerlord and AlliedModders LLC", description = "Revote and Cancel support for NativeVotes", - version = "26w08a", + version = "26w11a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" } diff --git a/addons/sourcemod/scripting/nativevotes_basevotes.sp b/addons/sourcemod/scripting/nativevotes_basevotes.sp index 1711704..d4c08cf 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes.sp @@ -50,7 +50,7 @@ public Plugin myinfo = name = "NativeVotes | Basic Votes", author = "Powerlord and AlliedModders LLC", description = "NativeVotes Basic Vote Commands", - version = "26w08a", + version = "26w11a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -220,8 +220,6 @@ public void OnAllPluginsLoaded() { LogMessage("Moving basevotes to disabled."); } - - //SetFailState("This plugin replaces basevotes. You cannot run both at once."); } /* Account for late loading */ diff --git a/addons/sourcemod/scripting/nativevotes_funvotes.sp b/addons/sourcemod/scripting/nativevotes_funvotes.sp index bf3b7fb..26c95cb 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes.sp @@ -49,7 +49,7 @@ public Plugin myinfo = name = "NativeVotes | Fun Votes", author = "Powerlord and AlliedModders LLC", description = "NativeVotes Fun Vote Commands", - version = "26w08a", + version = "26w11a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_mapchooser.sp b/addons/sourcemod/scripting/nativevotes_mapchooser.sp index 11da09e..fac67dc 100644 --- a/addons/sourcemod/scripting/nativevotes_mapchooser.sp +++ b/addons/sourcemod/scripting/nativevotes_mapchooser.sp @@ -58,7 +58,7 @@ public Plugin myinfo = name = "NativeVotes | MapChooser", author = "AlliedModders LLC and Powerlord", description = "Automated Map Voting", - version = "26w08a", + version = "26w11a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_nominations.sp b/addons/sourcemod/scripting/nativevotes_nominations.sp index 42c68e2..1119ffb 100644 --- a/addons/sourcemod/scripting/nativevotes_nominations.sp +++ b/addons/sourcemod/scripting/nativevotes_nominations.sp @@ -50,7 +50,7 @@ public Plugin myinfo = name = "NativeVotes | Map Nominations", author = "AlliedModders LLC and Powerlord", description = "Provides Map Nominations", - version = "26w08a", + version = "26w11a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_rockthevote.sp b/addons/sourcemod/scripting/nativevotes_rockthevote.sp index d832774..a350e0c 100644 --- a/addons/sourcemod/scripting/nativevotes_rockthevote.sp +++ b/addons/sourcemod/scripting/nativevotes_rockthevote.sp @@ -57,7 +57,7 @@ public Plugin myinfo = name = "NativeVotes | Rock The Vote", author = "AlliedModders LLC and Powerlord", description = "Provides RTV Map Voting", - version = "26w08a", + version = "26w11a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_voterp.sp b/addons/sourcemod/scripting/nativevotes_voterp.sp index 03082e5..0075447 100644 --- a/addons/sourcemod/scripting/nativevotes_voterp.sp +++ b/addons/sourcemod/scripting/nativevotes_voterp.sp @@ -16,7 +16,7 @@ public Plugin myinfo = name = "NativeVotes | Medieval Auto-RP", author = "Heapons", description = "Provides Medieval Auto-RP voting.", - version = "26w08a", + version = "26w11a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -146,7 +146,7 @@ int VoteHandler(NativeVote vote, MenuAction action, int param1, int param2) NativeVotes_GetItem(vote, item, info, sizeof(info)); int value = StringToInt(info); g_ConVars[tf_medieval_autorp].SetInt(value); - NativeVotes_DisplayPass(vote, "Set 'tf_medieval_autorp' to %s...", value ? "true" : "false"); + NativeVotes_DisplayPass(vote, "Setting 'tf_medieval_autorp' to %s...", value ? "true" : "false"); } case MenuAction_End: { diff --git a/addons/sourcemod/scripting/nativevotes_votescramble.sp b/addons/sourcemod/scripting/nativevotes_votescramble.sp index 0fbf4c7..6ec0564 100644 --- a/addons/sourcemod/scripting/nativevotes_votescramble.sp +++ b/addons/sourcemod/scripting/nativevotes_votescramble.sp @@ -13,7 +13,7 @@ public Plugin myinfo = name = "NativeVotes | Scramble Teams", author = "Heapons", description = "Provides RTV-style Scramble Teams Voting", - version = "26w08a", + version = "26w11a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; From 42d2740efc4183431190ae1127a92947439af52f Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Mon, 23 Mar 2026 14:24:00 +0100 Subject: [PATCH 113/128] Update nv_compile.sh Forgot to include nativevotes_votescramble.sp, whoops --- .github/nv_compile.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/nv_compile.sh b/.github/nv_compile.sh index 256c512..01ae2cd 100755 --- a/.github/nv_compile.sh +++ b/.github/nv_compile.sh @@ -13,6 +13,7 @@ nv_plugs+=("nativevotes_mapchooser") nv_plugs+=("nativevotes_nominations") nv_plugs+=("nativevotes_rockthevote") nv_plugs+=("nativevotes_voterp") +nv_plugs+=("nativevotes_votescramble") # we start in git root pushd ./addons/sourcemod/scripting @@ -29,4 +30,3 @@ mkdir build || true 7za a -r build/nativevotes_sm_"${versvers}".zip scripting/ translations/ plugins/ rm plugins/ -rfv ls -la - From 20062a7b802f0b929572e5321e0819b93352f2ea Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sat, 28 Mar 2026 17:44:07 +0100 Subject: [PATCH 114/128] Remove #pragma headers --- addons/sourcemod/scripting/nativevotes.sp | 3 --- addons/sourcemod/scripting/nativevotes_basecommands.sp | 3 --- addons/sourcemod/scripting/nativevotes_basevotes.sp | 2 -- addons/sourcemod/scripting/nativevotes_funvotes.sp | 2 -- addons/sourcemod/scripting/nativevotes_mapchooser.sp | 3 --- addons/sourcemod/scripting/nativevotes_nominations.sp | 3 --- addons/sourcemod/scripting/nativevotes_rockthevote.sp | 3 --- addons/sourcemod/scripting/nativevotes_voterp.sp | 3 --- 8 files changed, 22 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes.sp b/addons/sourcemod/scripting/nativevotes.sp index 19f9ec4..a32aedc 100644 --- a/addons/sourcemod/scripting/nativevotes.sp +++ b/addons/sourcemod/scripting/nativevotes.sp @@ -36,9 +36,6 @@ #include <sourcemod> #include <sdktools> -#pragma semicolon 1 -#pragma newdecls required - #include <nativevotes> #include <implodeexplode> diff --git a/addons/sourcemod/scripting/nativevotes_basecommands.sp b/addons/sourcemod/scripting/nativevotes_basecommands.sp index d00fddd..454efb9 100644 --- a/addons/sourcemod/scripting/nativevotes_basecommands.sp +++ b/addons/sourcemod/scripting/nativevotes_basecommands.sp @@ -38,9 +38,6 @@ #undef REQUIRE_PLUGIN #include <adminmenu> -#pragma newdecls required -#pragma semicolon 1 - #define PLUGIN_PREFIX "[\x04NativeVotes\x01]" TopMenu g_TopMenu; diff --git a/addons/sourcemod/scripting/nativevotes_basevotes.sp b/addons/sourcemod/scripting/nativevotes_basevotes.sp index d4c08cf..8fd2d9b 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes.sp @@ -33,8 +33,6 @@ * Version: $Id$ */ -#pragma semicolon 1 - #include <sourcemod> #include <sdktools> diff --git a/addons/sourcemod/scripting/nativevotes_funvotes.sp b/addons/sourcemod/scripting/nativevotes_funvotes.sp index 26c95cb..cc8d721 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes.sp @@ -33,8 +33,6 @@ * Version: $Id$ */ -#pragma semicolon 1 - #include <sourcemod> #include <sdktools> diff --git a/addons/sourcemod/scripting/nativevotes_mapchooser.sp b/addons/sourcemod/scripting/nativevotes_mapchooser.sp index fac67dc..780acae 100644 --- a/addons/sourcemod/scripting/nativevotes_mapchooser.sp +++ b/addons/sourcemod/scripting/nativevotes_mapchooser.sp @@ -48,9 +48,6 @@ #include <ripext> #define REQUIRE_EXTENSIONS -#pragma semicolon 1 -#pragma newdecls required - #define PLUGIN_PREFIX "[\x04MapChooser\x01]" public Plugin myinfo = diff --git a/addons/sourcemod/scripting/nativevotes_nominations.sp b/addons/sourcemod/scripting/nativevotes_nominations.sp index 1119ffb..48e24c5 100644 --- a/addons/sourcemod/scripting/nativevotes_nominations.sp +++ b/addons/sourcemod/scripting/nativevotes_nominations.sp @@ -40,9 +40,6 @@ #include <nativevotes> #define REQUIRE_PLUGIN -#pragma semicolon 1 -#pragma newdecls required - #define PLUGIN_PREFIX "[\x04Nominations\x01]" public Plugin myinfo = diff --git a/addons/sourcemod/scripting/nativevotes_rockthevote.sp b/addons/sourcemod/scripting/nativevotes_rockthevote.sp index a350e0c..a74dfb1 100644 --- a/addons/sourcemod/scripting/nativevotes_rockthevote.sp +++ b/addons/sourcemod/scripting/nativevotes_rockthevote.sp @@ -47,9 +47,6 @@ #include <tf2> #define REQUIRE_EXTENSIONS -#pragma semicolon 1 -#pragma newdecls required - #define PLUGIN_PREFIX "[\x04Rock The Vote\x01]" public Plugin myinfo = diff --git a/addons/sourcemod/scripting/nativevotes_voterp.sp b/addons/sourcemod/scripting/nativevotes_voterp.sp index 0075447..8e6f470 100644 --- a/addons/sourcemod/scripting/nativevotes_voterp.sp +++ b/addons/sourcemod/scripting/nativevotes_voterp.sp @@ -6,9 +6,6 @@ #include <nativevotes> #define REQUIRE_PLUGIN -#pragma semicolon 1 -#pragma newdecls required - #define PLUGIN_PREFIX "[\x04NativeVotes\x01]" public Plugin myinfo = From 4a82eb2bdd98e7d815c5ddd6daf9b94645f79287 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sat, 28 Mar 2026 17:44:42 +0100 Subject: [PATCH 115/128] Use MaxClients instead of MAXPLAYERS --- addons/sourcemod/scripting/nativevotes_rockthevote.sp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/sourcemod/scripting/nativevotes_rockthevote.sp b/addons/sourcemod/scripting/nativevotes_rockthevote.sp index a74dfb1..15aad9f 100644 --- a/addons/sourcemod/scripting/nativevotes_rockthevote.sp +++ b/addons/sourcemod/scripting/nativevotes_rockthevote.sp @@ -441,7 +441,7 @@ void ResetRTV() { g_Votes = 0; - for (int i = 1; i <= MAXPLAYERS; i++) + for (int i = 1; i <= MaxClients; i++) { g_Voted[i] = false; } From 6612bb2bcd681aeec456c6ca7e4b4d0ca0e6f740 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sun, 29 Mar 2026 12:09:07 +0200 Subject: [PATCH 116/128] Update MapChooser to match official one --- .../scripting/nativevotes_mapchooser.sp | 174 ++++++++++++------ 1 file changed, 119 insertions(+), 55 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_mapchooser.sp b/addons/sourcemod/scripting/nativevotes_mapchooser.sp index 780acae..95b3eb3 100644 --- a/addons/sourcemod/scripting/nativevotes_mapchooser.sp +++ b/addons/sourcemod/scripting/nativevotes_mapchooser.sp @@ -55,7 +55,7 @@ public Plugin myinfo = name = "NativeVotes | MapChooser", author = "AlliedModders LLC and Powerlord", description = "Automated Map Voting", - version = "26w11a", + version = "26w13a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -79,6 +79,7 @@ enum extendmap_fragstep, mapvote_exclude, mapvote_include, + mapvote_persistentmaps, mapvote_novote, mapvote_extend, mapvote_dontchange, @@ -111,6 +112,7 @@ Menu g_VoteMenu; NativeVote g_VoteNative; int g_Extends; +int g_TotalRounds; bool g_HasVoteStarted; bool g_WaitingForVote; bool g_MapVoteCompleted; @@ -121,8 +123,8 @@ int g_AppID; MapChange g_ChangeTime; -Handle g_NominationsResetForward = null; -Handle g_MapVoteStartedForward = null; +GlobalForward g_NominationsResetForward; +GlobalForward g_MapVoteStartedForward; /* Upper bound of how many team there could be */ #define MAX_TEAMS 10 @@ -141,20 +143,7 @@ public void OnPluginStart() LoadTranslations("common.phrases"); KeyValues kv = new KeyValues("GameInfo"); - kv.ImportFromFile("gameinfo.txt"); - - char gameDir[128]; - GetGameFolderName(gameDir, sizeof(gameDir)); - - EngineVersion engine = GetEngineVersion(); - if (!StrEqual(gameDir, "tf") && - (kv.GetNum("DependsOnAppID") == 440 || - (engine == Engine_SDK2013 && FileExists("resource/tf.ttf")))) - { - engine = Engine_TF2; - } - - if (kv.JumpToKey("FileSystem")) + if (kv.ImportFromFile("gameinfo.txt") && kv.JumpToKey("FileSystem")) { g_AppID = kv.GetNum("SteamAppId"); } @@ -177,6 +166,7 @@ public void OnPluginStart() g_ConVars[extendmap_fragstep] = CreateConVar("sm_extendmap_fragstep", "10", "Specifies how many more frags are allowed when map is extended.", _, true, 5.0); g_ConVars[mapvote_exclude] = CreateConVar("sm_mapvote_exclude", "5", "Specifies how many past maps to exclude from the vote.", _, true, 0.0); g_ConVars[mapvote_include] = CreateConVar("sm_mapvote_include", "5", "Specifies how many maps to include in the vote.", _, true, 2.0, true, 6.0); + g_ConVars[mapvote_persistentmaps] = CreateConVar("sm_mapvote_persistentmaps", "0", "Specifies if previous maps should be stored persistently.", _, true, 0.0, true, 1.0); g_ConVars[mapvote_novote] = CreateConVar("sm_mapvote_novote", "1", "Specifies whether or not MapChooser should pick a map if no votes are received.", _, true, 0.0, true, 1.0); g_ConVars[mapvote_extend] = CreateConVar("sm_mapvote_extend", "0", "Number of extensions allowed each map.", _, true, 0.0); g_ConVars[mapvote_dontchange] = CreateConVar("sm_mapvote_dontchange", "1", "Specifies if a 'Don't Change' option should be added to early votes.", _, true, 0.0, true, 1.0); @@ -204,21 +194,22 @@ public void OnPluginStart() if (g_ConVars[mp_winlimit] || g_ConVars[mp_maxrounds]) { - switch (engine) + if (HookEventEx("teamplay_win_panel", Event_TeamPlayWinPanel)) { - case Engine_TF2: - { - HookEvent("teamplay_win_panel", Event_TeamplayWinPanel); - HookEvent("arena_win_panel", Event_TeamplayWinPanel); - } - case Engine_NuclearDawn: - { - HookEvent("round_win", Event_RoundEnd); - } - default: - { - HookEvent("round_end", Event_RoundEnd); - } + HookEvent("teamplay_restart_round", Event_TFRestartRound); + HookEvent("arena_win_panel", Event_TeamPlayWinPanel); + } + else if (strcmp(folder, "nucleardawn") == 0) + { + HookEvent("round_win", Event_RoundEnd); + } + else if (strcmp(folder, "empires") == 0) + { + HookEvent("game_end", Event_RoundEnd); + } + else + { + HookEvent("round_end", Event_RoundEnd); } } @@ -330,9 +321,22 @@ public void OnConfigsExecuted() LogError("Unable to create a valid map list."); } } + /* First-load previous maps from a text file when persistency is enabled. */ + static bool g_FirstConfigExec = true; + if (g_FirstConfigExec) + { + if (g_ConVars[mapvote_persistentmaps].BoolValue) + { + ReadPreviousMapsFromText(); + } + + g_FirstConfigExec = false; + } CreateNextVote(); SetupTimeleftTimer(); + + g_TotalRounds = 0; g_Extends = 0; @@ -351,7 +355,7 @@ public void OnConfigsExecuted() { if (g_ConVars[mp_bonusroundtime].FloatValue <= g_ConVars[mapvote_voteduration].FloatValue) { - LogMessage("Warning - Bonus Round Time shorter than Vote Time. Votes during bonus round may not have time to complete"); + LogError("Warning - Bonus Round Time shorter than Vote Time. Votes during bonus round may not have time to complete"); } } } @@ -368,12 +372,18 @@ public void OnMapEnd() char map[PLATFORM_MAX_PATH]; GetCurrentMap(map, sizeof(map)); + RemoveStringFromArray(g_OldMapList, map); g_OldMapList.PushString(map); while (g_OldMapList.Length > g_ConVars[mapvote_exclude].IntValue) { g_OldMapList.Erase(0); } + + if (g_ConVars[mapvote_persistentmaps].BoolValue) + { + WritePreviousMapsToText(); + } } public void OnClientDisconnect(int client) @@ -417,10 +427,7 @@ public Action Command_SetNextMap(int client, int args) Format(displayName, sizeof(displayName), "\x05%s\x01", displayName); CShowActivity2(client, PLUGIN_PREFIX ... " ", "%t", "Changed Next Map", displayName); - if (client > 0) - { - LogAction(client, -1, "\"%L\" changed nextmap to \"%s\"", client, map); - } + LogAction(client, -1, "\"%L\" changed nextmap to \"%s\"", client, map); SetNextMap(map); g_MapVoteCompleted = true; @@ -489,6 +496,12 @@ public Action Timer_StartMapVote(Handle timer, DataPack data) return Plugin_Stop; } +public void Event_TFRestartRound(Event event, const char[] name, bool dontBroadcast) +{ + /* Game got restarted - reset our round count tracking */ + g_TotalRounds = 0; +} + public void Event_TeamplayWinPanel(Event event, const char[] name, bool dontBroadcast) { if (g_ChangeMapAtRoundEnd) @@ -505,7 +518,7 @@ public void Event_TeamplayWinPanel(Event event, const char[] name, bool dontBroa return; } - CheckMaxRounds(); + CheckMaxRounds(g_TotalRounds); int winning_team = event.GetInt("winning_team"); if (winning_team > 1 && winning_team < MAX_TEAMS) @@ -545,6 +558,8 @@ public void Event_RoundEnd(Event event, const char[] name, bool dontBroadcast) { SetFailState("Mod exceed maximum team count - Please file a bug report."); } + + g_TotalRounds++; g_winCount[winner]++; @@ -554,7 +569,7 @@ public void Event_RoundEnd(Event event, const char[] name, bool dontBroadcast) } CheckWinLimit(g_winCount[winner]); - CheckMaxRounds(); + CheckMaxRounds(g_TotalRounds); } public void CheckWinLimit(int winner_score) @@ -572,11 +587,10 @@ public void CheckWinLimit(int winner_score) } } -public void CheckMaxRounds() +public void CheckMaxRounds(int roundcount) { if (g_ConVars[mp_maxrounds]) { - int roundcount = GameRules_GetProp("m_nRoundsPlayed"); int maxrounds = g_ConVars[mp_maxrounds].IntValue; if (maxrounds) { @@ -647,12 +661,12 @@ void InitiateVote(MapChange when, ArrayList inputlist=null) { g_WaitingForVote = true; - if ((g_NativeVotes && NativeVotes_IsVoteInProgress()) || (!g_NativeVotes && IsVoteInProgress())) - //if (IsVoteInProgress()) + bool isVoteInProgress = g_NativeVotes ? NativeVotes_IsVoteInProgress() : IsVoteInProgress(); + if (isVoteInProgress) { // Can't start a vote, try again in 5 seconds. - //g_RetryTimer = CreateTimer(5.0, Timer_StartMapVote, _, TIMER_FLAG_NO_MAPCHANGE); - + // g_RetryTimer = CreateTimer(5.0, Timer_StartMapVote, _, TIMER_FLAG_NO_MAPCHANGE); + DataPack data; g_RetryTimer = CreateDataTimer(5.0, Timer_StartMapVote, data, TIMER_FLAG_NO_MAPCHANGE); data.WriteCell(when); @@ -660,8 +674,6 @@ void InitiateVote(MapChange when, ArrayList inputlist=null) data.Reset(); return; } - - g_WaitingForVote = false; /* If the main map vote has completed (and chosen result) and its currently changing (not a delayed change) we block further attempts */ if (g_MapVoteCompleted && g_ChangeMapInProgress) @@ -670,6 +682,8 @@ void InitiateVote(MapChange when, ArrayList inputlist=null) } g_ChangeTime = when; + + g_WaitingForVote = false; g_HasVoteStarted = true; if (g_NativeVotes) @@ -1004,9 +1018,9 @@ public void Handler_VoteFinishedGenericShared(const char[] map, const char[] dis g_VoteNative.DisplayPass(displayName); } - char formattedName[PLATFORM_MAX_PATH]; - Format(formattedName, sizeof(formattedName), "\x05%s\x01", displayName); - CPrintToChatAll(PLUGIN_PREFIX ... " %t", "Nextmap Voting Finished", formattedName, RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes); + char buffer[PLATFORM_MAX_PATH]; + Format(buffer, sizeof(buffer), "\x05%s\x01", displayName); + CPrintToChatAll(PLUGIN_PREFIX ... " %t", "Nextmap Voting Finished", buffer, RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes); LogAction(-1, -1, "Voting for next map has finished. Nextmap: %s.", map); } } @@ -1020,7 +1034,7 @@ public void Handler_NV_MapVoteFinished(NativeVote menu, int num_votes, int num_c if (winningvotes < required) { - //Added in 1.5.1 + //Added in 1.5.1 menu.DisplayFail(NativeVotesFail_NotEnoughVotes); /* Insufficient Winning margin - Lets do a runoff */ @@ -1109,7 +1123,7 @@ public void Handler_MapVoteFinished(Menu menu, int num_votes, int num_clients, c float map1percent = float(item_info[0][VOTEINFO_ITEM_VOTES])/ float(num_votes) * 100; float map2percent = float(item_info[1][VOTEINFO_ITEM_VOTES])/ float(num_votes) * 100; - CPrintToChatAll(PLUGIN_PREFIX ... " %t", "Starting Runoff", g_ConVars[mapvote_runoffpercent].FloatValue, info1, map1percent, info2, map2percent); + CPrintToChatAll(PLUGIN_PREFIX ... " %t", "Starting Runoff", g_ConVars[mapvote_runoffpercent].FloatValue, info1, map1percent, info2, map2percent); LogMessage("Voting for next map was indecisive, beginning runoff vote"); return; @@ -1132,7 +1146,7 @@ public int Handler_MapVoteMenu(Menu menu, MenuAction action, int param1, int par case MenuAction_Display: { char buffer[255]; - Format(buffer, sizeof(buffer), "%t", "Vote Nextmap", param1); + Format(buffer, sizeof(buffer), "%T", "Vote Nextmap", param1); Panel panel = view_as<Panel>(param2); panel.SetTitle(buffer); @@ -1146,12 +1160,12 @@ public int Handler_MapVoteMenu(Menu menu, MenuAction action, int param1, int par menu.GetItem(param2, map, sizeof(map)); if (strcmp(map, VOTE_EXTEND, false) == 0) { - Format(buffer, sizeof(buffer), "%T", LANG_SERVER, "Extend Map", param1); + Format(buffer, sizeof(buffer), "%T", "Extend Map", param1); return RedrawMenuItem(buffer); } else if (strcmp(map, VOTE_DONTCHANGE, false) == 0) { - Format(buffer, sizeof(buffer), "%T", LANG_SERVER, "Dont Change", param1); + Format(buffer, sizeof(buffer), "%T", "Dont Change", param1); return RedrawMenuItem(buffer); } } @@ -1263,7 +1277,7 @@ public int Handler_NV_MapVoteMenu(NativeVote menu, MenuAction action, int param1 menu.DisplayFail(NativeVotesFail_Generic); } - //g_HasVoteStarted = false; + g_HasVoteStarted = false; } } @@ -1598,6 +1612,56 @@ public int Native_GetNominatedMapList(Handle plugin, int numParams) return 0; } +/* Add functions for persistent previous map storage */ +void ReadPreviousMapsFromText() +{ + File file = OpenFile(GetTextFilePath(), "r"); + if (file == null) + { + return; + } + + g_OldMapList.Clear(); + char map[PLATFORM_MAX_PATH]; + do + { + if (file.ReadLine(map, sizeof(map))) + { + TrimString(map); + g_OldMapList.PushString(map); + } + } + while (!file.EndOfFile()); + file.Close(); +} + +void WritePreviousMapsToText() +{ + File file = OpenFile(GetTextFilePath(), "w"); + if (file == null) + { + return; + } + + char lastMap[PLATFORM_MAX_PATH]; + for (int idx=0; idx<g_OldMapList.Length; idx++) + { + g_OldMapList.GetString(idx, lastMap, sizeof(lastMap)); + TrimString(lastMap); + file.WriteLine(lastMap); + } + file.Close(); +} + +char[] GetTextFilePath() +{ + static char path[PLATFORM_MAX_PATH]; + if (path[0] == '\0') + BuildPath(Path_SM, path, PLATFORM_MAX_PATH, "data/mapchooser_history.txt"); + return path; +} + +/* Mapcycle generator */ void PopulateMapList() { char mapcycleFile[PLATFORM_MAX_PATH]; From bb5b615e1f3f2915670eb05d488aadc76dcf0456 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sun, 29 Mar 2026 12:09:28 +0200 Subject: [PATCH 117/128] Bump version --- addons/sourcemod/scripting/nativevotes.sp | 2 +- addons/sourcemod/scripting/nativevotes_basecommands.sp | 2 +- addons/sourcemod/scripting/nativevotes_basevotes.sp | 2 +- addons/sourcemod/scripting/nativevotes_funvotes.sp | 2 +- addons/sourcemod/scripting/nativevotes_nominations.sp | 2 +- addons/sourcemod/scripting/nativevotes_rockthevote.sp | 2 +- addons/sourcemod/scripting/nativevotes_voterp.sp | 2 +- addons/sourcemod/scripting/nativevotes_votescramble.sp | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes.sp b/addons/sourcemod/scripting/nativevotes.sp index a32aedc..768bd9c 100644 --- a/addons/sourcemod/scripting/nativevotes.sp +++ b/addons/sourcemod/scripting/nativevotes.sp @@ -149,7 +149,7 @@ public Plugin myinfo = name = "NativeVotes", author = "Powerlord", description = "Voting API to use the game's native vote panels. Compatible with L4D, L4D2, TF2, and CS:GO.", - version = "26w11a", + version = "26w13a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" } diff --git a/addons/sourcemod/scripting/nativevotes_basecommands.sp b/addons/sourcemod/scripting/nativevotes_basecommands.sp index 454efb9..d2e8135 100644 --- a/addons/sourcemod/scripting/nativevotes_basecommands.sp +++ b/addons/sourcemod/scripting/nativevotes_basecommands.sp @@ -47,7 +47,7 @@ public Plugin myinfo = name = "NativeVotes | Basic Commands", author = "Powerlord and AlliedModders LLC", description = "Revote and Cancel support for NativeVotes", - version = "26w11a", + version = "26w13a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" } diff --git a/addons/sourcemod/scripting/nativevotes_basevotes.sp b/addons/sourcemod/scripting/nativevotes_basevotes.sp index 8fd2d9b..ce46f5f 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes.sp @@ -48,7 +48,7 @@ public Plugin myinfo = name = "NativeVotes | Basic Votes", author = "Powerlord and AlliedModders LLC", description = "NativeVotes Basic Vote Commands", - version = "26w11a", + version = "26w13a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_funvotes.sp b/addons/sourcemod/scripting/nativevotes_funvotes.sp index cc8d721..400d180 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes.sp @@ -47,7 +47,7 @@ public Plugin myinfo = name = "NativeVotes | Fun Votes", author = "Powerlord and AlliedModders LLC", description = "NativeVotes Fun Vote Commands", - version = "26w11a", + version = "26w13a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_nominations.sp b/addons/sourcemod/scripting/nativevotes_nominations.sp index 48e24c5..4e56321 100644 --- a/addons/sourcemod/scripting/nativevotes_nominations.sp +++ b/addons/sourcemod/scripting/nativevotes_nominations.sp @@ -47,7 +47,7 @@ public Plugin myinfo = name = "NativeVotes | Map Nominations", author = "AlliedModders LLC and Powerlord", description = "Provides Map Nominations", - version = "26w11a", + version = "26w13a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_rockthevote.sp b/addons/sourcemod/scripting/nativevotes_rockthevote.sp index 15aad9f..0660f90 100644 --- a/addons/sourcemod/scripting/nativevotes_rockthevote.sp +++ b/addons/sourcemod/scripting/nativevotes_rockthevote.sp @@ -54,7 +54,7 @@ public Plugin myinfo = name = "NativeVotes | Rock The Vote", author = "AlliedModders LLC and Powerlord", description = "Provides RTV Map Voting", - version = "26w11a", + version = "26w13a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_voterp.sp b/addons/sourcemod/scripting/nativevotes_voterp.sp index 8e6f470..c2c3b94 100644 --- a/addons/sourcemod/scripting/nativevotes_voterp.sp +++ b/addons/sourcemod/scripting/nativevotes_voterp.sp @@ -13,7 +13,7 @@ public Plugin myinfo = name = "NativeVotes | Medieval Auto-RP", author = "Heapons", description = "Provides Medieval Auto-RP voting.", - version = "26w11a", + version = "26w13a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_votescramble.sp b/addons/sourcemod/scripting/nativevotes_votescramble.sp index 6ec0564..c32539a 100644 --- a/addons/sourcemod/scripting/nativevotes_votescramble.sp +++ b/addons/sourcemod/scripting/nativevotes_votescramble.sp @@ -13,7 +13,7 @@ public Plugin myinfo = name = "NativeVotes | Scramble Teams", author = "Heapons", description = "Provides RTV-style Scramble Teams Voting", - version = "26w11a", + version = "26w13a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; From e2dffa392bf6ef5ac8dad7e125fa52e0022cce8d Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Sun, 29 Mar 2026 12:30:26 +0200 Subject: [PATCH 118/128] Fix typos --- addons/sourcemod/scripting/nativevotes_mapchooser.sp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_mapchooser.sp b/addons/sourcemod/scripting/nativevotes_mapchooser.sp index 95b3eb3..8bcf9da 100644 --- a/addons/sourcemod/scripting/nativevotes_mapchooser.sp +++ b/addons/sourcemod/scripting/nativevotes_mapchooser.sp @@ -177,6 +177,7 @@ public void OnPluginStart() g_ConVars[mapcycle_auto] = CreateConVar("sm_mapcycle_auto", "0", "Specifies whether or not to automatically populate the maps list.", _, true, 0.0, true, 1.0); g_ConVars[mapcycle_exclude] = CreateConVar("sm_mapcycle_exclude", ".*test.*|background01|^tr.*$", "Specifies which maps shouldn't be automatically added with a regex pattern."); + EngineVersion engine = GetEngineVersion(); if (engine != Engine_SDK2013 && engine == Engine_TF2) { g_ConVars[workshop_map_collection] = CreateConVar("sm_workshop_map_collection", "", "Specifies the workshop collection to fetch the maps from."); @@ -194,10 +195,13 @@ public void OnPluginStart() if (g_ConVars[mp_winlimit] || g_ConVars[mp_maxrounds]) { - if (HookEventEx("teamplay_win_panel", Event_TeamPlayWinPanel)) + char folder[64]; + GetGameFolderName(folder, sizeof(folder)); + + if (HookEventEx("teamplay_win_panel", Event_TeamplayWinPanel)) { HookEvent("teamplay_restart_round", Event_TFRestartRound); - HookEvent("arena_win_panel", Event_TeamPlayWinPanel); + HookEvent("arena_win_panel", Event_TeamplayWinPanel); } else if (strcmp(folder, "nucleardawn") == 0) { From 00c53376e5556283f6568ebb5afe303112a11fc9 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Mon, 30 Mar 2026 16:10:24 +0200 Subject: [PATCH 119/128] Prevent map vote if the server is empty Potentially fixes "RTV has already started" false positives. --- .../scripting/nativevotes_mapchooser.sp | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_mapchooser.sp b/addons/sourcemod/scripting/nativevotes_mapchooser.sp index 8bcf9da..7e0b88c 100644 --- a/addons/sourcemod/scripting/nativevotes_mapchooser.sp +++ b/addons/sourcemod/scripting/nativevotes_mapchooser.sp @@ -55,7 +55,7 @@ public Plugin myinfo = name = "NativeVotes | MapChooser", author = "AlliedModders LLC and Powerlord", description = "Automated Map Voting", - version = "26w13a", + version = "26w14a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -486,7 +486,24 @@ public Action Timer_StartMapVote(Handle timer, DataPack data) { g_VoteTimer = null; } - + + // Prevent map vote if the server is empty + int playerCount = 0; + for (int i = 1; i <= MaxClients; i++) + { + if (!IsClientInGame(i)) + continue; + + if (IsFakeClient(i)) + continue; + + playerCount++; + } + if (playerCount == 0) + { + return Plugin_Stop; + } + if (!g_MapList.Length || !g_ConVars[mapvote_endvote].BoolValue || g_MapVoteCompleted || g_HasVoteStarted) { return Plugin_Stop; From 8f15fa725230fb48b5fbd57a113a550f9760364a Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Mon, 30 Mar 2026 16:16:07 +0200 Subject: [PATCH 120/128] Compile to 1.13.7301 https://github.com/Heapons/sourcemod-nativevotes-updated/issues/24 --- .github/workflows/nativevotes.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/nativevotes.yml b/.github/workflows/nativevotes.yml index 94ee54e..ea49ebc 100644 --- a/.github/workflows/nativevotes.yml +++ b/.github/workflows/nativevotes.yml @@ -45,7 +45,7 @@ jobs: - name: Setup SourcePawn 1.13 Compiler uses: rumblefrog/setup-sp@master with: - version: "1.13.x" + version: "1.13.7301" - name: Compile plugins 1.13 shell: bash From b29d15aa0e135909c839ba2ef57b0d6f7c5ba582 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Wed, 8 Apr 2026 18:45:22 +0200 Subject: [PATCH 121/128] Fix formatting --- addons/sourcemod/scripting/nativevotes_mapchooser.sp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_mapchooser.sp b/addons/sourcemod/scripting/nativevotes_mapchooser.sp index 7e0b88c..3f8a059 100644 --- a/addons/sourcemod/scripting/nativevotes_mapchooser.sp +++ b/addons/sourcemod/scripting/nativevotes_mapchooser.sp @@ -55,7 +55,7 @@ public Plugin myinfo = name = "NativeVotes | MapChooser", author = "AlliedModders LLC and Powerlord", description = "Automated Map Voting", - version = "26w14a", + version = "26w15a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; @@ -1083,7 +1083,7 @@ public void Handler_NV_MapVoteFinished(NativeVote menu, int num_votes, int num_c float map1percent = float(item_votes[0])/ float(num_votes) * 100; float map2percent = float(item_votes[1])/ float(num_votes) * 100; - CPrintToChatAll(PLUGIN_PREFIX ... " %t", "Starting Runoff", g_ConVars[mapvote_runoffpercent].FloatValue, info1, map1percent, info2, map2percent); + CPrintToChatAll(PLUGIN_PREFIX ... " %t", "Starting Runoff", g_ConVars[mapvote_runoffpercent].FloatValue, info1, map1percent, info2, map2percent); LogMessage("Voting for next map was indecisive, beginning runoff vote"); return; @@ -1247,12 +1247,12 @@ public int Handler_NV_MapVoteMenu(NativeVote menu, MenuAction action, int param1 menu.GetItem(param2, map, sizeof(map)); if (strcmp(map, VOTE_EXTEND, false) == 0) { - Format(buffer, sizeof(buffer), "%T", LANG_SERVER, "Extend Map", param1); + Format(buffer, sizeof(buffer), "%T", "Extend Map", param1); return view_as<int>(NativeVotes_RedrawVoteItem(buffer)); } else if (strcmp(map, VOTE_DONTCHANGE, false) == 0) { - Format(buffer, sizeof(buffer), "%T", LANG_SERVER, "Dont Change", param1); + Format(buffer, sizeof(buffer), "%T", "Dont Change", param1); return view_as<int>(NativeVotes_RedrawVoteItem(buffer)); } } From bde7967a71fb3af4ee8b733f0346849a6c9fabf3 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Wed, 8 Apr 2026 18:45:52 +0200 Subject: [PATCH 122/128] Bump version --- .gitignore | 2 +- addons/sourcemod/scripting/nativevotes.sp | 2 +- addons/sourcemod/scripting/nativevotes_basecommands.sp | 2 +- addons/sourcemod/scripting/nativevotes_basevotes.sp | 2 +- addons/sourcemod/scripting/nativevotes_funvotes.sp | 2 +- addons/sourcemod/scripting/nativevotes_nominations.sp | 2 +- addons/sourcemod/scripting/nativevotes_rockthevote.sp | 2 +- addons/sourcemod/scripting/nativevotes_voterp.sp | 2 +- addons/sourcemod/scripting/nativevotes_votescramble.sp | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index 9904c40..d1892d4 100644 --- a/.gitignore +++ b/.gitignore @@ -81,4 +81,4 @@ virtual_address.inc steam* ripext* multicolors* -source* \ No newline at end of file +source*.inc \ No newline at end of file diff --git a/addons/sourcemod/scripting/nativevotes.sp b/addons/sourcemod/scripting/nativevotes.sp index 768bd9c..d96324d 100644 --- a/addons/sourcemod/scripting/nativevotes.sp +++ b/addons/sourcemod/scripting/nativevotes.sp @@ -149,7 +149,7 @@ public Plugin myinfo = name = "NativeVotes", author = "Powerlord", description = "Voting API to use the game's native vote panels. Compatible with L4D, L4D2, TF2, and CS:GO.", - version = "26w13a", + version = "26w15a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" } diff --git a/addons/sourcemod/scripting/nativevotes_basecommands.sp b/addons/sourcemod/scripting/nativevotes_basecommands.sp index d2e8135..aff94d0 100644 --- a/addons/sourcemod/scripting/nativevotes_basecommands.sp +++ b/addons/sourcemod/scripting/nativevotes_basecommands.sp @@ -47,7 +47,7 @@ public Plugin myinfo = name = "NativeVotes | Basic Commands", author = "Powerlord and AlliedModders LLC", description = "Revote and Cancel support for NativeVotes", - version = "26w13a", + version = "26w15a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" } diff --git a/addons/sourcemod/scripting/nativevotes_basevotes.sp b/addons/sourcemod/scripting/nativevotes_basevotes.sp index ce46f5f..32ce422 100644 --- a/addons/sourcemod/scripting/nativevotes_basevotes.sp +++ b/addons/sourcemod/scripting/nativevotes_basevotes.sp @@ -48,7 +48,7 @@ public Plugin myinfo = name = "NativeVotes | Basic Votes", author = "Powerlord and AlliedModders LLC", description = "NativeVotes Basic Vote Commands", - version = "26w13a", + version = "26w15a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_funvotes.sp b/addons/sourcemod/scripting/nativevotes_funvotes.sp index 400d180..f7dde42 100644 --- a/addons/sourcemod/scripting/nativevotes_funvotes.sp +++ b/addons/sourcemod/scripting/nativevotes_funvotes.sp @@ -47,7 +47,7 @@ public Plugin myinfo = name = "NativeVotes | Fun Votes", author = "Powerlord and AlliedModders LLC", description = "NativeVotes Fun Vote Commands", - version = "26w13a", + version = "26w15a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_nominations.sp b/addons/sourcemod/scripting/nativevotes_nominations.sp index 4e56321..a92f21c 100644 --- a/addons/sourcemod/scripting/nativevotes_nominations.sp +++ b/addons/sourcemod/scripting/nativevotes_nominations.sp @@ -47,7 +47,7 @@ public Plugin myinfo = name = "NativeVotes | Map Nominations", author = "AlliedModders LLC and Powerlord", description = "Provides Map Nominations", - version = "26w13a", + version = "26w15a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_rockthevote.sp b/addons/sourcemod/scripting/nativevotes_rockthevote.sp index 0660f90..62c1a6e 100644 --- a/addons/sourcemod/scripting/nativevotes_rockthevote.sp +++ b/addons/sourcemod/scripting/nativevotes_rockthevote.sp @@ -54,7 +54,7 @@ public Plugin myinfo = name = "NativeVotes | Rock The Vote", author = "AlliedModders LLC and Powerlord", description = "Provides RTV Map Voting", - version = "26w13a", + version = "26w15a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_voterp.sp b/addons/sourcemod/scripting/nativevotes_voterp.sp index c2c3b94..2352f90 100644 --- a/addons/sourcemod/scripting/nativevotes_voterp.sp +++ b/addons/sourcemod/scripting/nativevotes_voterp.sp @@ -13,7 +13,7 @@ public Plugin myinfo = name = "NativeVotes | Medieval Auto-RP", author = "Heapons", description = "Provides Medieval Auto-RP voting.", - version = "26w13a", + version = "26w15a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; diff --git a/addons/sourcemod/scripting/nativevotes_votescramble.sp b/addons/sourcemod/scripting/nativevotes_votescramble.sp index c32539a..4f1a6ae 100644 --- a/addons/sourcemod/scripting/nativevotes_votescramble.sp +++ b/addons/sourcemod/scripting/nativevotes_votescramble.sp @@ -13,7 +13,7 @@ public Plugin myinfo = name = "NativeVotes | Scramble Teams", author = "Heapons", description = "Provides RTV-style Scramble Teams Voting", - version = "26w13a", + version = "26w15a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; From 8e4d106d25ec3f306653ebc9cb2a51194419d78e Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Fri, 31 Jul 2026 22:56:04 +0200 Subject: [PATCH 123/128] Fix mapcycle generator --- .../scripting/nativevotes_mapchooser.sp | 146 +++++++++++------- 1 file changed, 91 insertions(+), 55 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes_mapchooser.sp b/addons/sourcemod/scripting/nativevotes_mapchooser.sp index 3f8a059..49b3ef8 100644 --- a/addons/sourcemod/scripting/nativevotes_mapchooser.sp +++ b/addons/sourcemod/scripting/nativevotes_mapchooser.sp @@ -55,21 +55,21 @@ public Plugin myinfo = name = "NativeVotes | MapChooser", author = "AlliedModders LLC and Powerlord", description = "Automated Map Voting", - version = "26w15a", + version = "26w31a", url = "https://github.com/Heapons/sourcemod-nativevotes-updated/" }; /* ConVars */ enum { - /* Valve ConVars */ + /* Valve */ mp_winlimit, mp_maxrounds, mp_fraglimit, mp_bonusroundtime, mapcyclefile, - /* Plugin ConVars */ + /* Plugin */ mapvote_endvote, mapvote_start, mapvote_startround, @@ -92,6 +92,9 @@ enum workshop_map_collection, workshop_cleanup, + /* Rafmod */ + sig_etc_workshop_map_fix, + MAX_CONVARS } @@ -126,8 +129,11 @@ MapChange g_ChangeTime; GlobalForward g_NominationsResetForward; GlobalForward g_MapVoteStartedForward; -/* Upper bound of how many team there could be */ -#define MAX_TEAMS 10 +/** + * Upper bound of how many team there could be. + * https://github.com/ValveSoftware/source-sdk-2013/blob/master/src/game/shared/shareddefs.h#L288 + */ +#define MAX_TEAMS 32 int g_winCount[MAX_TEAMS]; #define VOTE_EXTEND "##extend##" @@ -187,11 +193,12 @@ public void OnPluginStart() RegAdminCmd("sm_mapvote", Command_MapVote, ADMFLAG_CHANGEMAP, "Forces MapChooser to attempt to run a map vote now."); RegAdminCmd("sm_setnextmap", Command_SetNextMap, ADMFLAG_CHANGEMAP, "sm_setnextmap <map>"); - g_ConVars[mp_winlimit] = FindConVar("mp_winlimit"); - g_ConVars[mp_maxrounds] = FindConVar("mp_maxrounds"); - g_ConVars[mp_fraglimit] = FindConVar("mp_fraglimit"); - g_ConVars[mp_bonusroundtime] = FindConVar("mp_bonusroundtime"); - g_ConVars[mapcyclefile] = FindConVar("mapcyclefile"); + g_ConVars[mp_winlimit] = FindConVar("mp_winlimit"); + g_ConVars[mp_maxrounds] = FindConVar("mp_maxrounds"); + g_ConVars[mp_fraglimit] = FindConVar("mp_fraglimit"); + g_ConVars[mp_bonusroundtime] = FindConVar("mp_bonusroundtime"); + g_ConVars[mapcyclefile] = FindConVar("mapcyclefile"); + g_ConVars[sig_etc_workshop_map_fix] = FindConVar("sig_etc_workshop_map_fix"); if (g_ConVars[mp_winlimit] || g_ConVars[mp_maxrounds]) { @@ -200,7 +207,7 @@ public void OnPluginStart() if (HookEventEx("teamplay_win_panel", Event_TeamplayWinPanel)) { - HookEvent("teamplay_restart_round", Event_TFRestartRound); + HookEvent("teamplay_restart_round", Event_TeamplayRestartRound); HookEvent("arena_win_panel", Event_TeamplayWinPanel); } else if (strcmp(folder, "nucleardawn") == 0) @@ -249,7 +256,6 @@ public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max CreateNative("GetNominatedMapList", Native_GetNominatedMapList); CreateNative("EndOfMapVoteEnabled", Native_EndOfMapVoteEnabled); - // Why doesn't RIP ext already set these as optional?? MarkNativeAsOptional("HTTPRequest.HTTPRequest"); MarkNativeAsOptional("HTTPRequest.AppendFormParam"); MarkNativeAsOptional("HTTPRequest.PostForm"); @@ -308,7 +314,7 @@ public void OnLibraryRemoved(const char[] name) public void OnConfigsExecuted() { - if (g_ConVars[workshop_cleanup].BoolValue) + if (g_ConVars[workshop_cleanup] != null && g_ConVars[workshop_cleanup].BoolValue) { CleanupWorkshopMaps(); } @@ -318,13 +324,14 @@ public void OnConfigsExecuted() PopulateMapList(); } - if (ReadMapList(g_MapList, g_mapFileSerial, "mapchooser", MAPLIST_FLAG_CLEARARRAY|MAPLIST_FLAG_MAPSFOLDER) != null) + if (ReadMapList(g_MapList, g_mapFileSerial, "mapchooser", MAPLIST_FLAG_CLEARARRAY|MAPLIST_FLAG_MAPSFOLDER) == null) { if (g_mapFileSerial == -1) { LogError("Unable to create a valid map list."); } } + /* First-load previous maps from a text file when persistency is enabled. */ static bool g_FirstConfigExec = true; if (g_FirstConfigExec) @@ -517,7 +524,7 @@ public Action Timer_StartMapVote(Handle timer, DataPack data) return Plugin_Stop; } -public void Event_TFRestartRound(Event event, const char[] name, bool dontBroadcast) +public void Event_TeamplayRestartRound(Event event, const char[] name, bool dontBroadcast) { /* Game got restarted - reset our round count tracking */ g_TotalRounds = 0; @@ -532,13 +539,15 @@ public void Event_TeamplayWinPanel(Event event, const char[] name, bool dontBroa g_ChangeMapInProgress = true; } - if (event.GetInt("round_complete") == 1 || StrEqual(name, "arena_win_panel")) + if (event.GetInt("round_complete") == 1 || StrContains(name, "_win")) { if (!g_MapList.Length || g_HasVoteStarted || g_MapVoteCompleted || !g_ConVars[mapvote_endvote].BoolValue) { return; } + g_TotalRounds++; + CheckMaxRounds(g_TotalRounds); int winning_team = event.GetInt("winning_team"); @@ -1163,35 +1172,34 @@ public int Handler_MapVoteMenu(Menu menu, MenuAction action, int param1, int par g_VoteMenu = null; delete menu; } - case MenuAction_Display: { char buffer[255]; - Format(buffer, sizeof(buffer), "%T", "Vote Nextmap", param1); + SetGlobalTransTarget(param1); + Format(buffer, sizeof(buffer), "%t", "Vote Nextmap"); Panel panel = view_as<Panel>(param2); panel.SetTitle(buffer); - } - + } case MenuAction_DisplayItem: { if (menu.ItemCount - 1 == param2) { char map[PLATFORM_MAX_PATH], buffer[255]; menu.GetItem(param2, map, sizeof(map)); + SetGlobalTransTarget(param1); if (strcmp(map, VOTE_EXTEND, false) == 0) { - Format(buffer, sizeof(buffer), "%T", "Extend Map", param1); + Format(buffer, sizeof(buffer), "%t", "Extend Map"); return RedrawMenuItem(buffer); } else if (strcmp(map, VOTE_DONTCHANGE, false) == 0) { - Format(buffer, sizeof(buffer), "%T", "Dont Change", param1); + Format(buffer, sizeof(buffer), "%t", "Dont Change"); return RedrawMenuItem(buffer); } } - } - + } case MenuAction_VoteCancel: { // If we receive 0 votes, pick at random. @@ -1220,11 +1228,9 @@ public int Handler_MapVoteMenu(Menu menu, MenuAction action, int param1, int par g_MapVoteCompleted = true; } } - g_HasVoteStarted = false; } } - return 0; } @@ -1238,26 +1244,25 @@ public int Handler_NV_MapVoteMenu(NativeVote menu, MenuAction action, int param1 menu.Close(); g_HasVoteStarted = false; } - case MenuAction_DisplayItem: { if (menu.ItemCount - 1 == param2) { char map[PLATFORM_MAX_PATH], buffer[255]; menu.GetItem(param2, map, sizeof(map)); + SetGlobalTransTarget(param1); if (strcmp(map, VOTE_EXTEND, false) == 0) { - Format(buffer, sizeof(buffer), "%T", "Extend Map", param1); + Format(buffer, sizeof(buffer), "%t", "Extend Map"); return view_as<int>(NativeVotes_RedrawVoteItem(buffer)); } else if (strcmp(map, VOTE_DONTCHANGE, false) == 0) { - Format(buffer, sizeof(buffer), "%T", "Dont Change", param1); + Format(buffer, sizeof(buffer), "%t", "Dont Change"); return view_as<int>(NativeVotes_RedrawVoteItem(buffer)); } } - } - + } case MenuAction_VoteCancel: { // If we receive 0 votes, pick at random. @@ -1297,11 +1302,9 @@ public int Handler_NV_MapVoteMenu(NativeVote menu, MenuAction action, int param1 // We were actually cancelled. Display the generic fail message menu.DisplayFail(NativeVotesFail_Generic); } - g_HasVoteStarted = false; } } - return 0; } @@ -1703,7 +1706,7 @@ void PopulateMapList() if (dir == null) { delete regex; - CloseHandle(file); + file.Close(); return; } @@ -1711,8 +1714,7 @@ void PopulateMapList() FileType type; int len; - file.WriteLine("// Generated with NativeVotes MapChooser."); - file.WriteLine("// https://github.com/Heapons/sourcemod-nativevotes-updated"); + ArrayList fastdl = new ArrayList(ByteCountToCells(PLATFORM_MAX_PATH)); // FastDL while (dir.GetNext(mapName, sizeof(mapName), type)) @@ -1729,24 +1731,43 @@ void PopulateMapList() if (regex.Match(mapName) >= 1) continue; - file.WriteLine("%s", mapName); + fastdl.PushString(mapName); } delete dir; delete regex; + fastdl.Sort(Sort_Ascending, Sort_String); + + file.WriteLine("// Generated with NativeVotes MapChooser."); + file.WriteLine("// https://github.com/Heapons/sourcemod-nativevotes-updated"); + + for (int i = 0; i < fastdl.Length; i++) + { + fastdl.GetString(i, mapName, sizeof(mapName)); + file.WriteLine("%s", mapName); + } + delete fastdl; + // Workshop - char workshopCollection[64]; - g_ConVars[workshop_map_collection].GetString(workshopCollection, sizeof(workshopCollection)); - if (g_RestInPawn && workshopCollection[0] != '\0') + if (g_ConVars[workshop_map_collection] != null) { - HTTPRequest req = new HTTPRequest("https://api.steampowered.com/ISteamRemoteStorage/GetCollectionDetails/v1/"); - req.AppendFormParam("collectioncount", "1"); - req.AppendFormParam("publishedfileids[0]", workshopCollection); - req.PostForm(HTTPResponse_GetCollectionDetails, file); + char workshopCollection[64]; + g_ConVars[workshop_map_collection].GetString(workshopCollection, sizeof(workshopCollection)); + if (g_RestInPawn && workshopCollection[0] != '\0') + { + HTTPRequest req = new HTTPRequest("https://api.steampowered.com/ISteamRemoteStorage/GetCollectionDetails/v1/"); + req.AppendFormParam("collectioncount", "1"); + req.AppendFormParam("publishedfileids[0]", workshopCollection); + req.PostForm(HTTPResponse_GetCollectionDetails, file); + } + else + { + file.Close(); + } } else { - CloseHandle(file); + file.Close(); } CreateTimer(3.0, Timer_ReadMapList, _, TIMER_FLAG_NO_MAPCHANGE|TIMER_REPEAT); @@ -1754,7 +1775,7 @@ void PopulateMapList() public Action Timer_ReadMapList(Handle timer, any data) { - if (ReadMapList(g_MapList, g_mapFileSerial, "mapchooser", MAPLIST_FLAG_CLEARARRAY|MAPLIST_FLAG_MAPSFOLDER) != null) + if (ReadMapList(g_MapList, g_mapFileSerial, "mapchooser", MAPLIST_FLAG_CLEARARRAY|MAPLIST_FLAG_MAPSFOLDER) == null) { if (g_mapFileSerial == -1) { @@ -1769,14 +1790,14 @@ void HTTPResponse_GetCollectionDetails(HTTPResponse response, File file) { if (response.Status != HTTPStatus_OK || response.Data == null) { - CloseHandle(file); + file.Close(); return; } JSONObject root = view_as<JSONObject>(response.Data); if (root == null) { - CloseHandle(file); + file.Close(); return; } @@ -1784,7 +1805,7 @@ void HTTPResponse_GetCollectionDetails(HTTPResponse response, File file) if (responseObj == null) { delete root; - CloseHandle(file); + file.Close(); return; } @@ -1793,7 +1814,7 @@ void HTTPResponse_GetCollectionDetails(HTTPResponse response, File file) { delete responseObj; delete root; - CloseHandle(file); + file.Close(); return; } @@ -1803,15 +1824,15 @@ void HTTPResponse_GetCollectionDetails(HTTPResponse response, File file) delete collectionDetailsArray; delete responseObj; delete root; - CloseHandle(file); + file.Close(); return; } JSONArray children = view_as<JSONArray>(collectionDetails.Get("children")); if (children != null) { - ConVar sig_etc_workshop_map_fix = FindConVar("sig_etc_workshop_map_fix"); - bool workshopMapFix = sig_etc_workshop_map_fix != null ? sig_etc_workshop_map_fix.BoolValue : false; + bool workshopMapFix = g_ConVars[sig_etc_workshop_map_fix] != null ? g_ConVars[sig_etc_workshop_map_fix].BoolValue : false; + ArrayList workshop = new ArrayList(ByteCountToCells(PLATFORM_MAX_PATH)); for (int i = 0; i < children.Length; i++) { JSONObject child = view_as<JSONObject>(children.Get(i)); @@ -1831,19 +1852,34 @@ void HTTPResponse_GetCollectionDetails(HTTPResponse response, File file) } else { - file.WriteLine("workshop/%s", publishedfileid); + char mapName[PLATFORM_MAX_PATH]; + Format(mapName, sizeof(mapName), "workshop/%s", publishedfileid); + if (g_MapList.FindString(mapName) == -1) + { + g_MapList.PushString(mapName); + workshop.PushString(mapName); + } } } } delete child; } delete children; + + workshop.Sort(Sort_Ascending, Sort_String); + for (int i = 0; i < workshop.Length; i++) + { + char mapName[PLATFORM_MAX_PATH]; + workshop.GetString(i, mapName, sizeof(mapName)); + file.WriteLine("%s", mapName); + } + delete workshop; } delete collectionDetails; delete collectionDetailsArray; delete responseObj; delete root; - CloseHandle(file); + file.Close(); } void CleanupWorkshopMaps() From 03899042d4e0a0522e418b58263c263b5915e5a2 Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Fri, 31 Jul 2026 23:17:12 +0200 Subject: [PATCH 124/128] Update Workflow --- .github/workflows/nativevotes.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/nativevotes.yml b/.github/workflows/nativevotes.yml index ea49ebc..80cfc4e 100644 --- a/.github/workflows/nativevotes.yml +++ b/.github/workflows/nativevotes.yml @@ -41,6 +41,7 @@ jobs: curl -o addons/sourcemod/scripting/include/multicolors.inc https://raw.githubusercontent.com/Bara/Multi-Colors/master/addons/sourcemod/scripting/include/multicolors.inc curl -o addons/sourcemod/scripting/include/multicolors/colors.inc https://raw.githubusercontent.com/Bara/Multi-Colors/master/addons/sourcemod/scripting/include/multicolors/colors.inc curl -o addons/sourcemod/scripting/include/multicolors/morecolors.inc https://raw.githubusercontent.com/Bara/Multi-Colors/master/addons/sourcemod/scripting/include/multicolors/morecolors.inc + curl -o addons/sourcemod/scripting/include/sourcebanspp.inc https://raw.githubusercontent.com/sbpp/sourcebans-pp/refs/heads/main/game/addons/sourcemod/scripting/include/sourcebanspp.inc - name: Setup SourcePawn 1.13 Compiler uses: rumblefrog/setup-sp@master From f367c197035b21f23ae0129d07c03d5b36ed5b1f Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Fri, 31 Jul 2026 23:20:17 +0200 Subject: [PATCH 125/128] Add missing includes --- .github/workflows/nativevotes.yml | 13 ++++++++----- .gitignore | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/.github/workflows/nativevotes.yml b/.github/workflows/nativevotes.yml index 80cfc4e..11e7508 100644 --- a/.github/workflows/nativevotes.yml +++ b/.github/workflows/nativevotes.yml @@ -32,16 +32,19 @@ jobs: - name: Download dependencies shell: bash run: | - curl -o addons/sourcemod/scripting/include/sourcebanspp.inc https://raw.githubusercontent.com/sbpp/sourcebans-pp/refs/heads/php81/game/addons/sourcemod/scripting/include/sourcebanspp.inc - mkdir -p addons/sourcemod/scripting/include/ripext - curl -o addons/sourcemod/scripting/include/ripext.inc https://raw.githubusercontent.com/ErikMinekus/sm-ripext/main/pawn/scripting/include/ripext.inc - curl -o addons/sourcemod/scripting/include/ripext/http.inc https://raw.githubusercontent.com/ErikMinekus/sm-ripext/main/pawn/scripting/include/ripext/http.inc - curl -o addons/sourcemod/scripting/include/ripext/json.inc https://raw.githubusercontent.com/ErikMinekus/sm-ripext/main/pawn/scripting/include/ripext/json.inc mkdir -p addons/sourcemod/scripting/include/multicolors + mkdir -p addons/sourcemod/scripting/include/ripext curl -o addons/sourcemod/scripting/include/multicolors.inc https://raw.githubusercontent.com/Bara/Multi-Colors/master/addons/sourcemod/scripting/include/multicolors.inc curl -o addons/sourcemod/scripting/include/multicolors/colors.inc https://raw.githubusercontent.com/Bara/Multi-Colors/master/addons/sourcemod/scripting/include/multicolors/colors.inc curl -o addons/sourcemod/scripting/include/multicolors/morecolors.inc https://raw.githubusercontent.com/Bara/Multi-Colors/master/addons/sourcemod/scripting/include/multicolors/morecolors.inc + curl -o addons/sourcemod/scripting/include/ripext.inc https://raw.githubusercontent.com/ErikMinekus/sm-ripext/main/pawn/scripting/include/ripext.inc + curl -o addons/sourcemod/scripting/include/ripext/http.inc https://raw.githubusercontent.com/ErikMinekus/sm-ripext/main/pawn/scripting/include/ripext/http.inc + curl -o addons/sourcemod/scripting/include/ripext/json.inc https://raw.githubusercontent.com/ErikMinekus/sm-ripext/main/pawn/scripting/include/ripext/json.inc + curl -o addons/sourcemod/scripting/include/sbpp_version.inc https://raw.githubusercontent.com/sbpp/sourcebans-pp/refs/heads/main/game/addons/sourcemod/scripting/include/sourcebanspp.inc + curl -o addons/sourcemod/scripting/include/sourcebanschecker.inc https://raw.githubusercontent.com/sbpp/sourcebans-pp/refs/heads/main/game/addons/sourcemod/scripting/include/sourcebanspp.inc curl -o addons/sourcemod/scripting/include/sourcebanspp.inc https://raw.githubusercontent.com/sbpp/sourcebans-pp/refs/heads/main/game/addons/sourcemod/scripting/include/sourcebanspp.inc + curl -o addons/sourcemod/scripting/include/sourcebanspp.inc https://raw.githubusercontent.com/sbpp/sourcebans-pp/refs/heads/php81/game/addons/sourcemod/scripting/include/sourcebanspp.inc + curl -o addons/sourcemod/scripting/include/sourcecomms.inc https://raw.githubusercontent.com/sbpp/sourcebans-pp/refs/heads/main/game/addons/sourcemod/scripting/include/sourcebanspp.inc - name: Setup SourcePawn 1.13 Compiler uses: rumblefrog/setup-sp@master diff --git a/.gitignore b/.gitignore index d1892d4..13f81bb 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ *.log *.smx -.* +.ref admin.inc adminmenu.inc From c9c56dfad08982f993be4e00e692fa968809406f Mon Sep 17 00:00:00 2001 From: Heapons <30896390+Heapons@users.noreply.github.com> Date: Fri, 31 Jul 2026 23:27:01 +0200 Subject: [PATCH 126/128] Whoops --- .github/workflows/nativevotes.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/nativevotes.yml b/.github/workflows/nativevotes.yml index 11e7508..612d6a9 100644 --- a/.github/workflows/nativevotes.yml +++ b/.github/workflows/nativevotes.yml @@ -40,11 +40,10 @@ jobs: curl -o addons/sourcemod/scripting/include/ripext.inc https://raw.githubusercontent.com/ErikMinekus/sm-ripext/main/pawn/scripting/include/ripext.inc curl -o addons/sourcemod/scripting/include/ripext/http.inc https://raw.githubusercontent.com/ErikMinekus/sm-ripext/main/pawn/scripting/include/ripext/http.inc curl -o addons/sourcemod/scripting/include/ripext/json.inc https://raw.githubusercontent.com/ErikMinekus/sm-ripext/main/pawn/scripting/include/ripext/json.inc - curl -o addons/sourcemod/scripting/include/sbpp_version.inc https://raw.githubusercontent.com/sbpp/sourcebans-pp/refs/heads/main/game/addons/sourcemod/scripting/include/sourcebanspp.inc - curl -o addons/sourcemod/scripting/include/sourcebanschecker.inc https://raw.githubusercontent.com/sbpp/sourcebans-pp/refs/heads/main/game/addons/sourcemod/scripting/include/sourcebanspp.inc + curl -o addons/sourcemod/scripting/include/sbpp_version.inc https://raw.githubusercontent.com/sbpp/sourcebans-pp/refs/heads/main/game/addons/sourcemod/scripting/include/sbpp_version.inc + curl -o addons/sourcemod/scripting/include/sourcebanschecker.inc https://raw.githubusercontent.com/sbpp/sourcebans-pp/refs/heads/main/game/addons/sourcemod/scripting/include/sourcebanschecker.inc curl -o addons/sourcemod/scripting/include/sourcebanspp.inc https://raw.githubusercontent.com/sbpp/sourcebans-pp/refs/heads/main/game/addons/sourcemod/scripting/include/sourcebanspp.inc - curl -o addons/sourcemod/scripting/include/sourcebanspp.inc https://raw.githubusercontent.com/sbpp/sourcebans-pp/refs/heads/php81/game/addons/sourcemod/scripting/include/sourcebanspp.inc - curl -o addons/sourcemod/scripting/include/sourcecomms.inc https://raw.githubusercontent.com/sbpp/sourcebans-pp/refs/heads/main/game/addons/sourcemod/scripting/include/sourcebanspp.inc + curl -o addons/sourcemod/scripting/include/sourcecomms.inc https://raw.githubusercontent.com/sbpp/sourcebans-pp/refs/heads/main/game/addons/sourcemod/scripting/include/sourcecomms.inc - name: Setup SourcePawn 1.13 Compiler uses: rumblefrog/setup-sp@master From 1ec4922d9e97b78965cdca14c9027be12a28239f Mon Sep 17 00:00:00 2001 From: Bradasparky <bradasparky12@yahoo.com> Date: Sun, 2 Aug 2026 17:29:46 -0400 Subject: [PATCH 127/128] Fix the vote duration being a second longer than specified --- addons/sourcemod/scripting/nativevotes.sp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes.sp b/addons/sourcemod/scripting/nativevotes.sp index d96324d..f4c3d1f 100644 --- a/addons/sourcemod/scripting/nativevotes.sp +++ b/addons/sourcemod/scripting/nativevotes.sp @@ -1416,7 +1416,7 @@ void StartVoting() public Action DisplayTimer(Handle timer) { - if (!Internal_IsVoteInProgress() || g_TimeLeft <= 0) + if (!Internal_IsVoteInProgress() || --g_TimeLeft <= 0) { if (g_hDisplayTimer != null) { @@ -1426,7 +1426,6 @@ public Action DisplayTimer(Handle timer) return Plugin_Stop; } DrawHintProgress(); - --g_TimeLeft; return Plugin_Continue; } From e9df9ab0572012a18c0adf1b53830c5578417370 Mon Sep 17 00:00:00 2001 From: Bradasparky <bradasparky12@yahoo.com> Date: Sun, 2 Aug 2026 17:35:11 -0400 Subject: [PATCH 128/128] Add support for Team Fortress 2 Classified's voting time left visual --- addons/sourcemod/scripting/nativevotes.sp | 4 ++-- addons/sourcemod/scripting/nativevotes/game.sp | 16 ++++++++++------ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/addons/sourcemod/scripting/nativevotes.sp b/addons/sourcemod/scripting/nativevotes.sp index f4c3d1f..e1e2081 100644 --- a/addons/sourcemod/scripting/nativevotes.sp +++ b/addons/sourcemod/scripting/nativevotes.sp @@ -1312,7 +1312,7 @@ bool DoClientVote(NativeVote vote, int[] clients, int num_clients) if (totalPlayers > 0) { - Game_DisplayVote(vote, realClients, totalPlayers); + Game_DisplayVote(vote, realClients, totalPlayers, (g_fStartTime + g_VoteTime) - GetGameTime()); return true; } else @@ -1592,7 +1592,7 @@ public Action RedrawTimer(Handle timer, DataPack data) if (Internal_IsVoteInProgress() && !Internal_IsCancelling() && vote == g_hCurVote) { - Game_DisplayVoteToOne(vote, client); + Game_DisplayVoteToOne(vote, client, (g_fStartTime + g_VoteTime) - GetGameTime()); } return Plugin_Stop; diff --git a/addons/sourcemod/scripting/nativevotes/game.sp b/addons/sourcemod/scripting/nativevotes/game.sp index c0bdfc5..b8265b8 100644 --- a/addons/sourcemod/scripting/nativevotes/game.sp +++ b/addons/sourcemod/scripting/nativevotes/game.sp @@ -645,7 +645,7 @@ bool Game_CheckVotePassType(NativeVotesPassType type) return returnVal; } -bool Game_DisplayVoteToOne(NativeVote vote, int client) +bool Game_DisplayVoteToOne(NativeVote vote, int client, float end_time = 0.0) { if (g_bCancelled) { @@ -655,10 +655,10 @@ bool Game_DisplayVoteToOne(NativeVote vote, int client) int clients[1]; clients[0] = client; - return Game_DisplayVote(vote, clients, 1); + return Game_DisplayVote(vote, clients, 1, end_time); } -bool Game_DisplayVote(NativeVote vote, int[] clients, int num_clients) +bool Game_DisplayVote(NativeVote vote, int[] clients, int num_clients, float end_time = 0.0) { if (g_bCancelled) { @@ -677,7 +677,7 @@ bool Game_DisplayVote(NativeVote vote, int[] clients, int num_clients) } case Engine_CSGO, Engine_TF2: { - TF2CSGO_DisplayVote(vote, clients, num_clients); + TF2CSGO_DisplayVote(vote, clients, num_clients, end_time); } } @@ -2172,7 +2172,7 @@ static stock void TF2CSGO_UpdateClientCount(int num_clients) } } -static void TF2CSGO_DisplayVote(NativeVote vote, int[] clients, int num_clients) +static void TF2CSGO_DisplayVote(NativeVote vote, int[] clients, int num_clients, float end_time) { NativeVotesType voteType = Data_GetType(vote); @@ -2358,8 +2358,12 @@ static void TF2CSGO_DisplayVote(NativeVote vote, int[] clients, int num_clients) { bfStart.WriteString(details); } - bfStart.WriteByte(bYesNo); + bfStart.WriteBool(bYesNo); bfStart.WriteByte(Data_GetTarget(vote) - 1); + if (g_AppID == 3545060) // Team Fortress 2 Classified + { + bfStart.WriteFloat(GetGameTime() + end_time); + } } EndMessage(); }