polix.triggers.core
Domain-agnostic reactive event-driven trigger system.
polix.triggers connects events to effects via conditions evaluated by polix.unify/unify. The library provides mechanics for trigger registration, event processing, timing, and stacking—consumers define their own event types, entities, and domain logic.
Quick Start
(require '[polix.triggers.core :as triggers])
;; Create a registry
(def reg (triggers/create-registry))
;; Register a trigger
(def reg' (triggers/register-trigger
reg
{:event-types #{:entity/damaged}
:timing :polix.triggers.timing/after
:condition [:= :doc/target-id :doc/trigger.self]
:effect {:type :heal :amount 1}}
"ability-123" ; source
"player-1" ; owner
"entity-1")) ; self
;; Fire an event
(triggers/fire-event
{:state {} :registry reg'}
{:type :entity/damaged :target-id "entity-1" :amount 5})
Core Concepts
- Event: A map with
:typekeyword and domain-specific fields - Trigger: A registered listener that responds to events
- Condition: A polix policy expression evaluated via polix.unify/unify
- Timing: When the trigger fires (
:before,:instead,:after,:at)
Condition Evaluation
Trigger conditions are evaluated using polix.unify/unify against a document built from the event and trigger context. The document contains:
- All event fields (e.g.,
:target-id,:amount) :self- the entity the trigger is attached to:owner- the owner of the trigger source:source- the source that registered the trigger:event-type- the event’s:typevalue
Use :doc/ prefixed accessors in conditions (e.g., :doc/self, :doc/target-id).
Conditions that return a residual (missing data) are treated conservatively: the trigger does NOT fire if its condition cannot be fully evaluated.
See polix.triggers.schema for data structure definitions.
create-registry
(create-registry)Creates an empty trigger registry.
The registry maintains triggers indexed by ID and by event type for efficient lookup during event processing.
fire-event
(fire-event ctx event)Fires an event and processes all matching triggers.
Takes a context map containing :registry and :state, plus the event to fire. Returns a result map with:
:state- updated state after applying effects:registry- updated registry (triggers may be removed if:once?):event- the original event:results- vector of trigger results:prevented?- true if a before trigger prevented the action
Processing order: 1. Before triggers (sorted by priority, can set :prevented?) 2. Instead triggers (first matching only, skipped if prevented) 3. After triggers (sorted by priority, skipped if prevented) 4. At triggers (always processed)
Example:
(fire-event {:state {:hp 10} :registry reg}
{:type :entity/damaged :target-id "e-1" :amount 5})
get-trigger
(get-trigger registry trigger-id)Returns a trigger by ID, or nil if not found.
get-triggers
(get-triggers registry)Returns all registered triggers as a sequence.
get-triggers-for-event
(get-triggers-for-event registry event-type)Returns triggers that listen for the given event type.
Triggers are sorted by priority (lower values fire first).
register-trigger
(register-trigger registry trigger-def source-id owner self)Registers a trigger definition in the registry.
Takes a trigger definition map and binding context (source, owner, self). Returns the updated registry with the new trigger added.
The trigger definition should contain:
:event-types- set of event type keywords to listen for:timing- when to fire (:polix.triggers.timing/before, etc.):condition- optional polix policy expression:effect- effect to apply when condition is satisfied:once?- optional, remove after firing (default false):priority- optional, lower values fire first (default 0)
The condition is a polix policy expression evaluated via polix.unify/unify when the trigger is processed. It can reference:
- Event fields (e.g.,
:doc/target-id,:doc/amount) - Trigger bindings (
:doc/self,:doc/owner,:doc/source) - Event type via
:doc/event-type
Example:
(register-trigger registry
{:event-types #{:entity/damaged}
:timing :polix.triggers.timing/after
:condition [:= :doc/target-id :doc/self]
:effect {:type :counter-attack}}
"ability-123" ; source that registered this
"player-1" ; owner of the source
"entity-1") ; entity the source is attached to
unregister-trigger
(unregister-trigger registry trigger-id)Removes a trigger by ID from the registry.
Returns the updated registry. If the trigger ID does not exist, returns the registry unchanged.
unregister-triggers-by-source
(unregister-triggers-by-source registry source-id)Removes all triggers registered by a source.
Useful when an ability or effect is removed and all its triggers should be cleaned up. Returns the updated registry.