This commit is contained in:
Greyscale
2009-08-21 12:57:59 -07:00
24 changed files with 2024 additions and 895 deletions

View File

@ -41,6 +41,7 @@
#include "zr/zombiereloaded"
#include "zr/translation"
#include "zr/cvars"
#include "zr/admintools"
#include "zr/log"
#include "zr/config"
#include "zr/steamidcache"
@ -49,6 +50,7 @@
#include "zr/menu"
#include "zr/cookies"
#include "zr/paramtools"
#include "zr/paramparser"
#include "zr/models"
#include "zr/downloads"
#include "zr/overlays"

168
src/zr/admintools.inc Normal file
View File

@ -0,0 +1,168 @@
/*
* ============================================================================
*
* Zombie:Reloaded
*
* File: admintools.inc
* Type: Core
* Description: Functions for checking extended admin privileges.
*
* Copyright (C) 2009 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/>.
*
* ============================================================================
*/
/**
* @section Pre-defined group names for authenticating players.
*/
#define ZR_GROUP_ADMINS "zr_admins"
#define ZR_GROUP_MODERATORS "zr_moderators"
#define ZR_GROUP_CONFIGURATORS "zr_configurators"
/**
* @endsection
*/
/**
* List of operation types to specify the category of a admin operation.
*/
enum OperationTypes
{
OperationType_Invalid = -1, /** Invalid operation type. */
OperationType_Generic, /** Generic events like infecting or teleporting players. */
OperationType_Configuration, /** Changing settings. */
}
/**
* Returns whether a player is allowed to do a certain operation or not.
*
* @param client The client index.
* @param operationType The operation category.
* @return True if allowed, false otherwise.
*/
stock bool:ZRIsClientPrivileged(client, OperationTypes:operationType = OperationType_Generic)
{
// Validate client index.
if (!ZRIsClientValid(client))
{
return false;
}
// Check if group permissions is enabled.
new bool:groupauth = GetConVarBool(g_hCvarsList[CVAR_PERMISSIONS_USE_GROUPS]);
if (groupauth)
{
/**********************************
* *
* GROUP BASED AUTHENTICATION *
* *
**********************************/
// Check if client is full admin.
if (ZRIsClientInGroup(client, ZR_GROUP_ADMINS))
{
return true;
}
// Check operation type.
switch (operationType)
{
case OperationType_Generic:
{
return ZRIsClientInGroup(client, ZR_GROUP_MODERATORS);
}
case OperationType_Configuration:
{
return ZRIsClientInGroup(client, ZR_GROUP_CONFIGURATORS);
}
}
// Invalid operation type.
return false;
}
else
{
/*********************************
* *
* FLAG BASED AUTHENTICATION *
* *
*********************************/
new AdminFlag:flag;
// Check operation type.
switch (operationType)
{
case OperationType_Generic:
{
flag = Admin_Generic;
}
case OperationType_Configuration:
{
flag = Admin_Config;
}
default:
{
// Invalid operation type.
return false;
}
}
return GetAdminFlag(GetUserAdmin(client), flag);
}
}
/**
* Returns whether a player is in a spesific group or not.
*
* @param client The client index.
* @param groupName SourceMod group name to check.
* @return True if in the group, false otherwise.
*/
stock bool:ZRIsClientInGroup(client, const String:groupName[])
{
new AdminId:id = GetUserAdmin(client);
// Validate id.
if (id == INVALID_ADMIN_ID)
{
return false;
}
// Get number of groups.
new groupnum = GetAdminGroupCount(id);
decl String:groupname[64];
// Validate number of groups.
if (groupnum > 0)
{
// Loop through each group.
for (new group = 0; group < groupnum; group++)
{
// Get group name.
GetAdminGroup(id, group, groupname, sizeof(groupname));
// Compare names.
if (StrEqual(groupName, groupname, false))
{
return true;
}
}
}
// No groups or no match.
return false;
}

View File

@ -67,4 +67,36 @@ bool:CookiesGetClientCookieBool(client, Handle:cookie)
// Return string casted into an int, then to bool.
return bool:StringToInt(cookievalue);
}
}
/**
* Sets a integer value on a cookie.
*
* @param client The client index.
* @param cookie The handle to the cookie.
* @param value The value to set.
*/
CookiesSetInt(client, Handle:cookie, value)
{
// Convert value to string.
decl String:strValue[16];
IntToString(value, strValue, sizeof(strValue));
// Set string value.
SetClientCookie(client, cookie, strValue);
}
/**
* Gets a integer value from a cookie.
*
* @param client The client index.
* @param cookie The handle to the cookie.
*/
CookiesGetInt(client, Handle:cookie)
{
decl String:strValue[16];
strValue[0] = 0;
GetClientCookie(client, cookie, strValue, sizeof(strValue));
return StringToInt(strValue);
}

View File

@ -52,8 +52,11 @@ enum CvarsList
Handle:CVAR_CONFIG_PATH_CLASSES,
Handle:CVAR_CONFIG_PATH_WEAPONS,
Handle:CVAR_CONFIG_PATH_HITGROUPS,
Handle:CVAR_PERMISSIONS_USE_GROUPS,
Handle:CVAR_CLASSES_SPAWN,
Handle:CVAR_CLASSES_RANDOM,
Handle:CVAR_CLASSES_CHANGE_TIMELIMIT,
Handle:CVAR_CLASSES_SAVE,
Handle:CVAR_CLASSES_DEFAULT_ZOMBIE,
Handle:CVAR_CLASSES_DEFAULT_M_ZOMB,
Handle:CVAR_CLASSES_DEFAULT_HUMAN,
@ -62,6 +65,9 @@ enum CvarsList
Handle:CVAR_CLASSES_OVERLAY_TOGGLE,
Handle:CVAR_CLASSES_OVERLAY_TOGGLECMDS,
Handle:CVAR_CLASSES_OVERLAY_DEFAULT,
Handle:CVAR_CLASSES_ZOMBIE_SELECT,
Handle:CVAR_CLASSES_HUMAN_SELECT,
Handle:CVAR_CLASSES_ADMIN_SELECT,
Handle:CVAR_WEAPONS,
Handle:CVAR_WEAPONS_RESTRICT,
Handle:CVAR_WEAPONS_ZMARKET,
@ -216,7 +222,7 @@ CvarsCreate()
// ===========================
g_hCvarsList[CVAR_LOG] = CreateConVar("zr_log", "1", "Enable logging of events in the plugin. Fatal errors are logged independent on this setting.");
g_hCvarsList[CVAR_LOG_FLAGS] = CreateConVar("zr_log_flags", "3", "A bit field that specify what event types to log. See logging section (3.3) in manual for details.");
g_hCvarsList[CVAR_LOG_MODULE_FILTER] = CreateConVar("zr_log_module_filter", "0", "Enable module filtering. Only log events from listed modules will be logged.");
g_hCvarsList[CVAR_LOG_MODULE_FILTER] = CreateConVar("zr_log_module_filter", "0", "Enable module filtering. Only events from listed modules will be logged.");
g_hCvarsList[CVAR_LOG_IGNORE_CONSOLE] = CreateConVar("zr_log_ignore_console", "1", "Don't log events triggered by console commands that are executed by the console itself, like commands in configs. Enable this command to avoid spamming logs with events like weapon restrictions.");
g_hCvarsList[CVAR_LOG_ERROR_OVERRIDE] = CreateConVar("zr_log_error_override", "1", "Always log error messages. Overrides module filter and logging flags.");
g_hCvarsList[CVAR_LOG_PRINT_ADMINS] = CreateConVar("zr_log_print_admins", "0", "Print log events to admin chat in addition to the log file.");
@ -233,17 +239,28 @@ CvarsCreate()
g_hCvarsList[CVAR_CONFIG_PATH_HITGROUPS] = CreateConVar("zr_config_path_hitgroups", "configs/zr/hitgroups.txt", "Path, relative to root sourcemod directory, to hitgroups config file.");
// ===========================
// Permission Settings
// ===========================
g_hCvarsList[CVAR_PERMISSIONS_USE_GROUPS] = CreateConVar("zr_permissions_use_groups", "0", "Use group authentication instead of flags to access admin features. Generic admin flag is still required on some features.");
// ===========================
// Classes (core)
// ===========================
// General
g_hCvarsList[CVAR_CLASSES_SPAWN] = CreateConVar("zr_classes_spawn", "0", "Re-display class selection menu every spawn.");
g_hCvarsList[CVAR_CLASSES_RANDOM] = CreateConVar("zr_classes_random", "0", "Player is assigned a random class every spawn. [Override: zr_classes_spawn and zr_classes_default_*]");
g_hCvarsList[CVAR_CLASSES_RANDOM] = CreateConVar("zr_classes_random", "0", "Player is assigned a random class every spawn. [Override: zr_classes_default_*]");
g_hCvarsList[CVAR_CLASSES_CHANGE_TIMELIMIT] = CreateConVar("zr_classes_change_timelimit", "20", "Time limit to change human class with instant change after spawning. Time is in seconds. Use 0 or negative to disable.");
g_hCvarsList[CVAR_CLASSES_SAVE] = CreateConVar("zr_classes_save", "1", "Save players' class selections in server cookies and restore when connecting. [Override: zr_classes_default_*]");
g_hCvarsList[CVAR_CLASSES_DEFAULT_ZOMBIE] = CreateConVar("zr_classes_default_zombie", "random", "Zombie class assigned to players on connect. [\"random\" = Random zombie class | \"\" = Class config default]");
g_hCvarsList[CVAR_CLASSES_DEFAULT_M_ZOMB] = CreateConVar("zr_classes_default_mother_zombie", "motherzombies","Zombie class assigned to mother zombies. [\"motherzombies\" = Random mother zombie class | \"random\" = Random regular zombie class | \"disabled\" = Don't change class on mother zombies]");
g_hCvarsList[CVAR_CLASSES_DEFAULT_HUMAN] = CreateConVar("zr_classes_default_human", "random", "Human class assigned to players on connect. [\"random\" = Random human class | \"\" = Class config default]");
g_hCvarsList[CVAR_CLASSES_DEFAULT_ADMIN] = CreateConVar("zr_classes_default_admin", "random", "Admin class assigned to admins on connect. [\"random\" = Random admin class | \"\" = Class config default]");
g_hCvarsList[CVAR_CLASSES_ZOMBIE_SELECT] = CreateConVar("zr_classes_zombie_select", "1", "Allow players to select zombie classes.");
g_hCvarsList[CVAR_CLASSES_HUMAN_SELECT] = CreateConVar("zr_classes_human_select", "1", "Allow players to select human classes.");
g_hCvarsList[CVAR_CLASSES_ADMIN_SELECT] = CreateConVar("zr_classes_admin_select", "1", "Allow admins to select admin mode classes. (Not to be confused by admin-only classes!)");
// Menu
g_hCvarsList[CVAR_CLASSES_MENU_AUTOCLOSE] = CreateConVar("zr_classes_menu_autoclose", "0", "Automatically close class selection menu after selecting a class.");

View File

@ -307,12 +307,6 @@ public ZRTools_Action:DamageOnTakeDamage(client, inflictor, attacker, Float:dama
// Client was damaged by falling.
else if (damagetype & DMG_CSS_FALL)
{
// If client isn't a zombie, then allow damage.
if (!InfectIsClientInfected(client))
{
return ZRTools_Continue;
}
// If class has "nofalldamage" disabled, then allow damage.
new bool:blockfalldamage = ClassGetNoFallDamage(client);
if (!blockfalldamage)

View File

@ -624,7 +624,7 @@ public Action:Command_LogRemoveModule(client, argc)
if (argc < 1)
{
// Display syntax info.
StrCat(buffer, sizeof(buffer), "Add one or more modules to the module filter. Usage: zr_log_add_module <module> [module] ...\n");
StrCat(buffer, sizeof(buffer), "Remove one or more modules to the module filter. Usage: zr_log_remove_module <module> [module] ...\n");
StrCat(buffer, sizeof(buffer), "See zr_log_list to list available module names (short names).");
ReplyToCommand(client, buffer);
}

View File

@ -101,13 +101,13 @@ ZMenuMain(client)
// Translate each line into client's language.
Format(title, sizeof(title), "%t\n ", "Menu main title", publictrigger, silenttrigger);
Format(zadmin, sizeof(zadmin), "%t", "Menu main zadmin", SAYHOOKS_KEYWORD_ZADMIN);
Format(zclass, sizeof(zclass), "%t", "Menu main zclass", SAYHOOKS_KEYWORD_ZCLASS);
Format(zcookies, sizeof(zcookies), "%t", "Menu main zcookies", SAYHOOKS_KEYWORD_ZCOOKIES);
Format(zspawn, sizeof(zspawn), "%t", "Menu main zspawn", SAYHOOKS_KEYWORD_ZSPAWN);
Format(ztele, sizeof(ztele), "%t", "Menu main ztele", SAYHOOKS_KEYWORD_ZTELE);
Format(zhp, sizeof(zhp), "%t", "Menu main zhp", SAYHOOKS_KEYWORD_ZHP);
Format(zmarket, sizeof(zmarket), "%t", "Menu main zmarket", SAYHOOKS_KEYWORD_ZMARKET);
Format(zadmin, sizeof(zadmin), "%t", "Menu main zadmin");
Format(zclass, sizeof(zclass), "%t", "Menu main zclass");
Format(zcookies, sizeof(zcookies), "%t", "Menu main zcookies");
Format(zspawn, sizeof(zspawn), "%t", "Menu main zspawn");
Format(ztele, sizeof(ztele), "%t", "Menu main ztele");
Format(zhp, sizeof(zhp), "%t", "Menu main zhp");
Format(zmarket, sizeof(zmarket), "%t", "Menu main zmarket");
// Add items to menu.
@ -117,7 +117,10 @@ ZMenuMain(client)
new bool:admin = ZRIsClientAdmin(client);
AddMenuItem(menu_main, "zadmin", zadmin, MenuGetItemDraw(admin));
AddMenuItem(menu_main, "zclass", zclass);
// Decide whether the client can use zclass.
new zclassdraw = ClassAllowSelection(client) ? ITEMDRAW_DEFAULT : ITEMDRAW_DISABLED;
AddMenuItem(menu_main, "zclass", zclass, zclassdraw);
AddMenuItem(menu_main, "zcookies", zcookies);
AddMenuItem(menu_main, "zspawn", zspawn);
AddMenuItem(menu_main, "ztele", ztele);

531
src/zr/paramparser.inc Normal file
View File

@ -0,0 +1,531 @@
/*
* ============================================================================
*
* Zombie:Reloaded
*
* File: paramparser.inc
* Type: Core
* Description: Provides functions for parsing single line strings with
* flags, and parameters in key=value format.
*
* Supports quoted strings and escaped characters like "\n"
* and "\t".
*
* Examle raw string:
* "type=interval -disabled msg="Title:\n\"Example\"."
*
* Copyright (C) 2009 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/>.
*
* ============================================================================
*/
/**
* @section Limit settings.
*/
#define PARAM_NAME_MAXLEN 64 /** Maximum length of key name or flag name. */
#define PARAM_VALUE_MAXLEN 256 /** Maximum length of value string. */
/**
* @endsection
*/
/**
* @section Parsing error codes.
*/
#define PARAM_ERROR_EMPTY 1 /** Source string is empty. */
#define PARAM_ERROR_FULL 2 /** Destination array is full. */
#define PARAM_ERROR_UNEXPECTED_KEY 3 /** Unexpected key name. Could not find a equation sign (=) after previous key name. */
#define PARAM_ERROR_UNEXPECTED_END 4 /** Unexpected end of source string. */
#define PARAM_ERROR_MISSING_QUOTE 5 /** Unexpected end of source string. Missing end quote character. */
#define PARAM_ERROR_UNKNOWN 6 /** Unknown error. The parser got a invalid result from a search function it couldn't handle. */
/**
* @endsection
*/
/**
* Modes for what to do and expect when parsing. White space characters between
* modes are ignored.
*/
enum ParamModes
{
ParamMode_TypeCheck, /** Check if it's a flag or a key. */
ParamMode_Flag, /** Expect a flag name (starts with "-"). */
ParamMode_Key, /** Expect a key name. */
ParamMode_Equal, /** Expect a equation sign. */
ParamMode_Value /** Expect a value string. */
}
/**
* Structure for storing a key/value pair.
*/
enum ParamParseResult
{
bool:Param_IsFlag, /** Specifies whether it's a flag or not. */
String:Param_Name[PARAM_NAME_MAXLEN], /** Key or flag name. */
String:Param_Value[PARAM_VALUE_MAXLEN] /** Value. Only used if a key. */
}
/**************************************
* *
* PARAMETER FUNCTIONS *
* *
**************************************/
/**
* Parses a parameter string in "key=value" format and store the result in a
* ParamParseResult array.
*
* @param buffer A ParamParseResult array to store results.
* @param maxlen Maximum number of keys that can be stored (first
* dimension of buffer).
* @param paramString The source string to parse. String is trimmed before
* parsing.
* @param err Opional output: Error code if parsing error.
* @param errPos Opional output: Position in paramString where the error
* occoured.
* @return Number of keys parsed.
*/
stock ParamParseString(buffer[][ParamParseResult], maxlen, String:paramString[], &err = 0, &errPos = -1)
{
/*
* VALIDATION OF INPUT AND BUFFERS
*/
// Trim raw string.
TrimString(paramString);
// Check if raw string is empty.
if (strlen(paramString) == 0)
{
err = PARAM_ERROR_EMPTY;
errPos = 0;
return 0;
}
// Validate specified length of destination buffer.
if (maxlen == 0)
{
err = PARAM_ERROR_FULL;
errPos = 0;
return 0;
}
/*
* PARSE LOOP
*/
// Get raw string length.
new rawlen = sizeof(paramString);
// Initialize. Expect the start to be a key or a flag.
new ParamModes:mode = ParamMode_TypeCheck;
// Counter for number of parameters parsed.
new paramcount;
// Buffers for temp values.
new startpos;
new endpos;
new bool:quoteon;
decl String:value[PARAM_VALUE_MAXLEN];
// Loop through all characters in the string. Exclude null terminator.
for (new strpos = 0; strpos < rawlen - 1; strpos++)
{
// Check if there's space left in the destination buffer.
if (paramcount > maxlen)
{
// Exit loop. No more parameters can be parsed.
err = PARAM_ERROR_FULL;
errPos = strpos;
break;
}
/*
* MODE CHECK
*/
// Check mode for deciding what to do.
switch (mode)
{
case ParamMode_TypeCheck:
{
// Find start position of first non white space character.
startpos = ParamFindStartPos(paramString, strpos);
// Check if it's a flag type.
if (paramString[startpos] == '-')
{
// It's a flag, change mode.
mode = ParamMode_Flag;
// Update current position.
strpos = startpos;
}
else
{
// Expect a key name.
mode = ParamMode_Key;
// Update current position. Substract by one to include
// the current character in next mode.
strpos = startpos - 1;
}
}
case ParamMode_Flag:
{
// Find stop position (last non white space character).
endpos = ParamFindEndPos(paramString, strpos);
// Extract key name.
StrExtract(value, sizeof(value), paramString, strpos, endpos);
// Copy flag to destination buffer.
strcopy(buffer[paramcount][Param_Name], PARAM_NAME_MAXLEN, value);
// Set flag type.
buffer[paramcount][Param_IsFlag] = true;
// Increment parameter counter.
paramcount++;
// Set next parse mode.
mode = ParamMode_TypeCheck;
}
case ParamMode_Key:
{
// Find stop position.
endpos = ParamFindEndPos(paramString, strpos);
// Extract key name.
StrExtract(value, sizeof(value), paramString, strpos, endpos);
// Copy key name to destination buffer.
strcopy(buffer[paramcount][Param_Name], PARAM_NAME_MAXLEN, value);
// Make sure flag type is not set.
buffer[paramcount][Param_IsFlag] = false;
// Note: Do not increment parameter counter until the
// entire key/value pair is parsed.
// Set next parse mode. Expect a equation sign.
mode = ParamMode_Equal;
}
case ParamMode_Equal:
{
// Find start position of first non white space character.
startpos = ParamFindStartPos(paramString, strpos);
// Validate position.
if (startpos >= 0)
{
// Check if it's a equation sign.
if (paramString[startpos] == '=')
{
// Change mode to expect a value at next position.
mode = ParamMode_Value;
// Update current position.
strpos = startpos;
}
else
{
// Parse error.
err = PARAM_ERROR_UNEXPECTED_KEY;
errPos = startpos;
break;
}
}
else
{
// Parse error.
err = PARAM_ERROR_UNEXPECTED_END;
errPos = strpos;
break;
}
}
case ParamMode_Value:
{
// Find start position of first non white space character.
startpos = ParamFindStartPos(paramString, strpos);
// Validate start position.
if (startpos >= 0)
{
// Reset quote and escape settings.
quoteon = false;
// Loop through all characters starting from the current
// position. Exclude null terminator.
for (strpos = startpos; strpos < rawlen - 1; strpos++)
{
// Check if the current character is a special character.
if (paramString[startpos] == '"')
{
// Toggle quote.
quoteon = !quoteon;
// Check quote state.
if (quoteon)
{
// Quote started, update start position.
startpos = strpos + 1;
}
else
{
// Quote end, set end position.
endpos = strpos - 1;
}
}
// Check if it's a white space character or end of the string.
else if (!quoteon && (IsCharSpace(paramString[strpos]) || strpos == rawlen - 1))
{
// End of value reached. Save positions.
endpos = strpos - 1;
// Exit loop.
break;
}
}
// Check if quote still haven't ended.
if (quoteon)
{
// Parse error.
err = PARAM_ERROR_MISSING_QUOTE;
errPos = strpos;
break;
}
// Extract value string.
StrExtract(value, sizeof(value), paramString, startpos, endpos);
// Unescape string (converting "\n" to newline, etc.).
StrUnescape(value);
// Copy value string to destination buffer.
strcopy(buffer[paramcount][Param_Value], PARAM_VALUE_MAXLEN, value);
// Make sure flag type is not set.
buffer[paramcount][Param_IsFlag] = false;
// Increment parameter counter.
paramcount++;
// Set next parse mode. Expect a key or a flag.
mode = ParamMode_TypeCheck;
}
else
{
// Parse error.
err = PARAM_ERROR_UNEXPECTED_END;
errPos = strpos;
break;
}
}
}
}
// Return number of parameters parsed.
return paramcount;
}
/**
* Finds the first key index in a parameter array matching the specified key.
*
* @param params A ParamParseResult array to search through.
* @param maxlen Size of parameter array (first dimension).
* @param key Key to find.
* @param caseSensitive Specifies whether the search is case sensitive or
* not (default).
* @return Index of the key if found, -1 otherwise.
*/
stock ParamFindKey(const params[][ParamParseResult], maxlen, const String:key[], bool:caseSensitive = false)
{
// Loop through all parameters.
for (new index = 0; index < maxlen; index++)
{
// Check parameter type.
if (params[index][Param_IsFlag])
{
// It's a flag type, skip index.
continue;
}
// Match key name.
if (StrEqual(params[index][Param_Name], key, caseSensitive))
{
// Key found, return the key index.
return index;
}
}
return -1;
}
/**
* Checks if the specified flag is set in a parameter array.
*
* @param params A ParamParseResult array to search through.
* @param maxlen Size of parameter array (first dimension).
* @param flag Flag to check.
* @param caseSensitive Specifies whether the search is case sensitive or
* not (default).
* @return True flag is found, false otherwise.
*/
stock bool:ParamHasFlag(const params[][ParamParseResult], maxlen, const String:flag[], bool:caseSensitive = false)
{
// Loop through all parameters.
for (new index = 0; index < maxlen; index++)
{
// Check parameter type.
if (!params[index][Param_IsFlag])
{
// It's a key type, skip index.
continue;
}
// Match flag name.
if (StrEqual(params[index][Param_Name], flag, caseSensitive))
{
// Flag found.
return true;
}
}
return false;
}
/**************************************
* *
* HELPER FUNCTIONS *
* *
**************************************/
/**
* Finds the position of the last non white space character from a specified start position.
*
* @param paramString Raw string search in.
* @param startPos Optional. Position to start searching from.
* @return Position of the last non white space character, or -1
* if failed.
*/
stock ParamFindEndPos(const String:paramString[], startPos = 0)
{
new rawlen = sizeof(paramString);
// Validate string length.
if (rawlen == 0)
{
return -1;
}
// Loop through all characters from the specified start position.
for (new strpos = startPos; strpos < rawlen; strpos++)
{
// Check if white space or if current position is the last
// character before the null terminator.
if (IsCharSpace(paramString[strpos]) || strpos == rawlen - 1)
{
return strpos - 1;
}
}
// It should never reach this place. Added to satisfy compiler.
return -1;
}
/**
* Finds the first non white space character in a string, starting from the
* specified position.
*
* @param paramString Raw string to search in.
* @param startPos Optional. Position to start searching from.
* @return Position of first character or -1 if failed.
*/
stock ParamFindStartPos(const String:paramString[], startPos = 0)
{
new rawlen = sizeof(paramString);
// Validate string length.
if (rawlen == 0)
{
return -1;
}
// Loop through all characters from the specified start position.
for (new strpos = startPos; strpos < rawlen; strpos++)
{
// Check if not white space.
if (!IsCharSpace(paramString[strpos]))
{
return strpos;
}
}
// No character found.
return -1;
}
/**
* Extracts a area in a string between two positions.
*
* @param buffer Destination string buffer.
* @param maxlen Size of destination buffer.
* @param source Source string to extract from.
* @param startpos Start position of string to extract.
* @param endpos End position of string to extract.
* @return Number of cells written.
*/
stock StrExtract(String:buffer[], maxlen, const String:source[], startpos, endpos)
{
new len;
// Calculate string length. Also add space for null terminator.
len = endpos - startpos + 1;
// Validate length.
if (len < 0)
{
return 0;
}
// Extract string and store it in the buffer.
return strcopy(buffer, len, source[startpos]);
}
/**
* Unescapes a string (replaces "\n" with newlines, etc.).
*
* @param str String to unescape.
*/
stock StrUnescape(String:str[])
{
new len = sizeof(str);
ReplaceString(str, len, "\\n", "\n");
ReplaceString(str, len, "\\r", "\r");
ReplaceString(str, len, "\\t", "\t");
ReplaceString(str, len, "\\\"", "\"");
ReplaceString(str, len, "\\\\", "\\");
}

View File

@ -89,16 +89,14 @@ bool:ClassApplyModel(client, classindex, cachetype = ZR_CLASS_CACHE_PLAYER)
ClassGetModelPath(classindex, modelpath, sizeof(modelpath), cachetype);
}
// Check if the user specified a random model.
if (strcmp(modelpath, "random", false) == 0)
// Check if the user specified a pre-defined model setting.
if (StrEqual(modelpath, "random", false))
{
// TODO: Make a function that gets a random model from the specified team.
ModelsGetRandomModelIndex(modelpath, sizeof(modelpath), false, true);
Format(modelpath, sizeof(modelpath), "%s.mdl", modelpath);
}
// Check if the user specified no change.
else if (strcmp(modelpath, "default", false) == 0)
else if (StrEqual(modelpath, "default", false))
{
// Get current model.
GetClientModel(client, modelpath, sizeof(modelpath));
@ -114,6 +112,11 @@ bool:ClassApplyModel(client, classindex, cachetype = ZR_CLASS_CACHE_PLAYER)
return true;
}
}
else if (StrEqual(modelpath, "nochange", false))
{
// Do nothing.
return true;
}
SetEntityModel(client, modelpath);
return true;

