Violet

Core Functions

On this page

This module provides essential functions that nearly all scripts require: callbacks for executing code, utilities for logging, time management, game information access, and settings manipulation.

Callbacks 🔄

Callbacks are the primary mechanism for executing your script logic. Define these functions in your script and the core will automatically invoke them at the appropriate times, or register any function for an event.

on_tick(stage: number)

Available on GMS and CW

Executes once per game update (approximately every 30ms) in every game stage. The numeric stage value identifies where the client currently is; compare it with a core.stage constant rather than a string or hard-coded number.

Scripts now continue receiving ticks in the login flow, Cash Shop, and Auction House. Code that requires a loaded player or map should begin with a core.stage.FIELD guard.

lua
function on_tick(stage)
    if stage == core.stage.CASH_SHOP then
        -- Cash Shop logic
    elseif stage == core.stage.AUCTION_HOUSE then
        -- Auction House logic
    elseif stage == core.stage.FIELD then
        -- Field logic
    end
end

on_render()

Available on GMS and CW

Runs once per presented frame, after that frame's on_tick if one ran, while the in-game overlay is able to draw. This is the only place core.render drawing calls are accepted; anything drawn is shown for one frame, so draw on every call.

The callback is skipped while core.render.is_ready() is false, so it never runs when nothing could be drawn. It takes no arguments and runs in every stage, so guard field-only drawing with a core.stage.FIELD check if needed. It runs at frame rate, not tick rate: keep game logic in on_tick, store what you need, and only draw here. A callback that raises an error is reported to the console once and removed until the script is reloaded, so a bug cannot spam every frame.

lua
function on_render()
    core.render.text(20, 20, "Hello", { shadow = true })
end

on_map_load()

Available on GMS and CW

Triggers when a new map loads. Called before on_tick for the new map.

on_packet_recv(packet: in_packet) -> boolean

Available on GMS and CW

Intercepts incoming packets from the server. Return true to allow or false to block. Requires plugin structure. See Packet Handlers.

on_packet_send(packet: in_packet) -> boolean

Available on GMS and CW

Intercepts outgoing packets to the server. The packet is provided as an in_packet for read-only decoding (same as on_packet_recv). Return true to allow or false to block. Requires plugin structure. See Packet Handlers.

on_evasion(player_names: table)

Available on GMS and CW

Called once when evasion is first triggered by a player appearing on the map who is not whitelisted and not in your party. player_names is an array of strings containing the names of those players. This fires before the evasion action (channel change, logout, etc.) begins.

lua
function on_evasion(player_names)
    for _, name in ipairs(player_names) do
        core.log_warning("Evading from: " .. name)
    end
end

on_gm_evasion()

Available on GMS and CW

Called once when Evade Potential GM (Autos → Evasion) fires. By the time it runs, the logout has already been requested and Auto Login is on its 6-hour hold, so anything you do here shapes the re-login: switch a profile, send a webhook, or release the hold with core.login.clear_hold(). Only fires while the Evade Potential GM checkbox is on. See the Login page for a full character-rotation example.

lua
function on_gm_evasion()
    core.log_warning("GM evade - switching to the next character")
    core.profiles.switch("Farm B")   -- its Auto Login points at a different slot
    core.login.clear_hold()          -- otherwise Auto Login waits 6 hours
end

on_force_evasion()

Available on GMS and CW

Called once when Force Evasion (Autos → Evasion) starts an evasion because someone you can't see is on the map. It fires before the evasion action begins, like on_evasion, which is not called for it. Only fires while the Force Evasion checkbox is on.

lua
function on_force_evasion()
    core.log_warning("Someone you can't see is on the map - evading")
end

on_skill_use(skill_id: number, time: number, handle: number | nil)

Available on GMS and CW

Called when the game accepts a skill you use with a key, including keys Violet presses for you, or with core.input.use_skill(), and when Auto Buff or a pet's buff skill casts a buff. skill_id is the ID of the skill that was pressed, passed to use_skill() or cast, and time is the game update time in milliseconds when the game accepted it, on the same clock as core.get_update_time(). handle is the value core.input.use_skill() returned for this use, and nil for every other use. Handles are unique, so another script's handle never matches one of yours. It fires when the game accepts the skill and does not wait for the server.

A skill the game refuses does not call it (on cooldown, not enough HP or MP, not usable right now). Pressing a toggle skill that is already on to turn it off is normally not reported either; for toggles that show as buffs, on_buff_lost reports them going off. Calls arrive at the start of the next tick, before on_tick, one per use and in order. Up to 64 uses are kept per tick; any more are dropped. The return value is ignored.

lua
local mine = {}   -- handles of this script's casts

local function cast(skill_id)
    local handle = core.input.use_skill(skill_id)
    if handle then
        mine[handle] = true
    end
end

function on_skill_use(skill_id, time, handle)
    if handle and mine[handle] then
        mine[handle] = nil              -- one of our casts
    else
        core.log("used " .. skill_id)   -- a key press, an automatic buff or another script's cast
    end
