Core Functions
On this page
- Callbacks 🔄
- on_tick(stage: number)Available on GMS and CW
- on_render()Available on GMS and CW
- on_map_load()Available on GMS and CW
- on_packet_recv(packet: in_packet) -> booleanAvailable on GMS and CW
- on_packet_send(packet: in_packet) -> booleanAvailable on GMS and CW
- on_evasion(player_names: table)Available on GMS and CW
- on_gm_evasion()Available on GMS and CW
- on_force_evasion()Available on GMS and CW
- on_skill_use(skill_id: number, time: number, handle: number | nil)Available on GMS and CW
- on_death()Available on GMS and CW
- on_level_up(level: number)Available on GMS and CW
- on_rune(event: string, x: number, y: number)Available on GMS
- on_lie_detector(event: string)Available on GMS and CW
- on_buff_gained(buff_id: number)Available on GMS and CW
- on_buff_lost(buff_id: number)Available on GMS and CW
- on_item_gained(item_id: number, count: number)Available on GMS and CW
- on_damage_taken(amount: number, time: number)Available on GMS and CW
- on_mob_action(mob: Mob, action: table)Available on GMS and CW
- on_mob_action_end(mob: Mob | nil, action: table)Available on GMS and CW
- on_field_effect(path: string)Available on GMS and CW
- on_key_prompt(event: string, prompt: table)Available on GMS
- on_ui_event(event: table)Available on GMS and CW
- on_window_open(window: Window)Available on GMS and CW
- on_window_close(window: table)Available on GMS and CW
- on_wndproc(event: table) -> nil | boolean | numberAvailable on GMS and CW
- on_dialog(dialog: table) -> nil | boolean | number | stringAvailable on GMS and CW
- on_step_start(step: table)Available on GMS and CW
- on_step_stop(step: table, reason: string)Available on GMS and CW
- on_unload(reason: string)Available on GMS and CW
- Registering callbacks
- Stage Enum
- Game Information
- Settings API ⚙️
- Finding Setting Paths
- Custom Storage 💾
- Common Setting Paths
- Dropdown Settings
- World Selection (Auto Login)
- Array Settings
- Custom Settings Creation 🧩
- Logging
- Utilities 🔧
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.
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
endon_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.
function on_render()
core.render.text(20, 20, "Hello", { shadow = true })
endon_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.
function on_evasion(player_names)
for _, name in ipairs(player_names) do
core.log_warning("Evading from: " .. name)
end
endon_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.
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
endon_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.
function on_force_evasion()
core.log_warning("Someone you can't see is on the map - evading")
endon_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.
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
endon_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".
event | When |
|---|---|
"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.
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.
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)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.
function on_field_effect(path)
if path:find("clear") then
core.log("stage cleared: " .. path)
end
endon_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.
event | When |
|---|---|
"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.
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:
| Field | Type | Description |
|---|---|---|
type | string | "click", "double_click", "right_click", "press" (a button pressed down, before its click), "check", "text", "tab", "select", "slide", "slide_end", "list_select" or "other" |
code | number | The event's number, which tells "other" events apart; not set for "list_select" |
value | number | Detail 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_id | number | The control's id within its window; not set for "list_select" |
control_handle | number | The control's handle; not set for "list_select" |
window_handle | number | The handle of the control's or list's window |
window | Window | nil | The Window, or nil if it has closed |
control | Control | nil | The Control, or nil if it no longer exists; not set for "list_select" |
list | List | nil | For "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).
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)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.
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:
| Field | Type | Description |
|---|---|---|
hwnd | number | Window handle as an integer |
msg | number | Windows keyboard/mouse message ID |
w_param | number | Raw WPARAM value |
l_param | number | Raw LPARAM value |
blockable | boolean | Always true for delivered events |
x | number | Mouse X coordinate from l_param for mouse messages |
y | number | Mouse Y coordinate from l_param for mouse messages |
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
endon_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:
| Field | Type | Description |
|---|---|---|
kind | string | "npc" for NPC dialogs, "bluebox" for blue box notifications |
type | number | Dialog sub-type (see table below) |
text | string | The dialog message text (NPC message or blue box text) |
choices | table | Array of {text, index} — present when the dialog has list options. index is the selection value for each choice |
type values:
| Kind | Type | Meaning |
|---|---|---|
npc | 0 | Close / prev |
npc | 1 | Next / ok |
npc | 5 | Text input |
npc | 6 | List select (has choices) |
bluebox | 1000 | Yes / no prompt |
bluebox | other | Notice |
Return values:
| Return | Effect |
|---|---|
nil or true | Pass to core — auto NPC / auto blue boxes handles it if enabled |
false | Block — do nothing, leave dialog open |
6 | Click yes / ok / next |
7 | Click no / close |
number | Select 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 |
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.
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
endThe 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().
function on_step_stop(step, reason)
if reason == "safety" then
core.log_warning("Stopped for safety")
end
endon_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.
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.
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
endRegistering 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.
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)A function that is both registered and defined as the global of that name runs twice.
Callback names must match exactly as shown above. The core finds these functions by name; core.register_callback takes the same names.
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.*)
endStage 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.
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.
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.
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
endcore.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.
| Field | Type | Description |
|---|---|---|
year | number | Full year (e.g. 2026) |
month | number | Month (1-12) |
day | number | Day of month (1-31) |
hour | number | Hour (0-23) |
min | number | Minute (0-59) |
sec | number | Second (0-59) |
ms | number | Millisecond (0-999) |
epoch | number | Milliseconds since Unix epoch |
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.
| Field | Type | Description |
|---|---|---|
year | number | Full year (e.g. 2026) |
month | number | Month (1-12) |
day | number | Day of month (1-31) |
hour | number | Hour (0-23) |
min | number | Minute (0-59) |
sec | number | Second (0-59) |
ms | number | Millisecond (0-999) |
epoch | number | Milliseconds since Unix epoch |
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.
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
endcore.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.
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)
- Open Settings in web menu
- Press
Ctrl+Shift+Dto enable Developer Mode - 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.
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
endcore.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.
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:
{
{
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 = {}
}
}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
endSetting 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.
The value type must match the setting's expected type or a Lua error will be thrown.
-- ❌ 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.
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.
local runs = core.settings.storage.get("my_script.runs") or 0core.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.
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.
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).
for key in pairs(core.settings.storage.all()) do
core.log(key)
endStorage 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
For the complete list of all 75 settings with types, defaults, and descriptions, see the Settings Reference.
Enable Developer Mode in Settings (Ctrl+Shift+D) and click any setting badge to copy its exact path.
| Setting Path | Type | Description |
|---|---|---|
| Autos | ||
autos.auto_pot.hp.enabled | boolean | Enable auto HP potion |
autos.auto_pot.hp.value | number | HP threshold (0-100) |
autos.auto_pot.hp.keybind | string | HP potion keybind |
autos.auto_pot.mp.enabled | boolean | Enable auto MP potion |
autos.auto_pot.mp.value | number | MP threshold (0-100) |
autos.auto_pot.mp.keybind | string | MP potion keybind |
autos.auto_login.enabled | boolean | Enable auto login |
autos.auto_login.world_id | number | World dropdown index (see table below) |
autos.auto_login.channel | number | Channel dropdown index (0 = random, 1-40 = specific) |
autos.auto_login.char_index | number | Character slot index |
autos.evasion.type | number | Evasion type dropdown (0=Next Map CC, 1=Disable, 2=Logout, 3=Terminate, 4=Town CC) |
autos.evasion.whitelisted_igns | table | Whitelisted IGNs (string array) |
| Hacks | ||
hacks.godmode | boolean | Godmode toggle |
hacks.bossing_godmode | boolean | Bossing godmode toggle |
hacks.pet_loot | boolean | Pet loot toggle |
hacks.speedy_fma | boolean | Speedy FMA toggle |
| Combat | ||
combat.kami.enabled | boolean | Kami (teleport hack) toggle |
combat.kami.type | number | Kami type dropdown (0=Closest, 1=Random, 2=Random Speedy) |
combat.kami.kami_exp | boolean | Kami EXP toggle |
combat.kami.kami_loot | boolean | Kami loot toggle |
combat.kami.x_offset | number | Kami X offset |
combat.kami.y_offset | number | Kami Y offset |
combat.skill_injection.enabled | boolean | Enable skill injection |
combat.skill_injection.safe_mode | boolean | Safe mode toggle |
combat.skill_injection.skills | table | Skill list (see Array Settings below) |
| Items | ||
items.filter_enabled | boolean | Enable item filter |
items.filter_mode | number | Filter mode dropdown (0=blacklist, 1=whitelist) |
items.filtered_items | table | Filtered item IDs (int array) |
items.meso_filter_enabled | boolean | Enable meso filter |
items.min_meso_amount | number | Minimum meso amount |
| Map | ||
map.rush_by_level | table | Level-based rush targets (see Array Settings below) |
map.spawn_points | table | Custom spawn points (see Array Settings below) |
| Packets | ||
packets.streaming_enabled | boolean | Enable packet streaming |
packets.blocked_incoming_opcodes | table | Blocked incoming opcodes (int array) |
packets.blocked_outgoing_opcodes | table | Blocked outgoing opcodes (int array) |
packets.ignored_incoming_opcodes | table | Ignored incoming opcodes (int array) |
packets.ignored_outgoing_opcodes | table | Ignored outgoing opcodes (int array) |
| Macros | ||
macros.list | table | Macro list (see Array Settings below) |
Dropdown Settings
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:
| Index | World Name | Notes |
|---|---|---|
0 | Scania | NA - Default world |
1 | Bera | NA |
2 | Kronos | NA - Reboot (40 channels) |
3 | Hyperion | NA |
4 | NA CW Heroic | NA - Classic Worlds (10 channels) |
5 | NA CW Interactive | NA - Classic Worlds (10 channels) |
6 | Luna | EU |
7 | Solis | EU |
8 | EU CW Heroic | EU - Classic Worlds (10 channels) |
9 | EU CW Interactive | EU - Classic Worlds (10 channels) |
Example:
-- 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 characterArray Settings
Array settings are accessed as Lua tables. Use get_setting to read and set_setting to write.
Skill Injection Skills
-- 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
-- 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
-- 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
-- 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
-- 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.
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.
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)
endFNV-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.