1353 lines
44 KiB
SourcePawn
1353 lines
44 KiB
SourcePawn
/*
|
|
* ============================================================================
|
|
*
|
|
* Zombie:Reloaded
|
|
*
|
|
* File: filtertools.inc
|
|
* Type: Core
|
|
* Description: Class system tools; validating, getting indexes or lists
|
|
*
|
|
* Copyright (C) 2009-2013 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 <http://www.gnu.org/licenses/>.
|
|
*
|
|
* ============================================================================
|
|
*/
|
|
|
|
/**
|
|
* Validates the team requirements in a class cache and check that theres at
|
|
* least one class for each team. Minium team requirements are zombies and
|
|
* humans. The admin team is optinal and not validated.
|
|
*
|
|
* @param cachetype Optional. Specifies what class cache to validate. Options:
|
|
* ZR_CLASS_CACHE_ORIGINAL (default, unchanged class data),
|
|
* ZR_CLASS_CACHE_MODIFIED (modified class data).
|
|
* @return True if validation was successful, false otherwise.
|
|
*/
|
|
stock bool:ClassValidateTeamRequirements(cachetype = ZR_CLASS_CACHE_ORIGINAL)
|
|
{
|
|
new zombieindex;
|
|
new humanindex;
|
|
|
|
// Check if there are no classes.
|
|
if (ClassCount == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Test if a zombie and human class was found.
|
|
zombieindex = ClassGetFirstClass(ZR_CLASS_TEAM_ZOMBIES, _, cachetype);
|
|
humanindex = ClassGetFirstClass(ZR_CLASS_TEAM_HUMANS, _, cachetype);
|
|
|
|
// Validate indexes.
|
|
if (ClassValidateIndex(zombieindex) && ClassValidateIndex(humanindex))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Validates that there's a class marked as team default for each team.
|
|
*
|
|
* @param cachetype Optional. Specifies what class cache to validate. Options:
|
|
* ZR_CLASS_CACHE_ORIGINAL (default, unchanged class data),
|
|
* ZR_CLASS_CACHE_MODIFIED (modified class data).
|
|
* @return True if validation was successful, false otherwise.
|
|
*/
|
|
stock bool:ClassValidateTeamDefaults(cachetype = ZR_CLASS_CACHE_ORIGINAL)
|
|
{
|
|
new zombieindex;
|
|
new humanindex;
|
|
|
|
// Check if there are no classes.
|
|
if (ClassCount == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Test if a default zombie and human class was found.
|
|
zombieindex = ClassGetDefaultClass(ZR_CLASS_TEAM_ZOMBIES, _, cachetype);
|
|
humanindex = ClassGetDefaultClass(ZR_CLASS_TEAM_HUMANS, _, cachetype);
|
|
|
|
// Validate indexes.
|
|
if (ClassValidateIndex(zombieindex) && ClassValidateIndex(humanindex))
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validates all the class attributes in the original class data array, to
|
|
* check if they have invalid values. Boolean settings are not validated.
|
|
*
|
|
* @param classindex The index of the class to validate.
|
|
* @param logErrors Log invalid attributes.
|
|
* @return A value with attribute error flags.
|
|
*/
|
|
stock ClassValidateAttributes(classindex, bool:logErrors = false)
|
|
{
|
|
new flags;
|
|
|
|
// Team.
|
|
new team = ClassData[classindex][Class_Team];
|
|
if (team < ZR_CLASS_TEAM_MIN || team > ZR_CLASS_TEAM_MAX)
|
|
{
|
|
flags += ZR_CLASS_TEAM;
|
|
if (logErrors)
|
|
{
|
|
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Playerclasses, "Config Validation", "Warning: Invalid team at index %d: %d", classindex, team);
|
|
}
|
|
}
|
|
|
|
// Class flags.
|
|
new class_flags = ClassData[classindex][Class_Flags];
|
|
if (class_flags < ZR_CLASS_FLAGS_MIN || class_flags > ZR_CLASS_FLAGS_MAX)
|
|
{
|
|
flags += ZR_CLASS_FLAGS;
|
|
if (logErrors)
|
|
{
|
|
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Playerclasses, "Config Validation", "Warning: Invalid flags at index %d: %d", classindex, class_flags);
|
|
}
|
|
}
|
|
|
|
// Group.
|
|
decl String:group[64];
|
|
group[0] = 0;
|
|
if (strcopy(group, sizeof(group), ClassData[classindex][Class_Group]) > 0)
|
|
{
|
|
// Check if the group exist.
|
|
if (FindAdmGroup(group) == INVALID_GROUP_ID)
|
|
{
|
|
flags += ZR_CLASS_GROUP;
|
|
if (logErrors)
|
|
{
|
|
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Playerclasses, "Config Validation", "Warning: Invalid group at index %d: \"%s\"", classindex, group);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Name.
|
|
decl String:name[64];
|
|
name[0] = 0;
|
|
if (strcopy(name, sizeof(name), ClassData[classindex][Class_Name]) < ZR_CLASS_NAME_MIN)
|
|
{
|
|
flags += ZR_CLASS_NAME;
|
|
if (logErrors)
|
|
{
|
|
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Playerclasses, "Config Validation", "Warning: Missing name at index %d.", classindex);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Check for reserved name keyworks. These aren't allowed as names.
|
|
if (StrEqual(name, "all", false) ||
|
|
StrEqual(name, "humans", false) ||
|
|
StrEqual(name, "zombies", false) ||
|
|
StrEqual(name, "admins", false))
|
|
{
|
|
flags += ZR_CLASS_NAME;
|
|
if (logErrors)
|
|
{
|
|
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Playerclasses, "Config Validation", "Warning: Invalid name at index %d. Cannot be a team name: \"%s\"", classindex, name);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Description.
|
|
if (strlen(ClassData[classindex][Class_Description]) < ZR_CLASS_DESCRIPTION_MIN)
|
|
{
|
|
flags += ZR_CLASS_DESCRIPTION;
|
|
if (logErrors)
|
|
{
|
|
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Playerclasses, "Config Validation", "Warning: Missing description at index %d.", classindex);
|
|
}
|
|
}
|
|
|
|
// Model path.
|
|
decl String:model_path[PLATFORM_MAX_PATH];
|
|
if (strcopy(model_path, sizeof(model_path), ClassData[classindex][Class_ModelPath]) == 0)
|
|
{
|
|
flags += ZR_CLASS_MODEL_PATH;
|
|
if (logErrors)
|
|
{
|
|
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Playerclasses, "Config Validation", "Warning: Missing model_path at index %d.", classindex, model_path);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Validate only if not a pre-defined setting.
|
|
if (!StrEqual(model_path, "random", false) &&
|
|
!StrEqual(model_path, "random_public", false) &&
|
|
!StrEqual(model_path, "random_hidden", false) &&
|
|
!StrEqual(model_path, "random_admin", false) &&
|
|
!StrEqual(model_path, "random_mother_zombie", false) &&
|
|
!StrEqual(model_path, "default", false) &&
|
|
!StrEqual(model_path, "no_change", false))
|
|
{
|
|
// Check if the file exists.
|
|
if (!FileExists(model_path))
|
|
{
|
|
flags += ZR_CLASS_MODEL_PATH;
|
|
if (logErrors)
|
|
{
|
|
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Playerclasses, "Config Validation", "Warning: Invalid model_path at index %d. File not found: \"%s\"", classindex, model_path);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Alpha, initial.
|
|
new alpha_initial = ClassData[classindex][Class_AlphaInitial];
|
|
if (!(alpha_initial >= ZR_CLASS_ALPHA_INITIAL_MIN && alpha_initial <= ZR_CLASS_ALPHA_INITIAL_MAX))
|
|
{
|
|
flags += ZR_CLASS_ALPHA_INITIAL;
|
|
if (logErrors)
|
|
{
|
|
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Playerclasses, "Config Validation", "Warning: Invalid alpha_inital at index %d: %d", classindex, alpha_initial);
|
|
}
|
|
}
|
|
|
|
// Alpha, damaged.
|
|
new alpha_damaged = ClassData[classindex][Class_AlphaDamaged];
|
|
if (!(alpha_damaged >= ZR_CLASS_ALPHA_DAMAGED_MIN && alpha_damaged <= ZR_CLASS_ALPHA_DAMAGED_MAX))
|
|
{
|
|
flags += ZR_CLASS_ALPHA_DAMAGED;
|
|
if (logErrors)
|
|
{
|
|
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Playerclasses, "Config Validation", "Warning: Invalid alpha_damaged at index %d: %d", classindex, alpha_damaged);
|
|
}
|
|
}
|
|
|
|
// Alpha, damage.
|
|
new alpha_damage = ClassData[classindex][Class_AlphaDamage];
|
|
if (!(alpha_damage >= ZR_CLASS_ALPHA_DAMAGE_MIN && alpha_damage <= ZR_CLASS_ALPHA_DAMAGE_MAX))
|
|
{
|
|
flags += ZR_CLASS_ALPHA_DAMAGE;
|
|
if (logErrors)
|
|
{
|
|
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Playerclasses, "Config Validation", "Warning: Invalid alpha_damage at index %d: %d", classindex, alpha_damage);
|
|
}
|
|
}
|
|
|
|
// Overlay path.
|
|
decl String:overlay_path[PLATFORM_MAX_PATH];
|
|
decl String:overlay[PLATFORM_MAX_PATH];
|
|
if (strcopy(overlay_path, sizeof(overlay_path), ClassData[classindex][Class_OverlayPath]) > 0)
|
|
{
|
|
// Check if the file exists.
|
|
Format(overlay, sizeof(overlay), "materials/%s.vmt", overlay_path);
|
|
if (!FileExists(overlay))
|
|
{
|
|
flags += ZR_CLASS_OVERLAY_PATH;
|
|
if (logErrors)
|
|
{
|
|
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Playerclasses, "Config Validation", "Warning: Invalid overlay_path at index %d. File not found: \"%s\"", classindex, overlay_path);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Field of view.
|
|
new fov = ClassData[classindex][Class_Fov];
|
|
if (!(fov >= ZR_CLASS_FOV_MIN && fov <= ZR_CLASS_FOV_MAX))
|
|
{
|
|
flags += ZR_CLASS_FOV;
|
|
if (logErrors)
|
|
{
|
|
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Playerclasses, "Config Validation", "Warning: Invalid fov at index %d: %d", classindex, fov);
|
|
}
|
|
}
|
|
|
|
// Napalm time.
|
|
new Float:napalm_time = ClassData[classindex][Class_NapalmTime];
|
|
if (!(napalm_time >= ZR_CLASS_NAPALM_TIME_MIN && napalm_time <= ZR_CLASS_NAPALM_TIME_MAX))
|
|
{
|
|
flags += ZR_CLASS_NAPALM_TIME;
|
|
if (logErrors)
|
|
{
|
|
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Playerclasses, "Config Validation", "Warning: Invalid napalm_time at index %d: %0.2f", classindex, napalm_time);
|
|
}
|
|
}
|
|
|
|
// Immunity mode.
|
|
new ImmunityMode:immunityMode = ClassData[classindex][Class_ImmunityMode];
|
|
if (immunityMode == Immunity_Invalid)
|
|
{
|
|
flags += ZR_CLASS_IMMUNITY_MODE;
|
|
if (logErrors)
|
|
{
|
|
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Playerclasses, "Config Validation", "Warning: Invalid immunity_mode at index %d.", classindex);
|
|
}
|
|
}
|
|
|
|
// Immunity amount.
|
|
new immunityAmount = ClassData[classindex][Class_ImmunityAmount];
|
|
if (!ImmunityIsValidAmount(immunityMode, immunityAmount))
|
|
{
|
|
flags += ZR_CLASS_IMMUNITY_AMOUNT;
|
|
if (logErrors)
|
|
{
|
|
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Playerclasses, "Config Validation", "Warning: Invalid immunity_amount at index %d.", classindex);
|
|
}
|
|
}
|
|
|
|
// Immunity cooldown.
|
|
new immunityCooldown = ClassData[classindex][Class_ImmunityCooldown];
|
|
if (!ImmunityIsValidCooldown(immunityMode, immunityCooldown))
|
|
{
|
|
flags += ZR_CLASS_IMMUNITY_COOLDOWN;
|
|
if (logErrors)
|
|
{
|
|
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Playerclasses, "Config Validation", "Warning: Invalid immunity_cooldown at index %d.", classindex);
|
|
}
|
|
}
|
|
|
|
// Health.
|
|
new health = ClassData[classindex][Class_Health];
|
|
if (!(health >= ZR_CLASS_HEALTH_MIN && health <= ZR_CLASS_HEALTH_MAX))
|
|
{
|
|
flags += ZR_CLASS_HEALTH;
|
|
if (logErrors)
|
|
{
|
|
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Playerclasses, "Config Validation", "Warning: Invalid health at index %d: %d", classindex, health);
|
|
}
|
|
}
|
|
|
|
// Health regen interval.
|
|
new Float:regen_interval = ClassData[classindex][Class_HealthRegenInterval];
|
|
if (!(regen_interval >= ZR_CLASS_REGEN_INTERVAL_MIN && regen_interval <= ZR_CLASS_REGEN_INTERVAL_MAX))
|
|
{
|
|
flags += ZR_CLASS_HEALTH_REGEN_INTERVAL;
|
|
if (logErrors)
|
|
{
|
|
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Playerclasses, "Config Validation", "Warning: Invalid health_regen_interval at index %d: %0.2f", classindex, regen_interval);
|
|
}
|
|
}
|
|
|
|
// Health regen amount.
|
|
new regen_amount = ClassData[classindex][Class_HealthRegenAmount];
|
|
if (!(regen_amount >= ZR_CLASS_REGEN_AMOUNT_MIN && regen_amount <= ZR_CLASS_REGEN_AMOUNT_MAX))
|
|
{
|
|
flags += ZR_CLASS_HEALTH_REGEN_AMOUNT;
|
|
if (logErrors)
|
|
{
|
|
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Playerclasses, "Config Validation", "Warning: Invalid health_regen_amount at index %d: %d", classindex, regen_amount);
|
|
}
|
|
}
|
|
|
|
// Health infect gain.
|
|
new infect_gain = ClassData[classindex][Class_HealthInfectGain];
|
|
if (!(infect_gain >= ZR_CLASS_HEALTH_INFECT_GAIN_MIN && infect_gain <= ZR_CLASS_HEALTH_INFECT_GAIN_MAX))
|
|
{
|
|
flags += ZR_CLASS_HEALTH_INFECT_GAIN;
|
|
if (logErrors)
|
|
{
|
|
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Playerclasses, "Config Validation", "Warning: Invalid health_infect_gain at index %d: %d", classindex, infect_gain);
|
|
}
|
|
}
|
|
|
|
// Kill bonus.
|
|
new kill_bonus = ClassData[classindex][Class_KillBonus];
|
|
if (!(kill_bonus >= ZR_CLASS_KILL_BONUS_MIN && kill_bonus <= ZR_CLASS_KILL_BONUS_MAX))
|
|
{
|
|
flags += ZR_CLASS_KILL_BONUS;
|
|
if (logErrors)
|
|
{
|
|
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Playerclasses, "Config Validation", "Warning: Invalid kill_bonus at index %d: %d", classindex, kill_bonus);
|
|
}
|
|
}
|
|
|
|
// Speed.
|
|
new Float:speed = ClassData[classindex][Class_Speed];
|
|
new Float:min;
|
|
new Float:max;
|
|
switch (ClassSpeedMethod)
|
|
{
|
|
case ClassSpeed_LMV:
|
|
{
|
|
min = ZR_CLASS_SPEED_LMV_MIN;
|
|
max = ZR_CLASS_SPEED_LMV_MAX;
|
|
}
|
|
case ClassSpeed_Prop:
|
|
{
|
|
min = ZR_CLASS_SPEED_PROP_MIN;
|
|
max = ZR_CLASS_SPEED_PROP_MAX;
|
|
}
|
|
}
|
|
if (!(speed >= min && speed <= max))
|
|
{
|
|
flags += ZR_CLASS_SPEED;
|
|
if (logErrors)
|
|
{
|
|
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Playerclasses, "Config Validation", "Warning: Invalid speed at index %d: %0.2f", classindex, speed);
|
|
}
|
|
}
|
|
|
|
// Knockback.
|
|
new Float:knockback = ClassData[classindex][Class_KnockBack];
|
|
if (!(knockback >= ZR_CLASS_KNOCKBACK_MIN && knockback <= ZR_CLASS_KNOCKBACK_MAX))
|
|
{
|
|
flags += ZR_CLASS_KNOCKBACK;
|
|
if (logErrors)
|
|
{
|
|
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Playerclasses, "Config Validation", "Warning: Invalid knockback at index %d: %0.2f", classindex, knockback);
|
|
}
|
|
}
|
|
|
|
// Jump height.
|
|
new Float:jump_height = ClassData[classindex][Class_JumpHeight];
|
|
if (!(jump_height >= ZR_CLASS_JUMP_HEIGHT_MIN && jump_height <= ZR_CLASS_JUMP_HEIGHT_MAX))
|
|
{
|
|
flags += ZR_CLASS_JUMP_HEIGHT;
|
|
if (logErrors)
|
|
{
|
|
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Playerclasses, "Config Validation", "Warning: Invalid jump_height at index %d: %0.2f", classindex, jump_height);
|
|
}
|
|
}
|
|
|
|
// Jump distance.
|
|
new Float:jump_distance = ClassData[classindex][Class_JumpDistance];
|
|
if (!(jump_distance >= ZR_CLASS_JUMP_DISTANCE_MIN && jump_distance <= ZR_CLASS_JUMP_DISTANCE_MAX))
|
|
{
|
|
flags += ZR_CLASS_JUMP_DISTANCE;
|
|
if (logErrors)
|
|
{
|
|
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Playerclasses, "Config Validation", "Warning: Invalid jump_distance at index %d: %0.2f", classindex, jump_distance);
|
|
}
|
|
}
|
|
|
|
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.
|
|
new ImmunityMode:immunityMode = attributes[ClassEdit_ImmunityMode];
|
|
if (immunityMode == Immunity_Invalid)
|
|
{
|
|
flags += ZR_CLASS_IMMUNITY_MODE;
|
|
}
|
|
|
|
// Immunity amount.
|
|
// TODO: Validate amount value. This depends on the immunity mode.
|
|
|
|
// 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)
|
|
{
|
|
new Float:min;
|
|
new Float:max;
|
|
switch (ClassSpeedMethod)
|
|
{
|
|
case ClassSpeed_LMV:
|
|
{
|
|
min = ZR_CLASS_SPEED_LMV_MIN;
|
|
max = ZR_CLASS_SPEED_LMV_MAX;
|
|
}
|
|
case ClassSpeed_Prop:
|
|
{
|
|
min = ZR_CLASS_SPEED_PROP_MIN;
|
|
max = ZR_CLASS_SPEED_PROP_MAX;
|
|
}
|
|
}
|
|
if (!(speed >= min && 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.
|
|
*
|
|
* @param classIndex The class index to validate.
|
|
* @return True if the class exist, false otherwise.
|
|
*/
|
|
stock bool:ClassValidateIndex(classIndex)
|
|
{
|
|
if (classIndex >= 0 && classIndex < ClassCount)
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Compares the class team ID with a team ID.
|
|
*
|
|
* @param index Index of the class in a class cache or a client index,
|
|
* depending on the cache type specified.
|
|
* @param teamid The team ID to compare with the class.
|
|
* @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.
|
|
* ZR_CLASS_CACHE_PLAYER - Player cache. If this one is used,
|
|
* index will be used as a client index.
|
|
* @return True if equal, false otherwise.
|
|
*/
|
|
stock bool:ClassTeamCompare(index, teamid, cachetype = ZR_CLASS_CACHE_MODIFIED)
|
|
{
|
|
switch (cachetype)
|
|
{
|
|
case ZR_CLASS_CACHE_ORIGINAL:
|
|
{
|
|
if (ClassData[index][Class_Team] == teamid)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
case ZR_CLASS_CACHE_MODIFIED:
|
|
{
|
|
if (ClassDataCache[index][Class_Team] == teamid)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
case ZR_CLASS_CACHE_PLAYER:
|
|
{
|
|
if (ClassPlayerCache[index][Class_Team] == teamid)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Gets the first class index of a class with the specified name (not a case
|
|
* sensitive search).
|
|
*
|
|
* @param name The name 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 ClassGetIndex(const String:name[], cachetype = ZR_CLASS_CACHE_MODIFIED)
|
|
{
|
|
decl String:current_name[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 name and compare it with the specified class name.
|
|
ClassGetName(classindex, current_name, sizeof(current_name), cachetype);
|
|
if (strcmp(name, current_name, 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.
|
|
*
|
|
* @param client The client index.
|
|
* @return The active class index. -1 on error or if a spectactor.
|
|
*/
|
|
stock ClassGetActiveIndex(client)
|
|
{
|
|
new teamid;
|
|
|
|
if (!ZRIsClientOnTeam(client))
|
|
{
|
|
// No active team.
|
|
return -1;
|
|
}
|
|
|
|
// Check if the player currently is in admin mode.
|
|
if (ClassPlayerInAdminMode[client])
|
|
{
|
|
teamid = ZR_CLASS_TEAM_ADMINS;
|
|
}
|
|
else
|
|
{
|
|
// Not in admin mode, check if player is human or zombie.
|
|
if (InfectIsClientHuman(client))
|
|
{
|
|
teamid = ZR_CLASS_TEAM_HUMANS;
|
|
}
|
|
else
|
|
{
|
|
teamid = ZR_CLASS_TEAM_ZOMBIES;
|
|
}
|
|
}
|
|
|
|
// Return the active class for the active team.
|
|
return ClassSelected[client][teamid];
|
|
}
|
|
|
|
/**
|
|
* Gets the multiplier for the specified team and attribute.
|
|
*
|
|
* @param client The client index.
|
|
* @param attribute Specifies what attribute multiplier to get.
|
|
* @return Multiplier for the specified team and attribute. 1.0 if the
|
|
* client is in admin mode.
|
|
*/
|
|
stock Float:ClassGetAttributeMultiplier(client, ClassMultipliers:attribute)
|
|
{
|
|
new teamid;
|
|
|
|
// Check if player is not in admin mode.
|
|
if (!ClassPlayerInAdminMode[client])
|
|
{
|
|
// Not in admin mode, check if player is human or zombie.
|
|
if (InfectIsClientHuman(client))
|
|
{
|
|
teamid = ZR_CLASS_TEAM_HUMANS;
|
|
}
|
|
else
|
|
{
|
|
teamid = ZR_CLASS_TEAM_ZOMBIES;
|
|
}
|
|
|
|
// Get multiplier for the specified team and attribute.
|
|
return Float:ClassMultiplierCache[teamid][attribute];
|
|
}
|
|
else
|
|
{
|
|
// Do not use multipliers on admin classes.
|
|
return 1.0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if a class pass the specified filter.
|
|
*
|
|
* @param index Index of the class in a class cache or a client index,
|
|
* depending on the cache type specified.
|
|
* @param filter Structure with filter settings.
|
|
* @param cachetype Optional. Specifies what class cache to read from.
|
|
* ZR_CLASS_CACHE_ORIGINAL - Unchanced class data.
|
|
* ZR_CLASS_CACHE_MODIFIED (default) - Changed/newest
|
|
* class data.
|
|
* ZR_CLASS_CACHE_PLAYER - Player cache. If this one is
|
|
* used index will be used as a client index.
|
|
* @return True if passed, false otherwise.
|
|
*/
|
|
stock bool:ClassFilterMatch(index, filter[ClassFilter], cachetype = ZR_CLASS_CACHE_MODIFIED)
|
|
{
|
|
// Check if the class is disabled and the enabled attribute is NOT ignored.
|
|
if (!filter[ClassFilter_IgnoreEnabled] && !ClassIsEnabled(index, cachetype))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Check if class flags pass the flag filter.
|
|
if (!ClassFlagFilterMatch(index, filter[ClassFilter_RequireFlags], filter[ClassFilter_DenyFlags], cachetype))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Get class group name.
|
|
decl String:groupname[64];
|
|
groupname[0] = 0;
|
|
ClassGetGroup(index, groupname, sizeof(groupname), cachetype);
|
|
|
|
// Check if a client is specified in the filter.
|
|
new client = filter[ClassFilter_Client];
|
|
if (ZRIsClientValid(client))
|
|
{
|
|
// Check if a group is set on the class.
|
|
if (strlen(groupname))
|
|
{
|
|
// Check if the client is not a member of that group.
|
|
if (!ZRIsClientInGroup(client, groupname))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check if classes with groups are set to be excluded.
|
|
if (client < 0)
|
|
{
|
|
// Exclude class if it has a group name.
|
|
if (strlen(groupname))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// The class passed the filter.
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Check if a class pass the specified flag filters.
|
|
*
|
|
* @param index Index of the class in a class cache or a client index,
|
|
* depending on the cache type specified.
|
|
* @param require Class flags to require. 0 for no filter.
|
|
* @param deny Class flags to exclude. 0 for no filter.
|
|
* @param cachetype Specifies what class cache to read from. Options:
|
|
* ZR_CLASS_CACHE_ORIGINAL - Unchanced class data.
|
|
* ZR_CLASS_CACHE_MODIFIED (default) - Changed/newest
|
|
* class data.
|
|
* ZR_CLASS_CACHE_PLAYER - Player cache. If this one is
|
|
* used index will be used as a client index.
|
|
* @return True if passed, false otherwise.
|
|
*/
|
|
stock bool:ClassFlagFilterMatch(index, require, deny, cachetype)
|
|
{
|
|
new flags;
|
|
new bool:requirepassed = false;
|
|
new bool:denypassed = false;
|
|
|
|
// Do quick check for optimization reasons: Check if no flags are specified.
|
|
if (require == 0 && deny == 0)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// Cache flags.
|
|
flags = ClassGetFlags(index, cachetype);
|
|
|
|
// Match require filter.
|
|
if (require == 0 || flags & require)
|
|
{
|
|
// All required flags are set.
|
|
requirepassed = true;
|
|
}
|
|
|
|
// Match deny filter.
|
|
if (deny == 0 || !(flags & deny))
|
|
{
|
|
// No denied flags are set.
|
|
denypassed = true;
|
|
}
|
|
|
|
// Check if required and denied flags passed the filter.
|
|
if (requirepassed && denypassed)
|
|
{
|
|
// The class pass the filter.
|
|
return true;
|
|
}
|
|
|
|
// The class didn't pass the filter.
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Decides whether a class selection menu should be enabled. The decision is
|
|
* based on zr_class_allow_* console variables.
|
|
*
|
|
* @param team Optional. Team ID to match. Default is all.
|
|
* @param filter Optional. Filter to use on classes.
|
|
* @return True if allowed, false otherwise.
|
|
*/
|
|
bool:ClassAllowSelection(client, team = -1, filter[ClassFilter] = ClassNoFilter)
|
|
{
|
|
// Get selection settings.
|
|
new bool:zombie = GetConVarBool(g_hCvarsList[CVAR_CLASSES_ZOMBIE_SELECT]);
|
|
new bool:human = GetConVarBool(g_hCvarsList[CVAR_CLASSES_HUMAN_SELECT]);
|
|
new bool:admin = GetConVarBool(g_hCvarsList[CVAR_CLASSES_ADMIN_SELECT]);
|
|
|
|
// Since admin mode classes are optional they must be counted to verify
|
|
// that they exist.
|
|
new bool:adminexist;
|
|
|
|
// Check if player is admin.
|
|
new bool:isadmin = ZRIsClientAdmin(client);
|
|
|
|
// Only count admin mode classes if client is admin.
|
|
if (isadmin)
|
|
{
|
|
adminexist = ClassCountTeam(ZR_CLASS_TEAM_ADMINS, filter) > 0;
|
|
}
|
|
|
|
// Check if a team id is specified.
|
|
if (team >= 0)
|
|
{
|
|
// Check team and return the corresponding selection setting.
|
|
switch (team)
|
|
{
|
|
case ZR_CLASS_TEAM_ZOMBIES:
|
|
{
|
|
return zombie;
|
|
}
|
|
case ZR_CLASS_TEAM_HUMANS:
|
|
{
|
|
return human;
|
|
}
|
|
case ZR_CLASS_TEAM_ADMINS:
|
|
{
|
|
// Player must be admin to select admin mode classes.
|
|
return admin && isadmin && adminexist;
|
|
}
|
|
}
|
|
|
|
// Team ID didn't match.
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
// Check zombie and human.
|
|
return zombie || human;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets all class indexes or from a specified team, and adds them to the
|
|
* specified array.
|
|
*
|
|
* @param array The destination array to add class indexes.
|
|
* @param teamfilter Optional. The team ID to filter. A negative value
|
|
* for no filter (default).
|
|
* @param filter Optional. Structure with filter settings.
|
|
* @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 True on success. False on error or if no classes were added or
|
|
* found.
|
|
*/
|
|
stock bool:ClassAddToArray(Handle:array, teamfilter = -1, filter[ClassFilter] = ClassNoFilter, cachetype = ZR_CLASS_CACHE_MODIFIED)
|
|
{
|
|
// Validate the array.
|
|
if (array == INVALID_HANDLE)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Check if there are no classes.
|
|
if (ClassCount == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Store a local boolean that says if the user specified a team filter or not.
|
|
new bool:hasteamfilter = bool:(teamfilter >= 0);
|
|
new classesadded;
|
|
|
|
// Loop through all classes.
|
|
for (new classindex = 0; classindex < ClassCount; classindex++)
|
|
{
|
|
// Validate filter settings.
|
|
if (!ClassFilterMatch(classindex, filter, cachetype))
|
|
{
|
|
// The class is didn't pass the filter, skip class.
|
|
continue;
|
|
}
|
|
|
|
// Check team filtering.
|
|
if (hasteamfilter)
|
|
{
|
|
// Only add classes with matching team ID.
|
|
if (ClassGetTeamID(classindex, cachetype) == teamfilter)
|
|
{
|
|
// Team ID match. Add class index to array.
|
|
PushArrayCell(array, classindex);
|
|
classesadded++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// No filter. Add any class to the array.
|
|
PushArrayCell(array, classindex);
|
|
classesadded++;
|
|
}
|
|
}
|
|
|
|
if (classesadded)
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// No classes were found/added.
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Counts total classes or classes in the specified team.
|
|
*
|
|
* @param teamfilter Optional. The team ID to filter. Negative value for
|
|
* no filter (default).
|
|
* @param filter Optional. Structure with filter settings.
|
|
* @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 Number of total classes or classes in the specified team.
|
|
*/
|
|
stock ClassCountTeam(teamfilter = -1, filter[ClassFilter] = ClassNoFilter, cachetype = ZR_CLASS_CACHE_MODIFIED)
|
|
{
|
|
// Check if there are no classes.
|
|
if (ClassCount == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// Store a local boolean that says if the user specified a team filter or not.
|
|
new bool:hasteamfilter = bool:(teamfilter >= 0);
|
|
new count;
|
|
|
|
// Loop through all classes.
|
|
for (new classindex = 0; classindex < ClassCount; classindex++)
|
|
{
|
|
// Validate filter settings.
|
|
if (!ClassFilterMatch(classindex, filter, cachetype))
|
|
{
|
|
// The class is didn't pass the filter, skip class.
|
|
continue;
|
|
}
|
|
|
|
// Check team filtering.
|
|
if (hasteamfilter)
|
|
{
|
|
// Only add classes with matching team ID.
|
|
if (ClassGetTeamID(classindex, cachetype) == teamfilter)
|
|
{
|
|
// Team ID match. Increment counter.
|
|
count++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// No filter. Increment counter.
|
|
count++;
|
|
}
|
|
}
|
|
|
|
// Return number of classes found.
|
|
return count;
|
|
}
|
|
|
|
/**
|
|
* Gets a random class index from a specified team or from all classes.
|
|
*
|
|
* @param teamfilter Optional. The team ID to filter. A negative value
|
|
* for no filter (default).
|
|
* @param filter Optional. Structure with filter settings.
|
|
* @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, or -1 on error.
|
|
*/
|
|
stock ClassGetRandomClass(teamfilter = -1, filter[ClassFilter] = ClassNoSpecialClasses, cachetype = ZR_CLASS_CACHE_MODIFIED)
|
|
{
|
|
new Handle:classarray;
|
|
new arraycount;
|
|
new randnum;
|
|
new buffer;
|
|
|
|
classarray = CreateArray();
|
|
|
|
// Try to get a class list.
|
|
if (ClassAddToArray(classarray, teamfilter, filter, cachetype))
|
|
{
|
|
// Get a random index from the new class array.
|
|
arraycount = GetArraySize(classarray);
|
|
randnum = GetRandomInt(0, arraycount - 1);
|
|
|
|
// Return the value at the random index.
|
|
buffer = GetArrayCell(classarray, randnum);
|
|
CloseHandle(classarray);
|
|
return buffer;
|
|
}
|
|
else
|
|
{
|
|
// Failed to get a random class.
|
|
CloseHandle(classarray);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets the first class index, or the first class index with the specified team
|
|
* ID.
|
|
*
|
|
* @param teamfilter Optional. The team ID to filter. A negative value
|
|
* for no filter (default).
|
|
* @param filter Optional. Structure with filter settings.
|
|
* @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 first class index, or the first class index with the specified
|
|
* team ID. -1 on error.
|
|
*/
|
|
stock ClassGetFirstClass(teamfilter = -1, filter[ClassFilter] = ClassNoSpecialClasses, cachetype = ZR_CLASS_CACHE_MODIFIED)
|
|
{
|
|
// Check if there are no classes.
|
|
if (ClassCount == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
new bool:hasteamfilter = bool:(teamfilter >= 0);
|
|
|
|
// Loop through all classes.
|
|
for (new classindex = 0; classindex < ClassCount; classindex++)
|
|
{
|
|
// Validate filter settings.
|
|
if (!ClassFilterMatch(classindex, filter, cachetype))
|
|
{
|
|
// The class is didn't pass the filter, skip class.
|
|
continue;
|
|
}
|
|
|
|
if (hasteamfilter)
|
|
{
|
|
if (teamfilter == ClassGetTeamID(classindex, cachetype))
|
|
{
|
|
// Team ID match. Return the class index.
|
|
return classindex;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// No team filter. Return the class index.
|
|
return classindex;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* Gets the first class marked as default for the specified team.
|
|
*
|
|
* @param teamid The team ID.
|
|
* @param filter Optional. Structure with filter settings. Default
|
|
* is to deny classes with special flags
|
|
* (ZR_CLASS_SPECIALFLAGS).
|
|
* @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 first default class index. -1 on error.
|
|
*/
|
|
stock ClassGetDefaultClass(teamid, filter[ClassFilter] = ClassNoSpecialClasses, cachetype = ZR_CLASS_CACHE_MODIFIED)
|
|
{
|
|
new Handle:classarray;
|
|
new arraycount;
|
|
new classindex;
|
|
|
|
classarray = CreateArray();
|
|
|
|
// Get all classes from the specified team.
|
|
if (!ClassAddToArray(classarray, teamid, filter, cachetype))
|
|
{
|
|
// Failed to get classes.
|
|
CloseHandle(classarray);
|
|
return -1;
|
|
}
|
|
|
|
// Loop through all classes and return the first class marked as team default.
|
|
arraycount = GetArraySize(classarray);
|
|
for (new i = 0; i < arraycount; i++)
|
|
{
|
|
// Get class index from the array.
|
|
classindex = GetArrayCell(classarray, i);
|
|
|
|
// Check if the current class is marked as team default.
|
|
if (ClassGetTeamDefault(classindex, cachetype))
|
|
{
|
|
// Default class found.
|
|
CloseHandle(classarray);
|
|
return classindex;
|
|
}
|
|
}
|
|
|
|
CloseHandle(classarray);
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* Gets the default class index for the specified team configured to be used
|
|
* when players join the server.
|
|
*
|
|
* @param teamid The team ID.
|
|
* @param filter Optional. Structure with filter settings. Default is to
|
|
* deny classes with special flags (ZR_CLASS_SPECIALFLAGS).
|
|
* @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 of the default class for the specified team if
|
|
* successful. -1 on critical errors. Otherwise it will try to fall
|
|
* back on the first class in the specified team.
|
|
*/
|
|
stock ClassGetDefaultSpawnClass(teamid, filter[ClassFilter] = ClassNoSpecialClasses, cachetype = ZR_CLASS_CACHE_MODIFIED)
|
|
{
|
|
decl String:classname[64];
|
|
new classindex;
|
|
|
|
// Get the default class name from the correct CVAR depending on teamid.
|
|
switch (teamid)
|
|
{
|
|
case ZR_CLASS_TEAM_ZOMBIES:
|
|
{
|
|
GetConVarString(g_hCvarsList[CVAR_CLASSES_DEFAULT_ZOMBIE], classname, sizeof(classname));
|
|
}
|
|
case ZR_CLASS_TEAM_HUMANS:
|
|
{
|
|
GetConVarString(g_hCvarsList[CVAR_CLASSES_DEFAULT_HUMAN], classname, sizeof(classname));
|
|
}
|
|
case ZR_CLASS_TEAM_ADMINS:
|
|
{
|
|
GetConVarString(g_hCvarsList[CVAR_CLASSES_DEFAULT_ADMIN_MODE], classname, sizeof(classname));
|
|
}
|
|
default:
|
|
{
|
|
// Invalid team ID.
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
// Check if the class name isn't empty.
|
|
if (strlen(classname) > 0)
|
|
{
|
|
// Check if the user set "random" as default class.
|
|
if (StrEqual(classname, "random", false))
|
|
{
|
|
// Get a list of all classes with the specified team ID. Deny
|
|
// classes with special flags.
|
|
classindex = ClassGetRandomClass(teamid, filter, cachetype);
|
|
|
|
// Validate the result, in case there were errors.
|
|
if (ClassValidateIndex(classindex))
|
|
{
|
|
return classindex;
|
|
}
|
|
else
|
|
{
|
|
// Invalid index. The ClassGetRandomClass function is pretty
|
|
// failsafe. So if we can't get a class index here, it's a
|
|
// critical error. No reason to fall back on other solutions.
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// The user set a spesific class.
|
|
|
|
// Try to get the class index with the specified class name.
|
|
classindex = ClassGetIndex(classname, cachetype);
|
|
|
|
// Validate the class index and check if the team IDs match.
|
|
if (ClassValidateIndex(classindex) && (teamid == ClassGetTeamID(classindex, cachetype)))
|
|
{
|
|
return classindex;
|
|
}
|
|
else
|
|
{
|
|
// The class index is invalid or the team IDs didn't match.
|
|
// Because it's user input, we'll fall back to the first class
|
|
// 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);
|
|
|
|
// Validate the new index.
|
|
if (ClassValidateIndex(classindex))
|
|
{
|
|
// Log a warning.
|
|
//LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Playerclasses, "Default Spawn Class", "Warning: The default class name \"%s\" does not exist or matches the team ID.", classname);
|
|
return classindex;
|
|
}
|
|
else
|
|
{
|
|
// Something went wrong. This is a critical error. There's
|
|
// probably missing classes with no special flags set.
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Blank class name, get the default class and return the index.
|
|
return ClassGetDefaultClass(teamid, filter, cachetype);
|
|
}
|
|
}
|