Skip to content

Stats ​

Needs the internal running and needs_internal = true in script_info.

p:get_stats() -> table | nil

Returns the player's combat stats with every item and modifier applied. Works on any player object, not just the local player. Percent fields are percentages: 18.0 means 18 percent. Without the internal, p:get_stat_deltas() gives what your items and buffs add to your own stats, without the hero's base.

Keys on every player ​

KeyMeaningUnit
max_healthMax healthHP
bullet_resistBullet damage reductionpercent
spirit_resistSpirit damage reductionpercent
melee_resistMelee damage reductionpercent
debuff_resistDebuff resistpercent
bullet_shieldShield pool in front of healthHP
barrierPool that soaks every damage typeHP
crit_taken_scaleCrit damage receivedmultiplier, 1.0 baseline
slow_resistSlow resistancepercent
ability_levelAbility level statnumber
dmg_taken_incAll damage taken increasepercent
bullet_dmg_taken_incBullet damage taken increasepercent
spirit_dmg_taken_incSpirit damage taken increasepercent
dmg_taken_redAll damage taken reductionpercent
bullet_dmg_red_pctBullet damage reductionpercent
ability_dmg_red_pctAbility damage reductionpercent
seqChanges when any row in the feed was refreshedinteger
entry_seqChanges when this row was refreshedinteger

The six damage-taken keys, dmg_taken_inc through ability_dmg_red_pct, are usually zero.

Keys on the local player only ​

Only present on the local player. On anyone else they are absent, not zero, so test with if s.bullet_damage ~= nil.

weapon_power_bonus, spirit_power, bullet_damage, fire_rate_bonus, clip_size, health_regen, move_speed, light_melee, heavy_melee, bullet_lifesteal, spirit_lifesteal, falloff_min, falloff_max

  • weapon_power_bonus and fire_rate_bonus are bonuses over the hero base, so 0 is the baseline.
  • bullet_damage is damage per bullet before resists and falloff.
  • falloff_min and falloff_max are in game units. p:get_distance() is in metres, so multiply it by 39.37 to compare. They include your items.

Your weapon and headshot numbers, also only on your own row:

KeyMeaning
crit_bonus_scaleA multiplier on your headshot bonus from items and effects. 1 means no change. It scales only the part of the headshot multiplier above 1
bullets_per_shotPellets per trigger pull
falloff_end_scaleDamage multiplier past the falloff end
crit_bonus_start, crit_bonus_endYour weapon's headshot multiplier at the start and at the end of its range
crit_bonus_start_range, crit_bonus_end_rangeThat range, in game units
weapon_falloff_start, weapon_falloff_endThe weapon's own falloff range in game units, without your items
can_crittrue when the weapon can headshot

bullets_per_shot through can_crit are missing when your weapon can't be read, so check them first. The damage preview does this math for you.

Returns nil when ​

  • the internal is not running
  • needs_internal = true is missing
  • nothing called p:get_stats() in the last 2 seconds. The first call after a gap returns nil while the feed starts up again
  • the player is not in the feed yet
  • the player is dead. The local player stays in the feed while dead

Notes ​

  • Rows refresh within 100 ms.
  • The same table is returned until entry_seq changes. Do not write into it, because other scripts get the same table.
  • 16 players maximum.
  • The player does not need to be visible.
  • internal.stats_diag() returns the stat feed's counters, or nil when the internal is not running. count is how many players are in the feed and wanted is whether a script asked in the last 2 seconds.

Example ​

Bullets to kill on the nearest enemy, from Shots to Kill:

lua
function on_tick()
    local x, y = 40, 460
    local function line(t, c, sc) draw.text(x, y, t, c or WHITE, sc or 1.15); y = y + 18 end

    if not internal.is_loaded() then line("shots_to_kill: internal not loaded", DIM); return end
    local me = local_player and local_player() or nil
    local ms = me and me:get_stats() or nil
    if not ms or not ms.bullet_damage then line("shots_to_kill: waiting for local stats", DIM); return end

    local e, d = nearest_enemy()
    local es = e and e:get_stats() or nil
    if not e or not es then line("shots_to_kill: no enemy stats yet", DIM); return end

    local B   = ms.bullet_damage
    local R   = (es.bullet_resist or 0) / 100
    local inc = ((es.dmg_taken_inc or 0) + (es.bullet_dmg_taken_inc or 0)) / 100
    local red = ((es.dmg_taken_red or 0) + (es.bullet_dmg_red_pct or 0)) / 100
    local per = B * (1 - R) * (1 + inc) * (1 - red)
    if per < 0.1 then line("shots_to_kill: per-hit damage ~0", DIM); return end

    local pool  = (e:get_health() or es.max_health or 0) + (es.bullet_shield or 0) + (es.barrier or 0)
    line(string.format("%d body", math.ceil(pool / per)), GREEN, 1.4)
