Attributes — Runtime¶
A general container for the numbers a character is made of: values keyed by owner and id, with bounds, change events, and seams for contribution and combination.
Overview¶
An AttributeSet on an owner holds attributes — authored as inspector rows, or created at runtime by the first write to a new id. Consumers read effective values through IAttributeSource, found on the owner's chain the way every optional capability in this framework is found.
There is no fixed stat list. The framework does not know what strength is, and it never will: an attribute is a string id, a float base value, and optional bounds. What the ids mean, and how contributions stack, belongs to the project.
Attributes supply; stats are consumed elsewhere¶
The framework's stat consumption seams already ship — IAttackerDamageModifier, ICraftingLevelSource and their siblings, each owned by the system that consults it. Attributes is the supply side. The adapters under Integrations/Attributes are where a stored value becomes a stat: how an attribute maps onto a crafting level is the adapter's visible, replaceable decision, not a rule of this assembly.
The container never combines¶
An effective value is base, handed through the project's IAttributeCombiner together with the contributions collected from IAttributeModifierProvider components on the owner's chain, then clamped to the attribute's bounds. No combiner ships with the framework, and there is nowhere in this assembly to put a stacking formula — that is a load-bearing absence, argued in Documentation/Attributes.
Sign convention¶
Values may be negative, and bounds may be negative. Nothing is normalised silently: a non-finite value is refused with a warning, and an inverted bound pair is reported and ignored rather than quietly rewritten.
Change events¶
The BaseValueChanged event on AttributeSet carries an AttributeDelta — owner, id, and both the old and new value — and fires only on an actual change. Effective values are computed reads and deliberately have no event: the container cannot know when a provider's answer changes, any more than Health knows when a damage rule would answer differently.