Skip to content

CreateMove

on_create_move(cmd) / on_create_move_post(cmd)

Section titled “on_create_move(cmd) / on_create_move_post(cmd)”
  • Thread: game thread, inside the game’s CreateMove. This can run several times per tick with the same cmd.sequence; cmd.new_tick is true on the first call of a tick.
  • Not called on official Valve servers, or when aqua had to disable command writes. Aiming and action requests require the same gate’s permission; priority cannot bypass it. Blocked calls discard pending aim without changing the camera or command.
  • cmd is a fresh table each call. Its methods only work during the callback that received it.

Order inside one CreateMove call: bhop -> air strafer -> on_create_move -> anti-aim -> ragebot -> doubletap -> script actions/aim arbitration -> on_create_move_post -> checksum. on_create_move submits movement, aim and action requests. Anti-aim rotates movement like your own keys, and ragebot auto-stop can override it. on_create_move_post is read-only and sees the final command. A script with aim priority above ragebot suspends ragebot and doubletap for that call before they can generate attacks.

Field Meaning
sequence, new_tick Command number, and whether this is the tick’s first call
alive, on_ground, velocity {x,y,z} Local player state
view_yaw Your real camera yaw (not the anti-aim yaw)
angles {pitch,yaw,roll} Base command angles, which may already contain anti-aim’s pose. In _post, the final base angles sent
eye_position {x,y,z} Local geometric eye position for look_at; nil when unavailable
ragebot_aim_priority, antiaim_aim_priority Built-in aim priorities: 100 and 50
weapon Copied active weapon details, or nil (see players and weapons)
forward_move, left_move Movement wish, -1..1. left_move +1 = strafe left
buttons Button bitmask
aa_active Anti-aim wrote the pose this tick
strafer_active The air strafer owns movement this tick (your request is ignored)
subtick_moves, subtick_remaining, subtick_capacity Sub-tick moves in the command, and the 32-move budget left this tick (read-only)
jump_priority Built-in Bunnyhop owns jump input because the user is holding jump; script jump requests are refused
jump_pending A script jump request is queued and has not expired (snapshot before this callback)
jump_staged The shared jump injector staged a press/release pair on this CreateMove call; not proof the engine accepted the jump

_post only: aa_pose {pitch,yaw,roll} (nil when anti-aim is off), move_applied, history_size, attack, aim_applied (a script aim was written), aim_owner ("script", "ragebot", "antiaim", "none" or "unavailable"), aim_priority (selected script request’s priority, nil without a request), and aim_silent (selected script request’s mode, even if it lost arbitration). These describe the shared command, not just the script receiving the callback. actions_applied is a table with boolean attack1, attack2, reload, walk and switch_weapon fields. Applied means the request reached the command; it does not prove the game accepted the action.

Methods work only in on_create_move; _post raises “read-only”.

