================================================================================ VIOLET LUA SDK - COMPLETE API REFERENCE FOR AI/LLM ================================================================================ Ultra-comprehensive plain text reference optimized for AI consumption. Maximum information density. Token-efficient format. Last Updated: 2025-01-06 ================================================================================ MANDATORY PLUGIN STRUCTURE ================================================================================ Every script MUST begin with this structure: plugin = { name = "Script Name", version = "1.0.0", author = "Author Name", description = "Brief description", load = true -- Auto-load on injection } ================================================================================ CORE CALLBACKS ================================================================================ Scripts execute through callbacks. Define only what you need: on_tick() - Called every ~25ms during gameplay - Primary location for game logic, movement, combat - Runs when player is loaded and game active on_map_load() - Called when loading a new map - Use for map-specific initialization - Called BEFORE on_tick for the new map on_login_tick() - Called during login/character selection screen - Use for pre-game automation on_packet_recv(packet: in_packet) -> boolean - Called when receiving packet from server - Return true to allow, false to block - Requires plugin metadata structure on_packet_send(packet: out_packet) -> boolean - Called when sending packet to server - Return true to allow, false to block - Requires plugin metadata structure ================================================================================ GUARD CLAUSE PATTERN (CRITICAL) ================================================================================ ALWAYS validate objects before use: function on_tick() local player = core.object_manager.get_local_player() if not player or not player:is_valid() then return end local map = core.object_manager.get_current_map() if not map or not map:is_valid() then return end -- Safe to use player and map here end ================================================================================ CORE MODULE - LOGGING ================================================================================ core.log(message: string) - Log white message with [-] prefix core.log_error(message: string) - Log red message with [!] prefix core.log_warning(message: string) - Log yellow message with [?] prefix NOTE: Use tostring() to convert numbers/booleans for logging ================================================================================ CORE MODULE - GAME INFORMATION ================================================================================ core.get_world_id() -> number | nil - Returns current world/server ID - nil if not in game core.get_channel_id() -> number | nil - Returns current channel number - nil if not in game core.change_channel(channel_id: number) - Switch to specified channel - Must be out of combat core.is_evading() -> boolean - Returns true if currently evading core.is_solving_rune() -> boolean - Returns true if solving rune puzzle - Use to pause automation during runes core.is_in_cutscene() -> boolean | nil - Returns true if in cutscene/dialogue - nil if state unknown core.get_update_time() -> number - Returns current game update time in milliseconds - Use for timing operations and delays ================================================================================ CORE MODULE - SETTINGS API ================================================================================ Dynamic runtime access to game settings. Changes sync to web UI automatically. FINDING SETTING PATHS: Developer Mode (Recommended): 1. Open Settings in web menu 2. Press Ctrl+Shift+D for Developer Mode 3. Right-click any setting to copy its path Path Format: "category.subcategory.property" Examples: "autos.autoPot.hp.enabled", "hacks.godmode" core.get_setting(path: string) -> value | nil - Retrieves setting value by path - Returns boolean, number, or string - Returns nil if setting not found Example: local auto_hp = core.get_setting("autos.autoPot.hp.enabled") local threshold = core.get_setting("autos.autoPot.hp.value") core.set_setting(path: string, value: boolean|number|string) -> boolean - Updates setting value - Value type MUST match setting's expected type - Returns true on success, false on failure - Changes sync automatically to web UI Example: core.set_setting("autos.autoPot.hp.enabled", true) core.set_setting("autos.autoPot.hp.value", 75) core.set_setting("hacks.godmode", false) TYPE MISMATCH ERROR: -- WRONG: Will throw Lua error core.set_setting("autos.autoPot.hp.enabled", 123) -- CORRECT: core.set_setting("autos.autoPot.hp.enabled", true) COMMON SETTING PATHS: Autos: "autos.autoPot.hp.enabled" -- boolean "autos.autoPot.hp.value" -- number (0-100) "autos.autoPot.hp.keybind" -- string "autos.autoPot.mp.enabled" -- boolean "autos.autoPot.mp.value" -- number (0-100) "autos.autoPot.mp.keybind" -- string "autos.autoLogin.enabled" -- boolean "autos.autoLogin.worldId" -- number (see world ID table) "autos.autoLogin.channel" -- number (0=random, 1-40=specific) "autos.autoLogin.charIndex" -- number Hacks: "hacks.godmode" -- boolean "hacks.petLoot" -- boolean "hacks.kami" -- boolean "hacks.speedHack" -- boolean Combat: "combat.combos.enabled" -- boolean "combat.combos.delay" -- number (ms) Packets: "packets.streamingEnabled" -- boolean WORLD ID TABLE (for autos.autoLogin.worldId): NA Worlds: 0 - Scania (default) 1 - Bera 45 - Kronos (Reboot, 40 channels) 70 - Hyperion 52 - NA CW Heroic (Classic Worlds, 10 channels) 48 - NA CW Interactive (Classic Worlds, 10 channels) EU Worlds: 30 - Luna 46 - Solis 54 - EU CW Heroic (Classic Worlds, 10 channels) 49 - EU CW Interactive (Classic Worlds, 10 channels) CHANNEL SYSTEM: - 0 = Random channel (system selects) - 1-40 = Specific channel number (user perspective) - Server conversion handled automatically (channel 1 becomes 0 internally) Example - Auto login to Kronos, Channel 5, Character 2: core.set_setting("autos.autoLogin.worldId", 45) core.set_setting("autos.autoLogin.channel", 5) core.set_setting("autos.autoLogin.charIndex", 2) ================================================================================ OBJECT MANAGER ================================================================================ core.object_manager.get_local_player() -> Player | nil - Returns local player object - nil if player not loaded - ALWAYS validate with is_valid() before use core.object_manager.get_current_map() -> Map | nil - Returns current map object - nil if map not loaded - ALWAYS validate with is_valid() before use ================================================================================ PLAYER OBJECT ================================================================================ player:is_valid() -> boolean - ALWAYS call before using other methods player:get_id() -> number player:get_name() -> string player:get_health() -> number player:get_max_health() -> number player:get_mana() -> number player:get_max_mana() -> number player:get_level() -> number player:get_exp() -> number player:get_meso() -> number player:get_job() -> number player:get_position() -> {x: number, y: number} - Returns position table - Example: local pos = player:get_position(); print(pos.x, pos.y) player:has_buff(buff_id: number) -> boolean - Returns true if buff active - More efficient than get_buff() for checking existence player:get_buff(buff_id: number) -> buff | nil - Returns buff object or nil - Buff: {id, type, name, time_remaining} - time_remaining in milliseconds player:get_buffs() -> table - Returns array of all active buffs - Buff: {id, type, name, time_remaining} ================================================================================ MAP OBJECT ================================================================================ map:is_valid() -> boolean - ALWAYS call before using other methods map:get_id() -> number - Returns map ID (e.g., 100000000 = Henesys) map:is_town() -> boolean - Returns true if map is a town map:get_burning_stage() -> number | nil - Returns burning XP stage (0-10) - nil if not burning field map:get_drops() -> table - Returns all drops on map - Drop: {id, position: {x, y}, type} map:get_portals() -> table - Returns all portals - Portal: {position: {x, y}, type, target_map_id} map:get_mobs() -> table - Returns all mobs on map map:get_bosses() -> table - Returns all boss mobs map:get_mob_count() -> number - Returns total mob count - More efficient than iterating get_mobs() map:get_npcs() -> table - Returns all NPCs on map map:get_footholds() -> table - Returns all platforms - Foothold: {x1, y1, x2, y2} map:get_ladders() -> table - Returns all ladders/ropes - Ladder: {x, y1, y2} ================================================================================ MOB OBJECT ================================================================================ mob:is_valid() -> boolean - ALWAYS call before using other methods mob:get_id() -> number | nil - Returns mob template ID mob:get_position() -> {x: number, y: number} | nil - Returns position or nil mob:get_name() -> string | nil - Returns mob name or nil mob:is_elite() -> boolean | nil - Returns true if elite/champion - nil if unknown ================================================================================ NPC OBJECT ================================================================================ npc:is_valid() -> boolean - ALWAYS call before using other methods npc:get_id() -> number - Returns NPC template ID npc:get_position() -> {x: number, y: number} - Returns NPC position npc:get_name() -> string - Returns NPC name ================================================================================ INPUT MODULE - GAME INTERACTION ================================================================================ core.input.use_skill(skill_id: number) - Cast skill by ID - Check cooldown first with skill_book.is_skill_on_cooldown() core.input.use_item(item_id: number) - Use item from inventory - Verify existence first with inventory.has_item() core.input.talk_to_npc(npc_id: number) - Initiate NPC dialogue - Ensure NPC is on current map core.input.enter_portal() - Enter portal at player position - Ensure player is on portal first core.input.press_key(key_code: number) - Simulate key press - Requires valid local player - See: https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes ================================================================================ INPUT MODULE - MOVEMENT ================================================================================ core.input.teleport(x: number, y: number) - Instant teleport to coordinates - WARNING: Spamming triggers anti-cheat - Use only for one-time teleports to portals/NPCs core.input.teleport_safe(x: number, y: number, x_offset?: number, y_offset?: number) - Safe teleport with foothold validation - Moves toward position over time (like Kami) - Optional x_offset and y_offset parameters core.input.is_moving() -> boolean - Returns true if player moving core.input.stop_moving() - Stop all movement immediately core.input.move_x(x_coord: number) - Move to X coordinate (maintains Y) core.input.move_y(y_coord: number) - Move to Y coordinate (maintains X) NOTE: Movement functions don't account for manual player input ================================================================================ SKILL BOOK ================================================================================ core.skill_book.get_skill_level(skill_id: number) -> number - Returns skill level (0 if not learned) core.skill_book.get_skill(skill_id: number) -> skill | nil - Returns skill object or nil - Skill: {id, type, name} core.skill_book.get_skills() -> table - Returns all learned skills - Skill: {id, type, name} core.skill_book.is_skill_on_cooldown(skill_id: number) -> boolean - Returns true if on cooldown ================================================================================ INVENTORY ================================================================================ TAB IDs: 1 = Equip 2 = Use 3 = Etc 4 = Setup 5 = Cash core.inventory.get_total_slots(tab_id: number) -> number - Returns total slots in tab core.inventory.get_free_slots(tab_id: number) -> number - Returns free slots in tab core.inventory.has_item(item_id: number) -> boolean - Returns true if item exists in any tab core.inventory.get_all_items() -> table - Returns all items across all tabs core.inventory.get_equip_items() -> table - Returns Equip tab items (tab_id=1) core.inventory.get_use_items() -> table - Returns Use tab items (tab_id=2) core.inventory.get_etc_items() -> table - Returns Etc tab items (tab_id=3) core.inventory.get_setup_items() -> table - Returns Setup tab items (tab_id=4) core.inventory.get_cash_items() -> table - Returns Cash tab items (tab_id=5) ITEM STRUCTURE: { id = number, -- Item template ID position = number, -- Slot index count = number, -- Quantity name = string, -- Display name tab_id = number, -- Tab (1-5) potentials = table, -- Array of strings (Equip only, up to 7) current_star_count = number, -- Star force (Equip only) max_star_count = number -- Max star force (Equip only) } ================================================================================ QUESTER ================================================================================ core.quester.get_quest_state(quest_id: number) -> number - Returns quest state: -1 = Not available 0 = Available 1 = In progress 2 = Completed core.quester.start_quest(npc_id: number, quest_id: number) - Start quest with NPC - Check state first with get_quest_state() core.quester.complete_quest(npc_id: number, quest_id: number) - Complete quest with NPC - Check if completable first with can_complete_quest() core.quester.can_complete_quest(npc_id: number, quest_id: number) -> boolean - Returns true if quest requirements met core.quester.queue_npc_selection(selection: string) - Queue dialogue option substring - When NPC dialogue appears, option containing substring is selected - For text input, queued string is used as answer core.quester.clear_npc_selection() - Clear entire NPC selection queue ================================================================================ RUSHER ================================================================================ core.rusher.rush(map_id: number) - Auto-navigate to target map - Handles pathfinding and portals automatically core.rusher.stop() - Stop current rushing immediately core.rusher.is_rushing() -> boolean - Returns true if currently rushing ================================================================================ IN PACKET (RECEIVING) ================================================================================ Used in on_packet_recv(packet) callback. packet:get_opcode() -> number - Returns packet opcode packet:decode_1() -> number - Read 1 byte (0-255) packet:decode_2() -> number - Read 2 bytes (0-65535) packet:decode_4() -> number - Read 4 bytes (0-4294967295) packet:decode_8() -> number - Read 8 bytes packet:decode_string() -> string - Read length-prefixed string NOTE: Decode methods read sequentially. Each call advances read position. Cannot rewind or re-read data. ================================================================================ OUT PACKET (SENDING) ================================================================================ Constructor: out_packet(opcode: number) -> OutPacket packet:encode_1(value: number) -> self - Encode 1 byte (0-255) - Returns self for chaining packet:encode_2(value: number) -> self - Encode 2 bytes (0-65535) - Returns self for chaining packet:encode_4(value: number) -> self - Encode 4 bytes (0-4294967295) - Returns self for chaining packet:encode_8(value: number) -> self - Encode 8 bytes - Returns self for chaining packet:encode_string(text: string) -> self - Encode length-prefixed string - Returns self for chaining packet:encode_buffer(data: string) -> self - Encode raw binary data - Returns self for chaining packet:to_string() -> string - Returns hex string for debugging packet:send() - Send packet to server EXAMPLE: out_packet(0x0029) :encode_1(1) :encode_4(map_id) :encode_string("test") :send() ================================================================================ COMMON PATTERNS ================================================================================ --- HP/MP MONITORING --- function on_tick() local player = core.object_manager.get_local_player() if not player or not player:is_valid() then return end local hp_percent = (player:get_health() / player:get_max_health()) * 100 if hp_percent < 50 then core.input.use_item(2000001) -- Red Potion end end --- MOB HUNTING --- function on_tick() local player = core.object_manager.get_local_player() if not player or not player:is_valid() then return end local map = core.object_manager.get_current_map() if not map or not map:is_valid() then return end local mobs = map:get_mobs() for _, mob in ipairs(mobs) do if mob:is_valid() then local pos = mob:get_position() if pos then core.input.teleport_safe(pos.x, pos.y) core.input.use_skill(2001004) break end end end end --- BUFF MAINTENANCE --- function on_tick() local player = core.object_manager.get_local_player() if not player or not player:is_valid() then return end local buff_id = 2001002 if not player:has_buff(buff_id) then if not core.skill_book.is_skill_on_cooldown(buff_id) then core.input.use_skill(buff_id) end end end --- DISTANCE CALCULATION --- function distance(pos1, pos2) local dx = pos1.x - pos2.x local dy = pos1.y - pos2.y return math.sqrt(dx * dx + dy * dy) end --- COOLDOWN MANAGER --- local cooldowns = {} function can_use(key, cooldown_ms) local now = core.get_update_time() if not cooldowns[key] or now - cooldowns[key] >= cooldown_ms then cooldowns[key] = now return true end return false end --- STATE MACHINE --- local state = "idle" function on_tick() if state == "idle" then state = "hunting" elseif state == "hunting" then -- hunting logic if condition then state = "looting" end elseif state == "looting" then -- looting logic state = "idle" end end --- MAP CHANGE DETECTION --- local last_map_id = nil function on_tick() local map = core.object_manager.get_current_map() if not map or not map:is_valid() then return end local current_map_id = map:get_id() if last_map_id ~= current_map_id then core.log("Map changed to: " .. current_map_id) last_map_id = current_map_id -- Reset state for new map end end ================================================================================ CRITICAL SAFETY RULES ================================================================================ 1. ALWAYS validate objects with is_valid() before calling methods 2. ALWAYS check for nil returns before using values 3. NEVER assume map or player exists - validate every tick 4. Use guard clauses at start of on_tick() to exit early if invalid 5. Wrap risky operations in pcall(): local success, result = pcall(function() return operation() end) 6. Avoid infinite loops - always have exit conditions 7. Be mindful of packet manipulation - can cause disconnects 8. Test scripts in safe environments first 9. Use core.log() liberally for debugging 10. Handle nil positions from mobs/npcs gracefully ================================================================================ PERFORMANCE OPTIMIZATION ================================================================================ 1. Cache frequently accessed objects within tick 2. Limit loop iterations: for i = 1, math.min(#mobs, 10) do 3. Use early returns: if core.is_solving_rune() then return end 4. Throttle expensive operations: tick_count = tick_count + 1 if tick_count % 10 == 0 then ... end 5. Prefer has_buff() over get_buff() for existence checks 6. Use local variables for frequently accessed values 7. Break out of loops early when target found 8. Minimize packet parsing - decode only what you need 9. Batch operations when possible ================================================================================ COMMON MAP IDS ================================================================================ Henesys: 100000000 Ellinia: 101000000 Perion: 102000000 Kerning City: 103000000 Lith Harbor: 104000000 Sleepywood: 105000000 Nautilus: 120000000 Orbis: 200000000 El Nath: 211000000 Ludibrium: 220000000 Leafre: 240000000 Mu Lung: 250000000 Ariant: 260000000 ================================================================================ COMMON JOB IDS ================================================================================ Beginner: 0 Warrior: 100 Fighter: 110, Hero: 111 Page: 120, Paladin: 121 Spearman: 130, Dark Knight: 131 Magician: 200 Fire/Poison: 210, Arch Mage (F/P): 211 Ice/Lightning: 220, Arch Mage (I/L): 221 Cleric: 230, Bishop: 231 Bowman: 300 Hunter: 310, Bow Master: 311 Crossbowman: 320, Marksman: 321 Thief: 400 Assassin: 410, Night Lord: 411 Bandit: 420, Shadower: 421 Pirate: 500 Brawler: 510, Buccaneer: 511 Gunslinger: 520, Corsair: 521 ================================================================================ LUA STANDARD LIBRARY ================================================================================ AVAILABLE: Math: math.abs, math.sqrt, math.sin, math.cos, math.floor, math.ceil, etc. String: string.format, string.sub, string.len, string.find, string.upper Table: table.insert, table.remove, table.sort Pairs/IPairs: for iteration NOT AVAILABLE: os library (use core.get_update_time() for timing) io library (file operations not supported) debug library ================================================================================ DEBUGGING TECHNIQUES ================================================================================ 1. Log everything during development 2. Use descriptive error messages 3. Print table contents with pairs/ipairs 4. Track execution flow with log statements 5. Monitor packet opcodes in packet handlers 6. Measure execution time with get_update_time() 7. Test edge cases (empty arrays, nil values, zero HP) 8. Validate data types before operations 9. Use pcall for error handling ================================================================================ END OF REFERENCE ================================================================================ This reference covers 100% of documented Violet Lua SDK APIs. Optimized for AI/LLM consumption with maximum information density. For support: Violet community Discord ================================================================================