Skip to content

Commands

Needs the internal running and needs_internal = true in script_info. Without it cmd.clear, cmd.hold, cmd.press, cmd.cast and cmd.use_item return false.

cmd.clear(buttons [, ms])  -> bool     take these buttons out of every command
cmd.hold(buttons [, ms])   -> bool     hold these buttons
cmd.release(buttons)       -> integer  end your clear or hold early
cmd.press(buttons)         -> bool     one press
cmd.cast(slot)             -> bool     cast ability 1-4
cmd.use_item(slot)         -> bool     use item 1-4
cmd.mask()                 -> table | nil
cmd.reset()                            drop everything this script asked for
cmd.on_change(fn)                      fn(c) once per new command
cmd.diag()                 -> table | nil

buttons is a button name like "attack", or a list of them like { "attack", "attack2" }. The names are the same ones input.* uses: attack, attack2, jump, duck, reload, dash, ability1-ability4, item1-item4, melee, parry, and the rest on the Input page. An unknown name raises an error.

What it is for

input.* presses keys for you, but it can never take one away. If you are holding the left mouse button, nothing in input.* can stop that button from reaching the game. cmd.* can. It changes the command the game sends, after your real keys are in it. So a script can say "no attack right now" while you keep holding the button.

lua
-- cleanse the moment a debuff lands, even while you are holding fire
local function should_cleanse(m)
    return false   -- your rule: check m.name against the debuffs you care about
end

function on_modifier_added(p, m)
    if p:is_local() and should_cleanse(m) then
        cmd.clear("attack", 150)   -- no shooting for 150 ms
        cmd.use_item(2)            -- your cleanse item in slot 2
    end
end

The modifier names to check come from your own logs. Debugging modifiers shows how to list them.

Every change is timed

A clear or a hold lasts ms milliseconds. Leave ms out and it lasts 100 ms. The longest is 5 seconds.

To keep one going, call it again. Calling it again makes it last longer, never shorter. For "while something is true", call it every tick while it is true:

lua
function on_tick()
    if i_am_channelling() then cmd.clear("attack") end   -- asked again every tick, so it never runs out
end

A clear can never get stuck. If your script stops asking, you get the button back when the time runs out, 100 ms by default. If it is turned off or errors, you get it back straight away. A short hitch in TSUKI does not let the button through early.

If TSUKI closes or crashes, your clears and holds stop within a quarter of a second. The game never keeps a button taken away after TSUKI is gone.

cmd.release(buttons) ends your clear and hold of those buttons straight away. cmd.reset() ends everything your script asked for.

Pressing and casting

cmd.press(buttons) presses once. cmd.cast(slot) casts ability 1 to 4 and cmd.use_item(slot) uses item 1 to 4.

These go through the game command, not your keyboard. They work with any keybinds and while a text box has focus, and they do not wait for Windows to deliver a keypress.

Timing

A change reaches the game a few milliseconds after you ask. It takes a little longer if the overlay's Refresh Rate is set low. It is not tied to one exact command. Use cmd.* for "stop shooting while I cast", not for "change exactly command 1234". It works the same at any frame rate.

If two scripts disagree

A clear beats a hold and a press. If one script clears attack and another holds it, attack is cleared.

cmd.mask() shows what is in force: { clear, hold, intents, mine = { clear, hold } }. clear and hold are button bit masks for every script together. mine has the same masks for your script only. Use input.bit(name) to test a bit:

lua
local m = cmd.mask()
if m and (m.clear & input.bit("attack")) ~= 0 then
    -- something is holding attack off right now
end

cmd.on_change(fn)

Calls fn(c) once for each new game command, about 64 times a second. c has:

FieldWhat it is
cmdThe new command number
prevThe one before
skippedCommands that went by between two calls. They are reported, not replayed
held, pressed, releasedButton bit masks, the same as input.buttons()

cmd.on_change(nil) stops it.

Returns false when

  1. The internal is not running.
  2. needs_internal = true is missing from script_info.
  3. 64 clears and holds are already live across every script.

A press is also dropped if it cannot reach the game within half a second, for example during a loading screen. It is never delivered late.

When a script is turned off

Everything a script asked for is removed when it is turned off or errors: its clears, holds, on_change, and anything else it owns. See on_disable.

Checking it worked

cmd.diag() shows what was applied, not what was asked for. The main fields:

FieldWhat it is
cmds_seenCommands that went out while a clear, hold or press was live
cmds_clearedCommands where a cleared button was really there and was taken out
presses_appliedPresses written into a command
presses_droppedPresses that were too late to send
last_press_cmdThe command the last press went out on
press_wordsThe last press as it went out: { held, pressed, released }, as button bit masks

Clearing a button nobody is pressing leaves cmds_cleared at 0.

To check that a press went out as a full tap:

lua
local d, b = cmd.diag(), input.bit("ability1")
if d and (d.press_words[2] & b) ~= 0 and (d.press_words[3] & b) ~= 0 then
    -- pressed and released on the same command
end

If you were already holding the button, the press keeps it held, so pressed is set and released is not.

It costs nothing while no script is using it.