Core Concepts

Object Lists

Learn how to work with filtered arrays of SYNQ objects for enemies, friends, and more

Object Lists are arrays containing SYNQ objects and their expected attributes and functions. Each list is filtered based on its own specific criteria, making it easy to work with groups of units, players, or objects in your routines.

What are Object Lists?

Think of object lists as smart collections that automatically filter game objects based on specific rules. Instead of manually checking every unit in the game, you can use pre-filtered lists like synq.enemies or synq.friends to get exactly what you need.

Performance

Most lists remain completely empty until referenced. They're generated on-demand by filtering the unlocker's Object Manager (OM), then cached for constant time re-reads the rest of the current 'tick'. This means:

  • Lazy loading – Lists only populate when you actually use them
  • Cached per tick – Once generated, they're fast to access multiple times
  • Memory efficient – Empty lists take up minimal resources

Freshness

You must always grab lists fresh where you need them. You can't declare them locally outside of your working scope like you can static objects, or they'll become stale.

Good:

bm:Init(function()
    synq.enemies.loop(function(enemy)
        -- List is generated fresh every tick - always up to date
    end)
end)

Bad:

local enemies = synq.enemies -- Stale after first tick - don't do this!
bm:Init(function()
    enemies.loop(function(enemy) -- Using old data from first tick - incorrect!
    end)
end)

Generally you'll want to access them directly from the synq namespace, e.g., synq.enemies. Though there are a couple of exceptions where they can be accessed by name directly (like enemies or group), namely inside the Routine Actor and your Spell Object :Callback functions.

Magic Methods

Object Lists contain several Magic Methods which allow you to do powerful searches, iterations, and more with the objects contained. Some examples:

  • around – finds objects from the list that are within a given distance of another SYNQ object
  • loop – an iteration alternative to standard for loops, allowing you to mimic and reap the benefits of a continue statement to write cleaner, significantly less indented code

We'll cover all the magic methods in detail below.

Available Lists

group

Other players in our party or raid, not including the player.

See also: fgroup - Players in our group, including the player. (fullGroup)

Usage: Players in your party or raid.

-- Basic pet healing maintenance for BM Hunter
mendPet:Callback(function(spell)
    -- Check pet health and heal when needed
    if pet.exists and pet.hp < 50 then
        return spell:Cast(pet)
    end
end)

enemies

Contains enemy units that are relevant to the player's current environment.

  • Arena / Battleground: Enemy players only
  • PvE / Open World: Enemy units that are in combat or targeting a player, or enemy players that are targeting or attacking the player

Note: Explosives are excluded from enemies and can be found in synq.explosives.

-- Basic Serpent Sting maintenance on enemies (BM Hunter)
serpentSting:Callback("maintain", function(spell)
    synq.enemies.loop(function(enemy)
        -- Skip enemies that already have Serpent Sting with more than 4 seconds remaining
        -- dbr is shorthand for debuffRemains
        if enemy.dbr(spell.id, player) > 4 then return end
        -- Cast Serpent Sting on enemy and break loop on success
        return spell:Cast(enemy)
    end)
end)

friends

Contains all friendly players within render distance.

Everywhere: All friendly players, even ones that are not in your group. This can be a needlessly large list in the open world.

-- Cast heal on any friend below 40% health
synq.friends.loop(function(friend)
    -- Skip friends not in combat or above 40% health
    if not friend.combat or friend.hp > 40 then return end
    -- Cast heal on friend and break loop on success
    return spell:Cast(friend)
end)

dead

Contains all targetable dead units within render distance.

-- Cast battle res on a dead friendly player
synq.dead.loop(function(unit)
    -- Skip units that aren't friendly
    if not unit.friend then return end
    -- Skip units that aren't players (only res players, not NPCs)
    if not unit.player then return end
    -- Cast battle res and break loop on success
    return spell:Cast(unit)
end)

totems

Contains enemy totems or related objects (PvP).

Tip: The list of totems and corresponding object IDs that populate this list can be found in lists.lua near SYNQ loader.

synq.totems.stomp(function(totem, uptime)
    -- Only target totems that have been up for at least 300ms
    -- This prevents targeting totems that are still spawning or despawning
    if uptime < 0.3 then return end
    -- Cast spell on totem to destroy it
    return spell:Cast(totem)
end)

explosives

Contains M+ affix explosive objects, doesn't appear in synq.enemies.

someSpell:Callback("kill explosives", function(spell)
    synq.explosives.loop(function(explosive)
        return spell:Cast(explosive)
    end)
end)

objects

Contains all objects of type GameObject.

-- Find and interact with a soulwell object
synq.objects.loop(function(obj)
    if obj.name == "Soulwell" then
        -- Interact with the soulwell when found
        obj:Interact()
    end
end)

-- Find a specific quest object by ID
local quest_object_id = 123
synq.objects.loop(function(obj)
    if obj.id == quest_object_id then
        print("Found quest object with ID: " .. quest_object_id)
        -- Return true to break the loop once we find what we're looking for
        return true
    end
end)

