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() {