Script Settings
You can add user-configurable options to your script in two ways:
settings = { ... }declaration (recommended for most scripts): a table that is read when the script loads. It shows inline under your script in the menu, is saved automatically to a per-script JSON config, and is restored on the next launch.ui.*widget API (for advanced or dynamic UIs): you create widgets in code, with custom tabs, buttons with callbacks, and layouts you can change at runtime. See UI.
settings declaration
name = "My Script"
id = hero_id.any
settings = {
{ key = "enabled", type = "bool", default = true, label = "Enabled" },
{ key = "speed", type = "float", default = 1.0, min = 0.1, max = 10.0, label = "Speed" },
{ key = "count", type = "int", default = 5, min = 1, max = 20, label = "Count" },
{ key = "mode", type = "combo", default = 0, label = "Mode",
options = { "Off", "Conservative", "Aggressive" } },
{ key = "hotkey", type = "keybind", default = 0, label = "Hotkey" },
{ key = "slot", type = "item_slot", default = 0, label = "Item Slot" },
{ key = "font", type = "font", default = "", label = "Draw Font" },
{ key = "tint", type = "color", default = {r=255, g=80, b=80, a=255}, label = "Tint" },
}Each entry is a table with these keys:
| Key | Required | Description |
|---|---|---|
key | yes | Key the value is saved under. Must be unique within the script |
type | yes | One of: bool, int, float, combo, keybind, item_slot, font, color |
default | yes | Default value when the user hasn't set one. For int, combo, keybind and item_slot it must be a whole number: 5 and 5.0 both work, 2.5 is an error (the script does not load and the console says why) |
label | yes | Display label in the menu |
min, max | for int/float | Slider bounds. Default 0 and 100 |
options | for combo | Array of strings shown in the dropdown |
depends_on | optional | Key of another setting that controls this one's visibility |
depends_value | optional | Value the controlling setting must have for this one to show |
tooltip | optional | Help text shown when you hover the setting in the menu. Example: { key = "speed", type = "float", default = 1.0, min = 0.1, max = 10.0, label = "Speed", tooltip = "Controls how fast the script reacts" } |
Setting types
| Type | Backing type | Notes |
|---|---|---|
bool | boolean | Renders as a checkbox |
int | integer | Renders as a slider; needs min and max |
float | number | Same as int but for fractional values |
combo | integer | Dropdown index (0-based); needs options array |
keybind | integer | VK code; renders as a "click to bind" picker |
item_slot | integer | Item slot 0-3; renders as a dropdown of Slot 1 to Slot 4. Pass it to item_slot_to_key, or add 4 to get slot.item1 to slot.item4 |
font | string | Font name; renders as a font picker with system font search |
color | RGBA table | {r, g, b, a} with components 0-255; renders as an inline swatch that opens a full RGBA picker on click. default must be a four-key table. A fraction is rounded and anything outside 0-255 is clamped |
Conditional visibility
Hide a setting unless another one has a specific value:
settings = {
{ key = "auto_dispel", type = "bool", default = false, label = "Auto Dispel" },
{ key = "dispel_threshold", type = "float", default = 2.0,
min = 0.5, max = 5.0, label = "Threshold (seconds)",
depends_on = "auto_dispel", depends_value = 1 },
}For booleans, depends_value = 1 (or true) means the controlling setting must be on, and 0 (or false) means it must be off. For combo, it's the option index (0-based). The value must be a whole number or a boolean.
config namespace
Read and write your script's persisted setting values at runtime.
| Function | Returns | Description |
|---|---|---|
config.get_bool(key, default) | boolean | Read a bool setting |
config.get_int(key, default) | integer | Read an int, combo, keybind, or item_slot |
config.get_float(key, default) | number | Read a float setting |
config.get_string(key, default) | string | Read a font (or any other string-typed) setting |
config.get_color(key, default?) | RGBA table | Read a color setting; returns {r, g, b, a} with components 0-255. Defaults to white ({r=255,g=255,b=255,a=255}) when the key is absent and no default is provided |
config.set_bool(key, value) | - | Write a boolean. Saved to disk automatically (see below) |
config.set_int(key, value) | - | Write an integer. Saved to disk automatically (see below) |
config.set_float(key, value) | - | Write a float. Saved to disk automatically (see below) |
config.set_string(key, value) | - | Write a string. Saved to disk automatically (see below) |
config.set_color(key, {r,g,b,a}) | - | Write a color. Saved to disk automatically (see below) |
Each script's values are saved in its own JSON file. All setting types (bool, int, float, combo, keybind, item_slot, font, color) are saved. A change the user makes in the menu is written right away.
After a config.set_* call, config.get_* returns the new value immediately. The file is written at most once every 5 seconds, so you can set a value every tick. It is also written when the script is switched off, stops on an error, is reloaded, or TSUKI closes. The _of variants below work the same way.
Reading settings
function on_tick()
if not config.get_bool("enabled", true) then return end
local speed = config.get_float("speed", 1.0)
local mode = config.get_int("mode", 0)
local font = config.get_string("font", "")
-- ...
endThe default is returned when the key isn't in the config yet. Match it to the value in your settings declaration.
Read settings inside on_tick or a callback, not at the top level of the script. Every time a script loads, its top-level code runs before its saved config is loaded, so config.get_* there returns the default you pass, and a config.set_* there can be lost.
Writing settings
Use this when your script needs to change its own settings in response to events:
function on_tick()
if input.is_key_pressed(0x70) then -- F1
local was = config.get_bool("enabled", true)
config.set_bool("enabled", not was)
toast(not was and "ON" or "OFF")
end
endEach script has its own JSON file, so writing enabled in Script A doesn't touch Script B's enabled.
Cross-script config
Use the _of variants to read or write the saved settings of another loaded script. The first argument is the target script's display name: its script_info.name, else its top-level name, else its file name without .lua.
| Function | Returns | Description |
|---|---|---|
config.get_bool_of(script, key, default?) | boolean | Read a boolean from another script's config |
config.set_bool_of(script, key, value) | - | Write a boolean to another script's config and persist it |
config.get_int_of(script, key, default?) | integer | Read an integer from another script's config |
config.set_int_of(script, key, value) | - | Write an integer to another script's config and persist it |
config.get_float_of(script, key, default?) | number | Read a float from another script's config |
config.set_float_of(script, key, value) | - | Write a float to another script's config and persist it |
config.get_string_of(script, key, default?) | string | Read a string from another script's config |
config.set_string_of(script, key, value) | - | Write a string to another script's config and persist it |
There is no color_of variant.
- If the target script is not loaded,
get_*_ofreturns the default value (or the type's zero value when no default is given). It does not raise an error. - Writes only take effect when the target script is loaded. If it is not loaded, the write does nothing, so load the script first.
- Passing your own script's name is the same as calling plain
config.get_*/config.set_*.
-- A "coordinator" script that reads another script's toggle
function on_tick()
local hook_enabled = config.get_bool_of("Hook", "enabled", false)
if hook_enabled then
-- do something in response to the Hook script being active
end
end-- Disable the Dagger script's aim from a keybind in this script
function on_tick()
if input.is_key_pressed(0x70) then -- F1
local cur = config.get_bool_of("Dagger", "aim_enabled", true)
config.set_bool_of("Dagger", "aim_enabled", not cur)
end
endColor picker
The color type shows a swatch in the menu. Click it to open an RGBA picker with an alpha slider and hex and RGB entry. The value is saved like every other setting.
The default value is a table with r, g, b and a fields, each 0-255. A missing field reads as 255, so a list such as {255, 0, 0, 255} gives white. Name the fields:
settings = {
{ key = "alert_color", label = "Alert Color", type = "color",
default = {r = 255, g = 0, b = 0, a = 255} },
}Read the current value with config.get_color. Write a new one with config.set_color:
local c = config.get_color("alert_color")
-- c.r, c.g, c.b, c.a, all 0-255
config.set_color("alert_color", {r = 0, g = 255, b = 0, a = 255})You can pass the returned values straight to draw.color(c.r, c.g, c.b, c.a) from the Draw namespace. This complete script uses a color picker to draw 3D boxes around enemies:
-- Color Picker Test
-- Draws 3D boxes on enemies using color picker
script_info = {
name = "Color Picker Test",
description = "3D box ESP with color picker",
}
settings = {
{ key = "box_color", label = "Box Color", type = "color", default = {r=255, g=50, b=50, a=255} },
}
function on_tick()
local c = config.get_color("box_color")
local col = draw.color(c.r, c.g, c.b, c.a)
for _, p in ipairs(get_players()) do
if p:is_enemy() and p:is_alive() then
local pos = p:get_position()
local head = p:get_head_world()
local height = head.z - pos.z + 14
local width = 30
draw.box3d(vec3(pos.x, pos.y, pos.z + height * 0.5), width, width, height, col, 1.5)
end
end
endChanging the swatch in the menu updates the box color on the next tick.
Top-level globals
The globals TSUKI reads from the top level of a script:
| Global | Type | Description |
|---|---|---|
name | string | Display name shown in the scripts list. It can also go inside script_info |
id | integer | Which hero the script runs on. Use hero_id.any to run on any hero |
settings | table | Setting declarations as above |
script_info | table | Name, description, flags and icon |
Everything else, such as description, needs_all_particles and needs_internal, is only read from inside script_info. At the top level it is ignored. See Script Structure.
You can also define these callbacks: on_tick, is_enabled, on_kill, on_modifier_added, on_modifier_removed, on_ability_start, on_ability_channel, on_ability_cast, on_item_used, on_shot, on_particle_create, on_particle_destroy, on_sound_event, on_entity_added, on_entity_removed, on_disable and on_unload. See Events.