This document is about: QUANTUM 2
SWITCH TO

Attributes

Overview

The AttributesSystem provides a way to store and calculate values based on external conditions - e.g. an effect providing a +50% speed while active or +100% damage while standing in a specific zone.

Setup

In order to implement an attribute, two things need to be done:

  1. The controller of the target entity using the attribute (usually an Actor) has to implement IAttributesController. This controls which attributes are present on the entity, resets them to the default state when necessary and processes the collected values.
  2. The controller of the source entity holding the attribute properties (e.g. effect or zone) has to implement IAttributesProvider. This is used to change the attribute values collected by the target entitiy.

The attribute values are computed by the source entity and made available for collection to the target entity. The relationship between the target and source entities, as well as the computation of attribute values can be visualized in the following diagram.

attributes system
Attribute Values Computation Flow.

The AttributesSystem executes the followings for each entity with an Attributes component:

  1. IAttributesController.Reset() is called on the target entity's entity controller; this entity is the one with the Attributes compoent. At this point, all attribute components should be reset to their default values, usually 1.0 for multiplicative and 0.0 for additive modifiers. (Represented by the Red line in the diagram)
  2. IAttributeProvider.Collect() is called on all registered attribute providers, i.e. the entity controllers of the source entities. Each provider updates its internal attribute value for the entity. (Represented by the Green line in the diagram)
  3. IAttributesController.Process() is called on the target entity's entity controller. At this stage, the values from all providers have already multiplied/accumulated and the final value is ready for further calculations or being passed to component desires. (Represented by the Blue line in the diagram)

N.B.: To participate in an entity's attributes collection, an attribute has to register to its owner entity's collection via Attributes.RegisterProvider() or IAttributesProvider.RegisterOnCreate. To unregister an attribute, Attributes.UnregisterProvider() has to be called.

Example

This section exemplifies how the AttributesSystem would be interfacing with the DamageAttributeEffectController implementation included in the FPS Template.

C#

public unsafe sealed class DamageAttributeEffectController : AttributeEffectController, IAttributesProvider
{
    //========== PUBLIC MEMBERS ===================================================================================

    public FP PercentDamageDoneModifier     = FP._0;
    public FP PercentDamageReceivedModifier = FP._0;

    //========== AttributeEffectController INTERFACE ==============================================================

    protected override bool OnActivate(Frame frame, EntityRef entity, EntityRef target)
    {
        // 1. The effect is activated (returns true) only if the target has Damage attribute
        
        DamageAttribute* damageAttribute = frame.TryGetComponent<DamageAttribute>(target);
        return damageAttribute != null;
    }

    //========== IAttributesProvider INTERFACE ====================================================================

    // 2. This attribute provider is registered to the pipeline when created
    
    bool IAttributesProvider.RegisterOnCreate => true;

    void IAttributesProvider.Collect(Frame frame, EntityRef entity, EntityRef target)
    {
        // 3. Collection phase, called each frame the effect exists
        
        DamageAttribute* damageAttribute          = frame.TryGetComponent<DamageAttribute>(target);
        damageAttribute->DamageDoneMultiplier    += PercentDamageDoneModifier * FP._0_01;
        damageAttribute->DamageReceiveMultiplier += PercentDamageReceivedModifier * FP._0_01;
    }
}
Back to top