end

on_death()

Available on GMS and CW

Called once when your character dies.

on_level_up(level: number)

Available on GMS and CW

Called for each level your character gains, with the new level. Gaining two levels at once calls it twice, in order. A jump of more than 20 levels at once is not reported.

on_rune(event: string, x: number, y: number)

Available on GMS

Called as a rune changes on your map. x and y are only passed with "appeared".

eventWhen
"appeared"A rune is on your map, including when you enter a map that already has one. x and y are its position.
"solving"A rune solve started, by you or by Violet.
"solved"You activated a rune and its cooldown started.
"stopped"The rune solve ended, whether or not it was solved.

on_lie_detector(event: string)

Available on GMS and CW

Called with "started" when Violet starts solving a lie detector and "ended" when it stops.

on_buff_gained(buff_id: number)

Available on GMS and CW

Called when a buff appears on your character. buff_id is the ID player:get_buff() uses. Refreshing a buff that is still active does not call it.

on_buff_lost(buff_id: number)

Available on GMS and CW

Called when a buff disappears from your character, whether it expired or was cancelled.

on_item_gained(item_id: number, count: number)

Available on GMS and CW

Called when you have more of an item than before: pickups, rewards, purchases and storage withdrawals. count is how many more you have. Moving, equipping or using items does not call it. Cosmetic equipment is not reported.

on_damage_taken(amount: number, time: number)

Available on GMS and CW

Called when your character takes damage. time is the game update time in milliseconds when it landed, on the same clock as core.get_update_time(). Calls arrive at the start of the next tick, before on_tick.

Info

on_death, on_level_up, on_rune, on_lie_detector, on_buff_gained, on_buff_lost and on_item_gained are checked once per tick while you are in a map, and a change is reported at the start of the next tick, before on_tick. Loading a script, logging in or switching characters does not report what you already have, only what changes after that. For about two seconds after a channel change, a Cash Shop visit or logging in, buff changes and the rune cooldown are not reported. A rune solve or lie detector still running when you log out or switch characters gets its "stopped" or "ended" call.

on_mob_action(mob: Mob, action: table)

Available on GMS and CW

Called when a mob on your map begins an attack, a skill or another one-off action, such as a boss pattern or a mob flinching from a hit. mob is the Mob and action has the fields mob:get_action() returns, with started always set. Movement (stand, move, fly and so on) does not call it.

on_mob_action_end(mob: Mob | nil, action: table)

Available on GMS and CW

Called when that action finishes or another one replaces it. action is the same table with ended added, the game update time in milliseconds when it ended. mob is nil when the mob has left the map or started dying.

lua
core.register_callback("on_mob_action", function(mob, action)
    if mob:is_boss() and action.name == "skill2" then
        core.log(mob:get_name() .. " started skill2, facing " .. (action.facing_left and "left" or "right"))
    end
end)

core.register_callback("on_mob_action_end", function(mob, action)
    if action.started then
        core.log(action.name .. " lasted " .. (action.ended - action.started) .. " ms")
    end
end)
Info

Mob actions are checked once per tick while you are in a map, and each start and end is reported at the start of the next tick, before on_tick. started and ended are on the same clock as core.get_update_time(). A mob that repeats the same action with no gap in between reports it once. Actions already playing when a script loads or you enter a map are not reported as started. Their end is still reported, with started set to nil when the start was not seen. Leaving the map ends every action still playing.

on_field_effect(path: string)

Available on GMS and CW

Called when the game shows a full-screen effect on your map, such as a stage or wave banner or a clear, fail or time-over message. path names the effect, for example one ending in monsterPark/clear or quest/party/clear, so matching on it tells you how an encounter went. Calls arrive at the start of the next tick, before on_tick.

lua
function on_field_effect(path)
    if path:find("clear") then
        core.log("stage cleared: " .. path)
    end
end

on_key_prompt(event: string, prompt: table)

Available on GMS

Called as a timed key prompt for your character changes, such as the ones in Tengu's fight in Mushroom Shrine. prompt has the fields map:get_key_prompt() returns.

eventWhen
"armed"The prompt that starts an attack can now appear (prompt.step is 0). Stand within 400 pixels of prompt.mob_id, left or right.
"opened"A prompt appeared and is waiting for prompt.key. prompt.time_left is the time left to press it.
"passed"You pressed the expected key in time.
"failed"You pressed another of the five keys, or time ran out.

With "passed" and "failed", prompt.open is false and prompt.pressed names the key you pressed, or is nil when you pressed none. Calls arrive at the start of the next tick, before on_tick. A prompt already on screen when a script loads or you enter a map is not reported as opened; its answer still is.

lua
core.register_callback("on_key_prompt", function(event, prompt)
    if event == "opened" then
        core.input.press_key(prompt.key_code)
    elseif event == "failed" then
        core.log_warning("missed " .. prompt.key .. ": " .. (prompt.pressed or "out of time"))
    end
end)

