/* * ============================================================================ * * 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 . * * ============================================================================ */ /** * 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 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, bool:plugin = false) { new count = 0; // 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 (plugin && APIOnClientMotherZombieEligible(x) == Plugin_Handled) { continue; } // Add eligible client to array. if (arrayEligibleClients != INVALID_HANDLE) PushArrayCell(arrayEligibleClients, x); count++; } return count; } /** * 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; }