view Bloodhound2.lua @ 7:8e81e24efb3f Release

Cleaned up some code Added in the ability to track misc nodes for toons without a gathering profession (Troves or Dark Soil) Note: Dark Soil DB has not yet been added and will be added prior to the next release.
author only1yzerman
date Mon, 24 Jun 2013 06:09:23 -0400
parents ee1d6e6a4c52
children 8875b9029a32
line wrap: on
line source

-------- SINGLE OBJECT IN GLOBAL NAMESPACE

Bloodhound2 = {};


-------- CREATE OUR INVISIBLE FRAME TO RECEIVE EVENTS AND UPDATES

local frame = CreateFrame("Frame", nil, UIParent);
Bloodhound2.Frame = frame;


-------- DEFINE AND REGISTER EVENT HANDLERS

local events = {};

function events:MINIMAP_UPDATE_TRACKING(...)
    if player and player.Z then
	    Bloodhound2.UpdateMinimap();
    end
end

frame:SetScript("OnEvent", 
	function(self, event, ...)
		events[event](self, ...);
	end);

local k, v;

for k, v in pairs(events) do
	frame:RegisterEvent(k);
end






----------------------------------------------------------------------------------------------------
-------- STORES A POINTER TO THE MINIMAP LOCALLY SO IT CAN BE CHANGED BY A RE-PARENTING FUNCTION
-------- - Added by Zasurus(www.curseforge.com/profiles/Zasurus/) for MiniHugeHUD
----------------------------------------------------------------------------------------------------
local Minimap = _G.Minimap;
Minimap.MinimapName = "Minimap";
----------------------------------------------------------------------------------------------------






-------- WORK AROUND WOW BUG THAT CAUSES GetPlayerMapPosition TO RETURN 0,0

WorldMapFrame:Show()
WorldMapFrame:Hide()


-------- HOOK INTO PER-FRAME UPDATE HANDLER

local player = {};				-- player position
local totalElapsed = 0.0;		-- time elapsed since last update
local updatePeriod = 0.1;		-- sec. interval for each minimap update
local cpuThrottle = 0.05;		-- throttle to 5% usage while moving
local minUpdatePeriod = 0.04;	-- sec. don't update more often than this
local lastzoom = 0;				-- previous minimap zoom level
local updateTotalTime = 0;		-- time spent updating minimap
local updateCount = 0;			-- number of times minimap updated

----------------------------------------------------------------------------------------------------
-------- MOVED OnUpdate Script into seperate function so it could be called by re-parent function
-------- - Added by Zasurus(www.curseforge.com/profiles/Zasurus/) for MiniHugeHUD
----------------------------------------------------------------------------------------------------
frame:SetScript("OnUpdate",    function(self, elapsed) Bloodhound2.OnUpdateScript(self, elapsed) end);
function Bloodhound2.OnUpdateScript(self, elapsed)
		totalElapsed = totalElapsed + elapsed;				-- measure total elapsed time

		if (totalElapsed >= updatePeriod) then				-- if enough time has passed
			totalElapsed = mod(totalElapsed, updatePeriod);
			local isInInstance, instanceType = IsInInstance()
			
			if isInInstance ~= nil then
				Bloodhound2.DrawArrow(0, 0, 0, 0)
				return
			end

			local cmap = GetCurrentMapContinent();
			local zmap = GetCurrentMapZone();
			SetMapToCurrentZone();
			local c = GetCurrentMapContinent();				-- get current player position
			local z = GetCurrentMapZone();
			local x, y = GetPlayerMapPosition("player");
			local f = GetPlayerFacing();
			local m = Minimap:GetZoom();
			local ztxt = GetZoneText();
			local zstxt = GetSubZoneText();
			local badzones =  { "","The Star's Bazaar", "The Emperor's Step", "The Golden Lantern", "Chamber of Reflection", "The Imperial Exchange", "Path of Serenity", "Ethereal Corridor", "The Celestial Vault", "Chamber of Enlightenment" }
			SetMapZoom(cmap, zmap);
           
            if (c ~= player.C and c ~= 0 and c ~= -1) then						-- reload continent data if continent has changed
                Bloodhound2.HerbNodes, Bloodhound2.ContinentHerbs = Bloodhound2.ReloadNodes(Bloodhound2.HerbDatabase[c], Bloodhound2.ZoneDatum[c]);
                Bloodhound2.OreNodes, Bloodhound2.ContinentOre = Bloodhound2.ReloadNodes(Bloodhound2.OreDatabase[c], Bloodhound2.ZoneDatum[c]);
                Bloodhound2.MiscNodes, Bloodhound2.ContinentNode = Bloodhound2.ReloadNodes(Bloodhound2.MiscDatabase[c], Bloodhound2.ZoneDatum[c]);
            end
          

            if (c ~= player.C or z ~= player.Z or x ~= player.X or y ~= player.Y or f ~= player.F or m ~= lastzoom) then
                player.C = c;
                player.Z = z;
                player.X = x;
                player.Y = y;
				player.F = f;
                lastzoom = m;
                local t0 = GetTime();
                Bloodhound2.UpdateMinimap();	-- player has moved or minimap scale has changed
                local t1 = GetTime() - t0;
                
                -- calculate and throttle CPU usage to 5%
                updateTotalTime = updateTotalTime + t1
                
                if updateCount > 10 then
					local avgTime = updateTotalTime / updateCount
					updatePeriod = avgTime / cpuThrottle
					Print(updateCount);
					
					if updatePeriod < minUpdatePeriod then
						updatePeriod = minUpdatePeriod
					end
					
					if updateCount >= 100 then
						updateTotalTime = updateTotalTime / 2
						updateCount = updateCount / 2
					end
                end
            end
		end
	end
