HOOZiDocs
Skip to content

game

Live data (read-only, writes raise a Lua error). Accessing a field = the latest published snapshot for the current frame.

Update strategy

The field tables carry an Update strategy column — not every field is read from the game each frame. The backend re-paces reads by state, and a skipped frame reuses the previous value. Understanding this column saves the "why does an off-screen enemy's HP update so slowly" / "why are bones zero off-screen" confusion.

  • Enemy players / loot / projectiles down-throttle off-screen: off-screen, farther away, and non-alive all read slower. Any field marked "on-screen only" does not refresh while off-screen — you read the last value seen while it was on-screen.
  • One exception: the aimbot's currently locked target is exempt from all throttling and is read every frame (so aiming never depends on ESP toggles).
  • The local player is never throttled — you're always relevant, so its default is "every frame".
  • "Computed each update" = the value is not read from memory directly; it's derived locally each cycle from the freshest other fields, so its freshness follows whatever it depends on.
  • "Every frame / every N frames" refers to backend read cycles, not a fixed 60 Hz.

game.localplayer

The local player has no on-screen / distance throttling; default is "every frame".

FieldTypeUpdate strategyDescription
indexintEvery frameentity_list index
base / self_baseuint64Every frameEntity memory base — combine with offsets + mem.read/write to self-service read/write any attribute. The two diverge while dead-spectating: base follows the spectated player, self_base is always the real local machine — use self_base to read "my own" attributes
healthintEvery frameCurrent HP
max_healthintLow frequencyMax HP (only changes on armor pickup / gold-knockdown revive)
shieldintEvery frameCurrent shield
max_shieldintLow frequencyMax shield (changes on armor swap)
origin / camera_originVec3Every frameEntity / camera position (origin follows the spectated player while dead)
view_angleVec2Every frame{pitch, yaw}
sway_angleVec2Every frameBullet firing direction including breath/sway
view_offset / punch_angleVec3Every frame, alive & not knockedView offset / recoil; keeps last value when dead/knocked
abs_velocityVec3Every frame
flags_rawuint32Every frameRaw m_fFlags bits (FL_ONGROUND etc.)
team_num / squad_idintRead once on create
platform_uidstringRead once on createuint64 → string
weapon_idintEvery frame, alive & armedWeapon string-table index; blank when empty-handed
weapon_enumintResolved on weapon swapsdk::ItemId value, more reliable than the weapon string
weaponstringResolved on weapon swapWeapon name shown in ESP
weapon_speed / weapon_scalenumberEvery frame, alive & armedFire rate / charge-up
target_zoom_fovnumberEvery frame, alive & armedADS fov of the current weapon
weapon_next_ready_timenumberEvery frame, alive & armed
weap_state / burst_fire_index / ammo_in_clipintEvery frame, alive & armedInternal ammo state
is_semi_autoboolEvery frame, alive & armed
is_zoomingboolEvery frame, alive & not knocked
is_dead / is_down / is_on_groundboolEvery frame
is_grenade / is_handsboolResolved on weapon swap
is_skydiveboolLow frequency (~every 6 frames)
time_basenumberEvery frameWorld time base
spec_indexintEvery frameSpectated target (only set while dead-spectating; -1 when alive)
backpack_tierintLow frequency (~1 Hz)0=None / 1=White / 2=Blue / 3=Purple / 4=Gold
consumablestableLow frequency (~every 15 frames)16-slot consumable inventory (each slot {item: uint16, count: uint16}; count==0 = empty)

game.entities.players (iterable via pairs)

Per-PlayerEntity fields. The update strategy here stacks two layers: the entity itself is read every frame on-screen, throttled off-screen, and a field may add its own gate (bones need on-screen, identity is read once on create). The effective refresh rate is the slower of the two.

