diff --git a/src/zr/ambience.inc b/src/zr/ambience.inc index bd59d0e..2056350 100644 --- a/src/zr/ambience.inc +++ b/src/zr/ambience.inc @@ -6,6 +6,8 @@ * ==================== */ +// TODO: Move skybox and lightning functions into this file. + new bool:AmbienceLoaded = false; new String:AmbienceSound[64]; new Float:AmbienceVolume; diff --git a/src/zr/playerclasses/apply.inc b/src/zr/playerclasses/apply.inc index 6a56004..6934400 100644 --- a/src/zr/playerclasses/apply.inc +++ b/src/zr/playerclasses/apply.inc @@ -32,6 +32,11 @@ bool:ClassApplyAttributes(client, bool:improved = false) ClassApplyModel(client, classindex); ClassApplyAlpha(client, classindex); ClassApplyOverlay(client, classindex); + ClassApplyNightVision(client, classindex); + ClassApplyFOV(client, classindex); + ClassApplyHealth(client, classindex, improved); + ClassApplyHealthRegen(client, classindex); + ClassApplySpeed(client, classindex); } /** @@ -69,6 +74,8 @@ bool:ClassApplyModel(client, classindex, cachetpye = ZR_CLASS_CACHE_PLAYER) Format(modelpath, sizeof(modelpath), "%s.mdl", modelpath); } + // TODO: Add support for keeping the default cs model ("default"). + SetPlayerModel(client, modelpath); return true; } @@ -145,3 +152,179 @@ bool:ClassApplyOverlay(client, classindex, cachetype = ZR_CLASS_CACHE_PLAYER) ClassOverlayInitialize(client, overlay); return true; } + +/** + * Gives night vision to a player. + * + * @param client The client index. + * @param classindex The class to read from. + * @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. + * @return True on success, false otherwise. + */ +bool:ClassApplyNightVision(client, classindex, cachetype = ZR_CLASS_CACHE_PLAYER) +{ + new bool:nvgs; + + // Get the night vision setting from the specified cache. + if (cachetype == ZR_CLASS_CACHE_PLAYER) + { + nvgs = ClassGetNvgs(client, cachetype); + } + else + { + nvgs = ClassGetNvgs(classindex, cachetype); + } + + NightVision(client, nvgs); + + // Turn on night vision if applied. + if (nvgs) + { + NightVisionOn(client, nvgs); + } + return true; +} + +/** + * Sets the field of view setting on a player. + * + * @param client The client index. + * @param classindex The class to read from. + * @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. + * @return True on success, false otherwise. + */ +bool:ClassApplyFOV(client, classindex, cachetype = ZR_CLASS_CACHE_PLAYER) +{ + new fov; + + // Get the field of view setting from the specified cache. + if (cachetype == ZR_CLASS_CACHE_PLAYER) + { + fov = ClassGetFOV(client, cachetype); + } + else + { + fov = ClassGetFOV(classindex, cachetype); + } + + SetPlayerFOV(client, fov); + return true; +} + +/** + * Gives health points on a player. + * + * @param client The client index. + * @param classindex The class to read from. + * @param boost Double health boost. Default: false + * @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. + * @return True on success, false otherwise. + */ +bool:ClassApplyHealth(client, classindex, bool:boost = false, cachetype = ZR_CLASS_CACHE_PLAYER) +{ + new health; + + // Get the health points from the specified cache. + if (cachetype == ZR_CLASS_CACHE_PLAYER) + { + health = ClassGetHealth(client, cachetype); + } + else + { + health = ClassGetHealth(classindex, cachetype); + } + + if (boost) + { + health *= 2; + } + + SetEntityHealth(client, health); + return true; +} + +/** + * Applies health regeneration on a player if enabled. + * + * @param client The client index. + * @param classindex The class to read from. + * @param boost Double health boost. Default: false + * @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. + * @return True if applied, false otherwise. + */ +bool:ClassApplyHealthRegen(client, classindex, cachetype = ZR_CLASS_CACHE_PLAYER) +{ + new Float:interval; + new amount; + new max; + + // Get the health regeneration info from the specified cache. + if (cachetype == ZR_CLASS_CACHE_PLAYER) + { + interval = ClassGetHealthRegenInterval(client, cachetype); + amount = ClassGetHealthRegenAmount(client, cachetype); + max = ClassGetHealth(client, cachetype); + } + else + { + interval = ClassGetHealthRegenInterval(classindex, cachetype); + amount = ClassGetHealthRegenAmount(classindex, cachetype); + max = ClassGetHealth(classindex, cachetype); + } + + if (interval > 0) + { + ClassHealthRegenInitialize(client, interval, amount, max); + return true; + } + else + { + return false; + } +} + +/** + * Sets the players speed. + * + * @param client The client index. + * @param classindex The class to read from. + * @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. + * @return True on success, false otherwise. + */ +bool:ClassApplySpeed(client, classindex, cachetype = ZR_CLASS_CACHE_PLAYER) +{ + new speed; + + // Get the health points from the specified cache. + if (cachetype == ZR_CLASS_CACHE_PLAYER) + { + speed = ClassGetSpeed(client, cachetype); + } + else + { + speed = ClassGetSpeed(classindex, cachetype); + } + + SetPlayerSpeed(client, speed); + return true; +} diff --git a/src/zr/playerclasses/attributes.inc b/src/zr/playerclasses/attributes.inc index a6400e5..4927837 100644 --- a/src/zr/playerclasses/attributes.inc +++ b/src/zr/playerclasses/attributes.inc @@ -384,7 +384,7 @@ ClassGetOverlayPath(index, String:buffer[], maxlen, cachetype = ZR_CLASS_CACHE_P * is used, index will be used as a client index. * @return The night vision setting from the specified class. False on error. */ -bool:ClassGetNVGs(index, cachetype = ZR_CLASS_CACHE_PLAYER) +bool:ClassGetNvgs(index, cachetype = ZR_CLASS_CACHE_PLAYER) { switch (cachetype) { diff --git a/src/zr/playerclasses/filtertools.inc b/src/zr/playerclasses/filtertools.inc index e05daae..fa7b7bf 100644 --- a/src/zr/playerclasses/filtertools.inc +++ b/src/zr/playerclasses/filtertools.inc @@ -45,15 +45,138 @@ bool:ClassValidateTeamRequirements(cachetype = ZR_CLASS_CACHE_ORIGINAL) /** * Validates all the class attributes in the original class data array, to - * check if they have invalid values. + * check if they have invalid values. Boolean settings are not validated. * * @param classindex The index of the class to validate. - * @return True if validation was successful, false otherwise. + * @return A value with attribute error flags. */ -bool:ClassValidateAttributes(classindex) +ClassValidateAttributes(classindex) { - return true; - // TODO: Manually check each attribute (hard coded). Better solutions? + // TODO: Validate immunity mode and amount. + // TODO: Validate jump values. + + new flags; + + // Name. + if (strlen(ClassData[classindex][class_name]) == 0) + { + flags += ZR_CLASS_ATTRIB_ERR_NAME; + } + + // Description. + if (strlen(ClassData[classindex][class_description]) == 0) + { + flags += ZR_CLASS_ATTRIB_ERR_DESCRIPTION; + } + + // Model path. + decl String:model_path[256]; + if (strcopy(model_path, sizeof(model_path), ClassData[classindex][class_model_path]) == 0) + { + flags += ZR_CLASS_ATTRIB_ERR_MODEL_PATH; + } + else + { + // Check if default or random model is specified. + if (strcmp(model_path, "random", false) != 0 && strcmp(model_path, "default", false) != 0) + { + // Check if the file exists. + if (!FileExists(model_path)) + { + flags += ZR_CLASS_ATTRIB_ERR_MODEL_PATH; + } + } + } + + // Alpha, initial. + new alpha_initial = ClassData[classindex][class_alpha_initial]; + if (!(alpha_initial >= 0 && alpha_initial <= 255)) + { + flags += ZR_CLASS_ATTRIB_ERR_ALPHA_INITIAL; + } + + // Alpha, damaged. + new alpha_damaged = ClassData[classindex][class_alpha_damaged]; + if (!(alpha_damaged >= 0 && alpha_damaged <= 255)) + { + flags += ZR_CLASS_ATTRIB_ERR_ALPHA_DAMAGED; + } + + // Alpha, damage. + new alpha_damage = ClassData[classindex][class_alpha_damage]; + if (!(alpha_damage >= 0 && alpha_damage <= 65536)) + { + flags += ZR_CLASS_ATTRIB_ERR_ALPHA_DAMAGE; + } + + // Overlay path. + decl String:overlay_path[256]; + if (strcopy(overlay_path, sizeof(overlay_path), ClassData[classindex][class_overlay_path]) != 0) + { + // Check if the file exists. + if (!FileExists(overlay_path)) + { + flags += ZR_CLASS_ATTRIB_ERR_OVERLAY_PATH; + } + } + + // Field of view. + new fov = ClassData[classindex][class_fov]; + if (!(fov > 15 && fov < 180)) + { + flags += ZR_CLASS_ATTRIB_ERR_FOV; + } + + // Napalm time. + new Float:napalm_time = ClassData[classindex][class_napalm_time]; + if (!(napalm_time >= 0.0 && napalm_time <= 900.0)) + { + flags += ZR_CLASS_ATTRIB_ERR_NAPALM_TIME; + } + + // Health regen interval. + new Float:regen_interval = ClassData[classindex][class_health_regen_interval]; + if (!(regen_interval >= 0.0 && regen_interval <= 900.0)) + { + flags += ZR_CLASS_ATTRIB_ERR_HEALTH_REGEN_INTERVAL; + } + + // Health regen amount. + new regen_amount = ClassData[classindex][class_health_regen_amount]; + if (!(regen_amount > 0 && regen_amount <= 65536)) + { + flags += ZR_CLASS_ATTRIB_ERR_HEALTH_REGEN_AMOUNT; + } + + // Health infect gain. + new infect_gain = ClassData[classindex][class_health_infect_gain]; + if (!(infect_gain >= 0 && infect_gain <= 65536)) + { + flags += ZR_CLASS_ATTRIB_ERR_INFECT_GAIN; + } + + // Kill bonus. + new kill_bonus = ClassData[classindex][class_kill_bonus]; + if (!(kill_bonus >= 0 && kill_bonus <= 128)) + { + flags += ZR_CLASS_ATTRIB_ERR_KILL_BONUS; + } + + // Speed. + new Float:speed = ClassData[classindex][class_speed]; + if (!(speed >= 0.0 && speed <= 1024.0)) + { + flags += ZR_CLASS_ATTRIB_ERR_SPEED; + } + + // Knockback. + new Float:knockback = ClassData[classindex][class_knockback]; + if (!(knockback >= -10.0 && knockback <= 50.0)) + { + flags += ZR_CLASS_ATTRIB_ERR_KNOCKBACK; + } + + return flags; } /** @@ -65,12 +188,7 @@ bool:ClassValidateAttributes(classindex) */ bool:ClassValidateIndex(classindex) { - if (ClassCount == 0) - { - return false; - } - - if (classindex >= 0 && classid < ClassCount) + if (classindex >= 0 && classindex < ClassCount) { return true; } diff --git a/src/zr/playerclasses/healthregen.inc b/src/zr/playerclasses/healthregen.inc new file mode 100644 index 0000000..84ccee6 --- /dev/null +++ b/src/zr/playerclasses/healthregen.inc @@ -0,0 +1,66 @@ +/* + * ============================================================================ + * + * Zombie:Reloaded + * + * File: healthregen.inc + * Description: Functions for managing health regeneration on a client. + * Author: Richard Helgeby + * + * ============================================================================ + */ + +new ClientHealthRegenAmount[MAXPLAYERS + 1]; +new ClientHealthRegenMax[MAXPLAYERS + 1]; +new Handle:tHealthRegen[MAXPLAYERS + 1] = {INVALID_HANDLE, ...}; + +ClassHealthRegenInitialize(client, Float:interval, amount, max) +{ + ClientHealthRegenAmount[client] = amount; + ClientHealthRegenMax[client] = max; + ClassHealthRegenStart(client, interval); +} + +ClassHealthRegenStart(client, Float:interval) +{ + // Kill the timer if it exist. + if (tHealthRegen[client] != INVALID_HANDLE) + { + KillTimer(tHealthRegen[client]); + tHealthRegen[client] = INVALID_HANDLE; + } + + tHealthRegen[client] = CreateTimer(interval, ClassHealthRegenTimer, client, TIMER_REPEAT); +} + +ClassHealthRegenStop(client) +{ + // Kill the timer if it exist. + if (tHealthRegen[client] != INVALID_HANDLE) + { + KillTimer(tHealthRegen[client]); + tHealthRegen[client] = INVALID_HANDLE; + } +} + +public Action:ClassHealthRegenTimer(Handle:timer, any:client) +{ + // Kill the timer if the player is dead. + if (!IsPlayerAlive(client)) + { + tHealthRegen[client] = INVALID_HANDLE; + return Plugin_Stop; + } + + new health = GetClientHealth(client); + health += ClientHealthRegenAmount[client]; + + // Check if the health points is below the limit. + if (health < ClientHealthRegenMax[client]) + { + // Increase health. + SetEntityHealth(client, health); + } + + return Plugin_Continue; +} \ No newline at end of file diff --git a/src/zr/playerclasses/playerclasses.inc b/src/zr/playerclasses/playerclasses.inc index 02af8a6..2509331 100644 --- a/src/zr/playerclasses/playerclasses.inc +++ b/src/zr/playerclasses/playerclasses.inc @@ -129,6 +129,34 @@ * @endsection */ +/** + * @section Error flags for invalid class attributes. + */ +#define ZR_CLASS_ATTRIB_ERR_OK 0 +#define ZR_CLASS_ATTRIB_ERR_NAME 1 +#define ZR_CLASS_ATTRIB_ERR_DESCRIPTION 2 +#define ZR_CLASS_ATTRIB_ERR_MODEL_PATH 4 +#define ZR_CLASS_ATTRIB_ERR_ALPHA_INITIAL 8 +#define ZR_CLASS_ATTRIB_ERR_ALPHA_DAMAGED 16 +#define ZR_CLASS_ATTRIB_ERR_ALPHA_DAMAGE 32 +#define ZR_CLASS_ATTRIB_ERR_OVERLAY_PATH 64 +#define ZR_CLASS_ATTRIB_ERR_FOV 128 +#define ZR_CLASS_ATTRIB_ERR_NAPALM_TIME 256 +#define ZR_CLASS_ATTRIB_ERR_IMMUNITY_MODE 512 +#define ZR_CLASS_ATTRIB_ERR_IMMUNITY_AMOUNT 1024 +#define ZR_CLASS_ATTRIB_ERR_HEALTH_REGEN_INTERVAL 2048 +#define ZR_CLASS_ATTRIB_ERR_HEALTH_REGEN_AMOUNT 4096 +#define ZR_CLASS_ATTRIB_ERR_INFECT_GAIN 8192 +#define ZR_CLASS_ATTRIB_ERR_KILL_BONUS 16384 +#define ZR_CLASS_ATTRIB_ERR_SPEED 32768 +#define ZR_CLASS_ATTRIB_ERR_KNOCKBACK 65536 +#define ZR_CLASS_ATTRIB_ERR_JUMP_HEIGHT 131072 +#define ZR_CLASS_ATTRIB_ERR_JUMP_DISTANCE 262144 +/** + * @endsection + */ + + /** * Generic player attributes. */ @@ -208,6 +236,7 @@ new ClassActive[MAXPLAYERS + 1][ZR_CLASS_TEAMCOUNT - 1]; #include "zr/playerclasses/attributes" #include "zr/playerclasses/apply" #include "zr/playerclasses/clientoverlays" +#include "zr/playerclasses/healthregen" /** * Loads class attributes from playerclasses.txt into the ClassData array. If @@ -248,6 +277,7 @@ ClassLoad(const String:classfile[256] = "configs/zr/playerclasses.txt") decl String:overlay_path[256]; ClassCount = 0; + new ClassErrorFlags; // Loop through all classes and store attributes in the ClassData array. do @@ -312,17 +342,20 @@ ClassLoad(const String:classfile[256] = "configs/zr/playerclasses.txt") ClassData[ClassCount][class_jump_distance] = KvGetFloat(kvClassData, "jump_distance", ZR_CLASS_DEFAULT_JUMP_DISTANCE); // Validate the class attributes. - if (!ClassValidateAttributes(ClassCount)) + ClassErrorFlags = ClassValidateAttributes(ClassCount); + if (ClassErrorFlags > 0) { - // TODO: There's an invalid class, what do we do? - // Skip it (clearing data too), disable it and give a log warning, - // or set the plugin in a failed state? - } - else - { - // The class is valid. Update the counter. - ClassCount++; + // There's one or more invalid class attributes. Disable the class + // and log an error message. + ClassData[ClassCount][class_enabled] = false; + if (LogFlagCheck(LOG_CORE_EVENTS, LOG_MODULE_CLASSES)) + { + ZR_LogMessageFormatted(-1, "classes", "load", "Invalid class at index %d. Class error flags: %d.", LOG_FORMAT_TYPE_ERROR, ClassCount, ClassErrorFlags); + } } + + // Update the counter. + ClassCount++; } while (KvGotoNextKey(kvClassData)); // Validate team requirements.