sm-zombiereloaded-3/src/zr/zombiereloaded.inc

527 lines
13 KiB
SourcePawn

/*
* ============================================================================
*
* Zombie:Reloaded
*
* File: zombiereloaded.inc
* Type: Core
* Description: General plugin functions and defines.
*
* 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/>.
*
* ============================================================================
*/
/**
* Index of server console.
*/
#define ZR_CONSOLE_INDEX 0
/**
* @section Conversion factors.
*/
#define CONVERSION_UNITS_TO_FEET 16.000
#define CONVERSION_FEET_TO_UNITS 0.0625
/**
* @endsection
*/
/**
* Options that a condition must pass to be eligible.
*/
enum EligibleCondition
{
Condition_False = 0, /** Condition must be false. */
Condition_True = 1, /** Condition must be true. */
Condition_Either = -1 /** Condition can be either true or false. */
}
/**
* Global variable set to true when the first zombie(s) is/are spawned.
*/
new bool:g_bZombieSpawned;
/**
* Supported games.
*/
enum Game
{
Game_Unknown = -1,
Game_CSS,
Game_CSGO
}
/**
* Current game.
*/
new Game:g_Game = Game_Unknown;
#pragma unused g_Game
/**
* Updates g_game. Will log a warning if a unsupported game is detected.
*/
UpdateGameFolder()
{
new String:gameFolder[PLATFORM_MAX_PATH];
GetGameFolderName(gameFolder, sizeof(gameFolder));
if (StrEqual(gameFolder, "cstrike", false))
{
g_Game = Game_CSS;
PrintToServer("Game detected: cstrike");
return;
}
else if (StrEqual(gameFolder, "csgo", false))
{
g_Game = Game_CSGO;
PrintToServer("Game detected: csgo");
return;
}
LogError("Warning: Zombie:Reloaded doesn't support this game: %s", gameFolder);
g_Game = Game_Unknown;
}
/**
* Function to convert numbers to defined units.
*
* @param number The number to convert.
* @param conversion The conversion factor to multiply by. (See defines above)
* @return The converted number.
*/
Float:ZRConvertUnitsFloat(Float:number, Float:conversion)
{
return number / conversion;
}
/**
* Create an array populated with eligible clients to be zombie.
*
* @param arrayEligibleClients The handle of the array, don't forget to call CloseHandle
* on it when finished!
* @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.
*/
stock ZRCreateEligibleClientList(&Handle:arrayEligibleClients, bool:team = false, bool:alive = false, bool:human = false)
{
// Create array.
arrayEligibleClients = CreateArray();
// Populate list with eligible clients.
// x = client index.
for (new x = 1; x <= MaxClients; x++)
{
// If client isn't in-game, then stop.
if (!IsClientInGame(x))
{
continue;
}
// If client isn't on a team, then stop.
if (team && !ZRIsClientOnTeam(x))
{
continue;
}
// If client is dead, then stop.
if (alive && !IsPlayerAlive(x))
{
continue;
}
// If client is already zombie (via admin), then stop.
if (human && !InfectIsClientHuman(x))
{
continue;
}
// Ask plugin API what they think about our client
if (APIOnClientMotherZombieEligible(x) == Plugin_Handled)
{
continue;
}
// Add eligible client to array.
PushArrayCell(arrayEligibleClients, x);
}
return GetArraySize(arrayEligibleClients);
}
/**
* Checks if a timer is currently running.
*
* @param timer The timer handle.
*/
stock bool:ZRIsTimerRunning(Handle:timer)
{
// Return true if the handle isn't empty.
return (timer != INVALID_HANDLE);
}
/**
* Wrapper functions for KilLTimer.
* Ends a timer if running, and resets its timer handle variable.
*
* @param timer The timer handle.
* @param kill True to kill the timer and reset the variable, false to only reset the variable.
* Using false is useful when calling from the timer callback, because the timer is already killed.
*
* @return True if the handle wasn't INVALID_HANDLE, false if the handle wasn't valid.
*/
stock bool:ZREndTimer(&Handle:timer, bool:kill = true)
{
// If the timer is running, then kill it.
if (ZRIsTimerRunning(timer))
{
// Kill if caller says to.
if (kill)
{
KillTimer(timer);
}
// Reset variable.
timer = INVALID_HANDLE;
return true;
}
// Reset variable.
timer = INVALID_HANDLE;
return false;
}
/**
* Check if a client index is a valid player.
*
* @param client The client index.
* @param console True to include console (index 0), false if not.
* @return True if client is valid, false otherwise.
*/
stock bool:ZRIsClientValid(client, bool:console = false)
{
// If index is greater than max number of clients, then return false.
if (client > MaxClients)
{
return false;
}
// If console is true, return if client is >= 0, if not, then return client > 0.
return console ? (client >= 0) : (client > 0);
}
/**
* Check if a given index is console.
*
* @param client The client index.
* @param console True to include console (index 0), false if not.
* @return True if client is valid, false otherwise.
*/
stock bool:ZRIsConsole(index)
{
// Return true if index is equal to console's index.
return (index == ZR_CONSOLE_INDEX);
}
/**
* Count clients on each team.
*
* @param zombies This is set to the number of clients that are zombies.
* @param humans This is set to the number of clients that are humans.
* @param alive If true it will only count live players, false will count alive and dead.
* @return True if successful (zombie has spawned), false otherwise.
*/
stock bool:ZRCountValidClients(&zombiecount = 0, &humancount = 0, bool:alive = true, bool:ignorezombiespawned = false)
{
// If zombie hasn't spawned and were not only counting humans, then stop.
if (!InfectHasZombieSpawned() && !ignorezombiespawned)
{
return false;
}
// x = client index.
for (new x = 1; x <= MaxClients; x++)
{
// If client isn't in-game, then stop.
if (!IsClientInGame(x))
{
continue;
}
// If client isn't on a team, then stop.
if (!ZRIsClientOnTeam(x))
{
continue;
}
// If player must be alive, and player is dead, then stop.
if (alive && !IsPlayerAlive(x))
{
continue;
}
// If player is a zombie, then increment zombie variable.
if (InfectIsClientInfected(x))
{
zombiecount++;
}
// If player is a human, then increment human variable.
else if (InfectIsClientHuman(x))
{
humancount++;
}
}
return true;
}
/**
* Check if a client index is on a team.
*
* @param client The client index.
* @param team Team to check if player is on, -1 to check both.
* @return True if client is on a team, false otherwise.
*/
stock bool:ZRIsClientOnTeam(client, team = -1)
{
// If index is invalid, then stop.
if (!ZRIsClientValid(client))
{
return false;
}
// Get client team.
new clientteam = GetClientTeam(client);
if (team == -1)
{
return (clientteam == CS_TEAM_T || clientteam == CS_TEAM_CT);
}
return (clientteam == team);
}
/**
* Check if there are clients on a team.
*
* @param team (Optional) Team to check if there are clients on.
*/
stock bool:ZRTeamHasClients(team = -1)
{
// If team is
if (team == -1)
{
// Return true if both teams have at least 1 client.
return (GetTeamClientCount(CS_TEAM_T) && GetTeamClientCount(CS_TEAM_CT));
}
// Return true if given team has at least 1 client.
return bool:GetTeamClientCount(team);
}
/**
* Returns whether a player is a admin or not.
*
* @param client The client index.
* @param flag Optional. Flag to check. Default is generic admin flag.
* @return True if generic admin, false otherwise.
*/
stock bool:ZRIsClientAdmin(client, AdminFlag:flag = Admin_Generic)
{
// If index is invalid, then stop.
if (!ZRIsClientValid(client))
{
return false;
}
// If client doesn't have the specified flag, then stop.
if (!GetAdminFlag(GetUserAdmin(client), flag))
{
return false;
}
// Client is an admin.
return true;
}
/**
* Replies to a client with a given message describing a targetting
* failure reason. (formatted for ZR)
*
* Note: The translation phrases are found in common.phrases.txt.
*
* @param client Client index, or 0 for server.
* @param reason COMMAND_TARGET reason.
*/
stock ZRReplyToTargetError(client, reason)
{
switch (reason)
{
case COMMAND_TARGET_NONE:
{
TranslationReplyToCommand(client, "No matching client");
}
case COMMAND_TARGET_NOT_ALIVE:
{
TranslationReplyToCommand(client, "Target must be alive");
}
case COMMAND_TARGET_NOT_DEAD:
{
TranslationReplyToCommand(client, "Target must be dead");
}
case COMMAND_TARGET_NOT_IN_GAME:
{
TranslationReplyToCommand(client, "Target is not in game");
}
case COMMAND_TARGET_IMMUNE:
{
TranslationReplyToCommand(client, "Unable to target");
}
case COMMAND_TARGET_EMPTY_FILTER:
{
TranslationReplyToCommand(client, "No matching clients");
}
case COMMAND_TARGET_NOT_HUMAN:
{
TranslationReplyToCommand(client, "Cannot target bot");
}
case COMMAND_TARGET_AMBIGUOUS:
{
TranslationReplyToCommand(client, "More than one client matched");
}
}
}
/**
* Adds support for printing strings longer than 1 KB to console. Max 4 KB.
*
* Note: 1024 characters is max for the console, including newline and null
* terminator.
*
* @param client The client index.
* @param text Long text to write.
* @param splitsize Optional. Sets the split size. 1022 is default.
* Allowed range: 128 to 1022.
*/
stock ZRPrintToConsoleLong(client, const String:text[], splitsize = 1022)
{
// Validate split size.
if (splitsize < 128 || splitsize > 1022)
{
return;
}
decl String:partbuffer[splitsize];
new pos;
new cellswritten = 1; // Initialize for the loop.
while (cellswritten)
{
cellswritten = strcopy(partbuffer, splitsize, text[pos]);
(client > 0) ? PrintToConsole(client, partbuffer) : PrintToServer(partbuffer);
pos += cellswritten;
}
}
/**
* Converts a boolean value into a string.
*
* @param value The value to convert to string.
* @param output The converted string.
* @param maxlen The maximum length of the string.
*/
ZRBoolToString(bool:value, String:output[], maxlen)
{
// If the value is true, then set string to "1".
if (value)
{
strcopy(output, maxlen, "1");
}
// If the value is false, then set string to "0".
else
{
strcopy(output, maxlen, "0");
}
}
/**
* (from SMLIB 0.10.2)
*
* Returns a random, uniform Integer number in the specified (inclusive) range.
* This is safe to use multiple times in a function.
* The seed is set automatically for each plugin.
* Rewritten by MatthiasVance, thanks.
*
* @param min Min value used as lower border
* @param max Max value used as upper border
* @return Random Integer number between min and max
*/
#define SIZE_OF_INT 2147483647 // without 0
stock Math_GetRandomInt(min, max)
{
new random = GetURandomInt();
if (random == 0) {
random++;
}
return RoundToCeil(float(random) / (float(SIZE_OF_INT) / float(max - min + 1))) + min - 1;
}
/**
* (from SMLIB)
* Gets the parent entity of an entity.
*
* @param entity Entity Index.
* @return Entity Index of the parent.
*/
stock Entity_GetParent(entity)
{
return GetEntPropEnt(entity, Prop_Data, "m_pParent");
}
/**
* Returns whether an entity is referred to as the parent entity.
*
* @praram entity Entity index.
*
* @return True if entity has children, false otherwise.
*/
stock bool:Entity_HasChildren(entity)
{
new maxEntities = GetMaxEntities();
// Loop through all entity indexes, after players.
for (new loopEntity = MAXPLAYERS + 1; loopEntity < maxEntities; loopEntity++)
{
if (!IsValidEntity(loopEntity))
{
continue;
}
new parentEntity = Entity_GetParent(loopEntity);
if (parentEntity == entity)
{
return true;
}
}
return false;
}