/* * ============================================================================ * * 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 . * * ============================================================================ */ /** * 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 [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; }