/* * ============================================================================ * * Zombie:Reloaded * * File: restrict.inc * Type: Core * Description: Weapon restriction system. * * Copyright (C) 2009 Greyscale, Richard Helgeby * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * ============================================================================ */ /** * Maximum types allowed for a single weapon. */ #define WEAPONS_RESTRICT_MAX_TYPES 8 /** * Restrict config data indexes. */ enum RestrictData { RESTRICT_DATA_NAME = 0, } /** * Array that stores the "HookID" to be later unhooked on player disconnect. */ new g_iCanUseHookID[MAXPLAYERS + 1] = {-1, ...}; /** * Array to block any client from picking up weapons. */ new bool:g_bRestrictBlockWeapon[MAXPLAYERS + 1]; /** * Array to store a list of different weapon groups */ new Handle:arrayWeaponTypes = INVALID_HANDLE; /** * Query results returned when (un)restricting a weapon. */ enum RestrictQuery { Query_Successful, /** (Un)restrict was successful. */ Query_Stopped, /** (Un)restrict was stopped because action was redundant. */ Query_Locked, /** (Un)restrict was stopped because the weapon is marked "untoggleable." */ Query_Invalid, /** (Un)restrict failed because invalid info was given. */ } /** * Initialize data and hook commands. */ RestrictInit() { // Hook buy command. RegConsoleCmd("buy", RestrictBuyCommand); RegConsoleCmd("autobuy", RestrictBuyCommand); RegConsoleCmd("rebuy", RestrictBuyCommand); } /** * Auto-create a list of types loaded by weapons module. */ RestrictLoad() { // If array exists, then destroy it. if (arrayWeaponTypes != INVALID_HANDLE) { CloseHandle(arrayWeaponTypes); } // Initialize array. arrayWeaponTypes = CreateArray(WEAPONS_MAX_LENGTH); decl String:weapontype[WEAPONS_MAX_LENGTH]; new String:weapontypes[WEAPONS_RESTRICT_MAX_TYPES][WEAPONS_MAX_LENGTH]; // x = Array index. new size = GetArraySize(arrayWeapons); for (new x = 0; x < size; x++) { WeaponsGetType(x, weapontype, sizeof(weapontype)); ExplodeString(weapontype, ",", weapontypes, sizeof(weapontypes), sizeof(weapontypes[])); for (new y = 0; y < WEAPONS_RESTRICT_MAX_TYPES; y++) { // Cut off whitespace. TrimString(weapontypes[y]); // If we've reached the end of the weapon's types, then stop. if (!weapontypes[y][0]) { break; } // If the weapon type isn't in the main array, then push it in. if (FindStringInArray(arrayWeaponTypes, weapontypes[y]) == -1) { // Push weapon type name into weapon type array. PushArrayString(arrayWeaponTypes, weapontypes[y]); } } } } /** * Hook commands related to restrict here. */ RestrictOnCommandsCreate() { // Create weapon admin commands. RegConsoleCmd("zr_restrict", RestrictCommand, "Restricts a weapon or a weapon type. Usage: zr_restrict [weapon2|weapontype2] ..."); RegConsoleCmd("zr_unrestrict", UnrestrictCommand, "Unrestricts a weapon or a weapon type. Usage: zr_unrestrict [weapon2|weapontype2] ..."); } /** * Client is joining the server. * * @param client The client index. */ RestrictClientInit(client) { // Hook "Weapon_CanUse" on client. #if defined USE_SDKHOOKS SDKHook(client, SDKHook_WeaponCanUse, RestrictCanUse); // Set dummy value so it think it's hooked. g_iCanUseHookID[client] = 1; #else g_iCanUseHookID[client] = ZRTools_HookWeapon_CanUse(client, RestrictCanUse); #endif // Reset block weapons flag. g_bRestrictBlockWeapon[client] = false; } /** * Unhook Weapon_CanUse function on a client. * * @param client The client index. */ RestrictOnClientDisconnect(client) { // Unhook "Weapon_CanUse" callback, and reset variable. if (g_iCanUseHookID[client] != -1) { #if defined USE_SDKHOOKS SDKUnhook(client, SDKHook_WeaponCanUse, RestrictCanUse); #else ZRTools_UnhookWeapon_CanUse(g_iCanUseHookID[client]); #endif g_iCanUseHookID[client] = -1; } } /** * Client is spawning into the game. * * @param client The client index. */ RestrictOnClientSpawn(client) { // Re-hook "canuse" on client. #if defined USE_SDKHOOKS SDKUnhook(client, SDKHook_WeaponCanUse, RestrictCanUse); // <--- What happens if it's not already hooked??? SDKHook(client, SDKHook_WeaponCanUse, RestrictCanUse); g_iCanUseHookID[client] = 1; #else ZRTools_UnhookWeapon_CanUse(g_iCanUseHookID[client]); g_iCanUseHookID[client] = ZRTools_HookWeapon_CanUse(client, RestrictCanUse); #endif // H4x. Unfortunately I can't disable this flag early enough for CS:S to give start USP/Glock. // So I have to give BOOTLEG weapons. (Sorry Valve) if (g_bRestrictBlockWeapon[client]) { // Reset block weapons flag. g_bRestrictBlockWeapon[client] = false; if (ZRIsClientOnTeam(client, CS_TEAM_T)) { GivePlayerItem(client, WEAPONS_SPAWN_T_WEAPON); } else if (ZRIsClientOnTeam(client, CS_TEAM_CT)) { GivePlayerItem(client, WEAPONS_SPAWN_CT_WEAPON); } } } /** * The round is ending. */ RestrictOnRoundEnd() { new bool:restrictzombieequip = GetConVarBool(g_hCvarsList[CVAR_WEAPONS_RESTRICT_ENDEQUIP]); if (!restrictzombieequip) { return; } // x = Client index. for (new x = 1; x <= MaxClients; x++) { // If client isn't in-game, then stop. if (!IsClientInGame(x)) { continue; } // If client is a human, then stop. if (InfectIsClientHuman(x)) { continue; } // Enable block weapon flag. g_bRestrictBlockWeapon[x] = true; } } /** * Command callback function for the "buy" command * Used to block use of this command under certain conditions. * * @param client The client index. * @param argc Argument count. */ public Action:RestrictBuyCommand(client, argc) { // If client isn't valid, then stop. if (!ZRIsClientValid(client) || !IsClientInGame(client)) { return Plugin_Continue; } // If the client isn't in a buyzone, then stop. if (!WeaponsIsClientInBuyZone(client)) { return Plugin_Continue; } // If player is a zombie, then block command. if (InfectIsClientInfected(client)) { TranslationPrintToChat(client, "Zombie cant use weapon"); // Block command return Plugin_Handled; } decl String:weapon[WEAPONS_MAX_LENGTH]; GetCmdArg(1, weapon, sizeof(weapon)); // If the weapon is restricted, then prevent pickup. decl String:weaponname[WEAPONS_MAX_LENGTH]; new weaponindex = WeaponsEntityToDisplay(weapon, weaponname, sizeof(weaponname), true); // If weapon isn't configged, then allow pickup. if (weaponindex == -1) { // Allow command. return Plugin_Continue; } // If weapon is restricted, then stop. if (RestrictIsWeaponRestricted(weaponindex)) { TranslationPrintToChat(client, "Weapon is restricted", weaponname); // Block command. return Plugin_Handled; } // Allow command. return Plugin_Continue; } /** * Restricts (or unrestricts) a given weapon or weapon type. * * @param restrict True to restrict, false to unrestrict. * @param target Weapon or weapon type to restrict/unrestrict. * @param single True if a single weapon is being restricted, false if weapon type. * @param returntarget The proper targetname. (same as param 'target' if invalid) * @param maxlen The maximum length of param 'returntarget.' * @return Query result. (See enum RestrictQuery) */ RestrictQuery:RestrictWeapon(bool:restrict, const String:target[], &bool:single = true, String:returntarget[], maxlen) { // Copy 'target' to 'returntarget' to be possibly changed later. strcopy(returntarget, maxlen, target); // Find index of the given weapon type. new typeindex = RestrictTypeToIndex(returntarget); // Single weapon. if (typeindex == -1) { single = true; new weaponindex = WeaponsNameToIndex(returntarget); // If weapon index is invalid, then return invalid. if (weaponindex == -1) { return Query_Invalid; } // Return proper weapon name. WeaponsGetName(weaponindex, returntarget, maxlen); // If weapon is untoggleable, then return locked. if (!WeaponsGetToggleable(weaponindex)) { return Query_Locked; } // If weapon restriction is redundant then return stopped. if (RestrictIsWeaponRestricted(weaponindex) == restrict) { return Query_Stopped; } // Set weapon's restricted state. RestrictSetWeaponRestricted(weaponindex, false, restrict); // Successfully restricted weapon. return Query_Successful; } // Weapon type. else { single = false; // Get all weapons in the given type. new Handle:arrayTypeWeapons; new count = RestrictGetTypeWeapons(typeindex, arrayTypeWeapons); // Return proper weapon name. RestrictWeaponTypeGetName(typeindex, returntarget, maxlen); // If weapon restriction is redundant then return stopped. if (RestrictIsTypeUniform(restrict, typeindex)) { return Query_Stopped; } for (new x = 0; x < count; x++) { // Get weapon index. new weaponindex = GetArrayCell(arrayTypeWeapons, x); // If weapon is untoggleable, then stop. if (!WeaponsGetToggleable(weaponindex)) { continue; } // Set weapon's restricted state. RestrictSetWeaponRestricted(weaponindex, false, restrict); } // Successfully restricted weapon type. return Query_Successful; } } /** Print weapon (un)restriction query result to client(s). * * @param client The client to print response to. (0 for all clients) * @param query The query result. * @param single True if a single weapon is being restricted. * @param restrict True if the query was to restrict/unrestrict a weapon. * @param target The target to be restricted/unrestricted. */ RestrictPrintQueryResponse(client, RestrictQuery:query, bool:single, bool:restrict, const String:target[]) { switch(query) { // Query was successful. case Query_Successful: { if (single) { if (restrict) { TranslationPrintToChatAll(true, false, "Restrict weapon", target); } else { TranslationPrintToChatAll(true, false, "Unrestrict weapon", target); } } else { if (restrict) { TranslationPrintToChatAll(true, false, "Restrict weapon type", target); } else { TranslationPrintToChatAll(true, false, "Unrestrict weapon type", target); } } } // Query was redundant. case Query_Stopped: { if (single) { if (restrict) { TranslationReplyToCommand(client, "Restrict weapon stopped", target); } else { TranslationReplyToCommand(client, "Unrestrict weapon stopped", target); } } else { if (restrict) { TranslationReplyToCommand(client, "Restrict weapon type stopped", target); } else { TranslationReplyToCommand(client, "Unrestrict weapon type stopped", target); } } } // Weapon is untoggleable. case Query_Locked: { TranslationReplyToCommand(client, "Restrict weapon untoggleable", target); } // Weapon was invalid. case Query_Invalid: { TranslationReplyToCommand(client, "Weapon invalid", target); } } } stock RestrictTypeToIndex(const String:type[]) { decl String:typename[WEAPONS_MAX_LENGTH]; // x = Array index. new size = GetArraySize(arrayWeaponTypes); for (new x = 0; x < size; x++) { RestrictWeaponTypeGetName(x, typename, sizeof(typename)); // If types match, then return index. if (StrEqual(type, typename, false)) { return x; } } // Type doesn't exist. return -1; } /** * Gets the name of a weapon type at a given index. * @param index The weapon type index. * @param weapon The string to return name in. * @param maxlen The max length of the string. */ stock RestrictWeaponTypeGetName(index, String:weapontype[], maxlen) { // Get weapon type name at given index. GetArrayString(arrayWeaponTypes, index, weapontype, maxlen); } /** * Returns an array containing all weapon indexes matching the given type. * * @param index The weapon type index. * @param arrayTypeWeapons A handle to store array containing matching weapons. * Don't forget to close this! */ stock RestrictGetTypeWeapons(index, &Handle:arrayTypeWeapons) { // Create array to hold weapons of the given type. arrayTypeWeapons = CreateArray(); // Get name of the weapon type at given index. decl String:typename[WEAPONS_MAX_LENGTH]; RestrictWeaponTypeGetName(index, typename, sizeof(typename)); new count; decl String:weapontype[WEAPONS_MAX_LENGTH]; new String:weapontypes[WEAPONS_RESTRICT_MAX_TYPES][WEAPONS_MAX_LENGTH]; // x = Array index. new size = GetArraySize(arrayWeapons); for (new x = 0; x < size; x++) { WeaponsGetType(x, weapontype, sizeof(weapontype)); ExplodeString(weapontype, ",", weapontypes, sizeof(weapontypes), sizeof(weapontypes[])); for (new y = 0; y < WEAPONS_RESTRICT_MAX_TYPES; y++) { // Cut off whitespace. TrimString(weapontypes[y]); // If we've reached the end of the weapon's types, then stop. if (!weapontypes[y][0]) { break; } // If types match, then add weapon to array. if (StrEqual(typename, weapontypes[y], false)) { PushArrayCell(arrayTypeWeapons, x); count++; } } } // Return number of weapons of the given type. return count; } /** * Gets the restricted status on a weapon. * * @param index The weapon index. * @param toggle If true, the value is toggled, otherwise 'restrict' param is used. * @param restrict (Only if 'toggle' is 'false') Restricted status of the weapon. */ stock RestrictSetWeaponRestricted(index, bool:toggle, bool:restrict = false) { // Get array handle of weapon at given index. new Handle:arrayWeapon = GetArrayCell(arrayWeapons, index); // Set restricted status. new bool:value = toggle ? !RestrictIsWeaponRestricted(index) : restrict; SetArrayCell(arrayWeapon, _:WEAPONS_DATA_RESTRICTED, value); } /** * Gets the restricted status on a weapon. * * @param index The weapon index. * @return True if weapon is restricted, false if not. */ stock bool:RestrictIsWeaponRestricted(index) { // Get array handle of weapon at given index. new Handle:arrayWeapon = GetArrayCell(arrayWeapons, index); // Return restricted status. return bool:GetArrayCell(arrayWeapon, _:WEAPONS_DATA_RESTRICTED); } /** * Used to check if all weapons of a type are restricted. * * @param restricted True to check if all weapons of given type are restricted. * @param index The weapon type index. * @return True if all weapons of the given type are restricted or not, false if not. */ stock bool:RestrictIsTypeUniform(bool:restricted, index) { new Handle:arrayTypeWeapons; new count = RestrictGetTypeWeapons(index, arrayTypeWeapons); // x = array index for (new x = 0; x < count; x++) { // Get weapon index to check restricted status of. new weaponindex = GetArrayCell(arrayTypeWeapons, x); // If weapon is toggleable and it's not uniform with the given status, then return false. if (WeaponsGetToggleable(weaponindex) && RestrictIsWeaponRestricted(weaponindex) != restricted) { return false; } } // All weapons are restricted, so return true. return true; } /** * Hook callback, called when a player is trying to pick up a weapon. * @param client The client index. * @param weapon The weapon index. * @return Return ZRTools_Handled to stop weapon pickup. * ZRTools_Continue to allow weapon pickup. */ #if defined USE_SDKHOOKS public Action:RestrictCanUse(client, weapon) #else public ZRTools_Action:RestrictCanUse(client, weapon) #endif { new String:weaponentity[WEAPONS_MAX_LENGTH]; GetEdictClassname(weapon, weaponentity, sizeof(weaponentity)); // If weapon is a knife, then allow pickup. if (StrEqual(weaponentity, "weapon_knife")) { return ACTION_CONTINUE; } // If the player is a zombie, then prevent pickup. if (InfectIsClientInfected(client)) { return ACTION_HANDLED; } // If client is flagged for not picking up weapons, then stop. if (g_bRestrictBlockWeapon[client]) { return ACTION_HANDLED; } // If weapons module is disabled, then stop. new bool:weapons = GetConVarBool(g_hCvarsList[CVAR_WEAPONS]); if (!weapons) { return ACTION_CONTINUE; } // If restrict module is disabled, then stop. new bool:restrict = GetConVarBool(g_hCvarsList[CVAR_WEAPONS_RESTRICT]); if (!restrict) { return ACTION_CONTINUE; } // If the weapon is restricted, then prevent pickup. decl String:weaponname[WEAPONS_MAX_LENGTH]; new weaponindex = WeaponsEntityToDisplay(weaponentity, weaponname, sizeof(weaponname)); // If weapon isn't configged, then allow pickup. if (weaponindex == -1) { // Allow pickup. return ACTION_CONTINUE; } // If weapon is restricted, then stop. if (RestrictIsWeaponRestricted(weaponindex)) { return ACTION_HANDLED; } // Forward event to weapons module. WeaponsOnItemPickup(client, weapon); // Allow pickup. return ACTION_CONTINUE; } /** * Command callbacks. */ /** * Command callback (zr_restrict) * Restricts a weapon or group * * @param client The client index. * @param argc Argument count. */ public Action:RestrictCommand(client, argc) { // Check if privileged. if (!ZRIsClientPrivileged(client, OperationType_Configuration)) { TranslationReplyToCommand(client, "No access to command"); return Plugin_Handled; } // If weapons module is disabled, then stop. new bool:weapons = GetConVarBool(g_hCvarsList[CVAR_WEAPONS]); if (!weapons) { // Tell client command is disabled. TranslationReplyToCommand(client, "Feature is disabled"); return Plugin_Handled; } // If restrict module is disabled, then stop. new bool:restrict = GetConVarBool(g_hCvarsList[CVAR_WEAPONS_RESTRICT]); if (!restrict) { // Tell client command is disabled. TranslationReplyToCommand(client, "Feature is disabled"); return Plugin_Handled; } // If not enough arguments given, then stop. if (argc < 1) { TranslationReplyToCommand(client, "Weapons command restrict syntax"); return Plugin_Handled; } decl String:target[WEAPONS_MAX_LENGTH]; new args = GetCmdArgs(); for (new x = 1; x <= args; x++) { // Get target to restrict. GetCmdArg(x, target, sizeof(target)); // Query restrict on this target, and get a result back. new bool:single; decl String:returntarget[WEAPONS_MAX_LENGTH]; new RestrictQuery:query = RestrictWeapon(true, target, single, returntarget, sizeof(returntarget)); // Print response to client(s). RestrictPrintQueryResponse(client, query, single, true, returntarget); } return Plugin_Handled; } /** * Command callback (zr_unrestrict) * Unrestricts a weapon or group * * @param client The client index. * @param argc Argument count. */ public Action:UnrestrictCommand(client, argc) { // Check if privileged. if (!ZRIsClientPrivileged(client, OperationType_Configuration)) { TranslationReplyToCommand(client, "No access to command"); return Plugin_Handled; } // If weapons module is disabled, then stop. new bool:weapons = GetConVarBool(g_hCvarsList[CVAR_WEAPONS]); if (!weapons) { // Tell client command is disabled. TranslationReplyToCommand(client, "Feature is disabled"); return Plugin_Handled; } // If restrict module is disabled, then stop. new bool:restrict = GetConVarBool(g_hCvarsList[CVAR_WEAPONS_RESTRICT]); if (!restrict) { // Tell client command is disabled. TranslationReplyToCommand(client, "Feature is disabled"); return Plugin_Handled; } // If not enough arguments given, then stop. if (argc < 1) { TranslationReplyToCommand(client, "Weapons command unrestrict syntax"); return Plugin_Handled; } // arg1 = weapon being restricted decl String:target[WEAPONS_MAX_LENGTH]; new args = GetCmdArgs(); for (new x = 1; x <= args; x++) { // Get target to restrict. GetCmdArg(x, target, sizeof(target)); // Query unrestrict on this target, and get a result back. new bool:single; decl String:returntarget[WEAPONS_MAX_LENGTH]; new RestrictQuery:query = RestrictWeapon(false, target, single, returntarget, sizeof(returntarget)); // Print response to client(s). RestrictPrintQueryResponse(client, query, single, false, returntarget); } return Plugin_Handled; }