7 Commits
13 changed files with 1036 additions and 41 deletions
@@ -325,6 +325,34 @@ zr_infect_mzombie_countdown "0"
// Default: "0"
zr_infect_mzombie_respawn "0"
// Exclude fake clients (bots, SourceTV) from being picked as mother zombie. Excluded players are still used as a last resort when no one else can be picked. [0 = Include | 1 = Exclude]
// Default: "1"
zr_infect_mzombie_exclude_fakeclients "1"
// Exclude AFK players (AFKManager idle time of at least zr_infect_mzombie_exclude_afk_time) from being picked as mother zombie. Requires the AFKManager plugin. Excluded players are still used as a last resort when no one else can be picked. [0 = Include | 1 = Exclude]
// Default: "1"
zr_infect_mzombie_exclude_afk "1"
// Seconds a player has to be idle (according to AFKManager) to count as AFK for mother zombie selection.
// Default: "180"
zr_infect_mzombie_exclude_afk_time "180"
// Exclude autism bots (IsClientAutismBot from the BotTargeting plugin) from being picked as mother zombie. Requires the BotTargeting plugin. Excluded players are still used as a last resort when no one else can be picked. [0 = Include | 1 = Exclude]
// Default: "1"
zr_infect_mzombie_exclude_autismbots "1"
// The zr_infect_mzombie_exclude_* cvars only apply while at least this many REAL players are present.
// A REAL player is a player who is:
// - NOT a fake client
// - NOT AFK (AFKManager idle time >= zr_infect_mzombie_exclude_afk_time)
// - NOT an autism bot (BotTargeting)
// - NOT in spectate/unassigned
// - NOT dead
// Example: 5 = while there are fewer than 5 real players, fake clients, AFKs and autism bots can be mother zombie too.
// [0 = Always apply the exclusions]
// Default: "0"
zr_infect_mzombie_exclude_min_real "0"
// Maximum time from the start of the round until picking the mother zombie(s).
// Default: "50.0"
zr_infect_spawntime_max "50.0"
@@ -1143,6 +1143,69 @@
"en" "HP: {1}"
}
// ===========================
// ZVolume (module)
// ===========================
"Volume_Menu_Title"
{
"en" "[ZR] Volume Control Menu\nCommand: !zvolume"
}
"Zombie Voice"
{
"en" "Zombie Voice"
}
"Ambient"
{
"en" "Ambient"
}
"ZombieVoice_Usage"
{
"en" "Usage: zr_zombie_volume <0 - 100>"
}
"ZombieVoice_Title_Menu"
{
"#format" "{1:d}"
"en" "[ZR] Zombie Voice Volume (Current: {1:d}%)"
}
"ZombieVoice_CommandUsage"
{
"en" "Command: !zombie_volume <1-100>"
}
"Set_ZombieVoice_Volume"
{
"#format" "{1:d}"
"en" "Set Zombie Voice Volume to {lightgreen}{1:d}%."
}
"Ambient_Usage"
{
"en" "Usage: zr_ambient_volume <0 - 100>"
}
"Ambient_Title_Menu"
{
"#format" "{1:d}"
"en" "[ZR] Ambient Volume (Current: {1:d}%)"
}
"Ambient_CommandUsage"
{
"en" "Command: !ambient_volume <1-100>"
}
"Set_Ambient_Volume"
{
"#format" "{1:d}"
"en" "Set Ambient Volume to {lightgreen}{1:d}%."
}
// ===========================
// Volumetric features (module)
// ===========================
@@ -325,6 +325,34 @@ zr_infect_mzombie_countdown "0"
// Default: "0"
zr_infect_mzombie_respawn "0"
// Exclude fake clients (bots, SourceTV) from being picked as mother zombie. Excluded players are still used as a last resort when no one else can be picked. [0 = Include | 1 = Exclude]
// Default: "1"
zr_infect_mzombie_exclude_fakeclients "1"
// Exclude AFK players (AFKManager idle time of at least zr_infect_mzombie_exclude_afk_time) from being picked as mother zombie. Requires the AFKManager plugin. Excluded players are still used as a last resort when no one else can be picked. [0 = Include | 1 = Exclude]
// Default: "1"
zr_infect_mzombie_exclude_afk "1"
// Seconds a player has to be idle (according to AFKManager) to count as AFK for mother zombie selection.
// Default: "180"
zr_infect_mzombie_exclude_afk_time "180"
// Exclude autism bots (IsClientAutismBot from the BotTargeting plugin) from being picked as mother zombie. Requires the BotTargeting plugin. Excluded players are still used as a last resort when no one else can be picked. [0 = Include | 1 = Exclude]
// Default: "1"
zr_infect_mzombie_exclude_autismbots "1"
// The zr_infect_mzombie_exclude_* cvars only apply while at least this many REAL players are present.
// A REAL player is a player who is:
// - NOT a fake client
// - NOT AFK (AFKManager idle time >= zr_infect_mzombie_exclude_afk_time)
// - NOT an autism bot (BotTargeting)
// - NOT in spectate/unassigned
// - NOT dead
// Example: 5 = while there are fewer than 5 real players, fake clients, AFKs and autism bots can be mother zombie too.
// [0 = Always apply the exclusions]
// Default: "0"
zr_infect_mzombie_exclude_min_real "0"
// Maximum time from the start of the round until picking the mother zombie(s).
// Default: "50.0"
zr_infect_spawntime_max "50.0"
+30
View File
@@ -0,0 +1,30 @@
#if defined _BotTargeting_Included
#endinput
#endif
#define _BotTargeting_Included
/**
* Returns whether the client is one of the BotTargeting "autism bot" SteamIDs.
*
* @param client Client index. Must be in-game and authorized, otherwise a native error is thrown.
* @return True if the client is an autism bot, false otherwise.
*/
native bool IsClientAutismBot(int client);
public SharedPlugin __pl_BotTargeting =
{
name = "BotTargeting",
file = "BotTargeting.smx",
#if defined REQUIRE_PLUGIN
required = 1,
#else
required = 0,
#endif
};
#if !defined REQUIRE_PLUGIN
public __pl_BotTargeting_SetNTVOptional()
{
MarkNativeAsOptional("IsClientAutismBot");
}
#endif
+26
View File
@@ -0,0 +1,26 @@
#if defined _unloze_playtime_included_
#endinput
#endif
#define _unloze_playtime_included_
/**
* @returns the average playtime of all connected players excluding autism bots, fakeclients, AFKS and nosteamers
*/
native int GetAveragePlayerTimeOnServer();
/**
* @returns the players rtv amount boost
*/
native int GetPlayerWorthRTV_boost_(int client);
/**
* returns a players hours for a specific server
*
* @int the actual client
* @int Game time hours that the client has on the server.
* @int Game time minutes the client has on the server.
*/
native int GetPlayerTier_native(int client); //just the clients tier
forward void GetPlayerHoursServer(int client, int hours, int minutes);
forward void GetPlayerTier(int client, int tier);
+1
View File
@@ -351,6 +351,7 @@ public OnClientCookiesCached(client)
ClassOnCookiesCached(client);
WeaponsOnCookiesCached(client);
ZHPOnCookiesCached(client);
ZVolumeOnCookiesCached(client);
}
/**
+1
View File
@@ -45,6 +45,7 @@ CommandsInit()
ZTele_OnCommandsCreate();
ZHPOnCommandsCreate();
VolOnCommandsCreate();
ZVolumeOnCommandsCreate();
ZombieSoundsOnCommandsCreate();
ImmunityOnCommandsCreate();
+1
View File
@@ -34,6 +34,7 @@ CookiesInit()
ClassOnCookiesCreate();
WeaponsOnCookiesCreate();
ZHPOnCookiesCreate();
ZVolumeOnCookiesCreate();
}
/**
+12
View File
@@ -116,6 +116,11 @@ enum CvarsList
Handle:CVAR_INFECT_MZOMBIE_MAX,
Handle:CVAR_INFECT_MZOMBIE_COUNTDOWN,
Handle:CVAR_INFECT_MZOMBIE_RESPAWN,
Handle:CVAR_INFECT_MZOMBIE_EXCLUDE_FAKECLIENTS,
Handle:CVAR_INFECT_MZOMBIE_EXCLUDE_AFK,
Handle:CVAR_INFECT_MZOMBIE_EXCLUDE_AFK_TIME,
Handle:CVAR_INFECT_MZOMBIE_EXCLUDE_AUTISMBOTS,
Handle:CVAR_INFECT_MZOMBIE_EXCLUDE_MIN_REAL,
Handle:CVAR_INFECT_EXPLOSION,
Handle:CVAR_INFECT_FIREBALL,
Handle:CVAR_INFECT_SMOKE,
@@ -152,6 +157,7 @@ enum CvarsList
Handle:CVAR_SEFFECTS_DEATH,
Handle:CVAR_SEFFECTS_COMMAND_LIMIT,
Handle:CVAR_SEFFECTS_COMMAND_TIMESPAN,
Handle:CVAR_SEFFECTS_VOLUME,
Handle:CVAR_AMBIENTSOUNDS,
Handle:CVAR_AMBIENTSOUNDS_FILE,
Handle:CVAR_AMBIENTSOUNDS_LENGTH,
@@ -337,6 +343,11 @@ CvarsCreate()
g_hCvarsList[CVAR_INFECT_MZOMBIE_MAX] = CreateConVar("zr_infect_mzombie_max", "3", "Maximum number of mother zombies. Range mode only, cannot be zero.");
g_hCvarsList[CVAR_INFECT_MZOMBIE_COUNTDOWN] = CreateConVar("zr_infect_mzombie_countdown", "0", "Counts down to the first infection of the round. Countdown is printed in the middle of the client's screen.");
g_hCvarsList[CVAR_INFECT_MZOMBIE_RESPAWN] = CreateConVar("zr_infect_mzombie_respawn", "0", "Teleport mother zombies back to spawn on infect.");
g_hCvarsList[CVAR_INFECT_MZOMBIE_EXCLUDE_FAKECLIENTS] = CreateConVar("zr_infect_mzombie_exclude_fakeclients", "1", "Exclude fake clients (bots, SourceTV) from being picked as mother zombie. Excluded players are still used as a last resort when no one else can be picked. [0 = Include | 1 = Exclude]");
g_hCvarsList[CVAR_INFECT_MZOMBIE_EXCLUDE_AFK] = CreateConVar("zr_infect_mzombie_exclude_afk", "1", "Exclude AFK players (AFKManager idle time of at least zr_infect_mzombie_exclude_afk_time) from being picked as mother zombie. Requires the AFKManager plugin. Excluded players are still used as a last resort when no one else can be picked. [0 = Include | 1 = Exclude]");
g_hCvarsList[CVAR_INFECT_MZOMBIE_EXCLUDE_AFK_TIME] = CreateConVar("zr_infect_mzombie_exclude_afk_time", "180", "Seconds a player has to be idle (according to AFKManager) to count as AFK for mother zombie selection.", _, true, 1.0);
g_hCvarsList[CVAR_INFECT_MZOMBIE_EXCLUDE_AUTISMBOTS] = CreateConVar("zr_infect_mzombie_exclude_autismbots", "1", "Exclude autism bots (IsClientAutismBot from the BotTargeting plugin) from being picked as mother zombie. Requires the BotTargeting plugin. Excluded players are still used as a last resort when no one else can be picked. [0 = Include | 1 = Exclude]");
g_hCvarsList[CVAR_INFECT_MZOMBIE_EXCLUDE_MIN_REAL] = CreateConVar("zr_infect_mzombie_exclude_min_real", "0", "The zr_infect_mzombie_exclude_* cvars only apply while at least this many REAL players are present. A REAL player is a player who is NOT a fake client, NOT AFK (AFKManager idle time >= zr_infect_mzombie_exclude_afk_time), NOT an autism bot (BotTargeting), NOT in spectate/unassigned and NOT dead. Example: 5 = while there are fewer than 5 real players, fake clients, AFKs and autism bots can be mother zombie too. [0 = Always apply the exclusions]", _, true, 0.0);
g_hCvarsList[CVAR_INFECT_SPAWNTIME_MIN] = CreateConVar("zr_infect_spawntime_min", "30.0", "Minimum time from the start of the round until picking the mother zombie(s).");
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.");
@@ -450,6 +461,7 @@ CvarsCreate()
g_hCvarsList[CVAR_SEFFECTS_DEATH] = CreateConVar("zr_seffects_death", "1", "Emit a death sound when a zombie dies.");
g_hCvarsList[CVAR_SEFFECTS_COMMAND_LIMIT] = CreateConVar("zr_seffects_command_limit", "4", "Number of sound commands allowed within the time span, or total limit if time span is disabled. ['0' = Disable sound command limit]");
g_hCvarsList[CVAR_SEFFECTS_COMMAND_TIMESPAN] = CreateConVar("zr_seffects_command_timespan", "10", "Time span for sound command limiter (in seconds). ['0' = Disable time span check | positive number = Time span]");
g_hCvarsList[CVAR_SEFFECTS_VOLUME] = CreateConVar("zr_seffects_volume", "0.8", "Default volume of the zombie voice sounds, used as each client's starting !zsound value. [1.0 = Max volume | 0.0001 = Not audible]");
// Ambient Sounds
g_hCvarsList[CVAR_AMBIENTSOUNDS] = CreateConVar("zr_ambientsounds", "1", "Play an ambient sound to all players during gameplay.");
+265 -20
View File
@@ -32,6 +32,7 @@
#tryinclude "AFKManager.inc"
#tryinclude "TeamManager.inc"
#include <BotTargeting>
/* Restore old REQUIRE_PLUGIN value if necessary */
#if defined TEMP_REQUIRE_PLUGIN
@@ -41,6 +42,7 @@
new bool:g_AFKManagerLoaded = false;
new bool:g_TeamManagerLoaded = false;
new bool:g_BotTargetingLoaded = false;
/**
* @section Explosion flags.
@@ -122,6 +124,9 @@ InfectOnAllPluginsLoaded()
g_TeamManagerLoaded = LibraryExists("TeamManager");
LogMessage("TeamManager: %s", (g_TeamManagerLoaded ? "loaded" : "not loaded"));
#endif
g_BotTargetingLoaded = LibraryExists("BotTargeting");
LogMessage("BotTargeting: %s", (g_BotTargetingLoaded ? "loaded" : "not loaded"));
}
/**
@@ -139,6 +144,11 @@ InfectOnLibraryAdded(const String:name[])
// TeamManager loaded.
g_TeamManagerLoaded = true;
}
else if (StrEqual(name, "BotTargeting"))
{
// BotTargeting loaded.
g_BotTargetingLoaded = true;
}
}
/**
@@ -156,6 +166,200 @@ InfectOnLibraryRemoved(const String:name[])
// TeamManager unloaded.
g_TeamManagerLoaded = false;
}
else if (StrEqual(name, "BotTargeting"))
{
// BotTargeting unloaded.
g_BotTargetingLoaded = false;
}
}
/**
* Checks if a (non-fake) client is AFK according to AFKManager.
*
* @param client The client index. Must be in-game and not a fake client.
* @return True if AFKManager is loaded and the client has been idle for
* at least zr_infect_mzombie_exclude_afk_time seconds.
*/
bool:InfectIsClientAFK(client)
{
#if defined _AFKManager_Included
if (g_AFKManagerLoaded && GetClientIdleTime(client) >= GetConVarInt(g_hCvarsList[CVAR_INFECT_MZOMBIE_EXCLUDE_AFK_TIME]))
{
return true;
}
#endif
return false;
}
/**
* Checks if a (non-fake) client is an autism bot according to BotTargeting.
*
* @param client The client index. Must be in-game and not a fake client.
* @return True if BotTargeting is loaded and says the client is an autism bot.
*/
bool:InfectIsClientAutismBot(client)
{
// The native throws on unauthorized clients, so check that first.
return (g_BotTargetingLoaded && IsClientAuthorized(client) && IsClientAutismBot(client));
}
/**
* Checks if a client falls in one of the categories excluded from mother zombie
* by the zr_infect_mzombie_exclude_fakeclients/afk/autismbots cvars.
*
* This only looks at the category cvars, not at zr_infect_mzombie_exclude_min_real.
*
* @param client The client index. Must be in-game.
* @return True if the client is excluded, false otherwise.
*/
bool:InfectIsClientMotherExcluded(client)
{
// Bots / SourceTV.
if (IsFakeClient(client))
{
return GetConVarBool(g_hCvarsList[CVAR_INFECT_MZOMBIE_EXCLUDE_FAKECLIENTS]);
}
// AFK players.
if (GetConVarBool(g_hCvarsList[CVAR_INFECT_MZOMBIE_EXCLUDE_AFK]) && InfectIsClientAFK(client))
{
return true;
}
// Autism bots.
if (GetConVarBool(g_hCvarsList[CVAR_INFECT_MZOMBIE_EXCLUDE_AUTISMBOTS]) && InfectIsClientAutismBot(client))
{
return true;
}
return false;
}
/**
* Checks if a client is a "real" player, as used by zr_infect_mzombie_exclude_min_real.
*
* A real player is in-game, on a team (not spectate/unassigned), alive, and is
* not a fake client, not AFK and not an autism bot. This definition is fixed and
* does not depend on the zr_infect_mzombie_exclude_* category cvars.
*
* @param client The client index.
* @return True if the client is a real player, false otherwise.
*/
bool:InfectIsClientRealPlayer(client)
{
if (!IsClientInGame(client) || !ZRIsClientOnTeam(client) || !IsPlayerAlive(client))
{
return false;
}
if (IsFakeClient(client) || InfectIsClientAFK(client) || InfectIsClientAutismBot(client))
{
return false;
}
return true;
}
/**
* Checks if the mother zombie exclusions currently apply, based on
* zr_infect_mzombie_exclude_min_real.
*
* @return True if there are at least zr_infect_mzombie_exclude_min_real real
* players (or the cvar is 0), false otherwise.
*/
bool:InfectMotherExclusionsActive()
{
new minreal = GetConVarInt(g_hCvarsList[CVAR_INFECT_MZOMBIE_EXCLUDE_MIN_REAL]);
if (minreal <= 0)
{
return true;
}
new realplayers = 0;
for (new x = 1; x <= MaxClients; x++)
{
if (InfectIsClientRealPlayer(x))
{
realplayers++;
}
}
return (realplayers >= minreal);
}
/**
* Builds the mother zombie candidate lists.
*
* All alive humans on a team that the API allows are split into two lists:
* - arrayEligibleClients: regular candidates. Only these go through the
* consecutive block / round-robin cycle logic.
* - arrayExcludedClients: fake clients, AFKs and autism bots excluded by the
* zr_infect_mzombie_exclude_* cvars. Only used as a last resort when the
* regular candidates can't fill the mother zombie count.
*
* When zr_infect_mzombie_exclude_min_real isn't reached, nobody is excluded.
*
* @param arrayEligibleClients Array to push regular candidates into.
* @param arrayExcludedClients Array to push excluded candidates into.
* @return Number of regular candidates.
*/
InfectCreateMotherCandidateLists(Handle:arrayEligibleClients, Handle:arrayExcludedClients)
{
new Handle:arrayAllClients = CreateArray();
new allclients = ZRCreateEligibleClientList(arrayAllClients, true, true, true, true);
new bool:exclude = InfectMotherExclusionsActive();
new eligibleclients = 0;
for (new n = 0; n < allclients; n++)
{
new client = GetArrayCell(arrayAllClients, n);
if (exclude && InfectIsClientMotherExcluded(client))
{
PushArrayCell(arrayExcludedClients, client);
}
else
{
PushArrayCell(arrayEligibleClients, client);
eligibleclients++;
}
}
CloseHandle(arrayAllClients);
return eligibleclients;
}
/**
* Picks a random client from the list that is still human.
*
* @param arrayClients List of client indexes.
* @return Client index, or -1 if nobody in the list is human.
*/
InfectGetRandomHumanFromList(Handle:arrayClients)
{
new Handle:arrayHumans = CreateArray();
new size = GetArraySize(arrayClients);
for (new n = 0; n < size; n++)
{
new client = GetArrayCell(arrayClients, n);
if (IsClientInGame(client) && InfectIsClientHuman(client))
{
PushArrayCell(arrayHumans, client);
}
}
new client = -1;
new humans = GetArraySize(arrayHumans);
if (humans)
{
client = GetArrayCell(arrayHumans, Math_GetRandomInt(0, humans - 1));
}
CloseHandle(arrayHumans);
return client;
}
/**
@@ -234,13 +438,10 @@ InfectClientInit(client)
if(!IsClientInGame(x))
continue;
#if defined _AFKManager_Included
if(g_AFKManagerLoaded)
{
if(GetClientIdleTime(x) > 3 * 60)
// Clients that can never be mother zombie are never in the cycle,
// don't let them water down the ratio.
if(InfectIsClientMotherExcluded(x))
continue;
}
#endif
players++;
@@ -578,15 +779,18 @@ public Action:InfectMotherZombie(Handle:timer)
}
#endif
// Create eligible player list.
// Create candidate lists: regular candidates and excluded (fake clients, AFKs, autism bots).
new Handle:arrayEligibleClients = CreateArray();
new eligibleclients = ZRCreateEligibleClientList(arrayEligibleClients, true, true, true, true);
new Handle:arrayExcludedClients = CreateArray();
new eligibleclients = InfectCreateMotherCandidateLists(arrayEligibleClients, arrayExcludedClients);
new excludedclients = GetArraySize(arrayExcludedClients);
// If there are no eligible client's then stop.
if (!eligibleclients)
// If nobody is alive on a team (everyone dead or in spectate), then stop.
if (!eligibleclients && !excludedclients)
{
// Destroy handle.
// Destroy handles.
CloseHandle(arrayEligibleClients);
CloseHandle(arrayExcludedClients);
return;
}
@@ -688,6 +892,15 @@ public Action:InfectMotherZombie(Handle:timer)
}
}
// Split excluded clients too, so the last resort prefers those who weren't mother zombie last round.
new Handle:aExcludedMain = CreateArray();
new Handle:aExcludedAlt = CreateArray();
for (new n = 0; n < excludedclients; n++)
{
new client = GetArrayCell(arrayExcludedClients, n);
PushArrayCell(g_bInfectMotherLast[client] ? aExcludedAlt : aExcludedMain, client);
}
// Remove mother zombie last flag from all players.
for (int client = 0; client <= MAXPLAYERS; client++)
{
@@ -699,14 +912,17 @@ public Action:InfectMotherZombie(Handle:timer)
// Infect players.
new infected = 0;
new infected_human_to_zombie_failed = 0;
while (infected < mothercount)
// Only run the regular selection (and its cycle reset) if there are regular candidates.
// If there are none, all alive players are excluded ones and the cycle of the regular
// players must not be touched.
while (eligibleclients && infected < mothercount)
{
// Infect one of the main candidates.
if (candidatesMain)
{
// Get a random array index.
new i = Math_GetRandomInt(0, candidatesMain - 1);
// Get the client stored in the random array index.
new client = GetArrayCell(aCandidatesMain, i);
@@ -737,7 +953,6 @@ public Action:InfectMotherZombie(Handle:timer)
{
// Get a random array index.
new i = Math_GetRandomInt(0, candidatesAlt - 1);
// Get the client stored in the random array index.
new client = GetArrayCell(aCandidatesAlt, i);
@@ -751,11 +966,14 @@ public Action:InfectMotherZombie(Handle:timer)
}
else // We have no candidates at all. time to fuck over random people!
{
// Infect player.
if (eligibleclients)
// Pick a random regular candidate that is still human (ignores the consecutive block).
new client = InfectGetRandomHumanFromList(arrayEligibleClients);
if (client == -1)
{
new randindex = Math_GetRandomInt(0, eligibleclients - 1);
new client = GetArrayCell(arrayEligibleClients, randindex);
break; // All regular candidates are zombies already, fall through to the excluded clients.
}
// Infect player.
if (InfectHumanToZombie(client, _, true))
{
infected++;
@@ -770,11 +988,35 @@ public Action:InfectMotherZombie(Handle:timer)
}
}
}
}
}
// Last resort: not enough regular candidates, so infect excluded clients (fake clients, AFKs,
// autism bots). A round must not start without mother zombies.
// Prefer those who weren't mother zombie last round.
while (infected < mothercount)
{
new Handle:aExcluded = INVALID_HANDLE;
if (GetArraySize(aExcludedMain))
{
aExcluded = aExcludedMain;
}
else if (GetArraySize(aExcludedAlt))
{
aExcluded = aExcludedAlt;
}
else
{
break; //prevent infinite loop. should be very unlikely we end up here.
}
break; // Nobody left.
}
new i = Math_GetRandomInt(0, GetArraySize(aExcluded) - 1);
new client = GetArrayCell(aExcluded, i);
RemoveFromArray(aExcluded, i);
if (IsClientInGame(client) && InfectIsClientHuman(client) && InfectHumanToZombie(client, _, true))
{
infected++;
}
}
@@ -783,8 +1025,11 @@ public Action:InfectMotherZombie(Handle:timer)
// Destroy client list.
CloseHandle(arrayEligibleClients);
CloseHandle(arrayExcludedClients);
CloseHandle(aCandidatesMain);
CloseHandle(aCandidatesAlt);
CloseHandle(aExcludedMain);
CloseHandle(aExcludedAlt);
}
/**
+42 -4
View File
@@ -38,6 +38,7 @@
#include "zr/soundeffects/voice"
#include "zr/soundeffects/ambientsounds"
#include "zr/soundeffects/zombiesounds"
#include "zr/soundeffects/volumecontrol"
/**
* Load sound effects data.
@@ -172,8 +173,17 @@ SEffectsEmitAmbientSound(const String:sound[], Float:ambientvolume = 1.0, client
if (ZRIsClientValid(client))
{
// Scale by this client's personal ambient volume setting.
new Float:finalvolume = ambientvolume * ZVolume_GetAmbientVolume(client);
// If client has muted ambient sounds, then stop.
if (finalvolume <= 0.0)
{
return;
}
// Emit ambient sound.
EmitSoundToClient(client, sound, SOUND_FROM_PLAYER, SOUND_AMBIENT_CHANNEL, _, _, ambientvolume);
EmitSoundToClient(client, sound, SOUND_FROM_PLAYER, SOUND_AMBIENT_CHANNEL, _, _, finalvolume);
// Flag client that sound is playing.
bAmbientSoundsIsPlaying[client] = true;
@@ -188,8 +198,17 @@ SEffectsEmitAmbientSound(const String:sound[], Float:ambientvolume = 1.0, client
continue;
}
// Scale by this client's personal ambient volume setting.
new Float:finalvolume = ambientvolume * ZVolume_GetAmbientVolume(x);
// If client has muted ambient sounds, then skip them.
if (finalvolume <= 0.0)
{
continue;
}
// Emit ambient sound.
EmitSoundToClient(x, sound, SOUND_FROM_PLAYER, SNDCHAN_AUTO, _, _, ambientvolume);
EmitSoundToClient(x, sound, SOUND_FROM_PLAYER, SNDCHAN_AUTO, _, _, finalvolume);
}
}
}
@@ -233,6 +252,25 @@ SEffectsEmitSoundFromClient(client, const String:sound[], level = SNDLEVEL_NORMA
// Precache sound before playing.
PrecacheSound(sound);
// Emit sound from client.
EmitSoundToAll(sound, client, _, level);
// x = receiving client index.
for (new x = 1; x <= MaxClients; x++)
{
// If client isn't in-game, then skip.
if (!IsClientInGame(x))
{
continue;
}
// Get this client's personal zombie-voice volume.
new Float:zvolume = ZVolume_GetZSoundVolume(x);
// If client has muted zombie voice sounds, then skip them.
if (zvolume <= 0.0)
{
continue;
}
// Emit sound from client, at this receiving client's personal volume.
EmitSoundToClient(x, sound, client, _, level, _, zvolume);
}
}
+408
View File
@@ -0,0 +1,408 @@
/*
* ============================================================================
*
* Zombie:Reloaded
*
* File: volumecontrol.inc
* Type: Core
* Description: Basic volume control for player to change.
*
* Copyright (C) 2009-2013 Greyscale, Richard Helgeby
*
* 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 <http://www.gnu.org/licenses/>.
*
* ============================================================================
*/
/**
* Cookie names for client volume settings.
*/
#define ZVOLUME_COOKIE_ZSOUND "zr_zombie_volume"
#define ZVOLUME_COOKIE_AMBIENT "zr_ambient_volume"
/**
* Cookie handles for client volume settings.
*/
new Handle:g_hZSoundVolumeCookie = INVALID_HANDLE;
new Handle:g_hAmbientVolumeCookie = INVALID_HANDLE;
/**
* Per-client volume, as a fraction (0.0 - 1.0), applied on top of sounds
* emitted through SEffectsEmitSoundFromClient() and SEffectsEmitAmbientSound().
*/
new Float:zfZSoundVolume[MAXPLAYERS + 1];
new Float:zfAmbientVolume[MAXPLAYERS + 1];
/**
* Create commands specific to ZVolume.
*/
ZVolumeOnCommandsCreate()
{
RegConsoleCmd("sm_zvol", Command_ZVolume, "Brings up the zvolume menu");
RegConsoleCmd("sm_zvolume", Command_ZVolume, "Brings up the zvolume menu");
RegConsoleCmd("sm_zsound", Command_ZSound, "Brings up the zsound menu");
RegConsoleCmd("sm_zombie_volume", Command_ZSound, "Brings up the zsound menu");
RegConsoleCmd("sm_zambient", Command_ZAmbient, "Brings up the ambient menu");
RegConsoleCmd("sm_ambient_volume", Command_ZAmbient, "Brings up the ambient menu");
}
/**
* Create ZVolume-related cookies here.
*/
ZVolumeOnCookiesCreate()
{
if (g_hZSoundVolumeCookie == INVALID_HANDLE)
{
g_hZSoundVolumeCookie = RegClientCookie(ZVOLUME_COOKIE_ZSOUND, "Control Zombie Sound.", CookieAccess_Protected);
}
if (g_hAmbientVolumeCookie == INVALID_HANDLE)
{
g_hAmbientVolumeCookie = RegClientCookie(ZVOLUME_COOKIE_AMBIENT, "Control Ambient Sound.", CookieAccess_Protected);
}
}
/**
* Called once a client's saved cookies have been loaded from the database.
*
* @param client The client index.
*/
ZVolumeOnCookiesCached(client)
{
decl String:sValue[8];
sValue[0] = '\0';
GetClientCookie(client, g_hZSoundVolumeCookie, sValue, sizeof(sValue));
if (!sValue[0])
{
new Float:seffectsvolume = GetConVarFloat(g_hCvarsList[CVAR_SEFFECTS_VOLUME]);
IntToString(RoundToNearest(seffectsvolume * 100.0), sValue, sizeof(sValue));
}
SetZombieVoiceVolume(client, StringToInt(sValue));
sValue[0] = '\0';
GetClientCookie(client, g_hAmbientVolumeCookie, sValue, sizeof(sValue));
if (!sValue[0])
{
new Float:ambientvolume = GetConVarFloat(g_hCvarsList[CVAR_AMBIENTSOUNDS_VOLUME]);
IntToString(RoundToNearest(ambientvolume * 100.0), sValue, sizeof(sValue));
}
SetAmbientVolume(client, StringToInt(sValue));
}
/**
* Returns the client's personal zombie-voice volume, as a fraction (0.0 - 1.0).
*
* @param client The client index.
*/
Float:ZVolume_GetZSoundVolume(client)
{
return zfZSoundVolume[client];
}
/**
* Returns the client's personal ambient-sound volume, as a fraction (0.0 - 1.0).
*
* @param client The client index.
*/
Float:ZVolume_GetAmbientVolume(client)
{
return zfAmbientVolume[client];
}
/**
* Command callback (sm_zvol, sm_zvolume)
* Opens the volume control menu.
*/
public Action:Command_ZVolume(client, args)
{
if (IsClientInGame(client))
{
VolumeControlMenu(client);
return Plugin_Handled;
}
return Plugin_Handled;
}
VolumeControlMenu(client)
{
new Handle:menu = CreateMenu(VolumeControlMenuHandler);
decl String:sMenuTitle[128];
Format(sMenuTitle, sizeof(sMenuTitle), "%t", "Volume_Menu_Title");
SetMenuTitle(menu, sMenuTitle);
decl String:sItem[64];
Format(sItem, sizeof(sItem), "%t", "Zombie Voice");
AddMenuItem(menu, "1", sItem);
Format(sItem, sizeof(sItem), "%t", "Ambient");
AddMenuItem(menu, "2", sItem);
SetMenuExitBackButton(menu, true);
DisplayMenu(menu, client, MENU_TIME_FOREVER);
}
public VolumeControlMenuHandler(Handle:menu, MenuAction:action, param1, param2)
{
switch (action)
{
case MenuAction_Select:
{
decl String:info[32];
GetMenuItem(menu, param2, info, sizeof(info));
if (StrEqual(info, "1", false))
{
ZombieVoiceVolumeMenu(param1);
}
else if (StrEqual(info, "2"))
{
AmbientVolumeMenu(param1);
}
}
case MenuAction_Cancel:
{
if (param2 == MenuCancel_ExitBack)
{
ZMenuMain(param1);
}
}
case MenuAction_End:
{
CloseHandle(menu);
}
}
return 0;
}
// Zombie Voice Section
/**
* Command callback (sm_zsound, sm_zombie_volume)
*/
public Action:Command_ZSound(client, args)
{
if (args == 0)
{
ZombieVoiceVolumeMenu(client);
return Plugin_Handled;
}
decl String:sArg[32];
GetCmdArg(1, sArg, sizeof(sArg));
new iVolume = StringToInt(sArg);
if (iVolume < 0 || iVolume > 100)
{
TranslationReplyToCommand(client, "ZombieVoice_Usage");
return Plugin_Handled;
}
else if (0 <= iVolume <= 100)
{
SetZombieVoiceVolume(client, iVolume);
return Plugin_Handled;
}
return Plugin_Handled;
}
ZombieVoiceVolumeMenu(client)
{
new Handle:menu = CreateMenu(ZombieVoiceVolumeMenuHandler);
decl String:sMenuTitle[128];
Format(sMenuTitle, sizeof(sMenuTitle), "%t\n%t", "ZombieVoice_Title_Menu", RoundToNearest(zfZSoundVolume[client] * 100), "ZombieVoice_CommandUsage");
SetMenuTitle(menu, sMenuTitle);
AddMenuItem(menu, "1", "100%");
AddMenuItem(menu, "2", "80%");
AddMenuItem(menu, "3", "60%");
AddMenuItem(menu, "4", "40%");
AddMenuItem(menu, "5", "20%");
SetMenuExitBackButton(menu, true);
DisplayMenu(menu, client, MENU_TIME_FOREVER);
}
public ZombieVoiceVolumeMenuHandler(Handle:menu, MenuAction:action, param1, param2)
{
switch (action)
{
case MenuAction_Select:
{
decl String:info[32];
GetMenuItem(menu, param2, info, sizeof(info));
if (StrEqual(info, "1", false))
{
SetZombieVoiceVolume(param1, 100);
ZombieVoiceVolumeMenu(param1);
}
else if (StrEqual(info, "2"))
{
SetZombieVoiceVolume(param1, 80);
ZombieVoiceVolumeMenu(param1);
}
else if (StrEqual(info, "3"))
{
SetZombieVoiceVolume(param1, 60);
ZombieVoiceVolumeMenu(param1);
}
else if (StrEqual(info, "4"))
{
SetZombieVoiceVolume(param1, 40);
ZombieVoiceVolumeMenu(param1);
}
else if (StrEqual(info, "5"))
{
SetZombieVoiceVolume(param1, 20);
ZombieVoiceVolumeMenu(param1);
}
}
case MenuAction_Cancel:
{
if (param2 == MenuCancel_ExitBack)
{
VolumeControlMenu(param1);
}
}
case MenuAction_End:
{
CloseHandle(menu);
}
}
return 0;
}
SetZombieVoiceVolume(client, volume)
{
zfZSoundVolume[client] = volume * 0.01;
decl String:sValue[8];
IntToString(volume, sValue, sizeof(sValue));
SetClientCookie(client, g_hZSoundVolumeCookie, sValue);
if (IsClientInGame(client))
{
TranslationPrintToChat(client, "Set_ZombieVoice_Volume", volume);
}
}
// Ambient Section
/**
* Command callback (sm_zambient, sm_ambient_volume)
*/
public Action:Command_ZAmbient(client, args)
{
if (args == 0)
{
AmbientVolumeMenu(client);
return Plugin_Handled;
}
decl String:sArg[32];
GetCmdArg(1, sArg, sizeof(sArg));
new iVolume = StringToInt(sArg);
if (iVolume < 0 || iVolume > 100)
{
TranslationReplyToCommand(client, "Ambient_Usage");
return Plugin_Handled;
}
else if (0 <= iVolume <= 100)
{
SetAmbientVolume(client, iVolume);
return Plugin_Handled;
}
return Plugin_Handled;
}
AmbientVolumeMenu(client)
{
new Handle:menu = CreateMenu(AmbientVolumeMenuHandler);
decl String:sMenuTitle[128];
Format(sMenuTitle, sizeof(sMenuTitle), "%t\n%t", "Ambient_Title_Menu", RoundToNearest(zfAmbientVolume[client] * 100), "Ambient_CommandUsage");
SetMenuTitle(menu, sMenuTitle);
AddMenuItem(menu, "1", "100%");
AddMenuItem(menu, "2", "80%");
AddMenuItem(menu, "3", "60%");
AddMenuItem(menu, "4", "40%");
AddMenuItem(menu, "5", "20%");
SetMenuExitBackButton(menu, true);
DisplayMenu(menu, client, MENU_TIME_FOREVER);
}
public AmbientVolumeMenuHandler(Handle:menu, MenuAction:action, param1, param2)
{
switch (action)
{
case MenuAction_Select:
{
decl String:info[32];
GetMenuItem(menu, param2, info, sizeof(info));
if (StrEqual(info, "1", false))
{
SetAmbientVolume(param1, 100);
AmbientVolumeMenu(param1);
}
else if (StrEqual(info, "2"))
{
SetAmbientVolume(param1, 80);
AmbientVolumeMenu(param1);
}
else if (StrEqual(info, "3"))
{
SetAmbientVolume(param1, 60);
AmbientVolumeMenu(param1);
}
else if (StrEqual(info, "4"))
{
SetAmbientVolume(param1, 40);
AmbientVolumeMenu(param1);
}
else if (StrEqual(info, "5"))
{
SetAmbientVolume(param1, 20);
AmbientVolumeMenu(param1);
}
}
case MenuAction_Cancel:
{
if (param2 == MenuCancel_ExitBack)
{
VolumeControlMenu(param1);
}
}
case MenuAction_End:
{
CloseHandle(menu);
}
}
return 0;
}
SetAmbientVolume(client, volume)
{
zfAmbientVolume[client] = volume * 0.01;
decl String:sValue[8];
IntToString(volume, sValue, sizeof(sValue));
SetClientCookie(client, g_hAmbientVolumeCookie, sValue);
if (IsClientInGame(client))
{
TranslationPrintToChat(client, "Set_Ambient_Volume", volume);
}
}
+116 -2
View File
@@ -30,6 +30,7 @@
*/
#define ZMARKET_COOKIE_AUTOREBUY "zr_zmarket_autorebuy"
#define ZMARKET_COOKIE_REBUY "zr_zmarket_rebuy"
#define ZMARKET_COOKIE_RANDOMIZE "zr_zmarket_randomize"
/**
* @endsection
*/
@@ -39,6 +40,14 @@
*/
#define ZMARKET_REBUY_WEAPONS_SLOTS_MAX 1
/**
* 2026 randomization of zmarket guns for round start.
*/
new Handle:g_hZMarketRandomizeCookie = INVALID_HANDLE;
new bool:g_bZMarketRandomize[MAXPLAYERS + 1];
new String:g_sZMarketRandomHistoryPrimary[MAXPLAYERS + 1][3][WEAPONS_MAX_LENGTH];
/**
* Array to store the client's current weapon type within menu.
*/
@@ -90,6 +99,12 @@ ZMarketOnCookiesCreate()
g_hZMarketLoadoutCookie[x] = RegClientCookie(rebuycookiename, rebuycookiedesc, CookieAccess_Protected);
}
}
// Randomize toggle cookie.
if (g_hZMarketRandomizeCookie == INVALID_HANDLE)
{
g_hZMarketRandomizeCookie = RegClientCookie(ZMARKET_COOKIE_RANDOMIZE, "Whether weapon randomization is enabled", CookieAccess_Protected);
}
}
ZMarketLoad()
@@ -207,6 +222,12 @@ ZMarketClientInit(client)
// No weapons bought automatically.
g_bZMarketWeaponsBought[client] = false;
// Clear randomize weapon history.
for (new i = 0; i < 3; i++)
{
g_sZMarketRandomHistoryPrimary[client][i][0] = '\0';
}
}
/**
@@ -216,6 +237,10 @@ ZMarketClientInit(client)
*/
ZMarketOnCookiesCached(client)
{
decl String:value[4];
GetClientCookie(client, g_hZMarketRandomizeCookie, value, sizeof(value));
g_bZMarketRandomize[client] = (value[0] == '1');
// Rebuy wapons (if auto-rebuy is enabled, and not bought already).
if (!g_bZMarketWeaponsBought[client])
{
@@ -371,6 +396,7 @@ bool:ZMarketMenuMain(client)
decl String:editloadout[MENU_LINE_REG_LENGTH];
decl String:rebuy[MENU_LINE_REG_LENGTH];
decl String:weaponslist[MENU_LINE_REG_LENGTH];
decl String:randomize[MENU_LINE_REG_LENGTH];
// Format menu options.
Format(title, sizeof(title), "%t\n ", "Weapons menu zmarket main title");
@@ -378,6 +404,7 @@ bool:ZMarketMenuMain(client)
Format(editloadout, sizeof(editloadout), "%t", "Weapons menu zmarket main edit loadout");
Format(rebuy, sizeof(rebuy), "%t", "Weapons menu zmarket main rebuy");
Format(weaponslist, sizeof(weaponslist), "%t", "Weapons menu zmarket main weapons list");
Format(randomize, sizeof(randomize), "Randomize Weapons: %s", g_bZMarketRandomize[client] ? "ON" : "OFF");
// Add formatted options to menu.
SetMenuTitle(menu_zmarket_main, title);
@@ -385,6 +412,7 @@ bool:ZMarketMenuMain(client)
AddMenuItem(menu_zmarket_main, "Edit Loadout", editloadout, MenuGetItemDraw(zmarketrebuy || zmarketrebuyauto));
AddMenuItem(menu_zmarket_main, "Rebuy", rebuy, MenuGetItemDraw(zmarketrebuy || zmarketrebuyauto));
AddMenuItem(menu_zmarket_main, "Weapons List", weaponslist);
AddMenuItem(menu_zmarket_main, "Randomize", randomize);
// Set exit back button.
SetMenuExitBackButton(menu_zmarket_main, true);
@@ -440,6 +468,18 @@ public ZMarketMenuMainHandle(Handle:menu_zmarket_main, MenuAction:action, client
// Send the weapon types list.
ZMarketMenuTypes(client);
}
case 4:
{
// Toggle randomize.
g_bZMarketRandomize[client] = !g_bZMarketRandomize[client];
decl String:value[4];
Format(value, sizeof(value), "%d", g_bZMarketRandomize[client] ? 1 : 0);
SetClientCookie(client, g_hZMarketRandomizeCookie, value);
PrintToChat(client, "Weapons zmarket randomize toggled: %s", g_bZMarketRandomize[client] ? "ON" : "OFF");
ZMarketMenuMain(client);
}
}
}
// Client closed the menu.
@@ -1305,6 +1345,75 @@ stock ZMarketSetRebuyCookie(client, WeaponsSlot:slot, const String:value[])
CloseHandle(rebuycookie);
}
/**
* Picks a random weapon name from the given slot, avoiding the client's
* last 3 picks for the primiary slot.
*
* @param client The client index.
* @param slot Slot_Primary
* @param buffer Output buffer for the weapon name.
* @param maxlen Size of buffer.
*/
stock ZMarketGetRandomWeaponForSlot(client, WeaponsSlot:slot, String:buffer[], maxlen)
{
new Handle:candidates = CreateArray(WEAPONS_MAX_LENGTH);
new size = GetArraySize(arrayWeapons);
decl String:weaponname[WEAPONS_MAX_LENGTH];
for (new i = 0; i < size; i++)
{
if (WeaponsGetSlot(i) != slot)
continue;
WeaponsGetName(i, weaponname, sizeof(weaponname));
// Skip if restricted.
if (RestrictIsWeaponRestricted(i))
continue;
// Skip if in the client's last-3 history for this slot.
new bool:inHistory = false;
for (new h = 0; h < 3; h++)
{
decl String:histEntry[WEAPONS_MAX_LENGTH];
if (slot == Slot_Primary)
strcopy(histEntry, sizeof(histEntry), g_sZMarketRandomHistoryPrimary[client][h]);
if (StrEqual(histEntry, weaponname))
{
inHistory = true;
break;
}
}
if (inHistory)
continue;
PushArrayString(candidates, weaponname);
}
new candidateCount = GetArraySize(candidates);
if (candidateCount == 0)
{
buffer[0] = '\0';
CloseHandle(candidates);
return;
}
new pick = Math_GetRandomInt(0, candidateCount - 1);
GetArrayString(candidates, pick, buffer, maxlen);
CloseHandle(candidates);
// Push into history (shift, then insert at front).
if (slot == Slot_Primary)
{
strcopy(g_sZMarketRandomHistoryPrimary[client][2], WEAPONS_MAX_LENGTH, g_sZMarketRandomHistoryPrimary[client][1]);
strcopy(g_sZMarketRandomHistoryPrimary[client][1], WEAPONS_MAX_LENGTH, g_sZMarketRandomHistoryPrimary[client][0]);
strcopy(g_sZMarketRandomHistoryPrimary[client][0], WEAPONS_MAX_LENGTH, buffer);
}
}
/**
* Force a client to rebuy their weapons.
*
@@ -1340,9 +1449,14 @@ ZMarketRebuy(client, bool:autorebuy = false)
// x = Weapon slot.
for (new x = 0; x < ZMARKET_REBUY_WEAPONS_SLOTS_MAX + 1; x++)
{
// If slot is empty, then use the default value.
if (!rebuyweapons[x][0])
// If randomize is enabled, override any saved loadout for primary/secondary.
if (g_bZMarketRandomize[client] && (x == Slot_Primary || x == Slot_Secondary))
{
ZMarketGetRandomWeaponForSlot(client, WeaponsSlot:x, rebuyweapons[x], sizeof(rebuyweapons[]));
}
else if (!rebuyweapons[x][0])
{
// No saved loadout, fall back to cvar default.
switch(x)
{
case Slot_Primary: