Implemented infection modes. Compiles, but not tested.

This commit is contained in:
Richard Helgeby 2013-01-10 06:21:30 +01:00
parent ed4c628e81
commit d87138977b
3 changed files with 283 additions and 105 deletions

View File

@ -289,9 +289,21 @@ zr_hitgroups "1"
// General // General
// Number of mother zombies to infect (when infect timer is up) in proportion to number of humans on the server. // Mother zombie infection mode. ['dynamic' = every n-th zombie (ratio) | 'absolute' = n zombies (ratio) | 'range' = min/max]
// Default: "5" // Default: "dynamic"
zr_infect_mzombie_ratio "5" zr_infect_mzombie_mode "dynamic"
// Dynamic mode: Infection ratio. Every n-th player is infected. | Absolute mode: Number of zombies to infect (positive ratio), or number of humans to keep (negative ratio).
// Default: "7"
zr_infect_mzombie_ratio "7"
// Minimum number of mother zombies. Range mode only, cannot be zero.
// Default: "1"
zr_infect_mzombie_min "1"
// Maximum number of mother zombies. Range mode only, cannot be zero.
// Default: "3"
zr_infect_mzombie_max "3"
// Counts down to the first infection of the round. The counter is displayed in the middle of the screen. // Counts down to the first infection of the round. The counter is displayed in the middle of the screen.
// Default: "0" // Default: "0"

View File

@ -101,7 +101,10 @@ enum CvarsList
Handle:CVAR_INFECT_SPAWNTIME_MAX, Handle:CVAR_INFECT_SPAWNTIME_MAX,
Handle:CVAR_INFECT_CONSECUTIVE_BLOCK, Handle:CVAR_INFECT_CONSECUTIVE_BLOCK,
Handle:CVAR_INFECT_WEAPONS_DROP, Handle:CVAR_INFECT_WEAPONS_DROP,
Handle:CVAR_INFECT_MZOMBIE_MODE,
Handle:CVAR_INFECT_MZOMBIE_RATIO, Handle:CVAR_INFECT_MZOMBIE_RATIO,
Handle:CVAR_INFECT_MZOMBIE_MIN,
Handle:CVAR_INFECT_MZOMBIE_MAX,
Handle:CVAR_INFECT_MZOMBIE_COUNTDOWN, Handle:CVAR_INFECT_MZOMBIE_COUNTDOWN,
Handle:CVAR_INFECT_MZOMBIE_RESPAWN, Handle:CVAR_INFECT_MZOMBIE_RESPAWN,
Handle:CVAR_INFECT_EXPLOSION, Handle:CVAR_INFECT_EXPLOSION,
@ -319,7 +322,10 @@ CvarsCreate()
// =========================== // ===========================
// General // General
g_hCvarsList[CVAR_INFECT_MZOMBIE_RATIO] = CreateConVar("zr_infect_mzombie_ratio", "5", "Number of mother zombies to infect (when infect timer is up) in proportion to number of humans on the server."); g_hCvarsList[CVAR_INFECT_MZOMBIE_MODE] = CreateConVar("zr_infect_mzombie_mode", "dynamic", "Mother zombie infection mode. ['dynamic' = every n-th zombie (ratio) | 'absolute' = n zombies (ratio) | 'range' = min/max]");
g_hCvarsList[CVAR_INFECT_MZOMBIE_RATIO] = CreateConVar("zr_infect_mzombie_ratio", "7", "Dynamic mode: Infection ratio. Every n-th player is infected. | Absolute mode: Number of zombies to infect (positive ratio), or number of humans to keep (negative ratio).");
g_hCvarsList[CVAR_INFECT_MZOMBIE_MIN] = CreateConVar("zr_infect_mzombie_min", "1", "Minimum number of mother zombies. Range mode only, cannot be zero.");
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_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_RESPAWN] = CreateConVar("zr_infect_mzombie_respawn", "0", "Teleport mother zombies back to spawn on infect.");
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_MIN] = CreateConVar("zr_infect_spawntime_min", "30.0", "Minimum time from the start of the round until picking the mother zombie(s).");

View File

