Skip to content

Entity Properties

Needs the internal running and needs_internal = true in script_info.

p:get_property("m_FieldName") -> integer | number | boolean | vec3 | nil
row:get("m_FieldName")        -> integer | number | boolean | vec3 | nil

Reads one game field by name, off a player's entity or off a row from entities.by_class. The value comes back with the type the game declares for it.

Declared typeLua value
int8 to int64, uint8 to uint64, GameTick_t, CUtlStringToken, Colorinteger
float32, float64, GameTime_tnumber
boolboolean
Vector, QAngle, Vector2D, Vector4D, Quaternionvec3, the first three components
CHandle<...>, CEntityHandle, CBaseHandleinteger, the raw handle. Its entity index is handle & 0x7FFF

Enums, strings, pointers, arrays and structs are not supported and return nil.

Fields known to work: m_iHealth, m_iMaxHealth, m_iTeamNum, m_flSimulationTime, m_bTakesDamage, m_vecAbsVelocity, m_hOwnerEntity, m_nLevel, m_lifeState, m_flCreateTime.

p:get_property("m_iMaxHealth") and p:get_max_health() read different fields and can differ. Which one the health bar shows has not been confirmed.

Returns nil when

  • the internal is not running, or the flag is missing
  • it is the first call for that (entity, field). The first call registers it; a later call gets the value
  • the class has no such field (logged once)
  • the entity is stale (logged once)
  • the type is unsupported (logged once)
  • 32 pairs are already registered across all scripts

Limits

  • 32 (entity, field) pairs across all scripts.
  • Values refresh within 250 ms, so poll every few ticks rather than every tick.
  • A pair nobody asked for in 10 seconds is dropped.
  • Field names up to 47 characters.
  • A row is a plain table with a get method and nothing else. Keep calling entities.by_class every tick; a class nobody asked for in 2 seconds is dropped and you get no rows.

Cheaper external reads that need no flag: p:get_level(), row.health, row.max_health, row.team, and p:get_ability(slot).points for an ability's upgrade tier.

Example

How long each nearby enemy trooper has been alive. my_team, max_m and COL are set earlier in the script:

lua
local rows = entities.by_class("npc_trooper")
if rows then
    for _, e in ipairs(rows) do
        if e.team ~= my_team and e.distance_m <= max_m then
            draw.text3d(e.position, string.format("%d/%d  %.0fm", e.health, e.max_health, e.distance_m), COL)
            local ct = e:get("m_flCreateTime")
            if ct then
                draw.text3d(vec3(e.x, e.y, e.z + 24), string.format("age %.0fs", sim_time() - ct), COL)
            end
        end
    end
end

The player form:

lua
local me = local_player()
local vel = me and me:get_property("m_vecAbsVelocity")   -- vec3, nil on the first call
if vel then draw.text(40, 300, string.format("%.0f u/s", vel:length()), 0xFFFFFFFF) end