FieldTypeUpdate strategyDescription
index / base / is_npcint/uint64/boolFixed on createNot read from memory
name / platform_uid / xpstring/string/intRead once on createplatform_uid retries a few times in-match if the first read fails
team_num / squad_id / rank_sourceintRead once on create
grade / rank / rank_icon_key / rank_colorint/stringLocked for the matchComputed from score on the first frame, fixed after one cloud-rank resolve
legend / legend_icon_keystringResolved on legend changeModel name sampled ~every 64 frames; resolves ~once per match
kills / damageintLow frequency (~every 64 frames)Read on- and off-screen, diluted by entity throttling
weapon_idintShort lag on weapon swapWeapon pointer refreshes ~every 12 frames on-screen; the underlying handle is sampled very infrequently
weaponstringResolved on weapon swap
originVec3Every frame on-screen, throttled off-screenNot read after death
abs_velocityVec3On-screen & in valid range onlyFalls back to position-delta estimation off-screen
head_posVec3On-screen onlyDerived from the head bone each update (needs bones resolved)
yawnumber<300m ~every 6 frames, slower beyondRead on- and off-screen
health / shield / max_shieldintOn-screen only (~every 12 frames)
flagsintOn-screen only (~every 3 frames)m_fFlags state bits
is_down / cloak_endtimebool/numberOn-screen only (~every 3 frames)
is_deadboolLow frequency (~every 8 frames)
spec_indexintDead players only (~every 8 frames)Whom this player is spectating; -1 for the living
last_visible_timenumberOn-screen & in valid range onlyVisibility timestamp
vec_min / vec_maxVec3On-screen (~every 3 frames), needs dynamic-box/map-identCollision AABB; not read if neither is enabled
is_glow / is_wallboolOn-screen (~every 12 frames), needs glow enabled
rosteruint8Roster lookup each updateRoster bitmask (pro/star/cheater/friends); not re-queried unless the roster changes
distancenumberComputed each updateMeters
is_visible / is_teammate / is_cloakboolComputed each updateNot read from memory
in_screen / can_draw / is_model_blockedboolComputed each updateRendering-related state; static occlusion like Gibraltar's bubble
has_bones / has_fresh_bones()boolOn-screen only; true only when all 17 bones were read this framefalse off-screen; calling it triggers a full-bone read (see below)

#game.entities.players does not work (userdata-backed tables have no #) — count with for _ in pairs(...) do n = n + 1 end.

Skeleton

PlayerEntity exposes 17 bone world positions; pair with game.BONE (part constants) and game.BONE_LINKS (connection table) to draw skeleton ESP.

Member / constantDescription
player:bone(idx)World-space Vec3 of body part idx (0-based body part id); returns a zero vector if out of range or bones not ready
player:has_fresh_bones()Whether all 17 bones were actually read this frame (when false, bone() is likely zero — skip)
game.BONENamed part constants: head=0, upper_chest, chest, waist, hip, l_shoulder, l_elbow, l_hand, r_shoulder, r_elbow, r_hand, l_thigh, l_knee, l_foot, r_thigh, r_knee, r_foot=16
game.BONE_LINKSArray of {a, b} part-id pairs (matches the native skeleton, 16 segments)

Update strategy: bones are read on-screen only (bone() returns zero off-screen). By default the backend reads only the head bone; all 17 are read when one of these holds: skeleton ESP is on, the player is the aimbot's locked target, or it's used as a chams far-enemy anchor. Calling bone() or has_fresh_bones() makes the backend read all 17 bones for on-screen players for the next ~0.5s — so drawing your own skeleton does not need the menu's skeleton-ESP toggle; just call it.

math.WorldToScreen(world) returns 3 values (sx, sy, ok), not a Vec2.

lua
-- Skeleton ESP: iterate links, project each bone to screen, draw a line
local LINKS = game.BONE_LINKS
event.on("frame_update", function()
    for _, p in pairs(game.entities.players) do
        if p.is_visible and not p.is_dead and not p.is_teammate and p:has_fresh_bones() then
            for _, link in ipairs(LINKS) do
                local ax, ay, ok1 = math.WorldToScreen(p:bone(link[1]))
                local bx, by, ok2 = math.WorldToScreen(p:bone(link[2]))
                if ok1 and ok2 then
                    draw.line(ax, ay, bx, by, draw.u8(255, 255, 255, 200), 1.5)
                end
            end
        end
    end
end)

game.entities.loots (iterable via pairs)

Per-LootEntity fields. Loot properties are essentially fixed at create; only origin keeps re-pacing by state. A newly dropped item is discovered after ~0.25s (discovery lag, unrelated to field refresh).

