diff --git a/src/include/zombiereloaded.inc b/src/include/zombiereloaded.inc index 52f08be..4382fae 100644 --- a/src/include/zombiereloaded.inc +++ b/src/include/zombiereloaded.inc @@ -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"); diff --git a/src/include/zr/class.zr.inc b/src/include/zr/class.zr.inc index ac86996..e46d1f1 100644 --- a/src/include/zr/class.zr.inc +++ b/src/include/zr/class.zr.inc @@ -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. * diff --git a/src/zr/api/class.api.inc b/src/zr/api/class.api.inc index 9f2c307..f7f539e 100644 --- a/src/zr/api/class.api.inc +++ b/src/zr/api/class.api.inc @@ -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; +} \ No newline at end of file diff --git a/src/zr/cookies.inc b/src/zr/cookies.inc index cd31089..8a24632 100644 --- a/src/zr/cookies.inc +++ b/src/zr/cookies.inc @@ -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]; diff --git a/src/zr/playerclasses/attributes.inc b/src/zr/playerclasses/attributes.inc index d4ac13e..47d6640 100644 --- a/src/zr/playerclasses/attributes.inc +++ b/src/zr/playerclasses/attributes.inc @@ -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, diff --git a/src/zr/playerclasses/classcommands.inc b/src/zr/playerclasses/classcommands.inc index 3a27fed..ce24231 100644 --- a/src/zr/playerclasses/classcommands.inc +++ b/src/zr/playerclasses/classcommands.inc @@ -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 "); 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 [is_multiplier]"); + RegConsoleCmd("zr_class_modify", ClassModifyCommand, "Modify class data on one or more classes. Usage: zr_class_modify [is_multiplier]"); RegConsoleCmd("zr_class_set_multiplier", ClassSetMultiplierCommand, "Sets the multiplier on a class attribute. Usage: zr_class_set_multiplier <\"zombies\"|\"humans\"> "); RegConsoleCmd("zr_class_reload", ClassReloadCommand, "Refreshes the player cache and reloads class attributes on one or more players. Usage: zr_class_reload "); } @@ -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)) diff --git a/src/zr/playerclasses/classevents.inc b/src/zr/playerclasses/classevents.inc index 49c2858..674378e 100644 --- a/src/zr/playerclasses/classevents.inc +++ b/src/zr/playerclasses/classevents.inc @@ -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)) diff --git a/src/zr/playerclasses/classmenus.inc b/src/zr/playerclasses/classmenus.inc index f477e31..04f2442 100644 --- a/src/zr/playerclasses/classmenus.inc +++ b/src/zr/playerclasses/classmenus.inc @@ -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); diff --git a/src/zr/playerclasses/filtertools.inc b/src/zr/playerclasses/filtertools.inc index 29bbfbd..e6a9694 100644 --- a/src/zr/playerclasses/filtertools.inc +++ b/src/zr/playerclasses/filtertools.inc @@ -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)) diff --git a/src/zr/playerclasses/playerclasses.inc b/src/zr/playerclasses/playerclasses.inc index 178a33e..c52c8e1 100644 --- a/src/zr/playerclasses/playerclasses.inc +++ b/src/zr/playerclasses/playerclasses.inc @@ -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); diff --git a/src/zr/volfeatures/volclassedit.inc b/src/zr/volfeatures/volclassedit.inc index 25748a3..ee107c1 100644 --- a/src/zr/volfeatures/volclassedit.inc +++ b/src/zr/volfeatures/volclassedit.inc @@ -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);