SYNQ provides many useful tools to help clean up code and make complex tasks easier to perform. These tools form the foundation for building sophisticated routines with proper timing, event handling, visual feedback, configuration persistence, and navigation capabilities.
Current time in seconds, equal to GetTime(), but only updated when SYNQ framework ticks.
synq.time
This is useful for timing-based logic that needs to account for framework tick timing.
A combination of dynamic values which is roughly equal to the time it should take for what's displayed on your client to reach the servers, plus 1 tick of buffer for your script to react before it's too late, and 30ms to account for any latency jitter:
buffer = latency + tickRate + jitter
Buffer is used in many places within the framework to dynamically account for latency.
Example Usage:
-- Time an interrupt at the very latest possible moment
-- buffer accounts for latency, tick rate, and jitter
if target.castRemains <= synq.buffer then
if intimidation.cd == 0 and intimidation:Cast(target) then
synq.alert("Interrupted " .. target.castName, intimidation.id)
end
end
Your latency to the game server, averaged out over the last 30 seconds, with HTTP overhead accounted for.
synq.latency
The time between each SYNQ framework tick.
synq.tickRate
Generally, if an event will happen before time + tickRate, your script will not 'tick' again to react in time. That's why this is an important variable, it determines how many ticks you want to think ahead.
The value always varies for two reasons:
1/GetFramerate(), but the value is actually calculated each tick (current tick time - last tick time) then averaged out between the last 5 ticks.The Spell Queue Window duration set by SYNQ.
synq.spellCastBuffer
This is also the maximum amount of time remaining on GCD or cast time that it will queue the next spell within.
The total expected GCD duration per general 1.5s GCD incurring spell cast.
synq.gcd
Most spells on GCD incur a base 1.5s cooldown modified by haste, some incur their own shorter one:
1.5 / (1 + player.haste)
See also: Spell Object Attribute spell.gcd - the gcd that the queried spell incurs.
Whether or not the player has control of their character.
synq.hasControl
Current zone text.
synq.zone
Current mapID.
synq.mapID
A table mapping power type names to their numeric IDs.
synq.powerTypes = {
["mana"] = 0,
["rage"] = 1,
["focus"] = 2,
["energy"] = 3,
["combopoints"] = 4,
["cp"] = 4,
["runes"] = 5,
["runicpower"] = 6,
["soulshards"] = 7,
["shards"] = 7,
["astralpower"] = 8,
["ap"] = 8,
["lunarpower"] = 8,
["holypower"] = 9,
["alternatepower"] = 10,
["maelstrom"] = 11,
["chi"] = 12,
["insanity"] = 13,
["arcanecharges"] = 16,
["fury"] = 17,
["pain"] = 18,
["essence"] = 19
}
Adds a callback function to the main framework ticker. These are called in the order added, just before the routine actor.
synq.onTick(callback[, enabled])
Examples:
-- Only run while BM Hunter routine is enabled
synq.onTick(function()
-- Track pet buffs or other maintenance tasks
if pet.exists and pet.hp < 50 then
mendPet()
end
end, true)
-- Run every tick regardless of routine state
synq.onTick(function()
-- Global tracking or UI updates
-- This runs even when routine is disabled
end)
Does the same thing as onTick, but runs every frame instead (almost always faster).
synq.onUpdate(callback[, enabled])
Assign a callback to a combat log or other event firing.
synq.onEvent(callback, eventType)
Examples:
-- Listen for specialization changes (useful for multi-spec routines)
local function onSpecChange()
print("Hunter specialization changed to: " .. player.spec)
-- Reload rotation files or adjust logic based on new spec
end
synq.onEvent(onSpecChange, "PLAYER_SPECIALIZATION_CHANGED")
-- Listen to combat log events for tracking enemy casts
synq.onEvent(function(info, event, source, dest)
if event == "SPELL_CAST_SUCCESS" then
-- Combat log event info is passed as a table
-- SPELL_CAST_SUCCESS's 12th entry is the spellID
local spellID, spellName = select(12, unpack(info))
-- Track when enemies cast important abilities
if source.enemy and spellID == 12345 then -- Example: important enemy spell
synq.alert("Enemy cast " .. spellName, spellID)
end
end
end)
Converts value of given expression / variable into binary.
1nil, false) is 0synq.bin(conditions) : 1 | 0
Examples:
local bin = synq.bin
-- Convert boolean conditions to 1 or 0
print("Target is enemy (binary): " .. bin(target.enemy))
-- 1 if enemy, 0 if not
print("Pet exists (binary): " .. bin(pet.exists))
-- 1 if pet exists, 0 if not
-- Use in calculations
local rangeBonus = bin(target.distance < 30) * 10
-- Adds 10 if target is within 30 yards, 0 otherwise
-- Practical use case: adjust timing based on conditions
-- Only cast Intimidation if target has low DR or specific DR timing
if target.incapDR == 1 or target.idrRemains > 14 + bin(target.idr == 0.25) * 3.5 and target.idr >= 0.25 then
intimidation:Cast(target)
end
-- Without bin, you'd need a ternary-like expression
if target.incapDR == 1 or target.idrRemains > 14 + (target.idr == 0.25 and 3.5 or 0) and target.idr >= 0.25 then
intimidation:Cast(target)
end
Temporarily disables movement (and optionally facing) input from the player.
synq.controlMovement(duration[, facing])
See also: stopMoving spell :Cast option
Example:
-- In your BM Hunter actor, control movement to ensure important casts complete
-- Control movement for 2 ticks to prevent player from interrupting the cast
if player.castID == cobraShot.id and player.castTarget.los then
synq.alert("Controlling Movement (Cobra Shot)", cobraShot.id)
synq.controlMovement(synq.tickRate * 2)
end
Temporarily disables facing input from the player, including right mouse button movement and keyboard turning actions.
synq.controlFacing(duration)
Immediately stops the player from moving, as long as they're not in the air.
synq.StopMoving()
Creates a reference to each entry within given associative array (Param 1) inside of all other given tables, namespaces, scopes, etc. as well (Params 2 - N).
synq.Populate(aArray, t1[, t2, t3, t4, t5, ...])
This is a useful tool when used correctly, that allows you to 'merge' references to all values (including functions, objects, tables, etc.) between multiple tables - which can be namespaces, lists of objects, etc.
Example Usage:
local Unlocker, synq, example = ...
local bm = example.hunter.bm
local Spell = synq.Spell
-- Create a list of BM Hunter spells as an associative array
local spells = {
killCommand = Spell(34026, { damage = "physical", targeted = true }),
barbedShot = Spell(217200, { damage = "physical", ranged = true, targeted = true }),
cobraShot = Spell(193455, { damage = "physical", ranged = true, targeted = true }),
bestialWrath = Spell(19574, { damage = "physical" }),
}
-- Make references available to the actor and current file scope
-- First parameter: source list to copy references from
-- Remaining parameters: destinations where references will be copied
synq.Populate(spells, bm, getfenv(1))
-- Now bm actor can access all spells directly
-- And spells are available in this file's scope without local variables
killCommand:Callback(function(spell) spell:Cast(target) end)
barbedShot:Callback(function(spell) spell:Cast(target) end)
Note: The key parent will be written to each table within the associative array. Mainly to allow objects to find their siblings in the list. For example, all spell objects in a list automatically have direct access to each other within their :Callback function environments after Populate is called.
TL;DR: This thing puts a copy of all the stuff in one table into other tables.
Creates a reference to a pull timer that has been used by things like DBM or BigWigs. Defaults to 0 if no pull timer.
synq.pullTimer : 0
These are visual tools that may help to keep the user aware of what's happening, in style.
Displays a toast alert for the user.
Trying to recreate an identical alert while it's still displayed will just extend the duration, so feel free to spam it while it's relevant!
We included a lot of customization options so you can get the tone & delivery of your message across clearly, but we also want to keep the theme consistent with other alerts.
You can opt for as much or as little customization as you want. All that is required is a message, even the texture is optional.
Always returns true so you can include it as part of your conditional expressions for cleaner code.
synq.alert([message / {options}], [texture]) : true
Options:
{ r, g, b [,a] }Examples:
-- Simple text alert
synq.alert("Burst window active!")
-- Alert with spell texture (Bestial Wrath)
synq.alert("Bestial Wrath!", 19574)
-- Alert with colored text
synq.alert("Low Focus! " .. synq.colors.red .. "Use Cobra Shot", 193455)
-- Customized alert with background color
synq.alert({
message = "Burst! " .. synq.colors.yellow .. "All cooldowns active",
texture = 19574, -- Bestial Wrath icon
bgColor = synq.rgbColors.yellow
})
-- Custom colors for BM Hunter theme
synq.alert({
message = "Kill Command! |cFFFFD700Ready",
texture = 34026, -- Kill Command icon
bgColor = {255/255, 215/255, 0/255, 0.95} -- Gold color
})
-- Alert for Aspect of the Wild
synq.alert("Aspect of the Wild!", 193530)
-- Fine-tune texture position and scale
synq.alert({
message = "Bloodshed!",
texture = 321530, -- Bloodshed icon
imgX = 1,
imgY = 0.55,
imgScale = 0.875
})
Converts spellID or texture ID into a texture escape sequence string for use in alerts, other frames, or prints.
synq.textureEscape(spellID/FileDataID[, size, offsets])
Example:
-- Create texture escape sequence for Kill Command spell
-- Spell ID: 34026, size: 16px, offset: move up 2 pixels (format "x:y")
local killCmdTexture = synq.textureEscape(34026, 16, "0:2")
-- Use the texture in an alert and print statement
synq.alert(killCmdTexture .. " Casting Kill Command!", 34026)
print("Kill Command texture: " .. killCmdTexture .. " displayed in chat!")
-- Use with Barbed Shot
local barbedTexture = synq.textureEscape(217200, 20, "0:1")
synq.alert(barbedTexture .. " Maintaining Barbed Shot", 217200)
synq.Draw draws various things ingame.
local Draw = synq.Draw
Draw(function(draw)
draw:SetWidth(number) -- Set the width of the draw, such as the width of a Line
draw:SetColor(r, g, b, a) -- Set the color of the draw, such as the color of a Line
draw:Line(x1, y1, z1, x2, y2, z2, maxDistance) -- Draw a line from xyz1 to xyz2
draw:Circle(x, y, z, radius, steps) -- Draw a circle at xyz with a radius and steps
draw:Cylinder(x, y, z, radius, height) -- Draw a cylinder at xyz with a radius and height
draw:Arc(x, y, z, size, arc, rotation) -- Draw an arc at xyz with a size, arc degrees, and rotation
draw:Rectangle(x, y, z, width, length, rotation) -- Draw a rectangle at xyz
draw:Outline(x, y, z, radius) -- Draw an outline at xyz with a radius, basically a thick circle
draw:FilledCircle(x, y, z, radius, steps) -- Draw a filled circle at xyz with a radius and steps
draw:Triangle(x, y, z, v1, v2, v3, cull, wireframe) -- Draw a triangle at xyz with vertices
draw:Text(string, font, x, y, z) -- Draw a string of text using the defined font on coordinates
draw:Texture(config, x, y, z, alphaA) -- Draw a texture at xyz with an alpha value
end)
Some users may wish to store certain things in between reloads or per character. This can be accomplished by using the synq.config method.
local Unlocker, synq, example = ...
-- Create a new config file within SYNQ for this project
local config = synq.NewConfig("example")
-- Store the config globally so it can be accessed from other files
example.config = config
-- In another file (e.g., hunter/bm-actor.lua), retrieve the config
local Unlocker, synq, example = ...
local config = example.config
-- Store BM Hunter rotation settings
config.burstMode = true
config.autoPetHeal = true
config.savedPos = {
point = "CENTER",
x = 0,
y = 0,
}
local Unlocker, synq, example = ...
local name, realm = UnitFullName("player")
-- Create per-character config using player name and realm
local config = synq.NewConfig('example' .. name .. realm)
-- Store per-character BM Hunter preferences
config.burstThreshold = 80 -- Use burst when target HP is above this
config.focusThreshold = 50 -- Maintain focus above this value
config.petHealThreshold = 40 -- Heal pet when HP drops below this
-- Store rotation settings
config.rotationSettings = {
useBloodshed = true,
maintainSerpentSting = true,
autoDisengage = false
}
Any shallow write to the config will trigger a config save:
-- config.savedVar value is updated and saved to persist through reloads and restarts
config.savedVar = true
-- config.newSavedTable is updated and saved to persist
config.newSavedTable = { 'a', 'b' }
-- config.newSavedTable is updated for the current session, but is NOT saved and will not persist
table.insert(config.newSavedTable, 'c')
-- you must do a shallow write to the config table to save and persist
local function insertSubConfigValue(key, value)
-- nested write
tinsert(config[key], value)
-- shallow write saves and persists
config[key] = config[key]
end
-- saves and persists
insertSubConfigValue("newSavedTable", 'c')
Returns a SYNQ path object for navigation.
Path between player and coordinates:
local Unlocker, synq, example = ...
local bm = example.hunter.bm
bm:Init(function()
-- Create path to optimal ranged position (e.g., 30 yards from target)
if target.enemy and target.distance < 25 then
local optimalX, optimalY, optimalZ = target.x, target.y, target.z
-- Calculate position 30 yards away
-- ... position calculation logic ...
local path = synq.path(player, optimalX, optimalY, optimalZ)
path = path.simplify(1, 1)
-- Draw path for visualization (useful for debugging)
path.draw()
-- Follow the path to optimal range
path.follow()
end
end)
Path object is first return of synq.path - an array of nodes { x = 1093.3312, y = 1996.940, z = 92.01355 }
Contains some built in methods for manipulating and interacting with the path. Unlocker agnostic, should work nearly the same with any unlocker supported.
-- returns the path simplified with Douglas Peucker path simplification algorithm
path.simplify(tolerance, highestQuality)
-- draws the path for this tick
path.draw()
-- follow the path, moving your character along it each time called
path.follow()
General Tools provide the foundation for building sophisticated SYNQ routines with proper timing, event handling, visual feedback, configuration persistence, and navigation capabilities.
Key Takeaways:
buffer, tickRate, and latency for precise timingonTick, onUpdate, and onEvent for reactive codealert and Draw for user awarenessNewConfigsynq.pathThese tools work together to create robust, responsive routines that adapt to game conditions and provide clear feedback to users.
Next: Explore Advanced Routine Features to see these tools in action, or check out the SYNQ UI documentation.