add "identifier" to playerclasses (kv section name) and use it as default for cookies, zr_class_modify, etc.

This commit is contained in:
BotoX 2019-11-01 16:17:50 +01:00
parent a7caf1de7b
commit 7265c1922f
11 changed files with 342 additions and 107 deletions

View File

@ -55,7 +55,9 @@ public void __pl_zombiereloaded_SetNTVOptional()
MarkNativeAsOptional("ZR_GetActiveClass"); MarkNativeAsOptional("ZR_GetActiveClass");
MarkNativeAsOptional("ZR_SelectClientClass"); MarkNativeAsOptional("ZR_SelectClientClass");
MarkNativeAsOptional("ZR_GetClassByName"); MarkNativeAsOptional("ZR_GetClassByName");
MarkNativeAsOptional("ZR_GetClassByIdentifier");
MarkNativeAsOptional("ZR_GetClassDisplayName"); MarkNativeAsOptional("ZR_GetClassDisplayName");
MarkNativeAsOptional("ZR_GetClassIdentifier");
MarkNativeAsOptional("ZR_IsClientZombie"); MarkNativeAsOptional("ZR_IsClientZombie");
MarkNativeAsOptional("ZR_IsClientHuman"); MarkNativeAsOptional("ZR_IsClientHuman");

View File

@ -116,6 +116,19 @@ native ClassSelectResult ZR_SelectClientClass(int client, int classIndex, bool a
*/ */
native int ZR_GetClassByName(const char[] className, int cacheType = ZR_CLASS_CACHE_MODIFIED); native int ZR_GetClassByName(const char[] className, int cacheType = ZR_CLASS_CACHE_MODIFIED);
/**
* Gets the class index of the class with the specified identifier.
*
* Note: This search is linear and probably won't perform well in large loops.
*
* @param classIdent Class identifier to search for.
* @param cacheType Optional. Specifies which class cache to read from,
* except player cache.
*
* @return Class index, or -1 if none found.
*/
native int ZR_GetClassByIdentifier(const char[] classIdent, int cacheType = ZR_CLASS_CACHE_MODIFIED);
/** /**
* Gets the class name displayed in the class menu. * Gets the class name displayed in the class menu.
* *

View File

@ -39,7 +39,9 @@ APIClassInit()
CreateNative("ZR_GetZombieClass", APIGetZombieClass); CreateNative("ZR_GetZombieClass", APIGetZombieClass);
CreateNative("ZR_SelectClientClass", APISelectClientClass); CreateNative("ZR_SelectClientClass", APISelectClientClass);
CreateNative("ZR_GetClassByName", APIGetClassByName); CreateNative("ZR_GetClassByName", APIGetClassByName);
CreateNative("ZR_GetClassByIdentifier", APIGetClassByIdentifier);
CreateNative("ZR_GetClassDisplayName", APIGetClassDisplayName); CreateNative("ZR_GetClassDisplayName", APIGetClassDisplayName);
CreateNative("ZR_GetClassIdentifier", APIGetClassIdentifier);
} }
/** /**
@ -148,7 +150,34 @@ public APIGetClassByName(Handle:plugin, numParams)
return -1; return -1;
} }
return ClassGetIndex(className, cacheType); return ClassGetIndexByName(className, cacheType);
}
/**
* Native call function (ZR_GetClassByIdentifier)
*
* native ZR_GetClassByIdentifier(const String:classIdent[], cacheType = ZR_CLASS_CACHE_MODIFIED);
*/
public APIGetClassByIdentifier(Handle:plugin, numParams)
{
decl String:classIdent[64];
classIdent[0] = 0;
// Get class name.
if (GetNativeString(1, classIdent, sizeof(classIdent)) != SP_ERROR_NONE)
{
ThrowNativeError(SP_ERROR_NATIVE, "Unexpected error when reading classIdent parameter. Possibly corrupt or missing data.");
return -1;
}
new cacheType = GetNativeCell(2);
if (cacheType == ZR_CLASS_CACHE_PLAYER)
{
ThrowNativeError(SP_ERROR_NATIVE, "Invalid cache type. Player cache is not allowed in this function.");
return -1;
}
return ClassGetIndexByIdentifier(classIdent, cacheType);
} }
/** /**
@ -197,3 +226,50 @@ public APIGetClassDisplayName(Handle:plugin, numParams)
return bytes; return bytes;
} }
/**
* Native call function (ZR_GetClassIdentifier)
*
* native ZR_GetClassIdentifier(classIndex, String:buffer[], maxlen, cacheType = ZR_CLASS_CACHE_MODIFIED);
*/
public APIGetClassIdentifier(Handle:plugin, numParams)
{
new index = GetNativeCell(1);
new maxlen = GetNativeCell(3);
new cacheType = GetNativeCell(4);
if (maxlen <= 0)
{
// No buffer size.
return 0;
}
// Validate index.
if (cacheType == ZR_CLASS_CACHE_PLAYER)
{
// Client index.
APIValidateClientIndex(index, Condition_Either);
}
else
{
// Class index.
if (!ClassValidateIndex(index))
{
ThrowNativeError(SP_ERROR_NATIVE, "Invalid class index. (%d)", index);
return 0;
}
}
decl String:classIdent[maxlen];
new bytes = ClassGetIdentifier(index, classIdent, maxlen, cacheType);
if (bytes <= 0)
{
// The class doesn't have a name for some reason. Make sure the buffer is empty.
classIdent[0] = 0;
}
SetNativeString(2, classIdent, maxlen);
return bytes;
}

View File

