Draw ​
2D and 3D drawing primitives. Drawing only works inside frame callbacks (on_tick); calling these from event callbacks does nothing.
Colors ​
Colors are IM_COL32 packed uint32 in 0xAABBGGRR format (alpha in the high byte, red in the low byte).
local WHITE = 0xFFFFFFFF
local RED = 0xFF0000FF -- red in the low byte, FF alpha
local GREEN = 0xFF00FF00
local BLUE = 0xFFFF0000
local YELLOW = 0xFF00FFFFUse draw.color to build colors from RGBA components without hand-packing:
local muted = draw.color(180, 180, 180) -- alpha defaults to 255
local fade = draw.color(255, 255, 255, 80)Byte order gotcha
This format is ABGR, not ARGB. 0xFFFF0000 is solid blue, not red, most other engines and CSS use 0xAARRGGBB. If you're copying a hex color from elsewhere, the red and blue channels need to swap. draw.color(r, g, b, a) builds the right uint32 without the mental gymnastics.
2D primitives ​
| Function | Description |
|---|---|
draw.line(x1, y1, x2, y2, color, [thickness]) | Line between two points |
draw.rect(x1, y1, x2, y2, color, [thickness]) | Outlined rectangle |
draw.rect_filled(x1, y1, x2, y2, color) | Filled rectangle |
draw.circle(x, y, radius, color, [thickness]) | Outlined circle |
draw.circle_filled(x, y, radius, color) | Filled circle |
Text ​
Text functions accept an optional font name OR scale parameter. Pass a string for a font name, or a number for scale on the default font.
| Function | Description |
|---|---|
draw.text(x, y, text, color, [font_or_size], [size]) | Text with shadow |
draw.text_raw(x, y, text, color, [font_or_size], [size]) | Text without shadow |
draw.text3d(pos, text, color, [font_or_size], [size]) | Text anchored to a world position (vec3) |
draw.text(100, 100, "Hello", 0xFFFFFFFF) -- default font, scale 1.0
draw.text(100, 120, "Hello", 0xFFFFFFFF, 1.5) -- default font, scale 1.5
draw.text(100, 140, "Hello", 0xFFFFFFFF, "hud") -- custom font, scale 1.0
draw.text(100, 160, "Hello", 0xFFFFFFFF, "hud", 1.5) -- custom font, scale 1.5All text functions return nothing.
Fonts ​
Scripts can use custom fonts. Either pre-load fonts via fonts.lua or let users pick from system fonts via the font picker setting.
| Function | Returns | Description |
|---|---|---|
draw.set_font(name) | - | Set the per-script default font for subsequent draw.text calls. Pass nil or "" to reset to the overlay default |
draw.get_font() | string | Current per-script default font name |
draw.get_fonts() | table | Array of all loaded custom font names |
Font state is per-script. Calling draw.set_font("Arial") in Script A doesn't affect Script B.
If the font name isn't loaded yet, draw.set_font triggers dynamic loading. The engine searches in this order:
%APPDATA%/TsukiProject/Deadlock/fonts/C:\ProgramData\TsukiProject\Deadlock\fonts\C:\Windows\Fonts\
The first frame may render with the default font while the atlas rebuilds.
fonts.lua (optional pre-loading) ​
Pre-load fonts at specific sizes into the atlas at startup. Drop a fonts.lua next to your scripts. Not required; the font picker dynamically loads system fonts on demand.
-- %APPDATA%/TsukiProject/Deadlock/scripts/fonts.lua
return {
hud = { file = "roboto.ttf", size = 16 },
small = { file = "consolas.ttf", size = 10 },
big = { file = "inter.ttf", size = 24 },
}.ttf and .otf files go in %APPDATA%/TsukiProject/Deadlock/fonts/. The keys (hud, small, etc.) become the names you pass to draw.set_font.
Font priority ​
Highest precedence wins:
- Explicit font passed to
draw.text(..., font_name) - Per-script default set via
draw.set_font(name) - Overlay default font
3D primitives ​
3D primitives are reprojected per frame.
| Function | Description |
|---|---|
draw.line3d(p1, p2, color, [thickness]) | Line between two vec3 points |
draw.circle3d(center, radius, color, [thickness]) | Outlined circle in world space |
draw.circle3d_filled(center, radius, color) | Filled circle in world space |
draw.box3d(center, width, depth, height, color, [thickness]) | Axis-aligned box centered at center (vec3) with dimensions in world units along X (width), Y (depth), and Z (height) |
draw.sphere3d(center, radius, color, [segments]) | Wireframe sphere |
Projection and screen size ​
| Function | Returns | Description |
|---|---|---|
draw.world_to_screen(vec3) | vec2 | nil | Pixel position as a vec2 with .x and .y, or nil if behind the camera |
draw.screen_size() | vec2 | Screen dimensions as vec2 with .x (width) and .y (height) |
world_to_screen is also available as camera.world_to_screen. Same function.
Example ​
local WHITE = 0xFFFFFFFF
local RED = 0xFF3232FF
function on_tick()
for _, p in ipairs(get_players()) do
if p:is_enemy() and p:is_alive() then
local box = p:screen_box()
if box then
draw.rect(box.x, box.y, box.x + box.w, box.y + box.h, RED, 2)
draw.text(box.x, box.y - 14, p:hero_name(), WHITE)
end
end
end
end