diff --git a/src/zombiereloaded.sp b/src/zombiereloaded.sp index 14fea07..adf6def 100644 --- a/src/zombiereloaded.sp +++ b/src/zombiereloaded.sp @@ -140,6 +140,7 @@ public OnPluginStart() CommandsInit(); WeaponsInit(); EventInit(); + VolInit(); } /** diff --git a/src/zr/playerclasses/filtertools.inc b/src/zr/playerclasses/filtertools.inc index 79f8656..9099ec1 100644 --- a/src/zr/playerclasses/filtertools.inc +++ b/src/zr/playerclasses/filtertools.inc @@ -292,6 +292,168 @@ stock ClassValidateAttributes(classindex) return flags; } +/** + * Validates a set of editable attributes. + * + * @param attributes Attribute set to validate. + * @return 0 if successful, or a bit field (positivie number) of + * failed attributes. + */ +stock ClassValidateEditableAttributes(attributes[ClassEditableAttributes]) +{ + new flags; + + // Alpha initial. + new alphaInitial = attributes[ClassEdit_AlphaInitial]; + if (alphaInitial >= 0) + { + if (!(alphaInitial >= ZR_CLASS_ALPHA_INITIAL_MIN && alphaInitial <= ZR_CLASS_ALPHA_INITIAL_MAX)) + { + flags += ZR_CLASS_ALPHA_INITIAL; + } + } + + // Alpha damaged. + new alphaDamaged = attributes[ClassEdit_AlphaDamaged]; + if (alphaDamaged >= 0) + { + if (!(alphaDamaged >= ZR_CLASS_ALPHA_DAMAGED_MIN && alphaDamaged <= ZR_CLASS_ALPHA_DAMAGED_MAX)) + { + flags += ZR_CLASS_ALPHA_DAMAGED; + } + } + + // Alpha damage. + new alphaDamage = attributes[ClassEdit_AlphaDamage]; + if (alphaDamage >= 0) + { + if (!(alphaDamage >= ZR_CLASS_ALPHA_DAMAGE_MIN && alphaDamage <= ZR_CLASS_ALPHA_DAMAGE_MAX)) + { + flags += ZR_CLASS_ALPHA_DAMAGE; + } + } + + // Overlay. + if (!StrEqual(attributes[ClassEdit_OverlayPath], "nochange", false)) + { + decl String:overlay_path[PLATFORM_MAX_PATH]; + decl String:overlay[PLATFORM_MAX_PATH]; + if (strcopy(overlay_path, sizeof(overlay_path), attributes[ClassEdit_OverlayPath]) > 0) + { + // Check if the file exists. + Format(overlay, sizeof(overlay), "materials/%s.vmt", overlay_path); + if (!FileExists(overlay)) + { + flags += ZR_CLASS_OVERLAY_PATH; + } + } + } + + // Fov. + new fov = attributes[ClassEdit_Fov]; + if (fov >= 0) + { + if (!(fov >= ZR_CLASS_FOV_MIN && fov <= ZR_CLASS_FOV_MAX)) + { + flags += ZR_CLASS_FOV; + } + } + + // Napalm time. + new Float:napalmTime = attributes[ClassEdit_NapalmTime]; + if (napalmTime >= 0.0) + { + if (!(napalmTime >= ZR_CLASS_NAPALM_TIME_MIN && napalmTime <= ZR_CLASS_NAPALM_TIME_MAX)) + { + flags += ZR_CLASS_NAPALM_TIME; + } + } + + // Immunity mode not implemented yet. + + // Health regen interval. + new Float:healthRegenInterval = attributes[ClassEdit_RegenInterval]; + if (healthRegenInterval >= 0.0) + { + if (!(healthRegenInterval >= ZR_CLASS_REGEN_INTERVAL_MIN && healthRegenInterval <= ZR_CLASS_REGEN_INTERVAL_MAX)) + { + flags += ZR_CLASS_REGEN_INTERVAL; + } + } + + // Health regen amount. + new healthRegenAmount = attributes[ClassEdit_RegenAmount]; + if (healthRegenAmount >= 0) + { + if (!(healthRegenAmount >= ZR_CLASS_REGEN_AMOUNT_MIN && healthRegenAmount <= ZR_CLASS_REGEN_AMOUNT_MAX)) + { + flags += ZR_CLASS_REGEN_AMOUNT; + } + } + + // Infect gain. + new infectGain = attributes[ClassEdit_InfectGain]; + if (infectGain >= 0) + { + if (!(infectGain >= ZR_CLASS_HEALTH_INFECT_GAIN_MIN && infectGain <= ZR_CLASS_HEALTH_INFECT_GAIN_MAX)) + { + flags += ZR_CLASS_HEALTH_INFECT_GAIN; + } + } + + // Kill bonus. + new killBonus = attributes[ClassEdit_KillBonus]; + if (killBonus >= 0) + { + if (!(killBonus >= ZR_CLASS_KILL_BONUS_MIN && killBonus <= ZR_CLASS_KILL_BONUS_MAX)) + { + flags += ZR_CLASS_KILL_BONUS; + } + } + + // Speed. + new Float:speed = attributes[ClassEdit_Speed]; + if (speed >= 0) + { + if (!(speed >= ZR_CLASS_SPEED_MIN && speed <= ZR_CLASS_SPEED_MAX)) + { + flags += ZR_CLASS_SPEED; + } + } + + // Knock back. + new Float:knockBack = attributes[ClassEdit_KnockBack]; + if (knockBack > ZR_CLASS_KNOCKBACK_IGNORE) + { + if (!(knockBack >= ZR_CLASS_KNOCKBACK_MIN && knockBack <= ZR_CLASS_KNOCKBACK_MAX)) + { + flags += ZR_CLASS_KNOCKBACK; + } + } + + // Jump heigt. + new Float:jumpHeight = attributes[ClassEdit_JumpHeight]; + if (jumpHeight >= 0.0) + { + if (!(jumpHeight >= ZR_CLASS_JUMP_HEIGHT_MIN && jumpHeight <= ZR_CLASS_JUMP_HEIGHT_MAX)) + { + flags += ZR_CLASS_JUMP_HEIGHT; + } + } + + // Jump distance. + new Float:jumpDistance = attributes[ClassEdit_JumpDistance]; + if (jumpDistance >= 0.0) + { + if (!(jumpDistance >= ZR_CLASS_JUMP_DISTANCE_MIN && jumpDistance <= ZR_CLASS_JUMP_DISTANCE_MAX)) + { + flags += ZR_CLASS_JUMP_DISTANCE; + } + } + + return flags; +} + /** * Checks if the specified class index is a valid index. * diff --git a/src/zr/playerclasses/healthregen.inc b/src/zr/playerclasses/healthregen.inc index cb382b8..940965e 100644 --- a/src/zr/playerclasses/healthregen.inc +++ b/src/zr/playerclasses/healthregen.inc @@ -60,8 +60,8 @@ ClassHealthRegenStop(client) public Action:ClassHealthRegenTimer(Handle:timer, any:client) { - // Kill the timer if the player is dead. - if (!IsPlayerAlive(client)) + // Kill the timer if the player is dead or not in game. + if (!IsClientConnected(client) || !IsClientInGame(client) || !IsPlayerAlive(client)) { tHealthRegen[client] = INVALID_HANDLE; return Plugin_Stop; diff --git a/src/zr/playerclasses/playerclasses.inc b/src/zr/playerclasses/playerclasses.inc index ba13f15..3eb94c0 100644 --- a/src/zr/playerclasses/playerclasses.inc +++ b/src/zr/playerclasses/playerclasses.inc @@ -114,11 +114,11 @@ #define ZR_CLASS_DEFAULT_OVERLAY_PATH "overlays/zr/zvision" #define ZR_CLASS_DEFAULT_NVGS "no" #define ZR_CLASS_DEFAULT_FOV 90 -#define ZR_CLASS_DEFAULT_HAS_NAPALM "yes" +#define ZR_CLASS_DEFAULT_HAS_NAPALM "no" #define ZR_CLASS_DEFAULT_NAPALM_TIME 10.0 #define ZR_CLASS_DEFAULT_IMMUNITY_MODE ZR_CLASS_IMMUNITY_DISABLED #define ZR_CLASS_DEFAULT_IMMUNITY_AMOUNT 0.0 -#define ZR_CLASS_DEFAULT_NO_FALL_DAMAGE "on" +#define ZR_CLASS_DEFAULT_NO_FALL_DAMAGE "yes" #define ZR_CLASS_DEFAULT_HEALTH 6000 #define ZR_CLASS_DEFAULT_HEALTH_REGEN_INTERVAL 0.0 #define ZR_CLASS_DEFAULT_HEALTH_REGEN_AMOUNT 2 @@ -167,6 +167,7 @@ #define ZR_CLASS_SPEED_MAX 2000.0 #define ZR_CLASS_KNOCKBACK_MIN -30.0 #define ZR_CLASS_KNOCKBACK_MAX 30.0 +#define ZR_CLASS_KNOCKBACK_IGNORE -31.0 /** Used by class editor volumetric feature. */ #define ZR_CLASS_JUMP_HEIGHT_MIN 0.0 #define ZR_CLASS_JUMP_HEIGHT_MAX 5.0 #define ZR_CLASS_JUMP_DISTANCE_MIN 0.0 @@ -272,6 +273,48 @@ enum ClassAttributes Float:Class_JumpDistance } +/** + * Structure of class attributes that are allowed to be modified directly, + * while the player is alive. + * + * Note: This structure is also used as a mask to tell if a individual + * attribute should be ignored or not. Negative valueas usually indicate + * ignored attributes. Booleans are now ints so they can be negative. + * Strings have reserved keywords like "nochange" that indicate a ignored + * attribute. + */ +enum ClassEditableAttributes +{ + /* Model */ + ClassEdit_AlphaInitial = 0, + ClassEdit_AlphaDamaged, + ClassEdit_AlphaDamage, + + /* Hud */ + String:ClassEdit_OverlayPath[PLATFORM_MAX_PATH], + ClassEdit_Nvgs, + ClassEdit_Fov, + + /* Effects */ + ClassEdit_HasNapalm, + Float:ClassEdit_NapalmTime, + + /* Player behaviour */ + ClassEdit_ImmunityMode, + Float:ClassEdit_ImmunityAmount, + + ClassEdit_NoFallDamage, + Float:ClassEdit_RegenInterval, + ClassEdit_RegenAmount, + ClassEdit_InfectGain, + ClassEdit_KillBonus, + + Float:ClassEdit_Speed, + Float:ClassEdit_KnockBack, + Float:ClassEdit_JumpHeight, + Float:ClassEdit_JumpDistance +} + /** * Class attributes that support multipliers. */ @@ -533,7 +576,7 @@ ClassLoad(bool:keepMultipliers = false) /* Effects */ - ClassData[ClassCount][Class_HasNapalm] = ConfigKvGetStringBool(kvClassData, "have_napalm", ZR_CLASS_DEFAULT_HAS_NAPALM); + ClassData[ClassCount][Class_HasNapalm] = ConfigKvGetStringBool(kvClassData, "has_napalm", ZR_CLASS_DEFAULT_HAS_NAPALM); ClassData[ClassCount][Class_NapalmTime] = KvGetFloat(kvClassData, "napalm_time", ZR_CLASS_DEFAULT_NAPALM_TIME); diff --git a/src/zr/volfeatures/volclassedit.inc b/src/zr/volfeatures/volclassedit.inc new file mode 100644 index 0000000..79cf1a0 --- /dev/null +++ b/src/zr/volfeatures/volclassedit.inc @@ -0,0 +1,1411 @@ +/* + * ============================================================================ + * + * Zombie:Reloaded + * + * File: volclassedit.inc + * Type: Module + * Description: Class editor volumetric feature. + * + * Copyright (C) 2009 Greyscale, Richard Helgeby + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * ============================================================================ + */ + +#define VOL_CLASSNAME_SIZE 64 + +/** + * Empty attribute structure with all settings set to be ignored. + */ +new VolEmptyAttributes[ClassEditableAttributes] = { + -1, /** AlphaInitial */ + -1, /** AlphaDamaged */ + -1, /** AlphaDamage */ + + "nochange", /** OverlayPath */ + -1, /** Nvgs */ + -1, /** Fov */ + + -1, /** HasNapalm */ + -1.0, /** NapalmTime */ + -1, /** ImmunityMode */ + -1.0, /** ImmunityAmount */ + -1, /** NoFallDamage */ + + -1.0, /** RegenInterval */ + -1, /** RegenAmount */ + -1, /** InfectGain */ + -1, /** KillBonus */ + + -1.0, /** Speed */ + ZR_CLASS_KNOCKBACK_IGNORE, /** KnockBack */ + -1.0, /** JumpHeight */ + -1.0, /** JumpDistance */ +}; + +/** + * List of class edit modes. + */ +enum VolClassEditMode +{ + ClassEditMode_Name, /** Change whole player class. */ + ClassEditMode_Attributes /** Override class attributes. */ +} + +/** + * Data structure for class editor. + */ +enum VolTypeClassEdit +{ + bool:VolClassEdit_InUse, + + VolClassEditMode:VolClassEdit_Mode, + String:VolClassEdit_ClassName[VOL_CLASSNAME_SIZE], + VolClassEdit_ClassData[ClassEditableAttributes] +} + +/** + * Class editor data. + */ +new VolClassEditData[ZR_VOLUMES_MAX][VolTypeClassEdit]; + +/** + * The player's selected class index. Used by volumes in "name" mode. + */ +new VolClassEditSelectedClass[MAXPLAYERS + 1]; + +/** + * Checks if the specified index is in use. + * + * @param dataIndex Index to check. + * @return True if in use, false otherwise. + */ +bool:VolClassEditInUse(dataIndex) +{ + return VolClassEditData[dataIndex][VolClassEdit_InUse]; +} + +/** + * Gets the first free data index. + * + * @return Data index or -1 if failed. + */ +VolClassEditGetFreeIndex() +{ + // Loop through all indexes. + for (new dataindex = 0; dataindex < ZR_VOLUMES_MAX; dataindex++) + { + // Check if unused. + if (!VolClassEditInUse(dataindex)) + { + // Mark as in use. + VolClassEditData[dataindex][VolClassEdit_InUse] = true; + + // Unused index found. + return dataindex; + } + } + + // Unused index not found. + return -1; +} + +VolClassEditReset(dataIndex) +{ + VolClassEditData[dataIndex][VolClassEdit_InUse] = false; + VolClassEditData[dataIndex][VolClassEdit_Mode] = ClassEditMode_Attributes; + + strcopy(VolClassEditData[dataIndex][VolClassEdit_ClassName], 64, ""); + VolClassEditData[dataIndex][VolClassEdit_ClassData] = VolEmptyAttributes; +} + +/** + * Initialize class editor. + */ +VolClassEditInit() +{ + for (new dataindex = 0; dataindex < ZR_VOLUMES_MAX; dataindex++) + { + VolClassEditReset(dataindex); + } +} + +/** + * Sets class editor spesific attributes on a anticamp volume. + * + * @param dataIndex Local data index. + * @param attribName Attribute to modify. + * @param attribVlue Attribute value to set. + * @return True if successfully set, false otherwise. + */ +VolClassEditSetAttribute(dataIndex, const String:attribName[], const String:attribValue[]) +{ + // Validate index. + if (!VolIsValidIndex(dataIndex)) + { + return false; + } + + /* Class Editor Attributes */ + if (StrEqual(attribName, "mode", false)) + { + if (VolClassEditSetMode(dataIndex, attribValue)) + { + return true; + } + } + else if (StrEqual(attribName, "name", false)) + { + if (VolClassEditSetName(dataIndex, attribValue)) + { + return true; + } + } + + /* Model */ + else if (StrEqual(attribName, "alpha_initial", false)) + { + if (VolClassEditSetAlphaInitial(dataIndex, attribValue)) + { + return true; + } + } + else if (StrEqual(attribName, "alpha_damaged", false)) + { + if (VolClassEditSetAlphaDamaged(dataIndex, attribValue)) + { + return true; + } + } + else if (StrEqual(attribName, "alpha_damage", false)) + { + if (VolClassEditSetAlphaDamage(dataIndex, attribValue)) + { + return true; + } + } + + /* Hud */ + else if (StrEqual(attribName, "overlay_path", false)) + { + if (VolClassEditSetOverlayPath(dataIndex, attribValue)) + { + return true; + } + } + else if (StrEqual(attribName, "nvgs", false)) + { + if (VolClassEditSetNvgs(dataIndex, attribValue)) + { + return true; + } + } + else if (StrEqual(attribName, "fov", false)) + { + if (VolClassEditSetFov(dataIndex, attribValue)) + { + return true; + } + } + + /* Effects */ + else if (StrEqual(attribName, "has_napalm", false)) + { + if (VolClassEditSetHasNapalm(dataIndex, attribValue)) + { + return true; + } + } + else if (StrEqual(attribName, "napalm_time", false)) + { + if (VolClassEditSetNapalmTime(dataIndex, attribValue)) + { + return true; + } + } + + /* Player behaviour */ + else if (StrEqual(attribName, "immunity_mode", false)) + { + if (VolClassEditSetImmunityMode(dataIndex, attribValue)) + { + return true; + } + } + else if (StrEqual(attribName, "immunity_amount", false)) + { + if (VolClassEditSetImmunityAmount(dataIndex, attribValue)) + { + return true; + } + } + else if (StrEqual(attribName, "no_fall_damage", false)) + { + if (VolClassEditSetNoFallDamage(dataIndex, attribValue)) + { + return true; + } + } + else if (StrEqual(attribName, "health_regen_interval", false)) + { + if (VolClassEditSetRegenInterval(dataIndex, attribValue)) + { + return true; + } + } + else if (StrEqual(attribName, "health_regen_amount", false)) + { + if (VolClassEditSetRegenAmount(dataIndex, attribValue)) + { + return true; + } + } + else if (StrEqual(attribName, "infect_gain", false)) + { + if (VolClassEditSetInfectGain(dataIndex, attribValue)) + { + return true; + } + } + else if (StrEqual(attribName, "kill_bonus", false)) + { + if (VolClassEditSetKillBonus(dataIndex, attribValue)) + { + return true; + } + } + else if (StrEqual(attribName, "speed", false)) + { + if (VolClassEditSetSpeed(dataIndex, attribValue)) + { + return true; + } + } + else if (StrEqual(attribName, "knockback", false)) + { + if (VolClassEditSetKnockBack(dataIndex, attribValue)) + { + return true; + } + } + else if (StrEqual(attribName, "jump_height", false)) + { + if (VolClassEditSetJumpHeight(dataIndex, attribValue)) + { + return true; + } + } + else if (StrEqual(attribName, "jump_distance", false)) + { + if (VolClassEditSetJumpDistance(dataIndex, attribValue)) + { + return true; + } + } + + // Invalid attribute name or empty value. + return false; +} + +/** + * Dumps data to be used by zr_vol_list command. + * + * @param dataIndex Index in anticamp data array. + * @param buffer Destination string buffer. + * @param maxlen Size of destination buffer. + * @return Number of cells written. + */ +VolClassEditDumpData(dataIndex, String:buffer[], maxlen) +{ + #define CLASSEDIT_DUMP_FORMAT "%-19s %s\n" + + decl String:linebuffer[128]; + decl String:valuebuffer[256]; + new cache[VolTypeClassEdit]; + new cellswritten; + + // Validate index. + if (!VolIsValidIndex(dataIndex)) + { + return 0; + } + + // Initialize and clear buffer. + buffer[0] = 0; + + // Cache data. + cache = VolClassEditData[dataIndex]; + + VolClassEditModeToString(cache[VolClassEdit_Mode], valuebuffer, sizeof(valuebuffer)); + Format(linebuffer, sizeof(linebuffer), CLASSEDIT_DUMP_FORMAT, "Mode:", valuebuffer); + cellswritten += StrCat(buffer, maxlen, linebuffer); + + switch (cache[VolClassEdit_Mode]) + { + case ClassEditMode_Name: + { + Format(linebuffer, sizeof(linebuffer), CLASSEDIT_DUMP_FORMAT, "Class Name:", cache[VolClassEdit_ClassName]); + cellswritten += StrCat(buffer, maxlen, linebuffer); + } + case ClassEditMode_Attributes: + { + VolClassEditIntToString(dataIndex, ClassEdit_AlphaInitial, valuebuffer, sizeof(valuebuffer)); + Format(linebuffer, sizeof(linebuffer), CLASSEDIT_DUMP_FORMAT, "Alpha initial:", valuebuffer); + cellswritten += StrCat(buffer, maxlen, linebuffer); + + VolClassEditIntToString(dataIndex, ClassEdit_AlphaDamaged, valuebuffer, sizeof(valuebuffer)); + Format(linebuffer, sizeof(linebuffer), CLASSEDIT_DUMP_FORMAT, "Alpha damaged:", valuebuffer); + cellswritten += StrCat(buffer, maxlen, linebuffer); + + VolClassEditIntToString(dataIndex, ClassEdit_AlphaDamage, valuebuffer, sizeof(valuebuffer)); + Format(linebuffer, sizeof(linebuffer), CLASSEDIT_DUMP_FORMAT, "Alpha damage:", valuebuffer); + cellswritten += StrCat(buffer, maxlen, linebuffer); + + VolClassEditStringToHumanStr(dataIndex, ClassEdit_OverlayPath, valuebuffer, sizeof(valuebuffer)); + Format(linebuffer, sizeof(linebuffer), CLASSEDIT_DUMP_FORMAT, "Overlay path:", valuebuffer); + cellswritten += StrCat(buffer, maxlen, linebuffer); + + VolClassEditIntToString(dataIndex, ClassEdit_Nvgs, valuebuffer, sizeof(valuebuffer)); + Format(linebuffer, sizeof(linebuffer), CLASSEDIT_DUMP_FORMAT, "NVGs:", valuebuffer); + cellswritten += StrCat(buffer, maxlen, linebuffer); + + VolClassEditIntToString(dataIndex, ClassEdit_Fov, valuebuffer, sizeof(valuebuffer)); + Format(linebuffer, sizeof(linebuffer), CLASSEDIT_DUMP_FORMAT, "FOV:", valuebuffer); + cellswritten += StrCat(buffer, maxlen, linebuffer); + + VolClassEditIntToString(dataIndex, ClassEdit_HasNapalm, valuebuffer, sizeof(valuebuffer)); + Format(linebuffer, sizeof(linebuffer), CLASSEDIT_DUMP_FORMAT, "Has napalm:", valuebuffer); + cellswritten += StrCat(buffer, maxlen, linebuffer); + + VolClassEditFloatToString(dataIndex, ClassEdit_NapalmTime, valuebuffer, sizeof(valuebuffer)); + Format(linebuffer, sizeof(linebuffer), CLASSEDIT_DUMP_FORMAT, "Napalm time:", valuebuffer); + cellswritten += StrCat(buffer, maxlen, linebuffer); + + VolClassEditIntToString(dataIndex, ClassEdit_ImmunityMode, valuebuffer, sizeof(valuebuffer)); + Format(linebuffer, sizeof(linebuffer), CLASSEDIT_DUMP_FORMAT, "Immunity mode:", valuebuffer); + cellswritten += StrCat(buffer, maxlen, linebuffer); + + VolClassEditFloatToString(dataIndex, ClassEdit_ImmunityAmount, valuebuffer, sizeof(valuebuffer)); + Format(linebuffer, sizeof(linebuffer), CLASSEDIT_DUMP_FORMAT, "Immunity amount:", valuebuffer); + cellswritten += StrCat(buffer, maxlen, linebuffer); + + VolClassEditIntToString(dataIndex, ClassEdit_NoFallDamage, valuebuffer, sizeof(valuebuffer)); + Format(linebuffer, sizeof(linebuffer), CLASSEDIT_DUMP_FORMAT, "No fall damage:", valuebuffer); + cellswritten += StrCat(buffer, maxlen, linebuffer); + + VolClassEditFloatToString(dataIndex, ClassEdit_RegenInterval, valuebuffer, sizeof(valuebuffer)); + Format(linebuffer, sizeof(linebuffer), CLASSEDIT_DUMP_FORMAT, "Regen interval:", valuebuffer); + cellswritten += StrCat(buffer, maxlen, linebuffer); + + VolClassEditIntToString(dataIndex, ClassEdit_RegenAmount, valuebuffer, sizeof(valuebuffer)); + Format(linebuffer, sizeof(linebuffer), CLASSEDIT_DUMP_FORMAT, "Regen amount:", valuebuffer); + cellswritten += StrCat(buffer, maxlen, linebuffer); + + VolClassEditIntToString(dataIndex, ClassEdit_InfectGain, valuebuffer, sizeof(valuebuffer)); + Format(linebuffer, sizeof(linebuffer), CLASSEDIT_DUMP_FORMAT, "Infect gain:", valuebuffer); + cellswritten += StrCat(buffer, maxlen, linebuffer); + + VolClassEditIntToString(dataIndex, ClassEdit_KillBonus, valuebuffer, sizeof(valuebuffer)); + Format(linebuffer, sizeof(linebuffer), CLASSEDIT_DUMP_FORMAT, "Kill bonus:", valuebuffer); + cellswritten += StrCat(buffer, maxlen, linebuffer); + + VolClassEditFloatToString(dataIndex, ClassEdit_Speed, valuebuffer, sizeof(valuebuffer)); + Format(linebuffer, sizeof(linebuffer), CLASSEDIT_DUMP_FORMAT, "Speed:", valuebuffer); + cellswritten += StrCat(buffer, maxlen, linebuffer); + + VolClassEditFloatToString(dataIndex, ClassEdit_KnockBack, valuebuffer, sizeof(valuebuffer), ZR_CLASS_KNOCKBACK_IGNORE); + Format(linebuffer, sizeof(linebuffer), CLASSEDIT_DUMP_FORMAT, "Knock back:", valuebuffer); + cellswritten += StrCat(buffer, maxlen, linebuffer); + + VolClassEditFloatToString(dataIndex, ClassEdit_JumpHeight, valuebuffer, sizeof(valuebuffer)); + Format(linebuffer, sizeof(linebuffer), CLASSEDIT_DUMP_FORMAT, "Jump height:", valuebuffer); + cellswritten += StrCat(buffer, maxlen, linebuffer); + + VolClassEditFloatToString(dataIndex, ClassEdit_JumpDistance, valuebuffer, sizeof(valuebuffer)); + Format(linebuffer, sizeof(linebuffer), CLASSEDIT_DUMP_FORMAT, "Jump distance:", valuebuffer); + cellswritten += StrCat(buffer, maxlen, linebuffer); + } + } + + return cellswritten; +} + + +/************************************** + * * + * EVENTS * + * * + **************************************/ + +/** + * Applies new class attributes to the player. + * + * @param client The client index. + * @param dataIndex Local data index. + */ +VolClassEditApply(client, dataIndex) +{ + switch (VolClassEditData[client][VolClassEdit_Mode]) + { + case ClassEditMode_Name: + { + // Cache volume attributes. + new classindex = ClassGetIndex(VolClassEditData[dataIndex][VolClassEdit_ClassName]); + + // Save player's selected class. + VolClassEditSelectedClass[client] = ClassGetActiveIndex(client); + + // Update cache with new attributes. + ClassReloadPlayerCache(client, classindex); + } + case ClassEditMode_Attributes: + { + // Update player's entire cache with attributes from the specified + // class attributes. + VolClassEditUpdateAttributes(client, VolClassEditData[dataIndex][VolClassEdit_ClassData]); + } + } + + LogEvent(_, LogType_Normal, LOG_DEBUG, LogModule_Volfeatures, "ClassEdit", "Applied class data on client %d.", client); + + // Apply the updated attributes. + ClassApplyAttributes(client); +} + +/** + * Restores the player's regular class attributes (from modified cache). + * + * @param client The client index. + */ +VolClassEditRestore(client, dataIndex) +{ + new classindex = ClassGetActiveIndex(client); + + switch (VolClassEditData[client][VolClassEdit_Mode]) + { + case ClassEditMode_Name: + { + // Restore player's entire cache with attributes from the selected + // class. + ClassReloadPlayerCache(client, VolClassEditSelectedClass[client]); + } + case ClassEditMode_Attributes: + { + // Restore individual attributes, if specified. + VolClassEditRestoreAttributes(client, classindex, VolClassEditData[dataIndex][VolClassEdit_ClassData]); + } + } + + // Apply the restored attributes. + if (ClassApplyAttributes(client)) + { + LogEvent(_, LogType_Normal, LOG_DEBUG, LogModule_Volfeatures, "ClassEdit", "Restored class data on client %d.", client); + } +} + +/** + * Event callback. A player entered a class edit volume. + * + * @param client The client index. + * @param volumeIndex The volume index. + */ +VolClassEditOnPlayerEnter(client, volumeIndex) +{ + VolClassEditApply(client, Volumes[volumeIndex][Vol_DataIndex]); +} + +/** + * Event callback. A player left a class edit volume. + * + * @param client The client index. + * @param volumeIndex The volume index. + */ +VolClassEditOnPlayerLeave(client, volumeIndex) +{ + VolClassEditRestore(client, Volumes[volumeIndex][Vol_DataIndex]); +} + +/** + * Event callback. A class edit volume was enabled. + * + * @param volumeIndex The volume index. + */ +/*VolClassEditOnEnabled(volumeIndex) +{ + +}*/ + +/** + * Event callback. A class edit volume was enabled. + * + * @param volumeIndex The volume index. + */ +VolClassEditOnDisabled(volumeIndex) +{ + for (new client = 1; client < MaxClients; client++) + { + if (IsClientConnected(client) && IsClientInGame(client)) + { + // Only forward event if the player really is in the volume.. + if (VolPlayerInVolume[client][volumeIndex] && VolClassEditSelectedClass[client] >= 0) + { + VolClassEditOnPlayerLeave(client, volumeIndex); + } + } + } +} + + +/************************************** + * * + * ATTRIBUTE FUNCTIONS * + * * + **************************************/ + +/** + * Converts the specified mode to a string. + * + * @param mode Mode to convert. + * @param buffer Destination string buffer. + * @param maxlen Size of buffer. + * @return Number of cells written. + */ +VolClassEditModeToString(VolClassEditMode:mode, String:buffer[], maxlen) +{ + switch (mode) + { + case ClassEditMode_Name: + { + return strcopy(buffer, maxlen, "Name"); + } + case ClassEditMode_Attributes: + { + return strcopy(buffer, maxlen, "Attributes"); + } + } + + return 0; +} + +/** + * Gets a integer attribute and converts it to a human readable string. + * + * Note: attribute is assumed to be a integer (cell) and is not type cheked! + * + * @param dataIndex Local data index. + * @param attribute Attribute to convert. + * @param buffer Destination string buffer. + * @param maxlen Size of destination buffer. + */ +VolClassEditIntToString(dataIndex, ClassEditableAttributes:attribute, String:buffer[], maxlen) +{ + new intVal; + new String:strVal[8]; + + intVal = VolClassEditData[dataIndex][VolClassEdit_ClassData][attribute]; + + // Check if the attribute is marked as ignored. + if (intVal == -1) + { + return strcopy(buffer, maxlen, "(no change)"); + } + else + { + IntToString(intVal, strVal, sizeof(strVal)); + return strcopy(buffer, maxlen, strVal); + } +} + +/** + * Gets a float attribute and converts it to a human readable string. + * + * Note: attribute is assumed to be a float and is not type cheked! + * + * @param dataIndex Local data index. + * @param attribute Attribute to convert. + * @param buffer Destination string buffer. + * @param maxlen Size of destination buffer. + */ +VolClassEditFloatToString(dataIndex, ClassEditableAttributes:attribute, String:buffer[], maxlen, Float:minval = -1.0) +{ + new Float:floatVal; + new String:strVal[8]; + + floatVal = Float:VolClassEditData[dataIndex][VolClassEdit_ClassData][attribute]; + + // Check if the attribute is marked as ignored. + if (floatVal == minval) + { + return strcopy(buffer, maxlen, "(no change)"); + } + else + { + FloatToString(floatVal, strVal, sizeof(strVal)); + return strcopy(buffer, maxlen, strVal); + } +} + +/** + * Gets a string attribute and converts it to a human readable string. + * + * Note: attribute is assumed to be a string and is not type cheked! + * + * @param dataIndex Local data index. + * @param attribute Attribute to convert. + * @param buffer Destination string buffer. + * @param maxlen Size of destination buffer. + */ +VolClassEditStringToHumanStr(dataIndex, ClassEditableAttributes:attribute, String:buffer[], maxlen) +{ + decl String:strVal[PLATFORM_MAX_PATH]; + strcopy(strVal, sizeof(strVal), String:VolClassEditData[dataIndex][VolClassEdit_ClassData][attribute]); + + // Check if the attribute is marked as ignored. + if (StrEqual(strVal, "nochange", false)) + { + return strcopy(buffer, maxlen, "(no change)"); + } + else + { + return strcopy(buffer, maxlen, strVal); + } +} + +/** + * Sets the mode attribute. + * + * @param dataIndex Local data index. + * @param value String value to set (converted to proper type by this + * function). + * @return True if set, false if empty. + */ +bool:VolClassEditSetMode(dataIndex, const String:value[]) +{ + if (strlen(value) == 0) + { + return false; + } + + if (StrEqual(value, "name", false)) + { + VolClassEditData[dataIndex][VolClassEdit_Mode] = ClassEditMode_Name; + return true; + } + else if (StrEqual(value, "attributes", false)) + { + VolClassEditData[dataIndex][VolClassEdit_Mode] = ClassEditMode_Attributes; + return true; + } + + // No match. + return false; +} + +/** + * Sets the class name attribute (name of the class to switch to). + * + * @param dataIndex Local data index. + * @param value String value to set (converted to proper type by this + * function). + * @return True if set, false if empty. + */ +bool:VolClassEditSetName(dataIndex, const String:value[]) +{ + if (strlen(value) == 0) + { + return false; + } + + strcopy(VolClassEditData[dataIndex][VolClassEdit_ClassName], VOL_CLASSNAME_SIZE, value); + + // TODO: Validate name. + + return true; +} + +/** + * Sets the alpha initial attribute. + * + * Note: The value is not validated! + * + * @param dataIndex Local data index. + * @param value String value to set (converted to proper type by this + * function). + * @return True if set, false if empty. + */ +bool:VolClassEditSetAlphaInitial(dataIndex, const String:value[]) +{ + if (strlen(value) == 0) + { + return false; + } + + VolClassEditData[dataIndex][VolClassEdit_ClassData][ClassEdit_AlphaInitial] = StringToInt(value); + return true; +} + +/** + * Sets the alpha damaged attribute. + * + * Note: The value is not validated! + * + * @param dataIndex Local data index. + * @param value String value to set (converted to proper type by this + * function). + * @return True if set, false if empty. + */ +bool:VolClassEditSetAlphaDamaged(dataIndex, const String:value[]) +{ + if (strlen(value) == 0) + { + return false; + } + + VolClassEditData[dataIndex][VolClassEdit_ClassData][ClassEdit_AlphaDamaged] = StringToInt(value); + return true; +} + +/** + * Sets the alpha damage attribute. + * + * Note: The value is not validated! + * + * @param dataIndex Local data index. + * @param value String value to set (converted to proper type by this + * function). + * @return True if set, false if empty. + */ +bool:VolClassEditSetAlphaDamage(dataIndex, const String:value[]) +{ + if (strlen(value) == 0) + { + return false; + } + + VolClassEditData[dataIndex][VolClassEdit_ClassData][ClassEdit_AlphaDamage] = StringToInt(value); + return true; +} + +/** + * Sets the overlay path attribute. + * + * Note: The value is not validated! + * + * @param dataIndex Local data index. + * @param value String value to set (converted to proper type by this + * function). + * @return True if set, false if empty. + */ +bool:VolClassEditSetOverlayPath(dataIndex, const String:value[]) +{ + if (strlen(value) == 0) + { + return false; + } + + strcopy(VolClassEditData[dataIndex][VolClassEdit_ClassData][ClassEdit_OverlayPath], PLATFORM_MAX_PATH, value); + return true; +} + +/** + * Sets the nvgs attribute. + * + * Note: The value is not validated! + * + * @param dataIndex Local data index. + * @param value String value to set (converted to proper type by this + * function). + * @return True if set, false if empty. + */ +bool:VolClassEditSetNvgs(dataIndex, const String:value[]) +{ + if (strlen(value) == 0) + { + return false; + } + + VolClassEditData[dataIndex][VolClassEdit_ClassData][ClassEdit_Nvgs] = StringToInt(value); + return true; +} + +/** + * Sets the fov attribute. + * + * Note: The value is not validated! + * + * @param dataIndex Local data index. + * @param value String value to set (converted to proper type by this + * function). + * @return True if set, false if empty. + */ +bool:VolClassEditSetFov(dataIndex, const String:value[]) +{ + if (strlen(value) == 0) + { + return false; + } + + VolClassEditData[dataIndex][VolClassEdit_ClassData][ClassEdit_Fov] = StringToInt(value); + return true; +} + +/** + * Sets the has napalm attribute. + * + * Note: The value is not validated! + * + * @param dataIndex Local data index. + * @param value String value to set (converted to proper type by this + * function). + * @return True if set, false if empty. + */ +bool:VolClassEditSetHasNapalm(dataIndex, const String:value[]) +{ + if (strlen(value) == 0) + { + return false; + } + + VolClassEditData[dataIndex][VolClassEdit_ClassData][ClassEdit_HasNapalm] = StringToInt(value); + return true; +} + +/** + * Sets the napalm time attribute. + * + * Note: The value is not validated! + * + * @param dataIndex Local data index. + * @param value String value to set (converted to proper type by this + * function). + * @return True if set, false if empty. + */ +bool:VolClassEditSetNapalmTime(dataIndex, const String:value[]) +{ + if (strlen(value) == 0) + { + return false; + } + + VolClassEditData[dataIndex][VolClassEdit_ClassData][ClassEdit_NapalmTime] = StringToFloat(value); + return true; +} + +/** + * Sets the immunity mode attribute. + * + * Note: The value is not validated! + * + * @param dataIndex Local data index. + * @param value String value to set (converted to proper type by this + * function). + * @return True if set, false if empty. + */ +bool:VolClassEditSetImmunityMode(dataIndex, const String:value[]) +{ + if (strlen(value) == 0) + { + return false; + } + + VolClassEditData[dataIndex][VolClassEdit_ClassData][ClassEdit_ImmunityMode] = StringToInt(value); + return true; +} + +/** + * Sets the immunity amount attribute. + * + * Note: The value is not validated! + * + * @param dataIndex Local data index. + * @param value String value to set (converted to proper type by this + * function). + * @return True if set, false if empty. + */ +bool:VolClassEditSetImmunityAmount(dataIndex, const String:value[]) +{ + if (strlen(value) == 0) + { + return false; + } + + VolClassEditData[dataIndex][VolClassEdit_ClassData][ClassEdit_ImmunityAmount] = StringToFloat(value); + return true; +} + +/** + * Sets the no fall damage attribute. + * + * Note: The value is not validated! + * + * @param dataIndex Local data index. + * @param value String value to set (converted to proper type by this + * function). + * @return True if set, false if empty. + */ +bool:VolClassEditSetNoFallDamage(dataIndex, const String:value[]) +{ + if (strlen(value) == 0) + { + return false; + } + + VolClassEditData[dataIndex][VolClassEdit_ClassData][ClassEdit_NoFallDamage] = StringToInt(value); + return true; +} + +/** + * Sets the health regen interval attribute. + * + * Note: The value is not validated! + * + * @param dataIndex Local data index. + * @param value String value to set (converted to proper type by this + * function). + * @return True if set, false if empty. + */ +bool:VolClassEditSetRegenInterval(dataIndex, const String:value[]) +{ + if (strlen(value) == 0) + { + return false; + } + + VolClassEditData[dataIndex][VolClassEdit_ClassData][ClassEdit_RegenInterval] = StringToFloat(value); + return true; +} + +/** + * Sets the health regen amount attribute. + * + * Note: The value is not validated! + * + * @param dataIndex Local data index. + * @param value String value to set (converted to proper type by this + * function). + * @return True if set, false if empty. + */ +bool:VolClassEditSetRegenAmount(dataIndex, const String:value[]) +{ + if (strlen(value) == 0) + { + return false; + } + + VolClassEditData[dataIndex][VolClassEdit_ClassData][ClassEdit_RegenAmount] = StringToInt(value); + return true; +} + +/** + * Sets the infect gain attribute. + * + * Note: The value is not validated! + * + * @param dataIndex Local data index. + * @param value String value to set (converted to proper type by this + * function). + * @return True if set, false if empty. + */ +bool:VolClassEditSetInfectGain(dataIndex, const String:value[]) +{ + if (strlen(value) == 0) + { + return false; + } + + VolClassEditData[dataIndex][VolClassEdit_ClassData][ClassEdit_InfectGain] = StringToInt(value); + return true; +} + +/** + * Sets the kill bonus attribute. + * + * Note: The value is not validated! + * + * @param dataIndex Local data index. + * @param value String value to set (converted to proper type by this + * function). + * @return True if set, false if empty. + */ +bool:VolClassEditSetKillBonus(dataIndex, const String:value[]) +{ + if (strlen(value) == 0) + { + return false; + } + + VolClassEditData[dataIndex][VolClassEdit_ClassData][ClassEdit_KillBonus] = StringToInt(value); + return true; +} + +/** + * Sets the speed attribute. + * + * Note: The value is not validated! + * + * @param dataIndex Local data index. + * @param value String value to set (converted to proper type by this + * function). + * @return True if set, false if empty. + */ +bool:VolClassEditSetSpeed(dataIndex, const String:value[]) +{ + if (strlen(value) == 0) + { + return false; + } + + VolClassEditData[dataIndex][VolClassEdit_ClassData][ClassEdit_Speed] = StringToFloat(value); + return true; +} + +/** + * Sets the knock back attribute. + * + * Note: The value is not validated! + * + * @param dataIndex Local data index. + * @param value String value to set (converted to proper type by this + * function). + * @return True if set, false if empty. + */ +bool:VolClassEditSetKnockBack(dataIndex, const String:value[]) +{ + if (strlen(value) == 0) + { + return false; + } + + VolClassEditData[dataIndex][VolClassEdit_ClassData][ClassEdit_KnockBack] = StringToFloat(value); + return true; +} + +/** + * Sets the jump heigt attribute. + * + * Note: The value is not validated! + * + * @param dataIndex Local data index. + * @param value String value to set (converted to proper type by this + * function). + * @return True if set, false if empty. + */ +bool:VolClassEditSetJumpHeight(dataIndex, const String:value[]) +{ + if (strlen(value) == 0) + { + return false; + } + + VolClassEditData[dataIndex][VolClassEdit_ClassData][ClassEdit_JumpHeight] = StringToFloat(value); + return true; +} + +/** + * Sets the jump distance attribute. + * + * Note: The value is not validated! + * + * @param dataIndex Local data index. + * @param value String value to set (converted to proper type by this + * function). + * @return True if set, false if empty. + */ +bool:VolClassEditSetJumpDistance(dataIndex, const String:value[]) +{ + if (strlen(value) == 0) + { + return false; + } + + VolClassEditData[dataIndex][VolClassEdit_ClassData][ClassEdit_JumpDistance] = StringToFloat(value); + return true; +} + +/** + * Updates a player's cache with the specified attribute set. Only active + * attributes are applied. + * + * Note: These attributes are not validated! + * + * @param client The client index. + * @param attributes Attribute set to apply (ClassEditableAttributes) + * @return Number of attributes changed. + */ +VolClassEditUpdateAttributes(client, const attributes[]) +{ + new numChanges; + + // Alpha initial. + if (attributes[ClassEdit_AlphaInitial] > -1) + { + ClassPlayerCache[client][Class_AlphaInitial] = attributes[ClassEdit_AlphaInitial]; + numChanges++; + } + + // Alpha damaged. + if (attributes[ClassEdit_AlphaDamaged] > -1) + { + ClassPlayerCache[client][Class_AlphaDamaged] = attributes[ClassEdit_AlphaDamaged]; + numChanges++; + } + + // Alpha damage. + if (attributes[ClassEdit_AlphaDamage] > -1) + { + ClassPlayerCache[client][Class_AlphaDamage] = attributes[ClassEdit_AlphaDamage]; + numChanges++; + } + + // Overlay path. + if (!StrEqual(attributes[ClassEdit_OverlayPath], "nochange")) + { + strcopy(ClassPlayerCache[client][Class_OverlayPath], PLATFORM_MAX_PATH, attributes[ClassEdit_OverlayPath]); + numChanges++; + } + + // Nvgs. + if (attributes[ClassEdit_Nvgs] > -1) + { + ClassPlayerCache[client][Class_Nvgs] = bool:attributes[ClassEdit_Nvgs]; + numChanges++; + } + + // Napalm time. + if (attributes[ClassEdit_NapalmTime] > -1.0) + { + ClassPlayerCache[client][Class_NapalmTime] = attributes[ClassEdit_NapalmTime]; + numChanges++; + } + + // Immunity mode. + if (attributes[ClassEdit_ImmunityMode] > -1) + { + ClassPlayerCache[client][Class_ImmunityMode] = attributes[ClassEdit_ImmunityMode]; + numChanges++; + } + + // Immunity amount. + if (attributes[ClassEdit_ImmunityAmount] > -1.0) + { + ClassPlayerCache[client][Class_ImmunityAmount] = attributes[ClassEdit_ImmunityAmount]; + numChanges++; + } + + // No fall damage. + if (attributes[ClassEdit_NoFallDamage] > -1) + { + ClassPlayerCache[client][Class_NoFallDamage] = bool:attributes[ClassEdit_NoFallDamage]; + numChanges++; + } + + // Health regen interval. + if (attributes[ClassEdit_RegenInterval] > -1.0) + { + ClassPlayerCache[client][Class_HealthRegenInterval] = attributes[ClassEdit_RegenInterval]; + numChanges++; + } + + // Health regen amount. + if (attributes[ClassEdit_RegenAmount] > -1) + { + ClassPlayerCache[client][Class_HealthRegenAmount] = attributes[ClassEdit_RegenAmount]; + numChanges++; + } + + // Infect gain. + if (attributes[ClassEdit_InfectGain] > -1) + { + ClassPlayerCache[client][Class_HealthInfectGain] = attributes[ClassEdit_InfectGain]; + numChanges++; + } + + // Kill bonus. + if (attributes[ClassEdit_KillBonus] > -1) + { + ClassPlayerCache[client][Class_KillBonus] = attributes[ClassEdit_KillBonus]; + numChanges++; + } + + // Speed. + if (attributes[ClassEdit_Speed] > -1.0) + { + ClassPlayerCache[client][Class_Speed] = attributes[ClassEdit_Speed]; + numChanges++; + } + + // Knock back. + if (attributes[ClassEdit_KnockBack] > ZR_CLASS_KNOCKBACK_IGNORE) + { + ClassPlayerCache[client][Class_KnockBack] = attributes[ClassEdit_KnockBack]; + numChanges++; + } + + // Jump height. + if (attributes[ClassEdit_JumpHeight] > -1.0) + { + ClassPlayerCache[client][Class_JumpHeight] = attributes[ClassEdit_JumpHeight]; + numChanges++; + } + + // Jump distance. + if (attributes[ClassEdit_JumpDistance] > -1.0) + { + ClassPlayerCache[client][Class_JumpDistance] = attributes[ClassEdit_JumpDistance]; + numChanges++; + } + + LogEvent(_, LogType_Normal, LOG_DEBUG, LogModule_Volfeatures, "ClassEdit", "Applied %d attribute(s).", numChanges); + return numChanges; +} + +/** + * Restores a player's cache to the original values (from modified cache). A + * attribute set is used as a mask to determine what attributes to restore. + * + * @param client The client index. + * @param classindex Index of class to restore. + * @param attributes Attribute mask (ClassEditableAttributes). + * @return Number of attributes changed. + */ +VolClassEditRestoreAttributes(client, classindex, const attributes[]) +{ + new numChanges; + + // Alpha initial. + if (attributes[ClassEdit_AlphaInitial] > -1) + { + ClassPlayerCache[client][Class_AlphaInitial] = ClassGetAlphaInitial(classindex, ZR_CLASS_CACHE_MODIFIED); + numChanges++; + } + + // Alpha damaged. + if (attributes[ClassEdit_AlphaDamaged] > -1) + { + ClassPlayerCache[client][Class_AlphaDamaged] = ClassGetAlphaDamaged(classindex, ZR_CLASS_CACHE_MODIFIED); + numChanges++; + } + + // Alpha damage. + if (attributes[ClassEdit_AlphaDamage] > -1) + { + ClassPlayerCache[client][Class_AlphaDamage] = ClassGetAlphaDamage(classindex, ZR_CLASS_CACHE_MODIFIED); + numChanges++; + } + + // Overlay path. + if (!StrEqual(attributes[ClassEdit_OverlayPath], "nochange")) + { + decl String:path[PLATFORM_MAX_PATH]; + ClassGetOverlayPath(classindex, path, sizeof(path), ZR_CLASS_CACHE_MODIFIED); + strcopy(ClassPlayerCache[client][Class_OverlayPath], PLATFORM_MAX_PATH, path); + numChanges++; + } + + // Nvgs. + if (attributes[ClassEdit_Nvgs] > -1) + { + ClassPlayerCache[client][Class_Nvgs] = ClassGetNvgs(classindex, ZR_CLASS_CACHE_MODIFIED); + numChanges++; + } + + // Napalm time. + if (attributes[ClassEdit_NapalmTime] > -1.0) + { + ClassPlayerCache[client][Class_NapalmTime] = ClassGetNapalmTime(classindex, ZR_CLASS_CACHE_MODIFIED); + numChanges++; + } + + // Immunity mode. + if (attributes[ClassEdit_ImmunityMode] > -1) + { + ClassPlayerCache[client][Class_ImmunityMode] = ClassGetImmunityMode(classindex, ZR_CLASS_CACHE_MODIFIED); + numChanges++; + } + + // Immunity amount. + if (attributes[ClassEdit_ImmunityAmount] > -1.0) + { + ClassPlayerCache[client][Class_ImmunityAmount] = ClassGetImmunityAmount(classindex, ZR_CLASS_CACHE_MODIFIED); + numChanges++; + } + + // No fall damage. + if (attributes[ClassEdit_NoFallDamage] > -1) + { + ClassPlayerCache[client][Class_NoFallDamage] = ClassGetNoFallDamage(classindex, ZR_CLASS_CACHE_MODIFIED); + numChanges++; + } + + // Health regen interval. + if (attributes[ClassEdit_RegenInterval] > -1.0) + { + ClassPlayerCache[client][Class_HealthRegenInterval] = ClassGetHealthRegenInterval(classindex, ZR_CLASS_CACHE_MODIFIED); + numChanges++; + } + + // Health regen amount. + if (attributes[ClassEdit_RegenAmount] > -1) + { + ClassPlayerCache[client][Class_HealthRegenAmount] = ClassGetHealthRegenAmount(classindex, ZR_CLASS_CACHE_MODIFIED); + numChanges++; + } + + // Infect gain. + if (attributes[ClassEdit_InfectGain] > -1) + { + ClassPlayerCache[client][Class_HealthInfectGain] = ClassGetHealthInfectGain(classindex, ZR_CLASS_CACHE_MODIFIED); + numChanges++; + } + + // Kill bonus. + if (attributes[ClassEdit_KillBonus] > -1) + { + ClassPlayerCache[client][Class_KillBonus] = ClassGetKillBonus(classindex, ZR_CLASS_CACHE_MODIFIED); + numChanges++; + } + + // Speed. + if (attributes[ClassEdit_Speed] > -1.0) + { + ClassPlayerCache[client][Class_Speed] = ClassGetSpeed(classindex, ZR_CLASS_CACHE_MODIFIED); + numChanges++; + } + + // Knock back. + if (attributes[ClassEdit_KnockBack] > ZR_CLASS_KNOCKBACK_IGNORE) + { + ClassPlayerCache[client][Class_KnockBack] = ClassGetKnockback(classindex, ZR_CLASS_CACHE_MODIFIED); + numChanges++; + } + + // Jump height. + if (attributes[ClassEdit_JumpHeight] > -1.0) + { + ClassPlayerCache[client][Class_JumpHeight] = ClassGetJumpHeight(classindex, ZR_CLASS_CACHE_MODIFIED); + numChanges++; + } + + // Jump distance. + if (attributes[ClassEdit_JumpDistance] > -1.0) + { + ClassPlayerCache[client][Class_JumpDistance] = ClassGetJumpDistance(classindex, ZR_CLASS_CACHE_MODIFIED); + numChanges++; + } + + LogEvent(_, LogType_Normal, LOG_DEBUG, LogModule_Volfeatures, "ClassEdit", "Applied %d attribute(s).", numChanges); + return numChanges; +} diff --git a/src/zr/volfeatures/volcommands.inc b/src/zr/volfeatures/volcommands.inc index 9e1c535..9ef859a 100644 --- a/src/zr/volfeatures/volcommands.inc +++ b/src/zr/volfeatures/volcommands.inc @@ -78,6 +78,7 @@ public Action:VolAddVolumeCommand(client, argc) StrCat(buffer, sizeof(buffer), "x2, y2, z2 Coordinates to oposite corner (diagonally to oposite height)\n"); StrCat(buffer, sizeof(buffer), "type Volumetric feature type:\n"); StrCat(buffer, sizeof(buffer), " anticamp\n"); + StrCat(buffer, sizeof(buffer), " classedit\n"); StrCat(buffer, sizeof(buffer), "params Parameter string with additional volume data. Generic parameters:\n"); StrCat(buffer, sizeof(buffer), " teamfilter=all|humans|zombies\n"); StrCat(buffer, sizeof(buffer), " delay=0\n"); @@ -103,7 +104,7 @@ public Action:VolAddVolumeCommand(client, argc) new dataindex; new paramcount; - decl String:params[256]; + decl String:params[512]; decl String:argbuffer[256]; params[0] = 0; @@ -418,9 +419,9 @@ public Action:VolListCommand(client, argc) { VolAnticampDumpData(volcache[Vol_DataIndex], buffer, sizeof(buffer)); } - case VolFeature_Knockback: + case VolFeature_ClassEdit: { - + VolClassEditDumpData(volcache[Vol_DataIndex], buffer, sizeof(buffer)); } } @@ -587,6 +588,27 @@ VolClear(volumeIndex) Volumes[volumeIndex][Vol_yMax] = 0.0; Volumes[volumeIndex][Vol_zMax] = 0.0; + Volumes[volumeIndex][Vol_Effect] = VolEffect_None; + Volumes[volumeIndex][Vol_EffectColor][0] = 0; + Volumes[volumeIndex][Vol_EffectColor][1] = 0; + Volumes[volumeIndex][Vol_EffectColor][2] = 0; + + new dataindex = Volumes[volumeIndex][Vol_DataIndex]; + if (dataindex >= 0) + { + switch (Volumes[volumeIndex][Vol_Type]) + { + case VolFeature_Anticamp: + { + VolAnticampReset(dataindex); + } + case VolFeature_ClassEdit: + { + VolClassEditReset(dataindex); + } + } + } + Volumes[volumeIndex][Vol_Type] = VolFeature_Invalid; Volumes[volumeIndex][Vol_DataIndex] = -1; @@ -594,6 +616,17 @@ VolClear(volumeIndex) Volumes[volumeIndex][Vol_TriggerDelay] = 0.0; } +/** + * Clears all volumes. + */ +VolClearAll() +{ + for (new volindex = 0; volindex < ZR_VOLUMES_MAX; volindex++) + { + VolClear(volindex); + } +} + /** * Sets extra attributes on a volume. * @@ -697,6 +730,13 @@ VolSetAttributes(volumeIndex, const String:attributes[]) successfulCount++; } } + case VolFeature_ClassEdit: + { + if (VolClassEditSetAttribute(dataindex, attribName, attribValue)) + { + successfulCount++; + } + } } } } diff --git a/src/zr/volfeatures/volevents.inc b/src/zr/volfeatures/volevents.inc index eed16d2..6e72eea 100644 --- a/src/zr/volfeatures/volevents.inc +++ b/src/zr/volfeatures/volevents.inc @@ -43,7 +43,14 @@ VolOnPlayerEnter(client, volumeIndex) LogEvent(_, LogType_Normal, LOG_DEBUG, LogModule_Volfeatures, "Event", "Player %N entered volume %d.", client, volumeIndex); // Forward event to features. - + new VolumeFeatureTypes:voltype = Volumes[volumeIndex][Vol_Type]; + switch (voltype) + { + case VolFeature_ClassEdit: + { + VolClassEditOnPlayerEnter(client, volumeIndex); + } + } } /** @@ -64,7 +71,14 @@ VolOnPlayerLeave(client, volumeIndex) LogEvent(_, LogType_Normal, LOG_DEBUG, LogModule_Volfeatures, "Event", "Player %N left volume %d.", client, volumeIndex); // Forward event to features. - + new VolumeFeatureTypes:voltype = Volumes[volumeIndex][Vol_Type]; + switch (voltype) + { + case VolFeature_ClassEdit: + { + VolClassEditOnPlayerLeave(client, volumeIndex); + } + } } /** @@ -184,6 +198,10 @@ VolOnDisabled(volumeIndex) { VolAnticampDisable(volumeIndex); } + case VolFeature_ClassEdit: + { + VolClassEditOnDisabled(volumeIndex); + } } } diff --git a/src/zr/volfeatures/volfeatures.inc b/src/zr/volfeatures/volfeatures.inc index 9f13f83..e3deba8 100644 --- a/src/zr/volfeatures/volfeatures.inc +++ b/src/zr/volfeatures/volfeatures.inc @@ -36,30 +36,32 @@ enum VolumeAttributes { /* General */ - bool:Vol_Enabled, /** Volume state. */ - bool:Vol_InUse, /** Marks if the volume is used. */ + bool:Vol_Enabled, /** Volume state. */ + bool:Vol_InUse, /** Marks if the volume is used. */ /* Location */ - Float:Vol_xMin, /** Minimum x position. */ - Float:Vol_xMax, /** Maximum x position. */ + Float:Vol_xMin, /** Minimum x position. */ + Float:Vol_xMax, /** Maximum x position. */ - Float:Vol_yMin, /** Minimum y position. */ - Float:Vol_yMax, /** Maximum y position. */ + Float:Vol_yMin, /** Minimum y position. */ + Float:Vol_yMax, /** Maximum y position. */ - Float:Vol_zMin, /** Minimum z position. */ - Float:Vol_zMax, /** Maximum z position. */ + Float:Vol_zMin, /** Minimum z position. */ + Float:Vol_zMax, /** Maximum z position. */ /* Style */ - VolumeEffects:Vol_Effect, /** Visual effect to apply on the volume. */ - Vol_EffectColor[3], /** Render color of the effect. RGB colors. */ + VolumeEffects:Vol_Effect, /** Visual effect to apply on the volume. */ + Vol_EffectColor[3], /** Render color of the effect. RGB colors. */ /* Data */ - VolumeFeatureTypes:Vol_Type, /** The volumetric feature type. */ - Vol_DataIndex, /** Index in remote feature array. */ + VolumeFeatureTypes:Vol_Type, /** The volumetric feature type. */ + Vol_DataIndex, /** Index in remote feature array. */ /* Behaviour */ - VolumeTeamFilters:Vol_TeamFilter, /** Team filtering. Trigger by certain teams, or all. */ - Float:Vol_TriggerDelay /** Trigger delay. How many seconds players have to stay to trigger volume events. */ + VolumeTeamFilters:Vol_TeamFilter, /** Team filtering. Trigger by certain teams, or all. */ + Float:Vol_TriggerDelay, /** Trigger delay. How many seconds players have to stay to trigger volume events. */ + Vol_ConflictAction, /** What to do if volumes of same type overlap. */ + Vol_Priority, /** Volume priority. */ } /** @@ -69,7 +71,7 @@ enum VolumeFeatureTypes { VolFeature_Invalid = 0, VolFeature_Anticamp, - VolFeature_Knockback + VolFeature_ClassEdit } /** @@ -92,6 +94,15 @@ enum VolumeTeamFilters VolTeam_Zombies } +/** + * Conflict actions. What to do with overlapping volumes of same type. + */ +enum VolumeConflictActions +{ + VolPriority = 0, /** Use the volume with highest priority, ignore others. */ + VolMerge /** Merge volume settings/attributes. Using priority if there's a merge conflict. */ +} + /** * Volumes. */ @@ -147,18 +158,29 @@ new Float:VolTriggerInterval; // Sub features. #include "zr/volfeatures/volanticamp" +#include "zr/volfeatures/volclassedit" /** * Initialize volumetric features. */ +VolInit() +{ + // Clear all volumes. + VolClearAll(); + + // Initialize sub features. + VolAnticampInit(); + VolClassEditInit(); +} + +/** + * Initialize volumetric feature settings. + */ VolLoad() { // Cache CVAR value. VolEnabled = GetConVarBool(g_hCvarsList[CVAR_VOL]); - - // Initialize sub features. - VolAnticampInit(); } /** @@ -273,7 +295,7 @@ bool:VolStartUpdateTimer() else { // Volumetric features disabled. - LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Volfeatures, "Config Validation", "Warning: Console variable \"zr_vol_update_interval\" is set to zero or negative. Must be positive."); + LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Volfeatures, "Config Validation", "Warning: Console variable \"zr_vol_update_interval\" is zero or negative. Must be positive."); return false; } } @@ -322,7 +344,7 @@ bool:VolStartTriggerTimer() else { // Trigger timer not running. Either disabled or invalid interval. - LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Volfeatures, "Config Validation", "Warning: Console variable \"zr_vol_trigger_interval\" is set to zero or negative. Must be positive."); + LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Volfeatures, "Config Validation", "Warning: Console variable \"zr_vol_trigger_interval\" is zero or negative. Must be positive."); return false; } } @@ -621,10 +643,9 @@ VolGetFreeDataIndex(VolumeFeatureTypes:volumeType) { return VolAnticampGetFreeIndex(); } - case VolFeature_Knockback: + case VolFeature_ClassEdit: { - // TOTO: Finish incomplete feature. - return -1; + return VolClassEditGetFreeIndex(); } } @@ -754,9 +775,9 @@ VolumeFeatureTypes:VolGetTypeFromString(const String:volType[]) { return VolFeature_Anticamp; } - else if (StrEqual(volType, "knockback", false)) + else if (StrEqual(volType, "classedit", false)) { - return VolFeature_Knockback; + return VolFeature_ClassEdit; } // No match. @@ -783,11 +804,11 @@ VolTypeToString(VolumeFeatureTypes:volType, String:buffer[], maxlen, bool:shortN } case VolFeature_Anticamp: { - return shortName ? strcopy(buffer, maxlen, "anticamp") : strcopy(buffer, maxlen, "Anti camp"); + return shortName ? strcopy(buffer, maxlen, "anticamp") : strcopy(buffer, maxlen, "Anti-Camp"); } - case VolFeature_Knockback: + case VolFeature_ClassEdit: { - return shortName ? strcopy(buffer, maxlen, "knockback") : strcopy(buffer, maxlen, "Knock back modifier"); + return shortName ? strcopy(buffer, maxlen, "classedit") : strcopy(buffer, maxlen, "Class Editor"); } }