Collectors
A collector is a special kind of property that collects values from multiple entities, and then merges them into a single value. It can be used to create temporary effects like speed boosts or damage buffs. It can also be used to create a collection of values from across the world, such as a list of buildings that belong to a player.
Declaration
A collector is declared using the collect keyword:
pub collect owner.FruitBasket
This declares a collector called FruitBasket on the owner Entity.
When written like this without any additional parameters,
it will simply collect all values it is given into an Array.
Giving values to the collector
Entities give values to a collector using the give keyword.
give owner.FruitBasket("Apple")
This makes the current entity this give the String value "Apple" to the FruitBasket on the owner Entity.
Note that the current entity this is implicitly found from context.
Only until despawned
Whenever an entity despawns, any values it was giving are removed.
This is a powerful feature of collectors that makes it easy to manage temporary effects.
For example, this is how we can give an "Apple" that only lasts for 5 seconds:
Spawn {
give owner.FruitBasket("Apple")
once Tick(5s) { Expire }
}
Omitting the object parameter
If omitted, the object parameter will be found from context, if possible:
SpawnEachPlayer owner {
give FruitBasket("Apple") // `owner` omitted, will be found from context
}
Reading the value of the collector
The value of the collector can be read by simply calling it directly.
let fruits = owner.FruitBasket
This returns an Array of all the values in the FruitBasket.
Waiting for change
You can use the await keyword on the collector to wait until it changes.
let fruits = await owner.FruitBasket
The above line will suspend execution of the current function until FruitBasket changes,
then it will return its new value.
More commonly, you would use a with, on or once block to wait on a separate thread, rather than awaiting it directly:
with FruitBasket {
"You have: " + FruitBasket.Join(", ")
}
Merge functions
By default, collect will just return an Array of all its collected values.
Sometimes, you want to take all the collected values and merge them into a single value.
For example, you might want to know the Sum of all the active boosts belonging to a player.
To do this, you can provide a merge function after a ~ character.
The function will be given an array of values as input, and should return a value.
pub collect owner.NumBoosts ~ Sum
The above code merges all the collected values into a single Number value using the Sum function.
Now, whenever the NumBoosts collector is read, it will return the sum of all the collected values.
Any function that takes an array of values and returns a value can be used as a merge function. You can even write your own merge function like this:
pub collect owner.PowerLevel ~ SumExtremes
fn SumExtremes(values) -> value {
return values.FindMax + values.FindMin
}
Common built-in functions that are useful for merging include: Sum, Count, FindMax and FindMin.
Default values
Functions like FindMax return undefined when there are no values in the collector.
Use the null coalescing operator ?? to specify a default value to use in those cases:
pub collect owner.BoostFactor ~ FindMax ?? 1
Inline custom merge functions
If needed, you can specify your own merge function inline using the Callback syntax:
pub collect owner.AbsorbProportion ~ |values| values.FindMin ?? 1
Picking out one value
Sometimes, you only need to look at one of the values in the collector,
and it doesn't matter which one.
Easel has a special syntax for situation.
If you specify a default value for the collector using the = character,
it will either return the first value if it exists, or the default value you provide:
pub collect owner.IsInvisible = false
Imagine a game where your hero can pick up an invisibility powerup.
When activated, the powerup will give IsInvisible(true).
Since all the collected values are the same, it doesn't matter which one we look at.
We can just check the first value in the collector to see if the hero is invisible,
and we don't need to look at the rest.
In this situation, the above code snippet will return either true if we have
any invisibility powerup, regardless of how many we have, or false if we have none.
While you can achieve the same effect using the custom merge function below, using a default value is much more performant as it has a special code path in the engine.
pub collect owner.IsInvisible ~ |values| values[0] ?? false
Managing given values
Deleting given values
If you want to delete a value from a collector, you can use a delete give statement.
You must provide the same ID to both the give and delete give statements.
give<cherry> augustus.FruitBasket("Cherry")
delete give<cherry>