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_SelectClientClass");
MarkNativeAsOptional("ZR_GetClassByName");
MarkNativeAsOptional("ZR_GetClassByIdentifier");
MarkNativeAsOptional("ZR_GetClassDisplayName");
MarkNativeAsOptional("ZR_GetClassIdentifier");
MarkNativeAsOptional("ZR_IsClientZombie");
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);
/**
* 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.
*

View File

@ -39,7 +39,9 @@ APIClassInit()
CreateNative("ZR_GetZombieClass", APIGetZombieClass);
CreateNative("ZR_SelectClientClass", APISelectClientClass);
CreateNative("ZR_GetClassByName", APIGetClassByName);
CreateNative("ZR_GetClassByIdentifier", APIGetClassByIdentifier);
CreateNative("ZR_GetClassDisplayName", APIGetClassDisplayName);
CreateNative("ZR_GetClassIdentifier", APIGetClassIdentifier);
}
/**
@ -148,7 +150,34 @@ public APIGetClassByName(Handle:plugin, numParams)
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;
}
/**
* 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 cookie The handle to the cookie.
*/
bool:CookiesGetClientCookieBool(client, Handle:cookie)
stock bool:CookiesGetClientCookieBool(client, Handle:cookie)
{
// Get cookie string.
decl String:cookievalue[8];
@ -59,7 +59,7 @@ bool:CookiesGetClientCookieBool(client, Handle:cookie)
* @param cookie The handle to the cookie.
* @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.
decl String:strCookievalue[8];
@ -75,7 +75,7 @@ CookiesSetClientCookieBool(client, Handle:cookie, bool:cookievalue)
* @param client The client index.
* @param cookie The handle to the cookie.
*/
CookiesGetInt(client, Handle:cookie)
stock CookiesGetInt(client, Handle:cookie)
{
decl String:strValue[16];
strValue[0] = 0;
@ -91,7 +91,7 @@ CookiesGetInt(client, Handle:cookie)
* @param cookie The handle to the cookie.
* @param value The value to set.
*/
CookiesSetInt(client, Handle:cookie, value)
stock CookiesSetInt(client, Handle:cookie, value)
{
// Convert value to string.
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.
*
@ -1401,7 +1436,8 @@ stock ClassDataTypes:ClassGetAttributeType(attributeflag)
}
// String.
case ZR_CLASS_GROUP,
case ZR_CLASS_IDENTIFIER,
ZR_CLASS_GROUP,
ZR_CLASS_NAME,
ZR_CLASS_DESCRIPTION,
ZR_CLASS_MODEL_PATH,

View File

@ -33,7 +33,7 @@ ClassOnCommandsCreate()
// 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_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_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;
}
decl String:classname[64];
decl String:identifier[64];
decl String:attributename[128];
decl String:value[256];
decl String:ismultiplier[4];
@ -313,7 +313,7 @@ public Action:ClassModifyCommand(client, argc)
classlist = CreateArray();
// Get command arguments.
GetCmdArg(1, classname, sizeof(classname));
GetCmdArg(1, identifier, sizeof(identifier));
GetCmdArg(2, attributename, sizeof(attributename));
GetCmdArg(3, value, sizeof(value));
@ -342,36 +342,36 @@ public Action:ClassModifyCommand(client, argc)
// Get attribute data type.
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.
if (StrEqual(classname, "all", false))
if (StrEqual(identifier, "all", false))
{
listresult = ClassAddToArray(classlist);
isgroup = true;
}
else if (StrEqual(classname, "humans", false))
else if (StrEqual(identifier, "humans", false))
{
listresult = ClassAddToArray(classlist, ZR_CLASS_TEAM_HUMANS);
isgroup = true;
}
else if (StrEqual(classname, "zombies", false))
else if (StrEqual(identifier, "zombies", false))
{
listresult = ClassAddToArray(classlist, ZR_CLASS_TEAM_ZOMBIES);
isgroup = true;
}
else if (StrEqual(classname, "admins", false))
else if (StrEqual(identifier, "admins", false))
{
listresult = ClassAddToArray(classlist, ZR_CLASS_TEAM_ADMINS);
isgroup = true;
}
// Check if classname is a group.
// Check if identifier is a group.
if (isgroup)
{
// Check if the list is valid.
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);
return Plugin_Handled;
@ -430,7 +430,7 @@ public Action:ClassModifyCommand(client, argc)
else
{
// It's a single class.
classindex = ClassGetIndex(classname);
classindex = ClassGetIndexByIdentifier(identifier);
// Validate classindex.
if (!ClassValidateIndex(classindex))

View File

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

View File

@ -265,6 +265,7 @@ ClassMenuSelect(client, teamid)
new classindex;
decl String:title[MENU_LINE_TITLE_LENGTH];
decl String:classident[64];
decl String:classname[MENU_LINE_REG_LENGTH];
decl String:description[MENU_LINE_BIG_LENGTH];
decl String:menuitem[MENU_LINE_HUGE_LENGTH];
@ -319,12 +320,13 @@ ClassMenuSelect(client, teamid)
{
// Get index, name and description.
classindex = GetArrayCell(classarray, i);
ClassGetIdentifier(classindex, classident, sizeof(classident));
ClassGetName(classindex, classname, sizeof(classname), 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.
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)
{
decl String:className[MENU_LINE_REG_LENGTH];
decl String:classIdent[MENU_LINE_REG_LENGTH];
new classIndex;
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:
{
// 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.
classIndex = ClassGetIndex(className);
classIndex = ClassGetIndexByIdentifier(classIdent);
// Select (and eventually apply) class.
ClassSelectClientClass(client, classIndex);

View File

@ -105,6 +105,44 @@ stock ClassValidateAttributes(classindex, bool:logErrors = false)
{
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.
new team = ClassData[classindex][Class_Team];
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.
* @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];
@ -735,6 +773,42 @@ stock ClassGetIndex(const String:name[], cachetype = ZR_CLASS_CACHE_MODIFIED)
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.
* 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)
{
decl String:classname[64];
decl String:classident[64];
new classindex;
// 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:
{
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:
{
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:
{
GetConVarString(g_hCvarsList[CVAR_CLASSES_DEFAULT_ADMIN_MODE], classname, sizeof(classname));
GetConVarString(g_hCvarsList[CVAR_CLASSES_DEFAULT_ADMIN_MODE], classident, sizeof(classident));
}
default:
{
@ -1327,10 +1401,10 @@ stock ClassGetDefaultSpawnClass(teamid, filter[ClassFilter] = ClassNoSpecialClas
}
// 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.
if (StrEqual(classname, "random", false))
if (StrEqual(classident, "random", false))
{
// Get a list of all classes with the specified team ID. Deny
// classes with special flags.
@ -1354,7 +1428,7 @@ stock ClassGetDefaultSpawnClass(teamid, filter[ClassFilter] = ClassNoSpecialClas
// The user set a spesific class.
// 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.
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.
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.
if (ClassValidateIndex(classindex))

View File

@ -137,6 +137,7 @@
/**
* @section Attribute limit values. Used when validating.
*/
#define ZR_CLASS_IDENTIFIER_MIN 1
#define ZR_CLASS_TEAM_MIN 0
#define ZR_CLASS_TEAM_MAX 2
#define ZR_CLASS_FLAGS_MIN 0
@ -186,37 +187,38 @@
/**
* @section Class attribute flags.
*/
#define ZR_CLASS_ENABLED (1<<0)
#define ZR_CLASS_TEAM (1<<1)
#define ZR_CLASS_TEAM_DEFAULT (1<<2)
#define ZR_CLASS_FLAGS (1<<3)
#define ZR_CLASS_GROUP (1<<4)
#define ZR_CLASS_SM_FLAGS (1<<5)
#define ZR_CLASS_NAME (1<<6)
#define ZR_CLASS_DESCRIPTION (1<<7)
#define ZR_CLASS_MODEL_PATH (1<<8)
#define ZR_CLASS_MODEL_SKIN_INDEX (1<<9)
#define ZR_CLASS_ALPHA_INITIAL (1<<10)
#define ZR_CLASS_ALPHA_DAMAGED (1<<11)
#define ZR_CLASS_ALPHA_DAMAGE (1<<12)
#define ZR_CLASS_OVERLAY_PATH (1<<13)
#define ZR_CLASS_NVGS (1<<14)
#define ZR_CLASS_FOV (1<<15)
#define ZR_CLASS_HAS_NAPALM (1<<16)
#define ZR_CLASS_NAPALM_TIME (1<<17)
#define ZR_CLASS_IMMUNITY_MODE (1<<18)
#define ZR_CLASS_IMMUNITY_AMOUNT (1<<19)
#define ZR_CLASS_IMMUNITY_COOLDOWN (1<<20)
#define ZR_CLASS_NO_FALL_DAMAGE (1<<21)
#define ZR_CLASS_HEALTH (1<<22)
#define ZR_CLASS_HEALTH_REGEN_INTERVAL (1<<23)
#define ZR_CLASS_HEALTH_REGEN_AMOUNT (1<<24)
#define ZR_CLASS_HEALTH_INFECT_GAIN (1<<25)
#define ZR_CLASS_KILL_BONUS (1<<26)
#define ZR_CLASS_SPEED (1<<27)
#define ZR_CLASS_KNOCKBACK (1<<28)
#define ZR_CLASS_JUMP_HEIGHT (1<<29)
#define ZR_CLASS_JUMP_DISTANCE (1<<30)
#define ZR_CLASS_IDENTIFIER (1<<0)
#define ZR_CLASS_ENABLED (1<<1)
#define ZR_CLASS_TEAM (1<<2)
#define ZR_CLASS_TEAM_DEFAULT (1<<3)
#define ZR_CLASS_FLAGS (1<<4)
#define ZR_CLASS_GROUP (1<<5)
#define ZR_CLASS_SM_FLAGS (1<<6)
#define ZR_CLASS_NAME (1<<7)
#define ZR_CLASS_DESCRIPTION (1<<8)
#define ZR_CLASS_MODEL_PATH (1<<9)
#define ZR_CLASS_MODEL_SKIN_INDEX (1<<10)
#define ZR_CLASS_ALPHA_INITIAL (1<<11)
#define ZR_CLASS_ALPHA_DAMAGED (1<<12)
#define ZR_CLASS_ALPHA_DAMAGE (1<<13)
#define ZR_CLASS_OVERLAY_PATH (1<<14)
#define ZR_CLASS_NVGS (1<<15)
#define ZR_CLASS_FOV (1<<16)
#define ZR_CLASS_HAS_NAPALM (1<<17)
#define ZR_CLASS_NAPALM_TIME (1<<18)
#define ZR_CLASS_IMMUNITY_MODE (1<<19)
#define ZR_CLASS_IMMUNITY_AMOUNT (1<<20)
#define ZR_CLASS_IMMUNITY_COOLDOWN (1<<21)
#define ZR_CLASS_NO_FALL_DAMAGE (1<<22)
#define ZR_CLASS_HEALTH (1<<23)
#define ZR_CLASS_HEALTH_REGEN_INTERVAL (1<<24)
#define ZR_CLASS_HEALTH_REGEN_AMOUNT (1<<25)
#define ZR_CLASS_HEALTH_INFECT_GAIN (1<<26)
#define ZR_CLASS_KILL_BONUS (1<<27)
#define ZR_CLASS_SPEED (1<<28)
#define ZR_CLASS_KNOCKBACK (1<<29)
#define ZR_CLASS_JUMP_HEIGHT (1<<30)
#define ZR_CLASS_JUMP_DISTANCE (1<<31)
/**
* @endsection
*/
@ -243,6 +245,7 @@
enum ClassAttributes
{
/* General */
String:Class_Identifier[64],
bool:Class_Enabled,
Class_Team,
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);
}
new String:section_name[64];
new String:name[64];
new String:group[64];
new String:sm_flags[64];
@ -583,6 +587,10 @@ ClassLoad()
break;
}
/* Class identifier = section name */
KvGetSectionName(kvClassData, section_name, sizeof(section_name));
strcopy(ClassData[ClassCount][Class_Identifier], 64, section_name);
/* General */
ClassData[ClassCount][Class_Enabled] = ConfigKvGetStringBool(kvClassData, "enabled", ZR_CLASS_DEFAULT_ENABLED);
ClassData[ClassCount][Class_Team] = KvGetNum(kvClassData, "team", ZR_CLASS_DEFAULT_TEAM);
@ -748,6 +756,7 @@ bool:ClassReloadDataCache()
for (new classindex = 0; classindex < ClassCount; classindex++)
{
/* General */
strcopy(ClassDataCache[classindex][Class_Identifier], 64, ClassData[classindex][Class_Identifier]);
ClassDataCache[classindex][Class_Enabled] = ClassData[classindex][Class_Enabled];
ClassDataCache[classindex][Class_Team] = ClassData[classindex][Class_Team];
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:
{
/* General */
strcopy(ClassPlayerCache[client][Class_Identifier], 64, ClassData[classindex][Class_Identifier]);
ClassPlayerCache[client][Class_Enabled] = ClassData[classindex][Class_Enabled];
ClassPlayerCache[client][Class_Team] = ClassData[classindex][Class_Team];
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:
{
/* General */
strcopy(ClassPlayerCache[client][Class_Identifier], 64, ClassDataCache[classindex][Class_Identifier]);
ClassPlayerCache[client][Class_Enabled] = ClassDataCache[classindex][Class_Enabled];
ClassPlayerCache[client][Class_Team] = ClassDataCache[classindex][Class_Team];
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.
*
* @param client Optional. The client index. If specified, cookies are used.
*/
ClassClientSetDefaultIndexes(client = -1)
ClassClientSetDefaultIdentifiers(client = -1)
{
new bool:clientvalid = ZRIsClientValid(client);
new filter[ClassFilter];
new filter_valid[ClassFilter];
new bool:saveclasses = GetConVarBool(g_hCvarsList[CVAR_CLASSES_SAVE]);
new zombieindex;
new humanindex;
new adminindex;
char zombie_ident[64];
char human_ident[64];
char admin_ident[64];
new zombieindex = -1;
new humanindex = -1;
new adminindex = -1;
new bool:haszombie;
new bool:hashuman;
@ -1066,17 +1082,26 @@ ClassClientSetDefaultIndexes(client = -1)
* 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.
filter[ClassFilter_RequireFlags] = 0;
filter_valid[ClassFilter_RequireFlags] = 0;
// Set filter to hide mother zombie classes.
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.
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.
filter[ClassFilter_Client] = client;
filter_valid[ClassFilter_Client] = client;
/*
@ -1089,61 +1114,61 @@ ClassClientSetDefaultIndexes(client = -1)
// 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;
GetClientCookie(client, g_hClassCookieClassSelected[ZR_CLASS_TEAM_ZOMBIES], zombie_ident, sizeof(zombie_ident));
GetClientCookie(client, g_hClassCookieClassSelected[ZR_CLASS_TEAM_HUMANS], human_ident, sizeof(human_ident));
GetClientCookie(client, g_hClassCookieClassSelected[ZR_CLASS_TEAM_ADMINS], admin_ident, sizeof(admin_ident));
zombieindex = ClassGetIndexByIdentifier(zombie_ident);
humanindex = ClassGetIndexByIdentifier(human_ident);
adminindex = ClassGetIndexByIdentifier(admin_ident);
}
// 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.
// Check if class indexes are set and that the client pass the filter.
// Check if class identifiers are set and that the client pass the filter.
// Also check that the saved class' team id match with the loaded class.
// If not, fall back to default class indexes. Otherwise substract
// index by one.
if (zombieindex <= 0 ||
!ClassTeamCompare(zombieindex - 1, ZR_CLASS_TEAM_ZOMBIES) ||
!ClassFilterMatch(zombieindex - 1, filter))
// If not, fall back to default class indexes.
if (zombieindex < 0 ||
!ClassTeamCompare(zombieindex, ZR_CLASS_TEAM_ZOMBIES) ||
!ClassFilterMatch(zombieindex, filter_valid))
{
zombieindex = ClassGetDefaultSpawnClass(ZR_CLASS_TEAM_ZOMBIES, filter);
}
else
{
zombieindex--;
haszombie = true;
if (!ClassIsEnabled(zombieindex))
{
zombieindex = ClassGetDefaultSpawnClass(ZR_CLASS_TEAM_ZOMBIES, filter);
}
}
if (humanindex <= 0 ||
!ClassTeamCompare(humanindex - 1, ZR_CLASS_TEAM_HUMANS) ||
!ClassFilterMatch(humanindex - 1, filter))
if (humanindex < 0 ||
!ClassTeamCompare(humanindex, ZR_CLASS_TEAM_HUMANS) ||
!ClassFilterMatch(humanindex, filter_valid))
{
humanindex = ClassGetDefaultSpawnClass(ZR_CLASS_TEAM_HUMANS, filter);
}
else
{
humanindex--;
hashuman = true;
if (!ClassIsEnabled(humanindex))
{
humanindex = ClassGetDefaultSpawnClass(ZR_CLASS_TEAM_HUMANS, filter);
}
}
if (adminindex <= 0 ||
!ClassTeamCompare(adminindex - 1, ZR_CLASS_TEAM_ADMINS) ||
!ClassFilterMatch(adminindex - 1, filter))
if (adminindex < 0 ||
!ClassTeamCompare(adminindex, ZR_CLASS_TEAM_ADMINS) ||
!ClassFilterMatch(adminindex, filter_valid))
{
adminindex = ClassGetDefaultSpawnClass(ZR_CLASS_TEAM_ADMINS, filter);
}
else
{
adminindex--;
hasadmin = true;
if (!ClassIsEnabled(adminindex))
{
adminindex = ClassGetDefaultSpawnClass(ZR_CLASS_TEAM_ADMINS, filter);
}
}
}
else
@ -1211,17 +1236,20 @@ ClassClientSetDefaultIndexes(client = -1)
// Save indexes in cookies if enabled, and not already saved.
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, "-------------------------------------------------------------------------------\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));
cellcount += StrCat(buffer, maxlen, attribute);

View File

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