Lua v4 Combat Actions
Lua v4 exposes two deliberately different combat actions:
hexis.combat.fireissues one bounded stationary ranged shot. Success proves that Hexis fired, not that the shot hit or defeated the target.hexis.combat.engagepursues and fights one exact target with the implementedmelee_kitebehavior. Success proves that target was defeated.
Both actions are implemented in the source foundation. They remain non-executable in these docs until a named distributed build passes controlled live acceptance.
Shared boundary
Both actions require the combat capability. Their potential resources are
exactly MOVEMENT, CAMERA, ATTACK_USE, and HOTBAR, acquired atomically.
Contention returns retryable busy. A cleanup failure returns non-retryable
internal_error and quarantines the shared controls.
The target shape for both actions is exactly:
target = {
ref = {
id = "f81d4fae-7dec-11d0-a765-00a0c91e6bf6",
world_generation = 8,
},
}
id is a UUID and world_generation is a non-negative integer copied from
bounded entity evidence. The target must resolve to the same live living entity
in the current resident world and be no farther than 128 blocks at preflight.
A stale generation returns non-retryable world_changed; a missing, dead, or
too-distant entity returns retryable target_lost. Both happen before controls
or a combat mechanism are acquired. Neither action falls back to coordinates,
substitutes another entity, or chooses a replacement target.
Unknown, missing, or malformed request fields are catchable Lua errors before dispatch. Action results become visible only after owned controls and inputs have been safely released. Host timeout, explicit cancellation, world change, authority loss, or runtime invalidation can terminate either action through the shared lifecycle with empty action-specific data.
Fire one ranged shot
hexis.combat.fire accepts exactly these fields:
local shot = hexis.combat.fire({
target = {ref = observed_target.ref},
held = {
name_patterns = {"Frozen Scythe"},
},
projectile = "direct",
timing = {
reaction_ms = 100,
dwell_ms = 100,
cooldown_ms = 500,
},
})
local result = hexis.await(shot, {timeout = 5})
The held selector contains 1 through 16 nonblank name substrings of at most
128 characters. Matching is trimmed and case-insensitive. This action does not
select a weapon: the current main-hand item must match at the start and again
immediately before firing.
projectile is exactly direct or arrow. direct aims at the target;
arrow adds the implemented vertical gravity compensation. It does not infer
the projectile from the held item. Timing is strict and bounded:
| Field | Bounds |
|---|---|
reaction_ms | integer 0 through 750 |
dwell_ms | integer 50 through 500 |
cooldown_ms | integer 250 through 2,000 |
The action takes exclusive movement control and clears movement input. It waits
for the manager-owned minimum interval since the previous accepted fire,
applies the requested reaction delay, then requires line of sight and aim within
1 degree continuously for dwell_ms. Aim is bounded to 3 seconds. It sends one
vanilla right-click and stops. cooldown_ms is a lower bound before this shot,
not an extra sleep after it.
A mechanism result has exactly:
{
outcome = "fired", -- or "not_fired"
target = {ref = observed_target.ref},
projectile = "direct",
elapsed_ms = 284,
fired_tick = 93012,
}
fired_tick is 0 when no shot was sent. The mechanism outcomes are:
| Code | Retryable | Meaning |
|---|---|---|
ok | No | One right-click was accepted; outcome is fired |
item_not_found | No | The held item did not match or changed before firing |
target_lost | Yes | The exact living target disappeared or exceeded 128 blocks |
aim_failed | Yes | Visibility and settled aim were not achieved within 3 seconds |
busy | Yes | Physical combat control could not issue the shot |
cancelled | No | The bounded shot was cancelled |
Those mechanism outcomes include the exact data above. Preflight and shared
lifecycle failures can instead carry empty data. combat.fire does not confirm
damage, health change, death, drops, or a kill. It does not emit player_dead.
Engage one melee target
hexis.combat.engage accepts required target, behavior, and
timeout_seconds, plus optional held and stop_below_health_percent:
local fight = hexis.combat.engage({
target = {ref = observed_target.ref},
held = {name_patterns = {"Melee Weapon"}},
behavior = {
kind = "melee_kite",
attack_range = 3,
minimum_distance = 1.5,
preferred_distance = 2,
maximum_distance = 2.5,
maximum_pursuit_distance = 64,
attacks_per_second = 7,
},
timeout_seconds = 15,
stop_below_health_percent = 25,
})
local result = hexis.await(fight, {timeout = 20})
behavior has exactly the seven fields shown. kind is exactly
melee_kite. All distances must be finite and satisfy:
0.75 <= minimum_distance <= preferred_distance <= maximum_distance
<= attack_range <= 3.0
maximum_pursuit_distance is from 4 through 128,
attacks_per_second is an integer from 1 through 12, and
timeout_seconds is an integer from 1 through 30. An optional held uses the
same 1-through-16, 128-character selector bounds as combat.fire.
stop_below_health_percent, when present, must be a finite number from 1
through 100 inclusive. Omitting it disables this threshold and preserves the
engagement's behavior before the field was added; there is no implicit default.
The action never selects a weapon. When held is present, the current item must
match it. When omitted, the current selected slot, display name, and item ID are
accepted as the weapon. Either way, any later slot or item change returns
non-retryable item_not_found.
Outside maximum_distance, Hexis pursues the target's observed position with
the Pathfinder. This action explicitly disallows drops and Ether Transmission.
It returns retryable unreachable only when the target exceeds the explicit
maximum_pursuit_distance policy bound or the typed navigator proves complete
frontier exhaustion. Two failed navigation attempts and 4 seconds without
meaningful pursuit progress instead return retryable navigation_failed.
Unlike the standalone navigator's non-retryable unreachable, combat keeps
this code retryable because the moving target and a later observation can
change the pursuit decision.
Inside maximum_distance, Hexis aims at the exact target. Below
preferred_distance, it moves backward only when loaded local collision checks
prove solid, dry support and clear body space. Otherwise it holds position.
minimum_distance selects precision retreat input; it is not a guaranteed hard
separation distance. Hexis attacks only within attack_range, with line of
sight, the exact entity under the live crosshair, and the requested attack-rate
limit. Three seconds without visibility while in the engagement range returns
retryable aim_failed.
When stop_below_health_percent is present, the combat mechanism checks the
player before it begins physical combat and on every later engagement loop.
Health at or below the threshold returns non-retryable player_low_health.
The mechanism stops active navigation and safely clears and releases its
movement, camera, attack/use, and hotbar controls before publishing that
result. Lua owns any later retreat, healing, or stop policy; the action does
not perform one automatically.
A mechanism result has exactly:
{
outcome = "defeated", -- or "not_defeated"
target = {ref = observed_target.ref},
attacks = 3,
elapsed_ms = 2810,
stage = "attacking", -- "pursuing" or "retreating"
}
A low-health result adds exactly the sampled percentage and accepted threshold to that common mechanism data:
{
outcome = "not_defeated",
target = {ref = observed_target.ref},
attacks = 2,
elapsed_ms = 30,
stage = "pursuing",
health_percent = 20.0,
threshold = 25.0,
}
health_percent is the mechanism's finite player-health percentage sample,
clamped to 0 through 100. threshold repeats the exact accepted request value.
The mechanism outcomes are:
| Code | Retryable | Meaning |
|---|---|---|
ok | No | The exact target was observed dead or dying; outcome is defeated |
item_not_found | No | The guarded main-hand item changed or failed its selector |
target_lost | Yes | The exact target disappeared without proof of defeat |
player_dead (ActionCode.PLAYER_DEAD) | No | The local player died during combat |
player_low_health | No | The sampled player health was at or below the requested stop threshold |
timeout | Yes | timeout_seconds elapsed before a terminal target outcome |
unreachable | Yes | The target exceeded the maximum-pursuit policy or a complete search frontier was exhausted |
navigation_failed | Yes | Navigation failed twice or pursuit made no meaningful progress for 4 seconds without proof of unreachability |
aim_failed | Yes | Visibility could not be established for 3 seconds in range |
busy | Yes | Required physical combat control was unavailable |
cancelled | No | The engagement was cancelled |
Those mechanism outcomes include outcome, target, attacks, elapsed_ms,
and the final stage. Only player_low_health also includes health_percent
and threshold. player_dead and player_low_health are distinct from
target_lost and never claim the target was defeated. Preflight, internal,
safe-stop, and shared lifecycle failures can instead carry empty data.
Policy belongs in Lua
Lua owns bounded observation, candidate filtering and ranking, the decision to fire or engage, server-specific item selectors, retries after fresh evidence, quest and world state, and when to stop. Hexis owns UUID revalidation, resource arbitration, path pursuit, safe retreat, camera and line-of-sight checks, exact crosshair attacks, cadence, held-item guarding, deadlines, and cleanup.
There is no v4 hexis.combat.hunt. A hunt is a Lua policy loop that repeatedly
observes, chooses one entity, invokes one of these actions, handles its typed
result, and observes again before retrying. A retryable target_lost is not
permission to reuse stale identity or assume defeat.
Official reduced acceptance bundles
The embedded v4 catalog now contains two maintained combat acceptance bundles. They are deliberately small test workloads, not renamed versions of the old products:
| Bundle | What its policy proves | What it does not prove |
|---|---|---|
hexis:zealot-one-shot-acceptance | Enters The End, equips a Frozen or Glacial Scythe, chooses one resident Enderman using exact carried-block evidence before label evidence, fires one direct shot, and temporarily avoids that UUID after a confirmed fired outcome | Zealot Farmer parity, movement or chase, hit or kill confirmation, loot, player or staff safety, lag handling, inventory recovery, or a complete farming loop |
hexis:sven-engage-acceptance | Enters the Park, chooses exactly one self-labelled Sven boss when the bounded heuristic is unambiguous, otherwise chooses a regular wolf, and runs one bounded melee_kite engagement with configured low-health stop and an explicit retry allowlist | Sven Slayer parity, quest start or phases, definitive boss ownership, weapon selection, healing, abilities, pups, drops, inventory policy, or world recovery |
The Zealot policy prefers an Enderman carrying
minecraft:end_portal_frame, then a label containing Special Zealot, then
the first other eligible Enderman. A successful action means only that the
right-click was sent. The policy re-observes before choosing again and stops on
every non-retryable result.
The Sven policy treats labels_contain_self_name as bounded heuristic evidence,
not proof of ownership. It skips a boss carrying another player's label and
refuses to choose when more than one self-labelled boss is visible. Its current
retry allowlist contains target_lost, timeout, unreachable,
navigation_failed, aim_failed, and busy when the result is also
retryable. Its required flee_health integer setting has default 10, minimum
5, maximum 20, and step 5. The entrypoint passes that snapshot value as
stop_below_health_percent and stops on player_low_health. player_dead and
every other non-retryable result also stop it. The threshold does not select a
healing item, heal the player, or implement a retreat route.
Zealot has an empty configuration schema; Sven has only the required
flee_health field. Both use strict bundle-local workload packs plus hidden
official area catalogs. They are UI-visible preview workloads. Their parser
and headless composition tests are source evidence; neither bundle nor combat
action has passed a named distributed-build live acceptance gate.
Acceptance gates and adjacent gaps
The source foundation has strict parser tests, action/result tests, and a Lua composition test that observes a Zealot, fires once, re-observes after a lost Sven target, then proves defeat through a second engagement. That is headless composition evidence, not live combat acceptance.
Before either action becomes public or AI-runnable, a named distributed build
must verify at least direct and arrow aim, shot cadence, movement
neutralization, melee pursuit, blocked and unsafe retreat, item changes,
PLAYER_DEAD, an initially low player, a mid-engagement health-threshold
crossing, omission of the optional threshold, target loss versus defeat,
cancellation, world change, control contention, and safe-stop on a real
client.
There is no maintained v4 Mob Killer source in this checkout or the embedded v4 catalog. The two maintained combat bundles are reduced acceptance workloads, not Zealot Farmer or Sven Slayer controllers. Their strict packs and policies do not establish accepted handling for player or staff safety, lag, loot, inventory, quest phases, boss mechanics, healing, abilities, or world recovery. The two actions make those products possible to compose; the acceptance bundles do not prove those products complete.