Skip to content

Types & Constants ​

Vec2/vec3 math types and the named constant tables (modifier_flag, slot, bone, hero_id, bucket, modifier_token, player_flag and VK).

vec3 ​

3D vector. Returned by position queries and used as input to drawing functions.

lua
local v = vec3(100, 200, 50)
v.x, v.y, v.z  -- field access
MethodReturnsDescription
v:length()numberMagnitude
v:length_sqr()numberSquared magnitude (cheaper than length())
v:length_2d()numberXY magnitude (ignores Z)
v:normalized()vec3Unit vector
v:dot(other)numberDot product
v:cross(other)vec3Cross product
v:distance(other)numberDistance to another point
v:angle_to(other)numberAngle between the two vectors in degrees, 0 to 180. 0 if either is a zero vector
v:empty()booleanTrue if all zero

Operators

ExpressionResultNotes
v + other, v - othervec3Component-wise
v + scalar, v - scalarvec3The scalar is broadcast to every component. Only this order works. scalar + v raises tsuki_vec3 expected, got number
v * scalar, scalar * vvec3Both orders work
v * othervec3Component-wise multiply. For the dot product, use v:dot(other)
v / scalarvec3Scalar only, and only on the right. A vector divisor raises number expected, got tsuki_vec3. Dividing by 0 divides by 1e-10 instead, so you do not get inf

There is no __unm, __eq, __lt or __len. -v and #v raise an error. Write v * -1 to negate a vector. a == b compares object identity, so two vectors with the same components are never equal. Compare the fields instead, or test (a - b):length() < eps.

vec2 ​

2D vector. Create one with vec2(x, y). It has the same operators as vec3, including the component-wise v * other. It has no cross and no length_2d.

MethodReturnsDescription
v:length()numberMagnitude
v:length_sqr()numberSquared magnitude (cheaper than length())
v:normalized()vec2Unit vector
v:dot(other)numberDot product
v:distance(other)numberDistance to another point
v:angle_to(other)numberDirection from v to other in degrees, -180 to 180 (atan2). Not the angle between two vectors, unlike vec3
v:empty()booleanTrue if both components are zero

modifier_flag ​

Status flags you can check with player:has_modifier_flag(...). The table below lists the ones most scripts use. For the full list, see Modifier Flags Reference.

lua
if player:has_modifier_flag(modifier_flag.STUNNED) then ... end

Use the name, not the number. The numbers change when a game update adds new states. A name the game removed reads as nil, and has_modifier_flag(nil) returns false.

There is no string shorthand for modifier_flag checks. player:is_(flag) is a different check that takes an integer player_flag bitmask, such as player_flag.onground, and passing it a string throws a Lua error. To check a modifier by name, use player:has_modifier("stunned") instead.

FlagValue
IMMOBILIZED13
DISARMED14
MUTED15
ITEMS_DISABLED16
SILENCED17
SILENCE_MOVEMENT_ABILITES18
STUNNED20
INVULNERABLE21
STATUS_IMMUNE26
UNSTOPPABLE27
COMMAND_RESTRICTED30
CHARGING31
OBSCURED32
INVISIBLE_TO_ENEMY33
INVISIBLE_TO_ENEMY_CAST34
SPRINTING38
UNKILLABLE39
IN_SHOP50
IN_FOUNTAIN51
DASH_DISABLED57
BURNING59
SLOWED66
SHOOTING_DISABLED67
SLIDING70
VISIBLE_TO_ENEMY73
IS_ASLEEP80
USING_ZIPLINE88
BULLET_INVULNERABLE95
MELEE_DISABLED110
GLITCHED113
RELOAD_DISABLED117
FLYING124
SCOPED125
VISCOUS_CUBED127
IN_COMBAT168
YAMATO_SHADOW_FORM171
FROZEN203
PARRY_ACTIVE229
WEREWOLF269

Caveat ​