on_ui_event(event: table)

Available on GMS and CW

Called when a control in a game window reports something to its window: a button click, a check box toggle, a text change, a new selection, a tab switch or a slider move. Also called with type "list_select" when a different item is selected in a window's List. Input from core.game_ui actions is reported too. Moving the mouse over a control is not. Each event is reported once, by the control itself, so window is the window that directly contains the control, which can be a panel nested inside a larger window.

event fields:

FieldTypeDescription
typestring"click", "double_click", "right_click", "press" (a button pressed down, before its click), "check", "text", "tab", "select", "slide", "slide_end", "list_select" or "other"
codenumberThe event's number, which tells "other" events apart; not set for "list_select"
valuenumberDetail that depends on type: 1 or 0 for the new state of a "check", the new tab index for "tab", the new position for "slide" and "slide_end", the chosen item's value for "select" (often, but not always, its index; control:get_selected() returns the index), and the selected item's 0-based index for "list_select"
control_idnumberThe control's id within its window; not set for "list_select"
control_handlenumberThe control's handle; not set for "list_select"
window_handlenumberThe handle of the control's or list's window
windowWindow | nilThe Window, or nil if it has closed
controlControl | nilThe Control, or nil if it no longer exists; not set for "list_select"
listList | nilFor "list_select", the List, or nil if it no longer exists

on_window_open(window: Window)

Available on GMS and CW

Called when a game window opens, with the Window.

on_window_close(window: table)

Available on GMS and CW

Called when a window closes. The window is gone by then, so window is a table of what it was: handle, kind, and ui_type and resource_path when the window had them (see Game UI).

lua
core.register_callback("on_window_open", function(window)
    core.log("opened " .. window:get_kind() .. " " .. tostring(window:get_ui_type()))
end)

core.register_callback("on_window_close", function(window)
    core.log("closed " .. window.kind .. " " .. tostring(window.ui_type))
end)

core.register_callback("on_ui_event", function(event)
    if event.type == "click" then
        core.log("clicked button " .. event.control_id)
    end
end)
Info

Game UI events are collected as they happen and reported at the start of the next tick, before on_tick, so the window or control may already be gone. Up to 128 events are kept between ticks; any more are dropped. List selections are compared once per tick, before on_tick, so a list reports only its latest selection, and lists that are already open when a script starts listening report only later changes. Open windows are compared once per tick, before on_tick. Windows that are already open when a script starts listening get no on_window_open, but their on_window_close is still reported, and a window that opens and closes within one tick is not reported.

on_wndproc(event: table) -> nil | boolean | number

Available on GMS and CW

Called for keyboard and mouse WndProc messages received by the game window. Non-input WndProc messages are not passed to Lua.

WM_MOUSEMOVE not delivered

Mouse-move messages (WM_MOUSEMOVE) fire hundreds of times per second while the cursor is over the window and are not dispatched to Lua. If you need cursor position, poll core.input.get_mouse_position() instead. All other mouse messages (clicks, wheel, X buttons) and all keyboard messages are delivered normally.

Return false to block the input message with result 0, return a number to block with that LRESULT, or return nil / true to allow the original WndProc.

If multiple scripts define on_wndproc, the first script to return false or a number blocks the message and later scripts do not receive that event.

event fields:

FieldTypeDescription
hwndnumberWindow handle as an integer
msgnumberWindows keyboard/mouse message ID
w_paramnumberRaw WPARAM value
l_paramnumberRaw LPARAM value
blockablebooleanAlways true for delivered events
xnumberMouse X coordinate from l_param for mouse messages
ynumberMouse Y coordinate from l_param for mouse messages
lua
function on_wndproc(event)
    if event.x and event.y then
        core.log("mouse: " .. event.x .. ", " .. event.y)
    end

    -- Example: block a specific input message if needed
    -- if event.msg == 0x0201 then return false end
end

on_dialog(dialog: table) -> nil | boolean | number | string

Available on GMS and CW

Intercepts NPC dialogs and blue boxes before core auto-handling (auto NPC / auto blue boxes). Called once per dialog — the result is cached until the dialog closes. Other dialog boxes, such as a game window's own confirmation pop-up, don't call on_dialog: find them with core.game_ui (window kind "dialog") and click their buttons.

dialog fields:

FieldTypeDescription
kindstring"npc" for NPC dialogs, "bluebox" for blue box notifications
typenumberDialog sub-type (see table below)
textstringThe dialog message text (NPC message or blue box text)
choicestableArray of {text, index} — present when the dialog has list options. index is the selection value for each choice

type values:

KindTypeMeaning
npc0Close / prev
npc1Next / ok
npc5Text input
npc6List select (has choices)
bluebox1000Yes / no prompt
blueboxotherNotice

Return values:

