Skip to content

Instantly share code, notes, and snippets.

@StinkyTwitch
Last active August 29, 2015 14:10
Show Gist options
  • Save StinkyTwitch/2ba6098796652884b552 to your computer and use it in GitHub Desktop.
Save StinkyTwitch/2ba6098796652884b552 to your computer and use it in GitHub Desktop.
AutoTarget Drop In Module
-- version: 0.9
-- This variable is here just for testing purposes while I/we verify the code works correctly in
-- all situations. Will eventually not be needed.
GRunAutoTargetCode = false
-- Our main table for storing the objects and their distance from the player.
-- The table looks like this: [index] [object_pointer_address] [distance_to_player]
-- The table is sorted by distance to the player.
-- To get the object pointer use "GUnitCache[i].key"
GUnitCache = {}
--[[------------------------------------------------------------------------------------------------
GLOBAL TABLE OF SPECIAL CASE TARGETS
* Credit: MrTheSoulz for the framework
--------------------------------------------------------------------------------------------------]]
GSpecialTargets = {
-- TRAINING DUMMIES
31144, -- Training Dummy - Lvl 80
31146, -- Raider's Training Dummy - Lvl ??
32541, -- Initiate's Training Dummy - Lvl 55 (Scarlet Enclave)
32542, -- Disciple's Training Dummy - Lvl 65
32545, -- Initiate's Training Dummy - Lvl 55
32546, -- Ebon Knight's Training Dummy - Lvl 80
32666, -- Training Dummy - Lvl 60
32667, -- Training Dummy - Lvl 70
46647, -- Training Dummy - Lvl 85
60197, -- Scarlet Monastery Dummy
67127, -- Training Dummy - Lvl 90
87761, -- Dungeoneer's Training Dummy <Damage>
88314, -- Dungeoneer's Training Dummy <Tanking>
88316, -- Training Dummy <Healing>
89078, -- Training Dummy (Garrison)
87318, -- Dungeoneer's Training Dummy <Damage>
-- WOD DUNGEONS/RAIDS
75966, -- Defiled Spirit (Shadowmoon Burial Grounds)
76518, -- Ritual of Bones (Shadowmoon Burial Grounds)
81638, -- Aqueous Globule (The Everbloom)
}
--[[------------------------------------------------------------------------------------------------
GLOBAL TABLE OF IMMUNE AURAS
* Credit: MrTheSoulz for the framework
--------------------------------------------------------------------------------------------------]]
GImmuneAuras = {
-- CROWD CONTROL
118, -- Polymorph
1513, -- Scare Beast
1776, -- Gouge
2637, -- Hibernate
3355, -- Freezing Trap
6770, -- Sap
9484, -- Shackle Undead
19386, -- Wyvern Sting
20066, -- Repentance
28271, -- Polymorph (turtle)
28272, -- Polymorph (pig)
49203, -- Hungering Cold
51514, -- Hex
61025, -- Polymorph (serpent) -- FIXME: gone ?
61305, -- Polymorph (black cat)
61721, -- Polymorph (rabbit)
61780, -- Polymorph (turkey)
76780, -- Bind Elemental
82676, -- Ring of Frost
90337, -- Bad Manner (Monkey) -- FIXME: to check
115078, -- Paralysis
115268, -- Mesmerize
-- MOP DUNGEONS/RAIDS
106062, -- Water Bubble (Wise Mari)
110945, -- Charging Soul (Gu Cloudstrike)
116994, -- Unstable Energy (Elegon)
122540, -- Amber Carapace (Amber Monstrosity - Heat of Fear)
123250, -- Protect (Lei Shi)
143574, -- Swelling Corruption (Immerseus)
143593, -- Defensive Stance (General Nazgrim)
}
--[[------------------------------------------------------------------------------------------------
CHECK SPECIAL TARGET
* Credit: MrTheSoulz for the framework
--------------------------------------------------------------------------------------------------]]
function CheckSpecialTarget(unit)
-- Check if the unit supplied exists.
if not UnitExists(unit) then
return false
-- If the unit exists then check to see if it has a valid GUID.
elseif UnitGUID(unit) then
-- Take the GUID and strip it out for the ID and convert it to a number.
targets_guid = tonumber(string.match(UnitGUID(unit), "-(%d+)-%x+$"))
-- If the GUID is not valid default to 0.
else
targets_guid = 0
end
-- Loop through the special targets Global table.
for i=1, #GSpecialTargets do
-- If we find a matching GUID between our target and the table then this is a special target.
if targets_guid == GSpecialTargets[i] then
return true
end
end
-- Otherwise we return false, not a special target.
return false
end
--[[------------------------------------------------------------------------------------------------
CHECK AURA TABLE
* Credit: MrTheSoulz for the framework
--------------------------------------------------------------------------------------------------]]
function CheckAuraTable(unit, spells_table)
-- Loop through the number of possible buffs on a target (40). Will break whenever it gets to
-- the end of the buff list (1st nil value). Will not ALWAYS scan through 40.
for i = 1, 40 do
-- Get the spellID of each buff on the unit.
local _,_,_,_,_,_,_,_,_,_,spell_id = UnitAura(unit, i)
-- Loop through our Immune CC Spell table (v) for each unit scanned (k).
for k,v in pairs(spells_table) do
-- If we find a match between the unit and our spell table then return true.
if spell_id == v then
return true
-- Otherwise return false
else
return false
end
end
end
end
--[[------------------------------------------------------------------------------------------------
CHECK IMMUNE TARGET
* Credit: MrTheSoulz for the framework
--------------------------------------------------------------------------------------------------]]
function CheckImmuneTarget(unit)
-- If we can't attack the 'unit' then the target is considered immune.
if not UnitCanAttack("player", unit) then
return true
-- If the 'unit' is not in combat and not a special target then it is considered immune.
elseif not UnitAffectingCombat(unit) and not CheckSpecialTarget(unit) then
return true
-- If the 'unit' is effected by CC then it is considered immune.
elseif CheckAuraTable(unit, GImmuneAuras) then
return true
-- Otherwise it is not an immune target.
else
return false
end
end
--[[------------------------------------------------------------------------------------------------
CHECK TARGET IS INFRONT
* Credit: MrTheSoulz for the framework
--------------------------------------------------------------------------------------------------]]
function CheckTargetIsInFront(unit)
-- 3d position of target.
local aX, aY, aZ = ObjectPosition(unit)
-- 3d position of player.
local bX, bY, bZ = ObjectPosition("player")
-- Get the "players" orientation in radians. 0 being North.
local player_facing = GetPlayerFacing()
-- The orientation of in terms of North between the "player" and "target".
local facing = math.atan2(bY - aY, bX - aX) % 6.2831853071796
-- Check if the "player" is within a 90 degree arc of facing the "target". Imagine drawing a
-- straight line from the center of the "player" to the center of the "target". Then check if
-- the "players" current orientation (field of view for attacking) is within 90 degrees of that.
return math.abs(math.deg(math.abs(player_facing - (facing))) - 180) < 90
end
--[[------------------------------------------------------------------------------------------------
AUTO TARGET
* Credit: MrTheSoulz for the framework
--------------------------------------------------------------------------------------------------]]
function AutoTarget()
-- Check if the "player" already has a target and that target is not dead or a ghost.
if UnitExists("target") and not UnitIsDeadOrGhost("target") then
return false
end
-- Loop through the object table.
for i=1,#GUnitCache do
-- Check that the current object in the table (GUnitCache[i].key) is not immune.
if not CheckImmuneTarget(GUnitCache[i].key) then
-- Check that the current object in the table (GUnitCache[i].key) is in front of the
-- player.
if CheckTargetIsInFront(GUnitCache[i].key) then
-- Run the macro "/target OBJECT". Because we now sort Object table based on
-- distance this will always select the nearest VALID target. Much joy was had in
-- writting this!
return Macro("/target "..GUnitCache[i].key)
end
end
end
-- Otherwise auto target should fail.
return false
end
--[[------------------------------------------------------------------------------------------------
UNIT CACHE
* Credit: Mirakuru and MrTheSoulz for the framework
--------------------------------------------------------------------------------------------------]]
function Cache()
-- Wipe the table each read.
wipe(GUnitCache)
-- Get the total number of objects in the "player's" sphere of influence.
local total_objects = ObjectCount()
-- Loop through the total number of objects.
for i=1, total_objects do
-- Assign the pointer for the current object.
local object = ObjectWithIndex(i)
-- If the current object exists...
if ObjectExists(object) then
-- If the current object is a unit, is within 40 yards of the player and is considered
-- alive then...
if ObjectIsType(object, ObjectTypes.Unit)
and ProbablyEngine.condition["distance"](object) <= 40
and ProbablyEngine.condition["alive"](object)
then
-- Get the player's position.
local x1, y1, z1 = ObjectPosition("player")
-- Get the object's position.
local x2, y2, z2 = ObjectPosition(object)
-- Trig.
local dx = x2 - x1
-- Trig.
local dy = y2 - y1
-- Trig.
local dz = z2 - z1
-- Distance from player to target in yards.
local distance = math.sqrt((dx*dx) + (dy*dy) + (dz*dz))
-- Store the object's pointer address as a string.
local obj_text = tostring(object)
-- Insert the VALID objects pointer address and it's distance from the player
-- into the cache table.
table.insert(GUnitCache, {key=obj_text, value=distance})
-- Sort the table so that the closest VALID objects are first.
table.sort(GUnitCache, function(a,b) return a.value < b.value end)
end
end
end
end
--[[------------------------------------------------------------------------------------------------
CACHE UPDATE TIMER
* Credit: MrTheSoulz
* Requires that you have an 'autotarget' custom toggle in your rotation file:
ProbablyEngine.toggle.create(
'autotarget',
'Interface\\Icons\\ability_hunter_snipershot',
'Auto Target',
'Automatically target the nearest enemy when target dies or does not exist'
)
* Requirement #2: You need to add the following to your rotation somewhere in the function for
custom toggles, "GRunAutoTargetCode = true". This turns on the code for the ticker. You will
still be able to turn on/off auto targetting using the toggle. This is only necessary as I test
this new code out so that I can stop it from running if its breaking.
This is what your custom toggle code should look like at the end of your rotation if it was the
only toggle.
function()
ProbablyEngine.toggle.create(
'autotarget',
'Interface\\Icons\\ability_hunter_snipershot',
'Auto Target',
'Automatically target the nearest enemy when target dies or does not exist'
)
GRunAutoTargetCode = true
end
--------------------------------------------------------------------------------------------------]]
C_Timer.NewTicker(0.1, (function()
if GRunAutoTargetCode then
if ProbablyEngine.config.read('button_states', 'MasterToggle', false)
and ProbablyEngine.module.player.combat
then
if ProbablyEngine.config.read('button_states', 'autotarget', false) then
AutoTarget()
end
Cache()
end
end
end), nil)
* Requires that you have an 'autotarget' custom toggle in your rotation file:
ProbablyEngine.toggle.create(
'autotarget',
'Interface\\Icons\\ability_hunter_snipershot',
'Auto Target',
'Automatically target the nearest enemy when target dies or does not exist'
)
* Requirement #2: You need to add the following to your rotation somewhere in the function for
custom toggles, "GRunAutoTargetCode = true". This turns on the code for the ticker. You will
still be able to turn on/off auto targetting using the toggle. This is only necessary as I test
this new code out so that I can stop it from running if its breaking.
This is what your custom toggle code should look like at the end of your rotation if it was the
only toggle.
function()
ProbablyEngine.toggle.create(
'autotarget',
'Interface\\Icons\\ability_hunter_snipershot',
'Auto Target',
'Automatically target the nearest enemy when target dies or does not exist'
)
GRunAutoTargetCode = true
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment