diff --git a/.github/nv_compile.sh b/.github/nv_compile.sh
index f370aac..01ae2cd 100755
--- a/.github/nv_compile.sh
+++ b/.github/nv_compile.sh
@@ -6,36 +6,27 @@ 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_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")
+nv_plugs+=("nativevotes_voterp")
+nv_plugs+=("nativevotes_votescramble")
# we start in git root
pushd ./addons/sourcemod/scripting
-mkdir ../plugins/disabled -p || true
-
+mkdir ../plugins || true
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
7za a -r build/nativevotes_sm_"${versvers}".zip scripting/ translations/ plugins/
rm plugins/ -rfv
ls -la
-
diff --git a/.github/workflows/nativevotes.yml b/.github/workflows/nativevotes.yml
index b803e90..612d6a9 100644
--- a/.github/workflows/nativevotes.yml
+++ b/.github/workflows/nativevotes.yml
@@ -4,11 +4,22 @@ name: Nativevotes Autobuild
on:
push:
branches: [ master ]
+ paths-ignore:
+ - '.gitignore'
+ - 'LICENSE'
+ - 'README.md'
+ pull_request:
+ branches: [ master ]
+ paths-ignore:
+ - '.gitignore'
+ - 'LICENSE'
+ - 'README.md'
+ workflow_dispatch:
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]')"
@@ -18,24 +29,28 @@ 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
+ - name: Download dependencies
shell: bash
run: |
- bash .github/nv_compile.sh
- bash
+ 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/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/sourcecomms.inc https://raw.githubusercontent.com/sbpp/sourcebans-pp/refs/heads/main/game/addons/sourcemod/scripting/include/sourcecomms.inc
-
- - name: Setup SourcePawn 1.12 Compiler
+ - name: Setup SourcePawn 1.13 Compiler
uses: rumblefrog/setup-sp@master
with:
- version: "1.12.x"
+ version: "1.13.7301"
- - name: Compile plugins 1.12
+ - name: Compile plugins 1.13
shell: bash
run: |
bash .github/nv_compile.sh
@@ -61,3 +76,4 @@ jobs:
with:
tag_name: workflow-build${{ github.run_number }}
files: build/nativevotes*
+
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..13f81bb
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,84 @@
+*.bat
+*.sh
+*.exe
+*.log
+*.smx
+
+.ref
+
+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
+
+steam*
+ripext*
+multicolors*
+source*.inc
\ No newline at end of file
diff --git a/README.md b/README.md
index 60b6b59..a993071 100644
--- a/README.md
+++ b/README.md
@@ -1,47 +1,114 @@
-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.
-
+
+#
/
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).
+
+# [Differences](https://github.com/sapphonie/sourcemod-nativevotes-updated/compare/master...Heapons:sourcemod-nativevotes-updated:master)
+## General
+- 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.
+- Vote Progress HUD:
+ -
Added `nativevotes_progress_hintcaption` convar to use the style of [game instructor hints](https://developer.valvesoftware.com/wiki/Env_instructor_hint) for supported games.
+
+##
Team Fortress 2
+- Fixes:
+ - ✔️/❌ vote counts.
+ - 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.
+- 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:
+ - `sm_forcertv`.
+ - Force a RTV.
+ - `sm_resetrtv`.
+ - Reset RTV counts.
+- Allow players to retract their rock-the-vote.
+ - Execute `sm_rtv` again to undo.
+---
+|Name|Default Value|Description|
+|-|-|-|
+|`sm_rtv_needed`|`0.60`|Percentage of players needed to rockthevote|
+|`sm_rtv_minplayers`|`0`|Number of players required before RTV will be enabled|
+|`sm_rtv_initialdelay`|`30.0`|Time (in seconds) before first RTV can be held|
+|`sm_rtv_interval`|`240.0`|Time (in seconds) after a failed RTV before another can be held|
+|`sm_rtv_changetime`|`0`|When to change the map after a successful RTV: 0 - Instant, 1 - RoundEnd, 2 - MapEnd|
+|`sm_rtv_postvoteaction`|`0`|What to do with RTV's after a mapvote has completed.
0 - Allow (success = instant change), 1 - Deny|
+
+## [Nominations](https://github.com/Heapons/sourcemod-nativevotes-updated/blob/master/addons/sourcemod/scripting/nativevotes_nominations.sp)
+- 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|
+
+## [MapChooser](https://github.com/Heapons/sourcemod-nativevotes-updated/blob/master/addons/sourcemod/scripting/nativevotes_mapchooser.sp)
+-
Clean up workshop maps to reduce disk size.
+- 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|
+|-|-|-|
+|`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. Use '0' on TF2 to start vote during bonus round time|
+|`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_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).|
+|
`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|
+
+## [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_full_reset`|`1`|Whether time/rounds played should reset after a scramble is triggered|
\ 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/nativevotes.inc b/addons/sourcemod/scripting/include/nativevotes.inc
index 426e6d1..65be07b 100644
--- a/addons/sourcemod/scripting/include/nativevotes.inc
+++ b/addons/sourcemod/scripting/include/nativevotes.inc
@@ -31,8 +31,7 @@
#include
#include
-
-// NativeVotes 1.1 series
+#include
#if defined _nativevotes_included
#endinput
@@ -84,65 +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_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_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
@@ -238,7 +241,10 @@ enum NativeVotesOverride
NativeVotesOverride_AutoBalance,
NativeVotesOverride_ClassLimits,
NativeVotesOverride_Extend,
- NativeVotesOverride_Count,
+ NativeVotesOverride_ChgCivilian,
+ NativeVotesOverride_ChgGamemode,
+ NativeVotesOverride_ChgDifficulty,
+ NativeVotesOverride_Count
}
methodmap NativeVote < KeyValues
@@ -521,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.
*/
@@ -1395,7 +1401,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();
@@ -1502,6 +1508,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");
@@ -1550,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 4de0473..e1e2081 100644
--- a/addons/sourcemod/scripting/nativevotes.sp
+++ b/addons/sourcemod/scripting/nativevotes.sp
@@ -1,2529 +1,2693 @@
-/**
- * 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/nativevotes.inc"
-#include "include/implodeexplode.inc"
-
-EngineVersion g_EngineVersion = Engine_Unknown;
-
-#include "nativevotes/data-keyvalues.sp"
-
-#define VERSION "1.1.1fix"
-
-#define LOGTAG "NV"
-
-#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
-ConVar g_Cvar_VoteHintbox;
-ConVar g_Cvar_VoteChat;
-ConVar g_Cvar_VoteConsole;
-ConVar g_Cvar_VoteClientConsole;
-ConVar g_Cvar_VoteDelay;
-
-//----------------------------------------------------------------------------
-// Used to track current vote data
-//new 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 STRINGTABLE_NAME "ServerMapCycle"
-#define STRINGTABLE_ITEM "ServerMapCycle"
-//#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 = VERSION,
- url = "https://forums.alliedmods.net/showthread.php?t=208008"
-}
-
-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);
- //strcopy(error, err_max, "Unsupported game");
- 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");
-
- 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);
-
- Game_InitializeCvars();
-
- HookConVarChange(g_Cvar_VoteDelay, 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");
-
- // 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;
-}
-
-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()
-{
- int stringTableIndex = FindStringTable(STRINGTABLE_NAME);
- int stringIndex = FindStringIndex(stringTableIndex, STRINGTABLE_ITEM);
-
- StringMap overrideList = new StringMap();
-
- // 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);
-
- 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)
- {
-#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);
- }
- else
- {
- delete overrideList;
- }
-}
-
-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_Cvar_VoteChat.BoolValue || g_Cvar_VoteConsole.BoolValue || g_Cvar_VoteClientConsole.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_Cvar_VoteConsole.BoolValue)
- {
- PrintToServer("[%s] %T", LOGTAG, "Voted For", LANG_SERVER, name, choice);
- }
-
- if (g_Cvar_VoteChat.BoolValue || g_Cvar_VoteClientConsole.BoolValue)
- {
- char phrase[30];
-
- if (g_bRevoting[client])
- {
- strcopy(phrase, sizeof(phrase), "Changed Vote");
- }
- else
- {
- strcopy(phrase, sizeof(phrase), "Voted For");
- }
-
- if (g_Cvar_VoteChat.BoolValue)
- {
- PrintToChatAll("[%s] %t", LOGTAG, phrase, name, choice);
- }
-
- if (g_Cvar_VoteClientConsole.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))
- {
- new 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[][])
-{
- new 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_Cvar_VoteHintbox.BoolValue)
- {
- 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_Cvar_VoteHintbox.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_Cvar_VoteDelay.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_Cvar_VoteDelay.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)
-{
- DrawHintProgress();
- if (--g_TimeLeft == 0)
- {
- if (g_hDisplayTimer != null)
- {
- g_hDisplayTimer = null;
- EndVoting();
- }
- return Plugin_Stop;
- }
-
- 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
- //new Handle:menuForward = Data_GetHandler(vote);
- //RemoveAllFromForward(menuForward, plugin);
-
- //new Handle:voteResults = Data_GetResultCallback(vote);
- //RemoveAllFromForward(voteResults, plugin);
-
- // Do the datatype-specific close operations
- Data_CloseVote(vote);
-}
-
-// native bool:NativeVotes_Display(Handle:vote, clients[], numClients, time, 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 String:info[], const String: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, position, const String:info[], const String: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, 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,
-// position,
-// String:infoBuf[],
-// infoBufLen,
-// String:dispBuf[]="",
-// 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, String:buffer[], 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, String: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, String:buffer[], 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, String: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(client);
-public int Native_IsClientInVotePool(Handle plugin, int numParams)
-{
- int client = GetNativeCell(1);
-
- if (client <= 0 || 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(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()
- // 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
- 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, 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 NativeVotes_SetInitiator(Handle:vote, 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 String: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 String: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 String: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(client, NativeVotesPassType:passType, const String:details[]="", 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(client, team, const String: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, 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(client, NativeVotesFailType:reason=NativeVotesFail_Generic, team=NATIVEVOTES_ALL_TEAMS);
-/*
-public Native_DisplayRawFailToOne(Handle:plugin, numParams)
-{
- new client = GetNativeCell(1);
-
- new NativeVotesFailType:reason = NativeVotesFailType:GetNativeCell(2);
-
- new 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, String: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;
- }
-
- int size = GetNativeCell(3);
- char[] steamId = new char[size];
- GetNativeString(2, steamId, size);
-
- Data_GetTargetSteam(vote, steamId, size);
-}
-
-// native NativeVotes_SetTarget(Handle:vote, 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[20];
-
- if (client <= 0)
- {
- 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(client, NativeVotesCallFailType:reason, 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 String: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 String: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
+
+#include
+#include
+
+EngineVersion g_EngineVersion = Engine_Unknown;
+int g_AppID = 0;
+
+#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,
+ progress_hintcaption,
+ progress_hintcaption_icon,
+ 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];
+
+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 = "26w15a",
+ 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;
+ }
+
+ 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);
+ 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", _, 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);
+
+ 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_icon] = CreateConVar("nativevotes_progress_hintcaption_icon", "icon_tip", "Set the icon to use in instructor hint captions during votes");
+ instructor.Cancel();
+ }
+
+ 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);
+ if (result >= Plugin_Handled)
+ {
+ return 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);
+ }
+ }
+ }
+ }
+ }
+
+ 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 (int 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()
+{
+ bool showHintbox = g_ConVars[progress_hintbox].BoolValue;
+ bool showHintCaption = g_ConVars[progress_hintcaption] != null && g_ConVars[progress_hintcaption].BoolValue;
+ if (!showHintbox && !showHintCaption)
+ {
+ return;
+ }
+
+ float timeRemaining = (g_fStartTime + g_VoteTime) - GetGameTime();
+
+ if (timeRemaining < 0.0)
+ {
+ timeRemaining = 0.0;
+ }
+
+ int iTimeRemaining = RoundFloat(timeRemaining);
+
+ NativeVotesType voteType = Data_GetType(g_hCurVote);
+ bool isMultVote = (voteType == NativeVotesType_Custom_Mult || voteType == NativeVotesType_NextLevelMult);
+ char leaderList[1024];
+ if (isMultVote)
+ {
+ BuildVoteLeaders(leaderList, sizeof(leaderList));
+ }
+ else
+ {
+ leaderList[0] = '\0';
+ }
+
+ for (int client = 1; client <= MaxClients; client++)
+ {
+ if (!IsClientInGame(client) || IsFakeClient(client))
+ continue;
+
+ SetGlobalTransTarget(client);
+ if (showHintbox)
+ {
+ 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", g_NumVotes, g_TotalClients, iTimeRemaining);
+ if (isMultVote && leaderList[0] != '\0')
+ {
+ int baseLen = strlen(baseText);
+ int maxLeaderLen = 256 - baseLen - 1;
+ if (maxLeaderLen < 1)
+ {
+ char caption[1024];
+ 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[1024];
+ Format(caption, sizeof(caption), "%s\n%s", baseText, cappedLeaders);
+ SendInstructorHint(client, caption);
+ }
+ }
+ else
+ {
+ char caption[1024];
+ Format(caption, sizeof(caption), "%t", "Vote Count", g_NumVotes, g_TotalClients, iTimeRemaining);
+ SendInstructorHint(client, caption);
+ }
+ }
+ }
+}
+
+void BuildVoteLeaders(char[] leaderList, int leaderListSize)
+{
+ if (leaderListSize <= 1)
+ {
+ return;
+ }
+
+ leaderList[0] = '\0';
+
+ if (g_NumVotes == 0)
+ {
+ 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);
+
+ /* Build the full leaderboard */
+ for (int i = 0; i < num_items; i++)
+ {
+ int cur_item = votes[i][VOTEINFO_ITEM_INDEX];
+ char choice[1024];
+ Data_GetItemDisplay(g_hCurVote, cur_item, choice, sizeof(choice));
+ char line[1024];
+ Format(line, sizeof(line), "%i. %s: (%i)", i + 1, choice, votes[i][VOTEINFO_ITEM_VOTES]);
+ if (i == 0)
+ {
+ Format(leaderList, leaderListSize, "%s", line);
+ }
+ else
+ {
+ Format(leaderList, leaderListSize, "%s\n%s", leaderList, line);
+ }
+ }
+}
+
+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()
+{
+ 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)
+ {
+ 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, (g_fStartTime + g_VoteTime) - GetGameTime());
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+bool InitializeVoting(NativeVote vote, int time, int flags)
+{
+ if (Internal_IsVoteInProgress())
+ {
+ return false;
+ }
+
+ if (g_hVoteTimer != null)
+ {
+ delete g_hVoteTimer;
+ }
+ if (g_hDisplayTimer != null)
+ {
+ delete g_hDisplayTimer;
+ }
+
+ 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();
+ 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_TotalClients = 0;
+
+ if (g_hDisplayTimer != null)
+ {
+ 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);
+ }
+}
+
+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, (g_fStartTime + g_VoteTime) - GetGameTime());
+ }
+
+ 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)
+ {
+ SetGlobalTransTarget(LANG_SERVER);
+ 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 0;
+ }
+
+ 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 0;
+ }
+
+ int count = GetNativeCell(3);
+ if (count <= 0)
+ {
+ return 0;
+ }
+
+ 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 0;
+ }
+
+ NativeVotesType voteType = Data_GetType(vote);
+
+ if (voteType != NativeVotesType_NextLevelMult && voteType != NativeVotesType_Custom_Mult)
+ {
+ return 0;
+ }
+
+ 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 0;
+ }
+
+ NativeVotesType voteType = Data_GetType(vote);
+
+ if (voteType != NativeVotesType_NextLevelMult && voteType != NativeVotesType_Custom_Mult)
+ {
+ return 0;
+ }
+
+ int position = GetNativeCell(2);
+
+ if (position < 0)
+ {
+ ThrowNativeError(SP_ERROR_NATIVE, "Vote index can't be negative: %d", position);
+ return 0;
+ }
+
+ 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 0;
+ }
+
+ NativeVotesType voteType = Data_GetType(vote);
+
+ if (voteType != NativeVotesType_NextLevelMult && voteType != NativeVotesType_Custom_Mult)
+ {
+ return 0;
+ }
+
+ 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 0;
+ }
+
+ 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 0;
+ }
+
+ 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 0;
+ }
+
+ 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 0;
+ }
+
+ char details[MAX_VOTE_DETAILS_LENGTH];
+
+ 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 0;
+ }
+
+ 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 0;
+ }
+
+ char details[MAX_VOTE_DETAILS_LENGTH];
+
+ 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 0;
+ }
+
+ 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 0;
+ }
+
+ 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 0;
+ }
+
+ 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 0;
+ }
+
+ 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 0;
+ }
+
+ 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 0;
+ }
+
+ 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 0;
+ }
+
+ 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 0;
+ }
+
+ 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];
+ 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 0;
+ }
+
+ 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 0;
+ }
+
+ 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 0;
+ }
+
+ if (callVoteHandler == INVALID_FUNCTION)
+ {
+ ThrowNativeError(SP_ERROR_NATIVE, "CallVoteHandler function was invalid");
+ return 0;
+ }
+
+ 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 0;
+ }
+
+ if (callVoteHandler == INVALID_FUNCTION)
+ {
+ ThrowNativeError(SP_ERROR_NATIVE, "CallVoteHandler function was invalid");
+ return 0;
+ }
+
+ 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 0;
+ }
+
+ 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);
+}
+
+// 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;
+ }
+
+ Event event = CreateEvent("instructor_server_hint_create", true);
+ if (event == null)
+ {
+ return;
+ }
+
+ 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);
+ 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);
+ event.SetInt("hint_forcecaption", 1);
+ event.SetBool("hint_local_player_only", true);
+
+ event.FireToClient(client);
+}
+
+// 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;
+ }
+
+ Event event = CreateEvent("instructor_server_hint_stop", true);
+ if (event == null)
+ {
+ return;
+ }
+ event.SetString("hint_name", "nativevotes_progress");
+ event.FireToClient(client);
+}
\ 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 46f8286..b8265b8 100644
--- a/addons/sourcemod/scripting/nativevotes/game.sp
+++ b/addons/sourcemod/scripting/nativevotes/game.sp
@@ -1,3723 +1,3669 @@
-/**
- * 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.
- */
-static int s_nNativeVoteIdx = 0;
-
-bool isTF2SDKModHack = false;
-
-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") )
- {
- isTF2SDKModHack = true;
- }
-
- //Fix for Open Fortress
- if ( g_EngineVersion == Engine_SDK2013 && ( StrEqual(gameDir, "open_fortress") || StrEqual(gameDir, "tf2classic") || StrEqual(gameDir, "pf2") ) )
- {
- g_EngineVersion = Engine_TF2;
- }
-
- 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)
- {
- 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);
- }
-/*
- case Engine_Left4Dead:
- {
- L4D_ClientSelectedItem(vote, client, item);
- }
-
- case Engine_Left4Dead2:
- {
- L4D2_ClientSelectedItem(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_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);
- }
-
- }
-}
-
-// 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:
- {
- 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");
- }
-
- 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())
- {
- new 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 = view_as(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_AlltalkOn, NativeVotesPass_AlltalkOff,
- 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_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
-
- 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));
- 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("entityid", client);
- castEvent.SetInt("vote_option", item);
- castEvent.Fire();
-}
-
-static void TF2CSGO_UpdateVoteCounts(ArrayList votes)
-{
- if (CheckVoteController())
- {
- int size = votes.Length;
- for (int i = 0; i < size; i++)
- {
- SetEntProp(g_VoteController, Prop_Send, "m_nVoteOptionCount", votes.Get(i), 4, i);
- }
- }
-}
-
-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);
- }
-
- 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)
- }
- }
-
- // 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);
- 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);
- }
-
- 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))
- {
- 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);
- 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.WriteBool(bYesNo);
- }
-
- 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);
- if (!isTF2SDKModHack)
- {
- optionsEvent.SetInt("voteidx", s_nNativeVoteIdx); // TODO(UPDATE)
- }
- 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)
- {
- 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)
- {
- votePass = UserMessageToBfWrite(StartMessageAll("VotePass", USERMSG_RELIABLE));
- }
- else
- {
- votePass = UserMessageToBfWrite(StartMessageOne("VotePass", client, USERMSG_RELIABLE));
- }
-
- votePass.WriteByte(team);
- if (!isTF2SDKModHack)
- {
- 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);
- if (!isTF2SDKModHack)
- {
- voteFailed.WriteNum(s_nNativeVoteIdx); // TODO(UPDATE)
- }
- 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_iActiveIssueIndex", INVALID_ISSUE);
- if (!isTF2SDKModHack)
- {
- SetEntProp(g_VoteController, Prop_Send, "m_nVoteIdx", -1); // TODO(UPDATE)
- }
- }
-
- 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_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)
-{
- int globalEnable = g_Cvar_Votes_Enabled.BoolValue;
- if (!globalEnable && bHideDisabledVotes)
- {
- return;
- }
-
- if (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 = NativeVotesType_None;
-
- 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))
- {
- if (g_Cvar_AutoBalance.BoolValue)
- {
- voteType = NativeVotesType_AutoBalanceOff;
- }
- else
- {
- voteType = NativeVotesType_AutoBalanceOn;
- }
- }
- else if (StrEqual(voteString, TF2_VOTE_STRING_CLASSLIMIT, false))
- {
- if (g_Cvar_ClassLimit.IntValue)
- {
- voteType = NativeVotesType_ClassLimitsOff;
- }
- else
- {
- voteType = 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 if (StrEqual(voteString, TF2_VOTE_STRING_CHANGEMISSION, false))
- {
- voteType = NativeVotesType_ChgMission;
- }
-
- return voteType;
-}
-
-static stock NativeVotesOverride TF2_VoteTypeToVoteOverride(NativeVotesType voteType)
-{
- NativeVotesOverride overrideType = NativeVotesOverride_None;
-
- 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;
- }
- }
-
- return overrideType;
-}
-
-static stock NativeVotesType TF2_VoteOverrideToVoteType(NativeVotesOverride overrideType)
-{
- NativeVotesType voteType = NativeVotesType_None;
-
- 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;
- }
- }
-
- return voteType;
-}
-
-static stock NativeVotesOverride TF2_VoteStringToVoteOverride(const char[] voteString)
-{
- NativeVotesOverride overrideType = NativeVotesOverride_None;
-
- 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;
- }
-
-#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,
+}
+
+//----------------------------------------------------------------------------
+// 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"
+
+//----------------------------------------------------------------------------
+// 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.
+#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;
+
+// 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.
+ */
+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");
+
+ // 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");
+ }
+ }
+}
+
+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, float end_time = 0.0)
+{
+ if (g_bCancelled)
+ {
+ return false;
+ }
+
+ int clients[1];
+ clients[0] = client;
+
+ return Game_DisplayVote(vote, clients, 1, end_time);
+}
+
+bool Game_DisplayVote(NativeVote vote, int[] clients, int num_clients, float end_time = 0.0)
+{
+ 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, end_time);
+ }
+ }
+
+
+#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)
+{
+ g_hVoteTimer = null;
+
+ 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;
+ }
+ case NativeVotesType_ChgCivilian:
+ {
+ passType = NativeVotesPass_ChgCivilian;
+ }
+ case NativeVotesType_ChgGamemode:
+ {
+ passType = NativeVotesPass_ChgGamemode;
+ }
+ 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, float end_time)
+{
+ 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.WriteBool(bYesNo);
+ bfStart.WriteByte(Data_GetTarget(vote) - 1);
+ if (g_AppID == 3545060) // Team Fortress 2 Classified
+ {
+ bfStart.WriteFloat(GetGameTime() + end_time);
+ }
+ }
+ 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);
+
+ 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()
+{
+ 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,
+ NativeVotesType_ChgCivilian,
+ NativeVotesType_ChgDifficulty,
+ NativeVotesType_ChgGamemode:
+ {
+ 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,
+ NativeVotesPass_ChgCivilian,
+ NativeVotesPass_ChgDifficulty,
+ NativeVotesPass_ChgGamemode:
+ {
+ 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);
+ }
+ case NativeVotesType_ChgCivilian:
+ {
+ 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);
+ }
+ }
+
+ 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);
+ }
+ case NativeVotesPass_ChgCivilian:
+ {
+ 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);
+ }
+ }
+}
+
+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);
+ }
+
+ switch (g_AppID)
+ {
+ case 3545060: // Team Fortress 2 Classified
+ {
+ /* 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);
+ }
+ 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);
+ }
+ }
+}
+
+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);
+ }
+ 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;
+}
+
+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 if (StrEqual(voteString, TF2CLASSIFIED_VOTE_STRING_CHANGE_CIVILIAN, false))
+ {
+ 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;
+ }
+
+ 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;
+ }
+ case NativeVotesType_ChgCivilian:
+ {
+ overrideType = NativeVotesOverride_ChgCivilian;
+ }
+ case NativeVotesType_ChgDifficulty:
+ {
+ overrideType = NativeVotesOverride_ChgDifficulty;
+ }
+ case NativeVotesType_ChgGamemode:
+ {
+ overrideType = NativeVotesOverride_ChgGamemode;
+ }
+ 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;
+ }
+ case NativeVotesOverride_ChgCivilian:
+ {
+ voteType = NativeVotesType_ChgCivilian;
+ }
+ case NativeVotesOverride_ChgDifficulty:
+ {
+ voteType = NativeVotesType_ChgDifficulty;
+ }
+ case NativeVotesOverride_ChgGamemode:
+ {
+ voteType = NativeVotesType_ChgGamemode;
+ }
+ 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 if (StrEqual(voteString, TF2CLASSIFIED_VOTE_STRING_CHANGE_CIVILIAN, false))
+ {
+ 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;
+ }
+
+#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);
+ }
+ 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)
+{
+ 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:
+ {
+ strcopy(translationString, maxlength, GetConVarBool(g_Cvar_AutoBalance) ? TF2_VOTE_MENU_AUTOBALANCE_OFF : TF2_VOTE_MENU_AUTOBALANCE_ON);
+ valid = true;
+ }
+ case NativeVotesOverride_ClassLimits:
+ {
+ strcopy(translationString, maxlength, GetConVarInt(g_Cvar_ClassLimit) ? TF2_VOTE_MENU_CLASSLIMIT_OFF : TF2_VOTE_MENU_CLASSLIMIT_ON);
+ valid = true;
+ }
+ case NativeVotesOverride_Extend:
+ {
+ strcopy(translationString, maxlength, TF2_VOTE_MENU_EXTEND);
+ }
+ case NativeVotesOverride_ChgCivilian:
+ {
+ 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;
+}
+
+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
diff --git a/addons/sourcemod/scripting/nativevotes-basecommands.sp b/addons/sourcemod/scripting/nativevotes_basecommands.sp
similarity index 63%
rename from addons/sourcemod/scripting/nativevotes-basecommands.sp
rename to addons/sourcemod/scripting/nativevotes_basecommands.sp
index 8696a54..aff94d0 100644
--- a/addons/sourcemod/scripting/nativevotes-basecommands.sp
+++ b/addons/sourcemod/scripting/nativevotes_basecommands.sp
@@ -1,188 +1,221 @@
-/**
- * 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 PLUGIN_PREFIX "[\x04NativeVotes\x01]"
+
+TopMenu g_TopMenu;
+
+public Plugin myinfo =
+{
+ name = "NativeVotes | Basic Commands",
+ author = "Powerlord and AlliedModders LLC",
+ description = "Revote and Cancel support for NativeVotes",
+ version = "26w15a",
+ 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");
+ RegConsoleCmd("sm_callvote", Command_CallVote, "Start a vote on an issue.");
+}
+
+bool PerformCancelVote(int client)
+{
+ if (!NativeVotes_IsVoteInProgress())
+ {
+ return false;
+ }
+
+ CShowActivity2(client, PLUGIN_PREFIX ... " ", "%t", "Cancelled Vote");
+
+ NativeVotes_Cancel();
+ 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))
+ {
+ 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)
+{
+ switch (action)
+ {
+ 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 (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;
+ }
+
+ if (!NativeVotes_IsVoteInProgress())
+ {
+ return Plugin_Continue;
+ }
+
+ if (!NativeVotes_IsClientInVotePool(client))
+ {
+ if (IsVoteInProgress())
+ {
+ // Let basecommands handle it
+ return Plugin_Continue;
+ }
+
+ CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Cannot participate in vote");
+ return Plugin_Stop;
+ }
+
+ if (NativeVotes_RedrawClientVote(client))
+ {
+ return Plugin_Stop;
+ }
+ else if (!IsVoteInProgress())
+ {
+ CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Cannot change vote");
+ return Plugin_Stop;
+ }
+
+ 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);
+
+ /* Block us from being called twice */
+ if (topmenu == g_TopMenu)
+ {
+ return;
+ }
+
+ /* Save the Handle */
+ g_TopMenu = topmenu;
+
+ TopMenuObject voting_commands = g_TopMenu.FindCategory(ADMINMENU_VOTINGCOMMANDS);
+
+ if (voting_commands != INVALID_TOPMENUOBJECT)
+ {
+ g_TopMenu.AddItem("sm_cancelvote", AdminMenu_CancelVote, voting_commands, "sm_cancelvote", ADMFLAG_VOTE);
+ }
+}
+
+public void OnLibraryRemoved(const char[] name)
+{
+ if (strcmp(name, "adminmenu") == 0)
+ {
+ g_TopMenu = null;
+ }
+}
\ No newline at end of file
diff --git a/addons/sourcemod/scripting/nativevotes_basevotes.sp b/addons/sourcemod/scripting/nativevotes_basevotes.sp
new file mode 100644
index 0000000..32ce422
--- /dev/null
+++ b/addons/sourcemod/scripting/nativevotes_basevotes.sp
@@ -0,0 +1,868 @@
+/**
+ * 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$
+ */
+
+#include
+#include
+
+#undef REQUIRE_PLUGIN
+#include
+#include
+#include
+
+#define PLUGIN_PREFIX "[\x04NativeVotes\x01]"
+
+public Plugin myinfo =
+{
+ name = "NativeVotes | Basic Votes",
+ author = "Powerlord and AlliedModders LLC",
+ description = "NativeVotes Basic Vote Commands",
+ version = "26w15a",
+ url = "https://github.com/Heapons/sourcemod-nativevotes-updated/"
+};
+
+#define VOTE_NO "###no###"
+#define VOTE_YES "###yes###"
+
+// Handle g_hVoteMenu;
+
+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
+{
+ map,
+ kick,
+ ban,
+ 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
+
+#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
+
+Handle g_TopMenu = INVALID_HANDLE;
+
+bool g_KickVoteRegistered = false;
+bool g_isMvM = false;
+
+// Libraries
+bool g_NativeVotes;
+bool g_SourceBans;
+
+//Handle g_Cvar_NativeVotesMenu = INVALID_HANDLE;
+
+#include "nativevotes_basevotes/votekick.sp"
+#include "nativevotes_basevotes/voteban.sp"
+#include "nativevotes_basevotes/votemap.sp"
+
+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]");
+ RegAdminCmd("sm_vote", Command_Vote, ADMFLAG_VOTE, "sm_vote [option1] [option2] ... [option5]");
+
+ 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);
+
+ g_MapList = CreateMenu(MenuHandler_Map, MenuAction_DrawItem|MenuAction_Display);
+ SetMenuTitle(g_MapList, "%T", "Please select a map", LANG_SERVER);
+ SetMenuExitBackButton(g_MapList, true);
+
+ char mapListPath[PLATFORM_MAX_PATH];
+ BuildPath(Path_SM, mapListPath, sizeof(mapListPath), "configs/adminmenu_maplist.ini");
+ 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)
+ {
+ LogMessage("Unloading basevotes to prevent conflicts...");
+ ServerCommand("sm plugins unload basevotes");
+
+ 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))
+ {
+ LogMessage("Moving basevotes to disabled.");
+ }
+ }
+
+ /* 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)
+{
+ 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;
+ RegisterKickVoteCommand();
+ }
+ else if (StrEqual(name, "sourcebans++"))
+ {
+ g_SourceBans = true;
+ }
+}
+
+public void OnLibraryRemoved(const char[] name)
+{
+ if (StrEqual(name, "adminmenu"))
+ {
+ 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 == g_TopMenu)
+ {
+ return;
+ }
+
+ /* Save the Handle */
+ g_TopMenu = topmenu;
+
+ /* Build the "Voting Commands" category */
+ TopMenuObject voting_commands = FindTopMenuCategory(g_TopMenu, ADMINMENU_VOTINGCOMMANDS);
+
+ if (voting_commands != INVALID_TOPMENUOBJECT)
+ {
+ AddToTopMenu(g_TopMenu,
+ "sm_votekick",
+ TopMenuObject_Item,
+ AdminMenu_VoteKick,
+ voting_commands,
+ "sm_votekick",
+ ADMFLAG_VOTE|ADMFLAG_KICK);
+
+ AddToTopMenu(g_TopMenu,
+ "sm_voteban",
+ TopMenuObject_Item,
+ AdminMenu_VoteBan,
+ voting_commands,
+ "sm_voteban",
+ ADMFLAG_VOTE|ADMFLAG_BAN);
+
+ AddToTopMenu(g_TopMenu,
+ "sm_votemap",
+ TopMenuObject_Item,
+ AdminMenu_VoteMap,
+ voting_commands,
+ "sm_votemap",
+ ADMFLAG_VOTE|ADMFLAG_CHANGEMAP);
+ }
+}
+
+public Action Command_Vote(int client, int args)
+{
+ if (args < 1)
+ {
+ CReplyToCommand(client, PLUGIN_PREFIX ... " Usage: sm_vote [option1] [option2] ... [option5]");
+ return Plugin_Handled;
+ }
+
+ if (Internal_IsVoteInProgress())
+ {
+ CReplyToCommand(client, "%t", "Vote in Progress");
+ return Plugin_Handled;
+ }
+
+ if (!TestVoteDelay(client))
+ {
+ return Plugin_Handled;
+ }
+
+ char text[256];
+ GetCmdArgString(text, sizeof(text));
+
+ char answers[5][64];
+ int answerCount = 0;
+ 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, PLUGIN_PREFIX ... " ", "%t", "Initiate Vote", g_VoteArg);
+ g_VoteType = question;
+ Handle voteMenu;
+
+ if (g_NativeVotes)
+ {
+ NativeVotesType nVoteType = answerCount == 0 ? NativeVotesType_Custom_YesNo : NativeVotesType_Custom_Mult;
+ voteMenu = NativeVotes_Create(Handler_NativeVoteCallback, nVoteType, view_as(MENU_ACTIONS_ALL));
+ if (nVoteType == NativeVotesType_Custom_Mult)
+ {
+ for (int i = 0; i < answerCount; i++)
+ {
+ NativeVotes_AddItem(voteMenu, answers[i], answers[i]);
+ }
+ }
+ NativeVotes_SetTitle(voteMenu, g_VoteArg);
+ NativeVotes_DisplayToAll(voteMenu, g_ConVars[sv_vote_timer_duration].IntValue);
+ }
+ else
+ {
+ voteMenu = CreateMenu(Handler_VoteCallback, view_as(MENU_ACTIONS_ALL));
+ SetMenuTitle(voteMenu, g_VoteArg);
+
+ if (answerCount == 0)
+ {
+ AddMenuItem(voteMenu, VOTE_YES, "Yes");
+ AddMenuItem(voteMenu, VOTE_NO, "No");
+ }
+ else
+ {
+ for (int i = 0; i < answerCount; i++)
+ {
+ AddMenuItem(voteMenu, answers[i], answers[i]);
+ }
+ }
+
+ SetMenuExitButton(voteMenu, false);
+ VoteMenuToAll(voteMenu, g_ConVars[sv_vote_timer_duration].IntValue);
+ }
+ return Plugin_Handled;
+}
+
+public Handler_VoteCallback(Handle:menu, MenuAction:action, param1, param2)
+{
+ switch (action)
+ {
+ case MenuAction_End:
+ {
+ CloseHandle(menu);
+ }
+ case MenuAction_Display:
+ {
+ 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]);
+
+ Handle panel = Handle:param2;
+ SetPanelTitle(panel, buffer);
+ }
+ }
+ case MenuAction_DisplayItem:
+ {
+ char display[64];
+ GetMenuItem(menu, param2, "", 0, _, display, sizeof(display));
+
+ if (strcmp(display, "No") == 0 || strcmp(display, "Yes") == 0)
+ {
+ char 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:
+ {
+ char item[64], display[64];
+ float percent, limit;
+ int 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 != 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 ((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);
+
+ 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);
+ }
+
+ switch (g_VoteType)
+ {
+ case question:
+ {
+ if (strcmp(item, VOTE_NO) == 0 || strcmp(item, VOTE_YES) == 0)
+ {
+ 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);
+ }
+ }
+ }
+ else
+ {
+ CPrintToChatAll("%t", "Vote End", g_VoteArg, item);
+ }
+ }
+ case map:
+ {
+ LogAction(-1, -1, "Changing map to %s due to vote.", item);
+ CPrintToChatAll("%t", "Changing map", item);
+ DataPack dp;
+ CreateDataTimer(5.0, Timer_ChangeMap, dp);
+ WritePackString(dp, item);
+ }
+ case 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]))
+ {
+ 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')
+ {
+ strcopy(g_VoteArg, sizeof(g_VoteArg), "Votebanned");
+ }
+
+ 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);
+
+ BanClient(g_VoteClient[VOTE_CLIENTID],
+ 30,
+ BANFLAG_AUTO,
+ g_VoteArg,
+ "Banned by vote",
+ "sm_voteban");
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return Plugin_Continue;
+}
+
+public Handler_NativeVoteCallback(Handle:menu, MenuAction:action, param1, param2)
+{
+ switch (action)
+ {
+
+ case MenuAction_End:
+ {
+ NativeVotes_Close(menu);
+ }
+
+ case MenuAction_Display:
+ {
+ 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]);
+
+ 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:
+ {
+ char item[64], display[64];
+ float percent, limit;
+ int votes, totalVotes;
+
+ 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);
+
+ switch (nVoteType)
+ {
+ 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;
+ }
+ }
+
+ 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.
+ */
+ 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 question:
+ {
+ if (nVoteType == NativeVotesType_Custom_YesNo)
+ {
+ 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);
+ }
+ }
+ }
+ else
+ {
+ CPrintToChatAll("%t", "Vote End", g_VoteArg, item);
+ NativeVotes_DisplayPassCustom(menu, "%t", "Vote End", g_VoteArg, item);
+ }
+ }
+ case 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);
+ DataPack dp;
+ CreateDataTimer(5.0, Timer_ChangeMap, dp);
+ WritePackString(dp, item);
+ }
+ case 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);
+ 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')
+ {
+ 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 Plugin_Continue;
+}
+
+float GetVotePercent(int votes, int totalVotes)
+{
+ return float(votes) / float(totalVotes);
+}
+
+bool TestVoteDelay(int client)
+{
+ int delay = Internal_CheckVoteDelay();
+ if (client <= 0 || GetUserFlagBits(client) & ADMFLAG_GENERIC)
+ {
+ delay = 0;
+ }
+
+ 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, DataPack dp)
+{
+ char mapname[PLATFORM_MAX_PATH];
+
+ 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();
+}
+
+int Internal_CheckVoteDelay()
+{
+ if (g_NativeVotes)
+ {
+ return NativeVotes_CheckVoteDelay();
+ }
+ return CheckVoteDelay();
+}
+
+bool Internal_IsNewVoteAllowed()
+{
+ if (g_NativeVotes)
+ {
+ return NativeVotes_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
new file mode 100644
index 0000000..9f40954
--- /dev/null
+++ b/addons/sourcemod/scripting/nativevotes_basevotes/voteban.sp
@@ -0,0 +1,186 @@
+/**
+ * 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$
+ */
+
+void DisplayVoteBanMenu(int client, int 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[]));
+
+ LogAction(client, target, "\"%L\" initiated a ban vote against %s", client, playerName);
+ CShowActivity2(client, PLUGIN_PREFIX ... " ", "%t", "Initiated Vote Ban", playerName);
+
+ 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);
+ NativeVotes_DisplayToAll(voteMenu, g_ConVars[sv_vote_timer_duration].IntValue);
+ }
+ else
+ {
+ 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, g_ConVars[sv_vote_timer_duration].IntValue);
+ }
+}
+
+void DisplayBanTargetMenu(int client)
+{
+ 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 void AdminMenu_VoteBan(Handle topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int 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 int MenuHandler_Ban(Handle menu, MenuAction action, int param1, int param2)
+{
+ switch (action)
+ {
+ case MenuAction_End:
+ {
+ CloseHandle(menu);
+ }
+ case MenuAction_Cancel:
+ {
+ if (param2 == MenuCancel_ExitBack && g_TopMenu != INVALID_HANDLE)
+ {
+ DisplayTopMenu(g_TopMenu, param1, TopMenuPosition_LastCategory);
+ }
+ }
+ case 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)
+ {
+ CPrintToChat(param1, PLUGIN_PREFIX ... " %t", "Player no longer available");
+ }
+ else if (!CanUserTarget(param1, target))
+ {
+ CPrintToChat(param1, PLUGIN_PREFIX ... " %t", "Unable to target");
+ }
+ else
+ {
+ g_VoteArg[0] = '\0';
+ DisplayVoteBanMenu(param1, target);
+ }
+ }
+ }
+
+ return Plugin_Continue;
+}
+
+public Action Command_Voteban(int client, int args)
+{
+ if (args < 1)
+ {
+ CReplyToCommand(client, PLUGIN_PREFIX ... " Usage: sm_voteban [reason]");
+ return Plugin_Handled;
+ }
+
+ if (Internal_IsVoteInProgress())
+ {
+ CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Vote in Progress");
+ return Plugin_Handled;
+ }
+
+ if (!TestVoteDelay(client))
+ {
+ return Plugin_Handled;
+ }
+
+ char text[256], arg[64];
+ GetCmdArgString(text, sizeof(text));
+ int len = BreakString(text, arg, sizeof(arg));
+ if (len != -1)
+ {
+ strcopy(g_VoteArg, sizeof(g_VoteArg), text[len]);
+ }
+ else
+ {
+ g_VoteArg[0] = '\0';
+ }
+
+ char target_name[MAX_TARGET_LENGTH];
+ int 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;
+}
\ No newline at end of file
diff --git a/addons/sourcemod/scripting/nativevotes_basevotes/votekick.sp b/addons/sourcemod/scripting/nativevotes_basevotes/votekick.sp
new file mode 100644
index 0000000..e14488e
--- /dev/null
+++ b/addons/sourcemod/scripting/nativevotes_basevotes/votekick.sp
@@ -0,0 +1,170 @@
+/**
+ * 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$
+ */
+
+void DisplayVoteKickMenu(int client, int 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[]));
+
+ LogAction(client, target, "\"%L\" initiated a kick vote against %N", client, target);
+ CShowActivity(client, "%t", "Initiated Vote Kick", playerName);
+
+ 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_DisplayToAll(voteMenu, g_ConVars[sv_vote_timer_duration].IntValue);
+ }
+ else
+ {
+ Handle voteMenu = CreateMenu(Handler_VoteCallback, MENU_ACTIONS_ALL);
+ SetMenuTitle(voteMenu, "Votekick Player");
+ AddMenuItem(voteMenu, VOTE_YES, "Yes");
+ AddMenuItem(voteMenu, VOTE_NO, "No");
+ SetMenuExitButton(voteMenu, false);
+ VoteMenuToAll(voteMenu, g_ConVars[sv_vote_timer_duration].IntValue);
+ }
+}
+
+void DisplayKickTargetMenu(int client)
+{
+ 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 void AdminMenu_VoteKick(Handle topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int 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 int MenuHandler_Kick(Handle menu, MenuAction action, int param1, int param2)
+{
+ switch (action)
+ {
+ case MenuAction_End:
+ {
+ CloseHandle(menu);
+ }
+ case MenuAction_Cancel:
+ {
+ if (param2 == MenuCancel_ExitBack && g_TopMenu != INVALID_HANDLE)
+ {
+ DisplayTopMenu(g_TopMenu, param1, TopMenuPosition_LastCategory);
+ }
+ }
+ case 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)
+ {
+ CPrintToChat(param1, PLUGIN_PREFIX ... " %t", "Player no longer available");
+ }
+ else if (!CanUserTarget(param1, target))
+ {
+ CPrintToChat(param1, PLUGIN_PREFIX ... " %t", "Unable to target");
+ }
+ else
+ {
+ g_VoteArg[0] = '\0';
+ DisplayVoteKickMenu(param1, target);
+ }
+ }
+ }
+
+ return Plugin_Continue;
+}
+
+public Action Command_Votekick(int client, int args)
+{
+ if (args < 1)
+ {
+ CReplyToCommand(client, PLUGIN_PREFIX ... " Usage: sm_votekick [reason]");
+ return Plugin_Handled;
+ }
+ if (Internal_IsVoteInProgress())
+ {
+ CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Vote in Progress");
+ return Plugin_Handled;
+ }
+ if (!TestVoteDelay(client))
+ {
+ return Plugin_Handled;
+ }
+ char text[256], arg[64];
+ GetCmdArgString(text, sizeof(text));
+ 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]);
+ }
+ 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..e147ed3
--- /dev/null
+++ b/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp
@@ -0,0 +1,329 @@
+/**
+ * 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$
+ */
+
+Handle g_MapList;
+int g_mapCount;
+
+Handle g_SelectedMaps;
+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");
+
+ g_VoteType = map;
+
+ 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[]));
+ }
+
+ if (g_NativeVotes && (mapCount == 1 || NativeVotes_IsVoteTypeSupported(NativeVotesType_NextLevelMult)) )
+ {
+ Handle voteMenu;
+ if (mapCount == 1)
+ {
+ strcopy(g_VoteInfo[VOTE_NAME], sizeof(g_VoteInfo[]), displayNames[0]);
+
+ 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, view_as(MENU_ACTIONS_ALL));
+
+ g_VoteInfo[VOTE_NAME][0] = '\0';
+
+ // No title, builtin type
+ for (int i = 0; i < mapCount; i++)
+ {
+ NativeVotes_AddItem(voteMenu, resolvedMaps[i], displayNames[i]);
+ }
+ }
+ NativeVotes_DisplayToAll(voteMenu, g_ConVars[sv_vote_timer_duration].IntValue);
+ }
+ else
+ {
+ Handle voteMenu = CreateMenu(Handler_VoteCallback, 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 (int i = 0; i < mapCount; i++)
+ {
+ AddMenuItem(voteMenu, resolvedMaps[i], displayNames[i]);
+ }
+ }
+ SetMenuExitButton(voteMenu, false);
+ VoteMenuToAll(voteMenu, g_ConVars[sv_vote_timer_duration].IntValue);
+ }
+}
+
+void ResetMenu()
+{
+ g_VoteMapInUse = false;
+ ClearArray(g_SelectedMaps);
+}
+
+void ConfirmVote(int client)
+{
+ Handle menu = CreateMenu(MenuHandler_Confirm);
+ char title[100];
+
+ Format(title, sizeof(title), "%T:", "Confirm Vote", client);
+ SetMenuTitle(menu, title);
+ SetMenuExitBackButton(menu, true);
+
+ char itemtext[256];
+ Format(itemtext, sizeof(itemtext), "%T", "Start the Vote", client);
+ AddMenuItem(menu, "Confirm", itemtext);
+
+ DisplayMenu(menu, client, MENU_TIME_FOREVER);
+}
+
+public int MenuHandler_Confirm(Handle menu, MenuAction action, int param1, int param2)
+{
+ switch (action)
+ {
+ case MenuAction_End:
+ {
+ CloseHandle(menu);
+ g_VoteMapInUse = false;
+ }
+ case MenuAction_Cancel:
+ {
+ ResetMenu();
+ if (param2 == MenuCancel_ExitBack && g_TopMenu != INVALID_HANDLE)
+ {
+ DisplayTopMenu(g_TopMenu, param1, TopMenuPosition_LastCategory);
+ }
+ }
+ case MenuAction_Select:
+ {
+ 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();
+ }
+ }
+
+ return Plugin_Continue;
+}
+
+public int MenuHandler_Map(Handle menu, MenuAction action, int param1, int param2)
+{
+ switch (action)
+ {
+ case MenuAction_Cancel:
+ {
+ if (param2 == MenuCancel_ExitBack && g_TopMenu != INVALID_HANDLE)
+ {
+ ConfirmVote(param1);
+ }
+ else // no action was selected.
+ {
+ /* Re-enable the menu option */
+ ResetMenu();
+ }
+ }
+ case MenuAction_DrawItem:
+ {
+ 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;
+ }
+ }
+ case MenuAction_Select:
+ {
+ 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);
+ }
+ }
+ case MenuAction_Display:
+ {
+ char title[128];
+ Format(title, sizeof(title), "%T", "Please select a map", param1);
+ SetPanelTitle(view_as(param2), title);
+ }
+ }
+
+ return Plugin_Continue;
+}
+
+public void AdminMenu_VoteMap(Handle topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength)
+{
+ switch (action)
+ {
+ case TopMenuAction_DisplayOption:
+ {
+ Format(buffer, maxlength, "%T", "Map vote", param);
+ }
+ case TopMenuAction_SelectOption:
+ {
+ if (!g_VoteMapInUse)
+ {
+ ResetMenu();
+ g_VoteMapInUse = true;
+ DisplayMenu(g_MapList, param, MENU_TIME_FOREVER);
+ }
+ else
+ {
+ CPrintToChat(param, PLUGIN_PREFIX ... " %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;
+ }
+ }
+}
+
+public Action Command_Votemap(int client, int args)
+{
+ if (args < 1)
+ {
+ CReplyToCommand(client, PLUGIN_PREFIX ... " Usage: sm_votemap [mapname2] ... [mapname5]");
+ return Plugin_Handled;
+ }
+
+ if (Internal_IsVoteInProgress())
+ {
+ CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Vote in Progress");
+ return Plugin_Handled;
+ }
+
+ if (!TestVoteDelay(client) && !CheckCommandAccess(client, "sm_votemap", ADMFLAG_CHANGEMAP))
+ {
+ return Plugin_Handled;
+ }
+
+ char text[256];
+ GetCmdArgString(text, sizeof(text));
+
+ 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, PLUGIN_PREFIX ... " %t", "Map was not found", maps[mapCount]);
+ return Plugin_Handled;
+ }
+
+ mapCount++;
+
+ if (pos != -1)
+ {
+ len += pos;
+ }
+ }
+
+ DisplayVoteMapMenu(client, mapCount, maps);
+
+ return Plugin_Handled;
+}
+
+Handle g_map_array = INVALID_HANDLE;
+int g_map_serial = -1;
+
+int LoadMapList(Handle menu)
+{
+ 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);
+
+ char map_name[PLATFORM_MAX_PATH];
+ int map_count = GetArraySize(g_map_array);
+
+ for (int 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..f7dde42
--- /dev/null
+++ b/addons/sourcemod/scripting/nativevotes_funvotes.sp
@@ -0,0 +1,571 @@
+/**
+ * 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$
+ */
+
+#include
+#include
+
+#undef REQUIRE_PLUGIN
+#include
+#include
+
+#define PLUGIN_PREFIX "[\x04NativeVotes\x01]"
+
+public Plugin myinfo =
+{
+ name = "NativeVotes | Fun Votes",
+ author = "Powerlord and AlliedModders LLC",
+ description = "NativeVotes Fun Vote Commands",
+ version = "26w15a",
+ url = "https://github.com/Heapons/sourcemod-nativevotes-updated/"
+};
+
+#define VOTE_NO "###no###"
+#define VOTE_YES "###yes###"
+
+// Handle g_hVoteMenu;
+
+enum
+{
+ sv_gravity,
+ sv_alltalk,
+ mp_friendlyfire,
+ sv_vote_timer_duration,
+
+ vote_gravity,
+ vote_burn,
+ vote_slay,
+ vote_alltalk,
+ vote_ff,
+
+ MAX_CONVARS
+}
+
+ConVar g_ConVars[MAX_CONVARS];
+
+enum VoteType
+{
+ gravity,
+ burn,
+ slay,
+ alltalk,
+ ff
+};
+
+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
+
+#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
+
+Handle g_TopMenu;
+
+// NativeVotes
+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 void 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_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()
+{
+ if (FindPluginByFile("basefunvotes.smx") != null)
+ {
+ SetFailState("This plugin replaces basefuncommands. You cannot run both at once.");
+ }
+
+ if (FindPluginByFile("funvotes.smx") != null)
+ {
+ LogMessage("Unloading funvotes to prevent conflicts...");
+ ServerCommand("sm plugins unload funvotes");
+
+ 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 */
+ Handle topmenu;
+ if (LibraryExists("adminmenu") && ((topmenu = GetAdminTopMenu()) != null))
+ {
+ OnAdminMenuReady(topmenu);
+ }
+
+ g_NativeVotes = LibraryExists("nativevotes") && NativeVotes_IsVoteTypeSupported(NativeVotesType_Custom_YesNo);
+}
+
+public void OnLibraryAdded(const char[] name)
+{
+ Handle topmenu;
+ if (StrEqual(name, "adminmenu") && ((topmenu = GetAdminTopMenu()) != null))
+ {
+ OnAdminMenuReady(topmenu);
+ }
+ else if (StrEqual(name, "nativevotes") && NativeVotes_IsVoteTypeSupported(NativeVotesType_Custom_YesNo))
+ {
+ g_NativeVotes = true;
+ }
+}
+
+public void OnLibraryRemoved(const char[] name)
+{
+ if (StrEqual(name, "adminmenu"))
+ {
+ g_TopMenu = null;
+ }
+ else if (StrEqual(name, "nativevotes"))
+ {
+ g_NativeVotes = false;
+ }
+}
+
+public void OnAdminMenuReady(Handle topmenu)
+{
+ /* Block us from being called twice */
+ if (topmenu == g_TopMenu)
+ {
+ return;
+ }
+
+ /* Save the Handle */
+ g_TopMenu = topmenu;
+
+ /* Build the "Voting Commands" category */
+ TopMenuObject voting_commands = FindTopMenuCategory(g_TopMenu, ADMINMENU_VOTINGCOMMANDS);
+
+ if (voting_commands != INVALID_TOPMENUOBJECT)
+ {
+ AddToTopMenu(g_TopMenu,
+ "sm_votegravity",
+ TopMenuObject_Item,
+ AdminMenu_VoteGravity,
+ voting_commands,
+ "sm_votegravity",
+ ADMFLAG_VOTE);
+
+ AddToTopMenu(g_TopMenu,
+ "sm_voteburn",
+ TopMenuObject_Item,
+ AdminMenu_VoteBurn,
+ voting_commands,
+ "sm_voteburn",
+ ADMFLAG_VOTE|ADMFLAG_SLAY);
+
+ AddToTopMenu(g_TopMenu,
+ "sm_voteslay",
+ TopMenuObject_Item,
+ AdminMenu_VoteSlay,
+ voting_commands,
+ "sm_voteslay",
+ ADMFLAG_VOTE|ADMFLAG_SLAY);
+
+ AddToTopMenu(g_TopMenu,
+ "sm_votealltalk",
+ TopMenuObject_Item,
+ AdminMenu_VoteAllTalk,
+ voting_commands,
+ "sm_votealltalk",
+ ADMFLAG_VOTE);
+
+ AddToTopMenu(g_TopMenu,
+ "sm_voteff",
+ TopMenuObject_Item,
+ AdminMenu_VoteFF,
+ voting_commands,
+ "sm_voteff",
+ ADMFLAG_VOTE);
+ }
+}
+
+public int Handler_VoteCallback(Handle menu, MenuAction action, int param1, int param2)
+{
+ switch (action)
+ {
+ case MenuAction_End:
+ {
+ //VoteMenuClose();
+ CloseHandle(menu);
+ }
+
+ case MenuAction_Display:
+ {
+ char title[64];
+ GetMenuTitle(menu, title, sizeof(title));
+
+ char buffer[255];
+ Format(buffer, sizeof(buffer), "%T", title, param1, g_VoteInfo[VOTE_NAME]);
+
+ Handle panel = view_as(param2);
+ SetPanelTitle(panel, buffer);
+ }
+
+ case MenuAction_DisplayItem:
+ {
+ char display[64];
+ GetMenuItem(menu, param2, "", 0, _, display, sizeof(display));
+
+ if (strcmp(display, VOTE_NO) == 0 || strcmp(display, VOTE_YES) == 0)
+ {
+ char 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:
+ {
+ char item[64];
+ float percent, limit;
+ int 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_ConVars[vote_gravity + view_as(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);
+
+ char name[MAX_NAME_LENGTH];
+ GetPlayerName(g_VoteClient[VOTE_CLIENTID], name, sizeof(name));
+
+ switch (g_VoteType)
+ {
+ case gravity:
+ {
+ CPrintToChatAll("%t", "Cvar changed", "sv_gravity", item);
+ LogAction(-1, -1, "Changing gravity to %s due to vote.", item);
+ SetConVarInt(g_ConVars[sv_gravity], StringToInt(item));
+ }
+ 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]);
+
+ 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]);
+
+ 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"));
+ 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 Plugin_Continue;
+}
+
+public int Handler_NativeVoteCallback(Handle menu, MenuAction action, int param1, int param2)
+{
+ switch (action)
+ {
+ case MenuAction_End:
+ {
+ NativeVotes_Close(menu);
+ }
+
+ case MenuAction_Display:
+ {
+ NativeVotesType nVoteType = NativeVotes_GetType(menu);
+ if (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]);
+
+ 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:
+ {
+ char item[64];
+ float percent, limit;
+ int votes, totalVotes;
+
+ 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_ConVars[vote_gravity + view_as(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 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_ConVars[sv_gravity], StringToInt(item));
+ }
+ 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]);
+
+ 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]);
+
+ 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"));
+ 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 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 Plugin_Continue;
+}
+
+float GetVotePercent(int votes, int totalVotes)
+{
+ return float(votes) / float(totalVotes);
+}
+
+bool TestVoteDelay(int client)
+{
+ int delay = Internal_CheckVoteDelay();
+ if (client <= 0 || GetUserFlagBits(client) & ADMFLAG_GENERIC)
+ {
+ delay = 0;
+ }
+
+ 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();
+}
+
+int Internal_CheckVoteDelay()
+{
+ if (g_NativeVotes)
+ {
+ return NativeVotes_CheckVoteDelay();
+ }
+
+ return CheckVoteDelay();
+}
+
+bool Internal_IsNewVoteAllowed()
+{
+ if (g_NativeVotes)
+ {
+ return NativeVotes_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/votealltalk.sp b/addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp
new file mode 100644
index 0000000..054c174
--- /dev/null
+++ b/addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp
@@ -0,0 +1,138 @@
+ /**
+ * 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$
+ */
+
+void DisplayVoteAllTalkMenu(int client)
+{
+ if (Internal_IsVoteInProgress())
+ {
+ CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Vote in Progress");
+ return;
+ }
+
+ if (!TestVoteDelay(client))
+ {
+ return;
+ }
+
+ LogAction(client, -1, "\"%L\" initiated an alltalk vote.", client);
+ CShowActivity2(client, PLUGIN_PREFIX ... " ", "%t", "Initiated Vote Alltalk");
+
+ g_VoteType = alltalk;
+ g_VoteInfo[VOTE_NAME][0] = '\0';
+
+ if (g_NativeVotes)
+ {
+ Handle hVoteMenu;
+ if (NativeVotes_IsVoteTypeSupported(NativeVotesType_AlltalkOn))
+ {
+ NativeVotesType nVoteType;
+ if (GetConVarBool(g_ConVars[sv_alltalk]))
+ {
+ nVoteType = NativeVotesType_AlltalkOff;
+ }
+ else
+ {
+ nVoteType = NativeVotesType_AlltalkOn;
+ }
+ hVoteMenu = NativeVotes_Create(Handler_NativeVoteCallback, nVoteType, view_as(MENU_ACTIONS_ALL));
+ }
+ else
+ {
+ hVoteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Custom_YesNo, view_as(MENU_ACTIONS_ALL));
+
+ if (GetConVarBool(g_ConVars[sv_alltalk]))
+ {
+ NativeVotes_SetTitle(hVoteMenu, "Votealltalk Off");
+ }
+ else
+ {
+ NativeVotes_SetTitle(hVoteMenu, "Votealltalk On");
+ }
+ }
+
+ NativeVotes_DisplayToAll(hVoteMenu, g_ConVars[sv_vote_timer_duration].IntValue);
+ }
+ else
+ {
+ Handle hVoteMenu = CreateMenu(Handler_VoteCallback, MENU_ACTIONS_ALL);
+
+ if (GetConVarBool(g_ConVars[sv_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, g_ConVars[sv_vote_timer_duration].IntValue);
+ }
+}
+
+
+public void AdminMenu_VoteAllTalk(Handle topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength)
+{
+ switch (action)
+ {
+ 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(int client, int args)
+{
+ if (args > 0)
+ {
+ CReplyToCommand(client, PLUGIN_PREFIX ... " 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..ef0a4e1
--- /dev/null
+++ b/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp
@@ -0,0 +1,192 @@
+/**
+ * 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$
+ */
+
+void DisplayVoteBurnMenu(int client, int target, char name[MAX_NAME_LENGTH])
+{
+ if (!IsPlayerAlive(target))
+ {
+ CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Cannot be performed on dead", name);
+ return;
+ }
+
+ g_VoteClient[VOTE_CLIENTID] = 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 %N", client, target);
+ CShowActivity2(client, PLUGIN_PREFIX ... " ", "%t", "Initiated Vote Burn", playerName);
+
+ 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_DisplayToAll(hVoteMenu, g_ConVars[sv_vote_timer_duration].IntValue);
+ }
+ else
+ {
+ Handle hVoteMenu = CreateMenu(Handler_VoteCallback, view_as(MENU_ACTIONS_ALL));
+ SetMenuTitle(hVoteMenu, "Voteburn player");
+ AddMenuItem(hVoteMenu, VOTE_YES, "Yes");
+ AddMenuItem(hVoteMenu, VOTE_NO, "No");
+ SetMenuExitButton(hVoteMenu, false);
+ VoteMenuToAll(hVoteMenu, g_ConVars[sv_vote_timer_duration].IntValue);
+ }
+}
+
+void DisplayBurnTargetMenu(int client)
+{
+ Handle menu = CreateMenu(MenuHandler_Burn);
+
+ 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 void AdminMenu_VoteBurn(Handle topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength)
+{
+ switch (action)
+ {
+ 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 int MenuHandler_Burn(Handle menu, MenuAction action, int param1, int param2)
+{
+ switch (action)
+ {
+ case MenuAction_End:
+ {
+ CloseHandle(menu);
+ }
+ case MenuAction_Cancel:
+ {
+ if (param2 == MenuCancel_ExitBack && g_TopMenu != INVALID_HANDLE)
+ {
+ DisplayTopMenu(g_TopMenu, param1, TopMenuPosition_LastCategory);
+ }
+ }
+ case 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)
+ {
+ CPrintToChat(param1, PLUGIN_PREFIX ... " %t", "Player no longer available");
+ }
+ else if (!CanUserTarget(param1, target))
+ {
+ CPrintToChat(param1, PLUGIN_PREFIX ... " %t", "Unable to target");
+ }
+ else if (!IsPlayerAlive(target))
+ {
+ CPrintToChat(param1, PLUGIN_PREFIX ... " %t", "Player has since died");
+ }
+ else
+ {
+ DisplayVoteBurnMenu(param1, target, name);
+ }
+ }
+ }
+
+ return Plugin_Continue;
+}
+
+public Action Command_VoteBurn(int client, int args)
+{
+ if (args < 1)
+ {
+ CReplyToCommand(client, PLUGIN_PREFIX ... " Usage: sm_voteburn ");
+ return Plugin_Handled;
+ }
+
+ if (Internal_IsVoteInProgress())
+ {
+ CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Vote in Progress");
+ return Plugin_Handled;
+ }
+
+ if (!TestVoteDelay(client))
+ {
+ return Plugin_Handled;
+ }
+
+ char text[256], arg[64];
+ GetCmdArgString(text, sizeof(text));
+
+ BreakString(text, arg, sizeof(arg));
+
+ char target_name[MAX_TARGET_LENGTH];
+ int 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;
+}
\ No newline at end of file
diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp b/addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp
new file mode 100644
index 0000000..72527c2
--- /dev/null
+++ b/addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp
@@ -0,0 +1,119 @@
+ /**
+ * 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$
+ */
+
+void DisplayVoteFFMenu(int client)
+{
+ if (Internal_IsVoteInProgress())
+ {
+ CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Vote in Progress");
+ return;
+ }
+
+ if (!TestVoteDelay(client))
+ {
+ return;
+ }
+
+ LogAction(client, -1, "\"%L\" initiated a friendly fire vote.", client);
+ CShowActivity2(client, PLUGIN_PREFIX ... " ", "%t", "Initiated Vote FF");
+
+ g_VoteType = ff;
+ g_VoteInfo[VOTE_NAME][0] = '\0';
+
+ if (g_NativeVotes)
+ {
+ Handle hVoteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Custom_YesNo, view_as(MENU_ACTIONS_ALL));
+
+ if (GetConVarBool(g_ConVars[mp_friendlyfire]))
+ {
+ NativeVotes_SetTitle(hVoteMenu, "Voteff Off");
+ }
+ else
+ {
+ NativeVotes_SetTitle(hVoteMenu, "Voteff On");
+ }
+ NativeVotes_DisplayToAll(hVoteMenu, g_ConVars[sv_vote_timer_duration].IntValue);
+ }
+ else
+ {
+ Handle hVoteMenu = CreateMenu(Handler_VoteCallback, view_as(MENU_ACTIONS_ALL));
+
+ if (GetConVarBool(g_ConVars[mp_friendlyfire]))
+ {
+ SetMenuTitle(hVoteMenu, "Voteff Off");
+ }
+ else
+ {
+ SetMenuTitle(hVoteMenu, "Voteff On");
+ }
+
+ AddMenuItem(hVoteMenu, VOTE_YES, "Yes");
+ AddMenuItem(hVoteMenu, VOTE_NO, "No");
+ SetMenuExitButton(hVoteMenu, false);
+ VoteMenuToAll(hVoteMenu, g_ConVars[sv_vote_timer_duration].IntValue);
+ }
+}
+
+public void AdminMenu_VoteFF(Handle topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength)
+{
+ switch (action)
+ {
+ 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(int client, int args)
+{
+ if (args > 0)
+ {
+ CReplyToCommand(client, PLUGIN_PREFIX ... " 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..3948a58
--- /dev/null
+++ b/addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp
@@ -0,0 +1,159 @@
+ /**
+ * 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$
+ */
+
+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");
+
+ 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]);
+
+ 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, view_as(MENU_ACTIONS_ALL));
+ NativeVotes_SetTitle(hVoteMenu, "Gravity Vote");
+ for (int i = 0; i < count; i++)
+ {
+ NativeVotes_AddItem(hVoteMenu, items[i], items[i]);
+ }
+ }
+ NativeVotes_DisplayToAll(hVoteMenu, g_ConVars[sv_vote_timer_duration].IntValue);
+ }
+ else
+ {
+ Handle hVoteMenu = CreateMenu(Handler_VoteCallback, view_as(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 (int i = 0; i < count; i++)
+ {
+ AddMenuItem(hVoteMenu, items[i], items[i]);
+ }
+ }
+
+ SetMenuExitButton(hVoteMenu, false);
+ VoteMenuToAll(hVoteMenu, g_ConVars[sv_vote_timer_duration].IntValue);
+ }
+}
+
+public void AdminMenu_VoteGravity(Handle topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int 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 */
+ 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(int client, int args)
+{
+ if (args < 1)
+ {
+ CReplyToCommand(client, PLUGIN_PREFIX ... " Usage: sm_votegravity [amount2] ... [amount5]");
+ return Plugin_Handled;
+ }
+
+ if (Internal_IsVoteInProgress())
+ {
+ CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Vote in Progress");
+ return Plugin_Handled;
+ }
+
+ if (!TestVoteDelay(client))
+ {
+ return Plugin_Handled;
+ }
+
+ char text[256];
+ GetCmdArgString(text, sizeof(text));
+
+ char items[5][64];
+ int count;
+ int len, pos;
+
+ while (pos != -1 && count < 5)
+ {
+ pos = BreakString(text[len], items[count], sizeof(items[]));
+
+ float temp;
+ if (StringToFloatEx(items[count], temp) == 0)
+ {
+ CReplyToCommand(client, PLUGIN_PREFIX ... " %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..1376a3d
--- /dev/null
+++ b/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp
@@ -0,0 +1,194 @@
+/**
+ * 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$
+ */
+
+void DisplayVoteSlayMenu(int client, int target, char name[MAX_NAME_LENGTH])
+{
+ if (!IsPlayerAlive(target))
+ {
+ CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Cannot be performed on dead", name);
+ return;
+ }
+
+ g_VoteClient[VOTE_CLIENTID] = 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 %N", client, target);
+ CShowActivity2(client, PLUGIN_PREFIX ... " ", "%t", "Initiated Vote Slay", playerName);
+
+ 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);
+ NativeVotes_DisplayToAll(hVoteMenu, g_ConVars[sv_vote_timer_duration].IntValue);
+ }
+ else
+ {
+ Handle hVoteMenu = CreateMenu(Handler_VoteCallback, view_as(MENU_ACTIONS_ALL));
+ SetMenuTitle(hVoteMenu, "Voteslay Player");
+ AddMenuItem(hVoteMenu, VOTE_YES, "Yes");
+ AddMenuItem(hVoteMenu, VOTE_NO, "No");
+ SetMenuExitButton(hVoteMenu, false);
+ VoteMenuToAll(hVoteMenu, g_ConVars[sv_vote_timer_duration].IntValue);
+ }
+}
+
+void DisplaySlayTargetMenu(int client)
+{
+ Handle menu = CreateMenu(MenuHandler_Slay);
+ char 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 void AdminMenu_VoteSlay(Handle topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength)
+{
+ switch (action)
+ {
+ 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 int MenuHandler_Slay(Handle menu, MenuAction action, int param1, int param2)
+{
+ switch (action)
+ {
+ case MenuAction_End:
+ {
+ CloseHandle(menu);
+ }
+ case MenuAction_Cancel:
+ {
+ if (param2 == MenuCancel_ExitBack && g_TopMenu != INVALID_HANDLE)
+ {
+ DisplayTopMenu(g_TopMenu, param1, TopMenuPosition_LastCategory);
+ }
+ }
+ case 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)
+ {
+ CPrintToChat(param1, PLUGIN_PREFIX ... " %t", "Player no longer available");
+ }
+ else if (!CanUserTarget(param1, target))
+ {
+ CPrintToChat(param1, PLUGIN_PREFIX ... " %t", "Unable to target");
+ }
+ else if (!IsPlayerAlive(target))
+ {
+ CPrintToChat(param1, PLUGIN_PREFIX ... " %t", "Player has since died");
+ }
+ else
+ {
+ DisplayVoteSlayMenu(param1, target, name);
+ }
+ }
+ }
+
+ return Plugin_Continue;
+}
+
+public Action Command_VoteSlay(int client, int args)
+{
+ if (args < 1)
+ {
+ CReplyToCommand(client, PLUGIN_PREFIX ... " Usage: sm_voteslay ");
+ return Plugin_Handled;
+ }
+
+ if (Internal_IsVoteInProgress())
+ {
+ CReplyToCommand(client, PLUGIN_PREFIX ... " %t", "Vote in Progress");
+ return Plugin_Handled;
+ }
+
+ if (!TestVoteDelay(client))
+ {
+ return Plugin_Handled;
+ }
+
+ char text[256], arg[64];
+ GetCmdArgString(text, sizeof(text));
+
+ BreakString(text, arg, sizeof(arg));
+
+ char target_name[MAX_TARGET_LENGTH];
+ int 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;
+}
\ No newline at end of file
diff --git a/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp b/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp
deleted file mode 100644
index 8bc7f44..0000000
--- a/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp
+++ /dev/null
@@ -1,169 +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 .
- *
- * 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
-
-#pragma semicolon 1
-#pragma newdecls required
-
-#define VERSION "3.0.0"
-
-ConVar g_Cvar_Votes;
-ConVar g_Cvar_KickVote;
-ConVar g_Cvar_KickVoteMvM;
-
-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 = VERSION,
- url = "https://forums.alliedmods.net/showthread.php?t=208008"
-};
-
-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");
-
- HookConVarChange(g_Cvar_Votes, Cvar_CheckEnable);
- HookConVarChange(g_Cvar_KickVote, Cvar_CheckEnable);
- 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()
-{
- 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 Cvar_CheckEnable(ConVar convar, const char[] oldValue, const char[] newValue)
-{
- if (!g_bMapActive)
- return;
-
- CheckStatus();
-}
-
-void CheckStatus()
-{
- bool bIsMvM = IsMvM();
- if (g_bRegistered)
- {
- if (!g_Cvar_Votes.BoolValue ||
- (bIsMvM && !g_Cvar_KickVoteMvM.BoolValue) ||
- (!bIsMvM && !g_Cvar_KickVote.BoolValue)
- )
- {
- LogMessage("Disabling.");
- UnregisterVoteCommand();
- }
- }
- else
- {
- if (g_Cvar_Votes.BoolValue &&
- ((bIsMvM && g_Cvar_KickVoteMvM.BoolValue) ||
- (!bIsMvM && g_Cvar_KickVote.BoolValue))
- )
- {
- LogMessage("Enabling.");
- RegisterVoteCommand();
- }
- }
-}
-
-stock bool IsMvM()
-{
- return view_as(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);
- PrintToChat(client, "%t", "Unable to target");
- return Plugin_Stop;
- }
-
- return Plugin_Continue;
-}
\ No newline at end of file
diff --git a/addons/sourcemod/scripting/nativevotes_mapchooser.sp b/addons/sourcemod/scripting/nativevotes_mapchooser.sp
index ffaccc0..49b3ef8 100644
--- a/addons/sourcemod/scripting/nativevotes_mapchooser.sp
+++ b/addons/sourcemod/scripting/nativevotes_mapchooser.sp
@@ -1,1569 +1,1922 @@
-/**
- * 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
-
-#undef REQUIRE_PLUGIN
-#include
-#define REQUIRE_PLUGIN
-
-#pragma semicolon 1
-#pragma newdecls required
-
-#define VERSION "1.8.0 beta 1"
-
-public Plugin myinfo =
-{
- name = "NativeVotes MapChooser",
- author = "AlliedModders LLC and Powerlord",
- description = "Automated Map Voting",
- version = VERSION,
- url = "https://forums.alliedmods.net/showthread.php?t=208010"
-};
-
-/* 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;
-
-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;
-#define LIBRARY "nativevotes"
-
-public void OnPluginStart()
-{
- LoadTranslations("mapchooser.phrases");
- LoadTranslations("common.phrases");
-
- 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);
- 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);
-
- 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