--[[ ############################################################################## _____/\\\\\\\\\\\____/\\\________/\\\__/\\\________/\\\__/\\\\\\\\\\\_ # ___/\\\/////////\\\_\/\\\_______\/\\\_\/\\\_______\/\\\_\/////\\\///__ # __\//\\\______\///__\//\\\______/\\\__\/\\\_______\/\\\_____\/\\\_____ # ___\////\\\__________\//\\\____/\\\___\/\\\_______\/\\\_____\/\\\_____ # ______\////\\\________\//\\\__/\\\____\/\\\_______\/\\\_____\/\\\_____ # _________\////\\\______\//\\\/\\\_____\/\\\_______\/\\\_____\/\\\_____ # __/\\\______\//\\\______\//\\\\\______\//\\\______/\\\______\/\\\_____ # _\///\\\\\\\\\\\/________\//\\\________\///\\\\\\\\\/____/\\\\\\\\\\\_# ___\///////////___________\///___________\/////////_____\///////////_# ############################################################################## S U P E R - V I L L A I N - U I By: Munglunch # ############################################################################## ########################################################## LOCALIZED LUA FUNCTIONS ########################################################## ]]-- --[[ GLOBALS ]]-- local _G = _G; local unpack = _G.unpack; local select = _G.select; local pairs = _G.pairs; local ipairs = _G.ipairs; local type = _G.type; local tinsert = _G.tinsert; local math = _G.math; local bit = _G.bit; --[[ MATH METHODS ]]-- local abs, ceil, floor, round = math.abs, math.ceil, math.floor, math.round; -- Basic local fmod, modf, sqrt = math.fmod, math.modf, math.sqrt; -- Algebra local atan2, cos, deg, rad, sin = math.atan2, math.cos, math.deg, math.rad, math.sin; -- Trigonometry local min, huge, random = math.min, math.huge, math.random; -- Uncommon --[[ BINARY METHODS ]]-- local band = bit.band; --[[ ########################################################## GET ADDON DATA ########################################################## ]]-- local SuperVillain, L = unpack(select(2, ...)) --[[ ########################################################## MEASURING UTILITY FUNCTIONS (from Astrolabe by: Esamynn) ########################################################## ]]-- local radian90 = (3.141592653589793 / 2) local GetDistance, GetTarget do local WORLDMAPAREA_DEFAULT_DUNGEON_FLOOR_IS_TERRAIN = 0x00000004 local WORLDMAPAREA_VIRTUAL_CONTINENT = 0x00000008 local DUNGEONMAP_MICRO_DUNGEON = 0x00000001 local _failsafe, _cache, _dungeons, _transform = {}, {}, {}, {}; local _mapdata = { [0] = { height = 22266.74312, system = -1, width = 33400.121, xOffset = 0, yOffset = 0, [1] = { xOffset = -10311.71318, yOffset = -19819.33898, scale = 0.56089997291565, }, [0] = { xOffset = -48226.86993, yOffset = -16433.90283, scale = 0.56300002336502, }, [571] = { xOffset = -29750.89905, yOffset = -11454.50802, scale = 0.5949000120163, }, [870] = { xOffset = -27693.71178, yOffset = -29720.0585, scale = 0.65140002965927, }, }, } local _failsafeFunc = function(tbl, key) if(type(key) == "number") then return _failsafe; else return rawget(_failsafe, key); end end setmetatable(_failsafe, { xOffset = 0, height = 1, yOffset = 0, width = 1, __index = _failsafeFunc }); setmetatable(_mapdata, _failsafe); for _, ID in ipairs(GetWorldMapTransforms()) do local terrain, newterrain, _, _, transformMinY, transformMaxY, transformMinX, transformMaxX, offsetY, offsetX = GetWorldMapTransformInfo(ID) if ( offsetX ~= 0 or offsetY ~= 0 ) then _transform[ID] = { terrain = terrain, newterrain = newterrain, BRy = -transformMinY, TLy = -transformMaxY, BRx = -transformMinX, TLx = -transformMaxX, offsetY = offsetY, offsetX = offsetX, } end end local function _getmapdata(t) local chunk = {} local mapName = GetMapInfo(); local id = GetCurrentMapAreaID(); local numFloors = GetNumDungeonMapLevels(); chunk.mapName = mapName; chunk.cont = (GetCurrentMapContinent()) or -100; chunk.zone = (GetCurrentMapZone()) or -100; chunk.numFloors = numFloors; local _, TLx, TLy, BRx, BRy = GetCurrentMapZone(); if(TLx and TLy and BRx and BRy and (TLx~=0 or TLy~=0 or BRx~=0 or BRy~=0)) then chunk[0] = {}; chunk[0].TLx = TLx; chunk[0].TLy = TLy; chunk[0].BRx = BRx; chunk[0].BRy = BRy; end if(not chunk[0] and numFloors == 0 and (GetCurrentMapDungeonLevel()) == 1) then numFloors = 1; chunk.hiddenFloor = true; end if(numFloors > 0) then for f = 1, numFloors do SetDungeonMapLevel(f); local _, TLx, TLy, BRx, BRy = GetCurrentMapDungeonLevel(); if(TLx and TLy and BRx and BRy) then chunk[f] = {}; chunk[f].TLx = TLx; chunk[f].TLy = TLy; chunk[f].BRx = BRx; chunk[f].BRy = BRy; end end end t[id] = chunk; end do local continents = { GetMapContinents() }; for C in pairs(continents) do local zones = { GetMapZones(C) }; continents[C] = zones; SetMapZoom(C, 0); zones[0] = GetCurrentMapAreaID(); _getmapdata(_cache); for Z in ipairs(zones) do SetMapZoom(C, Z); zones[Z] = GetCurrentMapAreaID(); _getmapdata(_cache); end end for _, id in ipairs(GetAreaMaps()) do if not (_cache[id]) then if(SetMapByID(id)) then _getmapdata(_cache); end end end end for id, map in pairs(_cache) do local terrain, _, _, _, _, _, _, _, _, flags = GetAreaMapInfo(id) local origin = terrain; local chunk = _mapdata[id]; if not (chunk) then chunk = {}; end if(map.numFloors > 0 or map.hiddenFloor) then for f, coords in pairs(map) do if(type(f) == "number" and f > 0) then if not (chunk[f]) then chunk[f] = {}; end local flr = chunk[f] local TLx, TLy, BRx, BRy = -coords.BRx, -coords.BRy, -coords.TLx, -coords.TLy if not (flr.width) then flr.width = BRx - TLx end if not (flr.height) then flr.height = BRy - TLy end if not (flr.xOffset) then flr.xOffset = TLx end if not (flr.yOffset) then flr.yOffset = TLy end end end for f = 1, map.numFloors do if not (chunk[f]) then if(f == 1 and map[0] and map[0].TLx and map[0].TLy and map[0].BRx and map[0].BRy and band(flags, WORLDMAPAREA_DEFAULT_DUNGEON_FLOOR_IS_TERRAIN) == WORLDMAPAREA_DEFAULT_DUNGEON_FLOOR_IS_TERRAIN) then chunk[f] = {}; local flr = chunk[f] local coords = map[0] local TLx, TLy, BRx, BRy = -coords.TLx, -coords.TLy, -coords.BRx, -coords.BRy flr.width = BRx - TLx flr.height = BRy - TLy flr.xOffset = TLx flr.yOffset = TLy end end end if(map.hiddenFloor) then chunk.width = chunk[1].width chunk.height = chunk[1].height chunk.xOffset = chunk[1].xOffset chunk.yOffset = chunk[1].yOffset end else local coords = map[0] if(coords ~= nil) then local TLx, TLy, BRx, BRy = -coords.TLx, -coords.TLy, -coords.BRx, -coords.BRy for _, trans in pairs(_transform) do if(trans.terrain == terrain) then if((trans.TLx < TLx and BRx < trans.BRx) and (trans.TLy < TLy and BRy < trans.BRy)) then TLx = TLx - trans.offsetX; BRx = BRx - trans.offsetX; BRy = BRy - trans.offsetY; TLy = TLy - trans.offsetY; terrain = trans.newterrain; break; end end end if not (TLx==0 and TLy==0 and BRx==0 and BRy==0) then if not (TLx < BRx) then printError("Bad x-axis Orientation (Zone): ", id, TLx, BRx); end if not (TLy < BRy) then printError("Bad y-axis Orientation (Zone): ", id, TLy, BRy); end end if not (chunk.width) then chunk.width = BRx - TLx end if not (chunk.height) then chunk.height = BRy - TLy end if not (chunk.xOffset) then chunk.xOffset = TLx end if not (chunk.yOffset) then chunk.yOffset = TLy end end end if not (next(chunk, nil)) then chunk = { xOffset = 0, height = 1, yOffset = 0, width = 1 }; end if not (chunk.origin) then chunk.origin = origin; end _mapdata[id] = chunk; if(chunk and chunk ~= _failsafe) then if not (chunk.system) then chunk.system = terrain; end if(map.cont > 0 and map.zone > 0) then _dungeons[terrain] = {} end setmetatable(chunk, _failsafe); end end local function _getpos(map, mapFloor, x, y) if (mapFloor ~= 0) then map = rawget(map, mapFloor) or _dungeons[map.origin][mapFloor]; end x = x * map.width + map.xOffset; y = y * map.height + map.yOffset; return x, y; end function GetDistance(map1, floor1, x1, y1, map2, floor2, x2, y2) if not (map1 and map2) then return end; floor1 = floor1 or min(#_mapdata[map1], 1); floor2 = floor2 or min(#_mapdata[map2], 1); local dist, xDelta, yDelta; if(map1 == map2 and floor1 == floor2) then local chunk = _mapdata[map1]; if(floor1 ~= 0) then chunk = rawget(chunk, floor1) end local w,h = 1,1 if(not chunk) then if(_dungeons[chunk.origin] and _dungeons[chunk.origin][floor1]) then chunk = _dungeons[chunk.origin][floor1] w = chunk.width h = chunk.height else w = 1 h = 1 end else w = chunk.width h = chunk.height end xDelta = (x2 - x1) * w; yDelta = (y2 - y1) * h; else local map1 = _mapdata[map1]; local map2 = _mapdata[map2]; if(map1.system == map2.system) then x1, y1 = _getpos(map1, floor1, x1, y1); x2, y2 = _getpos(map2, floor2, x2, y2); xDelta = (x2 - x1); yDelta = (y2 - y1); else local s1 = map1.system; local s2 = map2.system; if((map1==0 or _mapdata[0][s1]) and (map2 == 0 or _mapdata[0][s2])) then x1, y1 = _getpos(map1, floor1, x1, y1); x2, y2 = _getpos(map2, floor2, x2, y2); if(map1 ~= 0) then local cont1 = _mapdata[0][s1]; x1 = (x1 - cont1.xOffset) * cont1.scale; y1 = (y1 - cont1.yOffset) * cont1.scale; end if(map2 ~= 0) then local cont2 = _mapdata[0][s2]; x2 = (x2 - cont2.xOffset) * cont2.scale; y2 = (y2 - cont2.yOffset) * cont2.scale; end xDelta = x2 - x1; yDelta = y2 - y1; end end end if(xDelta and yDelta) then dist = sqrt(xDelta*xDelta + yDelta*yDelta); end return dist, xDelta, yDelta; end end do local function _findunit(unit, noMapChange) local x, y = GetPlayerMapPosition(unit); if(x <= 0 and y <= 0) then if(noMapChange) then return; end local lastMapID, lastFloor = GetCurrentMapAreaID(), GetCurrentMapDungeonLevel(); SetMapToCurrentZone(); x, y = GetPlayerMapPosition(unit); if(x <= 0 and y <= 0) then if(ZoomOut()) then elseif(GetCurrentMapZone() ~= WORLDMAP_WORLD_ID) then SetMapZoom(GetCurrentMapContinent()); else SetMapZoom(WORLDMAP_WORLD_ID); end x, y = GetPlayerMapPosition(unit); if(x <= 0 and y <= 0) then return; end end local thisMapID, thisFloor = GetCurrentMapAreaID(), GetCurrentMapDungeonLevel(); if(thisMapID ~= lastMapID or thisFloor ~= lastFloor) then SetMapByID(lastMapID); SetDungeonMapLevel(lastFloor); end return thisMapID, thisFloor, x, y; end return GetCurrentMapAreaID(), GetCurrentMapDungeonLevel(), x, y; end local function _findplayer() local x, y = GetPlayerMapPosition("player"); if(x <= 0 and y <= 0) then if(WorldMap:IsShown()) then return; end SetMapToCurrentZone(); x, y = GetPlayerMapPosition("player"); if(x <= 0 and y <= 0) then if(ZoomOut()) then elseif(GetCurrentMapZone() ~= WORLDMAP_WORLD_ID) then SetMapZoom(GetCurrentMapContinent()); else SetMapZoom(WORLDMAP_WORLD_ID); end x, y = GetPlayerMapPosition("player"); if(x <= 0 and y <= 0) then return; end end end return GetCurrentMapAreaID(), GetCurrentMapDungeonLevel(), x, y; end function GetTarget(unit, checkMap) local plot1, plot2, plot3, plot4; if unit == "player" or UnitIsUnit("player", unit) then plot1, plot2, plot3, plot4 = _findplayer() else plot1, plot2, plot3, plot4 = _findunit(unit, checkMap or WorldMapFrame:IsVisible()) end; if not (plot1 and plot4) then return false else return true, plot1, plot2, plot3, plot4 end end end; function SuperVillain:Triangulate(unit1, unit2, checkMap) local allowed, plot1, plot2, plot3, plot4 = GetTarget(unit1, checkMap) if not allowed then return end; local allowed, plot5, plot6, plot7, plot8 = GetTarget(unit2, checkMap) if not allowed then return end; local distance, deltaX, deltaY = GetDistance(plot1, plot2, plot3, plot4, plot5, plot6, plot7, plot8) if distance and deltaX and deltaY then return distance, -radian90 - GetPlayerFacing() - atan2(deltaY, deltaX) elseif distance then return distance end end;