• Dear Guest,

    You're browsing our forum as a Guest meaning you can only see a portion of the forum in read-only mode.
    To view all forum nodes and be able to create threads/posts please register or log-in with your existing account.

    TwinStar team

Luna Unit Frames (an edit of v. 2008 beta)

Duplicate names shouldn't be an issue except maybe with pets because there's a check to ensure the target is friendly:
elseif not UnitExists'target' or UnitIsFriend'target'
You are absolutely right.

Worked fine in 10man, visibly choppy in 40man with 1 sec refresh. :(
Last edited:
Couldn't you just get the name with UnitName'target' before the loop and then retarget with TargetByName(name, true) when finished? Seems pointless to keep retargeting 40 times while looping through the raid.

i tried that a year ago when i was playing with the 40yrd range and it didnt work well for some reason, but i dont remember why...will try out with a UnitID buffer later :)

edit: LUF does not update TOT and TOTOT onupdate btw, it opens every x seconds
Then one should choose a refreshrate that makes them out of sync, I guess. I noticed that LUF's Units.lua has a pauseUpdates, so I tried using that before the loop above and resuming after, but I could still see TOT and TOTOT "blinking" (appear and reappear in a fraction of a second).

As for choppiness, maybe it would work if one scanned one group of five on each refresh instead of the whole raid?
i tried the version without TargetLastTarget() and it works fine :) will test this in a big raid today, the TargetLastTarget() worked fine last saturday :) - updates 1 times a sec.

idk if its my modded luna, but i dont have any problems with choppy frames Oo


code: http://pastebin.com/LXxBrCyH
This is what I use for range.lua at the moment. It is... not horrible. But still.. when someone walks out of the 40yd range, the frame does not update with new alpha. Anyone have an idea why? I didn't really look into the proximityLib.

local Range = {}
LunaUF:RegisterModule(Range, "range", LunaUF.L["Range"])
local proximity = ProximityLib:GetInstance("1")

local within40yd = {}
local lastUpdate = 0

    local f = CreateFrame'Frame'
    f:SetScript('OnEvent', function()this[event]() end)

    CreateFrame('GameTooltip', 'RangeCheckTooltip', nil, 'GameTooltipTemplate')

    local spell, slot

        function f.UPDATE()
            local Time = GetTime()
            if this.lastUpdate==nil then this.lastUpdate=0 end
            if (this.lastUpdate < Time) then
                local openFrame = (InspectFrame and InspectFrame:IsVisible()) or (LootFrame and LootFrame:IsVisible()) or (XLootFrame and XLootFrame:IsVisible()) or (TradeFrame and TradeFrame:IsVisible())
                local enemyTargeted = UnitExists('target') and not UnitIsFriend('target','player')
                local str, unitID
                if GetNumRaidMembers() > 0 then
                    str = "raid"
                elseif GetNumPartyMembers() > 0 and not UnitInRaid('player') then
                    str = "party"
                    this.lastUpdate = Time + 5;
                if not (openFrame or enemyTargeted) then
                    local oldTargetName,_ = UnitName('target')
                    for i = 1,  (GetNumRaidMembers() or GetNumPartyMembers()) do
                        unitID = str..i
                        if UnitIsConnected(unitID) and (not UnitIsDeadOrGhost(unitID)) and UnitIsVisible(unitID) and UnitIsFriend('player',unitID) then --because Mind Control?
                            if CheckInteractDistance(unitID, 4) then
                                within40yd[unitID] = GetTime()
                                within40yd[unitID] = slot and IsActionInRange(slot) and GetTime()
                    if oldTargetName then TargetByName(oldTargetName,true) else ClearTarget() end
                this.lastUpdate = Time + REFRESH_INTERVAL;

        local newSlot
        for i = 1, 120 do
            RangeCheckTooltip:SetOwner(UIParent, 'ANCHOR_NONE')
            if RangeCheckTooltipTextLeft1:GetText() == spell then
                newSlot = i
        slot = newSlot
        if not slot then f:SetScript('OnUpdate', nil) else f:SetScript('OnUpdate', f.UPDATE) end

    function f.PLAYER_LOGIN()
        spell = ({ Paladin='Holy Light', Priest='Flash Heal', Druid='Healing Touch', Shaman='Healing Wave' })[UnitClass'player']
        if spell then
            f:SetScript('OnUpdate', f.UPDATE)

local function OnUpdate()

function Range:OnEnable(frame)
    if not frame.range then
        frame.range = CreateFrame("Frame", nil, frame)
    frame.range:SetScript("OnUpdate", OnUpdate)

