WoWInterface

WoWInterface (https://www.wowinterface.com/forums/index.php)
-   AddOn Help/Support (https://www.wowinterface.com/forums/forumdisplay.php?f=3)
-   -   A more efficient 'enemies in range'? (https://www.wowinterface.com/forums/showthread.php?t=58354)

thatrickguy 10-28-20 08:15 PM

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


DahkCeles 10-29-20 07:17 AM

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

Vrul 10-29-20 10:58 AM

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


jeruku 10-29-20 11:10 AM

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

thatrickguy 10-29-20 11:11 AM

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.

Vrul 10-29-20 03:35 PM

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.

thatrickguy 10-29-20 08:42 PM

Nice. Yeah, this is throttled to once ever 100ms.

Thanks!
-Rick


All times are GMT -6. The time now is 04:32 AM.

vBulletin © 2021, Jelsoft Enterprises Ltd
© 2004 - 2020 MMOUI