Abrams Auto Charge ​
Hold mouse-5 to snap to the closest enemy in your FOV, charge them with ability 2, and keep tracking through the charge until either the charge connects, the target dies, or the target becomes untargetable.
What this shows ​
- Hero-locked script. Setting
id = hero_id.abramsmeans the script only runs when you're playing Abrams. Switch heroes and it goes idle on its own. - Modifier-driven state. The script doesn't try to time the charge with
clock(). It watches the local player'sbullchargingmodifier. When Abrams is mid-charge, that modifier is active. As soon as it ends (hit, miss, or cancel), the script naturally exits the tracking phase. - Hit detection via modifier. The enemy gets a
chargedragenemymodifier the moment the charge connects. Reading that on the target tells you the hit landed without polling positions or doing geometry. - Identity guard. Player indices can shift mid-frame as entities spawn/despawn. Caching the hero name when you lock and checking it every tick prevents the "I was tracking Haze, suddenly the script thinks I'm tracking Lash" failure mode.
- Zero-bone guard. Bone positions can briefly read as
(0, 0, 0)during certain animation frames or just after a respawn. Snapping to origin would yank your camera underground. The check skips those frames.
Script ​
lua
-- Abrams Auto Charge
-- Press mouse5 -> snap to closest enemy -> charge -> track until hit
--
-- API reference:
-- hero_id.abrams , hero filter, script only runs on Abrams
-- slot.ability2 , ability slot (0-3 abilities, 4-7 items)
-- bone.head , named bone (head, neck, chest, spine_1, pelvis, etc.)
-- VK.XBUTTON2 , mouse5 virtual key code
--
-- local_player() , returns local player
-- targeting.find_closest_by_fov(fov_deg, range_m), best target in FOV cone
-- snap_to_target(player, {bone = bone.X}) , instant aim snap
-- press_ability(slot.X) , press ability keybind
--
-- player:has_modifier(name) , substring match on active modifiers
-- player:is_alive() , alive check
-- player:is_targetable() , alive + not immune
-- player:hero_name() , hero name string
-- player:get_distance() , distance in meters
-- player:bone_pos(bone) , bone world position
--
-- toast(text, dur, {bg, text, outline, scale}), styled notification
-- clock() , high-res timer (seconds)
-- input.is_key_held(vk) , true while key held
name = "Abrams Auto Charge"
id = hero_id.abrams
settings = {
{ key = "fov", label = "FOV (degrees)", type = "float", default = 15.0, min = 1.0, max = 30.0 },
{ key = "max_range", label = "Max Range (m)", type = "float", default = 30.0, min = 5.0, max = 100.0 },
}
local locked_target = nil
local locked_hero = nil
local lock_time = 0
function on_tick()
local lp = local_player()
if not lp or not lp:is_alive() then
locked_target = nil
locked_hero = nil
return
end
local charging = lp:has_modifier("bullcharging")
local now = clock()
-- Phase 2: mid-charge tracking
if locked_target then
local in_grace = (now - lock_time) < 0.3
if charging or in_grace then
-- index shift guard: make sure we're still aimed at the right hero
if locked_target:hero_name() ~= locked_hero then
locked_target = nil
locked_hero = nil
return
end
-- hit: enemy received charge drag modifier
if locked_target:has_modifier("chargedragenemy") then
toast(string.format("Charged %s!", locked_hero), 3, {
bg = {r=20, g=60, b=20, a=220},
text = {r=100, g=255, b=100},
})
locked_target = nil
locked_hero = nil
return
end
-- target lost
if not locked_target:is_alive() or not locked_target:is_targetable() then
toast("Target lost", 2, {
bg = {r=80, g=10, b=10, a=220},
text = {r=255, g=80, b=80},
})
locked_target = nil
locked_hero = nil
return
end
-- zero bone guard + track
local bp = locked_target:bone_pos(bone.head)
if bp and (bp.x ~= 0 or bp.y ~= 0 or bp.z ~= 0) then
snap_to_target(locked_target, { bone = bone.head })
end
return
end
-- charge ended
locked_target = nil
locked_hero = nil
end
-- Phase 1: initiate
if not input.is_key_held(VK.XBUTTON2) then return end
if not lp:is_ability_ready(slot.ability2) then return end
local target = targeting.find_closest_by_fov(
config.get_float("fov"),
config.get_float("max_range")
)
if not target or not target:is_targetable() then return end
-- lock + snap + fire
locked_target = target
locked_hero = target:hero_name()
lock_time = now
snap_to_target(target, { bone = bone.head })
press_ability(slot.ability2)
toast(string.format("Charging %s!", locked_hero), 2, {
bg = {r=15, g=25, b=60, a=220},
text = {r=120, g=180, b=255},
})
endAdapting this to other heroes ​
The pattern (lock on input → snap to target → fire ability → track via modifier → release on hit/miss) applies to most ability-with-travel-time charges. Things you'd change for a different hero:
id = hero_id.X, which hero this binds to. Drop the line entirely to make it hero-agnostic.slot.abilityN, which ability slot triggers (and which one's readiness you check).- The two modifier names: the "I am using this ability" modifier on the local player, and the "I was hit by it" modifier on the target. Both are easiest to find using the Debugging Modifiers workflow, turn on the live modifier dump, do the thing in-game, see what shows up.
bone.headfor the snap target. Head is right for upper-body abilities; for AOE drops you'd wantbone.pelvisor just use the player position.
The grace period (0.3 seconds) exists because some abilities have a brief animation lock-in window before the modifier appears on the local player. Without it, the script would exit tracking before the charge even starts. If you're adapting this to an ability with a longer windup, bump that number up.