ReturnEffect
nil or truePass to core — auto NPC / auto blue boxes handles it if enabled
falseBlock — do nothing, leave dialog open
6Click yes / ok / next
7Click no / close
numberSelect the choice matching this index value (requires choices)
string (type 5)Submit as text input
string (with choices)Select the first choice whose text contains this substring
Tip

For list selection, prefer returning choice.index (number) for an exact match, or a substring (string) for flexible text matching. Since 6 and 7 are reserved for yes/no, use the string method if a choice happens to have index 6 or 7.

lua
function on_dialog(dialog)
    -- Log all dialog text
    core.log("[" .. dialog.kind .. "] " .. (dialog.text or ""))

    if dialog.kind == "npc" then
        -- Type 6 (list): select by index or text
        if dialog.type == 6 and dialog.choices then
            return dialog.choices[1].index  -- select by index
            -- return "Complete"            -- or select by text substring
        end

        -- Type 5 (text input): return the string to enter
        if dialog.type == 5 then
            return "answer"  -- submits "answer" as text input
        end

        if dialog.type == 1 then
            return 6  -- click next
        end
    end

    -- return nil = let core handle it
end
Warning

The callback fires once per dialog. The result is cached — returning false will block the dialog for its entire lifetime, not just one tick. Returning nil lets core auto-handling run every tick as normal.

on_step_start(step: table)

Available on GMS and CW

Called when the web menu's Scheduler starts a step this script registered with core.scheduler.register_step. step holds the step's id, its type, the params chosen in its form and resumed, which is true when it starts again after a pause. Report back with step:done() or step:fail(). Only the script that registered the step receives it. See Scheduler.

on_step_stop(step: table, reason: string)

Available on GMS and CW

Called when the Scheduler ends this script's step before the script did: reason is "time", "skipped", "stopped", "paused", "safety" or "replaced". Stop the step's work here. It is not called after step:done() or step:fail().

lua
function on_step_stop(step, reason)
    if reason == "safety" then
        core.log_warning("Stopped for safety")
    end
end

on_unload(reason: string)

Available on GMS and CW

Called just before this script stops, so it can switch off what it switched on. Only the stopping script receives it. reason is:

  • "disabled": the script was unticked, or the profile just activated leaves it off.
  • "reload": the script starts again straight away, because a new version arrived, a module it requires changed, or the profile just activated keeps it on.
  • "panic": Panic Mode stopped every script.
  • "deleted": the script was removed from your scripts.

The script's own settings, storage and every core function still work inside on_unload. When it returns, the script's callbacks and menu elements are removed.

Warning

on_unload gets 100 ms. A script still running after that is stopped with an error in the console, and it unloads anyway. Nothing that would arrive later reaches the script, including HTTP replies, on_tick and other callbacks. on_unload is not called when the game closes.

lua
local switched_on = {}

local function switch_on(path)
    if not core.get_setting(path) then
        core.set_setting(path, true)
        table.insert(switched_on, path)
    end
end

switch_on("combat.kami.enabled")
switch_on("combat.auto_attack.enabled")

function on_unload(reason)
    for _, path in ipairs(switched_on) do
        core.set_setting(path, false)
    end
end

Registering callbacks

Instead of defining a global function with a callback's name, you can register any function for an event. Both styles work together: callbacks for the same event run in the order they were registered, and a script's global of that name counts as registered when the script finishes loading.

core.register_callback(event: string, fn: function) -> number

Available on GMS and CW

Registers fn for event, any callback name such as "on_tick", "on_chat" or "on_level_up", and returns its callback ID. fn receives the same arguments as the global function of that name, and its return value is used the same way (for example, returning false from on_packet_send blocks the packet). Raises an error for an unknown event name or when fn is not a function. A callback registered from inside another callback starts with the next event. Callbacks are removed when their script unloads.


core.unregister_callback(event: string, fn_or_id: function | number) -> boolean

Available on GMS and CW

Removes the first callback registered with core.register_callback that matches the function or ID, whichever script registered it. Returns true if one was removed. Raises an error when fn_or_id is neither a function nor a number. Removing a callback from inside another callback takes effect after the current event.

lua
local id = core.register_callback("on_level_up", function(level)
    core.log("Reached level " .. level)
end)

core.register_callback("on_tick", function(stage)
    if stage ~= core.stage.FIELD then return end
    -- field logic
end)

-- later
core.unregister_callback("on_level_up", id)
Warning

A function that is both registered and defined as the global of that name runs twice.

Info

Callback names must match exactly as shown above. The core finds these functions by name; core.register_callback takes the same names.

lua
function on_tick(stage)
    if stage ~= core.stage.FIELD then return end
    -- Your field logic here
end

function on_map_load()
    -- Map initialization code here
end

function on_render()
    -- Overlay drawing here (core.render.*)
end

Stage Enum

core.stage is a read-only table of numeric constants used by on_tick(stage). Compare against these names instead of strings or raw numbers.

core.stage.LOGO -> number

