The bot API
Every property and method on L2Bot: what it does, its parameters, and what true and false mean.
On this page
Everything a script can reach through the bot object (type L2Bot).
- Properties read the world and return models —
bot.usergives you aPlayer,bot.npcsgives you aList<Npc>. The fields of those classes are on the Models page. - Commands are
suspendfunctions and all of them returnBoolean. Whattrueandfalsemean is spelled out for every command individually. logis the only method that is neithersuspendnor returns anything.
Commands run sequentially: a call returns once the command is done, so you don't need to wait after it. There is no auto-retry: false is an answer, not an error — retrying is up to the script.
Contents
For how id differs from oid and why many commands come in three forms, see “id or oid”.
World reads
These are properties, not functions: no suspend, read them as often as you like. Every read gives you the current picture of the world, but the object you get is a snapshot — it never updates itself, so re-read the property on every loop iteration.
| Property | Type | What you get |
|---|---|---|
bot.id | String | Identifier of the character this script controls. |
bot.user | Player | Your own character: HP/MP/CP, coordinates, level, weight, adena, casting state, inventory, skills. |
bot.target | LiveEntity? | The current target. null — no target. The concrete type is Npc, Char, Player or Pet. |
bot.npcs | List<Npc> | Every visible NPC and mob. An empty list means nobody is around. |
bot.chars | List<Char> | Every visible other player. |
bot.drops | List<Drop> | Every visible item on the ground. |
bot.pets | List<Pet> | Your own pets and summons. Currently 0 or 1 element. |
bot.skills | List<Skill> | Your skills. The same as bot.user.skills. |
bot.inventory | List<InventoryItem> | Your inventory. The same as bot.user.inventory. |
bot.party | List<PartyMember> | Party members. An empty list means you are not in a party. |
bot.dialogText | String | HTML of the current or last NPC dialog. Empty string — there was none. |
bot.storeItems | List<StoreItem> | Goods of the last private store you opened. Empty — you never opened one. |
bot.storeSellerOid | Int | Who owns bot.storeItems. 0 — you never opened a store. |
bot.boardText | String | HTML of the current or last community board (custom panel). Empty string — the board was never opened. |
bot.automationRunning | Boolean | Whether the bot's built-in automation is running. Lets a script restore it to the state it found, instead of switching it on for someone who never had it running. |
bot.events | Flow<ScriptEvent> | The game event stream. |
val me = bot.user // Player
bot.log("${me.name}: ${me.hp}/${me.maxHp} hp, ${me.adena} adena")
val mob = bot.npcs.nearest { it.attackable && !it.dead } // Npc?
if (mob != null) bot.log("${mob.name}, lvl ${mob.level}, ${mob.distToSelf} units away")Target and combat
| Method | What it does | What it returns |
|---|---|---|
setTarget(id: Int): Boolean | Selects a target. id is a mob template id (the nearest living mob of that type is used) or an object id. | true — the target is selected. false — no such object, or the server did not confirm. |
setTarget(entity: LiveEntity): Boolean | Selects a target by entity. | true — the target is selected. false — the object is no longer in the world. |
setTargetByOid(oid: Int): Boolean | Selects a target strictly by object id. | true — the target is selected. false — no such object. |
setTargetByType(npcId: Int): Boolean | Selects the nearest living mob of the given type. | true — the target is selected. false — no mob of that type nearby. |
cancelTarget(): Boolean | Clears the current target. | true — the target is cleared. false — clearing failed. |
attack(ctrl: Boolean = false): Boolean | Attacks the current target. ctrl — attack with Ctrl held (force). | Always true — the attack was sent. Whether it landed shows up in the AttackStarted/Died events or in the target's HP. |
attack(target: LiveEntity, ctrl: Boolean = false): Boolean | Selects the target, then attacks. | Always true. Target selection is not checked — see the note below. |
attack(id: Int, ctrl: Boolean = false): Boolean | Same, target given by template id or object id. | Always true. |
forceAttack(target: LiveEntity, ctrl: Boolean = false): Boolean | Force-attacks an entity. | Always true — the attack was sent. |
forceAttack(id: Int, ctrl: Boolean = false): Boolean | Force-attacks by template id (nearest living) or object id. | Always true. |
forceAttackByOid(oid: Int, ctrl: Boolean = false): Boolean | Force-attacks strictly by object id. | Always true. |
assist(other: LiveEntity): Boolean | Takes that character's target. | true — the target was taken. false — the character isn't visible or has no target. |
assistByOid(oid: Int): Boolean | Same, strictly by object id. | true — the target was taken. false — no object with that oid is visible. |
assist(name: String): Boolean | Takes the target of the character with that name. | true — the target was taken. false — no visible character with that name. |
autoTarget(range: Int = 2500, zRange: Int = 500): Boolean | Finds and targets the nearest enemy within range and within zRange vertically. | true — an enemy was found and targeted. false — nothing suitable. |
ignore(id: Int): Boolean | Adds to the ignore list. id is a template id (all currently visible mobs of that type are added) or an object id. | Always true — the entries were added. |
ignore(entity: LiveEntity): Boolean | Adds a specific entity to the ignore list. | Always true. |
ignoreByOid(oid: Int): Boolean | Same, strictly by object id. | Always true. |
clearIgnore(): Boolean | Clears the ignore list. | Always true. |
stopCasting(): Boolean | Interrupts your own cast. | true — the interrupt was sent and accepted. false — interrupting failed. |
The ignore list holds objects, not types. ignore(templateId) adds every mob of that type visible at the moment of the call; ones that spawn later are not covered — call it again.
Forms that take a target don't check target selection. attack(mob) means "select the target, then attack", and true only covers sending the attack. If you need to be sure the target was selected:
if (bot.setTarget(mob)) bot.attack()Skills
| Method | What it does | What it returns |
|---|---|---|
castSkill(skillId: Int, target: LiveEntity? = null, ctrl: Boolean = false, shift: Boolean = false): Boolean | Casts a skill. target = null — on the current target. ctrl — force cast (Ctrl), shift — cast without walking up to the target (Shift). | true — the cast started. false — you don't have the skill, it's on cooldown, not enough MP, the character is sitting, or the server rejected the cast. |
castSkill(skillId: Int, targetId: Int, ctrl: Boolean = false, shift: Boolean = false): Boolean | Same; target given by mob template id (nearest living) or object id. | As above. |
castSkillByOid(skillId: Int, targetOid: Int? = null, ctrl: Boolean = false, shift: Boolean = false): Boolean | Same; target strictly by object id, null — the current target. | As above. |
castAttackSkill(skillId: Int, target: LiveEntity? = null, ctrl: Boolean = false, shift: Boolean = false): Boolean | Casts an attack skill. Use this one for offensive skills and castSkill for everything else. | true — the cast started. false — same reasons as castSkill. |
castAttackSkill(skillId: Int, targetId: Int, ctrl: Boolean = false, shift: Boolean = false): Boolean | Same; target by template id or object id. | As above. |
castAttackSkillByOid(skillId: Int, targetOid: Int? = null, ctrl: Boolean = false, shift: Boolean = false): Boolean | Same; target strictly by object id. | As above. |
castSkillAt(skillId: Int, x: Int, y: Int, z: Int, ctrl: Boolean = false, shift: Boolean = false): Boolean | Casts a skill at a point on the ground (for skills that pick a location). | true — the cast started. false — the skill is unavailable or the server rejected it. |
dispel(skillId: Int): Boolean | Removes that buff from yourself — the same as clicking its icon. | true — the buff was removed. false — no such buff, or removal failed. |
If a target is passed explicitly, it is selected first and only then the cast goes out.
You can check a skill before casting via Skill — skill.ready means "off cooldown, not passive, not blocked", but it does not account for MP:
val nuke = bot.user.skill(1177)
if (nuke != null && nuke.ready && bot.user.mp > 100) bot.castAttackSkill(nuke.id, mob)A sitting character cannot cast — call stand() first.
Items
pet = true means "operate on the pet's inventory".
| Method | What it does | What it returns |
|---|---|---|
useItem(id: Int, pet: Boolean = false): Boolean | Uses an item. id is an item template id or the object id of a specific item. | true — the item was used. false — it isn't in the inventory or cannot be used. |
useItem(item: InventoryItem, pet: Boolean = false): Boolean | Uses a specific item from the inventory. | As above. |
useItemByType(itemId: Int, pet: Boolean = false): Boolean | Strictly by template id. | As above. |
useItemByOid(oid: Int, pet: Boolean = false): Boolean | Strictly by object id. | As above. |
dropItem(id: Int, count: Long): Boolean | Drops an item on the ground. id — template id or object id. | true — the item was dropped. false — it isn't in the inventory or cannot be dropped. |
dropItem(item: InventoryItem, count: Long): Boolean | Same for a specific item. | As above. |
dropItemByOid(oid: Int, count: Long): Boolean | Strictly by object id. | true — the item was dropped. false — no item with that oid in the inventory. |
destroyItem(id: Int, count: Long): Boolean | Destroys an item. id — template id or object id. | true — the item was destroyed. false — no such item, or it cannot be destroyed. |
destroyItem(item: InventoryItem, count: Long): Boolean | Same for a specific item. | As above. |
destroyItemByType(itemId: Int, count: Long): Boolean | Strictly by template id. | true — the item was destroyed. false — no item of that type in the inventory. |
destroyItemByOid(oid: Int, count: Long): Boolean | Strictly by object id. | true — the item was destroyed. false — no item with that oid in the inventory. |
crystallizeItem(id: Int): Boolean | Crystallizes an item. id — template id or object id. | true — crystallization started. false — no such item, or the character cannot crystallize (user.canCrystallize). |
crystallizeItem(item: InventoryItem): Boolean | Same for a specific item. | As above. |
crystallizeItemByOid(oid: Int): Boolean | Strictly by object id. | As above. |
transferItem(itemId: Int, count: Long, toPet: Boolean): Boolean | Moves an item between the character and the pet. toPet = true — character → pet, false — pet → character. | true — the transfer went through. false — no such item, no pet, or the item cannot be transferred. |
transferItem(item: InventoryItem, count: Long, toPet: Boolean): Boolean | Same for a specific item. | As above. |
craftItem(recipeId: Int): Boolean | Crafts an item from a recipe. | true — crafting started. false — no recipe, no materials or not enough MP. |
setAutoShots(itemId: Int, enabled: Boolean, pet: Boolean = false): Boolean | Turns automatic shots of that type on or off. pet = true — the pet's shots. | true — the setting was applied. false — no shots of that type in the inventory. |
setAutoShots(item: InventoryItem, enabled: Boolean, pet: Boolean = false): Boolean | Same, given an inventory item. | As above. |
count is a Long in every quantity-taking method.
Movement
A script has two ways to move, and they are very different.
| Method | What it does | What it returns |
|---|---|---|
moveToByGeo(x: Int, y: Int, z: Int, timeoutMs: Long = 15_000): Boolean | Walks the character to a point along the server's walkable geodata, going around obstacles. Blocks the script until the walk ends. | true — the character arrived. false — no path, stuck for good, stopMove() was called, the timeout expired, or moving is impossible (dead, sitting). |
moveToByGeo(target: Positioned, timeoutMs: Long = 15_000): Boolean | Same, but the destination is an entity or a drop (its coordinates are used). | As above. |
stopMove(): Boolean | Aborts the walk in progress — the running moveToByGeo returns false immediately. | Always true — the abort was sent. |
moveTo(x: Int, y: Int, z: Int, timeoutMs: Long = 8000): Boolean | Sends a single straight move to a point and waits for arrival. Does not go around obstacles. | true — the character reached the point (within ≈80 units on X/Y) before the timeout. false — it didn't: blocked, too far, or never moved. |
moveTo(target: Positioned, timeoutMs: Long = 8000): Boolean | Same, towards an entity or a drop. | As above. |
moveToNoWait(x: Int, y: Int, z: Int): Boolean | A straight move without waiting for arrival. | Always true — the move was sent. Check bot.user.x/y yourself to see whether the character got there. |
moveToNoWait(target: Positioned): Boolean | Same, towards an entity or a drop. | Always true. |
moveToByGeo — the way to actually travel
The command blocks: when it returns, the character has either arrived (true) or is no longer going anywhere (false).
timeoutMsis an upper bound, 15 seconds by default. For a long route raise it or pass0— no deadline.- Even without a deadline it cannot hang forever: if the character stops moving the route is replanned, and once the attempts run out the command returns
false. - Casting and immobilisation are waited out inside the command — no need to abort it because of them.
- The character's zone limits (working area, forbidden areas) do not constrain the route: you said "go there", so it goes.
- A new call preempts the previous walk instead of queueing behind it.
moveTo / moveToNoWait — one direct step
These send a single straight move. In the open, or for a short step, that's fast and sufficient; facing a wall the character stops and moveTo returns false on timeout. Arrival in moveTo is checked on X/Y only, height is ignored.
// walking somewhere far: better switch automation off and drop the deadline
bot.disableAutomation()
val ok = bot.moveToByGeo(82000, 148000, -3470, timeoutMs = 0)
bot.log(if (ok) "arrived" else "did not make it")Character state
| Method | What it does | What it returns |
|---|---|---|
sit(): Boolean | Sits down. | true — the character sat down. false — sitting isn't possible (in combat, dead) or the server refused. |
stand(): Boolean | Stands up. | true — the character stood up. false — standing up failed. |
dismissPet(): Boolean | Dismisses the pet. | true — the pet was dismissed. false — no pet, or dismissing failed. |
dismissSummon(): Boolean | Unsummons a summon or servitor. | true — the summon was released. false — no summon, or releasing failed. |
restart(): Boolean | Goes to the character selection screen and waits for it. | true — the selection screen appeared. false — it never did, or leaving isn't allowed. |
restartNoWait(): Boolean | Same, without waiting for the screen. | true — the command was sent. false — sending failed. |
selectCharacter(slot: Int): Boolean | Enters the world as the character in slot (zero-based). Only from the selection screen — from in-game, call restart() first. | true — the character entered the world. false — the client is not on the selection screen, the slot does not exist, or no reply arrived. |
goHome(restartType: RestartType = RestartType.TOWN): Boolean | Resurrects the character or returns it to the chosen point. | true — the respawn went through. false — the point is unavailable or the character is alive. |
RestartType values: TOWN · CLAN_HALL · CASTLE · FORTRESS · FLAG.
Switching characters is restart() followed by selectCharacter(n):
if (bot.restart() && bot.selectCharacter(2)) {
bot.log("Now playing ${bot.user.name}")
}After a successful switch the world is a different character: user, the inventory, the skills and the surroundings have nothing in common with the previous ones. Re-read bot.* from scratch and drop everything remembered before the switch (oids, targets, drops).
A sitting character cannot cast skills — if your script sits down to regenerate, remember to stand() before fighting.
NPC dialogs
| Method | What it does | What it returns |
|---|---|---|
openDialog(): Boolean | Opens a dialog with the current target. The target is not re-selected — it must already be chosen. | true — the NPC answered with a dialog. false — no target, or no dialog followed. |
openDialog(id: Int): Boolean | Selects an NPC and opens a dialog. id — template id (nearest living of that type) or object id. | true — the NPC answered with a dialog. false — the NPC wasn't found, or no dialog followed. |
openDialog(npc: Npc): Boolean | Same for a specific NPC. | As above. |
openDialogByOid(npcOid: Int): Boolean | Strictly by object id. | As above. |
selectDialog(option: String): Boolean | Picks a dialog entry by its caption — the text you see in the window. Case-insensitive; an exact match is tried first, then a substring. Waits for the server for about 2.5 seconds. | true — the entry was picked and a new dialog arrived. false — no entry with that caption, or no answer in time. |
selectDialog(number: Int): Boolean | Picks an entry by position: the first entry is 1, the second 2, and so on. Every clickable entry of the dialog counts, top to bottom. | true — the entry was picked. false — no such position in the dialog. |
sendBypass(cmd: String): Boolean | Sends a dialog command directly, without looking up an entry. | Always true — the command was sent. The answer arrives as a DialogReceived event. |
selectBoard(option: String): Boolean | Presses a community board button (a custom panel: buffer, teleport, shop) by its caption. Case-insensitive; an exact match is tried first, then a substring. | true — the button was pressed and the panel sent a new page. false — no button with that caption, or no answer in time. |
selectBoard(number: Int): Boolean | Same by position: the first button is 1. Every clickable entry of the page counts, top to bottom. | true — the button was pressed. false — no such position on the page. |
confirmDialog(msgId: Int, requestId: Int, accept: Boolean): Boolean | Answers a confirmation window. msgId and requestId come from the ConfirmDialogReceived event. | Always true — the answer was sent. |
The HTML of the current (or last) dialog is in bot.dialogText; an empty string means there was none.
if (bot.openDialog(30080)) {
bot.selectDialog("Teleport")
delay(300)
bot.selectDialog(1) // first entry of the list that opened
}Private stores
A player's store is not a dialog: the server sends its contents only when asked, so there is one command that does the whole trip.
| Method | What it does | What it returns |
|---|---|---|
openStore(seller: Char): Boolean | Walks to the seller and opens their store. Walking uses geodata, the same channel as moveByGeo. | true — the goods arrived and are in bot.storeItems. false — the seller is gone, you couldn't get there, or nothing came back. |
openStoreByOid(sellerOid: Int): Boolean | Same, strictly by object id. | As above. |
Both take walkTimeoutMs (15 s by default) for the walk and timeoutMs (5 s) for the answer.
The goods do not come back as the return value — read them from bot.storeItems right after a true. That list is a snapshot taken when the store opened, not live prices: the server says nothing when someone else buys something out from under you.
val seller = bot.chars.nearest { it.storeType == 1 } ?: return
if (bot.openStore(seller)) {
bot.storeItems
.filter { it.price < it.basePrice } // cheaper than par
.forEach { bot.log("${it.itemId}: ${it.price} vs ${it.basePrice}") }
}Community board
Custom panels (buffer, teleport, donate) are not NPC dialogs: they have their own window, their own markup in bot.boardText and their own selectBoard commands. The two don't mix — selectDialog cannot see a panel.
Panel buttons are looked up by caption. The server issues a fresh internal code for every button each time a page is rendered, so remembering one in a script is pointless — a minute later it belongs to someone else's page.
bot.sendBypass("_bbshome") // open the board; this command is permanent
delay(500)
bot.selectBoard("Buffer") // buff section
delay(500)
bot.selectBoard("Might")A long page arrives in several packets; selectBoard waits for all of them, so the next command already sees the complete markup. If a button needs text from an input field ($name at the end of its command in the markup), selectBoard won't fill it in — use sendBypass("<code> value") by hand.
Party
| Method | What it does | What it returns |
|---|---|---|
inviteParty(name: String, lootMode: LootMode = LootMode.FINDERS_KEEPERS): Boolean | Invites a player to the party with the chosen loot distribution mode. | Always true — the invitation was sent. Whether it was accepted shows up in the PartyMemberJoined event or in bot.party. |
leaveParty(): Boolean | Leaves the party. | true — the party was left. false — the character is not in a party. |
setPartyLeader(member: PartyMember): Boolean | Hands leadership to a party member. | true — the handover was sent. false — no member with that oid is visible. |
setPartyLeader(oid: Int): Boolean | Same, by the member's object id. | As above. |
setPartyLeaderByOid(oid: Int): Boolean | Same, explicit form. | As above. |
setPartyLeader(name: String): Boolean | Hands leadership by player name. | Always true — the handover was sent. |
Party members have no template id — the number in setPartyLeader(oid) is always treated as an object id (member.oid).
LootMode values: FINDERS_KEEPERS · RANDOM · RANDOM_SPOIL · BY_TURN · BY_TURN_SPOIL.
An invitation sent to you arrives as the PartyInviteReceived event; answer it with confirmDialog.
Chat
| Method | What it does | What it returns |
|---|---|---|
say(text: String, channel: Int = 0, target: String = ""): Boolean | Sends a chat message. target is the recipient's name, needed only for private messages. | Always true — the message was sent. |
Channel codes:
| Code | Channel | Code | Channel |
|---|---|---|---|
0 | normal (around you) | 8 | trade |
1 | shout | 9 | alliance |
2 | private (needs target) | 10 | announcement |
3 | party | 11 | boat |
4 | clan | 13 | battlefield |
5 | GM | 14 | command channel |
6 | petition (player) | 25 | friends |
7 | petition (GM) |
bot.say("ready") // around you
bot.say("on my way", channel = 3) // party
bot.say("hi", channel = 2, target = "Friend") // privateLoot
| Method | What it does | What it returns |
|---|---|---|
pickup(id: Int): Boolean | Picks up a drop. id is an itemId (the nearest such drop on the ground is used) or the drop's object id. | true — the item was picked up. false — no such drop, it belongs to someone else, the character is too far, or the inventory is full. |
pickup(drop: Drop): Boolean | Picks up a specific drop. | As above. |
pickupByOid(oid: Int): Boolean | Strictly by the drop's object id. | As above. |
The command does not walk the character to the drop — get there yourself. The server only allows pickup at close range (roughly 50–80 units):
val drop = bot.drops.nearest { it.isMine } ?: return
if (drop.distToSelf > 60) bot.moveToByGeo(drop)
bot.pickup(drop)Raw packet
A last resort, for when no typed command exists for the action. It does not replace or duplicate the commands above: there the game client builds the packet itself, and it is correct by definition.
| Method | What it does | What it returns |
|---|---|---|
sendPacket(hex: String, active: Boolean = false): Boolean | Sends a ready-made game packet — the bytes go to the server as they are. | true — the client confirmed the send. false — the packet did not go out, or the outcome is unknown. |
sendPacket(bytes: ByteArray, active: Boolean = false): Boolean | Same for a packet built in code. | As above. |
hex is the packet body: opcode in the first byte, without the length prefix (the client appends the length and the encryption itself). An extended packet is written whole, D0 and subcommand included. Spaces and commas are ignored, case does not matter.
bot.sendPacket("11 01") // cancel target
bot.sendPacket("D0 0D 00 …") // extended: D0 + subcommand + bodyactive = true means the action needs the client window to be in front, so the window will be brought forward. It says nothing about delivery — sending itself works in the background.
Three things worth knowing before you reach for this:
- Nobody validates the contents. A server usually drops the connection on malformed bytes.
trueis about the client, not the server. It means "the bytes went out", not "the action was accepted". Whether the server took it shows in the events and in the world state.- Retrying is on you. On
falsethe application does not resend a raw packet: for a non-idempotent command (buy, use an item) a second attempt means the action runs twice.
Raw traffic
A sniffer: packets exactly as they are on the wire, unparsed. Useful for reconnaissance — seeing what the client sends when you press a button, or catching a server packet that has no event of its own.
| Method / field | What it does | What it returns |
|---|---|---|
capturePackets(incoming: Boolean = false, outgoing: Boolean = false): Boolean | Turns capture on or off. Replaces the previous setting entirely; calling it with no arguments turns both directions off. | true — applied. false — character not found. |
packetsIn: Flow<Packet> | Server→client packets. | A flow; stays silent until capture is on. |
packetsOut: Flow<Packet> | Client→server packets — both the bot's commands and whatever the player did by hand. | A flow; stays silent until outgoing capture is on. |
Capture is off by default, and that is deliberate: in combat dozens of packets a second cross the wire, and pushing them into a script unasked buys nothing.
bot.capturePackets(incoming = true)
bot.packetsIn
.filter { it.opcode == 0x0B }
.collect { bot.log("${it.size} bytes: $it") }Capture is dropped on its own when the script ends — forgetting to turn it off is harmless.
What a Packet holds
| Field | What it is |
|---|---|
hex: String | The packet bytes as hex, upper case, no separators. |
bytes: ByteArray | The same bytes. |
opcode: Int | The first byte — the opcode. |
subOpcode: Int | The extended packet's subcommand (after D0). -1 if the packet is not extended. |
size: Int | Body length in bytes. |
byteAt(index: Int): Int | The byte at that position, or -1 past the end. |
The body is whole, opcode first and without the length prefix — exactly the form sendPacket accepts.
Outgoing needs an interceptor
packetsOut works differently from packetsIn: by default the client does not hand the application what it sends to the server at all. capturePackets(outgoing = true) raises that interceptor — the same one used for recording profiles (game_buff, return to spot).
The interceptor is shared, so it is leased rather than switched: turning your capture off will not cut a recording running alongside, and that recording finishing will not cut your flow.
Automation and profiles
A script can drive the built-in automation — the one you configure in the application.
| Method | What it does | What it returns |
|---|---|---|
enableAutomation(): Boolean | Turns built-in automation on. | Always true — the switch went through (if it was already on, nothing changes). |
disableAutomation(): Boolean | Turns built-in automation off. | Always true. |
loadZone(path: String): Boolean | Reads an .iz zone file (as saved by the application) and applies it to the character. | true — the file was read and the zones applied. false — the file is missing, unreadable or malformed. |
clearZone(): Boolean | Clears the character's zones. | Always true — the zones were cleared. |
loadConfig(path: String): Boolean | Reads a .json settings profile (as saved by the application) and applies it to the character. The profile name is taken from the file name. | true — the file was read and sent to be applied. false — the file could not be read. |
path is an ordinary filesystem path, absolute or relative to the application's working directory.
Output
| Method | What it does | What it returns |
|---|---|---|
log(text: String) | Writes a line to the script log. Not suspend, callable from anywhere. | Nothing (Unit). |