Drawing
Call aqua.draw functions inside on_draw(). A drawing lasts one frame, so
draw it again every frame.
Positions are screen pixels from the top-left corner. Colors are
{ r, g, b, a } from 0 to 1; alpha is optional. Options go in a table at the
end.
local WHITE = { 1, 1, 1 }local PANEL = { 0.05, 0.05, 0.08, 0.9 }
function on_draw() aqua.draw.rect(20, 20, 180, 36, PANEL, { rounding = 6 }) aqua.draw.text(32, 28, "Hello from Lua", WHITE, { size = 16 })endYou can also pass a color as separate numbers (1, 0, 0, 1). If a number
follows it, include the alpha, or that number is read as the alpha.
Shapes
Section titled “Shapes”| Function | Draws | Options |
|---|---|---|
rect(x, y, w, h, color, [opts]) |
Filled rectangle | rounding, clip |
rect_outline(x, y, w, h, color, [opts]) |
Rectangle border | rounding, thickness, clip |
rect_gradient(x, y, w, h, tl, tr, br, bl, [opts]) |
Rectangle blending four corner colors | rounding, clip |
line(x1, y1, x2, y2, color, [opts]) |
Line | thickness, clip |
circle(x, y, radius, color, [opts]) |
Circle outline | thickness, segments, clip |
arc(x, y, radius, startDeg, endDeg, color, [opts]) |
Part of a circle outline | thickness, segments, clip |
polyline(points, color, [opts]) |
Line through a list of points | thickness, closed, clip |
bar(x, y, w, h, fraction, background, fill, [opts]) |
Bar filled from 0 to 1 | rounding, vertical, clip |
| Option | Meaning |
|---|---|
rounding |
Corner radius in pixels. Default 0 |
thickness |
Line width in pixels. Default 1 |
segments |
Straight pieces per curve. Default 48 |
closed |
Join the last point back to the first |
vertical |
Fill from the bottom up |
clip |
{ x, y, w, h }: draw only inside this rectangle |
A number instead of the table sets thickness (rounding for rect).
Arc angles are degrees: 0 points right and 90 points down.
local WHITE = { 1, 1, 1 }local RED = { 1, 0.2, 0.2 }local GREEN = { 0.2, 0.9, 0.3 }local DARK = { 0.1, 0.1, 0.1, 0.9 }local BLUE = { 0.3, 0.55, 1, 1 }local CLEAR = { 0.3, 0.55, 1, 0 }
function on_draw() aqua.draw.rect(20, 20, 120, 60, DARK, { rounding = 4 }) aqua.draw.rect_outline(20, 100, 120, 60, WHITE, { thickness = 2 }) aqua.draw.line(20, 180, 140, 220, RED, 2)
-- Centre (80, 280), radius 40 aqua.draw.circle(80, 280, 40, WHITE, 2)
-- Top half: from pointing left (180) to pointing right (360) aqua.draw.arc(80, 380, 40, 180, 360, WHITE, 2)
-- Points as { x, y } pairs, or a flat list: { 10, 400, 50, 470, 90, 410 } aqua.draw.polyline({ { 10, 400 }, { 50, 470 }, { 90, 410 } }, RED, { closed = true })
-- 75% full aqua.draw.bar(20, 500, 120, 6, 0.75, DARK, GREEN)
-- Corners: top-left, top-right, bottom-right, bottom-left aqua.draw.rect_gradient(20, 520, 200, 40, BLUE, CLEAR, CLEAR, BLUE)end| Function | Does |
|---|---|
text(x, y, str, color, [opts]) |
Draws text. x, y is the top-left of the line |
text_width(str, [size], [font]) |
Width in pixels |
text_metrics([size], [font]) |
lineHeight, ascent, sizePx |
| Option | Meaning |
|---|---|
size |
Pixels. Snaps to the nearest available size from 10 to 32 |
align |
"left", "center" or "right"; x becomes that point of the text |
shadow |
true for a drop shadow |
outline |
true for a black outline, or a color |
font |
Handle from load_font |
clip |
{ x, y, w, h }: draw only inside this rectangle |
local WHITE = { 1, 1, 1 }
function on_draw() local screenW = aqua.draw.screen_size()
aqua.draw.text(20, 20, "hello", WHITE, { size = 14 }) aqua.draw.text(screenW / 2, 40, "centered", WHITE, { size = 18, align = "center", outline = true })endTo fit a box around text, measure the height with text_metrics, not size:
a line is taller than its font size.
local SIZE, PAD = 14, 6
function on_draw() local w = aqua.draw.text_width("aqua!v2", SIZE) local h = aqua.draw.text_metrics(SIZE)
aqua.draw.rect(20, 20, w + PAD * 2, h + PAD * 2, { 0, 0, 0, 0.8 }, 4) aqua.draw.text(20 + PAD, 20 + PAD, "aqua!v2", { 1, 1, 1 }, { size = SIZE })endload_font(path) loads a .ttf or .otf from your script folder and returns
a handle, or nil if the file is missing or not a font.
local font = aqua.draw.load_font("Roboto-Medium.ttf") -- once, outside on_draw
function on_draw() aqua.draw.text(20, 20, "aqua!v2", { 1, 1, 1 }, { size = 13, font = font })end- Pass the same handle to
text_widthto measure with that font. load_font(path, { hinting = "none" })gives softer letters that keep their original shape.- Characters the font lacks are drawn with the default font.
World to screen
Section titled “World to screen”| Function | Returns |
|---|---|
world_to_screen(point) or (x, y, z) |
sx, sy, visible |
world_to_screen_many(points) |
{ x, y, visible } per point |
screen_size() |
width, height |
visible is false for points behind the camera.
local WHITE = { 1, 1, 1 }
function on_draw() for _, player in ipairs(aqua.get_players()) do if player.alive and player.origin then local x, y, visible = aqua.draw.world_to_screen(player.origin) if visible then aqua.draw.text(x, y, player.name, WHITE, { size = 12, align = "center" }) end end endendSkeleton:
local ORANGE = { 1, 0.5, 0.2, 0.9 }local edges = aqua.skeleton.edges() -- pairs of bone numbers to connect
function on_draw() for _, player in ipairs(aqua.get_players()) do if player.bones then local screen = aqua.draw.world_to_screen_many(player.bones) for _, edge in ipairs(edges) do local a, b = screen[edge[1]], screen[edge[2]] if a and b and a.visible and b.visible then aqua.draw.line(a.x, a.y, b.x, b.y, ORANGE, 1.5) end end end endendImages
Section titled “Images”| Function | Draws |
|---|---|
icon(x, y, w, h, name, [color], [opts]) |
Flat weapon icon, as in ESP: "ak47" or a def index |
weapon(x, y, w, h, defIndex, [paintId | opts]) |
Inventory picture of a weapon |
econ(x, y, w, h, key, [color], [opts]) |
Inventory picture by key |
image(x, y, w, h, path, [color], [opts]) |
PNG from your script folder (png is the same function) |
Inventory pictures load in the background and may be missing for the first
frames. Pictures keep their shape inside the box; letterbox = false
stretches them.
aqua.draw.icon(20, 20, 48, 24, "ak47")aqua.draw.weapon(20, 60, 120, 90, 7) -- AK-47aqua.draw.weapon(150, 60, 120, 90, 7, { paint = 44, wear = 0.5 }) -- with a skinaqua.draw.image(20, 160, 64, 64, "img/logo.png")weapon options: paint, wear (0 to 1), letterbox, color, clip.
econ keys:
| Builder | Picture |
|---|---|
aqua.econ_key_base(weapon) |
Weapon without a skin |
aqua.econ_key_skin(weapon, paint, [wear]) |
Weapon with a skin; paint is an id or a name like "asiimov" |
aqua.econ_key_mat(path) |
Sticker, agent or music kit by image path |
aqua.draw.econ(20, 20, 120, 90, aqua.econ_key_skin("weapon_ak47", "asiimov"))aqua.draw.econ(150, 20, 80, 80, aqua.econ_key_mat("econ/stickers/default/stickers1"))aqua.econ_ready(key) is true once loaded; aqua.econ_miss(key) is true
if it never will be.
PNG paths are relative to your script folder and cannot leave it. JPEG is not
supported. image options:
| Option | Meaning |
|---|---|
letterbox |
false stretches the image |
src |
{ x, y, w, h }: draw only this part of the image (sprite sheets) |
menu_attach |
x, y are relative to the aqua menu and move with it |
clip |
{ x, y, w, h }: draw only inside this rectangle |
| Function | Does |
|---|---|
aqua.image_ready(path) |
Loads the image; true once loaded |
aqua.image_size(path) |
width, height, or nil |
aqua.image_miss(path) |
true if it failed to load |
aqua.image_unload(path) |
Reloads the file on the next draw |
aqua.image_delete(path) |
Unloads and deletes the PNG file |
Images over 512 pixels on a side are shrunk; src uses the shrunk size from
image_size.