HOOZiDocs
Skip to content

esp ​

ESP element registration. Each ESP element has 4 independent palettes (one per scenario: INV / VIS / DWN / ALLY); the system picks one automatically per player state. Scripts read esp.scenario for the current preview scenario, use el:set_color(value, scenario) to change a single scenario, or el.colors = {c0,c1,c2,c3} to batch-set all four in one line.


esp.add(id, fn, opts?) → ESPElement ​

Parameters

  • id : string — unique element identifier
  • fn : function(p : PlayerEntity, ctx : table) — invoked per player, return value is polymorphic:
fn returnsBehavior
stringtext element
number 0..1bar element
table {text=, text_color=, bar=, bar_color=}mixed
nil (already drawn via ctx.dl)free drawing
nil (nothing drawn)the player is not displayed
  • opts? — optional table
    • label : string — default = id

    • colors : table — 4-element {c_inv, c_vis, c_dwn, c_ally}, each = {r,g,b,a}

    • dist : table — {min_m, max_m}

    • settings : function(group : Groupbox) — callback for adding extra controls to this element

    • kind : string — "text" (default) / "bar" / "mixed" / "free", controls slot-drag behavior:

      • "text" → 6 slots (top / top-left / bottom-left / top-right / bottom-right / bottom)
      • "bar" → 4 slots (top / middle-left / middle-right / bottom)
      • "mixed" → 4 bar slots (strictest constraint; fn returns text + bar simultaneously)
      • "free" → not registered in the slot UI (no drag handle); user fully draws via ctx.dl / draw.*; scenarios / dist / cleanup still apply; fn return value is arbitrary (the slot system does not consume it).

      Must match the fn return type: "text" pairs with string / table.text; "bar" pairs with number / table.bar; "mixed" pairs with table {text=, bar=}; "free" does not consume the fn return value (user draws). Unrecognized values trigger a LOG_WARN and fall back to "text".

ctx fields:

FieldTypeDescription
ctx.scenarioint0..3, determined by the system from player state
ctx.box{min=Vec2, max=Vec2}screen bounding box
ctx.distancenumberdistance (meters)
ctx.dlDrawListcurrent frame's foreground draw list (required when kind="free")

esp.find(id) → ESPElement | nil ​

esp.remove(id) ​

esp.elements : map<id, ESPElement> (iterable via pairs) ​

esp.scenario : int (read-only) ​

esp.INV / esp.VIS / esp.DWN / esp.ALLY = 0 / 1 / 2 / 3 ​


ESPElement Methods / Fields ​

el:get_color(scenario?) → table<r,g,b,a> / el:set_color(value, scenario?) ​

Omitting scenario = current scenario.

el:get_enabled(scenario?) → bool / el:set_enabled(value, scenario?) ​

el.colors = {c0, c1, c2, c3} ​

Set all 4 scenario colors at once.

This table is 1-based while esp.INV/VIS/DWN/ALLY are 0..3, so the two do not mix. el.colors[esp.VIS] gives you the INV slot and el.colors[esp.INV] is plain nil. To pick a color by scenario constant always go through el:get_color(esp.VIS); when writing the whole table, use positional order {[1]=inv, [2]=vis, [3]=dwn, [4]=ally} and never index it with the constants.

el.enableds = {b0, b1, b2, b3} ​

Set all 4 scenario enabled flags at once.

el.dist : table = {min_m, max_m} ​

el.label : string ​


Example ​

lua
-- Text element: return a string
esp.add("tag", function(p, ctx)
    if p.is_teammate then return "ALLY" end
    if p.is_visible  then return "VIS"  end
    return "INV"
end, {
    label  = "Tag",
    kind   = "text",
    colors = {
        { 0.85, 0.85, 0.85, 1.0 },  -- INV
        { 1.00, 0.35, 0.35, 1.0 },  -- VIS
        { 1.00, 0.80, 0.20, 1.0 },  -- DWN
        { 0.35, 0.85, 1.00, 1.0 },  -- ALLY
    },
    dist = { 5.0, 300.0 },
})

-- Bar element: return 0..1
esp.add("hp_bar", function(p, ctx)
    local total = (p.health or 0) + (p.shield or 0)
    return total / 200.0
end, { kind = "bar" })

-- Free drawing: draw via ctx.dl, fn returns nil
esp.add("crosshair", function(p, ctx)
    local cx, cy = (ctx.box.min.x + ctx.box.max.x) * 0.5, ctx.box.min.y - 10
    draw.line(cx - 5, cy, cx + 5, cy, draw.rgba(1, 0, 0, 1))
    return nil
end, { kind = "free" })