The startup logo stage.

core.stage.LOGIN -> number

The login, world-selection, or character-selection stage.

core.stage.FIELD -> number

An active field or map.

core.stage.CASH_SHOP -> number

The Cash Shop stage.

core.stage.AUCTION_HOUSE -> number

The Auction House or Global Market stage.

core.stage.INTER_STAGE -> number

An intermediate transition stage.

core.stage.LOADING -> number

The loading stage.

core.stage.GATEWAY -> number

The gateway stage.

core.stage.UNKNOWN -> number

No active recognized stage, including brief transitions.

Game Information

core.get_world_id() -> number | nil

Available on GMS and CW

Returns the current world ID (e.g., Kronos).


core.get_channel_id() -> number | nil

Available on GMS and CW

Returns the current channel ID.


core.get_game_version() -> string

Available on GMS and CW

Returns the game client version string (e.g. "8.269.1.1"). This is the version the client is currently running, and changes with each game patch.

lua
core.log("Game version: " .. core.get_game_version())

core.change_channel(channel_id: number) -> boolean

Available on GMS and CW

Switches to the specified channel. Must be out of combat.

Returns: true if the channel change was issued, false if the call was rejected.

Throttled

Excessive channel changes in a short period are blocked and will return false without issuing a change. Always check the return value instead of assuming the swap happened.


core.is_evading() -> boolean

Available on GMS and CW

Returns true if currently evading.


core.is_solving_rune() -> boolean

Available on GMS

Returns true if currently solving a rune.


core.is_solving_lie_detector() -> boolean

Available on GMS and CW

Returns true while the figure-tracking lie detector (anti-macro challenge) is active and being solved. All automated systems (kami, rushing, skill injection, rune solving, auto attack, evasion, antimacro tracking) are automatically suspended while this is true.

lua
function on_tick(stage)
    if stage ~= core.stage.FIELD then return end

    if core.is_solving_lie_detector() then
        return -- wait for lie detector to finish
    end
    -- ... your logic
end

core.is_in_cutscene() -> boolean | nil

Available on GMS and CW

Returns true if currently in a cutscene.


core.get_update_time() -> number

Available on GMS and CW

Returns the current game update time in milliseconds. Useful for timing operations.


core.get_system_time() -> table

Available on GMS and CW

Returns the current system (wall clock) time as a table.

FieldTypeDescription
yearnumberFull year (e.g. 2026)
monthnumberMonth (1-12)
daynumberDay of month (1-31)
hournumberHour (0-23)
minnumberMinute (0-59)
secnumberSecond (0-59)
msnumberMillisecond (0-999)
epochnumberMilliseconds since Unix epoch
lua
local t = core.get_system_time()
core.log(string.format("%04d-%02d-%02d %02d:%02d:%02d",
    t.year, t.month, t.day, t.hour, t.min, t.sec))

core.get_utc_time() -> table

Available on GMS and CW

Returns the current UTC time as a table. Same fields as core.get_system_time(), but in UTC instead of the computer's local timezone — useful for timing against server events regardless of where the client is running.

FieldTypeDescription
yearnumberFull year (e.g. 2026)
monthnumberMonth (1-12)
daynumberDay of month (1-31)
hournumberHour (0-23)
minnumberMinute (0-59)
secnumberSecond (0-59)
msnumberMillisecond (0-999)
epochnumberMilliseconds since Unix epoch
lua
local t = core.get_utc_time()
core.log(string.format("UTC %02d:%02d:%02d", t.hour, t.min, t.sec))

core.is_rushing() -> boolean

Available on GMS and CW

Returns true if currently rushing to a map (via the rusher module).


core.is_auto_selling() -> boolean

Available on GMS and CW

Returns true while the auto-sell flow is active — covers every step (rushing to the shop map, walking, talking to the NPC, waiting for the shop dialog, and selling). Returns false when idle.

Useful for gating script logic that shouldn't run while the player is being driven to a shop and back.

lua
function on_tick(stage)
    if stage ~= core.stage.FIELD then return end

    if core.is_auto_selling() then
        return -- let auto-sell finish before doing anything else
    end
    -- ... your logic
end

core.use_hyper_rock(map_id: number) -> boolean

Available on GMS

Teleports to the specified map using the Hyper Teleport Rock. Returns true on success, false otherwise.


core.maple_guide_teleport(map_id: number) -> boolean

Available on GMS and CW

Teleports to the specified map using the Maple Guide (no Hyper Teleport Rock needed). Only maps the guide currently offers your character are accepted. The move goes through the Maple Guide's own checks, including its cooldown. Returns true if the request was sent, false if you are not in a field or the game refused the move (for example on that map already, or during the cooldown); the game shows its usual notice when it refuses.


core.is_hyper_rock_ready() -> boolean

Available on GMS

Returns true when the Hyper Teleport Rock can be used right now, false while its cooldown is still running or no character is loaded.


core.logout() -> boolean