function Bloodhound2.BadZone(ztxt, zstxt, badzones)
  for tbl, value in pairs(badzones) do
    if (ztxt == "Shrine of Seven Stars" or zstxt == value) then return false
    end
  end
  return true
end

function Bloodhound2.UpdateMinimap()
    local facing = 0
	
	if GetCVar("rotateMinimap") ~= "0" then
		facing = GetPlayerFacing() * 57.2957795;
	end
	
	local cf = cos(facing);
	local sf = sin(facing);
	local gx, gy = Bloodhound2.CalculateForce(cf, sf);
	Bloodhound2.DrawArrow(gx, gy, cf, sf);
end


-------- DATABASE LOADING

Bloodhound2.HerbNodes = {};
Bloodhound2.OreNodes = {};
Bloodhound2.MiscNodes = {};
Bloodhound2.ContinentHerbs = {};
Bloodhound2.ContinentOre = {};

function Bloodhound2.ReloadNodes(database, zoneDatum)

	if (database == nil) then return {}; end

	local nodes = {};
	local db = {};

	local zoneNode, positions;
	for zoneNode, positions in pairs(database) do
		local _, _, zone, node = string.find(zoneNode, "Z(%d+)N(%d+)");
		local z = tonumber(zone);
		local datum = zoneDatum[z];
		local n = tonumber(node);
		db[n] = true;
		local pxy = 0

		for _,pos in ipairs(positions) do
			local xy = pos + pxy
			pxy = xy
			local x = floor(xy / 1000);
			local y = xy - 1000 * x;
			x, y = Bloodhound2.ZoneDatum.LocalToGlobal(x / 1000.0, y / 1000.0, datum);
			tinsert(nodes, {Z = z, N = n, X = x, Y = y, TimeStamp = time() - 60 - 3600 * math.random() });
		end
	end

	return nodes, db;
end


-------- UTILITY FUNCTIONS

function Print(message)
	DEFAULT_CHAT_FRAME:AddMessage(message);
end



-------- ARROW LOADING AND DISPLAY

local function GetArrowTexture()

	local tex = Bloodhound2.GravityTexture

	if not ( tex ) then
		tex = Minimap:CreateTexture("Bloodhound2_Arrow", "OVERLAY");
		tex:SetTexture("Interface\\AddOns\\Bloodhound2\\MinimapArrow");
		Bloodhound2.GravityTexture = tex
	end

	return tex
end

