Skip to content

Powerup ESP

Color-coded ESP labels for every pickup entity in range: powerups from bridge spawn points (survival, casting, movement, gun), permanent stat modifiers from breakable props (hp, wp, cd, spirit, firerate, ammo), gold boxes, and health pickups. Each pickup category has a distinct color.

What this shows

  • One unified loop for all four pickup types. powerup, pickup_modifier, pickup_gold, and pickup_health get read in one pass via get_world_entities(type). The pickup_name field disambiguates.
  • The active filter is doing real work. Each powerup spawn point has four entities stacked at the same position (one per buff variant). Without the active filter the script would draw four overlapping labels at every powerup location.
  • pickup_name drives the label and color. A small get_label_and_color helper substring-matches the pickup name to a human-readable label and a category color. Adding a new pickup type means adding one if name:find(...) branch in get_label_and_color.
  • Distance label. A faint distance readout below each pickup label so you can quickly tell what's nearby versus far away without parsing world geometry.

Script

lua
-- powerup_esp.lua
-- Shows active powerup and pickup entities with their type labels.

local colors = {
    survival  = draw.color(90, 210, 170),
    casting   = draw.color(180, 100, 255),
    movement  = draw.color(100, 200, 255),
    gun       = draw.color(255, 160, 50),
    hp        = draw.color(50, 255, 50),
    wp        = draw.color(200, 200, 255),
    cd        = draw.color(255, 255, 100),
    spirit    = draw.color(200, 100, 255),
    firerate  = draw.color(255, 100, 100),
    ammo      = draw.color(255, 180, 50),
    gold      = draw.color(255, 215, 0),
    health    = draw.color(50, 255, 100),
    fallback  = draw.color(200, 200, 200),
    dist      = draw.color(160, 160, 160),
}

local function get_label_and_color(name)
    if not name or name == "" then return "PICKUP", colors.fallback end
    name = name:lower()
    if name:find("survival") then return "SURVIVAL", colors.survival end
    if name:find("casting") then return "CASTING", colors.casting end
    if name:find("movement") then return "MOVEMENT", colors.movement end
    if name:find("gun") then return "GUN", colors.gun end
    if name:find("firerate") then return "FIRE RATE", colors.firerate end
    if name:find("ammo") then return "AMMO", colors.ammo end
    if name:find("spirit") then return "SPIRIT", colors.spirit end
    if name:find("wp_") then return "WEAPON", colors.wp end
    if name:find("hp_") then return "HEALTH+", colors.hp end
    if name:find("cd_") then return "COOLDOWN", colors.cd end
    if name:find("gold") then return "GOLD", colors.gold end
    if name:find("health") then return "HEALTH", colors.health end
    return name:gsub("_pickup.*", ""):upper(), colors.fallback
end

function on_tick()
    local types = {"powerup", "pickup_modifier", "pickup_gold", "pickup_health"}
    for _, t in ipairs(types) do
        local ents = get_world_entities(t)
        for _, e in ipairs(ents) do
            if e.active and e.distance < 200 then
                local sp = draw.world_to_screen(vec3(e.x, e.y, e.z))
                if sp then
                    local label, color = get_label_and_color(e.pickup_name)
                    draw.text(sp.x, sp.y, label, color)
                    draw.text(sp.x, sp.y + 14, string.format("%.0fm", e.distance), colors.dist)
                end
            end
        end
    end
end

Notes

pickup_name is absent when empty, so e.pickup_name can be nil. This script only reads it for active entities, and get_label_and_color guards on nil. Add your own nil check if you adapt the loop to other entity types.

The 200 m distance gate keeps the screen clean; pickups across the map don't get labeled. Lower it if you only want immediate surroundings, or remove the check entirely for full-map awareness during pre-fight planning.

get_label_and_color falls back to a generic "PICKUP" label for anything it doesn't recognize. If new pickup classes are added in a future patch, the script keeps working. Those pickups render with the fallback label until a matching case is added. The fallback also strips _pickup.* from the unrecognized name and uppercases it, which usually produces something readable.

To change colors or add categories, edit the colors table at the top and append a new if name:find(...) branch in get_label_and_color. The render loop never needs to change.