FieldTypeUpdate strategyDescription
index / baseint/uint64Fixed on createEntity memory base — combine with offsets + mem.read/write for self-service reads
model_namestringRead once on create
model_hashuint32Read once on createhash of model_name
quality_levelintRead once on create0=unknown, 1..5=COMMON..HEIRLOOM
weapon_name_indexintRead once on createWeapon string-table index
custom_script_int / context_idintRead once on create
classified_idintComputed each updateItemId enum value
classified_name / item_id_str / base_namestringComputed each updateClassified names
originVec3Low-frequency pollingHigh when just moved; static on-screen ~4 Hz; static off-screen ~1 Hz; beyond loot display range ~every 4s. Never fully stops
distancenumberComputed each updateMeters

game.entities.projectiles (iterable via pairs)

Per-ProjectileEntity fields. The core job is high-frequency trajectory drawing: origin is read every frame for nearby local/enemy projectiles. Lifetime-constant fields (owner/team/radius/weapon class) are read once when the projectile appears.

FieldTypeUpdate strategyDescription
index / baseint/uint64Fixed on createEntity memory base — combine with offsets + mem.read/write for self-service reads
owner_handle_rawuint32Read once on first appearanceRaw EHandle bits of m_hOwnerEntity
team_numintRead once on first appearance
dmg_radiusnumberRead once on first appearancem_DmgRadius
weapon_class_indexuint16Read once on first appearancem_weaponClassIndex; pair with game.world to look up weapon name
creation_timenumberStamped once on first appearanceEntity birth timestamp
kindstringTyped once when weapon name resolves"frag" / "thermite" / "arc_star" / "other" / "unknown"
originVec3Every frame (nearby local/enemy)~1 Hz beyond trajectory display range; not read for teammate or static map props once confirmed
is_throwableboolComputed each updateReal player grenade (filters static map props)
is_enemy / is_localboolComputed each update

game.aimbot (read-only)

FieldTypeDescription
target_indexintEntity index of the current aimbot target (-1 when no target)
target_distancenumber (meters)Distance to the current target
predict_posVec3 | nilThe aimbot's internal predicted target position
trigger_busyboolWhether the trigger decision is currently in a busy state (for custom trigger HUDs)
triggertableDetailed trigger state snapshot (see below, for trigger HUDs)

weapon_next_ready_time is on game.localplayer; trigger state is not its own namespace to avoid duplicate paths.

game.aimbot.trigger

All timestamps are engine-clock seconds (QPC, base::get_time), not time.game()/time.now(). The table includes a same-clock now; use it for countdowns (e.g. ready_in = t.weapon_ready_at - t.now).

FieldTypeDescription
nownumberCurrent engine clock (QPC seconds), same clock as the timestamps below; use it for countdowns
busyboolIn a hold/release-window countdown
target_indexintTarget being evaluated (0 = none)
pressedboolAlready mouse_down
last_hitboolWhether the last evaluation hit
snapshot_timenumberGame time of this snapshot
release_at / again_atnumberPlanned mouse_up / next-allowed-fire time
visible_atnumberTarget visible start (react timing origin)
weapon_ready_atnumberWeapon ready time (incl. rdyfudge)
react_window / again_window / release_windownumberReact / re-fire / hold-duration windows
rdyfudge / radscalenumberReady offset / radius scale
aim_offsetVec3Offset of the aim part relative to spine_head
lua
event.on("frame_update", function()
    local t = game.aimbot.trigger
    if t.target_index ~= 0 then
        local ready_in = math.max(0, t.weapon_ready_at - t.now)
        draw.text(20, 200, draw.u8(255,255,255,255),
                  string.format("trigger: tgt=%d hit=%s ready_in=%.2f",
                                t.target_index, tostring(t.last_hit), ready_in))
    end
end)

Custom aim algorithms (movement/smoothing + prediction override)

Scripts can register their own movement algorithm (added to the dropdown) and prediction algorithm (global override). Execution model: callbacks run on the render thread (same thread as the Lua VM — no lock). Smoothing cadence = min(aim publish rate, render FPS) (uncapped render → high rate); prediction is slow-changing, so the render thread computes the aim point and the aim thread consumes it at 500Hz with no cadence loss. The native Normal/PID paths are unaffected.

game.aimbot.solve(shooter, target_pos, target_vel, v0, gravity){pitch, yaw, time} | nil