Method Effect
cmd:move_world(x, y) Walk toward world-space direction (x, y). Length is clamped to 1; shorter = slower wish. z is not used
cmd:move(forward, left) Same, relative to your camera (not the anti-aim yaw). Clamped to length 1
cmd:stop() Request zero movement (overrides the player’s keys)
cmd:clear_move() Drop this script’s request; the player’s keys apply again
cmd:set_button(name, down) "duck", "walk" or "use". Not play-tested
cmd:jump(down=true) Queue a jump intent; returns true if queued, false if dead or built-in jump priority applies. false cancels this script’s pending request
cmd:aim(pitch, yaw, opts) Submit aim angles. Pitch clamps to -89..89, yaw wraps, roll is zero. Options: silent=false, priority=75. Returns whether the request was accepted, not whether it won final arbitration
cmd:look_at(x, y, z, opts) Submit aim toward world coordinates from eye_position, with the same options. Returns false if dead, the eye is unavailable, or the target equals the eye
cmd:clear_aim() Cancel this script’s pending aim request
cmd:attack1(down=true, opts) Request primary attack press/hold or release
cmd:attack2(down=true, opts) Request secondary attack press/hold or release
cmd:reload(down=true, opts) Request reload input
cmd:walk(down=true, opts) Request Shift/walking input
cmd:switch_weapon(handle, opts) Request a held weapon by its full handle from aqua.get_weapon_inventory()
cmd:clear_actions() Cancel this script’s pending action requests
  • A planar movement request lasts for the current tick only: call move_world every tick you want to move. No call = no override. It is also dropped when you die, and when the script stops, errors or unloads. If two scripts ask in the same tick, the last one wins.
  • Movement methods do not change view angles. Movement is planar: pitch never changes the direction. Aqua’s anti-aim always sends roll 0; a non-zero roll from elsewhere would skew the server’s movement basis, and this API does not compensate for it.
  • While the air strafer drives (strafer_active), planar movement requests are ignored. Turn the strafer off for a walkbot that jumps.
  • Direction accuracy: servers usually run sv_quantize_movement_input 1, which rounds the wish to 8 directions relative to the sent yaw.
    • Anti-aim on: aqua compensates through the sent yaw, so move_world is exact (measured in game: within 0.012 deg). With anti-aim’s movement dither on, the heading ripples about +/-2 deg around the target instead. A direction change shows up one tick late.
    • Anti-aim off: the sent yaw is your camera, so the direction snaps to the nearest of 8 around it (up to about 30 deg off; measured 29 deg for a request 61 deg off the camera). Steer the camera toward the target if you need exact paths without anti-aim.
  • cmd:jump() requests a jump on the next CreateMove call, which may be a later call in this tick or the next tick. It works with Bunnyhop disabled. Refresh it each tick while you want to jump; it expires after more than one command advance. Last requesting script wins; cancellation affects only its owner’s request. Death, command-write blocking, stopping, errors and unload clear requests. Already staged edges cannot be cancelled.
  • Jump requests use the same timing and shared subtick budget as Bunnyhop, before the air strafer claims slots. Built-in held-space Bunnyhop has priority (jump_priority). The module reserves a press/release pair when a jump is plausible; raw budget writes are not exposed. Queued does not mean executed: grounding, movement type, water, engine timing, capacity and ordering can prevent staging. Keep requesting until airborne when attempting a single grounded jump. jump_staged includes built-in jumps as well as script jumps.
  • Raw sub-tick move emission is not exposed.
  • Each lasts one CreateMove call. Repeat a hold every callback; false explicitly releases input. No request leaves normal input in charge. Requests cancel on stop/error/unload, death or blocked writes. Methods return whether queued, not final success.
  • opts.priority is an integer 0..200, default 75. Each action is arbitrated separately between scripts; highest wins, last equal request wins. Combat actions at 100 or below yield while ragebot is enabled. Above 100 they suspend ragebot and doubletap for this call. Walking uses script priority only and is applied before anti-aim movement correction.
  • Attack requests use the current camera direction in existing shot history when no script aim takes over, preventing a button-only request from firing along anti-aim. Pair attack with aim/look_at to choose another direction. Keep the chosen policy across a firing sequence. These inputs do not add recoil/spread compensation.
  • Weapon handles are checked against the live held inventory before writing a switch. A dropped/recycled handle is rejected. Reload, scope, grenades and other secondary actions remain subject to native ammo, cooldown and weapon-state rules.

opts.priority is an integer 0..200, default 75. Larger values have more control. Aim has one winner across scripts; actions choose a winner separately for attack1, attack2, reload, weapon switching and walking. Priorities apply to requests from the current CreateMove call, including repeated calls with the same sequence.

Script priority Against an applied anti-aim pose (50) Against ragebot-authored aim (100) Combat actions while ragebot is enabled
0..50 Anti-aim wins Ragebot wins Script request yields
51..100 Script wins Ragebot wins Script request yields
101..200 Script wins Script suspends ragebot and doubletap Script takes combat control

