Quantcast
A more efficient 'enemies in range'? - WoWInterface
Thread Tools Display Modes
10-28-20, 08:15 PM   #1
thatrickguy
A Murloc Raider
Join Date: Sep 2020
Posts: 5
A more efficient 'enemies in range'?

So this is what I have right now for seeing if there are 3+ enemies in combat and within range of whirlwind. Is there a more elegant solution? Broken out into multiple ifs just for debugging purposes.

Code:
function are3EnemiesInRange()
   local inRange = 0
   local id
   for i = 1, 40 do
      id = "nameplate" .. i
      --local message = ""
       if UnitExists(id) then
         --message = id .. " "
         if UnitAffectingCombat(id) then
           -- message = message .. "in combat "
            if UnitIsEnemy("player",id) then
              -- message = message .. "is hostile "
               if IsItemInRange(63427, id) == true then
                 -- message = message .. "in range"
                  inRange = inRange + 1
               end
            end
         end
      end
      -- if (message ~= "") then
      --    print (message)
      -- end 
      if (inRange > 2 ) then
         break
      end
   end
   
   return (inRange > 2)
end
  Reply With Quote
10-29-20, 07:17 AM   #2
DahkCeles
An Aku'mai Servant
 
DahkCeles's Avatar
AddOn Author - Click to view addons
Join Date: Jun 2020
Posts: 32
I don't know how nameplate numbering works, but if it always starts at 1 then maybe you could add this optimization:

Lua Code:
  1. for i = 1, 40 do
  2.     id = "nameplate" .. i
  3.     if UnitExists(id) then
  4.         -- etc...
  5.     else
  6.         break;
  7.     end
  8. end
  Reply With Quote
10-29-20, 10:58 AM   #3
Vrul
An Onyxian Warder
 
Vrul's Avatar
AddOn Author - Click to view addons
Join Date: Nov 2007
Posts: 368
UnitCanAttack should be used insteadof UnitIsEnemy and UnitAffectingCombat since Whirlwind will hit a unit whether it is in combat or not. UnitExists is implied if any Unit function returns true so it can be removed. Also, inRange only updates in one place so you should check it's value right after instead of at the end of the loop where it may not have changed.
Code:
function are3EnemiesInRange()
   local inRange = 0
   for id = 1, 40 do
      local unitID = "nameplate" .. id
      if UnitCanAttack("player", unitID) and IsItemInRange(63427, unitID) then
         inRange = inRange + 1
         if inRange >= 3 then return true end
      end
   end
end
  Reply With Quote
10-29-20, 11:10 AM   #4
jeruku
A Cobalt Mageweaver
 
jeruku's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2010
Posts: 218
You can also use C_NamePlate.GetNamePlates to get only visible nameplates. A plus to this is it gives you the nameplate.

Lua Code:
  1. function are3EnemiesInRange()
  2.     local inRange, unitID = 0
  3.     for _, plate in pairs(C_NamePlate.GetNamePlates()) do
  4.         unitID = plate.namePlateUnitToken
  5.         if UnitCanAttack("player", unitID) and IsItemInRange(63427, unitID) then
  6.             inRange = inRange + 1
  7.             if inRange >= 3 then return true end
  8.         end
  9.     end
  10. end
__________________
"I have not failed, I simply found 10,000 ways that did not work." - Thomas Edison
  Reply With Quote
10-29-20, 11:11 AM   #5
thatrickguy
A Murloc Raider
Join Date: Sep 2020
Posts: 5
Perfect Vrul! I wasn't aware of UnitCanAttack, between that and removing the exists check I should be able to drop some time off the function.

DahkCeles - nameplates is a fixed array, it doesn't compress or expand as nameplates are added or removed. If nameplate1 points to a mob, and that mob dies, nameplate1 becomes a null even though nameplate2 may still point to a mob.
  Reply With Quote
10-29-20, 03:35 PM   #6
Vrul
An Onyxian Warder
 
Vrul's Avatar
AddOn Author - Click to view addons
Join Date: Nov 2007
Posts: 368
If you value efficiency over elegance then you want a slightly modified version of what jeruku posted:
Code:
function are3EnemiesInRange()
   local inRange, nameplates = 0, C_NamePlate.GetNamePlates()
   for index = 1, #nameplates do
      local unit = nameplates[index].namePlateUnitToken
      if UnitCanAttack("player", unit) and IsItemInRange(63427, unit) then
         if inRange > 1 then return true end
         inRange = inRange + 1
      end
   end
end
If the garbage churn is too much then this is an alternative:
Code:
local pairs_iter, nameplates = pairs(NamePlateDriverFrame.pools.pools.NamePlateUnitFrameTemplate.activeObjects)

function are3EnemiesInRange()
   local inRange = 0
   for nameplate in pairs_iter, nameplates, nil do
      if UnitCanAttack("player", nameplate.unit) and IsItemInRange(63427, nameplate.unit) then
         if inRange > 1 then return true end
         inRange = inRange + 1
      end
   end
end
This is more efficient than my previous post's code for a lower number of nameplates. My previous post's code is more efficient the closer you are to 40 nameplates. I'm not sure where the break even point is but my guess is the top code in this post is better in the long run as long as its not in an unthrottled OnUpdate script.
  Reply With Quote
10-29-20, 08:42 PM   #7
thatrickguy
A Murloc Raider
Join Date: Sep 2020
Posts: 5
Nice. Yeah, this is throttled to once ever 100ms.

Thanks!
-Rick
  Reply With Quote

WoWInterface » AddOns, Compilations, Macros » AddOn Help/Support » A more efficient 'enemies in range'?

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off