From ceeaf938a215df3009f0ebfe78d0821766d49b49 Mon Sep 17 00:00:00 2001
From: Heapons <30896390+Heapons@users.noreply.github.com>
Date: Sat, 27 Dec 2025 20:22:44 +0100
Subject: [PATCH 001/128] Update NativeVotes! More info in README.md (+ French
translations)
---
.github/workflows/nativevotes.yml | 15 +-
.gitignore | 77 ++
README.md | 71 +-
.../scripting/csgo_votestart_test.sp | 240 -----
.../scripting/include/multicolors.inc | 460 ++++++++
.../scripting/include/multicolors/colors.inc | 897 ++++++++++++++++
.../include/multicolors/morecolors.inc | 992 ++++++++++++++++++
.../scripting/include/nativevotes.inc | 1 +
addons/sourcemod/scripting/nativevotes.sp | 10 +-
.../sourcemod/scripting/nativevotes/game.sp | 8 +-
...ommands.sp => nativevotes_basecommands.sp} | 376 +++----
.../scripting/nativevotes_basevotes.sp | 734 +++++++++++++
.../nativevotes_basevotes/voteban.sp | 202 ++++
.../nativevotes_basevotes/votekick.sp | 190 ++++
.../nativevotes_basevotes/votemap.sp | 336 ++++++
.../scripting/nativevotes_funvotes.sp | 562 ++++++++++
.../nativevotes_funvotes/votealltalk.sp | 140 +++
.../nativevotes_funvotes/voteburn.sp | 200 ++++
.../scripting/nativevotes_funvotes/voteff.sp | 121 +++
.../nativevotes_funvotes/votegravity.sp | 164 +++
.../nativevotes_funvotes/voteslay.sp | 199 ++++
.../nativevotes_kickvote_immunity.sp | 6 +-
.../scripting/nativevotes_mapchooser.sp | 38 +-
.../scripting/nativevotes_nominations.sp | 264 +++--
.../scripting/nativevotes_rockthevote.sp | 161 ++-
.../scripting/nativevotes_votemanager_test.sp | 252 -----
.../sourcemod/scripting/nativevotes_voterp.sp | 103 ++
.../scripting/nativevotes_votetest.sp | 437 --------
.../scripting/votedelay_changelevel.sp | 223 ----
addons/sourcemod/scripting/votediagnostics.sp | 790 --------------
addons/sourcemod/scripting/votefailed.sp | 151 ---
.../translations/fr/nativevotes.phrases.txt | 12 +
32 files changed, 5926 insertions(+), 2506 deletions(-)
create mode 100644 .gitignore
delete mode 100644 addons/sourcemod/scripting/csgo_votestart_test.sp
create mode 100644 addons/sourcemod/scripting/include/multicolors.inc
create mode 100644 addons/sourcemod/scripting/include/multicolors/colors.inc
create mode 100644 addons/sourcemod/scripting/include/multicolors/morecolors.inc
rename addons/sourcemod/scripting/{nativevotes-basecommands.sp => nativevotes_basecommands.sp} (87%)
create mode 100644 addons/sourcemod/scripting/nativevotes_basevotes.sp
create mode 100644 addons/sourcemod/scripting/nativevotes_basevotes/voteban.sp
create mode 100644 addons/sourcemod/scripting/nativevotes_basevotes/votekick.sp
create mode 100644 addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp
create mode 100644 addons/sourcemod/scripting/nativevotes_funvotes.sp
create mode 100644 addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp
create mode 100644 addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp
create mode 100644 addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp
create mode 100644 addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp
create mode 100644 addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp
delete mode 100644 addons/sourcemod/scripting/nativevotes_votemanager_test.sp
create mode 100644 addons/sourcemod/scripting/nativevotes_voterp.sp
delete mode 100644 addons/sourcemod/scripting/nativevotes_votetest.sp
delete mode 100644 addons/sourcemod/scripting/votedelay_changelevel.sp
delete mode 100644 addons/sourcemod/scripting/votediagnostics.sp
delete mode 100644 addons/sourcemod/scripting/votefailed.sp
create mode 100644 addons/sourcemod/translations/fr/nativevotes.phrases.txt
diff --git a/.github/workflows/nativevotes.yml b/.github/workflows/nativevotes.yml
index b803e90..028b4b2 100644
--- a/.github/workflows/nativevotes.yml
+++ b/.github/workflows/nativevotes.yml
@@ -4,6 +4,9 @@ name: Nativevotes Autobuild
on:
push:
branches: [ master ]
+ pull_request:
+ branches: [ master ]
+ workflow_dispatch:
jobs:
run:
@@ -29,7 +32,6 @@ jobs:
bash .github/nv_compile.sh
bash
-
- name: Setup SourcePawn 1.12 Compiler
uses: rumblefrog/setup-sp@master
with:
@@ -41,6 +43,17 @@ jobs:
bash .github/nv_compile.sh
bash
+ - name: Setup SourcePawn 1.13 Compiler
+ uses: rumblefrog/setup-sp@master
+ with:
+ version: "1.13.x"
+
+ - name: Compile plugins 1.13
+ shell: bash
+ run: |
+ bash .github/nv_compile.sh
+ bash
+
- name: Set Commit Hash
id: commit_info
run: echo "::set-output name=sha_short::$(git rev-parse --short HEAD)"
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..af0ad3f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,77 @@
+*.bat
+*.sh
+*.exe
+*.log
+*.smx
+
+admin.inc
+adminmenu.inc
+adt_array.inc
+adt_stack.inc
+adt_trie.inc
+adt.inc
+banning.inc
+basecomm.inc
+bitbuffer.inc
+clientprefs.inc
+clients.inc
+commandfilters.inc
+commandline.inc
+console.inc
+convars.inc
+core.inc
+cstrike.inc
+datapack.inc
+dbi.inc
+dhooks.inc
+entity_prop_stocks.inc
+entity.inc
+entitylump.inc
+events.inc
+files.inc
+float.inc
+functions.inc
+geoip.inc
+halflife.inc
+handles.inc
+helpers.inc
+keyvalues.inc
+lang.inc
+logging.inc
+mapchooser.inc
+menus.inc
+nextmap.inc
+profiler.inc
+protobuf.inc
+regex.inc
+sdkhooks.inc
+sdktools_client.inc
+sdktools_engine.inc
+sdktools_entinput.inc
+sdktools_entoutput.inc
+sdktools_functions.inc
+sdktools_gamerules.inc
+sdktools_hooks.inc
+sdktools_sound.inc
+sdktools_stocks.inc
+sdktools_stringtables.inc
+sdktools_tempents_stocks.inc
+sdktools_tempents.inc
+sdktools_trace.inc
+sdktools_variant_t.inc
+sdktools_voice.inc
+sdktools.inc
+sorting.inc
+sourcemod.inc
+string.inc
+testing.inc
+textparse.inc
+tf2_stocks.inc
+tf2.inc
+timers.inc
+topmenus.inc
+usermessages.inc
+vector.inc
+version_auto.inc
+version.inc
+virtual_address.inc
\ No newline at end of file
diff --git a/README.md b/README.md
index 60b6b59..ca9d53a 100644
--- a/README.md
+++ b/README.md
@@ -1,47 +1,24 @@
-sourcemod-nativevotes-updated
-=====================
-
-### NativeVotes SourceMod plugin, with the following fixes:
-
-@nosoop
-- [Prevent NativeVote menu callback from freeing in-use forward handle](https://github.com/powerlord/sourcemod-nativevotes/pull/3)
-- [Hotfix for 2022-06-22 Update](https://github.com/sapphonie/sourcemod-nativevotes-updated/pull/11)
-
-@rowedahelicon
-- [Fixed MvM mission votes from causing an Invalid forward handle](https://github.com/powerlord/sourcemod-nativevotes/pull/8)
-
-@Blueberryy
-- [Added Russian translation](https://github.com/powerlord/sourcemod-nativevotes/pull/7)
-
-@arthurdead
-- [Sourcemod 1.11 syntax update](https://github.com/arthurdead/sourcemod-nativevotes/tree/sm111-fix)
-
-@sapphonie (me)
-- [Stop memleak with FireToClient](https://github.com/powerlord/sourcemod-nativevotes/pull/9)
-
-@justkamiii
-- [Fix caps](https://github.com/sapphonie/sourcemod-nativevotes-updated/pull/7)
-
-@HotoCocoaco
-- [Add chi translation](https://github.com/sapphonie/sourcemod-nativevotes-updated/pull/8)
-
-@fdxx
-- [Fixed L4D2_VotePass](https://github.com/sapphonie/sourcemod-nativevotes-updated/pull/11)
-
-@satanskitty
-- [danish and turkish translations](https://github.com/sapphonie/sourcemod-nativevotes-updated/pull/17)
-
-@Spaenny
-- [added german translation](https://github.com/sapphonie/sourcemod-nativevotes-updated/pull/18)
-
-@iBoonie
-- [fixed a bug where you can spam invalid votes and forcibly prematurely change the level](https://github.com/sapphonie/sourcemod-nativevotes-updated/pull/20)
-- [removed an unnecessary ThrowNativeError](https://github.com/sapphonie/sourcemod-nativevotes-updated/pull/21)
-
-This repo also autoreleases all changes to master, compiled on SM 1.11 and SM 1.12. Check out the latest release [here](https://github.com/sapphonie/sourcemod-nativevotes-updated/releases/latest).
-
-NativeVotes is a SourceMod Plugin that lets plugins use the L4D/L4D2/TF2/CSGO built-in vote screens.
-It supplies an API that plugins can use to create and display these votes.
-
-It replaces the BuiltinVotes SourceMod extension and has a few new features as well as bugfixes.
-
+
+#
/
NativeVotes — Continued!
+This fork aims to expand upon [
sapphonie](https://github.com/sapphonie)['s work](https://github.com/sapphonie/sourcemod-nativevotes-updated).
+
+> [!WARNING]
+> This plugin has only been tested in [
**Team Fortress 2**](https://store.steampowered.com/app/440)‼ If it doesn't work in any other game, open an [issue](https://github.com/Heapons/sourcemod-nativevotes-updated/issues/new).
+
+# To-Do (Wishlist...?)
+## General
+- [x] Include [NativeVotes BaseVotes and FunVotes](https://github.com/powerlord/sourcemod-nativevotes-basevotes) in this repository.
+- Colored chat.
+ - [x] Team-colored player names.
+ - [x] Highlight map names.
+ - [] Parse workshop map titles.
+- [x] Update [Nominations](https://github.com/Heapons/sourcemod-nativevotes-updated/blob/master/addons/sourcemod/scripting/nativevotes_nominations.sp) and [Rock The Vote](https://github.com/Heapons/sourcemod-nativevotes-updated/blob/master/addons/sourcemod/scripting/nativevotes_rockthevote.sp) to match with the latest [Sourcemod](https://github.com/alliedmodders/sourcemod/tree/master/plugins) version.
+- [x] Add [`sm_voterp`](https://github.com/Heapons/sourcemod-nativevotes-updated/blob/master/addons/sourcemod/scripting/nativevotes_voterp.sp).
+ - Controls `tf_medieval_autorp` cvar.
+## [Rock The Vote](https://github.com/Heapons/sourcemod-nativevotes-updated/blob/master/addons/sourcemod/scripting/nativevotes_rockthevote.sp)
+- [x] Add `sm_forcertv` for admins.
+- [x] Allow players to retract their rock-the-vote.
+ - Execute `sm_rtv` again to undo.
+## [Nominations](https://github.com/Heapons/sourcemod-nativevotes-updated/blob/master/addons/sourcemod/scripting/nativevotes_nominations.sp)
+- [] Download and nominate workshop maps.
+ - Inspired by [[TF2] Workshop Vote](https://forums.alliedmods.net/showthread.php?p=2717878).
\ No newline at end of file
diff --git a/addons/sourcemod/scripting/csgo_votestart_test.sp b/addons/sourcemod/scripting/csgo_votestart_test.sp
deleted file mode 100644
index be36804..0000000
--- a/addons/sourcemod/scripting/csgo_votestart_test.sp
+++ /dev/null
@@ -1,240 +0,0 @@
-/**
- * vim: set ts=4 :
- * =============================================================================
- * CSGO VoteStart Multiple Test
- * Test CS:GO Start Vote Panel
- *
- * CSGO VoteStart Multiple Test(C) 2014 Ross Bemrose (Powerlord). All rights reserved.
- * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved.
- * =============================================================================
- *
- * This program is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, version 3.0, as published by the
- * Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program. If not, see .
- *
- * As a special exception, AlliedModders LLC gives you permission to link the
- * code of this program (as well as its derivative works) to "Half-Life 2," the
- * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
- * by the Valve Corporation. You must obey the GNU General Public License in
- * all respects for all other code used. Additionally, AlliedModders LLC grants
- * this exception to all derivative works. AlliedModders LLC defines further
- * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
- * or .
- *
- * Version: $Id$
- */
-
-#include
-#include
-
-#pragma semicolon 1
-
-#define VERSION "1.1"
-
-new bool:g_bVoteActive = false;
-
-public Plugin:myinfo =
-{
- name = "CSGO VoteStart Multiple Test",
- author = "Powerlord",
- description = "Test CS:GO VoteStart Panels",
- version = VERSION,
- url = "https://forums.alliedmods.net/showthread.php?t=208008"
-}
-
-public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max)
-{
- if (GetEngineVersion() != Engine_CSGO)
- {
- strcopy(error, err_max, "Only runs on CS:GO");
- return APLRes_Failure;
- }
- return APLRes_Success;
-}
-
-public OnPluginStart()
-{
- LoadTranslations("core.phrases");
-
- RegAdminCmd("votemulti", Cmd_VoteMulti, ADMFLAG_VOTE, "Multiple Choice Vote");
- RegAdminCmd("voteyesno", Cmd_VoteYesNo, ADMFLAG_VOTE, "YesNo Choice Vote");
- RegAdminCmd("voteclear", Cmd_VoteFail, ADMFLAG_VOTE, "Multiple Choice Vote");
-
- AddCommandListener(Listener_Vote, "vote");
-}
-
-public Action:Cmd_VoteMulti(client, args)
-{
- new entity = FindEntityByClassname(-1, "vote_controller");
- if (entity > -1)
- {
- SetEntProp(entity, Prop_Send, "m_bIsYesNoVote", false);
- //SetEntProp(entity, Prop_Send, "m_iActiveIssueIndex", 3);
- SetEntProp(entity, Prop_Send, "m_iOnlyTeamToVote", -1);
- for (new i = 0; i < 5; i++)
- {
- SetEntProp(entity, Prop_Send, "m_nVoteOptionCount", 0, _, i);
- }
- }
-
- new Handle:optionsEvent = CreateEvent("vote_options");
-
- SetEventString(optionsEvent, "option1", "de_dust");
- SetEventString(optionsEvent, "option2", "de_dust2");
- SetEventString(optionsEvent, "option3", "cs_italy");
- SetEventString(optionsEvent, "option4", "de_sugarcane");
- SetEventString(optionsEvent, "option5", "ar_shoots");
- SetEventInt(optionsEvent, "count", 5);
- FireEvent(optionsEvent);
-
- if (entity > -1)
- {
- SetEntProp(entity, Prop_Send, "m_nPotentialVotes", GetClientCount(true));
- }
-
- LogMessage("Finished setting vote_controller properties.");
-
- g_bVoteActive = true;
-
- CreateTimer(0.2, Timer_StartMultiVote, GetClientUserId(client));
- return Plugin_Handled;
-}
-
-public Action:Timer_StartMultiVote(Handle:timer, any:userid)
-{
- new client = GetClientOfUserId(userid);
- if (client <= 0 && client > MaxClients)
- {
- return Plugin_Continue;
- }
-
- new Handle:voteStart = StartMessageAll("VoteStart", USERMSG_RELIABLE);
- PbSetInt(voteStart, "team", -1);
- PbSetInt(voteStart, "ent_idx", 99);
- PbSetString(voteStart, "disp_str", "#SFUI_vote_nextlevel_choices");
- PbSetString(voteStart, "details_str", "");
- PbSetBool(voteStart, "is_yes_no_vote", false);
- PbSetString(voteStart, "other_team_str", "#SFUI_otherteam_vote_unimplemented");
- PbSetInt(voteStart, "vote_type", 1);
- EndMessage();
-
- return Plugin_Stop;
-}
-
-
-public Action:Cmd_VoteYesNo(client, args)
-{
- new entity = FindEntityByClassname(-1, "vote_controller");
- if (entity > -1)
- {
- SetEntProp(entity, Prop_Send, "m_bIsYesNoVote", true);
- //SetEntProp(entity, Prop_Send, "m_iActiveIssueIndex", 2);
- SetEntProp(entity, Prop_Send, "m_iOnlyTeamToVote", -1);
- for (new i = 0; i < 5; i++)
- {
- SetEntProp(entity, Prop_Send, "m_nVoteOptionCount", 0, _, i);
- }
- }
-
- new Handle:optionsEvent = CreateEvent("vote_options");
- SetEventString(optionsEvent, "option1", "Yes");
- SetEventString(optionsEvent, "option2", "No");
- SetEventString(optionsEvent, "option3", "");
- SetEventString(optionsEvent, "option4", "");
- SetEventString(optionsEvent, "option5", "");
- SetEventInt(optionsEvent, "count", 2);
- FireEvent(optionsEvent);
-
- if (entity > -1)
- {
- SetEntProp(entity, Prop_Send, "m_nPotentialVotes", GetClientCount(true));
- }
-
- g_bVoteActive = true;
-
- LogMessage("Finished setting vote_controller properties.");
-
- CreateTimer(0.2, Timer_StartYesNoVote, GetClientUserId(client));
- return Plugin_Handled;
-}
-
-public Action:Timer_StartYesNoVote(Handle:timer, any:userid)
-{
- new client = GetClientOfUserId(userid);
- if (client <= 0 && client > MaxClients)
- {
- return Plugin_Continue;
- }
-
- new Handle:voteStart = StartMessageAll("VoteStart", USERMSG_RELIABLE);
- PbSetInt(voteStart, "team", -1);
- PbSetInt(voteStart, "ent_idx", client);
- PbSetString(voteStart, "disp_str", "#SFUI_vote_changelevel");
- PbSetString(voteStart, "details_str", "de_dust2");
- PbSetBool(voteStart, "is_yes_no_vote", true);
- PbSetString(voteStart, "other_team_str", "#SFUI_otherteam_vote_unimplemented");
- PbSetInt(voteStart, "vote_type", 1);
- EndMessage();
-
- return Plugin_Stop;
-}
-
-public Action:Cmd_VoteFail(client, args)
-{
- if (g_bVoteActive)
- VoteFail();
-
- return Plugin_Handled;
-}
-
-public Action:Listener_Vote(client, const String:command[], argc)
-{
- if (!g_bVoteActive)
- {
- return Plugin_Continue;
- }
-
- new ReplySource:oldSource = SetCmdReplySource(SM_REPLY_TO_CHAT);
- ReplyToCommand(client, "Caught vote.");
-
- VoteFail();
-
- SetCmdReplySource(oldSource);
- return Plugin_Handled;
-}
-
-VoteFail()
-{
- g_bVoteActive = false;
- new Handle:voteFailed = StartMessageAll("VoteFailed", USERMSG_RELIABLE);
-
- PbSetInt(voteFailed, "team", -1);
- PbSetInt(voteFailed, "reason", 0);
- EndMessage();
-
- CreateTimer(5.0, Timer_ResetData);
-}
-
-public Action:Timer_ResetData(Handle:timer)
-{
- new entity = FindEntityByClassname(-1, "vote_controller");
- if (entity > -1)
- {
- //SetEntProp(entity, Prop_Send, "m_iActiveIssueIndex", -1);
- for (new i = 0; i < 5; i++)
- {
- SetEntProp(entity, Prop_Send, "m_nVoteOptionCount", 0, _, i);
- }
- SetEntProp(entity, Prop_Send, "m_nPotentialVotes", 0);
- SetEntProp(entity, Prop_Send, "m_iOnlyTeamToVote", -1);
- SetEntProp(entity, Prop_Send, "m_bIsYesNoVote", true);
- }
-}
\ No newline at end of file
diff --git a/addons/sourcemod/scripting/include/multicolors.inc b/addons/sourcemod/scripting/include/multicolors.inc
new file mode 100644
index 0000000..e6612a7
--- /dev/null
+++ b/addons/sourcemod/scripting/include/multicolors.inc
@@ -0,0 +1,460 @@
+#if defined _multicolors_included
+ #endinput
+#endif
+#define _multicolors_included
+
+#define MuCo_VERSION "2.1.2"
+
+#include
+#include
+
+/*
+*
+* Credits:
+* - Popoklopsi
+* - Powerlord
+* - exvel
+* - Dr. McKay
+*
+* Based on stamm-colors
+* - https://github.com/popoklopsi/Stamm/blob/master/include/stamm/stamm-colors.inc
+*
+*/
+
+
+
+#define PREFIX_MAX_LENGTH 64
+#define PREFIX_SEPARATOR "{default} "
+
+/* Global var to check whether colors are fixed or not */
+static bool g_bCFixColors;
+
+static char g_sCPrefix[PREFIX_MAX_LENGTH];
+
+/**
+ * Add a chat prefix before all chat msg
+ *
+ * @param sPrefix Prefix
+ */
+stock void CSetPrefix(const char[] sPrefix, any ...) {
+ if (!sPrefix[0]) {
+ return;
+ }
+
+ SetGlobalTransTarget(LANG_SERVER);
+ VFormat(g_sCPrefix, sizeof(g_sCPrefix) - strlen(PREFIX_SEPARATOR), sPrefix, 2);
+
+ // Add ending space
+ Format(g_sCPrefix, sizeof(g_sCPrefix), "%s%s", g_sCPrefix, PREFIX_SEPARATOR);
+}
+
+/**
+ * Add a chat prefix before all chat msg
+ *
+ * @param sPrefix Prefix
+ */
+stock void CClearPrefix() {
+ g_sCPrefix[0] = '\0';
+}
+
+/**
+ * Writes a message to a client with the correct stock for the game.
+ *
+ * @param client Client index.
+ * @param message Message (formatting rules).
+ *
+ * @error If the client is not connected an error will be thrown.
+ */
+stock void CPrintToChat(int client, const char[] message, any ...) {
+ char buffer[MAX_MESSAGE_LENGTH];
+ SetGlobalTransTarget(client);
+ VFormat(buffer, sizeof(buffer), message, 3);
+
+ if (!g_bCFixColors) {
+ CFixColors();
+ }
+
+ if (!IsSource2009()) {
+ C_PrintToChat(client, "%s%s", g_sCPrefix, buffer);
+ }
+ else {
+ MC_PrintToChat(client, "%s%s", g_sCPrefix, buffer);
+ }
+}
+
+/**
+ * Prints a message to all clients in the chat area.
+ * Supports color tags.
+ *
+ * @param client Client index.
+ * @param message Message (formatting rules)
+ */
+stock void CPrintToChatAll(const char[] message, any ...) {
+ if (!g_bCFixColors) {
+ CFixColors();
+ }
+
+ char buffer[MAX_BUFFER_LENGTH];
+ if (!IsSource2009()) {
+ for (int i = 1; i <= MaxClients; ++i) {
+ if (IsClientInGame(i) && !IsFakeClient(i) && !C_SkipList[i]) {
+ SetGlobalTransTarget(i);
+ VFormat(buffer, sizeof(buffer), message, 2);
+
+ C_PrintToChat(i, "%s", buffer);
+ }
+
+ C_SkipList[i] = false;
+ }
+ }
+ else {
+ MC_CheckTrie();
+
+ char buffer2[MAX_BUFFER_LENGTH];
+
+ for (int i = 1; i <= MaxClients; ++i) {
+ if (!IsClientInGame(i) || MC_SkipList[i]) {
+ MC_SkipList[i] = false;
+ continue;
+ }
+
+ SetGlobalTransTarget(i);
+ Format(buffer, sizeof(buffer), "\x01%s", message);
+ VFormat(buffer2, sizeof(buffer2), buffer, 2);
+
+ MC_ReplaceColorCodes(buffer2);
+ MC_SendMessage(i, buffer2);
+ }
+ }
+}
+
+/**
+ * Writes a message to all of a client's observers.
+ *
+ * @param target Client index.
+ * @param message Message (formatting rules).
+ */
+stock void CPrintToChatObservers(int target, const char[] message, any ...) {
+ char buffer[MAX_MESSAGE_LENGTH];
+ SetGlobalTransTarget(LANG_SERVER);
+ VFormat(buffer, sizeof(buffer), message, 3);
+
+ if (!g_bCFixColors) {
+ CFixColors();
+ }
+
+ for (int client = 1; client <= MaxClients; client++) {
+ if (IsClientInGame(client) && !IsPlayerAlive(client) && !IsFakeClient(client)) {
+ int observee = GetEntPropEnt(client, Prop_Send, "m_hObserverTarget");
+ int ObserverMode = GetEntProp(client, Prop_Send, "m_iObserverMode");
+
+ if (observee == target && (ObserverMode == 4 || ObserverMode == 5)) {
+ CPrintToChat(client, buffer);
+ }
+ }
+ }
+}
+
+/**
+ * Writes a message to a client with the correct stock for the game.
+ *
+ * @param client Client index.
+ * @param author Author index.
+ * @param message Message (formatting rules).
+ *
+ * @error If the client is not connected an error will be thrown.
+ */
+stock void CPrintToChatEx(int client, int author, const char[] message, any ...) {
+ char buffer[MAX_MESSAGE_LENGTH];
+ SetGlobalTransTarget(client);
+ VFormat(buffer, sizeof(buffer), message, 4);
+
+ if (!g_bCFixColors) {
+ CFixColors();
+ }
+
+ if (!IsSource2009()) {
+ C_PrintToChatEx(client, author, "%s%s", g_sCPrefix, buffer);
+ }
+ else {
+ MC_PrintToChatEx(client, author, "%s%s", g_sCPrefix, buffer);
+ }
+}
+
+/**
+ * Writes a message to all clients with the correct stock for the game.
+ *
+ * @param author Author index.
+ * @param message Message (formatting rules).
+ */
+stock void CPrintToChatAllEx(int author, const char[] message, any ...) {
+ char buffer[MAX_MESSAGE_LENGTH];
+ SetGlobalTransTarget(LANG_SERVER);
+ VFormat(buffer, sizeof(buffer), message, 3);
+
+ if (!g_bCFixColors) {
+ CFixColors();
+ }
+
+ if (!IsSource2009()) {
+ C_PrintToChatAllEx(author, "%s%s", g_sCPrefix, buffer);
+ }
+ else {
+ MC_PrintToChatAllEx(author, "%s%s", g_sCPrefix, buffer);
+ }
+}
+
+/**
+ * Writes a message to all of a client's observers with the correct
+ * game stock.
+ *
+ * @param target Client index.
+ * @param message Message (formatting rules).
+ */
+stock void CPrintToChatObserversEx(int target, const char[] message, any ...) {
+ char buffer[MAX_MESSAGE_LENGTH];
+ SetGlobalTransTarget(LANG_SERVER);
+ VFormat(buffer, sizeof(buffer), message, 3);
+
+ if (!g_bCFixColors) {
+ CFixColors();
+ }
+
+ for (int client = 1; client <= MaxClients; client++) {
+ if (IsClientInGame(client) && !IsPlayerAlive(client) && !IsFakeClient(client)) {
+ int observee = GetEntPropEnt(client, Prop_Send, "m_hObserverTarget");
+ int ObserverMode = GetEntProp(client, Prop_Send, "m_iObserverMode");
+
+ if (observee == target && (ObserverMode == 4 || ObserverMode == 5)) {
+ CPrintToChatEx(client, target, buffer);
+ }
+ }
+ }
+}
+
+/**
+ * Replies to a command with colors
+ *
+ * @param client Client to reply to
+ * @param message Message (formatting rules)
+ */
+stock void CReplyToCommand(int client, const char[] message, any ...) {
+ char buffer[MAX_MESSAGE_LENGTH];
+ SetGlobalTransTarget(client);
+ VFormat(buffer, sizeof(buffer), message, 3);
+
+ if (!g_bCFixColors) {
+ CFixColors();
+ }
+
+ if (!IsSource2009()) {
+ C_ReplyToCommand(client, "%s%s", g_sCPrefix, buffer);
+ }
+ else {
+ MC_ReplyToCommand(client, "%s%s", g_sCPrefix, buffer);
+ }
+}
+
+/**
+ * Replies to a command with colors
+ *
+ * @param client Client to reply to
+ * @param author Client to use for {teamcolor}
+ * @param message Message (formatting rules)
+ */
+stock void CReplyToCommandEx(int client, int author, const char[] message, any ...) {
+ char buffer[MAX_MESSAGE_LENGTH];
+ SetGlobalTransTarget(client);
+ VFormat(buffer, sizeof(buffer), message, 4);
+
+ if (!g_bCFixColors) {
+ CFixColors();
+ }
+
+ if (!IsSource2009()) {
+ C_ReplyToCommandEx(client, author, "%s%s", g_sCPrefix, buffer);
+ }
+ else {
+ MC_ReplyToCommandEx(client, author, "%s%s", g_sCPrefix, buffer);
+ }
+}
+
+/**
+ * Remove all tags and print to server
+ *
+ * @param message Message (formatting rules)
+ */
+stock void CPrintToServer(const char[] message, any ...) {
+ char buffer[MAX_MESSAGE_LENGTH];
+ char prefixBuffer[PREFIX_MAX_LENGTH];
+ SetGlobalTransTarget(LANG_SERVER);
+ VFormat(buffer, sizeof(buffer), message, 2);
+ strcopy(prefixBuffer, sizeof(prefixBuffer), g_sCPrefix);
+
+ if (!g_bCFixColors) {
+ CFixColors();
+ }
+
+ CRemoveTags(buffer, sizeof(buffer));
+ CRemoveTags(prefixBuffer, sizeof(prefixBuffer));
+
+ PrintToServer("%s%s", prefixBuffer, buffer);
+}
+
+/**
+ * Displays usage of an admin command to users depending on the
+ * setting of the sm_show_activity cvar.
+ *
+ * This version does not display a message to the originating client
+ * if used from chat triggers or menus. If manual replies are used
+ * for these cases, then this function will suffice. Otherwise,
+ * CShowActivity2() is slightly more useful.
+ * Supports color tags.
+ *
+ * @param client Client index doing the action, or 0 for server.
+ * @param format Formatting rules.
+ * @param ... Variable number of format parameters.
+ *
+ * @error
+ */
+stock void CShowActivity(int author, const char[] message, any ...) {
+ char buffer[MAX_MESSAGE_LENGTH];
+ SetGlobalTransTarget(LANG_SERVER);
+ VFormat(buffer, sizeof(buffer), message, 3);
+
+ if (!g_bCFixColors) {
+ CFixColors();
+ }
+
+ if (!IsSource2009()) {
+ C_ShowActivity(author, "%s", buffer);
+ }
+ else {
+ MC_ShowActivity(author, "%s", buffer);
+ }
+}
+
+/**
+ * Same as C_ShowActivity(), except the tag parameter is used instead of "[SM] " (note that you must supply any spacing).
+ * Supports color tags.
+ *
+ * @param client Client index doing the action, or 0 for server.
+ * @param tags Tag to display with.
+ * @param format Formatting rules.
+ * @param ... Variable number of format parameters.
+ *
+ * @error
+ */
+stock void CShowActivityEx(int author, const char[] tag, const char[] message, any ...) {
+ char buffer[MAX_MESSAGE_LENGTH];
+ SetGlobalTransTarget(LANG_SERVER);
+ VFormat(buffer, sizeof(buffer), message, 4);
+
+ if (!g_bCFixColors) {
+ CFixColors();
+ }
+
+ if (!IsSource2009()) {
+ C_ShowActivityEx(author, tag, "%s", buffer);
+ }
+ else {
+ MC_ShowActivityEx(author, tag, "%s", buffer);
+ }
+}
+
+/**
+ * Displays usage of an admin command to users depending on the setting of the sm_show_activity cvar.
+ * All users receive a message in their chat text, except for the originating client,
+ * who receives the message based on the current ReplySource.
+ * Supports color tags.
+ *
+ * @param client Client index doing the action, or 0 for server.
+ * @param tags Tag to prepend to the message.
+ * @param format Formatting rules.
+ * @param ... Variable number of format parameters.
+ *
+ * @error
+ */
+ stock void CShowActivity2(int author, const char[] tag, const char[] message, any ...) {
+ char buffer[MAX_MESSAGE_LENGTH];
+ SetGlobalTransTarget(LANG_SERVER);
+ VFormat(buffer, sizeof(buffer), message, 4);
+
+ if (!g_bCFixColors) {
+ CFixColors();
+ }
+
+ if (!IsSource2009()) {
+ C_ShowActivity2(author, tag, "%s", buffer);
+ }
+ else {
+ MC_ShowActivity2(author, tag, "%s", buffer);
+ }
+}
+
+/**
+ * Replaces color tags in a string with color codes
+ *
+ * @param message String.
+ * @param maxlength Maximum length of the string buffer.
+ */
+stock void CFormatColor(char[] message, int maxlength, int author = -1) {
+ if (!g_bCFixColors) {
+ CFixColors();
+ }
+
+ if (!IsSource2009()) {
+ if (author == 0) {
+ author = -1;
+ }
+
+ C_Format(message, maxlength, author);
+ }
+ else {
+ if (author == -1) {
+ author = 0;
+ }
+
+ MC_ReplaceColorCodes(message, author, false, maxlength);
+ }
+}
+
+/**
+ * Removes color tags from a message
+ *
+ * @param message Message to remove tags from
+ * @param maxlen Maximum buffer length
+ */
+stock void CRemoveTags(char[] message, int maxlen) {
+ if (!IsSource2009()) {
+ C_RemoveTags(message, maxlen);
+ }
+ else {
+ MC_RemoveTags(message, maxlen);
+ }
+}
+
+/**
+ * Fixes missing Lightgreen color.
+ */
+stock void CFixColors() {
+ g_bCFixColors = true;
+
+ // Replace lightgreen if not exists
+ if (!C_ColorAllowed(Color_Lightgreen)) {
+ if (C_ColorAllowed(Color_Lime)) {
+ C_ReplaceColor(Color_Lightgreen, Color_Lime);
+ }
+ else if (C_ColorAllowed(Color_Olive)) {
+ C_ReplaceColor(Color_Lightgreen, Color_Olive);
+ }
+ }
+}
+
+stock bool IsSource2009() {
+ return (GetEngineVersion() == Engine_CSS
+ || GetEngineVersion() == Engine_HL2DM
+ || GetEngineVersion() == Engine_DODS
+ || GetEngineVersion() == Engine_TF2
+ || GetEngineVersion() == Engine_SDK2013);
+}
diff --git a/addons/sourcemod/scripting/include/multicolors/colors.inc b/addons/sourcemod/scripting/include/multicolors/colors.inc
new file mode 100644
index 0000000..65c3f1c
--- /dev/null
+++ b/addons/sourcemod/scripting/include/multicolors/colors.inc
@@ -0,0 +1,897 @@
+/**************************************************************************
+ * *
+ * Colored Chat Functions *
+ * Author: exvel, Editor: Popoklopsi, Powerlord, Bara *
+ * Version: 2.0.0-MC *
+ * *
+ **************************************************************************/
+
+
+#if defined _colors_included
+ #endinput
+#endif
+#define _colors_included
+
+#define MAX_MESSAGE_LENGTH 250
+#define MAX_COLORS 18
+
+#define SERVER_INDEX 0
+#define NO_INDEX -1
+#define NO_PLAYER -2
+
+enum C_Colors {
+ Color_Default = 0,
+ Color_Darkred,
+ Color_Green,
+ Color_Lightgreen,
+ Color_Red,
+ Color_Blue,
+ Color_Olive,
+ Color_Lime,
+ Color_Lightred,
+ Color_Purple,
+ Color_Grey,
+ Color_Yellow,
+ Color_Orange,
+ Color_Bluegrey,
+ Color_Lightblue,
+ Color_Darkblue,
+ Color_Grey2,
+ Color_Orchid,
+ Color_Lightred2
+}
+
+/* C_Colors' properties */
+char C_Tag[][] = {"{default}", "{darkred}", "{green}", "{lightgreen}", "{red}", "{blue}", "{olive}", "{lime}", "{lightred}", "{purple}", "{grey}", "{yellow}", "{orange}", "{bluegrey}", "{lightblue}", "{darkblue}", "{grey2}", "{orchid}", "{lightred2}"};
+char C_TagCode[][] = {"\x01", "\x02", "\x04", "\x03", "\x03", "\x03", "\x05", "\x06", "\x07", "\x03", "\x08", "\x09", "\x10", "\x0A", "\x0B", "\x0C", "\x0D", "\x0E", "\x0F"};
+bool C_TagReqSayText2[] = {false, false, false, true, true, true, false, false, false, false, false, false, false, false, false, false, false, false, false};
+bool C_EventIsHooked;
+bool C_SkipList[MAXPLAYERS+1];
+
+/* Game default profile */
+bool C_Profile_Colors[] = {true, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false};
+int C_Profile_TeamIndex[] = {NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX};
+bool C_Profile_SayText2;
+
+static ConVar sm_show_activity;
+
+/**
+ * Prints a message to a specific client in the chat area.
+ * Supports color tags.
+ *
+ * @param client Client index.
+ * @param szMessage Message (formatting rules).
+ * @return No return
+ *
+ * On error/Errors: If the client is not connected an error will be thrown.
+ */
+stock void C_PrintToChat(int client, const char[] szMessage, any ...) {
+ if (client <= 0 || client > MaxClients) {
+ ThrowError("Invalid client index %d", client);
+ }
+
+ if (!IsClientInGame(client)) {
+ ThrowError("Client %d is not in game", client);
+ }
+
+ char szBuffer[MAX_MESSAGE_LENGTH];
+ char szCMessage[MAX_MESSAGE_LENGTH];
+
+ SetGlobalTransTarget(client);
+
+ Format(szBuffer, sizeof(szBuffer), "\x01%s", szMessage);
+ VFormat(szCMessage, sizeof(szCMessage), szBuffer, 3);
+
+ int index = C_Format(szCMessage, sizeof(szCMessage));
+
+ if (index == NO_INDEX) {
+ PrintToChat(client, "%s", szCMessage);
+ }
+ else {
+ C_SayText2(client, index, szCMessage);
+ }
+}
+
+/**
+ * Reples to a message in a command. A client index of 0 will use PrintToServer().
+ * If the command was from the console, PrintToConsole() is used. If the command was from chat, C_PrintToChat() is used.
+ * Supports color tags.
+ *
+ * @param client Client index, or 0 for server.
+ * @param szMessage Formatting rules.
+ * @param ... Variable number of format parameters.
+ * @return No return
+ *
+ * On error/Errors: If the client is not connected or invalid.
+ */
+stock void C_ReplyToCommand(int client, const char[] szMessage, any ...) {
+ char szCMessage[MAX_MESSAGE_LENGTH];
+ SetGlobalTransTarget(client);
+ VFormat(szCMessage, sizeof(szCMessage), szMessage, 3);
+
+ if (client == 0) {
+ C_RemoveTags(szCMessage, sizeof(szCMessage));
+ PrintToServer("%s", szCMessage);
+ }
+ else if (GetCmdReplySource() == SM_REPLY_TO_CONSOLE) {
+ C_RemoveTags(szCMessage, sizeof(szCMessage));
+ PrintToConsole(client, "%s", szCMessage);
+ }
+ else {
+ C_PrintToChat(client, "%s", szCMessage);
+ }
+}
+
+/**
+ * Reples to a message in a command. A client index of 0 will use PrintToServer().
+ * If the command was from the console, PrintToConsole() is used. If the command was from chat, C_PrintToChat() is used.
+ * Supports color tags.
+ *
+ * @param client Client index, or 0 for server.
+ * @param author Author index whose color will be used for teamcolor tag.
+ * @param szMessage Formatting rules.
+ * @param ... Variable number of format parameters.
+ * @return No return
+ *
+ * On error/Errors: If the client is not connected or invalid.
+ */
+stock void C_ReplyToCommandEx(int client, int author, const char[] szMessage, any ...) {
+ char szCMessage[MAX_MESSAGE_LENGTH];
+ SetGlobalTransTarget(client);
+ VFormat(szCMessage, sizeof(szCMessage), szMessage, 4);
+
+ if (client == 0) {
+ C_RemoveTags(szCMessage, sizeof(szCMessage));
+ PrintToServer("%s", szCMessage);
+ }
+ else if (GetCmdReplySource() == SM_REPLY_TO_CONSOLE) {
+ C_RemoveTags(szCMessage, sizeof(szCMessage));
+ PrintToConsole(client, "%s", szCMessage);
+ }
+ else {
+ C_PrintToChatEx(client, author, "%s", szCMessage);
+ }
+}
+
+/**
+ * Prints a message to all clients in the chat area.
+ * Supports color tags.
+ *
+ * @param client Client index.
+ * @param szMessage Message (formatting rules)
+ * @return No return
+ */
+stock void C_PrintToChatAll(const char[] szMessage, any ...) {
+ char szBuffer[MAX_MESSAGE_LENGTH];
+
+ for (int i = 1; i <= MaxClients; ++i) {
+ if (IsClientInGame(i) && !IsFakeClient(i) && !C_SkipList[i]) {
+ SetGlobalTransTarget(i);
+ VFormat(szBuffer, sizeof(szBuffer), szMessage, 2);
+
+ C_PrintToChat(i, "%s", szBuffer);
+ }
+
+ C_SkipList[i] = false;
+ }
+}
+
+/**
+ * Prints a message to a specific client in the chat area.
+ * Supports color tags and teamcolor tag.
+ *
+ * @param client Client index.
+ * @param author Author index whose color will be used for teamcolor tag.
+ * @param szMessage Message (formatting rules).
+ * @return No return
+ *
+ * On error/Errors: If the client or author are not connected an error will be thrown.
+ */
+stock void C_PrintToChatEx(int client, int author, const char[] szMessage, any ...) {
+ if (client <= 0 || client > MaxClients) {
+ ThrowError("Invalid client index %d", client);
+ }
+
+ if (!IsClientInGame(client)) {
+ ThrowError("Client %d is not in game", client);
+ }
+
+ if (author < 0 || author > MaxClients) {
+ ThrowError("Invalid client index %d", author);
+ }
+
+ char szBuffer[MAX_MESSAGE_LENGTH];
+ char szCMessage[MAX_MESSAGE_LENGTH];
+
+ SetGlobalTransTarget(client);
+
+ Format(szBuffer, sizeof(szBuffer), "\x01%s", szMessage);
+ VFormat(szCMessage, sizeof(szCMessage), szBuffer, 4);
+
+ int index = C_Format(szCMessage, sizeof(szCMessage), author);
+
+ if (index == NO_INDEX) {
+ PrintToChat(client, "%s", szCMessage);
+ }
+ else {
+ C_SayText2(client, author, szCMessage);
+ }
+}
+
+/**
+ * Prints a message to all clients in the chat area.
+ * Supports color tags and teamcolor tag.
+ *
+ * @param author Author index whos color will be used for teamcolor tag.
+ * @param szMessage Message (formatting rules).
+ * @return No return
+ *
+ * On error/Errors: If the author is not connected an error will be thrown.
+ */
+stock void C_PrintToChatAllEx(int author, const char[] szMessage, any ...) {
+ if (author < 0 || author > MaxClients) {
+ ThrowError("Invalid client index %d", author);
+ }
+
+ if (!IsClientInGame(author)) {
+ ThrowError("Client %d is not in game", author);
+ }
+
+ char szBuffer[MAX_MESSAGE_LENGTH];
+
+ for (int i = 1; i <= MaxClients; ++i) {
+ if (IsClientInGame(i) && !IsFakeClient(i) && !C_SkipList[i]) {
+ SetGlobalTransTarget(i);
+ VFormat(szBuffer, sizeof(szBuffer), szMessage, 3);
+
+ C_PrintToChatEx(i, author, "%s", szBuffer);
+ }
+
+ C_SkipList[i] = false;
+ }
+}
+
+/**
+ * Removes color tags from the string.
+ *
+ * @param szMessage String.
+ * @return No return
+ */
+stock void C_RemoveTags(char[] szMessage, int maxlength) {
+ for (int i = 0; i < MAX_COLORS; i++) {
+ ReplaceString(szMessage, maxlength, C_Tag[i], "", false);
+ }
+
+ ReplaceString(szMessage, maxlength, "{teamcolor}", "", false);
+}
+
+/**
+ * Checks whether a color is allowed or not
+ *
+ * @param tag Color Tag.
+ * @return True when color is supported, otherwise false
+ */
+stock bool C_ColorAllowed(C_Colors color) {
+ if (!C_EventIsHooked) {
+ C_SetupProfile();
+
+ C_EventIsHooked = true;
+ }
+
+ return C_Profile_Colors[color];
+}
+
+/**
+ * Replace the color with another color
+ * Handle with care!
+ *
+ * @param color color to replace.
+ * @param newColor color to replace with.
+ * @noreturn
+ */
+stock void C_ReplaceColor(C_Colors color, C_Colors newColor) {
+ if (!C_EventIsHooked) {
+ C_SetupProfile();
+
+ C_EventIsHooked = true;
+ }
+
+ C_Profile_Colors[color] = C_Profile_Colors[newColor];
+ C_Profile_TeamIndex[color] = C_Profile_TeamIndex[newColor];
+
+ C_TagReqSayText2[color] = C_TagReqSayText2[newColor];
+ Format(C_TagCode[color], sizeof(C_TagCode[]), C_TagCode[newColor]);
+}
+
+/**
+ * This function should only be used right in front of
+ * C_PrintToChatAll or C_PrintToChatAllEx and it tells
+ * to those funcions to skip specified client when printing
+ * message to all clients. After message is printed client will
+ * no more be skipped.
+ *
+ * @param client Client index
+ * @return No return
+ */
+stock void C_SkipNextClient(int client) {
+ if (client <= 0 || client > MaxClients) {
+ ThrowError("Invalid client index %d", client);
+ }
+
+ C_SkipList[client] = true;
+}
+
+/**
+ * Replaces color tags in a string with color codes
+ *
+ * @param szMessage String.
+ * @param maxlength Maximum length of the string buffer.
+ * @return Client index that can be used for SayText2 author index
+ *
+ * On error/Errors: If there is more then one team color is used an error will be thrown.
+ */
+stock int C_Format(char[] szMessage, int maxlength, int author = NO_INDEX) {
+ /* Hook event for auto profile setup on map start */
+ if (!C_EventIsHooked) {
+ C_SetupProfile();
+ HookEvent("server_spawn", C_Event_MapStart, EventHookMode_PostNoCopy);
+
+ C_EventIsHooked = true;
+ }
+
+ int iRandomPlayer = NO_INDEX;
+
+ // On CS:GO set invisible precolor
+ if (GetEngineVersion() == Engine_CSGO) {
+ Format(szMessage, maxlength, " %s", szMessage);
+ }
+
+ /* If author was specified replace {teamcolor} tag */
+ if (author != NO_INDEX) {
+ if (C_Profile_SayText2) {
+ ReplaceString(szMessage, maxlength, "{teamcolor}", "\x03", false);
+
+ iRandomPlayer = author;
+ }
+ /* If saytext2 is not supported by game replace {teamcolor} with green tag */
+ else {
+ ReplaceString(szMessage, maxlength, "{teamcolor}", C_TagCode[Color_Green], false);
+ }
+ }
+ else {
+ ReplaceString(szMessage, maxlength, "{teamcolor}", "", false);
+ }
+
+ /* For other color tags we need a loop */
+ for (int i = 0; i < MAX_COLORS; i++) {
+ /* If tag not found - skip */
+ if (StrContains(szMessage, C_Tag[i], false) == -1) {
+ continue;
+ }
+
+ /* If tag is not supported by game replace it with green tag */
+ else if (!C_Profile_Colors[i]) {
+ ReplaceString(szMessage, maxlength, C_Tag[i], C_TagCode[Color_Green], false);
+ }
+
+ /* If tag doesn't need saytext2 simply replace */
+ else if (!C_TagReqSayText2[i]) {
+ ReplaceString(szMessage, maxlength, C_Tag[i], C_TagCode[i], false);
+ }
+
+ /* Tag needs saytext2 */
+ else {
+ /* If saytext2 is not supported by game replace tag with green tag */
+ if (!C_Profile_SayText2) {
+ ReplaceString(szMessage, maxlength, C_Tag[i], C_TagCode[Color_Green], false);
+ }
+
+ /* Game supports saytext2 */
+ else {
+ /* If random player for tag wasn't specified replace tag and find player */
+ if (iRandomPlayer == NO_INDEX) {
+ /* Searching for valid client for tag */
+ iRandomPlayer = C_FindRandomPlayerByTeam(C_Profile_TeamIndex[i]);
+
+ /* If player not found replace tag with green color tag */
+ if (iRandomPlayer == NO_PLAYER) {
+ ReplaceString(szMessage, maxlength, C_Tag[i], C_TagCode[Color_Green], false);
+ }
+
+ /* If player was found simply replace */
+ else {
+ ReplaceString(szMessage, maxlength, C_Tag[i], C_TagCode[i], false);
+ }
+
+ }
+ /* If found another team color tag throw error */
+ else {
+ //ReplaceString(szMessage, maxlength, C_Tag[i], "");
+ ThrowError("Using two team colors in one message is not allowed");
+ }
+ }
+ }
+ }
+
+ return iRandomPlayer;
+}
+
+/**
+ * Finds a random player with specified team
+ *
+ * @param color_team Client team.
+ * @return Client index or NO_PLAYER if no player found
+ */
+stock int C_FindRandomPlayerByTeam(int color_team) {
+ if (color_team == SERVER_INDEX) {
+ return 0;
+ }
+
+ int[] players = new int[MaxClients];
+ int count;
+
+ for (int i = 1; i <= MaxClients; ++i) {
+ if (IsClientInGame(i) && GetClientTeam(i) == color_team) {
+ players[count++] = i;
+ }
+ }
+
+ if (count) {
+ return players[GetRandomInt(0, count-1)];
+ }
+
+ return NO_PLAYER;
+}
+
+/**
+ * Sends a SayText2 usermessage to a client
+ *
+ * @param szMessage Client index
+ * @param maxlength Author index
+ * @param szMessage Message
+ * @return No return.
+ */
+stock void C_SayText2(int client, int author, const char[] szMessage) {
+ Handle hBuffer = StartMessageOne("SayText2", client, USERMSG_RELIABLE|USERMSG_BLOCKHOOKS);
+
+ if (GetFeatureStatus(FeatureType_Native, "GetUserMessageType") == FeatureStatus_Available && GetUserMessageType() == UM_Protobuf) {
+ Protobuf pb = UserMessageToProtobuf(hBuffer);
+ pb.SetInt("ent_idx", author);
+ pb.SetBool("chat", true);
+ pb.SetString("msg_name", szMessage);
+ pb.AddString("params", "");
+ pb.AddString("params", "");
+ pb.AddString("params", "");
+ pb.AddString("params", "");
+ }
+ else {
+ BfWrite bf = UserMessageToBfWrite(hBuffer);
+ bf.WriteByte(author);
+ bf.WriteByte(true);
+ bf.WriteString(szMessage);
+ }
+
+ EndMessage();
+}
+
+/**
+ * Creates game color profile
+ * This function must be edited if you want to add more games support
+ *
+ * @return No return.
+ */
+stock void C_SetupProfile() {
+ EngineVersion engine = GetEngineVersion();
+
+ if (engine == Engine_CSS) {
+ C_Profile_Colors[Color_Lightgreen] = true;
+ C_Profile_Colors[Color_Red] = true;
+ C_Profile_Colors[Color_Blue] = true;
+ C_Profile_Colors[Color_Olive] = true;
+ C_Profile_TeamIndex[Color_Lightgreen] = SERVER_INDEX;
+ C_Profile_TeamIndex[Color_Red] = 2;
+ C_Profile_TeamIndex[Color_Blue] = 3;
+ C_Profile_SayText2 = true;
+ }
+ else if (engine == Engine_CSGO) {
+ C_Profile_Colors[Color_Red] = true;
+ C_Profile_Colors[Color_Blue] = true;
+ C_Profile_Colors[Color_Olive] = true;
+ C_Profile_Colors[Color_Darkred] = true;
+ C_Profile_Colors[Color_Lime] = true;
+ C_Profile_Colors[Color_Lightred] = true;
+ C_Profile_Colors[Color_Purple] = true;
+ C_Profile_Colors[Color_Grey] = true;
+ C_Profile_Colors[Color_Yellow] = true;
+ C_Profile_Colors[Color_Orange] = true;
+ C_Profile_Colors[Color_Bluegrey] = true;
+ C_Profile_Colors[Color_Lightblue] = true;
+ C_Profile_Colors[Color_Darkblue] = true;
+ C_Profile_Colors[Color_Grey2] = true;
+ C_Profile_Colors[Color_Orchid] = true;
+ C_Profile_Colors[Color_Lightred2] = true;
+ C_Profile_TeamIndex[Color_Red] = 2;
+ C_Profile_TeamIndex[Color_Blue] = 3;
+ C_Profile_SayText2 = true;
+ }
+ else if (engine == Engine_TF2) {
+ C_Profile_Colors[Color_Lightgreen] = true;
+ C_Profile_Colors[Color_Red] = true;
+ C_Profile_Colors[Color_Blue] = true;
+ C_Profile_Colors[Color_Olive] = true;
+ C_Profile_TeamIndex[Color_Lightgreen] = SERVER_INDEX;
+ C_Profile_TeamIndex[Color_Red] = 2;
+ C_Profile_TeamIndex[Color_Blue] = 3;
+ C_Profile_SayText2 = true;
+ }
+ else if (engine == Engine_Left4Dead || engine == Engine_Left4Dead2) {
+ C_Profile_Colors[Color_Lightgreen] = true;
+ C_Profile_Colors[Color_Red] = true;
+ C_Profile_Colors[Color_Blue] = true;
+ C_Profile_Colors[Color_Olive] = true;
+ C_Profile_TeamIndex[Color_Lightgreen] = SERVER_INDEX;
+ C_Profile_TeamIndex[Color_Red] = 3;
+ C_Profile_TeamIndex[Color_Blue] = 2;
+ C_Profile_SayText2 = true;
+ }
+ else if (engine == Engine_HL2DM) {
+ /* hl2mp profile is based on mp_teamplay convar */
+ if (GetConVarBool(FindConVar("mp_teamplay"))) {
+ C_Profile_Colors[Color_Red] = true;
+ C_Profile_Colors[Color_Blue] = true;
+ C_Profile_Colors[Color_Olive] = true;
+ C_Profile_TeamIndex[Color_Red] = 3;
+ C_Profile_TeamIndex[Color_Blue] = 2;
+ C_Profile_SayText2 = true;
+ }
+ else {
+ C_Profile_SayText2 = false;
+ C_Profile_Colors[Color_Olive] = true;
+ }
+ }
+ else if (engine == Engine_DODS) {
+ C_Profile_Colors[Color_Olive] = true;
+ C_Profile_SayText2 = false;
+ }
+ /* Profile for other games */
+ else {
+ if (GetUserMessageId("SayText2") == INVALID_MESSAGE_ID) {
+ C_Profile_SayText2 = false;
+ }
+ else {
+ C_Profile_Colors[Color_Red] = true;
+ C_Profile_Colors[Color_Blue] = true;
+ C_Profile_TeamIndex[Color_Red] = 2;
+ C_Profile_TeamIndex[Color_Blue] = 3;
+ C_Profile_SayText2 = true;
+ }
+ }
+}
+
+public void C_Event_MapStart(Event event, const char[] name, bool dontBroadcast) {
+ C_SetupProfile();
+
+ for (int i = 1; i <= MaxClients; ++i) {
+ C_SkipList[i] = false;
+ }
+}
+
+/**
+ * Displays usage of an admin command to users depending on the
+ * setting of the sm_show_activity cvar.
+ *
+ * This version does not display a message to the originating client
+ * if used from chat triggers or menus. If manual replies are used
+ * for these cases, then this function will suffice. Otherwise,
+ * C_ShowActivity2() is slightly more useful.
+ * Supports color tags.
+ *
+ * @param client Client index doing the action, or 0 for server.
+ * @param format Formatting rules.
+ * @param ... Variable number of format parameters.
+ * @noreturn
+ * @error
+ */
+stock int C_ShowActivity(int client, const char[] format, any ...) {
+ if (sm_show_activity == null) {
+ sm_show_activity = FindConVar("sm_show_activity");
+ }
+
+ char tag[] = "[SM] ";
+
+ char szBuffer[MAX_MESSAGE_LENGTH];
+ //char szCMessage[MAX_MESSAGE_LENGTH];
+ int value = sm_show_activity.IntValue;
+ ReplySource replyto = GetCmdReplySource();
+
+ char name[MAX_NAME_LENGTH] = "Console";
+ char sign[MAX_NAME_LENGTH] = "ADMIN";
+ bool display_in_chat = false;
+ if (client != 0) {
+ if (client < 0 || client > MaxClients || !IsClientConnected(client)) {
+ ThrowError("Client index %d is invalid", client);
+ }
+
+ GetClientName(client, name, sizeof(name));
+ AdminId id = GetUserAdmin(client);
+ if (id == INVALID_ADMIN_ID || !GetAdminFlag(id, Admin_Generic, Access_Effective)) {
+ sign = "PLAYER";
+ }
+
+ /* Display the message to the client? */
+ if (replyto == SM_REPLY_TO_CONSOLE) {
+ SetGlobalTransTarget(client);
+ VFormat(szBuffer, sizeof(szBuffer), format, 3);
+
+ C_RemoveTags(szBuffer, sizeof(szBuffer));
+ PrintToConsole(client, "%s%s\n", tag, szBuffer);
+ display_in_chat = true;
+ }
+ }
+ else {
+ SetGlobalTransTarget(LANG_SERVER);
+ VFormat(szBuffer, sizeof(szBuffer), format, 3);
+
+ C_RemoveTags(szBuffer, sizeof(szBuffer));
+ PrintToServer("%s%s\n", tag, szBuffer);
+ }
+
+ if (!value) {
+ return 1;
+ }
+
+ for (int i = 1; i <= MaxClients; ++i) {
+ if (!IsClientInGame(i) || IsFakeClient(i) || (display_in_chat && i == client)) {
+ continue;
+ }
+
+ AdminId id = GetUserAdmin(i);
+ SetGlobalTransTarget(i);
+ if (id == INVALID_ADMIN_ID || !GetAdminFlag(id, Admin_Generic, Access_Effective)) {
+ /* Treat this as a normal user. */
+ if ((value & 1) | (value & 2)) {
+ char newsign[MAX_NAME_LENGTH];
+
+ if ((value & 2) || (i == client)) {
+ newsign = name;
+ }
+ else {
+ newsign = sign;
+ }
+
+ VFormat(szBuffer, sizeof(szBuffer), format, 3);
+
+ C_PrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer);
+ }
+ }
+ else {
+ /* Treat this as an admin user */
+ bool is_root = GetAdminFlag(id, Admin_Root, Access_Effective);
+ if ((value & 4) || (value & 8) || ((value & 16) && is_root)) {
+ char newsign[MAX_NAME_LENGTH];
+
+ if ((value & 8) || ((value & 16) && is_root) || (i == client)) {
+ newsign = name;
+ }
+ else {
+ newsign = sign;
+ }
+
+ VFormat(szBuffer, sizeof(szBuffer), format, 3);
+
+ C_PrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer);
+ }
+ }
+ }
+
+ return 1;
+}
+
+/**
+ * Same as C_ShowActivity(), except the tag parameter is used instead of "[SM] " (note that you must supply any spacing).
+ * Supports color tags.
+ *
+ * @param client Client index doing the action, or 0 for server.
+ * @param tags Tag to display with.
+ * @param format Formatting rules.
+ * @param ... Variable number of format parameters.
+ * @noreturn
+ * @error
+ */
+stock int C_ShowActivityEx(int client, const char[] tag, const char[] format, any ...) {
+ if (sm_show_activity == null) {
+ sm_show_activity = FindConVar("sm_show_activity");
+ }
+
+ char szBuffer[MAX_MESSAGE_LENGTH];
+ //char szCMessage[MAX_MESSAGE_LENGTH];
+ int value = sm_show_activity.IntValue;
+ ReplySource replyto = GetCmdReplySource();
+
+ char name[MAX_NAME_LENGTH] = "Console";
+ char sign[MAX_NAME_LENGTH] = "ADMIN";
+ bool display_in_chat = false;
+ if (client != 0) {
+ if (client < 0 || client > MaxClients || !IsClientConnected(client)) {
+ ThrowError("Client index %d is invalid", client);
+ }
+
+ GetClientName(client, name, sizeof(name));
+ AdminId id = GetUserAdmin(client);
+ if (id == INVALID_ADMIN_ID || !GetAdminFlag(id, Admin_Generic, Access_Effective)) {
+ sign = "PLAYER";
+ }
+
+ /* Display the message to the client? */
+ if (replyto == SM_REPLY_TO_CONSOLE) {
+ SetGlobalTransTarget(client);
+ VFormat(szBuffer, sizeof(szBuffer), format, 4);
+
+ C_RemoveTags(szBuffer, sizeof(szBuffer));
+ PrintToConsole(client, "%s%s\n", tag, szBuffer);
+ display_in_chat = true;
+ }
+ }
+ else {
+ SetGlobalTransTarget(LANG_SERVER);
+ VFormat(szBuffer, sizeof(szBuffer), format, 4);
+
+ C_RemoveTags(szBuffer, sizeof(szBuffer));
+ PrintToServer("%s%s\n", tag, szBuffer);
+ }
+
+ if (!value) {
+ return 1;
+ }
+
+ for (int i = 1; i <= MaxClients; ++i) {
+ if (!IsClientInGame(i) || IsFakeClient(i) || (display_in_chat && i == client)) {
+ continue;
+ }
+
+ AdminId id = GetUserAdmin(i);
+ SetGlobalTransTarget(i);
+ if (id == INVALID_ADMIN_ID || !GetAdminFlag(id, Admin_Generic, Access_Effective)) {
+ /* Treat this as a normal user. */
+ if ((value & 1) | (value & 2)) {
+ char newsign[MAX_NAME_LENGTH];
+
+ if ((value & 2) || (i == client)) {
+ newsign = name;
+ }
+ else {
+ newsign = sign;
+ }
+
+ VFormat(szBuffer, sizeof(szBuffer), format, 4);
+
+ C_PrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer);
+ }
+ }
+ else {
+ /* Treat this as an admin user */
+ bool is_root = GetAdminFlag(id, Admin_Root, Access_Effective);
+ if ((value & 4) || (value & 8) || ((value & 16) && is_root)) {
+ char newsign[MAX_NAME_LENGTH];
+
+ if ((value & 8) || ((value & 16) && is_root) || (i == client)) {
+ newsign = name;
+ }
+ else {
+ newsign = sign;
+ }
+
+ VFormat(szBuffer, sizeof(szBuffer), format, 4);
+
+ C_PrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer);
+ }
+ }
+ }
+
+ return 1;
+}
+
+/**
+ * Displays usage of an admin command to users depending on the setting of the sm_show_activity cvar.
+ * All users receive a message in their chat text, except for the originating client,
+ * who receives the message based on the current ReplySource.
+ * Supports color tags.
+ *
+ * @param client Client index doing the action, or 0 for server.
+ * @param tags Tag to prepend to the message.
+ * @param format Formatting rules.
+ * @param ... Variable number of format parameters.
+ * @noreturn
+ * @error
+ */
+stock int C_ShowActivity2(int client, const char[] tag, const char[] format, any ...) {
+ if (sm_show_activity == null) {
+ sm_show_activity = FindConVar("sm_show_activity");
+ }
+
+ char szBuffer[MAX_MESSAGE_LENGTH];
+ //char szCMessage[MAX_MESSAGE_LENGTH];
+ int value = sm_show_activity.IntValue;
+ // ReplySource replyto = GetCmdReplySource();
+
+ char name[MAX_NAME_LENGTH] = "Console";
+ char sign[MAX_NAME_LENGTH] = "ADMIN";
+ if (client != 0) {
+ if (client < 0 || client > MaxClients || !IsClientConnected(client)) {
+ ThrowError("Client index %d is invalid", client);
+ }
+
+ GetClientName(client, name, sizeof(name));
+ AdminId id = GetUserAdmin(client);
+ if (id == INVALID_ADMIN_ID || !GetAdminFlag(id, Admin_Generic, Access_Effective)) {
+ sign = "PLAYER";
+ }
+
+ SetGlobalTransTarget(client);
+ VFormat(szBuffer, sizeof(szBuffer), format, 4);
+
+ /* We don't display directly to the console because the chat text
+ * simply gets added to the console, so we don't want it to print
+ * twice.
+ */
+ C_PrintToChatEx(client, client, "%s%s", tag, szBuffer);
+ }
+ else {
+ SetGlobalTransTarget(LANG_SERVER);
+ VFormat(szBuffer, sizeof(szBuffer), format, 4);
+
+ C_RemoveTags(szBuffer, sizeof(szBuffer));
+ PrintToServer("%s%s\n", tag, szBuffer);
+ }
+
+ if (!value) {
+ return 1;
+ }
+
+ for (int i = 1; i <= MaxClients; ++i) {
+ if (!IsClientInGame(i) || IsFakeClient(i) || i == client) {
+ continue;
+ }
+
+ AdminId id = GetUserAdmin(i);
+ SetGlobalTransTarget(i);
+ if (id == INVALID_ADMIN_ID
+ || !GetAdminFlag(id, Admin_Generic, Access_Effective)) {
+ /* Treat this as a normal user. */
+ if ((value & 1) | (value & 2)) {
+ char newsign[MAX_NAME_LENGTH];
+
+ if ((value & 2)) {
+ newsign = name;
+ }
+ else {
+ newsign = sign;
+ }
+
+ VFormat(szBuffer, sizeof(szBuffer), format, 4);
+
+ C_PrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer);
+ }
+ }
+ else {
+ /* Treat this as an admin user */
+ bool is_root = GetAdminFlag(id, Admin_Root, Access_Effective);
+ if ((value & 4) || (value & 8) || ((value & 16) && is_root)) {
+ char newsign[MAX_NAME_LENGTH];
+
+ if ((value & 8) || ((value & 16) && is_root)) {
+ newsign = name;
+ }
+ else {
+ newsign = sign;
+ }
+
+ VFormat(szBuffer, sizeof(szBuffer), format, 4);
+
+ C_PrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer);
+ }
+ }
+ }
+
+ return 1;
+}
diff --git a/addons/sourcemod/scripting/include/multicolors/morecolors.inc b/addons/sourcemod/scripting/include/multicolors/morecolors.inc
new file mode 100644
index 0000000..2020b60
--- /dev/null
+++ b/addons/sourcemod/scripting/include/multicolors/morecolors.inc
@@ -0,0 +1,992 @@
+// MOAR COLORS
+// By Dr. McKay
+// Inspired by: https://forums.alliedmods.net/showthread.php?t=96831
+
+#if defined _more_colors_included
+ #endinput
+#endif
+#define _more_colors_included
+
+#pragma newdecls optional
+#include
+
+#define MORE_COLORS_VERSION "2.0.0-MC"
+#define MC_MAX_MESSAGE_LENGTH 256
+#define MAX_BUFFER_LENGTH (MC_MAX_MESSAGE_LENGTH * 4)
+
+#define MCOLOR_RED 0xFF4040
+#define MCOLOR_BLUE 0x99CCFF
+#define MCOLOR_GRAY 0xCCCCCC
+#define MCOLOR_GREEN 0x3EFF3E
+
+#define MC_GAME_DODS 0
+
+bool MC_SkipList[MAXPLAYERS+1];
+StringMap MC_Trie;
+int MC_TeamColors[][] = {{0xCCCCCC, 0x4D7942, 0xFF4040}}; // Multi-dimensional array for games that don't support SayText2. First index is the game index (as defined by the GAME_ defines), second index is team. 0 = spectator, 1 = team1, 2 = team2
+
+static ConVar sm_show_activity;
+
+/**
+ * Prints a message to a specific client in the chat area.
+ * Supports color tags.
+ *
+ * @param client Client index.
+ * @param message Message (formatting rules).
+ *
+ * On error/Errors: If the client is not connected an error will be thrown.
+ */
+stock void MC_PrintToChat(int client, const char[] message, any ...) {
+ MC_CheckTrie();
+
+ if (client <= 0 || client > MaxClients) {
+ ThrowError("Invalid client index %i", client);
+ }
+
+ if (!IsClientInGame(client)) {
+ ThrowError("Client %i is not in game", client);
+ }
+
+ char buffer[MAX_BUFFER_LENGTH];
+ char buffer2[MAX_BUFFER_LENGTH];
+
+ SetGlobalTransTarget(client);
+ Format(buffer, sizeof(buffer), "\x01%s", message);
+ VFormat(buffer2, sizeof(buffer2), buffer, 3);
+
+ MC_ReplaceColorCodes(buffer2);
+ MC_SendMessage(client, buffer2);
+}
+
+/**
+ * Prints a message to all clients in the chat area.
+ * Supports color tags.
+ *
+ * @param client Client index.
+ * @param message Message (formatting rules).
+ */
+stock void MC_PrintToChatAll(const char[] message, any ...) {
+ MC_CheckTrie();
+
+ char buffer[MAX_BUFFER_LENGTH], buffer2[MAX_BUFFER_LENGTH];
+
+ for (int i = 1; i <= MaxClients; ++i) {
+ if (!IsClientInGame(i) || MC_SkipList[i]) {
+ MC_SkipList[i] = false;
+ continue;
+ }
+
+ SetGlobalTransTarget(i);
+ Format(buffer, sizeof(buffer), "\x01%s", message);
+ VFormat(buffer2, sizeof(buffer2), buffer, 2);
+
+ MC_ReplaceColorCodes(buffer2);
+ MC_SendMessage(i, buffer2);
+ }
+}
+
+/**
+ * Prints a message to a specific client in the chat area.
+ * Supports color tags and teamcolor tag.
+ *
+ * @param client Client index.
+ * @param author Author index whose color will be used for teamcolor tag.
+ * @param message Message (formatting rules).
+ *
+ * On error/Errors: If the client or author are not connected an error will be thrown
+ */
+stock void MC_PrintToChatEx(int client, int author, const char[] message, any ...) {
+ MC_CheckTrie();
+
+ if (client <= 0 || client > MaxClients) {
+ ThrowError("Invalid client index %i", client);
+ }
+
+ if (!IsClientInGame(client)) {
+ ThrowError("Client %i is not in game", client);
+ }
+
+ if (author <= 0 || author > MaxClients) {
+ ThrowError("Invalid client index %i", author);
+ }
+
+ if (!IsClientInGame(author)) {
+ ThrowError("Client %i is not in game", author);
+ }
+
+ char buffer[MAX_BUFFER_LENGTH], buffer2[MAX_BUFFER_LENGTH];
+ SetGlobalTransTarget(client);
+ Format(buffer, sizeof(buffer), "\x01%s", message);
+ VFormat(buffer2, sizeof(buffer2), buffer, 4);
+ MC_ReplaceColorCodes(buffer2, author);
+ MC_SendMessage(client, buffer2, author);
+}
+
+/**
+ * Prints a message to all clients in the chat area.
+ * Supports color tags and teamcolor tag.
+ *
+ * @param author Author index whose color will be used for teamcolor tag.
+ * @param message Message (formatting rules).
+ *
+ * On error/Errors: If the author is not connected an error will be thrown.
+ */
+stock void MC_PrintToChatAllEx(int author, const char[] message, any ...) {
+ MC_CheckTrie();
+
+ if (author <= 0 || author > MaxClients) {
+ ThrowError("Invalid client index %i", author);
+ }
+
+ if (!IsClientInGame(author)) {
+ ThrowError("Client %i is not in game", author);
+ }
+
+ char buffer[MAX_BUFFER_LENGTH];
+ char buffer2[MAX_BUFFER_LENGTH];
+
+ for (int i = 1; i <= MaxClients; ++i) {
+ if (!IsClientInGame(i) || MC_SkipList[i]) {
+ MC_SkipList[i] = false;
+ continue;
+ }
+
+ SetGlobalTransTarget(i);
+ Format(buffer, sizeof(buffer), "\x01%s", message);
+ VFormat(buffer2, sizeof(buffer2), buffer, 3);
+
+ MC_ReplaceColorCodes(buffer2, author);
+ MC_SendMessage(i, buffer2, author);
+ }
+}
+
+/**
+ * Sends a SayText2 usermessage
+ *
+ * @param client Client to send usermessage to
+ * @param message Message to send
+ */
+stock void MC_SendMessage(int client, const char[] message, int author = 0) {
+ if (author == 0) {
+ author = client;
+ }
+
+ char buffer[MC_MAX_MESSAGE_LENGTH];
+ strcopy(buffer, sizeof(buffer), message);
+
+ UserMsg index = GetUserMessageId("SayText2");
+ if (index == INVALID_MESSAGE_ID) {
+ if (GetEngineVersion() == Engine_DODS) {
+ int team = GetClientTeam(author);
+ if (team == 0) {
+ ReplaceString(buffer, sizeof(buffer), "\x03", "\x04", false); // Unassigned gets green
+ }
+ else {
+ char temp[16];
+ Format(temp, sizeof(temp), "\x07%06X", MC_TeamColors[MC_GAME_DODS][team - 1]);
+ ReplaceString(buffer, sizeof(buffer), "\x03", temp, false);
+ }
+ }
+
+ PrintToChat(client, "%s", buffer);
+ return;
+ }
+
+ Handle buf = StartMessageOne("SayText2", client, USERMSG_RELIABLE|USERMSG_BLOCKHOOKS);
+ if (GetFeatureStatus(FeatureType_Native, "GetUserMessageType") == FeatureStatus_Available && GetUserMessageType() == UM_Protobuf) {
+ Protobuf pb = UserMessageToProtobuf(buf);
+ pb.SetInt("ent_idx", author);
+ pb.SetBool("chat", true);
+ pb.SetString("msg_name", buffer);
+ pb.AddString("params", "");
+ pb.AddString("params", "");
+ pb.AddString("params", "");
+ pb.AddString("params", "");
+ }
+ else {
+ BfWrite bf = UserMessageToBfWrite(buf);
+ bf.WriteByte(author); // Message author
+ bf.WriteByte(true); // Chat message
+ bf.WriteString(buffer); // Message text
+ }
+
+ EndMessage();
+}
+
+/**
+ * This function should only be used right in front of
+ * MC_PrintToChatAll or MC_PrintToChatAllEx. It causes those functions
+ * to skip the specified client when printing the message.
+ * After printing the message, the client will no longer be skipped.
+ *
+ * @param client Client index
+ */
+stock void MC_SkipNextClient(int client) {
+ if (client <= 0 || client > MaxClients) {
+ ThrowError("Invalid client index %i", client);
+ }
+
+ MC_SkipList[client] = true;
+}
+
+/**
+ * Checks if the colors trie is initialized and initializes it if it's not (used internally)
+ *
+ * @return No return
+ */
+stock void MC_CheckTrie() {
+ if (MC_Trie == null) {
+ MC_Trie = MC_InitColorTrie();
+ }
+}
+
+/**
+ * Replaces color tags in a string with color codes (used internally by MC_PrintToChat, MC_PrintToChatAll, MC_PrintToChatEx, and MC_PrintToChatAllEx
+ *
+ * @param buffer String.
+ * @param author Optional client index to use for {teamcolor} tags, or 0 for none
+ * @param removeTags Optional boolean value to determine whether we're replacing tags with colors, or just removing tags, used by MC_RemoveTags
+ * @param maxlen Optional value for max buffer length, used by MC_RemoveTags
+ *
+ * On error/Errors: If the client index passed for author is invalid or not in game.
+ */
+stock void MC_ReplaceColorCodes(char[] buffer, int author = 0, bool removeTags = false, int maxlen = MAX_BUFFER_LENGTH) {
+ MC_CheckTrie();
+ if (!removeTags) {
+ ReplaceString(buffer, maxlen, "{default}", "\x01", false);
+ }
+ else {
+ ReplaceString(buffer, maxlen, "{default}", "", false);
+ ReplaceString(buffer, maxlen, "{teamcolor}", "", false);
+ }
+
+ if (author != 0 && !removeTags) {
+ if (author < 0 || author > MaxClients) {
+ ThrowError("Invalid client index %i", author);
+ }
+
+ if (!IsClientInGame(author)) {
+ ThrowError("Client %i is not in game", author);
+ }
+
+ ReplaceString(buffer, maxlen, "{teamcolor}", "\x03", false);
+ }
+
+ int cursor = 0;
+ int value;
+ char tag[32], buff[32];
+ char[] output = new char[maxlen];
+
+ strcopy(output, maxlen, buffer);
+ // Since the string's size is going to be changing, output will hold the replaced string and we'll search buffer
+
+ Regex regex = new Regex("{[#a-zA-Z0-9]+}");
+ for (int i = 0; i < 1000; i++) { // The RegEx extension is quite flaky, so we have to loop here :/. This loop is supposed to be infinite and broken by return, but conditions have been added to be safe.
+ if (regex.Match(buffer[cursor]) < 1) {
+ delete regex;
+ strcopy(buffer, maxlen, output);
+ return;
+ }
+
+ regex.GetSubString(0, tag, sizeof(tag));
+ MC_StrToLower(tag);
+ cursor = StrContains(buffer[cursor], tag, false) + cursor + 1;
+ strcopy(buff, sizeof(buff), tag);
+ ReplaceString(buff, sizeof(buff), "{", "");
+ ReplaceString(buff, sizeof(buff), "}", "");
+
+ if (buff[0] == '#') {
+ if (strlen(buff) == 7) {
+ Format(buff, sizeof(buff), "\x07%s", buff[1]);
+ }
+ else if (strlen(buff) == 9) {
+ Format(buff, sizeof(buff), "\x08%s", buff[1]);
+ }
+ else {
+ continue;
+ }
+
+ if (removeTags) {
+ ReplaceString(output, maxlen, tag, "", false);
+ }
+ else {
+ ReplaceString(output, maxlen, tag, buff, false);
+ }
+ }
+ else if (!MC_Trie.GetValue(buff, value)) {
+ continue;
+ }
+
+ if (removeTags) {
+ ReplaceString(output, maxlen, tag, "", false);
+ }
+ else {
+ Format(buff, sizeof(buff), "\x07%06X", value);
+ ReplaceString(output, maxlen, tag, buff, false);
+ }
+ }
+ LogError("[MORE COLORS] Infinite loop broken.");
+}
+
+/**
+ * Gets a part of a string
+ *
+ * @param input String to get the part from
+ * @param output Buffer to write to
+ * @param maxlen Max length of output buffer
+ * @param start Position to start at
+ * @param numChars Number of characters to return, or 0 for the end of the string
+ */
+stock void CSubString(const char[] input, char[] output, int maxlen, int start, int numChars = 0) {
+ int i = 0;
+ for (;;) {
+ if (i == maxlen - 1 || i >= numChars || input[start + i] == '\0') {
+ output[i] = '\0';
+ return;
+ }
+
+ output[i] = input[start + i];
+ i++;
+ }
+}
+
+/**
+ * Converts a string to lowercase
+ *
+ * @param buffer String to convert
+ */
+stock void MC_StrToLower(char[] buffer) {
+ int len = strlen(buffer);
+ for (int i = 0; i < len; i++) {
+ buffer[i] = CharToLower(buffer[i]);
+ }
+}
+
+/**
+ * Adds a color to the colors trie
+ *
+ * @param name Color name, without braces
+ * @param color Hexadecimal representation of the color (0xRRGGBB)
+ * @return True if color was added successfully, false if a color already exists with that name
+ */
+stock bool MC_AddColor(const char[] name, int color) {
+ MC_CheckTrie();
+
+ int value;
+
+ if (MC_Trie.GetValue(name, value)) {
+ return false;
+ }
+
+ char newName[64];
+ strcopy(newName, sizeof(newName), name);
+
+ MC_StrToLower(newName);
+ MC_Trie.SetValue(newName, color);
+ return true;
+}
+
+/**
+ * Removes color tags from a message
+ *
+ * @param message Message to remove tags from
+ * @param maxlen Maximum buffer length
+ */
+stock void MC_RemoveTags(char[] message, int maxlen) {
+ MC_ReplaceColorCodes(message, 0, true, maxlen);
+}
+
+/**
+ * Replies to a command with colors
+ *
+ * @param client Client to reply to
+ * @param message Message (formatting rules)
+ */
+stock void MC_ReplyToCommand(int client, const char[] message, any ...) {
+ char buffer[MAX_BUFFER_LENGTH];
+ SetGlobalTransTarget(client);
+ VFormat(buffer, sizeof(buffer), message, 3);
+
+ if (client == 0) {
+ MC_RemoveTags(buffer, sizeof(buffer));
+ PrintToServer("%s", buffer);
+ }
+
+ if (GetCmdReplySource() == SM_REPLY_TO_CONSOLE) {
+ MC_RemoveTags(buffer, sizeof(buffer));
+ PrintToConsole(client, "%s", buffer);
+ }
+ else {
+ MC_PrintToChat(client, "%s", buffer);
+ }
+}
+
+/**
+ * Replies to a command with colors
+ *
+ * @param client Client to reply to
+ * @param author Client to use for {teamcolor}
+ * @param message Message (formatting rules)
+ */
+stock void MC_ReplyToCommandEx(int client, int author, const char[] message, any ...) {
+ char buffer[MAX_BUFFER_LENGTH];
+ SetGlobalTransTarget(client);
+ VFormat(buffer, sizeof(buffer), message, 4);
+
+ if (client == 0) {
+ MC_RemoveTags(buffer, sizeof(buffer));
+ PrintToServer("%s", buffer);
+ }
+
+ if (GetCmdReplySource() == SM_REPLY_TO_CONSOLE) {
+ MC_RemoveTags(buffer, sizeof(buffer));
+ PrintToConsole(client, "%s", buffer);
+ }
+ else {
+ MC_PrintToChatEx(client, author, "%s", buffer);
+ }
+}
+
+/**
+ * Displays usage of an admin command to users depending on the
+ * setting of the sm_show_activity cvar.
+ *
+ * This version does not display a message to the originating client
+ * if used from chat triggers or menus. If manual replies are used
+ * for these cases, then this function will suffice. Otherwise,
+ * MC_ShowActivity2() is slightly more useful.
+ * Supports color tags.
+ *
+ * @param client Client index doing the action, or 0 for server.
+ * @param format Formatting rules.
+ * @param ... Variable number of format parameters.
+ * @error
+ */
+stock int MC_ShowActivity(int client, const char[] format, any ...) {
+ if (sm_show_activity == null) {
+ sm_show_activity = FindConVar("sm_show_activity");
+ }
+
+ char tag[] = "[SM] ";
+
+ char szBuffer[MC_MAX_MESSAGE_LENGTH];
+ //char szCMessage[MC_MAX_MESSAGE_LENGTH];
+ int value = sm_show_activity.IntValue;
+ ReplySource replyto = GetCmdReplySource();
+
+ char name[MAX_NAME_LENGTH] = "Console";
+ char sign[MAX_NAME_LENGTH] = "ADMIN";
+ bool display_in_chat = false;
+ if (client != 0) {
+ if (client < 0 || client > MaxClients || !IsClientConnected(client))
+ ThrowError("Client index %d is invalid", client);
+
+ GetClientName(client, name, sizeof(name));
+ AdminId id = GetUserAdmin(client);
+ if (id == INVALID_ADMIN_ID || !id.HasFlag(Admin_Generic, Access_Effective)) {
+ sign = "PLAYER";
+ }
+
+ /* Display the message to the client? */
+ if (replyto == SM_REPLY_TO_CONSOLE) {
+ SetGlobalTransTarget(client);
+ VFormat(szBuffer, sizeof(szBuffer), format, 3);
+
+ MC_RemoveTags(szBuffer, sizeof(szBuffer));
+ PrintToConsole(client, "%s%s\n", tag, szBuffer);
+ display_in_chat = true;
+ }
+ }
+ else {
+ SetGlobalTransTarget(LANG_SERVER);
+ VFormat(szBuffer, sizeof(szBuffer), format, 3);
+
+ MC_RemoveTags(szBuffer, sizeof(szBuffer));
+ PrintToServer("%s%s\n", tag, szBuffer);
+ }
+
+ if (!value) {
+ return 1;
+ }
+
+ for (int i = 1; i <= MaxClients; ++i) {
+ if (!IsClientInGame(i) || IsFakeClient(i) || (display_in_chat && i == client)) {
+ continue;
+ }
+
+ AdminId id = GetUserAdmin(i);
+ SetGlobalTransTarget(i);
+ if (id == INVALID_ADMIN_ID || !id.HasFlag(Admin_Generic, Access_Effective)) {
+ /* Treat this as a normal user. */
+ if ((value & 1) | (value & 2)) {
+ char newsign[MAX_NAME_LENGTH];
+
+ if ((value & 2) || (i == client)) {
+ newsign = name;
+ }
+ else {
+ newsign = sign;
+ }
+
+ VFormat(szBuffer, sizeof(szBuffer), format, 3);
+
+ MC_PrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer);
+ }
+ }
+ else {
+ /* Treat this as an admin user */
+ bool is_root = id.HasFlag(Admin_Root, Access_Effective);
+ if ((value & 4) || (value & 8) || ((value & 16) && is_root)) {
+ char newsign[MAX_NAME_LENGTH];
+
+ if ((value & 8) || ((value & 16) && is_root) || (i == client)) {
+ newsign = name;
+ }
+ else {
+ newsign = sign;
+ }
+
+ VFormat(szBuffer, sizeof(szBuffer), format, 3);
+
+ MC_PrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer);
+ }
+ }
+ }
+
+ return 1;
+}
+
+/**
+ * Same as MC_ShowActivity(), except the tag parameter is used instead of "[SM] " (note that you must supply any spacing).
+ * Supports color tags.
+ *
+ * @param client Client index doing the action, or 0 for server.
+ * @param tags Tag to display with.
+ * @param format Formatting rules.
+ * @param ... Variable number of format parameters.
+ * @error
+ */
+stock int MC_ShowActivityEx(int client, const char[] tag, const char[] format, any ...) {
+ if (sm_show_activity == null) {
+ sm_show_activity = FindConVar("sm_show_activity");
+ }
+
+ char szBuffer[MC_MAX_MESSAGE_LENGTH];
+ //char szCMessage[MC_MAX_MESSAGE_LENGTH];
+ int value = sm_show_activity.IntValue;
+ ReplySource replyto = GetCmdReplySource();
+
+ char name[MAX_NAME_LENGTH] = "Console";
+ char sign[MAX_NAME_LENGTH] = "ADMIN";
+ bool display_in_chat = false;
+ if (client != 0) {
+ if (client < 0 || client > MaxClients || !IsClientConnected(client)) {
+ ThrowError("Client index %d is invalid", client);
+ }
+
+ GetClientName(client, name, sizeof(name));
+ AdminId id = GetUserAdmin(client);
+ if (id == INVALID_ADMIN_ID || !id.HasFlag(Admin_Generic, Access_Effective)) {
+ sign = "PLAYER";
+ }
+
+ /* Display the message to the client? */
+ if (replyto == SM_REPLY_TO_CONSOLE) {
+ SetGlobalTransTarget(client);
+ VFormat(szBuffer, sizeof(szBuffer), format, 4);
+
+ MC_RemoveTags(szBuffer, sizeof(szBuffer));
+ PrintToConsole(client, "%s%s\n", tag, szBuffer);
+ display_in_chat = true;
+ }
+ }
+ else {
+ SetGlobalTransTarget(LANG_SERVER);
+ VFormat(szBuffer, sizeof(szBuffer), format, 4);
+
+ MC_RemoveTags(szBuffer, sizeof(szBuffer));
+ PrintToServer("%s%s\n", tag, szBuffer);
+ }
+
+ if (!value) {
+ return 1;
+ }
+
+ for (int i = 1; i <= MaxClients; ++i) {
+ if (!IsClientInGame(i) || IsFakeClient(i) || (display_in_chat && i == client)) {
+ continue;
+ }
+
+ AdminId id = GetUserAdmin(i);
+ SetGlobalTransTarget(i);
+ if (id == INVALID_ADMIN_ID || !id.HasFlag(Admin_Generic, Access_Effective)) {
+ /* Treat this as a normal user. */
+ if ((value & 1) | (value & 2)) {
+ char newsign[MAX_NAME_LENGTH];
+
+ if ((value & 2) || (i == client)) {
+ newsign = name;
+ }
+ else {
+ newsign = sign;
+ }
+
+ VFormat(szBuffer, sizeof(szBuffer), format, 4);
+
+ MC_PrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer);
+ }
+ }
+ else {
+ /* Treat this as an admin user */
+ bool is_root = id.HasFlag(Admin_Root, Access_Effective);
+ if ((value & 4) || (value & 8) || ((value & 16) && is_root)) {
+ char newsign[MAX_NAME_LENGTH];
+
+ if ((value & 8) || ((value & 16) && is_root) || (i == client)) {
+ newsign = name;
+ }
+ else {
+ newsign = sign;
+ }
+
+ VFormat(szBuffer, sizeof(szBuffer), format, 4);
+
+ MC_PrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer);
+ }
+ }
+ }
+
+ return 1;
+}
+
+/**
+ * Displays usage of an admin command to users depending on the setting of the sm_show_activity cvar.
+ * All users receive a message in their chat text, except for the originating client,
+ * who receives the message based on the current ReplySource.
+ * Supports color tags.
+ *
+ * @param client Client index doing the action, or 0 for server.
+ * @param tags Tag to prepend to the message.
+ * @param format Formatting rules.
+ * @param ... Variable number of format parameters.
+ * @error
+ */
+stock int MC_ShowActivity2(int client, const char[] tag, const char[] format, any ...) {
+ if (sm_show_activity == null) {
+ sm_show_activity = FindConVar("sm_show_activity");
+ }
+
+ char szBuffer[MC_MAX_MESSAGE_LENGTH];
+ //char szCMessage[MC_MAX_MESSAGE_LENGTH];
+ int value = sm_show_activity.IntValue;
+ // ReplySource replyto = GetCmdReplySource();
+
+ char name[MAX_NAME_LENGTH] = "Console";
+ char sign[MAX_NAME_LENGTH] = "ADMIN";
+
+ if (client != 0) {
+ if (client < 0 || client > MaxClients || !IsClientConnected(client)) {
+ ThrowError("Client index %d is invalid", client);
+ }
+
+ GetClientName(client, name, sizeof(name));
+
+ AdminId id = GetUserAdmin(client);
+ if (id == INVALID_ADMIN_ID || !id.HasFlag(Admin_Generic, Access_Effective)) {
+ sign = "PLAYER";
+ }
+
+ SetGlobalTransTarget(client);
+ VFormat(szBuffer, sizeof(szBuffer), format, 4);
+
+ /* We don't display directly to the console because the chat text
+ * simply gets added to the console, so we don't want it to print
+ * twice.
+ */
+ MC_PrintToChatEx(client, client, "%s%s", tag, szBuffer);
+ }
+ else {
+ SetGlobalTransTarget(LANG_SERVER);
+ VFormat(szBuffer, sizeof(szBuffer), format, 4);
+
+ MC_RemoveTags(szBuffer, sizeof(szBuffer));
+ PrintToServer("%s%s\n", tag, szBuffer);
+ }
+
+ if (!value) {
+ return 1;
+ }
+
+ for (int i = 1; i <= MaxClients; ++i) {
+ if (!IsClientInGame(i) || IsFakeClient(i) || i == client) {
+ continue;
+ }
+
+ AdminId id = GetUserAdmin(i);
+ SetGlobalTransTarget(i);
+ if (id == INVALID_ADMIN_ID || !id.HasFlag(Admin_Generic, Access_Effective)) {
+ /* Treat this as a normal user. */
+ if ((value & 1) | (value & 2)) {
+ char newsign[MAX_NAME_LENGTH];
+
+ if ((value & 2)) {
+ newsign = name;
+ }
+ else {
+ newsign = sign;
+ }
+
+ VFormat(szBuffer, sizeof(szBuffer), format, 4);
+
+ MC_PrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer);
+ }
+ }
+ else {
+ /* Treat this as an admin user */
+ bool is_root = id.HasFlag(Admin_Root, Access_Effective);
+ if ((value & 4) || (value & 8) || ((value & 16) && is_root)) {
+ char newsign[MAX_NAME_LENGTH];
+
+
+ if ((value & 8) || ((value & 16) && is_root)) {
+ newsign = name;
+ }
+ else {
+ newsign = sign;
+ }
+
+ VFormat(szBuffer, sizeof(szBuffer), format, 4);
+
+ MC_PrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer);
+ }
+ }
+ }
+
+ return 1;
+}
+
+/**
+ * Determines whether a color name exists
+ *
+ * @param color The color name to check
+ * @return True if the color exists, false otherwise
+ */
+stock bool CColorExists(const char[] color) {
+ MC_CheckTrie();
+ int temp;
+ return MC_Trie.GetValue(color, temp);
+}
+
+/**
+ * Returns the hexadecimal representation of a client's team color (will NOT initialize the trie)
+ *
+ * @param client Client to get the team color for
+ * @return Client's team color in hexadecimal, or green if unknown
+ * On error/Errors: If the client index passed is invalid or not in game.
+ */
+stock int CGetTeamColor(int client) {
+ if (client <= 0 || client > MaxClients) {
+ ThrowError("Invalid client index %i", client);
+ }
+
+ if (!IsClientInGame(client)) {
+ ThrowError("Client %i is not in game", client);
+ }
+
+ int value;
+ switch(GetClientTeam(client)) {
+ case 1: {
+ value = MCOLOR_GRAY;
+ }
+ case 2: {
+ value = MCOLOR_RED;
+ }
+ case 3: {
+ value = MCOLOR_BLUE;
+ }
+ default: {
+ value = MCOLOR_GREEN;
+ }
+ }
+
+ return value;
+}
+
+stock StringMap MC_InitColorTrie() {
+ StringMap hTrie = new StringMap();
+ hTrie.SetValue("aliceblue", 0xF0F8FF);
+ hTrie.SetValue("allies", 0x4D7942); // same as Allies team in DoD:S
+ hTrie.SetValue("ancient", 0xEB4B4B); // same as Ancient item rarity in Dota 2
+ hTrie.SetValue("antiquewhite", 0xFAEBD7);
+ hTrie.SetValue("aqua", 0x00FFFF);
+ hTrie.SetValue("aquamarine", 0x7FFFD4);
+ hTrie.SetValue("arcana", 0xADE55C); // same as Arcana item rarity in Dota 2
+ hTrie.SetValue("axis", 0xFF4040); // same as Axis team in DoD:S
+ hTrie.SetValue("azure", 0x007FFF);
+ hTrie.SetValue("beige", 0xF5F5DC);
+ hTrie.SetValue("bisque", 0xFFE4C4);
+ hTrie.SetValue("black", 0x000000);
+ hTrie.SetValue("blanchedalmond", 0xFFEBCD);
+ hTrie.SetValue("blue", 0x99CCFF); // same as BLU/Counter-Terrorist team color
+ hTrie.SetValue("blueviolet", 0x8A2BE2);
+ hTrie.SetValue("brown", 0xA52A2A);
+ hTrie.SetValue("burlywood", 0xDEB887);
+ hTrie.SetValue("cadetblue", 0x5F9EA0);
+ hTrie.SetValue("chartreuse", 0x7FFF00);
+ hTrie.SetValue("chocolate", 0xD2691E);
+ hTrie.SetValue("collectors", 0xAA0000); // same as Collector's item quality in TF2
+ hTrie.SetValue("common", 0xB0C3D9); // same as Common item rarity in Dota 2
+ hTrie.SetValue("community", 0x70B04A); // same as Community item quality in TF2
+ hTrie.SetValue("coral", 0xFF7F50);
+ hTrie.SetValue("cornflowerblue", 0x6495ED);
+ hTrie.SetValue("cornsilk", 0xFFF8DC);
+ hTrie.SetValue("corrupted", 0xA32C2E); // same as Corrupted item quality in Dota 2
+ hTrie.SetValue("crimson", 0xDC143C);
+ hTrie.SetValue("cyan", 0x00FFFF);
+ hTrie.SetValue("darkblue", 0x00008B);
+ hTrie.SetValue("darkcyan", 0x008B8B);
+ hTrie.SetValue("darkgoldenrod", 0xB8860B);
+ hTrie.SetValue("darkgray", 0xA9A9A9);
+ hTrie.SetValue("darkgrey", 0xA9A9A9);
+ hTrie.SetValue("darkgreen", 0x006400);
+ hTrie.SetValue("darkkhaki", 0xBDB76B);
+ hTrie.SetValue("darkmagenta", 0x8B008B);
+ hTrie.SetValue("darkolivegreen", 0x556B2F);
+ hTrie.SetValue("darkorange", 0xFF8C00);
+ hTrie.SetValue("darkorchid", 0x9932CC);
+ hTrie.SetValue("darkred", 0x8B0000);
+ hTrie.SetValue("darksalmon", 0xE9967A);
+ hTrie.SetValue("darkseagreen", 0x8FBC8F);
+ hTrie.SetValue("darkslateblue", 0x483D8B);
+ hTrie.SetValue("darkslategray", 0x2F4F4F);
+ hTrie.SetValue("darkslategrey", 0x2F4F4F);
+ hTrie.SetValue("darkturquoise", 0x00CED1);
+ hTrie.SetValue("darkviolet", 0x9400D3);
+ hTrie.SetValue("deeppink", 0xFF1493);
+ hTrie.SetValue("deepskyblue", 0x00BFFF);
+ hTrie.SetValue("dimgray", 0x696969);
+ hTrie.SetValue("dimgrey", 0x696969);
+ hTrie.SetValue("dodgerblue", 0x1E90FF);
+ hTrie.SetValue("exalted", 0xCCCCCD); // same as Exalted item quality in Dota 2
+ hTrie.SetValue("firebrick", 0xB22222);
+ hTrie.SetValue("floralwhite", 0xFFFAF0);
+ hTrie.SetValue("forestgreen", 0x228B22);
+ hTrie.SetValue("frozen", 0x4983B3); // same as Frozen item quality in Dota 2
+ hTrie.SetValue("fuchsia", 0xFF00FF);
+ hTrie.SetValue("fullblue", 0x0000FF);
+ hTrie.SetValue("fullred", 0xFF0000);
+ hTrie.SetValue("gainsboro", 0xDCDCDC);
+ hTrie.SetValue("genuine", 0x4D7455); // same as Genuine item quality in TF2
+ hTrie.SetValue("ghostwhite", 0xF8F8FF);
+ hTrie.SetValue("gold", 0xFFD700);
+ hTrie.SetValue("goldenrod", 0xDAA520);
+ hTrie.SetValue("gray", 0xCCCCCC); // same as spectator team color
+ hTrie.SetValue("grey", 0xCCCCCC);
+ hTrie.SetValue("green", 0x3EFF3E);
+ hTrie.SetValue("greenyellow", 0xADFF2F);
+ hTrie.SetValue("haunted", 0x38F3AB); // same as Haunted item quality in TF2
+ hTrie.SetValue("honeydew", 0xF0FFF0);
+ hTrie.SetValue("hotpink", 0xFF69B4);
+ hTrie.SetValue("immortal", 0xE4AE33); // same as Immortal item rarity in Dota 2
+ hTrie.SetValue("indianred", 0xCD5C5C);
+ hTrie.SetValue("indigo", 0x4B0082);
+ hTrie.SetValue("ivory", 0xFFFFF0);
+ hTrie.SetValue("khaki", 0xF0E68C);
+ hTrie.SetValue("lavender", 0xE6E6FA);
+ hTrie.SetValue("lavenderblush", 0xFFF0F5);
+ hTrie.SetValue("lawngreen", 0x7CFC00);
+ hTrie.SetValue("legendary", 0xD32CE6); // same as Legendary item rarity in Dota 2
+ hTrie.SetValue("lemonchiffon", 0xFFFACD);
+ hTrie.SetValue("lightblue", 0xADD8E6);
+ hTrie.SetValue("lightcoral", 0xF08080);
+ hTrie.SetValue("lightcyan", 0xE0FFFF);
+ hTrie.SetValue("lightgoldenrodyellow", 0xFAFAD2);
+ hTrie.SetValue("lightgray", 0xD3D3D3);
+ hTrie.SetValue("lightgrey", 0xD3D3D3);
+ hTrie.SetValue("lightgreen", 0x99FF99);
+ hTrie.SetValue("lightpink", 0xFFB6C1);
+ hTrie.SetValue("lightsalmon", 0xFFA07A);
+ hTrie.SetValue("lightseagreen", 0x20B2AA);
+ hTrie.SetValue("lightskyblue", 0x87CEFA);
+ hTrie.SetValue("lightslategray", 0x778899);
+ hTrie.SetValue("lightslategrey", 0x778899);
+ hTrie.SetValue("lightsteelblue", 0xB0C4DE);
+ hTrie.SetValue("lightyellow", 0xFFFFE0);
+ hTrie.SetValue("lime", 0x00FF00);
+ hTrie.SetValue("limegreen", 0x32CD32);
+ hTrie.SetValue("linen", 0xFAF0E6);
+ hTrie.SetValue("magenta", 0xFF00FF);
+ hTrie.SetValue("maroon", 0x800000);
+ hTrie.SetValue("mediumaquamarine", 0x66CDAA);
+ hTrie.SetValue("mediumblue", 0x0000CD);
+ hTrie.SetValue("mediumorchid", 0xBA55D3);
+ hTrie.SetValue("mediumpurple", 0x9370D8);
+ hTrie.SetValue("mediumseagreen", 0x3CB371);
+ hTrie.SetValue("mediumslateblue", 0x7B68EE);
+ hTrie.SetValue("mediumspringgreen", 0x00FA9A);
+ hTrie.SetValue("mediumturquoise", 0x48D1CC);
+ hTrie.SetValue("mediumvioletred", 0xC71585);
+ hTrie.SetValue("midnightblue", 0x191970);
+ hTrie.SetValue("mintcream", 0xF5FFFA);
+ hTrie.SetValue("mistyrose", 0xFFE4E1);
+ hTrie.SetValue("moccasin", 0xFFE4B5);
+ hTrie.SetValue("mythical", 0x8847FF); // same as Mythical item rarity in Dota 2
+ hTrie.SetValue("navajowhite", 0xFFDEAD);
+ hTrie.SetValue("navy", 0x000080);
+ hTrie.SetValue("normal", 0xB2B2B2); // same as Normal item quality in TF2
+ hTrie.SetValue("oldlace", 0xFDF5E6);
+ hTrie.SetValue("olive", 0x9EC34F);
+ hTrie.SetValue("olivedrab", 0x6B8E23);
+ hTrie.SetValue("orange", 0xFFA500);
+ hTrie.SetValue("orangered", 0xFF4500);
+ hTrie.SetValue("orchid", 0xDA70D6);
+ hTrie.SetValue("palegoldenrod", 0xEEE8AA);
+ hTrie.SetValue("palegreen", 0x98FB98);
+ hTrie.SetValue("paleturquoise", 0xAFEEEE);
+ hTrie.SetValue("palevioletred", 0xD87093);
+ hTrie.SetValue("papayawhip", 0xFFEFD5);
+ hTrie.SetValue("peachpuff", 0xFFDAB9);
+ hTrie.SetValue("peru", 0xCD853F);
+ hTrie.SetValue("pink", 0xFFC0CB);
+ hTrie.SetValue("plum", 0xDDA0DD);
+ hTrie.SetValue("powderblue", 0xB0E0E6);
+ hTrie.SetValue("purple", 0x800080);
+ hTrie.SetValue("rare", 0x4B69FF); // same as Rare item rarity in Dota 2
+ hTrie.SetValue("red", 0xFF4040); // same as RED/Terrorist team color
+ hTrie.SetValue("rosybrown", 0xBC8F8F);
+ hTrie.SetValue("royalblue", 0x4169E1);
+ hTrie.SetValue("saddlebrown", 0x8B4513);
+ hTrie.SetValue("salmon", 0xFA8072);
+ hTrie.SetValue("sandybrown", 0xF4A460);
+ hTrie.SetValue("seagreen", 0x2E8B57);
+ hTrie.SetValue("seashell", 0xFFF5EE);
+ hTrie.SetValue("selfmade", 0x70B04A); // same as Self-Made item quality in TF2
+ hTrie.SetValue("sienna", 0xA0522D);
+ hTrie.SetValue("silver", 0xC0C0C0);
+ hTrie.SetValue("skyblue", 0x87CEEB);
+ hTrie.SetValue("slateblue", 0x6A5ACD);
+ hTrie.SetValue("slategray", 0x708090);
+ hTrie.SetValue("slategrey", 0x708090);
+ hTrie.SetValue("snow", 0xFFFAFA);
+ hTrie.SetValue("springgreen", 0x00FF7F);
+ hTrie.SetValue("steelblue", 0x4682B4);
+ hTrie.SetValue("strange", 0xCF6A32); // same as Strange item quality in TF2
+ hTrie.SetValue("tan", 0xD2B48C);
+ hTrie.SetValue("teal", 0x008080);
+ hTrie.SetValue("thistle", 0xD8BFD8);
+ hTrie.SetValue("tomato", 0xFF6347);
+ hTrie.SetValue("turquoise", 0x40E0D0);
+ hTrie.SetValue("uncommon", 0xB0C3D9); // same as Uncommon item rarity in Dota 2
+ hTrie.SetValue("unique", 0xFFD700); // same as Unique item quality in TF2
+ hTrie.SetValue("unusual", 0x8650AC); // same as Unusual item quality in TF2
+ hTrie.SetValue("valve", 0xA50F79); // same as Valve item quality in TF2
+ hTrie.SetValue("vintage", 0x476291); // same as Vintage item quality in TF2
+ hTrie.SetValue("violet", 0xEE82EE);
+ hTrie.SetValue("wheat", 0xF5DEB3);
+ hTrie.SetValue("white", 0xFFFFFF);
+ hTrie.SetValue("whitesmoke", 0xF5F5F5);
+ hTrie.SetValue("yellow", 0xFFFF00);
+ hTrie.SetValue("yellowgreen", 0x9ACD32);
+
+ return hTrie;
+}
diff --git a/addons/sourcemod/scripting/include/nativevotes.inc b/addons/sourcemod/scripting/include/nativevotes.inc
index 426e6d1..9bfaaf7 100644
--- a/addons/sourcemod/scripting/include/nativevotes.inc
+++ b/addons/sourcemod/scripting/include/nativevotes.inc
@@ -31,6 +31,7 @@
#include
#include
+#include
// NativeVotes 1.1 series
diff --git a/addons/sourcemod/scripting/nativevotes.sp b/addons/sourcemod/scripting/nativevotes.sp
index 4de0473..d6fc915 100644
--- a/addons/sourcemod/scripting/nativevotes.sp
+++ b/addons/sourcemod/scripting/nativevotes.sp
@@ -46,7 +46,7 @@ EngineVersion g_EngineVersion = Engine_Unknown;
#include "nativevotes/data-keyvalues.sp"
-#define VERSION "1.1.1fix"
+#define VERSION "25w52a"
#define LOGTAG "NV"
@@ -148,7 +148,7 @@ public Plugin myinfo =
author = "Powerlord",
description = "Voting API to use the game's native vote panels. Compatible with L4D, L4D2, TF2, and CS:GO.",
version = VERSION,
- url = "https://forums.alliedmods.net/showthread.php?t=208008"
+ url = "https://github.com/Heapons/sourcemod-nativevotes-updated/"
}
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max)
@@ -746,7 +746,7 @@ void OnVoteSelect(NativeVote vote, int client, int item)
if (g_Cvar_VoteChat.BoolValue)
{
- PrintToChatAll("[%s] %t", LOGTAG, phrase, name, choice);
+ CPrintToChatAll("[%s] %t", LOGTAG, phrase, name, choice);
}
if (g_Cvar_VoteClientConsole.BoolValue)
@@ -1989,7 +1989,7 @@ public int Native_IsClientInVotePool(Handle plugin, int numParams)
{
int client = GetNativeCell(1);
- if (client <= 0 || client > MaxClients)
+ if (!client || client > MaxClients)
{
ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index %d", client);
return false;
@@ -2379,7 +2379,7 @@ public int Native_SetTarget(Handle plugin, int numParams)
int userid;
char steamId[20];
- if (client <= 0)
+ if (!client)
{
userid = 0;
steamId = "";
diff --git a/addons/sourcemod/scripting/nativevotes/game.sp b/addons/sourcemod/scripting/nativevotes/game.sp
index 46f8286..ed941eb 100644
--- a/addons/sourcemod/scripting/nativevotes/game.sp
+++ b/addons/sourcemod/scripting/nativevotes/game.sp
@@ -736,7 +736,7 @@ void Game_DisplayRawVotePass(NativeVotesPassType passType, int team, int client=
{
case Engine_Left4Dead:
{
- if (!client)
+ if (client <= 0)
{
L4DL4D2_VotePassToTranslation(passType, translation, sizeof(translation));
@@ -2083,7 +2083,7 @@ static void L4D2_DisplayVote(NativeVote vote, int[] clients, int num_clients)
static void L4D2_VotePass(const char[] translation, const char[] details, int team, int client=0)
{
BfWrite votePass;
- if (!client)
+ if (client <= 0)
{
votePass = UserMessageToBfWrite(StartMessageAll("VotePass", USERMSG_RELIABLE));
}
@@ -2509,7 +2509,7 @@ static void CSGO_VotePass(const char[] translation, const char[] details, int te
{
Protobuf votePass = null;
- if (!client)
+ if (client <= 0)
{
votePass = UserMessageToProtobuf(StartMessageAll("VotePass", USERMSG_RELIABLE));
}
@@ -2530,7 +2530,7 @@ static void TF2_VotePass(const char[] translation, const char[] details, int tea
{
BfWrite votePass = null;
- if (!client)
+ if (client <= 0)
{
votePass = UserMessageToBfWrite(StartMessageAll("VotePass", USERMSG_RELIABLE));
}
diff --git a/addons/sourcemod/scripting/nativevotes-basecommands.sp b/addons/sourcemod/scripting/nativevotes_basecommands.sp
similarity index 87%
rename from addons/sourcemod/scripting/nativevotes-basecommands.sp
rename to addons/sourcemod/scripting/nativevotes_basecommands.sp
index 8696a54..e50e8af 100644
--- a/addons/sourcemod/scripting/nativevotes-basecommands.sp
+++ b/addons/sourcemod/scripting/nativevotes_basecommands.sp
@@ -1,188 +1,188 @@
-/**
- * vim: set ts=4 :
- * =============================================================================
- * NativeVotes Basecommands Plugin
- * Provides cancelvote and revote functionality for NativeVotes
- *
- * NativeVotes (C) 2011-2014 Ross Bemrose (Powerlord). All rights reserved.
- * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved.
- * =============================================================================
- *
- * This program is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, version 3.0, as published by the
- * Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program. If not, see .
- *
- * As a special exception, AlliedModders LLC gives you permission to link the
- * code of this program (as well as its derivative works) to "Half-Life 2," the
- * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
- * by the Valve Corporation. You must obey the GNU General Public License in
- * all respects for all other code used. Additionally, AlliedModders LLC grants
- * this exception to all derivative works. AlliedModders LLC defines further
- * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
- * or .
- *
- * Version: $Id$
- */
-
-#include
-#include
-
-#undef REQUIRE_PLUGIN
-#include
-
-#define VERSION "1.1"
-#pragma newdecls required
-#pragma semicolon 1
-
-TopMenu hTopMenu;
-
-public Plugin myinfo =
-{
- name = "NativeVotes Basic Commands",
- author = "Powerlord and AlliedModders LLC",
- description = "Revote and Cancel support for NativeVotes",
- version = VERSION,
- url = "https://forums.alliedmods.net/showthread.php?t=208008"
-}
-
-public void OnPluginStart()
-{
- LoadTranslations("core.phrases");
- LoadTranslations("common.phrases");
-
- AddCommandListener(Command_CancelVote, "sm_cancelvote");
- AddCommandListener(Command_ReVote, "sm_revote");
-}
-
-bool PerformCancelVote(int client)
-{
- if (!NativeVotes_IsVoteInProgress())
- {
- return false;
- }
-
- ShowActivity2(client, "[NV] ", "%t", "Cancelled Vote");
-
- NativeVotes_Cancel();
- return true;
-}
-
-public Action Command_CancelVote(int client, const char[] command, int argc)
-{
- if (!CheckCommandAccess(client, "sm_cancelvote", ADMFLAG_VOTE))
- {
- if (IsVoteInProgress())
- {
- // Let basecommands handle it
- return Plugin_Continue;
- }
-
- ReplyToCommand(client, "%t", "No Access");
- return Plugin_Stop;
- }
-
- if (PerformCancelVote(client))
- {
- return Plugin_Stop;
- }
- else
- {
- return Plugin_Continue;
- }
-}
-
-public void AdminMenu_CancelVote(Handle topmenu,
- TopMenuAction action,
- TopMenuObject object_id,
- int param,
- char[] buffer,
- int maxlength)
-{
- if (action == TopMenuAction_DisplayOption)
- {
- Format(buffer, maxlength, "%T", "Cancel vote", param);
- }
- else if (action == TopMenuAction_SelectOption)
- {
- PerformCancelVote(param);
- RedisplayAdminMenu(topmenu, param);
- }
- else if (action == TopMenuAction_DrawOption)
- {
- buffer[0] = NativeVotes_IsVoteInProgress() ? ITEMDRAW_DEFAULT : ITEMDRAW_IGNORE;
- }
-}
-
-public Action Command_ReVote(int client, const char[] command, int argc)
-{
- if (client == 0)
- {
- return Plugin_Continue;
- }
-
- if (!NativeVotes_IsVoteInProgress())
- {
- return Plugin_Continue;
- }
-
- if (!NativeVotes_IsClientInVotePool(client))
- {
- if (IsVoteInProgress())
- {
- // Let basecommands handle it
- return Plugin_Continue;
- }
-
- ReplyToCommand(client, "[NV] %t", "Cannot participate in vote");
- return Plugin_Stop;
- }
-
- if (NativeVotes_RedrawClientVote(client))
- {
- return Plugin_Stop;
- }
- else if (!IsVoteInProgress())
- {
- ReplyToCommand(client, "[NV] %t", "Cannot change vote");
- return Plugin_Stop;
- }
-
- return Plugin_Continue;
-}
-
-public void OnAdminMenuReady(Handle aTopMenu)
-{
- TopMenu topmenu = TopMenu.FromHandle(aTopMenu);
-
- /* Block us from being called twice */
- if (topmenu == hTopMenu)
- {
- return;
- }
-
- /* Save the Handle */
- hTopMenu = topmenu;
-
- TopMenuObject voting_commands = hTopMenu.FindCategory(ADMINMENU_VOTINGCOMMANDS);
-
- if (voting_commands != INVALID_TOPMENUOBJECT)
- {
- hTopMenu.AddItem("sm_cancelvote", AdminMenu_CancelVote, voting_commands, "sm_cancelvote", ADMFLAG_VOTE);
- }
-}
-
-public void OnLibraryRemoved(const char[] name)
-{
- if (strcmp(name, "adminmenu") == 0)
- {
- hTopMenu = null;
- }
-}
+/**
+ * vim: set ts=4 :
+ * =============================================================================
+ * NativeVotes Basecommands Plugin
+ * Provides cancelvote and revote functionality for NativeVotes
+ *
+ * NativeVotes (C) 2011-2014 Ross Bemrose (Powerlord). All rights reserved.
+ * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved.
+ * =============================================================================
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 3.0, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ *
+ * As a special exception, AlliedModders LLC gives you permission to link the
+ * code of this program (as well as its derivative works) to "Half-Life 2," the
+ * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
+ * by the Valve Corporation. You must obey the GNU General Public License in
+ * all respects for all other code used. Additionally, AlliedModders LLC grants
+ * this exception to all derivative works. AlliedModders LLC defines further
+ * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
+ * or .
+ *
+ * Version: $Id$
+ */
+
+#include
+#include
+
+#undef REQUIRE_PLUGIN
+#include
+
+#define VERSION "25w52a"
+#pragma newdecls required
+#pragma semicolon 1
+
+TopMenu hTopMenu;
+
+public Plugin myinfo =
+{
+ name = "NativeVotes Basic Commands",
+ author = "Powerlord and AlliedModders LLC",
+ description = "Revote and Cancel support for NativeVotes",
+ version = VERSION,
+ url = "https://github.com/Heapons/sourcemod-nativevotes-updated/"
+}
+
+public void OnPluginStart()
+{
+ LoadTranslations("core.phrases");
+ LoadTranslations("common.phrases");
+
+ AddCommandListener(Command_CancelVote, "sm_cancelvote");
+ AddCommandListener(Command_ReVote, "sm_revote");
+}
+
+bool PerformCancelVote(int client)
+{
+ if (!NativeVotes_IsVoteInProgress())
+ {
+ return false;
+ }
+
+ CShowActivity2(client, "[{lightgreen}NativeVotes{default}] ", "%t", "Cancelled Vote");
+
+ NativeVotes_Cancel();
+ return true;
+}
+
+public Action Command_CancelVote(int client, const char[] command, int argc)
+{
+ if (!CheckCommandAccess(client, "sm_cancelvote", ADMFLAG_VOTE))
+ {
+ if (IsVoteInProgress())
+ {
+ // Let basecommands handle it
+ return Plugin_Continue;
+ }
+
+ CReplyToCommand(client, "%t", "No Access");
+ return Plugin_Stop;
+ }
+
+ if (PerformCancelVote(client))
+ {
+ return Plugin_Stop;
+ }
+ else
+ {
+ return Plugin_Continue;
+ }
+}
+
+public void AdminMenu_CancelVote(Handle topmenu,
+ TopMenuAction action,
+ TopMenuObject object_id,
+ int param,
+ char[] buffer,
+ int maxlength)
+{
+ if (action == TopMenuAction_DisplayOption)
+ {
+ Format(buffer, maxlength, "%T", "Cancel vote", param);
+ }
+ else if (action == TopMenuAction_SelectOption)
+ {
+ PerformCancelVote(param);
+ RedisplayAdminMenu(topmenu, param);
+ }
+ else if (action == TopMenuAction_DrawOption)
+ {
+ buffer[0] = NativeVotes_IsVoteInProgress() ? ITEMDRAW_DEFAULT : ITEMDRAW_IGNORE;
+ }
+}
+
+public Action Command_ReVote(int client, const char[] command, int argc)
+{
+ if (client == 0)
+ {
+ return Plugin_Continue;
+ }
+
+ if (!NativeVotes_IsVoteInProgress())
+ {
+ return Plugin_Continue;
+ }
+
+ if (!NativeVotes_IsClientInVotePool(client))
+ {
+ if (IsVoteInProgress())
+ {
+ // Let basecommands handle it
+ return Plugin_Continue;
+ }
+
+ CReplyToCommand(client, "[{lightgreen}NativeVotes{default}] %t", "Cannot participate in vote");
+ return Plugin_Stop;
+ }
+
+ if (NativeVotes_RedrawClientVote(client))
+ {
+ return Plugin_Stop;
+ }
+ else if (!IsVoteInProgress())
+ {
+ CReplyToCommand(client, "[{lightgreen}NativeVotes{default}] %t", "Cannot change vote");
+ return Plugin_Stop;
+ }
+
+ return Plugin_Continue;
+}
+
+public void OnAdminMenuReady(Handle aTopMenu)
+{
+ TopMenu topmenu = TopMenu.FromHandle(aTopMenu);
+
+ /* Block us from being called twice */
+ if (topmenu == hTopMenu)
+ {
+ return;
+ }
+
+ /* Save the Handle */
+ hTopMenu = topmenu;
+
+ TopMenuObject voting_commands = hTopMenu.FindCategory(ADMINMENU_VOTINGCOMMANDS);
+
+ if (voting_commands != INVALID_TOPMENUOBJECT)
+ {
+ hTopMenu.AddItem("sm_cancelvote", AdminMenu_CancelVote, voting_commands, "sm_cancelvote", ADMFLAG_VOTE);
+ }
+}
+
+public void OnLibraryRemoved(const char[] name)
+{
+ if (strcmp(name, "adminmenu") == 0)
+ {
+ hTopMenu = null;
+ }
+}
diff --git a/addons/sourcemod/scripting/nativevotes_basevotes.sp b/addons/sourcemod/scripting/nativevotes_basevotes.sp
new file mode 100644
index 0000000..4e8e677
--- /dev/null
+++ b/addons/sourcemod/scripting/nativevotes_basevotes.sp
@@ -0,0 +1,734 @@
+/**
+ * vim: set ts=4 :
+ * =============================================================================
+ * NativeVotes Basic Votes Plugin
+ * Implements basic vote commands using NativeVotes.
+ * Based on the SourceMod version.
+ *
+ * NativeVotes (C)2011-2014 Ross Bemrose (Powerlord). All rights reserved.
+ * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved.
+ * =============================================================================
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 3.0, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ *
+ * As a special exception, AlliedModders LLC gives you permission to link the
+ * code of this program (as well as its derivative works) to "Half-Life 2," the
+ * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
+ * by the Valve Corporation. You must obey the GNU General Public License in
+ * all respects for all other code used. Additionally, AlliedModders LLC grants
+ * this exception to all derivative works. AlliedModders LLC defines further
+ * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
+ * or .
+ *
+ * Version: $Id$
+ */
+
+#pragma semicolon 1
+
+#include
+#undef REQUIRE_PLUGIN
+#include
+#include
+
+#define VERSION "25w52a"
+
+public Plugin:myinfo =
+{
+ name = "NativeVotes Basic Votes",
+ author = "Powerlord and AlliedModders LLC",
+ description = "NativeVotes Basic Vote Commands",
+ version = VERSION,
+ url = "https://github.com/Heapons/sourcemod-nativevotes-updated/"
+};
+
+#define VOTE_NO "###no###"
+#define VOTE_YES "###yes###"
+
+//new Handle:g_hVoteMenu = INVALID_HANDLE;
+
+new Handle:g_Cvar_Limits[3] = {INVALID_HANDLE, ...};
+//new Handle:g_Cvar_VoteSay = INVALID_HANDLE;
+
+enum voteType
+{
+ map,
+ kick,
+ ban,
+ question
+}
+
+new voteType:g_voteType = voteType:question;
+
+// Menu API does not provide us with a way to pass multiple peices of data with a single
+// choice, so some globals are used to hold stuff.
+//
+#define VOTE_CLIENTID 0
+#define VOTE_USERID 1
+new g_voteClient[2]; /* Holds the target's client id and user id */
+
+#define VOTE_NAME 0
+#define VOTE_AUTHID 1
+#define VOTE_IP 2
+new String:g_voteInfo[3][65]; /* Holds the target's name, authid, and IP */
+
+new String:g_voteArg[256]; /* Used to hold ban/kick reasons or vote questions */
+
+
+new Handle:hTopMenu = INVALID_HANDLE;
+
+// NativeVotes
+new bool:g_NativeVotes;
+
+//new g_Cvar_NativeVotesMenu = INVALID_HANDLE;
+
+#include "nativevotes_basevotes/votekick.sp"
+#include "nativevotes_basevotes/voteban.sp"
+#include "nativevotes_basevotes/votemap.sp"
+
+public OnPluginStart()
+{
+ LoadTranslations("common.phrases");
+ LoadTranslations("basevotes.phrases");
+ LoadTranslations("plugin.basecommands");
+ LoadTranslations("basebans.phrases");
+
+ RegAdminCmd("sm_votemap", Command_Votemap, ADMFLAG_VOTE|ADMFLAG_CHANGEMAP, "sm_votemap [mapname2] ... [mapname5] ");
+ RegAdminCmd("sm_votekick", Command_Votekick, ADMFLAG_VOTE|ADMFLAG_KICK, "sm_votekick [reason]");
+ RegAdminCmd("sm_voteban", Command_Voteban, ADMFLAG_VOTE|ADMFLAG_BAN, "sm_voteban [reason]");
+ RegAdminCmd("sm_vote", Command_Vote, ADMFLAG_VOTE, "sm_vote [Answer1] [Answer2] ... [Answer5]");
+
+ /*
+ g_Cvar_Show = FindConVar("sm_vote_show");
+ if (g_Cvar_Show == INVALID_HANDLE)
+ {
+ g_Cvar_Show = CreateConVar("sm_vote_show", "1", "Show player's votes? Default on.", 0, true, 0.0, true, 1.0);
+ }
+ */
+
+ g_Cvar_Limits[0] = CreateConVar("sm_vote_map", "0.60", "percent required for successful map vote.", 0, true, 0.05, true, 1.0);
+ g_Cvar_Limits[1] = CreateConVar("sm_vote_kick", "0.60", "percent required for successful kick vote.", 0, true, 0.05, true, 1.0);
+ g_Cvar_Limits[2] = CreateConVar("sm_vote_ban", "0.60", "percent required for successful ban vote.", 0, true, 0.05, true, 1.0);
+ CreateConVar("nativevotes_basevotes_version", VERSION, "NativeVotes Basic Votes version", FCVAR_PLUGIN|FCVAR_NOTIFY|FCVAR_DONTRECORD|FCVAR_SPONLY);
+
+ g_SelectedMaps = CreateArray(PLATFORM_MAX_PATH);
+
+ g_MapList = CreateMenu(MenuHandler_Map, MenuAction_DrawItem|MenuAction_Display);
+ SetMenuTitle(g_MapList, "%T", "Please select a map", LANG_SERVER);
+ SetMenuExitBackButton(g_MapList, true);
+
+ decl String:mapListPath[PLATFORM_MAX_PATH];
+ BuildPath(Path_SM, mapListPath, sizeof(mapListPath), "configs/adminmenu_maplist.ini");
+ SetMapListCompatBind("sm_votemap menu", mapListPath);
+}
+
+public OnAllPluginsLoaded()
+{
+ if (FindPluginByFile("basevotes.smx") != INVALID_HANDLE)
+ {
+
+ LogMessage("Unloading basevotes to prevent conflicts...");
+ ServerCommand("sm plugins unload basevotes");
+
+ decl String:oldPath[PLATFORM_MAX_PATH];
+ decl String:newPath[PLATFORM_MAX_PATH];
+
+ BuildPath(Path_SM, oldPath, sizeof(oldPath), "plugins/basevotes.smx");
+ BuildPath(Path_SM, newPath, sizeof(newPath), "plugins/disabled/basevotes.smx");
+ if (RenameFile(newPath, oldPath))
+ {
+ LogMessage("Moving basevotes to disabled.");
+ }
+
+ //SetFailState("This plugin replaces basevotes. You cannot run both at once.");
+ }
+
+ /* Account for late loading */
+ new Handle:topmenu;
+ if (LibraryExists("adminmenu") && ((topmenu = GetAdminTopMenu()) != INVALID_HANDLE))
+ {
+ OnAdminMenuReady(topmenu);
+ }
+
+ g_NativeVotes = LibraryExists("nativevotes") && NativeVotes_IsVoteTypeSupported(NativeVotesType_Custom_YesNo);
+}
+
+public OnLibraryAdded(const String:name[])
+{
+ new Handle:topmenu;
+ if (StrEqual(name, "adminmenu") && ((topmenu = GetAdminTopMenu()) != INVALID_HANDLE))
+ {
+ OnAdminMenuReady(topmenu);
+ }
+ else
+ if (StrEqual(name, "nativevotes") && NativeVotes_IsVoteTypeSupported(NativeVotesType_Custom_YesNo))
+ {
+ g_NativeVotes = true;
+ }
+}
+
+public OnLibraryRemoved(const String:name[])
+{
+ if (StrEqual(name, "adminmenu"))
+ {
+ hTopMenu = INVALID_HANDLE;
+ }
+ else
+ if (StrEqual(name, "nativevotes"))
+ {
+ g_NativeVotes = false;
+ }
+}
+
+public OnConfigsExecuted()
+{
+ g_mapCount = LoadMapList(g_MapList);
+}
+
+public OnAdminMenuReady(Handle:topmenu)
+{
+ /* Block us from being called twice */
+ if (topmenu == hTopMenu)
+ {
+ return;
+ }
+
+ /* Save the Handle */
+ hTopMenu = topmenu;
+
+ /* Build the "Voting Commands" category */
+ new TopMenuObject:voting_commands = FindTopMenuCategory(hTopMenu, ADMINMENU_VOTINGCOMMANDS);
+
+ if (voting_commands != INVALID_TOPMENUOBJECT)
+ {
+ AddToTopMenu(hTopMenu,
+ "sm_votekick",
+ TopMenuObject_Item,
+ AdminMenu_VoteKick,
+ voting_commands,
+ "sm_votekick",
+ ADMFLAG_VOTE|ADMFLAG_KICK);
+
+ AddToTopMenu(hTopMenu,
+ "sm_voteban",
+ TopMenuObject_Item,
+ AdminMenu_VoteBan,
+ voting_commands,
+ "sm_voteban",
+ ADMFLAG_VOTE|ADMFLAG_BAN);
+
+ AddToTopMenu(hTopMenu,
+ "sm_votemap",
+ TopMenuObject_Item,
+ AdminMenu_VoteMap,
+ voting_commands,
+ "sm_votemap",
+ ADMFLAG_VOTE|ADMFLAG_CHANGEMAP);
+ }
+}
+
+public Action:Command_Vote(client, args)
+{
+ if (args < 1)
+ {
+ CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] Usage: sm_vote [Answer1] [Answer2] ... [Answer5]");
+ return Plugin_Handled;
+ }
+
+ if (Internal_IsVoteInProgress())
+ {
+ CReplyToCommand(client, "%t", "Vote in Progress");
+ return Plugin_Handled;
+ }
+
+ if (!TestVoteDelay(client))
+ {
+ return Plugin_Handled;
+ }
+
+ decl String:text[256];
+ GetCmdArgString(text, sizeof(text));
+
+ decl String:answers[5][64];
+ new answerCount;
+ new len = BreakString(text, g_voteArg, sizeof(g_voteArg));
+ new pos = len;
+
+ while (args > 1 && pos != -1 && answerCount < 5)
+ {
+ pos = BreakString(text[len], answers[answerCount], sizeof(answers[]));
+ answerCount++;
+
+ if (pos != -1)
+ {
+ len += pos;
+ }
+ }
+
+ LogAction(client, -1, "\"%L\" initiated a generic vote.", client);
+ CShowActivity2(client, "[{lightgreen}NativeVotes\x01] ", "%t", "Initiate Vote", g_voteArg);
+
+ g_voteType = voteType:question;
+ new Handle:voteMenu;
+
+ if (g_NativeVotes && (answerCount < 2 || NativeVotes_IsVoteTypeSupported(NativeVotesType_Custom_Mult)) )
+ {
+ new NativeVotesType:nVoteType = answerCount < 2 ? NativeVotesType_Custom_YesNo : NativeVotesType_Custom_Mult;
+
+ voteMenu = NativeVotes_Create(Handler_NativeVoteCallback, nVoteType, MenuAction:MENU_ACTIONS_ALL);
+ NativeVotes_SetTitle(voteMenu, g_voteArg);
+
+ if (answerCount >= 2)
+ {
+ for (new i = 0; i < answerCount; i++)
+ {
+ NativeVotes_AddItem(voteMenu, answers[i], answers[i]);
+ }
+ }
+
+ //NativeVotes_SetInitiator(voteMenu, client);
+ NativeVotes_DisplayToAll(voteMenu, 20);
+ }
+ else
+ {
+ voteMenu = CreateMenu(Handler_VoteCallback, MenuAction:MENU_ACTIONS_ALL);
+ SetMenuTitle(voteMenu, "%s?", g_voteArg);
+
+ if (answerCount < 2)
+ {
+ AddMenuItem(voteMenu, VOTE_YES, "Yes");
+ AddMenuItem(voteMenu, VOTE_NO, "No");
+ }
+ else
+ {
+ for (new i = 0; i < answerCount; i++)
+ {
+ AddMenuItem(voteMenu, answers[i], answers[i]);
+ }
+ }
+
+ SetMenuExitButton(voteMenu, false);
+ VoteMenuToAll(voteMenu, 20);
+ }
+ return Plugin_Handled;
+}
+
+public Handler_VoteCallback(Handle:menu, MenuAction:action, param1, param2)
+{
+ switch (action)
+ {
+ case MenuAction_End:
+ {
+ //VoteMenuClose();
+ CloseHandle(menu);
+ }
+
+ case MenuAction_Display:
+ {
+ if (g_voteType != voteType:question)
+ {
+ decl String:title[64];
+ GetMenuTitle(menu, title, sizeof(title));
+
+ decl String:buffer[255];
+ Format(buffer, sizeof(buffer), "%T", title, param1, g_voteInfo[VOTE_NAME]);
+
+ new Handle:panel = Handle:param2;
+ SetPanelTitle(panel, buffer);
+ }
+ }
+
+ case MenuAction_DisplayItem:
+ {
+ decl String:display[64];
+ GetMenuItem(menu, param2, "", 0, _, display, sizeof(display));
+
+ if (strcmp(display, "No") == 0 || strcmp(display, "Yes") == 0)
+ {
+ decl String:buffer[255];
+ Format(buffer, sizeof(buffer), "%T", display, param1);
+
+ return RedrawMenuItem(buffer);
+ }
+ }
+
+ case MenuAction_VoteCancel:
+ {
+ if (param1 == VoteCancel_NoVotes)
+ {
+ CPrintToChatAll("%t", "No Votes Cast");
+ }
+ }
+
+ case MenuAction_VoteEnd:
+ {
+ decl String:item[64], String:display[64];
+ new Float:percent, Float:limit, votes, totalVotes;
+
+ GetMenuVoteInfo(param2, votes, totalVotes);
+ GetMenuItem(menu, param1, item, sizeof(item), _, display, sizeof(display));
+
+ if (strcmp(item, VOTE_NO) == 0 && param1 == 1)
+ {
+ votes = totalVotes - votes; // Reverse the votes to be in relation to the Yes option.
+ }
+
+ percent = GetVotePercent(votes, totalVotes);
+
+ if (g_voteType != voteType:question)
+ {
+ limit = GetConVarFloat(g_Cvar_Limits[g_voteType]);
+ }
+
+ /* :TODO: g_voteClient[userid] needs to be checked */
+
+ // A multi-argument vote is "always successful", but have to check if its a Yes/No vote.
+ if ((strcmp(item, VOTE_YES) == 0 && FloatCompare(percent,limit) < 0 && param1 == 0) || (strcmp(item, VOTE_NO) == 0 && param1 == 1))
+ {
+ /* :TODO: g_voteClient[userid] should be used here and set to -1 if not applicable.
+ */
+ LogAction(-1, -1, "Vote failed.");
+ CPrintToChatAll("%t", "Vote Failed", RoundToNearest(100.0*limit), RoundToNearest(100.0*percent), totalVotes);
+ }
+ else
+ {
+ CPrintToChatAll("%t", "Vote Successful", RoundToNearest(100.0*percent), totalVotes);
+
+ switch (g_voteType)
+ {
+ case (voteType:question):
+ {
+ if (strcmp(item, VOTE_NO) == 0 || strcmp(item, VOTE_YES) == 0)
+ {
+ for (new i = 1; i <= MaxClients; i++)
+ {
+ if (IsClientInGame(i) && !IsFakeClient(i))
+ {
+ Format(item, sizeof(item), "%T", display, i);
+ CPrintToChat(i, "%t", "Vote End", g_voteArg, item);
+ }
+ }
+ }
+ else
+ {
+ CPrintToChatAll("%t", "Vote End", g_voteArg, item);
+ }
+ }
+
+ case (voteType:map):
+ {
+ LogAction(-1, -1, "Changing map to %s due to vote.", item);
+ CPrintToChatAll("%t", "Changing map", item);
+ new Handle:dp;
+ CreateDataTimer(5.0, Timer_ChangeMap, dp);
+ WritePackString(dp, item);
+ }
+
+ case (voteType:kick):
+ {
+ if (g_voteArg[0] == '\0')
+ {
+ strcopy(g_voteArg, sizeof(g_voteArg), "Votekicked");
+ }
+
+ if (GetClientOfUserId(g_voteClient[VOTE_USERID]) > 0 && !IsClientInKickQueue(g_voteClient[VOTE_CLIENTID]))
+ {
+ CPrintToChatAll("%t", "Kicked target", "_s", g_voteInfo[VOTE_NAME]);
+ LogAction(-1, g_voteClient[VOTE_CLIENTID], "Vote kick successful, kicked \"%L\" (reason \"%s\")", g_voteClient[VOTE_CLIENTID], g_voteArg);
+
+ KickClient(g_voteClient[VOTE_CLIENTID], "%s", g_voteArg);
+ }
+
+ }
+
+ case (voteType:ban):
+ {
+ if (g_voteArg[0] == '\0')
+ {
+ strcopy(g_voteArg, sizeof(g_voteArg), "Votebanned");
+ }
+
+ if (GetClientOfUserId(g_voteClient[VOTE_USERID]) > 0)
+ {
+ CPrintToChatAll("%t", "Banned player", g_voteInfo[VOTE_NAME], 30);
+ LogAction(-1, g_voteClient[VOTE_CLIENTID], "Vote ban successful, banned \"%L\" (minutes \"30\") (reason \"%s\")", g_voteClient[VOTE_CLIENTID], g_voteArg);
+
+ BanClient(g_voteClient[VOTE_CLIENTID],
+ 30,
+ BANFLAG_AUTO,
+ g_voteArg,
+ "Banned by vote",
+ "sm_voteban");
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+public Handler_NativeVoteCallback(Handle:menu, MenuAction:action, param1, param2)
+{
+ switch (action)
+ {
+
+ case MenuAction_End:
+ {
+ NativeVotes_Close(menu);
+ }
+
+ case MenuAction_Display:
+ {
+ new NativeVotesType:nVoteType = NativeVotes_GetType(menu);
+ if (g_voteType != voteType:question && (nVoteType == NativeVotesType_Custom_YesNo || nVoteType == NativeVotesType_Custom_Mult))
+ {
+ decl String:title[64];
+ NativeVotes_GetTitle(menu, title, sizeof(title));
+
+ decl String:buffer[255];
+ Format(buffer, sizeof(buffer), "%T", title, param1, g_voteInfo[VOTE_NAME]);
+
+ return _:NativeVotes_RedrawVoteTitle(buffer);
+ }
+ }
+
+ case MenuAction_VoteCancel:
+ {
+ if (param1 == VoteCancel_NoVotes)
+ {
+ NativeVotes_DisplayFail(menu, NativeVotesFail_NotEnoughVotes);
+ CPrintToChatAll("%t", "No Votes Cast");
+ }
+ else
+ {
+ NativeVotes_DisplayFail(menu, NativeVotesFail_Generic);
+ }
+ }
+
+ case MenuAction_VoteEnd:
+ {
+ decl String:item[64], String:display[64];
+ new Float:percent, Float:limit, votes, totalVotes;
+
+ new NativeVotesType:nVoteType = NativeVotes_GetType(menu);
+
+ NativeVotes_GetInfo(param2, votes, totalVotes);
+ NativeVotes_GetItem(menu, param1, item, sizeof(item), display, sizeof(display));
+
+ if (nVoteType == NativeVotesType_Custom_YesNo && param1 == NATIVEVOTES_VOTE_NO)
+ {
+ votes = totalVotes - votes; // Reverse the votes to be in relation to the Yes option.
+ }
+
+ percent = GetVotePercent(votes, totalVotes);
+
+ if (g_voteType != voteType:question)
+ {
+ limit = GetConVarFloat(g_Cvar_Limits[g_voteType]);
+ }
+
+ /* :TODO: g_voteClient[userid] needs to be checked */
+
+ // A multi-argument vote is "always successful", but have to check if its a Yes/No vote.
+ if ((nVoteType != NativeVotesType_NextLevelMult && nVoteType != NativeVotesType_Custom_Mult) && ((param1 == NATIVEVOTES_VOTE_YES && FloatCompare(percent,limit) < 0) || (param1 == NATIVEVOTES_VOTE_NO)))
+ {
+ /* :TODO: g_voteClient[userid] should be used here and set to -1 if not applicable.
+ */
+ NativeVotes_DisplayFail(menu, NativeVotesFail_Loses);
+ LogAction(-1, -1, "Vote failed.");
+ CPrintToChatAll("%t", "Vote Failed", RoundToNearest(100.0*limit), RoundToNearest(100.0*percent), totalVotes);
+ }
+ else
+ {
+ CPrintToChatAll("%t", "Vote Successful", RoundToNearest(100.0*percent), totalVotes);
+
+ switch (g_voteType)
+ {
+ case (voteType:question):
+ {
+ if (nVoteType == NativeVotesType_Custom_YesNo)
+ {
+ for (new i = 1; i <= MaxClients; i++)
+ {
+ if (IsClientInGame(i) && !IsFakeClient(i))
+ {
+ Format(item, sizeof(item), "%T", display, i);
+ CPrintToChat(i, "%t", "Vote End", g_voteArg, item);
+ NativeVotes_DisplayPassCustomToOne(menu, i, "%t", "Vote End", g_voteArg, item);
+ }
+ }
+ }
+ else
+ {
+ CPrintToChatAll("%t", "Vote End", g_voteArg, item);
+ NativeVotes_DisplayPassCustom(menu, "%t", "Vote End", g_voteArg, item);
+ }
+ }
+
+ case (voteType:map):
+ {
+ if (nVoteType == NativeVotesType_ChgLevel)
+ {
+ NativeVotes_GetDetails(menu, item, sizeof(item));
+ }
+
+ //NativeVotes_DisplayPass(menu, item);
+ NativeVotes_DisplayPassEx(menu, NativeVotesPass_ChgLevel, item);
+ LogAction(-1, -1, "Changing map to %s due to vote.", item);
+ CPrintToChatAll("%t", "Changing map", item);
+ new Handle:dp;
+ CreateDataTimer(5.0, Timer_ChangeMap, dp);
+ WritePackString(dp, item);
+ }
+
+ case (voteType:kick):
+ {
+ if (g_voteArg[0] == '\0')
+ {
+ strcopy(g_voteArg, sizeof(g_voteArg), "Votekicked");
+ }
+
+ if (GetClientOfUserId(g_voteClient[VOTE_USERID]) > 0 && !IsClientInKickQueue(g_voteClient[VOTE_CLIENTID]))
+ {
+ CPrintToChatAll("%t", "Kicked target", "_s", g_voteInfo[VOTE_NAME]);
+ LogAction(-1, g_voteClient[VOTE_CLIENTID], "Vote kick successful, kicked \"%L\" (reason \"%s\")", g_voteClient[VOTE_CLIENTID], g_voteArg);
+
+ KickClient(g_voteClient[VOTE_CLIENTID], "%s", g_voteArg);
+ NativeVotes_DisplayPass(menu, g_voteInfo[VOTE_NAME]);
+ }
+ }
+
+ case (voteType:ban):
+ {
+ if (g_voteArg[0] == '\0')
+ {
+ strcopy(g_voteArg, sizeof(g_voteArg), "Votebanned");
+ }
+
+ if (GetClientOfUserId(g_voteClient[VOTE_USERID]) > 0)
+ {
+ CPrintToChatAll("%t", "Banned player", g_voteInfo[VOTE_NAME], 30);
+ LogAction(-1, g_voteClient[VOTE_CLIENTID], "Vote ban successful, banned \"%L\" (minutes \"30\") (reason \"%s\")", g_voteClient[VOTE_CLIENTID], g_voteArg);
+
+ BanClient(g_voteClient[VOTE_CLIENTID],
+ 30,
+ BANFLAG_AUTO,
+ g_voteArg,
+ "Banned by vote",
+ "sm_voteban");
+
+ NativeVotes_DisplayPassCustom(menu, "%t", "Banned player", g_voteInfo[VOTE_NAME], 30);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
+VoteSelect(Handle:menu, param1, param2 = 0)
+{
+ if (GetConVarInt(g_Cvar_VoteShow) == 1)
+ {
+ decl String:voter[64], String:junk[64], String:choice[64];
+ GetClientName(param1, voter, sizeof(voter));
+ GetMenuItem(menu, param2, junk, sizeof(junk), _, choice, sizeof(choice));
+ CPrintToChatAll("%t", "Vote Select", LANG_SERVER, voter, choice);
+ }
+}
+*/
+
+/*
+VoteMenuClose()
+{
+ CloseHandle(g_hVoteMenu);
+ g_hVoteMenu = INVALID_HANDLE;
+}
+*/
+
+Float:GetVotePercent(votes, totalVotes)
+{
+ return FloatDiv(float(votes),float(totalVotes));
+}
+
+bool:TestVoteDelay(client)
+{
+ new delay = Internal_CheckVoteDelay();
+
+ if (delay > 0)
+ {
+ if (delay > 60)
+ {
+ CReplyToCommand(client, "%t", "Vote Delay Minutes", delay % 60);
+ }
+ else
+ {
+ CReplyToCommand(client, "%t", "Vote Delay Seconds", delay);
+ }
+
+ if (g_NativeVotes)
+ {
+ NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_Recent, delay);
+ }
+
+ return false;
+ }
+
+ return true;
+}
+
+public Action:Timer_ChangeMap(Handle:timer, Handle:dp)
+{
+ decl String:mapname[65];
+
+ ResetPack(dp);
+ ReadPackString(dp, mapname, sizeof(mapname));
+
+ ForceChangeLevel(mapname, "sm_votemap Result");
+
+ return Plugin_Stop;
+}
+
+bool:Internal_IsVoteInProgress()
+{
+ if (g_NativeVotes)
+ {
+ return NativeVotes_IsVoteInProgress();
+ }
+
+ return IsVoteInProgress();
+}
+
+Internal_CheckVoteDelay()
+{
+ if (g_NativeVotes)
+ {
+ return NativeVotes_CheckVoteDelay();
+ }
+
+ return CheckVoteDelay();
+}
+
+bool:Internal_IsNewVoteAllowed()
+{
+ if (g_NativeVotes)
+ {
+ return NativeVotes_IsNewVoteAllowed();
+ }
+
+ return IsNewVoteAllowed();
+}
diff --git a/addons/sourcemod/scripting/nativevotes_basevotes/voteban.sp b/addons/sourcemod/scripting/nativevotes_basevotes/voteban.sp
new file mode 100644
index 0000000..100a347
--- /dev/null
+++ b/addons/sourcemod/scripting/nativevotes_basevotes/voteban.sp
@@ -0,0 +1,202 @@
+/**
+ * vim: set ts=4 :
+ * =============================================================================
+ * NativeVotes Basic Votes Plugin
+ * Provides ban functionality
+ *
+ * NativeVotes (C)2011-2014 Ross Bemrose (Powerlord). All rights reserved.
+ * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved.
+ * =============================================================================
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 3.0, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ *
+ * As a special exception, AlliedModders LLC gives you permission to link the
+ * code of this program (as well as its derivative works) to "Half-Life 2," the
+ * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
+ * by the Valve Corporation. You must obey the GNU General Public License in
+ * all respects for all other code used. Additionally, AlliedModders LLC grants
+ * this exception to all derivative works. AlliedModders LLC defines further
+ * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
+ * or .
+ *
+ * Version: $Id$
+ */
+
+DisplayVoteBanMenu(client, target)
+{
+ g_voteClient[VOTE_CLIENTID] = target;
+ g_voteClient[VOTE_USERID] = GetClientUserId(target);
+
+ char playerName[MAX_NAME_LENGTH]; int r, g, b, a, color;
+ GetEntityRenderColor(target, r, g, b, a);
+ color = (r << 16) | (g << 8) | b;
+ if (color != 0xFFFFFF) {
+ Format(playerName, sizeof(playerName), "{#%06X}%N\x01", color, target);
+ }
+ else {
+ Format(playerName, sizeof(playerName), "%s%N\x01", GetClientTeam(target) == 2 ? "{red}" : GetClientTeam(target) == 3 ? "{blue}" : "{grey}", target);
+ }
+
+ GetClientName(target, g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[]));
+ GetClientIP(target, g_voteInfo[VOTE_IP], sizeof(g_voteInfo[]));
+
+ LogAction(client, target, "\"%L\" initiated a ban vote against %s", client, playerName);
+ CShowActivity2(client, "[{lightgreen}NativeVotes\x01] ", "%t", "Initiated Vote Ban", playerName);
+
+ g_voteType = voteType:ban;
+
+ if (g_NativeVotes)
+ {
+ new Handle:voteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Custom_YesNo, MenuAction:MENU_ACTIONS_ALL);
+ NativeVotes_SetTitle(voteMenu, "Voteban Player");
+ NativeVotes_DisplayToAll(voteMenu, 20);
+ NativeVotes_SetTarget(voteMenu, target);
+ }
+ else
+ {
+ new Handle:voteMenu = CreateMenu(Handler_VoteCallback, MenuAction:MENU_ACTIONS_ALL);
+ SetMenuTitle(voteMenu, "Voteban Player");
+ AddMenuItem(voteMenu, VOTE_YES, "Yes");
+ AddMenuItem(voteMenu, VOTE_NO, "No");
+ SetMenuExitButton(voteMenu, false);
+ VoteMenuToAll(voteMenu, 20);
+ }
+}
+
+DisplayBanTargetMenu(client)
+{
+ new Handle:menu = CreateMenu(MenuHandler_Ban);
+
+ decl String:title[100];
+ Format(title, sizeof(title), "%T:", "Ban vote", client);
+ SetMenuTitle(menu, title);
+ SetMenuExitBackButton(menu, true);
+
+ AddTargetsToMenu(menu, client, false, false);
+
+ DisplayMenu(menu, client, MENU_TIME_FOREVER);
+}
+
+public AdminMenu_VoteBan(Handle:topmenu,
+ TopMenuAction:action,
+ TopMenuObject:object_id,
+ param,
+ String:buffer[],
+ maxlength)
+{
+ if (action == TopMenuAction_DisplayOption)
+ {
+ Format(buffer, maxlength, "%T", "Ban vote", param);
+ }
+ else if (action == TopMenuAction_SelectOption)
+ {
+ DisplayBanTargetMenu(param);
+ }
+ else if (action == TopMenuAction_DrawOption)
+ {
+ /* disable this option if a vote is already running */
+ buffer[0] = Internal_IsNewVoteAllowed() ? ITEMDRAW_IGNORE : ITEMDRAW_DEFAULT;
+ }
+}
+
+public MenuHandler_Ban(Handle:menu, MenuAction:action, param1, param2)
+{
+ if (action == MenuAction_End)
+ {
+ CloseHandle(menu);
+ }
+ else if (action == MenuAction_Cancel)
+ {
+ if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE)
+ {
+ DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory);
+ }
+ }
+ else if (action == MenuAction_Select)
+ {
+ decl String:info[32], String:name[32];
+ new userid, target;
+
+ GetMenuItem(menu, param2, info, sizeof(info), _, name, sizeof(name));
+ userid = StringToInt(info);
+
+ if ((target = GetClientOfUserId(userid)) == 0)
+ {
+ PrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Player no longer available");
+ }
+ else if (!CanUserTarget(param1, target))
+ {
+ PrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Unable to target");
+ }
+ else
+ {
+ g_voteArg[0] = '\0';
+ DisplayVoteBanMenu(param1, target);
+ }
+ }
+}
+
+public Action:Command_Voteban(client, args)
+{
+ if (args < 1)
+ {
+ CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] Usage: sm_voteban [reason]");
+ return Plugin_Handled;
+ }
+
+ if (Internal_IsVoteInProgress())
+ {
+ CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Vote in Progress");
+ return Plugin_Handled;
+ }
+
+ if (!TestVoteDelay(client))
+ {
+ return Plugin_Handled;
+ }
+
+ decl String:text[256], String:arg[64];
+ GetCmdArgString(text, sizeof(text));
+
+ new len = BreakString(text, arg, sizeof(arg));
+
+ if (len != -1)
+ {
+ strcopy(g_voteArg, sizeof(g_voteArg), text[len]);
+ }
+ else
+ {
+ g_voteArg[0] = '\0';
+ }
+
+ decl String:target_name[MAX_TARGET_LENGTH];
+ decl target_list[MAXPLAYERS], target_count, bool:tn_is_ml;
+
+ if ((target_count = ProcessTargetString(
+ arg,
+ client,
+ target_list,
+ MAXPLAYERS,
+ COMMAND_FILTER_NO_MULTI|COMMAND_FILTER_NO_BOTS,
+ target_name,
+ sizeof(target_name),
+ tn_is_ml)) <= 0)
+ {
+ ReplyToTargetError(client, target_count);
+ return Plugin_Handled;
+ }
+
+ DisplayVoteBanMenu(client, target_list[0]);
+
+ return Plugin_Handled;
+}
diff --git a/addons/sourcemod/scripting/nativevotes_basevotes/votekick.sp b/addons/sourcemod/scripting/nativevotes_basevotes/votekick.sp
new file mode 100644
index 0000000..415ab96
--- /dev/null
+++ b/addons/sourcemod/scripting/nativevotes_basevotes/votekick.sp
@@ -0,0 +1,190 @@
+/**
+ * vim: set ts=4 :
+ * =============================================================================
+ * NativeVotes Basic Votes Plugin
+ * Provides kick functionality
+ *
+ * NativeVotes (C)2011-2014 Ross Bemrose (Powerlord). All rights reserved.
+ * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved.
+ * =============================================================================
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 3.0, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ *
+ * As a special exception, AlliedModders LLC gives you permission to link the
+ * code of this program (as well as its derivative works) to "Half-Life 2," the
+ * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
+ * by the Valve Corporation. You must obey the GNU General Public License in
+ * all respects for all other code used. Additionally, AlliedModders LLC grants
+ * this exception to all derivative works. AlliedModders LLC defines further
+ * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
+ * or .
+ *
+ * Version: $Id$
+ */
+
+DisplayVoteKickMenu(client, target)
+{
+ g_voteClient[VOTE_CLIENTID] = target;
+ g_voteClient[VOTE_USERID] = GetClientUserId(target);
+
+ char playerName[MAX_NAME_LENGTH]; int r, g, b, a, color;
+ GetEntityRenderColor(target, r, g, b, a);
+ color = (r << 16) | (g << 8) | b;
+ if (color != 0xFFFFFF) {
+ Format(playerName, sizeof(playerName), "{#%06X}%N\x01", color, target);
+ }
+ else {
+ Format(playerName, sizeof(playerName), "%s%N\x01", GetClientTeam(target) == 2 ? "{red}" : GetClientTeam(target) == 3 ? "{blue}" : "{grey}", target);
+ }
+
+ GetClientName(target, g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[]));
+
+ LogAction(client, target, "\"%L\" initiated a kick vote against %s", client, playerName);
+ CShowActivity(client, "%t", "Initiated Vote Kick", playerName);
+
+ g_voteType = voteType:kick;
+
+ if (g_NativeVotes)
+ {
+ new Handle:voteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Kick, MenuAction:MENU_ACTIONS_ALL);
+ // No title, builtin type
+ NativeVotes_SetTarget(voteMenu, target);
+ NativeVotes_DisplayToAll(voteMenu, 20);
+ }
+ else
+ {
+ new Handle:voteMenu = CreateMenu(Handler_VoteCallback, MenuAction:MENU_ACTIONS_ALL);
+ SetMenuTitle(voteMenu, "Votekick Player");
+ AddMenuItem(voteMenu, VOTE_YES, "Yes");
+ AddMenuItem(voteMenu, VOTE_NO, "No");
+ SetMenuExitButton(voteMenu, false);
+ VoteMenuToAll(voteMenu, 20);
+ }
+}
+
+DisplayKickTargetMenu(client)
+{
+ new Handle:menu = CreateMenu(MenuHandler_Kick);
+
+ decl String:title[100];
+ Format(title, sizeof(title), "%T:", "Kick vote", client);
+ SetMenuTitle(menu, title);
+ SetMenuExitBackButton(menu, true);
+
+ AddTargetsToMenu(menu, client, false, false);
+
+ DisplayMenu(menu, client, MENU_TIME_FOREVER);
+}
+
+public AdminMenu_VoteKick(Handle:topmenu,
+ TopMenuAction:action,
+ TopMenuObject:object_id,
+ param,
+ String:buffer[],
+ maxlength)
+{
+ if (action == TopMenuAction_DisplayOption)
+ {
+ Format(buffer, maxlength, "%T", "Kick vote", param);
+ }
+ else if (action == TopMenuAction_SelectOption)
+ {
+ DisplayKickTargetMenu(param);
+ }
+ else if (action == TopMenuAction_DrawOption)
+ {
+ /* disable this option if a vote is already running */
+ buffer[0] = !IsNewVoteAllowed() ? ITEMDRAW_IGNORE : ITEMDRAW_DEFAULT;
+ }
+}
+
+public MenuHandler_Kick(Handle:menu, MenuAction:action, param1, param2)
+{
+ if (action == MenuAction_End)
+ {
+ CloseHandle(menu);
+ }
+ else if (action == MenuAction_Cancel)
+ {
+ if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE)
+ {
+ DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory);
+ }
+ }
+ else if (action == MenuAction_Select)
+ {
+ decl String:info[32], String:name[32];
+ new userid, target;
+
+ GetMenuItem(menu, param2, info, sizeof(info), _, name, sizeof(name));
+ userid = StringToInt(info);
+
+ if ((target = GetClientOfUserId(userid)) == 0)
+ {
+ PrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Player no longer available");
+ }
+ else if (!CanUserTarget(param1, target))
+ {
+ PrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Unable to target");
+ }
+ else
+ {
+ g_voteArg[0] = '\0';
+ DisplayVoteKickMenu(param1, target);
+ }
+ }
+}
+
+public Action:Command_Votekick(client, args)
+{
+ if (args < 1)
+ {
+ CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] Usage: sm_votekick [reason]");
+ return Plugin_Handled;
+ }
+
+ if (Internal_IsVoteInProgress())
+ {
+ CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Vote in Progress");
+ return Plugin_Handled;
+ }
+
+ if (!TestVoteDelay(client))
+ {
+ return Plugin_Handled;
+ }
+
+ decl String:text[256], String:arg[64];
+ GetCmdArgString(text, sizeof(text));
+
+ new len = BreakString(text, arg, sizeof(arg));
+
+ new target = FindTarget(client, arg);
+ if (target == -1)
+ {
+ return Plugin_Handled;
+ }
+
+ if (len != -1)
+ {
+ strcopy(g_voteArg, sizeof(g_voteArg), text[len]);
+ }
+ else
+ {
+ g_voteArg[0] = '\0';
+ }
+
+ DisplayVoteKickMenu(client, target);
+
+ return Plugin_Handled;
+}
\ No newline at end of file
diff --git a/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp b/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp
new file mode 100644
index 0000000..0e7612c
--- /dev/null
+++ b/addons/sourcemod/scripting/nativevotes_basevotes/votemap.sp
@@ -0,0 +1,336 @@
+/**
+ * vim: set ts=4 :
+ * =============================================================================
+ * NativeVotes Basic Votes Plugin
+ * Provides map functionality
+ *
+ * NativeVotes (C)2011-2014 Ross Bemrose (Powerlord). All rights reserved.
+ * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved.
+ * =============================================================================
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 3.0, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ *
+ * As a special exception, AlliedModders LLC gives you permission to link the
+ * code of this program (as well as its derivative works) to "Half-Life 2," the
+ * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
+ * by the Valve Corporation. You must obey the GNU General Public License in
+ * all respects for all other code used. Additionally, AlliedModders LLC grants
+ * this exception to all derivative works. AlliedModders LLC defines further
+ * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
+ * or .
+ *
+ * Version: $Id$
+ */
+
+new Handle:g_MapList = INVALID_HANDLE;
+new g_mapCount;
+
+new Handle:g_SelectedMaps;
+new bool:g_VoteMapInUse;
+
+DisplayVoteMapMenu(client, mapCount, String:maps[5][])
+{
+ LogAction(client, -1, "\"%L\" initiated a map vote.", client);
+ CShowActivity2(client, "[{lightgreen}NativeVotes\x01] ", "%t", "Initiated Vote Map");
+
+ g_voteType = voteType:map;
+
+ decl String:resolvedMaps[5][PLATFORM_MAX_PATH];
+ decl String:displayNames[5][PLATFORM_MAX_PATH];
+ for (new i = 0; i < mapCount; i++)
+ {
+ FindMap(maps[i], resolvedMaps[i], sizeof(resolvedMaps[]));
+ GetMapDisplayName(resolvedMaps[i], displayNames[i], sizeof(displayNames[]));
+ }
+
+ if (g_NativeVotes && (mapCount == 1 || NativeVotes_IsVoteTypeSupported(NativeVotesType_NextLevelMult)) )
+ {
+ new Handle:voteMenu;
+
+ if (mapCount == 1)
+ {
+ strcopy(g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[]), displayNames[0]);
+
+ voteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_ChgLevel, MenuAction:MENU_ACTIONS_ALL);
+
+ // No title, builtin type
+ NativeVotes_SetDetails(voteMenu, displayNames[0]);
+ }
+ else
+ {
+ voteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_NextLevelMult, MenuAction:MENU_ACTIONS_ALL);
+
+ g_voteInfo[VOTE_NAME][0] = '\0';
+
+ // No title, builtin type
+ for (new i = 0; i < mapCount; i++)
+ {
+ NativeVotes_AddItem(voteMenu, resolvedMaps[i], displayNames[i]);
+ }
+ }
+
+ NativeVotes_DisplayToAll(voteMenu, 20);
+ }
+ else
+ {
+ new Handle:voteMenu = CreateMenu(Handler_VoteCallback, MenuAction:MENU_ACTIONS_ALL);
+
+ if (mapCount == 1)
+ {
+ strcopy(g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[]), displayNames[0]);
+
+ SetMenuTitle(voteMenu, "Change Map To");
+ AddMenuItem(voteMenu, resolvedMaps[0], "Yes");
+ AddMenuItem(voteMenu, VOTE_NO, "No");
+ }
+ else
+ {
+ g_voteInfo[VOTE_NAME][0] = '\0';
+
+ SetMenuTitle(voteMenu, "Map Vote");
+ for (new i = 0; i < mapCount; i++)
+ {
+ AddMenuItem(voteMenu, resolvedMaps[i], displayNames[i]);
+ }
+ }
+
+ SetMenuExitButton(voteMenu, false);
+ VoteMenuToAll(voteMenu, 20);
+ }
+}
+
+ResetMenu()
+{
+ g_VoteMapInUse = false;
+ ClearArray(g_SelectedMaps);
+}
+
+ConfirmVote(client)
+{
+ new Handle:menu = CreateMenu(MenuHandler_Confirm);
+
+ decl String:title[100];
+ Format(title, sizeof(title), "%T:", "Confirm Vote", client);
+ SetMenuTitle(menu, title);
+ SetMenuExitBackButton(menu, true);
+
+ decl String:itemtext[256];
+ Format(itemtext, sizeof(itemtext), "%T", "Start the Vote", client);
+ AddMenuItem(menu, "Confirm", itemtext);
+
+ DisplayMenu(menu, client, MENU_TIME_FOREVER);
+}
+
+public MenuHandler_Confirm(Handle:menu, MenuAction:action, param1, param2)
+{
+ if (action == MenuAction_End)
+ {
+ CloseHandle(menu);
+ g_VoteMapInUse = false;
+ }
+ else if (action == MenuAction_Cancel)
+ {
+ ResetMenu();
+
+ if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE)
+ {
+ DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory);
+ }
+ }
+ else if (action == MenuAction_Select)
+ {
+ decl String:maps[5][64];
+ new selectedmaps = GetArraySize(g_SelectedMaps);
+
+ for (new i = 0; i < selectedmaps; i++)
+ {
+ GetArrayString(g_SelectedMaps, i, maps[i], sizeof(maps[]));
+ }
+
+ DisplayVoteMapMenu(param1, selectedmaps, maps);
+
+ ResetMenu();
+ }
+}
+
+public MenuHandler_Map(Handle:menu, MenuAction:action, param1, param2)
+{
+ if (action == MenuAction_Cancel)
+ {
+ if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE)
+ {
+ ConfirmVote(param1);
+ }
+ else // no action was selected.
+ {
+ /* Re-enable the menu option */
+ ResetMenu();
+ }
+ }
+ else if (action == MenuAction_DrawItem)
+ {
+ decl String:info[32], String:name[32];
+
+ GetMenuItem(menu, param2, info, sizeof(info), _, name, sizeof(name));
+
+ if (FindStringInArray(g_SelectedMaps, info) != -1)
+ {
+ return ITEMDRAW_IGNORE;
+ }
+ else
+ {
+ return ITEMDRAW_DEFAULT;
+ }
+ }
+ else if (action == MenuAction_Select)
+ {
+ decl String:info[32], String:name[32];
+
+ GetMenuItem(menu, param2, info, sizeof(info), _, name, sizeof(name));
+
+ PushArrayString(g_SelectedMaps, info);
+
+ /* Redisplay the list */
+ if (GetArraySize(g_SelectedMaps) < 5)
+ {
+ DisplayMenu(g_MapList, param1, MENU_TIME_FOREVER);
+ }
+ else
+ {
+ ConfirmVote(param1);
+ }
+ }
+ else if (action == MenuAction_Display)
+ {
+ decl String:title[128];
+ Format(title, sizeof(title), "%T", "Please select a map", param1);
+ SetPanelTitle(Handle:param2, title);
+ }
+
+ return 0;
+}
+
+public AdminMenu_VoteMap(Handle:topmenu,
+ TopMenuAction:action,
+ TopMenuObject:object_id,
+ param,
+ String:buffer[],
+ maxlength)
+{
+ if (action == TopMenuAction_DisplayOption)
+ {
+ Format(buffer, maxlength, "%T", "Map vote", param);
+ }
+ else if (action == TopMenuAction_SelectOption)
+ {
+ if (!g_VoteMapInUse)
+ {
+ ResetMenu();
+ g_VoteMapInUse = true;
+ DisplayMenu(g_MapList, param, MENU_TIME_FOREVER);
+ }
+ else
+ {
+ PrintToChat(param, "[{lightgreen}NativeVotes\x01] %T", "Map Vote In Use", param);
+ }
+ }
+ else if (action == TopMenuAction_DrawOption)
+ {
+ /* disable this option if a vote is already running, theres no maps listed or someone else has already acessed this menu */
+ buffer[0] = (!IsNewVoteAllowed() || g_mapCount < 1 || g_VoteMapInUse) ? ITEMDRAW_IGNORE : ITEMDRAW_DEFAULT;
+ }
+}
+
+public Action:Command_Votemap(client, args)
+{
+ if (args < 1)
+ {
+ CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] Usage: sm_votemap [mapname2] ... [mapname5]");
+ return Plugin_Handled;
+ }
+
+ if (Internal_IsVoteInProgress())
+ {
+ CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Vote in Progress");
+ return Plugin_Handled;
+ }
+
+ if (!TestVoteDelay(client))
+ {
+ return Plugin_Handled;
+ }
+
+ decl String:text[256];
+ GetCmdArgString(text, sizeof(text));
+
+ decl String:maps[5][64];
+ new mapCount;
+ new len, pos;
+
+ while (pos != -1 && mapCount < 5)
+ {
+ pos = BreakString(text[len], maps[mapCount], sizeof(maps[]));
+
+ if (!IsMapValid(maps[mapCount]))
+ {
+ CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Map was not found", maps[mapCount]);
+ return Plugin_Handled;
+ }
+
+ mapCount++;
+
+ if (pos != -1)
+ {
+ len += pos;
+ }
+ }
+
+ DisplayVoteMapMenu(client, mapCount, maps);
+
+ return Plugin_Handled;
+}
+
+new Handle:g_map_array = INVALID_HANDLE;
+new g_map_serial = -1;
+
+LoadMapList(Handle:menu)
+{
+ new Handle:map_array;
+
+ if ((map_array = ReadMapList(g_map_array,
+ g_map_serial,
+ "sm_votemap menu",
+ MAPLIST_FLAG_CLEARARRAY|MAPLIST_FLAG_NO_DEFAULT|MAPLIST_FLAG_MAPSFOLDER))
+ != INVALID_HANDLE)
+ {
+ g_map_array = map_array;
+ }
+
+ if (g_map_array == INVALID_HANDLE)
+ {
+ return 0;
+ }
+
+ RemoveAllMenuItems(menu);
+
+ decl String:map_name[64];
+ new map_count = GetArraySize(g_map_array);
+
+ for (new i = 0; i < map_count; i++)
+ {
+ GetArrayString(g_map_array, i, map_name, sizeof(map_name));
+ AddMenuItem(menu, map_name, map_name);
+ }
+
+ return map_count;
+}
diff --git a/addons/sourcemod/scripting/nativevotes_funvotes.sp b/addons/sourcemod/scripting/nativevotes_funvotes.sp
new file mode 100644
index 0000000..99aa484
--- /dev/null
+++ b/addons/sourcemod/scripting/nativevotes_funvotes.sp
@@ -0,0 +1,562 @@
+/**
+ * vim: set ts=4 :
+ * =============================================================================
+ * NativeVotes Fun Votes Plugin
+ * Implements extra fun vote commands using NativeVotes.
+ * Based on the SourceMod version.
+ *
+ * NativeVotes (C)2011-2014 Ross Bemrose (Powerlord). All rights reserved.
+ * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved.
+ * =============================================================================
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 3.0, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ *
+ * As a special exception, AlliedModders LLC gives you permission to link the
+ * code of this program (as well as its derivative works) to "Half-Life 2," the
+ * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
+ * by the Valve Corporation. You must obey the GNU General Public License in
+ * all respects for all other code used. Additionally, AlliedModders LLC grants
+ * this exception to all derivative works. AlliedModders LLC defines further
+ * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
+ * or .
+ *
+ * Version: $Id$
+ */
+
+#pragma semicolon 1
+
+#include
+#include
+
+#undef REQUIRE_PLUGIN
+#include
+#include
+
+#define VERSION "25w52a"
+
+public Plugin:myinfo =
+{
+ name = "NativeVotes Fun Votes",
+ author = "Powerlord and AlliedModders LLC",
+ description = "NativeVotes Fun Vote Commands",
+ version = VERSION,
+ url = "https://github.com/Heapons/sourcemod-nativevotes-updated/"
+};
+
+#define VOTE_NO "###no###"
+#define VOTE_YES "###yes###"
+
+//new Handle:g_hVoteMenu = INVALID_HANDLE;
+
+new Handle:g_Cvar_Limits[5] = {INVALID_HANDLE, ...};
+new Handle:g_Cvar_Gravity = INVALID_HANDLE;
+new Handle:g_Cvar_Alltalk = INVALID_HANDLE;
+new Handle:g_Cvar_FF = INVALID_HANDLE;
+
+// new Handle:g_Cvar_Show = INVALID_HANDLE;
+
+enum voteType
+{
+ gravity = 0,
+ burn,
+ slay,
+ alltalk,
+ ff
+};
+
+new voteType:g_voteType = voteType:gravity;
+
+// Menu API does not provide us with a way to pass multiple peices of data with a single
+// choice, so some globals are used to hold stuff.
+//
+#define VOTE_CLIENTID 0
+#define VOTE_USERID 1
+new g_voteClient[2]; /* Holds the target's client id and user id */
+
+#define VOTE_NAME 0
+#define VOTE_AUTHID 1
+#define VOTE_IP 2
+new String:g_voteInfo[3][65]; /* Holds the target's name, authid, and IP */
+
+new Handle:hTopMenu = INVALID_HANDLE;
+
+// NativeVotes
+new bool:g_NativeVotes;
+
+#include "nativevotes_funvotes/votegravity.sp"
+#include "nativevotes_funvotes/voteburn.sp"
+#include "nativevotes_funvotes/voteslay.sp"
+#include "nativevotes_funvotes/votealltalk.sp"
+#include "nativevotes_funvotes/voteff.sp"
+
+public OnPluginStart()
+{
+ LoadTranslations("common.phrases");
+ LoadTranslations("basevotes.phrases");
+ LoadTranslations("funvotes.phrases");
+ LoadTranslations("funcommands.phrases");
+
+ RegAdminCmd("sm_votegravity", Command_VoteGravity, ADMFLAG_VOTE, "sm_votegravity [amount2] ... [amount5]");
+ RegAdminCmd("sm_voteburn", Command_VoteBurn, ADMFLAG_VOTE|ADMFLAG_SLAY, "sm_voteburn ");
+ RegAdminCmd("sm_voteslay", Command_VoteSlay, ADMFLAG_VOTE|ADMFLAG_SLAY, "sm_voteslay ");
+ RegAdminCmd("sm_votealltalk", Command_VoteAlltalk, ADMFLAG_VOTE, "sm_votealltalk");
+ RegAdminCmd("sm_voteff", Command_VoteFF, ADMFLAG_VOTE, "sm_voteff");
+
+ g_Cvar_Limits[0] = CreateConVar("sm_vote_gravity", "0.60", "percent required for successful gravity vote.", 0, true, 0.05, true, 1.0);
+ g_Cvar_Limits[1] = CreateConVar("sm_vote_burn", "0.60", "percent required for successful burn vote.", 0, true, 0.05, true, 1.0);
+ g_Cvar_Limits[2] = CreateConVar("sm_vote_slay", "0.60", "percent required for successful slay vote.", 0, true, 0.05, true, 1.0);
+ g_Cvar_Limits[3] = CreateConVar("sm_vote_alltalk", "0.60", "percent required for successful alltalk vote.", 0, true, 0.05, true, 1.0);
+ g_Cvar_Limits[4] = CreateConVar("sm_vote_ff", "0.60", "percent required for successful friendly fire vote.", 0, true, 0.05, true, 1.0);
+ CreateConVar("nativevotes_funvotes_version", VERSION, "NativeVotes Fun Votes version", FCVAR_PLUGIN|FCVAR_NOTIFY|FCVAR_DONTRECORD|FCVAR_SPONLY);
+
+ g_Cvar_Gravity = FindConVar("sv_gravity");
+ g_Cvar_Alltalk = FindConVar("sv_alltalk");
+ g_Cvar_FF = FindConVar("mp_friendlyfire");
+
+ /*
+ g_Cvar_Show = FindConVar("sm_vote_show");
+ if (g_Cvar_Show == INVALID_HANDLE)
+ {
+ g_Cvar_Show = CreateConVar("sm_vote_show", "1", "Show player's votes? Default on.", 0, true, 0.0, true, 1.0);
+ }
+ */
+}
+
+public OnAllPluginsLoaded()
+{
+ if (FindPluginByFile("basefunvotes.smx") != INVALID_HANDLE)
+ {
+ SetFailState("This plugin replaces basefuncommands. You cannot run both at once.");
+ }
+
+ if (FindPluginByFile("funvotes.smx") != INVALID_HANDLE)
+ {
+ LogMessage("Unloading funvotes to prevent conflicts...");
+ ServerCommand("sm plugins unload funvotes");
+
+ decl String:oldPath[PLATFORM_MAX_PATH];
+ decl String:newPath[PLATFORM_MAX_PATH];
+
+ BuildPath(Path_SM, oldPath, sizeof(oldPath), "plugins/funvotes.smx");
+ BuildPath(Path_SM, newPath, sizeof(newPath), "plugins/disabled/funvotes.smx");
+ if (RenameFile(newPath, oldPath))
+ {
+ LogMessage("Moving funvotes to disabled.");
+ }
+
+
+ //SetFailState("This plugin replaces funvotes. You cannot run both at once.");
+
+ }
+
+ /* Account for late loading */
+ new Handle:topmenu;
+ if (LibraryExists("adminmenu") && ((topmenu = GetAdminTopMenu()) != INVALID_HANDLE))
+ {
+ OnAdminMenuReady(topmenu);
+ }
+
+ g_NativeVotes = LibraryExists("nativevotes") && NativeVotes_IsVoteTypeSupported(NativeVotesType_Custom_YesNo);
+}
+
+public OnLibraryAdded(const String:name[])
+{
+ new Handle:topmenu;
+ if (StrEqual(name, "adminmenu") && ((topmenu = GetAdminTopMenu()) != INVALID_HANDLE))
+ {
+ OnAdminMenuReady(topmenu);
+ }
+ else
+ if (StrEqual(name, "nativevotes") && NativeVotes_IsVoteTypeSupported(NativeVotesType_Custom_YesNo))
+ {
+ g_NativeVotes = true;
+ }
+}
+
+public OnLibraryRemoved(const String:name[])
+{
+ if (StrEqual(name, "adminmenu"))
+ {
+ hTopMenu = INVALID_HANDLE;
+ }
+ else
+ if (StrEqual(name, "nativevotes"))
+ {
+ g_NativeVotes = false;
+ }
+}
+
+public OnAdminMenuReady(Handle:topmenu)
+{
+ /* Block us from being called twice */
+ if (topmenu == hTopMenu)
+ {
+ return;
+ }
+
+ /* Save the Handle */
+ hTopMenu = topmenu;
+
+ /* Build the "Voting Commands" category */
+ new TopMenuObject:voting_commands = FindTopMenuCategory(hTopMenu, ADMINMENU_VOTINGCOMMANDS);
+
+ if (voting_commands != INVALID_TOPMENUOBJECT)
+ {
+ AddToTopMenu(hTopMenu,
+ "sm_votegravity",
+ TopMenuObject_Item,
+ AdminMenu_VoteGravity,
+ voting_commands,
+ "sm_votegravity",
+ ADMFLAG_VOTE);
+
+ AddToTopMenu(hTopMenu,
+ "sm_voteburn",
+ TopMenuObject_Item,
+ AdminMenu_VoteBurn,
+ voting_commands,
+ "sm_voteburn",
+ ADMFLAG_VOTE|ADMFLAG_SLAY);
+
+ AddToTopMenu(hTopMenu,
+ "sm_voteslay",
+ TopMenuObject_Item,
+ AdminMenu_VoteSlay,
+ voting_commands,
+ "sm_voteslay",
+ ADMFLAG_VOTE|ADMFLAG_SLAY);
+
+ AddToTopMenu(hTopMenu,
+ "sm_votealltalk",
+ TopMenuObject_Item,
+ AdminMenu_VoteAllTalk,
+ voting_commands,
+ "sm_votealltalk",
+ ADMFLAG_VOTE);
+
+ AddToTopMenu(hTopMenu,
+ "sm_voteff",
+ TopMenuObject_Item,
+ AdminMenu_VoteFF,
+ voting_commands,
+ "sm_voteff",
+ ADMFLAG_VOTE);
+ }
+}
+
+public Handler_VoteCallback(Handle:menu, MenuAction:action, param1, param2)
+{
+ switch (action)
+ {
+ case MenuAction_End:
+ {
+ //VoteMenuClose();
+ CloseHandle(menu);
+ }
+
+ case MenuAction_Display:
+ {
+ decl String:title[64];
+ GetMenuTitle(menu, title, sizeof(title));
+
+ decl String:buffer[255];
+ Format(buffer, sizeof(buffer), "%T", title, param1, g_voteInfo[VOTE_NAME]);
+
+ new Handle:panel = Handle:param2;
+ SetPanelTitle(panel, buffer);
+ }
+
+ case MenuAction_DisplayItem:
+ {
+ decl String:display[64];
+ GetMenuItem(menu, param2, "", 0, _, display, sizeof(display));
+
+ if (strcmp(display, VOTE_NO) == 0 || strcmp(display, VOTE_YES) == 0)
+ {
+ decl String:buffer[255];
+ Format(buffer, sizeof(buffer), "%T", display, param1);
+
+ return RedrawMenuItem(buffer);
+ }
+ }
+
+ case MenuAction_VoteCancel:
+ {
+ if (param1 == VoteCancel_NoVotes)
+ {
+ CPrintToChatAll("%t", "No Votes Cast");
+ }
+ }
+
+ case MenuAction_VoteEnd:
+ {
+ decl String:item[64];
+ new Float:percent, Float:limit, votes, totalVotes;
+
+ GetMenuVoteInfo(param2, votes, totalVotes);
+ GetMenuItem(menu, param1, item, sizeof(item));
+
+ if (strcmp(item, VOTE_NO) == 0 && param1 == 1)
+ {
+ votes = totalVotes - votes; // Reverse the votes to be in relation to the Yes option.
+ }
+
+ percent = GetVotePercent(votes, totalVotes);
+
+ limit = GetConVarFloat(g_Cvar_Limits[g_voteType]);
+
+ /* :TODO: g_voteClient[userid] needs to be checked */
+
+ // A multi-argument vote is "always successful", but have to check if its a Yes/No vote.
+ if ((strcmp(item, VOTE_YES) == 0 && FloatCompare(percent,limit) < 0 && param1 == 0) || (strcmp(item, VOTE_NO) == 0 && param1 == 1))
+ {
+ /* :TODO: g_voteClient[userid] should be used here and set to -1 if not applicable.
+ */
+ LogAction(-1, -1, "Vote failed.");
+ CPrintToChatAll("%t", "Vote Failed", RoundToNearest(100.0*limit), RoundToNearest(100.0*percent), totalVotes);
+ }
+ else
+ {
+ CPrintToChatAll("%t", "Vote Successful", RoundToNearest(100.0*percent), totalVotes);
+
+ switch (g_voteType)
+ {
+ case (voteType:gravity):
+ {
+ CPrintToChatAll("%t", "Cvar changed", "sv_gravity", item);
+ LogAction(-1, -1, "Changing gravity to %s due to vote.", item);
+ SetConVarInt(g_Cvar_Gravity, StringToInt(item));
+ }
+
+ case (voteType:burn):
+ {
+ CPrintToChatAll("%t", "Set target on fire", "_s", g_voteInfo[VOTE_NAME]);
+ LogAction(-1, g_voteClient[VOTE_CLIENTID], "Vote burn successful, igniting \"%L\"", g_voteClient[VOTE_CLIENTID]);
+
+ IgniteEntity(g_voteClient[VOTE_CLIENTID], 19.8);
+ }
+
+ case (voteType:slay):
+ {
+ CPrintToChatAll("%t", "Slayed player", g_voteInfo[VOTE_NAME]);
+ LogAction(-1, g_voteClient[VOTE_CLIENTID], "Vote slay successful, slaying \"%L\"", g_voteClient[VOTE_CLIENTID]);
+
+ ExtinguishEntity(g_voteClient[VOTE_CLIENTID]);
+ ForcePlayerSuicide(g_voteClient[VOTE_CLIENTID]);
+ }
+
+ case (voteType:alltalk):
+ {
+ CPrintToChatAll("%t", "Cvar changed", "sv_alltalk", (GetConVarBool(g_Cvar_Alltalk) ? "0" : "1"));
+ LogAction(-1, -1, "Changing alltalk to %s due to vote.", (GetConVarBool(g_Cvar_Alltalk) ? "0" : "1"));
+ SetConVarBool(g_Cvar_Alltalk, !GetConVarBool(g_Cvar_Alltalk));
+ }
+
+ case (voteType:ff):
+ {
+ CPrintToChatAll("%t", "Cvar changed", "mp_friendlyfire", (GetConVarBool(g_Cvar_FF) ? "0" : "1"));
+ LogAction(-1, -1, "Changing friendly fire to %s due to vote.", (GetConVarBool(g_Cvar_FF) ? "0" : "1"));
+ SetConVarBool(g_Cvar_FF, !GetConVarBool(g_Cvar_FF));
+ }
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+public Handler_NativeVoteCallback(Handle:menu, MenuAction:action, param1, param2)
+{
+ switch (action)
+ {
+
+ case MenuAction_End:
+ {
+ NativeVotes_Close(menu);
+ }
+
+ case MenuAction_Display:
+ {
+ new NativeVotesType:nVoteType = NativeVotes_GetType(menu);
+ if (nVoteType == NativeVotesType_Custom_YesNo || nVoteType == NativeVotesType_Custom_Mult)
+ {
+ decl String:title[64];
+ NativeVotes_GetTitle(menu, title, sizeof(title));
+
+ decl String:buffer[255];
+ Format(buffer, sizeof(buffer), "%T", title, param1, g_voteInfo[VOTE_NAME]);
+
+ return _:NativeVotes_RedrawVoteTitle(buffer);
+ }
+ }
+
+ case MenuAction_VoteCancel:
+ {
+ if (param1 == VoteCancel_NoVotes)
+ {
+ NativeVotes_DisplayFail(menu, NativeVotesFail_NotEnoughVotes);
+ CPrintToChatAll("%t", "No Votes Cast");
+ }
+ else
+ {
+ NativeVotes_DisplayFail(menu, NativeVotesFail_Generic);
+ }
+ }
+
+ case MenuAction_VoteEnd:
+ {
+ decl String:item[64];
+ new Float:percent, Float:limit, votes, totalVotes;
+
+ new NativeVotesType:nVoteType = NativeVotes_GetType(menu);
+
+ NativeVotes_GetInfo(param2, votes, totalVotes);
+ NativeVotes_GetItem(menu, param1, item, sizeof(item));
+
+ if (nVoteType == NativeVotesType_Custom_YesNo && param1 == NATIVEVOTES_VOTE_NO)
+ {
+ votes = totalVotes - votes; // Reverse the votes to be in relation to the Yes option.
+ }
+
+ percent = GetVotePercent(votes, totalVotes);
+
+ limit = GetConVarFloat(g_Cvar_Limits[g_voteType]);
+
+ /* :TODO: g_voteClient[userid] needs to be checked */
+
+ // A multi-argument vote is "always successful", but have to check if its a Yes/No vote.
+ if ((nVoteType != NativeVotesType_NextLevelMult && nVoteType != NativeVotesType_Custom_Mult) && ((param1 == NATIVEVOTES_VOTE_YES && FloatCompare(percent,limit) < 0) || (param1 == NATIVEVOTES_VOTE_NO)))
+ {
+ /* :TODO: g_voteClient[userid] should be used here and set to -1 if not applicable.
+ */
+ NativeVotes_DisplayFail(menu, NativeVotesFail_Loses);
+ LogAction(-1, -1, "Vote failed.");
+ CPrintToChatAll("%t", "Vote Failed", RoundToNearest(100.0*limit), RoundToNearest(100.0*percent), totalVotes);
+ }
+ else
+ {
+ CPrintToChatAll("%t", "Vote Successful", RoundToNearest(100.0*percent), totalVotes);
+
+ switch (g_voteType)
+ {
+ case (voteType:gravity):
+ {
+ CPrintToChatAll("%t", "Cvar changed", "sv_gravity", item);
+ NativeVotes_DisplayPassCustom(menu, "%t", "Cvar changed", "sv_gravity", item);
+ LogAction(-1, -1, "Changing gravity to %s due to vote.", item);
+ SetConVarInt(g_Cvar_Gravity, StringToInt(item));
+ }
+
+ case (voteType:burn):
+ {
+ CPrintToChatAll("%t", "Set target on fire", "_s", g_voteInfo[VOTE_NAME]);
+ NativeVotes_DisplayPassCustom(menu, "%t", "Set target on fire", "_s", g_voteInfo[VOTE_NAME]);
+ LogAction(-1, g_voteClient[VOTE_CLIENTID], "Vote burn successful, igniting \"%L\"", g_voteClient[VOTE_CLIENTID]);
+
+ IgniteEntity(g_voteClient[VOTE_CLIENTID], 19.8);
+ }
+
+ case (voteType:slay):
+ {
+ CPrintToChatAll("%t", "Slayed player", g_voteInfo[VOTE_NAME]);
+ NativeVotes_DisplayPassCustom(menu, "%t", "Slayed player", g_voteInfo[VOTE_NAME]);
+ LogAction(-1, g_voteClient[VOTE_CLIENTID], "Vote slay successful, slaying \"%L\"", g_voteClient[VOTE_CLIENTID]);
+
+ ExtinguishEntity(g_voteClient[VOTE_CLIENTID]);
+ ForcePlayerSuicide(g_voteClient[VOTE_CLIENTID]);
+ }
+
+ case (voteType:alltalk):
+ {
+ CPrintToChatAll("%t", "Cvar changed", "sv_alltalk", (GetConVarBool(g_Cvar_Alltalk) ? "0" : "1"));
+ NativeVotes_DisplayPassCustom(menu, "%t", "Cvar changed", "sv_alltalk", (GetConVarBool(g_Cvar_Alltalk) ? "0" : "1"));
+ LogAction(-1, -1, "Changing alltalk to %s due to vote.", (GetConVarBool(g_Cvar_Alltalk) ? "0" : "1"));
+ SetConVarBool(g_Cvar_Alltalk, !GetConVarBool(g_Cvar_Alltalk));
+ }
+
+ case (voteType:ff):
+ {
+ CPrintToChatAll("%t", "Cvar changed", "mp_friendlyfire", (GetConVarBool(g_Cvar_FF) ? "0" : "1"));
+ NativeVotes_DisplayPassCustom(menu, "%t", "Cvar changed", "mp_friendlyfire", (GetConVarBool(g_Cvar_FF) ? "0" : "1"));
+ LogAction(-1, -1, "Changing friendly fire to %s due to vote.", (GetConVarBool(g_Cvar_FF) ? "0" : "1"));
+ SetConVarBool(g_Cvar_FF, !GetConVarBool(g_Cvar_FF));
+ }
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+Float:GetVotePercent(votes, totalVotes)
+{
+ return FloatDiv(float(votes),float(totalVotes));
+}
+
+bool:TestVoteDelay(client)
+{
+ new delay = Internal_CheckVoteDelay();
+
+ if (delay > 0)
+ {
+ if (delay > 60)
+ {
+ CReplyToCommand(client, "%t", "Vote Delay Minutes", delay % 60);
+ }
+ else
+ {
+ CReplyToCommand(client, "%t", "Vote Delay Seconds", delay);
+ }
+
+ if (g_NativeVotes)
+ {
+ NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_Recent, delay);
+ }
+
+ return false;
+ }
+
+ return true;
+}
+
+bool:Internal_IsVoteInProgress()
+{
+ if (g_NativeVotes)
+ {
+ return NativeVotes_IsVoteInProgress();
+ }
+
+ return IsVoteInProgress();
+}
+
+Internal_CheckVoteDelay()
+{
+ if (g_NativeVotes)
+ {
+ return NativeVotes_CheckVoteDelay();
+ }
+
+ return CheckVoteDelay();
+}
+
+bool:Internal_IsNewVoteAllowed()
+{
+ if (g_NativeVotes)
+ {
+ return NativeVotes_IsNewVoteAllowed();
+ }
+
+ return IsNewVoteAllowed();
+}
diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp b/addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp
new file mode 100644
index 0000000..703dabc
--- /dev/null
+++ b/addons/sourcemod/scripting/nativevotes_funvotes/votealltalk.sp
@@ -0,0 +1,140 @@
+ /**
+ * vim: set ts=4 :
+ * =============================================================================
+ * NativeVotes Fun Votes Plugin
+ * Provides votealltalk functionality
+ *
+ * NativeVotes (C)2011-2014 Ross Bemrose (Powerlord). All rights reserved.
+ * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved.
+ * =============================================================================
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 3.0, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ *
+ * As a special exception, AlliedModders LLC gives you permission to link the
+ * code of this program (as well as its derivative works) to "Half-Life 2," the
+ * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
+ * by the Valve Corporation. You must obey the GNU General Public License in
+ * all respects for all other code used. Additionally, AlliedModders LLC grants
+ * this exception to all derivative works. AlliedModders LLC defines further
+ * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
+ * or .
+ *
+ * Version: $Id$
+ */
+
+DisplayVoteAllTalkMenu(client)
+{
+ if (Internal_IsVoteInProgress())
+ {
+ CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Vote in Progress");
+ return;
+ }
+
+ if (!TestVoteDelay(client))
+ {
+ return;
+ }
+
+ LogAction(client, -1, "\"%L\" initiated an alltalk vote.", client);
+ CShowActivity2(client, "[{lightgreen}NativeVotes\x01] ", "%t", "Initiated Vote Alltalk");
+
+ g_voteType = voteType:alltalk;
+ g_voteInfo[VOTE_NAME][0] = '\0';
+
+ if (g_NativeVotes)
+ {
+ new Handle:hVoteMenu;
+ if (NativeVotes_IsVoteTypeSupported(NativeVotesType_AlltalkOn))
+ {
+ new NativeVotesType:nVoteType;
+ if (GetConVarBool(g_Cvar_Alltalk))
+ {
+ nVoteType = NativeVotesType_AlltalkOff;
+ }
+ else
+ {
+ nVoteType = NativeVotesType_AlltalkOn;
+ }
+ hVoteMenu = NativeVotes_Create(Handler_NativeVoteCallback, nVoteType, MenuAction:MENU_ACTIONS_ALL);
+ }
+ else
+ {
+ hVoteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Custom_YesNo, MenuAction:MENU_ACTIONS_ALL);
+
+ if (GetConVarBool(g_Cvar_Alltalk))
+ {
+ NativeVotes_SetTitle(hVoteMenu, "Votealltalk Off");
+ }
+ else
+ {
+ NativeVotes_SetTitle(hVoteMenu, "Votealltalk On");
+ }
+ }
+
+ NativeVotes_DisplayToAll(hVoteMenu, 20);
+ }
+ else
+ {
+ new Handle:hVoteMenu = CreateMenu(Handler_VoteCallback, MenuAction:MENU_ACTIONS_ALL);
+
+ if (GetConVarBool(g_Cvar_Alltalk))
+ {
+ SetMenuTitle(hVoteMenu, "Votealltalk Off");
+ }
+ else
+ {
+ SetMenuTitle(hVoteMenu, "Votealltalk On");
+ }
+
+ AddMenuItem(hVoteMenu, VOTE_YES, "Yes");
+ AddMenuItem(hVoteMenu, VOTE_NO, "No");
+ SetMenuExitButton(hVoteMenu, false);
+ VoteMenuToAll(hVoteMenu, 20);
+ }
+}
+
+
+public AdminMenu_VoteAllTalk(Handle:topmenu,
+ TopMenuAction:action,
+ TopMenuObject:object_id,
+ param,
+ String:buffer[],
+ maxlength)
+{
+ if (action == TopMenuAction_DisplayOption)
+ {
+ Format(buffer, maxlength, "%T", "Alltalk vote", param);
+ }
+ else if (action == TopMenuAction_SelectOption)
+ {
+ DisplayVoteAllTalkMenu(param);
+ }
+ else if (action == TopMenuAction_DrawOption)
+ {
+ /* disable this option if a vote is already running */
+ buffer[0] = !Internal_IsNewVoteAllowed() ? ITEMDRAW_IGNORE : ITEMDRAW_DEFAULT;
+ }
+}
+
+public Action:Command_VoteAlltalk(client, args)
+{
+ if (args > 0)
+ {
+ CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] Usage: sm_votealltalk");
+ return Plugin_Handled;
+ }
+
+ DisplayVoteAllTalkMenu(client);
+
+ return Plugin_Handled;
+}
\ No newline at end of file
diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp b/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp
new file mode 100644
index 0000000..3acaa4f
--- /dev/null
+++ b/addons/sourcemod/scripting/nativevotes_funvotes/voteburn.sp
@@ -0,0 +1,200 @@
+ /**
+ * vim: set ts=4 :
+ * =============================================================================
+ * NativeVotes Fun Votes Plugin
+ * Provides voteburn functionality
+ *
+ * NativeVotes (C)2011-2014 Ross Bemrose (Powerlord). All rights reserved.
+ * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved.
+ * =============================================================================
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 3.0, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ *
+ * As a special exception, AlliedModders LLC gives you permission to link the
+ * code of this program (as well as its derivative works) to "Half-Life 2," the
+ * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
+ * by the Valve Corporation. You must obey the GNU General Public License in
+ * all respects for all other code used. Additionally, AlliedModders LLC grants
+ * this exception to all derivative works. AlliedModders LLC defines further
+ * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
+ * or .
+ *
+ * Version: $Id$
+ */
+
+DisplayVoteBurnMenu(client, target, String:name[])
+{
+ if (!IsPlayerAlive(target))
+ {
+ CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Cannot be performed on dead", name);
+ return;
+ }
+
+ g_voteClient[VOTE_CLIENTID] = target;
+
+ char playerName[MAX_NAME_LENGTH]; int r, g, b, a, color;
+ GetEntityRenderColor(target, r, g, b, a);
+ color = (r << 16) | (g << 8) | b;
+ if (color != 0xFFFFFF) {
+ Format(playerName, sizeof(playerName), "{#%06X}%N\x01", color, target);
+ }
+ else {
+ Format(playerName, sizeof(playerName), "%s%N\x01", GetClientTeam(target) == 2 ? "{red}" : GetClientTeam(target) == 3 ? "{blue}" : "{grey}", target);
+ }
+
+ GetClientName(target, g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[]));
+
+ LogAction(client, target, "\"%L\" initiated a burn vote against %s", client, playerName);
+ CShowActivity2(client, "[{lightgreen}NativeVotes\x01] ", "%t", "Initiated Vote Burn", playerName);
+
+ g_voteType = voteType:burn;
+
+ if (g_NativeVotes)
+ {
+ new Handle:hVoteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Custom_YesNo, MenuAction:MENU_ACTIONS_ALL);
+ NativeVotes_SetTitle(hVoteMenu, "Voteburn player");
+ NativeVotes_SetTarget(hVoteMenu, target);
+ NativeVotes_DisplayToAll(hVoteMenu, 20);
+ }
+ else
+ {
+ new Handle:hVoteMenu = CreateMenu(Handler_VoteCallback, MenuAction:MENU_ACTIONS_ALL);
+ SetMenuTitle(hVoteMenu, "Voteburn player");
+ AddMenuItem(hVoteMenu, VOTE_YES, "Yes");
+ AddMenuItem(hVoteMenu, VOTE_NO, "No");
+ SetMenuExitButton(hVoteMenu, false);
+ VoteMenuToAll(hVoteMenu, 20);
+ }
+}
+
+DisplayBurnTargetMenu(client)
+{
+ new Handle:menu = CreateMenu(MenuHandler_Burn);
+
+ decl String:title[100];
+ Format(title, sizeof(title), "%T:", "Burn vote", client);
+ SetMenuTitle(menu, title);
+ SetMenuExitBackButton(menu, true);
+
+ AddTargetsToMenu(menu, client, true, true);
+
+ DisplayMenu(menu, client, MENU_TIME_FOREVER);
+}
+
+public AdminMenu_VoteBurn(Handle:topmenu,
+ TopMenuAction:action,
+ TopMenuObject:object_id,
+ param,
+ String:buffer[],
+ maxlength)
+{
+ if (action == TopMenuAction_DisplayOption)
+ {
+ Format(buffer, maxlength, "%T", "Burn vote", param);
+ }
+ else if (action == TopMenuAction_SelectOption)
+ {
+ DisplayBurnTargetMenu(param);
+ }
+ else if (action == TopMenuAction_DrawOption)
+ {
+ /* disable this option if a vote is already running */
+ buffer[0] = !Internal_IsNewVoteAllowed() ? ITEMDRAW_IGNORE : ITEMDRAW_DEFAULT;
+ }
+}
+
+public MenuHandler_Burn(Handle:menu, MenuAction:action, param1, param2)
+{
+ if (action == MenuAction_End)
+ {
+ CloseHandle(menu);
+ }
+ else if (action == MenuAction_Cancel)
+ {
+ if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE)
+ {
+ DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory);
+ }
+ }
+ else if (action == MenuAction_Select)
+ {
+ decl String:info[32], String:name[32];
+ new userid, target;
+
+ GetMenuItem(menu, param2, info, sizeof(info), _, name, sizeof(name));
+ userid = StringToInt(info);
+
+ if ((target = GetClientOfUserId(userid)) == 0)
+ {
+ PrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Player no longer available");
+ }
+ else if (!CanUserTarget(param1, target))
+ {
+ PrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Unable to target");
+ }
+ else if (!IsPlayerAlive(target))
+ {
+ PrintToChat(param1, "[{lightgreen}NativeVotes\x01] %t", "Player has since died");
+ }
+ else
+ {
+ DisplayVoteBurnMenu(param1, target, name);
+ }
+ }
+}
+
+public Action:Command_VoteBurn(client, args)
+{
+ if (args < 1)
+ {
+ CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] Usage: sm_voteburn ");
+ return Plugin_Handled;
+ }
+
+ if (Internal_IsVoteInProgress())
+ {
+ CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Vote in Progress");
+ return Plugin_Handled;
+ }
+
+ if (!TestVoteDelay(client))
+ {
+ return Plugin_Handled;
+ }
+
+ decl String:text[256], String:arg[64];
+ GetCmdArgString(text, sizeof(text));
+
+ BreakString(text, arg, sizeof(arg));
+
+ decl String:target_name[MAX_TARGET_LENGTH];
+ decl target_list[MAXPLAYERS], target_count, bool:tn_is_ml;
+
+ if ((target_count = ProcessTargetString(
+ arg,
+ client,
+ target_list,
+ MAXPLAYERS,
+ COMMAND_FILTER_NO_MULTI,
+ target_name,
+ sizeof(target_name),
+ tn_is_ml)) <= 0)
+ {
+ ReplyToTargetError(client, target_count);
+ return Plugin_Handled;
+ }
+
+ DisplayVoteBurnMenu(client, target_list[0], arg);
+
+ return Plugin_Handled;
+}
diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp b/addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp
new file mode 100644
index 0000000..a46ff4a
--- /dev/null
+++ b/addons/sourcemod/scripting/nativevotes_funvotes/voteff.sp
@@ -0,0 +1,121 @@
+ /**
+ * vim: set ts=4 :
+ * =============================================================================
+ * NativeVotes Fun Votes Plugin
+ * Provides vote ff functionality
+ *
+ * NativeVotes (C)2011-2014 Ross Bemrose (Powerlord). All rights reserved.
+ * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved.
+ * =============================================================================
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 3.0, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ *
+ * As a special exception, AlliedModders LLC gives you permission to link the
+ * code of this program (as well as its derivative works) to "Half-Life 2," the
+ * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
+ * by the Valve Corporation. You must obey the GNU General Public License in
+ * all respects for all other code used. Additionally, AlliedModders LLC grants
+ * this exception to all derivative works. AlliedModders LLC defines further
+ * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
+ * or .
+ *
+ * Version: $Id$
+ */
+
+DisplayVoteFFMenu(client)
+{
+ if (Internal_IsVoteInProgress())
+ {
+ CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Vote in Progress");
+ return;
+ }
+
+ if (!TestVoteDelay(client))
+ {
+ return;
+ }
+
+ LogAction(client, -1, "\"%L\" initiated a friendly fire vote.", client);
+ CShowActivity2(client, "[{lightgreen}NativeVotes\x01] ", "%t", "Initiated Vote FF");
+
+ g_voteType = voteType:ff;
+ g_voteInfo[VOTE_NAME][0] = '\0';
+
+ if (g_NativeVotes)
+ {
+ new Handle:hVoteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Custom_YesNo, MenuAction:MENU_ACTIONS_ALL);
+
+ if (GetConVarBool(g_Cvar_FF))
+ {
+ NativeVotes_SetTitle(hVoteMenu, "Voteff Off");
+ }
+ else
+ {
+ NativeVotes_SetTitle(hVoteMenu, "Voteff On");
+ }
+ NativeVotes_DisplayToAll(hVoteMenu, 20);
+ }
+ else
+ {
+ new Handle:hVoteMenu = CreateMenu(Handler_VoteCallback, MenuAction:MENU_ACTIONS_ALL);
+
+ if (GetConVarBool(g_Cvar_FF))
+ {
+ SetMenuTitle(hVoteMenu, "Voteff Off");
+ }
+ else
+ {
+ SetMenuTitle(hVoteMenu, "Voteff On");
+ }
+
+ AddMenuItem(hVoteMenu, VOTE_YES, "Yes");
+ AddMenuItem(hVoteMenu, VOTE_NO, "No");
+ SetMenuExitButton(hVoteMenu, false);
+ VoteMenuToAll(hVoteMenu, 20);
+ }
+}
+
+public AdminMenu_VoteFF(Handle:topmenu,
+ TopMenuAction:action,
+ TopMenuObject:object_id,
+ param,
+ String:buffer[],
+ maxlength)
+{
+ if (action == TopMenuAction_DisplayOption)
+ {
+ Format(buffer, maxlength, "%T", "Vote FF", param);
+ }
+ else if (action == TopMenuAction_SelectOption)
+ {
+ DisplayVoteFFMenu(param);
+ }
+ else if (action == TopMenuAction_DrawOption)
+ {
+ /* disable this option if a vote is already running */
+ buffer[0] = !Internal_IsNewVoteAllowed() ? ITEMDRAW_IGNORE : ITEMDRAW_DEFAULT;
+ }
+}
+
+public Action:Command_VoteFF(client, args)
+{
+ if (args > 0)
+ {
+ CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] Usage: sm_voteff");
+ return Plugin_Handled;
+ }
+
+ DisplayVoteFFMenu(client);
+
+ return Plugin_Handled;
+}
\ No newline at end of file
diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp b/addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp
new file mode 100644
index 0000000..04ce41a
--- /dev/null
+++ b/addons/sourcemod/scripting/nativevotes_funvotes/votegravity.sp
@@ -0,0 +1,164 @@
+ /**
+ * vim: set ts=4 :
+ * =============================================================================
+ * NativeVotes Fun Votes Plugin
+ * Provides votegravity functionality
+ *
+ * NativeVotes (C)2011-2014 Ross Bemrose (Powerlord). All rights reserved.
+ * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved.
+ * =============================================================================
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 3.0, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ *
+ * As a special exception, AlliedModders LLC gives you permission to link the
+ * code of this program (as well as its derivative works) to "Half-Life 2," the
+ * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
+ * by the Valve Corporation. You must obey the GNU General Public License in
+ * all respects for all other code used. Additionally, AlliedModders LLC grants
+ * this exception to all derivative works. AlliedModders LLC defines further
+ * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
+ * or .
+ *
+ * Version: $Id$
+ */
+
+DisplayVoteGravityMenu(client,count,String:items[5][])
+{
+ LogAction(client, -1, "\"%L\" initiated a gravity vote.", client);
+ CShowActivity2(client, "[{lightgreen}NativeVotes\x01] ", "%t", "Initiated Vote Gravity");
+
+ g_voteType = voteType:gravity;
+
+ if (g_NativeVotes && (count == 1 || NativeVotes_IsVoteTypeSupported(NativeVotesType_Custom_Mult)) )
+ {
+ new Handle:hVoteMenu;
+ if (count == 1)
+ {
+ strcopy(g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[]), items[0]);
+
+ hVoteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Custom_YesNo, MenuAction:MENU_ACTIONS_ALL);
+ NativeVotes_SetTitle(hVoteMenu, "Change Gravity To");
+ // No details for custom votes
+ }
+ else
+ {
+ hVoteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Custom_Mult, MenuAction:MENU_ACTIONS_ALL);
+ NativeVotes_SetTitle(hVoteMenu, "Gravity Vote");
+ for (new i = 0; i < count; i++)
+ {
+ NativeVotes_AddItem(hVoteMenu, items[i], items[i]);
+ }
+ }
+ NativeVotes_DisplayToAll(hVoteMenu, 20);
+ }
+ else
+ {
+ new Handle:hVoteMenu = CreateMenu(Handler_VoteCallback, MenuAction:MENU_ACTIONS_ALL);
+
+ if (count == 1)
+ {
+ strcopy(g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[]), items[0]);
+
+ SetMenuTitle(hVoteMenu, "Change Gravity To");
+ AddMenuItem(hVoteMenu, items[0], "Yes");
+ AddMenuItem(hVoteMenu, VOTE_NO, "No");
+ }
+ else
+ {
+ g_voteInfo[VOTE_NAME][0] = '\0';
+
+ SetMenuTitle(hVoteMenu, "Gravity Vote");
+ for (new i = 0; i < count; i++)
+ {
+ AddMenuItem(hVoteMenu, items[i], items[i]);
+ }
+ }
+
+ SetMenuExitButton(hVoteMenu, false);
+ VoteMenuToAll(hVoteMenu, 20);
+ }
+}
+
+public AdminMenu_VoteGravity(Handle:topmenu,
+ TopMenuAction:action,
+ TopMenuObject:object_id,
+ param,
+ String:buffer[],
+ maxlength)
+{
+ if (action == TopMenuAction_DisplayOption)
+ {
+ Format(buffer, maxlength, "%T", "Gravity vote", param);
+ }
+ else if (action == TopMenuAction_SelectOption)
+ {
+ /* Might need a better way of selecting the list of pre-defined gravity choices */
+ new String:items[5][5] ={"200","400","800","1600","3200"};
+ DisplayVoteGravityMenu(param,5, items);
+ }
+ else if (action == TopMenuAction_DrawOption)
+ {
+ /* disable this option if a vote is already running */
+ buffer[0] = !Internal_IsNewVoteAllowed() ? ITEMDRAW_IGNORE : ITEMDRAW_DEFAULT;
+ }
+}
+
+public Action:Command_VoteGravity(client, args)
+{
+ if (args < 1)
+ {
+ CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] Usage: sm_votegravity [amount2] ... [amount5]");
+ return Plugin_Handled;
+ }
+
+ if (Internal_IsVoteInProgress())
+ {
+ CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Vote in Progress");
+ return Plugin_Handled;
+ }
+
+ if (!TestVoteDelay(client))
+ {
+ return Plugin_Handled;
+ }
+
+ decl String:text[256];
+ GetCmdArgString(text, sizeof(text));
+
+ decl String:items[5][64];
+ new count;
+ new len, pos;
+
+ while (pos != -1 && count < 5)
+ {
+ pos = BreakString(text[len], items[count], sizeof(items[]));
+
+ decl Float:temp;
+ if (StringToFloatEx(items[count], temp) == 0)
+ {
+ CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Invalid Amount");
+ return Plugin_Handled;
+ }
+
+ count++;
+
+ if (pos != -1)
+ {
+ len += pos;
+ }
+ }
+
+ DisplayVoteGravityMenu(client, count, items);
+
+ return Plugin_Handled;
+}
\ No newline at end of file
diff --git a/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp b/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp
new file mode 100644
index 0000000..5129dbe
--- /dev/null
+++ b/addons/sourcemod/scripting/nativevotes_funvotes/voteslay.sp
@@ -0,0 +1,199 @@
+ /**
+ * vim: set ts=4 :
+ * =============================================================================
+ * NativeVotes Fun Votes Plugin
+ * Provides voteslay functionality
+ *
+ * NativeVotes (C)2011-2014 Ross Bemrose (Powerlord). All rights reserved.
+ * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved.
+ * =============================================================================
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 3.0, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ *
+ * As a special exception, AlliedModders LLC gives you permission to link the
+ * code of this program (as well as its derivative works) to "Half-Life 2," the
+ * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
+ * by the Valve Corporation. You must obey the GNU General Public License in
+ * all respects for all other code used. Additionally, AlliedModders LLC grants
+ * this exception to all derivative works. AlliedModders LLC defines further
+ * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
+ * or .
+ *
+ * Version: $Id$
+ */
+
+DisplayVoteSlayMenu(client, target, String:name[])
+{
+ if (!IsPlayerAlive(target))
+ {
+ CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Cannot be performed on dead", name);
+ return;
+ }
+
+ g_voteClient[VOTE_CLIENTID] = target;
+
+ char playerName[MAX_NAME_LENGTH]; int r, g, b, a, color;
+ GetEntityRenderColor(target, r, g, b, a);
+ color = (r << 16) | (g << 8) | b;
+ if (color != 0xFFFFFF) {
+ Format(playerName, sizeof(playerName), "{#%06X}%N\x01", color, target);
+ }
+ else {
+ Format(playerName, sizeof(playerName), "%s%N\x01", GetClientTeam(target) == 2 ? "{red}" : GetClientTeam(target) == 3 ? "{blue}" : "{grey}", target);
+ }
+ // Use 'playerName' in printed messages, but keep g_voteInfo[VOTE_NAME] for vote panels
+ GetClientName(target, g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[]));
+
+ LogAction(client, target, "\"%L\" initiated a slay vote against %s", client, playerName);
+ CShowActivity2(client, "[{lightgreen}NativeVotes\x01] ", "%t", "Initiated Vote Slay", playerName);
+
+ g_voteType = voteType:slay;
+
+ if (g_NativeVotes)
+ {
+ new Handle:hVoteMenu = NativeVotes_Create(Handler_NativeVoteCallback, NativeVotesType_Custom_YesNo, MenuAction:MENU_ACTIONS_ALL);
+ NativeVotes_SetTitle(hVoteMenu, "Voteslay Player");
+ NativeVotes_DisplayToAll(hVoteMenu, 20);
+ }
+ else
+ {
+ new Handle:hVoteMenu = CreateMenu(Handler_VoteCallback, MenuAction:MENU_ACTIONS_ALL);
+ SetMenuTitle(hVoteMenu, "Voteslay Player");
+ AddMenuItem(hVoteMenu, VOTE_YES, "Yes");
+ AddMenuItem(hVoteMenu, VOTE_NO, "No");
+ SetMenuExitButton(hVoteMenu, false);
+ VoteMenuToAll(hVoteMenu, 20);
+ }
+}
+
+DisplaySlayTargetMenu(client)
+{
+ new Handle:menu = CreateMenu(MenuHandler_Slay);
+
+ decl String:title[100];
+ Format(title, sizeof(title), "%T:", "Slay vote", client);
+ SetMenuTitle(menu, title);
+ SetMenuExitBackButton(menu, true);
+
+ AddTargetsToMenu(menu, client, true, true);
+
+ DisplayMenu(menu, client, MENU_TIME_FOREVER);
+}
+
+public AdminMenu_VoteSlay(Handle:topmenu,
+ TopMenuAction:action,
+ TopMenuObject:object_id,
+ param,
+ String:buffer[],
+ maxlength)
+{
+ if (action == TopMenuAction_DisplayOption)
+ {
+ Format(buffer, maxlength, "%T", "Slay vote", param);
+ }
+ else if (action == TopMenuAction_SelectOption)
+ {
+ DisplaySlayTargetMenu(param);
+ }
+ else if (action == TopMenuAction_DrawOption)
+ {
+ /* disable this option if a vote is already running */
+ buffer[0] = !Internal_IsNewVoteAllowed() ? ITEMDRAW_IGNORE : ITEMDRAW_DEFAULT;
+ }
+}
+
+public MenuHandler_Slay(Handle:menu, MenuAction:action, param1, param2)
+{
+ if (action == MenuAction_End)
+ {
+ CloseHandle(menu);
+ }
+ else if (action == MenuAction_Cancel)
+ {
+ if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE)
+ {
+ DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory);
+ }
+ }
+ else if (action == MenuAction_Select)
+ {
+ decl String:info[32], String:name[32];
+ new userid, target;
+
+ GetMenuItem(menu, param2, info, sizeof(info), _, name, sizeof(name));
+ userid = StringToInt(info);
+
+ if ((target = GetClientOfUserId(userid)) == 0)
+ {
+ PrintToChat(param1, "[SM] %t", "Player no longer available");
+ }
+ else if (!CanUserTarget(param1, target))
+ {
+ PrintToChat(param1, "[SM] %t", "Unable to target");
+ }
+ else if (!IsPlayerAlive(target))
+ {
+ PrintToChat(param1, "[SM] %t", "Player has since died");
+ }
+ else
+ {
+ DisplayVoteSlayMenu(param1, target, name);
+ }
+ }
+}
+
+public Action:Command_VoteSlay(client, args)
+{
+ if (args < 1)
+ {
+ CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] Usage: sm_voteslay ");
+ return Plugin_Handled;
+ }
+
+ if (Internal_IsVoteInProgress())
+ {
+ CReplyToCommand(client, "[{lightgreen}NativeVotes\x01] %t", "Vote in Progress");
+ return Plugin_Handled;
+ }
+
+ if (!TestVoteDelay(client))
+ {
+ return Plugin_Handled;
+ }
+
+ decl String:text[256], String:arg[64];
+ GetCmdArgString(text, sizeof(text));
+
+ BreakString(text, arg, sizeof(arg));
+
+ decl String:target_name[MAX_TARGET_LENGTH];
+ decl target_list[MAXPLAYERS], target_count, bool:tn_is_ml;
+
+ if ((target_count = ProcessTargetString(
+ arg,
+ client,
+ target_list,
+ MAXPLAYERS,
+ COMMAND_FILTER_NO_MULTI,
+ target_name,
+ sizeof(target_name),
+ tn_is_ml)) <= 0)
+ {
+ ReplyToTargetError(client, target_count);
+ return Plugin_Handled;
+ }
+
+ DisplayVoteSlayMenu(client, target_list[0], arg);
+
+ return Plugin_Handled;
+}
diff --git a/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp b/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp
index 8bc7f44..b416e78 100644
--- a/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp
+++ b/addons/sourcemod/scripting/nativevotes_kickvote_immunity.sp
@@ -41,7 +41,7 @@
#pragma semicolon 1
#pragma newdecls required
-#define VERSION "3.0.0"
+#define VERSION "25w52a"
ConVar g_Cvar_Votes;
ConVar g_Cvar_KickVote;
@@ -56,7 +56,7 @@ public Plugin myinfo = {
author = "Powerlord",
description = "Causes TF2 kick votes to fail against people who the current user can't target.",
version = VERSION,
- url = "https://forums.alliedmods.net/showthread.php?t=208008"
+ url = "https://github.com/Heapons/sourcemod-nativevotes-updated/"
};
public void OnPluginStart()
@@ -161,7 +161,7 @@ public Action KickVoteHandler(int client, NativeVotesOverride overrideType, cons
if (!CanUserTarget(client, target))
{
NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFail_CantKickAdmin);
- PrintToChat(client, "%t", "Unable to target");
+ CPrintToChat(client, "%t", "Unable to target");
return Plugin_Stop;
}
diff --git a/addons/sourcemod/scripting/nativevotes_mapchooser.sp b/addons/sourcemod/scripting/nativevotes_mapchooser.sp
index ffaccc0..fdc7bd8 100644
--- a/addons/sourcemod/scripting/nativevotes_mapchooser.sp
+++ b/addons/sourcemod/scripting/nativevotes_mapchooser.sp
@@ -45,7 +45,7 @@
#pragma semicolon 1
#pragma newdecls required
-#define VERSION "1.8.0 beta 1"
+#define VERSION "25w52a"
public Plugin myinfo =
{
@@ -53,7 +53,7 @@ public Plugin myinfo =
author = "AlliedModders LLC and Powerlord",
description = "Automated Map Voting",
version = VERSION,
- url = "https://forums.alliedmods.net/showthread.php?t=208010"
+ url = "https://github.com/Heapons/sourcemod-nativevotes-updated/"
};
/* Valve ConVars */
@@ -306,7 +306,7 @@ public void OnMapEnd()
GetCurrentMap(map, sizeof(map));
g_OldMapList.PushString(map);
- if (g_OldMapList.Length > g_Cvar_ExcludeMaps.IntValue)
+ while (g_OldMapList.Length > g_Cvar_ExcludeMaps.IntValue)
{
g_OldMapList.Erase(0);
}
@@ -336,7 +336,7 @@ public Action Command_SetNextmap(int client, int args)
{
if (args < 1)
{
- ReplyToCommand(client, "[SM] Usage: sm_setnextmap