View File

@ -52,15 +52,15 @@ stock bool:ClassIsEnabled(index, cachetype = ZR_CLASS_CACHE_MODIFIED)
{
case ZR_CLASS_CACHE_ORIGINAL:
{
return ClassData[index][class_enabled];
return ClassData[index][Class_Enabled];
}
case ZR_CLASS_CACHE_MODIFIED:
{
return ClassDataCache[index][class_enabled];
return ClassDataCache[index][Class_Enabled];
}
case ZR_CLASS_CACHE_PLAYER:
{
return ClassPlayerCache[index][class_enabled];
return ClassPlayerCache[index][Class_Enabled];
}
}
return false;
@ -84,15 +84,15 @@ stock ClassGetTeamID(index, cachetype = ZR_CLASS_CACHE_PLAYER)
{
case ZR_CLASS_CACHE_ORIGINAL:
{
return ClassData[index][class_team];
return ClassData[index][Class_Team];
}
case ZR_CLASS_CACHE_MODIFIED:
{
return ClassDataCache[index][class_team];
return ClassDataCache[index][Class_Team];
}
case ZR_CLASS_CACHE_PLAYER:
{
return ClassPlayerCache[index][class_team];
return ClassPlayerCache[index][Class_Team];
}
}
return -1;
@ -118,15 +118,15 @@ stock bool:ClassGetTeamDefault(index, cachetype = ZR_CLASS_CACHE_MODIFIED)
{
case ZR_CLASS_CACHE_ORIGINAL:
{
return ClassData[index][class_team_default];
return ClassData[index][Class_TeamDefault];
}
case ZR_CLASS_CACHE_MODIFIED:
{
return ClassDataCache[index][class_team_default];
return ClassDataCache[index][Class_TeamDefault];
}
case ZR_CLASS_CACHE_PLAYER:
{
return ClassPlayerCache[index][class_team_default];
return ClassPlayerCache[index][Class_TeamDefault];
}
}
return false;
@ -151,15 +151,15 @@ stock ClassGetFlags(index, cachetype = ZR_CLASS_CACHE_MODIFIED)
{
case ZR_CLASS_CACHE_ORIGINAL:
{
return ClassData[index][class_flags];
return ClassData[index][Class_Flags];
}
case ZR_CLASS_CACHE_MODIFIED:
{
return ClassDataCache[index][class_flags];
return ClassDataCache[index][Class_Flags];
}
case ZR_CLASS_CACHE_PLAYER:
{
return ClassPlayerCache[index][class_flags];
return ClassPlayerCache[index][Class_Flags];
}
}
return -1;
@ -185,20 +185,55 @@ stock bool:ClassHasFlags(index, flags, cachetype = ZR_CLASS_CACHE_MODIFIED)
{
case ZR_CLASS_CACHE_ORIGINAL:
{
return bool:(ClassData[index][class_flags] & flags);
return bool:(ClassData[index][Class_Flags] & flags);
}
case ZR_CLASS_CACHE_MODIFIED:
{
return bool:(ClassDataCache[index][class_flags] & flags);
return bool:(ClassDataCache[index][Class_Flags] & flags);
}
case ZR_CLASS_CACHE_PLAYER:
{
return bool:(ClassPlayerCache[index][class_flags] & flags);
return bool:(ClassPlayerCache[index][Class_Flags] & flags);
}
}
return false;
}
/**
* Gets the class group required to be a member of to use the class.
*
* @param index Index of the class in a class cache or a client index,
* depending on the cache type specified.
* @param buffer The destination string buffer.
* @param maxlen The length of the destination string buffer.
* @param cachetype Optional. Specifies what class cache to read from. Options:
* ZR_CLASS_CACHE_ORIGINAL - Unchanced class data.
* ZR_CLASS_CACHE_MODIFIED - Changed/newest class data.
* ZR_CLASS_CACHE_PLAYER (default) - Player cache. If this one
* is used, index will be used as a client index.
* @return Number of cells written. -1 on error.
*/
stock ClassGetGroup(index, String:buffer[], maxlen, cachetype = ZR_CLASS_CACHE_PLAYER)
{
switch (cachetype)
{
case ZR_CLASS_CACHE_ORIGINAL:
{
return strcopy(buffer, maxlen, ClassData[index][Class_Group]);
}
case ZR_CLASS_CACHE_MODIFIED:
{
return strcopy(buffer, maxlen, ClassDataCache[index][Class_Group]);
}
case ZR_CLASS_CACHE_PLAYER:
{
return strcopy(buffer, maxlen, ClassPlayerCache[index][Class_Group]);
}
}
return -1;
}
/**
* Gets the class name to be displayed in the class menu.
*
@ -219,15 +254,15 @@ stock ClassGetName(index, String:buffer[], maxlen, cachetype = ZR_CLASS_CACHE_PL
{
case ZR_CLASS_CACHE_ORIGINAL:
{
return strcopy(buffer, maxlen, ClassData[index][class_name]);
return strcopy(buffer, maxlen, ClassData[index][Class_Name]);
}
case ZR_CLASS_CACHE_MODIFIED:
{
return strcopy(buffer, maxlen, ClassDataCache[index][class_name]);
return strcopy(buffer, maxlen, ClassDataCache[index][Class_Name]);
}
case ZR_CLASS_CACHE_PLAYER:
{
return strcopy(buffer, maxlen, ClassPlayerCache[index][class_name]);
return strcopy(buffer, maxlen, ClassPlayerCache[index][Class_Name]);
}
}
@ -254,15 +289,15 @@ stock ClassGetDescription(index, String:buffer[], maxlen, cachetype = ZR_CLASS_C
{
case ZR_CLASS_CACHE_ORIGINAL:
{
return strcopy(buffer, maxlen, ClassData[index][class_description]);
return strcopy(buffer, maxlen, ClassData[index][Class_Description]);
}
case ZR_CLASS_CACHE_MODIFIED:
{
return strcopy(buffer, maxlen, ClassDataCache[index][class_description]);
return strcopy(buffer, maxlen, ClassDataCache[index][Class_Description]);
}
case ZR_CLASS_CACHE_PLAYER:
{
return strcopy(buffer, maxlen, ClassPlayerCache[index][class_description]);
return strcopy(buffer, maxlen, ClassPlayerCache[index][Class_Description]);
}
}
return -1;
@ -298,15 +333,15 @@ stock ClassGetModelPath(index, String:buffer[], maxlen, cachetype = ZR_CLASS_CAC
{
case ZR_CLASS_CACHE_ORIGINAL:
{
return strcopy(buffer, maxlen, ClassData[index][class_model_path]);
return strcopy(buffer, maxlen, ClassData[index][Class_ModelPath]);
}
case ZR_CLASS_CACHE_MODIFIED:
{
return strcopy(buffer, maxlen, ClassDataCache[index][class_model_path]);
return strcopy(buffer, maxlen, ClassDataCache[index][Class_ModelPath]);
}
case ZR_CLASS_CACHE_PLAYER:
{
return strcopy(buffer, maxlen, ClassPlayerCache[index][class_model_path]);
return strcopy(buffer, maxlen, ClassPlayerCache[index][Class_ModelPath]);
}
}
return -1;
@ -330,15 +365,15 @@ stock ClassGetAlphaInitial(index, cachetype = ZR_CLASS_CACHE_PLAYER)
{
case ZR_CLASS_CACHE_ORIGINAL:
{
return ClassData[index][class_alpha_initial];
return ClassData[index][Class_AlphaInitial];
}
case ZR_CLASS_CACHE_MODIFIED:
{
return ClassDataCache[index][class_alpha_initial];
return ClassDataCache[index][Class_AlphaInitial];
}
case ZR_CLASS_CACHE_PLAYER:
{
return ClassPlayerCache[index][class_alpha_initial];
return ClassPlayerCache[index][Class_AlphaInitial];
}
}
return -1;
@ -363,15 +398,15 @@ stock ClassGetAlphaDamaged(index, cachetype = ZR_CLASS_CACHE_PLAYER)
{
case ZR_CLASS_CACHE_ORIGINAL:
{
return ClassData[index][class_alpha_damaged];
return ClassData[index][Class_AlphaDamaged];
}
case ZR_CLASS_CACHE_MODIFIED:
{
return ClassDataCache[index][class_alpha_damaged];
return ClassDataCache[index][Class_AlphaDamaged];
}
case ZR_CLASS_CACHE_PLAYER:
{
return ClassPlayerCache[index][class_alpha_damaged];
return ClassPlayerCache[index][Class_AlphaDamaged];
}
}
return -1;
@ -396,15 +431,15 @@ stock ClassGetAlphaDamage(index, cachetype = ZR_CLASS_CACHE_PLAYER)
{
case ZR_CLASS_CACHE_ORIGINAL:
{
return ClassData[index][class_alpha_damage];
return ClassData[index][Class_AlphaDamage];
}
case ZR_CLASS_CACHE_MODIFIED:
{
return ClassDataCache[index][class_alpha_damage];
return ClassDataCache[index][Class_AlphaDamage];
}
case ZR_CLASS_CACHE_PLAYER:
{
return ClassPlayerCache[index][class_alpha_damage];
return ClassPlayerCache[index][Class_AlphaDamage];
}
}
return -1;
@ -440,15 +475,15 @@ stock ClassGetOverlayPath(index, String:buffer[], maxlen, cachetype = ZR_CLASS_C
{
case ZR_CLASS_CACHE_ORIGINAL:
{
return strcopy(buffer, maxlen, ClassData[index][class_overlay_path]);
return strcopy(buffer, maxlen, ClassData[index][Class_OverlayPath]);
}
case ZR_CLASS_CACHE_MODIFIED:
{
return strcopy(buffer, maxlen, ClassDataCache[index][class_overlay_path]);
return strcopy(buffer, maxlen, ClassDataCache[index][Class_OverlayPath]);
}
case ZR_CLASS_CACHE_PLAYER:
{
return strcopy(buffer, maxlen, ClassPlayerCache[index][class_overlay_path]);
return strcopy(buffer, maxlen, ClassPlayerCache[index][Class_OverlayPath]);
}
}
return -1;
@ -472,15 +507,15 @@ stock bool:ClassGetNvgs(index, cachetype = ZR_CLASS_CACHE_PLAYER)
{
case ZR_CLASS_CACHE_ORIGINAL:
{
return ClassData[index][class_nvgs];
return ClassData[index][Class_Nvgs];
}
case ZR_CLASS_CACHE_MODIFIED:
{
return ClassDataCache[index][class_nvgs];
return ClassDataCache[index][Class_Nvgs];
}
case ZR_CLASS_CACHE_PLAYER:
{
return ClassPlayerCache[index][class_nvgs];
return ClassPlayerCache[index][Class_Nvgs];
}
}
return false;
@ -504,15 +539,15 @@ stock ClassGetFOV(index, cachetype = ZR_CLASS_CACHE_PLAYER)
{
case ZR_CLASS_CACHE_ORIGINAL:
{
return ClassData[index][class_fov];
return ClassData[index][Class_Fov];
}
case ZR_CLASS_CACHE_MODIFIED:
{
return ClassDataCache[index][class_fov];
return ClassDataCache[index][Class_Fov];
}
case ZR_CLASS_CACHE_PLAYER:
{
return ClassPlayerCache[index][class_fov];
return ClassPlayerCache[index][Class_Fov];
}
}
return -1;
@ -546,15 +581,15 @@ stock bool:ClassGetHasNapalm(index, cachetype = ZR_CLASS_CACHE_PLAYER)
{
case ZR_CLASS_CACHE_ORIGINAL:
{
return ClassData[index][class_has_napalm];
return ClassData[index][Class_HasNapalm];
}
case ZR_CLASS_CACHE_MODIFIED:
{
return ClassDataCache[index][class_has_napalm];
return ClassDataCache[index][Class_HasNapalm];
}
case ZR_CLASS_CACHE_PLAYER:
{
return ClassPlayerCache[index][class_has_napalm];
return ClassPlayerCache[index][Class_HasNapalm];
}
}
return false;
@ -580,15 +615,15 @@ stock Float:ClassGetNapalmTime(index, cachetype = ZR_CLASS_CACHE_PLAYER)
{
case ZR_CLASS_CACHE_ORIGINAL:
{
return ClassData[index][class_napalm_time];
return ClassData[index][Class_NapalmTime];
}
case ZR_CLASS_CACHE_MODIFIED:
{
return ClassDataCache[index][class_napalm_time];
return ClassDataCache[index][Class_NapalmTime];
}
case ZR_CLASS_CACHE_PLAYER:
{
return ClassPlayerCache[index][class_napalm_time] * ClassGetAttributeMultiplier(index, ClassM_NapalmTime);
return ClassPlayerCache[index][Class_NapalmTime] * ClassGetAttributeMultiplier(index, ClassM_NapalmTime);
}
}
return -1.0;
@ -622,15 +657,15 @@ stock ClassGetImmunityMode(index, cachetype = ZR_CLASS_CACHE_PLAYER)
{
case ZR_CLASS_CACHE_ORIGINAL:
{
return ClassData[index][class_immunity_mode];
return ClassData[index][Class_ImmunityMode];
}
case ZR_CLASS_CACHE_MODIFIED:
{
return ClassDataCache[index][class_immunity_mode];
return ClassDataCache[index][Class_ImmunityMode];
}
case ZR_CLASS_CACHE_PLAYER:
{
return ClassPlayerCache[index][class_immunity_mode];
return ClassPlayerCache[index][Class_ImmunityMode];
}
}
return -1;
@ -654,15 +689,15 @@ stock Float:ClassGetImmunityAmount(index, cachetype = ZR_CLASS_CACHE_PLAYER)
{
case ZR_CLASS_CACHE_ORIGINAL:
{
return ClassData[index][class_immunity_amount];
return ClassData[index][Class_ImmunityAmount];
}
case ZR_CLASS_CACHE_MODIFIED:
{
return ClassDataCache[index][class_immunity_amount];
return ClassDataCache[index][Class_ImmunityAmount];
}
case ZR_CLASS_CACHE_PLAYER:
{
return ClassPlayerCache[index][class_immunity_amount];
return ClassPlayerCache[index][Class_ImmunityAmount];
}
}
return -1.0;
@ -687,15 +722,15 @@ stock bool:ClassGetNoFallDamage(index, cachetype = ZR_CLASS_CACHE_PLAYER)
{
case ZR_CLASS_CACHE_ORIGINAL:
{
return ClassData[index][class_no_fall_damage];
return ClassData[index][Class_NoFallDamage];
}
case ZR_CLASS_CACHE_MODIFIED:
{
return ClassDataCache[index][class_no_fall_damage];
return ClassDataCache[index][Class_NoFallDamage];
}
case ZR_CLASS_CACHE_PLAYER:
{
return ClassPlayerCache[index][class_no_fall_damage];
return ClassPlayerCache[index][Class_NoFallDamage];
}
}
return false;
@ -721,15 +756,15 @@ stock ClassGetHealth(index, cachetype = ZR_CLASS_CACHE_PLAYER)
{
case ZR_CLASS_CACHE_ORIGINAL:
{
return ClassData[index][class_health];
return ClassData[index][Class_Health];
}
case ZR_CLASS_CACHE_MODIFIED:
{
return ClassDataCache[index][class_health];
return ClassDataCache[index][Class_Health];
}
case ZR_CLASS_CACHE_PLAYER:
{
return RoundToCeil(ClassPlayerCache[index][class_health] * ClassGetAttributeMultiplier(index, ClassM_Health));
return RoundToCeil(ClassPlayerCache[index][Class_Health] * ClassGetAttributeMultiplier(index, ClassM_Health));
}
}
return -1;
@ -756,15 +791,15 @@ stock Float:ClassGetHealthRegenInterval(index, cachetype = ZR_CLASS_CACHE_PLAYER
{
case ZR_CLASS_CACHE_ORIGINAL:
{
return ClassData[index][class_health_regen_interval];
return ClassData[index][Class_HealthRegenInterval];
}
case ZR_CLASS_CACHE_MODIFIED:
{
return ClassDataCache[index][class_health_regen_interval];
return ClassDataCache[index][Class_HealthRegenInterval];
}
case ZR_CLASS_CACHE_PLAYER:
{
return ClassPlayerCache[index][class_health_regen_interval] * ClassGetAttributeMultiplier(index, ClassM_HealthRegenInterval);
return ClassPlayerCache[index][Class_HealthRegenInterval] * ClassGetAttributeMultiplier(index, ClassM_HealthRegenInterval);
}
}
return -1.0;
@ -791,15 +826,15 @@ stock ClassGetHealthRegenAmount(index, cachetype = ZR_CLASS_CACHE_PLAYER)
{
case ZR_CLASS_CACHE_ORIGINAL:
{
return ClassData[index][class_health_regen_amount];
return ClassData[index][Class_HealthRegenAmount];
}
case ZR_CLASS_CACHE_MODIFIED:
{
return ClassDataCache[index][class_health_regen_amount];
return ClassDataCache[index][Class_HealthRegenAmount];
}
case ZR_CLASS_CACHE_PLAYER:
{
return RoundToCeil(ClassPlayerCache[index][class_health_regen_amount] * ClassGetAttributeMultiplier(index, ClassM_HealthRegenAmount));
return RoundToCeil(ClassPlayerCache[index][Class_HealthRegenAmount] * ClassGetAttributeMultiplier(index, ClassM_HealthRegenAmount));
}
}
return -1;
@ -826,15 +861,15 @@ stock ClassGetHealthInfectGain(index, cachetype = ZR_CLASS_CACHE_PLAYER)
{
case ZR_CLASS_CACHE_ORIGINAL:
{
return ClassData[index][class_health_infect_gain];
return ClassData[index][Class_HealthInfectGain];
}
case ZR_CLASS_CACHE_MODIFIED:
{
return ClassDataCache[index][class_health_infect_gain];
return ClassDataCache[index][Class_HealthInfectGain];
}
case ZR_CLASS_CACHE_PLAYER:
{
return RoundToCeil(ClassPlayerCache[index][class_health_infect_gain] * ClassGetAttributeMultiplier(index, ClassM_HealthInfectGain));
return RoundToCeil(ClassPlayerCache[index][Class_HealthInfectGain] * ClassGetAttributeMultiplier(index, ClassM_HealthInfectGain));
}
}
return -1;
@ -858,15 +893,15 @@ stock ClassGetKillBonus(index, cachetype = ZR_CLASS_CACHE_PLAYER)
{
case ZR_CLASS_CACHE_ORIGINAL:
{
return ClassData[index][class_kill_bonus];
return ClassData[index][Class_KillBonus];
}
case ZR_CLASS_CACHE_MODIFIED:
{
return ClassDataCache[index][class_kill_bonus];
return ClassDataCache[index][Class_KillBonus];
}
case ZR_CLASS_CACHE_PLAYER:
{
return ClassPlayerCache[index][class_kill_bonus];
return ClassPlayerCache[index][Class_KillBonus];
}
}
return -1;
@ -892,15 +927,15 @@ stock Float:ClassGetSpeed(index, cachetype = ZR_CLASS_CACHE_PLAYER)
{
case ZR_CLASS_CACHE_ORIGINAL:
{
return ClassData[index][class_speed];
return ClassData[index][Class_Speed];
}
case ZR_CLASS_CACHE_MODIFIED:
{
return ClassDataCache[index][class_speed];
return ClassDataCache[index][Class_Speed];
}
case ZR_CLASS_CACHE_PLAYER:
{
return ClassPlayerCache[index][class_speed] * ClassGetAttributeMultiplier(index, ClassM_Speed);
return ClassPlayerCache[index][Class_Speed] * ClassGetAttributeMultiplier(index, ClassM_Speed);
}
}
return -1.0;
@ -926,15 +961,15 @@ stock Float:ClassGetKnockback(index, cachetype = ZR_CLASS_CACHE_PLAYER)
{
case ZR_CLASS_CACHE_ORIGINAL:
{
return ClassData[index][class_knockback];
return ClassData[index][Class_KnockBack];
}
case ZR_CLASS_CACHE_MODIFIED:
{
return ClassDataCache[index][class_knockback];
return ClassDataCache[index][Class_KnockBack];
}
case ZR_CLASS_CACHE_PLAYER:
{
return ClassPlayerCache[index][class_knockback] * ClassGetAttributeMultiplier(index, ClassM_Knockback);
return ClassPlayerCache[index][Class_KnockBack] * ClassGetAttributeMultiplier(index, ClassM_Knockback);
}
}
return 0.0;
@ -960,15 +995,15 @@ stock Float:ClassGetJumpHeight(index, cachetype = ZR_CLASS_CACHE_PLAYER)
{
case ZR_CLASS_CACHE_ORIGINAL:
{
return ClassData[index][class_jump_height];
return ClassData[index][Class_JumpHeight];
}
case ZR_CLASS_CACHE_MODIFIED:
{
return ClassDataCache[index][class_jump_height];
return ClassDataCache[index][Class_JumpHeight];
}
case ZR_CLASS_CACHE_PLAYER:
{
return ClassPlayerCache[index][class_jump_height] * ClassGetAttributeMultiplier(index, ClassM_JumpHeight);
return ClassPlayerCache[index][Class_JumpHeight] * ClassGetAttributeMultiplier(index, ClassM_JumpHeight);
}
}
return -1.0;
@ -994,15 +1029,15 @@ stock Float:ClassGetJumpDistance(index, cachetype = ZR_CLASS_CACHE_PLAYER)
{
case ZR_CLASS_CACHE_ORIGINAL:
{
return ClassData[index][class_jump_distance];
return ClassData[index][Class_JumpDistance];
}
case ZR_CLASS_CACHE_MODIFIED:
{
return ClassDataCache[index][class_jump_distance];
return ClassDataCache[index][Class_JumpDistance];
}
case ZR_CLASS_CACHE_PLAYER:
{
return ClassPlayerCache[index][class_jump_distance] * ClassGetAttributeMultiplier(index, ClassM_JumpDistance);
return ClassPlayerCache[index][Class_JumpDistance] * ClassGetAttributeMultiplier(index, ClassM_JumpDistance);
}
}
return -1.0;
@ -1034,6 +1069,10 @@ stock ClassAttributeNameToFlag(const String:attributename[])
{
return ZR_CLASS_FLAGS;
}
else if (StrEqual(attributename, "group", false))
{
return ZR_CLASS_GROUP;
}
else if (StrEqual(attributename, "name", false))
{
return ZR_CLASS_NAME;
@ -1230,7 +1269,8 @@ stock ClassDataTypes:ClassGetAttributeType(attributeflag)
}
// String.
case ZR_CLASS_NAME,
case ZR_CLASS_GROUP,
ZR_CLASS_NAME,
ZR_CLASS_DESCRIPTION,
ZR_CLASS_MODEL_PATH,
ZR_CLASS_OVERLAY_PATH:

View File

@ -59,7 +59,14 @@ public Action:ZClassCommand(client, argc)
// If client is console, then stop and tell them this feature is for players only.
if (ZRIsConsole(client))
{
TranslationPrintToServer("Must be player");
TranslationReplyToCommand(client, "Must be player");
return Plugin_Handled;
}
// Check if class selection is allowed.
if (!ClassAllowSelection(client))
{
TranslationReplyToCommand(client, "Classes Selection Not Allowed");
return Plugin_Handled;
}
@ -612,22 +619,22 @@ stock bool:ClassModifyBoolean(classindex, attributeflag, bool:value)
{
case ZR_CLASS_ENABLED:
{
ClassDataCache[classindex][class_enabled] = bool:value;
ClassDataCache[classindex][Class_Enabled] = bool:value;
return true;
}
case ZR_CLASS_NVGS:
{
ClassDataCache[classindex][class_nvgs] = bool:value;
ClassDataCache[classindex][Class_Nvgs] = bool:value;
return true;
}
case ZR_CLASS_NO_FALL_DAMAGE:
{
ClassDataCache[classindex][class_no_fall_damage] = bool:value;
ClassDataCache[classindex][Class_NoFallDamage] = bool:value;
return true;
}
case ZR_CLASS_HAS_NAPALM:
{
ClassDataCache[classindex][class_has_napalm] = bool:value;
ClassDataCache[classindex][Class_HasNapalm] = bool:value;
return true;
}
}
@ -663,80 +670,80 @@ stock ClassModifyInteger(classindex, attributeflag, value, Float:multiplier = 0.
{
case ZR_CLASS_FLAGS:
{
ClassDataCache[classindex][class_flags] = value;
ClassDataCache[classindex][Class_Flags] = value;
return true;
}
case ZR_CLASS_ALPHA_INITIAL:
{
if (ismultiplier)
{
value = RoundToNearest(float(ClassData[classindex][class_alpha_initial]) * multiplier);
value = RoundToNearest(float(ClassData[classindex][Class_AlphaInitial]) * multiplier);
}
ClassDataCache[classindex][class_alpha_initial] = value;
ClassDataCache[classindex][Class_AlphaInitial] = value;
return true;
}
case ZR_CLASS_ALPHA_DAMAGED:
{
if (ismultiplier)
{
value = RoundToNearest(float(ClassData[classindex][class_alpha_damaged]) * multiplier);
value = RoundToNearest(float(ClassData[classindex][Class_AlphaDamaged]) * multiplier);
}
ClassDataCache[classindex][class_alpha_damaged] = value;
ClassDataCache[classindex][Class_AlphaDamaged] = value;
return true;
}
case ZR_CLASS_ALPHA_DAMAGE:
{
if (ismultiplier)
{
value = RoundToNearest(float(ClassData[classindex][class_alpha_damage]) * multiplier);
value = RoundToNearest(float(ClassData[classindex][Class_AlphaDamage]) * multiplier);
}
ClassDataCache[classindex][class_alpha_damage] = value;
ClassDataCache[classindex][Class_AlphaDamage] = value;
return true;
}
case ZR_CLASS_FOV:
{
ClassDataCache[classindex][class_fov] = value;
ClassDataCache[classindex][Class_Fov] = value;
return true;
}
case ZR_CLASS_IMMUNITY_MODE:
{
ClassDataCache[classindex][class_fov] = value;
ClassDataCache[classindex][Class_ImmunityMode] = value;
return true;
}
case ZR_CLASS_HEALTH:
{
if (ismultiplier)
{
value = RoundToNearest(float(ClassData[classindex][class_health]) * multiplier);
value = RoundToNearest(float(ClassData[classindex][Class_Health]) * multiplier);
}
ClassDataCache[classindex][class_health] = value;
ClassDataCache[classindex][Class_Health] = value;
return true;
}
case ZR_CLASS_HEALTH_REGEN_AMOUNT:
{
if (ismultiplier)
{
value = RoundToNearest(float(ClassData[classindex][class_health_regen_amount]) * multiplier);
value = RoundToNearest(float(ClassData[classindex][Class_HealthRegenAmount]) * multiplier);
}
ClassDataCache[classindex][class_health_regen_amount] = value;
ClassDataCache[classindex][Class_HealthRegenAmount] = value;
return true;
}
case ZR_CLASS_HEALTH_INFECT_GAIN:
{
if (ismultiplier)
{
value = RoundToNearest(float(ClassData[classindex][class_health_infect_gain]) * multiplier);
value = RoundToNearest(float(ClassData[classindex][Class_HealthInfectGain]) * multiplier);
}
ClassDataCache[classindex][class_health_infect_gain] = value;
ClassDataCache[classindex][Class_HealthInfectGain] = value;
return true;
}
case ZR_CLASS_KILL_BONUS:
{
if (ismultiplier)
{
value = RoundToNearest(float(ClassData[classindex][class_kill_bonus]) * multiplier);
value = RoundToNearest(float(ClassData[classindex][Class_KillBonus]) * multiplier);
}
ClassDataCache[classindex][class_kill_bonus] = value;
ClassDataCache[classindex][Class_KillBonus] = value;
return true;
}
}
@ -770,63 +777,63 @@ stock ClassModifyFloat(classindex, attributeflag, Float:value, bool:ismultiplier
{
if (ismultiplier)
{
value = ClassData[classindex][class_napalm_time] * value;
value = ClassData[classindex][Class_NapalmTime] * value;
}
ClassDataCache[classindex][class_napalm_time] = value;
ClassDataCache[classindex][Class_NapalmTime] = value;
return true;
}
case ZR_CLASS_IMMUNITY_AMOUNT:
{
if (ismultiplier)
{
value = ClassData[classindex][class_immunity_amount] * value;
value = ClassData[classindex][Class_ImmunityAmount] * value;
}
ClassDataCache[classindex][class_immunity_amount] = value;
ClassDataCache[classindex][Class_ImmunityAmount] = value;
return true;
}
case ZR_CLASS_HEALTH_REGEN_INTERVAL:
{
if (ismultiplier)
{
value = ClassData[classindex][class_health_regen_interval] * value;
value = ClassData[classindex][Class_HealthRegenInterval] * value;
}
ClassDataCache[classindex][class_health_regen_interval] = value;
ClassDataCache[classindex][Class_HealthRegenInterval] = value;
return true;
}
case ZR_CLASS_SPEED:
{
if (ismultiplier)
{
value = ClassData[classindex][class_speed] * value;
value = ClassData[classindex][Class_Speed] * value;
}
ClassDataCache[classindex][class_speed] = value;
ClassDataCache[classindex][Class_Speed] = value;
return true;
}
case ZR_CLASS_KNOCKBACK:
{
if (ismultiplier)
{
value = ClassData[classindex][class_knockback] * value;
value = ClassData[classindex][Class_KnockBack] * value;
}
ClassDataCache[classindex][class_knockback] = value;
ClassDataCache[classindex][Class_KnockBack] = value;
return true;
}
case ZR_CLASS_JUMP_HEIGHT:
{
if (ismultiplier)
{
value = ClassData[classindex][class_jump_height] * value;
value = ClassData[classindex][Class_JumpHeight] * value;
}
ClassDataCache[classindex][class_jump_height] = value;
ClassDataCache[classindex][Class_JumpHeight] = value;
return true;
}
case ZR_CLASS_JUMP_DISTANCE:
{
if (ismultiplier)
{
value = ClassData[classindex][class_jump_distance] * value;
value = ClassData[classindex][Class_JumpDistance] * value;
}
ClassDataCache[classindex][class_jump_distance] = value;
ClassDataCache[classindex][Class_JumpDistance] = value;
return true;
}
}
@ -853,24 +860,29 @@ stock ClassModifyString(classindex, attributeflag, const String:value[])
switch (attributeflag)
{
case ZR_CLASS_GROUP:
{
strcopy(ClassDataCache[classindex][Class_Group], 64, value);
return true;
}
case ZR_CLASS_NAME:
{
strcopy(ClassDataCache[classindex][class_name], 64, value);
strcopy(ClassDataCache[classindex][Class_Name], 64, value);
return true;
}
case ZR_CLASS_DESCRIPTION:
{
strcopy(ClassDataCache[classindex][class_description], 256, value);
strcopy(ClassDataCache[classindex][Class_Description], 256, value);
return true;
}
case ZR_CLASS_MODEL_PATH:
{
strcopy(ClassDataCache[classindex][class_model_path], PLATFORM_MAX_PATH, value);
strcopy(ClassDataCache[classindex][Class_ModelPath], PLATFORM_MAX_PATH, value);
return true;
}
case ZR_CLASS_OVERLAY_PATH:
{
strcopy(ClassDataCache[classindex][class_overlay_path], PLATFORM_MAX_PATH, value);
strcopy(ClassDataCache[classindex][Class_OverlayPath], PLATFORM_MAX_PATH, value);
return true;
}
}

View File

@ -24,6 +24,11 @@ ClassOnCookiesCreate()
{
// Forward event to sub-modules.
ClassOverlayOnCookiesCreate();
// Create class index cookies.
g_hClassCookieClassSelected[ZR_CLASS_TEAM_HUMANS] = RegClientCookie("zr_humanclass", "The last human class selected.", CookieAccess_Protected);
g_hClassCookieClassSelected[ZR_CLASS_TEAM_ZOMBIES] = RegClientCookie("zr_zombieclass", "The last zombie class selected.", CookieAccess_Protected);
g_hClassCookieClassSelected[ZR_CLASS_TEAM_ADMINS] = RegClientCookie("zr_adminclass", "The last admin mode class selected.", CookieAccess_Protected);
}
/**
@ -75,6 +80,7 @@ ClassOnClientSpawn(client)
decl String:originalmodel[PLATFORM_MAX_PATH];
decl String:steamid[16];
decl String:classname[64];
new filter[ClassFilter];
// Check if the player is dead. Spawning into the game is also a event in
// the connection process.
@ -121,15 +127,24 @@ ClassOnClientSpawn(client)
// Assign random classes if enabled. Always do it for bots.
if (randomclass || StrEqual(steamid, "BOT"))
{
// Exclude special class flags like mother zombies and admin classes.
new denyflags = ZR_CLASS_SPECIALFLAGS;
// Setup filtering
// ---------------
// Exclude special class flags like mother zombies and admin classes.
filter[ClassFilter_DenyFlags] = ZR_CLASS_SPECIALFLAGS;
// Allow admin classes if admin.
denyflags -= ZRIsClientAdmin(client) ? ZR_CLASS_FLAG_ADMIN_ONLY : 0;
filter[ClassFilter_DenyFlags] -= ZRIsClientAdmin(client) ? ZR_CLASS_FLAG_ADMIN_ONLY : 0;
// Specify client for checking group permissions.
filter[ClassFilter_Client] = client;
// Get classes
// -----------
// Get random classes for each type.
new randomzombie = ClassGetRandomClass(ZR_CLASS_TEAM_ZOMBIES, _, _, denyflags);
new randomhuman = ClassGetRandomClass(ZR_CLASS_TEAM_HUMANS, _, _, denyflags);
new randomzombie = ClassGetRandomClass(ZR_CLASS_TEAM_ZOMBIES, filter);
new randomhuman = ClassGetRandomClass(ZR_CLASS_TEAM_HUMANS, filter);
// Set selected zombie class index.
ClassSelected[client][ZR_CLASS_TEAM_ZOMBIES] = randomzombie;
@ -141,11 +156,34 @@ ClassOnClientSpawn(client)
ClassGetName(randomhuman, classname, sizeof(classname), ZR_CLASS_TEAM_HUMANS);
TranslationPrintToChat(client, "Classes random assignment", classname);
}
// Display class menu if enabled.
new bool:classmenu = GetConVarBool(g_hCvarsList[CVAR_CLASSES_SPAWN]);
if (classmenu)
{
ClassMenuMain(client);
}
}
// Apply class attributes for the active class.
ClassReloadPlayerCache(client, ClassGetActiveIndex(client));
ClassApplyAttributes(client);
// Check if instant class change cvar is set.
new Float:instantspawn = GetConVarFloat(g_hCvarsList[CVAR_CLASSES_CHANGE_TIMELIMIT]);
if (instantspawn > 0)
{
// Allow instant class change.
ClassAllowInstantChange[client] = true;
// Create timer to disable instant change.
CreateTimer(instantspawn, Event_ClassDisableInstantSpawn, client, TIMER_FLAG_NO_MAPCHANGE);
}
else
{
// Make sure instant change is not allowed.
ClassAllowInstantChange[client] = false;
}
}
/**
@ -176,12 +214,16 @@ ClassOnClientInfected(client, bool:motherzombie = false)
new classindex = ClassGetActiveIndex(client);
new isadmin;
new motherindex;
new filter[ClassFilter];
decl String:motherzombiesetting[64];
// Disable class attributes with timers.
ClassHealthRegenStop(client);
// Make sure the player is not allowed to instantly change class.
ClassAllowInstantChange[client] = false;
// Check if it's a mother zombie.
if (motherzombie)
{
@ -198,8 +240,23 @@ ClassOnClientInfected(client, bool:motherzombie = false)
}
else if (StrEqual(motherzombiesetting, "random", false))
{
// Setup filtering
// ---------------
// Exclude special class flags.
filter[ClassFilter_DenyFlags] = ZR_CLASS_SPECIALFLAGS;
// Allow admin classes if admin.
filter[ClassFilter_DenyFlags] -= isadmin;
// Specify client for checking group permissions.
filter[ClassFilter_Client] = client;
// Get class
// ---------
// Get random regular zombie class. Remove admin flag if admin.
motherindex = ClassGetRandomClass(ZR_CLASS_TEAM_ZOMBIES, _, _, ZR_CLASS_SPECIALFLAGS - isadmin);
motherindex = ClassGetRandomClass(ZR_CLASS_TEAM_ZOMBIES, filter);
// Validate index. Do not change class if it's invalid.
if (ClassValidateIndex(motherindex))
@ -213,8 +270,26 @@ ClassOnClientInfected(client, bool:motherzombie = false)
}
else if (StrEqual(motherzombiesetting, "motherzombies", false))
{
// Setup filtering
// ---------------
// Exclude special class flags except mother zombies.
filter[ClassFilter_DenyFlags] = ZR_CLASS_SPECIALFLAGS - ZR_CLASS_FLAG_MOTHER_ZOMBIE;
// Require mother zombie class flag.
filter[ClassFilter_RequireFlags] = ZR_CLASS_FLAG_MOTHER_ZOMBIE;
// Allow admin classes if admin.
filter[ClassFilter_DenyFlags] -= isadmin;
// Specify client for checking group permissions.
filter[ClassFilter_Client] = client;
// Get class
// ---------
// Get random mother zombie class. Include admin classes if admin.
motherindex = ClassGetRandomClass(ZR_CLASS_TEAM_ZOMBIES, _, ZR_CLASS_FLAG_MOTHER_ZOMBIE + isadmin, ZR_CLASS_FLAG_ADMIN_ONLY - isadmin);
motherindex = ClassGetRandomClass(ZR_CLASS_TEAM_ZOMBIES, filter);
// Validate index. Do not change class if it's invalid.
if (ClassValidateIndex(motherindex))
@ -259,3 +334,12 @@ ClassOnClientInfected(client, bool:motherzombie = false)
// Apply the new attributes.
ClassApplyAttributes(client, motherzombie);
}
/**
* Timer callback for disabling instant class change setting on a client.
*/
public Action:Event_ClassDisableInstantSpawn(Handle:timer, any:client)
{
// Disable instant class change.
ClassAllowInstantChange[client] = false;
}