Available on GMS and CW

Logs out to the world-select screen, as if you had logged out from the in-game menu. The client stays open — this does not close the game. Returns true if the logout was issued, or false (no-op) if you're not currently in the game world.


core.terminate()

Available on GMS and CW

Closes the game client immediately. This does not return — the process exits on the spot, so any code after the call will not run.

Warning

core.terminate() ends the process outright (no logout, no save, no confirmation). Use it when you want the client closed; use core.logout() instead to drop back to world select while leaving the client running.


core.set_auto_restart(enabled: boolean) -> boolean

Available on GMS and CW

Enables or disables auto-restart for the game profile this client was launched from. When enabled, the launcher automatically re-opens the client with the same account if it closes. The setting is saved to your account, so it persists across future launches. Returns true if the request was sent.

Settings API ⚙️

The Settings API provides runtime access to game settings. Changes are automatically synced to the web UI and persist across sessions.

Finding Setting Paths

Developer Mode (Recommended)

  1. Open Settings in web menu
  2. Press Ctrl+Shift+D to enable Developer Mode
  3. Right-click any setting to copy its path

Path Format: category.subcategory.property

Examples: autos.auto_pot.hp.enabled, hacks.godmode, combat.kami.enabled


core.get_setting(path: string) -> value | nil

Available on GMS and CW

Retrieves a setting value by path.

Returns: Setting value (boolean, number, string, or table) or nil if not found. Array/object settings (like skill injection lists) are returned as Lua tables.

lua
local auto_hp = core.get_setting("autos.auto_pot.hp.enabled")
local threshold = core.get_setting("autos.auto_pot.hp.value")

-- Array settings return as Lua tables
local skills = core.get_setting("combat.skill_injection.skills")
if skills then
    for i, skill in ipairs(skills) do
        core.log("Skill ID: " .. tostring(skill.id) .. " Delay: " .. tostring(skill.delay))
    end
end

core.get_scripts() -> table

Available on GMS and CW

Returns every discovered Lua script together with its current enabled and loaded state. For each loaded script, settings contains the menu elements that script registered, including each element's full setting path (id), type, label, and current value. This lets callers discover script settings without knowing their paths in advance.

Disabled scripts return an empty settings table because their Lua code is not loaded and cannot register menu elements.

Privacy and Consent

Using core.get_scripts() to scrape, collect, exfiltrate, or otherwise harvest a user's script data or settings is strictly prohibited. Only use this function for user-approved local discovery and configuration workflows.

Returns:

lua
{
    {
        filename = "auto_farm.lua",
        settings_key = "auto_farm.lua",
        name = "Auto Farm",
        description = "Automatically farms selected maps",
        author = "Violet",
        version = "1.0.0",
        source = "server",
        enabled = true,
        loaded = true,
        settings = {
            {
                id = "lua.auto_farm.enabled",
                type = "checkbox",
                label = "Enable Farming",
                tooltip = "Toggles Auto Farm",
                tab = "Lua",
                panel = "Auto Farm",
                order = 0,
                sourceScript = "auto_farm.lua",
                value = true
            },
            {
                id = "lua.auto_farm.attack_delay",
                type = "slider_int",
                label = "Attack Delay",
                tooltip = "",
                tab = "Lua",
                panel = "Auto Farm",
                order = 1,
                sourceScript = "auto_farm.lua",
                min = 50,
                max = 1000,
                step = 50,
                value = 200
            }
        }
    },
    {
        filename = "boss_helper.lua",
        settings_key = "boss_helper.lua",
        name = "Boss Helper",
        description = "",
        author = "",
        version = "",
        source = "local",
        enabled = false,
        loaded = false,
        settings = {}
    }
}
lua
for _, script in ipairs(core.get_scripts()) do
    core.log(script.name .. ": " .. (script.enabled and "enabled" or "disabled"))

    for _, setting in ipairs(script.settings) do
        core.log(setting.id .. " = " .. tostring(setting.value))
    end
end

Setting entries use the same schema descriptors as the web menu, augmented with their live value. Type-specific fields such as min, max, and step are included when that setting type defines them.


core.set_setting(path: string, value: boolean | number | string | table) -> boolean

Available on GMS and CW

Updates a setting value. Changes sync automatically to the web UI. Lua tables are converted to JSON arrays/objects for array settings.

Returns: true on success, false on failure.

Type Matching Required

The value type must match the setting's expected type or a Lua error will be thrown.

lua
-- ❌ Error: expects boolean, got number
core.set_setting("autos.auto_pot.hp.enabled", 123)

-- ✅ Correct
core.set_setting("autos.auto_pot.hp.enabled", true)
core.set_setting("autos.auto_pot.hp.value", 75)

-- ✅ Setting array values with tables
core.set_setting("combat.skill_injection.skills", {
    { id = 2321006, delay = 200, hits = 1, type = 0 },
    { id = 2321007, delay = 150, hits = 2, type = 1 }
})

