Skip to main content

Buffs

A buff is a property of an entity whose value is derived from a set of contributed values. Buffs are normally used to represent temporary effects on an entity, such as a speed boost or a damage reduction.

A buff involves a number of entities - one receiver and multiple senders. Senders each contribute their value to the buff using an assignment statement. Whenever a sender despawns, their contribution is removed. Upon reading the buff's value, the contributed values are integrated into a single value according to a custom function. This result is then cached and only recalculated when the contributed values change. Additionally, it is possible to await onto the buff to detect when it changes.

pub buff owner.Boost ~ |boosts| boosts.Sum

pub fn owner.Example() {
Transmission {
P { "Press X to boost!" }
}

on ButtonDown(BtnX) {
Subspawn { // create a new entity to represent the temporary effect
Boost(1) // this entity contribute a value to the buff
once Tick(5s) { Expire } // the entity and its contribution expire after 5 seconds
}
}

with Boost {
Transmission {
P { "Your current boost: " + Boost }
}
}
}

Declaration

Every buff must have a subject parameter. The subject parameter designates the entity that will be the receiver of the buff.

The buff's name must begin with an uppercase letter.

buff owner.Boost

Buffs can be declared as public using the pub keyword, which allows them to be accessed from all files in the program. Otherwise they are private and can only be accessed from within the same file. If two buffs have the same name but are in different files, they are not the same buff and will not interfere with each other.

pub buff owner.Boost

Buffs may optionally be declared with a meld function, which is used to integrate the contributed values into a single value. The meld function is a Callback that receives an array of contributed values and must return a single value.

pub buff owner.Boost ~ |boosts| boosts.Sum

Contributing a value

A sender contributes a value to a buff calling it as a function.

Boost(1) // contribute the value 1 to the buff

In the above example, the sender and receiver entities are inferred implicitly from context. This is the conventional way to contribute a value to a buff. However, if this is the sender and owner is the receiver, we could instead specify these explicitly as follows:

this.Boost(1, owner)

Normally, a single entity can only contribute a single value to the buff. Assigning a new contribution to the buff replaces the old contribution. It is possible to contribute multiple values to the same buff using an ID.

Boost<fred>(1)
Boost<fred>(2) // replace the previous contribution
Boost<george>(3) // add a new contribution

It is possible to explicitly delete a contribution using the delete keyword.

delete Boost

When the sender expires, its contribution is removed from the buff automatically.

Reading the value

The buff's value can be read the same way as any other property:

let value = Boost // implicit subject (recommended)
let value = this.Boost // or explicit subject

When the buff is read, the contributed values are melded into a single value according to its meld function, included in the buff declaration. The meld function receives an array of contributed values and must return a single value. For example, this is a meld function that returns the sum of all of its contributed values:

pub buff owner.Boost ~ |boosts| boosts.Sum

A statement block may also be used instead of an expression:

pub buff owner.Boost ~ |boosts| {
return boosts.Sum
}

It is possible to omit the meld function, in which case the maximum value will be returned instead. For example:

pub buff owner.Boost

The result of the meld function is cached, and the buff will keep returning the same value without recalculating it until the contributed values change.

Awaiting change

It is possible to await the buff to detect when it changes, for example:

await Boost

This will suspend execution until the contributions to the buff change. Like all await functions, it is possible to use with, on or once to handle the change:

with Boost {
Transmission {
P { "Your current boost: " + Boost }
}
}

Note that the buff does not return any value when awaited. The buff must be read separately in order to obtain its value. This can be used to avoid unnecessary recalculations. For example, if a buff changes frequently but its value is only needed to update a Transmission, then it may be a good idea to await for the Paint event before updating the Transmission. Changing the user interface more frequently than once per tick would never be seen by the player and so would be a waste of computation.

with Boost {
await Paint
Transmission {
P { "Your current boost: " + Boost }
}
}