end

bullet_damage is only on your own row and the resists are on the enemy's, so one read can be nil while the other has a value. nearest_enemy, WHITE, DIM and GREEN are defined in that script. p:get_shot_damage() below also handles headshots, shotgun pellets and falloff.

Damage preview ​

These two methods work out your damage on a player using the game's own numbers. Like get_stats, they need the internal running and needs_internal = true.

Your gun ​

p:get_shot_damage([distance_m]) -> table | nil

The damage of your next gun shot on this player. distance_m is the distance in metres, used for damage falloff and for your weapon's headshot range. Leave it out to use the distance from your camera to this player's head.

It counts your close and long range weapon bonuses at that distance, such as Close Quarters, Point Blank, Long Range and Sharpshooter. When you play Haze, it counts your Fixation stacks on this player, which uses one Ability Values pair. It also counts their Rat Armor and effects that change how much damage they take, such as Venator's self-heal channel. It does not count headshot items' flat bonuses (Headhunter, Headshot Booster), on-hit effects (Mystic Shot, Tesla Bullets, Lucky Shot) or damage over time.

KeyTypeMeaning
bodynumberOne trigger pull with every pellet on the body, after their bullet resist and damage modifiers
headnumberThe same on the head
per_bulletnumberOne pellet after falloff, with your range bonus and Fixation, before their resists
bulletsnumberPellets per trigger pull
falloffnumberThe falloff multiplier at this distance. 1 means no falloff
head_multnumberThe headshot multiplier used, from your weapon's headshot range and both players' headshot scales
poolnumberTheir health plus bullet shield plus barrier plus Rat Armor
shotsintegerBody shots to kill
head_shotsintegerHeadshots to kill
distancenumberThe distance used, in metres
range_multnumberThe close or long range weapon bonus used at this distance. 1 means none
fixationnumberHaze's Fixation bonus per bullet on this player, before falloff. 0 when you are not playing Haze, have no stacks on them, or the value is not read yet
fixation_stacksintegerYour Fixation stacks on this player

Returns nil when:

  • the internal is not running, or the flag is missing
  • you are not in a match
  • your stats or this player's are not in the feed yet. The first call starts the stat feed, and the answer arrives on a later tick, usually within 100 ms
  • this player is dead
  • the shot would deal no damage to this player
lua
function on_tick()
    local y = 300
    for _, e in ipairs(get_players()) do
        if e:is_enemy() then
            local s = e:get_shot_damage()
            if s then
                draw.text(40, y, string.format("%s: %.0f body, %.0f head, %d shots", e:hero_name(), s.body, s.head, s.shots), 0xFFFFFFFF)
                y = y + 18
            end
        end
    end
end

Your abilities ​

p:get_ability_damage(slot [, property [, kind]]) -> dealt, casts | nil

What your ability in slot (0 to 3) would deal to this player, after their resist and damage modifiers. property is the damage property to read, "Damage" by default. kind is the type of damage the ability deals:

kindResist used
"spirit" (default)Their spirit resist
"bullet"Their bullet resist. Their bullet shield also counts toward casts
"pure"None

Any other string counts as "spirit", so write them in lower case. Your own damage bonuses count: outgoing damage and all damage, plus your spirit damage bonus for "spirit" or your bullet damage bonus for "bullet". So do their Rat Armor and effects that change how much damage they take. If the ability also deals a share of the target's missing health, that gets added a moment after the first answer.

It returns two values: dealt, the damage after their resists, and casts, how many uses it takes to kill them (health plus barrier plus Rat Armor, and their bullet shield for "bullet").

It does not count Mystic Burst, your Escalating Exposure, the target's Spellbreaker or Shiv's Bloodletting. It also does not know when the target cannot die or cannot take that damage right now, for example during Metal Skin. If your script needs those, check them yourself. For the target's state, use modifier flags such as modifier_flag.UNKILLABLE, modifier_flag.TECH_INVULNERABLE and modifier_flag.BULLET_INVULNERABLE.

Returns nil when:

  • the internal is not running, or the flag is missing
  • you are not in a match
  • your ability slot is empty, has no such property, or its value has not arrived yet
  • this player's stats are not in the feed yet
  • this player is dead
  • the damage works out to 0

It uses Ability Values pairs: two for each slot you ask about, plus one for each other property name you pass.

lua
local dealt, casts = enemy:get_ability_damage(0)
if dealt then print(string.format("ability 1 deals %.0f, %d casts to kill", dealt, casts)) end

-- an ability that deals bullet damage
local b_dealt, b_casts = enemy:get_ability_damage(2, "Damage", "bullet")
if b_dealt then print(string.format("ability 3 deals %.0f, %d casts to kill", b_dealt, b_casts)) end