Custom Storage 💾

core.settings.storage is free-form, script-owned key/value storage for values that aren't tied to any menu setting — counters, timestamps, per-character bookkeeping, anything your script wants to remember between runs.

Storage is saved as part of the active profile: it persists across sessions, travels with the profile when you save it, and switching profiles swaps the stored values too. Values can be any JSON-compatible type (boolean, number, string, or table), and keys use the same dot-path format as settings.

Namespace your keys

Prefix keys with your script's name (e.g. "my_script.runs") so different scripts don't collide in the shared storage space.

core.settings.storage.get(path: string) -> value | nil

Available on GMS and CW

Reads a stored value. Returns nil if nothing is stored at that path. Whole numbers come back as a regular number, including an integer you stored. Only a whole number beyond ±9,007,199,254,740,991 comes back as an integer, because a number can't hold it exactly.

lua
local runs = core.settings.storage.get("my_script.runs") or 0

core.settings.storage.set(path: string, value: boolean | number | string | table) -> boolean

Available on GMS and CW

Stores a value, creating the path if needed. Unlike set_setting, any key and any type is accepted — there's no menu setting to match. Returns true on success.

lua
core.settings.storage.set("my_script.runs", runs + 1)
core.settings.storage.set("my_script.seen_intro", true)
core.settings.storage.set("my_script.config", { threshold = 50, names = { "A", "B" } })

core.settings.storage.delete(path: string) -> boolean

Available on GMS and CW

Removes a stored value. Returns true if a value existed at that path.

lua
core.settings.storage.delete("my_script.runs")

core.settings.storage.all() -> table

Available on GMS and CW

Returns the entire storage namespace as a table (empty if nothing is stored).

lua
for key in pairs(core.settings.storage.all()) do
    core.log(key)
end
Save to persist

Storage changes are live immediately but only persisted when the profile is saved — use core.profiles.save() to write them to the active profile, the same as any other setting.

Common Setting Paths

Tip

For the complete list of all 75 settings with types, defaults, and descriptions, see the Settings Reference.

Finding Paths

Enable Developer Mode in Settings (Ctrl+Shift+D) and click any setting badge to copy its exact path.

Setting PathTypeDescription
Autos
autos.auto_pot.hp.enabledbooleanEnable auto HP potion
autos.auto_pot.hp.valuenumberHP threshold (0-100)
autos.auto_pot.hp.keybindstringHP potion keybind
autos.auto_pot.mp.enabledbooleanEnable auto MP potion
autos.auto_pot.mp.valuenumberMP threshold (0-100)
autos.auto_pot.mp.keybindstringMP potion keybind
autos.auto_login.enabledbooleanEnable auto login
autos.auto_login.world_idnumberWorld dropdown index (see table below)
autos.auto_login.channelnumberChannel dropdown index (0 = random, 1-40 = specific)
autos.auto_login.char_indexnumberCharacter slot index
autos.evasion.typenumberEvasion type dropdown (0=Next Map CC, 1=Disable, 2=Logout, 3=Terminate, 4=Town CC)
autos.evasion.whitelisted_ignstableWhitelisted IGNs (string array)
Hacks
hacks.godmodebooleanGodmode toggle
hacks.bossing_godmodebooleanBossing godmode toggle
hacks.pet_lootbooleanPet loot toggle
hacks.speedy_fmabooleanSpeedy FMA toggle
Combat
combat.kami.enabledbooleanKami (teleport hack) toggle
combat.kami.typenumberKami type dropdown (0=Closest, 1=Random, 2=Random Speedy)
combat.kami.kami_expbooleanKami EXP toggle
combat.kami.kami_lootbooleanKami loot toggle
combat.kami.x_offsetnumberKami X offset
combat.kami.y_offsetnumberKami Y offset
combat.skill_injection.enabledbooleanEnable skill injection
combat.skill_injection.safe_modebooleanSafe mode toggle
combat.skill_injection.skillstableSkill list (see Array Settings below)
Items
items.filter_enabledbooleanEnable item filter
items.filter_modenumberFilter mode dropdown (0=blacklist, 1=whitelist)
items.filtered_itemstableFiltered item IDs (int array)
items.meso_filter_enabledbooleanEnable meso filter
items.min_meso_amountnumberMinimum meso amount
Map
map.rush_by_leveltableLevel-based rush targets (see Array Settings below)
map.spawn_pointstableCustom spawn points (see Array Settings below)
Packets
packets.streaming_enabledbooleanEnable packet streaming
packets.blocked_incoming_opcodestableBlocked incoming opcodes (int array)
packets.blocked_outgoing_opcodestableBlocked outgoing opcodes (int array)
packets.ignored_incoming_opcodestableIgnored incoming opcodes (int array)
packets.ignored_outgoing_opcodestableIgnored outgoing opcodes (int array)
Macros
macros.listtableMacro list (see Array Settings below)