Not every flag is set in every situation. STUNNED fires for almost every hard stun (Knockdown, Cursed Relic, Dynamo ult), and INVISIBLE_TO_ENEMY fires for Smoke Bomb and Shadow Weave. Other flags may not fire even when the player is clearly in that state. For example:

  • SLIDING may not be set while a player is sliding
  • USING_ZIPLINE may not be set while a player is on a zipline
  • IS_IN_CHARGE_MELEE may not be set during a heavy charged melee
  • SLOWED and BURNING are inconsistent. Some sources set them, but others apply the effect through stat modifiers and never set the flag

Some flags are unreliable, so test a flag before you rely on it. Use the Debugging Modifiers workflow to confirm it flips. If it does not, fall back to player:get_modifiers() by name or token.

slot ​

Ability, item, and action slot indexes.

ConstantValueDescription
slot.ability10Hero ability 1
slot.ability21Hero ability 2
slot.ability32Hero ability 3
slot.ability43Ultimate
slot.item14Active item 1
slot.item25Active item 2
slot.item36Active item 3
slot.item47Active item 4
slot.held8Currently-held / channeled action
slot.zipline9Zipline ride
slot.mantle10Mantle (ledge grab)
slot.climb_rope11Climb rope
slot.jump12Jump
slot.slide13Slide
slot.teleport14Teleport
slot.zipline_boost15Zipline boost
slot.cosmetic116Cosmetic slot 1
slot.innate117Innate ability 1
slot.innate218Innate ability 2
slot.innate319Innate ability 3
slot.weapon_secondary20Secondary weapon
slot.weapon_primary21Primary weapon
slot.weapon_melee22Melee weapon

Slots 0-3 are abilities, 4-7 are items, 8-22 are action/weapon slots. for i = slot.ability1, slot.ability4 do ... end iterates 0,1,2,3 and for i = slot.item1, slot.item4 do ... end iterates 4,5,6,7.

lua
local key = slot_to_key(slot.ability3)
input.press_key(key)

bone ​

String constants for player:bone_pos(...) and the targeting helpers. Bones are read from Deadlock's game files at startup. Custom mods and Deadlock Mod Manager are supported. Bones from a custom model are read when the model uses standard VPK skeleton names.

lua
local head_pos = player:bone_pos("head")        -- string literal works
local head_pos = player:bone_pos(bone.head)     -- bone.head == "head"
local head_pos = player:bone_pos(7)             -- raw integer index, legacy

Names are case-insensitive: "arm_upper_L" and "arm_upper_l" both resolve to the same bone.

Named aliases ​

These standard aliases map to VPK bones with fallback chains. If a hero's model does not have the primary bone, the alias falls back to the next one in the chain.

AliasPrimary boneFallback chain
bone.headhead(exact only)
bone.neckneck_0(exact only)
bone.chestspine_3spine_2 → spine_1 → spine_0
bone.lower_chestspine_2spine_1 → chest result
bone.stomachspine_0spine_1
bone.pelvispelvis(exact only)

Body bones ​

All standard humanoid body bones are available as bone.* constants. They have no fallback chain. If the model does not have the bone, bone_pos returns the player's origin (the feet) instead, so check player:bone_names() when it matters. That list keeps the model's own spelling, such as arm_upper_L, so compare names in lower case.

ConstantValue
bone.clavicle_l, bone.clavicle_r"clavicle_l", "clavicle_r"
bone.arm_upper_l, bone.arm_upper_r"arm_upper_l", "arm_upper_r"
bone.arm_lower_l, bone.arm_lower_r"arm_lower_l", "arm_lower_r"
bone.hand_l, bone.hand_r"hand_l", "hand_r"
bone.leg_upper_l, bone.leg_upper_r"leg_upper_l", "leg_upper_r"
bone.leg_lower_l, bone.leg_lower_r"leg_lower_l", "leg_lower_r"
bone.ankle_l, bone.ankle_r"ankle_l", "ankle_r"
bone.ball_l, bone.ball_r"ball_l", "ball_r"

