From 459815ce0ee048e434159dbdc72aa336c72a9daa Mon Sep 17 00:00:00 2001 From: BotoX Date: Tue, 19 Jan 2016 23:57:32 +0100 Subject: [PATCH] converted newlines to unix --- ConnectAnnounce/scripting/ConnectAnnounce.sp | 62 +- NapalmLagFix/scripting/NapalmLagFix.sp | 236 +- .../scripting/NoGrenadeRinging.sp | 84 +- StopSound/scripting/StopSound.sp | 664 +-- WeaponCleaner/scripting/WeaponCleaner.sp | 548 +- custom-chatcolors/scripting/include/ccc.inc | 378 +- includes/colors.inc | 1076 ++-- includes/dhooks.inc | 962 ++-- includes/nativevotes.inc | 1830 +++---- .../configs/mapchooser_extended/maps/csgo.txt | 42 +- .../mapchooser_extended/maps/cstrike.txt | 56 +- .../configs/mapchooser_extended/maps/dods.txt | 18 +- .../mapchooser_extended/maps/hl2mp.txt | 14 +- .../configs/mapchooser_extended/maps/tf.txt | 130 +- .../configs/mapchooser_extended/sounds/tf.cfg | 152 +- .../scripting/include/mapchooser_extended.inc | 216 +- .../scripting/mapchooser_extended.sp | 4684 ++++++++--------- .../scripting/nominations_extended.sp | 1406 ++--- .../scripting/rockthevote_extended.sp | 756 +-- .../translations/chi/incomplete.txt | 2 +- .../chi/mapchooser_extended.phrases.txt | 86 +- .../translations/cze/incomplete.txt | 2 +- .../cze/mapchooser_extended.phrases.txt | 86 +- .../translations/da/incomplete.txt | 2 +- .../da/mapchooser_extended.phrases.txt | 86 +- .../de/mapchooser_extended.phrases.txt | 194 +- .../es/mapchooser_extended.phrases.txt | 182 +- .../fr/mapchooser_extended.phrases.old.txt | 172 +- .../fr/mapchooser_extended.phrases.txt | 182 +- .../translations/hu/incomplete.txt | 2 +- .../hu/mapchooser_extended.phrases.txt | 86 +- .../it/mapchooser_extended.phrases.txt | 184 +- .../translations/jp/incomplete.txt | 2 +- .../jp/mapchooser_extended.phrases.txt | 86 +- .../ko/mapchooser_extended.phrases.txt | 196 +- .../translations/lv/incomplete.txt | 2 +- .../lv/mapchooser_extended.phrases.txt | 86 +- .../mapchooser_extended.phrases.txt | 216 +- .../translations/nl/incomplete.txt | 2 +- .../nl/mapchooser_extended.phrases.txt | 86 +- .../translations/no/incomplete.txt | 2 +- .../no/mapchooser_extended.phrases.txt | 86 +- .../pl/mapchooser_extended.phrases.txt | 182 +- .../pt/mapchooser_extended.phrases.txt | 194 +- .../ru/mapchooser_extended.phrases.txt | 164 +- .../translations/sv/incomplete.txt | 2 +- .../sv/mapchooser_extended.phrases.txt | 86 +- .../translations/tr/incomplete.txt | 2 +- .../tr/mapchooser_extended.phrases.txt | 86 +- 49 files changed, 8030 insertions(+), 8030 deletions(-) diff --git a/ConnectAnnounce/scripting/ConnectAnnounce.sp b/ConnectAnnounce/scripting/ConnectAnnounce.sp index 92bdd326..851cc4bf 100644 --- a/ConnectAnnounce/scripting/ConnectAnnounce.sp +++ b/ConnectAnnounce/scripting/ConnectAnnounce.sp @@ -1,31 +1,31 @@ -#pragma semicolon 1 - -#include -#include - -#pragma newdecls required - -public Plugin myinfo = { - name = "Connect Announce", - author = "BotoX", - description = "Simple connect announcer", - version = "1.0", - url = "" -} - -public void OnClientPostAdminCheck(int client) -{ - if(IsFakeClient(client)) - return; - - static char sAuth[32]; - static char sIP[16]; - static char sCountry[32]; - - GetClientAuthId(client, AuthId_Steam2, sAuth, sizeof(sAuth)); - - if(GetClientIP(client, sIP, sizeof(sIP)) && GeoipCountry(sIP, sCountry, sizeof(sCountry))) - PrintToChatAll("\x04%L [\x03%s\x04] connected from %s", client, sAuth, sCountry); - else - PrintToChatAll("\x04%L [\x03%s\x04] connected", client, sAuth); -} +#pragma semicolon 1 + +#include +#include + +#pragma newdecls required + +public Plugin myinfo = { + name = "Connect Announce", + author = "BotoX", + description = "Simple connect announcer", + version = "1.0", + url = "" +} + +public void OnClientPostAdminCheck(int client) +{ + if(IsFakeClient(client)) + return; + + static char sAuth[32]; + static char sIP[16]; + static char sCountry[32]; + + GetClientAuthId(client, AuthId_Steam2, sAuth, sizeof(sAuth)); + + if(GetClientIP(client, sIP, sizeof(sIP)) && GeoipCountry(sIP, sCountry, sizeof(sCountry))) + PrintToChatAll("\x04%L [\x03%s\x04] connected from %s", client, sAuth, sCountry); + else + PrintToChatAll("\x04%L [\x03%s\x04] connected", client, sAuth); +} diff --git a/NapalmLagFix/scripting/NapalmLagFix.sp b/NapalmLagFix/scripting/NapalmLagFix.sp index 8e22a9d9..b360008a 100644 --- a/NapalmLagFix/scripting/NapalmLagFix.sp +++ b/NapalmLagFix/scripting/NapalmLagFix.sp @@ -1,118 +1,118 @@ -#pragma semicolon 1 - -#include -#include -#undef REQUIRE_PLUGIN -#include - -#define PLUGIN_NAME "Napalm Lag Fix" -#define PLUGIN_VERSION "1.0.3" - -#define UPDATE_URL "http://godtony.mooo.com/napalmlagfix/napalmlagfix.txt" - -#define DMG_BURN (1 << 3) - -new Handle:g_hRadiusDamage = INVALID_HANDLE; -new bool:g_bCheckNullPtr = false; - -public Plugin:myinfo = -{ - name = PLUGIN_NAME, - author = "GoD-Tony + BotoX", - description = "Prevents lag when napalm is used on players", - version = PLUGIN_VERSION, - url = "https://forums.alliedmods.net/showthread.php?t=188093" // Demo: http://youtu.be/YdhAu5IEVVM -}; - -public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max) -{ - MarkNativeAsOptional("DHookIsNullParam"); - - return APLRes_Success; -} - -public OnPluginStart() -{ - // Convars. - new Handle:hCvar = CreateConVar("sm_napalmlagfix_version", PLUGIN_VERSION, PLUGIN_NAME, FCVAR_PLUGIN|FCVAR_NOTIFY|FCVAR_DONTRECORD); - SetConVarString(hCvar, PLUGIN_VERSION); - - // Gamedata. - new Handle:hConfig = LoadGameConfigFile("napalmlagfix.games"); - - if (hConfig == INVALID_HANDLE) - { - SetFailState("Could not find gamedata file: napalmlagfix.games.txt"); - } - - new offset = GameConfGetOffset(hConfig, "RadiusDamage"); - - if (offset == -1) - { - SetFailState("Failed to find RadiusDamage offset"); - } - - CloseHandle(hConfig); - - // DHooks. - g_bCheckNullPtr = (GetFeatureStatus(FeatureType_Native, "DHookIsNullParam") == FeatureStatus_Available); - - g_hRadiusDamage = DHookCreate(offset, HookType_GameRules, ReturnType_Void, ThisPointer_Ignore, Hook_RadiusDamage); - DHookAddParam(g_hRadiusDamage, HookParamType_ObjectPtr); // 1 - CTakeDamageInfo &info - DHookAddParam(g_hRadiusDamage, HookParamType_VectorPtr); // 2 - Vector &vecSrc - DHookAddParam(g_hRadiusDamage, HookParamType_Float); // 3 - float flRadius - DHookAddParam(g_hRadiusDamage, HookParamType_Int); // 4 - int iClassIgnore - DHookAddParam(g_hRadiusDamage, HookParamType_CBaseEntity); // 5 - CBaseEntity *pEntityIgnore - - // Updater. - if (LibraryExists("updater")) - { - Updater_AddPlugin(UPDATE_URL); - } -} - -public OnLibraryAdded(const String:name[]) -{ - if (StrEqual(name, "updater")) - { - Updater_AddPlugin(UPDATE_URL); - } -} - -public Updater_OnPluginUpdated() -{ - // There could be new gamedata in this update. - ReloadPlugin(); -} - -public OnMapStart() -{ - DHookGamerules(g_hRadiusDamage, false); -} - -public MRESReturn:Hook_RadiusDamage(Handle:hParams) -{ - // As of DHooks 1.0.12 we must check for a null param. - if (g_bCheckNullPtr && DHookIsNullParam(hParams, 5)) - return MRES_Ignored; - - new iDmgBits = DHookGetParamObjectPtrVar(hParams, 1, 60, ObjectValueType_Int); - new iEntIgnore = DHookGetParam(hParams, 5); - - if(!(iDmgBits & DMG_BURN)) - return MRES_Ignored; - - // Block napalm damage if it's coming from another client. - if (1 <= iEntIgnore <= MaxClients) - return MRES_Supercede; - - // Block napalm that comes from grenades - new String:sEntClassName[64]; - if(GetEntityClassname(iEntIgnore, sEntClassName, sizeof(sEntClassName))) - { - if(!strcmp(sEntClassName, "hegrenade_projectile")) - return MRES_Supercede; - } - - return MRES_Ignored; -} +#pragma semicolon 1 + +#include +#include +#undef REQUIRE_PLUGIN +#include + +#define PLUGIN_NAME "Napalm Lag Fix" +#define PLUGIN_VERSION "1.0.3" + +#define UPDATE_URL "http://godtony.mooo.com/napalmlagfix/napalmlagfix.txt" + +#define DMG_BURN (1 << 3) + +new Handle:g_hRadiusDamage = INVALID_HANDLE; +new bool:g_bCheckNullPtr = false; + +public Plugin:myinfo = +{ + name = PLUGIN_NAME, + author = "GoD-Tony + BotoX", + description = "Prevents lag when napalm is used on players", + version = PLUGIN_VERSION, + url = "https://forums.alliedmods.net/showthread.php?t=188093" // Demo: http://youtu.be/YdhAu5IEVVM +}; + +public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max) +{ + MarkNativeAsOptional("DHookIsNullParam"); + + return APLRes_Success; +} + +public OnPluginStart() +{ + // Convars. + new Handle:hCvar = CreateConVar("sm_napalmlagfix_version", PLUGIN_VERSION, PLUGIN_NAME, FCVAR_PLUGIN|FCVAR_NOTIFY|FCVAR_DONTRECORD); + SetConVarString(hCvar, PLUGIN_VERSION); + + // Gamedata. + new Handle:hConfig = LoadGameConfigFile("napalmlagfix.games"); + + if (hConfig == INVALID_HANDLE) + { + SetFailState("Could not find gamedata file: napalmlagfix.games.txt"); + } + + new offset = GameConfGetOffset(hConfig, "RadiusDamage"); + + if (offset == -1) + { + SetFailState("Failed to find RadiusDamage offset"); + } + + CloseHandle(hConfig); + + // DHooks. + g_bCheckNullPtr = (GetFeatureStatus(FeatureType_Native, "DHookIsNullParam") == FeatureStatus_Available); + + g_hRadiusDamage = DHookCreate(offset, HookType_GameRules, ReturnType_Void, ThisPointer_Ignore, Hook_RadiusDamage); + DHookAddParam(g_hRadiusDamage, HookParamType_ObjectPtr); // 1 - CTakeDamageInfo &info + DHookAddParam(g_hRadiusDamage, HookParamType_VectorPtr); // 2 - Vector &vecSrc + DHookAddParam(g_hRadiusDamage, HookParamType_Float); // 3 - float flRadius + DHookAddParam(g_hRadiusDamage, HookParamType_Int); // 4 - int iClassIgnore + DHookAddParam(g_hRadiusDamage, HookParamType_CBaseEntity); // 5 - CBaseEntity *pEntityIgnore + + // Updater. + if (LibraryExists("updater")) + { + Updater_AddPlugin(UPDATE_URL); + } +} + +public OnLibraryAdded(const String:name[]) +{ + if (StrEqual(name, "updater")) + { + Updater_AddPlugin(UPDATE_URL); + } +} + +public Updater_OnPluginUpdated() +{ + // There could be new gamedata in this update. + ReloadPlugin(); +} + +public OnMapStart() +{ + DHookGamerules(g_hRadiusDamage, false); +} + +public MRESReturn:Hook_RadiusDamage(Handle:hParams) +{ + // As of DHooks 1.0.12 we must check for a null param. + if (g_bCheckNullPtr && DHookIsNullParam(hParams, 5)) + return MRES_Ignored; + + new iDmgBits = DHookGetParamObjectPtrVar(hParams, 1, 60, ObjectValueType_Int); + new iEntIgnore = DHookGetParam(hParams, 5); + + if(!(iDmgBits & DMG_BURN)) + return MRES_Ignored; + + // Block napalm damage if it's coming from another client. + if (1 <= iEntIgnore <= MaxClients) + return MRES_Supercede; + + // Block napalm that comes from grenades + new String:sEntClassName[64]; + if(GetEntityClassname(iEntIgnore, sEntClassName, sizeof(sEntClassName))) + { + if(!strcmp(sEntClassName, "hegrenade_projectile")) + return MRES_Supercede; + } + + return MRES_Ignored; +} diff --git a/NoGrenadeRinging/scripting/NoGrenadeRinging.sp b/NoGrenadeRinging/scripting/NoGrenadeRinging.sp index 138b7f54..d5fcafb3 100644 --- a/NoGrenadeRinging/scripting/NoGrenadeRinging.sp +++ b/NoGrenadeRinging/scripting/NoGrenadeRinging.sp @@ -1,42 +1,42 @@ -#pragma semicolon 1 -#include -#include -#include - -//int CCSPlayer::OnDamagedByExplosion(CTakeDamageInfo const&) -Handle g_hDamagedByExplosion; - -public Plugin myinfo = -{ - name = "NoGrenadeRinging", - author = "BotoX", - description = "Block the annoying ringing noise when a grenade explodes next to you", - version = "1.0", - url = "" -}; - -public void OnPluginStart() -{ - Handle hTemp = LoadGameConfigFile("NoGrenadeRinging.games"); - if(hTemp == INVALID_HANDLE) - SetFailState("Why you no has gamedata?"); - - int Offset = GameConfGetOffset(hTemp, "OnDamagedByExplosion"); - g_hDamagedByExplosion = DHookCreate(Offset, HookType_Entity, ReturnType_Int, ThisPointer_CBaseEntity, OnDamagedByExplosion); - DHookAddParam(g_hDamagedByExplosion, HookParamType_ObjectPtr); - - CloseHandle(hTemp); -} - -public void OnClientPutInServer(int client) -{ - //Dont add removal callback for this one - DHookEntity(g_hDamagedByExplosion, false, client); -} - -//int CCSPlayer::OnDamagedByExplosion(CTakeDamageInfo const&) -public MRESReturn:OnDamagedByExplosion(int pThis, Handle hReturn, Handle hParams) -{ - // Block call - return MRES_Supercede; -} +#pragma semicolon 1 +#include +#include +#include + +//int CCSPlayer::OnDamagedByExplosion(CTakeDamageInfo const&) +Handle g_hDamagedByExplosion; + +public Plugin myinfo = +{ + name = "NoGrenadeRinging", + author = "BotoX", + description = "Block the annoying ringing noise when a grenade explodes next to you", + version = "1.0", + url = "" +}; + +public void OnPluginStart() +{ + Handle hTemp = LoadGameConfigFile("NoGrenadeRinging.games"); + if(hTemp == INVALID_HANDLE) + SetFailState("Why you no has gamedata?"); + + int Offset = GameConfGetOffset(hTemp, "OnDamagedByExplosion"); + g_hDamagedByExplosion = DHookCreate(Offset, HookType_Entity, ReturnType_Int, ThisPointer_CBaseEntity, OnDamagedByExplosion); + DHookAddParam(g_hDamagedByExplosion, HookParamType_ObjectPtr); + + CloseHandle(hTemp); +} + +public void OnClientPutInServer(int client) +{ + //Dont add removal callback for this one + DHookEntity(g_hDamagedByExplosion, false, client); +} + +//int CCSPlayer::OnDamagedByExplosion(CTakeDamageInfo const&) +public MRESReturn:OnDamagedByExplosion(int pThis, Handle hReturn, Handle hParams) +{ + // Block call + return MRES_Supercede; +} diff --git a/StopSound/scripting/StopSound.sp b/StopSound/scripting/StopSound.sp index ef30f877..3c02d98c 100644 --- a/StopSound/scripting/StopSound.sp +++ b/StopSound/scripting/StopSound.sp @@ -1,333 +1,333 @@ -#pragma semicolon 1 - -#include -#include -#include - -#define PLUGIN_NAME "Toggle Weapon Sounds" -#define PLUGIN_VERSION "1.2.0" - -#define UPDATE_URL "http://godtony.mooo.com/stopsound/stopsound.txt" - -new bool:g_bStopSound[MAXPLAYERS+1], bool:g_bHooked; -static String:g_sKVPATH[PLATFORM_MAX_PATH]; -new Handle:g_hWepSounds; - -public Plugin:myinfo = -{ - name = PLUGIN_NAME, - author = "GoD-Tony, edit by id/Obus", - description = "Allows clients to stop hearing weapon sounds", - version = PLUGIN_VERSION, - url = "http://www.sourcemod.net/" -}; - -public OnPluginStart() -{ - // Detect game and hook appropriate tempent. - decl String:sGame[32]; - GetGameFolderName(sGame, sizeof(sGame)); - - if (StrEqual(sGame, "cstrike")) - { - AddTempEntHook("Shotgun Shot", CSS_Hook_ShotgunShot); - } - else if (StrEqual(sGame, "dod")) - { - AddTempEntHook("FireBullets", DODS_Hook_FireBullets); - } - - // TF2/HL2:DM and misc weapon sounds will be caught here. - AddNormalSoundHook(Hook_NormalSound); - - CreateConVar("sm_stopsound_version", PLUGIN_VERSION, "Toggle Weapon Sounds", FCVAR_NOTIFY|FCVAR_DONTRECORD|FCVAR_REPLICATED); - RegConsoleCmd("sm_stopsound", Command_StopSound, "Toggle hearing weapon sounds"); - - if (g_hWepSounds != INVALID_HANDLE) - { - CloseHandle(g_hWepSounds); - } - - g_hWepSounds = CreateKeyValues("WeaponSounds"); - BuildPath(Path_SM, g_sKVPATH, sizeof(g_sKVPATH), "data/playerprefs.WepSounds.txt"); - - FileToKeyValues(g_hWepSounds, g_sKVPATH); - - // Updater. - //if (LibraryExists("updater")) - //{ - // Updater_AddPlugin(UPDATE_URL); - //} -} - -/*public OnLibraryAdded(const String:name[]) -{ - if (StrEqual(name, "updater")) - { - Updater_AddPlugin(UPDATE_URL); - } -}*/ - -public Action:Command_StopSound(client, args) -{ - if (client == 0) - { - PrintToServer("[SM] Cannot use command from server console."); - return Plugin_Handled; - } - - if (args > 0) - { - decl String:Arguments[32]; - GetCmdArg(1, Arguments, sizeof(Arguments)); - - if (StrEqual(Arguments, "save")) - { - KvRewind(g_hWepSounds); - - decl String:SID[32]; - GetClientAuthId(client, AuthId_Steam2, SID, sizeof(SID)); - - if (KvJumpToKey(g_hWepSounds, SID, true)) - { - new disabled; - disabled = KvGetNum(g_hWepSounds, "disabled", 0); - - if (!disabled) - { - //CPrintToChat(client, "[StopSound] Saved entry for STEAMID({green}%s{default}) {green}successfully{default}.", SID); - KvSetNum(g_hWepSounds, "disabled", 1); - KvRewind(g_hWepSounds); - KeyValuesToFile(g_hWepSounds, g_sKVPATH); - - g_bStopSound[client] = true; - CReplyToCommand(client, "{green}[StopSound]{default} Weapon sounds {red}disabled{default} - {green}entry saved{default}."); - CheckHooks(); - - return Plugin_Handled; - } - else - { - //CPrintToChat(client, "[StopSound] Entry for STEAMID({green}%s{default}) {green}successfully deleted{default}.", SID); - KvDeleteThis(g_hWepSounds); - KvRewind(g_hWepSounds); - KeyValuesToFile(g_hWepSounds, g_sKVPATH); - - g_bStopSound[client] = false; - CReplyToCommand(client, "{green}[StopSound]{default} Weapon sounds {green}enabled{default} - {red}entry deleted{default}."); - CheckHooks(); - - return Plugin_Handled; - } - } - - KvRewind(g_hWepSounds); - } - else if (StrEqual(Arguments, "delete")) - { - KvRewind(g_hWepSounds); - - decl String:SID[32]; - GetClientAuthId(client, AuthId_Steam2, SID, sizeof(SID)); - - if (KvJumpToKey(g_hWepSounds, SID, false)) - { - g_bStopSound[client] = false; - CReplyToCommand(client, "{green}[StopSound]{default} Weapon sounds {green}enabled{default} - {red}entry deleted{default}."); - CheckHooks(); - - KvDeleteThis(g_hWepSounds); - KvRewind(g_hWepSounds); - KeyValuesToFile(g_hWepSounds, g_sKVPATH); - - return Plugin_Handled; - } - else - { - CPrintToChat(client, "{green}[StopSound]{default} Entry {red}not found{default}."); - return Plugin_Handled; - } - } - else - { - PrintToChat(client, "[SM] Usage sm_stopsound "); - return Plugin_Handled; - } - } - - g_bStopSound[client] = !g_bStopSound[client]; - CReplyToCommand(client, "{green}[StopSound]{default} Weapon sounds %s.", g_bStopSound[client] ? "{red}disabled{default}" : "{green}enabled{default}"); - CheckHooks(); - - return Plugin_Handled; -} - -public OnClientPutInServer(client) -{ - KvRewind(g_hWepSounds); - - decl String:SID[32]; - GetClientAuthId(client, AuthId_Steam2, SID, sizeof(SID)); - - if (KvJumpToKey(g_hWepSounds, SID, false)) - { - new disabled; - disabled = KvGetNum(g_hWepSounds, "disabled", 0); - - if (disabled) - { - g_bStopSound[client] = true; - } - } - - CheckHooks(); - KvRewind(g_hWepSounds); -} - -public OnClientDisconnect_Post(client) -{ - g_bStopSound[client] = false; - CheckHooks(); -} - -CheckHooks() -{ - new bool:bShouldHook = false; - - for (new i = 1; i <= MaxClients; i++) - { - if (g_bStopSound[i]) - { - bShouldHook = true; - break; - } - } - - // Fake (un)hook because toggling actual hooks will cause server instability. - g_bHooked = bShouldHook; -} - -public Action:Hook_NormalSound(clients[64], &numClients, String:sample[PLATFORM_MAX_PATH], &entity, &channel, &Float:volume, &level, &pitch, &flags) -{ - // Ignore non-weapon sounds. - if (!g_bHooked || !(strncmp(sample, "weapons", 7) == 0 || strncmp(sample[1], "weapons", 7) == 0)) - { - return Plugin_Continue; - } - - decl i, j; - - for (i = 0; i < numClients; i++) - { - if (g_bStopSound[clients[i]]) - { - // Remove the client from the array. - for (j = i; j < numClients - 1; j++) - { - clients[j] = clients[j + 1]; - } - - numClients--; - i--; - } - } - - return (numClients > 0) ? Plugin_Changed : Plugin_Stop; -} - -public Action:CSS_Hook_ShotgunShot(const String:te_name[], const Players[], numClients, Float:delay) -{ - if (!g_bHooked) - { - return Plugin_Continue; - } - - // Check which clients need to be excluded. - decl newClients[MaxClients], client, i; - new newTotal = 0; - - for (i = 0; i < numClients; i++) - { - client = Players[i]; - - if (!g_bStopSound[client]) - { - newClients[newTotal++] = client; - } - } - - // No clients were excluded. - if (newTotal == numClients) - { - return Plugin_Continue; - } - else if (newTotal == 0) // All clients were excluded and there is no need to broadcast. - { - return Plugin_Stop; - } - - // Re-broadcast to clients that still need it. - decl Float:vTemp[3]; - TE_Start("Shotgun Shot"); - TE_ReadVector("m_vecOrigin", vTemp); - TE_WriteVector("m_vecOrigin", vTemp); - TE_WriteFloat("m_vecAngles[0]", TE_ReadFloat("m_vecAngles[0]")); - TE_WriteFloat("m_vecAngles[1]", TE_ReadFloat("m_vecAngles[1]")); - TE_WriteNum("m_iWeaponID", TE_ReadNum("m_iWeaponID")); - TE_WriteNum("m_iMode", TE_ReadNum("m_iMode")); - TE_WriteNum("m_iSeed", TE_ReadNum("m_iSeed")); - TE_WriteNum("m_iPlayer", TE_ReadNum("m_iPlayer")); - TE_WriteFloat("m_fInaccuracy", TE_ReadFloat("m_fInaccuracy")); - TE_WriteFloat("m_fSpread", TE_ReadFloat("m_fSpread")); - TE_Send(newClients, newTotal, delay); - - return Plugin_Stop; -} - -public Action:DODS_Hook_FireBullets(const String:te_name[], const Players[], numClients, Float:delay) -{ - if (!g_bHooked) - { - return Plugin_Continue; - } - - // Check which clients need to be excluded. - decl newClients[MaxClients], client, i; - new newTotal = 0; - - for (i = 0; i < numClients; i++) - { - client = Players[i]; - - if (!g_bStopSound[client]) - { - newClients[newTotal++] = client; - } - } - - // No clients were excluded. - if (newTotal == numClients) - { - return Plugin_Continue; - } - else if (newTotal == 0)// All clients were excluded and there is no need to broadcast. - { - return Plugin_Stop; - } - - // Re-broadcast to clients that still need it. - decl Float:vTemp[3]; - TE_Start("FireBullets"); - TE_ReadVector("m_vecOrigin", vTemp); - TE_WriteVector("m_vecOrigin", vTemp); - TE_WriteFloat("m_vecAngles[0]", TE_ReadFloat("m_vecAngles[0]")); - TE_WriteFloat("m_vecAngles[1]", TE_ReadFloat("m_vecAngles[1]")); - TE_WriteNum("m_iWeaponID", TE_ReadNum("m_iWeaponID")); - TE_WriteNum("m_iMode", TE_ReadNum("m_iMode")); - TE_WriteNum("m_iSeed", TE_ReadNum("m_iSeed")); - TE_WriteNum("m_iPlayer", TE_ReadNum("m_iPlayer")); - TE_WriteFloat("m_flSpread", TE_ReadFloat("m_flSpread")); - TE_Send(newClients, newTotal, delay); - - return Plugin_Stop; +#pragma semicolon 1 + +#include +#include +#include + +#define PLUGIN_NAME "Toggle Weapon Sounds" +#define PLUGIN_VERSION "1.2.0" + +#define UPDATE_URL "http://godtony.mooo.com/stopsound/stopsound.txt" + +new bool:g_bStopSound[MAXPLAYERS+1], bool:g_bHooked; +static String:g_sKVPATH[PLATFORM_MAX_PATH]; +new Handle:g_hWepSounds; + +public Plugin:myinfo = +{ + name = PLUGIN_NAME, + author = "GoD-Tony, edit by id/Obus", + description = "Allows clients to stop hearing weapon sounds", + version = PLUGIN_VERSION, + url = "http://www.sourcemod.net/" +}; + +public OnPluginStart() +{ + // Detect game and hook appropriate tempent. + decl String:sGame[32]; + GetGameFolderName(sGame, sizeof(sGame)); + + if (StrEqual(sGame, "cstrike")) + { + AddTempEntHook("Shotgun Shot", CSS_Hook_ShotgunShot); + } + else if (StrEqual(sGame, "dod")) + { + AddTempEntHook("FireBullets", DODS_Hook_FireBullets); + } + + // TF2/HL2:DM and misc weapon sounds will be caught here. + AddNormalSoundHook(Hook_NormalSound); + + CreateConVar("sm_stopsound_version", PLUGIN_VERSION, "Toggle Weapon Sounds", FCVAR_NOTIFY|FCVAR_DONTRECORD|FCVAR_REPLICATED); + RegConsoleCmd("sm_stopsound", Command_StopSound, "Toggle hearing weapon sounds"); + + if (g_hWepSounds != INVALID_HANDLE) + { + CloseHandle(g_hWepSounds); + } + + g_hWepSounds = CreateKeyValues("WeaponSounds"); + BuildPath(Path_SM, g_sKVPATH, sizeof(g_sKVPATH), "data/playerprefs.WepSounds.txt"); + + FileToKeyValues(g_hWepSounds, g_sKVPATH); + + // Updater. + //if (LibraryExists("updater")) + //{ + // Updater_AddPlugin(UPDATE_URL); + //} +} + +/*public OnLibraryAdded(const String:name[]) +{ + if (StrEqual(name, "updater")) + { + Updater_AddPlugin(UPDATE_URL); + } +}*/ + +public Action:Command_StopSound(client, args) +{ + if (client == 0) + { + PrintToServer("[SM] Cannot use command from server console."); + return Plugin_Handled; + } + + if (args > 0) + { + decl String:Arguments[32]; + GetCmdArg(1, Arguments, sizeof(Arguments)); + + if (StrEqual(Arguments, "save")) + { + KvRewind(g_hWepSounds); + + decl String:SID[32]; + GetClientAuthId(client, AuthId_Steam2, SID, sizeof(SID)); + + if (KvJumpToKey(g_hWepSounds, SID, true)) + { + new disabled; + disabled = KvGetNum(g_hWepSounds, "disabled", 0); + + if (!disabled) + { + //CPrintToChat(client, "[StopSound] Saved entry for STEAMID({green}%s{default}) {green}successfully{default}.", SID); + KvSetNum(g_hWepSounds, "disabled", 1); + KvRewind(g_hWepSounds); + KeyValuesToFile(g_hWepSounds, g_sKVPATH); + + g_bStopSound[client] = true; + CReplyToCommand(client, "{green}[StopSound]{default} Weapon sounds {red}disabled{default} - {green}entry saved{default}."); + CheckHooks(); + + return Plugin_Handled; + } + else + { + //CPrintToChat(client, "[StopSound] Entry for STEAMID({green}%s{default}) {green}successfully deleted{default}.", SID); + KvDeleteThis(g_hWepSounds); + KvRewind(g_hWepSounds); + KeyValuesToFile(g_hWepSounds, g_sKVPATH); + + g_bStopSound[client] = false; + CReplyToCommand(client, "{green}[StopSound]{default} Weapon sounds {green}enabled{default} - {red}entry deleted{default}."); + CheckHooks(); + + return Plugin_Handled; + } + } + + KvRewind(g_hWepSounds); + } + else if (StrEqual(Arguments, "delete")) + { + KvRewind(g_hWepSounds); + + decl String:SID[32]; + GetClientAuthId(client, AuthId_Steam2, SID, sizeof(SID)); + + if (KvJumpToKey(g_hWepSounds, SID, false)) + { + g_bStopSound[client] = false; + CReplyToCommand(client, "{green}[StopSound]{default} Weapon sounds {green}enabled{default} - {red}entry deleted{default}."); + CheckHooks(); + + KvDeleteThis(g_hWepSounds); + KvRewind(g_hWepSounds); + KeyValuesToFile(g_hWepSounds, g_sKVPATH); + + return Plugin_Handled; + } + else + { + CPrintToChat(client, "{green}[StopSound]{default} Entry {red}not found{default}."); + return Plugin_Handled; + } + } + else + { + PrintToChat(client, "[SM] Usage sm_stopsound "); + return Plugin_Handled; + } + } + + g_bStopSound[client] = !g_bStopSound[client]; + CReplyToCommand(client, "{green}[StopSound]{default} Weapon sounds %s.", g_bStopSound[client] ? "{red}disabled{default}" : "{green}enabled{default}"); + CheckHooks(); + + return Plugin_Handled; +} + +public OnClientPutInServer(client) +{ + KvRewind(g_hWepSounds); + + decl String:SID[32]; + GetClientAuthId(client, AuthId_Steam2, SID, sizeof(SID)); + + if (KvJumpToKey(g_hWepSounds, SID, false)) + { + new disabled; + disabled = KvGetNum(g_hWepSounds, "disabled", 0); + + if (disabled) + { + g_bStopSound[client] = true; + } + } + + CheckHooks(); + KvRewind(g_hWepSounds); +} + +public OnClientDisconnect_Post(client) +{ + g_bStopSound[client] = false; + CheckHooks(); +} + +CheckHooks() +{ + new bool:bShouldHook = false; + + for (new i = 1; i <= MaxClients; i++) + { + if (g_bStopSound[i]) + { + bShouldHook = true; + break; + } + } + + // Fake (un)hook because toggling actual hooks will cause server instability. + g_bHooked = bShouldHook; +} + +public Action:Hook_NormalSound(clients[64], &numClients, String:sample[PLATFORM_MAX_PATH], &entity, &channel, &Float:volume, &level, &pitch, &flags) +{ + // Ignore non-weapon sounds. + if (!g_bHooked || !(strncmp(sample, "weapons", 7) == 0 || strncmp(sample[1], "weapons", 7) == 0)) + { + return Plugin_Continue; + } + + decl i, j; + + for (i = 0; i < numClients; i++) + { + if (g_bStopSound[clients[i]]) + { + // Remove the client from the array. + for (j = i; j < numClients - 1; j++) + { + clients[j] = clients[j + 1]; + } + + numClients--; + i--; + } + } + + return (numClients > 0) ? Plugin_Changed : Plugin_Stop; +} + +public Action:CSS_Hook_ShotgunShot(const String:te_name[], const Players[], numClients, Float:delay) +{ + if (!g_bHooked) + { + return Plugin_Continue; + } + + // Check which clients need to be excluded. + decl newClients[MaxClients], client, i; + new newTotal = 0; + + for (i = 0; i < numClients; i++) + { + client = Players[i]; + + if (!g_bStopSound[client]) + { + newClients[newTotal++] = client; + } + } + + // No clients were excluded. + if (newTotal == numClients) + { + return Plugin_Continue; + } + else if (newTotal == 0) // All clients were excluded and there is no need to broadcast. + { + return Plugin_Stop; + } + + // Re-broadcast to clients that still need it. + decl Float:vTemp[3]; + TE_Start("Shotgun Shot"); + TE_ReadVector("m_vecOrigin", vTemp); + TE_WriteVector("m_vecOrigin", vTemp); + TE_WriteFloat("m_vecAngles[0]", TE_ReadFloat("m_vecAngles[0]")); + TE_WriteFloat("m_vecAngles[1]", TE_ReadFloat("m_vecAngles[1]")); + TE_WriteNum("m_iWeaponID", TE_ReadNum("m_iWeaponID")); + TE_WriteNum("m_iMode", TE_ReadNum("m_iMode")); + TE_WriteNum("m_iSeed", TE_ReadNum("m_iSeed")); + TE_WriteNum("m_iPlayer", TE_ReadNum("m_iPlayer")); + TE_WriteFloat("m_fInaccuracy", TE_ReadFloat("m_fInaccuracy")); + TE_WriteFloat("m_fSpread", TE_ReadFloat("m_fSpread")); + TE_Send(newClients, newTotal, delay); + + return Plugin_Stop; +} + +public Action:DODS_Hook_FireBullets(const String:te_name[], const Players[], numClients, Float:delay) +{ + if (!g_bHooked) + { + return Plugin_Continue; + } + + // Check which clients need to be excluded. + decl newClients[MaxClients], client, i; + new newTotal = 0; + + for (i = 0; i < numClients; i++) + { + client = Players[i]; + + if (!g_bStopSound[client]) + { + newClients[newTotal++] = client; + } + } + + // No clients were excluded. + if (newTotal == numClients) + { + return Plugin_Continue; + } + else if (newTotal == 0)// All clients were excluded and there is no need to broadcast. + { + return Plugin_Stop; + } + + // Re-broadcast to clients that still need it. + decl Float:vTemp[3]; + TE_Start("FireBullets"); + TE_ReadVector("m_vecOrigin", vTemp); + TE_WriteVector("m_vecOrigin", vTemp); + TE_WriteFloat("m_vecAngles[0]", TE_ReadFloat("m_vecAngles[0]")); + TE_WriteFloat("m_vecAngles[1]", TE_ReadFloat("m_vecAngles[1]")); + TE_WriteNum("m_iWeaponID", TE_ReadNum("m_iWeaponID")); + TE_WriteNum("m_iMode", TE_ReadNum("m_iMode")); + TE_WriteNum("m_iSeed", TE_ReadNum("m_iSeed")); + TE_WriteNum("m_iPlayer", TE_ReadNum("m_iPlayer")); + TE_WriteFloat("m_flSpread", TE_ReadFloat("m_flSpread")); + TE_Send(newClients, newTotal, delay); + + return Plugin_Stop; } \ No newline at end of file diff --git a/WeaponCleaner/scripting/WeaponCleaner.sp b/WeaponCleaner/scripting/WeaponCleaner.sp index 6388bb6d..ed58bc01 100644 --- a/WeaponCleaner/scripting/WeaponCleaner.sp +++ b/WeaponCleaner/scripting/WeaponCleaner.sp @@ -1,274 +1,274 @@ -#pragma semicolon 1 - -#include -#include -#include - -#define TIMER_INTERVAL 1.0 -Handle g_hTimer = INVALID_HANDLE; - -ConVar g_CVar_MaxWeapons; -ConVar g_CVar_WeaponLifetime; - -new g_RealRoundStartedTime; -new g_MaxWeapons; -new g_MaxWeaponLifetime; - -#define MAX_WEAPONS MAXPLAYERS -new G_WeaponArray[MAX_WEAPONS][2]; - - -public Plugin myinfo = -{ - name = "WeaponCleaner", - author = "BotoX", - description = "Clean unneeded weapons", - version = "2.0", - url = "" -}; - -public void OnPluginStart() -{ - RegAdminCmd("sm_sweep", Command_CleanupWeapons, ADMFLAG_GENERIC, "Cleans up all the weapons on the map unless they have a HammerID attached to them."); - - g_CVar_MaxWeapons = CreateConVar("sm_weaponcleaner_max", "5", "The maximum amount of weapons allowed in the game.", 0, true, 0.0, true, MAX_WEAPONS - 1.0); - g_MaxWeapons = g_CVar_MaxWeapons.IntValue; - g_CVar_MaxWeapons.AddChangeHook(OnConVarChanged); - - g_CVar_WeaponLifetime = CreateConVar("sm_weaponcleaner_lifetime", "15", "The maximum amount of time in seconds a weapon is allowed in the game.", 0, true, 0.0); - g_MaxWeaponLifetime = g_CVar_WeaponLifetime.IntValue; - g_CVar_WeaponLifetime.AddChangeHook(OnConVarChanged); - - HookEvent("round_start", Event_RoundStart); - - AutoExecConfig(true, "plugin.WeaponCleaner"); -} - -public void OnConVarChanged(ConVar convar, const char[] oldValue, const char[] newValue) -{ - if(convar == g_CVar_MaxWeapons) - { - if(StringToInt(newValue) < StringToInt(oldValue)) - { - // Need to shrink list and kill items - new d = StringToInt(oldValue) - StringToInt(newValue); - - // Kill items that don't have space anymore - for(new i = 0; d && i < g_MaxWeapons; i++) - { - if(!G_WeaponArray[i][0]) - continue; - - // Kill it - AcceptEntityInput(G_WeaponArray[0][0], "Kill"); - // This implicitly calls OnEntityDestroyed() which calls RemoveWeapon() - - // Move index backwards (since the list was modified by removing it) - i--; - d--; - } - } - g_MaxWeapons = StringToInt(newValue); - } - else if(convar == g_CVar_WeaponLifetime) - { - g_MaxWeaponLifetime = StringToInt(newValue); - CheckWeapons(); - } -} - -public void OnMapStart() -{ - if(g_hTimer != INVALID_HANDLE && CloseHandle(g_hTimer)) - g_hTimer = INVALID_HANDLE; - - g_hTimer = CreateTimer(TIMER_INTERVAL, Timer_CleanupWeapons, INVALID_HANDLE, TIMER_REPEAT); -} - -public void OnMapEnd() -{ - if(g_hTimer != INVALID_HANDLE && CloseHandle(g_hTimer)) - g_hTimer = INVALID_HANDLE; -} - -public void OnClientPutInServer(int client) -{ - SDKHook(client, SDKHook_WeaponDropPost, OnWeaponDrop); - SDKHook(client, SDKHook_WeaponEquipPost, OnWeaponEquip); -} - -public void OnClientDisconnect(int client) -{ - SDKUnhook(client, SDKHook_WeaponDropPost, OnWeaponDrop); - SDKUnhook(client, SDKHook_WeaponEquipPost, OnWeaponEquip); -} - -public void OnEntityCreated(int entity, const char[] classname) -{ - if(IsValidEntity(entity) && strncmp(classname, "weapon_", 7) == 0) - { - SDKHook(entity, SDKHook_Spawn, OnWeaponSpawned); - } -} - -public void OnEntityDestroyed(int entity) -{ - RemoveWeapon(entity); -} - -public void OnWeaponSpawned(int entity) -{ - new HammerID = GetEntProp(entity, Prop_Data, "m_iHammerID"); - // Should not be cleaned since it's a map spawned weapon - if(HammerID) - return; - - // Weapon doesn't belong to any player - if(GetEntPropEnt(entity, Prop_Data, "m_hOwnerEntity") == -1) - InsertWeapon(entity); -} - -public Action OnWeaponEquip(int client, int entity) -{ - if(!IsValidEntity(entity)) - return; - - new HammerID = GetEntProp(entity, Prop_Data, "m_iHammerID"); - // Should not be cleaned since it's a map spawned weapon - if(HammerID) - return; - - // Weapon should not be cleaned anymore - RemoveWeapon(entity); -} - -public Action OnWeaponDrop(int client, int entity) -{ - if(!IsValidEntity(entity)) - return; - - new HammerID = GetEntProp(entity, Prop_Data, "m_iHammerID"); - // Should not be cleaned since it's a map spawned weapon - if(HammerID) - return; - - // Kill all dropped weapons during mp_freezetime - if(GetTime() < g_RealRoundStartedTime) - { - // Kill it - AcceptEntityInput(entity, "Kill"); - return; - } - - // Weapon should be cleaned again - InsertWeapon(entity); -} - -bool InsertWeapon(int entity) -{ - // Try to find a free slot - for(new i = 0; i < g_MaxWeapons; i++) - { - if(G_WeaponArray[i][0]) - continue; - - // Found a free slot, add it here - G_WeaponArray[i][0] = entity; - G_WeaponArray[i][1] = GetTime(); - return true; - } - - // No free slot found - // Kill the first (oldest) item in the list - AcceptEntityInput(G_WeaponArray[0][0], "Kill"); - // This implicitly calls OnEntityDestroyed() which calls RemoveWeapon() - - // Add new weapon to the end of the list - G_WeaponArray[g_MaxWeapons - 1][0] = entity; - G_WeaponArray[g_MaxWeapons - 1][1] = GetTime(); - return true; -} - -bool RemoveWeapon(int entity) -{ - // Find the Weapon - for(new i = 0; i < g_MaxWeapons; i++) - { - if(G_WeaponArray[i][0] == entity) - { - G_WeaponArray[i][0] = 0; G_WeaponArray[i][1] = 0; - - // Move list items in front of this index back by one - for(new j = i + 1; j < g_MaxWeapons; j++) - { - G_WeaponArray[j - 1][0] = G_WeaponArray[j][0]; - G_WeaponArray[j - 1][1] = G_WeaponArray[j][1]; - } - - // Reset last list item - G_WeaponArray[g_MaxWeapons - 1][0] = 0; - G_WeaponArray[g_MaxWeapons - 1][1] = 0; - - return true; - } - } - return false; -} - -bool CheckWeapons() -{ - for(new i = 0; i < g_MaxWeapons; i++) - { - if(!G_WeaponArray[i][0]) - continue; - - if(GetTime() - G_WeaponArray[i][1] >= g_MaxWeaponLifetime) - { - // Kill it - AcceptEntityInput(G_WeaponArray[i][0], "Kill"); - // This implicitly calls OnEntityDestroyed() which calls RemoveWeapon() - - // Move index backwards (since the list was modified by removing it) - i--; - } - } - return true; -} - -void CleanupWeapons() -{ - for(new i = 0; i < g_MaxWeapons; i++) - { - if(!G_WeaponArray[i][0]) - continue; - - // Kill it - AcceptEntityInput(G_WeaponArray[i][0], "Kill"); - // This implicitly calls OnEntityDestroyed() which calls RemoveWeapon() - - // Move index backwards (since the list was modified by removing it) - i--; - } -} - -public Action Event_RoundStart(Handle:event, const char[] name, bool:dontBroadcast) -{ - for(new i = 0; i < MAX_WEAPONS; i++) - { - G_WeaponArray[i][0] = 0; G_WeaponArray[i][1] = 0; - } - g_RealRoundStartedTime = GetTime() + GetConVarInt(FindConVar("mp_freezetime")); -} - -public Action Timer_CleanupWeapons(Handle:timer) -{ - CheckWeapons(); -} - -public Action Command_CleanupWeapons(client, args) -{ - CleanupWeapons(); - - LogAction(client, -1, "%L performed a weapons cleanup", client); - PrintToChat(client, "[SM] Weapons cleaned successfully!"); -} +#pragma semicolon 1 + +#include +#include +#include + +#define TIMER_INTERVAL 1.0 +Handle g_hTimer = INVALID_HANDLE; + +ConVar g_CVar_MaxWeapons; +ConVar g_CVar_WeaponLifetime; + +new g_RealRoundStartedTime; +new g_MaxWeapons; +new g_MaxWeaponLifetime; + +#define MAX_WEAPONS MAXPLAYERS +new G_WeaponArray[MAX_WEAPONS][2]; + + +public Plugin myinfo = +{ + name = "WeaponCleaner", + author = "BotoX", + description = "Clean unneeded weapons", + version = "2.0", + url = "" +}; + +public void OnPluginStart() +{ + RegAdminCmd("sm_sweep", Command_CleanupWeapons, ADMFLAG_GENERIC, "Cleans up all the weapons on the map unless they have a HammerID attached to them."); + + g_CVar_MaxWeapons = CreateConVar("sm_weaponcleaner_max", "5", "The maximum amount of weapons allowed in the game.", 0, true, 0.0, true, MAX_WEAPONS - 1.0); + g_MaxWeapons = g_CVar_MaxWeapons.IntValue; + g_CVar_MaxWeapons.AddChangeHook(OnConVarChanged); + + g_CVar_WeaponLifetime = CreateConVar("sm_weaponcleaner_lifetime", "15", "The maximum amount of time in seconds a weapon is allowed in the game.", 0, true, 0.0); + g_MaxWeaponLifetime = g_CVar_WeaponLifetime.IntValue; + g_CVar_WeaponLifetime.AddChangeHook(OnConVarChanged); + + HookEvent("round_start", Event_RoundStart); + + AutoExecConfig(true, "plugin.WeaponCleaner"); +} + +public void OnConVarChanged(ConVar convar, const char[] oldValue, const char[] newValue) +{ + if(convar == g_CVar_MaxWeapons) + { + if(StringToInt(newValue) < StringToInt(oldValue)) + { + // Need to shrink list and kill items + new d = StringToInt(oldValue) - StringToInt(newValue); + + // Kill items that don't have space anymore + for(new i = 0; d && i < g_MaxWeapons; i++) + { + if(!G_WeaponArray[i][0]) + continue; + + // Kill it + AcceptEntityInput(G_WeaponArray[0][0], "Kill"); + // This implicitly calls OnEntityDestroyed() which calls RemoveWeapon() + + // Move index backwards (since the list was modified by removing it) + i--; + d--; + } + } + g_MaxWeapons = StringToInt(newValue); + } + else if(convar == g_CVar_WeaponLifetime) + { + g_MaxWeaponLifetime = StringToInt(newValue); + CheckWeapons(); + } +} + +public void OnMapStart() +{ + if(g_hTimer != INVALID_HANDLE && CloseHandle(g_hTimer)) + g_hTimer = INVALID_HANDLE; + + g_hTimer = CreateTimer(TIMER_INTERVAL, Timer_CleanupWeapons, INVALID_HANDLE, TIMER_REPEAT); +} + +public void OnMapEnd() +{ + if(g_hTimer != INVALID_HANDLE && CloseHandle(g_hTimer)) + g_hTimer = INVALID_HANDLE; +} + +public void OnClientPutInServer(int client) +{ + SDKHook(client, SDKHook_WeaponDropPost, OnWeaponDrop); + SDKHook(client, SDKHook_WeaponEquipPost, OnWeaponEquip); +} + +public void OnClientDisconnect(int client) +{ + SDKUnhook(client, SDKHook_WeaponDropPost, OnWeaponDrop); + SDKUnhook(client, SDKHook_WeaponEquipPost, OnWeaponEquip); +} + +public void OnEntityCreated(int entity, const char[] classname) +{ + if(IsValidEntity(entity) && strncmp(classname, "weapon_", 7) == 0) + { + SDKHook(entity, SDKHook_Spawn, OnWeaponSpawned); + } +} + +public void OnEntityDestroyed(int entity) +{ + RemoveWeapon(entity); +} + +public void OnWeaponSpawned(int entity) +{ + new HammerID = GetEntProp(entity, Prop_Data, "m_iHammerID"); + // Should not be cleaned since it's a map spawned weapon + if(HammerID) + return; + + // Weapon doesn't belong to any player + if(GetEntPropEnt(entity, Prop_Data, "m_hOwnerEntity") == -1) + InsertWeapon(entity); +} + +public Action OnWeaponEquip(int client, int entity) +{ + if(!IsValidEntity(entity)) + return; + + new HammerID = GetEntProp(entity, Prop_Data, "m_iHammerID"); + // Should not be cleaned since it's a map spawned weapon + if(HammerID) + return; + + // Weapon should not be cleaned anymore + RemoveWeapon(entity); +} + +public Action OnWeaponDrop(int client, int entity) +{ + if(!IsValidEntity(entity)) + return; + + new HammerID = GetEntProp(entity, Prop_Data, "m_iHammerID"); + // Should not be cleaned since it's a map spawned weapon + if(HammerID) + return; + + // Kill all dropped weapons during mp_freezetime + if(GetTime() < g_RealRoundStartedTime) + { + // Kill it + AcceptEntityInput(entity, "Kill"); + return; + } + + // Weapon should be cleaned again + InsertWeapon(entity); +} + +bool InsertWeapon(int entity) +{ + // Try to find a free slot + for(new i = 0; i < g_MaxWeapons; i++) + { + if(G_WeaponArray[i][0]) + continue; + + // Found a free slot, add it here + G_WeaponArray[i][0] = entity; + G_WeaponArray[i][1] = GetTime(); + return true; + } + + // No free slot found + // Kill the first (oldest) item in the list + AcceptEntityInput(G_WeaponArray[0][0], "Kill"); + // This implicitly calls OnEntityDestroyed() which calls RemoveWeapon() + + // Add new weapon to the end of the list + G_WeaponArray[g_MaxWeapons - 1][0] = entity; + G_WeaponArray[g_MaxWeapons - 1][1] = GetTime(); + return true; +} + +bool RemoveWeapon(int entity) +{ + // Find the Weapon + for(new i = 0; i < g_MaxWeapons; i++) + { + if(G_WeaponArray[i][0] == entity) + { + G_WeaponArray[i][0] = 0; G_WeaponArray[i][1] = 0; + + // Move list items in front of this index back by one + for(new j = i + 1; j < g_MaxWeapons; j++) + { + G_WeaponArray[j - 1][0] = G_WeaponArray[j][0]; + G_WeaponArray[j - 1][1] = G_WeaponArray[j][1]; + } + + // Reset last list item + G_WeaponArray[g_MaxWeapons - 1][0] = 0; + G_WeaponArray[g_MaxWeapons - 1][1] = 0; + + return true; + } + } + return false; +} + +bool CheckWeapons() +{ + for(new i = 0; i < g_MaxWeapons; i++) + { + if(!G_WeaponArray[i][0]) + continue; + + if(GetTime() - G_WeaponArray[i][1] >= g_MaxWeaponLifetime) + { + // Kill it + AcceptEntityInput(G_WeaponArray[i][0], "Kill"); + // This implicitly calls OnEntityDestroyed() which calls RemoveWeapon() + + // Move index backwards (since the list was modified by removing it) + i--; + } + } + return true; +} + +void CleanupWeapons() +{ + for(new i = 0; i < g_MaxWeapons; i++) + { + if(!G_WeaponArray[i][0]) + continue; + + // Kill it + AcceptEntityInput(G_WeaponArray[i][0], "Kill"); + // This implicitly calls OnEntityDestroyed() which calls RemoveWeapon() + + // Move index backwards (since the list was modified by removing it) + i--; + } +} + +public Action Event_RoundStart(Handle:event, const char[] name, bool:dontBroadcast) +{ + for(new i = 0; i < MAX_WEAPONS; i++) + { + G_WeaponArray[i][0] = 0; G_WeaponArray[i][1] = 0; + } + g_RealRoundStartedTime = GetTime() + GetConVarInt(FindConVar("mp_freezetime")); +} + +public Action Timer_CleanupWeapons(Handle:timer) +{ + CheckWeapons(); +} + +public Action Command_CleanupWeapons(client, args) +{ + CleanupWeapons(); + + LogAction(client, -1, "%L performed a weapons cleanup", client); + PrintToChat(client, "[SM] Weapons cleaned successfully!"); +} diff --git a/custom-chatcolors/scripting/include/ccc.inc b/custom-chatcolors/scripting/include/ccc.inc index 10480068..6c65eaad 100644 --- a/custom-chatcolors/scripting/include/ccc.inc +++ b/custom-chatcolors/scripting/include/ccc.inc @@ -1,190 +1,190 @@ -/** - * This is the include file for Custom Chat Colors - * https://forums.alliedmods.net/showthread.php?t=186695 - * To check that Custom Chat Colors is installed and running, verify that the "ccc" library exists - */ - -#if defined _ccc_included - #endinput -#endif -#define _ccc_included - -enum CCC_ColorType { - CCC_TagColor, - CCC_NameColor, - CCC_ChatColor -}; - -#define COLOR_NULL -1 -#define COLOR_NONE -2 -#define COLOR_CGREEN -3 //0x40FF40 -#define COLOR_OLIVE -4 //0x99FF99 -#define COLOR_TEAM -5 - -/** - * Gets a client's color as a hexadecimal integer. - * - * @param client Client index - * @param type Color type to retreive - * @param alpha Pass a boolean variable by reference here and it will be true if the color has alpha specified or false if it doesn't (or is a stock color) - * @return Color as a hexadecimal integer (use %X in formatting to get a hexadecimal string) - * - * On error/errors: Invalid client index or client is not in game - */ -native CCC_GetColor(client, CCC_ColorType:type, &bool:alpha = false); - -/** - * Sets a client's color as a hexadecimal integer. - * - * @param client Client index - * @param type Color type to set - * @param color Integer representation of the color (use StringToInt(input, 16) to convert a hexadecimal string) or one of the color defines - * @param alpha Are you specifying a color with alpha? - * @return True if the color is updated successfully, false otherwise - * - * On error/errors: Invalid client index or client is not in game - */ -native bool:CCC_SetColor(client, CCC_ColorType:type, color, bool:alpha); - -/** - * Gets a client's tag - * - * @param client Client index - * @param buffer Buffer to store the tag in - * @param maxlen Maximum buffer length - * @noreturn - * - * On error/errors: Invalid client index or client is not in game - */ -native CCC_GetTag(client, String:buffer[], maxlen); - -/** - * Sets a client's tag - * - * @param client Client index - * @param tag String containing the new tag - * @noreturn - * - * On error/errors: Invalid client index or client is not in game - */ -native CCC_SetTag(client, const String:tag[]); - -/** - * Resets a client's color to the value in the config file. - * - * @param client Client index - * @param type Color type to restore - * @noreturn - * - * On error/errors: Invalid client index or client is not in game - */ -native CCC_ResetColor(client, CCC_ColorType:type); - -/** - * Resets a client's tag to the value in the config file. - * - * @param client Client index - * @noreturn - * - * On error/errors: Invalid client index or client is not in game - */ -native CCC_ResetTag(client); - -/** - * Called when a cilent's name is about to be colored - * DO NOT START A NEW USERMESSAGE (i.e. PrintToChat, PrintToChatAll) WITHIN THIS FORWARD - * - * @param client Client index - * @return Plugin_Handled to prevent coloring, Plugin_Continue to allow coloring - */ -#pragma deprecated Use CCC_OnColor instead -forward Action:CCC_OnNameColor(client); - -/** - * Called when a client's chat is about to be colored - * DO NOT START A NEW USERMESSAGE (i.e. PrintToChat, PrintToChatAll) WITHIN THIS FORWARD - * - * @param client Client index - * @return Plugin_Handled to prevent coloring, Plugin_Continue to allow coloring - */ -#pragma deprecated Use CCC_OnColor instead -forward Action:CCC_OnChatColor(client); - -/** - * Called when a client's name is about to be tagged - * DO NOT START A NEW USERMESSAGE (i.e. PrintToChat, PrintToChatAll) WITHIN THIS FORWARD - * - * @param client Client index - * @return Plugin_Handled to prevent tagging, Plugin_Continue to allow tagging - */ -#pragma deprecated Use CCC_OnColor instead -forward Action:CCC_OnTagApplied(client); - -/** - * Called when a client's name is about to be tagged - * DO NOT START A NEW USERMESSAGE (i.e. PrintToChat, PrintToChatAll) WITHIN THIS FORWARD - * - * @param client Client index - * @param message Chat message that will be printed - * @param type What type of color will be applied. If this is CCC_TagColor, it controls whether the tag will be applied at all, not whether the tag will be colored. - * @return Plugin_Handled to prevent coloring, Plugin_Continue to allow coloring - */ -forward Action:CCC_OnColor(client, const String:message[], CCC_ColorType:type); - -/** - * Called when a message has been fully colored and will be sent, unless further plugins modify it through Simple Chat Processor - * - * @param author Author client index - * @param message Message - * @param maxlen Maximum length of message buffer - * @noreturn - */ -forward CCC_OnChatMessage(author, String:message[], maxlen); - -/** - * Called when a client's colors and tag are about to be loaded from the config file - * At this point, the client has NO COLORS - * - * @param client Client index - * @return Plugin_Handled or Plugin_Stop to prevent loading, Plugin_Continue or Plugin_Changed to allow - */ -forward Action:CCC_OnUserConfigPreLoaded(client); - -/** - * Called when a client's colors and tag have been loaded from the config file - * - * @param client Client index - * @noreturn - */ -forward CCC_OnUserConfigLoaded(client); - -/** - * Called when the configuration file is reloaded with the sm_reloadccc command - * - * @noreturn - */ -forward CCC_OnConfigReloaded(); - -native void CCC_UpdateIgnoredArray(bool IgnoredArray[(MAXPLAYERS + 1) * (MAXPLAYERS + 1)]); - -public SharedPlugin:__pl_ccc = { - name = "ccc", - file = "custom-chatcolors.smx", -#if defined REQUIRE_PLUGIN - required = 1 -#else - required = 0 -#endif -}; - -#if !defined REQUIRE_PLUGIN -public __pl_ccc_SetNTVOptional() { - MarkNativeAsOptional("CCC_GetColor"); - MarkNativeAsOptional("CCC_SetColor"); - MarkNativeAsOptional("CCC_GetTag"); - MarkNativeAsOptional("CCC_ResetTag"); - MarkNativeAsOptional("CCC_ResetColor"); - MarkNativeAsOptional("CCC_ResetTag"); - MarkNativeAsOptional("CCC_UpdateIgnoredArray"); -} +/** + * This is the include file for Custom Chat Colors + * https://forums.alliedmods.net/showthread.php?t=186695 + * To check that Custom Chat Colors is installed and running, verify that the "ccc" library exists + */ + +#if defined _ccc_included + #endinput +#endif +#define _ccc_included + +enum CCC_ColorType { + CCC_TagColor, + CCC_NameColor, + CCC_ChatColor +}; + +#define COLOR_NULL -1 +#define COLOR_NONE -2 +#define COLOR_CGREEN -3 //0x40FF40 +#define COLOR_OLIVE -4 //0x99FF99 +#define COLOR_TEAM -5 + +/** + * Gets a client's color as a hexadecimal integer. + * + * @param client Client index + * @param type Color type to retreive + * @param alpha Pass a boolean variable by reference here and it will be true if the color has alpha specified or false if it doesn't (or is a stock color) + * @return Color as a hexadecimal integer (use %X in formatting to get a hexadecimal string) + * + * On error/errors: Invalid client index or client is not in game + */ +native CCC_GetColor(client, CCC_ColorType:type, &bool:alpha = false); + +/** + * Sets a client's color as a hexadecimal integer. + * + * @param client Client index + * @param type Color type to set + * @param color Integer representation of the color (use StringToInt(input, 16) to convert a hexadecimal string) or one of the color defines + * @param alpha Are you specifying a color with alpha? + * @return True if the color is updated successfully, false otherwise + * + * On error/errors: Invalid client index or client is not in game + */ +native bool:CCC_SetColor(client, CCC_ColorType:type, color, bool:alpha); + +/** + * Gets a client's tag + * + * @param client Client index + * @param buffer Buffer to store the tag in + * @param maxlen Maximum buffer length + * @noreturn + * + * On error/errors: Invalid client index or client is not in game + */ +native CCC_GetTag(client, String:buffer[], maxlen); + +/** + * Sets a client's tag + * + * @param client Client index + * @param tag String containing the new tag + * @noreturn + * + * On error/errors: Invalid client index or client is not in game + */ +native CCC_SetTag(client, const String:tag[]); + +/** + * Resets a client's color to the value in the config file. + * + * @param client Client index + * @param type Color type to restore + * @noreturn + * + * On error/errors: Invalid client index or client is not in game + */ +native CCC_ResetColor(client, CCC_ColorType:type); + +/** + * Resets a client's tag to the value in the config file. + * + * @param client Client index + * @noreturn + * + * On error/errors: Invalid client index or client is not in game + */ +native CCC_ResetTag(client); + +/** + * Called when a cilent's name is about to be colored + * DO NOT START A NEW USERMESSAGE (i.e. PrintToChat, PrintToChatAll) WITHIN THIS FORWARD + * + * @param client Client index + * @return Plugin_Handled to prevent coloring, Plugin_Continue to allow coloring + */ +#pragma deprecated Use CCC_OnColor instead +forward Action:CCC_OnNameColor(client); + +/** + * Called when a client's chat is about to be colored + * DO NOT START A NEW USERMESSAGE (i.e. PrintToChat, PrintToChatAll) WITHIN THIS FORWARD + * + * @param client Client index + * @return Plugin_Handled to prevent coloring, Plugin_Continue to allow coloring + */ +#pragma deprecated Use CCC_OnColor instead +forward Action:CCC_OnChatColor(client); + +/** + * Called when a client's name is about to be tagged + * DO NOT START A NEW USERMESSAGE (i.e. PrintToChat, PrintToChatAll) WITHIN THIS FORWARD + * + * @param client Client index + * @return Plugin_Handled to prevent tagging, Plugin_Continue to allow tagging + */ +#pragma deprecated Use CCC_OnColor instead +forward Action:CCC_OnTagApplied(client); + +/** + * Called when a client's name is about to be tagged + * DO NOT START A NEW USERMESSAGE (i.e. PrintToChat, PrintToChatAll) WITHIN THIS FORWARD + * + * @param client Client index + * @param message Chat message that will be printed + * @param type What type of color will be applied. If this is CCC_TagColor, it controls whether the tag will be applied at all, not whether the tag will be colored. + * @return Plugin_Handled to prevent coloring, Plugin_Continue to allow coloring + */ +forward Action:CCC_OnColor(client, const String:message[], CCC_ColorType:type); + +/** + * Called when a message has been fully colored and will be sent, unless further plugins modify it through Simple Chat Processor + * + * @param author Author client index + * @param message Message + * @param maxlen Maximum length of message buffer + * @noreturn + */ +forward CCC_OnChatMessage(author, String:message[], maxlen); + +/** + * Called when a client's colors and tag are about to be loaded from the config file + * At this point, the client has NO COLORS + * + * @param client Client index + * @return Plugin_Handled or Plugin_Stop to prevent loading, Plugin_Continue or Plugin_Changed to allow + */ +forward Action:CCC_OnUserConfigPreLoaded(client); + +/** + * Called when a client's colors and tag have been loaded from the config file + * + * @param client Client index + * @noreturn + */ +forward CCC_OnUserConfigLoaded(client); + +/** + * Called when the configuration file is reloaded with the sm_reloadccc command + * + * @noreturn + */ +forward CCC_OnConfigReloaded(); + +native void CCC_UpdateIgnoredArray(bool IgnoredArray[(MAXPLAYERS + 1) * (MAXPLAYERS + 1)]); + +public SharedPlugin:__pl_ccc = { + name = "ccc", + file = "custom-chatcolors.smx", +#if defined REQUIRE_PLUGIN + required = 1 +#else + required = 0 +#endif +}; + +#if !defined REQUIRE_PLUGIN +public __pl_ccc_SetNTVOptional() { + MarkNativeAsOptional("CCC_GetColor"); + MarkNativeAsOptional("CCC_SetColor"); + MarkNativeAsOptional("CCC_GetTag"); + MarkNativeAsOptional("CCC_ResetTag"); + MarkNativeAsOptional("CCC_ResetColor"); + MarkNativeAsOptional("CCC_ResetTag"); + MarkNativeAsOptional("CCC_UpdateIgnoredArray"); +} #endif \ No newline at end of file diff --git a/includes/colors.inc b/includes/colors.inc index 523f6f6a..3d489243 100644 --- a/includes/colors.inc +++ b/includes/colors.inc @@ -1,539 +1,539 @@ -/************************************************************************** - * * - * Colored Chat Functions * - * Author: exvel, Editor: Popoklopsi, Powerlord, Bara * - * Version: 1.1.3 * - * * - **************************************************************************/ - - -#if defined _colors_included - #endinput -#endif -#define _colors_included - -#define MAX_MESSAGE_LENGTH 250 -#define MAX_COLORS 12 - -#define SERVER_INDEX 0 -#define NO_INDEX -1 -#define NO_PLAYER -2 - -enum 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_Orange -} - -/* Colors' properties */ -new String:CTag[][] = {"{default}", "{darkred}", "{green}", "{lightgreen}", "{red}", "{blue}", "{olive}", "{lime}", "{lightred}", "{purple}", "{grey}", "{orange}"}; -new String:CTagCode[][] = {"\x01", "\x02", "\x04", "\x03", "\x03", "\x03", "\x05", "\x06", "\x07", "\x03", "\x08", "\x09"}; -new bool:CTagReqSayText2[] = {false, false, false, true, true, true, false, false, false, false, false, false}; -new bool:CEventIsHooked = false; -new bool:CSkipList[MAXPLAYERS+1] = {false,...}; - -/* Game default profile */ -new bool:CProfile_Colors[] = {true, false, true, false, false, false, false, false, false, false, false, false}; -new CProfile_TeamIndex[] = {NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX}; -new bool:CProfile_SayText2 = false; - -/** - * 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 CPrintToChat(client, const String:szMessage[], any:...) -{ - if (client <= 0 || client > MaxClients) - ThrowError("Invalid client index %d", client); - - if (!IsClientInGame(client)) - ThrowError("Client %d is not in game", client); - - decl String:szBuffer[MAX_MESSAGE_LENGTH]; - decl String:szCMessage[MAX_MESSAGE_LENGTH]; - - SetGlobalTransTarget(client); - - Format(szBuffer, sizeof(szBuffer), "\x01%s", szMessage); - VFormat(szCMessage, sizeof(szCMessage), szBuffer, 3); - - new index = CFormat(szCMessage, sizeof(szCMessage)); - - if (index == NO_INDEX) - PrintToChat(client, "%s", szCMessage); - else - CSayText2(client, index, szCMessage); -} - -stock CReplyToCommand(client, const String:szMessage[], any:...) -{ - - decl String:szCMessage[MAX_MESSAGE_LENGTH]; - VFormat(szCMessage, sizeof(szCMessage), szMessage, 3); - - if (client == 0) - { - CRemoveTags(szCMessage, sizeof(szCMessage)); - PrintToServer("%s", szCMessage); - } - else if (GetCmdReplySource() == SM_REPLY_TO_CONSOLE) - { - CRemoveTags(szCMessage, sizeof(szCMessage)); - PrintToConsole(client, "%s", szCMessage); - } - else - { - CPrintToChat(client, "%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 CPrintToChatAll(const String:szMessage[], any:...) -{ - decl String:szBuffer[MAX_MESSAGE_LENGTH]; - - for (new i = 1; i <= MaxClients; i++) - { - if (IsClientInGame(i) && !IsFakeClient(i) && !CSkipList[i]) - { - SetGlobalTransTarget(i); - VFormat(szBuffer, sizeof(szBuffer), szMessage, 2); - - CPrintToChat(i, "%s", szBuffer); - } - - CSkipList[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 CPrintToChatEx(client, author, const String: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); - - decl String:szBuffer[MAX_MESSAGE_LENGTH]; - decl String:szCMessage[MAX_MESSAGE_LENGTH]; - - SetGlobalTransTarget(client); - - Format(szBuffer, sizeof(szBuffer), "\x01%s", szMessage); - VFormat(szCMessage, sizeof(szCMessage), szBuffer, 4); - - new index = CFormat(szCMessage, sizeof(szCMessage), author); - - if (index == NO_INDEX) - PrintToChat(client, "%s", szCMessage); - else - CSayText2(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 CPrintToChatAllEx(author, const String:szMessage[], any:...) -{ - if (author < 0 || author > MaxClients) - ThrowError("Invalid client index %d", author); - - if (!IsClientInGame(author)) - ThrowError("Client %d is not in game", author); - - decl String:szBuffer[MAX_MESSAGE_LENGTH]; - - for (new i = 1; i <= MaxClients; i++) - { - if (IsClientInGame(i) && !IsFakeClient(i) && !CSkipList[i]) - { - SetGlobalTransTarget(i); - VFormat(szBuffer, sizeof(szBuffer), szMessage, 3); - - CPrintToChatEx(i, author, "%s", szBuffer); - } - - CSkipList[i] = false; - } -} - -/** - * Removes color tags from the string. - * - * @param szMessage String. - * @return No return - */ -stock CRemoveTags(String:szMessage[], maxlength) -{ - for (new i = 0; i < MAX_COLORS; i++) - ReplaceString(szMessage, maxlength, CTag[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 CColorAllowed(Colors:color) -{ - if (!CEventIsHooked) - { - CSetupProfile(); - - CEventIsHooked = true; - } - - return CProfile_Colors[color]; -} - -/** - * Replace the color with another color - * Handle with care! - * - * @param color color to replace. - * @param newColor color to replace with. - * @noreturn - */ -stock CReplaceColor(Colors:color, Colors:newColor) -{ - if (!CEventIsHooked) - { - CSetupProfile(); - - CEventIsHooked = true; - } - - CProfile_Colors[color] = CProfile_Colors[newColor]; - CProfile_TeamIndex[color] = CProfile_TeamIndex[newColor]; - - CTagReqSayText2[color] = CTagReqSayText2[newColor]; - Format(CTagCode[color], sizeof(CTagCode[]), CTagCode[newColor]) -} - -/** - * This function should only be used right in front of - * CPrintToChatAll or CPrintToChatAllEx 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 CSkipNextClient(client) -{ - if (client <= 0 || client > MaxClients) - ThrowError("Invalid client index %d", client); - - CSkipList[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 CFormat(String:szMessage[], maxlength, author=NO_INDEX) -{ - decl String:szGameName[30]; - - GetGameFolderName(szGameName, sizeof(szGameName)); - - /* Hook event for auto profile setup on map start */ - if (!CEventIsHooked) - { - CSetupProfile(); - HookEvent("server_spawn", CEvent_MapStart, EventHookMode_PostNoCopy); - - CEventIsHooked = true; - } - - new iRandomPlayer = NO_INDEX; - - // On CS:GO set invisible precolor - if (StrEqual(szGameName, "csgo", false)) - Format(szMessage, maxlength, " \x01\x0B\x01%s", szMessage); - - /* If author was specified replace {teamcolor} tag */ - if (author != NO_INDEX) - { - if (CProfile_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}", CTagCode[Color_Green], false); - } - else - ReplaceString(szMessage, maxlength, "{teamcolor}", "", false); - - /* For other color tags we need a loop */ - for (new i = 0; i < MAX_COLORS; i++) - { - /* If tag not found - skip */ - if (StrContains(szMessage, CTag[i], false) == -1) - continue; - - /* If tag is not supported by game replace it with green tag */ - else if (!CProfile_Colors[i]) - ReplaceString(szMessage, maxlength, CTag[i], CTagCode[Color_Green], false); - - /* If tag doesn't need saytext2 simply replace */ - else if (!CTagReqSayText2[i]) - ReplaceString(szMessage, maxlength, CTag[i], CTagCode[i], false); - - /* Tag needs saytext2 */ - else - { - /* If saytext2 is not supported by game replace tag with green tag */ - if (!CProfile_SayText2) - ReplaceString(szMessage, maxlength, CTag[i], CTagCode[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 = CFindRandomPlayerByTeam(CProfile_TeamIndex[i]); - - /* If player not found replace tag with green color tag */ - if (iRandomPlayer == NO_PLAYER) - ReplaceString(szMessage, maxlength, CTag[i], CTagCode[Color_Green], false); - - /* If player was found simply replace */ - else - ReplaceString(szMessage, maxlength, CTag[i], CTagCode[i], false); - - } - /* If found another team color tag throw error */ - else - { - //ReplaceString(szMessage, maxlength, CTag[i], ""); - ThrowError("Using two team colors in one message is not allowed"); - } - } - - } - } - - return iRandomPlayer; -} - -/** - * Founds a random player with specified team - * - * @param color_team Client team. - * @return Client index or NO_PLAYER if no player found - */ -stock CFindRandomPlayerByTeam(color_team) -{ - if (color_team == SERVER_INDEX) - return 0; - else - { - for (new i = 1; i <= MaxClients; i++) - { - if (IsClientInGame(i) && GetClientTeam(i) == color_team) - return i; - } - } - - 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 CSayText2(client, author, const String:szMessage[]) -{ - new Handle:hBuffer = StartMessageOne("SayText2", client, USERMSG_RELIABLE|USERMSG_BLOCKHOOKS); - - if(GetFeatureStatus(FeatureType_Native, "GetUserMessageType") == FeatureStatus_Available && GetUserMessageType() == UM_Protobuf) - { - PbSetInt(hBuffer, "ent_idx", author); - PbSetBool(hBuffer, "chat", true); - PbSetString(hBuffer, "msg_name", szMessage); - PbAddString(hBuffer, "params", ""); - PbAddString(hBuffer, "params", ""); - PbAddString(hBuffer, "params", ""); - PbAddString(hBuffer, "params", ""); - } - else - { - BfWriteByte(hBuffer, author); - BfWriteByte(hBuffer, true); - BfWriteString(hBuffer, szMessage); - } - - EndMessage(); -} - -/** - * Creates game color profile - * This function must be edited if you want to add more games support - * - * @return No return. - */ -stock CSetupProfile() -{ - decl String:szGameName[30]; - GetGameFolderName(szGameName, sizeof(szGameName)); - - if (StrEqual(szGameName, "cstrike", false)) - { - CProfile_Colors[Color_Lightgreen] = true; - CProfile_Colors[Color_Red] = true; - CProfile_Colors[Color_Blue] = true; - CProfile_Colors[Color_Olive] = true; - CProfile_TeamIndex[Color_Lightgreen] = SERVER_INDEX; - CProfile_TeamIndex[Color_Red] = 2; - CProfile_TeamIndex[Color_Blue] = 3; - CProfile_SayText2 = true; - } - else if (StrEqual(szGameName, "csgo", false)) - { - CProfile_Colors[Color_Red] = true; - CProfile_Colors[Color_Blue] = true; - CProfile_Colors[Color_Olive] = true; - CProfile_Colors[Color_Darkred] = true; - CProfile_Colors[Color_Lime] = true; - CProfile_Colors[Color_Lightred] = true; - CProfile_Colors[Color_Purple] = true; - CProfile_Colors[Color_Grey] = true; - CProfile_Colors[Color_Orange] = true; - CProfile_TeamIndex[Color_Red] = 2; - CProfile_TeamIndex[Color_Blue] = 3; - CProfile_SayText2 = true; - } - else if (StrEqual(szGameName, "tf", false)) - { - CProfile_Colors[Color_Lightgreen] = true; - CProfile_Colors[Color_Red] = true; - CProfile_Colors[Color_Blue] = true; - CProfile_Colors[Color_Olive] = true; - CProfile_TeamIndex[Color_Lightgreen] = SERVER_INDEX; - CProfile_TeamIndex[Color_Red] = 2; - CProfile_TeamIndex[Color_Blue] = 3; - CProfile_SayText2 = true; - } - else if (StrEqual(szGameName, "left4dead", false) || StrEqual(szGameName, "left4dead2", false)) - { - CProfile_Colors[Color_Lightgreen] = true; - CProfile_Colors[Color_Red] = true; - CProfile_Colors[Color_Blue] = true; - CProfile_Colors[Color_Olive] = true; - CProfile_TeamIndex[Color_Lightgreen] = SERVER_INDEX; - CProfile_TeamIndex[Color_Red] = 3; - CProfile_TeamIndex[Color_Blue] = 2; - CProfile_SayText2 = true; - } - else if (StrEqual(szGameName, "hl2mp", false)) - { - /* hl2mp profile is based on mp_teamplay convar */ - if (GetConVarBool(FindConVar("mp_teamplay"))) - { - CProfile_Colors[Color_Red] = true; - CProfile_Colors[Color_Blue] = true; - CProfile_Colors[Color_Olive] = true; - CProfile_TeamIndex[Color_Red] = 3; - CProfile_TeamIndex[Color_Blue] = 2; - CProfile_SayText2 = true; - } - else - { - CProfile_SayText2 = false; - CProfile_Colors[Color_Olive] = true; - } - } - else if (StrEqual(szGameName, "dod", false)) - { - CProfile_Colors[Color_Olive] = true; - CProfile_SayText2 = false; - } - /* Profile for other games */ - else - { - if (GetUserMessageId("SayText2") == INVALID_MESSAGE_ID) - { - CProfile_SayText2 = false; - } - else - { - CProfile_Colors[Color_Red] = true; - CProfile_Colors[Color_Blue] = true; - CProfile_TeamIndex[Color_Red] = 2; - CProfile_TeamIndex[Color_Blue] = 3; - CProfile_SayText2 = true; - } - } -} - -public Action:CEvent_MapStart(Handle:event, const String:name[], bool:dontBroadcast) -{ - CSetupProfile(); - - for (new i = 1; i <= MaxClients; i++) - CSkipList[i] = false; +/************************************************************************** + * * + * Colored Chat Functions * + * Author: exvel, Editor: Popoklopsi, Powerlord, Bara * + * Version: 1.1.3 * + * * + **************************************************************************/ + + +#if defined _colors_included + #endinput +#endif +#define _colors_included + +#define MAX_MESSAGE_LENGTH 250 +#define MAX_COLORS 12 + +#define SERVER_INDEX 0 +#define NO_INDEX -1 +#define NO_PLAYER -2 + +enum 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_Orange +} + +/* Colors' properties */ +new String:CTag[][] = {"{default}", "{darkred}", "{green}", "{lightgreen}", "{red}", "{blue}", "{olive}", "{lime}", "{lightred}", "{purple}", "{grey}", "{orange}"}; +new String:CTagCode[][] = {"\x01", "\x02", "\x04", "\x03", "\x03", "\x03", "\x05", "\x06", "\x07", "\x03", "\x08", "\x09"}; +new bool:CTagReqSayText2[] = {false, false, false, true, true, true, false, false, false, false, false, false}; +new bool:CEventIsHooked = false; +new bool:CSkipList[MAXPLAYERS+1] = {false,...}; + +/* Game default profile */ +new bool:CProfile_Colors[] = {true, false, true, false, false, false, false, false, false, false, false, false}; +new CProfile_TeamIndex[] = {NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX}; +new bool:CProfile_SayText2 = false; + +/** + * 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 CPrintToChat(client, const String:szMessage[], any:...) +{ + if (client <= 0 || client > MaxClients) + ThrowError("Invalid client index %d", client); + + if (!IsClientInGame(client)) + ThrowError("Client %d is not in game", client); + + decl String:szBuffer[MAX_MESSAGE_LENGTH]; + decl String:szCMessage[MAX_MESSAGE_LENGTH]; + + SetGlobalTransTarget(client); + + Format(szBuffer, sizeof(szBuffer), "\x01%s", szMessage); + VFormat(szCMessage, sizeof(szCMessage), szBuffer, 3); + + new index = CFormat(szCMessage, sizeof(szCMessage)); + + if (index == NO_INDEX) + PrintToChat(client, "%s", szCMessage); + else + CSayText2(client, index, szCMessage); +} + +stock CReplyToCommand(client, const String:szMessage[], any:...) +{ + + decl String:szCMessage[MAX_MESSAGE_LENGTH]; + VFormat(szCMessage, sizeof(szCMessage), szMessage, 3); + + if (client == 0) + { + CRemoveTags(szCMessage, sizeof(szCMessage)); + PrintToServer("%s", szCMessage); + } + else if (GetCmdReplySource() == SM_REPLY_TO_CONSOLE) + { + CRemoveTags(szCMessage, sizeof(szCMessage)); + PrintToConsole(client, "%s", szCMessage); + } + else + { + CPrintToChat(client, "%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 CPrintToChatAll(const String:szMessage[], any:...) +{ + decl String:szBuffer[MAX_MESSAGE_LENGTH]; + + for (new i = 1; i <= MaxClients; i++) + { + if (IsClientInGame(i) && !IsFakeClient(i) && !CSkipList[i]) + { + SetGlobalTransTarget(i); + VFormat(szBuffer, sizeof(szBuffer), szMessage, 2); + + CPrintToChat(i, "%s", szBuffer); + } + + CSkipList[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 CPrintToChatEx(client, author, const String: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); + + decl String:szBuffer[MAX_MESSAGE_LENGTH]; + decl String:szCMessage[MAX_MESSAGE_LENGTH]; + + SetGlobalTransTarget(client); + + Format(szBuffer, sizeof(szBuffer), "\x01%s", szMessage); + VFormat(szCMessage, sizeof(szCMessage), szBuffer, 4); + + new index = CFormat(szCMessage, sizeof(szCMessage), author); + + if (index == NO_INDEX) + PrintToChat(client, "%s", szCMessage); + else + CSayText2(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 CPrintToChatAllEx(author, const String:szMessage[], any:...) +{ + if (author < 0 || author > MaxClients) + ThrowError("Invalid client index %d", author); + + if (!IsClientInGame(author)) + ThrowError("Client %d is not in game", author); + + decl String:szBuffer[MAX_MESSAGE_LENGTH]; + + for (new i = 1; i <= MaxClients; i++) + { + if (IsClientInGame(i) && !IsFakeClient(i) && !CSkipList[i]) + { + SetGlobalTransTarget(i); + VFormat(szBuffer, sizeof(szBuffer), szMessage, 3); + + CPrintToChatEx(i, author, "%s", szBuffer); + } + + CSkipList[i] = false; + } +} + +/** + * Removes color tags from the string. + * + * @param szMessage String. + * @return No return + */ +stock CRemoveTags(String:szMessage[], maxlength) +{ + for (new i = 0; i < MAX_COLORS; i++) + ReplaceString(szMessage, maxlength, CTag[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 CColorAllowed(Colors:color) +{ + if (!CEventIsHooked) + { + CSetupProfile(); + + CEventIsHooked = true; + } + + return CProfile_Colors[color]; +} + +/** + * Replace the color with another color + * Handle with care! + * + * @param color color to replace. + * @param newColor color to replace with. + * @noreturn + */ +stock CReplaceColor(Colors:color, Colors:newColor) +{ + if (!CEventIsHooked) + { + CSetupProfile(); + + CEventIsHooked = true; + } + + CProfile_Colors[color] = CProfile_Colors[newColor]; + CProfile_TeamIndex[color] = CProfile_TeamIndex[newColor]; + + CTagReqSayText2[color] = CTagReqSayText2[newColor]; + Format(CTagCode[color], sizeof(CTagCode[]), CTagCode[newColor]) +} + +/** + * This function should only be used right in front of + * CPrintToChatAll or CPrintToChatAllEx 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 CSkipNextClient(client) +{ + if (client <= 0 || client > MaxClients) + ThrowError("Invalid client index %d", client); + + CSkipList[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 CFormat(String:szMessage[], maxlength, author=NO_INDEX) +{ + decl String:szGameName[30]; + + GetGameFolderName(szGameName, sizeof(szGameName)); + + /* Hook event for auto profile setup on map start */ + if (!CEventIsHooked) + { + CSetupProfile(); + HookEvent("server_spawn", CEvent_MapStart, EventHookMode_PostNoCopy); + + CEventIsHooked = true; + } + + new iRandomPlayer = NO_INDEX; + + // On CS:GO set invisible precolor + if (StrEqual(szGameName, "csgo", false)) + Format(szMessage, maxlength, " \x01\x0B\x01%s", szMessage); + + /* If author was specified replace {teamcolor} tag */ + if (author != NO_INDEX) + { + if (CProfile_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}", CTagCode[Color_Green], false); + } + else + ReplaceString(szMessage, maxlength, "{teamcolor}", "", false); + + /* For other color tags we need a loop */ + for (new i = 0; i < MAX_COLORS; i++) + { + /* If tag not found - skip */ + if (StrContains(szMessage, CTag[i], false) == -1) + continue; + + /* If tag is not supported by game replace it with green tag */ + else if (!CProfile_Colors[i]) + ReplaceString(szMessage, maxlength, CTag[i], CTagCode[Color_Green], false); + + /* If tag doesn't need saytext2 simply replace */ + else if (!CTagReqSayText2[i]) + ReplaceString(szMessage, maxlength, CTag[i], CTagCode[i], false); + + /* Tag needs saytext2 */ + else + { + /* If saytext2 is not supported by game replace tag with green tag */ + if (!CProfile_SayText2) + ReplaceString(szMessage, maxlength, CTag[i], CTagCode[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 = CFindRandomPlayerByTeam(CProfile_TeamIndex[i]); + + /* If player not found replace tag with green color tag */ + if (iRandomPlayer == NO_PLAYER) + ReplaceString(szMessage, maxlength, CTag[i], CTagCode[Color_Green], false); + + /* If player was found simply replace */ + else + ReplaceString(szMessage, maxlength, CTag[i], CTagCode[i], false); + + } + /* If found another team color tag throw error */ + else + { + //ReplaceString(szMessage, maxlength, CTag[i], ""); + ThrowError("Using two team colors in one message is not allowed"); + } + } + + } + } + + return iRandomPlayer; +} + +/** + * Founds a random player with specified team + * + * @param color_team Client team. + * @return Client index or NO_PLAYER if no player found + */ +stock CFindRandomPlayerByTeam(color_team) +{ + if (color_team == SERVER_INDEX) + return 0; + else + { + for (new i = 1; i <= MaxClients; i++) + { + if (IsClientInGame(i) && GetClientTeam(i) == color_team) + return i; + } + } + + 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 CSayText2(client, author, const String:szMessage[]) +{ + new Handle:hBuffer = StartMessageOne("SayText2", client, USERMSG_RELIABLE|USERMSG_BLOCKHOOKS); + + if(GetFeatureStatus(FeatureType_Native, "GetUserMessageType") == FeatureStatus_Available && GetUserMessageType() == UM_Protobuf) + { + PbSetInt(hBuffer, "ent_idx", author); + PbSetBool(hBuffer, "chat", true); + PbSetString(hBuffer, "msg_name", szMessage); + PbAddString(hBuffer, "params", ""); + PbAddString(hBuffer, "params", ""); + PbAddString(hBuffer, "params", ""); + PbAddString(hBuffer, "params", ""); + } + else + { + BfWriteByte(hBuffer, author); + BfWriteByte(hBuffer, true); + BfWriteString(hBuffer, szMessage); + } + + EndMessage(); +} + +/** + * Creates game color profile + * This function must be edited if you want to add more games support + * + * @return No return. + */ +stock CSetupProfile() +{ + decl String:szGameName[30]; + GetGameFolderName(szGameName, sizeof(szGameName)); + + if (StrEqual(szGameName, "cstrike", false)) + { + CProfile_Colors[Color_Lightgreen] = true; + CProfile_Colors[Color_Red] = true; + CProfile_Colors[Color_Blue] = true; + CProfile_Colors[Color_Olive] = true; + CProfile_TeamIndex[Color_Lightgreen] = SERVER_INDEX; + CProfile_TeamIndex[Color_Red] = 2; + CProfile_TeamIndex[Color_Blue] = 3; + CProfile_SayText2 = true; + } + else if (StrEqual(szGameName, "csgo", false)) + { + CProfile_Colors[Color_Red] = true; + CProfile_Colors[Color_Blue] = true; + CProfile_Colors[Color_Olive] = true; + CProfile_Colors[Color_Darkred] = true; + CProfile_Colors[Color_Lime] = true; + CProfile_Colors[Color_Lightred] = true; + CProfile_Colors[Color_Purple] = true; + CProfile_Colors[Color_Grey] = true; + CProfile_Colors[Color_Orange] = true; + CProfile_TeamIndex[Color_Red] = 2; + CProfile_TeamIndex[Color_Blue] = 3; + CProfile_SayText2 = true; + } + else if (StrEqual(szGameName, "tf", false)) + { + CProfile_Colors[Color_Lightgreen] = true; + CProfile_Colors[Color_Red] = true; + CProfile_Colors[Color_Blue] = true; + CProfile_Colors[Color_Olive] = true; + CProfile_TeamIndex[Color_Lightgreen] = SERVER_INDEX; + CProfile_TeamIndex[Color_Red] = 2; + CProfile_TeamIndex[Color_Blue] = 3; + CProfile_SayText2 = true; + } + else if (StrEqual(szGameName, "left4dead", false) || StrEqual(szGameName, "left4dead2", false)) + { + CProfile_Colors[Color_Lightgreen] = true; + CProfile_Colors[Color_Red] = true; + CProfile_Colors[Color_Blue] = true; + CProfile_Colors[Color_Olive] = true; + CProfile_TeamIndex[Color_Lightgreen] = SERVER_INDEX; + CProfile_TeamIndex[Color_Red] = 3; + CProfile_TeamIndex[Color_Blue] = 2; + CProfile_SayText2 = true; + } + else if (StrEqual(szGameName, "hl2mp", false)) + { + /* hl2mp profile is based on mp_teamplay convar */ + if (GetConVarBool(FindConVar("mp_teamplay"))) + { + CProfile_Colors[Color_Red] = true; + CProfile_Colors[Color_Blue] = true; + CProfile_Colors[Color_Olive] = true; + CProfile_TeamIndex[Color_Red] = 3; + CProfile_TeamIndex[Color_Blue] = 2; + CProfile_SayText2 = true; + } + else + { + CProfile_SayText2 = false; + CProfile_Colors[Color_Olive] = true; + } + } + else if (StrEqual(szGameName, "dod", false)) + { + CProfile_Colors[Color_Olive] = true; + CProfile_SayText2 = false; + } + /* Profile for other games */ + else + { + if (GetUserMessageId("SayText2") == INVALID_MESSAGE_ID) + { + CProfile_SayText2 = false; + } + else + { + CProfile_Colors[Color_Red] = true; + CProfile_Colors[Color_Blue] = true; + CProfile_TeamIndex[Color_Red] = 2; + CProfile_TeamIndex[Color_Blue] = 3; + CProfile_SayText2 = true; + } + } +} + +public Action:CEvent_MapStart(Handle:event, const String:name[], bool:dontBroadcast) +{ + CSetupProfile(); + + for (new i = 1; i <= MaxClients; i++) + CSkipList[i] = false; } \ No newline at end of file diff --git a/includes/dhooks.inc b/includes/dhooks.inc index 571a0aab..9a1af292 100644 --- a/includes/dhooks.inc +++ b/includes/dhooks.inc @@ -1,482 +1,482 @@ -#if defined _dhooks_included -#endinput -#endif -#define _dhooks_included - -enum ObjectValueType -{ - ObjectValueType_Int = 0, - ObjectValueType_Bool, - ObjectValueType_Ehandle, - ObjectValueType_Float, - ObjectValueType_CBaseEntityPtr, - ObjectValueType_IntPtr, - ObjectValueType_BoolPtr, - ObjectValueType_EhandlePtr, - ObjectValueType_FloatPtr, - ObjectValueType_Vector, - ObjectValueType_VectorPtr, - ObjectValueType_CharPtr, - ObjectValueType_String -}; - -enum ListenType -{ - ListenType_Created, - ListenType_Deleted -}; - -enum ReturnType -{ - ReturnType_Unknown, - ReturnType_Void, - ReturnType_Int, - ReturnType_Bool, - ReturnType_Float, - ReturnType_String, //Note this is a string_t - ReturnType_StringPtr, //Note this is a string_t * - ReturnType_CharPtr, - ReturnType_Vector, - ReturnType_VectorPtr, - ReturnType_CBaseEntity, - ReturnType_Edict -}; - -enum HookParamType -{ - HookParamType_Unknown, - HookParamType_Int, - HookParamType_Bool, - HookParamType_Float, - HookParamType_String, //Note this is a string_t - HookParamType_StringPtr, //Note this is a string_t * - HookParamType_CharPtr, - HookParamType_VectorPtr, - HookParamType_CBaseEntity, - HookParamType_ObjectPtr, - HookParamType_Edict, - HookParamType_Object -}; - -enum ThisPointerType -{ - ThisPointer_Ignore, - ThisPointer_CBaseEntity, - ThisPointer_Address -}; - -enum HookType -{ - HookType_Entity, - HookType_GameRules, - HookType_Raw -}; - -enum MRESReturn -{ - MRES_ChangedHandled = -2, // Use changed values and return MRES_Handled - MRES_ChangedOverride, // Use changed values and return MRES_Override - MRES_Ignored, // plugin didn't take any action - MRES_Handled, // plugin did something, but real function should still be called - MRES_Override, // call real function, but use my return value - MRES_Supercede // skip real function; use my return value -}; - -enum DHookPassFlag -{ - DHookPass_ByVal = (1<<0), - DHookPass_ByRef = (1<<1) -}; - -typeset ListenCB -{ - //Deleted - function void (int entity); - - //Created - function void (int entity, const char[] classname); -}; - -typeset DHookRemovalCB -{ - function void (int hookid); -}; -typeset DHookCallback -{ - //Function Example: void Ham::Test() with this pointer ignore - function MRESReturn (); - - //Function Example: void Ham::Test() with this pointer passed - function MRESReturn (int pThis); - - //Function Example: void Ham::Test(int cake) with this pointer ignore - function MRESReturn (Handle hParams); - - //Function Example: void Ham::Test(int cake) with this pointer passed - function MRESReturn (int pThis, Handle hParams); - - //Function Example: int Ham::Test() with this pointer ignore - function MRESReturn (Handle hReturn); - - //Function Example: int Ham::Test() with this pointer passed - function MRESReturn (int pThis, Handle hReturn); - - //Function Example: int Ham::Test(int cake) with this pointer ignore - function MRESReturn (Handle hReturn, Handle hParams); - - //Function Example: int Ham::Test(int cake) with this pointer passed - function MRESReturn (int pThis, Handle hReturn, Handle hParams); - - //Address NOW - - //Function Example: void Ham::Test() with this pointer passed - function MRESReturn (Address pThis); - - //Function Example: void Ham::Test(int cake) with this pointer passed - function MRESReturn (Address pThis, Handle hParams); - - //Function Example: int Ham::Test() with this pointer passed - function MRESReturn (Address pThis, Handle hReturn); - - //Function Example: int Ham::Test(int cake) with this pointer passed - function MRESReturn (Address pThis, Handle hReturn, Handle hParams); - -}; - -/* Adds an entity listener hook - * - * @param type Type of listener to add - * @param callback Callback to use - * - * @noreturn -*/ -native void DHookAddEntityListener(ListenType type, ListenCB callback); - -/* Removes an entity listener hook - * - * @param type Type of listener to remove - * @param callback Callback this listener was using - * - * @return True if one was removed false otherwise. -*/ -native bool DHookRemoveEntityListener(ListenType type, ListenCB callback); - -/* Creates a hook - * - * @param offset vtable offset for function to hook - * @param hooktype Type of hook - * @param returntype Type type of return - * @param thistype Type of this pointer or ignore (ignore can be used if not needed) - * @param callback Callback function - * - * @return Returns setup handle for the hook or INVALID_HANDLE. -*/ -native Handle DHookCreate(int offset, HookType hooktype, ReturnType returntype, ThisPointerType thistype, DHookCallback callback); - -/* Adds param to a hook setup - * - * @param setup Setup handle to add the param to. - * @param type Param type - * @param size Used for Objects (not Object ptr) to define the size of the object. - * @param flag Used to change the pass type. - * - * @error Invalid setup handle or too many params added (request upping the max in thread) - * @noreturn -*/ -native void DHookAddParam(Handle setup, HookParamType type, int size=-1, DHookPassFlag flag=DHookPass_ByVal); -//native DHookAddParam(Handle:setup, HookParamType:type); - -/* Hook entity - * - * @param setup Setup handle to use to add the hook. - * @param post True to make the hook a post hook. (If you need to change the retunr value or need the return value use a post hook! If you need to change params and return use a pre and post hook!) - * @param entity Entity index to hook on. - * @param removalcb Callback for when the hook is removed (Entity hooks are auto-removed on entity destroyed and will call this callback) - * - * @error Invalid setup handle, invalid entity or invalid hook type. - * @return -1 on fail a hookid on success -*/ -native int DHookEntity(Handle setup, bool post, int entity, DHookRemovalCB removalcb=INVALID_FUNCTION); - -/* Hook gamerules - * - * @param setup Setup handle to use to add the hook. - * @param post True to make the hook a post hook. (If you need to change the retunr value or need the return value use a post hook! If you need to change params and return use a pre and post hook!) - * @param removalcb Callback for when the hook is removed (Game rules hooks are auto-removed on map end and will call this callback) - * - * @error Invalid setup handle, failing to get gamerules pointer or invalid hook type. - * @return -1 on fail a hookid on success -*/ -native int DHookGamerules(Handle setup, bool post, DHookRemovalCB removalcb=INVALID_FUNCTION); - -/* Hook a raw pointer - * - * @param setup Setup handle to use to add the hook. - * @param post True to make the hook a post hook. (If you need to change the retunr value or need the return value use a post hook! If you need to change params and return use a pre and post hook!) - * @param addr This pointer address. - * @param removalcb Callback for when the hook is removed (Entity hooks are auto-removed on entity destroyed and will call this callback) - * - * @error Invalid setup handle, invalid address or invalid hook type. - * @return -1 on fail a hookid on success -*/ -native int DHookRaw(Handle setup, bool post, Address addr, DHookRemovalCB removalcb=INVALID_FUNCTION); - -/* Remove hook by hook id - * - * @param hookid Hook id to remove - * - * @return true on success false otherwise - * @note This will not fire the removal callback! -*/ -native bool DHookRemoveHookID(int hookid); - -/* Get param value (Only use for: int, entity, bool or float param types) - * - * @param hParams Handle to params structure - * @param num Param number to get. (Example if the function has 2 params and you need the value of the first param num would be 1. 0 Will return the number of params stored) - * - * @error Invalid handle. Invalid param number. Invalid param type. - * @return value if num greater than 0. If 0 returns paramcount. -*/ -native any DHookGetParam(Handle hParams, int num); - -/* Get vector param value - * - * @param hParams Handle to params structure - * @param num Param number to get. (Example if the function has 2 params and you need the value of the first param num would be 1.) - * @param vec Vector buffer to store result. - * - * @error Invalid handle. Invalid param number. Invalid param type. - * @noreturn -*/ -native void DHookGetParamVector(Handle hParams, int num, float vec[3]); - -/* Get string param value - * - * @param hParams Handle to params structure - * @param num Param number to get. (Example if the function has 2 params and you need the value of the first param num would be 1.) - * @param buffer String buffer to store result - * @param size Buffer size - * - * @error Invalid handle. Invalid param number. Invalid param type. - * @noreturn -*/ -native void DHookGetParamString(Handle hParams, int num, char[] buffer, int size); - -/* Set param value (Only use for: int, entity, bool or float param types) - * - * @param hParams Handle to params structure - * @params num Param number to set (Example if the function has 2 params and you need to set the value of the first param num would be 1.) - * @param value Value to set it as (only pass int, bool, float or entity index) - * - * @error Invalid handle. Invalid param number. Invalid param type. - * @noreturn -*/ -native void DHookSetParam(Handle hParams, int num, any value); - -/* Set vector param value - * - * @param hParams Handle to params structure - * @params num Param number to set (Example if the function has 2 params and you need to set the value of the first param num would be 1.) - * @param vec Value to set vector as. - * - * @error Invalid handle. Invalid param number. Invalid param type. - * @noreturn -*/ -native void DHookSetParamVector(Handle hParams, int num, float vec[3]); - -/* Set string param value - * - * @param hParams Handle to params structure - * @params num Param number to set (Example if the function has 2 params and you need to set the value of the first param num would be 1.) - * @param value Value to set string as. - * - * @error Invalid handle. Invalid param number. Invalid param type. - * @noreturn -*/ -native void DHookSetParamString(Handle hParams, int num, char[] value); - -/* Get return value (Only use for: int, entity, bool or float return types) - * - * @param hReturn Handle to return structure - * - * @error Invalid Handle, invalid type. - * @return Returns default value if prehook returns actual value if post hook. -*/ -native any DHookGetReturn(Handle hReturn); - -/* Get return vector value - * - * @param hReturn Handle to return structure - * @param vec Vector buffer to store result in. (In pre hooks will be default value (0.0,0.0,0.0)) - * - * @error Invalid Handle, invalid type. - * @noreturn -*/ -native void DHookGetReturnVector(Handle hReturn, float vec[3]); - -/* Get return string value - * - * @param hReturn Handle to return structure - * @param buffer String buffer to store result in. (In pre hooks will be default value "") - * @param size String buffer size - * - * @error Invalid Handle, invalid type. - * @noreturn -*/ -native void DHookGetReturnString(Handle hReturn, char[] buffer, int size); - -/* Set return value (Only use for: int, entity, bool or float return types) - * - * @param hReturn Handle to return structure - * @param value Value to set return as - * - * @error Invalid Handle, invalid type. - * @noreturn -*/ -native void DHookSetReturn(Handle hReturn, any value); - -/* Set return vector value - * - * @param hReturn Handle to return structure - * @param vec Value to set return vector as - * - * @error Invalid Handle, invalid type. - * @noreturn -*/ -native void DHookSetReturnVector(Handle hReturn, float vec[3]); - -/* Set return string value - * - * @param hReturn Handle to return structure - * @param value Value to set return string as - * - * @error Invalid Handle, invalid type. - * @noreturn -*/ -native void DHookSetReturnString(Handle hReturn, char[] value); - -//WE SHOULD WRAP THESE AROUND STOCKS FOR NON PTR AS WE SUPPORT BOTH WITH THESE NATIVE'S - -/* Gets an objects variable value - * - * @param hParams Handle to params structure - * @param num Param number to get. - * @param offset Offset within the object to the var to get. - * @param type Type of var it is - * - * @error Invalid handle. Invalid param number. Invalid param type. Invalid Object type. - * @return Value of the objects var. If EHANDLE type or entity returns entity index. -*/ -native any DHookGetParamObjectPtrVar(Handle hParams, int num, int offset, ObjectValueType type); - -/* Sets an objects variable value - * - * @param hParams Handle to params structure - * @param num Param number to set. - * @param offset Offset within the object to the var to set. - * @param type Type of var it is - * @param value The value to set the var to. - * - * @error Invalid handle. Invalid param number. Invalid param type. Invalid Object type. - * @noreturn -*/ -native void DHookSetParamObjectPtrVar(Handle hParams, int num, int offset, ObjectValueType type, any value); - -/* Gets an objects vector variable value - * - * @param hParams Handle to params structure - * @param num Param number to get. - * @param offset Offset within the object to the var to get. - * @param type Type of var it is - * @param buffer Buffer to store the result vector - * - * @error Invalid handle. Invalid param number. Invalid param type. Invalid Object type. - * @noreturn -*/ -native void DHookGetParamObjectPtrVarVector(Handle hParams, int num, int offset, ObjectValueType type, float buffer[3]); - -/* Sets an objects vector variable value - * - * @param hParams Handle to params structure - * @param num Param number to set. - * @param offset Offset within the object to the var to set. - * @param type Type of var it is - * @param value The value to set the vector var to. - * - * @error Invalid handle. Invalid param number. Invalid param type. Invalid Object type. - * @noreturn -*/ -native void DHookSetParamObjectPtrVarVector(Handle hParams, int num, int offset, ObjectValueType type, float value[3]); - -/* Gets an objects string variable value - * - * @param hParams Handle to params structure - * @param num Param number to get. - * @param offset Offset within the object to the var to get. - * @param type Type of var it is - * @param buffer Buffer to store the result vector - * @param size Size of the buffer - * - * @error Invalid handle. Invalid param number. Invalid param type. Invalid Object type. - * @noreturn -*/ -native void DHookGetParamObjectPtrString(Handle hParams, int num, int offset, ObjectValueType type, char[] buffer, int size); - -/* Checks if a pointer param is null - * - * @param hParams Handle to params structure - * @param num Param number to check. - * - * @error Non pointer param - * @return True if null false otherwise. -*/ -native bool DHookIsNullParam(Handle hParams, int num); - -public Extension __ext_dhooks = -{ - name = "dhooks", - file = "dhooks.ext", -#if defined AUTOLOAD_EXTENSIONS - autoload = 1, -#else - autoload = 0, -#endif -#if defined REQUIRE_EXTENSIONS - required = 1, -#else - required = 0, -#endif -}; - -#if !defined REQUIRE_EXTENSIONS -public __ext_dhooks_SetNTVOptional() -{ - MarkNativeAsOptional("DHookAddEntityListener"); - MarkNativeAsOptional("DHookRemoveEntityListener"); - MarkNativeAsOptional("DHookCreate"); - MarkNativeAsOptional("DHookAddParam"); - MarkNativeAsOptional("DHookEntity"); - MarkNativeAsOptional("DHookGamerules"); - MarkNativeAsOptional("DHookRaw"); - MarkNativeAsOptional("DHookRemoveHookID"); - MarkNativeAsOptional("DHookGetParam"); - MarkNativeAsOptional("DHookGetParamVector"); - MarkNativeAsOptional("DHookGetParamString"); - MarkNativeAsOptional("DHookSetParam"); - MarkNativeAsOptional("DHookSetParamVector"); - MarkNativeAsOptional("DHookSetParamString"); - MarkNativeAsOptional("DHookGetReturn"); - MarkNativeAsOptional("DHookGetReturnVector"); - MarkNativeAsOptional("DHookGetReturnString"); - MarkNativeAsOptional("DHookSetReturn"); - MarkNativeAsOptional("DHookSetReturnVector"); - MarkNativeAsOptional("DHookSetReturnString"); - MarkNativeAsOptional("DHookGetParamObjectPtrVar"); - MarkNativeAsOptional("DHookSetParamObjectPtrVar"); - MarkNativeAsOptional("DHookGetParamObjectPtrVarVector"); - MarkNativeAsOptional("DHookSetParamObjectPtrVarVector"); - MarkNativeAsOptional("DHookIsNullParam"); - MarkNativeAsOptional("DHookGetParamObjectPtrString"); -} +#if defined _dhooks_included +#endinput +#endif +#define _dhooks_included + +enum ObjectValueType +{ + ObjectValueType_Int = 0, + ObjectValueType_Bool, + ObjectValueType_Ehandle, + ObjectValueType_Float, + ObjectValueType_CBaseEntityPtr, + ObjectValueType_IntPtr, + ObjectValueType_BoolPtr, + ObjectValueType_EhandlePtr, + ObjectValueType_FloatPtr, + ObjectValueType_Vector, + ObjectValueType_VectorPtr, + ObjectValueType_CharPtr, + ObjectValueType_String +}; + +enum ListenType +{ + ListenType_Created, + ListenType_Deleted +}; + +enum ReturnType +{ + ReturnType_Unknown, + ReturnType_Void, + ReturnType_Int, + ReturnType_Bool, + ReturnType_Float, + ReturnType_String, //Note this is a string_t + ReturnType_StringPtr, //Note this is a string_t * + ReturnType_CharPtr, + ReturnType_Vector, + ReturnType_VectorPtr, + ReturnType_CBaseEntity, + ReturnType_Edict +}; + +enum HookParamType +{ + HookParamType_Unknown, + HookParamType_Int, + HookParamType_Bool, + HookParamType_Float, + HookParamType_String, //Note this is a string_t + HookParamType_StringPtr, //Note this is a string_t * + HookParamType_CharPtr, + HookParamType_VectorPtr, + HookParamType_CBaseEntity, + HookParamType_ObjectPtr, + HookParamType_Edict, + HookParamType_Object +}; + +enum ThisPointerType +{ + ThisPointer_Ignore, + ThisPointer_CBaseEntity, + ThisPointer_Address +}; + +enum HookType +{ + HookType_Entity, + HookType_GameRules, + HookType_Raw +}; + +enum MRESReturn +{ + MRES_ChangedHandled = -2, // Use changed values and return MRES_Handled + MRES_ChangedOverride, // Use changed values and return MRES_Override + MRES_Ignored, // plugin didn't take any action + MRES_Handled, // plugin did something, but real function should still be called + MRES_Override, // call real function, but use my return value + MRES_Supercede // skip real function; use my return value +}; + +enum DHookPassFlag +{ + DHookPass_ByVal = (1<<0), + DHookPass_ByRef = (1<<1) +}; + +typeset ListenCB +{ + //Deleted + function void (int entity); + + //Created + function void (int entity, const char[] classname); +}; + +typeset DHookRemovalCB +{ + function void (int hookid); +}; +typeset DHookCallback +{ + //Function Example: void Ham::Test() with this pointer ignore + function MRESReturn (); + + //Function Example: void Ham::Test() with this pointer passed + function MRESReturn (int pThis); + + //Function Example: void Ham::Test(int cake) with this pointer ignore + function MRESReturn (Handle hParams); + + //Function Example: void Ham::Test(int cake) with this pointer passed + function MRESReturn (int pThis, Handle hParams); + + //Function Example: int Ham::Test() with this pointer ignore + function MRESReturn (Handle hReturn); + + //Function Example: int Ham::Test() with this pointer passed + function MRESReturn (int pThis, Handle hReturn); + + //Function Example: int Ham::Test(int cake) with this pointer ignore + function MRESReturn (Handle hReturn, Handle hParams); + + //Function Example: int Ham::Test(int cake) with this pointer passed + function MRESReturn (int pThis, Handle hReturn, Handle hParams); + + //Address NOW + + //Function Example: void Ham::Test() with this pointer passed + function MRESReturn (Address pThis); + + //Function Example: void Ham::Test(int cake) with this pointer passed + function MRESReturn (Address pThis, Handle hParams); + + //Function Example: int Ham::Test() with this pointer passed + function MRESReturn (Address pThis, Handle hReturn); + + //Function Example: int Ham::Test(int cake) with this pointer passed + function MRESReturn (Address pThis, Handle hReturn, Handle hParams); + +}; + +/* Adds an entity listener hook + * + * @param type Type of listener to add + * @param callback Callback to use + * + * @noreturn +*/ +native void DHookAddEntityListener(ListenType type, ListenCB callback); + +/* Removes an entity listener hook + * + * @param type Type of listener to remove + * @param callback Callback this listener was using + * + * @return True if one was removed false otherwise. +*/ +native bool DHookRemoveEntityListener(ListenType type, ListenCB callback); + +/* Creates a hook + * + * @param offset vtable offset for function to hook + * @param hooktype Type of hook + * @param returntype Type type of return + * @param thistype Type of this pointer or ignore (ignore can be used if not needed) + * @param callback Callback function + * + * @return Returns setup handle for the hook or INVALID_HANDLE. +*/ +native Handle DHookCreate(int offset, HookType hooktype, ReturnType returntype, ThisPointerType thistype, DHookCallback callback); + +/* Adds param to a hook setup + * + * @param setup Setup handle to add the param to. + * @param type Param type + * @param size Used for Objects (not Object ptr) to define the size of the object. + * @param flag Used to change the pass type. + * + * @error Invalid setup handle or too many params added (request upping the max in thread) + * @noreturn +*/ +native void DHookAddParam(Handle setup, HookParamType type, int size=-1, DHookPassFlag flag=DHookPass_ByVal); +//native DHookAddParam(Handle:setup, HookParamType:type); + +/* Hook entity + * + * @param setup Setup handle to use to add the hook. + * @param post True to make the hook a post hook. (If you need to change the retunr value or need the return value use a post hook! If you need to change params and return use a pre and post hook!) + * @param entity Entity index to hook on. + * @param removalcb Callback for when the hook is removed (Entity hooks are auto-removed on entity destroyed and will call this callback) + * + * @error Invalid setup handle, invalid entity or invalid hook type. + * @return -1 on fail a hookid on success +*/ +native int DHookEntity(Handle setup, bool post, int entity, DHookRemovalCB removalcb=INVALID_FUNCTION); + +/* Hook gamerules + * + * @param setup Setup handle to use to add the hook. + * @param post True to make the hook a post hook. (If you need to change the retunr value or need the return value use a post hook! If you need to change params and return use a pre and post hook!) + * @param removalcb Callback for when the hook is removed (Game rules hooks are auto-removed on map end and will call this callback) + * + * @error Invalid setup handle, failing to get gamerules pointer or invalid hook type. + * @return -1 on fail a hookid on success +*/ +native int DHookGamerules(Handle setup, bool post, DHookRemovalCB removalcb=INVALID_FUNCTION); + +/* Hook a raw pointer + * + * @param setup Setup handle to use to add the hook. + * @param post True to make the hook a post hook. (If you need to change the retunr value or need the return value use a post hook! If you need to change params and return use a pre and post hook!) + * @param addr This pointer address. + * @param removalcb Callback for when the hook is removed (Entity hooks are auto-removed on entity destroyed and will call this callback) + * + * @error Invalid setup handle, invalid address or invalid hook type. + * @return -1 on fail a hookid on success +*/ +native int DHookRaw(Handle setup, bool post, Address addr, DHookRemovalCB removalcb=INVALID_FUNCTION); + +/* Remove hook by hook id + * + * @param hookid Hook id to remove + * + * @return true on success false otherwise + * @note This will not fire the removal callback! +*/ +native bool DHookRemoveHookID(int hookid); + +/* Get param value (Only use for: int, entity, bool or float param types) + * + * @param hParams Handle to params structure + * @param num Param number to get. (Example if the function has 2 params and you need the value of the first param num would be 1. 0 Will return the number of params stored) + * + * @error Invalid handle. Invalid param number. Invalid param type. + * @return value if num greater than 0. If 0 returns paramcount. +*/ +native any DHookGetParam(Handle hParams, int num); + +/* Get vector param value + * + * @param hParams Handle to params structure + * @param num Param number to get. (Example if the function has 2 params and you need the value of the first param num would be 1.) + * @param vec Vector buffer to store result. + * + * @error Invalid handle. Invalid param number. Invalid param type. + * @noreturn +*/ +native void DHookGetParamVector(Handle hParams, int num, float vec[3]); + +/* Get string param value + * + * @param hParams Handle to params structure + * @param num Param number to get. (Example if the function has 2 params and you need the value of the first param num would be 1.) + * @param buffer String buffer to store result + * @param size Buffer size + * + * @error Invalid handle. Invalid param number. Invalid param type. + * @noreturn +*/ +native void DHookGetParamString(Handle hParams, int num, char[] buffer, int size); + +/* Set param value (Only use for: int, entity, bool or float param types) + * + * @param hParams Handle to params structure + * @params num Param number to set (Example if the function has 2 params and you need to set the value of the first param num would be 1.) + * @param value Value to set it as (only pass int, bool, float or entity index) + * + * @error Invalid handle. Invalid param number. Invalid param type. + * @noreturn +*/ +native void DHookSetParam(Handle hParams, int num, any value); + +/* Set vector param value + * + * @param hParams Handle to params structure + * @params num Param number to set (Example if the function has 2 params and you need to set the value of the first param num would be 1.) + * @param vec Value to set vector as. + * + * @error Invalid handle. Invalid param number. Invalid param type. + * @noreturn +*/ +native void DHookSetParamVector(Handle hParams, int num, float vec[3]); + +/* Set string param value + * + * @param hParams Handle to params structure + * @params num Param number to set (Example if the function has 2 params and you need to set the value of the first param num would be 1.) + * @param value Value to set string as. + * + * @error Invalid handle. Invalid param number. Invalid param type. + * @noreturn +*/ +native void DHookSetParamString(Handle hParams, int num, char[] value); + +/* Get return value (Only use for: int, entity, bool or float return types) + * + * @param hReturn Handle to return structure + * + * @error Invalid Handle, invalid type. + * @return Returns default value if prehook returns actual value if post hook. +*/ +native any DHookGetReturn(Handle hReturn); + +/* Get return vector value + * + * @param hReturn Handle to return structure + * @param vec Vector buffer to store result in. (In pre hooks will be default value (0.0,0.0,0.0)) + * + * @error Invalid Handle, invalid type. + * @noreturn +*/ +native void DHookGetReturnVector(Handle hReturn, float vec[3]); + +/* Get return string value + * + * @param hReturn Handle to return structure + * @param buffer String buffer to store result in. (In pre hooks will be default value "") + * @param size String buffer size + * + * @error Invalid Handle, invalid type. + * @noreturn +*/ +native void DHookGetReturnString(Handle hReturn, char[] buffer, int size); + +/* Set return value (Only use for: int, entity, bool or float return types) + * + * @param hReturn Handle to return structure + * @param value Value to set return as + * + * @error Invalid Handle, invalid type. + * @noreturn +*/ +native void DHookSetReturn(Handle hReturn, any value); + +/* Set return vector value + * + * @param hReturn Handle to return structure + * @param vec Value to set return vector as + * + * @error Invalid Handle, invalid type. + * @noreturn +*/ +native void DHookSetReturnVector(Handle hReturn, float vec[3]); + +/* Set return string value + * + * @param hReturn Handle to return structure + * @param value Value to set return string as + * + * @error Invalid Handle, invalid type. + * @noreturn +*/ +native void DHookSetReturnString(Handle hReturn, char[] value); + +//WE SHOULD WRAP THESE AROUND STOCKS FOR NON PTR AS WE SUPPORT BOTH WITH THESE NATIVE'S + +/* Gets an objects variable value + * + * @param hParams Handle to params structure + * @param num Param number to get. + * @param offset Offset within the object to the var to get. + * @param type Type of var it is + * + * @error Invalid handle. Invalid param number. Invalid param type. Invalid Object type. + * @return Value of the objects var. If EHANDLE type or entity returns entity index. +*/ +native any DHookGetParamObjectPtrVar(Handle hParams, int num, int offset, ObjectValueType type); + +/* Sets an objects variable value + * + * @param hParams Handle to params structure + * @param num Param number to set. + * @param offset Offset within the object to the var to set. + * @param type Type of var it is + * @param value The value to set the var to. + * + * @error Invalid handle. Invalid param number. Invalid param type. Invalid Object type. + * @noreturn +*/ +native void DHookSetParamObjectPtrVar(Handle hParams, int num, int offset, ObjectValueType type, any value); + +/* Gets an objects vector variable value + * + * @param hParams Handle to params structure + * @param num Param number to get. + * @param offset Offset within the object to the var to get. + * @param type Type of var it is + * @param buffer Buffer to store the result vector + * + * @error Invalid handle. Invalid param number. Invalid param type. Invalid Object type. + * @noreturn +*/ +native void DHookGetParamObjectPtrVarVector(Handle hParams, int num, int offset, ObjectValueType type, float buffer[3]); + +/* Sets an objects vector variable value + * + * @param hParams Handle to params structure + * @param num Param number to set. + * @param offset Offset within the object to the var to set. + * @param type Type of var it is + * @param value The value to set the vector var to. + * + * @error Invalid handle. Invalid param number. Invalid param type. Invalid Object type. + * @noreturn +*/ +native void DHookSetParamObjectPtrVarVector(Handle hParams, int num, int offset, ObjectValueType type, float value[3]); + +/* Gets an objects string variable value + * + * @param hParams Handle to params structure + * @param num Param number to get. + * @param offset Offset within the object to the var to get. + * @param type Type of var it is + * @param buffer Buffer to store the result vector + * @param size Size of the buffer + * + * @error Invalid handle. Invalid param number. Invalid param type. Invalid Object type. + * @noreturn +*/ +native void DHookGetParamObjectPtrString(Handle hParams, int num, int offset, ObjectValueType type, char[] buffer, int size); + +/* Checks if a pointer param is null + * + * @param hParams Handle to params structure + * @param num Param number to check. + * + * @error Non pointer param + * @return True if null false otherwise. +*/ +native bool DHookIsNullParam(Handle hParams, int num); + +public Extension __ext_dhooks = +{ + name = "dhooks", + file = "dhooks.ext", +#if defined AUTOLOAD_EXTENSIONS + autoload = 1, +#else + autoload = 0, +#endif +#if defined REQUIRE_EXTENSIONS + required = 1, +#else + required = 0, +#endif +}; + +#if !defined REQUIRE_EXTENSIONS +public __ext_dhooks_SetNTVOptional() +{ + MarkNativeAsOptional("DHookAddEntityListener"); + MarkNativeAsOptional("DHookRemoveEntityListener"); + MarkNativeAsOptional("DHookCreate"); + MarkNativeAsOptional("DHookAddParam"); + MarkNativeAsOptional("DHookEntity"); + MarkNativeAsOptional("DHookGamerules"); + MarkNativeAsOptional("DHookRaw"); + MarkNativeAsOptional("DHookRemoveHookID"); + MarkNativeAsOptional("DHookGetParam"); + MarkNativeAsOptional("DHookGetParamVector"); + MarkNativeAsOptional("DHookGetParamString"); + MarkNativeAsOptional("DHookSetParam"); + MarkNativeAsOptional("DHookSetParamVector"); + MarkNativeAsOptional("DHookSetParamString"); + MarkNativeAsOptional("DHookGetReturn"); + MarkNativeAsOptional("DHookGetReturnVector"); + MarkNativeAsOptional("DHookGetReturnString"); + MarkNativeAsOptional("DHookSetReturn"); + MarkNativeAsOptional("DHookSetReturnVector"); + MarkNativeAsOptional("DHookSetReturnString"); + MarkNativeAsOptional("DHookGetParamObjectPtrVar"); + MarkNativeAsOptional("DHookSetParamObjectPtrVar"); + MarkNativeAsOptional("DHookGetParamObjectPtrVarVector"); + MarkNativeAsOptional("DHookSetParamObjectPtrVarVector"); + MarkNativeAsOptional("DHookIsNullParam"); + MarkNativeAsOptional("DHookGetParamObjectPtrString"); +} #endif \ No newline at end of file diff --git a/includes/nativevotes.inc b/includes/nativevotes.inc index 97e80c29..c19f00ab 100644 --- a/includes/nativevotes.inc +++ b/includes/nativevotes.inc @@ -1,915 +1,915 @@ -/** - * vim: set ts=4 : - * ============================================================================= - * NativeVotes - * Copyright (C) 2011-2013 Ross Bemrose (Powerlord). All rights reserved. - * ============================================================================= - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, version 3.0, as published by the - * Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - * - * As a special exception, AlliedModders LLC gives you permission to link the - * code of this program (as well as its derivative works) to "Half-Life 2," the - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software - * by the Valve Corporation. You must obey the GNU General Public License in - * all respects for all other code used. Additionally, AlliedModders LLC grants - * this exception to all derivative works. AlliedModders LLC defines further - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), - * or . - * - * Version: $Id$ - */ - -#include -#include - -// NativeVotes 0.8 series - -#if defined _nativevotes_included - #endinput -#endif -#define _nativevotes_included - -#define CLIENT_DISCONNECTED -1 - -#define NATIVEVOTES_EXTEND "Extend current Map" /** Defined in TF2, but doesn't appear to be localized */ - -#define NATIVEVOTES_ALL_TEAMS -1 // Defined by TF2, may be the same in L4D/L4D2 -#define NATIVEVOTES_TF2_ALL_TEAMS 0 // Defined by TF2, may be the same in L4D/L4D2 -#define NATIVEVOTES_TEAM_UNASSIGNED 0 // For completeness, do not otherwise use -#define NATIVEVOTES_TEAM_SPECTATOR 1 // Spectators -#define NATIVEVOTES_TEAM_1 2 // RED/Survivors/Terrorists -#define NATIVEVOTES_TEAM_2 3 // BLU/Infected/Counter-Terrorists - -#define NATIVEVOTES_SERVER_INDEX 99 // Defined by TF2, may be the same in L4D/L4D2 - -// These may seem backwards, but this is the order that the votes appear in the vote screen -#define NATIVEVOTES_VOTE_INVALID -1 /**< Vote was invalid, currently only valid internally */ -#define NATIVEVOTES_VOTE_YES 0 /**< Vote was yes */ -#define NATIVEVOTES_VOTE_NO 1 /**< Vote was no */ - -/* -The following MenuActions are supported. Arguments are also listed, as they differ slightly from the default -MenuAction_Start A menu has been started (nothing passed). Only exists for compat reasons. -MenuAction_Display A menu is about to be displayed (param1=client). If you choose to change the vote text, - To change the text, use RedrawVoteTitle() - If you do so, return 1 or _:Plugin_Changed Otherwise, return _:Plugin_Continue or 0. -MenuAction_Select An item was selected (param1=client, param2=item). For subplugin support. -MenuAction_End A vote has fully ended and the vote object is ready to be cleaned up - param1 is MenuEnd reason, either MenuEnd_VotingCancelled or MenuEnd_VotingDone -MenuAction_VoteEnd A vote sequence has succeeded (param1=chosen item) - This is not called if NativeVotes_SetResultCallback has been used on the vote. - You should call NativeVotes_DisplayPass or NativeVotes_DisplayPassEx after this -MenuAction_VoteStart A vote sequence has started (nothing passed). Use this instead of MenuAction_Start -MenuAction_VoteCancel A vote sequence has been cancelled (param1=reason) -MenuAction_DisplayItem Item text is being drawn to the display (param1=client, param2=item) - To change the text, use RedrawVoteItem(). - If you do so, return 1 or _:Plugin_Changed. Otherwise, return _:Plugin_Continue or 0. -*/ - -#define NATIVEVOTES_ACTIONS_DEFAULT MenuAction_VoteStart|MenuAction_VoteCancel|MenuAction_VoteEnd|MenuAction_End - -/** - * Vote types. These are mapped to translation strings and pass strings by VoteStart and VotePass handlers - */ -enum NativeVotesType -{ - NativeVotesType_None = 0, /** Special placeholder for callvote with no arguments for NativeVotes_OnCallVote */ - NativeVotesType_Custom_YesNo, /**< Yes/No, details are vote text. */ - NativeVotesType_Custom_Mult, /**< TF2/CS:GO: Multiple-choice, details are vote text. */ - NativeVotesType_ChgCampaign, /**< L4D/L4D2: Yes/No, details are campaign name */ - NativeVotesType_ChgDifficulty, /**< L4D/L4D2: Yes/No, details are difficulty number in L4D/L4D2 */ - NativeVotesType_ReturnToLobby, /**< L4D/L4D2: Yes/No, details are ignored */ - NativeVotesType_AlltalkOn, /**< L4D2: Yes/No, details are ignored (handled internally by extension) */ - NativeVotesType_AlltalkOff, /**< L4D2: Yes/No, details are ignored (handled internally by extension) */ - NativeVotesType_Restart, /**< Yes/No, details are ignored */ - NativeVotesType_Kick, /**< Yes/No, target is player userid, details are auto-set by target */ - NativeVotesType_KickIdle, /**< TF2/CS:GO: Yes/No, target is player userid, details are auto-set by target */ - NativeVotesType_KickScamming, /**< TF2/CS:GO: Yes/No, target is player userid, details are auto-set by target */ - NativeVotesType_KickCheating, /**< TF2/CS:GO: Yes/No, target is player userid, details are auto-set by target */ - NativeVotesType_ChgLevel, /**< Yes/No, details are level number in L4D/L4D2 or map name in TF2 */ - NativeVotesType_NextLevel, /**< TF2/CS:GO: Yes/No, details are map name */ - NativeVotesType_NextLevelMult, /**< TF2/CS:GO: Multiple-choice, details are ignored */ - NativeVotesType_ScrambleNow, /**< TF2/CS:GO: Yes/No, details are ignored */ - NativeVotesType_ScrambleEnd, /**< TF2: Yes/No, details are ignored */ - NativeVotesType_ChgMission, /**< TF2: Yes/No, details are popfile */ - NativeVotesType_SwapTeams, /**< CS:GO: Yes/No, details are ignored */ - NativeVotesType_Surrender, /**< CS:GO: Yes/No, details are ignored */ - NativeVotesType_Rematch, /**< CS:GO: Yes/No, details are ignored */ - NativeVotesType_Continue, /**< CS:GO: Yes/No, details are ignored */ - NativeVotesType_StartRound, /**< TF2: Yes/No, details are ignored */ - NativeVotesType_Eternaween, /**< TF2: Yes/No, details are ignored */ - NativeVotesType_AutoBalanceOn, /**< TF2: Yes/No, details are ignored */ - NativeVotesType_AutoBalanceOff, /**< TF2: Yes/No, details are ignored */ - NativeVotesType_ClassLimitsOn, /**< TF2: Yes/No, details are ignored */ - NativeVotesType_ClassLimitsOff, /**< TF2: Yes/No, details are ignored */ -}; - -enum NativeVotesPassType -{ - NativeVotesPass_None = 0, /**< Special placeholder for error value */ - NativeVotesPass_Custom, /**< Details are custom pass message */ - NativeVotesPass_ChgCampaign, /**< L4D/L4D2: Details are campaign name */ - NativeVotesPass_ChgDifficulty, /**< L4D/L4D2/TF2: Details are difficulty number in L4D/L4D2 and mission name in TF2 */ - NativeVotesPass_ReturnToLobby, /**< L4D/L4D2: Details are ignored */ - NativeVotesPass_AlltalkOn, /**< L4D2: Details are ignored */ - NativeVotesPass_AlltalkOff, /**< L4D2: Details are ignored */ - NativeVotesPass_Restart, /**< Details are ignored */ - NativeVotesPass_Kick, /**< Details are player name */ - NativeVotesPass_ChgLevel, /**< Details are level number in L4D/L4D2 or map name in TF2/CS:GO */ - NativeVotesPass_NextLevel, /**< TF2/CS:GO: Details are map name */ - NativeVotesPass_Extend, /**< TF2/CS:GO: Details are ignored */ - NativeVotesPass_Scramble, /**< TF2/CS:GO: Details are ignored */ - NativeVotesPass_ChgMission, /**< TF2: Details are popfile */ - NativeVotesPass_SwapTeams, /**< CS:GO: Details are ignored */ - NativeVotesPass_Surrender, /**< CS:GO: Details are ignored */ - NativeVotesPass_Rematch, /**< CS:GO: Details are ignored */ - NativeVotesPass_Continue, /**< CS:GO: Details are ignored */ - NativeVotesPass_StartRound, /**< TF2: Details are ignored */ - NativeVotesPass_Eternaween, /**< TF2: Details are ignored */ - NativeVotesPass_AutoBalanceOn, /**< TF2: Yes/No, details are ignored */ - NativeVotesPass_AutoBalanceOff, /**< TF2: Yes/No, details are ignored */ - NativeVotesPass_ClassLimitsOn, /**< TF2: Yes/No, details are ignored */ - NativeVotesPass_ClassLimitsOff, /**< TF2: Yes/No, details are ignored */ -}; - -/** - * Reasons a vote was canceled. Not used for L4D/L4D2, as they don't care - */ -enum NativeVotesFailType -{ - NativeVotesFail_Generic = 0, /**< Vote was generically cancelled. */ - NativeVotesFail_Loses = 3, /**< No votes outnumbered Yes votes */ - NativeVotesFail_NotEnoughVotes = 4, /**< Vote did not receive enough votes. */ -}; - -/** - * Reasons a callvote command failed. - * This is provided as a convenience to plugin authors as it's not strictly part of the vote system - */ -enum NativeVotesCallFailType -{ - NativeVotesCallFail_Generic = 0, /**< Generic fail. */ - NativeVotesCallFail_Loading = 1, /**< L4D/L4D2: Players are still loading. */ - NativeVotesCallFail_Recent = 2, /**< TF2/CS:GO: You can't call another vote yet: Argument is seconds until you can call another vote. */ - NativeVotesCallFail_Disabled = 5, /**< TF2/CS:GO: Server has disabled that issue. */ - NativeVotesCallFail_MapNotFound = 6, /**< TF2/CS:GO: Server does not have that map. */ - NativeVotesCallFail_SpecifyMap = 7, /**< TF2/CS:GO: You must specify a map. */ - NativeVotesCallFail_Failed = 8, /**< TF2/CS:GO: This vote failed recently. */ - NativeVotesCallFail_WrongTeam = 9, /**< TF2/CS:GO: Team can't call this vote. */ - NativeVotesCallFail_Waiting = 10, /**< TF2/CS:GO: Vote can't be called during Waiting For Players. */ - NativeVotesCallFail_PlayerNotFound = 11, /**< TF2/CS:GO: Player to kick can't be found. Buggy in TF2. */ - NativeVotesCallFail_Unknown = 11, - NativeVotesCallFail_CantKickAdmin = 12, /**< TF2/CS:GO: Can't kick server admin. */ - NativeVotesCallFail_ScramblePending = 13, /**< TF2/CS:GO: Team Scramble is pending.. */ - NativeVotesCallFail_Spectators = 14, /**< TF2/CS:GO: Spectators aren't allowed to call votes. */ - NativeVotesCallFail_LevelSet = 15, /**< TF2/CS:GO: Next level already set. */ - NativeVotesCallFail_MapNotValid = 16, /**< ???: Map is invalid. */ - NativeVotesCallFail_KickTime = 17, /**< ???: Cannot kick for time. */ - NativeVotesCallFail_KickDuringRound = 18, /**< ???: Cannot kick during a round. */ - NativeVotesCallFail_AlreadyActive = 19 /**< TF2: Cannot call vote because modification (Eternaween) is already active (may not work) */ -}; - -/* - * Is a specific vote type supported by this game? - * - * @param voteType Vote type - */ -native bool:NativeVotes_IsVoteTypeSupported(NativeVotesType:voteType); - -/** - * Creates a new, empty vote. - * - * @param handler Function which will receive vote actions. - * @param voteType Vote type, cannot be changed after set - * @param actions Optionally set which actions to receive. Start, - * Cancel, and End will always be received regardless - * of whether they are set or not. They are also - * the only default actions. - * @return A new vote Handle on INVALID_HANDLE if a vote type is unsupported by this game. - */ -native Handle:NativeVotes_Create(MenuHandler:handler, NativeVotesType:voteType, - MenuAction:actions=NATIVEVOTES_ACTIONS_DEFAULT); - -/** - * Frees all handles related to a vote. - * - * THIS MUST BE CALLED TO AVOID HANDLE LEAKS - * - * @param vote Vote handle - * @noreturn - */ -native Handle:NativeVotes_Close(Handle:vote); - -/** - * Appends a new item to the end of a vote. Only valid for Multiple Choice votes - * - * @param vote NativeVotes Handle. - * @param info Item information string. - * @return True on success, false on failure. - * @error Invalid Handle, item limit reached, or if the vote is not multiple choice. - */ -native bool:NativeVotes_AddItem(Handle:vote, const String:info[], const String:display[]); - -/** - * Inserts an item into the menu before a certain position; the new item will - * be at the given position and all next items pushed forward. - * - * @param vote Vote Handle. - * @param position Position, starting from 0. - * @param info Item information string. - * @return True on success, false on failure. - * @error Invalid Handle or vote position, or if the vote is not multiple choice. - */ -native bool:NativeVotes_InsertItem(Handle:vote, position, const String:info[], const String:display[]); - -/** - * Removes an item from the menu. - * - * @param vote Vote Handle. - * @param position Position, starting from 0. - * @return True on success, false on failure. - * @error Invalid Handle or vote position, or if the vote is not multiple choice. - */ -native bool:NativeVotes_RemoveItem(Handle:vote, position); - -/** - * Removes all items from a vote. - * - * @param vote Vote Handle. - * @noreturn - * @error Invalid Handle or vote position, or if the vote is not multiple choice. - */ -native NativeVotes_RemoveAllItems(Handle:vote); - -/** - * Retrieves information about a vote item. - * - * @param vote Vote Handle. - * @param position Position, starting from 0. - * @param infoBuf Info buffer. - * @param infoBufLen Maximum length of the info buffer. - * @return True on success, false if position is invalid. - * @error Invalid Handlem - */ -native bool:NativeVotes_GetItem(Handle:vote, - position, - String:infoBuf[], - infoBufLen, - String:dispBuf[]="", - dispBufLen=0); - -/** - * Returns the number of items in a vote. - * - * @param vote Vote Handle. - * @return Number of items in the vote. - * @error Invalid Handle. - */ -native NativeVotes_GetItemCount(Handle:vote); - -/** - * Sets the vote's details for votes that support details - * If this is a custom vote, use NativeVotes_SetTitle to set the vote's title. - * - * @param vote Vote Handle. - * @param argument Details string. See vote types for what details stands for. - * @noreturn - * @error Invalid Handle. - */ -native NativeVotes_SetDetails(Handle:vote, const String:argument[]); - -/** - * Returns the text of a vote's details if set. - * - * @param vote Vote Handle. - * @param buffer Buffer to store details. - * @param maxlength Maximum length of the buffer. - * @noreturn - * @error Invalid Handle. - */ -native NativeVotes_GetDetails(Handle:vote, String:buffer[], maxlength); - -/** - * Sets a custom vote's title - * - * @param vote Vote Handle. - * @param title Vote title string. - * @noreturn - * @error Invalid Handle. - */ -native NativeVotes_SetTitle(Handle:vote, const String:argument[]); - -/** - * Return the vote's Title. - * If not set, returns Details instead. - * This behavior is for compatibility with NativeVotes 0.8.0 and below. - * - * @param vote Vote Handle. - * @param buffer Buffer to store title. - * @param maxlength Maximum length of the buffer. - * @noreturn - * @error Invalid Handle. - */ -native NativeVotes_GetTitle(Handle:vote, String:buffer[], maxlength); - -/** - * Sets the target userid for vote - * This should be used instead of SetArgument for votes that target players - * - * Also sets target SteamID - * - * @param vote Vote Handle. - * @param userid Client index of target player - * @param setDetails If true, also sets vote details to client's name - * @noreturn - * @error Invalid Handle. - */ -native NativeVotes_SetTarget(Handle:vote, client, bool:setDetails=true); - -/** - * Returns the userid of a vote's target - * - * @param vote Vote Handle. - * @return Client index of target player or 0 for no target or target disconnected since vote started - * @error Invalid Handle. - */ -native NativeVotes_GetTarget(Handle:vote); - -/** - * Get the Steam ID of a vote's target - * Useful if the target has disconnect from the server during a vote. - * This was added in specifically for Kick/Ban votes - * - * @param vote Vote Handle. - * @param buffer Buffer to store steamId. Should be 19 characters or more.. - * @param maxlength Maximum length of the buffer. - * @noreturn - * @error Invalid Handle. - */ -native NativeVotes_GetTargetSteam(Handle:vote, String:buffer[], maxlength); - -/** - * Returns whether a vote is in progress. - * - * @return True if a NativeVotes vote is in progress, false otherwise. - */ -native bool:NativeVotes_IsVoteInProgress(); - -/** - * Returns a style's maximum items - * - * @return Maximum items - */ -native NativeVotes_GetMaxItems(); - -/** - * Sets a vote's option flags. - * - * If a certain bit is not supported, it will be stripped before being set. - * - * NOTE: This is currently unused, but reserved for future use. - * - * @param menu Builtin Vote Handle. - * @param flags A new bitstring of VOTEFLAG bits. - * @noreturn - * @error Invalid Handle. - */ -native NativeVotes_SetOptionFlags(Handle:vote, flags); - -/** - * Retrieves a menu's option flags. - * - * NOTE: This is currently unused, but reserved for future use. - * - * @param vote Builtin Vote Handle. - * @return A bitstring of VOTEFLAG bits. - * @error Invalid Handle. - */ -native NativeVotes_GetOptionFlags(Handle:vote); - -/** - * Cancels the vote in progress. - * - * @noreturn - * @error If no vote is in progress. - */ -native NativeVotes_Cancel(); - -/** - * Callback for when a vote has ended and results are available. - * - * Due to SourceMod Forward limitations in plugins, multi-dimension arrays can't be passed - * to forwards. This means we have to split the client_info and item_info arrays into - * their components. - * - * @param vote The vote being voted on. - * @param num_votes Number of votes tallied in total. - * @param num_clients Number of clients who could vote. - * @param client_indexes Array of client indexes. Parallel with client_votes. - * @param client_votes Array of client votes. Parallel with client_indexes. - * @param num_items Number of unique items that were selected. - * @param item_indexes Array of vote item indexes. Parallel with item_votes.. - * @param item_votes Array of vote vote counts. Parallel with item_indexes. - * @noreturn - */ -functag public NativeVotes_VoteHandler(Handle:vote, - num_votes, - num_clients, - const client_indexes[], - const client_votes[], - num_items, - const item_indexes[], - const item_votes[]); -/** - * Function to convert client/vote arrays into their two-dimensional versions, - * which can then be passed to a standard vote handler. - * - * client_info and item_info are the resulting arrays. - * - * Note: When declaring client_info and item_info, you'll probably want to declare them like this: - * new client_info[num_clients][2]; - * new item_info[num_items][2]; - * - * @param num_clients Number of clients who could vote. - * @param client_indexes Array of client indexes. Parallel with client_votes. - * @param client_votes Array of client votes. Parallel with client_indexes. - * @param num_items Number of unique items that were selected. - * @param item_indexes Array of vote item indexes. Parallel with item_votes.. - * @param item_votes Array of vote vote counts. Parallel with item_indexes. - * @param client_info Array of clients. Use VOTEINFO_CLIENT_ defines. - * @param item_info Array of items, sorted by count. Use VOTEINFO_ITEM - * defines. - * @noreturn - */ -stock NativeVotes_FixResults(num_clients, - const client_indexes[], - const client_votes[], - num_items, - const item_indexes[], - const item_votes[], - client_info[][2], - item_info[][2]) -{ - for (new i = 0; i < num_clients; ++i) - { - client_info[i][VOTEINFO_CLIENT_INDEX] = client_indexes[i]; - client_info[i][VOTEINFO_CLIENT_ITEM] = client_votes[i]; - } - - for (new i = 0; i < num_items; ++i) - { - item_info[i][VOTEINFO_ITEM_INDEX] = item_indexes[i]; - item_info[i][VOTEINFO_ITEM_VOTES] = item_votes[i]; - } -} - -/** - * Sets an advanced vote handling callback. If this callback is set, - * MenuAction_VoteEnd will not be called. - * - * @param vote NativeVotes Handle. - * @param callback Callback function. - * @noreturn - * @error Invalid Handle or callback. - */ -native NativeVotes_SetResultCallback(Handle:vote, NativeVotes_VoteHandler:callback); - -/** - * Returns the number of seconds you should "wait" before displaying - * a public vote. This number is the time remaining until - * (last_vote + sm_vote_delay). - * - * @return Number of seconds to wait, or 0 for none. - */ -native NativeVotes_CheckVoteDelay(); - -/** - * Returns whether a client is in the pool of clients allowed - * to participate in the current vote. This is determined by - * the client list passed to NativeVotes_Display(). - * - * @param client Client index. - * @return True if client is allowed to vote, false otherwise. - * @error If no vote is in progress or client index is invalid. - */ -native bool:NativeVotes_IsClientInVotePool(client); - -/** - * Redraws the current vote to a client in the voting pool. - * - * @param client Client index. - * @param revotes True to allow revotes, false otherwise. - * @return True on success, false if the client is in the vote pool - * but cannot vote again. - * @error No vote in progress, client is not in the voting pool, - * or client index is invalid. - */ -native bool:NativeVotes_RedrawClientVote(client, bool:revotes=true); - -/** - * Retrieve the vote type - * - * @param vote NativeVotes Handle. - * @return The built in vote type - * @error Invalid Handle - */ -native NativeVotesType:NativeVotes_GetType(Handle:vote); - -/** - * Set the team this vote is for, or NATIVEVOTES_ALL_TEAMS for all teams. - * - * Defaults to NATIVEVOTES_ALL_TEAMS if not explicitly set. - * - * @param vote NativeVotes Handle. - * @param team Team number this vote is for - * @noreturn - * @error Invalid Handle - */ -native NativeVotes_SetTeam(Handle:vote, team); - -/** - * Retrieve the team this vote is for - * - * @param vote NativeVotes Handle. - * @return Team index or NATIVEVOTES_ALL_TEAMS for all teams. - * @error Invalid Handle - */ -native NativeVotes_GetTeam(Handle:vote); - -/** - * Set the client index of the player who initiated the vote. - * Use NATIVEVOTES_SERVER_INDEX if initiated by the server itself. - * - * Defaults to NATIVEVOTES_SERVER_INDEX if not explicitly set. - * - * @param vote NativeVotes Handle. - * @param client Client who initiated the vote or NATIVEVOTES_SERVER_INDEX - * @noreturn - * @error Invalid Handle - */ -native NativeVotes_SetInitiator(Handle:vote, client); - -/** - * Retrieve the client index of the player who initiated the vote or NATIVEVOTES_SERVER_INDEX if - * initiated by the server itself. - * - * @param Vote handle - * @return Client index or NATIVEVOTES_SERVER_INDEX - * @error Invalid Handle - */ -native NativeVotes_GetInitiator(Handle:vote); - -/** - * Broadcasts a vote to a list of clients. The most selected item will be - * returned through MenuAction_VoteEnd. On a tie, a random item will be returned - * from a list of the tied items. - * - * Note that MenuAction_VoteStart, MenuAction_VoteCancel, MenuAction_VoteEnd, and MenuAction_End are all - * default callbacks and do not need to be enabled. - * - * @param vote Vote Handle. - * @param clients Array of clients to broadcast to. - * @param numClients Number of clients in the array. - * @param time Maximum time to leave menu on the screen. - * @return True on success, false if a vote is already in progress. - * @error Invalid Handle, or a vote is already in progress. - */ -native bool:NativeVotes_Display(Handle:vote, clients[], numClients, time); - -/** - * Sends a vote menu to all clients. See NativeVotes_Display() for more information. - * - * @param vote Vote Handle. - * @param time Maximum time to leave menu on the screen. - * @return True on success, false if this menu already has a vote session - * in progress. - * @error Invalid Handle, or a vote is already in progress. - */ -stock bool:NativeVotes_DisplayToAll(Handle:vote, time) -{ - new total = 0; - decl players[MaxClients]; - - for (new i=1; i<=MaxClients; i++) - { - if (!IsClientInGame(i) || IsFakeClient(i)) - { - continue; - } - players[total++] = i; - } - - return NativeVotes_Display(vote, players, total, time); -} - -/** - * Sends a vote menu to a single team. See NativeVotes_Display() for more information. - * - * @param vote Vote Handle. - * @param team Team to send vote to. 1 = spectators, 2 = RED/Survivors/Terrorists, 3 = BLU/Infected/Counter-Terrorists - * @param time Maximum time to leave menu on the screen. - * @return True on success, false if this menu already has a vote session - * in progress. - * @error Invalid Handle, or a vote is already in progress. - */ -stock bool:NativeVotes_DisplayToTeam(Handle:vote, team, time) -{ - NativeVotes_SetTeam(vote, team); - - new total; - decl players[MaxClients]; - - for (new i=1; i<=MaxClients; i++) - { - if (!IsClientInGame(i) || IsFakeClient(i) || (GetClientTeam(i) != team)) - { - continue; - } - players[total++] = i; - } - - return NativeVotes_Display(vote, players, total, time); -} - -/** - * Sends a vote menu to all clients who are not spectators or waiting to choose a team. See NativeVotes_Display() for more information. - * - * @param vote Vote Handle. - * @param time Maximum time to leave menu on the screen. - * @return True on success, false if this menu already has a vote session - * in progress. - * @error Invalid Handle, or a vote is already in progress. - */ -stock bool:NativeVotes_DisplayToAllNonSpectators(Handle:vote, time) -{ - new total; - decl players[MaxClients]; - - for (new i=1; i<=MaxClients; i++) - { - if (!IsClientInGame(i) || IsFakeClient(i) || (GetClientTeam(i) < 2)) - { - continue; - } - players[total++] = i; - } - - return NativeVotes_Display(vote, players, total, time); -} - -/** - * Display vote passed screen - * - * You MUST call one of the NativeVotesDisplayPass* or NativeVotes_DisplayFail functions - * to hide the vote screen for users who didn't vote, and to clear out their selection - * for the next vote. - * - * @param vote Vote handle - * @param details Normally the item that won the vote or format string. Also used for custom vote winners - * @param ... Variable number of format parameters. - * @noreturn - */ -native NativeVotes_DisplayPass(Handle:vote, const String:details[]=""); - -/** - * Display vote passed screen with custom text to a single user - * - * You MUST call one of the NativeVotesDisplayPass* or NativeVotes_DisplayFail functions - * to hide the vote screen for users who didn't vote, and to clear out their selection - * for the next vote. - * - * @param vote Vote handle - * @param client client to display to - * @param format A format string. - * @param any Variable number of format parameters - * @noreturn - */ -native NativeVotes_DisplayPassCustomToOne(Handle:vote, client, const String:format[], any:...); - -/** - * Display vote passed screen with custom text - * - * You MUST call one of the NativeVotesDisplayPass* or NativeVotes_DisplayFail functions - * to hide the vote screen for users who didn't vote, and to clear out their selection - * for the next vote. - * - * @param vote Vote handle - * @param format A format string. - * @param any Variable number of format parameters - * @noreturn - */ -stock NativeVotes_DisplayPassCustom(Handle:vote, const String:format[], any:...) -{ - decl String:buffer[192]; - - for (new i = 1; i <= MaxClients; ++i) - { - if (IsClientInGame(i)) - { - SetGlobalTransTarget(i); - VFormat(buffer, sizeof(buffer), format, 3); - NativeVotes_DisplayPassCustomToOne(vote, i, "%s", buffer); - } - } -} - -/** - * Display vote passed screen with a custom type. - * - * A sample usage of this would be if Extend won an RTV vote: NativeVotes_DisplayPassEx(vote, NativeVotesPass_Extend, map); - * - * You MUST call one of NativeVotes_DisplayPass, NativeVotes_DisplayPassEx, - * or NativeVotes_DisplayFail to hide the vote screen for users who didn't vote - * and to clear out their selection for the next vote. - * - * #param vote Vote handle - * @param passType The pass screen to display - * @param details Normally the item that won the vote. Also used for custom vote winners - * @noreturn - */ -native NativeVotes_DisplayPassEx(Handle:vote, NativeVotesPassType:passType, const String:details[]=""); - -/** - * Display failure screen. - * - * You MUST call one of NativeVotes_DisplayPass, NativeVotes_DisplayPassEx, - * or NativeVotes_DisplayFail to hide the vote screen for users who didn't vote, - * and to clear out their selection for the next vote. - * - * @param reason Vote failure reason from NativeVotesFailType enum - * @noreturn - */ -native NativeVotes_DisplayFail(Handle:vote, NativeVotesFailType:reason=NativeVotesFail_Generic); - -/** - * Quick stock to determine whether voting is allowed. This doesn't let you - * fine-tune a reason for not voting, so it's not recommended for lazily - * telling clients that voting isn't allowed. - * - * @return True if voting is allowed, false if voting is in progress - * or the cooldown is active. - */ -stock bool:NativeVotes_IsNewVoteAllowed() -{ - if (NativeVotes_IsVoteInProgress() || NativeVotes_CheckVoteDelay() != 0) - { - return false; - } - - return true; -} - -/** - * Used when callvote is called with no arguments. - * - * This is used to configure the VoteSetup usermessage on TF2 and CS:GO - * - * @param client Client, in case the votes are restricted by client - * @param voteTypes Populate this array with the vote types this server supports - * Custom and multiple choice votes are not supported from - * the GUI and are thus ignored. - * @return Plugin_Continue to allow the server itself (or another plugin) to process the callvote - * Plugin_Changed if you're changing the voteTypes, - * Plugin_Handled to return a blank VoteSetup usermessage - * Plugin_Stop to prevent VoteSetup usermessage (not recommended) - */ -//functag public Action:NativeVotes_CallVoteSetupHandler(client, NativeVotesType:voteTypes[]); - -/** - * Forward for "callvote" handling - * - * You should respond to this by starting a vote or by calling NativeVotes_DisplayCallVoteFail - * - * @param client Client - * @param voteType Type of vote being called. This will NEVER be a multiple-choice or custom vote. - * @param voteArgument Vote argument or blank if the vote type has no argument. - * @param target target userid for kick votes or 0 for all other votes - * @return Plugin_Continue to allow the server itself (or another plugin) to process the callvote - * Plugin_Handled if you processed this vote type - * Plugin_Stop to block the vote type (not recommended) - */ -//functag public Action:NativeVotes_CallVoteHandler(client, NativeVotesType:voteType, const String:voteArgument[], target); - -/** - * Register a plugin as a vote manager. - * This is used to abstract away the details of the callvote command. - * - * @param callHandler Handler for callvote commands. - * @param setupHandler Handler to override the which vote types your server supports. Only useful on TF2 and CS:GO. - * @noreturn - */ -//native NativeVotes_RegisterVoteManager(NativeVotes_CallVoteHandler:callHandler, NativeVotes_CallVoteSetupHandler:setupHandler=INVALID_FUNCTION); - -/** - * Send a call vote fail screen to a user - * Used to respond to a callvote with invalid arguments or for other reasons - * (such as trying to target an admin for a kick/ban vote) - * - * @param client The client to display the fail screen to - * @param reason A vote call fail reason - * @param time For NativeVotesCallFail_Recent, the number of seconds until the vote - * can be called again - */ -native NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFailType:reason, time); - -/** - * Redraws the vote title from inside a MenuAction_Display callback - * Not supported on L4D - * - * @param text Vote title to draw - * @error If called from outside MenuAction_Display - * @return Plugin_Changed if the change is allowed, Plugin_Continue if it isn't. - */ -native Action:NativeVotes_RedrawVoteTitle(const String:text[]); - -/** - * Redraws the vote text from inside a MenuAction_DisplayItem callback. - * Only supported on multiple-choice votes - * - * @param text Vote text to draw. - * @error If called from outside MenuAction_DisplayItem - * @return Plugin_Changed if the change is allowed, Plugin_Continue if it isn't. - */ -native Action:NativeVotes_RedrawVoteItem(const String:text[]); - -/** - * Retrieves voting information from MenuAction_VoteEnd. - * - * @param param2 Second parameter of MenuAction_VoteEnd. - * @param winningVotes Number of votes received by the winning option. - * @param totalVotes Number of total votes received. - * @noreturn - */ -stock NativeVotes_GetInfo(param2, &winningVotes, &totalVotes) -{ - winningVotes = param2 & 0xFFFF; - totalVotes = param2 >> 16; -} - -/** - * Do not edit below this line! - */ -public SharedPlugin:__pl_nativevotes = -{ - name = "nativevotes", - file = "nativevotes.smx", -#if defined REQUIRE_PLUGINS - required = 1, -#else - required = 0, -#endif -}; - -public __pl_nativevotes_SetNTVOptional() -{ - MarkNativeAsOptional("NativeVotes_IsVoteTypeSupported"); - MarkNativeAsOptional("NativeVotes_Create"); - MarkNativeAsOptional("NativeVotes_Close"); - MarkNativeAsOptional("NativeVotes_AddItem"); - MarkNativeAsOptional("NativeVotes_InsertItem"); - MarkNativeAsOptional("NativeVotes_RemoveItem"); - MarkNativeAsOptional("NativeVotes_RemoveAllItems"); - MarkNativeAsOptional("NativeVotes_GetItem"); - MarkNativeAsOptional("NativeVotes_GetItemCount"); - MarkNativeAsOptional("NativeVotes_SetDetails"); - MarkNativeAsOptional("NativeVotes_GetDetails"); - MarkNativeAsOptional("NativeVotes_SetTitle"); - MarkNativeAsOptional("NativeVotes_GetTitle"); - MarkNativeAsOptional("NativeVotes_SetTarget"); - MarkNativeAsOptional("NativeVotes_GetTarget"); - MarkNativeAsOptional("NativeVotes_GetTargetSteam"); - MarkNativeAsOptional("NativeVotes_IsVoteInProgress"); - MarkNativeAsOptional("NativeVotes_GetMaxItems"); - MarkNativeAsOptional("NativeVotes_SetOptionFlags"); - MarkNativeAsOptional("NativeVotes_GetOptionFlags"); - MarkNativeAsOptional("NativeVotes_Cancel"); - MarkNativeAsOptional("NativeVotes_SetResultCallback"); - MarkNativeAsOptional("NativeVotes_CheckVoteDelay"); - MarkNativeAsOptional("NativeVotes_IsClientInVotePool"); - MarkNativeAsOptional("NativeVotes_RedrawClientVote"); - MarkNativeAsOptional("NativeVotes_RedrawClientVote"); - MarkNativeAsOptional("NativeVotes_GetType"); - MarkNativeAsOptional("NativeVotes_SetTeam"); - MarkNativeAsOptional("NativeVotes_GetTeam"); - MarkNativeAsOptional("NativeVotes_SetInitiator"); - MarkNativeAsOptional("NativeVotes_GetInitiator"); - MarkNativeAsOptional("NativeVotes_Display"); - MarkNativeAsOptional("NativeVotes_DisplayPass"); - MarkNativeAsOptional("NativeVotes_DisplayPassCustomToOne"); - MarkNativeAsOptional("NativeVotes_DisplayPassEx"); - MarkNativeAsOptional("NativeVotes_DisplayFail"); - MarkNativeAsOptional("NativeVotes_RegisterVoteManager"); - MarkNativeAsOptional("NativeVotes_DisplayCallVoteFail"); - MarkNativeAsOptional("NativeVotes_RedrawVoteTitle"); - MarkNativeAsOptional("NativeVotes_RedrawVoteItem"); -} +/** + * vim: set ts=4 : + * ============================================================================= + * NativeVotes + * Copyright (C) 2011-2013 Ross Bemrose (Powerlord). All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#include +#include + +// NativeVotes 0.8 series + +#if defined _nativevotes_included + #endinput +#endif +#define _nativevotes_included + +#define CLIENT_DISCONNECTED -1 + +#define NATIVEVOTES_EXTEND "Extend current Map" /** Defined in TF2, but doesn't appear to be localized */ + +#define NATIVEVOTES_ALL_TEAMS -1 // Defined by TF2, may be the same in L4D/L4D2 +#define NATIVEVOTES_TF2_ALL_TEAMS 0 // Defined by TF2, may be the same in L4D/L4D2 +#define NATIVEVOTES_TEAM_UNASSIGNED 0 // For completeness, do not otherwise use +#define NATIVEVOTES_TEAM_SPECTATOR 1 // Spectators +#define NATIVEVOTES_TEAM_1 2 // RED/Survivors/Terrorists +#define NATIVEVOTES_TEAM_2 3 // BLU/Infected/Counter-Terrorists + +#define NATIVEVOTES_SERVER_INDEX 99 // Defined by TF2, may be the same in L4D/L4D2 + +// These may seem backwards, but this is the order that the votes appear in the vote screen +#define NATIVEVOTES_VOTE_INVALID -1 /**< Vote was invalid, currently only valid internally */ +#define NATIVEVOTES_VOTE_YES 0 /**< Vote was yes */ +#define NATIVEVOTES_VOTE_NO 1 /**< Vote was no */ + +/* +The following MenuActions are supported. Arguments are also listed, as they differ slightly from the default +MenuAction_Start A menu has been started (nothing passed). Only exists for compat reasons. +MenuAction_Display A menu is about to be displayed (param1=client). If you choose to change the vote text, + To change the text, use RedrawVoteTitle() + If you do so, return 1 or _:Plugin_Changed Otherwise, return _:Plugin_Continue or 0. +MenuAction_Select An item was selected (param1=client, param2=item). For subplugin support. +MenuAction_End A vote has fully ended and the vote object is ready to be cleaned up + param1 is MenuEnd reason, either MenuEnd_VotingCancelled or MenuEnd_VotingDone +MenuAction_VoteEnd A vote sequence has succeeded (param1=chosen item) + This is not called if NativeVotes_SetResultCallback has been used on the vote. + You should call NativeVotes_DisplayPass or NativeVotes_DisplayPassEx after this +MenuAction_VoteStart A vote sequence has started (nothing passed). Use this instead of MenuAction_Start +MenuAction_VoteCancel A vote sequence has been cancelled (param1=reason) +MenuAction_DisplayItem Item text is being drawn to the display (param1=client, param2=item) + To change the text, use RedrawVoteItem(). + If you do so, return 1 or _:Plugin_Changed. Otherwise, return _:Plugin_Continue or 0. +*/ + +#define NATIVEVOTES_ACTIONS_DEFAULT MenuAction_VoteStart|MenuAction_VoteCancel|MenuAction_VoteEnd|MenuAction_End + +/** + * Vote types. These are mapped to translation strings and pass strings by VoteStart and VotePass handlers + */ +enum NativeVotesType +{ + NativeVotesType_None = 0, /** Special placeholder for callvote with no arguments for NativeVotes_OnCallVote */ + NativeVotesType_Custom_YesNo, /**< Yes/No, details are vote text. */ + NativeVotesType_Custom_Mult, /**< TF2/CS:GO: Multiple-choice, details are vote text. */ + NativeVotesType_ChgCampaign, /**< L4D/L4D2: Yes/No, details are campaign name */ + NativeVotesType_ChgDifficulty, /**< L4D/L4D2: Yes/No, details are difficulty number in L4D/L4D2 */ + NativeVotesType_ReturnToLobby, /**< L4D/L4D2: Yes/No, details are ignored */ + NativeVotesType_AlltalkOn, /**< L4D2: Yes/No, details are ignored (handled internally by extension) */ + NativeVotesType_AlltalkOff, /**< L4D2: Yes/No, details are ignored (handled internally by extension) */ + NativeVotesType_Restart, /**< Yes/No, details are ignored */ + NativeVotesType_Kick, /**< Yes/No, target is player userid, details are auto-set by target */ + NativeVotesType_KickIdle, /**< TF2/CS:GO: Yes/No, target is player userid, details are auto-set by target */ + NativeVotesType_KickScamming, /**< TF2/CS:GO: Yes/No, target is player userid, details are auto-set by target */ + NativeVotesType_KickCheating, /**< TF2/CS:GO: Yes/No, target is player userid, details are auto-set by target */ + NativeVotesType_ChgLevel, /**< Yes/No, details are level number in L4D/L4D2 or map name in TF2 */ + NativeVotesType_NextLevel, /**< TF2/CS:GO: Yes/No, details are map name */ + NativeVotesType_NextLevelMult, /**< TF2/CS:GO: Multiple-choice, details are ignored */ + NativeVotesType_ScrambleNow, /**< TF2/CS:GO: Yes/No, details are ignored */ + NativeVotesType_ScrambleEnd, /**< TF2: Yes/No, details are ignored */ + NativeVotesType_ChgMission, /**< TF2: Yes/No, details are popfile */ + NativeVotesType_SwapTeams, /**< CS:GO: Yes/No, details are ignored */ + NativeVotesType_Surrender, /**< CS:GO: Yes/No, details are ignored */ + NativeVotesType_Rematch, /**< CS:GO: Yes/No, details are ignored */ + NativeVotesType_Continue, /**< CS:GO: Yes/No, details are ignored */ + NativeVotesType_StartRound, /**< TF2: Yes/No, details are ignored */ + NativeVotesType_Eternaween, /**< TF2: Yes/No, details are ignored */ + NativeVotesType_AutoBalanceOn, /**< TF2: Yes/No, details are ignored */ + NativeVotesType_AutoBalanceOff, /**< TF2: Yes/No, details are ignored */ + NativeVotesType_ClassLimitsOn, /**< TF2: Yes/No, details are ignored */ + NativeVotesType_ClassLimitsOff, /**< TF2: Yes/No, details are ignored */ +}; + +enum NativeVotesPassType +{ + NativeVotesPass_None = 0, /**< Special placeholder for error value */ + NativeVotesPass_Custom, /**< Details are custom pass message */ + NativeVotesPass_ChgCampaign, /**< L4D/L4D2: Details are campaign name */ + NativeVotesPass_ChgDifficulty, /**< L4D/L4D2/TF2: Details are difficulty number in L4D/L4D2 and mission name in TF2 */ + NativeVotesPass_ReturnToLobby, /**< L4D/L4D2: Details are ignored */ + NativeVotesPass_AlltalkOn, /**< L4D2: Details are ignored */ + NativeVotesPass_AlltalkOff, /**< L4D2: Details are ignored */ + NativeVotesPass_Restart, /**< Details are ignored */ + NativeVotesPass_Kick, /**< Details are player name */ + NativeVotesPass_ChgLevel, /**< Details are level number in L4D/L4D2 or map name in TF2/CS:GO */ + NativeVotesPass_NextLevel, /**< TF2/CS:GO: Details are map name */ + NativeVotesPass_Extend, /**< TF2/CS:GO: Details are ignored */ + NativeVotesPass_Scramble, /**< TF2/CS:GO: Details are ignored */ + NativeVotesPass_ChgMission, /**< TF2: Details are popfile */ + NativeVotesPass_SwapTeams, /**< CS:GO: Details are ignored */ + NativeVotesPass_Surrender, /**< CS:GO: Details are ignored */ + NativeVotesPass_Rematch, /**< CS:GO: Details are ignored */ + NativeVotesPass_Continue, /**< CS:GO: Details are ignored */ + NativeVotesPass_StartRound, /**< TF2: Details are ignored */ + NativeVotesPass_Eternaween, /**< TF2: Details are ignored */ + NativeVotesPass_AutoBalanceOn, /**< TF2: Yes/No, details are ignored */ + NativeVotesPass_AutoBalanceOff, /**< TF2: Yes/No, details are ignored */ + NativeVotesPass_ClassLimitsOn, /**< TF2: Yes/No, details are ignored */ + NativeVotesPass_ClassLimitsOff, /**< TF2: Yes/No, details are ignored */ +}; + +/** + * Reasons a vote was canceled. Not used for L4D/L4D2, as they don't care + */ +enum NativeVotesFailType +{ + NativeVotesFail_Generic = 0, /**< Vote was generically cancelled. */ + NativeVotesFail_Loses = 3, /**< No votes outnumbered Yes votes */ + NativeVotesFail_NotEnoughVotes = 4, /**< Vote did not receive enough votes. */ +}; + +/** + * Reasons a callvote command failed. + * This is provided as a convenience to plugin authors as it's not strictly part of the vote system + */ +enum NativeVotesCallFailType +{ + NativeVotesCallFail_Generic = 0, /**< Generic fail. */ + NativeVotesCallFail_Loading = 1, /**< L4D/L4D2: Players are still loading. */ + NativeVotesCallFail_Recent = 2, /**< TF2/CS:GO: You can't call another vote yet: Argument is seconds until you can call another vote. */ + NativeVotesCallFail_Disabled = 5, /**< TF2/CS:GO: Server has disabled that issue. */ + NativeVotesCallFail_MapNotFound = 6, /**< TF2/CS:GO: Server does not have that map. */ + NativeVotesCallFail_SpecifyMap = 7, /**< TF2/CS:GO: You must specify a map. */ + NativeVotesCallFail_Failed = 8, /**< TF2/CS:GO: This vote failed recently. */ + NativeVotesCallFail_WrongTeam = 9, /**< TF2/CS:GO: Team can't call this vote. */ + NativeVotesCallFail_Waiting = 10, /**< TF2/CS:GO: Vote can't be called during Waiting For Players. */ + NativeVotesCallFail_PlayerNotFound = 11, /**< TF2/CS:GO: Player to kick can't be found. Buggy in TF2. */ + NativeVotesCallFail_Unknown = 11, + NativeVotesCallFail_CantKickAdmin = 12, /**< TF2/CS:GO: Can't kick server admin. */ + NativeVotesCallFail_ScramblePending = 13, /**< TF2/CS:GO: Team Scramble is pending.. */ + NativeVotesCallFail_Spectators = 14, /**< TF2/CS:GO: Spectators aren't allowed to call votes. */ + NativeVotesCallFail_LevelSet = 15, /**< TF2/CS:GO: Next level already set. */ + NativeVotesCallFail_MapNotValid = 16, /**< ???: Map is invalid. */ + NativeVotesCallFail_KickTime = 17, /**< ???: Cannot kick for time. */ + NativeVotesCallFail_KickDuringRound = 18, /**< ???: Cannot kick during a round. */ + NativeVotesCallFail_AlreadyActive = 19 /**< TF2: Cannot call vote because modification (Eternaween) is already active (may not work) */ +}; + +/* + * Is a specific vote type supported by this game? + * + * @param voteType Vote type + */ +native bool:NativeVotes_IsVoteTypeSupported(NativeVotesType:voteType); + +/** + * Creates a new, empty vote. + * + * @param handler Function which will receive vote actions. + * @param voteType Vote type, cannot be changed after set + * @param actions Optionally set which actions to receive. Start, + * Cancel, and End will always be received regardless + * of whether they are set or not. They are also + * the only default actions. + * @return A new vote Handle on INVALID_HANDLE if a vote type is unsupported by this game. + */ +native Handle:NativeVotes_Create(MenuHandler:handler, NativeVotesType:voteType, + MenuAction:actions=NATIVEVOTES_ACTIONS_DEFAULT); + +/** + * Frees all handles related to a vote. + * + * THIS MUST BE CALLED TO AVOID HANDLE LEAKS + * + * @param vote Vote handle + * @noreturn + */ +native Handle:NativeVotes_Close(Handle:vote); + +/** + * Appends a new item to the end of a vote. Only valid for Multiple Choice votes + * + * @param vote NativeVotes Handle. + * @param info Item information string. + * @return True on success, false on failure. + * @error Invalid Handle, item limit reached, or if the vote is not multiple choice. + */ +native bool:NativeVotes_AddItem(Handle:vote, const String:info[], const String:display[]); + +/** + * Inserts an item into the menu before a certain position; the new item will + * be at the given position and all next items pushed forward. + * + * @param vote Vote Handle. + * @param position Position, starting from 0. + * @param info Item information string. + * @return True on success, false on failure. + * @error Invalid Handle or vote position, or if the vote is not multiple choice. + */ +native bool:NativeVotes_InsertItem(Handle:vote, position, const String:info[], const String:display[]); + +/** + * Removes an item from the menu. + * + * @param vote Vote Handle. + * @param position Position, starting from 0. + * @return True on success, false on failure. + * @error Invalid Handle or vote position, or if the vote is not multiple choice. + */ +native bool:NativeVotes_RemoveItem(Handle:vote, position); + +/** + * Removes all items from a vote. + * + * @param vote Vote Handle. + * @noreturn + * @error Invalid Handle or vote position, or if the vote is not multiple choice. + */ +native NativeVotes_RemoveAllItems(Handle:vote); + +/** + * Retrieves information about a vote item. + * + * @param vote Vote Handle. + * @param position Position, starting from 0. + * @param infoBuf Info buffer. + * @param infoBufLen Maximum length of the info buffer. + * @return True on success, false if position is invalid. + * @error Invalid Handlem + */ +native bool:NativeVotes_GetItem(Handle:vote, + position, + String:infoBuf[], + infoBufLen, + String:dispBuf[]="", + dispBufLen=0); + +/** + * Returns the number of items in a vote. + * + * @param vote Vote Handle. + * @return Number of items in the vote. + * @error Invalid Handle. + */ +native NativeVotes_GetItemCount(Handle:vote); + +/** + * Sets the vote's details for votes that support details + * If this is a custom vote, use NativeVotes_SetTitle to set the vote's title. + * + * @param vote Vote Handle. + * @param argument Details string. See vote types for what details stands for. + * @noreturn + * @error Invalid Handle. + */ +native NativeVotes_SetDetails(Handle:vote, const String:argument[]); + +/** + * Returns the text of a vote's details if set. + * + * @param vote Vote Handle. + * @param buffer Buffer to store details. + * @param maxlength Maximum length of the buffer. + * @noreturn + * @error Invalid Handle. + */ +native NativeVotes_GetDetails(Handle:vote, String:buffer[], maxlength); + +/** + * Sets a custom vote's title + * + * @param vote Vote Handle. + * @param title Vote title string. + * @noreturn + * @error Invalid Handle. + */ +native NativeVotes_SetTitle(Handle:vote, const String:argument[]); + +/** + * Return the vote's Title. + * If not set, returns Details instead. + * This behavior is for compatibility with NativeVotes 0.8.0 and below. + * + * @param vote Vote Handle. + * @param buffer Buffer to store title. + * @param maxlength Maximum length of the buffer. + * @noreturn + * @error Invalid Handle. + */ +native NativeVotes_GetTitle(Handle:vote, String:buffer[], maxlength); + +/** + * Sets the target userid for vote + * This should be used instead of SetArgument for votes that target players + * + * Also sets target SteamID + * + * @param vote Vote Handle. + * @param userid Client index of target player + * @param setDetails If true, also sets vote details to client's name + * @noreturn + * @error Invalid Handle. + */ +native NativeVotes_SetTarget(Handle:vote, client, bool:setDetails=true); + +/** + * Returns the userid of a vote's target + * + * @param vote Vote Handle. + * @return Client index of target player or 0 for no target or target disconnected since vote started + * @error Invalid Handle. + */ +native NativeVotes_GetTarget(Handle:vote); + +/** + * Get the Steam ID of a vote's target + * Useful if the target has disconnect from the server during a vote. + * This was added in specifically for Kick/Ban votes + * + * @param vote Vote Handle. + * @param buffer Buffer to store steamId. Should be 19 characters or more.. + * @param maxlength Maximum length of the buffer. + * @noreturn + * @error Invalid Handle. + */ +native NativeVotes_GetTargetSteam(Handle:vote, String:buffer[], maxlength); + +/** + * Returns whether a vote is in progress. + * + * @return True if a NativeVotes vote is in progress, false otherwise. + */ +native bool:NativeVotes_IsVoteInProgress(); + +/** + * Returns a style's maximum items + * + * @return Maximum items + */ +native NativeVotes_GetMaxItems(); + +/** + * Sets a vote's option flags. + * + * If a certain bit is not supported, it will be stripped before being set. + * + * NOTE: This is currently unused, but reserved for future use. + * + * @param menu Builtin Vote Handle. + * @param flags A new bitstring of VOTEFLAG bits. + * @noreturn + * @error Invalid Handle. + */ +native NativeVotes_SetOptionFlags(Handle:vote, flags); + +/** + * Retrieves a menu's option flags. + * + * NOTE: This is currently unused, but reserved for future use. + * + * @param vote Builtin Vote Handle. + * @return A bitstring of VOTEFLAG bits. + * @error Invalid Handle. + */ +native NativeVotes_GetOptionFlags(Handle:vote); + +/** + * Cancels the vote in progress. + * + * @noreturn + * @error If no vote is in progress. + */ +native NativeVotes_Cancel(); + +/** + * Callback for when a vote has ended and results are available. + * + * Due to SourceMod Forward limitations in plugins, multi-dimension arrays can't be passed + * to forwards. This means we have to split the client_info and item_info arrays into + * their components. + * + * @param vote The vote being voted on. + * @param num_votes Number of votes tallied in total. + * @param num_clients Number of clients who could vote. + * @param client_indexes Array of client indexes. Parallel with client_votes. + * @param client_votes Array of client votes. Parallel with client_indexes. + * @param num_items Number of unique items that were selected. + * @param item_indexes Array of vote item indexes. Parallel with item_votes.. + * @param item_votes Array of vote vote counts. Parallel with item_indexes. + * @noreturn + */ +functag public NativeVotes_VoteHandler(Handle:vote, + num_votes, + num_clients, + const client_indexes[], + const client_votes[], + num_items, + const item_indexes[], + const item_votes[]); +/** + * Function to convert client/vote arrays into their two-dimensional versions, + * which can then be passed to a standard vote handler. + * + * client_info and item_info are the resulting arrays. + * + * Note: When declaring client_info and item_info, you'll probably want to declare them like this: + * new client_info[num_clients][2]; + * new item_info[num_items][2]; + * + * @param num_clients Number of clients who could vote. + * @param client_indexes Array of client indexes. Parallel with client_votes. + * @param client_votes Array of client votes. Parallel with client_indexes. + * @param num_items Number of unique items that were selected. + * @param item_indexes Array of vote item indexes. Parallel with item_votes.. + * @param item_votes Array of vote vote counts. Parallel with item_indexes. + * @param client_info Array of clients. Use VOTEINFO_CLIENT_ defines. + * @param item_info Array of items, sorted by count. Use VOTEINFO_ITEM + * defines. + * @noreturn + */ +stock NativeVotes_FixResults(num_clients, + const client_indexes[], + const client_votes[], + num_items, + const item_indexes[], + const item_votes[], + client_info[][2], + item_info[][2]) +{ + for (new i = 0; i < num_clients; ++i) + { + client_info[i][VOTEINFO_CLIENT_INDEX] = client_indexes[i]; + client_info[i][VOTEINFO_CLIENT_ITEM] = client_votes[i]; + } + + for (new i = 0; i < num_items; ++i) + { + item_info[i][VOTEINFO_ITEM_INDEX] = item_indexes[i]; + item_info[i][VOTEINFO_ITEM_VOTES] = item_votes[i]; + } +} + +/** + * Sets an advanced vote handling callback. If this callback is set, + * MenuAction_VoteEnd will not be called. + * + * @param vote NativeVotes Handle. + * @param callback Callback function. + * @noreturn + * @error Invalid Handle or callback. + */ +native NativeVotes_SetResultCallback(Handle:vote, NativeVotes_VoteHandler:callback); + +/** + * Returns the number of seconds you should "wait" before displaying + * a public vote. This number is the time remaining until + * (last_vote + sm_vote_delay). + * + * @return Number of seconds to wait, or 0 for none. + */ +native NativeVotes_CheckVoteDelay(); + +/** + * Returns whether a client is in the pool of clients allowed + * to participate in the current vote. This is determined by + * the client list passed to NativeVotes_Display(). + * + * @param client Client index. + * @return True if client is allowed to vote, false otherwise. + * @error If no vote is in progress or client index is invalid. + */ +native bool:NativeVotes_IsClientInVotePool(client); + +/** + * Redraws the current vote to a client in the voting pool. + * + * @param client Client index. + * @param revotes True to allow revotes, false otherwise. + * @return True on success, false if the client is in the vote pool + * but cannot vote again. + * @error No vote in progress, client is not in the voting pool, + * or client index is invalid. + */ +native bool:NativeVotes_RedrawClientVote(client, bool:revotes=true); + +/** + * Retrieve the vote type + * + * @param vote NativeVotes Handle. + * @return The built in vote type + * @error Invalid Handle + */ +native NativeVotesType:NativeVotes_GetType(Handle:vote); + +/** + * Set the team this vote is for, or NATIVEVOTES_ALL_TEAMS for all teams. + * + * Defaults to NATIVEVOTES_ALL_TEAMS if not explicitly set. + * + * @param vote NativeVotes Handle. + * @param team Team number this vote is for + * @noreturn + * @error Invalid Handle + */ +native NativeVotes_SetTeam(Handle:vote, team); + +/** + * Retrieve the team this vote is for + * + * @param vote NativeVotes Handle. + * @return Team index or NATIVEVOTES_ALL_TEAMS for all teams. + * @error Invalid Handle + */ +native NativeVotes_GetTeam(Handle:vote); + +/** + * Set the client index of the player who initiated the vote. + * Use NATIVEVOTES_SERVER_INDEX if initiated by the server itself. + * + * Defaults to NATIVEVOTES_SERVER_INDEX if not explicitly set. + * + * @param vote NativeVotes Handle. + * @param client Client who initiated the vote or NATIVEVOTES_SERVER_INDEX + * @noreturn + * @error Invalid Handle + */ +native NativeVotes_SetInitiator(Handle:vote, client); + +/** + * Retrieve the client index of the player who initiated the vote or NATIVEVOTES_SERVER_INDEX if + * initiated by the server itself. + * + * @param Vote handle + * @return Client index or NATIVEVOTES_SERVER_INDEX + * @error Invalid Handle + */ +native NativeVotes_GetInitiator(Handle:vote); + +/** + * Broadcasts a vote to a list of clients. The most selected item will be + * returned through MenuAction_VoteEnd. On a tie, a random item will be returned + * from a list of the tied items. + * + * Note that MenuAction_VoteStart, MenuAction_VoteCancel, MenuAction_VoteEnd, and MenuAction_End are all + * default callbacks and do not need to be enabled. + * + * @param vote Vote Handle. + * @param clients Array of clients to broadcast to. + * @param numClients Number of clients in the array. + * @param time Maximum time to leave menu on the screen. + * @return True on success, false if a vote is already in progress. + * @error Invalid Handle, or a vote is already in progress. + */ +native bool:NativeVotes_Display(Handle:vote, clients[], numClients, time); + +/** + * Sends a vote menu to all clients. See NativeVotes_Display() for more information. + * + * @param vote Vote Handle. + * @param time Maximum time to leave menu on the screen. + * @return True on success, false if this menu already has a vote session + * in progress. + * @error Invalid Handle, or a vote is already in progress. + */ +stock bool:NativeVotes_DisplayToAll(Handle:vote, time) +{ + new total = 0; + decl players[MaxClients]; + + for (new i=1; i<=MaxClients; i++) + { + if (!IsClientInGame(i) || IsFakeClient(i)) + { + continue; + } + players[total++] = i; + } + + return NativeVotes_Display(vote, players, total, time); +} + +/** + * Sends a vote menu to a single team. See NativeVotes_Display() for more information. + * + * @param vote Vote Handle. + * @param team Team to send vote to. 1 = spectators, 2 = RED/Survivors/Terrorists, 3 = BLU/Infected/Counter-Terrorists + * @param time Maximum time to leave menu on the screen. + * @return True on success, false if this menu already has a vote session + * in progress. + * @error Invalid Handle, or a vote is already in progress. + */ +stock bool:NativeVotes_DisplayToTeam(Handle:vote, team, time) +{ + NativeVotes_SetTeam(vote, team); + + new total; + decl players[MaxClients]; + + for (new i=1; i<=MaxClients; i++) + { + if (!IsClientInGame(i) || IsFakeClient(i) || (GetClientTeam(i) != team)) + { + continue; + } + players[total++] = i; + } + + return NativeVotes_Display(vote, players, total, time); +} + +/** + * Sends a vote menu to all clients who are not spectators or waiting to choose a team. See NativeVotes_Display() for more information. + * + * @param vote Vote Handle. + * @param time Maximum time to leave menu on the screen. + * @return True on success, false if this menu already has a vote session + * in progress. + * @error Invalid Handle, or a vote is already in progress. + */ +stock bool:NativeVotes_DisplayToAllNonSpectators(Handle:vote, time) +{ + new total; + decl players[MaxClients]; + + for (new i=1; i<=MaxClients; i++) + { + if (!IsClientInGame(i) || IsFakeClient(i) || (GetClientTeam(i) < 2)) + { + continue; + } + players[total++] = i; + } + + return NativeVotes_Display(vote, players, total, time); +} + +/** + * Display vote passed screen + * + * You MUST call one of the NativeVotesDisplayPass* or NativeVotes_DisplayFail functions + * to hide the vote screen for users who didn't vote, and to clear out their selection + * for the next vote. + * + * @param vote Vote handle + * @param details Normally the item that won the vote or format string. Also used for custom vote winners + * @param ... Variable number of format parameters. + * @noreturn + */ +native NativeVotes_DisplayPass(Handle:vote, const String:details[]=""); + +/** + * Display vote passed screen with custom text to a single user + * + * You MUST call one of the NativeVotesDisplayPass* or NativeVotes_DisplayFail functions + * to hide the vote screen for users who didn't vote, and to clear out their selection + * for the next vote. + * + * @param vote Vote handle + * @param client client to display to + * @param format A format string. + * @param any Variable number of format parameters + * @noreturn + */ +native NativeVotes_DisplayPassCustomToOne(Handle:vote, client, const String:format[], any:...); + +/** + * Display vote passed screen with custom text + * + * You MUST call one of the NativeVotesDisplayPass* or NativeVotes_DisplayFail functions + * to hide the vote screen for users who didn't vote, and to clear out their selection + * for the next vote. + * + * @param vote Vote handle + * @param format A format string. + * @param any Variable number of format parameters + * @noreturn + */ +stock NativeVotes_DisplayPassCustom(Handle:vote, const String:format[], any:...) +{ + decl String:buffer[192]; + + for (new i = 1; i <= MaxClients; ++i) + { + if (IsClientInGame(i)) + { + SetGlobalTransTarget(i); + VFormat(buffer, sizeof(buffer), format, 3); + NativeVotes_DisplayPassCustomToOne(vote, i, "%s", buffer); + } + } +} + +/** + * Display vote passed screen with a custom type. + * + * A sample usage of this would be if Extend won an RTV vote: NativeVotes_DisplayPassEx(vote, NativeVotesPass_Extend, map); + * + * You MUST call one of NativeVotes_DisplayPass, NativeVotes_DisplayPassEx, + * or NativeVotes_DisplayFail to hide the vote screen for users who didn't vote + * and to clear out their selection for the next vote. + * + * #param vote Vote handle + * @param passType The pass screen to display + * @param details Normally the item that won the vote. Also used for custom vote winners + * @noreturn + */ +native NativeVotes_DisplayPassEx(Handle:vote, NativeVotesPassType:passType, const String:details[]=""); + +/** + * Display failure screen. + * + * You MUST call one of NativeVotes_DisplayPass, NativeVotes_DisplayPassEx, + * or NativeVotes_DisplayFail to hide the vote screen for users who didn't vote, + * and to clear out their selection for the next vote. + * + * @param reason Vote failure reason from NativeVotesFailType enum + * @noreturn + */ +native NativeVotes_DisplayFail(Handle:vote, NativeVotesFailType:reason=NativeVotesFail_Generic); + +/** + * Quick stock to determine whether voting is allowed. This doesn't let you + * fine-tune a reason for not voting, so it's not recommended for lazily + * telling clients that voting isn't allowed. + * + * @return True if voting is allowed, false if voting is in progress + * or the cooldown is active. + */ +stock bool:NativeVotes_IsNewVoteAllowed() +{ + if (NativeVotes_IsVoteInProgress() || NativeVotes_CheckVoteDelay() != 0) + { + return false; + } + + return true; +} + +/** + * Used when callvote is called with no arguments. + * + * This is used to configure the VoteSetup usermessage on TF2 and CS:GO + * + * @param client Client, in case the votes are restricted by client + * @param voteTypes Populate this array with the vote types this server supports + * Custom and multiple choice votes are not supported from + * the GUI and are thus ignored. + * @return Plugin_Continue to allow the server itself (or another plugin) to process the callvote + * Plugin_Changed if you're changing the voteTypes, + * Plugin_Handled to return a blank VoteSetup usermessage + * Plugin_Stop to prevent VoteSetup usermessage (not recommended) + */ +//functag public Action:NativeVotes_CallVoteSetupHandler(client, NativeVotesType:voteTypes[]); + +/** + * Forward for "callvote" handling + * + * You should respond to this by starting a vote or by calling NativeVotes_DisplayCallVoteFail + * + * @param client Client + * @param voteType Type of vote being called. This will NEVER be a multiple-choice or custom vote. + * @param voteArgument Vote argument or blank if the vote type has no argument. + * @param target target userid for kick votes or 0 for all other votes + * @return Plugin_Continue to allow the server itself (or another plugin) to process the callvote + * Plugin_Handled if you processed this vote type + * Plugin_Stop to block the vote type (not recommended) + */ +//functag public Action:NativeVotes_CallVoteHandler(client, NativeVotesType:voteType, const String:voteArgument[], target); + +/** + * Register a plugin as a vote manager. + * This is used to abstract away the details of the callvote command. + * + * @param callHandler Handler for callvote commands. + * @param setupHandler Handler to override the which vote types your server supports. Only useful on TF2 and CS:GO. + * @noreturn + */ +//native NativeVotes_RegisterVoteManager(NativeVotes_CallVoteHandler:callHandler, NativeVotes_CallVoteSetupHandler:setupHandler=INVALID_FUNCTION); + +/** + * Send a call vote fail screen to a user + * Used to respond to a callvote with invalid arguments or for other reasons + * (such as trying to target an admin for a kick/ban vote) + * + * @param client The client to display the fail screen to + * @param reason A vote call fail reason + * @param time For NativeVotesCallFail_Recent, the number of seconds until the vote + * can be called again + */ +native NativeVotes_DisplayCallVoteFail(client, NativeVotesCallFailType:reason, time); + +/** + * Redraws the vote title from inside a MenuAction_Display callback + * Not supported on L4D + * + * @param text Vote title to draw + * @error If called from outside MenuAction_Display + * @return Plugin_Changed if the change is allowed, Plugin_Continue if it isn't. + */ +native Action:NativeVotes_RedrawVoteTitle(const String:text[]); + +/** + * Redraws the vote text from inside a MenuAction_DisplayItem callback. + * Only supported on multiple-choice votes + * + * @param text Vote text to draw. + * @error If called from outside MenuAction_DisplayItem + * @return Plugin_Changed if the change is allowed, Plugin_Continue if it isn't. + */ +native Action:NativeVotes_RedrawVoteItem(const String:text[]); + +/** + * Retrieves voting information from MenuAction_VoteEnd. + * + * @param param2 Second parameter of MenuAction_VoteEnd. + * @param winningVotes Number of votes received by the winning option. + * @param totalVotes Number of total votes received. + * @noreturn + */ +stock NativeVotes_GetInfo(param2, &winningVotes, &totalVotes) +{ + winningVotes = param2 & 0xFFFF; + totalVotes = param2 >> 16; +} + +/** + * Do not edit below this line! + */ +public SharedPlugin:__pl_nativevotes = +{ + name = "nativevotes", + file = "nativevotes.smx", +#if defined REQUIRE_PLUGINS + required = 1, +#else + required = 0, +#endif +}; + +public __pl_nativevotes_SetNTVOptional() +{ + MarkNativeAsOptional("NativeVotes_IsVoteTypeSupported"); + MarkNativeAsOptional("NativeVotes_Create"); + MarkNativeAsOptional("NativeVotes_Close"); + MarkNativeAsOptional("NativeVotes_AddItem"); + MarkNativeAsOptional("NativeVotes_InsertItem"); + MarkNativeAsOptional("NativeVotes_RemoveItem"); + MarkNativeAsOptional("NativeVotes_RemoveAllItems"); + MarkNativeAsOptional("NativeVotes_GetItem"); + MarkNativeAsOptional("NativeVotes_GetItemCount"); + MarkNativeAsOptional("NativeVotes_SetDetails"); + MarkNativeAsOptional("NativeVotes_GetDetails"); + MarkNativeAsOptional("NativeVotes_SetTitle"); + MarkNativeAsOptional("NativeVotes_GetTitle"); + MarkNativeAsOptional("NativeVotes_SetTarget"); + MarkNativeAsOptional("NativeVotes_GetTarget"); + MarkNativeAsOptional("NativeVotes_GetTargetSteam"); + MarkNativeAsOptional("NativeVotes_IsVoteInProgress"); + MarkNativeAsOptional("NativeVotes_GetMaxItems"); + MarkNativeAsOptional("NativeVotes_SetOptionFlags"); + MarkNativeAsOptional("NativeVotes_GetOptionFlags"); + MarkNativeAsOptional("NativeVotes_Cancel"); + MarkNativeAsOptional("NativeVotes_SetResultCallback"); + MarkNativeAsOptional("NativeVotes_CheckVoteDelay"); + MarkNativeAsOptional("NativeVotes_IsClientInVotePool"); + MarkNativeAsOptional("NativeVotes_RedrawClientVote"); + MarkNativeAsOptional("NativeVotes_RedrawClientVote"); + MarkNativeAsOptional("NativeVotes_GetType"); + MarkNativeAsOptional("NativeVotes_SetTeam"); + MarkNativeAsOptional("NativeVotes_GetTeam"); + MarkNativeAsOptional("NativeVotes_SetInitiator"); + MarkNativeAsOptional("NativeVotes_GetInitiator"); + MarkNativeAsOptional("NativeVotes_Display"); + MarkNativeAsOptional("NativeVotes_DisplayPass"); + MarkNativeAsOptional("NativeVotes_DisplayPassCustomToOne"); + MarkNativeAsOptional("NativeVotes_DisplayPassEx"); + MarkNativeAsOptional("NativeVotes_DisplayFail"); + MarkNativeAsOptional("NativeVotes_RegisterVoteManager"); + MarkNativeAsOptional("NativeVotes_DisplayCallVoteFail"); + MarkNativeAsOptional("NativeVotes_RedrawVoteTitle"); + MarkNativeAsOptional("NativeVotes_RedrawVoteItem"); +} diff --git a/mapchooser_extended/configs/mapchooser_extended/maps/csgo.txt b/mapchooser_extended/configs/mapchooser_extended/maps/csgo.txt index 0f222fdb..ca309de0 100644 --- a/mapchooser_extended/configs/mapchooser_extended/maps/csgo.txt +++ b/mapchooser_extended/configs/mapchooser_extended/maps/csgo.txt @@ -1,21 +1,21 @@ -ar_baggage -ar_monastery -ar_shoots -cs_assault -cs_italy -cs_militia -cs_office -de_aztec -de_bank -de_dust -de_dust2 -de_inferno -de_lake -de_mirage -de_nuke -de_safehouse -de_shorttrain -de_stmarc -de_sugarcane -de_train -de_vertigo +ar_baggage +ar_monastery +ar_shoots +cs_assault +cs_italy +cs_militia +cs_office +de_aztec +de_bank +de_dust +de_dust2 +de_inferno +de_lake +de_mirage +de_nuke +de_safehouse +de_shorttrain +de_stmarc +de_sugarcane +de_train +de_vertigo diff --git a/mapchooser_extended/configs/mapchooser_extended/maps/cstrike.txt b/mapchooser_extended/configs/mapchooser_extended/maps/cstrike.txt index 0edf7e6c..74eb5950 100644 --- a/mapchooser_extended/configs/mapchooser_extended/maps/cstrike.txt +++ b/mapchooser_extended/configs/mapchooser_extended/maps/cstrike.txt @@ -1,28 +1,28 @@ -as_oilrig -cs_747 -cs_assault -cs_backalley -cs_compound -cs_estate -cs_havana -cs_italy -cs_militia -cs_office -cs_siege -de_airstrip -de_aztec -de_cbble -de_chateau -de_dust2 -de_dust -de_inferno -de_nuke -de_piranesi -de_port -de_prodigy -de_storm -de_survivor -de_tides -de_torn -de_train -de_vertigo +as_oilrig +cs_747 +cs_assault +cs_backalley +cs_compound +cs_estate +cs_havana +cs_italy +cs_militia +cs_office +cs_siege +de_airstrip +de_aztec +de_cbble +de_chateau +de_dust2 +de_dust +de_inferno +de_nuke +de_piranesi +de_port +de_prodigy +de_storm +de_survivor +de_tides +de_torn +de_train +de_vertigo diff --git a/mapchooser_extended/configs/mapchooser_extended/maps/dods.txt b/mapchooser_extended/configs/mapchooser_extended/maps/dods.txt index 98759783..c4a3ed72 100644 --- a/mapchooser_extended/configs/mapchooser_extended/maps/dods.txt +++ b/mapchooser_extended/configs/mapchooser_extended/maps/dods.txt @@ -1,9 +1,9 @@ -dod_anzio -dod_argentan -dod_avalanche -dod_colmar -dod_donner -dod_flash -dod_jagd -dod_kalt -dod_palermo +dod_anzio +dod_argentan +dod_avalanche +dod_colmar +dod_donner +dod_flash +dod_jagd +dod_kalt +dod_palermo diff --git a/mapchooser_extended/configs/mapchooser_extended/maps/hl2mp.txt b/mapchooser_extended/configs/mapchooser_extended/maps/hl2mp.txt index a5e91bc0..c4d570e2 100644 --- a/mapchooser_extended/configs/mapchooser_extended/maps/hl2mp.txt +++ b/mapchooser_extended/configs/mapchooser_extended/maps/hl2mp.txt @@ -1,7 +1,7 @@ -dm_lockdown -dm_overwatch -dm_runoff -dm_steamlab -dm_underpass -dm_resistance -dm_powerhouse +dm_lockdown +dm_overwatch +dm_runoff +dm_steamlab +dm_underpass +dm_resistance +dm_powerhouse diff --git a/mapchooser_extended/configs/mapchooser_extended/maps/tf.txt b/mapchooser_extended/configs/mapchooser_extended/maps/tf.txt index 5faa9c1a..62be3697 100644 --- a/mapchooser_extended/configs/mapchooser_extended/maps/tf.txt +++ b/mapchooser_extended/configs/mapchooser_extended/maps/tf.txt @@ -1,65 +1,65 @@ -tc_hydro -cp_well -cp_granary -cp_dustbowl -cp_gravelpit -ctf_2fort -ctf_well -cp_badlands -pl_goldrush -cp_fastlane -ctf_turbine -pl_badwater -cp_steel -arena_badlands -arena_granary -arena_lumberyard -arena_ravine -arena_well -cp_egypt_final -cp_junction_final -arena_watchtower -plr_pipeline -arena_sawmill -arena_nucleus -pl_hoodoo_final -koth_sawmill -koth_nucleus -koth_viaduct -ctf_sawmill -arena_offblast_final -cp_yukon_final -koth_harvest_final -koth_harvest_event -ctf_doublecross -cp_gorge -cp_freight_final1 -pl_upward -plr_hightower -pl_thundermountain -cp_coldfront -cp_mountainlab -cp_manor_event -cp_degrootkeep -cp_5gorge -pl_frontier_final -plr_nightfall_final -koth_lakeside_final -koth_badlands -pl_barnblitz -cp_gullywash_final1 -koth_viaduct_event -cp_foundry -sd_doomsday -koth_king -mvm_mannworks -mvm_coaltown -mvm_decoy -koth_lakeside_event -mvm_bigrock -cp_process_final -cp_standin_final -plr_hightower_event -cp_snakewater_final1 -mvm_mannhattan -mvm_rottenburg +tc_hydro +cp_well +cp_granary +cp_dustbowl +cp_gravelpit +ctf_2fort +ctf_well +cp_badlands +pl_goldrush +cp_fastlane +ctf_turbine +pl_badwater +cp_steel +arena_badlands +arena_granary +arena_lumberyard +arena_ravine +arena_well +cp_egypt_final +cp_junction_final +arena_watchtower +plr_pipeline +arena_sawmill +arena_nucleus +pl_hoodoo_final +koth_sawmill +koth_nucleus +koth_viaduct +ctf_sawmill +arena_offblast_final +cp_yukon_final +koth_harvest_final +koth_harvest_event +ctf_doublecross +cp_gorge +cp_freight_final1 +pl_upward +plr_hightower +pl_thundermountain +cp_coldfront +cp_mountainlab +cp_manor_event +cp_degrootkeep +cp_5gorge +pl_frontier_final +plr_nightfall_final +koth_lakeside_final +koth_badlands +pl_barnblitz +cp_gullywash_final1 +koth_viaduct_event +cp_foundry +sd_doomsday +koth_king +mvm_mannworks +mvm_coaltown +mvm_decoy +koth_lakeside_event +mvm_bigrock +cp_process_final +cp_standin_final +plr_hightower_event +cp_snakewater_final1 +mvm_mannhattan +mvm_rottenburg diff --git a/mapchooser_extended/configs/mapchooser_extended/sounds/tf.cfg b/mapchooser_extended/configs/mapchooser_extended/sounds/tf.cfg index 188480da..92bdbb52 100644 --- a/mapchooser_extended/configs/mapchooser_extended/sounds/tf.cfg +++ b/mapchooser_extended/configs/mapchooser_extended/sounds/tf.cfg @@ -1,77 +1,77 @@ -"MapchooserSoundsList" -{ - "tf" - { - "counter" - { - "1" - { - "sound" "sourcemod/mapchooser/tf2/announcer_begins_1sec.mp3" - "builtin" "vo/announcer_begins_1sec.wav" - "event" "Announcer.RoundBegins1Seconds" - } - "2" - { - "sound" "sourcemod/mapchooser/tf2/announcer_begins_2sec.mp3" - "builtin" "vo/announcer_begins_2sec.wav" - "event" "Announcer.RoundBegins2Seconds" - } - "3" - { - "sound" "sourcemod/mapchooser/tf2/announcer_begins_3sec.mp3" - "builtin" "vo/announcer_begins_3sec.wav" - "event" "Announcer.RoundBegins3Seconds" - } - "4" - { - "sound" "sourcemod/mapchooser/tf2/announcer_begins_4sec.mp3" - "builtin" "vo/announcer_begins_4sec.wav" - "event" "Announcer.RoundBegins4Seconds" - } - "5" - { - "sound" "sourcemod/mapchooser/tf2/announcer_begins_5sec.mp3" - "builtin" "vo/announcer_begins_5sec.wav" - "event" "Announcer.RoundBegins5Seconds" - } - "10" - { - "sound" "sourcemod/mapchooser/tf2/announcer_dec_missionbegins10s01.mp3" - "builtin" "vo/announcer_dec_missionbegins10s01.wav" - } - "30" - { - "sound" "sourcemod/mapchooser/tf2/announcer_dec_missionbegins30s01.mp3" - "builtin" "vo/announcer_dec_missionbegins30s01.wav" - } - "60" - { - "sound" "sourcemod/mapchooser/tf2/announcer_dec_missionbegins60s06.mp3" - "builtin" "vo/announcer_dec_missionbegins60s06.wav" - } - } - "vote start" - { - "sound" "sourcemod/mapchooser/tf2/vote_started.mp3" - "event" "Vote.Created" - "builtin" "ui/vote_started.wav" - } - "vote end" - { - "sound" "sourcemod/mapchooser/tf2/vote_success.mp3" - "event" "Vote.Passed" - "builtin" "ui/vote_success.wav" - } - "vote warning" - { - "sound" "sourcemod/mapchooser/tf2/announcer_dec_missionbegins60s03.mp3" - "builtin" "vo/announcer_dec_missionbegins60s03.wav" - } - "runoff warning" - { - "sound" "sourcemod/mapchooser/tf2/vote_failure.mp3" - "event" "Vote.Failed" - "builtin" "ui/vote_failure.wav" - } - } +"MapchooserSoundsList" +{ + "tf" + { + "counter" + { + "1" + { + "sound" "sourcemod/mapchooser/tf2/announcer_begins_1sec.mp3" + "builtin" "vo/announcer_begins_1sec.wav" + "event" "Announcer.RoundBegins1Seconds" + } + "2" + { + "sound" "sourcemod/mapchooser/tf2/announcer_begins_2sec.mp3" + "builtin" "vo/announcer_begins_2sec.wav" + "event" "Announcer.RoundBegins2Seconds" + } + "3" + { + "sound" "sourcemod/mapchooser/tf2/announcer_begins_3sec.mp3" + "builtin" "vo/announcer_begins_3sec.wav" + "event" "Announcer.RoundBegins3Seconds" + } + "4" + { + "sound" "sourcemod/mapchooser/tf2/announcer_begins_4sec.mp3" + "builtin" "vo/announcer_begins_4sec.wav" + "event" "Announcer.RoundBegins4Seconds" + } + "5" + { + "sound" "sourcemod/mapchooser/tf2/announcer_begins_5sec.mp3" + "builtin" "vo/announcer_begins_5sec.wav" + "event" "Announcer.RoundBegins5Seconds" + } + "10" + { + "sound" "sourcemod/mapchooser/tf2/announcer_dec_missionbegins10s01.mp3" + "builtin" "vo/announcer_dec_missionbegins10s01.wav" + } + "30" + { + "sound" "sourcemod/mapchooser/tf2/announcer_dec_missionbegins30s01.mp3" + "builtin" "vo/announcer_dec_missionbegins30s01.wav" + } + "60" + { + "sound" "sourcemod/mapchooser/tf2/announcer_dec_missionbegins60s06.mp3" + "builtin" "vo/announcer_dec_missionbegins60s06.wav" + } + } + "vote start" + { + "sound" "sourcemod/mapchooser/tf2/vote_started.mp3" + "event" "Vote.Created" + "builtin" "ui/vote_started.wav" + } + "vote end" + { + "sound" "sourcemod/mapchooser/tf2/vote_success.mp3" + "event" "Vote.Passed" + "builtin" "ui/vote_success.wav" + } + "vote warning" + { + "sound" "sourcemod/mapchooser/tf2/announcer_dec_missionbegins60s03.mp3" + "builtin" "vo/announcer_dec_missionbegins60s03.wav" + } + "runoff warning" + { + "sound" "sourcemod/mapchooser/tf2/vote_failure.mp3" + "event" "Vote.Failed" + "builtin" "ui/vote_failure.wav" + } + } } \ No newline at end of file diff --git a/mapchooser_extended/scripting/include/mapchooser_extended.inc b/mapchooser_extended/scripting/include/mapchooser_extended.inc index e7f03c54..edeedbc9 100644 --- a/mapchooser_extended/scripting/include/mapchooser_extended.inc +++ b/mapchooser_extended/scripting/include/mapchooser_extended.inc @@ -1,108 +1,108 @@ -/** - * vim: set ts=4 : - * ============================================================================= - * MapChooser Extended - * Creates a map vote at appropriate times, setting sm_nextmap to the winning - * vote - * - * MapChooser Extended (C)2011-2013 Powerlord (Ross Bemrose) - * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. - * ============================================================================= - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, version 3.0, as published by the - * Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - * - * As a special exception, AlliedModders LLC gives you permission to link the - * code of this program (as well as its derivative works) to "Half-Life 2," the - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software - * by the Valve Corporation. You must obey the GNU General Public License in - * all respects for all other code used. Additionally, AlliedModders LLC grants - * this exception to all derivative works. AlliedModders LLC defines further - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), - * or . - * - * Version: $Id$ - */ - -#if defined _mapchooser_extended_included_ - #endinput -#endif -#define _mapchooser_extended_included_ -#include - -// MCE 1.9 series - -enum CanNominateResult -{ - CanNominate_No_VoteFull, /** No, nominations list is full */ - CanNominate_No_VoteInProgress, /** No, map vote is in progress */ - CanNominate_No_VoteComplete, /** No, map vote is completed */ - CanNominate_Yes, /** Yes, you can nominate */ -}; - - -/** - * Called whenever warning timer starts - * - */ -forward OnMapVoteWarningStart(); - -/** - * Called whenever runoff warning timer starts - */ -forward OnMapVoteRunnoffWarningStart(); - -/** - * Called whenever the timer ticks - */ -forward OnMapVoteWarningTick(time); - -/** - * Called whenever vote starts - * - * @deprecated Will be removed in MapChooser 1.8. Use OnMapVoteStarted instead. - */ -forward OnMapVoteStart(); - -/** - * Called whenever vote ends - */ -forward OnMapVoteEnd(const String:map[]); - -/** - * Is a map on the current game's official list? - * This should be treated as informative only. - * - * @param map Name of map to check - * @return true if it's on the list of official maps for this game - */ -native bool:IsMapOfficial(const String:map[]); - -/** - * Is nominate allowed? - * - * @return A CanNominateResult corresponding to whether a vote is allowed or not - */ -native CanNominateResult:CanNominate(); - -native bool:ExcludeMap(const String:map[]); - -public SharedPlugin:__pl_mapchooser_extended = -{ - name = "mapchooser", - file = "mapchooser_extended.smx", -#if defined REQUIRE_PLUGIN - required = 1, -#else - required = 0, -#endif -}; +/** + * vim: set ts=4 : + * ============================================================================= + * MapChooser Extended + * Creates a map vote at appropriate times, setting sm_nextmap to the winning + * vote + * + * MapChooser Extended (C)2011-2013 Powerlord (Ross Bemrose) + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#if defined _mapchooser_extended_included_ + #endinput +#endif +#define _mapchooser_extended_included_ +#include + +// MCE 1.9 series + +enum CanNominateResult +{ + CanNominate_No_VoteFull, /** No, nominations list is full */ + CanNominate_No_VoteInProgress, /** No, map vote is in progress */ + CanNominate_No_VoteComplete, /** No, map vote is completed */ + CanNominate_Yes, /** Yes, you can nominate */ +}; + + +/** + * Called whenever warning timer starts + * + */ +forward OnMapVoteWarningStart(); + +/** + * Called whenever runoff warning timer starts + */ +forward OnMapVoteRunnoffWarningStart(); + +/** + * Called whenever the timer ticks + */ +forward OnMapVoteWarningTick(time); + +/** + * Called whenever vote starts + * + * @deprecated Will be removed in MapChooser 1.8. Use OnMapVoteStarted instead. + */ +forward OnMapVoteStart(); + +/** + * Called whenever vote ends + */ +forward OnMapVoteEnd(const String:map[]); + +/** + * Is a map on the current game's official list? + * This should be treated as informative only. + * + * @param map Name of map to check + * @return true if it's on the list of official maps for this game + */ +native bool:IsMapOfficial(const String:map[]); + +/** + * Is nominate allowed? + * + * @return A CanNominateResult corresponding to whether a vote is allowed or not + */ +native CanNominateResult:CanNominate(); + +native bool:ExcludeMap(const String:map[]); + +public SharedPlugin:__pl_mapchooser_extended = +{ + name = "mapchooser", + file = "mapchooser_extended.smx", +#if defined REQUIRE_PLUGIN + required = 1, +#else + required = 0, +#endif +}; diff --git a/mapchooser_extended/scripting/mapchooser_extended.sp b/mapchooser_extended/scripting/mapchooser_extended.sp index c203c33d..023107fb 100644 --- a/mapchooser_extended/scripting/mapchooser_extended.sp +++ b/mapchooser_extended/scripting/mapchooser_extended.sp @@ -1,2343 +1,2343 @@ -/** - * vim: set ts=4 : - * ============================================================================= - * MapChooser Extended - * Creates a map vote at appropriate times, setting sm_nextmap to the winning - * vote. Includes extra options not present in the SourceMod MapChooser - * - * MapChooser Extended (C)2011-2013 Powerlord (Ross Bemrose) - * SourceMod (C)2004-2007 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$ - */ - -//#define DEBUG - -#if defined DEBUG - #define assert(%1) if (!(%1)) ThrowError("Debug Assertion Failed"); - #define assert_msg(%1,%2) if (!(%1)) ThrowError(%2); -#else - #define assert(%1) - #define assert_msg(%1,%2) -#endif - -#pragma semicolon 1 -#include -#include -#include "include/mapchooser_extended" -#include -#include -#include - -#undef REQUIRE_PLUGIN -#include - -#define MCE_VERSION "1.10.2" - -#define NV "nativevotes" - -enum RoundCounting -{ - RoundCounting_Standard = 0, - RoundCounting_MvM, - RoundCounting_ArmsRace, -} - -// CSGO requires two cvars to get the game type -enum -{ - GameType_Classic = 0, - GameType_GunGame = 1, - GameType_Training = 2, - GameType_Custom = 3, -} - -enum -{ - GunGameMode_ArmsRace = 0, - GunGameMode_Demolition = 1, - GunGameMode_DeathMatch = 2, -} - -public Plugin:myinfo = -{ - name = "MapChooser Extended", - author = "Powerlord, Zuko, and AlliedModders LLC", - description = "Automated Map Voting with Extensions", - version = MCE_VERSION, - url = "https://forums.alliedmods.net/showthread.php?t=156974" -}; - -/* Valve ConVars */ -new Handle:g_Cvar_Winlimit = INVALID_HANDLE; -new Handle:g_Cvar_Maxrounds = INVALID_HANDLE; -new Handle:g_Cvar_Fraglimit = INVALID_HANDLE; -new Handle:g_Cvar_Bonusroundtime = INVALID_HANDLE; -new Handle:g_Cvar_MatchClinch = INVALID_HANDLE; -new Handle:g_Cvar_VoteNextLevel = INVALID_HANDLE; -new Handle:g_Cvar_GameType = INVALID_HANDLE; -new Handle:g_Cvar_GameMode = INVALID_HANDLE; - -/* Plugin ConVars */ -new Handle:g_Cvar_StartTime = INVALID_HANDLE; -new Handle:g_Cvar_StartRounds = INVALID_HANDLE; -new Handle:g_Cvar_StartFrags = INVALID_HANDLE; -new Handle:g_Cvar_ExtendTimeStep = INVALID_HANDLE; -new Handle:g_Cvar_ExtendRoundStep = INVALID_HANDLE; -new Handle:g_Cvar_ExtendFragStep = INVALID_HANDLE; -new Handle:g_Cvar_ExcludeMaps = INVALID_HANDLE; -new Handle:g_Cvar_IncludeMaps = INVALID_HANDLE; -new Handle:g_Cvar_NoVoteMode = INVALID_HANDLE; -new Handle:g_Cvar_Extend = INVALID_HANDLE; -new Handle:g_Cvar_DontChange = INVALID_HANDLE; -new Handle:g_Cvar_EndOfMapVote = INVALID_HANDLE; -new Handle:g_Cvar_VoteDuration = INVALID_HANDLE; - -new Handle:g_VoteTimer = INVALID_HANDLE; -new Handle:g_RetryTimer = INVALID_HANDLE; -new Handle:g_WarningTimer = INVALID_HANDLE; - -/* Data Handles */ -new Handle:g_MapList = INVALID_HANDLE; -new Handle:g_NominateList = INVALID_HANDLE; -new Handle:g_NominateOwners = INVALID_HANDLE; -new Handle:g_OldMapList = INVALID_HANDLE; -new Handle:g_NextMapList = INVALID_HANDLE; -new Handle:g_VoteMenu = INVALID_HANDLE; - -new g_Extends; -new g_TotalRounds; -new bool:g_HasVoteStarted; -new bool:g_WaitingForVote; -new bool:g_MapVoteCompleted; -new bool:g_ChangeMapAtRoundEnd; -new bool:g_ChangeMapInProgress; -new bool:g_HasIntermissionStarted = false; -new g_mapFileSerial = -1; - -new g_NominateCount = 0; -new MapChange:g_ChangeTime; - -new Handle:g_NominationsResetForward = INVALID_HANDLE; -new Handle:g_MapVoteStartedForward = INVALID_HANDLE; - -/* Mapchooser Extended Plugin ConVars */ - -new Handle:g_Cvar_RunOff = INVALID_HANDLE; -new Handle:g_Cvar_RunOffPercent = INVALID_HANDLE; -new Handle:g_Cvar_BlockSlots = INVALID_HANDLE; -new Handle:g_Cvar_MaxRunOffs = INVALID_HANDLE; -new Handle:g_Cvar_StartTimePercent = INVALID_HANDLE; -new Handle:g_Cvar_StartTimePercentEnable = INVALID_HANDLE; -new Handle:g_Cvar_WarningTime = INVALID_HANDLE; -new Handle:g_Cvar_RunOffWarningTime = INVALID_HANDLE; -new Handle:g_Cvar_MenuStyle = INVALID_HANDLE; -new Handle:g_Cvar_TimerLocation = INVALID_HANDLE; -new Handle:g_Cvar_ExtendPosition = INVALID_HANDLE; -new Handle:g_Cvar_MarkCustomMaps = INVALID_HANDLE; -new Handle:g_Cvar_RandomizeNominations = INVALID_HANDLE; -new Handle:g_Cvar_HideTimer = INVALID_HANDLE; -new Handle:g_Cvar_NoVoteOption = INVALID_HANDLE; - -/* Mapchooser Extended Data Handles */ -new Handle:g_OfficialList = INVALID_HANDLE; - -/* Mapchooser Extended Forwards */ -new Handle:g_MapVoteWarningStartForward = INVALID_HANDLE; -new Handle:g_MapVoteWarningTickForward = INVALID_HANDLE; -new Handle:g_MapVoteStartForward = INVALID_HANDLE; -new Handle:g_MapVoteEndForward = INVALID_HANDLE; -new Handle:g_MapVoteRunoffStartForward = INVALID_HANDLE; - -/* Mapchooser Extended Globals */ -new g_RunoffCount = 0; -new g_mapOfficialFileSerial = -1; -new bool:g_NativeVotes = false; -new String:g_GameModName[64]; -new bool:g_WarningInProgress = false; -new bool:g_AddNoVote = false; - -new RoundCounting:g_RoundCounting = RoundCounting_Standard; - -/* Upper bound of how many team there could be */ -#define MAXTEAMS 10 -new g_winCount[MAXTEAMS]; - -new bool:g_BlockedSlots = false; -new g_ObjectiveEnt = -1; - -enum TimerLocation -{ - TimerLocation_Hint = 0, - TimerLocation_Center = 1, - TimerLocation_Chat = 2, -} - -enum WarningType -{ - WarningType_Vote, - WarningType_Revote, -} - -#define VOTE_EXTEND "##extend##" -#define VOTE_DONTCHANGE "##dontchange##" - -/* Mapchooser Extended Defines */ -#define LINE_ONE "##lineone##" -#define LINE_TWO "##linetwo##" -#define LINE_SPACER "##linespacer##" -#define FAILURE_TIMER_LENGTH 5 - -public OnPluginStart() -{ - LoadTranslations("mapchooser_extended.phrases"); - LoadTranslations("basevotes.phrases"); - LoadTranslations("common.phrases"); - - new arraySize = ByteCountToCells(PLATFORM_MAX_PATH); - g_MapList = CreateArray(arraySize); - g_NominateList = CreateArray(arraySize); - g_NominateOwners = CreateArray(1); - g_OldMapList = CreateArray(arraySize); - g_NextMapList = CreateArray(arraySize); - g_OfficialList = CreateArray(arraySize); - - GetGameFolderName(g_GameModName, sizeof(g_GameModName)); - - g_Cvar_EndOfMapVote = CreateConVar("mce_endvote", "1", "Specifies if MapChooser should run an end of map vote", _, true, 0.0, true, 1.0); - - g_Cvar_StartTime = CreateConVar("mce_starttime", "10.0", "Specifies when to start the vote based on time remaining.", _, true, 1.0); - g_Cvar_StartRounds = CreateConVar("mce_startround", "2.0", "Specifies when to start the vote based on rounds remaining. Use 0 on DoD:S, CS:S, and TF2 to start vote during bonus round time", _, true, 0.0); - g_Cvar_StartFrags = CreateConVar("mce_startfrags", "5.0", "Specifies when to start the vote base on frags remaining.", _, true, 1.0); - g_Cvar_ExtendTimeStep = CreateConVar("mce_extend_timestep", "15", "Specifies how much many more minutes each extension makes", _, true, 5.0); - g_Cvar_ExtendRoundStep = CreateConVar("mce_extend_roundstep", "5", "Specifies how many more rounds each extension makes", _, true, 1.0); - g_Cvar_ExtendFragStep = CreateConVar("mce_extend_fragstep", "10", "Specifies how many more frags are allowed when map is extended.", _, true, 5.0); - g_Cvar_ExcludeMaps = CreateConVar("mce_exclude", "5", "Specifies how many past maps to exclude from the vote.", _, true, 0.0); - g_Cvar_IncludeMaps = CreateConVar("mce_include", "5", "Specifies how many maps to include in the vote.", _, true, 2.0, true, 6.0); - g_Cvar_NoVoteMode = CreateConVar("mce_novote", "1", "Specifies whether or not MapChooser should pick a map if no votes are received.", _, true, 0.0, true, 1.0); - g_Cvar_Extend = CreateConVar("mce_extend", "0", "Number of extensions allowed each map.", _, true, 0.0); - g_Cvar_DontChange = CreateConVar("mce_dontchange", "1", "Specifies if a 'Don't Change' option should be added to early votes", _, true, 0.0); - g_Cvar_VoteDuration = CreateConVar("mce_voteduration", "20", "Specifies how long the mapvote should be available for.", _, true, 5.0); - - // MapChooser Extended cvars - CreateConVar("mce_version", MCE_VERSION, "MapChooser Extended Version", FCVAR_SPONLY|FCVAR_NOTIFY|FCVAR_DONTRECORD); - - g_Cvar_RunOff = CreateConVar("mce_runoff", "1", "Hold run off votes if winning choice has less than a certain percentage of votes", _, true, 0.0, true, 1.0); - g_Cvar_RunOffPercent = CreateConVar("mce_runoffpercent", "50", "If winning choice has less than this percent of votes, hold a runoff", _, true, 0.0, true, 100.0); - g_Cvar_BlockSlots = CreateConVar("mce_blockslots", "1", "Block slots to prevent accidental votes. Only applies when Voice Command style menus are in use.", _, true, 0.0, true, 1.0); - //g_Cvar_BlockSlotsCount = CreateConVar("mce_blockslots_count", "2", "Number of slots to block.", _, true, 1.0, true, 3.0); - g_Cvar_MaxRunOffs = CreateConVar("mce_maxrunoffs", "1", "Number of run off votes allowed each map.", _, true, 0.0); - g_Cvar_StartTimePercent = CreateConVar("mce_start_percent", "35.0", "Specifies when to start the vote based on percents.", _, true, 0.0, true, 100.0); - g_Cvar_StartTimePercentEnable = CreateConVar("mce_start_percent_enable", "0", "Enable or Disable percentage calculations when to start vote.", _, true, 0.0, true, 1.0); - g_Cvar_WarningTime = CreateConVar("mce_warningtime", "15.0", "Warning time in seconds.", _, true, 0.0, true, 60.0); - g_Cvar_RunOffWarningTime = CreateConVar("mce_runoffvotewarningtime", "5.0", "Warning time for runoff vote in seconds.", _, true, 0.0, true, 30.0); - g_Cvar_MenuStyle = CreateConVar("mce_menustyle", "0", "Menu Style. 0 is the game's default, 1 is the older Valve style that requires you to press Escape to see the menu, 2 is the newer 1-9 button Voice Command style, unavailable in some games. Ignored on TF2 if NativeVotes Plugin is loaded.", _, true, 0.0, true, 2.0); - g_Cvar_TimerLocation = CreateConVar("mce_warningtimerlocation", "0", "Location for the warning timer text. 0 is HintBox, 1 is Center text, 2 is Chat. Defaults to HintBox.", _, true, 0.0, true, 2.0); - g_Cvar_MarkCustomMaps = CreateConVar("mce_markcustommaps", "1", "Mark custom maps in the vote list. 0 = Disabled, 1 = Mark with *, 2 = Mark with phrase.", _, true, 0.0, true, 2.0); - g_Cvar_ExtendPosition = CreateConVar("mce_extendposition", "0", "Position of Extend/Don't Change options. 0 = at end, 1 = at start.", _, true, 0.0, true, 1.0); - g_Cvar_RandomizeNominations = CreateConVar("mce_randomizeorder", "0", "Randomize map order?", _, true, 0.0, true, 1.0); - g_Cvar_HideTimer = CreateConVar("mce_hidetimer", "0", "Hide the MapChooser Extended warning timer", _, true, 0.0, true, 1.0); - g_Cvar_NoVoteOption = CreateConVar("mce_addnovote", "1", "Add \"No Vote\" to vote menu?", _, true, 0.0, true, 1.0); - - RegAdminCmd("sm_mapvote", Command_Mapvote, ADMFLAG_CHANGEMAP, "sm_mapvote - Forces MapChooser to attempt to run a map vote now."); - RegAdminCmd("sm_setnextmap", Command_SetNextmap, ADMFLAG_CHANGEMAP, "sm_setnextmap "); - - // Mapchooser Extended Commands - RegAdminCmd("mce_reload_maplist", Command_ReloadMaps, ADMFLAG_CHANGEMAP, "mce_reload_maplist - Reload the Official Maplist file."); - - g_Cvar_Winlimit = FindConVar("mp_winlimit"); - g_Cvar_Maxrounds = FindConVar("mp_maxrounds"); - g_Cvar_Fraglimit = FindConVar("mp_fraglimit"); - - new EngineVersion:version = GetEngineVersionCompat(); - - decl String:mapListPath[PLATFORM_MAX_PATH]; - - BuildPath(Path_SM, mapListPath, PLATFORM_MAX_PATH, "configs/mapchooser_extended/maps/%s.txt", g_GameModName); - SetMapListCompatBind("official", mapListPath); - - switch (version) - { - case Engine_TF2: - { - g_Cvar_VoteNextLevel = FindConVar("sv_vote_issue_nextlevel_allowed"); - g_Cvar_Bonusroundtime = FindConVar("mp_bonusroundtime"); - } - - case Engine_CSGO: - { - g_Cvar_VoteNextLevel = FindConVar("mp_endmatch_votenextmap"); - g_Cvar_GameType = FindConVar("game_type"); - g_Cvar_GameMode = FindConVar("game_mode"); - g_Cvar_Bonusroundtime = FindConVar("mp_round_restart_delay"); - } - - case Engine_DODS: - { - g_Cvar_Bonusroundtime = FindConVar("dod_bonusroundtime"); - } - - case Engine_CSS: - { - g_Cvar_Bonusroundtime = FindConVar("mp_round_restart_delay"); - } - - default: - { - g_Cvar_Bonusroundtime = FindConVar("mp_bonusroundtime"); - } - } - - if (g_Cvar_Winlimit != INVALID_HANDLE || g_Cvar_Maxrounds != INVALID_HANDLE) - { - switch (version) - { - case Engine_TF2: - { - HookEvent("teamplay_win_panel", Event_TeamPlayWinPanel); - HookEvent("teamplay_restart_round", Event_TFRestartRound); - HookEvent("arena_win_panel", Event_TeamPlayWinPanel); - HookEvent("pve_win_panel", Event_MvMWinPanel); - } - - case Engine_NuclearDawn: - { - HookEvent("round_win", Event_RoundEnd); - } - - case Engine_CSGO: - { - HookEvent("round_end", Event_RoundEnd); - HookEvent("cs_intermission", Event_Intermission); - HookEvent("announce_phase_end", Event_PhaseEnd); - g_Cvar_MatchClinch = FindConVar("mp_match_can_clinch"); - } - - case Engine_DODS: - { - HookEvent("dod_round_win", Event_RoundEnd); - } - - default: - { - HookEvent("round_end", Event_RoundEnd); - } - } - } - - if (g_Cvar_Fraglimit != INVALID_HANDLE) - { - HookEvent("player_death", Event_PlayerDeath); - } - - AutoExecConfig(true, "mapchooser_extended"); - - //Change the mp_bonusroundtime max so that we have time to display the vote - //If you display a vote during bonus time good defaults are 17 vote duration and 19 mp_bonustime - if (g_Cvar_Bonusroundtime != INVALID_HANDLE) - { - SetConVarBounds(g_Cvar_Bonusroundtime, ConVarBound_Upper, true, 30.0); - } - - g_NominationsResetForward = CreateGlobalForward("OnNominationRemoved", ET_Ignore, Param_String, Param_Cell); - g_MapVoteStartedForward = CreateGlobalForward("OnMapVoteStarted", ET_Ignore); - - //MapChooser Extended Forwards - g_MapVoteStartForward = CreateGlobalForward("OnMapVoteStart", ET_Ignore); // Deprecated - g_MapVoteEndForward = CreateGlobalForward("OnMapVoteEnd", ET_Ignore, Param_String); - g_MapVoteWarningStartForward = CreateGlobalForward("OnMapVoteWarningStart", ET_Ignore); - g_MapVoteWarningTickForward = CreateGlobalForward("OnMapVoteWarningTick", ET_Ignore, Param_Cell); - g_MapVoteRunoffStartForward = CreateGlobalForward("OnMapVoteRunnoffWarningStart", ET_Ignore); - -} - -public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max) -{ - if (LibraryExists("mapchooser")) - { - strcopy(error, err_max, "MapChooser already loaded, aborting."); - return APLRes_Failure; - } - - RegPluginLibrary("mapchooser"); - - MarkNativeAsOptional("GetEngineVersion"); - - CreateNative("NominateMap", Native_NominateMap); - CreateNative("RemoveNominationByMap", Native_RemoveNominationByMap); - CreateNative("RemoveNominationByOwner", Native_RemoveNominationByOwner); - CreateNative("InitiateMapChooserVote", Native_InitiateVote); - CreateNative("CanMapChooserStartVote", Native_CanVoteStart); - CreateNative("HasEndOfMapVoteFinished", Native_CheckVoteDone); - CreateNative("GetExcludeMapList", Native_GetExcludeMapList); - CreateNative("GetNominatedMapList", Native_GetNominatedMapList); - CreateNative("EndOfMapVoteEnabled", Native_EndOfMapVoteEnabled); - - // MapChooser Extended natives - CreateNative("IsMapOfficial", Native_IsMapOfficial); - CreateNative("CanNominate", Native_CanNominate); - - // BotoX - CreateNative("ExcludeMap", Native_ExcludeMap); - - return APLRes_Success; -} - -public OnAllPluginsLoaded() -{ - g_NativeVotes = LibraryExists(NV) && NativeVotes_IsVoteTypeSupported(NativeVotesType_NextLevelMult); -} - -public OnLibraryAdded(const String:name[]) -{ - if (StrEqual(name, NV) && NativeVotes_IsVoteTypeSupported(NativeVotesType_NextLevelMult)) - { - g_NativeVotes = true; - } -} - -public OnLibraryRemoved(const String:name[]) -{ - if (StrEqual(name, NV)) - { - g_NativeVotes = false; - } -} - -public OnMapStart() -{ - decl String:folder[64]; - GetGameFolderName(folder, sizeof(folder)); - - g_RoundCounting = RoundCounting_Standard; - g_ObjectiveEnt = -1; - - if (strcmp(folder, "tf") == 0 && GameRules_GetProp("m_bPlayingMannVsMachine")) - { - g_RoundCounting = RoundCounting_MvM; - g_ObjectiveEnt = EntIndexToEntRef(FindEntityByClassname(-1, "tf_objective_resource")); - } - else if (strcmp(folder, "csgo") == 0 && GetConVarInt(g_Cvar_GameType) == GameType_GunGame && - GetConVarInt(g_Cvar_GameMode) == GunGameMode_ArmsRace) - { - g_RoundCounting = RoundCounting_ArmsRace; - } -} - -public OnConfigsExecuted() -{ - if (ReadMapList(g_MapList, - g_mapFileSerial, - "mapchooser", - MAPLIST_FLAG_CLEARARRAY|MAPLIST_FLAG_MAPSFOLDER) - != INVALID_HANDLE) - - { - if (g_mapFileSerial == -1) - { - LogError("Unable to create a valid map list."); - } - } - - // Disable the next level vote in TF2 and CS:GO - // In TF2, this has two effects: 1. Stop the next level vote (which overlaps rtv functionality). - // 2. Stop the built-in end level vote. This is the only thing that happens in CS:GO - if (g_Cvar_VoteNextLevel != INVALID_HANDLE) - { - SetConVarBool(g_Cvar_VoteNextLevel, false); - } - - CreateNextVote(); - SetupTimeleftTimer(); - - g_TotalRounds = 0; - - g_Extends = 0; - - g_MapVoteCompleted = false; - - g_NominateCount = 0; - ClearArray(g_NominateList); - ClearArray(g_NominateOwners); - - for (new i=0; i GetConVarInt(g_Cvar_ExcludeMaps)) - { - RemoveFromArray(g_OldMapList, 0); - } -} - -public OnClientDisconnect(client) -{ - new index = FindValueInArray(g_NominateOwners, client); - - if (index == -1) - { - return; - } - - new String:oldmap[PLATFORM_MAX_PATH]; - GetArrayString(g_NominateList, index, oldmap, PLATFORM_MAX_PATH); - Call_StartForward(g_NominationsResetForward); - Call_PushString(oldmap); - Call_PushCell(GetArrayCell(g_NominateOwners, index)); - Call_Finish(); - - RemoveFromArray(g_NominateOwners, index); - RemoveFromArray(g_NominateList, index); - g_NominateCount--; -} - -public Action:Command_SetNextmap(client, args) -{ - if (args < 1) - { - CReplyToCommand(client, "[MCE] Usage: sm_setnextmap "); - return Plugin_Handled; - } - - decl String:map[PLATFORM_MAX_PATH]; - GetCmdArg(1, map, PLATFORM_MAX_PATH); - - if (!IsMapValid(map)) - { - CReplyToCommand(client, "[MCE] %t", "Map was not found", map); - return Plugin_Handled; - } - - ShowActivity(client, "%t", "Changed Next Map", map); - LogAction(client, -1, "\"%L\" changed nextmap to \"%s\"", client, map); - - SetNextMap(map); - g_MapVoteCompleted = true; - - return Plugin_Handled; -} - -public Action:Command_ReloadMaps(client, args) -{ - InitializeOfficialMapList(); -} - -public OnMapTimeLeftChanged() -{ - if (GetArraySize(g_MapList)) - { - SetupTimeleftTimer(); - } -} - -SetupTimeleftTimer() -{ - new time; - if (GetMapTimeLeft(time) && time > 0) - { - new startTime; - if (GetConVarBool(g_Cvar_StartTimePercentEnable)) - { - new timeLimit; - if (GetMapTimeLimit(timeLimit) && timeLimit > 0) - { - startTime = GetConVarInt(g_Cvar_StartTimePercent) * (timeLimit * 60) / 100; - } - } - else - { - startTime = GetConVarInt(g_Cvar_StartTime) * 60; - } - - if (time - startTime < 0 && GetConVarBool(g_Cvar_EndOfMapVote) && !g_MapVoteCompleted && !g_HasVoteStarted) - { - SetupWarningTimer(WarningType_Vote); - } - else - { - if (g_WarningTimer == INVALID_HANDLE) - { - if (g_VoteTimer != INVALID_HANDLE) - { - KillTimer(g_VoteTimer); - g_VoteTimer = INVALID_HANDLE; - } - - //g_VoteTimer = CreateTimer(float(time - startTime), Timer_StartMapVoteTimer_StartMapVote, _, TIMER_FLAG_NO_MAPCHANGE); - g_VoteTimer = CreateTimer(float(time - startTime), Timer_StartWarningTimer, _, TIMER_FLAG_NO_MAPCHANGE); - } - } - } -} - -public Action:Timer_StartWarningTimer(Handle:timer) -{ - g_VoteTimer = INVALID_HANDLE; - - if (!g_WarningInProgress || g_WarningTimer == INVALID_HANDLE) - { - SetupWarningTimer(WarningType_Vote); - } -} - -public Action:Timer_StartMapVote(Handle:timer, Handle:data) -{ - static timePassed; - - // This is still necessary because InitiateVote still calls this directly via the retry timer - if (!GetArraySize(g_MapList) || !GetConVarBool(g_Cvar_EndOfMapVote) || g_MapVoteCompleted || g_HasVoteStarted) - { - g_WarningTimer = INVALID_HANDLE; - return Plugin_Stop; - } - - ResetPack(data); - new warningMaxTime = ReadPackCell(data); - new warningTimeRemaining = warningMaxTime - timePassed; - - new String:warningPhrase[32]; - ReadPackString(data, warningPhrase, sizeof(warningPhrase)); - - // Tick timer for external plugins - Call_StartForward(g_MapVoteWarningTickForward); - Call_PushCell(warningTimeRemaining); - Call_Finish(); - - if (timePassed == 0 || !GetConVarBool(g_Cvar_HideTimer)) - { - new TimerLocation:timerLocation = TimerLocation:GetConVarInt(g_Cvar_TimerLocation); - - switch(timerLocation) - { - case TimerLocation_Center: - { - PrintCenterTextAll("%t", warningPhrase, warningTimeRemaining); - } - - case TimerLocation_Chat: - { - PrintToChatAll("%t", warningPhrase, warningTimeRemaining); - } - - default: - { - PrintHintTextToAll("%t", warningPhrase, warningTimeRemaining); - } - } - } - - if (timePassed++ >= warningMaxTime) - { - if (timer == g_RetryTimer) - { - g_WaitingForVote = false; - g_RetryTimer = INVALID_HANDLE; - } - else - { - g_WarningTimer = INVALID_HANDLE; - } - - timePassed = 0; - new MapChange:mapChange = MapChange:ReadPackCell(data); - new Handle:hndl = Handle:ReadPackCell(data); - - InitiateVote(mapChange, hndl); - - return Plugin_Stop; - } - return Plugin_Continue; -} - -public Event_TFRestartRound(Handle:event, const String:name[], bool:dontBroadcast) -{ - /* Game got restarted - reset our round count tracking */ - g_TotalRounds = 0; -} - -public Event_TeamPlayWinPanel(Handle:event, const String:name[], bool:dontBroadcast) -{ - if (g_ChangeMapAtRoundEnd) - { - g_ChangeMapAtRoundEnd = false; - CreateTimer(2.0, Timer_ChangeMap, INVALID_HANDLE, TIMER_FLAG_NO_MAPCHANGE); - g_ChangeMapInProgress = true; - } - - new bluescore = GetEventInt(event, "blue_score"); - new redscore = GetEventInt(event, "red_score"); - - if(GetEventInt(event, "round_complete") == 1 || StrEqual(name, "arena_win_panel")) - { - g_TotalRounds++; - - if (!GetArraySize(g_MapList) || g_HasVoteStarted || g_MapVoteCompleted || !GetConVarBool(g_Cvar_EndOfMapVote)) - { - return; - } - - CheckMaxRounds(g_TotalRounds); - - switch(GetEventInt(event, "winning_team")) - { - case 3: - { - CheckWinLimit(bluescore); - } - case 2: - { - CheckWinLimit(redscore); - } - //We need to do nothing on winning_team == 0 this indicates stalemate. - default: - { - return; - } - } - } -} - -public Event_MvMWinPanel(Handle:event, const String:name[], bool:dontBroadcast) -{ - if (GetEventInt(event, "winning_team") == 2) - { - new objectiveEnt = EntRefToEntIndex(g_ObjectiveEnt); - if (objectiveEnt != INVALID_ENT_REFERENCE) - { - g_TotalRounds = GetEntProp(g_ObjectiveEnt, Prop_Send, "m_nMannVsMachineWaveCount"); - CheckMaxRounds(g_TotalRounds); - } - } -} - -public Event_Intermission(Handle:event, const String:name[], bool:dontBroadcast) -{ - g_HasIntermissionStarted = true; -} - -public Event_PhaseEnd(Handle:event, const String:name[], bool:dontBroadcast) -{ - /* announce_phase_end fires for both half time and the end of the map, but intermission fires first for end of the map. */ - if (g_HasIntermissionStarted) - { - return; - } - - /* No intermission yet, so this must be half time. Swap the score counters. */ - new t_score = g_winCount[2]; - g_winCount[2] = g_winCount[3]; - g_winCount[3] = t_score; -} - -public Event_WeaponRank(Handle:event, const String:name[], bool:dontBroadcast) -{ - new rank = GetEventInt(event, "weaponrank"); - if (rank > g_TotalRounds) - { - g_TotalRounds = rank; - CheckMaxRounds(g_TotalRounds); - } -} - -/* You ask, why don't you just use team_score event? And I answer... Because CSS doesn't. */ -public Event_RoundEnd(Handle:event, const String:name[], bool:dontBroadcast) -{ - if (g_RoundCounting == RoundCounting_ArmsRace) - { - return; - } - - if (g_ChangeMapAtRoundEnd) - { - g_ChangeMapAtRoundEnd = false; - CreateTimer(2.0, Timer_ChangeMap, INVALID_HANDLE, TIMER_FLAG_NO_MAPCHANGE); - g_ChangeMapInProgress = true; - } - - new winner; - if (strcmp(name, "round_win") == 0 || strcmp(name, "dod_round_win") == 0) - { - // Nuclear Dawn & DoD:S - winner = GetEventInt(event, "team"); - } - else - { - winner = GetEventInt(event, "winner"); - } - - if (winner == 0 || winner == 1 || !GetConVarBool(g_Cvar_EndOfMapVote)) - { - return; - } - - if (winner >= MAXTEAMS) - { - SetFailState("Mod exceed maximum team count - Please file a bug report."); - } - - g_TotalRounds++; - - g_winCount[winner]++; - - if (!GetArraySize(g_MapList) || g_HasVoteStarted || g_MapVoteCompleted) - { - return; - } - - CheckWinLimit(g_winCount[winner]); - CheckMaxRounds(g_TotalRounds); -} - -public CheckWinLimit(winner_score) -{ - if (g_Cvar_Winlimit != INVALID_HANDLE) - { - new winlimit = GetConVarInt(g_Cvar_Winlimit); - if (winlimit) - { - if (winner_score >= (winlimit - GetConVarInt(g_Cvar_StartRounds))) - { - if (!g_WarningInProgress || g_WarningTimer == INVALID_HANDLE) - { - SetupWarningTimer(WarningType_Vote, MapChange_MapEnd); - //InitiateVote(MapChange_MapEnd, INVALID_HANDLE); - } - } - } - } - - if (g_Cvar_MatchClinch != INVALID_HANDLE && g_Cvar_Maxrounds != INVALID_HANDLE) - { - new bool:clinch = GetConVarBool(g_Cvar_MatchClinch); - - if (clinch) - { - new maxrounds = GetConVarInt(g_Cvar_Maxrounds); - new winlimit = RoundFloat(maxrounds / 2.0); - - if(winner_score == winlimit - 1) - { - if (!g_WarningInProgress || g_WarningTimer == INVALID_HANDLE) - { - SetupWarningTimer(WarningType_Vote, MapChange_MapEnd); - //InitiateVote(MapChange_MapEnd, INVALID_HANDLE); - } - } - } - } -} - -public CheckMaxRounds(roundcount) -{ - new maxrounds = 0; - - if (g_RoundCounting == RoundCounting_ArmsRace) - { - maxrounds = GameRules_GetProp("m_iNumGunGameProgressiveWeaponsCT"); - } - else if (g_RoundCounting == RoundCounting_MvM) - { - maxrounds = GetEntProp(g_ObjectiveEnt, Prop_Send, "m_nMannVsMachineMaxWaveCount"); - } - else if (g_Cvar_Maxrounds != INVALID_HANDLE) - { - maxrounds = GetConVarInt(g_Cvar_Maxrounds); - } - else - { - return; - } - - if (maxrounds) - { - if (roundcount >= (maxrounds - GetConVarInt(g_Cvar_StartRounds))) - { - if (!g_WarningInProgress || g_WarningTimer == INVALID_HANDLE) - { - SetupWarningTimer(WarningType_Vote, MapChange_MapEnd); - //InitiateVote(MapChange_MapEnd, INVALID_HANDLE); - } - } - } -} - -public Event_PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast) -{ - if (!GetArraySize(g_MapList) || g_Cvar_Fraglimit == INVALID_HANDLE || g_HasVoteStarted) - { - return; - } - - if (!GetConVarInt(g_Cvar_Fraglimit) || !GetConVarBool(g_Cvar_EndOfMapVote)) - { - return; - } - - if (g_MapVoteCompleted) - { - return; - } - - new fragger = GetClientOfUserId(GetEventInt(event, "attacker")); - - if (!fragger) - { - return; - } - - if (GetClientFrags(fragger) >= (GetConVarInt(g_Cvar_Fraglimit) - GetConVarInt(g_Cvar_StartFrags))) - { - if (!g_WarningInProgress || g_WarningTimer == INVALID_HANDLE) - { - SetupWarningTimer(WarningType_Vote, MapChange_MapEnd); - //InitiateVote(MapChange_MapEnd, INVALID_HANDLE); - } - } -} - -public Action:Command_Mapvote(client, args) -{ - ShowActivity2(client, "[MCE] ", "%t", "Initiated Vote Map"); - - SetupWarningTimer(WarningType_Vote, MapChange_MapEnd, INVALID_HANDLE, true); - - //InitiateVote(MapChange_MapEnd, INVALID_HANDLE); - - return Plugin_Handled; -} - -/** - * Starts a new map vote - * - * @param when When the resulting map change should occur. - * @param inputlist Optional list of maps to use for the vote, otherwise an internal list of nominations + random maps will be used. - */ -InitiateVote(MapChange:when, Handle:inputlist=INVALID_HANDLE) -{ - g_WaitingForVote = true; - g_WarningInProgress = false; - - // Check if a nativevotes vots is in progress first - // NativeVotes running at the same time as a regular vote can cause hintbox problems, - // so always check for a standard vote - if (IsVoteInProgress() || (g_NativeVotes && NativeVotes_IsVoteInProgress())) - { - // Can't start a vote, try again in 5 seconds. - //g_RetryTimer = CreateTimer(5.0, Timer_StartMapVote, _, TIMER_FLAG_NO_MAPCHANGE); - - CPrintToChatAll("[MCE] %t", "Cannot Start Vote", FAILURE_TIMER_LENGTH); - new Handle:data; - g_RetryTimer = CreateDataTimer(1.0, Timer_StartMapVote, data, TIMER_FLAG_NO_MAPCHANGE | TIMER_REPEAT); - - /* Mapchooser Extended */ - WritePackCell(data, FAILURE_TIMER_LENGTH); - - if (GetConVarBool(g_Cvar_RunOff) && g_RunoffCount > 0) - { - WritePackString(data, "Revote Warning"); - } else { - WritePackString(data, "Vote Warning"); - } - /* End Mapchooser Extended */ - - WritePackCell(data, _:when); - WritePackCell(data, _:inputlist); - ResetPack(data); - return; - } - - /* If the main map vote has completed (and chosen result) and its currently changing (not a delayed change) we block further attempts */ - if (g_MapVoteCompleted && g_ChangeMapInProgress) - { - return; - } - - g_ChangeTime = when; - - g_WaitingForVote = false; - - g_HasVoteStarted = true; - - if (g_NativeVotes) - { - g_VoteMenu = NativeVotes_Create(Handler_MapVoteMenu, NativeVotesType_NextLevelMult, MenuAction_End | MenuAction_VoteCancel | MenuAction_Display | MenuAction_DisplayItem); - NativeVotes_SetResultCallback(g_VoteMenu, Handler_NativeVoteFinished); - } - else - { - new Handle:menuStyle = GetMenuStyleHandle(MenuStyle:GetConVarInt(g_Cvar_MenuStyle)); - - if (menuStyle != INVALID_HANDLE) - { - g_VoteMenu = CreateMenuEx(menuStyle, Handler_MapVoteMenu, MenuAction_End | MenuAction_Display | MenuAction_DisplayItem | MenuAction_VoteCancel); - } - else - { - // You chose... poorly - g_VoteMenu = CreateMenu(Handler_MapVoteMenu, MenuAction_End | MenuAction_Display | MenuAction_DisplayItem | MenuAction_VoteCancel); - } - - g_AddNoVote = GetConVarBool(g_Cvar_NoVoteOption); - - // Block Vote Slots - if (GetConVarBool(g_Cvar_BlockSlots)) - { - new Handle:radioStyle = GetMenuStyleHandle(MenuStyle_Radio); - - if (GetMenuStyle(g_VoteMenu) == radioStyle) - { - g_BlockedSlots = true; - AddMenuItem(g_VoteMenu, LINE_ONE, "Choose something...", ITEMDRAW_DISABLED); - AddMenuItem(g_VoteMenu, LINE_TWO, "...will ya?", ITEMDRAW_DISABLED); - if (!g_AddNoVote) - { - AddMenuItem(g_VoteMenu, LINE_SPACER, "", ITEMDRAW_SPACER); - } - } else { - g_BlockedSlots = false; - } - } - - if (g_AddNoVote) - { - SetMenuOptionFlags(g_VoteMenu, MENUFLAG_BUTTON_NOVOTE); - } - - SetMenuTitle(g_VoteMenu, "Vote Nextmap"); - SetVoteResultCallback(g_VoteMenu, Handler_MapVoteFinished); - } - - /* Call OnMapVoteStarted() Forward */ -// Call_StartForward(g_MapVoteStartedForward); -// Call_Finish(); - - /** - * TODO: Make a proper decision on when to clear the nominations list. - * Currently it clears when used, and stays if an external list is provided. - * Is this the right thing to do? External lists will probably come from places - * like sm_mapvote from the adminmenu in the future. - */ - - decl String:map[PLATFORM_MAX_PATH]; - - /* No input given - User our internal nominations and maplist */ - if (inputlist == INVALID_HANDLE) - { - new Handle:randomizeList = INVALID_HANDLE; - if (GetConVarBool(g_Cvar_RandomizeNominations)) - { - randomizeList = CloneArray(g_NominateList); - } - - new nominateCount = GetArraySize(g_NominateList); - - new voteSize = GetVoteSize(); - - // The if and else if could be combined, but it looks extremely messy - // This is a hack to lower the vote menu size by 1 when Don't Change or Extend Map should appear - if (g_NativeVotes) - { - if ((when == MapChange_Instant || when == MapChange_RoundEnd) && GetConVarBool(g_Cvar_DontChange)) - { - voteSize--; - } - else if (GetConVarBool(g_Cvar_Extend) && g_Extends < GetConVarInt(g_Cvar_Extend)) - { - voteSize--; - } - } - - /* Smaller of the two - It should be impossible for nominations to exceed the size though (cvar changed mid-map?) */ - new nominationsToAdd = nominateCount >= voteSize ? voteSize : nominateCount; - - new bool:extendFirst = GetConVarBool(g_Cvar_ExtendPosition); - - if (extendFirst) - { - AddExtendToMenu(g_VoteMenu, when); - } - - for (new i=0; i= availableMaps) - { - //Run out of maps, this will have to do. - break; - } - } - - if (randomizeList != INVALID_HANDLE) - { - // Fisher-Yates Shuffle - for (new j = GetArraySize(randomizeList) - 1; j >= 1; j--) - { - new k = GetRandomInt(0, j); - SwapArrayItems(randomizeList, j, k); - } - - for (new j = 0; j < GetArraySize(randomizeList); j++) - { - GetArrayString(randomizeList, j, map, PLATFORM_MAX_PATH); - AddMapItem(map); - } - - CloseHandle(randomizeList); - randomizeList = INVALID_HANDLE; - } - - /* Wipe out our nominations list - Nominations have already been informed of this */ - g_NominateCount = 0; - ClearArray(g_NominateOwners); - ClearArray(g_NominateList); - - if (!extendFirst) - { - AddExtendToMenu(g_VoteMenu, when); - } - } - else //We were given a list of maps to start the vote with - { - new size = GetArraySize(inputlist); - - for (new i=0; i 0) - { - ExtendMapTimeLimit(GetConVarInt(g_Cvar_ExtendTimeStep)*60); - } - } - - if (g_Cvar_Winlimit != INVALID_HANDLE) - { - new winlimit = GetConVarInt(g_Cvar_Winlimit); - if (winlimit) - { - SetConVarInt(g_Cvar_Winlimit, winlimit + GetConVarInt(g_Cvar_ExtendRoundStep)); - } - } - - if (g_Cvar_Maxrounds != INVALID_HANDLE) - { - new maxrounds = GetConVarInt(g_Cvar_Maxrounds); - if (maxrounds) - { - SetConVarInt(g_Cvar_Maxrounds, maxrounds + GetConVarInt(g_Cvar_ExtendRoundStep)); - } - } - - if (g_Cvar_Fraglimit != INVALID_HANDLE) - { - new fraglimit = GetConVarInt(g_Cvar_Fraglimit); - if (fraglimit) - { - SetConVarInt(g_Cvar_Fraglimit, fraglimit + GetConVarInt(g_Cvar_ExtendFragStep)); - } - } - - if (g_NativeVotes) - { - NativeVotes_DisplayPassEx(menu, NativeVotesPass_Extend); - } - - CPrintToChatAll("[MCE] %t", "Current Map Extended", RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes); - LogAction(-1, -1, "Voting for next map has finished. The current map has been extended."); - - // We extended, so we'll have to vote again. - g_RunoffCount = 0; - g_HasVoteStarted = false; - CreateNextVote(); - SetupTimeleftTimer(); - - } - else if (strcmp(map, VOTE_DONTCHANGE, false) == 0) - { - if (g_NativeVotes) - { - NativeVotes_DisplayPassEx(menu, NativeVotesPass_Extend); - } - - CPrintToChatAll("[MCE] %t", "Current Map Stays", RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes); - LogAction(-1, -1, "Voting for next map has finished. 'No Change' was the winner"); - - g_RunoffCount = 0; - g_HasVoteStarted = false; - CreateNextVote(); - SetupTimeleftTimer(); - } - else - { - if (g_ChangeTime == MapChange_MapEnd) - { - SetNextMap(map); - if (g_NativeVotes) - { - NativeVotes_DisplayPass(menu, map); - } - } - else if (g_ChangeTime == MapChange_Instant) - { - new Handle:data; - CreateDataTimer(4.0, Timer_ChangeMap, data); - WritePackString(data, map); - g_ChangeMapInProgress = false; - if (g_NativeVotes) - { - NativeVotes_DisplayPassEx(menu, NativeVotesPass_ChgLevel, map); - } - } - else // MapChange_RoundEnd - { - SetNextMap(map); - g_ChangeMapAtRoundEnd = true; - - if (g_NativeVotes) - { - NativeVotes_DisplayPass(menu, map); - } - } - - g_HasVoteStarted = false; - g_MapVoteCompleted = true; - - CPrintToChatAll("[MCE] %t", "Nextmap Voting Finished", map, RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes); - LogAction(-1, -1, "Voting for next map has finished. Nextmap: %s.", map); - } -} - -public Handler_MapVoteFinished(Handle:menu, - num_votes, - num_clients, - const client_info[][2], - num_items, - const item_info[][2]) -{ - // Implement revote logic - Only run this` block if revotes are enabled and this isn't the last revote - if (GetConVarBool(g_Cvar_RunOff) && num_items > 1 && g_RunoffCount < GetConVarInt(g_Cvar_MaxRunOffs)) - { - g_RunoffCount++; - new highest_votes = item_info[0][VOTEINFO_ITEM_VOTES]; - new required_percent = GetConVarInt(g_Cvar_RunOffPercent); - new required_votes = RoundToCeil(float(num_votes) * float(required_percent) / 100); - - if (highest_votes == item_info[1][VOTEINFO_ITEM_VOTES]) - { - g_HasVoteStarted = false; - - //Revote is needed - new arraySize = ByteCountToCells(PLATFORM_MAX_PATH); - new Handle:mapList = CreateArray(arraySize); - - for (new i = 0; i < num_items; i++) - { - if (item_info[i][VOTEINFO_ITEM_VOTES] == highest_votes) - { - decl String:map[PLATFORM_MAX_PATH]; - - GetMapItem(menu, item_info[i][VOTEINFO_ITEM_INDEX], map, PLATFORM_MAX_PATH); - PushArrayString(mapList, map); - } - else - { - break; - } - } - - if (g_NativeVotes) - { - NativeVotes_DisplayFail(menu, NativeVotesFail_NotEnoughVotes); - } - - CPrintToChatAll("[MCE] %t", "Tie Vote", GetArraySize(mapList)); - SetupWarningTimer(WarningType_Revote, MapChange:g_ChangeTime, mapList); - return; - } - else if (highest_votes < required_votes) - { - g_HasVoteStarted = false; - - //Revote is needed - new arraySize = ByteCountToCells(PLATFORM_MAX_PATH); - new Handle:mapList = CreateArray(arraySize); - - decl String:map1[PLATFORM_MAX_PATH]; - GetMapItem(menu, item_info[0][VOTEINFO_ITEM_INDEX], map1, PLATFORM_MAX_PATH); - - PushArrayString(mapList, map1); - - // We allow more than two maps for a revote if they are tied - for (new i = 1; i < num_items; i++) - { - if (GetArraySize(mapList) < 2 || item_info[i][VOTEINFO_ITEM_VOTES] == item_info[i - 1][VOTEINFO_ITEM_VOTES]) - { - decl String:map[PLATFORM_MAX_PATH]; - GetMapItem(menu, item_info[i][VOTEINFO_ITEM_INDEX], map, PLATFORM_MAX_PATH); - PushArrayString(mapList, map); - } - else - { - break; - } - } - - if (g_NativeVotes) - { - NativeVotes_DisplayFail(menu, NativeVotesFail_NotEnoughVotes); - } - - CPrintToChatAll("[MCE] %t", "Revote Is Needed", required_percent); - SetupWarningTimer(WarningType_Revote, MapChange:g_ChangeTime, mapList); - return; - } - } - - // No revote needed, continue as normal. - Handler_VoteFinishedGeneric(menu, num_votes, num_clients, client_info, num_items, item_info); -} - -// This is shared by NativeVotes now, but NV doesn't support Display or DisplayItem -public Handler_MapVoteMenu(Handle:menu, MenuAction:action, param1, param2) -{ - switch (action) - { - case MenuAction_End: - { - g_VoteMenu = INVALID_HANDLE; - if (g_NativeVotes) - { - NativeVotes_Close(menu); - } - else - { - CloseHandle(menu); - } - } - - case MenuAction_Display: - { - // NativeVotes uses the standard TF2/CSGO vote screen - if (!g_NativeVotes) - { - decl String:buffer[255]; - Format(buffer, sizeof(buffer), "%T", "Vote Nextmap", param1); - new Handle:panel = Handle:param2; - SetPanelTitle(panel, buffer); - } - } - - case MenuAction_DisplayItem: - { - new String:map[PLATFORM_MAX_PATH]; - new String:buffer[255]; - new mark = GetConVarInt(g_Cvar_MarkCustomMaps); - - if (g_NativeVotes) - { - NativeVotes_GetItem(menu, param2, map, PLATFORM_MAX_PATH); - } - else - { - GetMenuItem(menu, param2, map, PLATFORM_MAX_PATH); - } - - if (StrEqual(map, VOTE_EXTEND, false)) - { - Format(buffer, sizeof(buffer), "%T", "Extend Map", param1); - } - else if (StrEqual(map, VOTE_DONTCHANGE, false)) - { - Format(buffer, sizeof(buffer), "%T", "Dont Change", param1); - } - // Mapchooser Extended - else if (StrEqual(map, LINE_ONE, false)) - { - Format(buffer, sizeof(buffer),"%T", "Line One", param1); - } - else if (StrEqual(map, LINE_TWO, false)) - { - Format(buffer, sizeof(buffer),"%T", "Line Two", param1); - } - // Note that the first part is to discard the spacer line - else if (!StrEqual(map, LINE_SPACER, false)) - { - if (mark == 1 && !InternalIsMapOfficial(map)) - { - Format(buffer, sizeof(buffer), "%T", "Custom Marked", param1, map); - } - else if (mark == 2 && !InternalIsMapOfficial(map)) - { - Format(buffer, sizeof(buffer), "%T", "Custom", param1, map); - } - } - - if (buffer[0] != '\0') - { - if (g_NativeVotes) - { - NativeVotes_RedrawVoteItem(buffer); - return _:Plugin_Continue; - } - else - { - return RedrawMenuItem(buffer); - } - } - // End Mapchooser Extended - } - - case MenuAction_VoteCancel: - { - // If we receive 0 votes, pick at random. - if (param1 == VoteCancel_NoVotes && GetConVarBool(g_Cvar_NoVoteMode)) - { - new count; - if (g_NativeVotes) - { - count = NativeVotes_GetItemCount(menu); - } - else - { - count = GetMenuItemCount(menu); - } - - decl item; - decl String:map[PLATFORM_MAX_PATH]; - - do - { - new startInt = 0; - if (!g_NativeVotes && g_BlockedSlots) - { - if (g_AddNoVote) - { - startInt = 2; - } - else - { - startInt = 3; - } - } - item = GetRandomInt(startInt, count - 1); - if (g_NativeVotes) - { - NativeVotes_GetItem(menu, item, map, PLATFORM_MAX_PATH); - } - else - { - GetMenuItem(menu, item, map, PLATFORM_MAX_PATH); - } - } - while (strcmp(map, VOTE_EXTEND, false) == 0); - - SetNextMap(map); - g_MapVoteCompleted = true; - - if (g_NativeVotes) - { - NativeVotes_DisplayPass(menu, map); - } - } - else if (g_NativeVotes) - { - new NativeVotesFailType:reason = NativeVotesFail_Generic; - if (param1 == VoteCancel_NoVotes) - { - reason = NativeVotesFail_NotEnoughVotes; - } - - NativeVotes_DisplayFail(menu, reason); - } - else - { - // We were actually cancelled. I guess we do nothing. - } - - g_HasVoteStarted = false; - } - } - - return 0; -} - -public Action:Timer_ChangeMap(Handle:hTimer, Handle:dp) -{ - g_ChangeMapInProgress = false; - - new String:map[PLATFORM_MAX_PATH]; - - if (dp == INVALID_HANDLE) - { - if (!GetNextMap(map, PLATFORM_MAX_PATH)) - { - //No passed map and no set nextmap. fail! - return Plugin_Stop; - } - } - else - { - ResetPack(dp); - ReadPackString(dp, map, PLATFORM_MAX_PATH); - } - - ForceChangeLevel(map, "Map Vote"); - - return Plugin_Stop; -} - -bool:RemoveStringFromArray(Handle:array, String:str[]) -{ - new index = FindStringInArray(array, str); - if (index != -1) - { - RemoveFromArray(array, index); - return true; - } - - return false; -} - -CreateNextVote() -{ - assert(g_NextMapList) - ClearArray(g_NextMapList); - - decl String:map[PLATFORM_MAX_PATH]; - new Handle:tempMaps = CloneArray(g_MapList); - - GetCurrentMap(map, PLATFORM_MAX_PATH); - RemoveStringFromArray(tempMaps, map); - - if (GetConVarInt(g_Cvar_ExcludeMaps) && GetArraySize(tempMaps) > GetConVarInt(g_Cvar_ExcludeMaps)) - { - for (new i = 0; i < GetArraySize(g_OldMapList); i++) - { - GetArrayString(g_OldMapList, i, map, PLATFORM_MAX_PATH); - RemoveStringFromArray(tempMaps, map); - } - } - - new voteSize = GetVoteSize(); - new limit = (voteSize < GetArraySize(tempMaps) ? voteSize : GetArraySize(tempMaps)); - - for (new i = 0; i < limit; i++) - { - new b = GetRandomInt(0, GetArraySize(tempMaps) - 1); - GetArrayString(tempMaps, b, map, PLATFORM_MAX_PATH); - PushArrayString(g_NextMapList, map); - RemoveFromArray(tempMaps, b); - } - - CloseHandle(tempMaps); -} - -bool:CanVoteStart() -{ - if (g_WaitingForVote || g_HasVoteStarted) - { - return false; - } - - return true; -} - -NominateResult:InternalNominateMap(String:map[], bool:force, owner) -{ - if (!IsMapValid(map)) - { - return Nominate_InvalidMap; - } - - /* Map already in the vote */ - if (FindStringInArray(g_NominateList, map) != -1) - { - return Nominate_AlreadyInVote; - } - - new index; - - /* Look to replace an existing nomination by this client - Nominations made with owner = 0 aren't replaced */ - if (owner && ((index = FindValueInArray(g_NominateOwners, owner)) != -1)) - { - new String:oldmap[PLATFORM_MAX_PATH]; - GetArrayString(g_NominateList, index, oldmap, PLATFORM_MAX_PATH); - Call_StartForward(g_NominationsResetForward); - Call_PushString(oldmap); - Call_PushCell(owner); - Call_Finish(); - - SetArrayString(g_NominateList, index, map); - return Nominate_Replaced; - } - - /* Too many nominated maps. */ - if (g_NominateCount >= GetVoteSize() && !force) - { - return Nominate_VoteFull; - } - - PushArrayString(g_NominateList, map); - PushArrayCell(g_NominateOwners, owner); - g_NominateCount++; - - while (GetArraySize(g_NominateList) > GetVoteSize()) - { - new String:oldmap[PLATFORM_MAX_PATH]; - GetArrayString(g_NominateList, 0, oldmap, PLATFORM_MAX_PATH); - Call_StartForward(g_NominationsResetForward); - Call_PushString(oldmap); - Call_PushCell(GetArrayCell(g_NominateOwners, 0)); - Call_Finish(); - - RemoveFromArray(g_NominateList, 0); - RemoveFromArray(g_NominateOwners, 0); - } - - return Nominate_Added; -} - -/* Add natives to allow nominate and initiate vote to be call */ - -/* native bool:NominateMap(const String:map[], bool:force, &NominateError:error); */ -public Native_NominateMap(Handle:plugin, numParams) -{ - new len; - GetNativeStringLength(1, len); - - if (len <= 0) - { - return false; - } - - new String:map[len+1]; - GetNativeString(1, map, len+1); - - return _:InternalNominateMap(map, GetNativeCell(2), GetNativeCell(3)); -} - -bool:InternalRemoveNominationByMap(String:map[]) -{ - for (new i = 0; i < GetArraySize(g_NominateList); i++) - { - new String:oldmap[PLATFORM_MAX_PATH]; - GetArrayString(g_NominateList, i, oldmap, PLATFORM_MAX_PATH); - - if(strcmp(map, oldmap, false) == 0) - { - Call_StartForward(g_NominationsResetForward); - Call_PushString(oldmap); - Call_PushCell(GetArrayCell(g_NominateOwners, i)); - Call_Finish(); - - RemoveFromArray(g_NominateList, i); - RemoveFromArray(g_NominateOwners, i); - g_NominateCount--; - - return true; - } - } - - return false; -} - -/* native bool:RemoveNominationByMap(const String:map[]); */ -public Native_RemoveNominationByMap(Handle:plugin, numParams) -{ - new len; - GetNativeStringLength(1, len); - - if (len <= 0) - { - return false; - } - - new String:map[len+1]; - GetNativeString(1, map, len+1); - - return _:InternalRemoveNominationByMap(map); -} - -bool:InternalRemoveNominationByOwner(owner) -{ - new index; - - if (owner && ((index = FindValueInArray(g_NominateOwners, owner)) != -1)) - { - new String:oldmap[PLATFORM_MAX_PATH]; - GetArrayString(g_NominateList, index, oldmap, PLATFORM_MAX_PATH); - - Call_StartForward(g_NominationsResetForward); - Call_PushString(oldmap); - Call_PushCell(owner); - Call_Finish(); - - RemoveFromArray(g_NominateList, index); - RemoveFromArray(g_NominateOwners, index); - g_NominateCount--; - - return true; - } - - return false; -} - -/* native bool:RemoveNominationByOwner(owner); */ -public Native_RemoveNominationByOwner(Handle:plugin, numParams) -{ - return _:InternalRemoveNominationByOwner(GetNativeCell(1)); -} - -/* native InitiateMapChooserVote(); */ -public Native_InitiateVote(Handle:plugin, numParams) -{ - new MapChange:when = MapChange:GetNativeCell(1); - new Handle:inputarray = Handle:GetNativeCell(2); - - LogAction(-1, -1, "Starting map vote because outside request"); - - SetupWarningTimer(WarningType_Vote, when, inputarray); - //InitiateVote(when, inputarray); -} - -public Native_CanVoteStart(Handle:plugin, numParams) -{ - return CanVoteStart(); -} - -public Native_CheckVoteDone(Handle:plugin, numParams) -{ - return g_MapVoteCompleted; -} - -public Native_EndOfMapVoteEnabled(Handle:plugin, numParams) -{ - return GetConVarBool(g_Cvar_EndOfMapVote); -} - -public Native_GetExcludeMapList(Handle:plugin, numParams) -{ - new Handle:array = Handle:GetNativeCell(1); - - if (array == INVALID_HANDLE) - { - return; - } - new size = GetArraySize(g_OldMapList); - decl String:map[PLATFORM_MAX_PATH]; - - for (new i=0; i -1); -} - -public Native_IsWarningTimer(Handle:plugin, numParams) -{ - return g_WarningInProgress; -} - -public Native_CanNominate(Handle:plugin, numParams) -{ - if (g_HasVoteStarted) - { - return _:CanNominate_No_VoteInProgress; - } - - if (g_MapVoteCompleted) - { - return _:CanNominate_No_VoteComplete; - } - - if (g_NominateCount >= GetVoteSize()) - { - return _:CanNominate_No_VoteFull; - } - - return _:CanNominate_Yes; -} - -public Native_ExcludeMap(Handle:plugin, numParams) -{ - new len; - GetNativeStringLength(1, len); - - if (len <= 0) - { - return false; - } - - new String:map[len+1]; - GetNativeString(1, map, len+1); - - PushArrayString(g_OldMapList, map); - - return true; -} - -stock AddMapItem(const String:map[]) -{ - if (g_NativeVotes) - { - NativeVotes_AddItem(g_VoteMenu, map, map); - } - else - { - AddMenuItem(g_VoteMenu, map, map); - } -} - -stock GetMapItem(Handle:menu, position, String:map[], mapLen) -{ - if (g_NativeVotes) - { - NativeVotes_GetItem(menu, position, map, mapLen); - } - else - { - GetMenuItem(menu, position, map, mapLen); - } -} - -stock AddExtendToMenu(Handle:menu, MapChange:when) -{ - /* Do we add any special items? */ - // Moved for Mapchooser Extended - - if ((when == MapChange_Instant || when == MapChange_RoundEnd) && GetConVarBool(g_Cvar_DontChange)) - { - if (g_NativeVotes) - { - // Built-in votes don't have "Don't Change", send Extend instead - NativeVotes_AddItem(menu, VOTE_DONTCHANGE, "Don't Change"); - } - else - { - AddMenuItem(menu, VOTE_DONTCHANGE, "Don't Change"); - } - } - else if (GetConVarBool(g_Cvar_Extend) && g_Extends < GetConVarInt(g_Cvar_Extend)) - { - if (g_NativeVotes) - { - NativeVotes_AddItem(menu, VOTE_EXTEND, "Extend Map"); - } - else - { - AddMenuItem(menu, VOTE_EXTEND, "Extend Map"); - } - } -} - -// Using this stock REQUIRES you to add the following to AskPluginLoad2: -// MarkNativeAsOptional("GetEngineVersion"); -stock EngineVersion:GetEngineVersionCompat() -{ - new EngineVersion:version; - if (GetFeatureStatus(FeatureType_Native, "GetEngineVersion") != FeatureStatus_Available) - { - new sdkVersion = GuessSDKVersion(); - switch (sdkVersion) - { - case SOURCE_SDK_ORIGINAL: - { - version = Engine_Original; - } - - case SOURCE_SDK_DARKMESSIAH: - { - version = Engine_DarkMessiah; - } - - case SOURCE_SDK_EPISODE1: - { - version = Engine_SourceSDK2006; - } - - case SOURCE_SDK_EPISODE2: - { - version = Engine_SourceSDK2007; - } - - case SOURCE_SDK_BLOODYGOODTIME: - { - version = Engine_BloodyGoodTime; - } - - case SOURCE_SDK_EYE: - { - version = Engine_EYE; - } - - case SOURCE_SDK_CSS: - { - version = Engine_CSS; - } - - case SOURCE_SDK_EPISODE2VALVE: - { - decl String:gameFolder[PLATFORM_MAX_PATH]; - GetGameFolderName(gameFolder, PLATFORM_MAX_PATH); - if (StrEqual(gameFolder, "dod", false)) - { - version = Engine_DODS; - } - else if (StrEqual(gameFolder, "hl2mp", false)) - { - version = Engine_HL2DM; - } - else - { - version = Engine_TF2; - } - } - - case SOURCE_SDK_LEFT4DEAD: - { - version = Engine_Left4Dead; - } - - case SOURCE_SDK_LEFT4DEAD2: - { - decl String:gameFolder[PLATFORM_MAX_PATH]; - GetGameFolderName(gameFolder, PLATFORM_MAX_PATH); - if (StrEqual(gameFolder, "nd", false)) - { - version = Engine_NuclearDawn; - } - else - { - version = Engine_Left4Dead2; - } - } - - case SOURCE_SDK_ALIENSWARM: - { - version = Engine_AlienSwarm; - } - - case SOURCE_SDK_CSGO: - { - version = Engine_CSGO; - } - - default: - { - version = Engine_Unknown; - } - } - } - else - { - version = GetEngineVersion(); - } - - return version; -} - -GetVoteSize() -{ - new voteSize = GetConVarInt(g_Cvar_IncludeMaps); - - // New in 1.9.5 to let us bump up the included maps count - if (g_NativeVotes) - { - new max = NativeVotes_GetMaxItems(); - - if (max < voteSize) - { - voteSize = max; - } - } - - return voteSize; +/** + * vim: set ts=4 : + * ============================================================================= + * MapChooser Extended + * Creates a map vote at appropriate times, setting sm_nextmap to the winning + * vote. Includes extra options not present in the SourceMod MapChooser + * + * MapChooser Extended (C)2011-2013 Powerlord (Ross Bemrose) + * SourceMod (C)2004-2007 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$ + */ + +//#define DEBUG + +#if defined DEBUG + #define assert(%1) if (!(%1)) ThrowError("Debug Assertion Failed"); + #define assert_msg(%1,%2) if (!(%1)) ThrowError(%2); +#else + #define assert(%1) + #define assert_msg(%1,%2) +#endif + +#pragma semicolon 1 +#include +#include +#include "include/mapchooser_extended" +#include +#include +#include + +#undef REQUIRE_PLUGIN +#include + +#define MCE_VERSION "1.10.2" + +#define NV "nativevotes" + +enum RoundCounting +{ + RoundCounting_Standard = 0, + RoundCounting_MvM, + RoundCounting_ArmsRace, +} + +// CSGO requires two cvars to get the game type +enum +{ + GameType_Classic = 0, + GameType_GunGame = 1, + GameType_Training = 2, + GameType_Custom = 3, +} + +enum +{ + GunGameMode_ArmsRace = 0, + GunGameMode_Demolition = 1, + GunGameMode_DeathMatch = 2, +} + +public Plugin:myinfo = +{ + name = "MapChooser Extended", + author = "Powerlord, Zuko, and AlliedModders LLC", + description = "Automated Map Voting with Extensions", + version = MCE_VERSION, + url = "https://forums.alliedmods.net/showthread.php?t=156974" +}; + +/* Valve ConVars */ +new Handle:g_Cvar_Winlimit = INVALID_HANDLE; +new Handle:g_Cvar_Maxrounds = INVALID_HANDLE; +new Handle:g_Cvar_Fraglimit = INVALID_HANDLE; +new Handle:g_Cvar_Bonusroundtime = INVALID_HANDLE; +new Handle:g_Cvar_MatchClinch = INVALID_HANDLE; +new Handle:g_Cvar_VoteNextLevel = INVALID_HANDLE; +new Handle:g_Cvar_GameType = INVALID_HANDLE; +new Handle:g_Cvar_GameMode = INVALID_HANDLE; + +/* Plugin ConVars */ +new Handle:g_Cvar_StartTime = INVALID_HANDLE; +new Handle:g_Cvar_StartRounds = INVALID_HANDLE; +new Handle:g_Cvar_StartFrags = INVALID_HANDLE; +new Handle:g_Cvar_ExtendTimeStep = INVALID_HANDLE; +new Handle:g_Cvar_ExtendRoundStep = INVALID_HANDLE; +new Handle:g_Cvar_ExtendFragStep = INVALID_HANDLE; +new Handle:g_Cvar_ExcludeMaps = INVALID_HANDLE; +new Handle:g_Cvar_IncludeMaps = INVALID_HANDLE; +new Handle:g_Cvar_NoVoteMode = INVALID_HANDLE; +new Handle:g_Cvar_Extend = INVALID_HANDLE; +new Handle:g_Cvar_DontChange = INVALID_HANDLE; +new Handle:g_Cvar_EndOfMapVote = INVALID_HANDLE; +new Handle:g_Cvar_VoteDuration = INVALID_HANDLE; + +new Handle:g_VoteTimer = INVALID_HANDLE; +new Handle:g_RetryTimer = INVALID_HANDLE; +new Handle:g_WarningTimer = INVALID_HANDLE; + +/* Data Handles */ +new Handle:g_MapList = INVALID_HANDLE; +new Handle:g_NominateList = INVALID_HANDLE; +new Handle:g_NominateOwners = INVALID_HANDLE; +new Handle:g_OldMapList = INVALID_HANDLE; +new Handle:g_NextMapList = INVALID_HANDLE; +new Handle:g_VoteMenu = INVALID_HANDLE; + +new g_Extends; +new g_TotalRounds; +new bool:g_HasVoteStarted; +new bool:g_WaitingForVote; +new bool:g_MapVoteCompleted; +new bool:g_ChangeMapAtRoundEnd; +new bool:g_ChangeMapInProgress; +new bool:g_HasIntermissionStarted = false; +new g_mapFileSerial = -1; + +new g_NominateCount = 0; +new MapChange:g_ChangeTime; + +new Handle:g_NominationsResetForward = INVALID_HANDLE; +new Handle:g_MapVoteStartedForward = INVALID_HANDLE; + +/* Mapchooser Extended Plugin ConVars */ + +new Handle:g_Cvar_RunOff = INVALID_HANDLE; +new Handle:g_Cvar_RunOffPercent = INVALID_HANDLE; +new Handle:g_Cvar_BlockSlots = INVALID_HANDLE; +new Handle:g_Cvar_MaxRunOffs = INVALID_HANDLE; +new Handle:g_Cvar_StartTimePercent = INVALID_HANDLE; +new Handle:g_Cvar_StartTimePercentEnable = INVALID_HANDLE; +new Handle:g_Cvar_WarningTime = INVALID_HANDLE; +new Handle:g_Cvar_RunOffWarningTime = INVALID_HANDLE; +new Handle:g_Cvar_MenuStyle = INVALID_HANDLE; +new Handle:g_Cvar_TimerLocation = INVALID_HANDLE; +new Handle:g_Cvar_ExtendPosition = INVALID_HANDLE; +new Handle:g_Cvar_MarkCustomMaps = INVALID_HANDLE; +new Handle:g_Cvar_RandomizeNominations = INVALID_HANDLE; +new Handle:g_Cvar_HideTimer = INVALID_HANDLE; +new Handle:g_Cvar_NoVoteOption = INVALID_HANDLE; + +/* Mapchooser Extended Data Handles */ +new Handle:g_OfficialList = INVALID_HANDLE; + +/* Mapchooser Extended Forwards */ +new Handle:g_MapVoteWarningStartForward = INVALID_HANDLE; +new Handle:g_MapVoteWarningTickForward = INVALID_HANDLE; +new Handle:g_MapVoteStartForward = INVALID_HANDLE; +new Handle:g_MapVoteEndForward = INVALID_HANDLE; +new Handle:g_MapVoteRunoffStartForward = INVALID_HANDLE; + +/* Mapchooser Extended Globals */ +new g_RunoffCount = 0; +new g_mapOfficialFileSerial = -1; +new bool:g_NativeVotes = false; +new String:g_GameModName[64]; +new bool:g_WarningInProgress = false; +new bool:g_AddNoVote = false; + +new RoundCounting:g_RoundCounting = RoundCounting_Standard; + +/* Upper bound of how many team there could be */ +#define MAXTEAMS 10 +new g_winCount[MAXTEAMS]; + +new bool:g_BlockedSlots = false; +new g_ObjectiveEnt = -1; + +enum TimerLocation +{ + TimerLocation_Hint = 0, + TimerLocation_Center = 1, + TimerLocation_Chat = 2, +} + +enum WarningType +{ + WarningType_Vote, + WarningType_Revote, +} + +#define VOTE_EXTEND "##extend##" +#define VOTE_DONTCHANGE "##dontchange##" + +/* Mapchooser Extended Defines */ +#define LINE_ONE "##lineone##" +#define LINE_TWO "##linetwo##" +#define LINE_SPACER "##linespacer##" +#define FAILURE_TIMER_LENGTH 5 + +public OnPluginStart() +{ + LoadTranslations("mapchooser_extended.phrases"); + LoadTranslations("basevotes.phrases"); + LoadTranslations("common.phrases"); + + new arraySize = ByteCountToCells(PLATFORM_MAX_PATH); + g_MapList = CreateArray(arraySize); + g_NominateList = CreateArray(arraySize); + g_NominateOwners = CreateArray(1); + g_OldMapList = CreateArray(arraySize); + g_NextMapList = CreateArray(arraySize); + g_OfficialList = CreateArray(arraySize); + + GetGameFolderName(g_GameModName, sizeof(g_GameModName)); + + g_Cvar_EndOfMapVote = CreateConVar("mce_endvote", "1", "Specifies if MapChooser should run an end of map vote", _, true, 0.0, true, 1.0); + + g_Cvar_StartTime = CreateConVar("mce_starttime", "10.0", "Specifies when to start the vote based on time remaining.", _, true, 1.0); + g_Cvar_StartRounds = CreateConVar("mce_startround", "2.0", "Specifies when to start the vote based on rounds remaining. Use 0 on DoD:S, CS:S, and TF2 to start vote during bonus round time", _, true, 0.0); + g_Cvar_StartFrags = CreateConVar("mce_startfrags", "5.0", "Specifies when to start the vote base on frags remaining.", _, true, 1.0); + g_Cvar_ExtendTimeStep = CreateConVar("mce_extend_timestep", "15", "Specifies how much many more minutes each extension makes", _, true, 5.0); + g_Cvar_ExtendRoundStep = CreateConVar("mce_extend_roundstep", "5", "Specifies how many more rounds each extension makes", _, true, 1.0); + g_Cvar_ExtendFragStep = CreateConVar("mce_extend_fragstep", "10", "Specifies how many more frags are allowed when map is extended.", _, true, 5.0); + g_Cvar_ExcludeMaps = CreateConVar("mce_exclude", "5", "Specifies how many past maps to exclude from the vote.", _, true, 0.0); + g_Cvar_IncludeMaps = CreateConVar("mce_include", "5", "Specifies how many maps to include in the vote.", _, true, 2.0, true, 6.0); + g_Cvar_NoVoteMode = CreateConVar("mce_novote", "1", "Specifies whether or not MapChooser should pick a map if no votes are received.", _, true, 0.0, true, 1.0); + g_Cvar_Extend = CreateConVar("mce_extend", "0", "Number of extensions allowed each map.", _, true, 0.0); + g_Cvar_DontChange = CreateConVar("mce_dontchange", "1", "Specifies if a 'Don't Change' option should be added to early votes", _, true, 0.0); + g_Cvar_VoteDuration = CreateConVar("mce_voteduration", "20", "Specifies how long the mapvote should be available for.", _, true, 5.0); + + // MapChooser Extended cvars + CreateConVar("mce_version", MCE_VERSION, "MapChooser Extended Version", FCVAR_SPONLY|FCVAR_NOTIFY|FCVAR_DONTRECORD); + + g_Cvar_RunOff = CreateConVar("mce_runoff", "1", "Hold run off votes if winning choice has less than a certain percentage of votes", _, true, 0.0, true, 1.0); + g_Cvar_RunOffPercent = CreateConVar("mce_runoffpercent", "50", "If winning choice has less than this percent of votes, hold a runoff", _, true, 0.0, true, 100.0); + g_Cvar_BlockSlots = CreateConVar("mce_blockslots", "1", "Block slots to prevent accidental votes. Only applies when Voice Command style menus are in use.", _, true, 0.0, true, 1.0); + //g_Cvar_BlockSlotsCount = CreateConVar("mce_blockslots_count", "2", "Number of slots to block.", _, true, 1.0, true, 3.0); + g_Cvar_MaxRunOffs = CreateConVar("mce_maxrunoffs", "1", "Number of run off votes allowed each map.", _, true, 0.0); + g_Cvar_StartTimePercent = CreateConVar("mce_start_percent", "35.0", "Specifies when to start the vote based on percents.", _, true, 0.0, true, 100.0); + g_Cvar_StartTimePercentEnable = CreateConVar("mce_start_percent_enable", "0", "Enable or Disable percentage calculations when to start vote.", _, true, 0.0, true, 1.0); + g_Cvar_WarningTime = CreateConVar("mce_warningtime", "15.0", "Warning time in seconds.", _, true, 0.0, true, 60.0); + g_Cvar_RunOffWarningTime = CreateConVar("mce_runoffvotewarningtime", "5.0", "Warning time for runoff vote in seconds.", _, true, 0.0, true, 30.0); + g_Cvar_MenuStyle = CreateConVar("mce_menustyle", "0", "Menu Style. 0 is the game's default, 1 is the older Valve style that requires you to press Escape to see the menu, 2 is the newer 1-9 button Voice Command style, unavailable in some games. Ignored on TF2 if NativeVotes Plugin is loaded.", _, true, 0.0, true, 2.0); + g_Cvar_TimerLocation = CreateConVar("mce_warningtimerlocation", "0", "Location for the warning timer text. 0 is HintBox, 1 is Center text, 2 is Chat. Defaults to HintBox.", _, true, 0.0, true, 2.0); + g_Cvar_MarkCustomMaps = CreateConVar("mce_markcustommaps", "1", "Mark custom maps in the vote list. 0 = Disabled, 1 = Mark with *, 2 = Mark with phrase.", _, true, 0.0, true, 2.0); + g_Cvar_ExtendPosition = CreateConVar("mce_extendposition", "0", "Position of Extend/Don't Change options. 0 = at end, 1 = at start.", _, true, 0.0, true, 1.0); + g_Cvar_RandomizeNominations = CreateConVar("mce_randomizeorder", "0", "Randomize map order?", _, true, 0.0, true, 1.0); + g_Cvar_HideTimer = CreateConVar("mce_hidetimer", "0", "Hide the MapChooser Extended warning timer", _, true, 0.0, true, 1.0); + g_Cvar_NoVoteOption = CreateConVar("mce_addnovote", "1", "Add \"No Vote\" to vote menu?", _, true, 0.0, true, 1.0); + + RegAdminCmd("sm_mapvote", Command_Mapvote, ADMFLAG_CHANGEMAP, "sm_mapvote - Forces MapChooser to attempt to run a map vote now."); + RegAdminCmd("sm_setnextmap", Command_SetNextmap, ADMFLAG_CHANGEMAP, "sm_setnextmap "); + + // Mapchooser Extended Commands + RegAdminCmd("mce_reload_maplist", Command_ReloadMaps, ADMFLAG_CHANGEMAP, "mce_reload_maplist - Reload the Official Maplist file."); + + g_Cvar_Winlimit = FindConVar("mp_winlimit"); + g_Cvar_Maxrounds = FindConVar("mp_maxrounds"); + g_Cvar_Fraglimit = FindConVar("mp_fraglimit"); + + new EngineVersion:version = GetEngineVersionCompat(); + + decl String:mapListPath[PLATFORM_MAX_PATH]; + + BuildPath(Path_SM, mapListPath, PLATFORM_MAX_PATH, "configs/mapchooser_extended/maps/%s.txt", g_GameModName); + SetMapListCompatBind("official", mapListPath); + + switch (version) + { + case Engine_TF2: + { + g_Cvar_VoteNextLevel = FindConVar("sv_vote_issue_nextlevel_allowed"); + g_Cvar_Bonusroundtime = FindConVar("mp_bonusroundtime"); + } + + case Engine_CSGO: + { + g_Cvar_VoteNextLevel = FindConVar("mp_endmatch_votenextmap"); + g_Cvar_GameType = FindConVar("game_type"); + g_Cvar_GameMode = FindConVar("game_mode"); + g_Cvar_Bonusroundtime = FindConVar("mp_round_restart_delay"); + } + + case Engine_DODS: + { + g_Cvar_Bonusroundtime = FindConVar("dod_bonusroundtime"); + } + + case Engine_CSS: + { + g_Cvar_Bonusroundtime = FindConVar("mp_round_restart_delay"); + } + + default: + { + g_Cvar_Bonusroundtime = FindConVar("mp_bonusroundtime"); + } + } + + if (g_Cvar_Winlimit != INVALID_HANDLE || g_Cvar_Maxrounds != INVALID_HANDLE) + { + switch (version) + { + case Engine_TF2: + { + HookEvent("teamplay_win_panel", Event_TeamPlayWinPanel); + HookEvent("teamplay_restart_round", Event_TFRestartRound); + HookEvent("arena_win_panel", Event_TeamPlayWinPanel); + HookEvent("pve_win_panel", Event_MvMWinPanel); + } + + case Engine_NuclearDawn: + { + HookEvent("round_win", Event_RoundEnd); + } + + case Engine_CSGO: + { + HookEvent("round_end", Event_RoundEnd); + HookEvent("cs_intermission", Event_Intermission); + HookEvent("announce_phase_end", Event_PhaseEnd); + g_Cvar_MatchClinch = FindConVar("mp_match_can_clinch"); + } + + case Engine_DODS: + { + HookEvent("dod_round_win", Event_RoundEnd); + } + + default: + { + HookEvent("round_end", Event_RoundEnd); + } + } + } + + if (g_Cvar_Fraglimit != INVALID_HANDLE) + { + HookEvent("player_death", Event_PlayerDeath); + } + + AutoExecConfig(true, "mapchooser_extended"); + + //Change the mp_bonusroundtime max so that we have time to display the vote + //If you display a vote during bonus time good defaults are 17 vote duration and 19 mp_bonustime + if (g_Cvar_Bonusroundtime != INVALID_HANDLE) + { + SetConVarBounds(g_Cvar_Bonusroundtime, ConVarBound_Upper, true, 30.0); + } + + g_NominationsResetForward = CreateGlobalForward("OnNominationRemoved", ET_Ignore, Param_String, Param_Cell); + g_MapVoteStartedForward = CreateGlobalForward("OnMapVoteStarted", ET_Ignore); + + //MapChooser Extended Forwards + g_MapVoteStartForward = CreateGlobalForward("OnMapVoteStart", ET_Ignore); // Deprecated + g_MapVoteEndForward = CreateGlobalForward("OnMapVoteEnd", ET_Ignore, Param_String); + g_MapVoteWarningStartForward = CreateGlobalForward("OnMapVoteWarningStart", ET_Ignore); + g_MapVoteWarningTickForward = CreateGlobalForward("OnMapVoteWarningTick", ET_Ignore, Param_Cell); + g_MapVoteRunoffStartForward = CreateGlobalForward("OnMapVoteRunnoffWarningStart", ET_Ignore); + +} + +public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max) +{ + if (LibraryExists("mapchooser")) + { + strcopy(error, err_max, "MapChooser already loaded, aborting."); + return APLRes_Failure; + } + + RegPluginLibrary("mapchooser"); + + MarkNativeAsOptional("GetEngineVersion"); + + CreateNative("NominateMap", Native_NominateMap); + CreateNative("RemoveNominationByMap", Native_RemoveNominationByMap); + CreateNative("RemoveNominationByOwner", Native_RemoveNominationByOwner); + CreateNative("InitiateMapChooserVote", Native_InitiateVote); + CreateNative("CanMapChooserStartVote", Native_CanVoteStart); + CreateNative("HasEndOfMapVoteFinished", Native_CheckVoteDone); + CreateNative("GetExcludeMapList", Native_GetExcludeMapList); + CreateNative("GetNominatedMapList", Native_GetNominatedMapList); + CreateNative("EndOfMapVoteEnabled", Native_EndOfMapVoteEnabled); + + // MapChooser Extended natives + CreateNative("IsMapOfficial", Native_IsMapOfficial); + CreateNative("CanNominate", Native_CanNominate); + + // BotoX + CreateNative("ExcludeMap", Native_ExcludeMap); + + return APLRes_Success; +} + +public OnAllPluginsLoaded() +{ + g_NativeVotes = LibraryExists(NV) && NativeVotes_IsVoteTypeSupported(NativeVotesType_NextLevelMult); +} + +public OnLibraryAdded(const String:name[]) +{ + if (StrEqual(name, NV) && NativeVotes_IsVoteTypeSupported(NativeVotesType_NextLevelMult)) + { + g_NativeVotes = true; + } +} + +public OnLibraryRemoved(const String:name[]) +{ + if (StrEqual(name, NV)) + { + g_NativeVotes = false; + } +} + +public OnMapStart() +{ + decl String:folder[64]; + GetGameFolderName(folder, sizeof(folder)); + + g_RoundCounting = RoundCounting_Standard; + g_ObjectiveEnt = -1; + + if (strcmp(folder, "tf") == 0 && GameRules_GetProp("m_bPlayingMannVsMachine")) + { + g_RoundCounting = RoundCounting_MvM; + g_ObjectiveEnt = EntIndexToEntRef(FindEntityByClassname(-1, "tf_objective_resource")); + } + else if (strcmp(folder, "csgo") == 0 && GetConVarInt(g_Cvar_GameType) == GameType_GunGame && + GetConVarInt(g_Cvar_GameMode) == GunGameMode_ArmsRace) + { + g_RoundCounting = RoundCounting_ArmsRace; + } +} + +public OnConfigsExecuted() +{ + if (ReadMapList(g_MapList, + g_mapFileSerial, + "mapchooser", + MAPLIST_FLAG_CLEARARRAY|MAPLIST_FLAG_MAPSFOLDER) + != INVALID_HANDLE) + + { + if (g_mapFileSerial == -1) + { + LogError("Unable to create a valid map list."); + } + } + + // Disable the next level vote in TF2 and CS:GO + // In TF2, this has two effects: 1. Stop the next level vote (which overlaps rtv functionality). + // 2. Stop the built-in end level vote. This is the only thing that happens in CS:GO + if (g_Cvar_VoteNextLevel != INVALID_HANDLE) + { + SetConVarBool(g_Cvar_VoteNextLevel, false); + } + + CreateNextVote(); + SetupTimeleftTimer(); + + g_TotalRounds = 0; + + g_Extends = 0; + + g_MapVoteCompleted = false; + + g_NominateCount = 0; + ClearArray(g_NominateList); + ClearArray(g_NominateOwners); + + for (new i=0; i GetConVarInt(g_Cvar_ExcludeMaps)) + { + RemoveFromArray(g_OldMapList, 0); + } +} + +public OnClientDisconnect(client) +{ + new index = FindValueInArray(g_NominateOwners, client); + + if (index == -1) + { + return; + } + + new String:oldmap[PLATFORM_MAX_PATH]; + GetArrayString(g_NominateList, index, oldmap, PLATFORM_MAX_PATH); + Call_StartForward(g_NominationsResetForward); + Call_PushString(oldmap); + Call_PushCell(GetArrayCell(g_NominateOwners, index)); + Call_Finish(); + + RemoveFromArray(g_NominateOwners, index); + RemoveFromArray(g_NominateList, index); + g_NominateCount--; +} + +public Action:Command_SetNextmap(client, args) +{ + if (args < 1) + { + CReplyToCommand(client, "[MCE] Usage: sm_setnextmap "); + return Plugin_Handled; + } + + decl String:map[PLATFORM_MAX_PATH]; + GetCmdArg(1, map, PLATFORM_MAX_PATH); + + if (!IsMapValid(map)) + { + CReplyToCommand(client, "[MCE] %t", "Map was not found", map); + return Plugin_Handled; + } + + ShowActivity(client, "%t", "Changed Next Map", map); + LogAction(client, -1, "\"%L\" changed nextmap to \"%s\"", client, map); + + SetNextMap(map); + g_MapVoteCompleted = true; + + return Plugin_Handled; +} + +public Action:Command_ReloadMaps(client, args) +{ + InitializeOfficialMapList(); +} + +public OnMapTimeLeftChanged() +{ + if (GetArraySize(g_MapList)) + { + SetupTimeleftTimer(); + } +} + +SetupTimeleftTimer() +{ + new time; + if (GetMapTimeLeft(time) && time > 0) + { + new startTime; + if (GetConVarBool(g_Cvar_StartTimePercentEnable)) + { + new timeLimit; + if (GetMapTimeLimit(timeLimit) && timeLimit > 0) + { + startTime = GetConVarInt(g_Cvar_StartTimePercent) * (timeLimit * 60) / 100; + } + } + else + { + startTime = GetConVarInt(g_Cvar_StartTime) * 60; + } + + if (time - startTime < 0 && GetConVarBool(g_Cvar_EndOfMapVote) && !g_MapVoteCompleted && !g_HasVoteStarted) + { + SetupWarningTimer(WarningType_Vote); + } + else + { + if (g_WarningTimer == INVALID_HANDLE) + { + if (g_VoteTimer != INVALID_HANDLE) + { + KillTimer(g_VoteTimer); + g_VoteTimer = INVALID_HANDLE; + } + + //g_VoteTimer = CreateTimer(float(time - startTime), Timer_StartMapVoteTimer_StartMapVote, _, TIMER_FLAG_NO_MAPCHANGE); + g_VoteTimer = CreateTimer(float(time - startTime), Timer_StartWarningTimer, _, TIMER_FLAG_NO_MAPCHANGE); + } + } + } +} + +public Action:Timer_StartWarningTimer(Handle:timer) +{ + g_VoteTimer = INVALID_HANDLE; + + if (!g_WarningInProgress || g_WarningTimer == INVALID_HANDLE) + { + SetupWarningTimer(WarningType_Vote); + } +} + +public Action:Timer_StartMapVote(Handle:timer, Handle:data) +{ + static timePassed; + + // This is still necessary because InitiateVote still calls this directly via the retry timer + if (!GetArraySize(g_MapList) || !GetConVarBool(g_Cvar_EndOfMapVote) || g_MapVoteCompleted || g_HasVoteStarted) + { + g_WarningTimer = INVALID_HANDLE; + return Plugin_Stop; + } + + ResetPack(data); + new warningMaxTime = ReadPackCell(data); + new warningTimeRemaining = warningMaxTime - timePassed; + + new String:warningPhrase[32]; + ReadPackString(data, warningPhrase, sizeof(warningPhrase)); + + // Tick timer for external plugins + Call_StartForward(g_MapVoteWarningTickForward); + Call_PushCell(warningTimeRemaining); + Call_Finish(); + + if (timePassed == 0 || !GetConVarBool(g_Cvar_HideTimer)) + { + new TimerLocation:timerLocation = TimerLocation:GetConVarInt(g_Cvar_TimerLocation); + + switch(timerLocation) + { + case TimerLocation_Center: + { + PrintCenterTextAll("%t", warningPhrase, warningTimeRemaining); + } + + case TimerLocation_Chat: + { + PrintToChatAll("%t", warningPhrase, warningTimeRemaining); + } + + default: + { + PrintHintTextToAll("%t", warningPhrase, warningTimeRemaining); + } + } + } + + if (timePassed++ >= warningMaxTime) + { + if (timer == g_RetryTimer) + { + g_WaitingForVote = false; + g_RetryTimer = INVALID_HANDLE; + } + else + { + g_WarningTimer = INVALID_HANDLE; + } + + timePassed = 0; + new MapChange:mapChange = MapChange:ReadPackCell(data); + new Handle:hndl = Handle:ReadPackCell(data); + + InitiateVote(mapChange, hndl); + + return Plugin_Stop; + } + return Plugin_Continue; +} + +public Event_TFRestartRound(Handle:event, const String:name[], bool:dontBroadcast) +{ + /* Game got restarted - reset our round count tracking */ + g_TotalRounds = 0; +} + +public Event_TeamPlayWinPanel(Handle:event, const String:name[], bool:dontBroadcast) +{ + if (g_ChangeMapAtRoundEnd) + { + g_ChangeMapAtRoundEnd = false; + CreateTimer(2.0, Timer_ChangeMap, INVALID_HANDLE, TIMER_FLAG_NO_MAPCHANGE); + g_ChangeMapInProgress = true; + } + + new bluescore = GetEventInt(event, "blue_score"); + new redscore = GetEventInt(event, "red_score"); + + if(GetEventInt(event, "round_complete") == 1 || StrEqual(name, "arena_win_panel")) + { + g_TotalRounds++; + + if (!GetArraySize(g_MapList) || g_HasVoteStarted || g_MapVoteCompleted || !GetConVarBool(g_Cvar_EndOfMapVote)) + { + return; + } + + CheckMaxRounds(g_TotalRounds); + + switch(GetEventInt(event, "winning_team")) + { + case 3: + { + CheckWinLimit(bluescore); + } + case 2: + { + CheckWinLimit(redscore); + } + //We need to do nothing on winning_team == 0 this indicates stalemate. + default: + { + return; + } + } + } +} + +public Event_MvMWinPanel(Handle:event, const String:name[], bool:dontBroadcast) +{ + if (GetEventInt(event, "winning_team") == 2) + { + new objectiveEnt = EntRefToEntIndex(g_ObjectiveEnt); + if (objectiveEnt != INVALID_ENT_REFERENCE) + { + g_TotalRounds = GetEntProp(g_ObjectiveEnt, Prop_Send, "m_nMannVsMachineWaveCount"); + CheckMaxRounds(g_TotalRounds); + } + } +} + +public Event_Intermission(Handle:event, const String:name[], bool:dontBroadcast) +{ + g_HasIntermissionStarted = true; +} + +public Event_PhaseEnd(Handle:event, const String:name[], bool:dontBroadcast) +{ + /* announce_phase_end fires for both half time and the end of the map, but intermission fires first for end of the map. */ + if (g_HasIntermissionStarted) + { + return; + } + + /* No intermission yet, so this must be half time. Swap the score counters. */ + new t_score = g_winCount[2]; + g_winCount[2] = g_winCount[3]; + g_winCount[3] = t_score; +} + +public Event_WeaponRank(Handle:event, const String:name[], bool:dontBroadcast) +{ + new rank = GetEventInt(event, "weaponrank"); + if (rank > g_TotalRounds) + { + g_TotalRounds = rank; + CheckMaxRounds(g_TotalRounds); + } +} + +/* You ask, why don't you just use team_score event? And I answer... Because CSS doesn't. */ +public Event_RoundEnd(Handle:event, const String:name[], bool:dontBroadcast) +{ + if (g_RoundCounting == RoundCounting_ArmsRace) + { + return; + } + + if (g_ChangeMapAtRoundEnd) + { + g_ChangeMapAtRoundEnd = false; + CreateTimer(2.0, Timer_ChangeMap, INVALID_HANDLE, TIMER_FLAG_NO_MAPCHANGE); + g_ChangeMapInProgress = true; + } + + new winner; + if (strcmp(name, "round_win") == 0 || strcmp(name, "dod_round_win") == 0) + { + // Nuclear Dawn & DoD:S + winner = GetEventInt(event, "team"); + } + else + { + winner = GetEventInt(event, "winner"); + } + + if (winner == 0 || winner == 1 || !GetConVarBool(g_Cvar_EndOfMapVote)) + { + return; + } + + if (winner >= MAXTEAMS) + { + SetFailState("Mod exceed maximum team count - Please file a bug report."); + } + + g_TotalRounds++; + + g_winCount[winner]++; + + if (!GetArraySize(g_MapList) || g_HasVoteStarted || g_MapVoteCompleted) + { + return; + } + + CheckWinLimit(g_winCount[winner]); + CheckMaxRounds(g_TotalRounds); +} + +public CheckWinLimit(winner_score) +{ + if (g_Cvar_Winlimit != INVALID_HANDLE) + { + new winlimit = GetConVarInt(g_Cvar_Winlimit); + if (winlimit) + { + if (winner_score >= (winlimit - GetConVarInt(g_Cvar_StartRounds))) + { + if (!g_WarningInProgress || g_WarningTimer == INVALID_HANDLE) + { + SetupWarningTimer(WarningType_Vote, MapChange_MapEnd); + //InitiateVote(MapChange_MapEnd, INVALID_HANDLE); + } + } + } + } + + if (g_Cvar_MatchClinch != INVALID_HANDLE && g_Cvar_Maxrounds != INVALID_HANDLE) + { + new bool:clinch = GetConVarBool(g_Cvar_MatchClinch); + + if (clinch) + { + new maxrounds = GetConVarInt(g_Cvar_Maxrounds); + new winlimit = RoundFloat(maxrounds / 2.0); + + if(winner_score == winlimit - 1) + { + if (!g_WarningInProgress || g_WarningTimer == INVALID_HANDLE) + { + SetupWarningTimer(WarningType_Vote, MapChange_MapEnd); + //InitiateVote(MapChange_MapEnd, INVALID_HANDLE); + } + } + } + } +} + +public CheckMaxRounds(roundcount) +{ + new maxrounds = 0; + + if (g_RoundCounting == RoundCounting_ArmsRace) + { + maxrounds = GameRules_GetProp("m_iNumGunGameProgressiveWeaponsCT"); + } + else if (g_RoundCounting == RoundCounting_MvM) + { + maxrounds = GetEntProp(g_ObjectiveEnt, Prop_Send, "m_nMannVsMachineMaxWaveCount"); + } + else if (g_Cvar_Maxrounds != INVALID_HANDLE) + { + maxrounds = GetConVarInt(g_Cvar_Maxrounds); + } + else + { + return; + } + + if (maxrounds) + { + if (roundcount >= (maxrounds - GetConVarInt(g_Cvar_StartRounds))) + { + if (!g_WarningInProgress || g_WarningTimer == INVALID_HANDLE) + { + SetupWarningTimer(WarningType_Vote, MapChange_MapEnd); + //InitiateVote(MapChange_MapEnd, INVALID_HANDLE); + } + } + } +} + +public Event_PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast) +{ + if (!GetArraySize(g_MapList) || g_Cvar_Fraglimit == INVALID_HANDLE || g_HasVoteStarted) + { + return; + } + + if (!GetConVarInt(g_Cvar_Fraglimit) || !GetConVarBool(g_Cvar_EndOfMapVote)) + { + return; + } + + if (g_MapVoteCompleted) + { + return; + } + + new fragger = GetClientOfUserId(GetEventInt(event, "attacker")); + + if (!fragger) + { + return; + } + + if (GetClientFrags(fragger) >= (GetConVarInt(g_Cvar_Fraglimit) - GetConVarInt(g_Cvar_StartFrags))) + { + if (!g_WarningInProgress || g_WarningTimer == INVALID_HANDLE) + { + SetupWarningTimer(WarningType_Vote, MapChange_MapEnd); + //InitiateVote(MapChange_MapEnd, INVALID_HANDLE); + } + } +} + +public Action:Command_Mapvote(client, args) +{ + ShowActivity2(client, "[MCE] ", "%t", "Initiated Vote Map"); + + SetupWarningTimer(WarningType_Vote, MapChange_MapEnd, INVALID_HANDLE, true); + + //InitiateVote(MapChange_MapEnd, INVALID_HANDLE); + + return Plugin_Handled; +} + +/** + * Starts a new map vote + * + * @param when When the resulting map change should occur. + * @param inputlist Optional list of maps to use for the vote, otherwise an internal list of nominations + random maps will be used. + */ +InitiateVote(MapChange:when, Handle:inputlist=INVALID_HANDLE) +{ + g_WaitingForVote = true; + g_WarningInProgress = false; + + // Check if a nativevotes vots is in progress first + // NativeVotes running at the same time as a regular vote can cause hintbox problems, + // so always check for a standard vote + if (IsVoteInProgress() || (g_NativeVotes && NativeVotes_IsVoteInProgress())) + { + // Can't start a vote, try again in 5 seconds. + //g_RetryTimer = CreateTimer(5.0, Timer_StartMapVote, _, TIMER_FLAG_NO_MAPCHANGE); + + CPrintToChatAll("[MCE] %t", "Cannot Start Vote", FAILURE_TIMER_LENGTH); + new Handle:data; + g_RetryTimer = CreateDataTimer(1.0, Timer_StartMapVote, data, TIMER_FLAG_NO_MAPCHANGE | TIMER_REPEAT); + + /* Mapchooser Extended */ + WritePackCell(data, FAILURE_TIMER_LENGTH); + + if (GetConVarBool(g_Cvar_RunOff) && g_RunoffCount > 0) + { + WritePackString(data, "Revote Warning"); + } else { + WritePackString(data, "Vote Warning"); + } + /* End Mapchooser Extended */ + + WritePackCell(data, _:when); + WritePackCell(data, _:inputlist); + ResetPack(data); + return; + } + + /* If the main map vote has completed (and chosen result) and its currently changing (not a delayed change) we block further attempts */ + if (g_MapVoteCompleted && g_ChangeMapInProgress) + { + return; + } + + g_ChangeTime = when; + + g_WaitingForVote = false; + + g_HasVoteStarted = true; + + if (g_NativeVotes) + { + g_VoteMenu = NativeVotes_Create(Handler_MapVoteMenu, NativeVotesType_NextLevelMult, MenuAction_End | MenuAction_VoteCancel | MenuAction_Display | MenuAction_DisplayItem); + NativeVotes_SetResultCallback(g_VoteMenu, Handler_NativeVoteFinished); + } + else + { + new Handle:menuStyle = GetMenuStyleHandle(MenuStyle:GetConVarInt(g_Cvar_MenuStyle)); + + if (menuStyle != INVALID_HANDLE) + { + g_VoteMenu = CreateMenuEx(menuStyle, Handler_MapVoteMenu, MenuAction_End | MenuAction_Display | MenuAction_DisplayItem | MenuAction_VoteCancel); + } + else + { + // You chose... poorly + g_VoteMenu = CreateMenu(Handler_MapVoteMenu, MenuAction_End | MenuAction_Display | MenuAction_DisplayItem | MenuAction_VoteCancel); + } + + g_AddNoVote = GetConVarBool(g_Cvar_NoVoteOption); + + // Block Vote Slots + if (GetConVarBool(g_Cvar_BlockSlots)) + { + new Handle:radioStyle = GetMenuStyleHandle(MenuStyle_Radio); + + if (GetMenuStyle(g_VoteMenu) == radioStyle) + { + g_BlockedSlots = true; + AddMenuItem(g_VoteMenu, LINE_ONE, "Choose something...", ITEMDRAW_DISABLED); + AddMenuItem(g_VoteMenu, LINE_TWO, "...will ya?", ITEMDRAW_DISABLED); + if (!g_AddNoVote) + { + AddMenuItem(g_VoteMenu, LINE_SPACER, "", ITEMDRAW_SPACER); + } + } else { + g_BlockedSlots = false; + } + } + + if (g_AddNoVote) + { + SetMenuOptionFlags(g_VoteMenu, MENUFLAG_BUTTON_NOVOTE); + } + + SetMenuTitle(g_VoteMenu, "Vote Nextmap"); + SetVoteResultCallback(g_VoteMenu, Handler_MapVoteFinished); + } + + /* Call OnMapVoteStarted() Forward */ +// Call_StartForward(g_MapVoteStartedForward); +// Call_Finish(); + + /** + * TODO: Make a proper decision on when to clear the nominations list. + * Currently it clears when used, and stays if an external list is provided. + * Is this the right thing to do? External lists will probably come from places + * like sm_mapvote from the adminmenu in the future. + */ + + decl String:map[PLATFORM_MAX_PATH]; + + /* No input given - User our internal nominations and maplist */ + if (inputlist == INVALID_HANDLE) + { + new Handle:randomizeList = INVALID_HANDLE; + if (GetConVarBool(g_Cvar_RandomizeNominations)) + { + randomizeList = CloneArray(g_NominateList); + } + + new nominateCount = GetArraySize(g_NominateList); + + new voteSize = GetVoteSize(); + + // The if and else if could be combined, but it looks extremely messy + // This is a hack to lower the vote menu size by 1 when Don't Change or Extend Map should appear + if (g_NativeVotes) + { + if ((when == MapChange_Instant || when == MapChange_RoundEnd) && GetConVarBool(g_Cvar_DontChange)) + { + voteSize--; + } + else if (GetConVarBool(g_Cvar_Extend) && g_Extends < GetConVarInt(g_Cvar_Extend)) + { + voteSize--; + } + } + + /* Smaller of the two - It should be impossible for nominations to exceed the size though (cvar changed mid-map?) */ + new nominationsToAdd = nominateCount >= voteSize ? voteSize : nominateCount; + + new bool:extendFirst = GetConVarBool(g_Cvar_ExtendPosition); + + if (extendFirst) + { + AddExtendToMenu(g_VoteMenu, when); + } + + for (new i=0; i= availableMaps) + { + //Run out of maps, this will have to do. + break; + } + } + + if (randomizeList != INVALID_HANDLE) + { + // Fisher-Yates Shuffle + for (new j = GetArraySize(randomizeList) - 1; j >= 1; j--) + { + new k = GetRandomInt(0, j); + SwapArrayItems(randomizeList, j, k); + } + + for (new j = 0; j < GetArraySize(randomizeList); j++) + { + GetArrayString(randomizeList, j, map, PLATFORM_MAX_PATH); + AddMapItem(map); + } + + CloseHandle(randomizeList); + randomizeList = INVALID_HANDLE; + } + + /* Wipe out our nominations list - Nominations have already been informed of this */ + g_NominateCount = 0; + ClearArray(g_NominateOwners); + ClearArray(g_NominateList); + + if (!extendFirst) + { + AddExtendToMenu(g_VoteMenu, when); + } + } + else //We were given a list of maps to start the vote with + { + new size = GetArraySize(inputlist); + + for (new i=0; i 0) + { + ExtendMapTimeLimit(GetConVarInt(g_Cvar_ExtendTimeStep)*60); + } + } + + if (g_Cvar_Winlimit != INVALID_HANDLE) + { + new winlimit = GetConVarInt(g_Cvar_Winlimit); + if (winlimit) + { + SetConVarInt(g_Cvar_Winlimit, winlimit + GetConVarInt(g_Cvar_ExtendRoundStep)); + } + } + + if (g_Cvar_Maxrounds != INVALID_HANDLE) + { + new maxrounds = GetConVarInt(g_Cvar_Maxrounds); + if (maxrounds) + { + SetConVarInt(g_Cvar_Maxrounds, maxrounds + GetConVarInt(g_Cvar_ExtendRoundStep)); + } + } + + if (g_Cvar_Fraglimit != INVALID_HANDLE) + { + new fraglimit = GetConVarInt(g_Cvar_Fraglimit); + if (fraglimit) + { + SetConVarInt(g_Cvar_Fraglimit, fraglimit + GetConVarInt(g_Cvar_ExtendFragStep)); + } + } + + if (g_NativeVotes) + { + NativeVotes_DisplayPassEx(menu, NativeVotesPass_Extend); + } + + CPrintToChatAll("[MCE] %t", "Current Map Extended", RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes); + LogAction(-1, -1, "Voting for next map has finished. The current map has been extended."); + + // We extended, so we'll have to vote again. + g_RunoffCount = 0; + g_HasVoteStarted = false; + CreateNextVote(); + SetupTimeleftTimer(); + + } + else if (strcmp(map, VOTE_DONTCHANGE, false) == 0) + { + if (g_NativeVotes) + { + NativeVotes_DisplayPassEx(menu, NativeVotesPass_Extend); + } + + CPrintToChatAll("[MCE] %t", "Current Map Stays", RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes); + LogAction(-1, -1, "Voting for next map has finished. 'No Change' was the winner"); + + g_RunoffCount = 0; + g_HasVoteStarted = false; + CreateNextVote(); + SetupTimeleftTimer(); + } + else + { + if (g_ChangeTime == MapChange_MapEnd) + { + SetNextMap(map); + if (g_NativeVotes) + { + NativeVotes_DisplayPass(menu, map); + } + } + else if (g_ChangeTime == MapChange_Instant) + { + new Handle:data; + CreateDataTimer(4.0, Timer_ChangeMap, data); + WritePackString(data, map); + g_ChangeMapInProgress = false; + if (g_NativeVotes) + { + NativeVotes_DisplayPassEx(menu, NativeVotesPass_ChgLevel, map); + } + } + else // MapChange_RoundEnd + { + SetNextMap(map); + g_ChangeMapAtRoundEnd = true; + + if (g_NativeVotes) + { + NativeVotes_DisplayPass(menu, map); + } + } + + g_HasVoteStarted = false; + g_MapVoteCompleted = true; + + CPrintToChatAll("[MCE] %t", "Nextmap Voting Finished", map, RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes); + LogAction(-1, -1, "Voting for next map has finished. Nextmap: %s.", map); + } +} + +public Handler_MapVoteFinished(Handle:menu, + num_votes, + num_clients, + const client_info[][2], + num_items, + const item_info[][2]) +{ + // Implement revote logic - Only run this` block if revotes are enabled and this isn't the last revote + if (GetConVarBool(g_Cvar_RunOff) && num_items > 1 && g_RunoffCount < GetConVarInt(g_Cvar_MaxRunOffs)) + { + g_RunoffCount++; + new highest_votes = item_info[0][VOTEINFO_ITEM_VOTES]; + new required_percent = GetConVarInt(g_Cvar_RunOffPercent); + new required_votes = RoundToCeil(float(num_votes) * float(required_percent) / 100); + + if (highest_votes == item_info[1][VOTEINFO_ITEM_VOTES]) + { + g_HasVoteStarted = false; + + //Revote is needed + new arraySize = ByteCountToCells(PLATFORM_MAX_PATH); + new Handle:mapList = CreateArray(arraySize); + + for (new i = 0; i < num_items; i++) + { + if (item_info[i][VOTEINFO_ITEM_VOTES] == highest_votes) + { + decl String:map[PLATFORM_MAX_PATH]; + + GetMapItem(menu, item_info[i][VOTEINFO_ITEM_INDEX], map, PLATFORM_MAX_PATH); + PushArrayString(mapList, map); + } + else + { + break; + } + } + + if (g_NativeVotes) + { + NativeVotes_DisplayFail(menu, NativeVotesFail_NotEnoughVotes); + } + + CPrintToChatAll("[MCE] %t", "Tie Vote", GetArraySize(mapList)); + SetupWarningTimer(WarningType_Revote, MapChange:g_ChangeTime, mapList); + return; + } + else if (highest_votes < required_votes) + { + g_HasVoteStarted = false; + + //Revote is needed + new arraySize = ByteCountToCells(PLATFORM_MAX_PATH); + new Handle:mapList = CreateArray(arraySize); + + decl String:map1[PLATFORM_MAX_PATH]; + GetMapItem(menu, item_info[0][VOTEINFO_ITEM_INDEX], map1, PLATFORM_MAX_PATH); + + PushArrayString(mapList, map1); + + // We allow more than two maps for a revote if they are tied + for (new i = 1; i < num_items; i++) + { + if (GetArraySize(mapList) < 2 || item_info[i][VOTEINFO_ITEM_VOTES] == item_info[i - 1][VOTEINFO_ITEM_VOTES]) + { + decl String:map[PLATFORM_MAX_PATH]; + GetMapItem(menu, item_info[i][VOTEINFO_ITEM_INDEX], map, PLATFORM_MAX_PATH); + PushArrayString(mapList, map); + } + else + { + break; + } + } + + if (g_NativeVotes) + { + NativeVotes_DisplayFail(menu, NativeVotesFail_NotEnoughVotes); + } + + CPrintToChatAll("[MCE] %t", "Revote Is Needed", required_percent); + SetupWarningTimer(WarningType_Revote, MapChange:g_ChangeTime, mapList); + return; + } + } + + // No revote needed, continue as normal. + Handler_VoteFinishedGeneric(menu, num_votes, num_clients, client_info, num_items, item_info); +} + +// This is shared by NativeVotes now, but NV doesn't support Display or DisplayItem +public Handler_MapVoteMenu(Handle:menu, MenuAction:action, param1, param2) +{ + switch (action) + { + case MenuAction_End: + { + g_VoteMenu = INVALID_HANDLE; + if (g_NativeVotes) + { + NativeVotes_Close(menu); + } + else + { + CloseHandle(menu); + } + } + + case MenuAction_Display: + { + // NativeVotes uses the standard TF2/CSGO vote screen + if (!g_NativeVotes) + { + decl String:buffer[255]; + Format(buffer, sizeof(buffer), "%T", "Vote Nextmap", param1); + new Handle:panel = Handle:param2; + SetPanelTitle(panel, buffer); + } + } + + case MenuAction_DisplayItem: + { + new String:map[PLATFORM_MAX_PATH]; + new String:buffer[255]; + new mark = GetConVarInt(g_Cvar_MarkCustomMaps); + + if (g_NativeVotes) + { + NativeVotes_GetItem(menu, param2, map, PLATFORM_MAX_PATH); + } + else + { + GetMenuItem(menu, param2, map, PLATFORM_MAX_PATH); + } + + if (StrEqual(map, VOTE_EXTEND, false)) + { + Format(buffer, sizeof(buffer), "%T", "Extend Map", param1); + } + else if (StrEqual(map, VOTE_DONTCHANGE, false)) + { + Format(buffer, sizeof(buffer), "%T", "Dont Change", param1); + } + // Mapchooser Extended + else if (StrEqual(map, LINE_ONE, false)) + { + Format(buffer, sizeof(buffer),"%T", "Line One", param1); + } + else if (StrEqual(map, LINE_TWO, false)) + { + Format(buffer, sizeof(buffer),"%T", "Line Two", param1); + } + // Note that the first part is to discard the spacer line + else if (!StrEqual(map, LINE_SPACER, false)) + { + if (mark == 1 && !InternalIsMapOfficial(map)) + { + Format(buffer, sizeof(buffer), "%T", "Custom Marked", param1, map); + } + else if (mark == 2 && !InternalIsMapOfficial(map)) + { + Format(buffer, sizeof(buffer), "%T", "Custom", param1, map); + } + } + + if (buffer[0] != '\0') + { + if (g_NativeVotes) + { + NativeVotes_RedrawVoteItem(buffer); + return _:Plugin_Continue; + } + else + { + return RedrawMenuItem(buffer); + } + } + // End Mapchooser Extended + } + + case MenuAction_VoteCancel: + { + // If we receive 0 votes, pick at random. + if (param1 == VoteCancel_NoVotes && GetConVarBool(g_Cvar_NoVoteMode)) + { + new count; + if (g_NativeVotes) + { + count = NativeVotes_GetItemCount(menu); + } + else + { + count = GetMenuItemCount(menu); + } + + decl item; + decl String:map[PLATFORM_MAX_PATH]; + + do + { + new startInt = 0; + if (!g_NativeVotes && g_BlockedSlots) + { + if (g_AddNoVote) + { + startInt = 2; + } + else + { + startInt = 3; + } + } + item = GetRandomInt(startInt, count - 1); + if (g_NativeVotes) + { + NativeVotes_GetItem(menu, item, map, PLATFORM_MAX_PATH); + } + else + { + GetMenuItem(menu, item, map, PLATFORM_MAX_PATH); + } + } + while (strcmp(map, VOTE_EXTEND, false) == 0); + + SetNextMap(map); + g_MapVoteCompleted = true; + + if (g_NativeVotes) + { + NativeVotes_DisplayPass(menu, map); + } + } + else if (g_NativeVotes) + { + new NativeVotesFailType:reason = NativeVotesFail_Generic; + if (param1 == VoteCancel_NoVotes) + { + reason = NativeVotesFail_NotEnoughVotes; + } + + NativeVotes_DisplayFail(menu, reason); + } + else + { + // We were actually cancelled. I guess we do nothing. + } + + g_HasVoteStarted = false; + } + } + + return 0; +} + +public Action:Timer_ChangeMap(Handle:hTimer, Handle:dp) +{ + g_ChangeMapInProgress = false; + + new String:map[PLATFORM_MAX_PATH]; + + if (dp == INVALID_HANDLE) + { + if (!GetNextMap(map, PLATFORM_MAX_PATH)) + { + //No passed map and no set nextmap. fail! + return Plugin_Stop; + } + } + else + { + ResetPack(dp); + ReadPackString(dp, map, PLATFORM_MAX_PATH); + } + + ForceChangeLevel(map, "Map Vote"); + + return Plugin_Stop; +} + +bool:RemoveStringFromArray(Handle:array, String:str[]) +{ + new index = FindStringInArray(array, str); + if (index != -1) + { + RemoveFromArray(array, index); + return true; + } + + return false; +} + +CreateNextVote() +{ + assert(g_NextMapList) + ClearArray(g_NextMapList); + + decl String:map[PLATFORM_MAX_PATH]; + new Handle:tempMaps = CloneArray(g_MapList); + + GetCurrentMap(map, PLATFORM_MAX_PATH); + RemoveStringFromArray(tempMaps, map); + + if (GetConVarInt(g_Cvar_ExcludeMaps) && GetArraySize(tempMaps) > GetConVarInt(g_Cvar_ExcludeMaps)) + { + for (new i = 0; i < GetArraySize(g_OldMapList); i++) + { + GetArrayString(g_OldMapList, i, map, PLATFORM_MAX_PATH); + RemoveStringFromArray(tempMaps, map); + } + } + + new voteSize = GetVoteSize(); + new limit = (voteSize < GetArraySize(tempMaps) ? voteSize : GetArraySize(tempMaps)); + + for (new i = 0; i < limit; i++) + { + new b = GetRandomInt(0, GetArraySize(tempMaps) - 1); + GetArrayString(tempMaps, b, map, PLATFORM_MAX_PATH); + PushArrayString(g_NextMapList, map); + RemoveFromArray(tempMaps, b); + } + + CloseHandle(tempMaps); +} + +bool:CanVoteStart() +{ + if (g_WaitingForVote || g_HasVoteStarted) + { + return false; + } + + return true; +} + +NominateResult:InternalNominateMap(String:map[], bool:force, owner) +{ + if (!IsMapValid(map)) + { + return Nominate_InvalidMap; + } + + /* Map already in the vote */ + if (FindStringInArray(g_NominateList, map) != -1) + { + return Nominate_AlreadyInVote; + } + + new index; + + /* Look to replace an existing nomination by this client - Nominations made with owner = 0 aren't replaced */ + if (owner && ((index = FindValueInArray(g_NominateOwners, owner)) != -1)) + { + new String:oldmap[PLATFORM_MAX_PATH]; + GetArrayString(g_NominateList, index, oldmap, PLATFORM_MAX_PATH); + Call_StartForward(g_NominationsResetForward); + Call_PushString(oldmap); + Call_PushCell(owner); + Call_Finish(); + + SetArrayString(g_NominateList, index, map); + return Nominate_Replaced; + } + + /* Too many nominated maps. */ + if (g_NominateCount >= GetVoteSize() && !force) + { + return Nominate_VoteFull; + } + + PushArrayString(g_NominateList, map); + PushArrayCell(g_NominateOwners, owner); + g_NominateCount++; + + while (GetArraySize(g_NominateList) > GetVoteSize()) + { + new String:oldmap[PLATFORM_MAX_PATH]; + GetArrayString(g_NominateList, 0, oldmap, PLATFORM_MAX_PATH); + Call_StartForward(g_NominationsResetForward); + Call_PushString(oldmap); + Call_PushCell(GetArrayCell(g_NominateOwners, 0)); + Call_Finish(); + + RemoveFromArray(g_NominateList, 0); + RemoveFromArray(g_NominateOwners, 0); + } + + return Nominate_Added; +} + +/* Add natives to allow nominate and initiate vote to be call */ + +/* native bool:NominateMap(const String:map[], bool:force, &NominateError:error); */ +public Native_NominateMap(Handle:plugin, numParams) +{ + new len; + GetNativeStringLength(1, len); + + if (len <= 0) + { + return false; + } + + new String:map[len+1]; + GetNativeString(1, map, len+1); + + return _:InternalNominateMap(map, GetNativeCell(2), GetNativeCell(3)); +} + +bool:InternalRemoveNominationByMap(String:map[]) +{ + for (new i = 0; i < GetArraySize(g_NominateList); i++) + { + new String:oldmap[PLATFORM_MAX_PATH]; + GetArrayString(g_NominateList, i, oldmap, PLATFORM_MAX_PATH); + + if(strcmp(map, oldmap, false) == 0) + { + Call_StartForward(g_NominationsResetForward); + Call_PushString(oldmap); + Call_PushCell(GetArrayCell(g_NominateOwners, i)); + Call_Finish(); + + RemoveFromArray(g_NominateList, i); + RemoveFromArray(g_NominateOwners, i); + g_NominateCount--; + + return true; + } + } + + return false; +} + +/* native bool:RemoveNominationByMap(const String:map[]); */ +public Native_RemoveNominationByMap(Handle:plugin, numParams) +{ + new len; + GetNativeStringLength(1, len); + + if (len <= 0) + { + return false; + } + + new String:map[len+1]; + GetNativeString(1, map, len+1); + + return _:InternalRemoveNominationByMap(map); +} + +bool:InternalRemoveNominationByOwner(owner) +{ + new index; + + if (owner && ((index = FindValueInArray(g_NominateOwners, owner)) != -1)) + { + new String:oldmap[PLATFORM_MAX_PATH]; + GetArrayString(g_NominateList, index, oldmap, PLATFORM_MAX_PATH); + + Call_StartForward(g_NominationsResetForward); + Call_PushString(oldmap); + Call_PushCell(owner); + Call_Finish(); + + RemoveFromArray(g_NominateList, index); + RemoveFromArray(g_NominateOwners, index); + g_NominateCount--; + + return true; + } + + return false; +} + +/* native bool:RemoveNominationByOwner(owner); */ +public Native_RemoveNominationByOwner(Handle:plugin, numParams) +{ + return _:InternalRemoveNominationByOwner(GetNativeCell(1)); +} + +/* native InitiateMapChooserVote(); */ +public Native_InitiateVote(Handle:plugin, numParams) +{ + new MapChange:when = MapChange:GetNativeCell(1); + new Handle:inputarray = Handle:GetNativeCell(2); + + LogAction(-1, -1, "Starting map vote because outside request"); + + SetupWarningTimer(WarningType_Vote, when, inputarray); + //InitiateVote(when, inputarray); +} + +public Native_CanVoteStart(Handle:plugin, numParams) +{ + return CanVoteStart(); +} + +public Native_CheckVoteDone(Handle:plugin, numParams) +{ + return g_MapVoteCompleted; +} + +public Native_EndOfMapVoteEnabled(Handle:plugin, numParams) +{ + return GetConVarBool(g_Cvar_EndOfMapVote); +} + +public Native_GetExcludeMapList(Handle:plugin, numParams) +{ + new Handle:array = Handle:GetNativeCell(1); + + if (array == INVALID_HANDLE) + { + return; + } + new size = GetArraySize(g_OldMapList); + decl String:map[PLATFORM_MAX_PATH]; + + for (new i=0; i -1); +} + +public Native_IsWarningTimer(Handle:plugin, numParams) +{ + return g_WarningInProgress; +} + +public Native_CanNominate(Handle:plugin, numParams) +{ + if (g_HasVoteStarted) + { + return _:CanNominate_No_VoteInProgress; + } + + if (g_MapVoteCompleted) + { + return _:CanNominate_No_VoteComplete; + } + + if (g_NominateCount >= GetVoteSize()) + { + return _:CanNominate_No_VoteFull; + } + + return _:CanNominate_Yes; +} + +public Native_ExcludeMap(Handle:plugin, numParams) +{ + new len; + GetNativeStringLength(1, len); + + if (len <= 0) + { + return false; + } + + new String:map[len+1]; + GetNativeString(1, map, len+1); + + PushArrayString(g_OldMapList, map); + + return true; +} + +stock AddMapItem(const String:map[]) +{ + if (g_NativeVotes) + { + NativeVotes_AddItem(g_VoteMenu, map, map); + } + else + { + AddMenuItem(g_VoteMenu, map, map); + } +} + +stock GetMapItem(Handle:menu, position, String:map[], mapLen) +{ + if (g_NativeVotes) + { + NativeVotes_GetItem(menu, position, map, mapLen); + } + else + { + GetMenuItem(menu, position, map, mapLen); + } +} + +stock AddExtendToMenu(Handle:menu, MapChange:when) +{ + /* Do we add any special items? */ + // Moved for Mapchooser Extended + + if ((when == MapChange_Instant || when == MapChange_RoundEnd) && GetConVarBool(g_Cvar_DontChange)) + { + if (g_NativeVotes) + { + // Built-in votes don't have "Don't Change", send Extend instead + NativeVotes_AddItem(menu, VOTE_DONTCHANGE, "Don't Change"); + } + else + { + AddMenuItem(menu, VOTE_DONTCHANGE, "Don't Change"); + } + } + else if (GetConVarBool(g_Cvar_Extend) && g_Extends < GetConVarInt(g_Cvar_Extend)) + { + if (g_NativeVotes) + { + NativeVotes_AddItem(menu, VOTE_EXTEND, "Extend Map"); + } + else + { + AddMenuItem(menu, VOTE_EXTEND, "Extend Map"); + } + } +} + +// Using this stock REQUIRES you to add the following to AskPluginLoad2: +// MarkNativeAsOptional("GetEngineVersion"); +stock EngineVersion:GetEngineVersionCompat() +{ + new EngineVersion:version; + if (GetFeatureStatus(FeatureType_Native, "GetEngineVersion") != FeatureStatus_Available) + { + new sdkVersion = GuessSDKVersion(); + switch (sdkVersion) + { + case SOURCE_SDK_ORIGINAL: + { + version = Engine_Original; + } + + case SOURCE_SDK_DARKMESSIAH: + { + version = Engine_DarkMessiah; + } + + case SOURCE_SDK_EPISODE1: + { + version = Engine_SourceSDK2006; + } + + case SOURCE_SDK_EPISODE2: + { + version = Engine_SourceSDK2007; + } + + case SOURCE_SDK_BLOODYGOODTIME: + { + version = Engine_BloodyGoodTime; + } + + case SOURCE_SDK_EYE: + { + version = Engine_EYE; + } + + case SOURCE_SDK_CSS: + { + version = Engine_CSS; + } + + case SOURCE_SDK_EPISODE2VALVE: + { + decl String:gameFolder[PLATFORM_MAX_PATH]; + GetGameFolderName(gameFolder, PLATFORM_MAX_PATH); + if (StrEqual(gameFolder, "dod", false)) + { + version = Engine_DODS; + } + else if (StrEqual(gameFolder, "hl2mp", false)) + { + version = Engine_HL2DM; + } + else + { + version = Engine_TF2; + } + } + + case SOURCE_SDK_LEFT4DEAD: + { + version = Engine_Left4Dead; + } + + case SOURCE_SDK_LEFT4DEAD2: + { + decl String:gameFolder[PLATFORM_MAX_PATH]; + GetGameFolderName(gameFolder, PLATFORM_MAX_PATH); + if (StrEqual(gameFolder, "nd", false)) + { + version = Engine_NuclearDawn; + } + else + { + version = Engine_Left4Dead2; + } + } + + case SOURCE_SDK_ALIENSWARM: + { + version = Engine_AlienSwarm; + } + + case SOURCE_SDK_CSGO: + { + version = Engine_CSGO; + } + + default: + { + version = Engine_Unknown; + } + } + } + else + { + version = GetEngineVersion(); + } + + return version; +} + +GetVoteSize() +{ + new voteSize = GetConVarInt(g_Cvar_IncludeMaps); + + // New in 1.9.5 to let us bump up the included maps count + if (g_NativeVotes) + { + new max = NativeVotes_GetMaxItems(); + + if (max < voteSize) + { + voteSize = max; + } + } + + return voteSize; } \ No newline at end of file diff --git a/mapchooser_extended/scripting/nominations_extended.sp b/mapchooser_extended/scripting/nominations_extended.sp index 3b5bae48..af6cbe52 100644 --- a/mapchooser_extended/scripting/nominations_extended.sp +++ b/mapchooser_extended/scripting/nominations_extended.sp @@ -1,703 +1,703 @@ -/** - * vim: set ts=4 : - * ============================================================================= - * Nominations Extended - * Allows players to nominate maps for Mapchooser - * - * Nominations Extended (C)2012-2013 Powerlord (Ross Bemrose) - * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. - * ============================================================================= - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, version 3.0, as published by the - * Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - * - * As a special exception, AlliedModders LLC gives you permission to link the - * code of this program (as well as its derivative works) to "Half-Life 2," the - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software - * by the Valve Corporation. You must obey the GNU General Public License in - * all respects for all other code used. Additionally, AlliedModders LLC grants - * this exception to all derivative works. AlliedModders LLC defines further - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), - * or . - * - * Version: $Id$ - */ - -#include -#include -#include "include/mapchooser_extended" -#include -#pragma semicolon 1 - -#define MCE_VERSION "1.10.0" - -public Plugin:myinfo = -{ - name = "Map Nominations Extended", - author = "Powerlord and AlliedModders LLC", - description = "Provides Map Nominations", - version = MCE_VERSION, - url = "https://forums.alliedmods.net/showthread.php?t=156974" -}; - -new Handle:g_Cvar_ExcludeOld = INVALID_HANDLE; -new Handle:g_Cvar_ExcludeCurrent = INVALID_HANDLE; - -new Handle:g_MapList = INVALID_HANDLE; -new Handle:g_AdminMapList = INVALID_HANDLE; -new Handle:g_MapMenu = INVALID_HANDLE; -new Handle:g_AdminMapMenu = INVALID_HANDLE; -new g_mapFileSerial = -1; -new g_AdminMapFileSerial = -1; - -#define MAPSTATUS_ENABLED (1<<0) -#define MAPSTATUS_DISABLED (1<<1) -#define MAPSTATUS_EXCLUDE_CURRENT (1<<2) -#define MAPSTATUS_EXCLUDE_PREVIOUS (1<<3) -#define MAPSTATUS_EXCLUDE_NOMINATED (1<<4) - -new Handle:g_mapTrie; - -// Nominations Extended Convars -new Handle:g_Cvar_MarkCustomMaps = INVALID_HANDLE; - -new Handle:g_Cvar_NominateDelay = INVALID_HANDLE; -new Handle:g_Cvar_InitialDelay = INVALID_HANDLE; -new g_Player_NominationDelay[MAXPLAYERS+1]; -new g_NominationDelay; - -public OnPluginStart() -{ - LoadTranslations("common.phrases"); - LoadTranslations("nominations.phrases"); - LoadTranslations("basetriggers.phrases"); // for Next Map phrase - LoadTranslations("mapchooser_extended.phrases"); - - new arraySize = ByteCountToCells(PLATFORM_MAX_PATH); - g_MapList = CreateArray(arraySize); - g_AdminMapList = CreateArray(arraySize); - - g_Cvar_ExcludeOld = CreateConVar("sm_nominate_excludeold", "1", "Specifies if the current map should be excluded from the Nominations list", 0, true, 0.00, true, 1.0); - g_Cvar_ExcludeCurrent = CreateConVar("sm_nominate_excludecurrent", "1", "Specifies if the MapChooser excluded maps should also be excluded from Nominations", 0, true, 0.00, true, 1.0); - g_Cvar_InitialDelay = CreateConVar("sm_nominate_initialdelay", "60.0", "Time in seconds before first Nomination can be made", 0, true, 0.00); - g_Cvar_NominateDelay = CreateConVar("sm_nominate_delay", "3.0", "Delay between nominations", 0, true, 0.00, true, 60.00); - - RegConsoleCmd("say", Command_Say); - RegConsoleCmd("say_team", Command_Say); - - RegConsoleCmd("sm_nominate", Command_Nominate); - RegConsoleCmd("sm_nomlist", Command_NominateList); - - RegAdminCmd("sm_nominate_addmap", Command_Addmap, ADMFLAG_CHANGEMAP, "sm_nominate_addmap - Forces a map to be on the next mapvote."); - - // BotoX - RegAdminCmd("sm_nominate_exclude", Command_AddExclude, ADMFLAG_CHANGEMAP, "sm_nominate_exclude - Forces a map to be inserted into the recently played maps. Effectively blocking the map from being nominated."); - - // Nominations Extended cvars - CreateConVar("ne_version", MCE_VERSION, "Nominations Extended Version", FCVAR_SPONLY|FCVAR_NOTIFY|FCVAR_DONTRECORD); - - g_mapTrie = CreateTrie(); -} - -public OnAllPluginsLoaded() -{ - // This is an MCE cvar... this plugin requires MCE to be loaded. Granted, this plugin SHOULD have an MCE dependency. - g_Cvar_MarkCustomMaps = FindConVar("mce_markcustommaps"); -} - -public OnConfigsExecuted() -{ - if (ReadMapList(g_MapList, - g_mapFileSerial, - "nominations", - MAPLIST_FLAG_CLEARARRAY|MAPLIST_FLAG_MAPSFOLDER) - == INVALID_HANDLE) - { - if (g_mapFileSerial == -1) - { - SetFailState("Unable to create a valid map list."); - } - } - if (ReadMapList(g_AdminMapList, - g_AdminMapFileSerial, - "sm_nominate_addmap menu", - MAPLIST_FLAG_CLEARARRAY|MAPLIST_FLAG_NO_DEFAULT|MAPLIST_FLAG_MAPSFOLDER) - == INVALID_HANDLE) - { - if (g_AdminMapFileSerial == -1) - { - SetFailState("Unable to create a valid admin map list."); - } - } - else - { - for (new i = 0; i < GetArraySize(g_MapList); i++) - { - decl String:map[PLATFORM_MAX_PATH]; - GetArrayString(g_MapList, i, map, sizeof(map)); - - new Index = FindStringInArray(g_AdminMapList, map); - if (Index != -1) - RemoveFromArray(g_AdminMapList, Index); - } - } - - g_NominationDelay = GetTime() + GetConVarInt(g_Cvar_InitialDelay); - - BuildMapMenu(); - BuildAdminMapMenu(); -} - -public OnNominationRemoved(const String:map[], owner) -{ - new status; - - /* Is the map in our list? */ - if (!GetTrieValue(g_mapTrie, map, status)) - { - return; - } - - /* Was the map disabled due to being nominated */ - if ((status & MAPSTATUS_EXCLUDE_NOMINATED) != MAPSTATUS_EXCLUDE_NOMINATED) - { - return; - } - - SetTrieValue(g_mapTrie, map, MAPSTATUS_ENABLED); -} - -public Action:Command_Addmap(client, args) -{ - if (args == 0) - { - AttemptAdminNominate(client); - return Plugin_Handled; - } - - if (args != 1) - { - CReplyToCommand(client, "[NE] Usage: sm_nominate_addmap "); - return Plugin_Handled; - } - - decl String:mapname[PLATFORM_MAX_PATH]; - GetCmdArg(1, mapname, sizeof(mapname)); - - // new status; - if (/*!GetTrieValue(g_mapTrie, mapname, status)*/!IsMapValid(mapname)) - { - CReplyToCommand(client, "%t", "Map was not found", mapname); - return Plugin_Handled; - } - - new NominateResult:result = NominateMap(mapname, true, 0); - - if (result > Nominate_Replaced) - { - /* We assume already in vote is the casue because the maplist does a Map Validity check and we forced, so it can't be full */ - CReplyToCommand(client, "%t", "Map Already In Vote", mapname); - - return Plugin_Handled; - } - - SetTrieValue(g_mapTrie, mapname, MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_NOMINATED); - - CReplyToCommand(client, "%t", "Map Inserted", mapname); - LogAction(client, -1, "\"%L\" inserted map \"%s\".", client, mapname); - - PrintToChatAll("[NE] %N has inserted %s into nominations", client, mapname); - - return Plugin_Handled; -} - -public Action:Command_AddExclude(client, args) -{ - if (args < 1) - { - CReplyToCommand(client, "[NE] Usage: sm_nominate_exclude "); - return Plugin_Handled; - } - - decl String:mapname[PLATFORM_MAX_PATH]; - GetCmdArg(1, mapname, sizeof(mapname)); - - new status; - if(!GetTrieValue(g_mapTrie, mapname, status)) - { - ReplyToCommand(client, "[NE] %t", "Map was not found", mapname); - return Plugin_Handled; - } - - ShowActivity(client, "Excluded map \"%s\" from nomination", mapname); - LogAction(client, -1, "\"%L\" excluded map \"%s\" from nomination", client, mapname); - - SetTrieValue(g_mapTrie, mapname, MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_PREVIOUS); - - // native call to mapchooser_extended - ExcludeMap(mapname); - - return Plugin_Handled; -} - -public Action:Command_Say(client, args) -{ - if (!client) - { - return Plugin_Continue; - } - - decl String:text[192]; - if (!GetCmdArgString(text, sizeof(text))) - { - return Plugin_Continue; - } - - new startidx = 0; - if(text[strlen(text)-1] == '"') - { - text[strlen(text)-1] = '\0'; - startidx = 1; - } - - new ReplySource:old = SetCmdReplySource(SM_REPLY_TO_CHAT); - - if (strcmp(text[startidx], "nominate", false) == 0) - { - if (IsNominateAllowed(client)) - { - if (g_NominationDelay > GetTime()) - ReplyToCommand(client, "[NE] Nominations will be unlocked in %d seconds", g_NominationDelay - GetTime()); - else - AttemptNominate(client); - } - } - - SetCmdReplySource(old); - - return Plugin_Continue; -} - -public Action:Command_Nominate(client, args) -{ - if (!client || !IsNominateAllowed(client)) - { - return Plugin_Handled; - } - - if (g_NominationDelay > GetTime()) - { - ReplyToCommand(client, "[NE] Nominations will be unlocked in %d seconds", g_NominationDelay - GetTime()); - return Plugin_Handled; - } - - if (args == 0) - { - AttemptNominate(client); - return Plugin_Handled; - } - - if (g_Player_NominationDelay[client] > GetTime()) - { - ReplyToCommand(client, "[NE] Please wait %d seconds before you can nominate again", g_Player_NominationDelay[client] - GetTime()); - return Plugin_Handled; - } - - decl String:mapname[PLATFORM_MAX_PATH]; - GetCmdArg(1, mapname, sizeof(mapname)); - - new status; - if (!GetTrieValue(g_mapTrie, mapname, status)) - { - CReplyToCommand(client, "%t", "Map was not found", mapname); - return Plugin_Handled; - } - - if ((status & MAPSTATUS_DISABLED) == MAPSTATUS_DISABLED) - { - if ((status & MAPSTATUS_EXCLUDE_CURRENT) == MAPSTATUS_EXCLUDE_CURRENT) - { - CReplyToCommand(client, "[NE] %t", "Can't Nominate Current Map"); - } - - if ((status & MAPSTATUS_EXCLUDE_PREVIOUS) == MAPSTATUS_EXCLUDE_PREVIOUS) - { - CReplyToCommand(client, "[NE] %t", "Map in Exclude List"); - } - - if ((status & MAPSTATUS_EXCLUDE_NOMINATED) == MAPSTATUS_EXCLUDE_NOMINATED) - { - CReplyToCommand(client, "[NE] %t", "Map Already Nominated"); - } - - return Plugin_Handled; - } - - new NominateResult:result = NominateMap(mapname, false, client); - - if (result > Nominate_Replaced) - { - if (result == Nominate_AlreadyInVote) - { - CReplyToCommand(client, "[NE] %t", "Map Already In Vote", mapname); - } - else if (result == Nominate_VoteFull) - { - CReplyToCommand(client, "[ME] %t", "Max Nominations"); - } - - return Plugin_Handled; - } - - /* Map was nominated! - Disable the menu item and update the trie */ - - SetTrieValue(g_mapTrie, mapname, MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_NOMINATED); - - decl String:name[MAX_NAME_LENGTH]; - GetClientName(client, name, sizeof(name)); - - if(result == Nominate_Added) - PrintToChatAll("[NE] %t", "Map Nominated", name, mapname); - else if(result == Nominate_Replaced) - PrintToChatAll("[NE] %t", "Map Nomination Changed", name, mapname); - - LogMessage("%s nominated %s", name, mapname); - - g_Player_NominationDelay[client] = GetTime() + GetConVarInt(g_Cvar_NominateDelay); - - return Plugin_Continue; -} - -public Action:Command_NominateList(client, args) -{ - new arraySize = ByteCountToCells(PLATFORM_MAX_PATH); - new Handle:MapList = CreateArray(arraySize); - GetNominatedMapList(MapList); - if (!GetArraySize(MapList)) - { - CReplyToCommand(client, "[NE] No maps have been nominated."); - return Plugin_Handled; - } - - new Handle:NominateListMenu = CreateMenu(Handler_NominateListMenu, MENU_ACTIONS_DEFAULT|MenuAction_DisplayItem); - - decl String:map[PLATFORM_MAX_PATH]; - for (new i = 0; i < GetArraySize(MapList); i++) - { - GetArrayString(MapList, i, map, sizeof(map)); - AddMenuItem(NominateListMenu, map, map); - } - - SetMenuTitle(NominateListMenu, "Nominated Maps", client); - DisplayMenu(NominateListMenu, client, MENU_TIME_FOREVER); - - return Plugin_Handled; -} - -public Handler_NominateListMenu(Handle:menu, MenuAction:action, param1, param2) -{ - return 0; -} - -AttemptNominate(client) -{ - SetMenuTitle(g_MapMenu, "%T", "Nominate Title", client); - DisplayMenu(g_MapMenu, client, MENU_TIME_FOREVER); - - return; -} - -AttemptAdminNominate(client) -{ - SetMenuTitle(g_AdminMapMenu, "%T", "Nominate Title", client); - DisplayMenu(g_AdminMapMenu, client, MENU_TIME_FOREVER); - - return; -} - -BuildMapMenu() -{ - if (g_MapMenu != INVALID_HANDLE) - { - CloseHandle(g_MapMenu); - g_MapMenu = INVALID_HANDLE; - } - - ClearTrie(g_mapTrie); - - g_MapMenu = CreateMenu(Handler_MapSelectMenu, MENU_ACTIONS_DEFAULT|MenuAction_DrawItem|MenuAction_DisplayItem); - - decl String:map[PLATFORM_MAX_PATH]; - - new Handle:excludeMaps = INVALID_HANDLE; - decl String:currentMap[32]; - - if (GetConVarBool(g_Cvar_ExcludeOld)) - { - excludeMaps = CreateArray(ByteCountToCells(PLATFORM_MAX_PATH)); - GetExcludeMapList(excludeMaps); - } - - if (GetConVarBool(g_Cvar_ExcludeCurrent)) - { - GetCurrentMap(currentMap, sizeof(currentMap)); - } - - - for (new i = 0; i < GetArraySize(g_MapList); i++) - { - new status = MAPSTATUS_ENABLED; - - GetArrayString(g_MapList, i, map, sizeof(map)); - - if (GetConVarBool(g_Cvar_ExcludeCurrent)) - { - if (StrEqual(map, currentMap)) - { - status = MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_CURRENT; - } - } - - /* Dont bother with this check if the current map check passed */ - if (GetConVarBool(g_Cvar_ExcludeOld) && status == MAPSTATUS_ENABLED) - { - if (FindStringInArray(excludeMaps, map) != -1) - { - status = MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_PREVIOUS; - } - } - - AddMenuItem(g_MapMenu, map, map); - SetTrieValue(g_mapTrie, map, status); - } - - SetMenuExitButton(g_MapMenu, true); - - if (excludeMaps != INVALID_HANDLE) - { - CloseHandle(excludeMaps); - } -} - -BuildAdminMapMenu() -{ - if (g_AdminMapMenu != INVALID_HANDLE) - { - CloseHandle(g_AdminMapMenu); - g_AdminMapMenu = INVALID_HANDLE; - } - - g_AdminMapMenu = CreateMenu(Handler_AdminMapSelectMenu, MENU_ACTIONS_DEFAULT|MenuAction_DrawItem|MenuAction_DisplayItem); - - decl String:map[PLATFORM_MAX_PATH]; - - for (new i = 0; i < GetArraySize(g_AdminMapList); i++) - { - GetArrayString(g_AdminMapList, i, map, sizeof(map)); - - AddMenuItem(g_AdminMapMenu, map, map); - } - - SetMenuExitButton(g_AdminMapMenu, true); -} - -public Handler_MapSelectMenu(Handle:menu, MenuAction:action, param1, param2) -{ - switch (action) - { - case MenuAction_Select: - { - decl String:map[PLATFORM_MAX_PATH], String:name[MAX_NAME_LENGTH]; - GetMenuItem(menu, param2, map, sizeof(map)); - - GetClientName(param1, name, MAX_NAME_LENGTH); - - new NominateResult:result = NominateMap(map, false, param1); - - /* Don't need to check for InvalidMap because the menu did that already */ - if (result == Nominate_AlreadyInVote) - { - PrintToChat(param1, "[NE] %t", "Map Already Nominated"); - return 0; - } - else if (result == Nominate_VoteFull) - { - PrintToChat(param1, "[NE] %t", "Max Nominations"); - return 0; - } - - SetTrieValue(g_mapTrie, map, MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_NOMINATED); - - if(result == Nominate_Added) - PrintToChatAll("[NE] %t", "Map Nominated", name, map); - else if(result == Nominate_Replaced) - PrintToChatAll("[NE] %t", "Map Nomination Changed", name, map); - - LogMessage("%s nominated %s", name, map); - } - - case MenuAction_DrawItem: - { - decl String:map[PLATFORM_MAX_PATH]; - GetMenuItem(menu, param2, map, sizeof(map)); - - new status; - - if (!GetTrieValue(g_mapTrie, map, status)) - { - LogError("Menu selection of item not in trie. Major logic problem somewhere."); - return ITEMDRAW_DEFAULT; - } - - if ((status & MAPSTATUS_DISABLED) == MAPSTATUS_DISABLED) - { - return ITEMDRAW_DISABLED; - } - - return ITEMDRAW_DEFAULT; - - } - - case MenuAction_DisplayItem: - { - decl String:map[PLATFORM_MAX_PATH]; - GetMenuItem(menu, param2, map, sizeof(map)); - - new mark = GetConVarInt(g_Cvar_MarkCustomMaps); - new bool:official; - - new status; - - if (!GetTrieValue(g_mapTrie, map, status)) - { - LogError("Menu selection of item not in trie. Major logic problem somewhere."); - return 0; - } - - decl String:buffer[100]; - decl String:display[150]; - - if (mark) - { - official = IsMapOfficial(map); - } - - if (mark && !official) - { - switch (mark) - { - case 1: - { - Format(buffer, sizeof(buffer), "%T", "Custom Marked", param1, map); - } - - case 2: - { - Format(buffer, sizeof(buffer), "%T", "Custom", param1, map); - } - } - } - else - { - strcopy(buffer, sizeof(buffer), map); - } - - if ((status & MAPSTATUS_DISABLED) == MAPSTATUS_DISABLED) - { - if ((status & MAPSTATUS_EXCLUDE_CURRENT) == MAPSTATUS_EXCLUDE_CURRENT) - { - Format(display, sizeof(display), "%s (%T)", buffer, "Current Map", param1); - return RedrawMenuItem(display); - } - - if ((status & MAPSTATUS_EXCLUDE_PREVIOUS) == MAPSTATUS_EXCLUDE_PREVIOUS) - { - Format(display, sizeof(display), "%s (%T)", buffer, "Recently Played", param1); - return RedrawMenuItem(display); - } - - if ((status & MAPSTATUS_EXCLUDE_NOMINATED) == MAPSTATUS_EXCLUDE_NOMINATED) - { - Format(display, sizeof(display), "%s (%T)", buffer, "Nominated", param1); - return RedrawMenuItem(display); - } - } - - if (mark && !official) - return RedrawMenuItem(buffer); - - return 0; - } - } - - return 0; -} - -stock bool:IsNominateAllowed(client) -{ - new CanNominateResult:result = CanNominate(); - - switch(result) - { - case CanNominate_No_VoteInProgress: - { - CReplyToCommand(client, "[ME] %t", "Nextmap Voting Started"); - return false; - } - - case CanNominate_No_VoteComplete: - { - new String:map[PLATFORM_MAX_PATH]; - GetNextMap(map, sizeof(map)); - CReplyToCommand(client, "[NE] %t", "Next Map", map); - return false; - } -/* - case CanNominate_No_VoteFull: - { - CReplyToCommand(client, "[ME] %t", "Max Nominations"); - return false; - } -*/ - } - - return true; -} - -public Handler_AdminMapSelectMenu(Handle:menu, MenuAction:action, param1, param2) -{ - switch (action) - { - case MenuAction_Select: - { - decl String:map[PLATFORM_MAX_PATH], String:name[MAX_NAME_LENGTH]; - GetMenuItem(menu, param2, map, sizeof(map)); - - new NominateResult:result = NominateMap(map, false, 0); - - if (result == Nominate_AlreadyInVote) - { - PrintToChat(param1, "[NE] %t", "Map Already In Vote", map); - return 0; - } - - SetTrieValue(g_mapTrie, map, MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_NOMINATED); - - PrintToChat(param1, "[NE] %t", "Map Inserted", map); - LogAction(param1, -1, "\"%L\" inserted map \"%s\".", param1, map); - - PrintToChatAll("[NE] %N has inserted %s into nominations", name, map); - } - } - - return 0; -} +/** + * vim: set ts=4 : + * ============================================================================= + * Nominations Extended + * Allows players to nominate maps for Mapchooser + * + * Nominations Extended (C)2012-2013 Powerlord (Ross Bemrose) + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#include +#include +#include "include/mapchooser_extended" +#include +#pragma semicolon 1 + +#define MCE_VERSION "1.10.0" + +public Plugin:myinfo = +{ + name = "Map Nominations Extended", + author = "Powerlord and AlliedModders LLC", + description = "Provides Map Nominations", + version = MCE_VERSION, + url = "https://forums.alliedmods.net/showthread.php?t=156974" +}; + +new Handle:g_Cvar_ExcludeOld = INVALID_HANDLE; +new Handle:g_Cvar_ExcludeCurrent = INVALID_HANDLE; + +new Handle:g_MapList = INVALID_HANDLE; +new Handle:g_AdminMapList = INVALID_HANDLE; +new Handle:g_MapMenu = INVALID_HANDLE; +new Handle:g_AdminMapMenu = INVALID_HANDLE; +new g_mapFileSerial = -1; +new g_AdminMapFileSerial = -1; + +#define MAPSTATUS_ENABLED (1<<0) +#define MAPSTATUS_DISABLED (1<<1) +#define MAPSTATUS_EXCLUDE_CURRENT (1<<2) +#define MAPSTATUS_EXCLUDE_PREVIOUS (1<<3) +#define MAPSTATUS_EXCLUDE_NOMINATED (1<<4) + +new Handle:g_mapTrie; + +// Nominations Extended Convars +new Handle:g_Cvar_MarkCustomMaps = INVALID_HANDLE; + +new Handle:g_Cvar_NominateDelay = INVALID_HANDLE; +new Handle:g_Cvar_InitialDelay = INVALID_HANDLE; +new g_Player_NominationDelay[MAXPLAYERS+1]; +new g_NominationDelay; + +public OnPluginStart() +{ + LoadTranslations("common.phrases"); + LoadTranslations("nominations.phrases"); + LoadTranslations("basetriggers.phrases"); // for Next Map phrase + LoadTranslations("mapchooser_extended.phrases"); + + new arraySize = ByteCountToCells(PLATFORM_MAX_PATH); + g_MapList = CreateArray(arraySize); + g_AdminMapList = CreateArray(arraySize); + + g_Cvar_ExcludeOld = CreateConVar("sm_nominate_excludeold", "1", "Specifies if the current map should be excluded from the Nominations list", 0, true, 0.00, true, 1.0); + g_Cvar_ExcludeCurrent = CreateConVar("sm_nominate_excludecurrent", "1", "Specifies if the MapChooser excluded maps should also be excluded from Nominations", 0, true, 0.00, true, 1.0); + g_Cvar_InitialDelay = CreateConVar("sm_nominate_initialdelay", "60.0", "Time in seconds before first Nomination can be made", 0, true, 0.00); + g_Cvar_NominateDelay = CreateConVar("sm_nominate_delay", "3.0", "Delay between nominations", 0, true, 0.00, true, 60.00); + + RegConsoleCmd("say", Command_Say); + RegConsoleCmd("say_team", Command_Say); + + RegConsoleCmd("sm_nominate", Command_Nominate); + RegConsoleCmd("sm_nomlist", Command_NominateList); + + RegAdminCmd("sm_nominate_addmap", Command_Addmap, ADMFLAG_CHANGEMAP, "sm_nominate_addmap - Forces a map to be on the next mapvote."); + + // BotoX + RegAdminCmd("sm_nominate_exclude", Command_AddExclude, ADMFLAG_CHANGEMAP, "sm_nominate_exclude - Forces a map to be inserted into the recently played maps. Effectively blocking the map from being nominated."); + + // Nominations Extended cvars + CreateConVar("ne_version", MCE_VERSION, "Nominations Extended Version", FCVAR_SPONLY|FCVAR_NOTIFY|FCVAR_DONTRECORD); + + g_mapTrie = CreateTrie(); +} + +public OnAllPluginsLoaded() +{ + // This is an MCE cvar... this plugin requires MCE to be loaded. Granted, this plugin SHOULD have an MCE dependency. + g_Cvar_MarkCustomMaps = FindConVar("mce_markcustommaps"); +} + +public OnConfigsExecuted() +{ + if (ReadMapList(g_MapList, + g_mapFileSerial, + "nominations", + MAPLIST_FLAG_CLEARARRAY|MAPLIST_FLAG_MAPSFOLDER) + == INVALID_HANDLE) + { + if (g_mapFileSerial == -1) + { + SetFailState("Unable to create a valid map list."); + } + } + if (ReadMapList(g_AdminMapList, + g_AdminMapFileSerial, + "sm_nominate_addmap menu", + MAPLIST_FLAG_CLEARARRAY|MAPLIST_FLAG_NO_DEFAULT|MAPLIST_FLAG_MAPSFOLDER) + == INVALID_HANDLE) + { + if (g_AdminMapFileSerial == -1) + { + SetFailState("Unable to create a valid admin map list."); + } + } + else + { + for (new i = 0; i < GetArraySize(g_MapList); i++) + { + decl String:map[PLATFORM_MAX_PATH]; + GetArrayString(g_MapList, i, map, sizeof(map)); + + new Index = FindStringInArray(g_AdminMapList, map); + if (Index != -1) + RemoveFromArray(g_AdminMapList, Index); + } + } + + g_NominationDelay = GetTime() + GetConVarInt(g_Cvar_InitialDelay); + + BuildMapMenu(); + BuildAdminMapMenu(); +} + +public OnNominationRemoved(const String:map[], owner) +{ + new status; + + /* Is the map in our list? */ + if (!GetTrieValue(g_mapTrie, map, status)) + { + return; + } + + /* Was the map disabled due to being nominated */ + if ((status & MAPSTATUS_EXCLUDE_NOMINATED) != MAPSTATUS_EXCLUDE_NOMINATED) + { + return; + } + + SetTrieValue(g_mapTrie, map, MAPSTATUS_ENABLED); +} + +public Action:Command_Addmap(client, args) +{ + if (args == 0) + { + AttemptAdminNominate(client); + return Plugin_Handled; + } + + if (args != 1) + { + CReplyToCommand(client, "[NE] Usage: sm_nominate_addmap "); + return Plugin_Handled; + } + + decl String:mapname[PLATFORM_MAX_PATH]; + GetCmdArg(1, mapname, sizeof(mapname)); + + // new status; + if (/*!GetTrieValue(g_mapTrie, mapname, status)*/!IsMapValid(mapname)) + { + CReplyToCommand(client, "%t", "Map was not found", mapname); + return Plugin_Handled; + } + + new NominateResult:result = NominateMap(mapname, true, 0); + + if (result > Nominate_Replaced) + { + /* We assume already in vote is the casue because the maplist does a Map Validity check and we forced, so it can't be full */ + CReplyToCommand(client, "%t", "Map Already In Vote", mapname); + + return Plugin_Handled; + } + + SetTrieValue(g_mapTrie, mapname, MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_NOMINATED); + + CReplyToCommand(client, "%t", "Map Inserted", mapname); + LogAction(client, -1, "\"%L\" inserted map \"%s\".", client, mapname); + + PrintToChatAll("[NE] %N has inserted %s into nominations", client, mapname); + + return Plugin_Handled; +} + +public Action:Command_AddExclude(client, args) +{ + if (args < 1) + { + CReplyToCommand(client, "[NE] Usage: sm_nominate_exclude "); + return Plugin_Handled; + } + + decl String:mapname[PLATFORM_MAX_PATH]; + GetCmdArg(1, mapname, sizeof(mapname)); + + new status; + if(!GetTrieValue(g_mapTrie, mapname, status)) + { + ReplyToCommand(client, "[NE] %t", "Map was not found", mapname); + return Plugin_Handled; + } + + ShowActivity(client, "Excluded map \"%s\" from nomination", mapname); + LogAction(client, -1, "\"%L\" excluded map \"%s\" from nomination", client, mapname); + + SetTrieValue(g_mapTrie, mapname, MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_PREVIOUS); + + // native call to mapchooser_extended + ExcludeMap(mapname); + + return Plugin_Handled; +} + +public Action:Command_Say(client, args) +{ + if (!client) + { + return Plugin_Continue; + } + + decl String:text[192]; + if (!GetCmdArgString(text, sizeof(text))) + { + return Plugin_Continue; + } + + new startidx = 0; + if(text[strlen(text)-1] == '"') + { + text[strlen(text)-1] = '\0'; + startidx = 1; + } + + new ReplySource:old = SetCmdReplySource(SM_REPLY_TO_CHAT); + + if (strcmp(text[startidx], "nominate", false) == 0) + { + if (IsNominateAllowed(client)) + { + if (g_NominationDelay > GetTime()) + ReplyToCommand(client, "[NE] Nominations will be unlocked in %d seconds", g_NominationDelay - GetTime()); + else + AttemptNominate(client); + } + } + + SetCmdReplySource(old); + + return Plugin_Continue; +} + +public Action:Command_Nominate(client, args) +{ + if (!client || !IsNominateAllowed(client)) + { + return Plugin_Handled; + } + + if (g_NominationDelay > GetTime()) + { + ReplyToCommand(client, "[NE] Nominations will be unlocked in %d seconds", g_NominationDelay - GetTime()); + return Plugin_Handled; + } + + if (args == 0) + { + AttemptNominate(client); + return Plugin_Handled; + } + + if (g_Player_NominationDelay[client] > GetTime()) + { + ReplyToCommand(client, "[NE] Please wait %d seconds before you can nominate again", g_Player_NominationDelay[client] - GetTime()); + return Plugin_Handled; + } + + decl String:mapname[PLATFORM_MAX_PATH]; + GetCmdArg(1, mapname, sizeof(mapname)); + + new status; + if (!GetTrieValue(g_mapTrie, mapname, status)) + { + CReplyToCommand(client, "%t", "Map was not found", mapname); + return Plugin_Handled; + } + + if ((status & MAPSTATUS_DISABLED) == MAPSTATUS_DISABLED) + { + if ((status & MAPSTATUS_EXCLUDE_CURRENT) == MAPSTATUS_EXCLUDE_CURRENT) + { + CReplyToCommand(client, "[NE] %t", "Can't Nominate Current Map"); + } + + if ((status & MAPSTATUS_EXCLUDE_PREVIOUS) == MAPSTATUS_EXCLUDE_PREVIOUS) + { + CReplyToCommand(client, "[NE] %t", "Map in Exclude List"); + } + + if ((status & MAPSTATUS_EXCLUDE_NOMINATED) == MAPSTATUS_EXCLUDE_NOMINATED) + { + CReplyToCommand(client, "[NE] %t", "Map Already Nominated"); + } + + return Plugin_Handled; + } + + new NominateResult:result = NominateMap(mapname, false, client); + + if (result > Nominate_Replaced) + { + if (result == Nominate_AlreadyInVote) + { + CReplyToCommand(client, "[NE] %t", "Map Already In Vote", mapname); + } + else if (result == Nominate_VoteFull) + { + CReplyToCommand(client, "[ME] %t", "Max Nominations"); + } + + return Plugin_Handled; + } + + /* Map was nominated! - Disable the menu item and update the trie */ + + SetTrieValue(g_mapTrie, mapname, MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_NOMINATED); + + decl String:name[MAX_NAME_LENGTH]; + GetClientName(client, name, sizeof(name)); + + if(result == Nominate_Added) + PrintToChatAll("[NE] %t", "Map Nominated", name, mapname); + else if(result == Nominate_Replaced) + PrintToChatAll("[NE] %t", "Map Nomination Changed", name, mapname); + + LogMessage("%s nominated %s", name, mapname); + + g_Player_NominationDelay[client] = GetTime() + GetConVarInt(g_Cvar_NominateDelay); + + return Plugin_Continue; +} + +public Action:Command_NominateList(client, args) +{ + new arraySize = ByteCountToCells(PLATFORM_MAX_PATH); + new Handle:MapList = CreateArray(arraySize); + GetNominatedMapList(MapList); + if (!GetArraySize(MapList)) + { + CReplyToCommand(client, "[NE] No maps have been nominated."); + return Plugin_Handled; + } + + new Handle:NominateListMenu = CreateMenu(Handler_NominateListMenu, MENU_ACTIONS_DEFAULT|MenuAction_DisplayItem); + + decl String:map[PLATFORM_MAX_PATH]; + for (new i = 0; i < GetArraySize(MapList); i++) + { + GetArrayString(MapList, i, map, sizeof(map)); + AddMenuItem(NominateListMenu, map, map); + } + + SetMenuTitle(NominateListMenu, "Nominated Maps", client); + DisplayMenu(NominateListMenu, client, MENU_TIME_FOREVER); + + return Plugin_Handled; +} + +public Handler_NominateListMenu(Handle:menu, MenuAction:action, param1, param2) +{ + return 0; +} + +AttemptNominate(client) +{ + SetMenuTitle(g_MapMenu, "%T", "Nominate Title", client); + DisplayMenu(g_MapMenu, client, MENU_TIME_FOREVER); + + return; +} + +AttemptAdminNominate(client) +{ + SetMenuTitle(g_AdminMapMenu, "%T", "Nominate Title", client); + DisplayMenu(g_AdminMapMenu, client, MENU_TIME_FOREVER); + + return; +} + +BuildMapMenu() +{ + if (g_MapMenu != INVALID_HANDLE) + { + CloseHandle(g_MapMenu); + g_MapMenu = INVALID_HANDLE; + } + + ClearTrie(g_mapTrie); + + g_MapMenu = CreateMenu(Handler_MapSelectMenu, MENU_ACTIONS_DEFAULT|MenuAction_DrawItem|MenuAction_DisplayItem); + + decl String:map[PLATFORM_MAX_PATH]; + + new Handle:excludeMaps = INVALID_HANDLE; + decl String:currentMap[32]; + + if (GetConVarBool(g_Cvar_ExcludeOld)) + { + excludeMaps = CreateArray(ByteCountToCells(PLATFORM_MAX_PATH)); + GetExcludeMapList(excludeMaps); + } + + if (GetConVarBool(g_Cvar_ExcludeCurrent)) + { + GetCurrentMap(currentMap, sizeof(currentMap)); + } + + + for (new i = 0; i < GetArraySize(g_MapList); i++) + { + new status = MAPSTATUS_ENABLED; + + GetArrayString(g_MapList, i, map, sizeof(map)); + + if (GetConVarBool(g_Cvar_ExcludeCurrent)) + { + if (StrEqual(map, currentMap)) + { + status = MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_CURRENT; + } + } + + /* Dont bother with this check if the current map check passed */ + if (GetConVarBool(g_Cvar_ExcludeOld) && status == MAPSTATUS_ENABLED) + { + if (FindStringInArray(excludeMaps, map) != -1) + { + status = MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_PREVIOUS; + } + } + + AddMenuItem(g_MapMenu, map, map); + SetTrieValue(g_mapTrie, map, status); + } + + SetMenuExitButton(g_MapMenu, true); + + if (excludeMaps != INVALID_HANDLE) + { + CloseHandle(excludeMaps); + } +} + +BuildAdminMapMenu() +{ + if (g_AdminMapMenu != INVALID_HANDLE) + { + CloseHandle(g_AdminMapMenu); + g_AdminMapMenu = INVALID_HANDLE; + } + + g_AdminMapMenu = CreateMenu(Handler_AdminMapSelectMenu, MENU_ACTIONS_DEFAULT|MenuAction_DrawItem|MenuAction_DisplayItem); + + decl String:map[PLATFORM_MAX_PATH]; + + for (new i = 0; i < GetArraySize(g_AdminMapList); i++) + { + GetArrayString(g_AdminMapList, i, map, sizeof(map)); + + AddMenuItem(g_AdminMapMenu, map, map); + } + + SetMenuExitButton(g_AdminMapMenu, true); +} + +public Handler_MapSelectMenu(Handle:menu, MenuAction:action, param1, param2) +{ + switch (action) + { + case MenuAction_Select: + { + decl String:map[PLATFORM_MAX_PATH], String:name[MAX_NAME_LENGTH]; + GetMenuItem(menu, param2, map, sizeof(map)); + + GetClientName(param1, name, MAX_NAME_LENGTH); + + new NominateResult:result = NominateMap(map, false, param1); + + /* Don't need to check for InvalidMap because the menu did that already */ + if (result == Nominate_AlreadyInVote) + { + PrintToChat(param1, "[NE] %t", "Map Already Nominated"); + return 0; + } + else if (result == Nominate_VoteFull) + { + PrintToChat(param1, "[NE] %t", "Max Nominations"); + return 0; + } + + SetTrieValue(g_mapTrie, map, MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_NOMINATED); + + if(result == Nominate_Added) + PrintToChatAll("[NE] %t", "Map Nominated", name, map); + else if(result == Nominate_Replaced) + PrintToChatAll("[NE] %t", "Map Nomination Changed", name, map); + + LogMessage("%s nominated %s", name, map); + } + + case MenuAction_DrawItem: + { + decl String:map[PLATFORM_MAX_PATH]; + GetMenuItem(menu, param2, map, sizeof(map)); + + new status; + + if (!GetTrieValue(g_mapTrie, map, status)) + { + LogError("Menu selection of item not in trie. Major logic problem somewhere."); + return ITEMDRAW_DEFAULT; + } + + if ((status & MAPSTATUS_DISABLED) == MAPSTATUS_DISABLED) + { + return ITEMDRAW_DISABLED; + } + + return ITEMDRAW_DEFAULT; + + } + + case MenuAction_DisplayItem: + { + decl String:map[PLATFORM_MAX_PATH]; + GetMenuItem(menu, param2, map, sizeof(map)); + + new mark = GetConVarInt(g_Cvar_MarkCustomMaps); + new bool:official; + + new status; + + if (!GetTrieValue(g_mapTrie, map, status)) + { + LogError("Menu selection of item not in trie. Major logic problem somewhere."); + return 0; + } + + decl String:buffer[100]; + decl String:display[150]; + + if (mark) + { + official = IsMapOfficial(map); + } + + if (mark && !official) + { + switch (mark) + { + case 1: + { + Format(buffer, sizeof(buffer), "%T", "Custom Marked", param1, map); + } + + case 2: + { + Format(buffer, sizeof(buffer), "%T", "Custom", param1, map); + } + } + } + else + { + strcopy(buffer, sizeof(buffer), map); + } + + if ((status & MAPSTATUS_DISABLED) == MAPSTATUS_DISABLED) + { + if ((status & MAPSTATUS_EXCLUDE_CURRENT) == MAPSTATUS_EXCLUDE_CURRENT) + { + Format(display, sizeof(display), "%s (%T)", buffer, "Current Map", param1); + return RedrawMenuItem(display); + } + + if ((status & MAPSTATUS_EXCLUDE_PREVIOUS) == MAPSTATUS_EXCLUDE_PREVIOUS) + { + Format(display, sizeof(display), "%s (%T)", buffer, "Recently Played", param1); + return RedrawMenuItem(display); + } + + if ((status & MAPSTATUS_EXCLUDE_NOMINATED) == MAPSTATUS_EXCLUDE_NOMINATED) + { + Format(display, sizeof(display), "%s (%T)", buffer, "Nominated", param1); + return RedrawMenuItem(display); + } + } + + if (mark && !official) + return RedrawMenuItem(buffer); + + return 0; + } + } + + return 0; +} + +stock bool:IsNominateAllowed(client) +{ + new CanNominateResult:result = CanNominate(); + + switch(result) + { + case CanNominate_No_VoteInProgress: + { + CReplyToCommand(client, "[ME] %t", "Nextmap Voting Started"); + return false; + } + + case CanNominate_No_VoteComplete: + { + new String:map[PLATFORM_MAX_PATH]; + GetNextMap(map, sizeof(map)); + CReplyToCommand(client, "[NE] %t", "Next Map", map); + return false; + } +/* + case CanNominate_No_VoteFull: + { + CReplyToCommand(client, "[ME] %t", "Max Nominations"); + return false; + } +*/ + } + + return true; +} + +public Handler_AdminMapSelectMenu(Handle:menu, MenuAction:action, param1, param2) +{ + switch (action) + { + case MenuAction_Select: + { + decl String:map[PLATFORM_MAX_PATH], String:name[MAX_NAME_LENGTH]; + GetMenuItem(menu, param2, map, sizeof(map)); + + new NominateResult:result = NominateMap(map, false, 0); + + if (result == Nominate_AlreadyInVote) + { + PrintToChat(param1, "[NE] %t", "Map Already In Vote", map); + return 0; + } + + SetTrieValue(g_mapTrie, map, MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_NOMINATED); + + PrintToChat(param1, "[NE] %t", "Map Inserted", map); + LogAction(param1, -1, "\"%L\" inserted map \"%s\".", param1, map); + + PrintToChatAll("[NE] %N has inserted %s into nominations", name, map); + } + } + + return 0; +} diff --git a/mapchooser_extended/scripting/rockthevote_extended.sp b/mapchooser_extended/scripting/rockthevote_extended.sp index 4e3d494a..2cbdc875 100644 --- a/mapchooser_extended/scripting/rockthevote_extended.sp +++ b/mapchooser_extended/scripting/rockthevote_extended.sp @@ -1,378 +1,378 @@ -/** - * vim: set ts=4 : - * ============================================================================= - * Rock The Vote Extended - * Creates a map vote when the required number of players have requested one. - * - * Rock The Vote Extended (C)2012-2013 Powerlord (Ross Bemrose) - * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. - * ============================================================================= - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, version 3.0, as published by the - * Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - * - * As a special exception, AlliedModders LLC gives you permission to link the - * code of this program (as well as its derivative works) to "Half-Life 2," the - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software - * by the Valve Corporation. You must obey the GNU General Public License in - * all respects for all other code used. Additionally, AlliedModders LLC grants - * this exception to all derivative works. AlliedModders LLC defines further - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), - * or . - * - * Version: $Id$ - */ - -#include -#include -#include -#include -#include "include/mapchooser_extended" -#include -#include - -#pragma semicolon 1 - -#define MCE_VERSION "1.10.0" - -public Plugin:myinfo = -{ - name = "Rock The Vote Extended", - author = "Powerlord and AlliedModders LLC", - description = "Provides RTV Map Voting", - version = MCE_VERSION, - url = "https://forums.alliedmods.net/showthread.php?t=156974" -}; - -new Handle:g_Cvar_Needed = INVALID_HANDLE; -new Handle:g_Cvar_MinPlayers = INVALID_HANDLE; -new Handle:g_Cvar_InitialDelay = INVALID_HANDLE; -new Handle:g_Cvar_Interval = INVALID_HANDLE; -new Handle:g_Cvar_ChangeTime = INVALID_HANDLE; -new Handle:g_Cvar_RTVPostVoteAction = INVALID_HANDLE; - -new bool:g_CanRTV = false; // True if RTV loaded maps and is active. -new bool:g_RTVAllowed = false; // True if RTV is available to players. Used to delay rtv votes. -new g_Voters = 0; // Total voters connected. Doesn't include fake clients. -new g_Votes = 0; // Total number of "say rtv" votes -new g_VotesNeeded = 0; // Necessary votes before map vote begins. (voters * percent_needed) -new bool:g_Voted[MAXPLAYERS+1] = {false, ...}; - -new bool:g_InChange = false; - -public OnPluginStart() -{ - LoadTranslations("common.phrases"); - LoadTranslations("rockthevote.phrases"); - LoadTranslations("basevotes.phrases"); - - g_Cvar_Needed = CreateConVar("sm_rtv_needed", "0.60", "Percentage of players needed to rockthevote (Def 60%)", 0, true, 0.05, true, 1.0); - g_Cvar_MinPlayers = CreateConVar("sm_rtv_minplayers", "0", "Number of players required before RTV will be enabled.", 0, true, 0.0, true, float(MAXPLAYERS)); - g_Cvar_InitialDelay = CreateConVar("sm_rtv_initialdelay", "30.0", "Time (in seconds) before first RTV can be held", 0, true, 0.00); - g_Cvar_Interval = CreateConVar("sm_rtv_interval", "240.0", "Time (in seconds) after a failed RTV before another can be held", 0, true, 0.00); - g_Cvar_ChangeTime = CreateConVar("sm_rtv_changetime", "0", "When to change the map after a succesful RTV: 0 - Instant, 1 - RoundEnd, 2 - MapEnd", _, true, 0.0, true, 2.0); - g_Cvar_RTVPostVoteAction = CreateConVar("sm_rtv_postvoteaction", "0", "What to do with RTV's after a mapvote has completed. 0 - Allow, success = instant change, 1 - Deny", _, true, 0.0, true, 1.0); - - HookEvent("player_team", OnPlayerChangedTeam); - - RegConsoleCmd("say", Command_Say); - RegConsoleCmd("say_team", Command_Say); - - RegConsoleCmd("sm_rtv", Command_RTV); - - RegAdminCmd("sm_forcertv", Command_ForceRTV, ADMFLAG_CHANGEMAP, "Force an RTV vote"); - RegAdminCmd("mce_forcertv", Command_ForceRTV, ADMFLAG_CHANGEMAP, "Force an RTV vote"); - - // Rock The Vote Extended cvars - CreateConVar("rtve_version", MCE_VERSION, "Rock The Vote Extended Version", FCVAR_SPONLY|FCVAR_NOTIFY|FCVAR_DONTRECORD); - - AutoExecConfig(true, "rtv"); -} - -public OnMapStart() -{ - g_Voters = 0; - g_Votes = 0; - g_VotesNeeded = 0; - g_InChange = false; - - /* Handle late load */ - for (new i=1; i<=MaxClients; i++) - { - if (IsClientConnected(i)) - { - OnClientConnected(i); - } - } -} - -public OnMapEnd() -{ - g_CanRTV = false; - g_RTVAllowed = false; -} - -public OnConfigsExecuted() -{ - g_CanRTV = true; - g_RTVAllowed = false; - CreateTimer(GetConVarFloat(g_Cvar_InitialDelay), Timer_DelayRTV, _, TIMER_FLAG_NO_MAPCHANGE); -} - -public OnClientConnected(client) -{ - if(IsFakeClient(client)) - return; - - g_Voted[client] = false; - - g_Voters = GetTeamClientCount(2) + GetTeamClientCount(3); - g_VotesNeeded = RoundToFloor(float(g_Voters) * GetConVarFloat(g_Cvar_Needed)); - - return; -} - -public OnClientDisconnect(client) -{ - if(IsFakeClient(client)) - return; - - if(g_Voted[client]) - { - g_Votes--; - } - - g_Voters = GetTeamClientCount(2) + GetTeamClientCount(3); - - g_VotesNeeded = RoundToFloor(float(g_Voters) * GetConVarFloat(g_Cvar_Needed)); - - if (!g_CanRTV) - { - return; - } - - if (g_Votes && - g_Voters && - g_Votes >= g_VotesNeeded && - g_RTVAllowed ) - { - if (GetConVarInt(g_Cvar_RTVPostVoteAction) == 1 && HasEndOfMapVoteFinished()) - { - return; - } - - StartRTV(); - } -} - -public OnPlayerChangedTeam(Handle:event, const String:name[], bool:dontBroadcast) -{ - new Client = GetClientOfUserId(GetEventInt(event, "userid")); - - if(IsFakeClient(Client)) - return; - - if(Client == 0) - { - return; - } - - if (IsClientInGame(Client) && IsClientConnected(Client)) - { - if (GetClientTeam(Client) == 1) - { - g_Voters = GetTeamClientCount(2) + GetTeamClientCount(3); - g_VotesNeeded = RoundToFloor(float(g_Voters) * GetConVarFloat(g_Cvar_Needed)); - - if (g_Votes && - g_Voters && - g_Votes >= g_VotesNeeded && - g_RTVAllowed ) - { - if (GetConVarInt(g_Cvar_RTVPostVoteAction) == 1 && HasEndOfMapVoteFinished()) - { - return; - } - - StartRTV(); - } - } - } -} - -public Action:Command_RTV(client, args) -{ - if (!g_CanRTV || !client) - { - return Plugin_Handled; - } - - AttemptRTV(client); - - return Plugin_Handled; -} - -public Action:Command_Say(client, args) -{ - if (!g_CanRTV || !client) - { - return Plugin_Continue; - } - - decl String:text[192]; - if (!GetCmdArgString(text, sizeof(text))) - { - return Plugin_Continue; - } - - new startidx = 0; - if(text[strlen(text)-1] == '"') - { - text[strlen(text)-1] = '\0'; - startidx = 1; - } - - new ReplySource:old = SetCmdReplySource(SM_REPLY_TO_CHAT); - - if (strcmp(text[startidx], "rtv", false) == 0 || strcmp(text[startidx], "rockthevote", false) == 0) - { - AttemptRTV(client); - } - - SetCmdReplySource(old); - - return Plugin_Continue; -} - -AttemptRTV(client) -{ - if (!g_RTVAllowed || (GetConVarInt(g_Cvar_RTVPostVoteAction) == 1 && HasEndOfMapVoteFinished())) - { - CReplyToCommand(client, "[SM] %t", "RTV Not Allowed"); - return; - } - - if (!CanMapChooserStartVote()) - { - CReplyToCommand(client, "[SM] %t", "RTV Started"); - return; - } - - if (GetClientCount(true) < GetConVarInt(g_Cvar_MinPlayers)) - { - CReplyToCommand(client, "[SM] %t", "Minimal Players Not Met"); - return; - } - - if (g_Voted[client]) - { - CReplyToCommand(client, "[SM] %t", "Already Voted", g_Votes, g_VotesNeeded); - return; - } - - new String:name[MAX_NAME_LENGTH]; - GetClientName(client, name, sizeof(name)); - - g_Votes++; - g_Voted[client] = true; - - CPrintToChatAll("[SM] %t", "RTV Requested", name, g_Votes, g_VotesNeeded); - - if (g_Votes >= g_VotesNeeded) - { - StartRTV(); - } -} - -public Action:Timer_DelayRTV(Handle:timer) -{ - g_RTVAllowed = true; -} - -StartRTV() -{ - if (g_InChange) - { - return; - } - - if (EndOfMapVoteEnabled() && HasEndOfMapVoteFinished()) - { - /* Change right now then */ - new String:map[PLATFORM_MAX_PATH]; - if (GetNextMap(map, sizeof(map))) - { - CPrintToChatAll("[SM] %t", "Changing Maps", map); - CreateTimer(5.0, Timer_ChangeMap, _, TIMER_FLAG_NO_MAPCHANGE); - g_InChange = true; - - ResetRTV(); - - g_RTVAllowed = false; - } - return; - } - - if (CanMapChooserStartVote()) - { - new MapChange:when = MapChange:GetConVarInt(g_Cvar_ChangeTime); - InitiateMapChooserVote(when); - - ResetRTV(); - - g_RTVAllowed = false; - CreateTimer(GetConVarFloat(g_Cvar_Interval), Timer_DelayRTV, _, TIMER_FLAG_NO_MAPCHANGE); - } -} - -ResetRTV() -{ - g_Votes = 0; - - for (new i=1; i<=MAXPLAYERS; i++) - { - g_Voted[i] = false; - } -} - -public Action:Timer_ChangeMap(Handle:hTimer) -{ - g_InChange = false; - - LogMessage("RTV changing map manually"); - - new String:map[PLATFORM_MAX_PATH]; - if (GetNextMap(map, sizeof(map))) - { - ForceChangeLevel(map, "RTV after mapvote"); - } - - return Plugin_Stop; -} - -// Rock The Vote Extended functions - -public Action:Command_ForceRTV(client, args) -{ - if (!g_CanRTV || !client) - { - return Plugin_Handled; - } - - ShowActivity2(client, "[RTVE] ", "%t", "Initiated Vote Map"); - - StartRTV(); - - return Plugin_Handled; -} - - +/** + * vim: set ts=4 : + * ============================================================================= + * Rock The Vote Extended + * Creates a map vote when the required number of players have requested one. + * + * Rock The Vote Extended (C)2012-2013 Powerlord (Ross Bemrose) + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#include +#include +#include +#include +#include "include/mapchooser_extended" +#include +#include + +#pragma semicolon 1 + +#define MCE_VERSION "1.10.0" + +public Plugin:myinfo = +{ + name = "Rock The Vote Extended", + author = "Powerlord and AlliedModders LLC", + description = "Provides RTV Map Voting", + version = MCE_VERSION, + url = "https://forums.alliedmods.net/showthread.php?t=156974" +}; + +new Handle:g_Cvar_Needed = INVALID_HANDLE; +new Handle:g_Cvar_MinPlayers = INVALID_HANDLE; +new Handle:g_Cvar_InitialDelay = INVALID_HANDLE; +new Handle:g_Cvar_Interval = INVALID_HANDLE; +new Handle:g_Cvar_ChangeTime = INVALID_HANDLE; +new Handle:g_Cvar_RTVPostVoteAction = INVALID_HANDLE; + +new bool:g_CanRTV = false; // True if RTV loaded maps and is active. +new bool:g_RTVAllowed = false; // True if RTV is available to players. Used to delay rtv votes. +new g_Voters = 0; // Total voters connected. Doesn't include fake clients. +new g_Votes = 0; // Total number of "say rtv" votes +new g_VotesNeeded = 0; // Necessary votes before map vote begins. (voters * percent_needed) +new bool:g_Voted[MAXPLAYERS+1] = {false, ...}; + +new bool:g_InChange = false; + +public OnPluginStart() +{ + LoadTranslations("common.phrases"); + LoadTranslations("rockthevote.phrases"); + LoadTranslations("basevotes.phrases"); + + g_Cvar_Needed = CreateConVar("sm_rtv_needed", "0.60", "Percentage of players needed to rockthevote (Def 60%)", 0, true, 0.05, true, 1.0); + g_Cvar_MinPlayers = CreateConVar("sm_rtv_minplayers", "0", "Number of players required before RTV will be enabled.", 0, true, 0.0, true, float(MAXPLAYERS)); + g_Cvar_InitialDelay = CreateConVar("sm_rtv_initialdelay", "30.0", "Time (in seconds) before first RTV can be held", 0, true, 0.00); + g_Cvar_Interval = CreateConVar("sm_rtv_interval", "240.0", "Time (in seconds) after a failed RTV before another can be held", 0, true, 0.00); + g_Cvar_ChangeTime = CreateConVar("sm_rtv_changetime", "0", "When to change the map after a succesful RTV: 0 - Instant, 1 - RoundEnd, 2 - MapEnd", _, true, 0.0, true, 2.0); + g_Cvar_RTVPostVoteAction = CreateConVar("sm_rtv_postvoteaction", "0", "What to do with RTV's after a mapvote has completed. 0 - Allow, success = instant change, 1 - Deny", _, true, 0.0, true, 1.0); + + HookEvent("player_team", OnPlayerChangedTeam); + + RegConsoleCmd("say", Command_Say); + RegConsoleCmd("say_team", Command_Say); + + RegConsoleCmd("sm_rtv", Command_RTV); + + RegAdminCmd("sm_forcertv", Command_ForceRTV, ADMFLAG_CHANGEMAP, "Force an RTV vote"); + RegAdminCmd("mce_forcertv", Command_ForceRTV, ADMFLAG_CHANGEMAP, "Force an RTV vote"); + + // Rock The Vote Extended cvars + CreateConVar("rtve_version", MCE_VERSION, "Rock The Vote Extended Version", FCVAR_SPONLY|FCVAR_NOTIFY|FCVAR_DONTRECORD); + + AutoExecConfig(true, "rtv"); +} + +public OnMapStart() +{ + g_Voters = 0; + g_Votes = 0; + g_VotesNeeded = 0; + g_InChange = false; + + /* Handle late load */ + for (new i=1; i<=MaxClients; i++) + { + if (IsClientConnected(i)) + { + OnClientConnected(i); + } + } +} + +public OnMapEnd() +{ + g_CanRTV = false; + g_RTVAllowed = false; +} + +public OnConfigsExecuted() +{ + g_CanRTV = true; + g_RTVAllowed = false; + CreateTimer(GetConVarFloat(g_Cvar_InitialDelay), Timer_DelayRTV, _, TIMER_FLAG_NO_MAPCHANGE); +} + +public OnClientConnected(client) +{ + if(IsFakeClient(client)) + return; + + g_Voted[client] = false; + + g_Voters = GetTeamClientCount(2) + GetTeamClientCount(3); + g_VotesNeeded = RoundToFloor(float(g_Voters) * GetConVarFloat(g_Cvar_Needed)); + + return; +} + +public OnClientDisconnect(client) +{ + if(IsFakeClient(client)) + return; + + if(g_Voted[client]) + { + g_Votes--; + } + + g_Voters = GetTeamClientCount(2) + GetTeamClientCount(3); + + g_VotesNeeded = RoundToFloor(float(g_Voters) * GetConVarFloat(g_Cvar_Needed)); + + if (!g_CanRTV) + { + return; + } + + if (g_Votes && + g_Voters && + g_Votes >= g_VotesNeeded && + g_RTVAllowed ) + { + if (GetConVarInt(g_Cvar_RTVPostVoteAction) == 1 && HasEndOfMapVoteFinished()) + { + return; + } + + StartRTV(); + } +} + +public OnPlayerChangedTeam(Handle:event, const String:name[], bool:dontBroadcast) +{ + new Client = GetClientOfUserId(GetEventInt(event, "userid")); + + if(IsFakeClient(Client)) + return; + + if(Client == 0) + { + return; + } + + if (IsClientInGame(Client) && IsClientConnected(Client)) + { + if (GetClientTeam(Client) == 1) + { + g_Voters = GetTeamClientCount(2) + GetTeamClientCount(3); + g_VotesNeeded = RoundToFloor(float(g_Voters) * GetConVarFloat(g_Cvar_Needed)); + + if (g_Votes && + g_Voters && + g_Votes >= g_VotesNeeded && + g_RTVAllowed ) + { + if (GetConVarInt(g_Cvar_RTVPostVoteAction) == 1 && HasEndOfMapVoteFinished()) + { + return; + } + + StartRTV(); + } + } + } +} + +public Action:Command_RTV(client, args) +{ + if (!g_CanRTV || !client) + { + return Plugin_Handled; + } + + AttemptRTV(client); + + return Plugin_Handled; +} + +public Action:Command_Say(client, args) +{ + if (!g_CanRTV || !client) + { + return Plugin_Continue; + } + + decl String:text[192]; + if (!GetCmdArgString(text, sizeof(text))) + { + return Plugin_Continue; + } + + new startidx = 0; + if(text[strlen(text)-1] == '"') + { + text[strlen(text)-1] = '\0'; + startidx = 1; + } + + new ReplySource:old = SetCmdReplySource(SM_REPLY_TO_CHAT); + + if (strcmp(text[startidx], "rtv", false) == 0 || strcmp(text[startidx], "rockthevote", false) == 0) + { + AttemptRTV(client); + } + + SetCmdReplySource(old); + + return Plugin_Continue; +} + +AttemptRTV(client) +{ + if (!g_RTVAllowed || (GetConVarInt(g_Cvar_RTVPostVoteAction) == 1 && HasEndOfMapVoteFinished())) + { + CReplyToCommand(client, "[SM] %t", "RTV Not Allowed"); + return; + } + + if (!CanMapChooserStartVote()) + { + CReplyToCommand(client, "[SM] %t", "RTV Started"); + return; + } + + if (GetClientCount(true) < GetConVarInt(g_Cvar_MinPlayers)) + { + CReplyToCommand(client, "[SM] %t", "Minimal Players Not Met"); + return; + } + + if (g_Voted[client]) + { + CReplyToCommand(client, "[SM] %t", "Already Voted", g_Votes, g_VotesNeeded); + return; + } + + new String:name[MAX_NAME_LENGTH]; + GetClientName(client, name, sizeof(name)); + + g_Votes++; + g_Voted[client] = true; + + CPrintToChatAll("[SM] %t", "RTV Requested", name, g_Votes, g_VotesNeeded); + + if (g_Votes >= g_VotesNeeded) + { + StartRTV(); + } +} + +public Action:Timer_DelayRTV(Handle:timer) +{ + g_RTVAllowed = true; +} + +StartRTV() +{ + if (g_InChange) + { + return; + } + + if (EndOfMapVoteEnabled() && HasEndOfMapVoteFinished()) + { + /* Change right now then */ + new String:map[PLATFORM_MAX_PATH]; + if (GetNextMap(map, sizeof(map))) + { + CPrintToChatAll("[SM] %t", "Changing Maps", map); + CreateTimer(5.0, Timer_ChangeMap, _, TIMER_FLAG_NO_MAPCHANGE); + g_InChange = true; + + ResetRTV(); + + g_RTVAllowed = false; + } + return; + } + + if (CanMapChooserStartVote()) + { + new MapChange:when = MapChange:GetConVarInt(g_Cvar_ChangeTime); + InitiateMapChooserVote(when); + + ResetRTV(); + + g_RTVAllowed = false; + CreateTimer(GetConVarFloat(g_Cvar_Interval), Timer_DelayRTV, _, TIMER_FLAG_NO_MAPCHANGE); + } +} + +ResetRTV() +{ + g_Votes = 0; + + for (new i=1; i<=MAXPLAYERS; i++) + { + g_Voted[i] = false; + } +} + +public Action:Timer_ChangeMap(Handle:hTimer) +{ + g_InChange = false; + + LogMessage("RTV changing map manually"); + + new String:map[PLATFORM_MAX_PATH]; + if (GetNextMap(map, sizeof(map))) + { + ForceChangeLevel(map, "RTV after mapvote"); + } + + return Plugin_Stop; +} + +// Rock The Vote Extended functions + +public Action:Command_ForceRTV(client, args) +{ + if (!g_CanRTV || !client) + { + return Plugin_Handled; + } + + ShowActivity2(client, "[RTVE] ", "%t", "Initiated Vote Map"); + + StartRTV(); + + return Plugin_Handled; +} + + diff --git a/mapchooser_extended/translations/chi/incomplete.txt b/mapchooser_extended/translations/chi/incomplete.txt index 9ec75619..20fcb48b 100644 --- a/mapchooser_extended/translations/chi/incomplete.txt +++ b/mapchooser_extended/translations/chi/incomplete.txt @@ -1,2 +1,2 @@ -Missing "Runoff Vote Nextmap", "Number Of Votes", "Custom", "Revote Is Needed", "Revote Warning", "Vote Warning", +Missing "Runoff Vote Nextmap", "Number Of Votes", "Custom", "Revote Is Needed", "Revote Warning", "Vote Warning", "Line One", "Line Two", "Cannot Start Vote", and "Tie Vote" \ No newline at end of file diff --git a/mapchooser_extended/translations/chi/mapchooser_extended.phrases.txt b/mapchooser_extended/translations/chi/mapchooser_extended.phrases.txt index 081e5aa9..82125f7d 100644 --- a/mapchooser_extended/translations/chi/mapchooser_extended.phrases.txt +++ b/mapchooser_extended/translations/chi/mapchooser_extended.phrases.txt @@ -1,43 +1,43 @@ -"Phrases" -{ - "Vote Nextmap" - { - "chi" "下一幅地图投票了!" - } - - "Nextmap Voting Started" - { - "chi" "下一幅地图投选已开始." - } - - "Nextmap Voting Finished" - { - "chi" " 地图投票已结束. 下一幅地图将为 {1}. ( 得票{2}%% , {3}票)" - } - - "Current Map Extended" - { - "chi" "当前地图已被延长." - } - - "Extend Map" - { - "chi" "延长当前地图" - } - - "Dont Change" - { - "chi" "请勿更换" - } - - "Current Map Stays" - { - "chi" "当前地图已被延长! 投票显示! (得票 {1}%% 共 {2} 票)" - } - - "Changed Next Map" - { - "chi" "更换下一幅地图为 \"{1}\"." - } - -} +"Phrases" +{ + "Vote Nextmap" + { + "chi" "下一幅地图投票了!" + } + + "Nextmap Voting Started" + { + "chi" "下一幅地图投选已开始." + } + + "Nextmap Voting Finished" + { + "chi" " 地图投票已结束. 下一幅地图将为 {1}. ( 得票{2}%% , {3}票)" + } + + "Current Map Extended" + { + "chi" "当前地图已被延长." + } + + "Extend Map" + { + "chi" "延长当前地图" + } + + "Dont Change" + { + "chi" "请勿更换" + } + + "Current Map Stays" + { + "chi" "当前地图已被延长! 投票显示! (得票 {1}%% 共 {2} 票)" + } + + "Changed Next Map" + { + "chi" "更换下一幅地图为 \"{1}\"." + } + +} diff --git a/mapchooser_extended/translations/cze/incomplete.txt b/mapchooser_extended/translations/cze/incomplete.txt index 9ec75619..20fcb48b 100644 --- a/mapchooser_extended/translations/cze/incomplete.txt +++ b/mapchooser_extended/translations/cze/incomplete.txt @@ -1,2 +1,2 @@ -Missing "Runoff Vote Nextmap", "Number Of Votes", "Custom", "Revote Is Needed", "Revote Warning", "Vote Warning", +Missing "Runoff Vote Nextmap", "Number Of Votes", "Custom", "Revote Is Needed", "Revote Warning", "Vote Warning", "Line One", "Line Two", "Cannot Start Vote", and "Tie Vote" \ No newline at end of file diff --git a/mapchooser_extended/translations/cze/mapchooser_extended.phrases.txt b/mapchooser_extended/translations/cze/mapchooser_extended.phrases.txt index 225bce1c..2b2ad099 100644 --- a/mapchooser_extended/translations/cze/mapchooser_extended.phrases.txt +++ b/mapchooser_extended/translations/cze/mapchooser_extended.phrases.txt @@ -1,43 +1,43 @@ -"Phrases" -{ - "Vote Nextmap" - { - "cze" "Hlasujte o příští mapě!" - } - - "Nextmap Voting Started" - { - "cze" "Hlasování o příští mapě začalo." - } - - "Nextmap Voting Finished" - { - "cze" "Hlasování o mapě skončilo. Příští mapou bude {1}. (Obdržela {2}%% z {3} hlasů)" - } - - "Current Map Extended" - { - "cze" "Současná mapa byla prodloužena. (Obdržela {1}%% z {2} hlasů)" - } - - "Extend Map" - { - "cze" "Prodloužit současnou mapu" - } - - "Dont Change" - { - "cze" "Neměnit" - } - - "Current Map Stays" - { - "cze" "Současná mapa pokračuje! Hlasování rozhodlo! (Obdržela {1}%% z {2} hlasů)" - } - - "Changed Next Map" - { - "cze" "Změnil příští mapu na \"{1}\"." - } - -} +"Phrases" +{ + "Vote Nextmap" + { + "cze" "Hlasujte o příští mapě!" + } + + "Nextmap Voting Started" + { + "cze" "Hlasování o příští mapě začalo." + } + + "Nextmap Voting Finished" + { + "cze" "Hlasování o mapě skončilo. Příští mapou bude {1}. (Obdržela {2}%% z {3} hlasů)" + } + + "Current Map Extended" + { + "cze" "Současná mapa byla prodloužena. (Obdržela {1}%% z {2} hlasů)" + } + + "Extend Map" + { + "cze" "Prodloužit současnou mapu" + } + + "Dont Change" + { + "cze" "Neměnit" + } + + "Current Map Stays" + { + "cze" "Současná mapa pokračuje! Hlasování rozhodlo! (Obdržela {1}%% z {2} hlasů)" + } + + "Changed Next Map" + { + "cze" "Změnil příští mapu na \"{1}\"." + } + +} diff --git a/mapchooser_extended/translations/da/incomplete.txt b/mapchooser_extended/translations/da/incomplete.txt index 9ec75619..20fcb48b 100644 --- a/mapchooser_extended/translations/da/incomplete.txt +++ b/mapchooser_extended/translations/da/incomplete.txt @@ -1,2 +1,2 @@ -Missing "Runoff Vote Nextmap", "Number Of Votes", "Custom", "Revote Is Needed", "Revote Warning", "Vote Warning", +Missing "Runoff Vote Nextmap", "Number Of Votes", "Custom", "Revote Is Needed", "Revote Warning", "Vote Warning", "Line One", "Line Two", "Cannot Start Vote", and "Tie Vote" \ No newline at end of file diff --git a/mapchooser_extended/translations/da/mapchooser_extended.phrases.txt b/mapchooser_extended/translations/da/mapchooser_extended.phrases.txt index f9edcc73..7ab4c304 100644 --- a/mapchooser_extended/translations/da/mapchooser_extended.phrases.txt +++ b/mapchooser_extended/translations/da/mapchooser_extended.phrases.txt @@ -1,43 +1,43 @@ -"Phrases" -{ - "Vote Nextmap" - { - "da" "Stem om næste bane!" - } - - "Nextmap Voting Started" - { - "da" "Afstemning for næste bane er begyndt." - } - - "Nextmap Voting Finished" - { - "da" "Bane afstemning er fuldført. Den næste bane vil være {1}. (Modtog {2}%% af {3} stemmer) " - } - - "Current Map Extended" - { - "da" "Den nuværende bane er blevet forlænget. (Modtog {1}%% af {2} stemmer) " - } - - "Extend Map" - { - "da" "Forlæng bane" - } - - "Dont Change" - { - "da" "Skift ikke!" - } - - "Current Map Stays" - { - "da" "Den aktuelle bane fortsætter! Afstemningen har talt! (Modtog {1}%% af {2} stemmer)" - } - - "Changed Next Map" - { - "da" "Skiftede næste bane til \"{1}'." - } - -} +"Phrases" +{ + "Vote Nextmap" + { + "da" "Stem om næste bane!" + } + + "Nextmap Voting Started" + { + "da" "Afstemning for næste bane er begyndt." + } + + "Nextmap Voting Finished" + { + "da" "Bane afstemning er fuldført. Den næste bane vil være {1}. (Modtog {2}%% af {3} stemmer) " + } + + "Current Map Extended" + { + "da" "Den nuværende bane er blevet forlænget. (Modtog {1}%% af {2} stemmer) " + } + + "Extend Map" + { + "da" "Forlæng bane" + } + + "Dont Change" + { + "da" "Skift ikke!" + } + + "Current Map Stays" + { + "da" "Den aktuelle bane fortsætter! Afstemningen har talt! (Modtog {1}%% af {2} stemmer)" + } + + "Changed Next Map" + { + "da" "Skiftede næste bane til \"{1}'." + } + +} diff --git a/mapchooser_extended/translations/de/mapchooser_extended.phrases.txt b/mapchooser_extended/translations/de/mapchooser_extended.phrases.txt index 21b90342..ad5b433b 100644 --- a/mapchooser_extended/translations/de/mapchooser_extended.phrases.txt +++ b/mapchooser_extended/translations/de/mapchooser_extended.phrases.txt @@ -1,98 +1,98 @@ -"Phrases" -{ - "Vote Nextmap" - { - "de" "Stimme für die nächste Karte!" - } - - "Nextmap Voting Started" - { - "de" "Abstimmung für die nächste Karte wurde gestartet." - } - - "Nextmap Voting Finished" - { - "de" "Karten-Abstimmung wurde abgeschlossen. Nächste Karte wird {1} sein. ({2}%% von {3} Stimmen erhalten) " - } - - "Current Map Extended" - { - "de" "Die aktuelle Karte wurde verlängert. ({1}%% von {2} Stimmen erhalten) " - } - - "Extend Map" - { - "de" "Verlängere aktuelle Karte" - } - - "Dont Change" - { - "de" "Nicht wechseln" - } - - "Current Map Stays" - { - "de" "Aktuelle Karte geht weiter! Die Abstimmung hat entschieden! ({1}%% von {2} Stimmen erhalten) " - } - - "Changed Next Map" - { - "de" "Nächste Karte wurde auf \"{1}\" geändert. " - } - - "Runoff Vote Nextmap" - { - "de" "Stichwahl für die nächste Karte!" - } - - "Number Of Votes" - { - "de" "Anzahl der Stimmen" - } - - "Custom" - { - "de" "{1} (nicht vorhanden)" - } - - "Revote Is Needed" - { - "de" "Keine Karte hat mehr als {1}%% der Stimmen.\nEine neue Abstimmung ist erforderlich!" - } - - "Revote Warning" - { - "de" "Stichwahl startet in: {1} Sekunden" - } - - "Vote Warning" - { - "de" "Achtung! Die Abstimmung für die nächste Karte startet in: {1} Sekunden" - } - - "Line One" - { - "de" "Überlegen Sie, welche Karte Sie spielen möchten ..." - } - - "Line Two" - { - "de" "... und nicht unüberlegt wählen!" - } - - "Cannot Start Vote" - { - "de" "Abstimmung ist bereits im Gange. Versuch Sie es erneut in {1} Sekunden" - } - - "Tie Vote" - { - "de" "Die Top-{1} Karten haben gleich viele Stimmen!.\nEine neue Abstimmung ist erforderlich!" - } - - "Custom Marked" - { - "de" "*{1}" - } - +"Phrases" +{ + "Vote Nextmap" + { + "de" "Stimme für die nächste Karte!" + } + + "Nextmap Voting Started" + { + "de" "Abstimmung für die nächste Karte wurde gestartet." + } + + "Nextmap Voting Finished" + { + "de" "Karten-Abstimmung wurde abgeschlossen. Nächste Karte wird {1} sein. ({2}%% von {3} Stimmen erhalten) " + } + + "Current Map Extended" + { + "de" "Die aktuelle Karte wurde verlängert. ({1}%% von {2} Stimmen erhalten) " + } + + "Extend Map" + { + "de" "Verlängere aktuelle Karte" + } + + "Dont Change" + { + "de" "Nicht wechseln" + } + + "Current Map Stays" + { + "de" "Aktuelle Karte geht weiter! Die Abstimmung hat entschieden! ({1}%% von {2} Stimmen erhalten) " + } + + "Changed Next Map" + { + "de" "Nächste Karte wurde auf \"{1}\" geändert. " + } + + "Runoff Vote Nextmap" + { + "de" "Stichwahl für die nächste Karte!" + } + + "Number Of Votes" + { + "de" "Anzahl der Stimmen" + } + + "Custom" + { + "de" "{1} (nicht vorhanden)" + } + + "Revote Is Needed" + { + "de" "Keine Karte hat mehr als {1}%% der Stimmen.\nEine neue Abstimmung ist erforderlich!" + } + + "Revote Warning" + { + "de" "Stichwahl startet in: {1} Sekunden" + } + + "Vote Warning" + { + "de" "Achtung! Die Abstimmung für die nächste Karte startet in: {1} Sekunden" + } + + "Line One" + { + "de" "Überlegen Sie, welche Karte Sie spielen möchten ..." + } + + "Line Two" + { + "de" "... und nicht unüberlegt wählen!" + } + + "Cannot Start Vote" + { + "de" "Abstimmung ist bereits im Gange. Versuch Sie es erneut in {1} Sekunden" + } + + "Tie Vote" + { + "de" "Die Top-{1} Karten haben gleich viele Stimmen!.\nEine neue Abstimmung ist erforderlich!" + } + + "Custom Marked" + { + "de" "*{1}" + } + } \ No newline at end of file diff --git a/mapchooser_extended/translations/es/mapchooser_extended.phrases.txt b/mapchooser_extended/translations/es/mapchooser_extended.phrases.txt index 162f7ab0..1dce35de 100644 --- a/mapchooser_extended/translations/es/mapchooser_extended.phrases.txt +++ b/mapchooser_extended/translations/es/mapchooser_extended.phrases.txt @@ -1,92 +1,92 @@ -"Phrases" -{ - "Vote Nextmap" - { - "es" "Vota para el siguiente mapa!" - } - - "Nextmap Voting Started" - { - "es" "La votacion para el siguiente mapa ha comenzado." - } - - "Nextmap Voting Finished" - { - "es" "Votacion de Mapa finalizado. El siguiente mapa sera {1}. (Recibidos {2}%% de {3} votos)" - } - - "Current Map Extended" - { - "es" "El mapa actual ha sido extendido. (Recibidos {1}%% de {2} votos)" - } - - "Extend Map" - { - "es" "Extender mapa actual" - } - - "Dont Change" - { - "es" "No cambiar" - } - - "Current Map Stays" - { - "es" "El mapa actual continua! La votacion ha hablado! (Recibidos {1}%% de {2} votos)" - } - - "Changed Next Map" - { - "es" "El siguiente mapa ha cambiado a \"{1}\"." - } - - "Runoff Vote Nextmap" - { - "es" "Hacer votacion para el siguiente mapa!" - } - - "Number Of Votes" - { - "es" "Numero de Votaciones" - } - - "Custom" - { - "es" "{1} (Custom)" - } - - "Revote Is Needed" - { - "es" "No hay ningun mapa que haya recibido el {1}%% de votaciones.\nEntonces, que mapa ganara? Es necesario otra votacion!" - } - - "Revote Warning" - { - "es" "La votacion empezara en: {1}s" - } - - "Vote Warning" - { - "es" "Atencion! La votacion para el siguiente mapa se iniciara en: {1}s" - } - - "Line One" - { - "es" "Considera que mapa quieres jugar..." - } - - "Line Two" - { - "es" "...y no pulse los botones sin pensar ;-)" - } - - "Cannot Start Vote" - { - "es" "Votacion ya iniciada. Intentando de nuevo en {1}s." - } - - "Tie Vote" - { - "es" "El top {1} de mapas tienen el mismo numero de votaciones.\nSe necesita una nueva votacion!" - } +"Phrases" +{ + "Vote Nextmap" + { + "es" "Vota para el siguiente mapa!" + } + + "Nextmap Voting Started" + { + "es" "La votacion para el siguiente mapa ha comenzado." + } + + "Nextmap Voting Finished" + { + "es" "Votacion de Mapa finalizado. El siguiente mapa sera {1}. (Recibidos {2}%% de {3} votos)" + } + + "Current Map Extended" + { + "es" "El mapa actual ha sido extendido. (Recibidos {1}%% de {2} votos)" + } + + "Extend Map" + { + "es" "Extender mapa actual" + } + + "Dont Change" + { + "es" "No cambiar" + } + + "Current Map Stays" + { + "es" "El mapa actual continua! La votacion ha hablado! (Recibidos {1}%% de {2} votos)" + } + + "Changed Next Map" + { + "es" "El siguiente mapa ha cambiado a \"{1}\"." + } + + "Runoff Vote Nextmap" + { + "es" "Hacer votacion para el siguiente mapa!" + } + + "Number Of Votes" + { + "es" "Numero de Votaciones" + } + + "Custom" + { + "es" "{1} (Custom)" + } + + "Revote Is Needed" + { + "es" "No hay ningun mapa que haya recibido el {1}%% de votaciones.\nEntonces, que mapa ganara? Es necesario otra votacion!" + } + + "Revote Warning" + { + "es" "La votacion empezara en: {1}s" + } + + "Vote Warning" + { + "es" "Atencion! La votacion para el siguiente mapa se iniciara en: {1}s" + } + + "Line One" + { + "es" "Considera que mapa quieres jugar..." + } + + "Line Two" + { + "es" "...y no pulse los botones sin pensar ;-)" + } + + "Cannot Start Vote" + { + "es" "Votacion ya iniciada. Intentando de nuevo en {1}s." + } + + "Tie Vote" + { + "es" "El top {1} de mapas tienen el mismo numero de votaciones.\nSe necesita una nueva votacion!" + } } \ No newline at end of file diff --git a/mapchooser_extended/translations/fr/mapchooser_extended.phrases.old.txt b/mapchooser_extended/translations/fr/mapchooser_extended.phrases.old.txt index cf2c859a..2d20efbf 100644 --- a/mapchooser_extended/translations/fr/mapchooser_extended.phrases.old.txt +++ b/mapchooser_extended/translations/fr/mapchooser_extended.phrases.old.txt @@ -1,87 +1,87 @@ -"Phrases" -{ - "Vote Nextmap" - { - "fr" "Voter pour la prochaine map!" - } - - "Nextmap Voting Started" - { - "fr" "Voter pour la prochaine map est lancer." - } - - "Nextmap Voting Finished" - { - "fr" "Le VoteMap est terminer. La prochaine map sera {1}. (Reçu {2}%% sur {3} votes)" - } - - "Current Map Extended" - { - "fr" "La map actuelle a été prolonger. (Reçu {1}%% sur {2} votes)" - } - - "Extend Map" - { - "fr" "Prolonger la map" - } - - "Dont Change" - { - "fr" "Ne pas changer" - } - - "Current Map Stays" - { - "fr" "La map continue! Le vote a tranché! (Reçu {1}%% sur {2} votes)" - } - - "Changed Next Map" - { - "fr" "La map suivante sera \"{1}\"." - } - - "Runoff Vote Nextmap" - { - "fr" "Votez a nouveau pour la prochaine Map!" - } - - "Number Of Votes" - { - "fr" "Nombres de votes" - } - - "Custom" - { - "fr" "{1} (Custom)" - } - - "Revote Is Needed" - { - "fr" "Aucune map n'a reçu plus de {1}%% du vote.\nAlors, quels maps va gagner? Un nouveau vote va être lancé!" - } - - "Revote Warning" - { - "fr" "Un nouveau vote commence dans: {1}s" - } - - "Vote Warning" - { - "fr" "Attention! Le vote pour la prochaine map commence dans: {1}s" - } - - "Line One" - { - "fr" "Voter pour la map que vous voulez jouer..." - } - - "Line Two" - { - "fr" "...et ne pas cliquez sur une touche comme un con ;-)" - } - - "Cannot Start Vote" - { - "fr" "Vote déjà en cours. Reassayer dans {1}s." - } +"Phrases" +{ + "Vote Nextmap" + { + "fr" "Voter pour la prochaine map!" + } + + "Nextmap Voting Started" + { + "fr" "Voter pour la prochaine map est lancer." + } + + "Nextmap Voting Finished" + { + "fr" "Le VoteMap est terminer. La prochaine map sera {1}. (Reçu {2}%% sur {3} votes)" + } + + "Current Map Extended" + { + "fr" "La map actuelle a été prolonger. (Reçu {1}%% sur {2} votes)" + } + + "Extend Map" + { + "fr" "Prolonger la map" + } + + "Dont Change" + { + "fr" "Ne pas changer" + } + + "Current Map Stays" + { + "fr" "La map continue! Le vote a tranché! (Reçu {1}%% sur {2} votes)" + } + + "Changed Next Map" + { + "fr" "La map suivante sera \"{1}\"." + } + + "Runoff Vote Nextmap" + { + "fr" "Votez a nouveau pour la prochaine Map!" + } + + "Number Of Votes" + { + "fr" "Nombres de votes" + } + + "Custom" + { + "fr" "{1} (Custom)" + } + + "Revote Is Needed" + { + "fr" "Aucune map n'a reçu plus de {1}%% du vote.\nAlors, quels maps va gagner? Un nouveau vote va être lancé!" + } + + "Revote Warning" + { + "fr" "Un nouveau vote commence dans: {1}s" + } + + "Vote Warning" + { + "fr" "Attention! Le vote pour la prochaine map commence dans: {1}s" + } + + "Line One" + { + "fr" "Voter pour la map que vous voulez jouer..." + } + + "Line Two" + { + "fr" "...et ne pas cliquez sur une touche comme un con ;-)" + } + + "Cannot Start Vote" + { + "fr" "Vote déjà en cours. Reassayer dans {1}s." + } } \ No newline at end of file diff --git a/mapchooser_extended/translations/fr/mapchooser_extended.phrases.txt b/mapchooser_extended/translations/fr/mapchooser_extended.phrases.txt index aaa868a7..46ea38a2 100644 --- a/mapchooser_extended/translations/fr/mapchooser_extended.phrases.txt +++ b/mapchooser_extended/translations/fr/mapchooser_extended.phrases.txt @@ -1,92 +1,92 @@ -"Phrases" -{ - "Vote Nextmap" - { - "fr" "Votez pour la prochaine map!" - } - - "Nextmap Voting Started" - { - "fr" "Le vote pour la prochaine map est lancé." - } - - "Nextmap Voting Finished" - { - "fr" "Le VoteMap est terminé. La prochaine map sera {1}. (Reçu {2}%% sur {3} votes)" - } - - "Current Map Extended" - { - "fr" "La map actuelle a été prolongée. (Reçu {1}%% sur {2} votes)" - } - - "Extend Map" - { - "fr" "Prolonger la map" - } - - "Dont Change" - { - "fr" "Ne pas changer" - } - - "Current Map Stays" - { - "fr" "La map continue! Le vote a tranché! (Reçu {1}%% sur {2} votes)" - } - - "Changed Next Map" - { - "fr" "La map suivante sera \"{1}\"." - } - - "Runoff Vote Nextmap" - { - "fr" "Votez à nouveau pour la prochaine Map!" - } - - "Number Of Votes" - { - "fr" "Nombres de votes" - } - - "Custom" - { - "fr" "{1} (Custom)" - } - - "Revote Is Needed" - { - "fr" "Aucune map n'a reçu plus de {1}%% du vote.\nAlors, quelle map va gagner? Un nouveau vote va être lancé!" - } - - "Revote Warning" - { - "fr" "Un nouveau vote commence dans: {1}s" - } - - "Vote Warning" - { - "fr" "Attention! Le vote pour la prochaine map commence dans: {1}s" - } - - "Line One" - { - "fr" "Votez pour la map que vous voulez jouer..." - } - - "Line Two" - { - "fr" "...et n'appuyez pas sur une touche comme un con ;-)" - } - - "Cannot Start Vote" - { - "fr" "Vote déjà en cours. Reassayez dans {1}s." - } - - "Tie Vote" - { - "fr" "{1} maps ont eu un même nombre de voix.\nUn nouveau vote est nécessaire!" - } +"Phrases" +{ + "Vote Nextmap" + { + "fr" "Votez pour la prochaine map!" + } + + "Nextmap Voting Started" + { + "fr" "Le vote pour la prochaine map est lancé." + } + + "Nextmap Voting Finished" + { + "fr" "Le VoteMap est terminé. La prochaine map sera {1}. (Reçu {2}%% sur {3} votes)" + } + + "Current Map Extended" + { + "fr" "La map actuelle a été prolongée. (Reçu {1}%% sur {2} votes)" + } + + "Extend Map" + { + "fr" "Prolonger la map" + } + + "Dont Change" + { + "fr" "Ne pas changer" + } + + "Current Map Stays" + { + "fr" "La map continue! Le vote a tranché! (Reçu {1}%% sur {2} votes)" + } + + "Changed Next Map" + { + "fr" "La map suivante sera \"{1}\"." + } + + "Runoff Vote Nextmap" + { + "fr" "Votez à nouveau pour la prochaine Map!" + } + + "Number Of Votes" + { + "fr" "Nombres de votes" + } + + "Custom" + { + "fr" "{1} (Custom)" + } + + "Revote Is Needed" + { + "fr" "Aucune map n'a reçu plus de {1}%% du vote.\nAlors, quelle map va gagner? Un nouveau vote va être lancé!" + } + + "Revote Warning" + { + "fr" "Un nouveau vote commence dans: {1}s" + } + + "Vote Warning" + { + "fr" "Attention! Le vote pour la prochaine map commence dans: {1}s" + } + + "Line One" + { + "fr" "Votez pour la map que vous voulez jouer..." + } + + "Line Two" + { + "fr" "...et n'appuyez pas sur une touche comme un con ;-)" + } + + "Cannot Start Vote" + { + "fr" "Vote déjà en cours. Reassayez dans {1}s." + } + + "Tie Vote" + { + "fr" "{1} maps ont eu un même nombre de voix.\nUn nouveau vote est nécessaire!" + } } \ No newline at end of file diff --git a/mapchooser_extended/translations/hu/incomplete.txt b/mapchooser_extended/translations/hu/incomplete.txt index 9ec75619..20fcb48b 100644 --- a/mapchooser_extended/translations/hu/incomplete.txt +++ b/mapchooser_extended/translations/hu/incomplete.txt @@ -1,2 +1,2 @@ -Missing "Runoff Vote Nextmap", "Number Of Votes", "Custom", "Revote Is Needed", "Revote Warning", "Vote Warning", +Missing "Runoff Vote Nextmap", "Number Of Votes", "Custom", "Revote Is Needed", "Revote Warning", "Vote Warning", "Line One", "Line Two", "Cannot Start Vote", and "Tie Vote" \ No newline at end of file diff --git a/mapchooser_extended/translations/hu/mapchooser_extended.phrases.txt b/mapchooser_extended/translations/hu/mapchooser_extended.phrases.txt index cb159e36..24f49907 100644 --- a/mapchooser_extended/translations/hu/mapchooser_extended.phrases.txt +++ b/mapchooser_extended/translations/hu/mapchooser_extended.phrases.txt @@ -1,43 +1,43 @@ -"Phrases" -{ - "Vote Nextmap" - { - "hu" "Mi legyen a következő pálya?" - } - - "Nextmap Voting Started" - { - "hu" "Palyavalaszto szavazas elindult!" - } - - "Nextmap Voting Finished" - { - "hu" "A szavazás lezárult. A következő pálya a {1} lesz" - } - - "Current Map Extended" - { - "hu" "Az aktualis palya meghosszabitva." - } - - "Extend Map" - { - "hu" "Palya meghosszabitasa" - } - - "Dont Change" - { - "hu" "Ne valtsunk!" - } - - "Current Map Stays" - { - "hu" "Jelenlegi palya folytatodik." - } - - "Changed Next Map" - { - "hu" "\"{1}\" lesz a kovetkezo palya" - } - -} +"Phrases" +{ + "Vote Nextmap" + { + "hu" "Mi legyen a következő pálya?" + } + + "Nextmap Voting Started" + { + "hu" "Palyavalaszto szavazas elindult!" + } + + "Nextmap Voting Finished" + { + "hu" "A szavazás lezárult. A következő pálya a {1} lesz" + } + + "Current Map Extended" + { + "hu" "Az aktualis palya meghosszabitva." + } + + "Extend Map" + { + "hu" "Palya meghosszabitasa" + } + + "Dont Change" + { + "hu" "Ne valtsunk!" + } + + "Current Map Stays" + { + "hu" "Jelenlegi palya folytatodik." + } + + "Changed Next Map" + { + "hu" "\"{1}\" lesz a kovetkezo palya" + } + +} diff --git a/mapchooser_extended/translations/it/mapchooser_extended.phrases.txt b/mapchooser_extended/translations/it/mapchooser_extended.phrases.txt index 057a7925..a0d967a7 100644 --- a/mapchooser_extended/translations/it/mapchooser_extended.phrases.txt +++ b/mapchooser_extended/translations/it/mapchooser_extended.phrases.txt @@ -1,92 +1,92 @@ -"Phrases" -{ - "Vote Nextmap" - { - "it" "Vota per la prossima mappa!" - } - - "Nextmap Voting Started" - { - "it" "Le votazione per scegliere la prossima mappa sono iniziate." - } - - "Nextmap Voting Finished" - { - "it" "Le votazioni sono terminate. La prossima mappa sarà {1}. (Con {2}%% su {3} votes)" - } - - "Current Map Extended" - { - "it" "La mappa attuale è stata estesa. (Con {1}%% su {2} votes)" - } - - "Extend Map" - { - "it" "Estendi la mappa corrente" - } - - "Dont Change" - { - "it" "Non cambiare" - } - - "Current Map Stays" - { - "it" "La mappa continua! Il voto ha parlato! (Con {1}%% su {2} votes)" - } - - "Changed Next Map" - { - "it" "La mappa successiva sarà \"{1}\"." - } - - "Runoff Vote Nextmap" - { - "it" "Vota nuovamente per scegliere la prossima mappa!" - } - - "Number Of Votes" - { - "it" "Numero di voti" - } - - "Custom" - { - "it" "{1} (Custom)" - } - - "Revote Is Needed" - { - "it" "Nessuna mappa ha ricevuto più del {1}%% di voti.\nAllora, quale mappa vincerà? Si inizia un' altra votazione!" - } - - "Revote Warning" - { - "it" "Una nuova votazione inizierà tra: {1}s" - } - - "Vote Warning" - { - "it" "Attenzione! Le votazione per la prossima mappa cominceranno tra: {1}s" - } - - "Line One" - { - "it" "Vota la mappa chevorresti giocare..." - } - - "Line Two" - { - "it" "...e non cliccare i tasti sconsideratamente :D" - } - - "Cannot Start Vote" - { - "it" "Il voto è in corso. Riprova tra {1}s." - } - - "Tie Vote" - { - "it" "Le mappe hanno raggiunto il pareggio dei voti.\nBisogna nuovamente votare!" - } -} +"Phrases" +{ + "Vote Nextmap" + { + "it" "Vota per la prossima mappa!" + } + + "Nextmap Voting Started" + { + "it" "Le votazione per scegliere la prossima mappa sono iniziate." + } + + "Nextmap Voting Finished" + { + "it" "Le votazioni sono terminate. La prossima mappa sarà {1}. (Con {2}%% su {3} votes)" + } + + "Current Map Extended" + { + "it" "La mappa attuale è stata estesa. (Con {1}%% su {2} votes)" + } + + "Extend Map" + { + "it" "Estendi la mappa corrente" + } + + "Dont Change" + { + "it" "Non cambiare" + } + + "Current Map Stays" + { + "it" "La mappa continua! Il voto ha parlato! (Con {1}%% su {2} votes)" + } + + "Changed Next Map" + { + "it" "La mappa successiva sarà \"{1}\"." + } + + "Runoff Vote Nextmap" + { + "it" "Vota nuovamente per scegliere la prossima mappa!" + } + + "Number Of Votes" + { + "it" "Numero di voti" + } + + "Custom" + { + "it" "{1} (Custom)" + } + + "Revote Is Needed" + { + "it" "Nessuna mappa ha ricevuto più del {1}%% di voti.\nAllora, quale mappa vincerà? Si inizia un' altra votazione!" + } + + "Revote Warning" + { + "it" "Una nuova votazione inizierà tra: {1}s" + } + + "Vote Warning" + { + "it" "Attenzione! Le votazione per la prossima mappa cominceranno tra: {1}s" + } + + "Line One" + { + "it" "Vota la mappa chevorresti giocare..." + } + + "Line Two" + { + "it" "...e non cliccare i tasti sconsideratamente :D" + } + + "Cannot Start Vote" + { + "it" "Il voto è in corso. Riprova tra {1}s." + } + + "Tie Vote" + { + "it" "Le mappe hanno raggiunto il pareggio dei voti.\nBisogna nuovamente votare!" + } +} diff --git a/mapchooser_extended/translations/jp/incomplete.txt b/mapchooser_extended/translations/jp/incomplete.txt index 9ec75619..20fcb48b 100644 --- a/mapchooser_extended/translations/jp/incomplete.txt +++ b/mapchooser_extended/translations/jp/incomplete.txt @@ -1,2 +1,2 @@ -Missing "Runoff Vote Nextmap", "Number Of Votes", "Custom", "Revote Is Needed", "Revote Warning", "Vote Warning", +Missing "Runoff Vote Nextmap", "Number Of Votes", "Custom", "Revote Is Needed", "Revote Warning", "Vote Warning", "Line One", "Line Two", "Cannot Start Vote", and "Tie Vote" \ No newline at end of file diff --git a/mapchooser_extended/translations/jp/mapchooser_extended.phrases.txt b/mapchooser_extended/translations/jp/mapchooser_extended.phrases.txt index be7e0ddf..4e985797 100644 --- a/mapchooser_extended/translations/jp/mapchooser_extended.phrases.txt +++ b/mapchooser_extended/translations/jp/mapchooser_extended.phrases.txt @@ -1,43 +1,43 @@ -"Phrases" -{ - "Vote Nextmap" - { - "jp" "次のマップを投票してください!" - } - - "Nextmap Voting Started" - { - "jp" "次のマップ投票をスタートしました。" - } - - "Nextmap Voting Finished" - { - "jp" "マップ投票が完了しました。次はマップは{1}です。({3}中{2}%%)" - } - - "Current Map Extended" - { - "jp" "現在のマップを延長します。({3}中{2}%%)" - } - - "Extend Map" - { - "jp" "現在のマップを延長" - } - - "Dont Change" - { - "jp" "変更しない" - } - - "Current Map Stays" - { - "jp" "現在のマップを延長します。({3}中{2}%%)" - } - - "Changed Next Map" - { - "jp" "次のマップを\"{1}\"に変更しました。" - } - -} +"Phrases" +{ + "Vote Nextmap" + { + "jp" "次のマップを投票してください!" + } + + "Nextmap Voting Started" + { + "jp" "次のマップ投票をスタートしました。" + } + + "Nextmap Voting Finished" + { + "jp" "マップ投票が完了しました。次はマップは{1}です。({3}中{2}%%)" + } + + "Current Map Extended" + { + "jp" "現在のマップを延長します。({3}中{2}%%)" + } + + "Extend Map" + { + "jp" "現在のマップを延長" + } + + "Dont Change" + { + "jp" "変更しない" + } + + "Current Map Stays" + { + "jp" "現在のマップを延長します。({3}中{2}%%)" + } + + "Changed Next Map" + { + "jp" "次のマップを\"{1}\"に変更しました。" + } + +} diff --git a/mapchooser_extended/translations/ko/mapchooser_extended.phrases.txt b/mapchooser_extended/translations/ko/mapchooser_extended.phrases.txt index 489dd1ca..9ce0781b 100644 --- a/mapchooser_extended/translations/ko/mapchooser_extended.phrases.txt +++ b/mapchooser_extended/translations/ko/mapchooser_extended.phrases.txt @@ -1,98 +1,98 @@ -"Phrases" -{ - "Vote Nextmap" - { - "ko" "다음 맵을 결정하기 위한 투표!" - } - - "Nextmap Voting Started" - { - "ko" "다음 맵을 결정하기 위한 투표가 시작되었습니다." - } - - "Nextmap Voting Finished" - { - "ko" "맵 투표가 끝났습니다. 다음 맵은 {1} 이 될 것입니다. (전체 인원 {2}명 중 {1}%%의 표를 받았습니다.)" - } - - "Current Map Extended" - { - "ko" "현재 맵을 더하기로 결정했습니다. (전체 인원 {2}명 중 {1}%%의 표를 받았습니다.)" - } - - "Extend Map" - { - "ko" "지금 맵을 더 하자" - } - - "Dont Change" - { - "ko" "바꾸지 말자" - } - - "Current Map Stays" - { - "ko" "현재 맵을 계속 합니다!(전체 인원 {2} 명의 {1}%% 의 표를 받았습니다)" - } - - "Changed Next Map" - { - "ko" "다음 맵을 \"{1}\" 로 바꾸었습니다." - } - - "Runoff Vote Nextmap" - { - "ko" "다음 맵을 결정하기 위한 재투표!" - } - - "Number Of Votes" - { - "ko" "투표수" - } - - "Custom" - { - "ko" "{1} (커스텀 맵)" - } - - "Revote Is Needed" - { - "ko" "{1}%% 이상 투표를 받은 맵이 없습니다.\n다음 맵을 결정하기 위해 재투표를 합니다!" - } - - "Revote Warning" - { - "ko" "재투표까지: {1}초" - } - - "Vote Warning" - { - "ko" "알립니다! 다음 맵을 결정하는 투표까지: {1}초" - } - - "Line One" - { - "ko" "원하는 맵을 선택하세요..." - } - - "Line Two" - { - "ko" "...그리고 아무거나 막 찍지 말고요 -_-" - } - - "Cannot Start Vote" - { - "ko" "투표가 진행 중에 있습니다. {1}초 후 다시 시도합니다." - } - - "Tie Vote" - { - "ko" "{1}개 맵이 같은 투표수를 얻었습니다.\n재투표를 합니다!" - } - - "Custom Marked" - { - "ko" "*{1}" - } - -} +"Phrases" +{ + "Vote Nextmap" + { + "ko" "다음 맵을 결정하기 위한 투표!" + } + + "Nextmap Voting Started" + { + "ko" "다음 맵을 결정하기 위한 투표가 시작되었습니다." + } + + "Nextmap Voting Finished" + { + "ko" "맵 투표가 끝났습니다. 다음 맵은 {1} 이 될 것입니다. (전체 인원 {2}명 중 {1}%%의 표를 받았습니다.)" + } + + "Current Map Extended" + { + "ko" "현재 맵을 더하기로 결정했습니다. (전체 인원 {2}명 중 {1}%%의 표를 받았습니다.)" + } + + "Extend Map" + { + "ko" "지금 맵을 더 하자" + } + + "Dont Change" + { + "ko" "바꾸지 말자" + } + + "Current Map Stays" + { + "ko" "현재 맵을 계속 합니다!(전체 인원 {2} 명의 {1}%% 의 표를 받았습니다)" + } + + "Changed Next Map" + { + "ko" "다음 맵을 \"{1}\" 로 바꾸었습니다." + } + + "Runoff Vote Nextmap" + { + "ko" "다음 맵을 결정하기 위한 재투표!" + } + + "Number Of Votes" + { + "ko" "투표수" + } + + "Custom" + { + "ko" "{1} (커스텀 맵)" + } + + "Revote Is Needed" + { + "ko" "{1}%% 이상 투표를 받은 맵이 없습니다.\n다음 맵을 결정하기 위해 재투표를 합니다!" + } + + "Revote Warning" + { + "ko" "재투표까지: {1}초" + } + + "Vote Warning" + { + "ko" "알립니다! 다음 맵을 결정하는 투표까지: {1}초" + } + + "Line One" + { + "ko" "원하는 맵을 선택하세요..." + } + + "Line Two" + { + "ko" "...그리고 아무거나 막 찍지 말고요 -_-" + } + + "Cannot Start Vote" + { + "ko" "투표가 진행 중에 있습니다. {1}초 후 다시 시도합니다." + } + + "Tie Vote" + { + "ko" "{1}개 맵이 같은 투표수를 얻었습니다.\n재투표를 합니다!" + } + + "Custom Marked" + { + "ko" "*{1}" + } + +} diff --git a/mapchooser_extended/translations/lv/incomplete.txt b/mapchooser_extended/translations/lv/incomplete.txt index 9ec75619..20fcb48b 100644 --- a/mapchooser_extended/translations/lv/incomplete.txt +++ b/mapchooser_extended/translations/lv/incomplete.txt @@ -1,2 +1,2 @@ -Missing "Runoff Vote Nextmap", "Number Of Votes", "Custom", "Revote Is Needed", "Revote Warning", "Vote Warning", +Missing "Runoff Vote Nextmap", "Number Of Votes", "Custom", "Revote Is Needed", "Revote Warning", "Vote Warning", "Line One", "Line Two", "Cannot Start Vote", and "Tie Vote" \ No newline at end of file diff --git a/mapchooser_extended/translations/lv/mapchooser_extended.phrases.txt b/mapchooser_extended/translations/lv/mapchooser_extended.phrases.txt index 9a55fc55..766fba1f 100644 --- a/mapchooser_extended/translations/lv/mapchooser_extended.phrases.txt +++ b/mapchooser_extended/translations/lv/mapchooser_extended.phrases.txt @@ -1,43 +1,43 @@ -"Phrases" -{ - "Vote Nextmap" - { - "lv" "Balso par nākamo karti!" - } - - "Nextmap Voting Started" - { - "lv" "Balsošana par nākamo karti ir sākusies." - } - - "Nextmap Voting Finished" - { - "lv" "Balsošana par karti ir beigusies. Nākamā karte būs {1}. (Saņēma {2}%% no {3} balsīm) " - } - - "Current Map Extended" - { - "lv" "Patreizējās kartes laiks ir pagarināts. (Saņēma {1}%% no {2} balsīm) " - } - - "Extend Map" - { - "lv" "Pagarināt laiku patreizējā kartē" - } - - "Dont Change" - { - "lv" "Nemainīt" - } - - "Current Map Stays" - { - "lv" "Patreizējā karte turpinās! Aptauja ir beigusies! (Saņēma {1}%% no {2} balsīm)" - } - - "Changed Next Map" - { - "lv" "Nomainīja nākamo karti uz \"{1}\"." - } - -} +"Phrases" +{ + "Vote Nextmap" + { + "lv" "Balso par nākamo karti!" + } + + "Nextmap Voting Started" + { + "lv" "Balsošana par nākamo karti ir sākusies." + } + + "Nextmap Voting Finished" + { + "lv" "Balsošana par karti ir beigusies. Nākamā karte būs {1}. (Saņēma {2}%% no {3} balsīm) " + } + + "Current Map Extended" + { + "lv" "Patreizējās kartes laiks ir pagarināts. (Saņēma {1}%% no {2} balsīm) " + } + + "Extend Map" + { + "lv" "Pagarināt laiku patreizējā kartē" + } + + "Dont Change" + { + "lv" "Nemainīt" + } + + "Current Map Stays" + { + "lv" "Patreizējā karte turpinās! Aptauja ir beigusies! (Saņēma {1}%% no {2} balsīm)" + } + + "Changed Next Map" + { + "lv" "Nomainīja nākamo karti uz \"{1}\"." + } + +} diff --git a/mapchooser_extended/translations/mapchooser_extended.phrases.txt b/mapchooser_extended/translations/mapchooser_extended.phrases.txt index fe35c8be..03d22af5 100644 --- a/mapchooser_extended/translations/mapchooser_extended.phrases.txt +++ b/mapchooser_extended/translations/mapchooser_extended.phrases.txt @@ -1,109 +1,109 @@ -"Phrases" -{ - "Vote Nextmap" - { - "en" "Vote for the next map!" - } - - "Nextmap Voting Started" - { - "en" "Voting for next map has started." - } - - "Nextmap Voting Finished" - { - "#format" "{1:s},{2:i},{3:i}" - "en" "Map voting has finished. The next map will be {1}. (Received {2}%% of {3} votes)" - } - - "Current Map Extended" - { - "#format" "{1:i},{2:i}" - "en" "The current map has been extended. (Received {1}%% of {2} votes)" - } - - "Extend Map" - { - "en" "Extend Current Map" - } - - "Dont Change" - { - "en" "Don't Change" - } - - "Current Map Stays" - { - "#format" "{1:i},{2:i}" - "en" "Current map continues! The Vote has spoken! (Received {1}%% of {2} votes)" - } - - "Changed Next Map" - { - "#format" "{1:s}" - "en" "Changed nextmap to \"{1}\"." - } - - "Runoff Vote Nextmap" - { - "en" "Runoff Vote for the next map!" - } - - "Number Of Votes" - { - "en" "Number of votes" - } - - "Custom" - { - "#format" "{1:s}" - "en" "{1} (Custom)" - } - - "Revote Is Needed" - { - "#format" "{1:i}" - "en" "No map has received more than {1}%% of the vote.\nSo, which map will win? A revote is needed!" - } - - "Revote Warning" - { - "#format" "{1:i}" - "en" "Runoff vote will start in: {1}s" - } - - "Vote Warning" - { - "#format" "{1:i}" - "en" "Warning! Voting for the next map will begin in: {1}s" - } - - "Line One" - { - "en" "Consider which map you want to play..." - } - - "Line Two" - { - "en" "...and don't hit buttons thoughtlessly ;-)" - } - - "Cannot Start Vote" - { - "#format" "{1:i}" - "en" "Vote already in progress. Retrying in {1}s." - } - - "Tie Vote" - { - "#format" "{1:i}" - "en" "The top {1} maps had the same number of votes.\nA revote is needed!" - } - - "Custom Marked" - { - "#format" "{1:s}" - "en" "*{1}" - } - +"Phrases" +{ + "Vote Nextmap" + { + "en" "Vote for the next map!" + } + + "Nextmap Voting Started" + { + "en" "Voting for next map has started." + } + + "Nextmap Voting Finished" + { + "#format" "{1:s},{2:i},{3:i}" + "en" "Map voting has finished. The next map will be {1}. (Received {2}%% of {3} votes)" + } + + "Current Map Extended" + { + "#format" "{1:i},{2:i}" + "en" "The current map has been extended. (Received {1}%% of {2} votes)" + } + + "Extend Map" + { + "en" "Extend Current Map" + } + + "Dont Change" + { + "en" "Don't Change" + } + + "Current Map Stays" + { + "#format" "{1:i},{2:i}" + "en" "Current map continues! The Vote has spoken! (Received {1}%% of {2} votes)" + } + + "Changed Next Map" + { + "#format" "{1:s}" + "en" "Changed nextmap to \"{1}\"." + } + + "Runoff Vote Nextmap" + { + "en" "Runoff Vote for the next map!" + } + + "Number Of Votes" + { + "en" "Number of votes" + } + + "Custom" + { + "#format" "{1:s}" + "en" "{1} (Custom)" + } + + "Revote Is Needed" + { + "#format" "{1:i}" + "en" "No map has received more than {1}%% of the vote.\nSo, which map will win? A revote is needed!" + } + + "Revote Warning" + { + "#format" "{1:i}" + "en" "Runoff vote will start in: {1}s" + } + + "Vote Warning" + { + "#format" "{1:i}" + "en" "Warning! Voting for the next map will begin in: {1}s" + } + + "Line One" + { + "en" "Consider which map you want to play..." + } + + "Line Two" + { + "en" "...and don't hit buttons thoughtlessly ;-)" + } + + "Cannot Start Vote" + { + "#format" "{1:i}" + "en" "Vote already in progress. Retrying in {1}s." + } + + "Tie Vote" + { + "#format" "{1:i}" + "en" "The top {1} maps had the same number of votes.\nA revote is needed!" + } + + "Custom Marked" + { + "#format" "{1:s}" + "en" "*{1}" + } + } \ No newline at end of file diff --git a/mapchooser_extended/translations/nl/incomplete.txt b/mapchooser_extended/translations/nl/incomplete.txt index 9ec75619..20fcb48b 100644 --- a/mapchooser_extended/translations/nl/incomplete.txt +++ b/mapchooser_extended/translations/nl/incomplete.txt @@ -1,2 +1,2 @@ -Missing "Runoff Vote Nextmap", "Number Of Votes", "Custom", "Revote Is Needed", "Revote Warning", "Vote Warning", +Missing "Runoff Vote Nextmap", "Number Of Votes", "Custom", "Revote Is Needed", "Revote Warning", "Vote Warning", "Line One", "Line Two", "Cannot Start Vote", and "Tie Vote" \ No newline at end of file diff --git a/mapchooser_extended/translations/nl/mapchooser_extended.phrases.txt b/mapchooser_extended/translations/nl/mapchooser_extended.phrases.txt index 02f6c45e..fe3bb629 100644 --- a/mapchooser_extended/translations/nl/mapchooser_extended.phrases.txt +++ b/mapchooser_extended/translations/nl/mapchooser_extended.phrases.txt @@ -1,43 +1,43 @@ -"Phrases" -{ - "Vote Nextmap" - { - "nl" "Stem voor de volgende map!" - } - - "Nextmap Voting Started" - { - "nl" "Stemmen voor de volgende map is gestart." - } - - "Nextmap Voting Finished" - { - "nl" "Map stemmen gestopt. De volgende map wordt {1}." - } - - "Current Map Extended" - { - "nl" "De huidige map is verlengd." - } - - "Extend Map" - { - "nl" "Verleng huidige map" - } - - "Dont Change" - { - "nl" "Niet veranderen" - } - - "Current Map Stays" - { - "nl" "Huidige map gaat verder! De Stem heeft gesproken! (Ontvangen {1}%% van de {2} stemmen)" - } - - "Changed Next Map" - { - "nl" "Volgende map verandert naar \"{1}\"." - } - -} +"Phrases" +{ + "Vote Nextmap" + { + "nl" "Stem voor de volgende map!" + } + + "Nextmap Voting Started" + { + "nl" "Stemmen voor de volgende map is gestart." + } + + "Nextmap Voting Finished" + { + "nl" "Map stemmen gestopt. De volgende map wordt {1}." + } + + "Current Map Extended" + { + "nl" "De huidige map is verlengd." + } + + "Extend Map" + { + "nl" "Verleng huidige map" + } + + "Dont Change" + { + "nl" "Niet veranderen" + } + + "Current Map Stays" + { + "nl" "Huidige map gaat verder! De Stem heeft gesproken! (Ontvangen {1}%% van de {2} stemmen)" + } + + "Changed Next Map" + { + "nl" "Volgende map verandert naar \"{1}\"." + } + +} diff --git a/mapchooser_extended/translations/no/incomplete.txt b/mapchooser_extended/translations/no/incomplete.txt index 9ec75619..20fcb48b 100644 --- a/mapchooser_extended/translations/no/incomplete.txt +++ b/mapchooser_extended/translations/no/incomplete.txt @@ -1,2 +1,2 @@ -Missing "Runoff Vote Nextmap", "Number Of Votes", "Custom", "Revote Is Needed", "Revote Warning", "Vote Warning", +Missing "Runoff Vote Nextmap", "Number Of Votes", "Custom", "Revote Is Needed", "Revote Warning", "Vote Warning", "Line One", "Line Two", "Cannot Start Vote", and "Tie Vote" \ No newline at end of file diff --git a/mapchooser_extended/translations/no/mapchooser_extended.phrases.txt b/mapchooser_extended/translations/no/mapchooser_extended.phrases.txt index 76e1bc9b..674031af 100644 --- a/mapchooser_extended/translations/no/mapchooser_extended.phrases.txt +++ b/mapchooser_extended/translations/no/mapchooser_extended.phrases.txt @@ -1,43 +1,43 @@ -"Phrases" -{ - "Vote Nextmap" - { - "no" "Stem for det neste kartet!" - } - - "Nextmap Voting Started" - { - "no" "Avstemning for det neste kartet har startet." - } - - "Nextmap Voting Finished" - { - "no" "Kart-avstemningen er avsluttet. Det neste kartet vil være {1}. (Mottok {2}%% av {3} stemmer)." - } - - "Current Map Extended" - { - "no" "Gjeldende kart videreføres! (Mottok {1}%% av {2} stemmer)." - } - - "Extend Map" - { - "no" "Forleng gjeldende kart." - } - - "Dont Change" - { - "no" "Ikke bytt!" - } - - "Current Map Stays" - { - "no" "Gjeldende kart videreføres! Avstemningen har talt! (Mottok {1}%% av {2} stemmer)." - } - - "Changed Next Map" - { - "no" "Byttet neste kart til \"{1}\"" - } - -} +"Phrases" +{ + "Vote Nextmap" + { + "no" "Stem for det neste kartet!" + } + + "Nextmap Voting Started" + { + "no" "Avstemning for det neste kartet har startet." + } + + "Nextmap Voting Finished" + { + "no" "Kart-avstemningen er avsluttet. Det neste kartet vil være {1}. (Mottok {2}%% av {3} stemmer)." + } + + "Current Map Extended" + { + "no" "Gjeldende kart videreføres! (Mottok {1}%% av {2} stemmer)." + } + + "Extend Map" + { + "no" "Forleng gjeldende kart." + } + + "Dont Change" + { + "no" "Ikke bytt!" + } + + "Current Map Stays" + { + "no" "Gjeldende kart videreføres! Avstemningen har talt! (Mottok {1}%% av {2} stemmer)." + } + + "Changed Next Map" + { + "no" "Byttet neste kart til \"{1}\"" + } + +} diff --git a/mapchooser_extended/translations/pl/mapchooser_extended.phrases.txt b/mapchooser_extended/translations/pl/mapchooser_extended.phrases.txt index 902c4a13..a244f81e 100644 --- a/mapchooser_extended/translations/pl/mapchooser_extended.phrases.txt +++ b/mapchooser_extended/translations/pl/mapchooser_extended.phrases.txt @@ -1,92 +1,92 @@ -"Phrases" -{ - "Vote Nextmap" - { - "pl" "Głosuj na następną mapę!" - } - - "Nextmap Voting Started" - { - "pl" "Rozpoczęto głosowanie na następną mapę." - } - - "Nextmap Voting Finished" - { - "pl" "Głosowanie na mapę zostało zakończone. Następną mapą będzie {1}. (Otrzymała {2} procent z {3} głosów) " - } - - "Current Map Extended" - { - "pl" "Aktualna mapa została przedłużona. (Otrzymała {1} procent z {2} głosów)" - } - - "Extend Map" - { - "pl" "Przedłuż bieżącą mapę" - } - - "Dont Change" - { - "pl" "Nie Zmieniaj" - } - - "Current Map Stays" - { - "pl" "Aktualna mapa będzie kontynuowana! (Otrzymano {1} procent z {2} głosów)" - } - - "Changed Next Map" - { - "pl" "Zmieniono następną mapę na: \"{1}\"." - } - - "Runoff Vote Nextmap" - { - "pl" "Wybierz ponownie!" - } - - "Number Of Votes" - { - "pl" "Otrzymane głosy" - } - - "Custom" - { - "pl" "{1} (Niestandardowa)" - } - - "Revote Is Needed" - { - "pl" "Żadna mapa nie otrzymała przynajmniej {1}%% głosów.\nWięc która mapa jest zwycięzcą? Trzeba zagłosować ponownie!" - } - - "Revote Warning" - { - "pl" "Ponowne głosowanie rozpocznie się za: {1}s\nTym razem się już zdecydujcie ;-)" - } - - "Vote Warning" - { - "pl" "UWAGA!!! Głosowanie na następną mapę rozpocznie się za: {1}s" - } - - "Line One" - { - "pl" "Zastanów się na której mapie chcesz grać..." - } - - "Line Two" - { - "pl" "...wpisując !revote możesz zmienić swój głos." - } - - "Cannot Start Vote" - { - "pl" "Głosowanie w toku. Ponawiam za {1}s." - } - - "Tie Vote" - { - "pl" "{1} najlepsze mapy otrzymały tę samą ilość głosów.\nPotrzeba ponownego głosowania!" - } +"Phrases" +{ + "Vote Nextmap" + { + "pl" "Głosuj na następną mapę!" + } + + "Nextmap Voting Started" + { + "pl" "Rozpoczęto głosowanie na następną mapę." + } + + "Nextmap Voting Finished" + { + "pl" "Głosowanie na mapę zostało zakończone. Następną mapą będzie {1}. (Otrzymała {2} procent z {3} głosów) " + } + + "Current Map Extended" + { + "pl" "Aktualna mapa została przedłużona. (Otrzymała {1} procent z {2} głosów)" + } + + "Extend Map" + { + "pl" "Przedłuż bieżącą mapę" + } + + "Dont Change" + { + "pl" "Nie Zmieniaj" + } + + "Current Map Stays" + { + "pl" "Aktualna mapa będzie kontynuowana! (Otrzymano {1} procent z {2} głosów)" + } + + "Changed Next Map" + { + "pl" "Zmieniono następną mapę na: \"{1}\"." + } + + "Runoff Vote Nextmap" + { + "pl" "Wybierz ponownie!" + } + + "Number Of Votes" + { + "pl" "Otrzymane głosy" + } + + "Custom" + { + "pl" "{1} (Niestandardowa)" + } + + "Revote Is Needed" + { + "pl" "Żadna mapa nie otrzymała przynajmniej {1}%% głosów.\nWięc która mapa jest zwycięzcą? Trzeba zagłosować ponownie!" + } + + "Revote Warning" + { + "pl" "Ponowne głosowanie rozpocznie się za: {1}s\nTym razem się już zdecydujcie ;-)" + } + + "Vote Warning" + { + "pl" "UWAGA!!! Głosowanie na następną mapę rozpocznie się za: {1}s" + } + + "Line One" + { + "pl" "Zastanów się na której mapie chcesz grać..." + } + + "Line Two" + { + "pl" "...wpisując !revote możesz zmienić swój głos." + } + + "Cannot Start Vote" + { + "pl" "Głosowanie w toku. Ponawiam za {1}s." + } + + "Tie Vote" + { + "pl" "{1} najlepsze mapy otrzymały tę samą ilość głosów.\nPotrzeba ponownego głosowania!" + } } \ No newline at end of file diff --git a/mapchooser_extended/translations/pt/mapchooser_extended.phrases.txt b/mapchooser_extended/translations/pt/mapchooser_extended.phrases.txt index 729e42ae..8a5746bf 100644 --- a/mapchooser_extended/translations/pt/mapchooser_extended.phrases.txt +++ b/mapchooser_extended/translations/pt/mapchooser_extended.phrases.txt @@ -1,98 +1,98 @@ -"Phrases" -{ - "Vote Nextmap" - { - "pt" "Vote para o prximo mapa!" - } - - "Nextmap Voting Started" - { - "pt" "Votao para o prximo mapa comeou." - } - - "Nextmap Voting Finished" - { - "pt" "A votao para o prximo mapa terminou. O prximo mapa ser {1}. (Recebidos {2}%% de {3} votos)" - } - - "Current Map Extended" - { - "pt" "A mapa atual foi estendido. (Recebidos {1}%% de {2} votos)" - } - - "Extend Map" - { - "pt" "Estender tempo do mapa atual" - } - - "Dont Change" - { - "pt" "No Mudar" - } - - "Current Map Stays" - { - "pt" "O mapa atual continua! O Voto foi dado! (Recebidos {1}%% de {2} votos)" - } - - "Changed Next Map" - { - "pt" "Mudado o prximo mapa para \"{1}\"." - } - - "Runoff Vote Nextmap" - { - "pt" "Segundo turno de votos para o prximo mapa!" - } - - "Number Of Votes" - { - "pt" "Nmero de votos" - } - - "Custom" - { - "pt" "{1} (Custom)" - } - - "Revote Is Needed" - { - "pt" "Nenhum mapa recebeu mais que {1}%% de votos.\nEnto, qual mapa ganhar? Precisa de uma nova votao!" - } - - "Revote Warning" - { - "pt" "Segundo turno comea em: {1}s" - } - - "Vote Warning" - { - "pt" "Ateno! Votao para o prximo mapa comear em: {1}s" - } - - "Line One" - { - "pt" "Considere que mapa voc quer jogar..." - } - - "Line Two" - { - "pt" "...e no aperte os botes sem pensar ;-)" - } - - "Cannot Start Vote" - { - "pt" "Votao em progresso. Tentando novamente em {1}s." - } - - "Tie Vote" - { - "pt" "Os {1} mapas tiveram o mesmo nmero de votos.\nUma nova votao necessria!" - } - - "Custom Marked" - { - "pt" "*{1}" - } - +"Phrases" +{ + "Vote Nextmap" + { + "pt" "Vote para o prximo mapa!" + } + + "Nextmap Voting Started" + { + "pt" "Votao para o prximo mapa comeou." + } + + "Nextmap Voting Finished" + { + "pt" "A votao para o prximo mapa terminou. O prximo mapa ser {1}. (Recebidos {2}%% de {3} votos)" + } + + "Current Map Extended" + { + "pt" "A mapa atual foi estendido. (Recebidos {1}%% de {2} votos)" + } + + "Extend Map" + { + "pt" "Estender tempo do mapa atual" + } + + "Dont Change" + { + "pt" "No Mudar" + } + + "Current Map Stays" + { + "pt" "O mapa atual continua! O Voto foi dado! (Recebidos {1}%% de {2} votos)" + } + + "Changed Next Map" + { + "pt" "Mudado o prximo mapa para \"{1}\"." + } + + "Runoff Vote Nextmap" + { + "pt" "Segundo turno de votos para o prximo mapa!" + } + + "Number Of Votes" + { + "pt" "Nmero de votos" + } + + "Custom" + { + "pt" "{1} (Custom)" + } + + "Revote Is Needed" + { + "pt" "Nenhum mapa recebeu mais que {1}%% de votos.\nEnto, qual mapa ganhar? Precisa de uma nova votao!" + } + + "Revote Warning" + { + "pt" "Segundo turno comea em: {1}s" + } + + "Vote Warning" + { + "pt" "Ateno! Votao para o prximo mapa comear em: {1}s" + } + + "Line One" + { + "pt" "Considere que mapa voc quer jogar..." + } + + "Line Two" + { + "pt" "...e no aperte os botes sem pensar ;-)" + } + + "Cannot Start Vote" + { + "pt" "Votao em progresso. Tentando novamente em {1}s." + } + + "Tie Vote" + { + "pt" "Os {1} mapas tiveram o mesmo nmero de votos.\nUma nova votao necessria!" + } + + "Custom Marked" + { + "pt" "*{1}" + } + } \ No newline at end of file diff --git a/mapchooser_extended/translations/ru/mapchooser_extended.phrases.txt b/mapchooser_extended/translations/ru/mapchooser_extended.phrases.txt index 4a0d74d0..812d2903 100644 --- a/mapchooser_extended/translations/ru/mapchooser_extended.phrases.txt +++ b/mapchooser_extended/translations/ru/mapchooser_extended.phrases.txt @@ -1,83 +1,83 @@ -"Phrases" -{ - "Vote Nextmap" - { - "ru" "Голосование за следующую карту." - } - - "Runoff Vote Nextmap" - { - "ru" "Повторное голосование за карту." - } - - "Nextmap Voting Started" - { - "ru" "Голосование за следующую карту запущено." - } - - "Nextmap Voting Finished" - { - "ru" "Голосование за карту завершено. Следующей картой будет: {1}. (Получено {2}%% из {3} голосов(а))" - } - - "Current Map Extended" - { - "ru" "Текущая карта была продлена. (Получено {1}%% из {2} голосов(а))" - } - - "Extend Map" - { - "ru" "Продлить текущую карту." - } - - "Dont Change" - { - "ru" "Не менять карту." - } - - "Current Map Stays" - { - "ru" "Текущая карта не сменится! (Получено {1}%% из {2} голосов(а))" - } - - "Changed Next Map" - { - "ru" "Следующая карта изменена на \"{1}\"." - } - - "Number Of Votes" - { - "ru" "Количество голосов" - } - - "Custom" - { - "ru" "{1} (Случайная)" - } - - "Revote Is Needed" - { - "ru" "Ни одна карта не получила более, чем {1}%% голосов(а).\nИтак, какая карта будет следующей? Приготовьтесь к повторному голосованию!" - } - - "Revote Warning" - { - "ru" "Повторное голосование начнётся через: {1}s" - } - - "Vote Warning" - { - "ru" "Внимание! Голосование за следующую карту начнётся через: {1}s" - } - - "Line One" - { - "ru" "Выбери, какая карта будет следующей..." - } - - "Line Two" - { - "ru" "...и не клацай кнопки,не подумав ;-)" - } - +"Phrases" +{ + "Vote Nextmap" + { + "ru" "Голосование за следующую карту." + } + + "Runoff Vote Nextmap" + { + "ru" "Повторное голосование за карту." + } + + "Nextmap Voting Started" + { + "ru" "Голосование за следующую карту запущено." + } + + "Nextmap Voting Finished" + { + "ru" "Голосование за карту завершено. Следующей картой будет: {1}. (Получено {2}%% из {3} голосов(а))" + } + + "Current Map Extended" + { + "ru" "Текущая карта была продлена. (Получено {1}%% из {2} голосов(а))" + } + + "Extend Map" + { + "ru" "Продлить текущую карту." + } + + "Dont Change" + { + "ru" "Не менять карту." + } + + "Current Map Stays" + { + "ru" "Текущая карта не сменится! (Получено {1}%% из {2} голосов(а))" + } + + "Changed Next Map" + { + "ru" "Следующая карта изменена на \"{1}\"." + } + + "Number Of Votes" + { + "ru" "Количество голосов" + } + + "Custom" + { + "ru" "{1} (Случайная)" + } + + "Revote Is Needed" + { + "ru" "Ни одна карта не получила более, чем {1}%% голосов(а).\nИтак, какая карта будет следующей? Приготовьтесь к повторному голосованию!" + } + + "Revote Warning" + { + "ru" "Повторное голосование начнётся через: {1}s" + } + + "Vote Warning" + { + "ru" "Внимание! Голосование за следующую карту начнётся через: {1}s" + } + + "Line One" + { + "ru" "Выбери, какая карта будет следующей..." + } + + "Line Two" + { + "ru" "...и не клацай кнопки,не подумав ;-)" + } + } \ No newline at end of file diff --git a/mapchooser_extended/translations/sv/incomplete.txt b/mapchooser_extended/translations/sv/incomplete.txt index 9ec75619..20fcb48b 100644 --- a/mapchooser_extended/translations/sv/incomplete.txt +++ b/mapchooser_extended/translations/sv/incomplete.txt @@ -1,2 +1,2 @@ -Missing "Runoff Vote Nextmap", "Number Of Votes", "Custom", "Revote Is Needed", "Revote Warning", "Vote Warning", +Missing "Runoff Vote Nextmap", "Number Of Votes", "Custom", "Revote Is Needed", "Revote Warning", "Vote Warning", "Line One", "Line Two", "Cannot Start Vote", and "Tie Vote" \ No newline at end of file diff --git a/mapchooser_extended/translations/sv/mapchooser_extended.phrases.txt b/mapchooser_extended/translations/sv/mapchooser_extended.phrases.txt index d70eee86..65daf640 100644 --- a/mapchooser_extended/translations/sv/mapchooser_extended.phrases.txt +++ b/mapchooser_extended/translations/sv/mapchooser_extended.phrases.txt @@ -1,43 +1,43 @@ -"Phrases" -{ - "Vote Nextmap" - { - "sv" "Rösta för nästa bana!" - } - - "Nextmap Voting Started" - { - "sv" "Röstning om nästa bana har börjat." - } - - "Nextmap Voting Finished" - { - "sv" "Röstningen om banan har avslutats. Nästa bana kommer att bli {1}. (Fick {2}%% av {3} röster) " - } - - "Current Map Extended" - { - "sv" "Den nuvarande banan har förlängts. (Fick {1}%% av {2} röster) " - } - - "Extend Map" - { - "sv" "Förläng nuvarande bana" - } - - "Dont Change" - { - "sv" "Byt inte" - } - - "Current Map Stays" - { - "sv" "Nuvarande banan fortsätter! Röstningen har talat! (Fick {1}%% av {2} röster) " - } - - "Changed Next Map" - { - "sv" "Bytta nästa bana till \"{1}\". " - } - -} +"Phrases" +{ + "Vote Nextmap" + { + "sv" "Rösta för nästa bana!" + } + + "Nextmap Voting Started" + { + "sv" "Röstning om nästa bana har börjat." + } + + "Nextmap Voting Finished" + { + "sv" "Röstningen om banan har avslutats. Nästa bana kommer att bli {1}. (Fick {2}%% av {3} röster) " + } + + "Current Map Extended" + { + "sv" "Den nuvarande banan har förlängts. (Fick {1}%% av {2} röster) " + } + + "Extend Map" + { + "sv" "Förläng nuvarande bana" + } + + "Dont Change" + { + "sv" "Byt inte" + } + + "Current Map Stays" + { + "sv" "Nuvarande banan fortsätter! Röstningen har talat! (Fick {1}%% av {2} röster) " + } + + "Changed Next Map" + { + "sv" "Bytta nästa bana till \"{1}\". " + } + +} diff --git a/mapchooser_extended/translations/tr/incomplete.txt b/mapchooser_extended/translations/tr/incomplete.txt index 9ec75619..20fcb48b 100644 --- a/mapchooser_extended/translations/tr/incomplete.txt +++ b/mapchooser_extended/translations/tr/incomplete.txt @@ -1,2 +1,2 @@ -Missing "Runoff Vote Nextmap", "Number Of Votes", "Custom", "Revote Is Needed", "Revote Warning", "Vote Warning", +Missing "Runoff Vote Nextmap", "Number Of Votes", "Custom", "Revote Is Needed", "Revote Warning", "Vote Warning", "Line One", "Line Two", "Cannot Start Vote", and "Tie Vote" \ No newline at end of file diff --git a/mapchooser_extended/translations/tr/mapchooser_extended.phrases.txt b/mapchooser_extended/translations/tr/mapchooser_extended.phrases.txt index 270696cb..e8f27953 100644 --- a/mapchooser_extended/translations/tr/mapchooser_extended.phrases.txt +++ b/mapchooser_extended/translations/tr/mapchooser_extended.phrases.txt @@ -1,43 +1,43 @@ -"Phrases" -{ - "Vote Nextmap" - { - "tr" "Sonraki harita için oy ver!" - } - - "Nextmap Voting Started" - { - "tr" "Sonraki harita için oylama başladı." - } - - "Nextmap Voting Finished" - { - "tr" "Harita oylaması sona erdi. Sıradaki harita {1} olacak. ({3} oyun %%{2}'i alındı) " - } - - "Current Map Extended" - { - "tr" "Geçerli harita uzatıldı. ({2} oyun %%{1}'i alındı) " - } - - "Extend Map" - { - "tr" "Geçerli Haritayı Uzat" - } - - "Dont Change" - { - "tr" "Değiştirme" - } - - "Current Map Stays" - { - "tr" "Geçerli harita devam ediyor! Oylama konuştu! ({2} oyun %%{1}'i alındı) " - } - - "Changed Next Map" - { - "tr" "Sıradaki harita \"{1}\" olarak değiştirildi." - } - -} +"Phrases" +{ + "Vote Nextmap" + { + "tr" "Sonraki harita için oy ver!" + } + + "Nextmap Voting Started" + { + "tr" "Sonraki harita için oylama başladı." + } + + "Nextmap Voting Finished" + { + "tr" "Harita oylaması sona erdi. Sıradaki harita {1} olacak. ({3} oyun %%{2}'i alındı) " + } + + "Current Map Extended" + { + "tr" "Geçerli harita uzatıldı. ({2} oyun %%{1}'i alındı) " + } + + "Extend Map" + { + "tr" "Geçerli Haritayı Uzat" + } + + "Dont Change" + { + "tr" "Değiştirme" + } + + "Current Map Stays" + { + "tr" "Geçerli harita devam ediyor! Oylama konuştu! ({2} oyun %%{1}'i alındı) " + } + + "Changed Next Map" + { + "tr" "Sıradaki harita \"{1}\" olarak değiştirildi." + } + +}