View File

@ -65,18 +65,19 @@ ClassMenuMain(client)
// Get number of enabled classes per team.
new zombiecount = ClassCountTeam(ZR_CLASS_TEAM_ZOMBIES);
new humancount = ClassCountTeam(ZR_CLASS_TEAM_ZOMBIES);
new admincount = ClassCountTeam(ZR_CLASS_TEAM_ZOMBIES);
new admincount = ClassCountTeam(ZR_CLASS_TEAM_ADMINS);
// Get previously selected class indexes, if set.
// Get next class indexes, if set.
new nextzombie = ClassSelectedNext[client][ZR_CLASS_TEAM_ZOMBIES];
new nexthuman = ClassSelectedNext[client][ZR_CLASS_TEAM_HUMANS];
new nextadmin = ClassSelectedNext[client][ZR_CLASS_TEAM_ADMINS];
// Set draw style on class options depending on number of enabled classes.
// Disable class selection if there's only one class.
new zombie_itemdraw = (zombiecount > 1) ? ITEMDRAW_DEFAULT : ITEMDRAW_DISABLED;
new human_itemdraw = (humancount > 1) ? ITEMDRAW_DEFAULT : ITEMDRAW_DISABLED;
new admin_itemdraw = (admincount > 1) ? ITEMDRAW_DEFAULT : ITEMDRAW_DISABLED;
// Set draw style on class options depending on number of enabled classes
// and selection permissions. Disable class selection if there's only one
// class.
new zombie_itemdraw = (zombiecount > 1 && ClassAllowSelection(client, ZR_CLASS_TEAM_ZOMBIES)) ? ITEMDRAW_DEFAULT : ITEMDRAW_DISABLED;
new human_itemdraw = (humancount > 1 && ClassAllowSelection(client, ZR_CLASS_TEAM_HUMANS)) ? ITEMDRAW_DEFAULT : ITEMDRAW_DISABLED;
new admin_itemdraw = (admincount > 1 && ClassAllowSelection(client, ZR_CLASS_TEAM_ADMINS)) ? ITEMDRAW_DEFAULT : ITEMDRAW_DISABLED;
// Check if the player is in admin mode.
if (ClassPlayerInAdminMode[client])
@ -92,18 +93,18 @@ ClassMenuMain(client)
// Get current class name.
ClassGetName(ClassSelected[client][ZR_CLASS_TEAM_ZOMBIES], zombieclass, sizeof(zombieclass), ZR_CLASS_CACHE_MODIFIED);
// Check if previous index is set.
// Check if next index is set.
if (ClassValidateIndex(nextzombie))
{
// Get name of previous class index and format item text.
ClassGetName(nextzombie, nextzombiename, sizeof(nextzombiename), ZR_CLASS_CACHE_MODIFIED);
Format(zombieselect, sizeof(zombieselect), "%t", "Classes menu zombie next", zombieclass, nextzombiename);
Format(zombieselect, sizeof(zombieselect), "%t\n %t\n %t", "Classes menu select zombie", "Classes menu active", zombieclass, "Classes menu next", nextzombiename);
}
else
{
// Use current class name and format item text.
Format(zombieselect, sizeof(zombieselect), "%t", "Classes menu zombie current", zombieclass);
Format(zombieselect, sizeof(zombieselect), "%t\n %s", "Classes menu select zombie", zombieclass);
}
// Add item to list.
@ -116,18 +117,18 @@ ClassMenuMain(client)
// Get current class name.
ClassGetName(ClassSelected[client][ZR_CLASS_TEAM_HUMANS], humanclass, sizeof(humanclass), ZR_CLASS_CACHE_MODIFIED);
// Check if previous index is set.
// Check if next index is set.
if (ClassValidateIndex(nexthuman))
{
// Get name of previous class index and format item text.
ClassGetName(nexthuman, nexthumanname, sizeof(nexthumanname), ZR_CLASS_CACHE_MODIFIED);
Format(humanselect, sizeof(humanselect), "%t", "Classes menu human next", humanclass, nexthumanname);
Format(humanselect, sizeof(humanselect), "%t\n %t\n %t", "Classes menu select human", "Classes menu active", humanclass, "Classes menu next", nexthumanname);
}
else
{
// Use current class name and format item text.
Format(humanselect, sizeof(humanselect), "%t", "Classes menu human current", humanclass);
Format(humanselect, sizeof(humanselect), "%t\n %s", "Classes menu select human", humanclass);
}
// Add item to list.
@ -143,17 +144,17 @@ ClassMenuMain(client)
// Get current class name.
ClassGetName(ClassSelected[client][ZR_CLASS_TEAM_ADMINS], adminclass, sizeof(adminclass), ZR_CLASS_CACHE_MODIFIED);
// Check if previous index is set.
// Check if next index is set.
if (ClassValidateIndex(nextadmin))
{
// Get name of previous class index and format item text.
ClassGetName(nextadmin, nextadminname, sizeof(nextadminname), ZR_CLASS_CACHE_MODIFIED);
Format(adminselect, sizeof(adminselect), "%t", "Classes menu admin next", adminclass, nextadminname);
Format(adminselect, sizeof(adminselect), "%t\n %t\n %t", "Classes menu select admin", "Classes menu active", adminclass, "Classes menu next", nextadminname);
}
else
{
// Use current class name and format item text.
Format(adminselect, sizeof(adminselect), "%t", "Classes menu admin current", adminclass);
Format(adminselect, sizeof(adminselect), "%t\n %s", "Classes menu select admin", adminclass);
}
// Add item to list.
@ -246,7 +247,6 @@ ClassMenuSelect(client, teamid)
new Handle:menu = CreateMenu(ClassMenuSelectHandle);
new arraycount;
new classindex;
new denyflags;
decl String:title[MENU_LINE_TITLE_LENGTH];
decl String:classname[MENU_LINE_REG_LENGTH];
@ -260,15 +260,15 @@ ClassMenuSelect(client, teamid)
{
case ZR_CLASS_TEAM_ZOMBIES:
{
Format(title, sizeof(title), "%t\n", "Classes menu zombie");
Format(title, sizeof(title), "%t:\n", "Classes menu select zombie");
}
case ZR_CLASS_TEAM_HUMANS:
{
Format(title, sizeof(title), "%t\n", "Classes menu human");
Format(title, sizeof(title), "%t:\n", "Classes menu select human");
}
case ZR_CLASS_TEAM_ADMINS:
{
Format(title, sizeof(title), "%t\n", "Classes menu admin");
Format(title, sizeof(title), "%t:\n", "Classes menu select admin");
}
}
SetMenuTitle(menu, title);
@ -276,12 +276,24 @@ ClassMenuSelect(client, teamid)
// Create buffer array.
new Handle:classarray = CreateArray();
// Set up filtering.
denyflags = ZR_CLASS_FLAG_MOTHER_ZOMBIE; // Hide mother zombie classes.
denyflags += !ZRIsClientAdmin(client) ? ZR_CLASS_FLAG_ADMIN_ONLY : 0; // Hide admin-only classes if not admin.
// Set up filtering
// ----------------
new filter[ClassFilter];
// Hide mother zombie classes.
filter[ClassFilter_DenyFlags] = ZR_CLASS_FLAG_MOTHER_ZOMBIE;
// Hide admin-only classes if not admin.
filter[ClassFilter_DenyFlags] += !ZRIsClientAdmin(client) ? ZR_CLASS_FLAG_ADMIN_ONLY : 0;
// Specify client for checking class group permissions.
filter[ClassFilter_Client] = client;
// Get classes
// -----------
// Copy all class indexes into the array, with the specified filter settings.
if (ClassAddToArray(classarray, teamid, _, _, denyflags))
if (ClassAddToArray(classarray, teamid, filter))
{
// Get number of classes.
arraycount = GetArraySize(classarray);
@ -331,28 +343,57 @@ public ClassMenuSelectHandle(Handle:menu, MenuAction:action, client, slot)
// Solve teamid from the class index.
teamid = ClassGetTeamID(classindex, ZR_CLASS_CACHE_MODIFIED);
// Check if the player is alive.
if (IsPlayerAlive(client))
// Allow instant class change if enabled and both class and player is human.
if (ClassAllowInstantChange[client] && !iszombie && teamid == ZR_CLASS_TEAM_HUMANS)
{
// Set next spawn index if the player is changing the class on
// his active team.
if ((iszombie && teamid == ZR_CLASS_TEAM_ZOMBIES) ||
(!iszombie && teamid == ZR_CLASS_TEAM_HUMANS) ||
(ClassPlayerInAdminMode[client] && teamid == ZR_CLASS_TEAM_ADMINS))
{
// Set class to be used on next spawn.
ClassSelectedNext[client][teamid] = classindex;
}
else
{
// Directly change the selected class index.
ClassSelected[client][teamid] = classindex;
}
// Directly change the selected class index.
ClassSelected[client][teamid] = classindex;
// Update cache and apply attributes.
ClassReloadPlayerCache(client, classindex);
ClassApplyAttributes(client);
}
else
{
// Player isn't alive. The class can be directly changed.
ClassSelected[client][teamid] = classindex;
// Check if the player is alive.
if (IsPlayerAlive(client))
{
// Set next spawn index if the player is changing the class on
// his active team.
if ((iszombie && teamid == ZR_CLASS_TEAM_ZOMBIES) ||
(!iszombie && teamid == ZR_CLASS_TEAM_HUMANS) ||
(ClassPlayerInAdminMode[client] && teamid == ZR_CLASS_TEAM_ADMINS))
{
// Check if player selected the same class that he already is.
if (ClassSelected[client][teamid] == classindex)
{
// Player is already the specified class. Disable
// next class for the specified team.
ClassSelectedNext[client][teamid] = -1;
}
else
{
// Set class to be used on next spawn.
ClassSelectedNext[client][teamid] = classindex;
}
}
else
{
// Directly change the selected class index.
ClassSelected[client][teamid] = classindex;
}
}
else
{
// Player isn't alive. The class can be directly changed.
ClassSelected[client][teamid] = classindex;
}
// Save selected class index in cookie if enabled.
if (GetConVarBool(g_hCvarsList[CVAR_CLASSES_SAVE]))
{
CookiesSetInt(client, g_hClassCookieClassSelected[teamid], classindex + 1);
}
}
}
case MenuAction_Cancel:
@ -395,15 +436,9 @@ public ClassMenuSelectHandle(Handle:menu, MenuAction:action, client, slot)
* @return True if displayed, false otherwise.
*/
bool:ClassTeamSelect(client)
{
// Validate client.
if (!ZRIsClientValid(client, false))
{
return false;
}
{
// Validate permissions.
if (!ZRIsClientAdmin(client, Admin_Config))
if (!ZRIsClientPrivileged(client, OperationType_Configuration))
{
return false;
}

View File

@ -47,8 +47,8 @@ stock bool:ClassValidateTeamRequirements(cachetype = ZR_CLASS_CACHE_ORIGINAL)
}
// Test if a zombie and human class was found.
zombieindex = ClassGetFirstClass(ZR_CLASS_TEAM_ZOMBIES, _, _, ZR_CLASS_SPECIALFLAGS, cachetype);
humanindex = ClassGetFirstClass(ZR_CLASS_TEAM_HUMANS, _, _, ZR_CLASS_SPECIALFLAGS, cachetype);
zombieindex = ClassGetFirstClass(ZR_CLASS_TEAM_ZOMBIES, _, cachetype);
humanindex = ClassGetFirstClass(ZR_CLASS_TEAM_HUMANS, _, cachetype);
// Validate indexes.
if (ClassValidateIndex(zombieindex) && ClassValidateIndex(humanindex))
@ -79,8 +79,8 @@ stock bool:ClassValidateTeamDefaults(cachetype = ZR_CLASS_CACHE_ORIGINAL)
}
// Test if a default zombie and human class was found.
zombieindex = ClassGetDefaultClass(ZR_CLASS_TEAM_ZOMBIES, _, _, _, cachetype);
humanindex = ClassGetDefaultClass(ZR_CLASS_TEAM_HUMANS, _, _, _, cachetype);
zombieindex = ClassGetDefaultClass(ZR_CLASS_TEAM_ZOMBIES, _, cachetype);
humanindex = ClassGetDefaultClass(ZR_CLASS_TEAM_HUMANS, _, cachetype);
// Validate indexes.
if (ClassValidateIndex(zombieindex) && ClassValidateIndex(humanindex))
@ -107,26 +107,36 @@ stock ClassValidateAttributes(classindex)
new flags;
// Team.
if (ClassData[classindex][class_team] < ZR_CLASS_TEAM_MIN || ClassData[classindex][class_team] > ZR_CLASS_TEAM_MAX)
if (ClassData[classindex][Class_Team] < ZR_CLASS_TEAM_MIN || ClassData[classindex][Class_Team] > ZR_CLASS_TEAM_MAX)
{
flags += ZR_CLASS_TEAM;
}
// Class flags.
if (ClassData[classindex][class_flags] < ZR_CLASS_FLAGS_MIN || ClassData[classindex][class_flags] > ZR_CLASS_FLAGS_MAX)
if (ClassData[classindex][Class_Flags] < ZR_CLASS_FLAGS_MIN || ClassData[classindex][Class_Flags] > ZR_CLASS_FLAGS_MAX)
{
flags += ZR_CLASS_FLAGS;
}
// Group.
if (strlen(ClassData[classindex][Class_Group]))
{
// Check if the group exist.
if (FindAdmGroup(ClassData[classindex][Class_Group]) == INVALID_GROUP_ID)
{
flags += ZR_CLASS_GROUP;
}
}
// Name.
if (strlen(ClassData[classindex][class_name]) < ZR_CLASS_NAME_MIN)
if (strlen(ClassData[classindex][Class_Name]) < ZR_CLASS_NAME_MIN)
{
flags += ZR_CLASS_NAME;
}
else
{
decl String:name[64];
strcopy(name, sizeof(name), ClassData[classindex][class_name]);
strcopy(name, sizeof(name), ClassData[classindex][Class_Name]);
// Check for reserved name keyworks. These aren't allowed as names.
if (StrEqual(name, "all", false) ||
@ -139,21 +149,23 @@ stock ClassValidateAttributes(classindex)
}
// Description.
if (strlen(ClassData[classindex][class_description]) < ZR_CLASS_DESCRIPTION_MIN)
if (strlen(ClassData[classindex][Class_Description]) < ZR_CLASS_DESCRIPTION_MIN)
{
flags += ZR_CLASS_DESCRIPTION;
}
// Model path.
decl String:model_path[PLATFORM_MAX_PATH];
if (strcopy(model_path, sizeof(model_path), ClassData[classindex][class_model_path]) == 0)
if (strcopy(model_path, sizeof(model_path), ClassData[classindex][Class_ModelPath]) == 0)
{
flags += ZR_CLASS_MODEL_PATH;
}
else
{
// Check if a model different from default or random is specified.
if (!StrEqual(model_path, "random", false) && !StrEqual(model_path, "default", false))
// Check if a model different from a pre-defined setting.
if (!StrEqual(model_path, "random", false) &&
!StrEqual(model_path, "default", false) &&
!StrEqual(model_path, "nochange", false))
{
// Check if the file exists.
if (!FileExists(model_path))
@ -164,21 +176,21 @@ stock ClassValidateAttributes(classindex)
}
// Alpha, initial.
new alpha_initial = ClassData[classindex][class_alpha_initial];
new alpha_initial = ClassData[classindex][Class_AlphaInitial];
if (!(alpha_initial >= ZR_CLASS_ALPHA_INITIAL_MIN && alpha_initial <= ZR_CLASS_ALPHA_INITIAL_MAX))
{
flags += ZR_CLASS_ALPHA_INITIAL;
}
// Alpha, damaged.
new alpha_damaged = ClassData[classindex][class_alpha_damaged];
new alpha_damaged = ClassData[classindex][Class_AlphaDamaged];
if (!(alpha_damaged >= ZR_CLASS_ALPHA_DAMAGED_MIN && alpha_damaged <= ZR_CLASS_ALPHA_DAMAGED_MAX))
{
flags += ZR_CLASS_ALPHA_DAMAGED;
}
// Alpha, damage.
new alpha_damage = ClassData[classindex][class_alpha_damage];
new alpha_damage = ClassData[classindex][Class_AlphaDamage];
if (!(alpha_damage >= ZR_CLASS_ALPHA_DAMAGE_MIN && alpha_damage <= ZR_CLASS_ALPHA_DAMAGE_MAX))
{
flags += ZR_CLASS_ALPHA_DAMAGE;
@ -187,7 +199,7 @@ stock ClassValidateAttributes(classindex)
// Overlay path.
decl String:overlay_path[PLATFORM_MAX_PATH];
decl String:overlay[PLATFORM_MAX_PATH];
if (strcopy(overlay_path, sizeof(overlay_path), ClassData[classindex][class_overlay_path]) > 0)
if (strcopy(overlay_path, sizeof(overlay_path), ClassData[classindex][Class_OverlayPath]) > 0)
{
// Check if the file exists.
Format(overlay, sizeof(overlay), "materials/%s.vmt", overlay_path);
@ -198,77 +210,80 @@ stock ClassValidateAttributes(classindex)
}
// Field of view.
new fov = ClassData[classindex][class_fov];
new fov = ClassData[classindex][Class_Fov];
if (!(fov >= ZR_CLASS_FOV_MIN && fov <= ZR_CLASS_FOV_MAX))
{
flags += ZR_CLASS_FOV;
}
// Napalm time.
new Float:napalm_time = ClassData[classindex][class_napalm_time];
new Float:napalm_time = ClassData[classindex][Class_NapalmTime];
if (!(napalm_time >= ZR_CLASS_NAPALM_TIME_MIN && napalm_time <= ZR_CLASS_NAPALM_TIME_MAX))
{
flags += ZR_CLASS_NAPALM_TIME;
}
// Immunity mode (not implemented).
// Health.
new health = ClassData[classindex][class_health];
new health = ClassData[classindex][Class_Health];
if (!(health >= ZR_CLASS_HEALTH_MIN && health <= ZR_CLASS_HEALTH_MAX))
{
flags += ZR_CLASS_HEALTH;
}
// Health regen interval.
new Float:regen_interval = ClassData[classindex][class_health_regen_interval];
new Float:regen_interval = ClassData[classindex][Class_HealthRegenInterval];
if (!(regen_interval >= ZR_CLASS_REGEN_INTERVAL_MIN && regen_interval <= ZR_CLASS_REGEN_INTERVAL_MAX))
{
flags += ZR_CLASS_HEALTH_REGEN_INTERVAL;
}
// Health regen amount.
new regen_amount = ClassData[classindex][class_health_regen_amount];
new regen_amount = ClassData[classindex][Class_HealthRegenAmount];
if (!(regen_amount >= ZR_CLASS_REGEN_AMOUNT_MIN && regen_amount <= ZR_CLASS_REGEN_AMOUNT_MAX))
{
flags += ZR_CLASS_HEALTH_REGEN_AMOUNT;
}
// Health infect gain.
new infect_gain = ClassData[classindex][class_health_infect_gain];
new infect_gain = ClassData[classindex][Class_HealthInfectGain];
if (!(infect_gain >= ZR_CLASS_HEALTH_INFECT_GAIN_MIN && infect_gain <= ZR_CLASS_HEALTH_INFECT_GAIN_MAX))
{
flags += ZR_CLASS_HEALTH_INFECT_GAIN;
}
// Kill bonus.
new kill_bonus = ClassData[classindex][class_kill_bonus];
new kill_bonus = ClassData[classindex][Class_KillBonus];
if (!(kill_bonus >= ZR_CLASS_KILL_BONUS_MIN && kill_bonus <= ZR_CLASS_KILL_BONUS_MAX))
{
flags += ZR_CLASS_KILL_BONUS;
}
// Speed.
new Float:speed = ClassData[classindex][class_speed];
new Float:speed = ClassData[classindex][Class_Speed];
if (!(speed >= ZR_CLASS_SPEED_MIN && speed <= ZR_CLASS_SPEED_MAX))
{
flags += ZR_CLASS_SPEED;
}
// Knockback.
new Float:knockback = ClassData[classindex][class_knockback];
new Float:knockback = ClassData[classindex][Class_KnockBack];
if (!(knockback >= ZR_CLASS_KNOCKBACK_MIN && knockback <= ZR_CLASS_KNOCKBACK_MAX))
{
flags += ZR_CLASS_KNOCKBACK;
}
// Jump height.
new Float:jump_height = ClassData[classindex][class_jump_height];
new Float:jump_height = ClassData[classindex][Class_JumpHeight];
if (!(jump_height >= ZR_CLASS_JUMP_HEIGHT_MIN && jump_height <= ZR_CLASS_JUMP_HEIGHT_MAX))
{
flags += ZR_CLASS_JUMP_HEIGHT;
}
// Jump distance.
new Float:jump_distance = ClassData[classindex][class_jump_distance];
new Float:jump_distance = ClassData[classindex][Class_JumpDistance];
if (!(jump_distance >= ZR_CLASS_JUMP_DISTANCE_MIN && jump_distance <= ZR_CLASS_JUMP_DISTANCE_MAX))
{
flags += ZR_CLASS_JUMP_DISTANCE;
@ -315,21 +330,21 @@ stock bool:ClassTeamCompare(index, teamid, cachetype = ZR_CLASS_CACHE_MODIFIED)
{
case ZR_CLASS_CACHE_ORIGINAL:
{
if (ClassData[index][class_team] == teamid)
if (ClassData[index][Class_Team] == teamid)
{
return true;
}
}
case ZR_CLASS_CACHE_MODIFIED:
{
if (ClassDataCache[index][class_team] == teamid)
if (ClassDataCache[index][Class_Team] == teamid)
{
return true;
}
}
case ZR_CLASS_CACHE_PLAYER:
{
if (ClassPlayerCache[index][class_team] == teamid)
if (ClassPlayerCache[index][Class_Team] == teamid)
{
return true;
}
@ -448,6 +463,68 @@ stock Float:ClassGetAttributeMultiplier(client, ClassMultipliers:attribute)
}
}
/**
* Check if a class pass the specified filter.
*
* @param index Index of the class in a class cache or a client index,
* depending on the cache type specified.
* @param filter Structure with filter settings.
* @param cachetype Optional. Specifies what class cache to read from.
* ZR_CLASS_CACHE_ORIGINAL - Unchanced class data.
* ZR_CLASS_CACHE_MODIFIED (default) - Changed/newest
* class data.
* ZR_CLASS_CACHE_PLAYER - Player cache. If this one is
* used index will be used as a client index.
* @return True if passed, false otherwise.
*/
stock bool:ClassFilterMatch(index, filter[ClassFilter], cachetype = ZR_CLASS_CACHE_MODIFIED)
{
// Check if the class is disabled and the enabled attribute is NOT ignored.
if (!filter[ClassFilter_IgnoreEnabled] && !ClassIsEnabled(index, cachetype))
{
return false;
}
// Check if class flags pass the flag filter.
if (!ClassFlagFilterMatch(index, filter[ClassFilter_RequireFlags], filter[ClassFilter_DenyFlags], cachetype))
{
return false;
}
// Get class group name.
decl String:groupname[64];
groupname[0] = 0;
ClassGetGroup(index, groupname, sizeof(groupname), cachetype);
// Check if a client is specified in the filter.
new client = filter[ClassFilter_Client];
if (ZRIsClientValid(client))
{
// Check if a group is set on the class.
if (strlen(groupname))
{
// Check if the client is not a member of that group.
if (!ZRIsClientInGroup(client, groupname))
{
return false;
}
}
}
// Check if classes with groups are set to be excluded.
if (client < 0)
{
// Exclude class if it has a group name.
if (strlen(groupname))
{
return false;
}
}
// The class passed the filter.
return true;
}
/**
* Check if a class pass the specified flag filters.
*
@ -515,6 +592,64 @@ stock bool:ClassFlagFilterMatch(index, require, deny, cachetype)
}
}
/**
* Decides whether a class selection menu should be enabled. The decision is
* based on zr_class_allow_* console variables.
*
* @param team Optional. Team ID to match. Default is all.
* @return True if allowed, false otherwise.
*/
bool:ClassAllowSelection(client, team = -1)
{
// Get selection settings.
new bool:zombie = GetConVarBool(g_hCvarsList[CVAR_CLASSES_ZOMBIE_SELECT]);
new bool:human = GetConVarBool(g_hCvarsList[CVAR_CLASSES_HUMAN_SELECT]);
new bool:admin = GetConVarBool(g_hCvarsList[CVAR_CLASSES_ADMIN_SELECT]);
// Since admin mode classes are optional they must be counted to verify
// that they exist.
new bool:adminexist;
// Check if player is admin.
new bool:isadmin = ZRIsClientAdmin(client);
// Only count admin mode classes if client is admin for better performance.
if (isadmin)
{
adminexist = ClassCountTeam(ZR_CLASS_TEAM_ADMINS) > 0;
}
// Check if a team id is specified.
if (team >= 0)
{
// Check team and return the corresponding selection setting.
switch (team)
{
case ZR_CLASS_TEAM_ZOMBIES:
{
return zombie;
}
case ZR_CLASS_TEAM_HUMANS:
{
return human;
}
case ZR_CLASS_TEAM_ADMINS:
{
// Player must be admin to select admin mode classes.
return admin && isadmin && adminexist;
}
}
// Team ID didn't match.
return false;
}
else
{
// Check zombie and human.
return zombie || human;
}
}
/**
* Gets all class indexes or from a specified team, and adds them to the
* specified array.
@ -522,12 +657,7 @@ stock bool:ClassFlagFilterMatch(index, require, deny, cachetype)
* @param array The destination array to add class indexes.
* @param teamfilter Optional. The team ID to filter. A negative value
* for no filter (default).
* @param ignoreEnabled Optional. Ignore whether the class is enabled or
* not. Default is false.
* @param requireflags Optional. Require certain class flags to be set.
* Default is no filtering.
* @param denyflags Optional. Require certain class flags to be off.
* Default is no filtering.
* @param filter Optional. Structure with filter settings.
* @param cachetype Optional. Specifies what class cache to read from.
* Options:
* ZR_CLASS_CACHE_ORIGINAL - Unchanced class data.
@ -536,7 +666,7 @@ stock bool:ClassFlagFilterMatch(index, require, deny, cachetype)
* @return True on success. False on error or if no classes were added or
* found.
*/
stock bool:ClassAddToArray(Handle:array, teamfilter = -1, bool:ignoreEnabled = false, requireflags = 0, denyflags = 0, cachetype = ZR_CLASS_CACHE_MODIFIED)
stock bool:ClassAddToArray(Handle:array, teamfilter = -1, filter[ClassFilter] = ClassNoFilter, cachetype = ZR_CLASS_CACHE_MODIFIED)
{
// Validate the array.
if (array == INVALID_HANDLE)
@ -551,46 +681,39 @@ stock bool:ClassAddToArray(Handle:array, teamfilter = -1, bool:ignoreEnabled = f
}
// Store a local boolean that says if the user specified a team filter or not.
new bool:has_filter = bool:(teamfilter >= 0);
new classes_added;
new bool:hasteamfilter = bool:(teamfilter >= 0);
new classesadded;
// Loop through all classes.
for (new classindex = 0; classindex < ClassCount; classindex++)
{
if (!ignoreEnabled && !ClassIsEnabled(classindex, cachetype))
// Validate filter settings.
if (!ClassFilterMatch(classindex, filter, cachetype))
{
// The class is disabled and the enabled attribute is NOT ignored.
// Skip to the next class.
continue;
}
// Check flag filter match.
if (!ClassFlagFilterMatch(classindex, requireflags, denyflags, cachetype))
{
// The class didn't pass filter.
// The class is didn't pass the filter, skip class.
continue;
}
// Check team filtering.
if (has_filter)
if (hasteamfilter)
{
// Only add classes with matching team ID.
if (ClassGetTeamID(classindex, cachetype) == teamfilter)
{
// Team ID match. Add class index to array.
PushArrayCell(array, classindex);
classes_added++;
classesadded++;
}
}
else
{
// No filter. Add any class to the array.
PushArrayCell(array, classindex);
classes_added++;
classesadded++;
}
}
if (classes_added)
if (classesadded)
{
return true;
}
@ -606,12 +729,7 @@ stock bool:ClassAddToArray(Handle:array, teamfilter = -1, bool:ignoreEnabled = f
*
* @param teamfilter Optional. The team ID to filter. Negative value for
* no filter (default).
* @param ignoreEnabled Optional. Ignore whether the class is enabled or
* not. Default is false.
* @param requireflags Optional. Require certain class flags to be set.
* Default is no filtering.
* @param denyflags Optional. Require certain class flags to be off.
* Default is no filtering.
* @param filter Optional. Structure with filter settings.
* @param cachetype Optional. Specifies what class cache to read from.
* Options:
* ZR_CLASS_CACHE_ORIGINAL - Unchanced class data.
@ -619,7 +737,7 @@ stock bool:ClassAddToArray(Handle:array, teamfilter = -1, bool:ignoreEnabled = f
* class data.
* @return Number of total classes or classes in the specified team.
*/
stock ClassCountTeam(teamfilter = -1, bool:ignoreEnabled = false, requireflags = 0, denyflags = 0, cachetype = ZR_CLASS_CACHE_MODIFIED)
stock ClassCountTeam(teamfilter = -1, filter[ClassFilter] = ClassNoFilter, cachetype = ZR_CLASS_CACHE_MODIFIED)
{
// Check if there are no classes.
if (ClassCount == 0)
@ -628,28 +746,21 @@ stock ClassCountTeam(teamfilter = -1, bool:ignoreEnabled = false, requireflags =
}
// Store a local boolean that says if the user specified a team filter or not.
new bool:has_filter = bool:(teamfilter >= 0);
new bool:hasteamfilter = bool:(teamfilter >= 0);
new count;
// Loop through all classes.
for (new classindex = 0; classindex < ClassCount; classindex++)
{
if (!ignoreEnabled && !ClassIsEnabled(classindex, cachetype))
// Validate filter settings.
if (!ClassFilterMatch(classindex, filter, cachetype))
{
// The class is disabled and the enabled attribute is NOT ignored.
// Skip to the next class.
continue;
}
// Check flag filter match.
if (!ClassFlagFilterMatch(classindex, requireflags, denyflags, cachetype))
{
// The class didn't pass filter.
// The class is didn't pass the filter, skip class.
continue;
}
// Check team filtering.
if (has_filter)
if (hasteamfilter)
{
// Only add classes with matching team ID.
if (ClassGetTeamID(classindex, cachetype) == teamfilter)
@ -674,12 +785,7 @@ stock ClassCountTeam(teamfilter = -1, bool:ignoreEnabled = false, requireflags =
*
* @param teamfilter Optional. The team ID to filter. A negative value
* for no filter (default).
* @param ignoreEnabled Optional. Ignore whether the class is enabled or
* not. Default is false.
* @param requireflags Optional. Require certain class flags to be set.
* Default is no filtering.
* @param denyflags Optional. Require certain class flags to be off.
* Default is no filtering.
* @param filter Optional. Structure with filter settings.
* @param cachetype Optional. Specifies what class cache to read from.
* Options:
* ZR_CLASS_CACHE_ORIGINAL - Unchanced class data.
@ -687,7 +793,7 @@ stock ClassCountTeam(teamfilter = -1, bool:ignoreEnabled = false, requireflags =
* class data.
* @return The class index if successful, or -1 on error.
*/
stock ClassGetRandomClass(teamfilter = -1, bool:ignoreEnabled = false, requireflags = 0, denyflags = 0, cachetype = ZR_CLASS_CACHE_MODIFIED)
stock ClassGetRandomClass(teamfilter = -1, filter[ClassFilter] = ClassNoSpecialClasses, cachetype = ZR_CLASS_CACHE_MODIFIED)
{
new Handle:classarray;
new arraycount;
@ -697,7 +803,7 @@ stock ClassGetRandomClass(teamfilter = -1, bool:ignoreEnabled = false, requirefl
classarray = CreateArray();
// Try to get a class list.
if (ClassAddToArray(classarray, teamfilter, ignoreEnabled, requireflags, denyflags, cachetype))
if (ClassAddToArray(classarray, teamfilter, filter, cachetype))
{
// Get a random index from the new class array.
arraycount = GetArraySize(classarray);
@ -722,12 +828,7 @@ stock ClassGetRandomClass(teamfilter = -1, bool:ignoreEnabled = false, requirefl
*
* @param teamfilter Optional. The team ID to filter. A negative value
* for no filter (default).
* @param ignoreEnabled Optional. Ignore whether the class is enabled or
* not. Default is false.
* @param requireflags Optional. Require certain class flags to be set.
* Default is no filtering.
* @param denyflags Optional. Require certain class flags to be off.
* Default is no filtering.
* @param filter Optional. Structure with filter settings.
* @param cachetype Optional. Specifies what class cache to read from.
* Options:
* ZR_CLASS_CACHE_ORIGINAL - Unchanced class data.
@ -736,7 +837,7 @@ stock ClassGetRandomClass(teamfilter = -1, bool:ignoreEnabled = false, requirefl
* @return The first class index, or the first class index with the specified
* team ID. -1 on error.
*/
stock ClassGetFirstClass(teamfilter = -1, bool:ignoreEnabled = false, requireflags = 0, denyflags = 0, cachetype = ZR_CLASS_CACHE_MODIFIED)
stock ClassGetFirstClass(teamfilter = -1, filter[ClassFilter] = ClassNoSpecialClasses, cachetype = ZR_CLASS_CACHE_MODIFIED)
{
// Check if there are no classes.
if (ClassCount == 0)
@ -744,26 +845,19 @@ stock ClassGetFirstClass(teamfilter = -1, bool:ignoreEnabled = false, requirefla
return false;
}
new bool:has_filter = bool:(teamfilter >= 0);
new bool:hasteamfilter = bool:(teamfilter >= 0);
// Loop through all classes.
for (new classindex = 0; classindex < ClassCount; classindex++)
{
if (!ignoreEnabled && !ClassIsEnabled(classindex, cachetype))
// Validate filter settings.
if (!ClassFilterMatch(classindex, filter, cachetype))
{
// The class is disabled and the enabled attribute is NOT ignored.
// Skip to the next class.
// The class is didn't pass the filter, skip class.
continue;
}
// Check flag filter match.
if (!ClassFlagFilterMatch(classindex, requireflags, denyflags, cachetype))
{
// The class didn't pass filter.
continue;
}
if (has_filter)
if (hasteamfilter)
{
if (teamfilter == ClassGetTeamID(classindex, cachetype))
{
@ -785,12 +879,8 @@ stock ClassGetFirstClass(teamfilter = -1, bool:ignoreEnabled = false, requirefla
* Gets the first class marked as default for the specified team.
*
* @param teamid The team ID.
* @param ignoreEnabled Optional. Ignore whether the class is enabled or
* not. Default is false.
* @param requireflags Optional. Require certain class flags to be set.
* Default is no filtering.
* @param denyflags Optional. Require certain class flags to be off.
* Default is to deny classes with special flags
* @param filter Optional. Structure with filter settings. Default
* is to deny classes with special flags
* (ZR_CLASS_SPECIALFLAGS).
* @param cachetype Optional. Specifies what class cache to read from.
* Options:
@ -799,7 +889,7 @@ stock ClassGetFirstClass(teamfilter = -1, bool:ignoreEnabled = false, requirefla
* class data.
* @return The first default class index. -1 on error.
*/
stock ClassGetDefaultClass(teamid, bool:ignoreEnabled = false, requireflags = 0, denyflags = ZR_CLASS_SPECIALFLAGS, cachetype = ZR_CLASS_CACHE_MODIFIED)
stock ClassGetDefaultClass(teamid, filter[ClassFilter] = ClassNoSpecialClasses, cachetype = ZR_CLASS_CACHE_MODIFIED)
{
new Handle:classarray;
new arraycount;
@ -808,7 +898,7 @@ stock ClassGetDefaultClass(teamid, bool:ignoreEnabled = false, requireflags = 0,
classarray = CreateArray();
// Get all classes from the specified team.
if (!ClassAddToArray(classarray, teamid, ignoreEnabled, requireflags, denyflags, cachetype))
if (!ClassAddToArray(classarray, teamid, filter, cachetype))
{
// Failed to get classes.
CloseHandle(classarray);
@ -840,6 +930,8 @@ stock ClassGetDefaultClass(teamid, bool:ignoreEnabled = false, requireflags = 0,
* when players join the server.
*
* @param teamid The team ID.
* @param filter Optional. Structure with filter settings. Default is to
* deny classes with special flags (ZR_CLASS_SPECIALFLAGS).
* @param cachetype Optional. Specifies what class cache to read from. Options:
* ZR_CLASS_CACHE_ORIGINAL - Unchanced class data.
* ZR_CLASS_CACHE_MODIFIED (default) - Changed/newest class
@ -848,7 +940,7 @@ stock ClassGetDefaultClass(teamid, bool:ignoreEnabled = false, requireflags = 0,
* successful. -1 on critical errors. Otherwise it will try to fall
* back on the first class in the specified team.
*/
stock ClassGetDefaultSpawnClass(teamid, cachetype = ZR_CLASS_CACHE_MODIFIED)
stock ClassGetDefaultSpawnClass(teamid, filter[ClassFilter] = ClassNoSpecialClasses, cachetype = ZR_CLASS_CACHE_MODIFIED)
{
decl String:classname[64];
new classindex;
@ -879,11 +971,11 @@ stock ClassGetDefaultSpawnClass(teamid, cachetype = ZR_CLASS_CACHE_MODIFIED)
if (strlen(classname) > 0)
{
// Check if the user set "random" as default class.
if (strcmp(classname, "random", false) == 0)
if (StrEqual(classname, "random", false))
{
// Get a list of all classes with the specified team ID. Deny
// classes with special flags.
classindex = ClassGetRandomClass(teamid, _, _, ZR_CLASS_SPECIALFLAGS, cachetype);
classindex = ClassGetRandomClass(teamid, filter, cachetype);
// Validate the result, in case there were errors.
if (ClassValidateIndex(classindex))
@ -915,7 +1007,7 @@ stock ClassGetDefaultSpawnClass(teamid, cachetype = ZR_CLASS_CACHE_MODIFIED)
// The class index is invalid or the team IDs didn't match.
// Because it's user input, we'll fall back to the first class
// in the specified team, and log a warning.
classindex = ClassGetFirstClass(teamid, _, _, ZR_CLASS_SPECIALFLAGS, cachetype);
classindex = ClassGetFirstClass(teamid, filter, cachetype);
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Playerclasses, "Default Spawn Class", "Warning: Failed to set \"%s\" as default spawn class for team %d. The class doesn't exist or the team IDs doesn't match. Falling back to the first class in the team.", classname, teamid);
@ -928,7 +1020,8 @@ stock ClassGetDefaultSpawnClass(teamid, cachetype = ZR_CLASS_CACHE_MODIFIED)
}
else
{
// Something went wrong. This is a critical error.
// Something went wrong. This is a critical error. There's
// probably missing classes with no special flags set.
return -1;
}
}
@ -937,6 +1030,6 @@ stock ClassGetDefaultSpawnClass(teamid, cachetype = ZR_CLASS_CACHE_MODIFIED)
else
{
// Blank class name, get the default class and return the index.
return ClassGetDefaultClass(teamid, _, _, ZR_CLASS_SPECIALFLAGS, cachetype);
return ClassGetDefaultClass(teamid, filter, cachetype);
}
}

View File

@ -31,7 +31,7 @@
- Zombies have to hurt humans so they loose hp. When the hp reach zero (or
below) they turn into zombies.
- Fully imune to all damage. Can't take or give damage. Sould only be used
on admin classes.
on admin mode classes.
TODO: Make class attributes for for changing model render mode and colors.
@ -100,10 +100,11 @@
* @section Overall default class settings. Since this is a zombie plugin the
* default values represent a zombie.
*/
#define ZR_CLASS_DEFAULT_ENABLED true
#define ZR_CLASS_DEFAULT_ENABLED "yes"
#define ZR_CLASS_DEFAULT_TEAM ZR_CLASS_TEAM_ZOMBIES
#define ZR_CLASS_DEFAULT_TEAM_DEFAULT true
#define ZR_CLASS_DEFAULT_TEAM_DEFAULT "yes"
#define ZR_CLASS_DEFAULT_FLAGS 0
#define ZR_CLASS_DEFAULT_GROUP ""
#define ZR_CLASS_DEFAULT_NAME "classic"
#define ZR_CLASS_DEFAULT_DESCRIPTION "Need brains!!! Arrrrggghh!"
#define ZR_CLASS_DEFAULT_MODEL_PATH "models/player/zh/zh_zombie003.mdl"
@ -111,13 +112,13 @@
#define ZR_CLASS_DEFAULT_ALPHA_DAMAGED 255
#define ZR_CLASS_DEFAULT_ALPHA_DAMAGE 0
#define ZR_CLASS_DEFAULT_OVERLAY_PATH "overlays/zr/zvision"
#define ZR_CLASS_DEFAULT_NVGS true
#define ZR_CLASS_DEFAULT_NVGS "no"
#define ZR_CLASS_DEFAULT_FOV 90
#define ZR_CLASS_DEFAULT_HAS_NAPALM 1
#define ZR_CLASS_DEFAULT_HAS_NAPALM "yes"
#define ZR_CLASS_DEFAULT_NAPALM_TIME 10.0
#define ZR_CLASS_DEFAULT_IMMUNITY_MODE ZR_CLASS_IMMUNITY_DISABLED
#define ZR_CLASS_DEFAULT_IMMUNITY_AMOUNT 0.0
#define ZR_CLASS_DEFAULT_NO_FALL_DAMAGE true
#define ZR_CLASS_DEFAULT_NO_FALL_DAMAGE "on"
#define ZR_CLASS_DEFAULT_HEALTH 6000
#define ZR_CLASS_DEFAULT_HEALTH_REGEN_INTERVAL 0.0
#define ZR_CLASS_DEFAULT_HEALTH_REGEN_AMOUNT 2
@ -181,77 +182,94 @@
#define ZR_CLASS_TEAM (1<<1)
#define ZR_CLASS_TEAM_DEFAULT (1<<2)
#define ZR_CLASS_FLAGS (1<<3)
#define ZR_CLASS_NAME (1<<4)
#define ZR_CLASS_DESCRIPTION (1<<5)
#define ZR_CLASS_MODEL_PATH (1<<6)
#define ZR_CLASS_ALPHA_INITIAL (1<<7)
#define ZR_CLASS_ALPHA_DAMAGED (1<<8)
#define ZR_CLASS_ALPHA_DAMAGE (1<<9)
#define ZR_CLASS_OVERLAY_PATH (1<<10)
#define ZR_CLASS_NVGS (1<<11)
#define ZR_CLASS_FOV (1<<12)
#define ZR_CLASS_HAS_NAPALM (1<<13)
#define ZR_CLASS_NAPALM_TIME (1<<14)
#define ZR_CLASS_IMMUNITY_MODE (1<<15)
#define ZR_CLASS_IMMUNITY_AMOUNT (1<<16)
#define ZR_CLASS_NO_FALL_DAMAGE (1<<17)
#define ZR_CLASS_HEALTH (1<<18)
#define ZR_CLASS_HEALTH_REGEN_INTERVAL (1<<19)
#define ZR_CLASS_HEALTH_REGEN_AMOUNT (1<<20)
#define ZR_CLASS_HEALTH_INFECT_GAIN (1<<21)
#define ZR_CLASS_KILL_BONUS (1<<22)
#define ZR_CLASS_SPEED (1<<23)
#define ZR_CLASS_KNOCKBACK (1<<24)
#define ZR_CLASS_JUMP_HEIGHT (1<<25)
#define ZR_CLASS_JUMP_DISTANCE (1<<26)
#define ZR_CLASS_GROUP (1<<4)
#define ZR_CLASS_NAME (1<<5)
#define ZR_CLASS_DESCRIPTION (1<<6)
#define ZR_CLASS_MODEL_PATH (1<<7)
#define ZR_CLASS_ALPHA_INITIAL (1<<8)
#define ZR_CLASS_ALPHA_DAMAGED (1<<9)
#define ZR_CLASS_ALPHA_DAMAGE (1<<10)
#define ZR_CLASS_OVERLAY_PATH (1<<11)
#define ZR_CLASS_NVGS (1<<12)
#define ZR_CLASS_FOV (1<<13)
#define ZR_CLASS_HAS_NAPALM (1<<14)
#define ZR_CLASS_NAPALM_TIME (1<<15)
#define ZR_CLASS_IMMUNITY_MODE (1<<16)
#define ZR_CLASS_IMMUNITY_AMOUNT (1<<17)
#define ZR_CLASS_NO_FALL_DAMAGE (1<<18)
#define ZR_CLASS_HEALTH (1<<19)
#define ZR_CLASS_HEALTH_REGEN_INTERVAL (1<<20)
#define ZR_CLASS_HEALTH_REGEN_AMOUNT (1<<21)
#define ZR_CLASS_HEALTH_INFECT_GAIN (1<<22)
#define ZR_CLASS_KILL_BONUS (1<<23)
#define ZR_CLASS_SPEED (1<<24)
#define ZR_CLASS_KNOCKBACK (1<<25)
#define ZR_CLASS_JUMP_HEIGHT (1<<26)
#define ZR_CLASS_JUMP_DISTANCE (1<<27)
/**
* @endsection
*/
/**
* Generic player attributes.
*
* Stuff that must be updated when new attributes are added:
* ZR_CLASS_DEFAULT_... define
* ZR_CLASS_..._MAX/MIN defines
* ZR_CLASS_... define (place in same order as listed in ClassAttributes, bump bit numbers + update numbers in docs)
* ClassLoad
* ClassReloadDataCache
* ClassReloadPlayerCache
* ClassDumpData
* attributes.inc - Add new Get-function
* ClassAttributeNameToFlag
* ClassGetAttributeType
* ClassValidateAttributes
* ClassModify* in classcommands.inc
* Update docs with detailed attribute description
*/
enum ClassAttributes
{
/* General */
bool:class_enabled,
class_team,
bool:class_team_default,
class_flags,
bool:Class_Enabled,
Class_Team,
bool:Class_TeamDefault,
Class_Flags,
String:Class_Group[64],
String:class_name[64],
String:class_description[256],
String:Class_Name[64],
String:Class_Description[256],
/* Model */
String:class_model_path[PLATFORM_MAX_PATH],
class_alpha_initial,
class_alpha_damaged,
class_alpha_damage,
String:Class_ModelPath[PLATFORM_MAX_PATH],
Class_AlphaInitial,
Class_AlphaDamaged,
Class_AlphaDamage,
/* Hud */
String:class_overlay_path[PLATFORM_MAX_PATH],
bool:class_nvgs,
class_fov,
String:Class_OverlayPath[PLATFORM_MAX_PATH],
bool:Class_Nvgs,
Class_Fov,
/* Effects */
bool:class_has_napalm,
Float:class_napalm_time,
bool:Class_HasNapalm,
Float:Class_NapalmTime,
/* Player behaviour */
class_immunity_mode,
Float:class_immunity_amount,
bool:class_no_fall_damage,
Class_ImmunityMode,
Float:Class_ImmunityAmount,
bool:Class_NoFallDamage,
class_health,
Float:class_health_regen_interval,
class_health_regen_amount,
class_health_infect_gain,
class_kill_bonus,
Class_Health,
Float:Class_HealthRegenInterval,
Class_HealthRegenAmount,
Class_HealthInfectGain,
Class_KillBonus,
Float:class_speed,
Float:class_knockback,
Float:class_jump_height,
Float:class_jump_distance
Float:Class_Speed,
Float:Class_KnockBack,
Float:Class_JumpHeight,
Float:Class_JumpDistance
}
/**
@ -294,6 +312,27 @@ enum ClassDataTypes
ClassDataType_String /** String value */
}
/**
* Structure for class filter settings passed to various functions.
*/
enum ClassFilter
{
bool:ClassFilter_IgnoreEnabled, /** Ignore whether the class is disabled or not. */
ClassFilter_RequireFlags, /** Flags the classes must have set. */
ClassFilter_DenyFlags, /** Flags the classes cannot have set. */
ClassFilter_Client /** The client to check for class group permissions. Use 0 to ignore group filter and negative to exclude classes with groups set. */
}
/**
* Empty filter structure.
*/
new ClassNoFilter[ClassFilter];
/**
* Filter structure for excluding special classes.
*/
new ClassNoSpecialClasses[ClassFilter] = {false, 0, ZR_CLASS_SPECIALFLAGS, -1};
/**
* Keyvalue handle to store class data.
*/
@ -351,6 +390,11 @@ new ClassSelectedNext[MAXPLAYERS + 1][ZR_CLASS_TEAMCOUNT];
*/
new ClassAdminTeamSelected[MAXPLAYERS + 1];
/**
* Cookies for storing class indexes.
*/
new Handle:g_hClassCookieClassSelected[ZR_CLASS_TEAMCOUNT];
/**
* Cache for the currently selected attribute multiplier (admin menus).
*/
@ -362,9 +406,13 @@ new ClassMultipliers:ClassAdminAttributeSelected[MAXPLAYERS + 1];
new bool:ClassPlayerInAdminMode[MAXPLAYERS + 1];
/**
* Specifies the admin class to use on next admin mode spawn.
* Specifies whether a player is allowed to change class with instant effect.
* This is only used on human classes, and in combination with the
* zr_classes_change_timelimit time limit, but could be used other places too.
* The class menu will automatically check this setting and apply attributes if
* set to true.
*/
//new ClassPlayerNextAdminClass[MAXPLAYERS + 1];
new bool:ClassAllowInstantChange[MAXPLAYERS + 1];
/**
* Cache for storing original model path before applying custom models. Used
@ -431,6 +479,7 @@ ClassLoad(bool:keepMultipliers = false)
}
decl String:name[64];
decl String:group[64];
decl String:description[256];
decl String:model_path[PLATFORM_MAX_PATH];
decl String:overlay_path[PLATFORM_MAX_PATH];
@ -451,55 +500,58 @@ ClassLoad(bool:keepMultipliers = false)
}
/* General */
ClassData[ClassCount][class_enabled] = bool:KvGetNum(kvClassData, "enabled", ZR_CLASS_DEFAULT_ENABLED);
ClassData[ClassCount][class_team] = KvGetNum(kvClassData, "team", ZR_CLASS_DEFAULT_TEAM);
ClassData[ClassCount][class_team_default] = bool:KvGetNum(kvClassData, "team_default", ZR_CLASS_DEFAULT_TEAM_DEFAULT);
ClassData[ClassCount][class_flags] = KvGetNum(kvClassData, "flags", ZR_CLASS_DEFAULT_FLAGS);
ClassData[ClassCount][Class_Enabled] = ConfigKvGetStringBool(kvClassData, "enabled", ZR_CLASS_DEFAULT_ENABLED);
ClassData[ClassCount][Class_Team] = KvGetNum(kvClassData, "team", ZR_CLASS_DEFAULT_TEAM);
ClassData[ClassCount][Class_TeamDefault] = ConfigKvGetStringBool(kvClassData, "team_default", ZR_CLASS_DEFAULT_TEAM_DEFAULT);
ClassData[ClassCount][Class_Flags] = KvGetNum(kvClassData, "flags", ZR_CLASS_DEFAULT_FLAGS);
KvGetString(kvClassData, "group", group, sizeof(group), ZR_CLASS_DEFAULT_GROUP);
strcopy(ClassData[ClassCount][Class_Group], 64, group);
KvGetString(kvClassData, "name", name, sizeof(name), ZR_CLASS_DEFAULT_NAME);
strcopy(ClassData[ClassCount][class_name], 64, name);
strcopy(ClassData[ClassCount][Class_Name], 64, name);
KvGetString(kvClassData, "description", description, sizeof(description), ZR_CLASS_DEFAULT_DESCRIPTION);
strcopy(ClassData[ClassCount][class_description], 256, description);
strcopy(ClassData[ClassCount][Class_Description], 256, description);
/* Model */
KvGetString(kvClassData, "model_path", model_path, sizeof(model_path), ZR_CLASS_DEFAULT_MODEL_PATH);
strcopy(ClassData[ClassCount][class_model_path], PLATFORM_MAX_PATH, model_path);
strcopy(ClassData[ClassCount][Class_ModelPath], PLATFORM_MAX_PATH, model_path);
ClassData[ClassCount][class_alpha_initial] = KvGetNum(kvClassData, "alpha_initial", ZR_CLASS_DEFAULT_ALPHA_INITIAL);
ClassData[ClassCount][class_alpha_damaged] = KvGetNum(kvClassData, "alpha_damaged", ZR_CLASS_DEFAULT_ALPHA_DAMAGED);
ClassData[ClassCount][class_alpha_damage] = KvGetNum(kvClassData, "alpha_damage", ZR_CLASS_DEFAULT_ALPHA_DAMAGE);
ClassData[ClassCount][Class_AlphaInitial] = KvGetNum(kvClassData, "alpha_initial", ZR_CLASS_DEFAULT_ALPHA_INITIAL);
ClassData[ClassCount][Class_AlphaDamaged] = KvGetNum(kvClassData, "alpha_damaged", ZR_CLASS_DEFAULT_ALPHA_DAMAGED);
ClassData[ClassCount][Class_AlphaDamage] = KvGetNum(kvClassData, "alpha_damage", ZR_CLASS_DEFAULT_ALPHA_DAMAGE);
/* Hud */
KvGetString(kvClassData, "overlay_path", overlay_path, sizeof(overlay_path), ZR_CLASS_DEFAULT_OVERLAY_PATH);
strcopy(ClassData[ClassCount][class_overlay_path], PLATFORM_MAX_PATH, overlay_path);
strcopy(ClassData[ClassCount][Class_OverlayPath], PLATFORM_MAX_PATH, overlay_path);
ClassData[ClassCount][class_nvgs] = bool:KvGetNum(kvClassData, "nvgs", ZR_CLASS_DEFAULT_NVGS);
ClassData[ClassCount][class_fov] = KvGetNum(kvClassData, "fov", ZR_CLASS_DEFAULT_FOV);
ClassData[ClassCount][Class_Nvgs] = ConfigKvGetStringBool(kvClassData, "nvgs", ZR_CLASS_DEFAULT_NVGS);
ClassData[ClassCount][Class_Fov] = KvGetNum(kvClassData, "fov", ZR_CLASS_DEFAULT_FOV);
/* Effects */
ClassData[ClassCount][class_has_napalm] = bool:KvGetNum(kvClassData, "have_napalm", ZR_CLASS_DEFAULT_HAS_NAPALM);
ClassData[ClassCount][class_napalm_time] = KvGetFloat(kvClassData, "napalm_time", ZR_CLASS_DEFAULT_NAPALM_TIME);
ClassData[ClassCount][Class_HasNapalm] = ConfigKvGetStringBool(kvClassData, "have_napalm", ZR_CLASS_DEFAULT_HAS_NAPALM);
ClassData[ClassCount][Class_NapalmTime] = KvGetFloat(kvClassData, "napalm_time", ZR_CLASS_DEFAULT_NAPALM_TIME);
/* Player behaviour */
ClassData[ClassCount][class_immunity_mode] = KvGetNum(kvClassData, "immunity_mode", ZR_CLASS_DEFAULT_IMMUNITY_MODE);
ClassData[ClassCount][class_immunity_amount] = KvGetFloat(kvClassData, "immunity_amount", ZR_CLASS_DEFAULT_IMMUNITY_AMOUNT);
ClassData[ClassCount][class_no_fall_damage] = bool:KvGetNum(kvClassData, "no_fall_damage", ZR_CLASS_DEFAULT_NO_FALL_DAMAGE);
ClassData[ClassCount][Class_ImmunityMode] = KvGetNum(kvClassData, "immunity_mode", ZR_CLASS_DEFAULT_IMMUNITY_MODE);
ClassData[ClassCount][Class_ImmunityAmount] = KvGetFloat(kvClassData, "immunity_amount", ZR_CLASS_DEFAULT_IMMUNITY_AMOUNT);
ClassData[ClassCount][Class_NoFallDamage] = ConfigKvGetStringBool(kvClassData, "no_fall_damage", ZR_CLASS_DEFAULT_NO_FALL_DAMAGE);
ClassData[ClassCount][class_health] = KvGetNum(kvClassData, "health", ZR_CLASS_DEFAULT_HEALTH);
ClassData[ClassCount][class_health_regen_interval] = KvGetFloat(kvClassData, "health_regen_interval", ZR_CLASS_DEFAULT_HEALTH_REGEN_INTERVAL);
ClassData[ClassCount][class_health_regen_amount] = KvGetNum(kvClassData, "health_regen_amount", ZR_CLASS_DEFAULT_HEALTH_REGEN_AMOUNT);
ClassData[ClassCount][class_health_infect_gain] = KvGetNum(kvClassData, "health_infect_gain", ZR_CLASS_DEFAULT_HEALTH_INFECT_GAIN);
ClassData[ClassCount][class_kill_bonus] = KvGetNum(kvClassData, "kill_bonus", ZR_CLASS_DEFAULT_KILL_BONUS);
ClassData[ClassCount][Class_Health] = KvGetNum(kvClassData, "health", ZR_CLASS_DEFAULT_HEALTH);
ClassData[ClassCount][Class_HealthRegenInterval] = KvGetFloat(kvClassData, "health_regen_interval", ZR_CLASS_DEFAULT_HEALTH_REGEN_INTERVAL);
ClassData[ClassCount][Class_HealthRegenAmount] = KvGetNum(kvClassData, "health_regen_amount", ZR_CLASS_DEFAULT_HEALTH_REGEN_AMOUNT);
ClassData[ClassCount][Class_HealthInfectGain] = KvGetNum(kvClassData, "health_infect_gain", ZR_CLASS_DEFAULT_HEALTH_INFECT_GAIN);
ClassData[ClassCount][Class_KillBonus] = KvGetNum(kvClassData, "kill_bonus", ZR_CLASS_DEFAULT_KILL_BONUS);
ClassData[ClassCount][class_speed] = KvGetFloat(kvClassData, "speed", ZR_CLASS_DEFAULT_SPEED);
ClassData[ClassCount][class_knockback] = KvGetFloat(kvClassData, "knockback", ZR_CLASS_DEFAULT_KNOCKBACK);
ClassData[ClassCount][class_jump_height] = KvGetFloat(kvClassData, "jump_height", ZR_CLASS_DEFAULT_JUMP_HEIGHT);
ClassData[ClassCount][class_jump_distance] = KvGetFloat(kvClassData, "jump_distance", ZR_CLASS_DEFAULT_JUMP_DISTANCE);
ClassData[ClassCount][Class_Speed] = KvGetFloat(kvClassData, "speed", ZR_CLASS_DEFAULT_SPEED);
ClassData[ClassCount][Class_KnockBack] = KvGetFloat(kvClassData, "knockback", ZR_CLASS_DEFAULT_KNOCKBACK);
ClassData[ClassCount][Class_JumpHeight] = KvGetFloat(kvClassData, "jump_height", ZR_CLASS_DEFAULT_JUMP_HEIGHT);
ClassData[ClassCount][Class_JumpDistance] = KvGetFloat(kvClassData, "jump_distance", ZR_CLASS_DEFAULT_JUMP_DISTANCE);
// Validate the class attributes.
ClassErrorFlags = ClassValidateAttributes(ClassCount);
@ -507,7 +559,7 @@ ClassLoad(bool:keepMultipliers = false)
{
// There's one or more invalid class attributes. Disable the class
// and log an error message.
ClassData[ClassCount][class_enabled] = false;
ClassData[ClassCount][Class_Enabled] = false;
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Playerclasses, "Config Validation", "Warning: Invalid class at index %d, disabled class. Class error flags: %d.", ClassCount, ClassErrorFlags);
failedcount++;
@ -585,41 +637,42 @@ bool:ClassReloadDataCache()
for (new classindex = 0; classindex < ClassCount; classindex++)
{
/* General */
ClassDataCache[classindex][class_enabled] = ClassData[classindex][class_enabled];
ClassDataCache[classindex][class_team] = ClassData[classindex][class_team];
ClassDataCache[classindex][class_team_default] = ClassData[classindex][class_team_default];
ClassDataCache[classindex][class_flags] = ClassData[classindex][class_flags];
strcopy(ClassDataCache[classindex][class_name], 64, ClassData[classindex][class_name]);
strcopy(ClassDataCache[classindex][class_description], 256, ClassData[classindex][class_description]);
ClassDataCache[classindex][Class_Enabled] = ClassData[classindex][Class_Enabled];
ClassDataCache[classindex][Class_Team] = ClassData[classindex][Class_Team];
ClassDataCache[classindex][Class_TeamDefault] = ClassData[classindex][Class_TeamDefault];
ClassDataCache[classindex][Class_Flags] = ClassData[classindex][Class_Flags];
strcopy(ClassDataCache[classindex][Class_Group], 64, ClassData[classindex][Class_Group]);
strcopy(ClassDataCache[classindex][Class_Name], 64, ClassData[classindex][Class_Name]);
strcopy(ClassDataCache[classindex][Class_Description], 256, ClassData[classindex][Class_Description]);
/* Model */
strcopy(ClassDataCache[classindex][class_model_path], PLATFORM_MAX_PATH, ClassData[classindex][class_model_path]);
ClassDataCache[classindex][class_alpha_initial] = ClassData[classindex][class_alpha_initial];
ClassDataCache[classindex][class_alpha_damaged] = ClassData[classindex][class_alpha_damaged];
ClassDataCache[classindex][class_alpha_damage] = ClassData[classindex][class_alpha_damage];
strcopy(ClassDataCache[classindex][Class_ModelPath], PLATFORM_MAX_PATH, ClassData[classindex][Class_ModelPath]);
ClassDataCache[classindex][Class_AlphaInitial] = ClassData[classindex][Class_AlphaInitial];
ClassDataCache[classindex][Class_AlphaDamaged] = ClassData[classindex][Class_AlphaDamaged];
ClassDataCache[classindex][Class_AlphaDamage] = ClassData[classindex][Class_AlphaDamage];
/* Hud */
strcopy(ClassDataCache[classindex][class_overlay_path], PLATFORM_MAX_PATH, ClassData[classindex][class_overlay_path]);
ClassDataCache[classindex][class_nvgs] = ClassData[classindex][class_nvgs];
ClassDataCache[classindex][class_fov] = ClassData[classindex][class_fov];
strcopy(ClassDataCache[classindex][Class_OverlayPath], PLATFORM_MAX_PATH, ClassData[classindex][Class_OverlayPath]);
ClassDataCache[classindex][Class_Nvgs] = ClassData[classindex][Class_Nvgs];
ClassDataCache[classindex][Class_Fov] = ClassData[classindex][Class_Fov];
/* Effects */
ClassDataCache[classindex][class_has_napalm] = ClassData[classindex][class_has_napalm];
ClassDataCache[classindex][class_napalm_time] = ClassData[classindex][class_napalm_time];
ClassDataCache[classindex][Class_HasNapalm] = ClassData[classindex][Class_HasNapalm];
ClassDataCache[classindex][Class_NapalmTime] = ClassData[classindex][Class_NapalmTime];
/* Player behaviour */
ClassDataCache[classindex][class_immunity_mode] = ClassData[classindex][class_immunity_mode];
ClassDataCache[classindex][class_immunity_amount] = ClassData[classindex][class_immunity_amount];
ClassDataCache[classindex][class_no_fall_damage] = ClassData[classindex][class_no_fall_damage];
ClassDataCache[classindex][class_health] = ClassData[classindex][class_health];
ClassDataCache[classindex][class_health_regen_interval] = ClassData[classindex][class_health_regen_interval];
ClassDataCache[classindex][class_health_regen_amount] = ClassData[classindex][class_health_regen_amount];
ClassDataCache[classindex][class_health_infect_gain] = ClassData[classindex][class_health_infect_gain];
ClassDataCache[classindex][class_kill_bonus] = ClassData[classindex][class_kill_bonus];
ClassDataCache[classindex][class_speed] = ClassData[classindex][class_speed];
ClassDataCache[classindex][class_knockback] = ClassData[classindex][class_knockback];
ClassDataCache[classindex][class_jump_height] = ClassData[classindex][class_jump_height];
ClassDataCache[classindex][class_jump_distance] = ClassData[classindex][class_jump_distance];
ClassDataCache[classindex][Class_ImmunityMode] = ClassData[classindex][Class_ImmunityMode];
ClassDataCache[classindex][Class_ImmunityAmount] = ClassData[classindex][Class_ImmunityAmount];
ClassDataCache[classindex][Class_NoFallDamage] = ClassData[classindex][Class_NoFallDamage];
ClassDataCache[classindex][Class_Health] = ClassData[classindex][Class_Health];
ClassDataCache[classindex][Class_HealthRegenInterval] = ClassData[classindex][Class_HealthRegenInterval];
ClassDataCache[classindex][Class_HealthRegenAmount] = ClassData[classindex][Class_HealthRegenAmount];
ClassDataCache[classindex][Class_HealthInfectGain] = ClassData[classindex][Class_HealthInfectGain];
ClassDataCache[classindex][Class_KillBonus] = ClassData[classindex][Class_KillBonus];
ClassDataCache[classindex][Class_Speed] = ClassData[classindex][Class_Speed];
ClassDataCache[classindex][Class_KnockBack] = ClassData[classindex][Class_KnockBack];
ClassDataCache[classindex][Class_JumpHeight] = ClassData[classindex][Class_JumpHeight];
ClassDataCache[classindex][Class_JumpDistance] = ClassData[classindex][Class_JumpDistance];
}
return true;
@ -649,80 +702,82 @@ bool:ClassReloadPlayerCache(client, classindex, cachetype = ZR_CLASS_CACHE_MODIF
case ZR_CLASS_CACHE_ORIGINAL:
{
/* General */
ClassPlayerCache[client][class_enabled] = ClassData[classindex][class_enabled];
ClassPlayerCache[client][class_team] = ClassData[classindex][class_team];
ClassPlayerCache[client][class_team_default] = ClassData[classindex][class_team_default];
ClassPlayerCache[client][class_flags] = ClassData[classindex][class_flags];
strcopy(ClassPlayerCache[client][class_name], 64, ClassData[classindex][class_name]);
strcopy(ClassPlayerCache[client][class_description], 256, ClassData[classindex][class_description]);
ClassPlayerCache[client][Class_Enabled] = ClassData[classindex][Class_Enabled];
ClassPlayerCache[client][Class_Team] = ClassData[classindex][Class_Team];
ClassPlayerCache[client][Class_TeamDefault] = ClassData[classindex][Class_TeamDefault];
ClassPlayerCache[client][Class_Flags] = ClassData[classindex][Class_Flags];
strcopy(ClassPlayerCache[client][Class_Group], 64, ClassData[classindex][Class_Group]);
strcopy(ClassPlayerCache[client][Class_Name], 64, ClassData[classindex][Class_Name]);
strcopy(ClassPlayerCache[client][Class_Description], 256, ClassData[classindex][Class_Description]);
/* Model */
strcopy(ClassPlayerCache[client][class_model_path], PLATFORM_MAX_PATH, ClassData[classindex][class_model_path]);
ClassPlayerCache[client][class_alpha_initial] = ClassData[classindex][class_alpha_initial];
ClassPlayerCache[client][class_alpha_damaged] = ClassData[classindex][class_alpha_damaged];
ClassPlayerCache[client][class_alpha_damage] = ClassData[classindex][class_alpha_damage];
strcopy(ClassPlayerCache[client][Class_ModelPath], PLATFORM_MAX_PATH, ClassData[classindex][Class_ModelPath]);
ClassPlayerCache[client][Class_AlphaInitial] = ClassData[classindex][Class_AlphaInitial];
ClassPlayerCache[client][Class_AlphaDamaged] = ClassData[classindex][Class_AlphaDamaged];
ClassPlayerCache[client][Class_AlphaDamage] = ClassData[classindex][Class_AlphaDamage];
/* Hud */
strcopy(ClassPlayerCache[client][class_overlay_path], PLATFORM_MAX_PATH, ClassData[classindex][class_overlay_path]);
ClassPlayerCache[client][class_nvgs] = ClassData[classindex][class_nvgs];
ClassPlayerCache[client][class_fov] = ClassData[classindex][class_fov];
strcopy(ClassPlayerCache[client][Class_OverlayPath], PLATFORM_MAX_PATH, ClassData[classindex][Class_OverlayPath]);
ClassPlayerCache[client][Class_Nvgs] = ClassData[classindex][Class_Nvgs];
ClassPlayerCache[client][Class_Fov] = ClassData[classindex][Class_Fov];
/* Effects */
ClassPlayerCache[client][class_has_napalm] = ClassData[classindex][class_has_napalm];
ClassPlayerCache[client][class_napalm_time] = ClassData[classindex][class_napalm_time];
ClassPlayerCache[client][Class_HasNapalm] = ClassData[classindex][Class_HasNapalm];
ClassPlayerCache[client][Class_NapalmTime] = ClassData[classindex][Class_NapalmTime];
/* Player behaviour */
ClassPlayerCache[client][class_immunity_mode] = ClassData[classindex][class_immunity_mode];
ClassPlayerCache[client][class_immunity_amount] = ClassData[classindex][class_immunity_amount];
ClassPlayerCache[client][class_no_fall_damage] = ClassData[classindex][class_no_fall_damage];
ClassPlayerCache[client][class_health] = ClassData[classindex][class_health];
ClassPlayerCache[client][class_health_regen_interval] = ClassData[classindex][class_health_regen_interval];
ClassPlayerCache[client][class_health_regen_amount] = ClassData[classindex][class_health_regen_amount];
ClassPlayerCache[client][class_health_infect_gain] = ClassData[classindex][class_health_infect_gain];
ClassPlayerCache[client][class_kill_bonus] = ClassData[classindex][class_kill_bonus];
ClassPlayerCache[client][class_speed] = ClassData[classindex][class_speed];
ClassPlayerCache[client][class_knockback] = ClassData[classindex][class_knockback];
ClassPlayerCache[client][class_jump_height] = ClassData[classindex][class_jump_height];
ClassPlayerCache[client][class_jump_distance] = ClassData[classindex][class_jump_distance];
ClassPlayerCache[client][Class_ImmunityMode] = ClassData[classindex][Class_ImmunityMode];
ClassPlayerCache[client][Class_ImmunityAmount] = ClassData[classindex][Class_ImmunityAmount];
ClassPlayerCache[client][Class_NoFallDamage] = ClassData[classindex][Class_NoFallDamage];
ClassPlayerCache[client][Class_Health] = ClassData[classindex][Class_Health];
ClassPlayerCache[client][Class_HealthRegenInterval] = ClassData[classindex][Class_HealthRegenInterval];
ClassPlayerCache[client][Class_HealthRegenAmount] = ClassData[classindex][Class_HealthRegenAmount];
ClassPlayerCache[client][Class_HealthInfectGain] = ClassData[classindex][Class_HealthInfectGain];
ClassPlayerCache[client][Class_KillBonus] = ClassData[classindex][Class_KillBonus];
ClassPlayerCache[client][Class_Speed] = ClassData[classindex][Class_Speed];
ClassPlayerCache[client][Class_KnockBack] = ClassData[classindex][Class_KnockBack];
ClassPlayerCache[client][Class_JumpHeight] = ClassData[classindex][Class_JumpHeight];
ClassPlayerCache[client][Class_JumpDistance] = ClassData[classindex][Class_JumpDistance];
}
case ZR_CLASS_CACHE_MODIFIED:
{
/* General */
ClassPlayerCache[client][class_enabled] = ClassDataCache[classindex][class_enabled];
ClassPlayerCache[client][class_team] = ClassDataCache[classindex][class_team];
ClassPlayerCache[client][class_team_default] = ClassDataCache[classindex][class_team_default];
ClassPlayerCache[client][class_flags] = ClassDataCache[classindex][class_flags];
strcopy(ClassPlayerCache[client][class_name], 64, ClassDataCache[classindex][class_name]);
strcopy(ClassPlayerCache[client][class_description], 256, ClassDataCache[classindex][class_description]);
ClassPlayerCache[client][Class_Enabled] = ClassDataCache[classindex][Class_Enabled];
ClassPlayerCache[client][Class_Team] = ClassDataCache[classindex][Class_Team];
ClassPlayerCache[client][Class_TeamDefault] = ClassDataCache[classindex][Class_TeamDefault];
ClassPlayerCache[client][Class_Flags] = ClassDataCache[classindex][Class_Flags];
strcopy(ClassPlayerCache[client][Class_Group], 64, ClassDataCache[classindex][Class_Group]);
strcopy(ClassPlayerCache[client][Class_Name], 64, ClassDataCache[classindex][Class_Name]);
strcopy(ClassPlayerCache[client][Class_Description], 256, ClassDataCache[classindex][Class_Description]);
/* Model */
strcopy(ClassPlayerCache[client][class_model_path], PLATFORM_MAX_PATH, ClassDataCache[classindex][class_model_path]);
ClassPlayerCache[client][class_alpha_initial] = ClassDataCache[classindex][class_alpha_initial];
ClassPlayerCache[client][class_alpha_damaged] = ClassDataCache[classindex][class_alpha_damaged];
ClassPlayerCache[client][class_alpha_damage] = ClassDataCache[classindex][class_alpha_damage];
strcopy(ClassPlayerCache[client][Class_ModelPath], PLATFORM_MAX_PATH, ClassDataCache[classindex][Class_ModelPath]);
ClassPlayerCache[client][Class_AlphaInitial] = ClassDataCache[classindex][Class_AlphaInitial];
ClassPlayerCache[client][Class_AlphaDamaged] = ClassDataCache[classindex][Class_AlphaDamaged];
ClassPlayerCache[client][Class_AlphaDamage] = ClassDataCache[classindex][Class_AlphaDamage];
/* Hud */
strcopy(ClassPlayerCache[client][class_overlay_path], PLATFORM_MAX_PATH, ClassDataCache[classindex][class_overlay_path]);
ClassPlayerCache[client][class_nvgs] = ClassDataCache[classindex][class_nvgs];
ClassPlayerCache[client][class_fov] = ClassDataCache[classindex][class_fov];
strcopy(ClassPlayerCache[client][Class_OverlayPath], PLATFORM_MAX_PATH, ClassDataCache[classindex][Class_OverlayPath]);
ClassPlayerCache[client][Class_Nvgs] = ClassDataCache[classindex][Class_Nvgs];
ClassPlayerCache[client][Class_Fov] = ClassDataCache[classindex][Class_Fov];
/* Effects */
ClassPlayerCache[client][class_has_napalm] = ClassDataCache[classindex][class_has_napalm];
ClassPlayerCache[client][class_napalm_time] = ClassDataCache[classindex][class_napalm_time];
ClassPlayerCache[client][Class_HasNapalm] = ClassDataCache[classindex][Class_HasNapalm];
ClassPlayerCache[client][Class_NapalmTime] = ClassDataCache[classindex][Class_NapalmTime];
/* Player behaviour */
ClassPlayerCache[client][class_immunity_mode] = ClassDataCache[classindex][class_immunity_mode];
ClassPlayerCache[client][class_immunity_amount] = ClassDataCache[classindex][class_immunity_amount];
ClassPlayerCache[client][class_no_fall_damage] = ClassDataCache[classindex][class_no_fall_damage];
ClassPlayerCache[client][class_health] = ClassDataCache[classindex][class_health];
ClassPlayerCache[client][class_health_regen_interval] = ClassDataCache[classindex][class_health_regen_interval];
ClassPlayerCache[client][class_health_regen_amount] = ClassDataCache[classindex][class_health_regen_amount];
ClassPlayerCache[client][class_health_infect_gain] = ClassDataCache[classindex][class_health_infect_gain];
ClassPlayerCache[client][class_kill_bonus] = ClassDataCache[classindex][class_kill_bonus];
ClassPlayerCache[client][class_speed] = ClassDataCache[classindex][class_speed];
ClassPlayerCache[client][class_knockback] = ClassDataCache[classindex][class_knockback];
ClassPlayerCache[client][class_jump_height] = ClassDataCache[classindex][class_jump_height];
ClassPlayerCache[client][class_jump_distance] = ClassDataCache[classindex][class_jump_distance];
ClassPlayerCache[client][Class_ImmunityMode] = ClassDataCache[classindex][Class_ImmunityMode];
ClassPlayerCache[client][Class_ImmunityAmount] = ClassDataCache[classindex][Class_ImmunityAmount];
ClassPlayerCache[client][Class_NoFallDamage] = ClassDataCache[classindex][Class_NoFallDamage];
ClassPlayerCache[client][Class_Health] = ClassDataCache[classindex][Class_Health];
ClassPlayerCache[client][Class_HealthRegenInterval] = ClassDataCache[classindex][Class_HealthRegenInterval];
ClassPlayerCache[client][Class_HealthRegenAmount] = ClassDataCache[classindex][Class_HealthRegenAmount];
ClassPlayerCache[client][Class_HealthInfectGain] = ClassDataCache[classindex][Class_HealthInfectGain];
ClassPlayerCache[client][Class_KillBonus] = ClassDataCache[classindex][Class_KillBonus];
ClassPlayerCache[client][Class_Speed] = ClassDataCache[classindex][Class_Speed];
ClassPlayerCache[client][Class_KnockBack] = ClassDataCache[classindex][Class_KnockBack];
ClassPlayerCache[client][Class_JumpHeight] = ClassDataCache[classindex][Class_JumpHeight];
ClassPlayerCache[client][Class_JumpDistance] = ClassDataCache[classindex][Class_JumpDistance];
}
default:
{
@ -782,7 +837,7 @@ ClassResetMultiplierCache()
}
/**
* Resets the selected class indexes for next span on one or all clients.
* Resets the selected class indexes for next spawn on one or all clients.
*
* @param client Optional. Specify client to reset. Default is all.
*/
@ -814,7 +869,7 @@ ClassResetNextIndexes(client = -1)
* Note: Does not apply attributes. The classes are only marked as selected.
*
* @param client The client index.
* @param excludeTeam Do not restore the specified team.
* @param excludeTeam Optional. Do not restore the specified team.
*/
ClassRestoreNextIndexes(client, excludeTeam = -1)
{
@ -870,52 +925,157 @@ ClassRestoreNextIndexes(client, excludeTeam = -1)
* Sets default class indexes for each team on all players, or a single player
* if specified.
*
* @param client Optional. The client index.
* @param client Optional. The client index. If specified, cookies are used.
*/
ClassClientSetDefaultIndexes(client = -1)
{
// Get indexes.
new zombieindex = ClassGetDefaultSpawnClass(ZR_CLASS_TEAM_ZOMBIES);
new humanindex = ClassGetDefaultSpawnClass(ZR_CLASS_TEAM_HUMANS);
new adminindex = ClassGetDefaultSpawnClass(ZR_CLASS_TEAM_ADMINS);
new bool:clientvalid = ZRIsClientValid(client);
new filter[ClassFilter];
new bool:saveclasses = GetConVarBool(g_hCvarsList[CVAR_CLASSES_SAVE]);
// Validate zombie class index.
new zombieindex;
new humanindex;
new adminindex;
new bool:haszombie;
new bool:hashuman;
new bool:hasadmin;
// Check if a client is specified.
if (clientvalid)
{
// Get cookie indexes if enabled.
if (saveclasses)
{
zombieindex = CookiesGetInt(client, g_hClassCookieClassSelected[ZR_CLASS_TEAM_ZOMBIES]);
humanindex = CookiesGetInt(client, g_hClassCookieClassSelected[ZR_CLASS_TEAM_HUMANS]);
adminindex = CookiesGetInt(client, g_hClassCookieClassSelected[ZR_CLASS_TEAM_ADMINS]);
}
else
{
// Do not use indexes in cookies. Set invalid values so it will
// fall back to default class.
zombieindex = 0;
humanindex = 0;
adminindex = 0;
}
// Note: When class indexes are set on cookies, they're incremented by
// one so zero means no class set and will result in a invalid
// class index when restored.
// Setup filtering so group permission is checked on player first.
filter[ClassFilter_Client] = client;
// Check if class indexes are set and that the client pass group
// permissions. If not, fall back to default class indexes. Otherwise
// substract index by one.
if (zombieindex <= 0 || !ClassFilterMatch(zombieindex, filter))
{
zombieindex = ClassGetDefaultSpawnClass(ZR_CLASS_TEAM_ZOMBIES, filter);
}
else
{
zombieindex--;
haszombie = true;
}
if (humanindex <= 0 || !ClassFilterMatch(humanindex, filter))
{
humanindex = ClassGetDefaultSpawnClass(ZR_CLASS_TEAM_HUMANS, filter);
}
else
{
humanindex--;
hashuman = true;
}
if (adminindex <= 0 || !ClassFilterMatch(adminindex, filter))
{
adminindex = ClassGetDefaultSpawnClass(ZR_CLASS_TEAM_ADMINS, filter);
}
else
{
adminindex--;
hasadmin = true;
}
}
else
{
// Setup filtering so classes with groups set are excluded.
filter[ClassFilter_Client] = -1;
// Get default class indexes.
zombieindex = ClassGetDefaultSpawnClass(ZR_CLASS_TEAM_ZOMBIES, filter);
humanindex = ClassGetDefaultSpawnClass(ZR_CLASS_TEAM_HUMANS, filter);
adminindex = ClassGetDefaultSpawnClass(ZR_CLASS_TEAM_ADMINS, filter);
}
// Validate indexes.
if (!ClassValidateIndex(zombieindex))
{
// Invalid class index. Fall back to default class in class config and
// log a warning.
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Playerclasses, "Set Default Indexes", "Warning: Failed to get default zombie class, falling back to default class in class config. Check spelling in \"zr_classes_default_zombie\".");
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Playerclasses, "Set Default Indexes", "Warning: Failed to get specified zombie class, falling back to default class in class config. Check spelling in \"zr_classes_default_zombie\".");
// Use default class.
zombieindex = ClassGetDefaultClass(ZR_CLASS_TEAM_ZOMBIES);
zombieindex = ClassGetDefaultClass(ZR_CLASS_TEAM_ZOMBIES, filter);
}
// Validate human class index.
// Get human class index.
if (!ClassValidateIndex(humanindex))
{
// Invalid class index. Fall back to default class in class config and
// log a warning.
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Playerclasses, "Set Default Indexes", "Warning: Failed to get default human class, falling back to default class in class config. Check spelling in \"zr_classes_default_human\".");
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Playerclasses, "Set Default Indexes", "Warning: Failed to get specified human class, falling back to default class in class config. Check spelling in \"zr_classes_default_human\".");
// Use default class.
humanindex = ClassGetDefaultClass(ZR_CLASS_TEAM_HUMANS);
humanindex = ClassGetDefaultClass(ZR_CLASS_TEAM_HUMANS, filter);
}
// Validate admin class index.
// Get admin class index.
if (!ClassValidateIndex(adminindex))
{
// Invalid class index. Fall back to default class in class config if
// possible. A invalid class index (-1) can also be stored if there are
// no admin classes at all.
adminindex = ClassGetDefaultClass(ZR_CLASS_TEAM_ADMINS);
adminindex = ClassGetDefaultClass(ZR_CLASS_TEAM_ADMINS, filter);
}
// Check if a client isn't specified.
if (client < 1)
// Check if a client is specified.
if (clientvalid)
{
// Set selected class idexes.
ClassSelected[client][ZR_CLASS_TEAM_ZOMBIES] = zombieindex;
ClassSelected[client][ZR_CLASS_TEAM_HUMANS] = humanindex;
ClassSelected[client][ZR_CLASS_TEAM_ADMINS] = adminindex;
// Copy human class data to player cache.
ClassReloadPlayerCache(client, humanindex);
// Save indexes in cookies if enabled, and not already saved.
if (saveclasses)
{
if (!haszombie)
{
CookiesSetInt(client, g_hClassCookieClassSelected[ZR_CLASS_TEAM_ZOMBIES], zombieindex + 1);
}
if (!hashuman)
{
CookiesSetInt(client, g_hClassCookieClassSelected[ZR_CLASS_TEAM_HUMANS], humanindex + 1);
}
if (!hasadmin)
{
CookiesSetInt(client, g_hClassCookieClassSelected[ZR_CLASS_TEAM_ADMINS], adminindex + 1);
}
}
}
else
{
// No client specified. Loop through all players.
for (new clientindex = 1; clientindex <= MAXPLAYERS; clientindex++)
for (new clientindex = 1; clientindex <= MaxClients; clientindex++)
{
// Set selected class idexes.
ClassSelected[clientindex][ZR_CLASS_TEAM_ZOMBIES] = zombieindex;
ClassSelected[clientindex][ZR_CLASS_TEAM_HUMANS] = humanindex;
ClassSelected[clientindex][ZR_CLASS_TEAM_ADMINS] = adminindex;
@ -924,15 +1084,6 @@ ClassClientSetDefaultIndexes(client = -1)
ClassReloadPlayerCache(client, humanindex);
}
}
else
{
ClassSelected[client][ZR_CLASS_TEAM_ZOMBIES] = zombieindex;
ClassSelected[client][ZR_CLASS_TEAM_HUMANS] = humanindex;
ClassSelected[client][ZR_CLASS_TEAM_ADMINS] = adminindex;
// Copy human class data to player cache.
ClassReloadPlayerCache(client, humanindex);
}
}
/**
@ -976,6 +1127,10 @@ ClassDumpData(index, cachetype, String:buffer[], maxlen)
Format(attribute, sizeof(attribute), "flags: \"%d\"\n", ClassGetFlags(index, cachetype));
cellcount += StrCat(buffer, maxlen, attribute);
ClassGetGroup(index, format_buffer, sizeof(format_buffer), cachetype);
Format(attribute, sizeof(attribute), "group: \"%s\"\n", format_buffer);
cellcount += StrCat(buffer, maxlen, attribute);
ClassGetName(index, format_buffer, sizeof(format_buffer), cachetype);
Format(attribute, sizeof(attribute), "name: \"%s\"\n", format_buffer);
cellcount += StrCat(buffer, maxlen, attribute);

View File

@ -333,16 +333,13 @@ stock TranslationReplyToCommand(client, any:...)
{
// Format string to create plugin style. (color)
TranslationPluginFormatString(translation, sizeof(translation));
// Print translated phrase to client's chat/console.
PrintToChat(client, translation);
}
else
{
// Format string to create plugin style. (no color)
TranslationPluginFormatString(translation, sizeof(translation), false);
// Print to server.
PrintToServer(translation);
}
// Print translated phrase to server or client's chat/console.
ReplyToCommand(client, translation);
}

View File

@ -64,7 +64,7 @@ public Action:ZAdminCommand(client, argc)
*/
bool:ZAdminMenu(client)
{
// If client isn't an admin, then stop.
// If client isn't an generic admin, then stop.
if (!ZRIsClientAdmin(client))
{
TranslationPrintToChat(client, "Must be admin");
@ -94,16 +94,18 @@ bool:ZAdminMenu(client)
Format(ztele, sizeof(ztele), "%t", "ZAdmin main force ztele");
// Get conditions for options.
new bool:hitgroupsenabled = GetConVarBool(g_hCvarsList[CVAR_HITGROUPS]);
new configdraw = MenuGetItemDraw(ZRIsClientPrivileged(client, OperationType_Configuration));
new moderatordraw = MenuGetItemDraw(ZRIsClientPrivileged(client, OperationType_Generic));
new bool:hitgroupsenabled = GetConVarBool(g_hCvarsList[CVAR_HITGROUPS]) && ZRIsClientPrivileged(client, OperationType_Configuration);
// Add items to menu.
SetMenuTitle(menu_zadmin, title);
AddMenuItem(menu_zadmin, "classmultipliers", classmultipliers);
AddMenuItem(menu_zadmin, "weapons", weapons);
AddMenuItem(menu_zadmin, "classmultipliers", classmultipliers, configdraw);
AddMenuItem(menu_zadmin, "weapons", weapons, configdraw);
AddMenuItem(menu_zadmin, "hitgroups", hitgroups, MenuGetItemDraw(hitgroupsenabled));
AddMenuItem(menu_zadmin, "infect", infect);
AddMenuItem(menu_zadmin, "zspawn", zspawn);
AddMenuItem(menu_zadmin, "ztele", ztele);
AddMenuItem(menu_zadmin, "infect", infect, moderatordraw);
AddMenuItem(menu_zadmin, "zspawn", zspawn, moderatordraw);
AddMenuItem(menu_zadmin, "ztele", ztele, moderatordraw);
// Set "Back" button.
SetMenuExitBackButton(menu_zadmin, true);

View File

@ -235,7 +235,7 @@ stock bool:ZRTeamHasClients(team = -1)
}
/**
* Returns whether a player is a generic admin or not.
* 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.
@ -249,7 +249,7 @@ stock bool:ZRIsClientAdmin(client, AdminFlag:flag = Admin_Generic)
return false;
}
// If client doesn't have the Admin_Generic flag, then stop.
// If client doesn't have the specified flag, then stop.
if (!GetAdminFlag(GetUserAdmin(client), flag))
{
return false;

View File

@ -233,7 +233,7 @@ bool:ZTeleClient(client, bool:force = false)
ZTeleTeleportClient(client)
{
// Teleport client.
TeleportEntity(client, g_vecZTeleSpawn[client], NULL_VECTOR, NULL_VECTOR);
TeleportEntity(client, g_vecZTeleSpawn[client], NULL_VECTOR, Float:{0.0, 0.0, 0.0});
}
/**