From 417513b502277dfe242545a437244c6ef88a3787 Mon Sep 17 00:00:00 2001 From: nosoop Date: Fri, 4 Oct 2019 04:16:37 -0700 Subject: [PATCH] Add HookValue natives These allow SourceMod plugins to transform values based on attribute classes in the same way the game does it. This means they don't have to implement rules based on calling GetValue for multiple names and multiple entities. --- gamedata/tf2.attributes.txt | 16 +++++++ scripting/include/tf2attributes.inc | 22 +++++++++ scripting/tf2attributes.sp | 74 ++++++++++++++++++++++++++++- 3 files changed, 111 insertions(+), 1 deletion(-) diff --git a/gamedata/tf2.attributes.txt b/gamedata/tf2.attributes.txt index c39ff00..bfcdb69 100644 --- a/gamedata/tf2.attributes.txt +++ b/gamedata/tf2.attributes.txt @@ -77,6 +77,22 @@ "linux" "@_ZN14CAttributeList20DestroyAllAttributesEv" "mac" "@_ZN14CAttributeList20DestroyAllAttributesEv" } + "CAttributeManager::AttribHookValue" + { + // (float value, string_t attrClass, CBaseEntity* ent, CUtlVector *reentrant, bool const_str) + // called in unique x-ref to "ubercharge_ammo" on Windows + "library" "server" + "linux" "@_ZN17CAttributeManager15AttribHookValueIfEET_S1_PKcPK11CBaseEntityP10CUtlVectorIPS4_10CUtlMemoryIS8_iEEb" + "windows" "\x55\x8B\xEC\x83\xEC\x0C\x8B\x0D\x2A\x2A\x2A\x2A\x53\x56\x57\x33\xF6\x33\xFF\x89\x75\xF4\x89\x7D\xF8\x8B\x41\x08\x85\xC0\x74\x2A\x68\x2A\x2A\x2A\x2A\x68\x2A\x2A\x2A\x2A\x68\x2A\x2A\x2A\x2A\x68\x2A\x2A\x2A\x2A\x6A\x6B" + } + "CAttributeManager::AttribHookValue" + { + // (int value, string_t attrClass, CBaseEntity* ent, CUtlVector *reentrant, bool const_str) + // called in unique x-ref to "mod_max_primary_clip_override" on Windows + "library" "server" + "linux" "@_ZN17CAttributeManager15AttribHookValueIiEET_S1_PKcPK11CBaseEntityP10CUtlVectorIPS4_10CUtlMemoryIS8_iEEb" + "windows" "\x55\x8B\xEC\x83\xEC\x10\x8B\x0D\x2A\x2A\x2A\x2A\x53\x56\x57\x33\xFF\x33\xDB\x89\x7D\xF0\x89\x5D\xF4\x8B\x41\x08\x85\xC0\x74\x2A\x68\x2A\x2A\x2A\x2A\x68\x2A\x2A\x2A\x2A\x68\x2A\x2A\x2A\x2A\x68\x2A\x2A\x2A\x2A\x6A\x6B" + } "CTFPlayer::AddCustomAttribute" //(const char*, float, float), returns void { "library" "server" diff --git a/scripting/include/tf2attributes.inc b/scripting/include/tf2attributes.inc index 5dc6403..3c671d4 100644 --- a/scripting/include/tf2attributes.inc +++ b/scripting/include/tf2attributes.inc @@ -233,6 +233,28 @@ native void TF2Attrib_AddCustomPlayerAttribute(int client, const char[] strAttri */ native void TF2Attrib_RemoveCustomPlayerAttribute(int client, const char[] strAttrib); +/** + * Applies a transformation to the given initial value, following the rules according to the given attribute class. + * + * @param flInitial Initial float value. + * @param attrClass The attribute class, as from the "attribute_class" key in items_game. + * @param iEntity The entity that should be checked. Checking players also checks their equipped items. + * + * @return Transformed initial value. + */ +native float TF2Attrib_HookValueFloat(float flInitial, const char[] attrClass, int iEntity); + +/** + * Applies a transformation to the given initial value, following the rules according to the given attribute class. + * + * @param iInitial Initial float value. + * @param attrClass The attribute class, as from the "attribute_class" key in items_game. + * @param iEntity The entity that should be checked. Checking players also checks their equipped items. + * + * @return Transformed initial value. + */ +native int TF2Attrib_HookValueInt(int nInitial, const char[] attrClass, int iEntity); + /** * Gets whether the plugin loaded without ANY errors. * For the purpose of allowing dependencies to ignore the plugin if this returns false. diff --git a/scripting/tf2attributes.sp b/scripting/tf2attributes.sp index aece07d..155022a 100644 --- a/scripting/tf2attributes.sp +++ b/scripting/tf2attributes.sp @@ -7,7 +7,7 @@ #define PLUGIN_NAME "[TF2] TF2Attributes" #define PLUGIN_AUTHOR "FlaminSarge" -#define PLUGIN_VERSION "1.3.3@nosoop-1.5.0" +#define PLUGIN_VERSION "1.3.3@nosoop-1.6.0" #define PLUGIN_CONTACT "http://forums.alliedmods.net/showthread.php?t=210221" #define PLUGIN_DESCRIPTION "Functions to add/get attributes for TF2 players/items" @@ -34,6 +34,8 @@ Handle hSDKRemoveAttribute; Handle hSDKDestroyAllAttributes; Handle hSDKAddCustomAttribute; Handle hSDKRemoveCustomAttribute; +Handle hSDKAttributeHookFloat; +Handle hSDKAttributeHookInt; static bool g_bPluginReady = false; public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) { @@ -66,6 +68,8 @@ public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max CreateNative("TF2Attrib_IsValidAttributeName", Native_IsValidAttributeName); CreateNative("TF2Attrib_AddCustomPlayerAttribute", Native_AddCustomAttribute); CreateNative("TF2Attrib_RemoveCustomPlayerAttribute", Native_RemoveCustomAttribute); + CreateNative("TF2Attrib_HookValueFloat", Native_HookValueFloat); + CreateNative("TF2Attrib_HookValueInt", Native_HookValueInt); CreateNative("TF2Attrib_IsReady", Native_IsReady); //unused, backcompat I guess? @@ -197,6 +201,32 @@ public void OnPluginStart() { SetFailState("Could not initialize call to CTFPlayer::AddCustomAttribute"); } + StartPrepSDKCall(SDKCall_Static); + PrepSDKCall_SetFromConf(hGameConf, SDKConf_Signature, "CAttributeManager::AttribHookValue"); + PrepSDKCall_SetReturnInfo(SDKType_Float, SDKPass_Plain); + PrepSDKCall_AddParameter(SDKType_Float, SDKPass_Plain); // initial value + PrepSDKCall_AddParameter(SDKType_String, SDKPass_Pointer); // attribute class + PrepSDKCall_AddParameter(SDKType_CBaseEntity, SDKPass_Pointer); // CBaseEntity* entity + PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); // CUtlVector, set to nullptr + PrepSDKCall_AddParameter(SDKType_Bool, SDKPass_Plain); // bool const_string + hSDKAttributeHookFloat = EndPrepSDKCall(); + if (!hSDKAttributeHookFloat) { + SetFailState("Could not initialize call to CAttributeManager::AttribHookValue"); + } + + StartPrepSDKCall(SDKCall_Static); + PrepSDKCall_SetFromConf(hGameConf, SDKConf_Signature, "CAttributeManager::AttribHookValue"); + PrepSDKCall_SetReturnInfo(SDKType_PlainOldData, SDKPass_Plain); + PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); // initial value + PrepSDKCall_AddParameter(SDKType_String, SDKPass_Pointer); // attribute class + PrepSDKCall_AddParameter(SDKType_CBaseEntity, SDKPass_Pointer); // CBaseEntity* entity + PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); // CUtlVector, set to nullptr + PrepSDKCall_AddParameter(SDKType_Bool, SDKPass_Plain); // bool const_string + hSDKAttributeHookInt = EndPrepSDKCall(); + if (!hSDKAttributeHookInt) { + SetFailState("Could not initialize call to CAttributeManager::AttribHookValue"); + } + CreateConVar("tf2attributes_version", PLUGIN_VERSION, "TF2Attributes version number", FCVAR_NOTIFY); g_bPluginReady = true; @@ -618,6 +648,48 @@ public int Native_RemoveCustomAttribute(Handle plugin, int numParams) { return; } +/* native float TF2Attrib_HookValueFloat(float flInitial, const char[] attrClass, int iEntity); */ +public int Native_HookValueFloat(Handle plugin, int numParams) { + /** + * CAttributeManager::AttribHookValue(float value, string_t attr_class, + * CBaseEntity const* entity, CUtlVector reentrantList, + * bool is_const_str); + * + * `value` is the value that is returned after modifiers based on `attr_class`. + * `reentrantList` seems to be a list of entities to ignore? + * `is_const_str` is true iff the `attr_class` is hardcoded + * (i.e., it's at a fixed location) -- this is never true from a plugin + * This determines if the game uses AllocPooledString_StaticConstantStringPointer + * (when is_const_str == true) or AllocPooledString (false). + */ + float initial = GetNativeCell(1); + + int buflen; + GetNativeStringLength(2, buflen); + char[] attrClass = new char[++buflen]; + GetNativeString(2, attrClass, buflen); + + int entity = GetNativeCell(3); + + return SDKCall(hSDKAttributeHookFloat, initial, attrClass, entity, + Address_Null, false); +} + +/* native float TF2Attrib_HookValueInt(int nInitial, const char[] attrClass, int iEntity); */ +public int Native_HookValueInt(Handle plugin, int numParams) { + int initial = GetNativeCell(1); + + int buflen; + GetNativeStringLength(2, buflen); + char[] attrClass = new char[++buflen]; + GetNativeString(2, attrClass, buflen); + + int entity = GetNativeCell(3); + + return SDKCall(hSDKAttributeHookInt, initial, attrClass, entity, + Address_Null, false); +} + /* helper functions */ static Address GetItemSchema() {