diff --git a/tf2.attributes.txt b/tf2.attributes.txt new file mode 100644 index 0000000..674ce5b --- /dev/null +++ b/tf2.attributes.txt @@ -0,0 +1,156 @@ +"Games" +{ + /* Team Fortress 2 */ + "tf" + { + "Offsets" + { + "CAttributeManager::OnAttributeValuesChanged" //use instead of ClearCache/NotifyManagerOfAttributeValueChanges + { + "windows" "12" + "linux" "13" + "mac" "13" + } + "CEconItemView::GetItemDefinition" + { + "windows" "4" + "linux" "5" + "mac" "5" + } + } + "Signatures" + { + "CEconItemSchema::GetItemDefinition" + { + "library" "server" + "windows" "\x55\x8B\xEC\x56\x8B\xF1\x8D\x45\x08\x57\x50\x8D\x4E\x60\xE8\x2A\x2A\x2A\x2A\x85\xC0" + "linux" "@_ZN15CEconItemSchema17GetItemDefinitionEi" + "mac" "@_ZN15CEconItemSchema17GetItemDefinitionEi" + } + "CEconItemView::GetSOCData" + { + "library" "server" + "windows" "\x56\x8B\xF1\x8B\x46\x28\x85\xC0\x75\x2A\xE8\x2A\x2A\x2A\x2A\xFF\x76\x20\x8B\xC8\x8B\x10\xFF\x52\x44\x85\xC0\x74\x2A\xFF\x76\x14\x8B\xC8\xFF\x76\x10\xE8\x96\x8B\xFD\xFF\x5E\x2A\x2A\x2A\x2A\xC3" + "linux" "@_ZNK13CEconItemView10GetSOCDataEv" + "mac" "@_ZNK13CEconItemView10GetSOCDataEv" + } + "CEconItem::SetCustomName" + { + "library" "server" + "windows" "\x55\x8B\xEC\xA1\x8C\xB3\x85\x10\x83\xEC\x14\x53\x8B\xD9\x56\x57\xA8\x01\x75\x2A\x83\xC8\x01\xC7\x05\x80\xB3\x85\x10\x04\x1A\x69\x10\x68\x04\x1A\x69\x10\xA3\x8C\xB3\x85\x10\xE8\x60\x22\x01\x00" + "linux" "@_ZN9CEconItem13SetCustomNameEPKc" + "mac" "@_ZN9CEconItem13SetCustomNameEPKc" + } + "GEconItemSchema" + { + "library" "server" + "windows" "\xE8\x2A\x2A\x2A\x2A\x83\xC0\x04\xC3" + "linux" "@_Z15GEconItemSchemav" + "mac" "@_Z15GEconItemSchemav" + } + "CEconItemSchema::GetAttributeDefinition" //int, returns CEconItemAttributeDefinition + { + "library" "server" + "windows" "\x55\x8B\xEC\x83\xEC\x40\x53\x56\x8B\xD9\x8D\x4D\xC4\x57" + "linux" "@_ZN15CEconItemSchema22GetAttributeDefinitionEi" + "mac" "@_ZN15CEconItemSchema22GetAttributeDefinitionEi" + } + "CEconItemSchema::GetAttributeDefinitionByName" //const char*, returns CEconItemAttributeDefinition + { + "library" "server" + "windows" "\x55\x8B\xEC\x83\xEC\x1C\x53\x8B\xD9\x8B\x0D\x2A\x2A\x2A\x2A\x56\x33\xF6\x89\x5D\xF8\x89\x75\xE4\x89\x75\xE8" + "linux" "@_ZN15CEconItemSchema28GetAttributeDefinitionByNameEPKc" + "mac" "@_ZN15CEconItemSchema28GetAttributeDefinitionByNameEPKc" + } + "CAttributeList::RemoveAttribute" //CEconItemAttributeDefinition*, returns CEconItemAttributeDefinition + { + "library" "server" + "windows" "\x55\x8B\xEC\x51\x53\x8B\xD9\x56\x33\xF6\x8B\x43\x10\x89\x45\xFC\x85\xC0\x7E\x2A\x57\x33\xFF" + "linux" "@_ZN14CAttributeList15RemoveAttributeEPK28CEconItemAttributeDefinition" + "mac" "@_ZN14CAttributeList15RemoveAttributeEPK28CEconItemAttributeDefinition" + } + "CAttributeList::SetRuntimeAttributeValue" //CEconItemAttributeDefinition*, float, returns void but somehow SetReturnInfo makes it return some nonzero client-based integer if success + { + "library" "server" + "windows" "\x55\x8B\xEC\x83\xEC\x14\x33\xD2\x53\x8B\xD9\x56\x57\x8B\x73\x10\x85\xF6" + "linux" "@_ZN14CAttributeList24SetRuntimeAttributeValueEPK28CEconItemAttributeDefinitionf" + "mac" "@_ZN14CAttributeList24SetRuntimeAttributeValueEPK28CEconItemAttributeDefinitionf" + } + "CAttributeList::GetAttributeByID" //int, returns CEconAttribute address + { + "library" "server" + "windows" "\x55\x8B\xEC\x51\x8B\xC1\x53\x56\x33\xF6\x89\x45\xFC\x8B\x58\x10" + "linux" "@_ZNK14CAttributeList16GetAttributeByIDEi" + "mac" "@_ZNK14CAttributeList16GetAttributeByIDEi" + } + "CAttributeList::DestroyAllAttributes" //this, returns int + { + "library" "server" + "windows" "\x56\x8B\xF1\x83\x7E\x10\x00\x74\x2A\xC7\x46\x10\x00\x00\x00\x00" + "linux" "@_ZN14CAttributeList20DestroyAllAttributesEv" + "mac" "@_ZN14CAttributeList20DestroyAllAttributesEv" + } + // Found this to be unused, commented out because of that +// "CAttributeList::NotifyManagerOfAttributeValueChanges" //this, presumably returns CAttributeManager +// { +// "library" "server" +// "windows" "\x56\x8B\xF1\x83\x7E\x10\x00\x74\x2A" +// "linux" "@_ZN14CAttributeList36NotifyManagerOfAttributeValueChangesEv" +// "mac" "@_ZN14CAttributeList36NotifyManagerOfAttributeValueChangesEv" +// } +//This apparently still works, and is called by the above + "CAttributeManager::ClearCache" //returns void + { + "library" "server" + "windows" "\x55\x8B\xEC\x51\x56\x8B\xF1\x80\x73\x3C\x00\x0F\x85\x1A\x01\x00\x00" + "linux" "@_ZN17CAttributeManager10ClearCacheEv" + "mac" "@_ZN17CAttributeManager10ClearCacheEv" + } +//The following are old, broken, and generally unusable as of July 10, 2013 +// "CAttributeList::GetAttributeByID" //int, returns CEconAttribute address +// { +// "library" "server" +// "windows" "\x55\x8B\xEC\x51\x53\x8B\xD9\x8B\x43\x10" +// "linux" "@_ZN14CAttributeList16GetAttributeByIDEi" +// "mac" "@_ZN14CAttributeList16GetAttributeByIDEi" +// } +// "CAttributeList::GetAttributeByName" //const char*, returns CEconAttribute address +// { +// "library" "server" +// "windows" "\x55\x8B\xEC\x51\x8B\x45\x08\x53\x50\x8B\xD9" +// "linux" "@_ZNK14CAttributeList18GetAttributeByNameEPKc" +// "mac" "@_ZNK14CAttributeList18GetAttributeByNameEPKc" +// } +// "CAttributeList::SetOrAddAttributeValueByName" //const char*, float, returns void but somehow SetReturnInfo makes it return some nonzero client-based integer if success +// { +// "library" "server" +// "windows" "\x55\x8B\xEC\x8B\x45\x08\x83\xEC\x18\x57" +// "linux" "@_ZN14CAttributeList28SetOrAddAttributeValueByNameEPKcf" +// "mac" "@_ZN14CAttributeList28SetOrAddAttributeValueByNameEPKcf" +// } +// "CAttributeList::RemoveAttribute" //const char*, returns void +// { +// "library" "server" +// "windows" "\x55\x8B\xEC\x51\x56\x8B\xF1\x8B\x46\x10" +// "linux" "@_ZN14CAttributeList15RemoveAttributeEPKc" +// "mac" "@_ZN14CAttributeList15RemoveAttributeEPKc" +// } +//The following aren't used since the above work for any entity with attributes and the below work only for CTFPlayer +//These are in every other regard identical to CAttributeList::SetOrAddAttributeValueByName and CAttributeList::RemoveAttribute +// "CTFPlayer::SetOrAddAttributeValueByName" //const char*, float, returns returns void but somehow SetReturnInfo makes it return some nonzero client-based integer if success +// { +// "library" "server" +// "windows" "\x55\x8B\xEC\x8B\x45\x08\x85\xC0\x74\x2A\x50\x81\xC1\x04\x0A\x00\x00" +// "linux" "@_ZN9CTFPlayer28SetOrAddAttributeValueByNameEPKcf" +// "mac" "@_ZN9CTFPlayer28SetOrAddAttributeValueByNameEPKcf" +// } +// "CTFPlayer::RemovePlayerAttribute" //const char*, returns void +// { +// "library" "server" +// "windows" "\x55\x8B\xEC\x8B\x45\x08\x85\xC0\x74\x2A\x80\x38\x00" +// "linux" "@_ZN9CTFPlayer21RemovePlayerAttributeEPKc" +// "mac" "@_ZN9CTFPlayer21RemovePlayerAttributeEPKc" +// } + } + } +} \ No newline at end of file diff --git a/tf2attributes.inc b/tf2attributes.inc new file mode 100644 index 0000000..73a1d4c --- /dev/null +++ b/tf2attributes.inc @@ -0,0 +1,262 @@ +#if defined _tf2attributes_included + #endinput +#endif +#define _tf2attributes_included + +/** + * Sets an attribute's value on an entity, adding it if it isn't on the entity. + * + * @param iEntity Entity index to set the attribute on. Must have m_AttributeList. + * @param strAttrib Name of the attribute, as from the "name" key in items_game. + * @param flValue Value to set the attribute to + * + * @return True if the attribute was added successfully, false if entity does not have m_AttributeList. + * @error Invalid entity index or attribute name passed. + */ +native bool:TF2Attrib_SetByName(iEntity, String:strAttrib[], Float:flValue); + +/** + * Sets an attribute's value on an entity, adding it if it isn't on the entity. + * + * @param iEntity Entity index to set the attribute on. Must have m_AttributeList. + * @param iDefIndex Definition index of the attribute, as from the number on the attribute entry in items_game. + * @param flValue Value to set the attribute to + * + * @return True if the attribute was added successfully, false if entity does not have m_AttributeList. + * @error Invalid entity index or attribute name passed. + */ +native bool:TF2Attrib_SetByDefIndex(iEntity, iDefIndex, Float:flValue); + +/** + * Returns the address of an attribute on an entity. + * + * @param iEntity Entity index to get attribute from. Must have m_AttributeList. + * @param strAttrib Name of the attribute, as from the "name" key in items_game. + * + * @return Address of the attribute on the entity, or Address_Null if the attribute does not exist on the entity. + * @error Invalid entity index or attribute name passed. + */ +native Address:TF2Attrib_GetByName(iEntity, String:strAttrib[]); + +/** + * Returns the address of an attribute (by attribute index) on an entity. + * + * @param iEntity Entity index to get attribute from. Must have m_AttributeList. + * @param iDefIndex Definition index of the attribute, as from the number on the attribute entry in items_game. + * + * @return Address of the attribute on the entity, or Address_Null if the attribute does not exist on the entity. + * @error Invalid entity index or attribute index passed. + */ +native Address:TF2Attrib_GetByDefIndex(iEntity, iDefIndex); + +/** + * Removes an attribute from an entity. + * + * @param iEntity Entity index to remove attribute from. Must have m_AttributeList. + * @param strAttrib Name of the attribute, as from the "name" key in items_game. + * + * @return True if the SDKCall was made, false if entity had invalid address or m_AttributeList missing. + * @error Invalid entity index or attribute name passed. + */ +native bool:TF2Attrib_RemoveByName(iEntity, String:strAttrib[]); + +/** + * Removes an attribute from an entity. + * + * @param iEntity Entity index to remove attribute from. Must have m_AttributeList. + * @param iDefIndex Definition index of the attribute, as from the number on the attribute entry in items_game. + * + * @return True if the SDKCall was made, false if entity had invalid address or m_AttributeList missing. + * @error Invalid entity index or attribute index passed. + */ +native bool:TF2Attrib_RemoveByDefIndex(iEntity, iDefIndex); + +/** + * Removes all attributes from an entity. + * + * @param iEntity Entity index to remove attribute from. Must have m_AttributeList. + * + * @return True if the SDKCall was made, false if entity had invalid address or m_AttributeList missing. + * @error Invalid entity index passed. + */ +native bool:TF2Attrib_RemoveAll(iEntity); + +/** + * WARNING: Currently silently fails because I haven't tested it and I don't want it crashing. + * + * Clears and presumably rebuilds the attribute cache for an entity, 'refreshing' attributes. + * Call this after making changes to an attribute with any of the TF2Attrib_Set*(Address:pAttrib, arg) natives below. + * You may also need to call this on the entity's m_hOwnerEntity if it is a weapon or wearable. + * You do NOT need to call this after calls to TF2Attrib_SetByName, TF2Attrib_Remove, and TF2Attrib_RemoveAll. + * + * @param iEntity Entity index to remove attribute from. Must have m_AttributeList. + * + * @return True if the SDKCall was made, false if entity had invalid address or m_AttributeList missing. + * @error Invalid entity index passed. + */ +native bool:TF2Attrib_ClearCache(iEntity); + +/** + * Sets the value of m_iAttributeDefinitionIndex (the attribute ID) on an attribute. + * Warning, this changes what GetByName/ID and SetByName 'see' as the name of the attribute, + * but will only change attribute's effects if TF2Attrib_ClearCache is called on the entity with the attribute after. + * + * @param pAttrib Address of the attribute. + * @param iDefIndex Value to set m_iAttributeDefinitionIndex to. + * + * @noreturn + */ +native TF2Attrib_SetDefIndex(Address:pAttrib, iDefIndex); + +/** + * Returns the value of m_iAttributeDefinitionIndex (the attribute ID) on an attribute. + * + * @param pAttrib Address of the attribute. + * + * @return The integer value of m_iAttributeDefinitionIndex on the attribute. + */ +native TF2Attrib_GetDefIndex(Address:pAttrib); + +/** + * Sets the value of m_flValue on an attribute. + * + * @param pAttrib Address of the attribute. + * @param flValue Value to set m_flValue to. + * + * @noreturn + */ +native TF2Attrib_SetValue(Address:pAttrib, Float:flValue); + +/** + * Returns the value of m_flValue on an attribute. + * + * @param pAttrib Address of the attribute. + * + * @return The floating point value of m_flValue on the attribute. + */ +native Float:TF2Attrib_GetValue(Address:pAttrib); + +/** + * Sets the value of m_nRefundableCurrency on an attribute. + * + * @param pAttrib Address of the attribute. + * @param nCurrency Value to set m_nRefundableCurrency to. + * + * @noreturn + */ +native TF2Attrib_SetRefundableCurrency(Address:pAttrib, nCurrency); + +/** + * Returns the value of m_nRefundableCurrency on an attribute. + * + * @param pAttrib Address of the attribute. + * + * @return The (unsigned) integer value of m_nRefundableCurrency on the attribute. + */ +native TF2Attrib_GetRefundableCurrency(Address:pAttrib); + +/** + * Returns the value of m_nRefundableCurrency on an attribute. + * + * @param iEntity Entity index to get attribute list from. Must have m_AttributeList. + * @param iDefIndices Array (max size 16) of attribute definition indices found on the entity. + * + * @return The number of attributes found on the entity's attribute list, or -1 if some error happened. + * @error Invalid entity index passed. + */ +native TF2Attrib_ListDefIndices(iEntity, iDefIndices[]); + +//flInitialValue and bSetBonus don't exist anymore +/** + * Sets the value of m_flInitialValue on an attribute. + * + * @param pAttrib Address of the attribute. + * @param flValue Value to set m_flInitialValue to. + * + * @noreturn + */ +//native TF2Attrib_SetInitialValue(Address:pAttrib, Float:flValue); + +/** + * Returns the value of m_flInitialValue on an attribute. + * + * @param pAttrib Address of the attribute. + * + * @return The floating point value of m_flInitialValue on the attribute. + */ +//native Float:TF2Attrib_GetInitialValue(Address:pAttrib); + +/** + * Sets the boolean value of m_bSetBonus on an attribute. + * + * @param pAttrib Address of the attribute. + * @param bSetBonus Value to set m_bSetBonus to. + * + * @noreturn + */ +//native TF2Attrib_SetIsSetBonus(Address:pAttrib, bool:bSetBonus); + +/** + * Returns the boolean value of m_bSetBonus on an attribute. + * + * @param pAttrib Address of the attribute. + * + * @return The boolean value of m_bSetBonus on the attribute. + */ +//native bool:TF2Attrib_GetIsSetBonus(Address:pAttrib); + +/** + * Gets whether an attribute is stored as an integer or as a float. + * Use TF2Attrib_SetValue(attribute, Float:intValue) on attributes that store values as ints. + * + * @param iDefIndex Index of the attribute (as returned by TF2Attrib_GetDefIndex()). + * + * @return True if attribute value is supposed to be an int, false if float. + */ +stock TF2Attrib_IsIntegerValue(iDefIndex) +{ + switch (iDefIndex) + { + case 133, 143, 147, 152, 184, 185, 186, 192, 193, 194, 198, 211, 214, 227, 228, 229, 262, 294, 302, 372, 373, 374, 379, 381, 383, 403, 420: + { + return true; + } + } + return false; +} + +public SharedPlugin:__pl_tf2attributes = +{ + name = "tf2attributes", + file = "tf2attributes.smx", +#if defined REQUIRE_PLUGIN + required = 1, +#else + required = 0, +#endif +}; + +#if !defined REQUIRE_PLUGIN +public __pl_tf2attributes_SetNTVOptional() +{ + MarkNativeAsOptional("TF2Attrib_SetByName"); + MarkNativeAsOptional("TF2Attrib_SetByDefIndex"); + MarkNativeAsOptional("TF2Attrib_GetByName"); + MarkNativeAsOptional("TF2Attrib_GetByDefIndex"); + MarkNativeAsOptional("TF2Attrib_RemoveByName"); + MarkNativeAsOptional("TF2Attrib_RemoveByDefIndex"); + MarkNativeAsOptional("TF2Attrib_RemoveAll"); + MarkNativeAsOptional("TF2Attrib_ClearCache"); + MarkNativeAsOptional("TF2Attrib_SetDefIndex"); + MarkNativeAsOptional("TF2Attrib_GetDefIndex"); + MarkNativeAsOptional("TF2Attrib_SetValue"); + MarkNativeAsOptional("TF2Attrib_GetValue"); + MarkNativeAsOptional("TF2Attrib_SetInitialValue"); + MarkNativeAsOptional("TF2Attrib_GetInitialValue"); + MarkNativeAsOptional("TF2Attrib_SetRefundableCurrency"); + MarkNativeAsOptional("TF2Attrib_GetRefundableCurrency"); + MarkNativeAsOptional("TF2Attrib_SetIsSetBonus"); + MarkNativeAsOptional("TF2Attrib_GetIsSetBonus"); + MarkNativeAsOptional("TF2Attrib_ListDefIndices"); +} +#endif diff --git a/tf2attributes.sp b/tf2attributes.sp new file mode 100644 index 0000000..0a85684 --- /dev/null +++ b/tf2attributes.sp @@ -0,0 +1,622 @@ +#pragma semicolon 1 + +#include +#include + +#define PLUGIN_NAME "[TF2] TF2Attributes" +#define PLUGIN_AUTHOR "FlaminSarge" +#define PLUGIN_VERSION "1.1.1" //as of Aug 27, 2013 +#define PLUGIN_CONTACT "http://forums.alliedmods.net/showthread.php?t=210221" +#define PLUGIN_DESCRIPTION "A bunch of functions to add attributes to players/weapons/wearables" + +public Plugin:myinfo = { + name = PLUGIN_NAME, + author = PLUGIN_AUTHOR, + description = PLUGIN_DESCRIPTION, + version = PLUGIN_VERSION, + url = PLUGIN_CONTACT +}; +/*class CEconItemAttribute +{ +public: + void *m_pVTable; //0 + + uint16 m_iAttributeDefinitionIndex; //4 + float m_flValue; //8 + int32 m_nRefundableCurrency; //12 +-----removed float m_flInitialValue; //12 +-----removed bool m_bSetBonus; //20 +}; +and +24 is still attribute manager +*/ +new Handle:hSDKSchema; +new Handle:hSDKGetAttributeDef; +new Handle:hSDKGetAttributeDefByName; +new Handle:hSDKSetRuntimeValue; +new Handle:hSDKGetAttributeByID; +new Handle:hSDKGetAttributeByName; +new Handle:hSDKOnAttribValuesChanged; +new Handle:hSDKSetOrAddAttribute; +new Handle:hSDKRemoveAttribute; +new Handle:hSDKDestroyAllAttributes; +new Handle:hSDKClearCache; +public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max) +{ + decl String:game[8]; + GetGameFolderName(game, sizeof(game)); + if (strncmp(game, "tf", 2, false) != 0) + { + strcopy(error, err_max, "Plugin only available for TF2 and possibly TF2Beta"); + return APLRes_Failure; + } + CreateNative("TF2Attrib_SetByName", Native_SetAttrib); + CreateNative("TF2Attrib_SetByDefIndex", Native_SetAttribByID); + CreateNative("TF2Attrib_GetByName", Native_GetAttrib); + CreateNative("TF2Attrib_GetByDefIndex", Native_GetAttribByID); + CreateNative("TF2Attrib_RemoveByName", Native_Remove); + CreateNative("TF2Attrib_RemoveByDefIndex", Native_RemoveByID); + CreateNative("TF2Attrib_RemoveAll", Native_RemoveAll); + CreateNative("TF2Attrib_SetDefIndex", Native_SetID); + CreateNative("TF2Attrib_GetDefIndex", Native_GetID); + CreateNative("TF2Attrib_SetValue", Native_SetVal); + CreateNative("TF2Attrib_GetValue", Native_GetVal); + CreateNative("TF2Attrib_SetInitialValue", Native_SetInitialVal); + CreateNative("TF2Attrib_GetInitialValue", Native_GetInitialVal); + CreateNative("TF2Attrib_SetRefundableCurrency", Native_SetCurrency); + CreateNative("TF2Attrib_GetRefundableCurrency", Native_GetCurrency); + CreateNative("TF2Attrib_SetIsSetBonus", Native_SetSetBonus); + CreateNative("TF2Attrib_GetIsSetBonus", Native_GetSetBonus); + CreateNative("TF2Attrib_ClearCache", Native_ClearCache); + CreateNative("TF2Attrib_ListDefIndices", Native_ListIDs); + RegPluginLibrary("tf2attributes"); + return APLRes_Success; +} +public OnPluginStart() +{ + new Handle:hGameConf = LoadGameConfigFile("tf2.attributes"); + if (hGameConf == INVALID_HANDLE) + { + SetFailState("Could not locate gamedata file tf2.attributes.txt for TF2Attributes, pausing plugin"); + } + StartPrepSDKCall(SDKCall_Static); + PrepSDKCall_SetFromConf(hGameConf, SDKConf_Signature, "GEconItemSchema"); + PrepSDKCall_SetReturnInfo(SDKType_PlainOldData, SDKPass_Plain); //Returns address of schema + hSDKSchema = EndPrepSDKCall(); + if (hSDKSchema == INVALID_HANDLE) + { + SetFailState("Could not initialize call to GEconItemSchema"); + } + + StartPrepSDKCall(SDKCall_Raw); + PrepSDKCall_SetFromConf(hGameConf, SDKConf_Signature, "CEconItemSchema::GetAttributeDefinition"); + PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); + PrepSDKCall_SetReturnInfo(SDKType_PlainOldData, SDKPass_Plain); //Returns address of a CEconItemAttributeDefinition + hSDKGetAttributeDef = EndPrepSDKCall(); + if (hSDKGetAttributeDef == INVALID_HANDLE) + { + SetFailState("Could not initialize call to CEconItemSchema::GetAttributeDefinition"); + } + + StartPrepSDKCall(SDKCall_Raw); + PrepSDKCall_SetFromConf(hGameConf, SDKConf_Signature, "CEconItemSchema::GetAttributeDefinitionByName"); + PrepSDKCall_AddParameter(SDKType_String, SDKPass_Pointer); + PrepSDKCall_SetReturnInfo(SDKType_PlainOldData, SDKPass_Plain); //Returns address of a CEconItemAttributeDefinition + hSDKGetAttributeDefByName = EndPrepSDKCall(); + if (hSDKGetAttributeDefByName == INVALID_HANDLE) + { + SetFailState("Could not initialize call to CEconItemSchema::GetAttributeDefinitionByName"); + } + + StartPrepSDKCall(SDKCall_Raw); + PrepSDKCall_SetFromConf(hGameConf, SDKConf_Signature, "CAttributeList::RemoveAttribute"); + PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); + PrepSDKCall_SetReturnInfo(SDKType_PlainOldData, SDKPass_Plain); // + hSDKRemoveAttribute = EndPrepSDKCall(); + if (hSDKRemoveAttribute == INVALID_HANDLE) + { + SetFailState("Could not initialize call to CAttributeList::RemoveAttribute"); + } + + StartPrepSDKCall(SDKCall_Raw); + PrepSDKCall_SetFromConf(hGameConf, SDKConf_Signature, "CAttributeList::SetRuntimeAttributeValue"); + PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); + PrepSDKCall_AddParameter(SDKType_Float, SDKPass_Plain); + PrepSDKCall_SetReturnInfo(SDKType_PlainOldData, SDKPass_Plain); //Apparently there's no return, but this is nonzero if the attribute is added successfully so let's try it + hSDKSetRuntimeValue = EndPrepSDKCall(); + if (hSDKSetRuntimeValue == INVALID_HANDLE) + { + SetFailState("Could not initialize call to CAttributeList::SetRuntimeAttributeValue"); + } + + StartPrepSDKCall(SDKCall_Raw); + PrepSDKCall_SetFromConf(hGameConf, SDKConf_Signature, "CAttributeList::DestroyAllAttributes"); + PrepSDKCall_SetReturnInfo(SDKType_PlainOldData, SDKPass_Plain); + hSDKDestroyAllAttributes = EndPrepSDKCall(); + if (hSDKDestroyAllAttributes == INVALID_HANDLE) + { + SetFailState("Could not initialize call to CAttributeList::DestroyAllAttributes"); + } + + StartPrepSDKCall(SDKCall_Raw); + PrepSDKCall_SetFromConf(hGameConf, SDKConf_Signature, "CAttributeList::GetAttributeByID"); + PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); + PrepSDKCall_SetReturnInfo(SDKType_PlainOldData, SDKPass_Plain); //Returns address of a CEconItemAttribute + hSDKGetAttributeByID = EndPrepSDKCall(); + if (hSDKGetAttributeByID == INVALID_HANDLE) + { + SetFailState("Could not initialize call to CAttributeList::GetAttributeByID"); + } + + StartPrepSDKCall(SDKCall_Raw); + PrepSDKCall_SetFromConf(hGameConf, SDKConf_Virtual, "CAttributeManager::OnAttributeValuesChanged"); + hSDKOnAttribValuesChanged = EndPrepSDKCall(); + if (hSDKOnAttribValuesChanged == INVALID_HANDLE) + { + SetFailState("Could not initialize call to CAttributeManager::OnAttributeValuesChanged"); + } + CreateConVar("tf2attributes_version", PLUGIN_VERSION, "TF2Attributes version number", FCVAR_NOTIFY|FCVAR_PLUGIN); + + return; + +//Below is stuff that we'll get back to later and/or never, for now everything above this works and PLEASE IGNORE THE COMPILER WARNING. + StartPrepSDKCall(SDKCall_Raw); + PrepSDKCall_SetFromConf(hGameConf, SDKConf_Signature, "CAttributeList::GetAttributeByName"); + PrepSDKCall_AddParameter(SDKType_String, SDKPass_Pointer); + PrepSDKCall_SetReturnInfo(SDKType_PlainOldData, SDKPass_Plain); //Returns address of a CEconItemAttribute + hSDKGetAttributeByName = EndPrepSDKCall(); + if (hSDKGetAttributeByName == INVALID_HANDLE) + { + SetFailState("Could not initialize call to CAttributeList::GetAttributeByName"); + } + + StartPrepSDKCall(SDKCall_Raw); + PrepSDKCall_SetFromConf(hGameConf, SDKConf_Signature, "CAttributeList::SetOrAddAttributeValueByName"); + PrepSDKCall_AddParameter(SDKType_String, SDKPass_Pointer); + PrepSDKCall_AddParameter(SDKType_Float, SDKPass_Plain); + PrepSDKCall_SetReturnInfo(SDKType_PlainOldData, SDKPass_Plain); //Apparently there's no return, but this is nonzero if the attribute is added successfully so let's try it + hSDKSetOrAddAttribute = EndPrepSDKCall(); + if (hSDKSetOrAddAttribute == INVALID_HANDLE) + { + SetFailState("Could not initialize call to CAttributeList::SetOrAddAttributeValueByName"); + } + + StartPrepSDKCall(SDKCall_Raw); + PrepSDKCall_SetFromConf(hGameConf, SDKConf_Signature, "CAttributeList::RemoveAttribute"); + PrepSDKCall_AddParameter(SDKType_String, SDKPass_Pointer); + hSDKRemoveAttribute = EndPrepSDKCall(); + if (hSDKRemoveAttribute == INVALID_HANDLE) + { + SetFailState("Could not initialize call to CAttributeList::RemoveAttribute"); + } + + StartPrepSDKCall(SDKCall_Raw); + PrepSDKCall_SetFromConf(hGameConf, SDKConf_Signature, "CAttributeManager::ClearCache"); + hSDKClearCache = EndPrepSDKCall(); + if (hSDKClearCache == INVALID_HANDLE) + { + SetFailState("Could not initialize call to CAttributeManager::ClearCache"); + } + + +} +public Native_SetAttrib(Handle:plugin, numParams) +{ + new entity = GetNativeCell(1); + if (!IsValidEntity(entity)) + { + return ThrowNativeError(SP_ERROR_NATIVE, "TF2Attrib_SetByName: Invalid entity index %d passed", entity); +// return; + } + decl String:strAttrib[128]; //"counts as assister is some kind of pet this update is going to be awesome" is 73 characters. Valve... Valve. + GetNativeString(2, strAttrib, sizeof(strAttrib)); + new Float:flVal = GetNativeCell(3); + + new offs = GetEntSendPropOffs(entity, "m_AttributeList", true); + if (offs <= 0) + { +// decl String:strClassname[64]; +// if (!GetEntityClassname(entity, strClassname, sizeof(strClassname))) strClassname = ""; +// ThrowNativeError(SP_ERROR_NATIVE, "TF2Attrib_SetByName: \"m_AttributeList\" not found (entity %d/%s)", entity, strClassname); + return false; + } + new Address:pEntity = GetEntityAddress(entity); + if (pEntity == Address_Null) return false; + new Address:pSchema = SDKCall(hSDKSchema); + if (pSchema == Address_Null) return false; + new Address:pAttribDef = SDKCall(hSDKGetAttributeDefByName, pSchema, strAttrib); + if (pAttribDef < Address_MinimumValid) + { + return ThrowNativeError(SP_ERROR_NATIVE, "TF2Attrib_SetByName: Attribute '%s' not valid", strAttrib); + } + new bool:bSuccess = !!SDKCall(hSDKSetRuntimeValue, pEntity+Address:offs, pAttribDef, flVal); + //Just a note, the above SDKCall returns ((entindex + 4) * 4) | 0xA000), and you can AND it with 0x1FFF to get back the entindex if you want, though it's pointless) + //I don't know any other specifics, such as if the highest 3 bits actually matter + //And I don't know what happens when you hit ent index 2047 +// ClearAttributeCache(GetEntPropEnt(entity, Prop_Send, "m_hOwnerEntity")); +// decl String:strClassname[64]; +// GetEntityClassname(entity, strClassname, sizeof(strClassname)); +// if (strncmp(strClassname, "tf_wea", 6, false) == 0 || StrEqual(strClassname, "tf_powerup_bottle", false)) +// { +// new client = GetEntPropEnt(entity, Prop_Send, "m_hOwnerEntity"); +// if (client > 0 && client <= MaxClients && IsClientInGame(client)) ClearAttributeCache(client); +// } + + return bSuccess; +} +public Native_SetAttribByID(Handle:plugin, numParams) +{ + new entity = GetNativeCell(1); + if (!IsValidEntity(entity)) + { + return ThrowNativeError(SP_ERROR_NATIVE, "TF2Attrib_SetByDefIndex: Invalid entity index %d passed", entity); +// return; + } + new iAttrib = GetNativeCell(2); + new Float:flVal = GetNativeCell(3); + + new offs = GetEntSendPropOffs(entity, "m_AttributeList", true); + if (offs <= 0) + { +// decl String:strClassname[64]; +// if (!GetEntityClassname(entity, strClassname, sizeof(strClassname))) strClassname = ""; +// ThrowNativeError(SP_ERROR_NATIVE, "TF2Attrib_SetByDefIndex: \"m_AttributeList\" not found (entity %d/%s)", entity, strClassname); + return false; + } + new Address:pEntity = GetEntityAddress(entity); + if (pEntity == Address_Null) return false; + new Address:pSchema = SDKCall(hSDKSchema); + if (pSchema == Address_Null) return false; + new Address:pAttribDef = SDKCall(hSDKGetAttributeDef, pSchema, iAttrib); + if (pAttribDef < Address_MinimumValid) + { + return ThrowNativeError(SP_ERROR_NATIVE, "TF2Attrib_SetByDefIndex: Attribute %d not valid", iAttrib); + } + new bool:bSuccess = !!SDKCall(hSDKSetRuntimeValue, pEntity+Address:offs, pAttribDef, flVal); +// ClearAttributeCache(GetEntPropEnt(entity, Prop_Send, "m_hOwnerEntity")); +// decl String:strClassname[64]; +// GetEntityClassname(entity, strClassname, sizeof(strClassname)); +// if (strncmp(strClassname, "tf_wea", 6, false) == 0 || StrEqual(strClassname, "tf_powerup_bottle", false)) +// { +// new client = GetEntPropEnt(entity, Prop_Send, "m_hOwnerEntity"); +// if (client > 0 && client <= MaxClients && IsClientInGame(client)) ClearAttributeCache(client); +// } + + return bSuccess; +} +public Native_GetAttrib(Handle:plugin, numParams) +{ + new entity = GetNativeCell(1); + if (!IsValidEntity(entity)) + { + return ThrowNativeError(SP_ERROR_NATIVE, "TF2Attrib_GetByName: Invalid entity index %d passed", entity); +// return; + } + decl String:strAttrib[128]; + GetNativeString(2, strAttrib, sizeof(strAttrib)); + + new offs = GetEntSendPropOffs(entity, "m_AttributeList", true); + if (offs <= 0) + { +// decl String:strClassname[64]; +// if (!GetEntityClassname(entity, strClassname, sizeof(strClassname))) strClassname = ""; +// ThrowNativeError(SP_ERROR_NATIVE, "TF2Attrib_GetByName: \"m_AttributeList\" not found (entity %d/%s)", entity, strClassname); + return _:Address_Null; + } + new Address:pEntity = GetEntityAddress(entity); + if (pEntity == Address_Null) return _:Address_Null; + new Address:pSchema = SDKCall(hSDKSchema); + if (pSchema == Address_Null) return _:Address_Null; + new Address:pAttribDef = SDKCall(hSDKGetAttributeDefByName, pSchema, strAttrib); + if (pAttribDef < Address_MinimumValid) + { + return ThrowNativeError(SP_ERROR_NATIVE, "TF2Attrib_GetByName: Attribute '%s' not valid", strAttrib); + } + new iDefIndex = LoadFromAddress(pAttribDef + Address:4, NumberType_Int16); + new Address:pAttrib = Address:SDKCall(hSDKGetAttributeByID, pEntity+Address:offs, iDefIndex); + return (pAttrib < Address_MinimumValid ? (_:Address_Null) : (_:pAttrib)); +} + +public Native_GetAttribByID(Handle:plugin, numParams) +{ + new entity = GetNativeCell(1); + if (!IsValidEntity(entity)) + { + return ThrowNativeError(SP_ERROR_NATIVE, "TF2Attrib_GetByDefIndex: Invalid entity index %d passed", entity); +// return; + } + new iDefIndex = GetNativeCell(2); + + new offs = GetEntSendPropOffs(entity, "m_AttributeList", true); + if (offs <= 0) + { +// decl String:strClassname[64]; +// if (!GetEntityClassname(entity, strClassname, sizeof(strClassname))) strClassname = ""; +// ThrowNativeError(SP_ERROR_NATIVE, "TF2Attrib_GetByName: \"m_AttributeList\" not found (entity %d/%s)", entity, strClassname); + return _:Address_Null; + } + new Address:pEntity = GetEntityAddress(entity); + if (pEntity == Address_Null) return _:Address_Null; + new Address:pAttrib = Address:SDKCall(hSDKGetAttributeByID, pEntity+Address:offs, iDefIndex); + return (pAttrib < Address_MinimumValid ? (_:Address_Null) : (_:pAttrib)); +} + +public Native_Remove(Handle:plugin, numParams) +{ + new entity = GetNativeCell(1); + if (!IsValidEntity(entity)) + { + ThrowNativeError(SP_ERROR_NATIVE, "TF2Attrib_RemoveByName: Invalid entity index %d passed", entity); + return false; + // return; + } + decl String:strAttrib[128]; + GetNativeString(2, strAttrib, sizeof(strAttrib)); + + new offs = GetEntSendPropOffs(entity, "m_AttributeList", true); + if (offs <= 0) + { +// decl String:strClassname[64]; +// if (!GetEntityClassname(entity, strClassname, sizeof(strClassname))) strClassname = ""; +// ThrowNativeError(SP_ERROR_NATIVE, "TF2Attrib_Remove: \"m_AttributeList\" not found (entity %d/%s)", entity, strClassname); + return false; + // return; + } + new Address:pEntity = GetEntityAddress(entity); + if (pEntity == Address_Null) + { + return false; + // return; + } + if (pEntity == Address_Null) return false; + new Address:pSchema = SDKCall(hSDKSchema); + if (pSchema == Address_Null) return false; + new Address:pAttribDef = SDKCall(hSDKGetAttributeDefByName, pSchema, strAttrib); + if (pAttribDef < Address_MinimumValid) + { + return ThrowNativeError(SP_ERROR_NATIVE, "TF2Attrib_RemoveByName: Attribute '%s' not valid", strAttrib); + } + SDKCall(hSDKRemoveAttribute, pEntity+Address:offs, pAttribDef); //Not a clue what the return is here, but it's probably a clone of the attrib being removed + +// SDKCall(hSDKRemoveAttribute, pEntity+Address:offs, strAttrib); +// ClearAttributeCache(GetEntPropEnt(entity, Prop_Send, "m_hOwnerEntity")); +// decl String:strClassname[64]; +// GetEntityClassname(entity, strClassname, sizeof(strClassname)); +// if (strncmp(strClassname, "tf_wea", 6, false) == 0 || StrEqual(strClassname, "tf_powerup_bottle", false)) +// { +// new client = GetEntPropEnt(entity, Prop_Send, "m_hOwnerEntity"); +// if (client > 0 && client <= MaxClients && IsClientInGame(client)) ClearAttributeCache(client); +// } + + return true; +} +public Native_RemoveByID(Handle:plugin, numParams) +{ + new entity = GetNativeCell(1); + if (!IsValidEntity(entity)) + { + ThrowNativeError(SP_ERROR_NATIVE, "TF2Attrib_RemoveByDefIndex: Invalid entity index %d passed", entity); + return false; + // return; + } + new iAttrib = GetNativeCell(2); + + new offs = GetEntSendPropOffs(entity, "m_AttributeList", true); + if (offs <= 0) + { +// decl String:strClassname[64]; +// if (!GetEntityClassname(entity, strClassname, sizeof(strClassname))) strClassname = ""; +// ThrowNativeError(SP_ERROR_NATIVE, "TF2Attrib_Remove: \"m_AttributeList\" not found (entity %d/%s)", entity, strClassname); + return false; + // return; + } + new Address:pEntity = GetEntityAddress(entity); + if (pEntity == Address_Null) + { + return false; + // return; + } + if (pEntity == Address_Null) return false; + new Address:pSchema = SDKCall(hSDKSchema); + if (pSchema == Address_Null) return false; + new Address:pAttribDef = SDKCall(hSDKGetAttributeDef, pSchema, iAttrib); + if (pAttribDef < Address_MinimumValid) + { + return ThrowNativeError(SP_ERROR_NATIVE, "TF2Attrib_RemoveByDefIndex: Attribute %d not valid", iAttrib); + } + SDKCall(hSDKRemoveAttribute, pEntity+Address:offs, pAttribDef); //Not a clue what the return is here, but it's probably a clone of the attrib being removed + +// SDKCall(hSDKRemoveAttribute, pEntity+Address:offs, strAttrib); +// ClearAttributeCache(GetEntPropEnt(entity, Prop_Send, "m_hOwnerEntity")); +// decl String:strClassname[64]; +// GetEntityClassname(entity, strClassname, sizeof(strClassname)); +// if (strncmp(strClassname, "tf_wea", 6, false) == 0 || StrEqual(strClassname, "tf_powerup_bottle", false)) +// { +// new client = GetEntPropEnt(entity, Prop_Send, "m_hOwnerEntity"); +// if (client > 0 && client <= MaxClients && IsClientInGame(client)) ClearAttributeCache(client); +// } + + return true; +} +public Native_RemoveAll(Handle:plugin, numParams) +{ + new entity = GetNativeCell(1); + if (!IsValidEntity(entity)) + { + ThrowNativeError(SP_ERROR_NATIVE, "TF2Attrib_RemoveAll: Invalid entity index %d passed", entity); + return false; + // return; + } + + new offs = GetEntSendPropOffs(entity, "m_AttributeList", true); + if (offs <= 0) + { +// decl String:strClassname[64]; +// if (!GetEntityClassname(entity, strClassname, sizeof(strClassname))) strClassname = ""; +// ThrowNativeError(SP_ERROR_NATIVE, "TF2Attrib_RemoveAll: \"m_AttributeList\" not found (entity %d/%s)", entity, strClassname); + return false; + // return; + } + new Address:pEntity = GetEntityAddress(entity); + if (pEntity == Address_Null) + { + return false; + // return; + } + SDKCall(hSDKDestroyAllAttributes, pEntity+Address:offs); //disregard the return (Valve does!) + +// ClearAttributeCache(GetEntPropEnt(entity, Prop_Send, "m_hOwnerEntity")); +// decl String:strClassname[64]; +// GetEntityClassname(entity, strClassname, sizeof(strClassname)); +// if (strncmp(strClassname, "tf_wea", 6, false) == 0 || StrEqual(strClassname, "tf_powerup_bottle", false)) +// { +// new client = GetEntPropEnt(entity, Prop_Send, "m_hOwnerEntity"); +// if (client > 0 && client <= MaxClients && IsClientInGame(client)) ClearAttributeCache(client); +// } + + return true; +} + +public Native_SetID(Handle:plugin, numParams) +{ + new Address:pAttrib = Address:GetNativeCell(1); +// if (pAttrib < Address_MinimumValid) return; + new iDefIndex = GetNativeCell(2); + StoreToAddress(pAttrib+Address:4, iDefIndex, NumberType_Int16); +} + +public Native_GetID(Handle:plugin, numParams) +{ + new Address:pAttrib = Address:GetNativeCell(1); +// if (pAttrib < Address_MinimumValid) return -1; + return LoadFromAddress(pAttrib+Address:4, NumberType_Int16); +} + +public Native_SetVal(Handle:plugin, numParams) +{ + new Address:pAttrib = Address:GetNativeCell(1); +// if (pAttrib < Address_MinimumValid) return; + new flVal = GetNativeCell(2); //It's a float but avoiding tag mismatch warnings + StoreToAddress(pAttrib+Address:8, flVal, NumberType_Int32); +} + +public Native_GetVal(Handle:plugin, numParams) +{ + new Address:pAttrib = Address:GetNativeCell(1); +// if (pAttrib < Address_MinimumValid) return -1; + return LoadFromAddress(pAttrib+Address:8, NumberType_Int32); +} + +public Native_SetInitialVal(Handle:plugin, numParams) +{ + return ThrowNativeError(SP_ERROR_NATIVE, "TF2Attrib_SetInitialValue: m_flInitialValue is no longer present on attributes"); + +// new Address:pAttrib = Address:GetNativeCell(1); +// if (pAttrib < Address_MinimumValid) return; +// new flInitialVal = GetNativeCell(2); //It's a float but avoiding tag mismatch warnings +// StoreToAddress(pAttrib+Address:12, flInitialVal, NumberType_Int32); +} + +public Native_GetInitialVal(Handle:plugin, numParams) +{ + return ThrowNativeError(SP_ERROR_NATIVE, "TF2Attrib_GetInitialValue: m_flInitialValue is no longer present on attributes"); + +// new Address:pAttrib = Address:GetNativeCell(1); +// if (pAttrib < Address_MinimumValid) return -1; +// return LoadFromAddress(pAttrib+Address:12, NumberType_Int32); +} + +public Native_SetCurrency(Handle:plugin, numParams) +{ + new Address:pAttrib = Address:GetNativeCell(1); +// if (pAttrib < Address_MinimumValid) return; + new nCurrency = GetNativeCell(2); + StoreToAddress(pAttrib+Address:12, nCurrency, NumberType_Int32); +} + +public Native_GetCurrency(Handle:plugin, numParams) +{ + new Address:pAttrib = Address:GetNativeCell(1); +// if (pAttrib < Address_MinimumValid) return -1; + return LoadFromAddress(pAttrib+Address:12, NumberType_Int32); +} + +public Native_SetSetBonus(Handle:plugin, numParams) +{ + return ThrowNativeError(SP_ERROR_NATIVE, "TF2Attrib_SetIsSetBonus: m_bSetBonus is no longer present on attributes"); + +// new Address:pAttrib = Address:GetNativeCell(1); +// if (pAttrib < Address_MinimumValid) return; +// new bool:bSetBonus = !!GetNativeCell(2); +// StoreToAddress(pAttrib+Address:20, bSetBonus, NumberType_Int8); +} + +public Native_GetSetBonus(Handle:plugin, numParams) +{ + return ThrowNativeError(SP_ERROR_NATIVE, "TF2Attrib_SetIsSetBonus: m_bSetBonus is no longer present on attributes"); + +// new Address:pAttrib = Address:GetNativeCell(1); +// if (pAttrib < Address_MinimumValid) return -1; +// return !!LoadFromAddress(pAttrib+Address:20, NumberType_Int8); +} + +public Native_ClearCache(Handle:plugin, numParams) +{ + new entity = GetNativeCell(1); + if (!IsValidEntity(entity)) + { + ThrowNativeError(SP_ERROR_NATIVE, "TF2Attrib_ClearCache: Invalid entity index %d passed", entity); + return false; + } + return ClearAttributeCache(entity); +} + +public Native_ListIDs(Handle:plugin, numParams) +{ + new entity = GetNativeCell(1); + if (!IsValidEntity(entity)) + { + ThrowNativeError(SP_ERROR_NATIVE, "TF2Attrib_ListDefIndices: Invalid entity index %d passed", entity); + return -1; + // return; + } + + new offs = GetEntSendPropOffs(entity, "m_AttributeList", true); + if (offs <= 0) + { +// decl String:strClassname[64]; +// if (!GetEntityClassname(entity, strClassname, sizeof(strClassname))) strClassname = ""; +// ThrowNativeError(SP_ERROR_NATIVE, "TF2Attrib_RemoveAll: \"m_AttributeList\" not found (entity %d/%s)", entity, strClassname); + return -1; + // return; + } + new Address:pEntity = GetEntityAddress(entity); + if (pEntity == Address_Null) + { + return -1; + // return; + } + new Address:pAttribList = Address:LoadFromAddress(pEntity + Address:(offs + 4), NumberType_Int32); + if (pAttribList < Address_MinimumValid) return -1; + new iNumAttribs = LoadFromAddress(pEntity + Address:(offs + 16), NumberType_Int32); + new iAttribIndices[16]; + for (new i = 0; i < iNumAttribs; i++) //THIS IS HOW YOU GET THE ATTRIBUTES ON AN ITEM! + { + iAttribIndices[i] = LoadFromAddress(pAttribList + Address:(i * 16 + 4), NumberType_Int16); + } + SetNativeArray(2, iAttribIndices, 16); + return iNumAttribs; +} + +stock bool:ClearAttributeCache(entity) +{ + if (hSDKOnAttribValuesChanged == INVALID_HANDLE) return false; + if (entity <= 0 || !IsValidEntity(entity)) return false; + new offs = GetEntSendPropOffs(entity, "m_AttributeList", true); + if (offs <= 0) return false; + new Address:pAttribs = GetEntityAddress(entity); + if (pAttribs < Address_MinimumValid) return false; + pAttribs = Address:LoadFromAddress(pAttribs+Address:(offs+24), NumberType_Int32); + if (pAttribs < Address_MinimumValid) return false; + SDKCall(hSDKOnAttribValuesChanged, pAttribs); + return true; +} \ No newline at end of file diff --git a/tf2attributes_example.sp b/tf2attributes_example.sp new file mode 100644 index 0000000..fc757b4 --- /dev/null +++ b/tf2attributes_example.sp @@ -0,0 +1,588 @@ +#pragma semicolon 1 + +#include +#include + +new Address:lastAddr[MAXPLAYERS + 1]; + +public OnPluginStart() +{ + RegAdminCmd("sm_addattrib", Command_AddAttrib, ADMFLAG_ROOT); + RegAdminCmd("sm_addwepatt", Command_AddWepAttrib, ADMFLAG_ROOT); + RegAdminCmd("sm_remattrib", Command_RemAttrib, ADMFLAG_ROOT); + RegAdminCmd("sm_remwepatt", Command_RemWepAttrib, ADMFLAG_ROOT); + RegAdminCmd("sm_remallatt", Command_RemAllAttrib, ADMFLAG_ROOT); + RegAdminCmd("sm_remallwepatt", Command_RemAllWepAttrib, ADMFLAG_ROOT); + RegAdminCmd("sm_getattrib", Command_GetAttrByName, ADMFLAG_ROOT); +// RegAdminCmd("sm_getattrid", Command_GetAttrByID, ADMFLAG_ROOT); + RegAdminCmd("sm_getattrs", Command_GetAttrs, ADMFLAG_ROOT); +// RegAdminCmd("sm_attrset", SetValueStuff, ADMFLAG_ROOT); //Definitely unsafe as all hell + LoadTranslations("common.phrases"); +} +public OnMapStart() +{ + for (new client = 0; client <= MaxClients; client++) + lastAddr[client] = Address_Null; +} +public Action:Command_RemAttrib(client, args) +{ + decl String:arg1[32]; + decl String:arg2[32]; + if (args < 2) + { + ReplyToCommand(client, "[SM] Usage: sm_remattrib "); + arg1 = "@me"; + return Plugin_Handled; + } + + GetCmdArg(1, arg1, sizeof(arg1)); + GetCmdArg(2, arg2, sizeof(arg2)); + new bool:bydefidx = false; + if (arg2[0] == '#') + { + strcopy(arg2, sizeof(arg2), arg2[1]); + bydefidx = true; + } + + /** + * target_name - stores the noun identifying the target(s) + * target_list - array to store clients + * target_count - variable to store number of clients + * tn_is_ml - stores whether the noun must be translated + */ + new String:target_name[MAX_TARGET_LENGTH]; + new target_list[MAXPLAYERS], target_count; + new bool:tn_is_ml; + + if (arg1[0] == '#' && arg1[1] == '#') //'##entindex' instead of target + { + target_list[0] = StringToInt(arg1[2]); + target_count = 1; + strcopy(target_name, sizeof(target_name), arg1[2]); + tn_is_ml = false; + } + else + if ((target_count = ProcessTargetString( + arg1, + client, + target_list, + MAXPLAYERS, + COMMAND_FILTER_CONNECTED, + target_name, + sizeof(target_name), + tn_is_ml)) <= 0) + { + /* This function replies to the admin with a failure message */ + ReplyToTargetError(client, target_count); + return Plugin_Handled; + } + + for (new i = 0; i < target_count; i++) + { + if (IsValidEntity(target_list[i])) + { + if (bydefidx) + TF2Attrib_RemoveByDefIndex(target_list[i], StringToInt(arg2)); + else + TF2Attrib_RemoveByName(target_list[i], arg2); + } + } + if (tn_is_ml) + ReplyToCommand(client, "[SM] Removed attrib '%s' from %t", arg2, target_name); + else + ReplyToCommand(client, "[SM] Removed attrib '%s' from %s", arg2, target_name); + return Plugin_Handled; +} +public Action:Command_RemWepAttrib(client, args) +{ + decl String:arg1[32]; + decl String:arg2[32]; + if (args < 2) + { + ReplyToCommand(client, "[SM] Usage: sm_remwepatt "); + arg1 = "@me"; + return Plugin_Handled; + } + + GetCmdArg(1, arg1, sizeof(arg1)); + GetCmdArg(2, arg2, sizeof(arg2)); + new bool:bydefidx = false; + if (arg2[0] == '#') + { + strcopy(arg2, sizeof(arg2), arg2[1]); + bydefidx = true; + } + /** + * target_name - stores the noun identifying the target(s) + * target_list - array to store clients + * target_count - variable to store number of clients + * tn_is_ml - stores whether the noun must be translated + */ + new String:target_name[MAX_TARGET_LENGTH]; + new target_list[MAXPLAYERS], target_count; + new bool:tn_is_ml; + + if ((target_count = ProcessTargetString( + arg1, + client, + target_list, + MAXPLAYERS, + COMMAND_FILTER_ALIVE, + target_name, + sizeof(target_name), + tn_is_ml)) <= 0) + { + /* This function replies to the admin with a failure message */ + ReplyToTargetError(client, target_count); + return Plugin_Handled; + } + for (new i = 0; i < target_count; i++) + { + if (IsValidClient(target_list[i])) + { + new wep = GetEntPropEnt(target_list[i], Prop_Send, "m_hActiveWeapon"); + if (IsValidEntity(wep)) + (bydefidx ? TF2Attrib_RemoveByDefIndex(wep, StringToInt(arg2)) : TF2Attrib_RemoveByName(wep, arg2)); + } + } + if (tn_is_ml) + ReplyToCommand(client, "[SM] Removed attrib '%s' from active wep of %t", arg2, target_name); + else + ReplyToCommand(client, "[SM] Removed attrib '%s' from active wep of %s", arg2, target_name); + return Plugin_Handled; +} +public Action:Command_RemAllAttrib(client, args) +{ + decl String:arg1[32]; + if (args < 1) + { + ReplyToCommand(client, "[SM] Usage: sm_remallatt "); + arg1 = "@me"; + } + else GetCmdArg(1, arg1, sizeof(arg1)); + + + /** + * target_name - stores the noun identifying the target(s) + * target_list - array to store clients + * target_count - variable to store number of clients + * tn_is_ml - stores whether the noun must be translated + */ + new String:target_name[MAX_TARGET_LENGTH]; + new target_list[MAXPLAYERS], target_count; + new bool:tn_is_ml; + + if (arg1[0] == '#' && arg1[1] == '#') //'##entindex' instead of target + { + target_list[0] = StringToInt(arg1[2]); + target_count = 1; + strcopy(target_name, sizeof(target_name), arg1[2]); + tn_is_ml = false; + } + else + if ((target_count = ProcessTargetString( + arg1, + client, + target_list, + MAXPLAYERS, + COMMAND_FILTER_CONNECTED, + target_name, + sizeof(target_name), + tn_is_ml)) <= 0) + { + /* This function replies to the admin with a failure message */ + ReplyToTargetError(client, target_count); + return Plugin_Handled; + } + for (new i = 0; i < target_count; i++) + { + if (IsValidEntity(target_list[i])) + { + TF2Attrib_RemoveAll(target_list[i]); + } + } + if (tn_is_ml) + ReplyToCommand(client, "[SM] Removed allattrib from %t", target_name); + else + ReplyToCommand(client, "[SM] Removed allattrib from %s", target_name); + return Plugin_Handled; +} +public Action:Command_RemAllWepAttrib(client, args) +{ + decl String:arg1[32]; + if (args < 1) + { + ReplyToCommand(client, "[SM] Usage: sm_remallwepatt "); + arg1 = "@me"; + } + else GetCmdArg(1, arg1, sizeof(arg1)); + + /** + * target_name - stores the noun identifying the target(s) + * target_list - array to store clients + * target_count - variable to store number of clients + * tn_is_ml - stores whether the noun must be translated + */ + new String:target_name[MAX_TARGET_LENGTH]; + new target_list[MAXPLAYERS], target_count; + new bool:tn_is_ml; + + if ((target_count = ProcessTargetString( + arg1, + client, + target_list, + MAXPLAYERS, + COMMAND_FILTER_ALIVE, + target_name, + sizeof(target_name), + tn_is_ml)) <= 0) + { + /* This function replies to the admin with a failure message */ + ReplyToTargetError(client, target_count); + return Plugin_Handled; + } + for (new i = 0; i < target_count; i++) + { + if (IsValidClient(target_list[i])) + { + new wep = GetEntPropEnt(target_list[i], Prop_Send, "m_hActiveWeapon"); + if (IsValidEntity(wep)) + TF2Attrib_RemoveAll(wep); + } + } + if (tn_is_ml) + ReplyToCommand(client, "[SM] Removed allattrib from active wep of %t", target_name); + else + ReplyToCommand(client, "[SM] Removed allattrib from active wep of %s", target_name); + return Plugin_Handled; +} +public Action:Command_AddAttrib(client, args) +{ + decl String:arg1[32]; + decl String:arg2[128]; + decl String:arg3[32]; + decl String:arg4[32]; + if (args < 3) + { + ReplyToCommand(client, "[SM] Usage: sm_addattrib [pass as int]"); + return Plugin_Handled; + } + + GetCmdArg(1, arg1, sizeof(arg1)); + GetCmdArg(2, arg2, sizeof(arg2)); + new bool:bydefidx = false; + if (arg2[0] == '#') + { + strcopy(arg2, sizeof(arg2), arg2[1]); + bydefidx = true; + } + GetCmdArg(3, arg3, sizeof(arg3)); + if (args > 3) GetCmdArg(4, arg4, sizeof(arg4)); + else arg4 = "0"; + new Float:val = (!!StringToInt(arg4) ? (Float:StringToInt(arg3)) : StringToFloat(arg3)); + /** + * target_name - stores the noun identifying the target(s) + * target_list - array to store clients + * target_count - variable to store number of clients + * tn_is_ml - stores whether the noun must be translated + */ + new String:target_name[MAX_TARGET_LENGTH]; + new target_list[MAXPLAYERS], target_count; + new bool:tn_is_ml; + + if (arg1[0] == '#' && arg1[1] == '#') //'##entindex' instead of target + { + target_list[0] = StringToInt(arg1[2]); + target_count = 1; + strcopy(target_name, sizeof(target_name), arg1[2]); + tn_is_ml = false; + } + else + if ((target_count = ProcessTargetString( + arg1, + client, + target_list, + MAXPLAYERS, + COMMAND_FILTER_CONNECTED, + target_name, + sizeof(target_name), + tn_is_ml)) <= 0) + { + /* This function replies to the admin with a failure message */ + ReplyToTargetError(client, target_count); + return Plugin_Handled; + } + for (new i = 0; i < target_count; i++) + { + if (IsValidEntity(target_list[i])) + { + new bool:result = false; + if (bydefidx) + result = TF2Attrib_SetByDefIndex(target_list[i], StringToInt(arg2), val); + else + result = TF2Attrib_SetByName(target_list[i], arg2, val); + if (target_count == 1) + { + ReplyToCommand(client, "[SM] AddAttrib returned %d", result); + } + } + } + if (tn_is_ml) + ReplyToCommand(client, "[SM] Added attrib '%s' val %s to %t", arg2, arg3, target_name); + else + ReplyToCommand(client, "[SM] Added attrib '%s' val %s to %s", arg2, arg3, target_name); + return Plugin_Handled; +} +public Action:Command_AddWepAttrib(client, args) +{ + decl String:arg1[32]; + decl String:arg2[128]; + decl String:arg3[32]; + decl String:arg4[32]; + if (args < 3) + { + ReplyToCommand(client, "[SM] Usage: sm_addattrib [pass as int]"); + return Plugin_Handled; + } + + GetCmdArg(1, arg1, sizeof(arg1)); + GetCmdArg(2, arg2, sizeof(arg2)); + GetCmdArg(3, arg3, sizeof(arg3)); + if (args > 3) GetCmdArg(4, arg4, sizeof(arg4)); + else arg4 = "0"; + new Float:val = (!!StringToInt(arg4) ? (Float:StringToInt(arg3)) : StringToFloat(arg3)); + new bool:bydefidx = false; + if (arg2[0] == '#') + { + strcopy(arg2, sizeof(arg2), arg2[1]); + bydefidx = true; + } + /** + * target_name - stores the noun identifying the target(s) + * target_list - array to store clients + * target_count - variable to store number of clients + * tn_is_ml - stores whether the noun must be translated + */ + new String:target_name[MAX_TARGET_LENGTH]; + new target_list[MAXPLAYERS], target_count; + new bool:tn_is_ml; + + if ((target_count = ProcessTargetString( + arg1, + client, + target_list, + MAXPLAYERS, + COMMAND_FILTER_ALIVE, + target_name, + sizeof(target_name), + tn_is_ml)) <= 0) + { + /* This function replies to the admin with a failure message */ + ReplyToTargetError(client, target_count); + return Plugin_Handled; + } + for (new i = 0; i < target_count; i++) + { + if (IsValidClient(target_list[i])) + { + new wep = GetEntPropEnt(target_list[i], Prop_Send, "m_hActiveWeapon"); + if (!IsValidEntity(wep)) continue; + new bool:result = (bydefidx ? TF2Attrib_SetByDefIndex(wep, StringToInt(arg2), val) : TF2Attrib_SetByName(wep, arg2, val)); + if (target_count == 1) + { + ReplyToCommand(client, "[SM] AddAttrib wep returned %d", result); + } + } + } + if (tn_is_ml) + ReplyToCommand(client, "[SM] Added attrib '%s' val %s to active wep of %t", arg2, arg3, target_name); + else + ReplyToCommand(client, "[SM] Added attrib '%s' val %s to active wep of %s", arg2, arg3, target_name); + return Plugin_Handled; +} +public Action:Command_GetAttrByName(client, args) +{ + decl String:arg1[32]; + decl String:arg2[128]; + decl String:arg3[32]; + if (args < 2) + { + ReplyToCommand(client, "[SM] Usage: sm_getattr [p/w]"); + return Plugin_Handled; + } + + GetCmdArg(1, arg1, sizeof(arg1)); + GetCmdArg(2, arg2, sizeof(arg2)); + new bool:bydefidx = false; + if (arg2[0] == '#') + { + strcopy(arg2, sizeof(arg2), arg2[1]); + bydefidx = true; + } + if (args > 2) GetCmdArg(3, arg3, sizeof(arg3)); + else arg3 = "p"; + new bool:usePlayer = arg3[0] != 'w'; + new target = -1; + if (arg1[0] == '#' && arg1[1] == '#') //'##entindex' instead of target + { + target = StringToInt(arg1[2]); + } + else target = FindTarget(client, arg1, false, false); + if (!IsValidEntity(target)) return Plugin_Handled; + + new wep = target; + if (!usePlayer) + { + wep = GetEntPropEnt(target, Prop_Send, "m_hActiveWeapon"); + if (!IsValidEntity(wep)) + { + usePlayer = true; + wep = target; + } + } + + new Address:pAttrib = (bydefidx ? TF2Attrib_GetByDefIndex(wep, StringToInt(arg2)) : TF2Attrib_GetByName(wep, arg2)); + lastAddr[client] = pAttrib; + if (Address:pAttrib < Address_MinimumValid) + { + ReplyToCommand(client, "[SM] GetAttrib got null attrib '%s' on %s%d", arg2, usePlayer ? "" : "active wep of ", target);//, target); + return Plugin_Handled; + } + new Float:result = TF2Attrib_GetValue(pAttrib); + new idx = TF2Attrib_GetDefIndex(pAttrib); + if (TF2Attrib_IsIntegerValue(idx)) result = float(_:result); + new Float:init;// = TF2Attrib_GetInitialValue(pAttrib); + if (TF2Attrib_IsIntegerValue(idx)) init = float(_:init); + ReplyToCommand(client, "[SM] GetAttrib got: %d %d ; %.4f, %.4f, %d, %d for attrib '%s' on %s%d", _:pAttrib, idx, result, init, TF2Attrib_GetRefundableCurrency(pAttrib), 0 /*TF2Attrib_GetIsSetBonus(pAttrib)*/, arg2, usePlayer ? "" : "active wep of ", target);//, target); + return Plugin_Handled; +} +public Action:Command_GetAttrByID(client, args) +{ + decl String:arg1[32]; + decl String:arg2[128]; + decl String:arg3[32]; + if (args < 2) + { + ReplyToCommand(client, "[SM] Usage: sm_getattr [p/w]"); + return Plugin_Handled; + } + + GetCmdArg(1, arg1, sizeof(arg1)); + GetCmdArg(2, arg2, sizeof(arg2)); + new index = StringToInt(arg2); + if (args > 2) GetCmdArg(3, arg3, sizeof(arg3)); + else arg3 = "p"; + new bool:usePlayer = arg3[0] != 'w'; + new target = -1; + if (arg1[0] == '#' && arg1[1] == '#') //'##entindex' instead of target + { + target = StringToInt(arg1[2]); + } + else target = FindTarget(client, arg1, false, false); + if (!IsValidEntity(target)) return Plugin_Handled; + + new wep = target; + if (!usePlayer) + { + wep = GetEntPropEnt(target, Prop_Send, "m_hActiveWeapon"); + if (!IsValidEntity(wep)) + { + usePlayer = true; + wep = target; + } + } + + new Address:pAttrib = TF2Attrib_GetByDefIndex(wep, index); + lastAddr[client] = pAttrib; + if (Address:pAttrib < Address_MinimumValid) + { + ReplyToCommand(client, "[SM] GetAttrib got null attrib '%d' on %s%d", index, usePlayer ? "" : "active wep of ", target);//, target); + return Plugin_Handled; + } + new Float:result = TF2Attrib_GetValue(pAttrib); + new idx = TF2Attrib_GetDefIndex(pAttrib); + if (TF2Attrib_IsIntegerValue(idx)) result = float(_:result); + new Float:init;// = TF2Attrib_GetInitialValue(pAttrib); + if (TF2Attrib_IsIntegerValue(idx)) init = float(_:init); + ReplyToCommand(client, "[SM] GetAttrib got: %d %d ; %.4f, %.4f, %d, %d for attrib '%s' on %s%d", _:pAttrib, idx, result, init, TF2Attrib_GetRefundableCurrency(pAttrib), 0 /*TF2Attrib_GetIsSetBonus(pAttrib)*/, arg2, usePlayer ? "" : "active wep of ", target);//, target); + return Plugin_Handled; +} +public Action:Command_GetAttrs(client, args) +{ + decl String:arg1[64]; + decl String:arg3[32]; + if (args < 1) + { + ReplyToCommand(client, "[SM] Usage: sm_getattrs [p/w]"); + return Plugin_Handled; + } + + GetCmdArg(1, arg1, sizeof(arg1)); + if (args > 1) GetCmdArg(2, arg3, sizeof(arg3)); + else arg3 = "p"; + new bool:usePlayer = arg3[0] != 'w'; + new target = -1; + if (arg1[0] == '#' && arg1[1] == '#') //'##entindex' instead of target + { + target = StringToInt(arg1[2]); + } + else target = FindTarget(client, arg1, false, false); + if (!IsValidEntity(target)) return Plugin_Handled; + + new wep = target; + if (!usePlayer) + { + wep = GetEntPropEnt(target, Prop_Send, "m_hActiveWeapon"); + if (!IsValidEntity(wep)) + { + usePlayer = true; + wep = target; + } + } + + new attriblist[16]; + arg1 = "[SM] ListDefIndices:"; + new count = TF2Attrib_ListDefIndices(wep, attriblist); + for (new i = 0; i < count; i++) + { + Format(arg1, sizeof(arg1), "%s %d", arg1, attriblist[i]); + } + ReplyToCommand(client, "%s on %s%d", arg1, usePlayer ? "" : "active wep of ", target);//, target); + return Plugin_Handled; +} +public Action:SetValueStuff(client, args) +{ + decl String:arg1[32]; + decl String:arg2[32]; + decl String:arg3[32]; + if (args < 3) + { + ReplyToCommand(client, "[SM] Usage: sm_attrset
"); + return Plugin_Handled; + } + GetCmdArg(1, arg1, sizeof(arg1)); + GetCmdArg(2, arg2, sizeof(arg2)); + GetCmdArg(3, arg3, sizeof(arg3)); + new Address:addr = Address:StringToInt(arg1); + if (addr != lastAddr[client] || addr < Address_MinimumValid) + { + ReplyToCommand(client, "[SM] Unsafe address"); + return Plugin_Handled; + } + new type = StringToInt(arg2); + switch (type) + { + case 1: TF2Attrib_SetDefIndex(addr, StringToInt(arg3)); + case 2: TF2Attrib_SetValue(addr, StringToFloat(arg3)); +// case 3: TF2Attrib_SetInitialValue(addr, StringToFloat(arg3)); + case 3: TF2Attrib_SetRefundableCurrency(addr, StringToInt(arg3)); +// case 5: TF2Attrib_SetIsSetBonus(addr, !!StringToInt(arg3)); + } + ReplyToCommand(client, "[SM] Set %d on %d to %s", type, addr, arg3); + return Plugin_Handled; +} +stock bool:IsValidClient(client) +{ + if (client <= 0 || client > MaxClients) return false; + return IsClientInGame(client); +} \ No newline at end of file