Native ballistic solver (stateless, callable anywhere). pitch/yaw are Source angles (degrees, pitch +down), time is flight time (seconds). v0 ≤ 1 is treated as hitscan (time=0); out of range returns nil. Coordinates are world-space Vec3, velocity u/s, gravity u/s².

game.aimbot.register_algorithm(name, fn)

Register a movement algorithm. name is appended to the aim / trigger Movement Algorithm dropdown; when selected, this algorithm drives mouse smoothing. fn(state) returns dx, dy (mickey delta; the engine accumulates sub-pixel residue). Auto-unregistered on script unload.

Must be called from the script's top-level load body — deferred callbacks (event.on("frame_update", ...), Delay(), gui.Button callbacks, etc.) are rejected with a throttled log warning, because the owner identity is cleared after load completion and cannot be reliably attributed (would silently leak across scripts). For lazy enable, keep register_algorithm in the body and gate behavior with a Lua-side flag.

state fields:

fieldtypemeaning
err_pitch / err_yawnumber (deg)current aim error (prediction-compensated + dead-zoned)
dtnumber (s)time since this algorithm was last called
distancenumber (m)target distance
zoomingboolADS or not
mouse_sensnumberin-game sensitivity (for screen-consistent scaling: err/0.022/mouse_sens → mickey)
target_indexinttarget entity index
shooter / swayVec3 / Vec2shooter origin / current reference view
target / velocityVec3world hit point / target velocity (u/s)

Sign convention matches native: dx = -err_yaw·k, dy = err_pitch·k.

game.aimbot.set_predictor(fn)

Globally override the prediction algorithm. fn(target) returns a world aim point Vec3; the aim thread consumes it at 500Hz in place of native solve. Pass nil to clear (fall back to native). target fields: index, shooter, aim_point, head, origin, velocity (Vec3), distance, v0, gravity.

aim_point vs head: aim_point is the current actual aim point — in upper-body / full-body modes it rides the body part nearest the crosshair and moves with it, so compute the lead against it. head is the stable real head-bone world point, only for head-specific logic (e.g. fixed head-aim compensation). Always lead with aim_point, not head (otherwise the aim point gets pulled back to the head and cancels the tracking).

Same as register_algorithmload-body-only. Also one-predictor-per-process: if another script already owns the predictor, calls are rejected silently to Lua (throttled log only). Convention: one predictor per process, or unload the holding script first.

lua
local tab  = gui.tab("AimAlgo")
local g    = tab:group("g", "Lua Aim Algo", 0, 0, 300, 0)
local gain = g:slider_float("gain", "Strength (mickey/deg)", 0.55, 0.05, 2.0)
local lead = g:checkbox("lead", "Prediction override", true)

-- Adaptive easing smoother: appears in the dropdown, active when selected
game.aimbot.register_algorithm("Lua Adaptive", function(s)
    local mag = math.sqrt(s.err_pitch^2 + s.err_yaw^2)
    local f   = gain:get() * (1.0 - 0.4 * math.exp(-mag))   -- soft near target, full far
    return -s.err_yaw * f, s.err_pitch * f
end)

-- Prediction override: reuse native solve for flight time, extrapolate by velocity
game.aimbot.set_predictor(function(t)
    if not lead:get() then return t.aim_point end
    local sol = game.aimbot.solve(t.shooter, t.aim_point, t.velocity, t.v0, t.gravity)
    if not sol then return t.aim_point end
    return Vec3(t.aim_point.x + t.velocity.x * sol.time,
                t.aim_point.y + t.velocity.y * sol.time,
                t.aim_point.z + t.velocity.z * sol.time)
end)

game.world

FieldTypeDescription
ringtable{origin=Vec3, radius_start=N, radius_end=N, time_start=N, time_end=N, is_active=bool}; radius / origin are in game units (≈ inches), divide by 39.37 for meters
map_namestringCurrent map string

game top-level methods

MethodReturnsDescription
game:is_in_game()boolWhether currently in a match (signon_state + not in lobby/match-making)
game:signon_state()intRaw client_state.signon_state

Example

lua
local lp = game.localplayer
if lp.is_dead then return end

for _, p in pairs(game.entities.players) do
    if (not p.is_teammate) and p.is_visible and p.distance < 50 then
        log.info("Enemy " .. (p.name or "?") .. " at " .. p.distance .. "m")
    end
end

if game.world.map_name == "mp_rr_arena_skygarden" then
    -- ...
end