The decision model
Everything nexBash does in combat comes down to one idea: on each game prompt it walks a priority lane top-to-bottom and uses the first action that is legal right now.
The pieces
| Piece | What it is |
|---|---|
| Action | One ability: { id, queue, canExecute(ctx, tuning), execute(ctx, tuning) }. canExecute is a pure yes/no gate; execute returns commands. |
| Lane | A curated, ordered list of catalog keys. The order is the priority. Strategies have a primary lane and usually a battlerage lane. |
| Strategy | A class expressed as data: its lanes plus optional profile-scoped shared args. See Strategies. |
Context (ctx) | A fresh per-tick bundle of tactical answers and player-state predicates. |
| Action tuning | A precomputed { strategy, action } envelope containing the active profile's two configuration scopes. |
| Selector | The first-valid walk over a lane that produces the chosen action and its tuning. |
The loop
On every prompt while in combat:
- nexBash builds one fresh decision context.
- It walks the active strategy's
primarylane in order. - For each catalog key it reads that key's precomputed tuning and asks
canExecute(ctx, tuning). - The first action that answers
truewins. Itsexecute(ctx, tuning)receives the exact same tuning object and returns the commands to queue. - If a coupled battlerage is ready, nexBash selects it through the same keyed tuning path and fuses it into the attack command stack.
Autonomous battlerages run an independent first-valid pass when rage/freerage state changes. See Battlerage.
The prompt and battlerage handlers run within the combat state of the core
state machine. See the Overview for the
complete state chart.
Because selection is a fresh walk each tick, priority is an order of preference, not a fixed script. A high-priority ability that is not currently legal is passed over in favor of the next valid one.
The two tuning scopes
Profile configuration is deliberately separate from the per-tick context. Every
catalog key receives an explicit ActionTuning envelope:
{
strategy: { daggerId: "59237", scytheId: "330399" },
action: { hp: 0.3 },
}
tuning.strategycontains values owned by the whole strategy/profile, such as shared equipment identities.tuning.actioncontains values owned by one catalog action, such as that action's HP threshold.
The scopes may use the same property name without colliding. Actions never read
nexBash.currentStrategy to find configuration. When a profile is applied,
nexBash derives immutable tuning objects and keyed lane entries for the effective
lanes. Prompt and rage selection then perform reads over that precomputed state;
they do not merge tuning per candidate. The winning reference is passed unchanged
to execution.
Actions with no declared values still receive stable empty strategy and
action objects. This keeps the invocation contract uniform without adding work
to the hot path.
The decision context
canExecute(ctx, tuning) is the entire situational brain of an action, and it is
pure: it reads only the context and static tuning, never host globals, and has
no side effects. The context is the adapter that reads nexSys, nexGui, GMCP, and
the target model and turns them into answers.
| Field | Meaning |
|---|---|
ctx.target | Active target facts: id, name, hp (0-100%), totalHp, shielded, shouldCC, cc, resistances, damageTypes, and canHeal. |
ctx.hasTarget / ctx.targetCount | Whether a target exists and how many mobs are in the room. |
ctx.aoeTargetIds | Unshielded mob IDs available to multi-target abilities. |
ctx.party | Party members, leader, size, isMember, and isLeader. |
ctx.enabled(key) | Whether a catalog key belongs to an effective lane. |
ctx.haveAff / ctx.haveAnyAff / ctx.haveDef / ctx.haveBal / ctx.isClass | Player-state predicates delegated to nexSys4. |
ctx.selfHp / ctx.selfMana | Self vitals as 0-1 ratios. |
ctx.rage / ctx.spark / ctx.transcendence | Class resource pools. |
ctx.wielded | The character's currently wielded items. |
ctx.battlerage | Battlerage balance, live flags, configured buffers, and razeReady. |
ctx.room | Environment answers such as canFly, canBurrow, and fleeDirection. |
ctx.config | Global player options plus the active area's targetThreshold. |
ctx.config remains the adapter for global options and active-area facts. It is
not a strategy-profile configuration channel; those values belong in
ActionTuning.
A damaging action is rejected before its local gate when the active target resists the action's damage type.
The action catalog
Every ability lives in a flat, namespaced catalog, keyed like magi.erode,
battlerage.disintegrate, or general.fly. Lanes reference these keys, and the
configuration bench is drawn from keys available to the selected class.
nexBash.actionCatalog.list({ namespace: "magi" });
nexBash.actionCatalog.get("magi.dissolution");
Where rules fit
nexBash does not use a per-tick rules engine to mutate priorities. The condition "use this ability only when these facts hold" lives directly in each pure gate, and first-valid resolves the ordered lane. A general rule registry exists as reserved infrastructure but is not wired into the runtime or public contract.
Seeing why an action was chosen
A decision trace can record each selection pass for debugging. It is off by default so the hot path stays free:
nexBash.trace.enable();
// fight for a bit
nexBash.trace.list();
nexBash.trace.disable();
You can also subscribe to a live stream with nexBash.trace.subscribe(fn).