Rendering
Epitaph exposes rendering helpers through the render API. These helpers are context-sensitive.
Render Context Rules
Section titled “Render Context Rules”- 2D operations must run inside
on_render_2d - 3D operations must run inside
on_render_3d
Epitaph tracks the active render event internally and will reject context-specific calls in the wrong callback.
Script Structure
Section titled “Script Structure”All callbacks are lambdas defined inside the script hash. Top-level def methods do not work.
script = { name: "My Script", description: "What it does.", category: "RENDER", on_load: lambda do # register settings here end,
on_render_2d: lambda do |_event| # 2D render code here end,
on_render_3d: lambda do |_event| # 3D render code here end}Wrong — top-level
defmethods are ignored:def on_render_2d(event) # does NOT work...end
render.color
Section titled “render.color”render.color(r, g, b, a) # => color objectAll four channels are required. Values are integers 0–255.
white = render.color(255, 255, 255, 255)dimmed = render.color(20, 20, 20, 70)render.fill
Section titled “render.fill”Draws a filled rectangle using absolute max coordinates (not width/height).
render.fill(x1, y1, x2, y2, color)render.border
Section titled “render.border”Draws a rectangle outline using x, y, width, height (not max coordinates).
render.border(x, y, width, height, color)render.text
Section titled “render.text”render.text(text, x, y, color, shadow)text— Stringx,y— Integer pixel positioncolor— color object fromrender.colorshadow— Boolean (optional, defaults tofalse)
render.text("Hello", 8, 8, white, true)render.measure_text
Section titled “render.measure_text”Returns the pixel width of the string using the default font.
width = render.measure_text(text) # => Floatrender.entity_screen_box
Section titled “render.entity_screen_box”Returns a screen-space box for the entity, or nil if off-screen or unavailable.
box = render.entity_screen_box(entity_or_id, padding) # => box or nilentity_or_id— entity table or Integer entity IDpadding— Float (extra margin, e.g.0.03)
Returns nil when the entity is off-screen or unavailable.
Box fields:
| Field | Type | Description |
|---|---|---|
on_screen | Boolean | Always true when non-nil |
x | Integer | Left edge (rounded) |
y | Integer | Top edge (rounded) |
width | Integer | Width (rounded) |
height | Integer | Height (rounded) |
min_x | Float | Left edge (precise) |
min_y | Float | Top edge (precise) |
max_x | Float | Right edge (precise) |
max_y | Float | Bottom edge (precise) |
Always check box.nil? and box.width > 0 before drawing.
3D Methods
Section titled “3D Methods”All 3D methods must run inside on_render_3d. The optional through_blocks argument disables depth testing (default false).
render.line_3d(x1, y1, z1, x2, y2, z2, color, through_blocks, line_width)render.box_3d(x1, y1, z1, x2, y2, z2, color, through_blocks, line_width)render.filled_box_3d(x1, y1, z1, x2, y2, z2, color, through_blocks)render.circle_3d(x, y, z, radius, color, through_blocks, line_width)through_blocks and line_width are optional. line_width defaults to 1.0.
Utility Methods
Section titled “Utility Methods”render.is_2d_context # => Booleanrender.is_3d_context # => Booleanrender.world_to_screen(x, y, z) projects a world position to screen space. Returns a table or nil:
pt = render.world_to_screen(entity.x, entity.y, entity.z)# pt.x, pt.y => Float screen coords# pt.depth => Float 0.0–1.0 depth value# pt.on_screen => Boolean (depth > 0 && depth < 1)Custom font rendering requires a size argument (font scale). Available font names: poppins, inter, arial, impact, bangers, jello.
render.font_text(font_name, text, x, y, size, color, shadow)render.measure_font_text(font_name, text, size) # => Float widthrender.font_height(font_name, size) # => Float heightrender.list_fonts # => Array of Stringrender.font_text("poppins", "Hello", 8.0, 8.0, 14.0, white, false)w = render.measure_font_text("poppins", "Hello", 14.0)2D Example — Ping HUD
Section titled “2D Example — Ping HUD”script = { name: "Ping Hud", description: "Shows FPS and ping", category: "RENDER",
on_render_2d: lambda do |_event| text = "FPS: #{client.get_fps} | Ping: #{client.get_ping}" white = render.color(255, 255, 255, 255) render.text(text, 8, 8, white, true) end}2D Example - Simple Arraylist Entry
Section titled “2D Example - Simple Arraylist Entry”` uby script = { name: “Arraylist Demo”, description: “Shows another module with its info suffix”, category: “RENDER”,
on_render_2d: lambda do |_event| feature = modules.get(“Aim Assist”) next unless feature && feature.is_enabled
info = feature.get_infolabel = info.nil? || info.empty? ? feature.get_name : "#{feature.get_name} #{info}"white = render.color(255, 255, 255, 255)render.text(label, 8, 8, white, true)end } `
2D Example — Player ESP
Section titled “2D Example — Player ESP”script = { name: "Player ESP", description: "2D player boxes.", category: "RENDER",
on_load: lambda do mod.register_double_setting("Max Distance", 64.0, 1.0, 256.0, 1.0) mod.register_boolean_setting("Draw Self", false) end,
on_render_2d: lambda do |_event| return unless player.is_present && world.is_present
max_distance = mod.get_setting_value("Max Distance") || 64.0 draw_self = mod.get_setting_value("Draw Self") || false outline = render.color(255, 255, 255, 255) fill = render.color(20, 20, 20, 70) text_color = render.color(255, 255, 255, 255) self_id = player.get_id
world.get_players.each do |entity| next if entity.nil? next if !draw_self && entity.id == self_id next unless entity.alive next if entity.removed next if entity.get_distance > max_distance
box = render.entity_screen_box(entity.id, 0.03) next if box.nil? || !box.on_screen next if box.width <= 0 || box.height <= 0
render.fill(box.min_x, box.min_y, box.max_x, box.max_y, fill) render.border(box.min_x, box.min_y, box.width, box.height, outline)
name = entity.name name_width = render.measure_text(name) render.text(name, (box.min_x + box.width / 2.0 - name_width / 2.0).round, (box.min_y - 10.0).round, text_color, true) end end}3D Example
Section titled “3D Example”script = { name: "Block Highlight", description: "Highlights the targeted block.", category: "RENDER",
on_render_3d: lambda do |_event| return unless targeting.is_block
block = targeting.get_block color = render.color(255, 64, 64, 180) render.box_3d(block.x, block.y, block.z, block.x + 1, block.y + 1, block.z + 1, color) end}