Skip to content

Example: ESP with Dependencies#

A walkthrough of examples/enemy_esp_with_deps.cpp — an ESP plugin that depends on the math helpers library.

Full Source#

#include <xenon/SDK.hpp>
#include <xenon/libs/math_helpers.hpp>
using namespace xenon;

// ── Configuration ────────────────────────────────────────
static bool g_showHealthBars = true;
static bool g_showDistance = true;
static bool g_showSnaplines = false;
static bool g_visibleOnly = false;
static float g_maxDistance = 100.f;
static float g_pulseTimer = 0.f;

// ── Plugin Info (with dependency) ────────────────────────
XENON_PLUGIN_INFO_DEPS(
    "VisualsWithDeps",
    "Enemy ESP (with deps)",
    "Xenon",
    "ESP using math_helpers library",
    "1.0",
    0,
    PluginFlags::HasOverlay | PluginFlags::HasMenu,
    "math_helpers\0"
)

// ── Load / Unload ────────────────────────────────────────
extern "C" void on_load()
{
    g_showHealthBars = Config::GetBool("showHealthBars", true);
    g_showDistance = Config::GetBool("showDistance", true);
    g_showSnaplines = Config::GetBool("showSnaplines", false);
    g_visibleOnly = Config::GetBool("visibleOnly", false);
    g_maxDistance = Config::GetFloat("maxDistance", 100.f);
    Log("Enemy ESP (with deps) loaded!");
}

extern "C" void on_unload()
{
    Config::SetBool("showHealthBars", g_showHealthBars);
    Config::SetBool("showDistance", g_showDistance);
    Config::SetBool("showSnaplines", g_showSnaplines);
    Config::SetBool("visibleOnly", g_visibleOnly);
    Config::SetFloat("maxDistance", g_maxDistance);
    Config::Save();
}

// ── Frame Logic ──────────────────────────────────────────
extern "C" void on_frame(float deltaTime)
{
    g_pulseTimer = g_pulseTimer + deltaTime;
    if (g_pulseTimer > 2.0f)
        g_pulseTimer = g_pulseTimer - 2.0f;
}

// ── Render ───────────────────────────────────────────────
extern "C" void on_render()
{
    Vector2 screenCenter = ScreenCenter();

    // Pulse alpha using library remap
    float pulseT = g_pulseTimer;
    if (pulseT > 1.0f) pulseT = 2.0f - pulseT;  // triangle wave 0→1→0
    int pulseAlpha = static_cast<int>(
        math::Remap(pulseT, 0.0f, 1.0f, 100.0f, 255.0f));

    for (Entity enemy : Players())
    {
        if (enemy.IsLocal()) continue;
        if (!enemy.IsAlive()) continue;
        if (g_visibleOnly && !enemy.IsVisible()) continue;

        Vector3 rootPos = enemy.GetPosition();
        Vector3 headWorld = enemy.GetBonePos(Bone::Head);
        if (headWorld.x == 0.f && headWorld.y == 0.f && headWorld.z == 0.f)
            headWorld = Vector3(rootPos.x, rootPos.y + 1.8f, rootPos.z);

        Vector2 headScreen;
        if (!WorldToScreen(headWorld, headScreen)) continue;

        // Dynamic circle radius using library clamp + remap
        float dist = enemy.GetDistance();
        float radius = math::Clamp(
            math::Remap(dist, 5.0f, 80.0f, 8.0f, 3.0f), 3.0f, 8.0f);

        Color color = enemy.IsVisible()
            ? Color(255, 50, 50, static_cast<uint8_t>(pulseAlpha))
            : Color::Orange();

        Draw::CircleFilled(headScreen, radius, color);

        if (g_showHealthBars)
        {
            float hp = enemy.GetHealthPercent();
            float barWidth = math::Lerp(20.0f, 60.0f,
                math::Clamp(80.0f / dist, 0.0f, 1.0f));
            Draw::HealthBar(headScreen.x - barWidth * 0.5f,
                           headScreen.y + 10, barWidth, 4, hp);
        }

        if (g_showDistance)
        {
            char distText[32];
            int d = static_cast<int>(dist);
            distText[0] = '0' + (d / 100) % 10;
            distText[1] = '0' + (d / 10) % 10;
            distText[2] = '0' + d % 10;
            distText[3] = 'm';
            distText[4] = '\0';
            Draw::Text(headScreen.x - 10, headScreen.y + 20,
                      Color::White(), distText);
        }

        if (g_showSnaplines)
        {
            float fadeAlpha = math::Lerp(200.0f, 40.0f,
                math::Clamp(dist / g_maxDistance, 0.0f, 1.0f));
            Draw::Line(screenCenter, headScreen,
                      color.WithAlpha(static_cast<uint8_t>(fadeAlpha)), 1.f);
        }
    }
}

// ── Menu ─────────────────────────────────────────────────
extern "C" void on_menu()
{
    if (ImGui::CollapsingHeader("Enemy ESP (deps)"))
    {
        ImGui::Checkbox("Show Health Bars", &g_showHealthBars);
        ImGui::Checkbox("Show Distance", &g_showDistance);
        ImGui::Checkbox("Show Snaplines", &g_showSnaplines);
        ImGui::Separator();
        ImGui::Checkbox("Visible Only", &g_visibleOnly);
        ImGui::SliderFloat("Max Distance", &g_maxDistance, 10.f, 200.f);
    }
}

What's Different from the Basic ESP#

Dependency Declaration#

#include <xenon/libs/math_helpers.hpp>

XENON_PLUGIN_INFO_DEPS(
    ...,
    "math_helpers\0"    // dependency on math_helpers service
)

The #include gives access to xenon::math::Lerp, Clamp, Remap. The XENON_PLUGIN_INFO_DEPS macro tells the host to load the math library first.

Pulsing Effect#

float pulseT = g_pulseTimer;
if (pulseT > 1.0f) pulseT = 2.0f - pulseT;  // triangle wave 0→1→0
int pulseAlpha = static_cast<int>(
    math::Remap(pulseT, 0.0f, 1.0f, 100.0f, 255.0f));

Uses math::Remap to convert a 0-1 triangle wave into an alpha range of 100-255. Visible enemies pulse in brightness.

Dynamic Circle Radius#

float radius = math::Clamp(
    math::Remap(dist, 5.0f, 80.0f, 8.0f, 3.0f), 3.0f, 8.0f);

Circles are larger for close enemies (8px) and smaller for far ones (3px), using Remap for smooth interpolation and Clamp to bound the result.

Adaptive Health Bar Width#

float barWidth = math::Lerp(20.0f, 60.0f,
    math::Clamp(80.0f / dist, 0.0f, 1.0f));

Health bars scale with distance — wider when close, narrower when far.

Fading Snaplines#

float fadeAlpha = math::Lerp(200.0f, 40.0f,
    math::Clamp(dist / g_maxDistance, 0.0f, 1.0f));

Snaplines fade out with distance using Lerp.

Build#

Both the library and this plugin must be built:

build.bat --library examples\math_helpers_lib.cpp
build.bat examples\enemy_esp_with_deps.cpp

Both .wasm files must be loaded by Xenon. The host automatically orders them so the library loads first.

See Also#