Your first script
From an empty file to a script with a menu, a saved setting and a reaction to the game - in about ten minutes.
You need nothing but a text editor. Every step below is something you can see working in the game before you move on to the next.
- Make the file. Open
C:\elysium\scripts- the folder is created the first time the cheat starts, and the open folder button in the Scripts tab opens it for you. Create a file calledhello.lua. The name is the script's name everywhere: in the list, in its saved settings, in its own folder. Put something in it.
hello.luaprint( "hello from lua" ) on( "frame", function( canvas ) canvas:text( 20, 20, "hello", color( 255, 255, 255 ) ) end )- Load it. Open the menu and go to the Scripts tab. Scripts are read when the cheat starts; a file added later shows up after rescan. Click the row of
helloto select it - the right half of the tab shows its controls, and the panel under the list shows its output:hello from lua. The wordhellois now drawn in the top left corner of your screen. - Change it, reload it. Edit the text in the file, save, and press reload with
helloselected. The state of the script is thrown away and the file is read again. That loop - save, reload, look - is the whole workflow.
#What those lines did
print( ... ) wrote to the output panel in the Scripts tab (and the console). The game has no console of its own that a script could write to, so this panel is where a script talks to you.
on( "frame", handler ) told the cheat: call this function every time a frame is drawn. frame is one of a dozen events. The handler receives a canvas - the thing you draw with. It is only valid while the handler runs; do not keep it.
canvas:text( x, y, text, color ) draws a line of text. color( 255, 255, 255 ) makes a white colour; the fourth number, alpha, is optional.
#Add a menu
Controls live in groups, and groups show up in the right half of the Scripts tab. Replace the file's content with this:
local group = ui.group( "hello" )
local enabled = group:toggle( "enabled", true )
local size = group:slider( "text size", 10, 40, 18, { step = 1 } )
local tint = group:color( "colour", color( 168, 85, 247 ) )
-- A font is built once per size and then reused.
local fonts = { }
local function font_for( px )
if fonts[ px ] == nil then
fonts[ px ] = render.font( "Segoe UI", px ) or false
end
return fonts[ px ]
end
on( "frame", function( canvas )
if not enabled:get( ) then
return
end
local options = { font = font_for( size:get( ) ) or nil, shadow = true }
canvas:text( 20, 20, "hello from lua", tint:get( ), options )
end )Reload. The right half now has a toggle, a slider and a colour picker. Move them - the text follows at once. Reload the script again, or restart the game: the controls come back with the values you left. They are written to C:\elysium\scripts\.data\hello.values.json a moment after you change something, keyed by the group's title and the control's label.
Three ideas are in that file:
- A control is an object.
group:toggle( ... )returns it, andenabled:get( )reads it whenever you need the value. You never wait for a callback unless you want one (on_change). render.font( "Segoe UI", px )answersnilif the face is missing, so the result is stored asfalseand never looked up again.font_for( ... ) or nilturns thatfalseback into "no font, use the default".- The options table at the end of
canvas:textis how a call takes more than its fixed arguments. Every optional table in the API works like this.
#React to the game
The game announces things - a player was hurt, a round started, a bomb was planted - and a script can listen to any of them by putting game: in front of the event's name. Add this to the end of the file:
on( "game:player_hurt", function( ev )
local victim = ev:player( "userid" )
if victim then
ui.notify( victim:name( ) .. " took " .. ev:int( "dmg_health" ) .. " damage" )
end
end )The ev object reads the event's fields by name: ev:int( "dmg_health" ) is a number, ev:player( "userid" ) is the player the field points to, as an entity. ui.notify puts a toast in the corner of the screen. Hurt someone in a game and watch it appear.
#When something goes wrong
Break the script on purpose: change canvas:text in the first handler to canvas:txt, save and reload. The script's row turns red and says error, and the output panel shows what happened and where:
[hello] error in frame: hello.lua:12: attempt to call a nil value (method 'txt')
stack traceback:
hello.lua:12: in function <hello.lua:10>A script that raises an error is stopped - all of it, not only the handler - and stays stopped until you reload it. This is deliberate: a handler that fails once per frame would fill the output with thousands of copies of the same error. Fix the typo, press reload, and it runs again.
The other thing that stops a script is time. A handler that takes longer than about 30 ms is interrupted with script ran past its time budget. The game waits while a script runs, so a handler that needs more than a millisecond or two every frame is worth looking at. How scripts run has the details.
#Where to go from here
- How scripts run - the folders, the lifecycle, threads, and what sandboxed and trusted mean.
- Events & timers - the full list of things a script can listen to.
- Drawing and Menus & controls - the two modules you will use most.
- Cookbook - short solutions to common jobs, ready to paste.