function Bloodhound2.DrawArrow(grx, gry, cf, sf)

	local gravityX = grx * cf - gry * sf;
	local gravityY = gry * cf + grx * sf;
	local gravityTexture = GetArrowTexture()

	if (gravityX == 0 and gravityY == 0) then 
		gravityTexture:Hide();
		return;
	end

	local gravityScale = sqrt(gravityX * gravityX + gravityY * gravityY)
	gravityX = gravityX / gravityScale
	gravityY = gravityY / gravityScale
			
	-- determine rotated and scaled texture coordinates
	local gy = (gravityX + gravityY) / 2.82843
	local gx = (gravityX - gravityY) / 2.82843

	gravityTexture:SetTexCoord(
		0.5 - gx, 0.5 + gy,
		0.5 + gy, 0.5 + gx,
		0.5 - gy, 0.5 - gx,
		0.5 + gx, 0.5 - gy);
				
	gravityTexture:SetPoint("CENTER", Minimap, "CENTER", 40 * gravityX, -40 * gravityY)
	gravityTexture:Show()
end


-------- "RUBY" MANAGEMENT (REUSE RUBIES AND ONLY CREATE NEW ONES WHEN NEEDED)

local circles = {};
local nextCircle = 1;
local circleScale = 1;

function Bloodhound2.ResetCircles()
	nextCircle = 1;
	circleScale = Minimap:GetHeight() * 0.015 / (7 - Minimap:GetZoom());
end

