-- *************************************************** -- ** DBM Range Check Frame ** -- ** http://www.deadlybossmods.com ** -- *************************************************** -- -- This addon is written and copyrighted by: -- * Paul Emmerich (Tandanu @ EU-Aegwynn) (DBM-Core) -- * Martin Verges (Nitram @ EU-Azshara) (DBM-GUI) -- -- The localizations are written by: -- * enGB/enUS: Tandanu -- * deDE: Tandanu/Nitram -- * ruRU: BootWin -- * zhTW: Azael/kc10577 -- * (add your names here!) -- -- Special thanks to: -- * Arta (DBM-Party) -- -- -- The code of this addon is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 License. (see license.txt) -- All included textures and sounds are copyrighted by their respective owners. -- -- -- You are free: -- * to Share — to copy, distribute, display, and perform the work -- * to Remix — to make derivative works -- Under the following conditions: -- * Attribution. You must attribute the work in the manner specified by the author or licensor (but not in any way that suggests that they endorse you or your use of the work). -- * Noncommercial. You may not use this work for commercial purposes. -- * Share Alike. If you alter, transform, or build upon this work, you may distribute the resulting work only under the same or similar license to this one. -- -- -- This file makes use of the following free (Creative Commons Sampling Plus 1.0) sounds: -- * alarmclockbeeps.ogg by tedthetrumpet (http://www.freesound.org/usersViewSingle.php?id=177) -- * blip_8.ogg by Corsica_S (http://www.freesound.org/usersViewSingle.php?id=7037) -- The full of text of the license can be found in the file "Sounds\Creative Commons Sampling Plus 1.0.txt". --------------- -- Globals -- --------------- DBM.RangeCheck = {} -------------- -- Locals -- -------------- local rangeCheck = DBM.RangeCheck local checkFuncs = {} local frame local createFrame local onUpdate local dropdownFrame local initializeDropdown local initRangeCheck -- initializes the range check for a specific range (if necessary), returns false if the initialization failed (because of a map range check in an unknown zone) -- for Phanx' Class Colors local RAID_CLASS_COLORS = CUSTOM_CLASS_COLORS or RAID_CLASS_COLORS --------------------- -- Dropdown Menu -- --------------------- -- todo: this dropdown menu is somewhat ugly and unflexible.... do local function setRange(self, range) rangeCheck:Show(range) end local sound0 = "none" local sound1 = "Interface\\AddOns\\DBM-Core\\Sounds\\blip_8.ogg" local sound2 = "Interface\\AddOns\\DBM-Core\\Sounds\\alarmclockbeeps.ogg" local function setSound(self, option, sound) DBM.Options[option] = sound if sound ~= "none" then PlaySoundFile(sound) end end local function toggleLocked() DBM.Options.RangeFrameLocked = not DBM.Options.RangeFrameLocked end function initializeDropdown(dropdownFrame, level, menu) local info if level == 1 then info = UIDropDownMenu_CreateInfo() info.text = DBM_CORE_RANGECHECK_SETRANGE info.notCheckable = true info.hasArrow = true info.menuList = "range" UIDropDownMenu_AddButton(info, 1) info = UIDropDownMenu_CreateInfo() info.text = DBM_CORE_RANGECHECK_SOUNDS info.notCheckable = true info.hasArrow = true info.menuList = "sounds" UIDropDownMenu_AddButton(info, 1) info = UIDropDownMenu_CreateInfo() info.text = DBM_CORE_RANGECHECK_LOCK if DBM.Options.RangeFrameLocked then info.checked = true end info.func = toggleLocked UIDropDownMenu_AddButton(info, 1) info = UIDropDownMenu_CreateInfo() info.text = DBM_CORE_RANGECHECK_HIDE info.notCheckable = true info.func = rangeCheck.Hide info.arg1 = rangeCheck UIDropDownMenu_AddButton(info, 1) elseif level == 2 then if menu == "range" then if initRangeCheck() then info = UIDropDownMenu_CreateInfo() info.text = DBM_CORE_RANGECHECK_SETRANGE_TO:format(6) info.func = setRange info.arg1 = 6 info.checked = (frame.range == 6) UIDropDownMenu_AddButton(info, 2) end if initRangeCheck() then info = UIDropDownMenu_CreateInfo() info.text = DBM_CORE_RANGECHECK_SETRANGE_TO:format(8) info.func = setRange info.arg1 = 8 info.checked = (frame.range == 8) UIDropDownMenu_AddButton(info, 2) end info = UIDropDownMenu_CreateInfo() info.text = DBM_CORE_RANGECHECK_SETRANGE_TO:format(10) info.func = setRange info.arg1 = 10 info.checked = (frame.range == 10) UIDropDownMenu_AddButton(info, 2) info = UIDropDownMenu_CreateInfo() info.text = DBM_CORE_RANGECHECK_SETRANGE_TO:format(11) info.func = setRange info.arg1 = 11 info.checked = (frame.range == 11) UIDropDownMenu_AddButton(info, 2) if initRangeCheck() then info = UIDropDownMenu_CreateInfo() info.text = DBM_CORE_RANGECHECK_SETRANGE_TO:format(12) info.func = setRange info.arg1 = 12 info.checked = (frame.range == 12) UIDropDownMenu_AddButton(info, 2) end info = UIDropDownMenu_CreateInfo() info.text = DBM_CORE_RANGECHECK_SETRANGE_TO:format(15) info.func = setRange info.arg1 = 15 info.checked = (frame.range == 15) UIDropDownMenu_AddButton(info, 2) if initRangeCheck() then info = UIDropDownMenu_CreateInfo() info.text = DBM_CORE_RANGECHECK_SETRANGE_TO:format(20) info.func = setRange info.arg1 = 20 info.checked = (frame.range == 20) UIDropDownMenu_AddButton(info, 2) end info = UIDropDownMenu_CreateInfo() info.text = DBM_CORE_RANGECHECK_SETRANGE_TO:format(28) info.func = setRange info.arg1 = 28 info.checked = (frame.range == 28) UIDropDownMenu_AddButton(info, 2) elseif menu == "sounds" then info = UIDropDownMenu_CreateInfo() info.text = DBM_CORE_RANGECHECK_SOUND_OPTION_1 info.notCheckable = true info.hasArrow = true info.menuList = "RangeFrameSound1" UIDropDownMenu_AddButton(info, 2) info = UIDropDownMenu_CreateInfo() info.text = DBM_CORE_RANGECHECK_SOUND_OPTION_2 info.notCheckable = true info.hasArrow = true info.menuList = "RangeFrameSound2" UIDropDownMenu_AddButton(info, 2) end elseif level == 3 then local option = menu info = UIDropDownMenu_CreateInfo() info.text = DBM_CORE_RANGECHECK_SOUND_0 info.func = setSound info.arg1 = option info.arg2 = sound0 info.checked = (DBM.Options[option] == sound0) UIDropDownMenu_AddButton(info, 3) info = UIDropDownMenu_CreateInfo() info.text = DBM_CORE_RANGECHECK_SOUND_1 info.func = setSound info.arg1 = option info.arg2 = sound1 info.checked = (DBM.Options[option] == sound1) UIDropDownMenu_AddButton(info, 3) info = UIDropDownMenu_CreateInfo() info.text = DBM_CORE_RANGECHECK_SOUND_2 info.func = setSound info.arg1 = option info.arg2 = sound2 info.checked = (DBM.Options[option] == sound2) UIDropDownMenu_AddButton(info, 3) end end end ----------------- -- Play Sounds -- ----------------- local function updateSound(numPlayers) -- called every 5 seconds if not UnitAffectingCombat("player") then return end if numPlayers == 1 then if DBM.Options.RangeFrameSound1 ~= "none" then PlaySoundFile(DBM.Options.RangeFrameSound1) end elseif numPlayers > 1 then if DBM.Options.RangeFrameSound2 ~= "none" then PlaySoundFile(DBM.Options.RangeFrameSound2) end end end ------------------------ -- Create the frame -- ------------------------ function createFrame() local elapsed = 0 local frame = CreateFrame("GameTooltip", "DBMRangeCheck", UIParent, "GameTooltipTemplate") dropdownFrame = CreateFrame("Frame", "DBMRangeCheckDropdown", frame, "UIDropDownMenuTemplate") frame:SetFrameStrata("DIALOG") frame:SetPoint(DBM.Options.RangeFramePoint, UIParent, DBM.Options.RangeFramePoint, DBM.Options.RangeFrameX, DBM.Options.RangeFrameY) frame:SetHeight(64) frame:SetWidth(64) frame:EnableMouse(true) frame:SetToplevel(true) frame:SetMovable() GameTooltip_OnLoad(frame) frame:SetPadding(16) frame:RegisterForDrag("LeftButton") frame:SetScript("OnDragStart", function(self) if not DBM.Options.RangeFrameLocked then self:StartMoving() end end) frame:SetScript("OnDragStop", function(self) self:StopMovingOrSizing() ValidateFramePosition(self) local point, _, _, x, y = self:GetPoint(1) DBM.Options.RangeFrameX = x DBM.Options.RangeFrameY = y DBM.Options.RangeFramePoint = point end) frame:SetScript("OnUpdate", function(self, e) elapsed = elapsed + e if elapsed >= 0.5 and self.checkFunc then onUpdate(self, elapsed) elapsed = 0 end end) frame:SetScript("OnMouseDown", function(self, button) if button == "RightButton" then UIDropDownMenu_Initialize(dropdownFrame, initializeDropdown, "MENU") ToggleDropDownMenu(1, nil, dropdownFrame, "cursor", 5, -10) end end) return frame end ---------------- -- OnUpdate -- ---------------- local soundUpdate = 0 function onUpdate(self, elapsed) local color local j = 0 self:ClearLines() self:SetText(DBM_CORE_RANGECHECK_HEADER:format(self.range), 1, 1, 1) if initRangeCheck(self.range) then for i = 1, GetNumRaidMembers() do local uId = "raid"..i if not UnitIsUnit(uId, "player") and not UnitIsDeadOrGhost(uId) and self.checkFunc(uId, self.range) and (not self.filter or self.filter(uId)) then j = j + 1 color = RAID_CLASS_COLORS[select(2, UnitClass(uId))] or NORMAL_FONT_COLOR local icon = GetRaidTargetIndex(uId) local text = icon and ("|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_%d:0|t %s"):format(icon, UnitName(uId)) or UnitName(uId) self:AddLine(text, color.r, color.g, color.b) if j >= 5 then break end end end else self:AddLine(DBM_CORE_RANGE_CHECK_ZONE_UNSUPPORTED:format(self.range)) end soundUpdate = soundUpdate + elapsed if soundUpdate >= 5 and j > 0 then updateSound(j) soundUpdate = 0 end self:Show() end ----------------------- -- Check functions -- ----------------------- checkFuncs[11] = function(uId) return CheckInteractDistance(uId, 2) end checkFuncs[10] = function(uId) return CheckInteractDistance(uId, 3) end checkFuncs[28] = function(uId) return CheckInteractDistance(uId, 4) end local getDistanceBetween do local mapSizes = DBM.MapSizes function getDistanceBetween(uId, x, y) local startX, startY = GetPlayerMapPosition(uId) local mapName = GetMapInfo() local dims = mapSizes[mapName] and mapSizes[mapName][GetCurrentMapDungeonLevel()] if not dims then return end local dX = (startX - x) * dims[1] local dY = (startY - y) * dims[2] return math.sqrt(dX * dX + dY * dY) end local function mapRangeCheck(uId, range) return getDistanceBetween(uId, GetPlayerMapPosition("player")) < range end function initRangeCheck(range) if checkFuncs[range] ~= mapRangeCheck then return true end local pX, pY = GetPlayerMapPosition("player") if pX == 0 and pY == 0 then SetMapToCurrentZone() pX, pY = GetPlayerMapPosition("player") end local levels = mapSizes[GetMapInfo()] if not levels then return false end local dims = levels[GetCurrentMapDungeonLevel()] if not dims and levels and GetCurrentMapDungeonLevel() == 0 then -- we are in a known zone but the dungeon level seems to be wrong SetMapToCurrentZone() -- fixes the dungeon level dims = levels[GetCurrentMapDungeonLevel()] -- try again if not dims then -- there is actually a level 0 in this zone but we don't know about it...too bad :( return false end elseif not dims then return false end return true -- everything ok! end setmetatable(checkFuncs, { __index = function(t, k) return mapRangeCheck end }) end do local bandages = {21991, 34721, 38643, 34722, 34721, 34722} -- you should have one of these bandages in your cache checkFuncs[15] = function(uId) for i, v in ipairs(bandages) do if IsItemInRange(v, uId) == 1 then return true elseif IsItemInRange(v, uId) == 0 then return false end end end end --------------- -- Methods -- --------------- function rangeCheck:Show(range, filter) if type(range) == "function" then -- the first argument is optional return self:Show(nil, range) end range = range or 10 frame = frame or createFrame() frame.checkFunc = checkFuncs[range] or error(("Range \"%d yd\" is not supported."):format(range), 2) frame.range = range frame.filter = filter frame:Show() frame:SetOwner(UIParent, "ANCHOR_PRESERVE") onUpdate(frame, 0) end function rangeCheck:Hide() if frame then frame:Hide() end end function rangeCheck:IsShown() return frame and frame:IsShown() end function rangeCheck:GetDistance(...) if initRangeCheck() then return getDistanceBetween(...) end end