Magic Methods

Magic Methods are available for every single SYNQ object list. You can make them available to a normal indexed array with synq.immerseOL(table).

All Magic Methods return true, so you can weave them into conditional statements if you want.

around

Find the number of units around a unit or position. You can set specific criteria (filter) they must meet to be added to the count. Receive a new list back with the units around.

local count, total, objects = list.around(unit, distance, criteria)

Parameters:

  • unit (SYNQ object) or position {x, y, z}: required - the unit or position to check
  • distance: number, required - the distance from the given unit/position
  • criteria: function, optional - criteria to check against each object

Returns:

  • main count: number of units around who met the criteria
  • total count: total number of units around
  • objects: array of objects that were around and met criteria
-- Check for breakable crowd control around player before using brutal slash
local bcc, bsCount = enemies.around(player, 8, function(obj) return obj.bcc end)
-- bcc = count of enemies in breakable CC, bsCount = total enemies around player
-- Only cast if no enemies are in breakable CC and we'll hit at least 2 targets
if bcc == 0 and bsCount >= 2 then
    brutalSlash:Cast()
end

filter

The filter method creates a new object list containing all objects that pass the test implemented by the provided function.

-- Filter enemies to only include melee role enemies
local enemyMelee = enemies.filter(function(obj)
    return obj.role == "melee"
end)

-- Loop through filtered melee enemies
enemyMelee.loop(function(obj)
    -- Process each melee enemy here
end)

loop

Iterate the object list, calling the provided function for each object. When any truthy value is returned, the loop will break.

Function is passed 3 arguments:

  • object - each object in the list
  • index - current index in the iteration
  • uptime - the amount of time since we first iterated over this object

Benefits:

  • Arguably better solution than for loops for iterating object lists
  • Allows you to mimic the functionality of a continue statement by simply eliminating conditions with a return
  • Reduces code indentation and complexity significantly
-- This accomplishes complex logic with clean, readable code using guard clauses
list.loop(function(unit)
    -- Skip if we don't have hot streak or combustion buffs
    if not hotStreak and not combustion then return end
    -- Skip if unit isn't enemy, too far away, or out of line of sight
    if not unit.enemy or unit.distance > 40 or not unit.los then return end
    -- Skip if unit is immune to magic or is our current target
    if unit.immuneMagic or unit.isUnit(target) then return end
    -- Skip if unit is facing player or help flag is not set
    if unit.facing(player) or not helpMePlease then return end
    -- Cast spell and show alert on success
    return spell:Cast(unit) and synq.alert("Casted the thing!", spell.id)
end)

-- Loop has extra features like uptime tracking built in synq.friends.loop(function(unit, i, uptime) -- uptime is 0 when we first detect a unit, useful for detecting new units if uptime == 0 then print("New friendly unit detected: " .. unit.name .. " (" .. unit.classString .. ")") end end)


### stomp

Very similar to `loop`, but with a couple of key differences:

1. Eliminates totems that are stuck at 1 hp or in the process of despawning from the iteration
2. Does not give back index - only object and uptime

```lua
synq.totems.stomp(function(totem, uptime)
    -- Only target totems that have been up for at least 250ms
    if uptime < 0.25 then return end
    -- Cast spell if totem will die from this cast (damage is more than half its HP)
    if totem.hp < spell.damage * 2 then
        spell:Cast(totem)
    end
end)

sort

Sorts the list using basic Lua table.sort. Not very performant, should be used sparingly.

synq.enemies.sort(function(x, y) return x.hp < y.hp end)

Custom Object Lists

You can create custom object lists that contain all other SYNQ object list methods. They only populate themselves when you attempt to read some value in them, like list.loop, list.filter, list[1] etc. Otherwise, they remain empty and do nothing.

synq.List:New(objectTypes, constructor)

Example

local Unlocker, synq, example = ...

-- Create custom list for units (type 5) and players (type 6)
-- The constructor function filters objects - only includes Hunters and Warlocks
example.rangedDPS = synq.List:New({ 5, 6 }, function(object, objectType, guid)
    -- Skip objects that are in our blacklist
    if object.id and blacklist[object.id] then return end
    -- Only include Hunters and Warlocks in this custom list
    return object.class2 == "HUNTER" or object.class2 == "WARLOCK"
end)

-- Use the custom list just like any other SYNQ object list
example.rangedDPS.loop(function(unit)
    print("Ranged DPS unit: " .. unit.name .. ", Health: " .. unit.health)
end)

Caveats

  • Population creates a SYNQ object for every object of type(s) passed, which could be memory intensive
  • Length operator #list will not trigger population of the list. Use list.length instead

Summary

Object lists are a powerful feature that make complex target selection and iteration tasks simple and performant in your SYNQ routines. They provide:

  • Automatic filtering – Pre-filtered lists for common use cases
  • Powerful iteration – Clean, readable code with magic methods
  • Performance optimized – Lazy loading and per-tick caching
  • Extensible – Create custom lists for your specific needs

Next: Learn about all available Object Attributes for accessing unit information.