diff Bloodhound2.lua @ 0:ff01eb61abab

Initial beta version Updated original addon herb and ore database Fixed the configuration options (maybe - requires testing) Added the 2 new zones in Pandaria (Isle of Giants and Isle of Thunder)
author only1yzerman
date Thu, 09 May 2013 18:53:18 -0400
parents
children 6ab5b23877a6
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Bloodhound2.lua	Thu May 09 18:53:18 2013 -0400
@@ -0,0 +1,425 @@
+
+-------- 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();
+			SetMapZoom(cmap, zmap);
+            
+            if (c ~= player.C and c ~= 0) 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]);
+            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.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.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();
+		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();
+		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;
+	
+	if c == 0 or z == 0 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
+			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
+----------------------------------------------------------------------------------------------------
\ No newline at end of file