function Range:OnDisable(frame)
    if frame.range then
        frame.range:SetScript("OnUpdate", nil)

function Range:FullUpdate(frame)
    if frame.DisableRangeAlpha then return end
    local range, lastseen = proximity:GetUnitRange(frame.unit)
    if (not range or range > 30) and within40yd[frame.unit] then
       range, lastseen = 40, within40yd[frame.unit]
    if range and ((GetTime()-lastseen) < REFRESH_INTERVAL + 1 ) then
        if range <= 30 then
            frame:SetAlpha(LunaUF.db.profile.units[frame.unitGroup].fader.enabled and LunaUF.db.profile.units[frame.unitGroup].fader.combatAlpha or 1)
        elseif range <= 40 and slot then
            frame:SetAlpha(LunaUF.db.profile.units[frame.unitGroup].fader.enabled and LunaUF.db.profile.units[frame.unitGroup].fader.combatAlpha or .8)
It's not proximityLib, it still doesn't update with this code:

function Range:FullUpdate(frame)
    if frame.DisableRangeAlpha then return end
    local lastseen = within40yd[frame.unit]
    if lastseen and ((GetTime()-lastseen) < REFRESH_INTERVAL + 1 ) then
        frame:SetAlpha(LunaUF.db.profile.units[frame.unitGroup].fader.enabled and LunaUF.db.profile.units[frame.unitGroup].fader.combatAlpha or 1)

I think I got it, it should be IsActionInRange(slot)==1 on line 43. There is still the problem of resetting the portrait animation, though. It looks very annoying.
Last edited:
This seems to be the relevant part in portrait.lua:
local function resetCamera()
Not too elegant but I guess you could just set a global boolean before you start the scan and add a check for it to this function.
I already used Units.pauseUpdates for this purpose. Not sure if that's very smart, but before the loop I pause updates and I unpause after the loop is done (I think I will change this). However, this does not work with the resetCamera() function. If you make this function empty, it still resets (because the function is called OnShow, I guess) I think one should instead never execute frame:Hide() for the target frames if the target is changed during the loop.

I think this can be accomplished by changing (i.e. disabling) Portrait:OnDisable(frame) in portrait.lua or perhaps Portrait:FullUpdate(frame) and Portrait:Update(frame). I am not sure which ones are called when we cycle through targets. I will do some more testing.
Last edited:
It's all yours my friend: https://www.mediafire.com/?lp26196w60krk58 (v. 0.3 beta)
As before, the password is Luna.

The 40yd check is now implemented. It is not fully tested, expect strange things. As discussed here, it will not refresh the range if you have an enemy targeted, if you have trade/loot/inspect windows open, if you are not a class that can heal or if you don't have your main healing spell on one of your actionbars. If you want to thank someone, thank the good people of this thread who contributed, bit and Renew in particular.

Next step is to optimize this, I guess. If graded alpha is disabled, there is really no point in using ProximityLib at all.
Last edited:
Another thing I noticed is that sometimes that annoying sound that plays when a target is acquired/cleared plays over and over again. It doesn't happen all the time, but when I noticed it, it was when the rest of the raid was not visible to the client (I was on the other continent). So perhaps one needs to override some of the game's functions. Please keep an eye out for this and tell me if you learn anything new.
Here's one possible fix. Download this file: http://www.mediafire.com/file/9626jrjlrqxn8r6/silent_targets.zip
and copy the contents into the main WoW folder.

For some reason, I couldn't use the minimal .wav files for this because they kept crashing the client. These have some tiny amount of sound in them, but you shouldn't hear anything. If someone finds a way to modify an empty wav so that it doesn't crash the game, I'd be happy to hear about it.
hey guys, i found out that you need 2 scale factors (X and Y) for the range calculation over the mapposition...therefor the sraidframes outdoor range check can be complete wrong!

im working on a complete list atm, so outdoor range check can be applied for luna in a few lines! without flickering frames etc!


the complete calculation in 3 lines!

px,py = GetPlayerMapPosition("player")ux,uy = GetPlayerMapPosition(UnitID)
distance = sqrt(((px - ux)/scaleX)^2 + ((py - uy)/scaleY)^2)

i will post the scale database soon :)
Do you have to work out each zone's scale by experimenting? God blessed you with patience, I see. :)
Do you have to work out each zone's scale by experimenting? God blessed you with patience, I see. :)
well, i measure the x and y way (must be exact x and y) and determinate the scale over the checkdistance function on a flat ground :)

here an example with x-> min y-> max => scale for y

so yea, it is a loooot of work, but i hope to finish this on monday - doing this on my own seerver with teleport function

