#pragma semicolon 1 // Force strict semicolon mode. // ====[ INCLUDES ]==================================================== #include #define REQUIRE_EXTENSIONS #include // ====[ CONSTANTS ]=================================================== #define PLUGIN_NAME "[TF2Items] Manager" #define PLUGIN_AUTHOR "Damizean & Asherkin" #define PLUGIN_VERSION "1.4.3" #define PLUGIN_CONTACT "http://limetech.org/" #define ARRAY_SIZE 2 #define ARRAY_ITEM 0 #define ARRAY_FLAGS 1 //#define DEBUG // ====[ VARIABLES ]=================================================== new Handle:g_hPlayerInfo; new Handle:g_hPlayerArray; new Handle:g_hGlobalSettings; new Handle:g_hCvarEnabled; new bool:g_bPlayerEnabled[MAXPLAYERS + 1] = { true, ... }; new Handle:g_hCvarPlayerControlEnabled; // ====[ PLUGIN ]====================================================== public Plugin:myinfo = { name = PLUGIN_NAME, author = PLUGIN_AUTHOR, description = PLUGIN_NAME, version = PLUGIN_VERSION, url = PLUGIN_CONTACT }; // ====[ FUNCTIONS ]=================================================== /* OnPluginStart() * * When the plugin starts up. * -------------------------------------------------------------------------- */ public OnPluginStart() { // Create convars CreateConVar("tf2items_manager_version", PLUGIN_VERSION, PLUGIN_NAME, FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY); g_hCvarEnabled = CreateConVar("tf2items_manager", "1", "Enables/disables the manager (0 - Disabled / 1 - Enabled", FCVAR_REPLICATED|FCVAR_NOTIFY); g_hCvarPlayerControlEnabled = CreateConVar("tf2items_manager_playercontrol", "1", "Enables/disables the player's ability to control the manager (0 - Disabled / 1 - Enabled"); // Register console commands RegAdminCmd("tf2items_manager_reload", CmdReload, ADMFLAG_GENERIC); RegConsoleCmd("tf2items_enable", CmdEnable); RegConsoleCmd("tf2items_disable", CmdDisable); // Parse the items list ParseItems(); } /* TF2Items_OnGiveNamedItem() * * When an item is about to be given to a client. * -------------------------------------------------------------------------- */ public Action:TF2Items_OnGiveNamedItem(iClient, String:strClassName[], iItemDefinitionIndex, &Handle:hItemOverride) { // If disabled, use the default values. if (!GetConVarBool(g_hCvarEnabled) || (GetConVarBool(g_hCvarPlayerControlEnabled) && !g_bPlayerEnabled[iClient])) return Plugin_Continue; // If another plugin already tryied to override the item, let him go ahead. if (hItemOverride != INVALID_HANDLE) return Plugin_Continue; // Plugin_Changed // Find item. If any is found, override the attributes with these. new Handle:hItem = FindItem(iClient, iItemDefinitionIndex); if (hItem != INVALID_HANDLE) { hItemOverride = hItem; return Plugin_Changed; } // None found, use default values. return Plugin_Continue; } // Fuck it, only one is needed. // Doing this for just-in-casenesses sake public OnClientConnected(client) { g_bPlayerEnabled[client] = true; } public OnClientDisconnect(client) { g_bPlayerEnabled[client] = true; } /* * ------------------------------------------------------------------ * ______ __ * / ____/___ ____ ___ ____ ___ ____ _____ ____/ /____ * / / / __ \/ __ `__ \/ __ `__ \/ __ `/ __ \/ __ / ___/ * / /___/ /_/ / / / / / / / / / / / /_/ / / / / /_/ (__ ) * \____/\____/_/ /_/ /_/_/ /_/ /_/\__,_/_/ /_/\__,_/____/ * ------------------------------------------------------------------ */ /* CmdReload() ** ** Reloads the item list. ** -------------------------------------------------------------------------- */ public Action:CmdReload(iClient, iAction) { // Fire a message telling about the operation. if (iClient) ReplyToCommand(iClient, "Reloading items list"); else LogMessage("Reloading items list"); // Call the ParseItems function. ParseItems(); return Plugin_Handled; } public Action:CmdEnable(iClient, iAction) { if (!GetConVarBool(g_hCvarPlayerControlEnabled)) { ReplyToCommand(iClient, "The server administrator has disabled this command."); return Plugin_Handled; } ReplyToCommand(iClient, "Re-enabling TF2Items for you."); g_bPlayerEnabled[iClient] = true; return Plugin_Handled; } public Action:CmdDisable(iClient, iAction) { if (!GetConVarBool(g_hCvarPlayerControlEnabled)) { ReplyToCommand(iClient, "The server administrator has disabled this command."); return Plugin_Handled; } ReplyToCommand(iClient, "Disabling TF2Items for you."); g_bPlayerEnabled[iClient] = false; return Plugin_Handled; } /* * ------------------------------------------------------------------ * __ ___ __ * / |/ /___ _____ ____ _____ ____ ____ ___ ___ ____ / /_ * / /|_/ / __ `/ __ \/ __ `/ __ `/ _ \/ __ `__ \/ _ \/ __ \/ __/ * / / / / /_/ / / / / /_/ / /_/ / __/ / / / / / __/ / / / /_ * /_/ /_/\__,_/_/ /_/\__,_/\__, /\___/_/ /_/ /_/\___/_/ /_/\__/ * /____/ * ------------------------------------------------------------------ */ /* FindItem() ** ** Tryies to find a custom item usable by the client. ** -------------------------------------------------------------------------- */ Handle:FindItem(iClient, iItemDefinitionIndex) { // Check if the player is valid if (!IsValidClient(iClient)) return INVALID_HANDLE; // Retrieve the STEAM auth string new String:strAuth[64]; GetClientAuthString(iClient, strAuth, sizeof(strAuth)); // Check if it's on the list. If not, try with the global settings. new Handle:hItemArray = INVALID_HANDLE; GetTrieValue(g_hPlayerInfo, strAuth, hItemArray); // Check for each. new Handle:hOutput; hOutput = FindItemOnArray(iClient, hItemArray, iItemDefinitionIndex); if (hOutput == INVALID_HANDLE) hOutput = FindItemOnArray(iClient, g_hGlobalSettings, iItemDefinitionIndex); // Done return hOutput; } /* FindItemOnArray() ** ** ** -------------------------------------------------------------------------- */ Handle:FindItemOnArray(iClient, Handle:hArray, iItemDefinitionIndex) { // Check if the array is valid. if (hArray == INVALID_HANDLE) return INVALID_HANDLE; new Handle:hWildcardItem = INVALID_HANDLE; // Iterate through each item entry and close the handle. for (new iItem = 0; iItem < GetArraySize(hArray); iItem++) { // Retrieve item new Handle:hItem = GetArrayCell(hArray, iItem, ARRAY_ITEM); new iItemFlags = GetArrayCell(hArray, iItem, ARRAY_FLAGS); if (hItem == INVALID_HANDLE) continue; // Is a wildcard item? If so, store it. if (TF2Items_GetItemIndex(hItem) == -1 && hWildcardItem == INVALID_HANDLE) if (CheckItemUsage(iClient, iItemFlags)) hWildcardItem = hItem; // Is the item we're looking for? If so return item, but first // check if it's possible due to the if (TF2Items_GetItemIndex(hItem) == iItemDefinitionIndex) if (CheckItemUsage(iClient, iItemFlags)) return hItem; } // Done, returns wildcard item if it exists. return hWildcardItem; } /* CheckItemUsage() * * Checks if a client has any of the specified flags. * -------------------------------------------------------------------------- */ bool:CheckItemUsage(iClient, iFlags) { if (iFlags == 0) return true; new iClientFlags = GetUserFlagBits(iClient); if (iClientFlags & ADMFLAG_ROOT) return true; else return (iClientFlags & iFlags) != 0; } /* ParseItems() * * Reads up the items information from the Key-Values. * -------------------------------------------------------------------------- */ ParseItems() { decl String:strBuffer[256]; decl String:strSplit[16][64]; // Destroy the current items data. DestroyItems(); // Create key values object and parse file. BuildPath(Path_SM, strBuffer, sizeof(strBuffer), "configs/tf2items.weapons.txt"); new Handle:hKeyValues = CreateKeyValues("TF2Items"); if (FileToKeyValues(hKeyValues, strBuffer) == false) SetFailState("Error, can't read file containing the item list : %s", strBuffer); // Check the version KvGetSectionName(hKeyValues, strBuffer, sizeof(strBuffer)); if (StrEqual("custom_weapons_v3", strBuffer) == false) SetFailState("tf2items.weapons.txt structure corrupt or incorrect version: \"%s\"", strBuffer); // Create the array and trie to store & access the item information. g_hPlayerArray = CreateArray(); g_hPlayerInfo = CreateTrie(); #if defined DEBUG LogMessage("Parsing items"); LogMessage("{"); #endif // Jump into the first subkey and go on. if (KvGotoFirstSubKey(hKeyValues)) { do { // Retrieve player information and split into multiple strings. KvGetSectionName(hKeyValues, strBuffer, sizeof(strBuffer)); new iNumAuths = ExplodeString(strBuffer, ";", strSplit, 16, 64); // Create new array entry and upload to the array. new Handle:hEntry = CreateArray(2); PushArrayCell(g_hPlayerArray, hEntry); #if defined DEBUG LogMessage(" Entry", strBuffer); LogMessage(" {"); LogMessage(" Used by:"); #endif // Iterate through each player auth strings and make an // entry for each. for (new iAuth = 0; iAuth < iNumAuths; iAuth++) { TrimString(strSplit[iAuth]); SetTrieValue(g_hPlayerInfo, strSplit[iAuth], hEntry); #if defined DEBUG LogMessage(" \"%s\"", strSplit[iAuth]); #endif } #if defined DEBUG LogMessage(""); #endif // Read all the item entries ParseItemsEntry(hKeyValues, hEntry); #if defined DEBUG LogMessage(" }"); #endif } while (KvGotoNextKey(hKeyValues)); KvGoBack(hKeyValues); } // Close key values CloseHandle(hKeyValues); // Try to find the global item settings. GetTrieValue(g_hPlayerInfo, "*", g_hGlobalSettings); // Done. #if defined DEBUG LogMessage("}"); #endif } /* ParseItemsEntry() * * Reads up a particular items entry. * -------------------------------------------------------------------------- */ ParseItemsEntry(Handle:hKeyValues, Handle:hEntry) { decl String:strBuffer[64]; decl String:strBuffer2[64]; decl String:strSplit[2][64]; // Jump into the first subkey. if (KvGotoFirstSubKey(hKeyValues)) { do { new Handle:hItem = TF2Items_CreateItem(OVERRIDE_ALL); new iItemFlags = 0; // Retrieve item definition index and store. KvGetSectionName(hKeyValues, strBuffer, sizeof(strBuffer)); if (strBuffer[0] == '*') TF2Items_SetItemIndex(hItem, -1); else TF2Items_SetItemIndex(hItem, StringToInt(strBuffer)); #if defined DEBUG LogMessage(" Item: %i", TF2Items_GetItemIndex(hItem)); LogMessage(" {"); #endif // Retrieve entity level new iLevel = KvGetNum(hKeyValues, "level", -1); if (iLevel != -1) { TF2Items_SetLevel(hItem, iLevel); iItemFlags |= OVERRIDE_ITEM_LEVEL; } #if defined DEBUG if (iItemFlags & OVERRIDE_ITEM_LEVEL) LogMessage(" Level: %i", TF2Items_GetLevel(hItem)); #endif // Retrieve entity quality new iQuality = KvGetNum(hKeyValues, "quality", -1); if (iQuality != -1) { TF2Items_SetQuality(hItem, iQuality); iItemFlags |= OVERRIDE_ITEM_QUALITY; } #if defined DEBUG if (iItemFlags & OVERRIDE_ITEM_QUALITY) LogMessage(" Quality: %i", TF2Items_GetQuality(hItem)); #endif // Check for attribute preservation key new iPreserve = KvGetNum(hKeyValues, "preserve-attributes", -1); if (iPreserve == 1) { iItemFlags |= PRESERVE_ATTRIBUTES; } else { iPreserve = KvGetNum(hKeyValues, "preserve_attributes", -1); if (iPreserve == 1) iItemFlags |= PRESERVE_ATTRIBUTES; } #if defined DEBUG LogMessage(" Preserve Attributes: %s", (iItemFlags & PRESERVE_ATTRIBUTES)?"true":"false"); #endif // Read all the attributes new iAttributeCount = 0; for (;;) { // Format the attribute entry name Format(strBuffer, sizeof(strBuffer), "%i", iAttributeCount+1); // Try to read the attribute KvGetString(hKeyValues, strBuffer, strBuffer2, sizeof(strBuffer2)); // If not found, break. if (strBuffer2[0] == '\0') break; // Split the information in two buffers ExplodeString(strBuffer2, ";", strSplit, 2, 64); new iAttributeIndex = StringToInt(strSplit[0]); new Float:fAttributeValue = StringToFloat(strSplit[1]); // Attribute found, set information. TF2Items_SetAttribute(hItem, iAttributeCount, iAttributeIndex, fAttributeValue); #if defined DEBUG LogMessage(" Attribute[%i] : %i / %f", iAttributeCount, TF2Items_GetAttributeId(hItem, iAttributeCount), TF2Items_GetAttributeValue(hItem, iAttributeCount) ); #endif // Increase attribute count and continue. iAttributeCount++; } // Done, set attribute count and upload. if (iAttributeCount != 0) { TF2Items_SetNumAttributes(hItem, iAttributeCount); iItemFlags |= OVERRIDE_ATTRIBUTES; } // Retrieve the admin flags KvGetString(hKeyValues, "admin-flags", strBuffer, sizeof(strBuffer), ""); new iFlags = ReadFlagString(strBuffer); // Set flags and upload. TF2Items_SetFlags(hItem, iItemFlags); PushArrayCell(hEntry, 0); SetArrayCell(hEntry, GetArraySize(hEntry)-1, hItem, ARRAY_ITEM); SetArrayCell(hEntry, GetArraySize(hEntry)-1, iFlags, ARRAY_FLAGS); #if defined DEBUG LogMessage(" Flags: %05b", TF2Items_GetFlags(hItem)); LogMessage(" Admin: %s", ((iFlags == 0)? "(none)":strBuffer)); LogMessage(" }"); #endif } while (KvGotoNextKey(hKeyValues)); KvGoBack(hKeyValues); } } /* DestroyItems() * * Destroys the current list for items. * -------------------------------------------------------------------------- */ DestroyItems() { if (g_hPlayerArray != INVALID_HANDLE) { // Iterate through each player and retrieve the internal // weapon list. for (new iEntry = 0; iEntry < GetArraySize(g_hPlayerArray); iEntry++) { // Retrieve the item array. new Handle:hItemArray = GetArrayCell(g_hPlayerArray, iEntry); if (hItemArray == INVALID_HANDLE) continue; // Iterate through each item entry and close the handle. for (new iItem = 0; iItem < GetArraySize(hItemArray); iItem++) { // Retrieve item new Handle:hItem = GetArrayCell(hItemArray, iItem); if (hItem == INVALID_HANDLE) continue; // Close handle CloseHandle(hItem); } } // Done, free array CloseHandle(g_hPlayerArray); } // Free player trie if (g_hPlayerInfo != INVALID_HANDLE) { CloseHandle(g_hPlayerInfo); } // Done g_hPlayerInfo = INVALID_HANDLE; g_hPlayerArray = INVALID_HANDLE; g_hGlobalSettings = INVALID_HANDLE; } /* IsValidClient() * * Checks if a client is valid. * -------------------------------------------------------------------------- */ bool:IsValidClient(iClient) { if (iClient < 1 || iClient > MaxClients) return false; if (!IsClientConnected(iClient)) return false; return IsClientInGame(iClient); }