Creating Custom Attributes - nosoop/SM-TFCustAttr GitHub Wiki

If you're already familiar with writing SourceMod plugins, this is just another set of natives to easily pull custom information from an attribute-holding entity (weapon, mainly) in a unified way. The following bit is emphasized:

Treat custom attributes as you would a non-static entity property.

The API provides just the minimum functions to make pulling these custom "properties" easy. If you need examples, check out the publicly-available attribute sets, or the examples provided in this project's repository.

For rapid iteration, install the tf_custom_attributes_debugger plugin, then use the sm_custattr_add command to dynamically add / update attributes.

Quick start

Here's a short example of the attribute-checking API:

// client is assumed to be in-game
int weapon = GetPlayerWeaponSlot(client, 0);
if (IsValidEntity(weapon) && TF2CustAttr_GetInt(weapon, "test attribute") != 0) {
    // user's primary weapon has "test attribute" assigned with a non-zero value.
}

That's all. The core plugin will return the integer value of the test attribute entry in an entity's key/value storage. It will carry over if someone else picks up a dropped instance of the item.

There is no strict form of namespacing — multiple plugins that refer to the name test attribute will get the same value. You'll need to make your identifier sufficiently unique enough if you're running multiple plugins.

At the moment (in core 0.3.2), calls to the attribute system are still relatively expensive; the best practice is to ensure you have sufficient preconditions before checking the attribute, and fail fast.

There is no particular path convention for attribute plugins; they can go anywhere within the plugins/ directory or any nested subdirectories, so it's up to you to organize them.

If you need to make changes to an attribute you're working on, all you need to do is reload your plugin — changes will take effect immediately (as long as you're not maintaining state like you shouldn't be).

For developers used to how Custom Weapons works

The Custom Attributes system is a different paradigm compared to the attribute system Custom Weapons provides. The core Custom Attributes plugin holds all the weapon state, not the attribute plugins themselves. Written correctly, a plugin using Custom Attributes:

  1. Can be directly reloaded, with instant feedback on plugin changes. No need to reequip a weapon or touch the resupply cabinet.
  2. Works correctly with dropped / picked-up weapons with no lingering effects.
  3. Reflects changes appropriately when the attribute value is updated.

So, here's a few habits that need to be changed:

  • Don't cache attribute values in-plugin. That violates (1) and possibly (3), and the whole bool g_bHasAttribute[MAX_ENTITIES]; boilerplate crap is what frustrated me enough to write my own system in the first place. Instead, use TF2CustAttr_Get*() natives to retrieve the values before you use them (again, as you would with any entity property).
  • Don't assume attributes are available in some particular forward. That violates (2) and (3), and provided you're using the adapter, Custom Weapons might not even know a weapon exists with that attribute (because it's applied later). The reason there is no "attributes applied" forward is to prevent developers falling back into this convention, which was a legacy assumption from a time before TF2 allowed players to pick up dropped weapons. You shouldn't have to worry about the lifetime of an attribute; just check it when you need it.
  • Don't use conditional hooks based on the presence of an attribute. I've seen Custom Weapon attributes that hook players or other things in response to the attribute applied forward. What you'll have to do is hook / detour every situation where the attribute might be present. So, yes, if it's a damage-based attribute, hook it on every entity it applies to.

Note that these are assumed to be habits / conventions picked up from Custom Weapons. There may be certain situations where you know that you can safely ignore these guidelines, but you should keep the numbered rules in mind.

If you're writing attributes that are intended for use with Custom Weapons, it's ideal to have CW installed, then follow the instructions on applying Custom Attributes. CW may behave in ways that your plugin doesn't expect in certain circumstances.