EDIT: here is the data (all areas has been tested)
Last edited:
here is an update, including battlegrounds!


a full example to calculate the range in yrds

dont forget:

the 40yrds are measured on flat ground and are exact up to 0.1yrds but you should use a save zone of -2/3 yrds

Last edited:
Are the zones really numbered like that? Battlegrounds are, of course, the most important zones for this thing. I will try to work this into it and fix a few other things.
Last edited:
Are the zones really numbered like that? Battlegrounds are, of course, the most important zones for this thing. I will try to work this into it and fix a few other things.

yes they are :) each zone has a unique ID number in its continent
Renew, I tested this in DunMorogh by testing Frostbolt range with two characters and I got
which I think is slightly better. Yours was off by .8yd or so versus the 30yd spell-range check. I meant to ask you how exactly did you measure this. You teleported your char to exact coords in a flat area? That's very nice, I don't have access to that. But how did you measure the actual yards? Did you teleport another char to the second location with the same x-coord (resp. y-coord)? Also, what happens when one of the UnitID's is offline?

I'm still not sure how to implement this. Maybe as a separate BG-only module for a start? If you use this in BG's and the world, you can make the range indicator very sophisticated, having alpha move from some small value to 1 as you get closer. The indicators as they are now only cover three ranges (1-29, 29-40 and 40+).

I made a mistake in the code in the last edition and the range indicators didn't work in 5man groups. This should be fixed now (not tested). I also added icons to the castbar. Here's the link: https://www.mediafire.com/?wcvl7hgbdpkf11m
Last edited:
nope, i teleported to a flat area or a lake (if it was avalible) and measured the x and y distance between the 2 characters

px, py = GetPlayerMapPosition("player")
ux, uy = GetPlayerMapPosition(UnitID )
distancey = py - uy
distancex = px - ux

then i devided this by 28 (wich is the distance i can get by CheckInteractDistance()

and printed it out, so i can move eather x or y to min and get the max. value( tried to reach exactly 28yrds)

if CheckInteractDistance("party1",4) then print("x: "..distancex/28); print("y: "..distancey/28) end

i also tested every area after recording and did not measure such difference...
tested in dun morogh, check the healing spell in slot 3:

make sure you dont stand in a hill etc, like i posted on my picture before...use 37-38yrds as threshold for the addon
Well, here's what I get with those numbers:

It reports more than 30yd, and terrain can only add to this, but Frostbolt is still in range. Could it be that the values are different on Kronos?

I still think you can be more precise; Here's what I did just now. Make a dummy char and fix his location. Log main char and make a frame that checks OnUpdate when CheckInteractDistance() against the dummy char changes value; when this happens, record your coordinates. When you have two points at which this happens (that aren't colinear or close to) and the third, fixed point of the dummy char, you have enough information to exactly recover the rescale values. I will try to test this in water and see how accurate it is. I'll get back to you :)

Do you know what is the exact value of CheckInteractDistance(unit,3)? Is it 10? 9.9? It would help to use smaller distances for higher precision.
Last edited:
well, i checked this with 30yrds and youre right...it seems this is one of the downsides of ingame measurement :)

also it seems that bigger distance is lowering this effect...the sweetpoint is @40yrds and that what we want to get :)

im pretty sure a lower CheckInteractDistance will not help you there, try out other scales factors but im sure it will be worse for the 40yrds

at the end all these doesnt matter since you rarely will have a flat ground to all your party/raid members and you have to build in a lower threshold
Last edited:
That is right, larger distances are actually better because the numbers are very small (and they get squared) so there's more room for error.

I tested my triangle method in lake Al'ameth and came up with x=0.00021,y=0.0003158. The two positions need to be chosen so that the two chars are roughly on the NW-SE line or the NE-SW line, to minimize the error. I still couldn't do better than what you did because the results can varry ~3% on multiple measurements, but even if this is more precise (idk how close you got to zero by minimizing x and y difference; when I tried, I could only get within 5 decimal places by moving the char), at 40yds in water my measurement differs from yours by 0.05yd , so it doesn't really matter.

When I get the time, I will probably implement this as a 38yd check. Thanks for taking the time, really. It must have been tedious to measure this in every single zone.
Last edited:
The map coords method is now added. As discussed, this is only approximate, but it shouldn't strain the CPU as much as target->check healing range->retarget. The old method is also improved (it doesn't check the full raid at once so it shouldn't be as choppy). Please report bugs and unusual behaviour. Also check lines 237-239 in locales/enUS.lua if you want to change the [gridname] indicators for "dead" and "ghost".

You can download the new version here:
Top Bottom