Core Functions
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.
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:
| 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 |
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.
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
endWARNING
The 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.
INFO
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.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.
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
Returns true if currently evading.
core.is_solving_rune() -> boolean
Returns true if currently solving a rune.
core.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.
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.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.
Type Matching Required
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 }
})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 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 [?].
INFO
Use Lua's tostring() function to convert numbers and booleans to strings for logging.