To list every bone on a model at runtime, use player:bone_names(). The bone set varies between heroes. Some have extra bones such as ears, tails or weapon bones.

Raw spine bones ​

The aliases above (bone.chest, bone.lower_chest, bone.stomach) map to spine bones with fallback chains. To target one vertebra with no fallback, use the raw bone constants:

ConstantValue
bone.spine_0"spine_0" (stomach)
bone.spine_1"spine_1"
bone.spine_2"spine_2" (lower chest)
bone.spine_3"spine_3" (chest)
bone.neck_0"neck_0" (neck)

Notes ​

  • Bone names are only looked up on enemies. On a teammate, names other than head usually return the player's origin. head still returns the head, and a raw integer index still works.
  • See Debugging Modifiers for the in-menu bone label overlay that shows every bone on a hero in real time.

hero_id ​

Hero IDs, used by the top-level id global and returned by player:get_hero_id().

lua
id = hero_id.any  -- script runs regardless of which hero is selected

-- Gate logic on a specific hero at runtime:
local lp = local_player()
if lp and lp:get_hero_id() == hero_id.haze then ... end
ConstantValue
hero_id.any-1
hero_id.none0
hero_id.infernus1
hero_id.seven2
hero_id.vindicta3
hero_id.ladygeist4
hero_id.abrams6
hero_id.wraith7
hero_id.mcginnis8
hero_id.paradox10
hero_id.dynamo11
hero_id.kelvin12
hero_id.haze13
hero_id.holliday14
hero_id.bebop15
hero_id.calico16
hero_id.greytalon17
hero_id.moandkrill18
hero_id.shiv19
hero_id.ivy20
hero_id.warden25
hero_id.yamato27
hero_id.lash31
hero_id.viscous35
hero_id.pocket50
hero_id.mirage52
hero_id.dummy55
hero_id.viper58
hero_id.vyper58
hero_id.sinclair60
hero_id.mina63
hero_id.drifter64
hero_id.venator65
hero_id.victor66
hero_id.paige67
hero_id.doorman69
hero_id.billy72
hero_id.graves76
hero_id.apollo77
hero_id.rem79
hero_id.silver80
hero_id.celeste81

hero_id.viper and hero_id.vyper have the same value (58). IDs are not sequential, and some numbers in the range are not assigned. To find the ID of a hero that is not listed yet, run print(player:hero_name(), player:get_hero_id()).

bucket ​

Item upgrade bucket categories. An active item's bucket field always reads 0 for now, because the game stopped sending it, so don't filter items by it.

ConstantValueDescription
bucket.innate0Innate / passive
bucket.weapon1Weapon
bucket.vitality2Vitality
bucket.spirit3Spirit
lua
local me = local_player()
if me then
    for _, item in ipairs(me:get_active_items()) do
        print(item.name, item.cooldown)
    end
end

modifier_token ​

Named subclass token IDs for well-known modifiers. Pass one to player:has_modifier(token). To compare one with a modifier's token field, mask it first: m.token == (modifier_token.mo_burrow & 0xFFFFFFFF). Four of them (bebop_sticky_bomb, paige_captivating_read, viktor_rebirth and mo_burrow) read as negative numbers in Lua, and the token field is never negative.

lua
if player:has_modifier(modifier_token.knockdown) then
    print("target is knocked down")
end
ConstantValue
modifier_token.knockdown0x4ABFEA98
modifier_token.bebop_sticky_bomb0x96512C8E
modifier_token.lash_death_slam0x17A9E77A
modifier_token.warden_binding_word0x62C27FFE
modifier_token.paige_captivating_read0xB6177F0A
modifier_token.seven_static_charge0x400ED943
modifier_token.doorman_call_bell0x062B3CCC
modifier_token.viktor_rebirth0xD04073B3
modifier_token.calico_cat0x3C249027
modifier_token.mo_burrow0x8F74398D
modifier_token.rem_ability0x5D82ADA5