function Bloodhound2.SetCircle(dx, dy, alpha)
	local circle;
	if (#circles < nextCircle) then
		circle = Minimap:CreateTexture("Bloodhound_Circle", "ARTWORK", nil, -7);
		circles[nextCircle] = circle;
		circle:SetTexture("Interface\\AddOns\\Bloodhound2\\Circle");
		circle:SetWidth(12);
		circle:SetHeight(12);
		circle:SetAlpha(1);
	else
		circle = circles[nextCircle];
	end

	nextCircle = nextCircle + 1;
	circle:SetPoint("CENTER", Minimap, "CENTER", dx * circleScale, -dy * circleScale);
	circle:SetAlpha(alpha);
	circle:Show();
end

function Bloodhound2.HideExtraCircles()
	for i=nextCircle,#circles,1 do
		circles[i]:Hide();
	end
end



-------- MAIN FUNCTION FOR CALCULATING RECOMMENDED DIRECTION OF TRAVEL

function Bloodhound2.CalculateForce(cf, sf)

	local c = player.C;			-- get player position
	local z = player.Z;
	local x = player.X;
	local y = player.Y;
	local ztxt = GetZoneText();
	local zstxt = GetSubZoneText();
  local badzones =  { "The Star's Bazaar", "The Emperor's Step", "The Golden Lantern", "Chamber of Reflection", "The Imperial Exchange", "Path of Serenity", "Ethereal Corridor", "The Celestial Vault", "Chamber of Enlightenment" }
  
  if (Bloodhound2.BadZone(ztxt, zstxt, badzones) == false) then return 0,0 end -- don't draw the arrow when inside Shrine of Seven Stars
	if c == 0 or z == 0 or c == -1 then return 0,0 end     -- don't draw arrow if using world coordinates
    if not Bloodhound2.ZoneDatum then return 0,0 end
    
    local datum = Bloodhound2.ZoneDatum[c][z];	-- get scale and offset for current zone map
    if datum == nil then return 0,0 end
    
	local gx, gy = Bloodhound2.ZoneDatum.LocalToGlobal(x, y, datum);	-- convert player position to world coordinates
	local now = time();			    -- value to use for time-stamping visited nodes
	local fx = 0;				    -- cumulative force, X-component
	local fy = 0;				    -- cumulative force, Y-component
	local multiZone = true;			-- whether to include more than the current zone
	local inspectionRadius = 60;

	if Settings then
		if Settings.MultiZoneMode==0 then
			multiZone = IsFlying();
		elseif Settings.MultiZoneMode==1 then
			multiZone = true;
		else
			multiZone = false;
		end

		if Settings.InspectionRadius then
			inspectionRadius = Settings.InspectionRadius;
		end
	end

	local settingsFilter = {};		-- which node types to ignore
	local nodes = {};				-- which nodes to track

	for i=1,GetNumTrackingTypes(),1 do
		local name, _, active, _ = GetTrackingInfo(i);
		if (active == 1) then
			if (name == L["Find Herbs"]) then
				for k,v in pairs(Settings.HerbFilter) do settingsFilter[k] = v end
				for k,v in pairs(Bloodhound2.HerbNodes) do tinsert(nodes, v) end
			elseif (name == L["Find Minerals"]) then
				for k,v in pairs(Settings.OreFilter) do settingsFilter[k] = v end
				for k,v in pairs(Bloodhound2.OreNodes) do tinsert(nodes, v) end
			elseif (IsQuestFlaggedCompleted(32609) ~= 1) then
					for k,v in pairs(Settings.MiscFilter) do settingsFilter[k] = v end
          for k,v in pairs(Bloodhound2.MiscNodes) do tinsert(nodes, v) end
      end
		end
	end

	local avoidZones = {};			-- which zones to avoid

	if multiZone then
		local cmc = GetCurrentMapContinent()
		
		if not Settings.ZoneFilter[cmc] then
			Settings.ZoneFilter[cmc] = {}
		end
		
		for k,v in pairs(Settings.ZoneFilter[GetCurrentMapContinent()]) do
			avoidZones[k] = true;
		end
	end

	local avoidNodes = {};			-- which node types to ignore

	if settingsFilter then
		for k,v in pairs(settingsFilter) do
			avoidNodes[k] = true;
		end
	end

	local minimapSize = Minimap:GetWidth() / 2 - 2;
	Bloodhound2.ResetCircles();
	
	for key, node in pairs(nodes) do
		if (avoidNodes[node.N] ~= true) and (avoidZones[node.Z] ~= true) and (multiZone or (node.Z == z)) then
			local dx = node.X - gx;
			local dy = node.Y - gy;
			local rsqrd = dx * dx + dy * dy;	
			local r = sqrt(rsqrd);			    -- distance to node
			local age = now - node.TimeStamp;	-- time since last inspected

			if (r < inspectionRadius) then
				node.TimeStamp = now;
			else
                --[[ 
                The magnitude of the force is proportional to the age and inversely 
                proportional to the square of the distance, and the direction is towards
                the node. The math for this starts out as
                
                local force = age / rsqrd
                local angle = math.atan2(dy, dx)
                fx = fx + force * math.cos(angle)
                fy = fy + force * math.sin(angle)
                
                But cos(angle) is just dx/r and sin(angle) is just dy/r, so we don't 
                actually need atan2, cos, and sin and instead we can write this as
                ]]
				local force = age / (r * rsqrd)
				fx = fx + force * dx
				fy = fy + force * dy
			end

			if (r * circleScale < minimapSize) and (age > 60) then	-- draw ruby
				local alpha = r / (minimapSize / circleScale) / 0.7 - 0.3;
				alpha = max(0, alpha);
				Bloodhound2.SetCircle(dx * cf - dy * sf, dy * cf + dx * sf, alpha);
			end
		end
	end

	Bloodhound2.HideExtraCircles();
	return fx, fy;
end







----------------------------------------------------------------------------------------------------
-------- REPARENT THE MINIMAP FOR 3RD PARTY MINIMAPS AND HUDS
-------- - Added by Zasurus(www.curseforge.com/profiles/Zasurus/) for MiniHugeHUD
----------------------------------------------------------------------------------------------------
function Bloodhound2.ReparentMinimap(NewMinimap, MinimapName)
    Bloodhound2.OnUpdateScript(nil, 1000000)
    -- Hide the circles currently on the minimap
    Bloodhound2.ResetCircles();
    Bloodhound2.HideExtraCircles();
    -- Hide this minimap's copy of the arrow
    if Bloodhound2.GravityTexture then
        Bloodhound2.GravityTexture:Hide();
    end;
    -- Store the current set of circles and arrow on this minimap for later
    Minimap.Bloodhound2Circles = circles;
    Minimap.Bloodhound2Arrow = Bloodhound2.GravityTexture;
    -- Pull back (or create for circles) the arrow and circles for this minimap
    circles = NewMinimap.Bloodhound2Circles or {};
    ZasCircles = circles;
    Bloodhound2.GravityTexture = NewMinimap.Bloodhound2Arrow;
    -- Move Bloodhound2's Local Minimap pointer to the new minimap
    Minimap = NewMinimap;
    Minimap.MinimapName = MinimapName;
    -- Update all points
    Bloodhound2.UpdateMinimap();
end
----------------------------------------------------------------------------------------------------