@ -78,6 +78,17 @@ new bool:bZombie[MAXPLAYERS + 1];
*/ */
new bool:bInfectImmune[MAXPLAYERS + 1][2]; new bool:bInfectImmune[MAXPLAYERS + 1][2];
/**
* Available mother zombie infection modes.
*/
enum InfectMode
{
InfectMode_Invalid = -1, /** Invalid mode, used by validators. */
InfectMode_Dynamic, /** Every n-th player is infected. */
InfectMode_Absolute, /** Keep n humans (negative n) or infect n zombies. */
InfectMode_Range /** An absolute number of zombies infected (min to max). */
}
/** /**
* Map is ending. * Map is ending.
*/ */
@ -454,126 +465,178 @@ public Action:InfectMotherZombie(Handle:timer)
return; return;
} }
// Variable to store client stored in random array index.
new client;
// Prune list of immune clients. // Prune list of immune clients.
// x = Array index. eligibleclients = InfectRemoveImmuneClients(arrayEligibleClients);
// client = client index.
for (new x = 0; x < eligibleclients; x++) // Move all clients to CT.
InfectMoveAllToCT();
new mothercount;
new ratio = GetConVarInt(g_hCvarsList[CVAR_INFECT_MZOMBIE_RATIO]);
new min = GetConVarInt(g_hCvarsList[CVAR_INFECT_MZOMBIE_MIN]);
new max = GetConVarInt(g_hCvarsList[CVAR_INFECT_MZOMBIE_MAX]);
// Count valid human clients.
new humancount;
ZRCountValidClients(_, humancount, _, true);
// Get and validate infection mode. This will also log a warning on error.
new InfectMode:mode = InfectGetModeOrFail();
// Apply infection mode.
switch (mode)
{ {
// Stop pruning if there is only 1 player left. case InfectMode_Invalid:
if (eligibleclients <= 1) {
// Validation failed. Fall back to one mother zombie.
mothercount = 1;
}
case InfectMode_Dynamic:
{
// Dynamic mode. Every n-th player is infected.
// A ratio of 0 will infect one zombie (to keep backwards compatibility).
if (ratio == 0)
{
mothercount = 1;
}
// Calculate number of zombies to infect.
mothercount = RoundToNearest(float(humancount) / ratio);
// Require at least one mother zombie.
if (mothercount == 0)
{
mothercount = 1;
}
}
case InfectMode_Absolute:
{
if (ratio > 0)
{
// Infect n humans.
mothercount = ratio;
}
else
{
// Infect all but n humans. Since ratio already is negative
// just add the numbers. (Zero ratio is catched by validator.)
mothercount = humancount + ratio;
// Force at least one mother zombie.
if (mothercount == 0)
{
mothercount = 1;
}
}
}
case InfectMode_Range:
{
// Get a random number between the range.
mothercount = Math_GetRandomInt(min, max);
}
}
// Infect players.
for (new n = 0; n < mothercount; n++)
{
// Recount eligible clients.
eligibleclients = GetArraySize(arrayEligibleClients);
// Stop if there are no more eligible clients.
if (eligibleclients <= 0)
{ {
break; break;
} }
// Get client stored in array index. // Get a random array index.
client = GetArrayCell(arrayEligibleClients, x); new i = Math_GetRandomInt(0, eligibleclients - 1);
// If client is immune from being a mother zombie, then stop. // Get the client stored in the random array index.
new client = GetArrayCell(arrayEligibleClients, i);
// Infect player.
InfectHumanToZombie(client, _, true);
// Remove player from eligible client list.
RemoveFromArray(arrayEligibleClients, i);
}
// Mother zombies have been infected.
g_bZombieSpawned = true;
// Destroy client list.
CloseHandle(arrayEligibleClients);
}
/**
* Moves all alive clients to the CT team.
*/
InfectMoveAllToCT()
{
// Move all clients to CT
for (new client = 1; client <= MaxClients; client++)
{
// If client isn't in-game, then stop.
if (!IsClientInGame(client))
{
continue;
}
// If client is dead, then stop.
if (!IsPlayerAlive(client))
{
continue;
}
// Switch client to CT team.
CS_SwitchTeam(client, CS_TEAM_CT);
}
}
/**
* Removes immune clients from a client list. If a client is removed, their
* immunity is also removed.
*
* @param clientList List of clients.
* @param keepLastPlayer Don't remove if there's only one player left.
*
* @return Number of clients remaining.
*/
InfectRemoveImmuneClients(Handle:clientList, bool:keepLastPlayer = true)
{
new len = GetArraySize(clientList);
// Loop though client list.
for (new i = 0; i < len; i++)
{
// Stop pruning if there is only one player left.
if (keepLastPlayer && len <= 1)
{
break;
}
// Get client.
new client = GetArrayCell(clientList, i);
// Check if client is immune from mother zombie infection.
if (bInfectImmune[client][INFECT_TYPE_MOTHER]) if (bInfectImmune[client][INFECT_TYPE_MOTHER])
{ {
// Take away immunity. // Take away immunity.
bInfectImmune[client][INFECT_TYPE_MOTHER] = false; bInfectImmune[client][INFECT_TYPE_MOTHER] = false;
// Remove client from array. // Remove client from array.
RemoveFromArray(arrayEligibleClients, x); RemoveFromArray(clientList, i);
// Subtract one from count. // Update list size.
eligibleclients--; len--;
// Backtrack one index, because we deleted it out from under the loop. // Backtrack one index, because we deleted it out from under the loop.
x--; i--;
} }
} }
// Move all clients to CT return len;
for (new x = 1; x <= MaxClients; x++)
{
// If client isn't in-game, then stop.
if (!IsClientInGame(x))
{
continue;
}
// If client is dead, then stop.
if (!IsPlayerAlive(x))
{
continue;
}
// Switch client to CT team.
CS_SwitchTeam(x, CS_TEAM_CT);
}
// Variable to store randomly chosen array index.
new randindex;
// Ratio of mother zombies to humans.
new ratio = GetConVarInt(g_hCvarsList[CVAR_INFECT_MZOMBIE_RATIO]);
// If ratio is 0 or lower, then pick 1 zombie.
if (ratio <= 0)
{
// Get a random valid array index.
randindex = GetRandomInt(0, eligibleclients - 1);
// Get the client stored in the random array index.
client = GetArrayCell(arrayEligibleClients, randindex);
// Infect player.
InfectHumanToZombie(client, _, true);
}
else
{
// Initialize count variables
new zombiecount;
new humancount;
// Count valid human clients
ZRCountValidClients(zombiecount, humancount, _, true);
// Calculate mother zombie count.
new mothercount = RoundToNearest(float(humancount) / ratio);
// If mothercount is 0, then set to 1.
if (!mothercount)
{
mothercount = 1;
}
// x = current mother zombie count.
for (new x = 0; x < mothercount; x++)
{
// Recount eligible clients.
eligibleclients = GetArraySize(arrayEligibleClients);
// If there are no more eligible clients, then break loop.
if (!eligibleclients)
{
break;
}
// Get a random valid array index.
randindex = GetRandomInt(0, eligibleclients - 1);
// Get the client stored in the random array index.
client = GetArrayCell(arrayEligibleClients, randindex);
// Infect player.
InfectHumanToZombie(client, _, true);
// Remove player from eligible zombie list.
RemoveFromArray(arrayEligibleClients, randindex);
}
}
// Mother zombies have been infected.
g_bZombieSpawned = true;
// Destroy handle.
CloseHandle(arrayEligibleClients);
} }
/** /**
@ -1289,3 +1352,100 @@ public Action:InfectHumanCommand(client, argc)
return Plugin_Handled; return Plugin_Handled;
} }
/**
* Converts a string to an infection mode.
*
* @param mode Mode string to convert.
*
* @return Infection mode or InfectMode_Invalid on error.
*/
InfectMode:InfectStringToMode(const String:mode[])
{
if (strlen(mode) == 0)
{
return InfectMode_Invalid;
}
if (StrEqual(mode, "dynamic", false))
{
return InfectMode_Dynamic;
}
else if (StrEqual(mode, "absolute", false))
{
return InfectMode_Absolute;
}
else if (StrEqual(mode, "range", false))
{
return InfectMode_Range;
}
return InfectMode_Invalid;
}
/**
* Gets and validates the infection mode. On error it will log a warning.
*
* @return Infection mode or InfectMode_Invalid on error.
*/
InfectMode:InfectGetModeOrFail()
{
new String:modeName[16];
GetConVarString(g_hCvarsList[CVAR_INFECT_MZOMBIE_MODE], modeName, sizeof(modeName));
new InfectMode:mode = InfectStringToMode(modeName);
new ratio = GetConVarInt(g_hCvarsList[CVAR_INFECT_MZOMBIE_RATIO]);
new min = GetConVarInt(g_hCvarsList[CVAR_INFECT_MZOMBIE_MIN]);
new max = GetConVarInt(g_hCvarsList[CVAR_INFECT_MZOMBIE_MAX]);
// Validate.
switch (mode)
{
case InfectMode_Invalid:
{
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Infect, "Config Validation", "Warning: Invalid infection mode (\"%s\"). Falling back to one mother zombie.", modeName);
}
case InfectMode_Dynamic:
{
if (ratio < 0)
{
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Infect, "Config Validation", "Warning: Invalid infection ratio (\"%d\"). Must be zero or positive in dynamic mode. Falling back to one mother zombie.", ratio);
return InfectMode_Invalid;
}
}
case InfectMode_Absolute:
{
if (ratio == 0)
{
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Infect, "Config Validation", "Warning: Invalid infection ratio (\"%d\"). Must be nonzero in absolute mode. Falling back to one mother zombie.", ratio);
return InfectMode_Invalid;
}
}
case InfectMode_Range:
{
new bool:failed = false;
if (min <= 0)
{
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Infect, "Config Validation", "Warning: Invalid infection range (\"%d\"). Cvar zr_infect_mzombie_min must be nonzero and positive. Falling back to one mother zombie.", min);
failed = true;
}
if (max <= 0)
{
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Infect, "Config Validation", "Warning: Invalid infection range (\"%d\"). Cvar zr_infect_mzombie_max must be nonzero and positive. Falling back to one mother zombie.", max);
failed = true;
}
if (min > max || max < min)
{
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Infect, "Config Validation", "Warning: Infection range values are overlapping or reversed. Check zr_infect_mzombie_min and zr_infect_mzombie_min. Falling back to one mother zombie.");
failed = true;
}
if (failed)
{
return InfectMode_Invalid;
}
}
}
return mode;
}