Core Functions
On this page
- Callbacks 🔄
- on_tick()
- on_map_load()
- on_login_tick()
- on_packet_recv(packet: in_packet) -> boolean
- on_packet_send(packet: in_packet) -> boolean
- on_evasion(player_names: table)
- on_wndproc(event: table) -> nil | boolean | number
- on_dialog(dialog: table) -> nil | boolean | number | string
- Game Information
- Settings API ⚙️
- Finding Setting Paths
- Custom Storage 💾
- Common Setting Paths
- Dropdown Settings
- World Selection (Auto Login)
- Array Settings
- Custom Settings Creation 🧩
- Logging
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.
on_tick()
Executes every game tick (approximately every 30ms). This is where most game logic belongs.
on_map_load()
Triggers when a new map loads. Called before on_tick for the new map.
on_login_tick()
Executes during the login screen, before entering the game world. Useful for automating login and character selection.
on_packet_recv(packet: in_packet) -> boolean
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
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)
Called once when evasion is first triggered by a non-whitelisted player appearing on the map. player_names is an array of strings containing the names of detected 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_wndproc(event: table) -> nil | boolean | number
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
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.
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 a CT_LIST (list options). index is the game's internal 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 pointer. 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.
Callback names must match exactly as shown above. The core registers functions by name.
function on_tick()
-- Your game logic here
end
function on_map_load()
-- Map initialization code here
endGame Information
core.get_world_id() -> number | nil
Returns the current world ID (e.g., Kronos).
core.get_channel_id() -> number | nil
Returns the current channel ID.
core.get_game_version() -> string
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
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
Returns true if currently evading.
core.is_solving_rune() -> boolean
Returns true if currently solving a rune.
core.is_solving_lie_detector() -> boolean
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()
if core.is_solving_lie_detector() then
return -- wait for lie detector to finish
end
-- ... your logic
endcore.is_in_cutscene() -> boolean | nil
Returns true if currently in a cutscene.
core.get_update_time() -> number
Returns the current game update time in milliseconds. Useful for timing operations.
core.get_system_time() -> table
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.is_rushing() -> boolean
Returns true if currently rushing to a map (via the rusher module).
core.is_auto_selling() -> boolean
Returns true while the auto-sell flow is active — covers the full state machine (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()
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
Teleports to the specified map using the Hyper Teleport Rock. Returns true on success, false if context is unavailable.
core.logout() -> boolean
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()
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
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
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
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
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
Reads a stored value. Returns nil if nothing is stored at that path.
local runs = core.settings.storage.get("my_script.runs") or 0core.settings.storage.set(path: string, value: boolean | number | string | table) -> boolean
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
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
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) |
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: { id = number, delay = number, hits = number, type = number }
-- 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", {
{ 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)
Prints a white message with prefix [-].
core.log_error(message: string)
Prints a red message with prefix [!].
core.log_warning(message: string)
Prints a yellow message with prefix [?].
core.log_computer(message: string [, event_type: string])
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.