CS2 Lua API

cmd

The cmd object is the movement command of the current tick. It is passed to the callbacks of the pre_movement, post_movement and createmove events. Writing fields changes the command the game will send to the server right away.

cmd points to the live command. If you keep the object and write to it later, the write goes into the command of the tick in which it happens. Use cmd:new() for an independent copy.

Fields#

Field Read Write
legacy_command_number number forbidden: error field is read-only
client_tick number number
viewangles angle — a reference to the command angles: cmd.viewangles.y = ... writes straight into the command angle
moveangles angle — a reference to the movement angles angle
forwardmove, leftmove, upmove number number
impulse, weaponselect, random_seed, mousedx, mousedy number number (integer)
camera boolean — camera turn flag, see below boolean
IN_* button: number 0–7 or nil number 0–7
  • An unknown field reads as nil; writing to it is the error invalid field.
  • A value of the wrong type — error number expected, angle expected or boolean expected.
events.createmove:set(function(cmd)
    print(cmd.legacy_command_number, " ", cmd.client_tick, " ", cmd.viewangles)
end)

IN_* buttons#

Each button is a field named after the game button enumeration: IN_ATTACK, IN_ATTACK2, IN_JUMP, IN_DUCK, IN_FORWARD, IN_BACK, IN_USE, IN_RELOAD and others. The name is case-sensitive.

The value is a number of three bits:

Bit Meaning
1 the button is held
2 the state changed in this tick
4 scroll

If there is no button with this name — nil. On write the bits set all three button states.

-- Do not shoot while the player is in the air
events.createmove:set(function(cmd)
    local me = entity.local_pawn()
    if not me then return end
    local on_ground = me.m_hGroundEntity ~= nil
    if not on_ground and bit.band(cmd.IN_ATTACK or 0, 1) ~= 0 then
        cmd.IN_ATTACK = 0
    end
end)

cmd.camera#

The "turn the camera to the command angles" flag. If you write cmd.camera = true, the cheat turns the player's real camera to cmd.viewangles — to the angles that were in the command at the moment of the write.

Read Write
boolean boolean; true — turn the camera, false — clear the flag
  • Angles first, then the flag. The flag remembers cmd.viewangles at the moment of the write; angles changed afterwards are not used.
  • When it is applied. Written in pre_movement — after the pre_movement dispatch and once more after post_movement; written in post_movement — after post_movement; written in createmove — right after the createmove dispatch.
  • The flag lasts one tick. It is reset before every pre_movement, so write it every tick to keep the camera.
  • Safe Mode. If Safe Mode does not allow turning the camera on the current server, the flag is not raised: writing true does nothing, reading returns false.

After the turn the camera keeps looking along the new angles — the mouse moves it from there.

-- Throw a grenade straight up on the Z key (VK 0x5A): a grenade must be in hand
local PULL_TICKS, HOLD_TICKS = 8, 24
local state, ticks, key_was = "idle", 0, false

local NADES = { C_HEGrenade = true, C_Flashbang = true, C_SmokeGrenade = true, C_MolotovGrenade = true, C_IncendiaryGrenade = true, C_DecoyGrenade = true }

events.createmove:set(function(cmd)
    local key_now = utils.key_state(0x5A)
    local pressed = key_now and not key_was
    key_was = key_now

    if state == "idle" then
        local me = entity.local_pawn()
        local w = me and me:is_alive() and me:weapon()
        if not pressed or not w or not NADES[w:classname()] then return end
        state, ticks = "pull", 0
    end

    cmd.viewangles.x = -89      -- angles first
    cmd.camera = true           -- then the flag: the camera turns to them

    if state == "pull" then     -- hold attack: pull the pin
        cmd.IN_ATTACK = (ticks == 0) and 3 or 1
        ticks = ticks + 1
        if ticks >= PULL_TICKS then state, ticks = "release", 0 end
    else                        -- release and keep the angles until the grenade leaves
        cmd.IN_ATTACK = (ticks == 0) and 2 or 0
        ticks = ticks + 1
        if ticks >= HOLD_TICKS then state = "idle" end
    end
end)

cmd:buttons()#

The three button masks of the command.

Returns: 64-bit integers value, changed, scroll — held, changed, scroll.

events.pre_movement:set(function(cmd)
    local value, changed = cmd:buttons()
    if value ~= 0 then
        -- at least one button is held
    end
end)

cmd:new()#

A copy of the command. Writing to the copy does not affect the game.

Returns: cmd.

events.pre_movement:set(function(cmd)
    local copy = cmd:new()
    copy.forwardmove = 0          -- the game will not see this
    print(cmd.forwardmove)        -- the original value
end)

cmd:finalize()#

Finalizes the command subticks in place.

Returns: nothing.

events.createmove:set(function(cmd)
    cmd:finalize()
end)

cmd:finalized()#

A copy of the command with finalized subticks. The original command does not change.

Returns: cmd.

events.pre_movement:set(function(cmd)
    local fin = cmd:finalized()
    print(fin.forwardmove)
end)