Actors, brains and health
Open the live demo · Read the source · View on GitHub
An Actor is a thin handle onto an entity: everything it answers, health,
brain, body, facing, is read off a component that may or may not be
there. A turret has no body; a barrel has no brain. Nothing forces an actor
to carry parts it never uses.
Step 1: A brain with its own memory #
A game's own brain extends Brain and overrides only the hooks it cares
about. This one remembers whether it has ever been hurt.
/// A brain with one bit of memory: whether it has ever been hurt.
final class _WaryBrain extends Brain {
bool everHurt = false;
@override
void onHurt(Mind it, double amount) => everHurt = true;
@override
Map<String, Object?> save() => <String, Object?>{'everHurt': everHurt};
@override
void restore(Map<String, Object?> from) =>
everHurt = from['everHurt'] == true;
}
Step 2: Build an actor from components #
Vitality wraps a Health; Thinking wraps a Brain. Setting both on an
entity and wrapping it in an Actor gives a single handle onto them.
final entities = EcsWorld();
final entity = entities.spawn();
entities
..set(entity, Vitality(Health(30.0)))
..set(entity, Thinking(_WaryBrain()));
final goblin = Actor(entities, entity, name: 'goblin');
Step 3: Hurt it and read the result #
applyDamage goes through Health.damage, which returns true only on the
hit that kills. The brain hears about it separately, through onHurt.
final brain = goblin.brain! as _WaryBrain;
goblin.applyDamage(12.0);
brain.onHurt(Mind(_stubSystem()), 12.0);
final currentHealth = goblin.health!.current;
Twelve damage against thirty health leaves the goblin at eighteen, still alive, and its brain now remembers being hurt.
Step 4: Hit it and watch #
The same goblin, on the floor, with a bar over its head. Hit it for 12
runs the damage through Health and tells the brain, exactly as above: the
bar shrinks, the goblin goes amber the first time it is hurt (the brain's own
memory, not a colour the page keeps), and after three hits it is flat and
grey. A fresh goblin builds it again from nothing.
final EcsWorld entities = EcsWorld();
final entity = entities.spawn();
_brain = _WaryBrain();
entities
..set(entity, Vitality(Health(30.0)))
..set(entity, Thinking(_brain));
_goblin = Actor(entities, entity, name: 'goblin');
// The damage goes through `Health`; the brain hears of it separately.
_goblin.applyDamage(_hitAsked);
_brain.onHurt(Mind(_stubSystem()), _hitAsked);