@ -42,7 +42,7 @@ CookiesInit()
* @param client The client index. * @param client The client index.
* @param cookie The handle to the cookie. * @param cookie The handle to the cookie.
*/ */
bool:CookiesGetClientCookieBool(client, Handle:cookie) stock bool:CookiesGetClientCookieBool(client, Handle:cookie)
{ {
// Get cookie string. // Get cookie string.
decl String:cookievalue[8]; decl String:cookievalue[8];
@ -59,7 +59,7 @@ bool:CookiesGetClientCookieBool(client, Handle:cookie)
* @param cookie The handle to the cookie. * @param cookie The handle to the cookie.
* @param cookievalue The bool value to set cookie as. * @param cookievalue The bool value to set cookie as.
*/ */
CookiesSetClientCookieBool(client, Handle:cookie, bool:cookievalue) stock CookiesSetClientCookieBool(client, Handle:cookie, bool:cookievalue)
{ {
// Convert bool to string. // Convert bool to string.
decl String:strCookievalue[8]; decl String:strCookievalue[8];
@ -75,7 +75,7 @@ CookiesSetClientCookieBool(client, Handle:cookie, bool:cookievalue)
* @param client The client index. * @param client The client index.
* @param cookie The handle to the cookie. * @param cookie The handle to the cookie.
*/ */
CookiesGetInt(client, Handle:cookie) stock CookiesGetInt(client, Handle:cookie)
{ {
decl String:strValue[16]; decl String:strValue[16];
strValue[0] = 0; strValue[0] = 0;
@ -91,7 +91,7 @@ CookiesGetInt(client, Handle:cookie)
* @param cookie The handle to the cookie. * @param cookie The handle to the cookie.
* @param value The value to set. * @param value The value to set.
*/ */
CookiesSetInt(client, Handle:cookie, value) stock CookiesSetInt(client, Handle:cookie, value)
{ {
// Convert value to string. // Convert value to string.
decl String:strValue[16]; decl String:strValue[16];

View File

@ -33,6 +33,41 @@
* ------------------------------------ * ------------------------------------
*/ */
/**
* Gets the class identifier (config class section name).
*
* @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 ClassGetIdentifier(index, String:buffer[], maxlen, cachetype = ZR_CLASS_CACHE_PLAYER)
{
switch (cachetype)
{
case ZR_CLASS_CACHE_ORIGINAL:
{
return strcopy(buffer, maxlen, ClassData[index][Class_Identifier]);
}
case ZR_CLASS_CACHE_MODIFIED:
{
return strcopy(buffer, maxlen, ClassDataCache[index][Class_Identifier]);
}
case ZR_CLASS_CACHE_PLAYER:
{
return strcopy(buffer, maxlen, ClassPlayerCache[index][Class_Identifier]);
}
}
return -1;
}
/** /**
* Checks if the specified class is enabled. * Checks if the specified class is enabled.
* *
@ -1401,7 +1436,8 @@ stock ClassDataTypes:ClassGetAttributeType(attributeflag)
} }
// String. // String.
case ZR_CLASS_GROUP, case ZR_CLASS_IDENTIFIER,
ZR_CLASS_GROUP,
ZR_CLASS_NAME, ZR_CLASS_NAME,
ZR_CLASS_DESCRIPTION, ZR_CLASS_DESCRIPTION,
ZR_CLASS_MODEL_PATH, ZR_CLASS_MODEL_PATH,

View File

@ -33,7 +33,7 @@ ClassOnCommandsCreate()
// Create base class commands. // Create base class commands.
RegConsoleCmd("zr_class_dump", ClassDumpCommand, "Dumps class data at a specified index in the specified cache. Usage: zr_class_dump <cachetype> <index|targetname>"); RegConsoleCmd("zr_class_dump", ClassDumpCommand, "Dumps class data at a specified index in the specified cache. Usage: zr_class_dump <cachetype> <index|targetname>");
RegConsoleCmd("zr_class_dump_multipliers", ClassDumpMultipliersCommand, "Dumps class attribute multipliers for the specified team. Usage: zr_class_dump_multipliers <\"zombies\"|\"humans\">"); RegConsoleCmd("zr_class_dump_multipliers", ClassDumpMultipliersCommand, "Dumps class attribute multipliers for the specified team. Usage: zr_class_dump_multipliers <\"zombies\"|\"humans\">");
RegConsoleCmd("zr_class_modify", ClassModifyCommand, "Modify class data on one or more classes. Usage: zr_class_modify <classname|\"zombies\"|\"humans\"|\"admins\"> <attribute> <value> [is_multiplier]"); RegConsoleCmd("zr_class_modify", ClassModifyCommand, "Modify class data on one or more classes. Usage: zr_class_modify <identifier|\"zombies\"|\"humans\"|\"admins\"> <attribute> <value> [is_multiplier]");
RegConsoleCmd("zr_class_set_multiplier", ClassSetMultiplierCommand, "Sets the multiplier on a class attribute. Usage: zr_class_set_multiplier <\"zombies\"|\"humans\"> <attribute> <value>"); RegConsoleCmd("zr_class_set_multiplier", ClassSetMultiplierCommand, "Sets the multiplier on a class attribute. Usage: zr_class_set_multiplier <\"zombies\"|\"humans\"> <attribute> <value>");
RegConsoleCmd("zr_class_reload", ClassReloadCommand, "Refreshes the player cache and reloads class attributes on one or more players. Usage: zr_class_reload <target>"); RegConsoleCmd("zr_class_reload", ClassReloadCommand, "Refreshes the player cache and reloads class attributes on one or more players. Usage: zr_class_reload <target>");
} }
@ -297,7 +297,7 @@ public Action:ClassModifyCommand(client, argc)
return Plugin_Handled; return Plugin_Handled;
} }
decl String:classname[64]; decl String:identifier[64];
decl String:attributename[128]; decl String:attributename[128];
decl String:value[256]; decl String:value[256];
decl String:ismultiplier[4]; decl String:ismultiplier[4];
@ -313,7 +313,7 @@ public Action:ClassModifyCommand(client, argc)
classlist = CreateArray(); classlist = CreateArray();
// Get command arguments. // Get command arguments.
GetCmdArg(1, classname, sizeof(classname)); GetCmdArg(1, identifier, sizeof(identifier));
GetCmdArg(2, attributename, sizeof(attributename)); GetCmdArg(2, attributename, sizeof(attributename));
GetCmdArg(3, value, sizeof(value)); GetCmdArg(3, value, sizeof(value));
@ -342,36 +342,36 @@ public Action:ClassModifyCommand(client, argc)
// Get attribute data type. // Get attribute data type.
attributetype = ClassGetAttributeType(attributeflag); attributetype = ClassGetAttributeType(attributeflag);
// Check if classname is a group. Add classes to the class list // Check if identifier is a group. Add classes to the class list
// and use the specified team filter. // and use the specified team filter.
if (StrEqual(classname, "all", false)) if (StrEqual(identifier, "all", false))
{ {
listresult = ClassAddToArray(classlist); listresult = ClassAddToArray(classlist);
isgroup = true; isgroup = true;
} }
else if (StrEqual(classname, "humans", false)) else if (StrEqual(identifier, "humans", false))
{ {
listresult = ClassAddToArray(classlist, ZR_CLASS_TEAM_HUMANS); listresult = ClassAddToArray(classlist, ZR_CLASS_TEAM_HUMANS);
isgroup = true; isgroup = true;
} }
else if (StrEqual(classname, "zombies", false)) else if (StrEqual(identifier, "zombies", false))
{ {
listresult = ClassAddToArray(classlist, ZR_CLASS_TEAM_ZOMBIES); listresult = ClassAddToArray(classlist, ZR_CLASS_TEAM_ZOMBIES);
isgroup = true; isgroup = true;
} }
else if (StrEqual(classname, "admins", false)) else if (StrEqual(identifier, "admins", false))
{ {
listresult = ClassAddToArray(classlist, ZR_CLASS_TEAM_ADMINS); listresult = ClassAddToArray(classlist, ZR_CLASS_TEAM_ADMINS);
isgroup = true; isgroup = true;
} }
// Check if classname is a group. // Check if identifier is a group.
if (isgroup) if (isgroup)
{ {
// Check if the list is valid. // Check if the list is valid.
if (!listresult) if (!listresult)
{ {
ReplyToCommand(client, "Failed to get classes in the specified team: \"%s\".", classname); ReplyToCommand(client, "Failed to get classes in the specified team: \"%s\".", identifier);
CloseHandle(classlist); CloseHandle(classlist);
return Plugin_Handled; return Plugin_Handled;
@ -430,7 +430,7 @@ public Action:ClassModifyCommand(client, argc)
else else
{ {
// It's a single class. // It's a single class.
classindex = ClassGetIndex(classname); classindex = ClassGetIndexByIdentifier(identifier);
// Validate classindex. // Validate classindex.
if (!ClassValidateIndex(classindex)) if (!ClassValidateIndex(classindex))

View File

@ -66,7 +66,7 @@ ClassOnCookiesCreate()
ClassOnModulesLoaded() ClassOnModulesLoaded()
{ {
// Set default classes on all player slots. // Set default classes on all player slots.
ClassClientSetDefaultIndexes(); ClassClientSetDefaultIdentifiers();
} }
/** /**
@ -149,7 +149,7 @@ ClassOnClientPostAdminCheck(client)
if (ClassValidated) if (ClassValidated)
{ {
// Set default class indexes on the player. // Set default class indexes on the player.
ClassClientSetDefaultIndexes(client); ClassClientSetDefaultIdentifiers(client);
} }
} }
@ -175,7 +175,7 @@ ClassOnCookiesCached(client)
if (ClassValidated) if (ClassValidated)
{ {
// Set default class indexes on the player. // Set default class indexes on the player.
ClassClientSetDefaultIndexes(client); ClassClientSetDefaultIdentifiers(client);
} }
} }
@ -447,7 +447,7 @@ ClassOnClientInfected(client, bool:motherzombie = false)
else else
{ {
// Assume it's a class name. Get index for the specified class name. // Assume it's a class name. Get index for the specified class name.
motherindex = ClassGetIndex(motherzombiesetting); motherindex = ClassGetIndexByIdentifier(motherzombiesetting);
// Validate index. // Validate index.
if (ClassValidateIndex(motherindex)) if (ClassValidateIndex(motherindex))

View File

@ -265,6 +265,7 @@ ClassMenuSelect(client, teamid)
new classindex; new classindex;
decl String:title[MENU_LINE_TITLE_LENGTH]; decl String:title[MENU_LINE_TITLE_LENGTH];
decl String:classident[64];
decl String:classname[MENU_LINE_REG_LENGTH]; decl String:classname[MENU_LINE_REG_LENGTH];
decl String:description[MENU_LINE_BIG_LENGTH]; decl String:description[MENU_LINE_BIG_LENGTH];
decl String:menuitem[MENU_LINE_HUGE_LENGTH]; decl String:menuitem[MENU_LINE_HUGE_LENGTH];
@ -319,12 +320,13 @@ ClassMenuSelect(client, teamid)
{ {
// Get index, name and description. // Get index, name and description.
classindex = GetArrayCell(classarray, i); classindex = GetArrayCell(classarray, i);
ClassGetIdentifier(classindex, classident, sizeof(classident));
ClassGetName(classindex, classname, sizeof(classname), ZR_CLASS_CACHE_MODIFIED); ClassGetName(classindex, classname, sizeof(classname), ZR_CLASS_CACHE_MODIFIED);
ClassGetDescription(classindex, description, sizeof(description), ZR_CLASS_CACHE_MODIFIED); ClassGetDescription(classindex, description, sizeof(description), ZR_CLASS_CACHE_MODIFIED);
// Add menu item. Using extra spaces for indention on the second line. // Add menu item. Using extra spaces for indention on the second line.
Format(menuitem, sizeof(menuitem), "%s\n %s", classname, description); Format(menuitem, sizeof(menuitem), "%s\n %s", classname, description);
AddMenuItem(menu, classname, menuitem); AddMenuItem(menu, classident, menuitem);
} }
} }
@ -340,7 +342,7 @@ ClassMenuSelect(client, teamid)
*/ */
public ClassMenuSelectHandle(Handle:menu, MenuAction:action, client, slot) public ClassMenuSelectHandle(Handle:menu, MenuAction:action, client, slot)
{ {
decl String:className[MENU_LINE_REG_LENGTH]; decl String:classIdent[MENU_LINE_REG_LENGTH];
new classIndex; new classIndex;
new bool:autoclose = GetConVarBool(g_hCvarsList[CVAR_CLASSES_MENU_AUTOCLOSE]); new bool:autoclose = GetConVarBool(g_hCvarsList[CVAR_CLASSES_MENU_AUTOCLOSE]);
@ -349,10 +351,10 @@ public ClassMenuSelectHandle(Handle:menu, MenuAction:action, client, slot)
case MenuAction_Select: case MenuAction_Select:
{ {
// Get class name from the information string. // Get class name from the information string.
GetMenuItem(menu, slot, className, sizeof(className)); GetMenuItem(menu, slot, classIdent, sizeof(classIdent));
// Solve class index from the name. // Solve class index from the name.
classIndex = ClassGetIndex(className); classIndex = ClassGetIndexByIdentifier(classIdent);
// Select (and eventually apply) class. // Select (and eventually apply) class.
ClassSelectClientClass(client, classIndex); ClassSelectClientClass(client, classIndex);

View File

@ -105,6 +105,44 @@ stock ClassValidateAttributes(classindex, bool:logErrors = false)
{ {
new flags; new flags;
// Identifier
decl String:identifier[64];
identifier[0] = 0;
if (strcopy(identifier, sizeof(identifier), ClassData[classindex][Class_Identifier]) < ZR_CLASS_IDENTIFIER_MIN)
{
flags += ZR_CLASS_IDENTIFIER;
if (logErrors)
{
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Playerclasses, "Config Validation", "Warning: Missing identifier at index %d.", classindex);
}
}
else
{
// Check for reserved identifier keyworks. These aren't allowed as identifiers.
if (StrEqual(identifier, "all", false) ||
StrEqual(identifier, "humans", false) ||
StrEqual(identifier, "zombies", false) ||
StrEqual(identifier, "admins", false))
{
flags += ZR_CLASS_IDENTIFIER;
if (logErrors)
{
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Playerclasses, "Config Validation", "Warning: Invalid identifier at index %d. Cannot be a team identifier: \"%s\"", classindex, identifier);
}
}
// Check for duplicate use.
int duplicate = ClassGetIndexByIdentifier(identifier);
if (duplicate >= 0)
{
flags += ZR_CLASS_IDENTIFIER;
if (logErrors)
{
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Playerclasses, "Config Validation", "Warning: Invalid identifier at index %d. Already exists at index %d: \"%s\".", classindex, duplicate, identifier);
}
}
}
// Team. // Team.
new team = ClassData[classindex][Class_Team]; new team = ClassData[classindex][Class_Team];
if (team < ZR_CLASS_TEAM_MIN || team > ZR_CLASS_TEAM_MAX) if (team < ZR_CLASS_TEAM_MIN || team > ZR_CLASS_TEAM_MAX)
@ -710,7 +748,7 @@ stock bool:ClassTeamCompare(index, teamid, cachetype = ZR_CLASS_CACHE_MODIFIED)
* data. * data.
* @return The class index if successful, -1 otherwise. * @return The class index if successful, -1 otherwise.
*/ */
stock ClassGetIndex(const String:name[], cachetype = ZR_CLASS_CACHE_MODIFIED) stock ClassGetIndexByName(const String:name[], cachetype = ZR_CLASS_CACHE_MODIFIED)
{ {
decl String:current_name[64]; decl String:current_name[64];
@ -735,6 +773,42 @@ stock ClassGetIndex(const String:name[], cachetype = ZR_CLASS_CACHE_MODIFIED)
return -1; return -1;
} }
/**
* Gets the first class index of a class with the specified identifier (not a case
* sensitive search).
*
* @param ident The identifier to search for.
* @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
* data.
* @return The class index if successful, -1 otherwise.
*/
stock ClassGetIndexByIdentifier(const String:ident[], cachetype = ZR_CLASS_CACHE_MODIFIED)
{
decl String:current_ident[64];
// Check if there are no classes, or reading from player cache.
if (ClassCount == 0 || cachetype == ZR_CLASS_CACHE_PLAYER)
{
return -1;
}
// Loop through all classes.
for (new classindex = 0; classindex < ClassCount; classindex++)
{
// Get its ident and compare it with the specified class ident.
ClassGetIdentifier(classindex, current_ident, sizeof(current_ident), cachetype);
if (strcmp(ident, current_ident, false) == 0)
{
return classindex;
}
}
// The class index wasn't found.
return -1;
}
/** /**
* Gets the currently active class index that the player is using. * Gets the currently active class index that the player is using.
* Note: Does not check if the player is dead. * Note: Does not check if the player is dead.
@ -1301,7 +1375,7 @@ stock ClassGetDefaultClass(teamid, filter[ClassFilter] = ClassNoSpecialClasses,
*/ */
stock ClassGetDefaultSpawnClass(teamid, filter[ClassFilter] = ClassNoSpecialClasses, cachetype = ZR_CLASS_CACHE_MODIFIED) stock ClassGetDefaultSpawnClass(teamid, filter[ClassFilter] = ClassNoSpecialClasses, cachetype = ZR_CLASS_CACHE_MODIFIED)
{ {
decl String:classname[64]; decl String:classident[64];
new classindex; new classindex;
// Get the default class name from the correct CVAR depending on teamid. // Get the default class name from the correct CVAR depending on teamid.
@ -1309,15 +1383,15 @@ stock ClassGetDefaultSpawnClass(teamid, filter[ClassFilter] = ClassNoSpecialClas
{ {
case ZR_CLASS_TEAM_ZOMBIES: case ZR_CLASS_TEAM_ZOMBIES:
{ {
GetConVarString(g_hCvarsList[CVAR_CLASSES_DEFAULT_ZOMBIE], classname, sizeof(classname)); GetConVarString(g_hCvarsList[CVAR_CLASSES_DEFAULT_ZOMBIE], classident, sizeof(classident));
} }
case ZR_CLASS_TEAM_HUMANS: case ZR_CLASS_TEAM_HUMANS:
{ {
GetConVarString(g_hCvarsList[CVAR_CLASSES_DEFAULT_HUMAN], classname, sizeof(classname)); GetConVarString(g_hCvarsList[CVAR_CLASSES_DEFAULT_HUMAN], classident, sizeof(classident));
} }
case ZR_CLASS_TEAM_ADMINS: case ZR_CLASS_TEAM_ADMINS:
{ {
GetConVarString(g_hCvarsList[CVAR_CLASSES_DEFAULT_ADMIN_MODE], classname, sizeof(classname)); GetConVarString(g_hCvarsList[CVAR_CLASSES_DEFAULT_ADMIN_MODE], classident, sizeof(classident));
} }
default: default:
{ {
@ -1327,10 +1401,10 @@ stock ClassGetDefaultSpawnClass(teamid, filter[ClassFilter] = ClassNoSpecialClas
} }
// Check if the class name isn't empty. // Check if the class name isn't empty.
if (strlen(classname) > 0) if (strlen(classident) > 0)
{ {
// Check if the user set "random" as default class. // Check if the user set "random" as default class.
if (StrEqual(classname, "random", false)) if (StrEqual(classident, "random", false))
{ {
// Get a list of all classes with the specified team ID. Deny // Get a list of all classes with the specified team ID. Deny
// classes with special flags. // classes with special flags.
@ -1354,7 +1428,7 @@ stock ClassGetDefaultSpawnClass(teamid, filter[ClassFilter] = ClassNoSpecialClas
// The user set a spesific class. // The user set a spesific class.
// Try to get the class index with the specified class name. // Try to get the class index with the specified class name.
classindex = ClassGetIndex(classname, cachetype); classindex = ClassGetIndexByIdentifier(classident, cachetype);
// Validate the class index and check if the team IDs match. // Validate the class index and check if the team IDs match.
if (ClassValidateIndex(classindex) && (teamid == ClassGetTeamID(classindex, cachetype))) if (ClassValidateIndex(classindex) && (teamid == ClassGetTeamID(classindex, cachetype)))
@ -1368,7 +1442,7 @@ stock ClassGetDefaultSpawnClass(teamid, filter[ClassFilter] = ClassNoSpecialClas
// in the specified team, and log a warning. // in the specified team, and log a warning.
classindex = ClassGetFirstClass(teamid, filter, 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); 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.", classident, teamid);
// Validate the new index. // Validate the new index.
if (ClassValidateIndex(classindex)) if (ClassValidateIndex(classindex))

View File

@ -137,6 +137,7 @@
/** /**
* @section Attribute limit values. Used when validating. * @section Attribute limit values. Used when validating.
*/ */
#define ZR_CLASS_IDENTIFIER_MIN 1
#define ZR_CLASS_TEAM_MIN 0 #define ZR_CLASS_TEAM_MIN 0
#define ZR_CLASS_TEAM_MAX 2 #define ZR_CLASS_TEAM_MAX 2
#define ZR_CLASS_FLAGS_MIN 0 #define ZR_CLASS_FLAGS_MIN 0
@ -186,37 +187,38 @@
/** /**
* @section Class attribute flags. * @section Class attribute flags.
*/ */
#define ZR_CLASS_ENABLED (1<<0) #define ZR_CLASS_IDENTIFIER (1<<0)
#define ZR_CLASS_TEAM (1<<1) #define ZR_CLASS_ENABLED (1<<1)
#define ZR_CLASS_TEAM_DEFAULT (1<<2) #define ZR_CLASS_TEAM (1<<2)
#define ZR_CLASS_FLAGS (1<<3) #define ZR_CLASS_TEAM_DEFAULT (1<<3)
#define ZR_CLASS_GROUP (1<<4) #define ZR_CLASS_FLAGS (1<<4)
#define ZR_CLASS_SM_FLAGS (1<<5) #define ZR_CLASS_GROUP (1<<5)
#define ZR_CLASS_NAME (1<<6) #define ZR_CLASS_SM_FLAGS (1<<6)
#define ZR_CLASS_DESCRIPTION (1<<7) #define ZR_CLASS_NAME (1<<7)
#define ZR_CLASS_MODEL_PATH (1<<8) #define ZR_CLASS_DESCRIPTION (1<<8)
#define ZR_CLASS_MODEL_SKIN_INDEX (1<<9) #define ZR_CLASS_MODEL_PATH (1<<9)
#define ZR_CLASS_ALPHA_INITIAL (1<<10) #define ZR_CLASS_MODEL_SKIN_INDEX (1<<10)
#define ZR_CLASS_ALPHA_DAMAGED (1<<11) #define ZR_CLASS_ALPHA_INITIAL (1<<11)
#define ZR_CLASS_ALPHA_DAMAGE (1<<12) #define ZR_CLASS_ALPHA_DAMAGED (1<<12)
#define ZR_CLASS_OVERLAY_PATH (1<<13) #define ZR_CLASS_ALPHA_DAMAGE (1<<13)
#define ZR_CLASS_NVGS (1<<14) #define ZR_CLASS_OVERLAY_PATH (1<<14)
#define ZR_CLASS_FOV (1<<15) #define ZR_CLASS_NVGS (1<<15)
#define ZR_CLASS_HAS_NAPALM (1<<16) #define ZR_CLASS_FOV (1<<16)
#define ZR_CLASS_NAPALM_TIME (1<<17) #define ZR_CLASS_HAS_NAPALM (1<<17)
#define ZR_CLASS_IMMUNITY_MODE (1<<18) #define ZR_CLASS_NAPALM_TIME (1<<18)
#define ZR_CLASS_IMMUNITY_AMOUNT (1<<19) #define ZR_CLASS_IMMUNITY_MODE (1<<19)
#define ZR_CLASS_IMMUNITY_COOLDOWN (1<<20) #define ZR_CLASS_IMMUNITY_AMOUNT (1<<20)
#define ZR_CLASS_NO_FALL_DAMAGE (1<<21) #define ZR_CLASS_IMMUNITY_COOLDOWN (1<<21)
#define ZR_CLASS_HEALTH (1<<22) #define ZR_CLASS_NO_FALL_DAMAGE (1<<22)
#define ZR_CLASS_HEALTH_REGEN_INTERVAL (1<<23) #define ZR_CLASS_HEALTH (1<<23)
#define ZR_CLASS_HEALTH_REGEN_AMOUNT (1<<24) #define ZR_CLASS_HEALTH_REGEN_INTERVAL (1<<24)
#define ZR_CLASS_HEALTH_INFECT_GAIN (1<<25) #define ZR_CLASS_HEALTH_REGEN_AMOUNT (1<<25)
#define ZR_CLASS_KILL_BONUS (1<<26) #define ZR_CLASS_HEALTH_INFECT_GAIN (1<<26)
#define ZR_CLASS_SPEED (1<<27) #define ZR_CLASS_KILL_BONUS (1<<27)
#define ZR_CLASS_KNOCKBACK (1<<28) #define ZR_CLASS_SPEED (1<<28)
#define ZR_CLASS_JUMP_HEIGHT (1<<29) #define ZR_CLASS_KNOCKBACK (1<<29)
#define ZR_CLASS_JUMP_DISTANCE (1<<30) #define ZR_CLASS_JUMP_HEIGHT (1<<30)
#define ZR_CLASS_JUMP_DISTANCE (1<<31)
/** /**
* @endsection * @endsection
*/ */
@ -243,6 +245,7 @@
enum ClassAttributes enum ClassAttributes
{ {
/* General */ /* General */
String:Class_Identifier[64],
bool:Class_Enabled, bool:Class_Enabled,
Class_Team, Class_Team,
bool:Class_TeamDefault, bool:Class_TeamDefault,
@ -560,6 +563,7 @@ ClassLoad()
LogEvent(false, LogType_Fatal, LOG_CORE_EVENTS, LogModule_Playerclasses, "Config Validation", "Can't find any classes in \"%s\"", pathclasses); LogEvent(false, LogType_Fatal, LOG_CORE_EVENTS, LogModule_Playerclasses, "Config Validation", "Can't find any classes in \"%s\"", pathclasses);
} }
new String:section_name[64];
new String:name[64]; new String:name[64];
new String:group[64]; new String:group[64];
new String:sm_flags[64]; new String:sm_flags[64];
@ -583,6 +587,10 @@ ClassLoad()
break; break;
} }
/* Class identifier = section name */
KvGetSectionName(kvClassData, section_name, sizeof(section_name));
strcopy(ClassData[ClassCount][Class_Identifier], 64, section_name);
/* General */ /* General */
ClassData[ClassCount][Class_Enabled] = ConfigKvGetStringBool(kvClassData, "enabled", ZR_CLASS_DEFAULT_ENABLED); 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_Team] = KvGetNum(kvClassData, "team", ZR_CLASS_DEFAULT_TEAM);
@ -748,6 +756,7 @@ bool:ClassReloadDataCache()
for (new classindex = 0; classindex < ClassCount; classindex++) for (new classindex = 0; classindex < ClassCount; classindex++)
{ {
/* General */ /* General */
strcopy(ClassDataCache[classindex][Class_Identifier], 64, ClassData[classindex][Class_Identifier]);
ClassDataCache[classindex][Class_Enabled] = ClassData[classindex][Class_Enabled]; ClassDataCache[classindex][Class_Enabled] = ClassData[classindex][Class_Enabled];
ClassDataCache[classindex][Class_Team] = ClassData[classindex][Class_Team]; ClassDataCache[classindex][Class_Team] = ClassData[classindex][Class_Team];
ClassDataCache[classindex][Class_TeamDefault] = ClassData[classindex][Class_TeamDefault]; ClassDataCache[classindex][Class_TeamDefault] = ClassData[classindex][Class_TeamDefault];
@ -816,6 +825,7 @@ bool:ClassReloadPlayerCache(client, classindex, cachetype = ZR_CLASS_CACHE_MODIF
case ZR_CLASS_CACHE_ORIGINAL: case ZR_CLASS_CACHE_ORIGINAL:
{ {
/* General */ /* General */
strcopy(ClassPlayerCache[client][Class_Identifier], 64, ClassData[classindex][Class_Identifier]);
ClassPlayerCache[client][Class_Enabled] = ClassData[classindex][Class_Enabled]; ClassPlayerCache[client][Class_Enabled] = ClassData[classindex][Class_Enabled];
ClassPlayerCache[client][Class_Team] = ClassData[classindex][Class_Team]; ClassPlayerCache[client][Class_Team] = ClassData[classindex][Class_Team];
ClassPlayerCache[client][Class_TeamDefault] = ClassData[classindex][Class_TeamDefault]; ClassPlayerCache[client][Class_TeamDefault] = ClassData[classindex][Class_TeamDefault];
@ -859,6 +869,7 @@ bool:ClassReloadPlayerCache(client, classindex, cachetype = ZR_CLASS_CACHE_MODIF
case ZR_CLASS_CACHE_MODIFIED: case ZR_CLASS_CACHE_MODIFIED:
{ {
/* General */ /* General */
strcopy(ClassPlayerCache[client][Class_Identifier], 64, ClassDataCache[classindex][Class_Identifier]);
ClassPlayerCache[client][Class_Enabled] = ClassDataCache[classindex][Class_Enabled]; ClassPlayerCache[client][Class_Enabled] = ClassDataCache[classindex][Class_Enabled];
ClassPlayerCache[client][Class_Team] = ClassDataCache[classindex][Class_Team]; ClassPlayerCache[client][Class_Team] = ClassDataCache[classindex][Class_Team];
ClassPlayerCache[client][Class_TeamDefault] = ClassDataCache[classindex][Class_TeamDefault]; ClassPlayerCache[client][Class_TeamDefault] = ClassDataCache[classindex][Class_TeamDefault];
@ -1042,20 +1053,25 @@ ClassRestoreNextIndexes(client, excludeTeam = -1)
} }
/** /**
* Sets default class indexes for each team on all players, or a single player * Sets default class identifiers for each team on all players, or a single player
* if specified. * if specified.
* *
* @param client Optional. The client index. If specified, cookies are used. * @param client Optional. The client index. If specified, cookies are used.
*/ */
ClassClientSetDefaultIndexes(client = -1) ClassClientSetDefaultIdentifiers(client = -1)
{ {
new bool:clientvalid = ZRIsClientValid(client); new bool:clientvalid = ZRIsClientValid(client);
new filter[ClassFilter]; new filter[ClassFilter];
new filter_valid[ClassFilter];
new bool:saveclasses = GetConVarBool(g_hCvarsList[CVAR_CLASSES_SAVE]); new bool:saveclasses = GetConVarBool(g_hCvarsList[CVAR_CLASSES_SAVE]);
new zombieindex; char zombie_ident[64];
new humanindex; char human_ident[64];
new adminindex; char admin_ident[64];
new zombieindex = -1;
new humanindex = -1;
new adminindex = -1;
new bool:haszombie; new bool:haszombie;
new bool:hashuman; new bool:hashuman;
@ -1066,17 +1082,26 @@ ClassClientSetDefaultIndexes(client = -1)
* SETUP CLASS FILTER * SETUP CLASS FILTER
*/ */
// Do not require class to be enabled.
// Disabled classes should not be deleted from the client cookies.
// We'll manually check if the class is disabled later.
filter_valid[ClassFilter_IgnoreEnabled] = true;
// Do not require any class flags to be set. // Do not require any class flags to be set.
filter[ClassFilter_RequireFlags] = 0; filter[ClassFilter_RequireFlags] = 0;
filter_valid[ClassFilter_RequireFlags] = 0;
// Set filter to hide mother zombie classes. // Set filter to hide mother zombie classes.
filter[ClassFilter_DenyFlags] = ZR_CLASS_FLAG_MOTHER_ZOMBIE; filter[ClassFilter_DenyFlags] = ZR_CLASS_FLAG_MOTHER_ZOMBIE;
filter_valid[ClassFilter_DenyFlags] = ZR_CLASS_FLAG_MOTHER_ZOMBIE;
// Set filter to also hide admin-only classes if not admin. // Set filter to also hide admin-only classes if not admin.
filter[ClassFilter_DenyFlags] += !ZRIsClientAdmin(client) ? ZR_CLASS_FLAG_ADMIN_ONLY : 0; filter[ClassFilter_DenyFlags] += !ZRIsClientAdmin(client) ? ZR_CLASS_FLAG_ADMIN_ONLY : 0;
filter_valid[ClassFilter_DenyFlags] += !ZRIsClientAdmin(client) ? ZR_CLASS_FLAG_ADMIN_ONLY : 0;
// Specify client so it can check group permissions. // Specify client so it can check group permissions.
filter[ClassFilter_Client] = client; filter[ClassFilter_Client] = client;
filter_valid[ClassFilter_Client] = client;
/* /*
@ -1089,61 +1114,61 @@ ClassClientSetDefaultIndexes(client = -1)
// Get cookie indexes if enabled. // Get cookie indexes if enabled.
if (saveclasses) if (saveclasses)
{ {
zombieindex = CookiesGetInt(client, g_hClassCookieClassSelected[ZR_CLASS_TEAM_ZOMBIES]); GetClientCookie(client, g_hClassCookieClassSelected[ZR_CLASS_TEAM_ZOMBIES], zombie_ident, sizeof(zombie_ident));
humanindex = CookiesGetInt(client, g_hClassCookieClassSelected[ZR_CLASS_TEAM_HUMANS]); GetClientCookie(client, g_hClassCookieClassSelected[ZR_CLASS_TEAM_HUMANS], human_ident, sizeof(human_ident));
adminindex = CookiesGetInt(client, g_hClassCookieClassSelected[ZR_CLASS_TEAM_ADMINS]); GetClientCookie(client, g_hClassCookieClassSelected[ZR_CLASS_TEAM_ADMINS], admin_ident, sizeof(admin_ident));
}
else zombieindex = ClassGetIndexByIdentifier(zombie_ident);
{ humanindex = ClassGetIndexByIdentifier(human_ident);
// Do not use indexes in cookies. Set invalid values so it will adminindex = ClassGetIndexByIdentifier(admin_ident);
// fall back to default class.
zombieindex = 0;
humanindex = 0;
adminindex = 0;
} }
// Note: When class indexes are set on cookies, they're incremented by // Check if class identifiers are set and that the client pass the filter.
// one so zero means no class set and will result in a invalid
// class index when restored.
// Check if class indexes are set and that the client pass the filter.
// Also check that the saved class' team id match with the loaded class. // Also check that the saved class' team id match with the loaded class.
// If not, fall back to default class indexes. Otherwise substract // If not, fall back to default class indexes.
// index by one. if (zombieindex < 0 ||
if (zombieindex <= 0 || !ClassTeamCompare(zombieindex, ZR_CLASS_TEAM_ZOMBIES) ||
!ClassTeamCompare(zombieindex - 1, ZR_CLASS_TEAM_ZOMBIES) || !ClassFilterMatch(zombieindex, filter_valid))
!ClassFilterMatch(zombieindex - 1, filter))
{ {
zombieindex = ClassGetDefaultSpawnClass(ZR_CLASS_TEAM_ZOMBIES, filter); zombieindex = ClassGetDefaultSpawnClass(ZR_CLASS_TEAM_ZOMBIES, filter);
} }
else else
{ {
zombieindex--;
haszombie = true; haszombie = true;
if (!ClassIsEnabled(zombieindex))
{
zombieindex = ClassGetDefaultSpawnClass(ZR_CLASS_TEAM_ZOMBIES, filter);
}
} }
if (humanindex <= 0 || if (humanindex < 0 ||
!ClassTeamCompare(humanindex - 1, ZR_CLASS_TEAM_HUMANS) || !ClassTeamCompare(humanindex, ZR_CLASS_TEAM_HUMANS) ||
!ClassFilterMatch(humanindex - 1, filter)) !ClassFilterMatch(humanindex, filter_valid))
{ {
humanindex = ClassGetDefaultSpawnClass(ZR_CLASS_TEAM_HUMANS, filter); humanindex = ClassGetDefaultSpawnClass(ZR_CLASS_TEAM_HUMANS, filter);
} }
else else
{ {
humanindex--;
hashuman = true; hashuman = true;
if (!ClassIsEnabled(humanindex))
{
humanindex = ClassGetDefaultSpawnClass(ZR_CLASS_TEAM_HUMANS, filter);
}
} }
if (adminindex <= 0 || if (adminindex < 0 ||
!ClassTeamCompare(adminindex - 1, ZR_CLASS_TEAM_ADMINS) || !ClassTeamCompare(adminindex, ZR_CLASS_TEAM_ADMINS) ||
!ClassFilterMatch(adminindex - 1, filter)) !ClassFilterMatch(adminindex, filter_valid))
{ {
adminindex = ClassGetDefaultSpawnClass(ZR_CLASS_TEAM_ADMINS, filter); adminindex = ClassGetDefaultSpawnClass(ZR_CLASS_TEAM_ADMINS, filter);
} }
else else
{ {
adminindex--;
hasadmin = true; hasadmin = true;
if (!ClassIsEnabled(adminindex))
{
adminindex = ClassGetDefaultSpawnClass(ZR_CLASS_TEAM_ADMINS, filter);
}
} }
} }
else else
@ -1211,17 +1236,20 @@ ClassClientSetDefaultIndexes(client = -1)
// Save indexes in cookies if enabled, and not already saved. // Save indexes in cookies if enabled, and not already saved.
if (saveclasses) if (saveclasses)
{ {
if (!haszombie) if (!haszombie && zombieindex != -1)
{ {
CookiesSetInt(client, g_hClassCookieClassSelected[ZR_CLASS_TEAM_ZOMBIES], zombieindex + 1); ClassGetIdentifier(zombieindex, zombie_ident, sizeof(zombie_ident));
SetClientCookie(client, g_hClassCookieClassSelected[ZR_CLASS_TEAM_ZOMBIES], zombie_ident);
} }
if (!hashuman) if (!hashuman && humanindex != -1)
{ {
CookiesSetInt(client, g_hClassCookieClassSelected[ZR_CLASS_TEAM_HUMANS], humanindex + 1); ClassGetIdentifier(humanindex, human_ident, sizeof(human_ident));
SetClientCookie(client, g_hClassCookieClassSelected[ZR_CLASS_TEAM_HUMANS], human_ident);
} }
if (!hasadmin) if (!hasadmin && adminindex != -1)
{ {
CookiesSetInt(client, g_hClassCookieClassSelected[ZR_CLASS_TEAM_ADMINS], adminindex + 1); ClassGetIdentifier(adminindex, admin_ident, sizeof(admin_ident));
SetClientCookie(client, g_hClassCookieClassSelected[ZR_CLASS_TEAM_ADMINS], admin_ident);
} }
} }
} }
@ -1350,6 +1378,10 @@ ClassDumpData(index, cachetype, String:buffer[], maxlen)
cellcount += StrCat(buffer, maxlen, format_buffer); cellcount += StrCat(buffer, maxlen, format_buffer);
cellcount += StrCat(buffer, maxlen, "-------------------------------------------------------------------------------\n"); cellcount += StrCat(buffer, maxlen, "-------------------------------------------------------------------------------\n");
ClassGetIdentifier(index, format_buffer, sizeof(format_buffer), cachetype);
Format(attribute, sizeof(attribute), "identifier: \"%s\"\n", format_buffer);
cellcount += StrCat(buffer, maxlen, attribute);
Format(attribute, sizeof(attribute), "enabled: \"%d\"\n", ClassIsEnabled(index, cachetype)); Format(attribute, sizeof(attribute), "enabled: \"%d\"\n", ClassIsEnabled(index, cachetype));
cellcount += StrCat(buffer, maxlen, attribute); cellcount += StrCat(buffer, maxlen, attribute);

View File

@ -476,7 +476,7 @@ VolClassEditApply(client, dataIndex)
case ClassEditMode_Name: case ClassEditMode_Name:
{ {
// Cache volume attributes. // Cache volume attributes.
new classindex = ClassGetIndex(VolClassEditData[dataIndex][VolClassEdit_ClassName]); new classindex = ClassGetIndexByName(VolClassEditData[dataIndex][VolClassEdit_ClassName]);
// Save player's selected class. // Save player's selected class.
VolClassEditSelectedClass[client] = ClassGetActiveIndex(client); VolClassEditSelectedClass[client] = ClassGetActiveIndex(client);