diff --git a/env/include/AFKManager.inc b/env/include/AFKManager.inc new file mode 100644 index 0000000..b2414a5 --- /dev/null +++ b/env/include/AFKManager.inc @@ -0,0 +1,24 @@ +#if defined _AFKManager_Included + #endinput +#endif +#define _AFKManager_Included + +native int GetClientIdleTime(int client); + +public SharedPlugin __pl_AFKManager = +{ + name = "AFKManager", + file = "AFKManager.smx", +#if defined REQUIRE_PLUGIN + required = 1, +#else + required = 0, +#endif +}; + +#if !defined REQUIRE_PLUGIN +public __pl_AFKManager_SetNTVOptional() +{ + MarkNativeAsOptional("GetClientIdleTime"); +} +#endif diff --git a/env/include/TeamManager.inc b/env/include/TeamManager.inc new file mode 100644 index 0000000..ab800c7 --- /dev/null +++ b/env/include/TeamManager.inc @@ -0,0 +1,36 @@ +#if defined _TeamManager_include + #endinput +#endif +#define _TeamManager_include + +/** + * Called when warmup ends + * + * @return None + */ +forward void TeamManager_WarmupEnd(); + +/** + * Returns the status of warmup + * + * @return bool inwarmup + */ +native bool TeamManager_InWarmup(); + +public SharedPlugin __pl_TeamManager = +{ + name = "TeamManager", + file = "TeamManager.smx", +#if defined REQUIRE_PLUGIN + required = 1 +#else + required = 0 +#endif +}; + +#if !defined REQUIRE_PLUGIN +public void __pl_TeamManager_SetNTVOptional() +{ + MarkNativeAsOptional("TeamManager_InWarmup"); +} +#endif diff --git a/src/zombiereloaded.sp b/src/zombiereloaded.sp index 593fa24..1318d18 100644 --- a/src/zombiereloaded.sp +++ b/src/zombiereloaded.sp @@ -173,6 +173,7 @@ public OnAllPluginsLoaded() RoundEndOnAllPluginsLoaded(); WeaponsOnAllPluginsLoaded(); ConfigOnAllPluginsLoaded(); + InfectOnAllPluginsLoaded(); } /** @@ -182,6 +183,8 @@ public OnLibraryAdded(const String:name[]) { // Forward event to modules. ConfigOnLibraryAdded(name); + RoundEndOnLibraryAdded(name); + InfectOnLibraryAdded(name); } /** @@ -190,6 +193,8 @@ public OnLibraryAdded(const String:name[]) public OnLibraryRemoved(const String:name[]) { ConfigOnLibraryRemoved(name); + RoundEndOnLibraryRemoved(name); + InfectOnLibraryRemoved(name); } /** diff --git a/src/zr/cvars.inc b/src/zr/cvars.inc index b6decf2..2a710d4 100644 --- a/src/zr/cvars.inc +++ b/src/zr/cvars.inc @@ -105,6 +105,7 @@ enum CvarsList Handle:CVAR_INFECT_SPAWNTIME_MAX, Handle:CVAR_INFECT_CONSECUTIVE_BLOCK, Handle:CVAR_INFECT_ROUND_ROBIN, + Handle:CVAR_INFECT_ROUND_ROBIN_RTD, Handle:CVAR_INFECT_WEAPONS_DROP, Handle:CVAR_INFECT_KNIFE_COOLDOWN, Handle:CVAR_INFECT_MAX_DISTANCE, @@ -337,6 +338,7 @@ CvarsCreate() g_hCvarsList[CVAR_INFECT_SPAWNTIME_MAX] = CreateConVar("zr_infect_spawntime_max", "50.0", "Maximum time from the start of the round until picking the mother zombie(s)."); g_hCvarsList[CVAR_INFECT_CONSECUTIVE_BLOCK] = CreateConVar("zr_infect_consecutive_block", "1", "Prevent a player from being chosen as mother zombie two rounds in a row."); g_hCvarsList[CVAR_INFECT_ROUND_ROBIN] = CreateConVar("zr_infect_round_robin", "1", "Use randomized round-robin (with SteamID cache) for mother zombie infection."); + g_hCvarsList[CVAR_INFECT_ROUND_ROBIN_RTD] = CreateConVar("zr_infect_round_robin_rtd", "1", "Roll the dice for players new to the random round-robin SteamID cache to make it fair for late joiners."); g_hCvarsList[CVAR_INFECT_WEAPONS_DROP] = CreateConVar("zr_infect_weapons_drop", "1", "Force player to drop all weapons on infect, disabling this will strip weapons instead."); g_hCvarsList[CVAR_INFECT_KNIFE_COOLDOWN] = CreateConVar("zr_infect_knife_cooldown", "1.0", "Time in seconds during which knife can not be used after becoming a zombie."); g_hCvarsList[CVAR_INFECT_MAX_DISTANCE] = CreateConVar("zr_infect_max_distance", "80.0", "The maximum allowed distance between a client and an attacker for a successful infection. [0.0 = Disabled]"); diff --git a/src/zr/infect.inc b/src/zr/infect.inc index 4af55d6..9b8b1ca 100644 --- a/src/zr/infect.inc +++ b/src/zr/infect.inc @@ -25,6 +25,23 @@ * ============================================================================ */ +#if defined REQUIRE_PLUGIN + #define TEMP_REQUIRE_PLUGIN + #undef REQUIRE_PLUGIN +#endif + +#tryinclude "AFKManager.inc" +#tryinclude "TeamManager.inc" + +/* Restore old REQUIRE_PLUGIN value if necessary */ +#if defined TEMP_REQUIRE_PLUGIN + #define REQUIRE_PLUGIN + #undef TEMP_REQUIRE_PLUGIN +#endif + +new bool:g_AFKManagerLoaded = false; +new bool:g_TeamManagerLoaded = false; + /** * @section Explosion flags. */ @@ -78,6 +95,7 @@ new bool:g_bInfectMotherLast[MAXPLAYERS + 1]; * SteamID cache for storing client mother zombie protection status. */ new Handle:g_hInfectMotherCycle = INVALID_HANDLE; +new Handle:g_hInfectMotherCycleRTD = INVALID_HANDLE; /** * Available mother zombie infection modes. @@ -90,6 +108,56 @@ enum InfectMode InfectMode_Range /** An absolute number of zombies infected (min to max). */ } +/** + * All plugins have finished loading. + */ +InfectOnAllPluginsLoaded() +{ + #if defined _AFKManager_Included + g_AFKManagerLoaded = LibraryExists("AFKManager"); + LogMessage("AFKManager: %s", (g_AFKManagerLoaded ? "loaded" : "not loaded")); + #endif + + #if defined _TeamManager_include + g_TeamManagerLoaded = LibraryExists("TeamManager"); + LogMessage("TeamManager: %s", (g_TeamManagerLoaded ? "loaded" : "not loaded")); + #endif +} + +/** + * A library was added. + */ +InfectOnLibraryAdded(const String:name[]) +{ + if (StrEqual(name, "AFKManager")) + { + // AFKManager loaded. + g_AFKManagerLoaded = true; + } + else if (StrEqual(name, "TeamManager")) + { + // TeamManager loaded. + g_TeamManagerLoaded = true; + } +} + +/** + * A library was removed. + */ +InfectOnLibraryRemoved(const String:name[]) +{ + if (StrEqual(name, "AFKManager")) + { + // AFKManager unloaded. + g_AFKManagerLoaded = false; + } + else if (StrEqual(name, "TeamManager")) + { + // TeamManager unloaded. + g_TeamManagerLoaded = false; + } +} + /** * Map is ending. */ @@ -103,6 +171,7 @@ InfectOnMapEnd() // Clear mother zombie round-robin cycle storage. SteamidCacheReset(g_hInfectMotherCycle); + SteamidCacheReset(g_hInfectMotherCycleRTD); } /** @@ -112,6 +181,7 @@ InfectLoad() { // Create mother zombie round-robin cycle storage. g_hInfectMotherCycle = SteamidCacheCreate(); + g_hInfectMotherCycleRTD = SteamidCacheCreate(); // Get infection sound. decl String:sound[PLATFORM_MAX_PATH]; @@ -151,6 +221,42 @@ InfectClientInit(client) // Reset mother zombie last flag. g_bInfectMotherLast[client] = false; + + new bool:infectroundrobinrtd = GetConVarBool(g_hCvarsList[CVAR_INFECT_ROUND_ROBIN_RTD]); + if (infectroundrobinrtd && !SteamidCacheClientExists(g_hInfectMotherCycleRTD, client)) + { + SteamidCacheAddClient(g_hInfectMotherCycleRTD, client); + + int players = 0; + int playersInList = 0; + for(int x = 1; x <= MaxClients; x++) + { + if(!IsClientInGame(x)) + continue; + + #if defined _AFKManager_Included + if(g_AFKManagerLoaded) + { + if(GetClientIdleTime(x) > 3 * 60) + continue; + } + #endif + + players++; + + if(SteamidCacheClientExists(g_hInfectMotherCycle, x)) + playersInList++; + } + + if(players && playersInList) + { + float mzombiechance = float(playersInList) / float(players); + float dice = GetRandomFloat(); + + if (dice < mzombiechance) + SteamidCacheAddClient(g_hInfectMotherCycle, client); + } + } } /** @@ -221,7 +327,7 @@ InfectOnClientDisconnect(client) } // Create eligible player list. - new Handle:arrayEligibleClients = INVALID_HANDLE; + new Handle:arrayEligibleClients = CreateArray(); // Create eligible client list, with no mother infect immunities new eligibleclients = ZRCreateEligibleClientList(arrayEligibleClients, true, true, true); @@ -402,6 +508,14 @@ InfectOnRoundFreezeEnd() return; } + // Warmup + #if defined _TeamManager_include + if(g_TeamManagerLoaded && TeamManager_InWarmup()) + { + return; + } + #endif + // Get min and max times. new Float:infectspawntimemin = GetConVarFloat(g_hCvarsList[CVAR_INFECT_SPAWNTIME_MIN]); new Float:infectspawntimemax = GetConVarFloat(g_hCvarsList[CVAR_INFECT_SPAWNTIME_MAX]); @@ -456,9 +570,17 @@ public Action:InfectMotherZombie(Handle:timer) // Reset timer handle. g_tInfect = INVALID_HANDLE; + // Warmup + #if defined _TeamManager_include + if(g_TeamManagerLoaded && TeamManager_InWarmup()) + { + return; + } + #endif + // Create eligible player list. - new Handle:arrayEligibleClients = INVALID_HANDLE; - new eligibleclients = ZRCreateEligibleClientList(arrayEligibleClients, true, true, true); + new Handle:arrayEligibleClients = CreateArray(); + new eligibleclients = ZRCreateEligibleClientList(arrayEligibleClients, true, true, true, true); // If there are no eligible client's then stop. if (!eligibleclients) @@ -603,6 +725,7 @@ public Action:InfectMotherZombie(Handle:timer) resetcycle = true; // Clear mother zombie round-robin cycle storage. SteamidCacheReset(g_hInfectMotherCycle); + SteamidCacheReset(g_hInfectMotherCycleRTD); // Announce start of new cycle TranslationPrintToChatAll(true, false, "Mother zombie infect cycle reset"); @@ -758,7 +881,9 @@ InfectHumanToZombie(client, attacker = -1, bool:motherinfect = false, bool:respa { g_bInfectMotherLast[client] = infectconsecutiveblock; if(infectroundrobin) + { SteamidCacheAddClient(g_hInfectMotherCycle, client); + } } // Apply effects. diff --git a/src/zr/roundend.inc b/src/zr/roundend.inc index 30ce31d..588752f 100644 --- a/src/zr/roundend.inc +++ b/src/zr/roundend.inc @@ -95,6 +95,30 @@ RoundEndOnAllPluginsLoaded() #endif } +/** + * A library was added. + */ +RoundEndOnLibraryAdded(const String:name[]) +{ + if (StrEqual(name, "sourcetvmanager")) + { + // sourcetvmanager loaded. + g_SourceTVManagerLoaded = true; + } +} + +/** + * A library was removed. + */ +RoundEndOnLibraryRemoved(const String:name[]) +{ + if (StrEqual(name, "sourcetvmanager")) + { + // sourcetvmanager unloaded. + g_SourceTVManagerLoaded = false; + } +} + /** * Map is starting. */ @@ -419,7 +443,7 @@ RoundEndTerminateRound(Float:delay, RoundEndOutcome:outcome = Restart) RoundEndBalanceTeams() { // Create eligible player list. - new Handle:arrayEligibleClients = INVALID_HANDLE; + new Handle:arrayEligibleClients = CreateArray(); new eligibleclients = ZRCreateEligibleClientList(arrayEligibleClients, true); // If there are no eligible client's then stop. diff --git a/src/zr/zombiereloaded.inc b/src/zr/zombiereloaded.inc index 4d41094..78ea91a 100644 --- a/src/zr/zombiereloaded.inc +++ b/src/zr/zombiereloaded.inc @@ -115,13 +115,11 @@ Float:ZRConvertUnitsFloat(Float:number, Float:conversion) * @param team Client is only eligible if on a team. * @param alive Client is only eligible if alive. * @param human Client is only eligible if human. - * @param immunity True to ignore clients immune from mother infect, false to count them. + * @param plugin True to ignore clients immune from mother infect, false to count them. */ -stock ZRCreateEligibleClientList(&Handle:arrayEligibleClients, bool:team = false, bool:alive = false, bool:human = false) +stock ZRCreateEligibleClientList(&Handle:arrayEligibleClients, bool:team = false, bool:alive = false, bool:human = false, bool:plugin = false) { - // Create array. - arrayEligibleClients = CreateArray(); - + new count = 0; // Populate list with eligible clients. // x = client index. for (new x = 1; x <= MaxClients; x++) @@ -151,16 +149,19 @@ stock ZRCreateEligibleClientList(&Handle:arrayEligibleClients, bool:team = false } // Ask plugin API what they think about our client - if (APIOnClientMotherZombieEligible(x) == Plugin_Handled) + if (plugin && APIOnClientMotherZombieEligible(x) == Plugin_Handled) { continue; } // Add eligible client to array. - PushArrayCell(arrayEligibleClients, x); + if (arrayEligibleClients != INVALID_HANDLE) + PushArrayCell(arrayEligibleClients, x); + + count++; } - return GetArraySize(arrayEligibleClients); + return count; } /**