sm-zombiereloaded-3/src/zr/hitgroups.inc

682 lines
21 KiB
SourcePawn

/*
* ============================================================================
*
* Zombie:Reloaded
*
* File: hitgroup.inc
* Type: Core
* Description: API for loading hitgroup specific settings.
*
* 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/>.
*
* ============================================================================
*/
/**
* Maximum length for a hitgroup name
*/
#define HITGROUPS_MAX_LENGTH 32
/**
* @section Player hitgroup values.
*/
#define HITGROUP_GENERIC 0
#define HITGROUP_HEAD 1
#define HITGROUP_CHEST 2
#define HITGROUP_STOMACH 3
#define HITGROUP_LEFTARM 4
#define HITGROUP_RIGHTARM 5
#define HITGROUP_LEFTLEG 6
#define HITGROUP_RIGHTLEG 7
#define HITGROUP_GEAR 10
/**
* @endsection
*/
/**
* Hitgroup config data indexes.
*/
enum HitgroupsData
{
HITGROUPS_DATA_NAME = 0,
HITGROUPS_DATA_INDEX,
HITGROUPS_DATA_DAMAGE,
HITGROUPS_DATA_KNOCKBACK,
}
/**
* Array handle to store hitgroups data.
*/
new Handle:arrayHitgroups = INVALID_HANDLE;
/**
* Create commands related to config here.
*/
HitgroupsOnCommandsCreate()
{
// Create config admin commands.
RegConsoleCmd("zr_hitgroup", HitgroupsCommand, "Toggles or sets if a zombie's hitgroup can be damaged. Usage: zr_hitgroup <hitgroup name> [1/0]");
RegConsoleCmd("zr_hitgroup_enable_all", HitgroupsEnableAllCommand, "Enables all zombie hitgroups to be damaged. Usage: zr_hitgroup_enable_all");
RegConsoleCmd("zr_hitgroup_headshots_only", HitgroupsHeadshotsOnlyCommand, "Disables all zombie hitgroups but the head. Usage: zr_hitgroup_headshots_only");
}
/**
* Loads hitgroup data from file.
*/
HitgroupsLoad()
{
// Register config file.
ConfigRegisterConfig(File_Hitgroups, Structure_Keyvalue, CONFIG_FILE_ALIAS_HITGROUPS);
// If module is disabled, then stop.
new bool:hitgroups = GetConVarBool(g_hCvarsList[CVAR_HITGROUPS]);
if (!hitgroups)
{
return;
}
// Get hitgroups config path.
decl String:pathhitgroups[PLATFORM_MAX_PATH];
new bool:exists = ConfigGetCvarFilePath(CVAR_CONFIG_PATH_HITGROUPS, pathhitgroups);
// If file doesn't exist, then log and stop.
if (!exists)
{
// Log failure.
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Hitgroups, "Config Validation", "Missing hitgroups config file: %s", pathhitgroups);
return;
}
// Set the path to the config file.
ConfigSetConfigPath(File_Hitgroups, pathhitgroups);
// Load config from file and create array structure.
new bool:success = ConfigLoadConfig(File_Hitgroups, arrayHitgroups);
// Unexpected error, stop plugin.
if (!success)
{
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Hitgroups, "Config Validation", "Unexpected error encountered loading: %s", pathhitgroups);
return;
}
// Validate hitgroups config.
new size = GetArraySize(arrayHitgroups);
if (!size)
{
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Hitgroups, "Config Validation", "No usable data found in hitgroups config file: %s", pathhitgroups);
}
// Now copy data to array structure.
HitgroupsCacheData();
// Set config data.
ConfigSetConfigLoaded(File_Hitgroups, true);
ConfigSetConfigReloadFunc(File_Hitgroups, GetFunctionByName(GetMyHandle(), "HitgroupsOnConfigReload"));
ConfigSetConfigHandle(File_Hitgroups, arrayHitgroups);
}
/**
* Caches hitgroup data from file into arrays.
* Make sure the file is loaded before (ConfigLoadConfig) to prep array structure.
*/
HitgroupsCacheData()
{
// Get config's file path.
decl String:pathhitgroups[PLATFORM_MAX_PATH];
ConfigGetConfigPath(File_Hitgroups, pathhitgroups, sizeof(pathhitgroups));
new Handle:kvHitgroups;
new bool:success = ConfigOpenConfigFile(File_Hitgroups, kvHitgroups);
if (!success)
{
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Hitgroups, "Config Validation", "Unexpected error caching data from hitgroups config file: %s", pathhitgroups);
}
decl String:hitgroupname[HITGROUPS_MAX_LENGTH];
// x = array index
new size = GetArraySize(arrayHitgroups);
for (new x = 0; x < size; x++)
{
HitgroupsGetName(x, hitgroupname, sizeof(hitgroupname));
KvRewind(kvHitgroups);
if (!KvJumpToKey(kvHitgroups, hitgroupname))
{
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Hitgroups, "Config Validation", "Couldn't cache hitgroup data for: %s (check hitgroup config)", hitgroupname);
continue;
}
// General
new index = KvGetNum(kvHitgroups, "index", -1);
// Damage
new bool:damage = ConfigKvGetStringBool(kvHitgroups, "damage", "yes");
// Knockback (module)
new Float:knockback = KvGetFloat(kvHitgroups, "knockback", 1.0);
new Handle:arrayHitgroup = GetArrayCell(arrayHitgroups, x);
// Push data into array.
PushArrayCell(arrayHitgroup, index); // Index: 1
PushArrayCell(arrayHitgroup, damage); // Index: 2
PushArrayCell(arrayHitgroup, knockback); // Index: 3
}
// We're done with this file now, so we can close it.
CloseHandle(kvHitgroups);
}
/**
* Called when configs are being reloaded.
*
* @param config The config being reloaded. (only if 'all' is false)
*/
public HitgroupsOnConfigReload(ConfigFile:config)
{
// Reload hitgroups config.
HitgroupsLoad();
}
/**
* Find the index at which the hitgroup's name is at.
*
* @param hitgroup The higroup name.
* @param maxlen (Only if 'overwritename' is true) The max length of the hitgroup name.
* @param overwritename (Optional) If true, the hitgroup given will be overwritten with the name from the config.
* @return The array index containing the given hitgroup name.
*/
stock HitgroupsNameToIndex(String:hitgroup[], maxlen = 0, bool:overwritename = false)
{
decl String:hitgroupname[HITGROUPS_MAX_LENGTH];
// x = Array index.
new size = GetArraySize(arrayHitgroups);
for (new x = 0; x < size; x++)
{
HitgroupsGetName(x, hitgroupname, sizeof(hitgroupname));
// If names match, then return index.
if (StrEqual(hitgroup, hitgroupname, false))
{
// If 'overwrite' name is true, then overwrite the old string with new.
if (overwritename)
{
// Copy config name to return string.
strcopy(hitgroup, maxlen, hitgroupname);
}
// Return this index.
return x;
}
}
// Name doesn't exist.
return -1;
}
/**
* Find the array index at which the hitgroup index is at.
*
* @param hitgroup The hitgroup index to search for.
* @return The array index that contains the given hitgroup index.
*/
stock HitgroupToIndex(hitgroup)
{
// x = Array index.
new size = GetArraySize(arrayHitgroups);
for (new x = 0; x < size; x++)
{
// Get hitgroup index at this array index.
new index = HitgroupsGetIndex(x);
// If hitgroup indexes match, then return array index.
if (hitgroup == index)
{
return x;
}
}
// Hitgroup index doesn't exist.
return -1;
}
/**
* Gets the name of a hitgroup at a given index. (static)
* @param index The hitgroup index.
* @param hitgroup The string to return name in.
* @param maxlen The max length of the string.
*/
stock HitgroupsGetName(index, String:hitgroup[], maxlen)
{
// Get array handle of hitgroup at given index.
new Handle:arrayHitgroup = GetArrayCell(arrayHitgroups, index);
// Get hitgroup name.
GetArrayString(arrayHitgroup, _:HITGROUPS_DATA_NAME, hitgroup, maxlen);
}
/**
* Retrieve hitgroup index. (static)
*
* @param index The array index.
* @return The hitgroup index.
*/
stock HitgroupsGetIndex(index)
{
// Get array handle of hitgroup at given index.
new Handle:arrayHitgroup = GetArrayCell(arrayHitgroups, index);
// Return hitgroup index of the hitgroup.
return GetArrayCell(arrayHitgroup, _:HITGROUPS_DATA_INDEX);
}
/**
* Set hitgroup damage value. (dynamic)
*
* @param index The array index.
* @param candamage True to allow damage to hitgroup, false to block damage.
*/
stock HitgroupsSetDamage(index, bool:candamage)
{
// Get array handle of hitgroup at given index.
new Handle:arrayHitgroup = GetArrayCell(arrayHitgroups, index);
// Return true if hitgroup can be damaged, false if not.
SetArrayCell(arrayHitgroup, _:HITGROUPS_DATA_DAMAGE, candamage);
}
/**
* Retrieve hitgroup damage value. (dynamic)
*
* @param index The array index.
* @return True if hitgroup can be damaged, false if not.
*/
stock bool:HitgroupsCanDamage(index)
{
// Get array handle of hitgroup at given index.
new Handle:arrayHitgroup = GetArrayCell(arrayHitgroups, index);
// Return true if hitgroup can be damaged, false if not.
return bool:GetArrayCell(arrayHitgroup, _:HITGROUPS_DATA_DAMAGE);
}
/**
* Set hitgroup knockback value. (dynamic)
*
* @param index The array index.
* @param knockback The knockback multiplier for the hitgroup.
*/
stock HitgroupsSetKnockback(index, Float:knockback)
{
// Get array handle of hitgroup at given index.
new Handle:arrayHitgroup = GetArrayCell(arrayHitgroups, index);
// Return the knockback multiplier for the hitgroup.
SetArrayCell(arrayHitgroup, _:HITGROUPS_DATA_KNOCKBACK, knockback);
}
/**
* Retrieve hitgroup knockback value. (dynamic)
*
* @param index The array index.
* @return The knockback multiplier of the hitgroup.
*/
stock Float:HitgroupsGetKnockback(index)
{
// Get array handle of hitgroup at given index.
new Handle:arrayHitgroup = GetArrayCell(arrayHitgroups, index);
// Return the knockback multiplier for the hitgroup.
return Float:GetArrayCell(arrayHitgroup, _:HITGROUPS_DATA_KNOCKBACK);
}
/**
* Sends list of hitgroups to client.
*
* @param client The client index.
* @return True if sent successfully, false if not.
*/
bool:HitgroupsMenuHitgroups(client)
{
// If hitgroups is disabled, then stop.
new bool:hitgroups = GetConVarBool(g_hCvarsList[CVAR_HITGROUPS]);
if (!hitgroups)
{
return false;
}
// Create menu handle.
new Handle:menu_hitgroups = CreateMenu(HitgroupsMenuHitgroupsHandle);
// Set client as translation target.
SetGlobalTransTarget(client);
decl String:title[MENU_LINE_HUGE_LENGTH];
decl String:enableall[MENU_LINE_REG_LENGTH];
decl String:headshotsonly[MENU_LINE_REG_LENGTH];
// Format menu options.
Format(title, sizeof(title), "%t\n ", "Hitgroups menu hitgroups title");
Format(enableall, sizeof(enableall), "%t", "Hitgroups menu hitgroups enable all");
Format(headshotsonly, sizeof(headshotsonly), "%t\n ", "Hitgroups menu hitgroups headshots only");
// Add options to menu.
SetMenuTitle(menu_hitgroups, title);
AddMenuItem(menu_hitgroups, "Enable All", enableall);
AddMenuItem(menu_hitgroups, "Headshots Only", headshotsonly);
decl String:hitgroupoption[MENU_LINE_REG_LENGTH];
decl String:hitgroupcandamage[MENU_LINE_SMALL_LENGTH];
decl String:hitgroupid[4];
// x = Hitgroup index.
new size = GetArraySize(arrayHitgroups);
for (new x = 0; x < size; x++)
{
// Get hitgroup name.
HitgroupsGetName(x, hitgroupoption, sizeof(hitgroupoption));
IntToString(x, hitgroupid, sizeof(hitgroupid));
// Convert bool to "On/Off"
ConfigBoolToSetting(HitgroupsCanDamage(x), hitgroupcandamage, sizeof(hitgroupcandamage), false, client);
// Format "on/off" to the option.
Format(hitgroupoption, sizeof(hitgroupoption), "%s: %s", hitgroupoption, hitgroupcandamage);
// Add option to menu.
AddMenuItem(menu_hitgroups, hitgroupid, hitgroupoption);
}
// Create a "Back" button to the main admin menu.
SetMenuExitBackButton(menu_hitgroups, true);
// Send menu.
DisplayMenu(menu_hitgroups, client, MENU_TIME_FOREVER);
return true;
}
/**
* Called when client selects option in the infect clients menu, and handles it.
* @param menu_hitgroups Handle of the menu being used.
* @param action The action done on the menu (see menus.inc, enum MenuAction).
* @param client The client index.
* @param slot The slot index selected (starting from 0).
*/
public HitgroupsMenuHitgroupsHandle(Handle:menu_hitgroups, MenuAction:action, client, slot)
{
// Client selected an option.
if (action == MenuAction_Select)
{
switch(slot)
{
// Enable all hitgroups.
case 0:
{
// x = Hitgroup index.
new size = GetArraySize(arrayHitgroups);
for (new x = 0; x < size; x++)
{
// Enable hitgroup.
HitgroupsSetDamage(x, true);
}
// Tell the server that all hitgroups have been enabled.
TranslationPrintToChatAll(true, false, "Hitgroups command enable all successful");
}
// Headshots only.
case 1:
{
// x = Hitgroup index.
new size = GetArraySize(arrayHitgroups);
for (new x = 0; x < size; x++)
{
if (HitgroupsGetIndex(x) == HITGROUP_HEAD)
{
// Enable hitgroup.
HitgroupsSetDamage(x, true);
continue;
}
// Disable hitgroup.
HitgroupsSetDamage(x, false);
}
// Tell the server that headshots only been enabled.
TranslationPrintToChatAll(true, false, "Hitgroups command headshots only successful");
}
default:
{
// Get selected hitgroup index.
decl String:hitgroupid[4];
GetMenuItem(menu_hitgroups, slot, hitgroupid, sizeof(hitgroupid));
new hitgroup = StringToInt(hitgroupid);
// Toggle value.
new bool:hitgroupcandamage = HitgroupsCanDamage(hitgroup);
HitgroupsSetDamage(hitgroup, !hitgroupcandamage);
}
}
// Re-send menu.
HitgroupsMenuHitgroups(client);
}
// Client closed the menu.
if (action == MenuAction_Cancel)
{
// Client hit "Back" button.
if (slot == MenuCancel_ExitBack)
{
// Re-open admin menu.
ZAdminMenu(client);
}
}
// Client hit "Exit" button.
else if (action == MenuAction_End)
{
CloseHandle(menu_hitgroups);
}
}
/**
* Command callback (zr_hitgroup)
* Toggles or sets if a zombie's hitgroup can be damaged.
*
* @param client The client index.
* @param argc Argument count.
*/
public Action:HitgroupsCommand(client, argc)
{
// Check if privileged.
if (!ZRIsClientPrivileged(client, OperationType_Configuration))
{
TranslationReplyToCommand(client, "No access to command");
return Plugin_Handled;
}
// If module is disabled, then stop.
new bool:hitgroups = GetConVarBool(g_hCvarsList[CVAR_HITGROUPS]);
if (!hitgroups)
{
TranslationReplyToCommand(client, "Feature is disabled");
return Plugin_Handled;
}
// If not enough arguments given, then stop.
if (argc < 1)
{
TranslationReplyToCommand(client, "Hitgroups command syntax");
TranslationReplyToCommand(client, "Hitgroups command related commands");
TranslationPrintToConsole(client, "Hitgroups command syntax names");
// Print all the hitgroup names in the client's console.
decl String:hitgroupname[HITGROUPS_MAX_LENGTH];
// x = Hitgroup index.
new size = GetArraySize(arrayHitgroups);
for (new x = 0; x < size; x++)
{
// Get the hitgroups name and print in console.
HitgroupsGetName(x, hitgroupname, sizeof(hitgroupname));
PrintToConsole(client, "* %s", hitgroupname);
}
return Plugin_Handled;
}
// Get hitgroup alias given.
decl String:target[HITGROUPS_MAX_LENGTH];
GetCmdArg(1, target, sizeof(target));
// If the hitgroup is invalid, then stop and tell client.
new hitgroup = HitgroupsNameToIndex(target, sizeof(target), true);
if (hitgroup == -1)
{
TranslationReplyToCommand(client, "Hitgroups command invalid hitgroup", target);
return Plugin_Handled;
}
new bool:hitgroupdamage;
// Check if value was given
decl String:value[4];
GetCmdArg(2, value, sizeof(value));
if (!value[0])
{
// Get the opposite value of the current hitgroup value.
hitgroupdamage = !HitgroupsCanDamage(hitgroup);
}
else
{
// Cast the given value to a bool.
hitgroupdamage = bool:StringToInt(value);
}
// Set new value in the hitgroup data cache.
HitgroupsSetDamage(hitgroup, hitgroupdamage);
// Tell client the new value of the hitgroup.
if (hitgroupdamage)
{
TranslationReplyToCommand(client, "Hitgroups command successful on", target);
}
else
{
TranslationReplyToCommand(client, "Hitgroups command successful off", target);
}
// Log action to game events.
LogEvent(false, LogType_Normal, LOG_GAME_EVENTS, LogModule_Hitgroups, "Headshots Toggle", "Admin \"%L\" toggled hitgroup \"%s\" to \"%d\". (zr_hitgroup)", client, target, hitgroupdamage);
return Plugin_Handled;
}
/**
* Command callback (zr_hitgroup_enable_all)
* Enables all zombie hitgroups to be damaged.
*
* @param client The client index.
* @param argc Argument count.
*/
public Action:HitgroupsEnableAllCommand(client, argc)
{
// Check if privileged.
if (!ZRIsClientPrivileged(client, OperationType_Configuration))
{
TranslationReplyToCommand(client, "No access to command");
return Plugin_Handled;
}
// If module is disabled, then stop.
new bool:hitgroups = GetConVarBool(g_hCvarsList[CVAR_HITGROUPS]);
if (!hitgroups)
{
TranslationReplyToCommand(client, "Feature is disabled");
return Plugin_Handled;
}
// x = Hitgroup index.
new size = GetArraySize(arrayHitgroups);
for (new x = 0; x < size; x++)
{
// Set that hitgroup index to true for damage.
HitgroupsSetDamage(x, true);
}
// Tell the server that all hitgroups have been enabled.
TranslationPrintToChatAll(true, false, "Hitgroups command enable all successful");
// Log action to game events.
LogEvent(false, LogType_Normal, LOG_GAME_EVENTS, LogModule_Hitgroups, "Enable All", "Admin \"%L\" enabled all zombie hitgroups. (zr_hitgroup_enable_all)", client);
return Plugin_Handled;
}
/**
* Command callback (zr_hitgroup_enable_all)
* Disables all zombie hitgroups but the head.
*
* @param client The client index.
* @param argc Argument count.
*/
public Action:HitgroupsHeadshotsOnlyCommand(client, argc)
{
// Check if privileged.
if (!ZRIsClientPrivileged(client, OperationType_Configuration))
{
TranslationReplyToCommand(client, "No access to command");
return Plugin_Handled;
}
// If module is disabled, then stop.
new bool:hitgroups = GetConVarBool(g_hCvarsList[CVAR_HITGROUPS]);
if (!hitgroups)
{
TranslationReplyToCommand(client, "Feature is disabled");
return Plugin_Handled;
}
// x = Hitgroup index.
new size = GetArraySize(arrayHitgroups);
for (new x = 0; x < size; x++)
{
// If this hitgroup is the head, then enable it and stop.
if (HitgroupsGetIndex(x) == HITGROUP_HEAD)
{
HitgroupsSetDamage(x, true);
continue;
}
// Set that hitgroup index to true for damage.
HitgroupsSetDamage(x, false);
}
// Tell the server that headshots only been enabled.
TranslationPrintToChatAll(true, false, "Hitgroups command headshots only successful");
// Log action to game events.
LogEvent(false, LogType_Normal, LOG_GAME_EVENTS, LogModule_Hitgroups, "Headshots Only", "Admin \"%L\" enabled headshots only. (zr_hitgroup_headshots_only)", client);
return Plugin_Handled;
}