Navigation
Moves the character to a point on the current map by itself. It plans a route over the map's platforms, ladders, ropes and portals using the moves your character has (flash jump, up jump, teleport, rope lift), plays it with human-like timing, and replans when the character is pushed off the route.
Navigation works within the current map. To travel to another map, use Rusher.
Functions
core.nav.path_to(x: number, y: number, opts?: table) -> boolean, string?
Starts travelling to the map coordinate (x, y) and returns at once; follow the trip with status(). The point can also be passed as a table: core.nav.path_to({x = 1200, y = -85}, opts).
The target is matched to the platform just below the point, or the nearest platform a short way from it, so y can be approximate. Calling path_to() during a trip replaces it.
| Option | Type | Description |
|---|---|---|
seed | number | Passing the same seed again repeats the same route choices between the same two points. Leave it out for a varied route each time |
Returns true when travel started, or false and a reason when it did not:
| Reason | Meaning |
|---|---|
stand on a platform before navigating | The character is in the air, on a ladder or rope, or not on a platform |
no platform near the target | There is no platform at or near the point |
no jump key is bound | Bind Jump to a key first |
map geometry unavailable | The map has not finished loading |
Rotation Builder owns the input | The Rotation Builder is recording or playing |
A trip ends when the character arrives, when no route can be found, and when you call stop(). Changing map or channel, logging out and Panic Mode stop it too. When the character is knocked or falls off the route, navigation plans again from where it lands, up to 4 times per trip; after that the trip fails with too many replans: ... in status().message.
core.nav.stop()
Stops the current trip and lets go of the movement keys.
core.nav.is_active() -> boolean
Returns true from a successful path_to() until the trip arrives, fails or is stopped.
core.nav.status() -> table
Returns what the current or last trip is doing.
| Field | Type | Description |
|---|---|---|
state | string | idle, planning, running, replanning, arrived or failed (see below) |
message | string | Why the trip failed, when state is failed; otherwise usually empty |
step | number | The route step being played, out of steps |
steps | number | Steps in the route |
replans | number | Times this trip has planned again, up to 4 |
target_x | number | x given to path_to() |
target_y | number | y given to path_to() |
state | Meaning |
|---|---|
idle | No trip, including right after stop() |
planning | Working out the route |
running | Moving along the route |
replanning | The character left the route; a new route is on its way |
arrived | Reached the target |
failed | Gave up; message says why |
A finished trip keeps its arrived or failed status until the next path_to().
core.nav.plan(x: number, y: number, seed?: number) -> table?, string?
Works out the route to (x, y) without moving, and returns it as a list of steps with a total_seconds field. Returns nil and a reason when there is no route; the reasons are the same as for path_to(), with stand on a platform before planning when the character is not on a platform. The call waits for the route, so on a large map it can take a moment.
| Step field | Type | Description |
|---|---|---|
kind | string | walk, jump, drop, teleport, climb, portal or rope |
dir | number | -1 left, 1 right, 0 neither |
run | boolean | true when the move is taken with a running start |
from_x, from_y | number | Where the step starts |
to_x, to_y | number | Where the step ends |
seconds | number | Expected duration of the step |
actions | table | The inputs in the step, each {at, kind}: at is seconds after the step starts, kind is one of hold_dir, release_dir, jump, flash_jump, up_jump, down_jump, teleport, teleport_up, teleport_down, climb, portal, walk, rope_lift |
core.nav.get_capabilities() -> table
Returns the movement model routes are planned with. Violet fills it in from the character's job and skills the first time it plans a route.
| Field | Type | Description |
|---|---|---|
flash_jump, up_jump, up_jump_airborne, teleport, down_jump, portals, rope_lift | boolean | Which moves the route may use |
flash_jump_max | number | Flash jumps in one jump |
teleport_skill, rope_lift_skill | number | Skill IDs used for teleport and rope lift; 0 when none |
walk_speed, climb_speed, rope_speed | number | Pixels per second |
walk_jump, gravity, flash_jump_vx, flash_jump_vy, up_jump_vy | number | Jump and fall speeds, in pixels per second (gravity in pixels per second squared) |
teleport_range_x, teleport_range_y, rope_range, ladder_attach_tolerance | number | Distances in pixels |
flash_jump_min_delay, teleport_delay, rope_delay | number | Seconds |
core.nav.set_capabilities(caps: table) -> boolean
Changes the movement model. Only the fields you pass change; the rest keep their values. From then on your values are used in place of the ones Violet fills in. Returns true.
core.nav.get_persona() -> table
Returns the style routes are chosen with. Each time Violet loads it picks a random style, so different characters take slightly different routes.
| Field | Type | Description |
|---|---|---|
alternatives | number | How many candidate routes are compared |
beta | number | How strongly the fastest candidate is preferred; higher picks the fastest almost every time |
cost_noise | number | Random variation added to each move's cost; 0 for none |
flash_jump_weight, ladder_weight, teleport_weight, portal_weight, drop_weight | number | Cost multipliers per kind of move (teleport_weight covers rope lift too): above 1 uses that move less, below 1 more |
fall_damage_penalty | number | Seconds added to a drop long enough to cause fall damage |
core.nav.set_persona(persona: table) -> boolean
Changes the route style. Only the fields you pass change. Returns true.
core.nav.refresh() -> boolean
Reads the current map's platforms again. Returns true when it succeeded. Navigation reads them by itself the first time it plans on a map, so this is rarely needed.
Example
-- Walk to a spot, and report how the trip ended
local ok, reason = core.nav.path_to(1200, -85)
if not ok then
core.log("Can't go there: " .. reason)
end
local reported = false
function on_tick(stage)
local status = core.nav.status()
if reported or core.nav.is_active() then return end
if status.state == "arrived" then
core.log("Arrived")
elseif status.state == "failed" then
core.log("Navigation failed: " .. status.message)
end
reported = true
end-- Preview a route before taking it
local route, reason = core.nav.plan(1200, -85)
if route then
core.log(string.format("%d steps, about %.1f s", #route, route.total_seconds))
for i, step in ipairs(route) do
core.log(string.format("%d. %s to (%.0f, %.0f)", i, step.kind, step.to_x, step.to_y))
end
else
core.log("No route: " .. reason)
end