At equal priority, built-ins win against scripts. Between scripts, the last equal-priority request wins; script execution order breaks the tie, so distinct priorities are clearer when one script must dominate. Walking competes only between scripts and never suspends ragebot/doubletap by itself.

Aim yields to ragebot only when ragebot actually authors aim, including held-shot history. Combat actions at 100 or below yield whenever ragebot is enabled, even without a target. If neither built-in authors aim, a lower-priority script can aim. Priority above 100 suspends ragebot/doubletap for the call, even if no target exists. No priority bypasses the Valve-server or command-write gate.

An accepted request may later lose arbitration. Check aim_owner/aim_applied and actions_applied in on_create_move_post; an applied request still does not prove the native weapon accepted the action. Cancelling a winner does not resurrect requests that already lost or were rejected. Choose one ownership policy through the firing sequence rather than switching priorities around a shot.

  • Aim requests last for this CreateMove call, including when the same command sequence runs again. Call aim or look_at every callback while you want control. No request means normal aiming. Stop, error, unload, death and blocked command writes discard requests.
  • A lower request is rejected while a higher one is pending. Clear your own request before lowering its priority within the same callback. Cancelling the winner does not restore previously rejected requests.
  • Taking control above 100 suppresses ragebot’s firing, auto-stop, scope/cock actions and doubletap for the call. Player attack input is retained; use action requests to control script input. Ragebot aim ownership includes the command following a shot.
  • silent=true writes the base command and existing history while leaving the camera alone. silent=false also turns the camera through the native input API. This API’s silent mode changes the network/body pose; it is not a history-only mode. If visible camera access is unavailable, or the camera-copy guard needed for silent mode is unavailable, aiming is refused and _post.aim_owner is "unavailable".
  • Both modes rotate base movement and directional buttons to keep the existing world-space wish. Server movement quantization still applies; existing airborne subtick steering is not retargeted. look_at is geometric aiming, without recoil/spread compensation, visibility checks, lag compensation or firing-time eye prediction.
  • Angles/coordinates must be finite floats. Invalid options raise an error. These methods work only during the owning script’s on_create_move, never in _post or on_draw. Pitch clamps to -89..89, yaw wraps to -180..180 and roll is zero. For example, cmd:aim(9999, 9999) writes pitch 89, yaw -81, roll 0. NaN/infinity raise errors. Visible/silent camera modes, bounds and priorities were tested on a local server.
function on_create_move(cmd)
if cmd.alive then
-- Override anti-aim, let ragebot own its shots, turn the camera visibly.
cmd:look_at(1200, -500, 64, {priority = 75, silent = false})
-- For full control with the camera unchanged, use priority = 101, silent = true.
-- Use the same 101 priority on cmd:attack1(want_fire, {priority = 101}).
end
end
function on_create_move_post(cmd)
if cmd.new_tick then
print("aim owner:", cmd.aim_owner, "script applied:", cmd.aim_applied)
end
end
-- Example only: replace this with your walkbot's decision to cross an obstacle.
local want_jump = false
function on_create_move(cmd)
if want_jump and cmd.alive and cmd.on_ground and not cmd.jump_priority then
cmd:jump()
else
cmd:jump(false)
end
end
-- Walk toward a world point, stopping inside 32 units.
local target = { x = 1200, y = -500 }
function on_create_move(cmd)
local me = aqua.get_local_player()
if not (cmd.alive and me and me.origin) then return end
local dx, dy = target.x - me.origin.x, target.y - me.origin.y
if dx * dx + dy * dy < 32 * 32 then
cmd:stop()
else
cmd:move_world(dx, dy) -- clamped to length 1
end
end
function on_create_move_post(cmd)
-- cmd.angles.yaw is the yaw actually sent; cmd.view_yaw is your camera.
if cmd.new_tick and cmd.move_applied then
print(("sent yaw %.1f, camera %.1f"):format(cmd.angles.yaw, cmd.view_yaw))
end
end