player_flag ​

Player entity flags (FL_* bitfield). Test with player:is_(flag). Values are bitmasks (1<<N), not raw bit indices. Do not pass them to has_modifier_flag.

lua
if player:is_(player_flag.onground) then
    print("player is on the ground")
end
ConstantValueBit
player_flag.onground11<<0
player_flag.ducking21<<1
player_flag.waterjump41<<2
player_flag.frozen321<<5
player_flag.atcontrols641<<6
player_flag.client1281<<7
player_flag.fakeclient2561<<8
player_flag.fly163841<<14
player_flag.godmode327681<<15
player_flag.notarget655361<<16
player_flag.aimtarget1310721<<17
player_flag.staticprop2621441<<18
player_flag.grenade5242881<<19
player_flag.donttouch10485761<<20
player_flag.basevelocity20971521<<21
player_flag.worldbrush41943041<<22
player_flag.object83886081<<23
player_flag.onfire335544321<<25
player_flag.dissolving671088641<<26
player_flag.transragdoll1342177281<<27
player_flag.unblockable_by_player2684354561<<28

VK codes (common) ​

Windows virtual-key codes for input.*. The full list is in Microsoft's docs.

The VK table has named constants, so you don't have to remember raw hex values:

lua
-- Named constants
input.is_key_held(VK.LBUTTON)   -- left mouse
input.is_key_held(VK.SHIFT)     -- Shift
input.is_key_held(VK.F)         -- F key
input.is_key_held(VK.LCONTROL)  -- Left Ctrl

-- Raw hex works too
input.is_key_held(0x01)
ConstantCodeKey
VK.LBUTTON0x01Left mouse
VK.RBUTTON0x02Right mouse
VK.MBUTTON0x04Middle mouse
VK.XBUTTON10x05Mouse 4
VK.XBUTTON20x06Mouse 5
VK.RETURN0x0DEnter / Return
VK.SHIFT0x10Shift
VK.CONTROL0x11Ctrl (either)
VK.ESCAPE0x1BEscape
VK.SPACE0x20Space
VK.KEY_0-VK.KEY_90x30-0x39Digit keys 0-9
VK.A-VK.Z0x41-0x5AA-Z
VK.F1-VK.F120x70-0x7BF1-F12
VK.LCONTROL0xA2Left Ctrl
VK.BACK0x08Backspace
VK.TAB0x09Tab
VK.MENU0x12Alt (either)
VK.CAPITAL0x14Caps Lock
VK.PRIOR, VK.NEXT0x21, 0x22Page Up, Page Down
VK.END, VK.HOME0x23, 0x24End, Home
VK.LEFT, VK.UP, VK.RIGHT, VK.DOWN0x25-0x28Arrow keys
VK.INSERT, VK.DELETE0x2D, 0x2EInsert, Delete
VK.NUMPAD0-VK.NUMPAD90x60-0x69Numpad 0-9
VK.LSHIFT, VK.RSHIFT0xA0, 0xA1Left and right Shift
VK.RCONTROL0xA3Right Ctrl
VK.LMENU, VK.RMENU0xA4, 0xA5Left and right Alt
VK.VOLUME_MUTE0xADMute
VK.VOLUME_DOWN, VK.VOLUME_UP0xAE, 0xAFVolume down, volume up
VK.MEDIA_NEXT_TRACK0xB0Next track
VK.MEDIA_PREV_TRACK0xB1Previous track
VK.MEDIA_STOP0xB2Stop media
VK.MEDIA_PLAY_PAUSE0xB3Play / pause media
VK.LAUNCH_MEDIA_SELECT0xB5Open the media player

The media and volume keys work with input.press_key, so a script can control music playing in another app. Like every script key press, they are only sent while the game window is focused and the game's cursor is hidden (no game menu, shop or chat open).