diff --git a/addons/sourcemod/plugins/hana/l4d2_mixmap.smx b/addons/sourcemod/plugins/hana/l4d2_mixmap.smx index 4fd2dd231..5661118f4 100644 Binary files a/addons/sourcemod/plugins/hana/l4d2_mixmap.smx and b/addons/sourcemod/plugins/hana/l4d2_mixmap.smx differ diff --git a/addons/sourcemod/scripting/include/l4d2_mixmap.inc b/addons/sourcemod/scripting/include/l4d2_mixmap.inc new file mode 100644 index 000000000..629a1b6af --- /dev/null +++ b/addons/sourcemod/scripting/include/l4d2_mixmap.inc @@ -0,0 +1,67 @@ +/* + SourcePawn is Copyright (C) 2006-2008 AlliedModders LLC. All rights reserved. + SourceMod is Copyright (C) 2006-2008 AlliedModders LLC. All rights reserved. + Pawn and SMALL are Copyright (C) 1997-2008 ITB CompuPhase. + Source is Copyright (C) Valve Corporation. + All trademarks are property of their respective owners. + + This program is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + 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 . +*/ +#if defined _l4d2_mixmap_included_ + #endinput +#endif +#define _l4d2_mixmap_included_ + +/** + * @brief Called when plugin is about to load first mix map + * @note Right before loading first map + * + * @param iMapCount Total Mix Map Count + * @param sMapName First Mix Map Name + */ +forward void OnCMTStart(int iMapCount, const char[] sMapName); + +/** + * @brief Called when mix map is loaded + * @note Right after the map is loaded + * + * @param sMapName Name of Current Mix Map Loaded + */ +forward void OnCMTNextKnown(const char[] sMapName); + +/** + * @brief Called when last map is played and mixmap is finished + * @note Right after round ends or final wins + * + * @param sMapName Name of Current Mix Map Loaded + */ +forward void OnCMTEnd(); + + +public SharedPlugin __pl_l4d2_mixmap = +{ + name = "l4d2_mixmap", + file = "l4d2_mixmap.smx", +#if defined REQUIRE_PLUGIN + required = 1, +#else + required = 0, +#endif +}; + +#if !defined REQUIRE_PLUGIN +public void __pl_l4d2_mixmap_SetNTVOptional() +{ +} +#endif \ No newline at end of file diff --git a/addons/sourcemod/scripting/l4d2_mixmap.sp b/addons/sourcemod/scripting/l4d2_mixmap.sp index 855c8ec9b..22754cf75 100644 --- a/addons/sourcemod/scripting/l4d2_mixmap.sp +++ b/addons/sourcemod/scripting/l4d2_mixmap.sp @@ -1,32 +1,83 @@ +/** + * 來源: https://gitee.com/honghl5/open-source-plug-in/tree/main/l4d2_mixmap + * 別人寫的插件: 輸入!mixmap可以投票,通過後隨機切換五個關卡 + * 1. 可適用於戰役模式 + * 2. 支援L4D1 + */ + #pragma semicolon 1 #pragma newdecls required #include -#include #include -#include +#include #include -#include +#include +#include //https://github.com/fbef0102/Game-Private_Plugin/releases/tag/builtinvotes #undef REQUIRE_PLUGIN -#include -#undef REQUIRE_PLUGIN -#include -//#include +#tryinclude +#tryinclude + +#if !defined _readyup_included_ + native bool EditFooterStringAtIndex(int index, const char[] string); + native int AddStringToReadyFooter(const char[] footer); +#endif -#define SECTION_NAME "CTerrorGameRules::SetCampaignScores" -#define LEFT4FRAMEWORK_GAMEDATA "left4dhooks.l4d2" public Plugin myinfo = { - name = "l4d2_mixmap", - author = "Bred", - description = "Randomly select five maps for versus. Adding for fun and reference from CMT", - version = "2.5", - url = "https://gitee.com/honghl5/open-source-plug-in/tree/main/l4d2_mixmap" + name = "[L4D1/L4D2] Mix Map", + author = "Bred, Harry", + description = "Randomly select five maps for versus/coop/realism. Adding for fun", + version = "1.0h-2024/12/25", + url = "https://github.com/fbef0102/L4D1_2-Plugins/tree/master/l4d2_mixmap" }; -#define DIR_CFGS "mixmap/" -#define PATH_KV "cfg/mixmap/mapnames.txt" +Handle g_hForwardStart; +Handle g_hForwardNext; +Handle g_hForwardEnd; + +bool g_bL4D2Version; +public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) +{ + EngineVersion test = GetEngineVersion(); + + if( test == Engine_Left4Dead ) + { + g_bL4D2Version = false; + } + else if( test == Engine_Left4Dead2 ) + { + g_bL4D2Version = true; + } + else + { + strcopy(error, err_max, "Plugin only supports Left 4 Dead 1 & 2."); + return APLRes_SilentFailure; + } + + MarkNativeAsOptional("EditFooterStringAtIndex"); + MarkNativeAsOptional("AddStringToReadyFooter"); + + // Right before loading first map; params: 1 = maplist size; 2 = name of first map + g_hForwardStart = CreateGlobalForward("OnCMTStart", ET_Ignore, Param_Cell, Param_String ); + // After loading a map (to let other plugins know what the next map will be ahead of time); 1 = name of next map + g_hForwardNext = CreateGlobalForward("OnCMTNextKnown", ET_Ignore, Param_String ); + // After last map is played; no params + g_hForwardEnd = CreateGlobalForward("OnCMTEnd", ET_Ignore ); + + RegPluginLibrary("l4d2_mixmap"); + + return APLRes_Success; +} + +#define SECTION_NAME "CTerrorGameRules::SetCampaignScores" +#define LEFT4FRAMEWORK_GAMEDATA_L4D2 "left4dhooks.l4d2" +#define LEFT4FRAMEWORK_GAMEDATA_L4D1 "left4dhooks.l4d1" + +#define DIR_CFGS_L4D2 "mixmap/" +#define DIR_CFGS_L4D1 "l4d2_mixmap/l4d1/" +#define PATH_KV "mapnames.txt" #define CFG_DEFAULT "default" #define CFG_DODEFAULT "disorderdefault" #define CFG_DODEFAULT_ST "do" @@ -38,185 +89,167 @@ public Plugin myinfo = #define CFG_UNOF_ST "uof" #define CFG_DOUNOF "disorderunofficial" #define CFG_DOUNOF_ST "douof" -#define BUF_SZ 64 +#define BUF_SZ 128 ConVar g_cvNextMapPrint, g_cvMaxMapsNum, - g_cvFinaleEndStart; + g_cvFinaleEndCoop, + g_cvFinaleEndVersus; char cfg_exec[BUF_SZ]; -Handle hVoteMixmap; -Handle hVoteStopMixmap; - //与随机抽签相关的变量 -Handle g_hArrayTags; // Stores tags for indexing g_hTriePools 存放地图池标签 -Handle g_hTriePools; // Stores pool array handles by tag name 存放由标签分类的地图 -Handle g_hArrayTagOrder; // Stores tags by rank 存放标签顺序 -Handle g_hArrayMapOrder; // Stores finalised map list in order 存放抽取完成后的地图顺序 - +ArrayList g_hArrayTags; // Stores tags for indexing g_hTriePools 存放地图池标签 +StringMap g_hTriePools; // Stores pool array handles by tag name 存放由标签分类的地图, tag - ArrayList +ArrayList g_hArrayTagOrder; // Stores tags by rank 存放标签顺序 +ArrayList g_hArrayMapOrder; // Stores finalised map list in order 存放抽取完成后的地图顺序 +KeyValues g_hKvMapNames; bool g_bMaplistFinalized; bool g_bMapsetInitialized; int g_iMapsPlayed; int g_iMapCount; -int - g_iPointsTeam_A = 0, - g_iPointsTeam_B = 0; +bool + g_bServerForceStart = false; -//bool bLeftStartArea; -//bool bReadyUpAvailable; -bool g_bCMapTransitioned = false, - g_bServerForceStart = false; +Handle g_hCountDownTimer; -Handle g_hForwardStart; -Handle g_hForwardNext; -Handle g_hForwardEnd; +bool + g_bRoundIsLive, + g_bReadyUpFooterAdded, + g_bDifferAbort; -Handle g_hCountDownTimer; -Handle g_hCMapSetCampaignScores; +Handle + g_hUpdateFooterTimer; + +char + g_slandmarkName[128]; + +int + g_iReadyUpFooterIndex, + g_iRoundStart, + g_iPlayerSpawn; // ---------------------------------------------------------- // Setup // ---------------------------------------------------------- -public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) -{ - // Right before loading first map; params: 1 = maplist size; 2 = name of first map - g_hForwardStart = CreateGlobalForward("OnCMTStart", ET_Ignore, Param_Cell, Param_String ); - // After loading a map (to let other plugins know what the next map will be ahead of time); 1 = name of next map - g_hForwardNext = CreateGlobalForward("OnCMTNextKnown", ET_Ignore, Param_String ); - // After last map is played; no params - g_hForwardEnd = CreateGlobalForward("OnCMTEnd", ET_Ignore ); - - MarkNativeAsOptional("PLAYSTATS_BroadcastRoundStats"); - MarkNativeAsOptional("PLAYSTATS_BroadcastGameStats"); - - return APLRes_Success; -} - public void OnPluginStart() { - LoadSDK(); - - g_cvNextMapPrint = CreateConVar("l4d2mm_nextmap_print", "1", "Determine whether to show what the next map will be", _, true, 0.0, true, 1.0); - g_cvMaxMapsNum = CreateConVar("l4d2mm_max_maps_num", "2", "Determine how many maps of one campaign can be selected; 0 = no limits;", _, true, 0.0, true, 5.0); - g_cvFinaleEndStart = CreateConVar("l4d2mm_finale_end_start", "1", "Determine whether to remixmap in the end of finale; 0 = disable;1 = enable", _, true, 0.0, true, 1.0); - - //Servercmd 服务器指令(用于cfg文件) - RegServerCmd("sm_addmap", Command_AddMap, "Add a map to the maplist under specified tags"); - RegServerCmd("sm_tagrank", Command_TagRank, "Set tag rank for map selection"); - - RegAdminCmd("sm_manualmixmap", Command_ManualMixmap, ADMFLAG_ROOT, "Start mixmap with specified maps 启用mixmap加载特定地图顺序的地图组"); - RegAdminCmd("sm_fmixmap", Command_ForceMixmap, ADMFLAG_ROOT, "Force start mixmap (arg1 empty for 'default' maps pool) 强制启用mixmap(随机官方地图)"); - RegConsoleCmd("sm_mixmap", Command_Mixmap, "Vote to start a mixmap (arg1 empty for 'default' maps pool);通过投票启用Mixmap,并可加载特定的地图池;无参数则启用官图顺序随机"); - RegConsoleCmd("sm_stopmixmap", Command_StopMixmap, "Stop a mixmap;中止mixmap,并初始化地图列表"); - RegAdminCmd("sm_fstopmixmap", Command_ForceStopMixmap, ADMFLAG_ROOT, "Force stop a mixmap ;强制中止mixmap,并初始化地图列表"); + LoadTranslations("l4d2_mixmap.phrases"); - RegConsoleCmd("sm_maplist", Command_Maplist, "Show the map list; 展示mixmap最终抽取出的地图列表"); - RegAdminCmd("sm_allmap", Command_ShowAllMaps, ADMFLAG_ROOT, "Show all official maps code; 展示所有官方地图的地图代码"); - RegAdminCmd("sm_allmaps", Command_ShowAllMaps, ADMFLAG_ROOT, "Show all official maps code; 展示所有官方地图的地图代码"); + g_cvNextMapPrint = CreateConVar("l4d2mm_nextmap_print", "1", "If 1, show what the next map will be", FCVAR_NOTIFY, true, 0.0, true, 1.0); + g_cvMaxMapsNum = CreateConVar("l4d2mm_max_maps_num", "2", "Determine how many maps of one campaign can be selected; 0 = no limits;", FCVAR_NOTIFY, true, 0.0, true, 5.0); + g_cvFinaleEndCoop = CreateConVar("l4d2mm_finale_end_coop", "0", "If 1, auto force start mixmap in the end of finale in coop/realism mode (When mixmap is alreaedy on)", FCVAR_NOTIFY, true, 0.0, true, 1.0); + g_cvFinaleEndVersus = CreateConVar("l4d2mm_finale_end_verus", "0", "If 1, auto force start mixmap in the end of finale in versus mode (When mixmap is alreaedy on)", FCVAR_NOTIFY, true, 0.0, true, 1.0); + //AutoExecConfig(true, "l4d2_mixmap"); - // HookEvent("player_left_start_area", LeftStartArea_Event, EventHookMode_PostNoCopy); - // HookEvent("round_start", RoundStart_Event, EventHookMode_PostNoCopy); - + //Servercmd 服务器指令(用于cfg文件) + RegServerCmd( "sm_addmap", AddMap, "Add a chatper and tag 新增關卡名稱與標記, Usage: sm_addmap "); + RegServerCmd( "sm_tagrank", TagRank, "Define map order 決定標記的地圖順序, Usage: sm_addmap , number starting from 0"); + + //Start/Stop 启用/中止指令 + RegAdminCmd( "sm_manualmixmap", ManualMixmap, ADMFLAG_ROOT, "Start mixmap with specified maps 启用mixmap加载特定地图顺序的地图组"); + RegAdminCmd( "sm_fmixmap", ForceMixmap, ADMFLAG_ROOT, "Force start mixmap (arg1 empty for 'default' maps pool) 强制启用mixmap(随机官方地图)"); + RegConsoleCmd( "sm_mixmap", Mixmap_Cmd, "Vote to start a mixmap (arg1 empty for 'default' maps pool);通过投票启用Mixmap,并可加载特定的地图池;无参数则启用官图顺序随机"); + RegConsoleCmd( "sm_stopmixmap", StopMixmap_Cmd, "Vote to Stop a mixmap;中止mixmap,并初始化地图列表"); + RegAdminCmd( "sm_fstopmixmap", StopMixmap, ADMFLAG_ROOT, "Force stop a mixmap ;强制中止mixmap,并初始化地图列表"); + + //Midcommand 插件启用后可使用的指令 + RegConsoleCmd( "sm_maplist", MixMaplist, "Show the mix map list; 展示mixmap最终抽取出的地图列表"); + //RegAdminCmd( "sm_allmap", ShowAllMaps, ADMFLAG_ROOT, "Show all official maps code; 展示所有官方地图的地图代码"); + //RegAdminCmd( "sm_allmaps", ShowAllMaps, ADMFLAG_ROOT, "Show all official maps code; 展示所有官方地图的地图代码"); + + HookEvent("round_start", Event_RoundStart, EventHookMode_PostNoCopy); + HookEvent("player_spawn", Event_PlayerSpawn); + HookEvent("round_end", Event_RoundEnd, EventHookMode_PostNoCopy); //trigger twice in versus/survival/scavenge mode, one when all survivors wipe out or make it to saferom, one when first round ends (second round_start begins). + HookEvent("map_transition", Event_RoundEnd, EventHookMode_PostNoCopy); //1. all survivors make it to saferoom in and server is about to change next level in coop mode (does not trigger round_end), 2. all survivors make it to saferoom in versus + HookEvent("mission_lost", Event_RoundEnd, EventHookMode_PostNoCopy); //all survivors wipe out in coop mode (also triggers round_end) + HookEvent("finale_vehicle_leaving", Event_RoundEnd, EventHookMode_PostNoCopy); //final map final rescue vehicle leaving (does not trigger round_end) + + HookEvent("map_transition", map_transition, EventHookMode_PostNoCopy); //1. all survivors make it to saferoom in and server is about to change next level in coop mode (does not trigger round_end), 2. all survivors make it to saferoom in versus + HookEvent("finale_win", finale_win); PluginStartInit(); - LoadTranslations("l4d2_mixmap.phrases"); - //AutoExecConfig(true, "l4d2_mixmap"); } -void PluginStartInit() -{ - g_hArrayTags = CreateArray(BUF_SZ/4); //1 block = 4 characters => X characters = X/4 blocks - g_hTriePools = CreateTrie(); - g_hArrayTagOrder = CreateArray(BUF_SZ/4); - g_hArrayMapOrder = CreateArray(BUF_SZ/4); - - g_bMapsetInitialized = false; - g_bMaplistFinalized = false; +bool + g_ReadyUpAvailable; - g_hCountDownTimer = null; - - g_iMapsPlayed = 0; - g_iMapCount = 0; +public void OnAllPluginsLoaded() +{ + g_ReadyUpAvailable = LibraryExists("readyup"); } -void LoadSDK() +public void OnLibraryRemoved(const char[] name) { - Handle hGameData = LoadGameConfigFile(LEFT4FRAMEWORK_GAMEDATA); - if (hGameData == null) { - SetFailState("Could not load gamedata/%s.txt", LEFT4FRAMEWORK_GAMEDATA); - } - - StartPrepSDKCall(SDKCall_GameRules); - if (!PrepSDKCall_SetFromConf(hGameData, SDKConf_Signature, SECTION_NAME)) { - SetFailState("Function '%s' not found.", SECTION_NAME); - } - - PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); - PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); - g_hCMapSetCampaignScores = EndPrepSDKCall(); - - if (g_hCMapSetCampaignScores == null) { - SetFailState("Function '%s' found, but something went wrong.", SECTION_NAME); - } - - delete hGameData; + g_ReadyUpAvailable = LibraryExists("readyup"); } +public void OnLibraryAdded(const char[] name) +{ + g_ReadyUpAvailable = LibraryExists("readyup"); +} -// ---------------------------------------------------------- -// Hooks -// ---------------------------------------------------------- // Otherwise nextmap would be stuck and people wouldn't be able to play normal campaigns without the plugin 结束后初始化sm_nextmap的值 -public void OnPluginEnd() { +public void OnPluginEnd() +{ ServerCommand("sm_nextmap ''"); + ClearDefault(); } -public void OnClientPutInServer(int client) -{ - if (g_bMapsetInitialized) - { - CreateTimer(10.0, Timer_ShowMaplist, client);//玩家加入服务器后,10s后提示正在使用mixmap插件。 - } -} +// Sourcemod API Forward------------------------------- -public Action Timer_ShowMaplist(Handle timer, int client) +public void OnMapStart() { - if (IsClientInGame(client)) + g_bDifferAbort = false; + + delete g_hKvMapNames; + g_hKvMapNames = new KeyValues("Mixmap Map Names"); + char sbuffer[BUF_SZ]; + if(g_bL4D2Version) Format(sbuffer, sizeof(sbuffer), "cfg/%s%s", DIR_CFGS_L4D2, PATH_KV); + else Format(sbuffer, sizeof(sbuffer), "cfg/%s%s", DIR_CFGS_L4D1, PATH_KV); + + if (!FileToKeyValues(g_hKvMapNames, sbuffer)) { - CPrintToChat(client, "%t", "Auto_Show_Maplist"); + LogError("Couldn't create KV for map names: %s", sbuffer); + g_hKvMapNames = null; + return; } - - return Plugin_Handled; -} -public void OnMapStart() { - - if (g_bCMapTransitioned) { - CreateTimer(1.0, Timer_OnMapStartDelay, _, TIMER_FLAG_NO_MAPCHANGE); //Clients have issues connecting if team swap happens exactly on map start, so we delay it - g_bCMapTransitioned = false; + if(!g_bL4D2Version) + { + PrecacheSound("ui/menu_enter05.wav"); + PrecacheSound("ui/beep_synthtone01.wav"); + PrecacheSound("ui/beep_error01.wav"); } ServerCommand("sm_nextmap ''"); - + char buffer[BUF_SZ]; - + //判断currentmap与预计的map的name是否一致,如果不一致就stopmixmap if (g_bMapsetInitialized) { + if(g_iMapsPlayed >= g_iMapCount) + { + PluginStartInit(); + return; + } + char OriginalSetMapName[BUF_SZ]; GetCurrentMap(buffer, BUF_SZ); - GetArrayString(g_hArrayMapOrder, g_iMapsPlayed, OriginalSetMapName, BUF_SZ); - - if (! StrEqual(buffer,OriginalSetMapName) && g_bMaplistFinalized) + g_hArrayMapOrder.GetString(g_iMapsPlayed, OriginalSetMapName, BUF_SZ); + + if (!StrEqual(buffer,OriginalSetMapName) && g_bMaplistFinalized) { PluginStartInit(); - CPrintToChatAll("%t", "Differ_Abort"); + + g_bDifferAbort = true; + CreateTimer(60.0, Timer_DifferAbort, _, TIMER_FLAG_NO_MAPCHANGE); return; } } @@ -226,132 +259,67 @@ public void OnMapStart() { return; } - GetArrayString(g_hArrayMapOrder, g_iMapsPlayed+1, buffer, BUF_SZ); + g_hArrayMapOrder.GetString(g_iMapsPlayed+1, buffer, BUF_SZ); + + CreateTimer(5.0, Timer_FindInfoChangelevel, _, TIMER_FLAG_NO_MAPCHANGE); Call_StartForward(g_hForwardNext); Call_PushString(buffer); Call_Finish(); } -public Action Timer_OnMapStartDelay(Handle hTimer) +public void OnMapEnd() { - SetScores(); - - return Plugin_Handled; + delete g_hUpdateFooterTimer; + ClearDefault(); } -void SetScores() -{ - //If team B is winning, swap teams. Does not change how scores are set - if (g_iPointsTeam_A < g_iPointsTeam_B) { - L4D2_SwapTeams(); - } - - //Set scores on scoreboard - SDKCall(g_hCMapSetCampaignScores, g_iPointsTeam_A, g_iPointsTeam_B); +public void OnClientPutInServer(int client) +{ + if(IsFakeClient(client)) return; - //Set actual scores - L4D2Direct_SetVSCampaignScore(0, g_iPointsTeam_A); - L4D2Direct_SetVSCampaignScore(1, g_iPointsTeam_B); + CreateTimer(10.0, Timer_ShowMaplist, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE);//玩家加入服务器后,10s后提示正在使用mixmap插件。 } -public void L4D2_OnEndVersusModeRound_Post() +/*public void OnEntityCreated(int entity, const char[] classname) { - if (InSecondHalfOfRound() && g_bMapsetInitialized) - { - PerformMapProgression(); + if (!g_bMapsetInitialized || !IsValidEntityIndex(entity)) return; - } - return; -} -// ---------------------------------------------------------- -// Map switching logic -// ---------------------------------------------------------- - -stock Action PerformMapProgression() -{ - if (++g_iMapsPlayed < g_iMapCount) + switch (classname[0]) { - GotoNextMap(false); - return Plugin_Handled; + case 'i': + { + if (StrEqual(classname, "info_landmark")) + { + RemoveEntity(entity); + } + } } - else if (g_cvFinaleEndStart.IntValue) - CreateTimer(9.0, Timed_ContinueMixmap); - - Call_StartForward(g_hForwardEnd); - Call_Finish(); - - return Plugin_Handled; -} - -void GotoNextMap(bool force = false) -{ - char sMapName[BUF_SZ]; - GetArrayString(g_hArrayMapOrder, g_iMapsPlayed, sMapName, BUF_SZ); - - GotoMap(sMapName, force); -} +}*/ -void GotoMap(const char[] sMapName, bool force = false) -{ - if (force) - { - ForceChangeLevel(sMapName, "Mixmap"); - return; - } - ServerCommand("sm_nextmap %s", sMapName); - CreateTimer(5.0, Timed_NextMapInfo); -} +// Command------------------------------- -public Action Timed_NextMapInfo(Handle timer) +Action Timer_ShowMaplist(Handle timer, int client) { - char sMapName_New[BUF_SZ], sMapName_Old[BUF_SZ]; - GetArrayString(g_hArrayMapOrder, g_iMapsPlayed, sMapName_New, BUF_SZ); - GetArrayString(g_hArrayMapOrder, g_iMapsPlayed - 1, sMapName_Old, BUF_SZ); - - g_cvNextMapPrint.IntValue ? CPrintToChatAll("%t", "Show_Next_Map", sMapName_New) : CPrintToChatAll("%t%t", "Show_Next_Map", "", "Secret"); - - if ((StrEqual(sMapName_Old, "c6m2_bedlam") && !StrEqual(sMapName_New, "c7m1_docks")) || (StrEqual(sMapName_Old, "c9m2_lots") && !StrEqual(sMapName_New, "c14m1_junkyard"))) + client = GetClientOfUserId(client); + if (client && IsClientInGame(client)) { - g_iPointsTeam_A = L4D2Direct_GetVSCampaignScore(0); - g_iPointsTeam_B = L4D2Direct_GetVSCampaignScore(1); - g_bCMapTransitioned = true; - CreateTimer(9.0, Timed_Gotomap); //this command must set ahead of the l4d2_map_transition plugin setting. Otherwise the map will be c7m1_docks/c14m1_junkyard after c6m2_bedlam/c9m2_lots - } - else if ((!StrEqual(sMapName_Old, "c6m2_bedlam") && StrEqual(sMapName_New, "c7m1_docks")) || (!StrEqual(sMapName_Old, "c9m2_lots") && StrEqual(sMapName_New, "c14m1_junkyard"))) - { - g_iPointsTeam_A = L4D2Direct_GetVSCampaignScore(0); - g_iPointsTeam_B = L4D2Direct_GetVSCampaignScore(1); - g_bCMapTransitioned = true; - CreateTimer(10.0, Timed_Gotomap); //this command must set ahead of the l4d2_map_transition plugin setting. Otherwise the map will be c7m1_docks/c14m1_junkyard after c6m2_bedlam/c9m2_lots + if(g_bDifferAbort) + { + CPrintToChat(client, "%T", "Differ_Abort", client); + } + else if(g_bMapsetInitialized) + { + CPrintToChat(client, "%T", "Auto_Show_Maplist", client); + } } return Plugin_Handled; } -public Action Timed_Gotomap(Handle timer) -{ - char sMapName_New[BUF_SZ]; - GetArrayString(g_hArrayMapOrder, g_iMapsPlayed, sMapName_New, BUF_SZ); - - GotoMap(sMapName_New, true); - return Plugin_Handled; -} - -public Action Timed_ContinueMixmap(Handle timer) -{ - ServerCommand("sm_fmixmap %s", cfg_exec); - return Plugin_Handled; -} - - -// ---------------------------------------------------------- -// Commands: Console/Admin -// ---------------------------------------------------------- - // Loads a specified set of maps -public Action Command_ForceMixmap(int client, int args) +Action ForceMixmap(int client, any args) { Format(cfg_exec, sizeof(cfg_exec), CFG_DEFAULT); @@ -360,7 +328,8 @@ public Action Command_ForceMixmap(int client, int args) char sbuffer[BUF_SZ]; char arg[BUF_SZ]; GetCmdArg(1, arg, BUF_SZ); - Format(sbuffer, sizeof(sbuffer), "cfg/%s%s.cfg", DIR_CFGS, arg); + if(g_bL4D2Version) Format(sbuffer, sizeof(sbuffer), "cfg/%s%s.cfg", DIR_CFGS_L4D2, arg); + else Format(sbuffer, sizeof(sbuffer), "cfg/%s%s.cfg", DIR_CFGS_L4D1, arg); if (FileExists(sbuffer)) Format(cfg_exec, sizeof(cfg_exec), arg); else { @@ -376,7 +345,7 @@ public Action Command_ForceMixmap(int client, int args) Format(cfg_exec, sizeof(cfg_exec), CFG_DOUNOF); else { - CReplyToCommand(client, "%t", "Invalid_Cfg"); + CReplyToCommand(client, "%T", "Invalid_Cfg", client); return Plugin_Handled; } } @@ -384,19 +353,21 @@ public Action Command_ForceMixmap(int client, int args) if (client) CPrintToChatAllEx(client, "%t", "Force_Start", client, cfg_exec); PluginStartInit(); if (client == 0) g_bServerForceStart = true; - ServerCommand("exec %s%s.cfg", DIR_CFGS, cfg_exec); + if(g_bL4D2Version) ServerCommand("exec %s%s.cfg", DIR_CFGS_L4D2, cfg_exec); + else ServerCommand("exec %s%s.cfg", DIR_CFGS_L4D1, cfg_exec); + g_bMapsetInitialized = true; - CreateTimer(0.1, Timed_PostMapSet); + CreateTimer(0.1, Timed_PostMapSet, _, TIMER_FLAG_NO_MAPCHANGE); return Plugin_Handled; } // Load a specified set of maps -public Action Command_ManualMixmap(int client, int args) +Action ManualMixmap(int client, any args) { if (args < 1) { - CPrintToChat(client, "%t", "Manualmixmap_Syntax"); + CPrintToChat(client, "%T", "Manualmixmap_Syntax", client); } PluginStartInit(); @@ -409,50 +380,62 @@ public Action Command_ManualMixmap(int client, int args) ServerCommand("sm_tagrank %d %d", i, i-1); } g_bMapsetInitialized = true; - CreateTimer(0.1, Timed_PostMapSet); + CreateTimer(0.1, Timed_PostMapSet, _, TIMER_FLAG_NO_MAPCHANGE); return Plugin_Handled; } - -public Action Command_ShowAllMaps(int client, int args) +/* +Action ShowAllMaps(int client, any Args) { - CPrintToChat(client, "%t", "AllMaps_Official"); - CPrintToChat(client, "c1m1_hotel,c1m2_streets,c1m3_mall,c1m4_atrium"); - CPrintToChat(client, "c2m1_highway,c2m2_fairgrounds,c2m3_coaster,c2m4_barns,c2m5_concert"); - CPrintToChat(client, "c3m1_plankcountry,c3m2_swamp,c3m3_shantytown,c3m4_plantation"); - CPrintToChat(client, "c4m1_milltown_a,c4m2_sugarmill_a,c4m3_sugarmill_b,c4m4_milltown_b,c4m5_milltown_escape"); - CPrintToChat(client, "c5m1_waterfront,c5m2_park,c5m3_cemetery,c5m4_quarter,c5m5_bridge"); - CPrintToChat(client, "c6m1_riverbank,c6m2_bedlam,c7m1_docks,c7m2_barge,c7m3_port"); - CPrintToChat(client, "c8m1_apartment,c8m2_subway,c8m3_sewers,c8m4_interior,c8m5_rooftop"); - CPrintToChat(client, "c9m1_alleys,c9m2_lots,c14m1_junkyard,c14m2_lighthouse"); - CPrintToChat(client, "c10m1_caves,c10m2_drainage,c10m3_ranchhouse,c10m4_mainstreet,c10m5_houseboat"); - CPrintToChat(client, "c11m1_greenhouse,c11m2_offices,c11m3_garage,c11m4_terminal,c11m5_runway"); - CPrintToChat(client, "c12m1_hilltop,c12m2_traintunnel,c12m3_bridge,c12m4_barn,c12m5_cornfield"); - CPrintToChat(client, "c13m1_alpinecreek,c13m2_southpinestream,c13m3_memorialbridge,c13m4_cutthroatcreek"); - CPrintToChat(client, "%t", "AllMaps_Usage"); - - return Plugin_Handled; -} - + CPrintToChat(client, "%T", "AllMaps_Official", client); -// ---------------------------------------------------------- -// Commands: Client -// ---------------------------------------------------------- + if(g_bL4D2Version) + { + CPrintToChat(client, "c1m1_hotel,c1m2_streets,c1m3_mall,c1m4_atrium"); + CPrintToChat(client, "c2m1_highway,c2m2_fairgrounds,c2m3_coaster,c2m4_barns,c2m5_concert"); + CPrintToChat(client, "c3m1_plankcountry,c3m2_swamp,c3m3_shantytown,c3m4_plantation"); + CPrintToChat(client, "c4m1_milltown_a,c4m2_sugarmill_a,c4m3_sugarmill_b,c4m4_milltown_b,c4m5_milltown_escape"); + CPrintToChat(client, "c5m1_waterfront,c5m2_park,c5m3_cemetery,c5m4_quarter,c5m5_bridge"); + CPrintToChat(client, "c6m1_riverbank,c6m2_bedlam,c7m1_docks,c7m2_barge,c7m3_port"); + CPrintToChat(client, "c8m1_apartment,c8m2_subway,c8m3_sewers,c8m4_interior,c8m5_rooftop"); + CPrintToChat(client, "c9m1_alleys,c9m2_lots,c14m1_junkyard,c14m2_lighthouse"); + CPrintToChat(client, "c10m1_caves,c10m2_drainage,c10m3_ranchhouse,c10m4_mainstreet,c10m5_houseboat"); + CPrintToChat(client, "c11m1_greenhouse,c11m2_offices,c11m3_garage,c11m4_terminal,c11m5_runway"); + CPrintToChat(client, "c12m1_hilltop,c12m2_traintunnel,c12m3_bridge,c12m4_barn,c12m5_cornfield"); + CPrintToChat(client, "c13m1_alpinecreek,c13m2_southpinestream,c13m3_memorialbridge,c13m4_cutthroatcreek"); + } + else + { + CPrintToChat(client, "l4d_vs_hospital01_apartment,l4d_vs_hospital02_subway,l4d_vs_hospital03_sewers,l4d_vs_hospital04_interior,l4d_vs_hospital05_rooftop"); + CPrintToChat(client, "l4d_vs_smalltown01_caves,l4d_vs_smalltown02_drainage,l4d_vs_smalltown03_ranchhouse,l4d_vs_smalltown04_mainstreet,l4d_vs_smalltown05_houseboat"); + CPrintToChat(client, "l4d_vs_airport01_greenhouse,l4d_vs_airport02_offices,l4d_vs_airport03_garage,l4d_vs_airport04_terminal,l4d_vs_airport05_runway"); + CPrintToChat(client, "l4d_vs_farm01_hilltop,l4d_vs_farm02_traintunnel,l4d_vs_farm03_bridge,l4d_vs_farm04_barn,l4d_vs_farm05_cornfield"); + CPrintToChat(client, "l4d_garage01_alleys,l4d_garage02_lots"); + CPrintToChat(client, "l4d_river01_docks,l4d_river02_barge,l4d_river03_port"); + } -/* public void RoundStart_Event(Event event, const char[] name, bool dontBroadcast) -{ - bLeftStartArea = false; + CPrintToChat(client, "%T", "AllMaps_Usage", client); + + return Plugin_Handled; } +*/ -public void LeftStartArea_Event(Event event, const char[] name, bool dontBroadcast) -{ - bLeftStartArea = true; -} */ - -public Action Command_Mixmap(int client, int args) +Action Mixmap_Cmd(int client, any args) { if (IsClientAndInGame(client)) { + if(g_ReadyUpAvailable && g_bRoundIsLive) + { + CPrintToChat(client, "%T", "Round is live", client); + return Plugin_Handled; + } + + if(GetClientTeam(client) <= 1) + { + CPrintToChat(client, "%T", "Spectator Blocked", client); + return Plugin_Handled; + } + if (!IsBuiltinVoteInProgress()) { Format(cfg_exec, sizeof(cfg_exec), CFG_DEFAULT); @@ -462,7 +445,8 @@ public Action Command_Mixmap(int client, int args) char sbuffer[BUF_SZ]; char arg[BUF_SZ]; GetCmdArg(1, arg, BUF_SZ); - Format(sbuffer, sizeof(sbuffer), "cfg/%s%s.cfg", DIR_CFGS, arg); + if(g_bL4D2Version) Format(sbuffer, sizeof(sbuffer), "cfg/%s%s.cfg", DIR_CFGS_L4D2, arg); + else Format(sbuffer, sizeof(sbuffer), "cfg/%s%s.cfg", DIR_CFGS_L4D1, arg); if (FileExists(sbuffer)) Format(cfg_exec, sizeof(cfg_exec), arg); else { @@ -478,7 +462,7 @@ public Action Command_Mixmap(int client, int args) Format(cfg_exec, sizeof(cfg_exec), CFG_DOUNOF); else { - CPrintToChat(client, "%t", "Invalid_Cfg"); + CPrintToChat(client, "%T", "Invalid_Cfg", client); return Plugin_Handled; } } @@ -495,22 +479,23 @@ public Action Command_Mixmap(int client, int args) iPlayers[iNumPlayers++] = i; } - char cVoteTitle[32]; + char cVoteTitle[64]; Format(cVoteTitle, sizeof(cVoteTitle), "%T", "Cvote_Title", LANG_SERVER, cfg_exec); - hVoteMixmap = CreateBuiltinVote(VoteMixmapActionHandler, BuiltinVoteType_Custom_YesNo, BuiltinVoteAction_Cancel | BuiltinVoteAction_VoteEnd | BuiltinVoteAction_End); + Handle hVote = CreateBuiltinVote(VoteActionHandler, BuiltinVoteType_Custom_YesNo, BuiltinVoteAction_Cancel | BuiltinVoteAction_VoteEnd | BuiltinVoteAction_End); - SetBuiltinVoteArgument(hVoteMixmap, cVoteTitle); - SetBuiltinVoteInitiator(hVoteMixmap, client); - SetBuiltinVoteResultCallback(hVoteMixmap, VoteMixmapResultHandler); - DisplayBuiltinVote(hVoteMixmap, iPlayers, iNumPlayers, 20); + SetBuiltinVoteArgument(hVote, cVoteTitle); + SetBuiltinVoteInitiator(hVote, client); + SetBuiltinVoteResultCallback(hVote, VoteMixmapResultHandler); + DisplayBuiltinVote(hVote, iPlayers, iNumPlayers, 20); CPrintToChatAllEx(client, "%t", "Start_Mixmap", client, cfg_exec); - FakeClientCommand(client, "Vote Yes"); + if(g_bL4D2Version) FakeClientCommand(client, "Vote Yes"); + if(!g_bL4D2Version) EmitSoundToAll("ui/beep_synthtone01.wav"); } else { - PrintToChat(client, "%t", "Vote_Progress"); + CPrintToChat(client, "%T", "Vote_Blocked", client); } return Plugin_Handled; @@ -519,91 +504,104 @@ public Action Command_Mixmap(int client, int args) return Plugin_Continue; } -public void VoteMixmapActionHandler(Handle vote, BuiltinVoteAction action, int param1, int param2) +// Specifiy a rank for a given tag +Action TagRank(any args) { - switch (action) + if (args < 2) { - case BuiltinVoteAction_End: - { - hVoteMixmap = null; - CloseHandle(vote); - } - case BuiltinVoteAction_Cancel: - { - DisplayBuiltinVoteFail(vote, view_as(param1)); - } -/* case BuiltinVoteAction_Select: - { - char cItemVal[64]; - char cItemName[64]; - GetBuiltinVoteItem(vote, param2, cItemVal, sizeof(cItemVal), cItemName, sizeof(cItemName)); - } */ + ReplyToCommand(0, "Syntax: sm_tagrank "); + ReplyToCommand(0, "Sets tag as the tag to be used to fetch maps for map in the map list."); + ReplyToCommand(0, "Rank 0 is map 1, rank 1 is map 2, etc."); + + return Plugin_Handled; } -} -public void VoteMixmapResultHandler(Handle vote, int num_votes, int num_clients, const int[][] client_info, int num_items, const int[][] item_info) -{ - for (int i = 0; i < num_items; i++) + char buffer[BUF_SZ]; + GetCmdArg(2, buffer, BUF_SZ); + int index = StringToInt(buffer); + + GetCmdArg(1, buffer, BUF_SZ); + + if (index >= g_hArrayTagOrder.Length) { - if (item_info[i][BUILTINVOTEINFO_ITEM_INDEX] == BUILTINVOTES_VOTE_YES) - { - if (item_info[i][BUILTINVOTEINFO_ITEM_VOTES] > (num_clients / 2)) - { - if (vote == hVoteMixmap) - { - char cExecTitle[32]; - Format(cExecTitle, sizeof(cExecTitle), "%T", "Cexec_Title", LANG_SERVER); - DisplayBuiltinVotePass(vote, cExecTitle); - if (g_hCountDownTimer) { - // interrupt any upcoming transitions - KillTimer(g_hCountDownTimer); - } - PluginStartInit(); - CreateTimer(3.0, StartVoteMixmap_Timer); - return; - } - } - } + g_hArrayTagOrder.Resize(index + 1); } - - DisplayBuiltinVoteFail(vote, BuiltinVoteFail_Loses); -} -public Action StartVoteMixmap_Timer(Handle timer) -{ - Mixmap(); - - return Plugin_Handled; -} + g_iMapCount++; + g_hArrayTagOrder.SetString(index, buffer); + if (g_hArrayTags.FindString(buffer) < 0) + { + g_hArrayTags.PushString(buffer); + } -// Load a mixmap cfg -public Action Mixmap() -{ - ServerCommand("exec %s%s.cfg", DIR_CFGS, cfg_exec); -// PrintToChatAll("\x01Loading \x05random \x01preset..."); - g_bMapsetInitialized = true; - CreateTimer(0.1, Timed_PostMapSet); - return Plugin_Handled; } -// Display current map list -public Action Command_Maplist(int client, int args) +// Add a map to the maplist under specified tags +Action AddMap(any args) { - if (! g_bMaplistFinalized) + if (args < 2) { - CPrintToChat(client, "%t", "Show_Maplist_Not_Start"); + ReplyToCommand(0, "Syntax: sm_addmap <...>"); + ReplyToCommand(0, "Adds to the map selection and tags it with every mentioned tag."); + return Plugin_Handled; } - char output[BUF_SZ]; + char map[BUF_SZ]; + GetCmdArg(1, map, BUF_SZ); + if(!IsMapValid(map)) + { + //LogError("[MixMap] mapname: %s is invalid", map); + ReplyToCommand(0, "[MixMap] mapname: %s is invalid", map); + PrintToChatAll("[MixMap] mapname: %s is invalid", map); + + return Plugin_Handled; + } + + char tag[BUF_SZ]; + + //add the map under only one of the tags + //TODO - maybe we should add it under all tags, since it might be removed from 1+ or even all of them anyway + //also, if that ends up being implemented, remember to remove vetoed maps from ALL the pools it belongs to + if (args == 2) + { + GetCmdArg(2, tag, BUF_SZ); + } + else + { + GetCmdArg(GetRandomInt(2, args), tag, BUF_SZ); + } + + ArrayList hArrayMapPool; + if (!g_hTriePools.GetValue(tag, hArrayMapPool)) + { + hArrayMapPool = new ArrayList(BUF_SZ/4); + g_hTriePools.SetValue(tag, hArrayMapPool); + } + + hArrayMapPool.PushString(map); + + return Plugin_Handled; +} + +// Display current map list +Action MixMaplist(int client, any args) +{ + if (! g_bMaplistFinalized) + { + CPrintToChat(client, "%T", "Show_Maplist_Not_Start", client); + return Plugin_Handled; + } + + char output[BUF_SZ]; char buffer[BUF_SZ]; - CPrintToChat(client, "%t", "Maplist_Title"); + CPrintToChat(client, "%T", "Maplist_Title", client); - for (int i = 0; i < GetArraySize(g_hArrayMapOrder); i++) + for (int i = 0; i < g_hArrayMapOrder.Length; i++) { - GetArrayString(g_hArrayMapOrder, i, buffer, BUF_SZ); + g_hArrayMapOrder.GetString(i, buffer, BUF_SZ); if (g_iMapsPlayed == i) FormatEx(output, BUF_SZ, "\x04 %d - %s", i + 1, buffer); else if (!g_cvNextMapPrint.IntValue && g_iMapsPlayed < i) @@ -614,6 +612,7 @@ public Action Command_Maplist(int client, int args) } else FormatEx(output, BUF_SZ, "\x01 %d - %s", i + 1, buffer); + //PrintToChatAll("Maplist: %s", buffer); if (GetPrettyName(buffer)) { if (g_iMapsPlayed == i) @@ -623,17 +622,17 @@ public Action Command_Maplist(int client, int args) } CPrintToChat(client, "%s", output); } - CPrintToChat(client, "%t", "Show_Maplist_Cmd"); + CPrintToChat(client, "%T", "Show_Maplist_Cmd", client); return Plugin_Handled; } // Abort a currently loaded mapset -public Action Command_StopMixmap(int client, int args) +Action StopMixmap_Cmd(int client, any args) { if (!g_bMapsetInitialized ) { - CPrintToChat(client, "%t", "Not_Start"); + CPrintToChat(client, "%T", "Not_Start", client); return Plugin_Handled; } if (IsClientAndInGame(client)) @@ -651,22 +650,23 @@ public Action Command_StopMixmap(int client, int args) iPlayers[iNumPlayers++] = i; } - char cVoteTitle[32]; + char cVoteTitle[64]; Format(cVoteTitle, sizeof(cVoteTitle), "%T", "Cvote_Title_Off", LANG_SERVER); - hVoteStopMixmap = CreateBuiltinVote(VoteStopMixmapActionHandler, BuiltinVoteType_Custom_YesNo, BuiltinVoteAction_Cancel | BuiltinVoteAction_VoteEnd | BuiltinVoteAction_End); + Handle hVote = CreateBuiltinVote(VoteActionHandler, BuiltinVoteType_Custom_YesNo, BuiltinVoteAction_Cancel | BuiltinVoteAction_VoteEnd | BuiltinVoteAction_End); - SetBuiltinVoteArgument(hVoteStopMixmap, cVoteTitle); - SetBuiltinVoteInitiator(hVoteStopMixmap, client); - SetBuiltinVoteResultCallback(hVoteStopMixmap, VoteStopMixmapResultHandler); - DisplayBuiltinVote(hVoteStopMixmap, iPlayers, iNumPlayers, 20); + SetBuiltinVoteArgument(hVote, cVoteTitle); + SetBuiltinVoteInitiator(hVote, client); + SetBuiltinVoteResultCallback(hVote, VoteStopMixmapResultHandler); + DisplayBuiltinVote(hVote, iPlayers, iNumPlayers, 20); CPrintToChatAllEx(client, "%t", "Vote_Stop", client); - FakeClientCommand(client, "Vote Yes"); + if(g_bL4D2Version) FakeClientCommand(client, "Vote Yes"); + if(!g_bL4D2Version) EmitSoundToAll("ui/beep_synthtone01.wav"); } else { - PrintToChat(client, "%t", "Vote_Progress"); + CPrintToChat(client, "%T", "Vote_Blocked", client); } return Plugin_Handled; @@ -675,29 +675,93 @@ public Action Command_StopMixmap(int client, int args) return Plugin_Continue; } -public void VoteStopMixmapActionHandler(Handle vote, BuiltinVoteAction action, int param1, int param2) +Action StopMixmap(int client, any args) +{ + if (!g_bMapsetInitialized) + { + CPrintToChatAll("%t", "Not_Start"); + return Plugin_Handled; + } + + PluginStartInit(); + + CPrintToChatAllEx(client, "%t", "Stop_Mixmap_Admin", client); + return Plugin_Handled; +} + +// Event------------------------------- + +void map_transition(Event event, const char[] name, bool dontBroadcast) +{ + if(L4D_HasPlayerControlledZombies() == false && g_bMapsetInitialized) + { + if (++g_iMapsPlayed < g_iMapCount) + { + CreateTimer(0.5, Timed_NextMapInfo, _, TIMER_FLAG_NO_MAPCHANGE); + CreateTimer(7.5, Timer_map_transition, _, TIMER_FLAG_NO_MAPCHANGE); + return; + } + + Call_StartForward(g_hForwardEnd); + Call_Finish(); + } +} + +void finale_win(Event event, const char[] name, bool dontBroadcast) +{ + if(L4D_HasPlayerControlledZombies() == false && g_bMapsetInitialized) + { + Call_StartForward(g_hForwardEnd); + Call_Finish(); + + if (g_cvFinaleEndCoop.BoolValue) + { + CreateTimer(10.0, Timed_ContinueMixmap, _, TIMER_FLAG_NO_MAPCHANGE); + } + } +} + +void Event_RoundStart(Event event, const char[] name, bool dontBroadcast) +{ + if( g_iPlayerSpawn == 1 && g_iRoundStart == 0 ) + CreateTimer(0.5, Timer_PluginStart, _, TIMER_FLAG_NO_MAPCHANGE); + g_iRoundStart = 1; + + g_bRoundIsLive = false; + g_bReadyUpFooterAdded = false; + g_iReadyUpFooterIndex = -1; +} + +void Event_PlayerSpawn(Event event, const char[] name, bool dontBroadcast) +{ + if( g_iPlayerSpawn == 0 && g_iRoundStart == 1 ) + CreateTimer(0.5, Timer_PluginStart, _, TIMER_FLAG_NO_MAPCHANGE); + g_iPlayerSpawn = 1; +} + +void Event_RoundEnd(Event event, const char[] name, bool dontBroadcast) +{ + ClearDefault(); +} + +// Vote------------------------------- + +void VoteActionHandler(Handle vote, BuiltinVoteAction action, int param1, int param2) { switch (action) { case BuiltinVoteAction_End: { - hVoteStopMixmap = null; CloseHandle(vote); } case BuiltinVoteAction_Cancel: { - DisplayBuiltinVoteFail(vote, view_as(param1)); + //DisplayBuiltinVoteFail(vote, view_as(param1)); } -/* case BuiltinVoteAction_Select: - { - char cItemVal[64]; - char cItemName[64]; - GetBuiltinVoteItem(vote, param2, cItemVal, sizeof(cItemVal), cItemName, sizeof(cItemName)); - } */ } } -public void VoteStopMixmapResultHandler(Handle vote, int num_votes, int num_clients, const int[][] client_info, int num_items, const int[][] item_info) +void VoteMixmapResultHandler(Handle vote, int num_votes, int num_clients, const int[][] client_info, int num_items, const int[][] item_info) { for (int i = 0; i < num_items; i++) { @@ -705,277 +769,419 @@ public void VoteStopMixmapResultHandler(Handle vote, int num_votes, int num_clie { if (item_info[i][BUILTINVOTEINFO_ITEM_VOTES] > (num_clients / 2)) { - if (vote == hVoteStopMixmap) - { - DisplayBuiltinVotePass(vote, "stop Mixmap……"); - CreateTimer(1.0, StartVoteStopMixmap_Timer); - return; - } + char cExecTitle[64]; + Format(cExecTitle, sizeof(cExecTitle), "%T", "Cexec_Title", LANG_SERVER); + DisplayBuiltinVotePass(vote, cExecTitle); + + if(!g_bL4D2Version) EmitSoundToAll("ui/menu_enter05.wav"); + + PluginStartInit(); + CreateTimer(3.0, StartVoteMixmap_Timer, _, TIMER_FLAG_NO_MAPCHANGE); + return; } } } DisplayBuiltinVoteFail(vote, BuiltinVoteFail_Loses); + if(!g_bL4D2Version) EmitSoundToAll("ui/beep_error01.wav"); } -public Action StartVoteStopMixmap_Timer(Handle timer) +void VoteStopMixmapResultHandler(Handle vote, int num_votes, int num_clients, const int[][] client_info, int num_items, const int[][] item_info) { - if (g_hCountDownTimer) + for (int i = 0; i < num_items; i++) { - // interrupt any upcoming transitions - KillTimer(g_hCountDownTimer); + if (item_info[i][BUILTINVOTEINFO_ITEM_INDEX] == BUILTINVOTES_VOTE_YES) + { + if (item_info[i][BUILTINVOTEINFO_ITEM_VOTES] > (num_clients / 2)) + { + char cExecTitle[64]; + Format(cExecTitle, sizeof(cExecTitle), "%T", "Stop Mixmap", LANG_SERVER); + DisplayBuiltinVotePass(vote, cExecTitle); + CreateTimer(1.0, StartVoteStopMixmap_Timer); + + if(!g_bL4D2Version) EmitSoundToAll("ui/menu_enter05.wav"); + + return; + } + } } - PluginStartInit(); - CPrintToChatAll("%t", "Stop_Mixmap"); - return Plugin_Handled; + DisplayBuiltinVoteFail(vote, BuiltinVoteFail_Loses); + if(!g_bL4D2Version) EmitSoundToAll("ui/beep_error01.wav"); } -public Action Command_ForceStopMixmap(int client, int args) +// Timer & Frame------------------------------- + +Action Timer_PluginStart(Handle timer) { - if (!g_bMapsetInitialized) + ClearDefault(); + + if(g_ReadyUpAvailable) UpdateReadyUpFooter(5.5); + + + + return Plugin_Continue; +} + +Action Timed_NextMapInfo(Handle timer) +{ + char sMapName_Pretty[BUF_SZ]; + g_hArrayMapOrder.GetString(g_iMapsPlayed, sMapName_Pretty, BUF_SZ); + + //PrintToChatAll("%s", sMapName_Pretty); + GetPrettyName(sMapName_Pretty); + //PrintToChatAll("%s", sMapName_Pretty); + + g_cvNextMapPrint.IntValue ? CPrintToChatAll("%t", "Show_Next_Map", sMapName_Pretty) : CPrintToChatAll("%t", "Show_Next_Map (Secret)", "Secret"); + + return Plugin_Continue; +} + +Action Timed_ContinueMixmap(Handle timer) +{ + ServerCommand("sm_fmixmap %s", cfg_exec); + + return Plugin_Continue; +} + +Action Timer_map_transition(Handle timer) +{ + //直接強制換圖 + GotoNextMap(true); + + return Plugin_Continue; +} + +Action Timer_UpdateReadyUpFooter(Handle timer) +{ + g_hUpdateFooterTimer = null; + + char sMapName_Pretty[BUF_SZ]; + if(g_hArrayMapOrder.Length == 0) { - CPrintToChatAll("%t", "Not_Start"); - return Plugin_Handled; + FormatEx(sMapName_Pretty, sizeof(sMapName_Pretty), "Mixmap : No active"); } + else + { + if(g_iMapsPlayed+1 >= g_iMapCount) + { + FormatEx(sMapName_Pretty, sizeof(sMapName_Pretty), "Mixmap : No Next Map"); + } + else + { + g_hArrayMapOrder.GetString(g_iMapsPlayed+1, sMapName_Pretty, BUF_SZ); + GetPrettyName(sMapName_Pretty); - if (g_hCountDownTimer) + Format(sMapName_Pretty, sizeof(sMapName_Pretty), "Mixmap : %s", sMapName_Pretty); + } + } + + // Check to see if the Ready Up footer has already been added + if (g_bReadyUpFooterAdded) + { + // Ready Up footer already exists, so we can just edit it. + EditFooterStringAtIndex(g_iReadyUpFooterIndex, sMapName_Pretty); + } + else { - // interrupt any upcoming transitions - KillTimer(g_hCountDownTimer); + // Ready Up footer hasn't been added yet. Must be the start of a new round! Lets add it. + g_iReadyUpFooterIndex = AddStringToReadyFooter(sMapName_Pretty); + g_bReadyUpFooterAdded = true; } - PluginStartInit(); + return Plugin_Continue; +} - CPrintToChatAllEx(client, "%t", "Stop_Mixmap_Admin", client); - return Plugin_Handled; +Action Timer_FindInfoChangelevel(Handle timer) +{ + if(L4D_HasPlayerControlledZombies() && strlen(g_slandmarkName) > 0) + { + float targetPos[3]; + + int entity = -1; + while ((entity = FindEntityByClassname(entity, "info_landmark")) != -1) + { + if(!IsValidEntity(entity)) continue; + + GetEntPropVector(entity, Prop_Data, "m_vecAbsOrigin", targetPos); + int info_landmark = CreateEntityByName("info_landmark"); + if(info_landmark > MaxClients) + { + // To prevent server console spam error: Most gross danger! Cannot find Landmark named xxxxxx! + DispatchKeyValueVector(info_landmark, "origin", targetPos); + DispatchKeyValue(info_landmark, "targetname", g_slandmarkName); + DispatchSpawn(info_landmark); + } + + break; + } + } + + g_slandmarkName[0] = '\0'; + int ent = FindEntityByClassname(-1, "info_changelevel"); + if(ent == -1) + { + ent = FindEntityByClassname(-1, "trigger_changelevel"); + } + + if(ent == -1) + { + return Plugin_Continue; + } + else + { + GetEntPropString(ent, Prop_Data, "m_landmarkName", g_slandmarkName, sizeof(g_slandmarkName)); + } + + return Plugin_Continue; } +Action StartVoteMixmap_Timer(Handle timer) +{ + Mixmap(); + + return Plugin_Continue; +} -// ---------------------------------------------------------- -// Map set picking -// ---------------------------------------------------------- +Action StartVoteStopMixmap_Timer(Handle timer) +{ + PluginStartInit(); + + CPrintToChatAll("%t", "Stop_Mixmap"); + return Plugin_Continue; +} //creates the initial map list after a map set has been loaded -public Action Timed_PostMapSet(Handle timer) +Action Timed_PostMapSet(Handle timer) { - int mapnum = GetArraySize(g_hArrayTagOrder); - int triesize = GetTrieSize(g_hTriePools); + if (g_hTriePools == null) + { + g_bMapsetInitialized = false; //failed to load it on the exec + CPrintToChatAll("%t", "Fail_Load_Preset"); + return Plugin_Continue; + } - if (mapnum == 0) + if (g_hArrayTagOrder == null) { g_bMapsetInitialized = false; //failed to load it on the exec CPrintToChatAll("%t", "Fail_Load_Preset"); - return Plugin_Handled; + return Plugin_Continue; + } + + int mapnum = g_hArrayTagOrder.Length; + int triesize = g_hTriePools.Size; + if (triesize == 0 || mapnum == 0) + { + g_bMapsetInitialized = false; //failed to load it on the exec + CPrintToChatAll("%t", "Fail_Load_Preset"); + return Plugin_Continue; } if (g_iMapCount < triesize) { g_bMapsetInitialized = false; //bad preset format CPrintToChatAll("%t", "Maps_Not_Match_Rank"); - return Plugin_Handled; + return Plugin_Continue; } CPrintToChatAll("%t", "Select_Maps_Succeed"); SelectRandomMap(); - return Plugin_Handled; + return Plugin_Continue; } -// ---------------------------------------------------------- -// Map pool logic -// ---------------------------------------------------------- +Action Timed_GiveThemTimeToReadTheMapList(Handle timer) +{ + g_hCountDownTimer = null; + if (IsBuiltinVoteInProgress() && !g_bServerForceStart) + { + CPrintToChatAll("%t", "Vote_Progress_delay", 20); + g_hCountDownTimer = CreateTimer(20.0, Timed_GiveThemTimeToReadTheMapList); + return Plugin_Continue; + } + if (g_bServerForceStart) g_bServerForceStart = false; -// Returns a handle to the first array which is found to contain the specified mapname -// (should be the first and only one) -stock Handle GetPoolThatContainsMap(char[] map, char[] tag) + // call starting forward + char buffer[BUF_SZ]; + g_hArrayMapOrder.GetString(0, buffer, BUF_SZ); + + Call_StartForward(g_hForwardStart); + Call_PushCell(g_iMapCount); + Call_PushString(buffer); + Call_Finish(); + + GotoNextMap(true); + return Plugin_Continue; +} + +Action Timer_DifferAbort(Handle timer) +{ + g_bDifferAbort = false; + + return Plugin_Continue; +} + +// Function------------------------------- + +// Load a mixmap cfg +void Mixmap() { - Handle hArrayMapPool; - - for (int i = 0; i < GetArraySize(g_hArrayTags); i++) - { - GetArrayString(g_hArrayTags, i, tag, BUF_SZ); - GetTrieValue(g_hTriePools, tag, hArrayMapPool); - if (FindStringInArray(hArrayMapPool, map) >= 0) { - return hArrayMapPool; - } - } - return INVALID_HANDLE; + if(g_bL4D2Version) ServerCommand("exec %s%s.cfg", DIR_CFGS_L4D2, cfg_exec); + else ServerCommand("exec %s%s.cfg", DIR_CFGS_L4D1, cfg_exec); + + g_bMapsetInitialized = true; + CreateTimer(0.1, Timed_PostMapSet, _, TIMER_FLAG_NO_MAPCHANGE); } -stock void SelectRandomMap() +void SelectRandomMap() { g_bMaplistFinalized = true; SetRandomSeed(view_as(GetEngineTime())); - int i, mapIndex, mapsmax = g_cvMaxMapsNum.IntValue; + int mapIndex, mapsmax = g_cvMaxMapsNum.IntValue; ArrayList hArrayPool; char tag[BUF_SZ], map[BUF_SZ]; // Select 1 random map for each rank out of the remaining ones - for (i = 0; i < GetArraySize(g_hArrayTagOrder); i++) + int length = g_hArrayTagOrder.Length; + for (int i = 0; i < length; i++) { - GetArrayString(g_hArrayTagOrder, i, tag, BUF_SZ); - GetTrieValue(g_hTriePools, tag, hArrayPool); + g_hArrayTagOrder.GetString(i, tag, BUF_SZ); + g_hTriePools.GetValue(tag, hArrayPool); SortADTArray(hArrayPool, Sort_Random, Sort_String); //randomlize the array - mapIndex = GetRandomInt(0, GetArraySize(hArrayPool) - 1); + mapIndex = GetRandomInt(0, hArrayPool.Length - 1); - GetArrayString(hArrayPool, mapIndex, map, BUF_SZ); - RemoveFromArray(hArrayPool, mapIndex); + hArrayPool.GetString(mapIndex, map, BUF_SZ); + hArrayPool.Erase(mapIndex); if (mapsmax) //if limit the number of missions in one campaign, check the number. { if (CheckSameCampaignNum(map) >= mapsmax) { - while (GetArraySize(hArrayPool) > 0) // Reselect if the number will exceed the limit + while (hArrayPool.Length > 0) // Reselect if the number will exceed the limit { - mapIndex = GetRandomInt(0, GetArraySize(hArrayPool) - 1); - GetArrayString(hArrayPool, mapIndex, map, BUF_SZ); - RemoveFromArray(hArrayPool, mapIndex); + mapIndex = GetRandomInt(0, hArrayPool.Length - 1); + hArrayPool.GetString(mapIndex, map, BUF_SZ); + hArrayPool.Erase(mapIndex); if (CheckSameCampaignNum(map) < mapsmax) break; } if (CheckSameCampaignNum(map) >= mapsmax) //Reselect some missions (like only 1 mission4, the mission4 can't select) { - GetTrieValue(g_hTriePools, tag, hArrayPool); - SortADTArray(hArrayPool, Sort_Random, Sort_String); - mapIndex = GetRandomInt(0, GetArraySize(hArrayPool) - 1); + g_hTriePools.GetValue(tag, hArrayPool); + hArrayPool.Sort(Sort_Random, Sort_String); + mapIndex = GetRandomInt(0, hArrayPool.Length - 1); hArrayPool.GetString(mapIndex, map, BUF_SZ); ReSelectMapOrder(map); } } } - PushArrayString(g_hArrayMapOrder, map); + g_hArrayMapOrder.PushString(map); } // Clear things because we only need the finalised map order in memory - ClearTrie(g_hTriePools); - ClearArray(g_hArrayTagOrder); + + StringMapSnapshot hTrieSnapshot; + ArrayList hArrayList; + hTrieSnapshot = g_hTriePools.Snapshot(); + int length2 = hTrieSnapshot.Length; + char sKey[BUF_SZ]; + for(int i = 0; i < length2; i++) + { + hTrieSnapshot.GetKey(i, sKey, sizeof(sKey)); + g_hTriePools.GetValue(sKey, hArrayList); + delete hArrayList; + } + delete hTrieSnapshot; + + g_hTriePools.Clear(); + g_hArrayTagOrder.Clear(); // Show final maplist to everyone - for (i = 1; i <= MaxClients; i++) + for (int i = 1; i <= MaxClients; i++) { if (IsClientInGame(i) && !IsFakeClient(i)) { - FakeClientCommand(i, "sm_maplist"); + MixMaplist(i, 0); } } + CPrintToChatAll("%t", "Change_Map_First", g_bServerForceStart ? 5 : 15); //Alternative for remixmap + delete g_hCountDownTimer; g_hCountDownTimer = CreateTimer(g_bServerForceStart ? 5.0 : 15.0, Timed_GiveThemTimeToReadTheMapList); //Alternative for remixmap } -public Action Timed_GiveThemTimeToReadTheMapList(Handle timer) +void GotoNextMap(bool force = false) { - if (IsBuiltinVoteInProgress() && !g_bServerForceStart) - { - CPrintToChatAll("%t", "Vote_Progress_delay"); - g_hCountDownTimer = CreateTimer(20.0, Timed_GiveThemTimeToReadTheMapList); - return Plugin_Handled; - } - if (g_bServerForceStart) g_bServerForceStart = false; - g_hCountDownTimer = null; - - // call starting forward - char buffer[BUF_SZ]; - GetArrayString(g_hArrayMapOrder, 0, buffer, BUF_SZ); - - Call_StartForward(g_hForwardStart); - Call_PushCell(g_iMapCount); - Call_PushString(buffer); - Call_Finish(); - - GotoNextMap(true); - return Plugin_Handled; -} + char sMapName[BUF_SZ]; + g_hArrayMapOrder.GetString(g_iMapsPlayed, sMapName, BUF_SZ); + + GotoMap(sMapName, force); +} -// Specifiy a rank for a given tag -public Action Command_TagRank(int args) +void GotoMap(const char[] sMapName, bool force = false) { - if (args < 2) - { - ReplyToCommand(0, "Syntax: sm_tagrank "); - ReplyToCommand(0, "Sets tag as the tag to be used to fetch maps for map in the map list."); - ReplyToCommand(0, "Rank 0 is map 1, rank 1 is map 2, etc."); - return Plugin_Handled; - } - - char buffer[BUF_SZ]; - GetCmdArg(2, buffer, BUF_SZ); - int index = StringToInt(buffer); - - GetCmdArg(1, buffer, BUF_SZ); - - if (index >= GetArraySize(g_hArrayTagOrder)) - { - ResizeArray(g_hArrayTagOrder, index + 1); - } - - g_iMapCount++; - SetArrayString(g_hArrayTagOrder, index, buffer); - if (FindStringInArray(g_hArrayTags, buffer) < 0) + if (force) { - PushArrayString(g_hArrayTags, buffer); + ForceChangeLevel(sMapName, "Mixmap"); + return; } - return Plugin_Handled; -} + // 對抗模式中使用sm_nextmap便足夠,自動換到指定的關卡 + // 戰役模式中使用sm_nextmap,自動換到指定的關卡,但會有bots不見的問題 + ServerCommand("sm_nextmap %s", sMapName); +} -// Add a map to the maplist under specified tags -public Action Command_AddMap(int args) +void PluginStartInit() { - if (args < 2) + delete g_hCountDownTimer; + + delete g_hArrayTags; + g_hArrayTags = new ArrayList(BUF_SZ/4); //1 block = 4 characters => X characters = X/4 blocks + + if(g_hTriePools != null) { - ReplyToCommand(0, "Syntax: sm_addmap <...>"); - ReplyToCommand(0, "Adds to the map selection and tags it with every mentioned tag."); - return Plugin_Handled; + StringMapSnapshot hTrieSnapshot; + ArrayList hArrayList; + hTrieSnapshot = g_hTriePools.Snapshot(); + int length2 = hTrieSnapshot.Length; + char sKey[BUF_SZ]; + for(int i = 0; i < length2; i++) + { + hTrieSnapshot.GetKey(i, sKey, sizeof(sKey)); + g_hTriePools.GetValue(sKey, hArrayList); + delete hArrayList; + } + delete hTrieSnapshot; } - char map[BUF_SZ]; - GetCmdArg(1, map, BUF_SZ); + delete g_hTriePools; + g_hTriePools = new StringMap(); - char tag[BUF_SZ]; + delete g_hArrayTagOrder; + g_hArrayTagOrder = new ArrayList(BUF_SZ/4); - //add the map under only one of the tags - //TODO - maybe we should add it under all tags, since it might be removed from 1+ or even all of them anyway - //also, if that ends up being implemented, remember to remove vetoed maps from ALL the pools it belongs to - if (args == 2) - { - GetCmdArg(2, tag, BUF_SZ); - } - else - { - GetCmdArg(GetRandomInt(2, args), tag, BUF_SZ); - } - - Handle hArrayMapPool; - if (! GetTrieValue(g_hTriePools, tag, hArrayMapPool)) - { - SetTrieValue(g_hTriePools, tag, (hArrayMapPool = CreateArray(BUF_SZ/4))); - } + delete g_hArrayMapOrder; + g_hArrayMapOrder = new ArrayList(BUF_SZ/4); - PushArrayString(hArrayMapPool, map); + g_bMapsetInitialized = false; + g_bMaplistFinalized = false; + + g_iMapsPlayed = 0; + g_iMapCount = 0; - return Plugin_Handled; + g_slandmarkName[0] = '\0'; } +// Others------------------------------- + // Return false if pretty name not found, ture otherwise -stock bool GetPrettyName(char[] map) +bool GetPrettyName(char[] map) { - static Handle hKvMapNames = INVALID_HANDLE; - if (hKvMapNames == INVALID_HANDLE) - { - hKvMapNames = CreateKeyValues("Mixmap Map Names"); - if (! FileToKeyValues(hKvMapNames, PATH_KV)) - { - LogMessage("Couldn't create KV for map names."); - hKvMapNames = INVALID_HANDLE; - return false; - } - } - + g_hKvMapNames.Rewind(); char buffer[BUF_SZ]; - KvGetString(hKvMapNames, map, buffer, BUF_SZ, "no"); + g_hKvMapNames.GetString(map, buffer, BUF_SZ, "no"); - if (! StrEqual(buffer, "no")) + if (!StrEqual(buffer, "no")) { strcopy(map, BUF_SZ, buffer); return true; @@ -983,23 +1189,19 @@ stock bool GetPrettyName(char[] map) return false; } -// ---------------------------------------------------------- -// Basic helpers -// ---------------------------------------------------------- - -stock bool IsClientAndInGame(int index) +bool IsClientAndInGame(int index) { - return (index > 0 && index <= MaxClients && IsClientInGame(index) && IsClientConnected(index) && !IsFakeClient(index) && GetClientTeam(index) != 1); + return (index > 0 && index <= MaxClients && IsClientInGame(index) && IsClientConnected(index) && !IsFakeClient(index)); } -stock int CheckSameCampaignNum(char[] map) +int CheckSameCampaignNum(char[] map) { int count = 0; char buffer[BUF_SZ]; - for (int i = 0; i < GetArraySize(g_hArrayMapOrder); i++) + for (int i = 0; i < g_hArrayMapOrder.Length; i++) { - GetArrayString(g_hArrayMapOrder, i, buffer, sizeof(buffer)); + g_hArrayMapOrder.GetString(i, buffer, sizeof(buffer)); if (IsSameCampaign(map, buffer)) count ++; } @@ -1007,7 +1209,7 @@ stock int CheckSameCampaignNum(char[] map) return count; } -stock bool IsSameCampaign(char[] map1, char[] map2) +bool IsSameCampaign(char[] map1, char[] map2) { char buffer1[BUF_SZ], buffer2[BUF_SZ]; @@ -1021,29 +1223,92 @@ stock bool IsSameCampaign(char[] map1, char[] map2) return false; } -stock void ReSelectMapOrder(char[] confirm) //hope this will work +void ReSelectMapOrder(char[] confirm) //hope this will work { char buffer[BUF_SZ]; ArrayList hArrayPool; int mapindex; - for (int i = GetArraySize(g_hArrayMapOrder) - 1; i >= 0; i--) { - GetArrayString(g_hArrayMapOrder, i, buffer, BUF_SZ); + for (int i = g_hArrayMapOrder.Length - 1; i >= 0; i--) { + g_hArrayMapOrder.GetString(i, buffer, BUF_SZ); if (IsSameCampaign(confirm, buffer)) { - GetArrayString(g_hArrayTagOrder, i, buffer, BUF_SZ); - GetTrieValue(g_hTriePools, buffer, hArrayPool); - RemoveFromArray(hArrayPool, FindStringInArray(hArrayPool, confirm)); + g_hArrayTagOrder.GetString(i, buffer, BUF_SZ); + g_hTriePools.GetValue(buffer, hArrayPool); + hArrayPool.Erase(hArrayPool.FindString(confirm)); for (int j = 0; j <= i; j++) { - SortADTArray(hArrayPool, Sort_Random, Sort_String); //randomlize the array - mapindex = GetRandomInt(0, GetArraySize(hArrayPool) - 1); - GetArrayString(hArrayPool, mapindex, buffer, BUF_SZ); + hArrayPool.Sort(Sort_Random, Sort_String); //randomlize the array + mapindex = GetRandomInt(0, hArrayPool.Length - 1); + hArrayPool.GetString(mapindex, buffer, BUF_SZ); hArrayPool.Erase(mapindex); if (CheckSameCampaignNum(buffer) < g_cvMaxMapsNum.IntValue) { - SetArrayString(g_hArrayMapOrder, i, buffer); + g_hArrayMapOrder.SetString(i, buffer); break; } } return; } } +} + +void UpdateReadyUpFooter(float interval = 0.1) +{ + delete g_hUpdateFooterTimer; + g_hUpdateFooterTimer = CreateTimer(interval, Timer_UpdateReadyUpFooter); +} + +bool InSecondHalfOfRound() +{ + return view_as(GameRules_GetProp("m_bInSecondHalfOfRound")); +} + +stock bool IsValidEntityIndex(int entity) +{ + return (MaxClients+1 <= entity <= GetMaxEntities()); +} + +void ClearDefault() +{ + g_iRoundStart = 0; + g_iPlayerSpawn = 0; +} + +// Other API-- + +public void L4D2_OnEndVersusModeRound_Post() +{ + if (InSecondHalfOfRound()) + { + if(g_bMapsetInitialized) + { + if (++g_iMapsPlayed < g_iMapCount) + { + GotoNextMap(false); + CreateTimer(5.0, Timed_NextMapInfo, _, TIMER_FLAG_NO_MAPCHANGE); + return; + } + + Call_StartForward(g_hForwardEnd); + Call_Finish(); + + if(g_cvFinaleEndVersus.BoolValue && L4D_IsMissionFinalMap(true)) + { + CreateTimer(9.0, Timed_ContinueMixmap); + } + } + } +} + +public void OnRoundIsLive() +{ + g_bRoundIsLive = true; +} + +public Action l4d2_map_transitions_OnChangeNextMap_Pre(const char[] sNextMapName) +{ + if (g_bMapsetInitialized) + { + return Plugin_Handled; + } + + return Plugin_Continue; } \ No newline at end of file diff --git a/addons/sourcemod/translations/l4d2_mixmap.phrases.txt b/addons/sourcemod/translations/l4d2_mixmap.phrases.txt index 284fac54e..ab801e528 100644 --- a/addons/sourcemod/translations/l4d2_mixmap.phrases.txt +++ b/addons/sourcemod/translations/l4d2_mixmap.phrases.txt @@ -2,154 +2,208 @@ { "Auto_Show_Maplist" { - "en" "{olive}{default}Using Mixmap plugin (show maplist{orange}!maplist{default})." - "chi" "{olive}{default}正在使用Mixmap插件(地图一览指令{orange}!maplist{default})。" + "en" "{olive}{default}Using Mixmap plugin (show maplist {orange}!maplist{default})." + "zho" "{olive}{default}正在使用Mixmap插件, 每一關隨機挑選(地圖一覽指令 {orange}!maplist{default})。" + "chi" "{olive}{default}正在使用Mixmap插件, 每一关随机挑选(地图一览指令 {orange}!maplist{default})。" } "Differ_Abort" { - "en" "{olive}{default}Current map differs from disigned map,abort……" - "chi" "{olive}{default}当前地图与设计地图不一致,插件中止……" + "en" "{olive}{default}Current map differs from disigned mix map list,abort……" + "zho" "{olive}{default}當前地圖與預先挑選的地圖表不一致,插件中止…" + "chi" "{olive}{default}当前地图与预先挑选的地图表不一致,插件中止……" } "Show_Next_Map" { - "en" "{olive}{default}Changing next map: %s" - "chi" "{olive}{default}切换下一张地图: %s" + "#format" "{1:s}" + "en" "{olive}{default}Changing next map: {1}" + "zho" "{olive}{default}切換下一張地圖: {1}" + "chi" "{olive}{default}切换下一张地图: {1}" + } + + "Show_Next_Map (Secret)" + { + "#format" "{1:t}" + "en" "{olive}{default}Changing next map: {1}" + "zho" "{olive}{default}切換下一張地圖: {1}" + "chi" "{olive}{default}切换下一张地图: {1}" } "Already_Start_Admin" { "en" "{olive}{default}Plugin already starts, type !fstopmixmap first if need to restart." + "zho" "{olive}{default}插件已經啟用,如需重新啟用請先輸入 !fstopmixmap 中止。" "chi" "{olive}{default}插件已经启用,如需重新启用请先输入 !fstopmixmap 中止。" } "Force_Start" { - "en" "{olive}{default}Admin {teamcolor}%N {default}forces to start Mixmap{orange}(%s)." - "chi" "{olive}{default}管理员 {teamcolor}%N {default}强制启用Mixmap{orange}(%s)。" + "en" "{olive}{default}Admin {teamcolor}%N {default}forces to start Mixmap, {orange}Map Pool: %s" + "zho" "{olive}{default}管理者 {teamcolor}%N {default}強制啟用Mixmap, {orange}地圖池: %s" + "chi" "{olive}{default}管理员 {teamcolor}%N {default}强制启用Mixmap, {orange}地图池: %s" } "Manualmixmap_Syntax" { "en" "Syntax: !manualmixmap <...>" + "zho" "使用方式: !manualmixmap <...>" "chi" "用法: !manualmixmap <...>" } "Invalid_Cfg" { "en" "{olive}{default}Invalid map pool!" + "zho" "{olive}{default}無效地圖池!" "chi" "{olive}{default}无效地图池!" } "Not_Start" { "en" "{olive}{default}Plugin did not start, no need to stop." + "zho" "{olive}{default}插件尚未啟用,無需中止。" "chi" "{olive}{default}插件尚未启用,无需中止。" } "Vote_Stop" { "en" "{olive}{default}Player {teamcolor}%N{default} initiates a vote to stop Mixmap." + "zho" "{olive}{default}玩家 {teamcolor}%N{default} 發起投票關閉Mixmap。" "chi" "{olive}{default}玩家 {teamcolor}%N{default} 发起投票关闭Mixmap。" } - "Vote_Progress" + "Vote_Blocked" { - "en" "[SM] There is a vote in progress!" - "chi" "[SM] 有投票正在进行!" + "en" "[SM] Can't call new vote, try again later!" + "zho" "[SM] 無法發起新投票, 請稍後再試" + "chi" "[SM] 无法发起新投票, 请稍后再试" } "Vote_Progress_delay" { - "en" "{olive}{default}There is a vote in progress! Changing map delays {orange} 15 {default}seconds! " - "chi" "{olive}{default}有投票正在进行!地图切换延迟{orange} 20 {default}秒! " + "en" "{olive}{default}There is a vote in progress! Changing map delays {orange}%d{default} seconds! " + "zho" "{olive}{default}有投票正在進行!地圖切換延遲 {orange}%d{default} 秒!" + "chi" "{olive}{default}有投票正在进行!地图切换延迟 {orange}%d{default} 秒! " } "Stop_Mixmap" { - "en" "{olive}{default}Maps initiated, abort……" - "chi" "{olive}{default}地图初始化,插件中止……" + "en" "{olive}{default}Mix Maps initiated, abort……" + "zho" "{olive}{default}Mixmap已關閉,地圖初始化,插件中止…" + "chi" "{olive}{default}Mixmap已关闭,地图初始化,插件中止……" } "Stop_Mixmap_Admin" { "en" "{olive}{default} Admin{teamcolor} %N {default}force-stop Mixmap and Maps initiate,abort……" + "zho" "{olive}{default} 管理員{teamcolor} %N {default}強制關閉Mixmap,地圖初始化,插件中止…" "chi" "{olive}{default} 管理员{teamcolor} %N {default}强制关闭Mixmap,地图初始化,插件中止……" } "Start_Mixmap" { - "en" "{olive}{default}Player {teamcolor}%N{default} initiates a vote to start Mixmap{orange}(%s)." - "chi" "{olive}{default}玩家 {teamcolor}%N{default} 发起投票启用Mixmap{orange}(%s)。" + "en" "{olive}{default}Player {teamcolor}%N{default} initiates a vote to start Mixmap, {orange}Map Pool: %s" + "zho" "{olive}{default}玩家 {teamcolor}%N{default} 發起投票啟用Mixmap,{orange}地圖池: %s" + "chi" "{olive}{default}玩家 {teamcolor}%N{default} 发起投票启用Mixmap,{orange}地图池: %s" } "Show_Maplist_Not_Start" { - "en" "{olive}{default}Plugin did not start, there is not a maplist." - "chi" "{olive}{default}插件未启用,无法查看地图列表。" + "en" "{olive}{default}Plugin did not start, there is no maplist." + "zho" "{olive}{default}插件未啟用,沒有隨機地圖列表。" + "chi" "{olive}{default}插件未启用,没有随机地图列表。" } "Show_Maplist_Cmd" { - "en" "{olive}Show maplist {orange}!maplist {default}| {olive}Abort Cmd{orange}!stopmixmap" + "en" "{olive}Show maplist {orange}!maplist {default}| {olive}Abort Cmd{orange} !stopmixmap" + "zho" "{olive}地圖一覽指令 {orange}!maplist {default}| {olive}中止指令{orange} !stopmixmap" "chi" "{olive}地图一览指令 {orange}!maplist {default}| {olive}中止指令{orange} !stopmixmap" } "Select_Maps_Succeed" { "en" "{olive}{default}Selecting maps randomly succeeds." - "chi" "{olive}{default}地图已成功抽取!" + "zho" "{olive}{default}地圖已成功隨機抽取!" + "chi" "{olive}{default}地图已成功随机抽取!" } "Change_Map_First" { - "en" "{olive}{default}Changing map after {orange} %d {default}seconds." + "en" "{olive}{default}Changing map after{orange} %d {default}seconds." + "zho" "{olive}{default}將在{orange} %d {default}秒後開始切換地圖。" "chi" "{olive}{default}将在{orange} %d {default}秒后开始切换地图。" } "AllMaps_Official" { "en" "{orange}[-----list of all official maps-----]" + "zho" "{orange}[-----所有官方地圖代碼列表-----]" "chi" "{orange}[-----所有官方地图代码列表-----]" } "AllMaps_Usage" { "en" "{orange}Usage: {default}!manualmixmap ..." + "zho" "{orange}用法:{default}!manualmixmap <地圖1> <地圖2>......" "chi" "{orange}用法:{default}!manualmixmap <地图1> <地图2>......" } "Fail_Load_Preset" { "en" "{olive}{default}Failed to load preset!" + "zho" "{olive}{default}地圖組未成功載入!" "chi" "{olive}{default}地图组未成功加载!" } "Maps_Not_Match_Rank" { "en" "{olive}{default}Preset has {orange}improper tagranks{default}: the number of maps to be played does not match the highest rank. Should have N+1 tagranks for highest rank N." + "zho" "{olive}{default}地圖組的{orange}標籤有誤{default}:設定的地圖數量和標籤數量不一致。當標籤最大值為N+1時,地圖數量應為N。" "chi" "{olive}{default}地图组的{orange}标签有误{default}:设定的地图数量和标签数量不一致。当标签最大值为N+1时,地图数量应为N。" } "Cvote_Title" { "en" "Start a Mixmap? (%s)" + "zho" "啟用Mixmap插件?(%s)" "chi" "启用Mixmap插件?(%s)" } "Cvote_Title_Off" { "en" "Stop a Mixmap? " + "zho" "中止Mixmap插件?" "chi" "中止Mixmap插件?" } "Cexec_Title" { "en" "Selecting maps randomly……" + "zho" "即將隨機抽取地圖…" "chi" "即将随机抽取地图……" } + "Stop Mixmap" + { + "en" "Stop Mixmap……" + "zho" "插件已停止Mixmap…" + "chi" "插件已停止Mixmap…" + } "Maplist_Title" { "en" "{olive}[-------- Maplist --------]" + "zho" "{olive}[-------- 地圖列表 --------]" "chi" "{olive}[-------- 地图列表 --------]" } "Secret" { "en" "Secret" - "chi" "秘密" + "zho" "地圖保密" + "chi" "地图保密" + } + "Spectator Blocked" + { + "en" "Spectator can not call vote" + "zho" "旁觀者不能投票" + "chi" "旁观者不能投票" + } + "Round is live" + { + "en" "You can't call this vote once round is live" + "zho" "只有在準備階段才能發起投票" + "chi" "只有在准备阶段才能发起投票" } } \ No newline at end of file diff --git a/cfg/cfgogl/zmcustom/shared_cvars.cfg b/cfg/cfgogl/zmcustom/shared_cvars.cfg index 336f8234b..6883e16a4 100644 --- a/cfg/cfgogl/zmcustom/shared_cvars.cfg +++ b/cfg/cfgogl/zmcustom/shared_cvars.cfg @@ -42,7 +42,7 @@ confogl_match_restart "1" // Sets whet // Confogl Cvars confogl_addcvar confogl_boss_tank "1" // Tank can't be prelit, frozen and ghost until player takes over, punch fix, and no rock throw for AI tank while waiting for player -confogl_addcvar confogl_boss_unprohibit "0" // Enable bosses spawning on all maps, even through they normally aren't allowed +//confogl_addcvar confogl_boss_unprohibit "0" // Enable bosses spawning on all maps, even through they normally aren't allowed confogl_addcvar confogl_lock_boss_spawns "1" // Enables forcing same coordinates for tank and witch spawns (excluding tanks during finales) confogl_addcvar confogl_remove_escape_tank "1" // Removes tanks which spawn as the rescue vehicle arrives on finales confogl_addcvar confogl_disable_tank_hordes "1" // Disables natural hordes while tanks are in play