Dropdown settings store a 0-based index, not the display string. Use the index values listed above.

World Selection (Auto Login)

The autos.auto_login.world_id dropdown uses these indices:

IndexWorld NameNotes
0ScaniaNA - Default world
1BeraNA
2KronosNA - Reboot (40 channels)
3HyperionNA
4NA CW HeroicNA - Classic Worlds (10 channels)
5NA CW InteractiveNA - Classic Worlds (10 channels)
6LunaEU
7SolisEU
8EU CW HeroicEU - Classic Worlds (10 channels)
9EU CW InteractiveEU - Classic Worlds (10 channels)

Example:

lua
-- Set auto login to Kronos, Channel 5, Character slot 2
core.set_setting("autos.auto_login.world_id", 2)    -- Kronos (index 2)
core.set_setting("autos.auto_login.channel", 5)      -- Channel 5 (index 5)
core.set_setting("autos.auto_login.char_index", 2)   -- 3rd character

Array Settings

Array settings are accessed as Lua tables. Use get_setting to read and set_setting to write.

Skill Injection Skills

lua
-- Each skill: { enabled = boolean, name = string, id = number, delay = number, hits = number, type = number }
-- enabled (default true) and name (display label, default "") are optional
-- type: 0=Generic, 1=Melee, 2=Magic, 3=Shoot, 4=Use Skill, 5=Safe Mode
local skills = core.get_setting("combat.skill_injection.skills")
core.set_setting("combat.skill_injection.skills", {
    { name = "Chain Lightning", id = 2321006, delay = 200, hits = 1, type = 0 },
    { id = 2321007, delay = 150, hits = 2, type = 1 }
})

Rush By Level

lua
-- Each entry: { min_level = number, max_level = number, map_id = number }
local rush_list = core.get_setting("map.rush_by_level")
core.set_setting("map.rush_by_level", {
    { min_level = 10, max_level = 30, map_id = 100000000 },
    { min_level = 31, max_level = 60, map_id = 101000000 }
})

Spawn Points

lua
-- Each entry: { map_id = number, x = number, y = number, label = string }
core.set_setting("map.spawn_points", {
    { map_id = 100000000, x = -50, y = 200, label = "Henesys Center" }
})

Macros

lua
-- Each entry: { name = string, key = string, delay = number, enabled = boolean }
core.set_setting("macros.list", {
    { name = "Buff", key = "F1", delay = 1000, enabled = true }
})

Simple Arrays

lua
-- Integer arrays (filtered items, blocked opcodes, etc.)
core.set_setting("items.filtered_items", { 2000000, 2000001, 2000003 })
core.set_setting("packets.blocked_incoming_opcodes", { 0x1234, 0x5678 })

-- String arrays (whitelisted IGNs)
core.set_setting("autos.evasion.whitelisted_igns", { "FriendName1", "FriendName2" })

Custom Settings Creation 🧩

See the Menu module for the core.menu API that lets scripts create their own settings (checkboxes, sliders, dropdowns, keybinds, text inputs) in the web UI.

Logging

core.log(message: string)

Available on GMS and CW

Prints a white message with prefix [-].

core.log_error(message: string)

Available on GMS and CW

Prints a red message with prefix [!].

core.log_warning(message: string)

Available on GMS and CW

Prints a yellow message with prefix [?].

core.log_computer(message: string [, event_type: string])

Available on GMS and CW

Writes a line to your computer's activity log on the web dashboard (the log shown in the computer view), separate from the in-client console that core.log writes to. event_type is an optional tag for the entry, shown as a label (defaults to "script"). Frequent calls are rate-limited, so excess lines are dropped.

Info

Use Lua's tostring() function to convert numbers and booleans to strings for logging.

Utilities 🔧

core.util.fnv1a(value: string | number) -> string | nil, string

Available on GMS and CW

Hashes value with standard 64-bit FNV-1a and returns the hash as a 16-character lowercase hex string. Integers are hashed as their base-10 text, so core.util.fnv1a(123) and core.util.fnv1a("123") return the same hash — both regular numbers and Luau integer (int64) values (e.g. from in_packet:decode_8) are accepted. Returns nil plus an error message if value is not a string or integer (fractional numbers are rejected).

The output is deterministic and stable across sessions, clients, and Violet updates, which makes it a good pseudonymous key for external storage — for example, keying per-account progression rows in your own database (Supabase, Firebase, etc.) without storing the raw identifier.

lua
local player = core.object_manager.get_local_player()
if player and player:is_valid() then
    -- Stable, pseudonymous key for this account
    local account_key = core.util.fnv1a(player:get_account_id())

    -- Or scope it per character
    local char_key = core.util.fnv1a(player:get_account_id() .. ":" .. player:get_id())

    core.log("Account key: " .. account_key)
end
Not for security

FNV-1a is a fast non-cryptographic hash. It keeps raw IDs out of your database, but it is not encryption — don't use it for anything security-sensitive.