Player Object â
Returned by local_player(), get_players(), event callbacks, and modifier.caster.
All methods are colon-style: p:method().
Identity â
| Function | Returns | Description |
|---|---|---|
p:hero_name() | string | Display name: "Ivy", "Vindicta", "Viscous", etc. |
p:get_hero_id() | integer | Numeric hero ID (compare with hero_id enum) |
p:get_team() | integer | Team number (typically 2 or 3) |
p:is_local() | boolean | True only for the local player |
p:is_enemy() | boolean | True if on the opposing team |
p:is_valid() | boolean | True if player data is currently readable |
p:entity_ptr() | integer | Raw pawn pointer (advanced use) |
State â
| Function | Returns | Description |
|---|---|---|
p:is_alive() | boolean | Health > 0 |
p:get_health() | number | Current health |
p:get_max_health() | number | Maximum health |
p:get_health_percentage() | number | Health as 0.0 to 1.0 |
p:is_visible() | boolean | Visible to local player (vischeck) |
p:is_on_screen() | boolean | Within screen bounds |
p:is_scoped() | boolean | Currently scoped or zoomed |
p:is_in_reload() | boolean | Currently reloading |
p:get_reload_progress() | number | Reload progress 0.0 to 1.0 |
p:is_primary_weapon_active() | boolean | Primary weapon is the active slot |
p:is_in_melee_attack() | boolean | Currently performing a melee attack |
p:is_targetable() | boolean | Can be targeted (not invulnerable) |
p:get_active_projectile_speed() | number | Effective gun projectile speed (u/s), includes velocity item multiplier. Returns 0 until the gun resolves. Local player only |
p:get_souls() | integer | Total souls (net worth) |
p:get_unsecured_souls() | integer | Unsecured souls (lost on death) |
Position and Movement â
| Function | Returns | Description |
|---|---|---|
p:get_position() | vec3 | World position (feet) |
p:get_head_world() | vec3 | Head position in world space |
p:get_velocity() | vec3 | Movement velocity vector |
p:get_view_angles() | vec3 | Pitch, yaw, roll in degrees |
p:bone_pos(name_or_index) | vec3 | nil | World position of a skeleton bone. Accepts a name string ("head", "arm_upper_l", etc.) or bone.* constant. Raw integer indexes work as legacy. Returns nil if the bone isn't found |
p:get_bone_position(...) | vec3 | nil | Legacy alias for p:bone_pos. Identical behavior |
p:bone_names() | table | All available bone names for this player's current model. Empty table if bones haven't been probed yet |
p:get_distance([other]) | number | Meters to local player, or to other if provided |
Stamina â
Local player only
Stamina is not replicated to other clients. These functions return 0 when called on enemy or teammate handles.
| Function | Returns | Description |
|---|---|---|
p:get_stamina() | number | Current stamina (e.g. 3.0) |
p:get_max_stamina() | number | Maximum stamina (e.g. 4.0) |
local me = local_player()
local pct = me:get_stamina() / me:get_max_stamina() * 100
print(string.format("Stamina: %.0f%%", pct))Crouch â
Local player only
Crouch state is not replicated to other clients. Returns 0 / false for non-local players.
| Function | Returns | Description |
|---|---|---|
p:get_crouch_fraction() | number | 0.0 = standing, 1.0 = fully crouched. Values between indicate a transition |
p:is_crouched() | boolean | true when crouch fraction > 0.5 |
local me = local_player()
if me:is_crouched() then
print("Crouching: " .. string.format("%.0f%%", me:get_crouch_fraction() * 100))
endGround Normal / Slope â
Local player only
Ground normal requires local movement data. Returns {0, 0, 0} / 0 for non-local players.
| Function | Returns | Description |
|---|---|---|
p:get_ground_normal() | vec3 | Surface normal of the ground. {0, 0, 1} on flat ground |
p:get_slope_angle() | number | Slope angle in degrees. 0 = flat, 45 = steep, 90 = wall |
The z component of the ground normal indicates steepness:
z value | Meaning |
|---|---|
1.0 | Flat ground |
0.87 | ~30° slope |
0.71 | ~45° slope |
0.0 | Vertical wall |
local me = local_player()
local slope = me:get_slope_angle()
if slope > 30 then
print("Steep slope: " .. string.format("%.1f°", slope))
end
local gn = me:get_ground_normal()
print(string.format("Surface: (%.2f, %.2f, %.2f)", gn.x, gn.y, gn.z))Screen Space â
| Function | Returns | Description |
|---|---|---|
p:screen_box() | table | nil | {x, y, w, h} bounding box on screen |
p:screen_head() | table | nil | {x, y} head position on screen |
p:screen_origin() | table | nil | {x, y} feet position on screen |
Returns nil if the player is off-screen or behind the camera.
Modifier Flags â
Fast bitmask check for common status effects.
| Function | Returns | Description |
|---|---|---|
p:has_modifier_flag(flag) | boolean | Check a single flag from modifier_flag enum |
p:is_(name) | boolean | String shorthand: p:is_("stunned"), p:is_("silenced") |
if player:has_modifier_flag(modifier_flag.STUNNED) then
-- stunned
endSee Types & Constants for the full flag list.
Modifiers (full data) â
For modifiers that aren't covered by flags (channeling abilities, item effects, etc.), use the full modifier query.
| Function | Returns | Description |
|---|---|---|
p:has_modifier(name_or_token) | boolean | True if any modifier matches. String arg = substring match on RTTI name; integer arg = exact token match |
p:get_modifier(name_or_token) | table | nil | First matching modifier, or nil. Same matching rules as has_modifier |
p:get_modifier_count() | integer | Number of active modifiers |
p:get_modifier_names() | table | Array of RTTI class name strings |
p:get_modifiers() | table | Array of all modifier tables |
-- Substring match: catches modifier_stunned, modifier_delayed_stun, etc.
if player:has_modifier("stun") then ... end
-- Exact token match
if player:has_modifier(0x9C02E614) then ... endEach modifier table (returned by get_modifier and as entries in get_modifiers) has these fields:
| Field | Type | Description |
|---|---|---|
name | string | RTTI class name, e.g. "modifier_glitch" |
token | integer | Ability subclass ID, e.g. 0x9C02E614 |
duration | number | -1 if permanent, > 0 if temporary |
remaining | number | Seconds left (0 if permanent or expired) |
expires_at | number | game_time() when effect ends |
is_active | boolean | Has duration and hasn't expired |
ability_name | string | Linked ability class name |
ability_cd | number | Ability cooldown remaining |
ability_cooling | boolean | Ability is on cooldown |
serial | integer | Unique instance ID (distinguishes duplicate tokens) |
caster | player | nil | Who applied the modifier (nil if unknown) |
local m = player:get_modifier("stunned")
if m and m.is_active then
print(m.remaining, "seconds left, applied by", m.caster and m.caster:hero_name())
end
for _, m in ipairs(player:get_modifiers()) do
if m.is_active and m.caster and m.caster:is_enemy() then
print(m.name, m.remaining)
end
endUltimate â
| Function | Returns | Description |
|---|---|---|
p:is_ult_trained() | boolean | Ultimate has been skilled |
p:is_ult_ready() | boolean | Trained AND off cooldown |
p:get_ult_cooldown() | number | Seconds remaining (-1 = not trained, 0 = ready) |
Abilities (slots 0-3) & Weapon Slots â
Hero abilities, active items, and weapon slots.
| Function | Returns | Description |
|---|---|---|
p:has_abilities() | boolean | Player has any abilities loaded |
p:is_ability_ready(slot) | boolean | Slot is ready to cast |
p:get_ability(slot) | table | nil | Single ability data (see below) |
p:get_abilities() | table | Array of all four ability tables |
Slot Constants â
slot.* | Value | Meaning |
|---|---|---|
slot.ability1 ⌠slot.ability4 | 0â3 | Hero abilities (signature 1â4) |
slot.item1 ⌠slot.item4 | 4â7 | Active items |
slot.weapon_secondary | 20 | Secondary weapon / alt-fire |
slot.weapon_primary | 21 | Primary gun |
slot.weapon_melee | 22 | Melee |
Local player only
Weapon slots (20â22) are local player only. Items (4â7) return as items, not abilities. They don't populate projectile_speed.
Each ability table has:
| Field | Type | Description |
|---|---|---|
name | string | RTTI name, e.g. "citadel_ability_tengu_airlift" |
slot | integer | Slot number |
points | integer | Upgrade points spent |
learned | boolean | Has been skilled |
cooldown | number | Seconds remaining |
is_ready | boolean | Learned, not on cd, not casting |
is_cooling_down | boolean | On cooldown |
is_casting | boolean | Mid-cast |
is_channeling | boolean | Currently channeling |
is_in_cast_delay | boolean | In pre-cast windup |
remaining_charges | integer | Remaining charges (-1 = not charge-based) |
projectile_speed | number | Base projectile speed in u/s (VData, not item-boosted). 0 = no projectile |
range | number | Cast range (currently always 0, unimplemented) |
Projectile Speed â
get_ability(slot).projectile_speed returns the base (VData) speed. For the gun's effective speed including velocity items, use get_active_projectile_speed() instead.
local me = local_player()
-- Ability projectile speed (base)
local dagger = me:get_ability(slot.ability1)
if dagger then print(dagger.name, dagger.projectile_speed) end
-- Gun base vs effective
local gun_base = me:get_ability(slot.weapon_primary)
local effective = me:get_active_projectile_speed()
-- effective = base Ă (1 + 0.6 Ă velocity_item_count)Owned Items â
All purchased items (m_vecUpgrades). Match by case-insensitive display name OR hex token.
| Function | Returns | Description |
|---|---|---|
p:get_item_count() | integer | Number of owned items |
p:has_item(name_or_token) | boolean | "Cursed Relic", "cursed relic", or 0x9C02E614 all work |
p:get_item(name_or_token) | table | nil | Item table or nil if not owned |
p:get_items() | table | All owned items |
Each item table has:
| Field | Type | Description |
|---|---|---|
token | integer | e.g. 0x5230D219 |
name | string | Display name, e.g. "Metal Skin" |
if player:has_item("Cursed Relic") then
-- ...
end
for _, it in ipairs(player:get_items()) do
print(string.format("0x%08X %s", it.token, it.name))
endActive Items (slots 4-7) â
Items in ability slots with cooldown tracking. Matched by RTTI substring (lowercase, snake_case).
| Function | Returns | Description |
|---|---|---|
p:get_active_item_count() | integer | Number of active items equipped |
p:has_active_item(substring) | boolean | RTTI substring match |
p:get_active_item(substring) | table | nil | Single active item data |
p:get_active_items() | table | Array of all active items |
Each active item table has:
| Field | Type | Description |
|---|---|---|
name | string | RTTI class name |
subclass | string | Subclass name |
slot | integer | 4-7 |
bucket | integer | Item bucket |
cooldown | number | Seconds remaining |
if player:has_active_item("metal_skin") then
local mskin = player:get_active_item("metal_skin")
print("cd:", mskin.cooldown)
end