Quantcast
---------------------------------------------------------------------------------------
-- NxMap - Map code
-- Copyright 2007-2012 Carbon Based Creations, LLC
---------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------
-- Carbonite - Addon for World of Warcraft(tm)
-- Copyright 2007-2012 Carbon Based Creations, LLC
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
---------------------------------------------------------------------------------------

-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-- Map

NxMAPOPTS_VERSION	= .26

NxMapOpts = {
	Version = 0
}

NxMapOptsDefaults = {
	Version = NxMAPOPTS_VERSION,

	NXMaps = {
		[1] = {
			[0] = {	-- Default map id
				NXPlyrFollow = true,
				NXWorldShow = true,
			},
			[9001] = {	-- AB
				NXPlyrFollow = false,
				NXWorldShow = false,
--				NXScale = 1,
			},
			[9002] = {	-- WG
				NXPlyrFollow = false,
				NXWorldShow = false,
			},
			[9003] = {	-- AV
				NXPlyrFollow = false,
				NXWorldShow = false,
			},
			[9004] = {	-- EOS
				NXPlyrFollow = false,
				NXWorldShow = false,
			},
			[9008] = {	-- SoA
				NXPlyrFollow = false,
				NXWorldShow = false,
			},
			[9009] = {	-- IC
				NXPlyrFollow = false,
				NXWorldShow = false,
			},
			[9010] = {	-- TBG
				NXPlyrFollow = false,
				NXWorldShow = false,
			},
			[9011] = {	-- TP
				NXPlyrFollow = false,
				NXWorldShow = false,
			},

			NXAutoScaleOn = true,

			NXKillShow = false,

			NXMMFull = false,
			NXMMAlpha = .1,
			NXMMDockScale = .4,
			NXMMDockScaleBG = .4,
			NXMMDockAlpha = 1,
			NXMMDockOnAtScale = .6,

			NXBackgndAlphaFade = .4,
			NXBackgndAlphaFull = 1,

			NXAutoScaleMin = .01,
			NXAutoScaleMax = 4,
			NXDotZoneScale = 1,
			NXDotPalScale = 1,
			NXDotPartyScale = 1,
			NXDotRaidScale = 1,
			NXIconNavScale = 1,
			NXIconScale = 1,
			NXDetailScale = 2,
			NXDetailAlpha = 1,
			NXPOIAtScale = 1,

			NXShowUnexplored = false,
			NXUnexploredAlpha = .35,

			-- OLD
			NXOverlayAlpha = nil,
			NXMiniAlpha = nil,
			NXMiniShow = nil,
		}
	}
}

--------
-- Init map stuff

function Nx.Map:Init()

	local gopts = Nx.GetGlobalOpts()
	self.GOpts = gopts

	local plFaction = UnitFactionGroup ("player")
--	Nx.prt ("fac %s", plFaction)
	plFaction = strsub (plFaction, 1, 1)
	self.PlFactionNum = plFaction == "A" and 0 or 1
	self.PlFactionShort = plFaction == "A" and "Ally" or "Horde"

	self.Maps = {}
	self.Created = false

	self:InitFuncs()
	self:InitTables()

--	if Nx.V32 then
--		hooksecurefunc ("UpdateQuestMapPOI", self.UpdateQuestMapPOIHook)
--	end

--	hooksecurefunc ("ToggleWorldMap", Nx.Map.ToggleWorldMapHook)

	self.PlyrNames = {}
	self.AFKers = {}
	self.PlyrNamesTipStr = ""

	self.ScanContinentsMod = 10

	self.ContPOIs = {}
	for cont = 1, self.ContCnt do
		self.ContPOIs[cont] = {}
	end

	self.BGTimers = {}

	local hist = {}
	self.PlyrHist = hist
	hist.LastX = -99999999
	hist.LastY = -99999999
	hist.Next = 1
	hist.Cnt = self.GOpts["MapTrailCnt"]

	for n = 1, hist.Cnt * 4, 4 do
		hist[n] = 0				-- Secs
		hist[n + 1] = 0		-- World X
		hist[n + 2] = 0		-- World Y
		hist[n + 3] = 0		-- Direction
	end

	Nx.MapPOITypes = {
		[0] =
		0, 0, 2, 1, 1, 0, 0, 0,  0, 1, 2, 1, 2, 2, 2, 1,
		0, 1, 1, 2, 2, 0, 1, 1,  2, 2, 0, 1, 1, 2, 2, 0,
		1, 1, 2, 2, 0, 1, 1, 2,  2, 0, 0, 1, 2, 0, 1, 1,
		2, 2,
		[136] = 1,
		[137] = 1,
		[138] = 2,
		[139] = 2,
		[141] = 1,
		[142] = 1,
		[143] = 2,
		[144] = 2,
		[146] = 1,
		[147] = 1,
		[148] = 2,
		[149] = 2,
		[151] = 1,
		[152] = 1,
		[153] = 2,
		[154] = 2,
	}

	self.WorldMapHideNames = {
		"WorldMapCorpse", "WorldMapDeathRelease", "WorldMapPing", "OutlandButton", "AzerothButton"
	}

	self.AddonMinimapNames = NXMapAddonMinimapNames
	NXMapAddonMinimapNames = nil

	-- Emulate TomTom

	if gopts["EmuTomTom"] and not TomTom then
		local tom = {}
		TomTom = tom
		tom["version"] = "v40200"
		tom["AddWaypoint"] = Nx.TTAddWaypoint
		tom["AddZWaypoint"] = Nx.TTAddZWaypoint
		tom["SetCustomWaypoint"] = Nx.TTSetCustomWaypoint
		tom["SetCustomMFWaypoint"] = Nx.TTSetCustomMFWaypoint
		tom["RemoveWaypoint"] = Nx.TTRemoveWaypoint
		tom["SetCrazyArrow"] = Nx.TTSetCrazyArrow
	end

	if gopts["EmuCartWP"] and not Cartographer then
		local cart = {}
		Cartographer = cart

		cart["HasModule"] = function (self, module)
				return module == "Waypoints" or module == Cartographer_Waypoints
			end
		cart["IsModuleActive"] = cart["HasModule"]

		cart["GetDistanceToPoint"] = Nx.CartGetDistanceToPoint

		local cartwp = {}
		Cartographer_Waypoints = cartwp
		cartwp["SetPointAsWaypoint"] = Nx.CartSetPointAsWaypoint
		cartwp["AddLHWaypoint"] = Nx.CartAddLHWaypoint
		cartwp["AddRoutesWaypoint"] = Nx.CartAddRoutesWaypoint
		cartwp["AddWaypoint"] = Nx.CartAddWaypoint
		cartwp["UpdateWaypoint"] = function (self) end
		cartwp["CancelWaypoint"] = Nx.CartCancelWaypoint

		cartwp["GetWaypointHitDistance"] = function() return 7 end
		cartwp["SetWaypointHitDistance"] = Nx.CartSetWaypointHitDistance

--		Nx.prtVar ("Cartographer_Waypoints", Cartographer:IsModuleActive ("Waypoints"))
--		Nx.prtVar ("Cartographer_Waypoints", Cartographer:IsModuleActive (Cartographer_Waypoints))

		if not NotePoint then
			local np = {}
			NotePoint = np
			np["new"] = Nx.CartNotePointNew
		end
	end
end

--------
-- Open and init

function Nx.Map:Open()

	local Map = Nx.Map

	local m = self.Maps[1]

	if not NxMapOpts.NXMaps then		-- Missing?
		NxMapOpts.Version = 0
	end

	if NxMapOpts.Version < NxMAPOPTS_VERSION then

		if NxMapOpts.Version > 0 then
			Nx.prt ("Reset map options %f", NxMapOpts.Version)
		end

		NxMapOpts = NxMapOptsDefaults
	end

	local opts = NxMapOpts.NXMaps[1]

	for k, v in pairs (NxMapOptsDefaults.NXMaps[1]) do
		if opts[k] == nil then
			opts[k] = v
		end
	end

	if self.Created then

		if m.Frm:IsShown() then
			m.Frm:Hide()
		else
			m.Frm:Show()
		end
		return
	end

	--

	self.Maps[1] = self:Create (1)
	self.Dock:Create()

	self.Created = true

	-- Create a table of all the Minimap's children

--[[
	local children = { Minimap:GetChildren() }

--	Map.MinimapChildren = children

	for i = #children, 1, -1 do

		-- Scan children starting from the end of the list to see if the object is a model
		-- If it is, and has no name (in case some addon attached a model to it), it's probably the right one

		-- FIX!!!! use model name!

		if children[i]:IsObjectType ("Model") and not children[i]:GetName() then

			Map.MinimapPlyrModel = children[i]

--			Nx.prtVar ("minimap model", Map.MinimapPlyrModel)

			break

		end
	end

	Map.MinimapCompass = getglobal ("MiniMapCompassRing")

--]]
end

--------

function Nx.Map:UpdateOptions (index)

	local src = Nx.Map.Maps[index]
	local dst = NxMapOpts.NXMaps[index]

	assert (src)
	assert (dst)

--	dst.NXMapScale = src.MapScale

	dst.NXShowUnexplored = src.ShowUnexplored
	dst.NXKillShow = src.KillShow

	dst.NXBackgndAlphaFade = src.BackgndAlphaFade
	dst.NXBackgndAlphaFull = src.BackgndAlphaFull

	dst.NXDotZoneScale = src.DotZoneScale
	dst.NXDotPalScale = src.DotPalScale
	dst.NXDotPartyScale = src.DotPartyScale
	dst.NXDotRaidScale = src.DotRaidScale
	dst.NXIconNavScale = src.IconNavScale
	dst.NXIconScale = src.IconScale

	local opts = src.CurOpts
	if opts then
--		Nx.prt ("Map UpdateOptions %s %s", src.MapPosX, src.MapPosY)
		opts.NXMapPosX = src.MapPosX
		opts.NXMapPosY = src.MapPosY
		opts.NXScale = src.Scale
	end
end

--------
-- Switch to a new set of options
-- self = map

function Nx.Map:SwitchOptions (id, startup)

--	Nx.prt ("Map SwitchOptions %s", id)

	local opts = NxMapOpts.NXMaps[self.MapIndex]
	local copts = opts[id] or opts[0]

	if copts ~= self.CurOpts then

--		Nx.prt ("Map SwitchOptions Diff %s", id)

		self.CurOpts = copts

		if copts.NXPlyrFollow then
			self:GotoPlayer()
		end

--		Nx.prtVar ("copts", copts)
--		Nx.prtVar ("inBG", Nx.InBG)

		if (not copts.NXPlyrFollow or startup) and copts.NXMapPosX then

--			Nx.prt ("Map SwitchOptions %s NXMapPosX %f %f %f", id, copts.NXMapPosX, copts.NXMapPosY, copts.NXScale)

			self.MapPosX = copts.NXMapPosX
			self.MapPosY = copts.NXMapPosY
			self.Scale = copts.NXScale
			self.StepTime = 1

		elseif copts.NXPlyrFollow or Nx.InBG then

--			Nx.prt ("Map SwitchOptions GotoCurrentZone")
			self:GotoCurrentZone()
		end

		local mode = opts[id] and tostring (id) or ""
		self.Win:SwitchLayoutMode (mode)
	end
end

--------

function Nx.Map:GetOptionsT (index)

	local map = Nx.Map.Maps[index]
	local opts = NxMapOpts.NXMaps[index]

	return opts[map.RMapId] or opts[0]
end

--------

function Nx.Map:GetOption (index, name)

	local map = Nx.Map.Maps[index]
	local opts = NxMapOpts.NXMaps[index]

	local id = map.RMapId
	id = opts[id] and id or 0

	return opts[id][name]
end

--------

function Nx.Map:SetOption (index, name, value)

	local map = Nx.Map.Maps[index]
	local opts = NxMapOpts.NXMaps[index]

	local id = map.RMapId
	id = opts[id] and id or 0

	opts[id][name] = value
end

--------

function Nx.Map:InitFuncs()

	self.Funcs = {
		["None"] = function() end,
		["Add Note"] = self.Menu_OnAddNote,
		["Goto"] = self.SetTargetAtClick,
		["Show Player Zone"] = self.GotoCurrentZone,
		["Show Selected Zone"] = self.CenterMap,
		["Menu"] = self.OpenMenu,
		["Zoom In"] = self.ClickZoomIn,
		["Zoom Out"] = self.ClickZoomOut,
	}
end

function Nx.Map:GetFuncs()

	local t = {}

	for name in pairs (self.Funcs) do
		tinsert (t, name)
	end

	sort (t)

	return t
end

--------
-- Call a map function from an option setting
-- self = map

function Nx.Map:CallFunc (optName)

	local name = self.GOpts[optName]
	if name == "None" then
		return	-- return nil
	end

	local func = self.Funcs[name]
	if func then
		func (self)
	else
		Nx.prt ("Unknown map function %s", name)
	end

	return true	-- We did something
end

--------
-- Create a map

function Nx.Map:Create (index)

	local Map = Nx.Map

	local m = {}

	local gopts = Nx.GetGlobalOpts()
	m.GOpts = gopts

	local opts = NxMapOpts.NXMaps[index]
	m.LOpts = opts		-- Local for map (stuff without a map mode)

	opts.NXPOIAtScale = opts.NXPOIAtScale or 1

	setmetatable (m, self)
	self.__index = self

	m.Tick = 0												-- Debug tick
	m.Debug = nil											-- Debug on
	m.DebugTime = nil
	m.DebugFullCoords = nil								-- Debug high precision map coords
	m.DebugAdjustScale = .1

	m.MapIndex = index
	m.MMOwn = gopts["MapMMOwn"] and index == 1

	m.ShowUnexplored = opts.NXShowUnexplored
	m.KillShow = opts.NXKillShow

	m.TitleH = 0
	m.PadX = 0
	m.Scale = .025
	m.ScaleDraw = .025									-- Actual draw scale
	m.MapScale = opts.NXMapScale or 1
	m.MapW = 150
	m.MapH = 140
	m.W = m.MapW + m.PadX * 2
	m.H = m.MapH + m.TitleH + 1
	m.LClickTime = 0
	m.MouseEnabled = true
	m.Scrolling = false
	m.StepTime = 0
	m.MapId = 0
	m.BaseScale = 1										-- Scale values, because instances are smaller
	m.PlyrX = 0
	m.PlyrY = 0
	m.PlyrRZX = 0
	m.PlyrRZY = 0
	m.PlyrDir = 0
	m.PlyrLastDir = 999
	m.PlyrSpeed = 0
	m.PlyrSpeedX = 0
	m.PlyrSpeedY = 0
	m.PlyrSpeedCalcTime = GetTime()
	m.MoveDir = 0
	m.MoveLastX = 0
	m.MoveLastY = 0
	m.ViewSavedData = {}
	m.MapPosX = 2200											-- World position of map
	m.MapPosY = -100
	m.MapPosXDraw = m.MapPosX								-- Actual draw position
	m.MapPosYDraw = m.MapPosY
	m.MapsDrawnOrder = {}									-- [index (1st is newest)] = map id
	m.MapsDrawnFade = {}										-- [map id] = fade
	m.MiniBlks = gopts["MapDetailSize"]
	m.BackgndAlphaFade = opts.NXBackgndAlphaFade
	m.BackgndAlphaFull = opts.NXBackgndAlphaFull
	m.BackgndAlpha = 0									-- Current value
	m.BackgndAlphaTarget = m.BackgndAlphaFade		-- Target value
	m.WorldAlpha = 0
	m.DotZoneScale = opts.NXDotZoneScale
	m.DotPalScale = opts.NXDotPalScale
	m.DotPartyScale = opts.NXDotPartyScale
	m.DotRaidScale = opts.NXDotRaidScale
	m.IconNavScale = opts.NXIconNavScale
	m.IconScale = opts.NXIconScale
	m.ArrowPulse = 1
	m.ArrowScroll = 0

	m.UpdateTargetDelay = 0
	m.UpdateTrackingDelay = 0

	m.Targets = {}
	m.TargetNextUniqueId = 1
	m.Tracking = {}
	m.TrackPlyrs = {}

	m.Data = {}
	m.IconFrms = {}
	m.IconFrms.Next = 1
	m.IconNIFrms = {}				-- Non interactive
	m.IconNIFrms.Next = 1
	m.IconStaticFrms = {}
	m.IconStaticFrms.Next = 1

	m.TextFStrs = {}
	m.TextFStrs.Next = 1

	m.MMGathererUpdateDelay = 1

	-- Create Window

	Nx.Window:SetCreateFade (1, 0)

	local wname = m:GetWinName()

	local i = gopts["MapShowTitle2"] and 2 or 1

	local win = Nx.Window:Create (wname, nil, nil, nil, i)
	m.Win = win

	win:SetBGAlpha (0, 1)

	win:CreateButtons (true)
	win:InitLayoutData (nil, -.0001, -.4, -.19, -.3, 1)

	for n = 9001, 9004 do
		win:InitLayoutData (tostring (n), -.0001, -.4, -.19, -.3, 1)
	end

	for n = 9008, 9011 do
		win:InitLayoutData (tostring (n), -.0001, -.4, -.19, -.3, 1)
	end

	win:SetUser (m, self.OnWin)
	win.UserUpdateFade = m.WinUpdateFade

	win.Frm:SetToplevel (true)
	win.Frm.NxMap = m

	m.StartupShown = win:IsShown()
	win.Frm:Show()

	-- Create main frame

	local f = CreateFrame ("Frame", nil, UIParent)
	m.Frm = f
	f.NxMap = m

	win:Attach (f, 0, 1, 0, 1)

--	win:RegisterEvent ("PLAYER_LOGIN", self.OnEvent)
--	win:RegisterEvent ("PLAYER_ENTERING_WORLD", self.OnEvent)
	win:RegisterEvent ("WORLD_MAP_UPDATE", self.OnEvent)

	f:SetScript ("OnMouseDown", self.OnMouseDown)
	f:SetScript ("OnMouseUp", self.OnMouseUp)
	f:SetScript ("OnMouseWheel", self.OnMouseWheel)
	f:EnableMouse (true)
	f:EnableMouseWheel (true)

	f:SetScript ("OnUpdate", self.OnUpdate)

	f:SetMovable (true)
	f:SetResizable (true)

--	f:SetFrameStrata ("LOW")
	f:SetWidth (m.W)
	f:SetHeight (m.H)
	f:SetMinResize (50, 50)

	local t = f:CreateTexture()
	t:SetTexture (0, 0, 0, .2)
	t:SetAllPoints (f)
	f.texture = t

	f:Show()

	-- Create text frame

	local tsf = CreateFrame ("ScrollFrame", nil, f)
	m.TextScFrm = tsf

	tsf:SetAllPoints (f)

	local tf = CreateFrame ("Frame", nil, tsf)
	m.TextFrm = tf

	tf:SetPoint ("TOPLEFT", 0, 0)
	tf:SetWidth (100)
	tf:SetHeight (100)

--	tf:SetAllPoints (f)
	tsf:SetScrollChild (tf)

	-- Tip

	m:CreateLocationTip()

	-- Create tool bar

	m:CreateToolBar()

	-- Create buttons

	local bw, bh = win:GetBorderSize()

	local function func (self, but)
		self.LOpts.NXAutoScaleOn = but:GetPressed()
	end

	m.ButAutoScaleOn = Nx.Button:Create (win.Frm, "MapAutoScale", nil, nil, -20, -bh, "TOPRIGHT", 12, 12, func, m)
	m.ButAutoScaleOn:SetPressed (opts.NXAutoScaleOn)

	-- Create menu

	local menu = Nx.Menu:Create (f)
	m.Menu = menu

	menu:AddItem (0, "Goto", self.Menu_OnGoto, m)
	menu:AddItem (0, "Clear Goto", self.Menu_OnClearGoto, m)

	menu:AddItem (0, "Add Note", self.Menu_OnAddNote, m)

	menu:AddItem (0, "Save Map Scale", self.Menu_OnScaleSave, m)
	menu:AddItem (0, "Restore Map Scale", self.Menu_OnScaleRestore, m)

	m.MenuIPlyrFollow = menu:AddItem (0, "Follow You", self.Menu_OnPlyrFollow, m)

	local item = menu:AddItem (0, "Select Cities Last", self.SetLevelWorldHotspots, m)
	item:SetChecked (m, "NXCitiesUnder")

	m.MenuIMonitorZone = menu:AddItem (0, "Monitor Zone", self.Menu_OnMonitorZone, m)

	menu:AddItem (0, "", nil, self)

	-- Create route sub menu

	local routeMenu = Nx.Menu:Create (f)
	menu:AddSubMenu (routeMenu, "Route...")

	local function func (self)
		self:RouteGathers()
	end
	local item = routeMenu:AddItem (0, "Current Gather Locations", func, m)

	local function func (self)
		self:RouteTargets()
	end
	local item = routeMenu:AddItem (0, "Current Goto Targets", func, m)

	local function func (self)
		self.ShowUnexplored = false
		self:UpdateOverlayUnexplored()
		self:TargetOverlayUnexplored()
		self:RouteTargets()
	end

	routeMenu:AddItem (0, "Unexplored Locations", func, m)

	local function func (self)
		self:ReverseTargets()
	end

	routeMenu:AddItem (0, "Reverse Targets", func, m)

	local item = routeMenu:AddItem (0, "Recycle Reached Targets")
	item:SetChecked (gopts, "RouteRecycle")

	local item = routeMenu:AddItem (0, "Gather Target Radius")
	item:SetSlider (gopts, 7, 300, nil, "RouteGatherRadius")

	local item = routeMenu:AddItem (0, "Gather Merge Radius")
	item:SetSlider (gopts, 0, 100, nil, "RouteMergeRadius")

	-- Create show sub menu

	local showMenu = Nx.Menu:Create (f)
	menu:AddSubMenu (showMenu, "Show...")

	showMenu:AddItem (0, "Show Player Zone", self.Menu_OnShowPlayerZone, m)

	local function func (self)
		self.Guide:UpdateGatherFolders()
	end

	local item = showMenu:AddItem (0, "Show Herb Locations", func, m)
	m.MenuIShowHerb = item
	item:SetChecked (Nx.CharOpts, "MapShowGatherH")

	local item = showMenu:AddItem (0, "Show Mining Locations", func, m)
	m.MenuIShowMine = item
	item:SetChecked (Nx.CharOpts, "MapShowGatherM")

	local item = showMenu:AddItem (0, "Show Artifact Locations", func, m)
--	m.MenuIShowArt = item
	item:SetChecked (Nx.CharOpts, "MapShowGatherA")


	local function func (self)
		self.Guide.POIDraw = nil
	end

	local item = showMenu:AddItem (0, "Show Mailboxes", func, m)
	item:SetChecked (gopts, "MapShowMailboxes")


	local item = showMenu:AddItem (0, "Show Notes")
	item:SetChecked (gopts, "MapShowNotes")

	local item = showMenu:AddItem (0, "Show Punks")
	item:SetChecked (gopts, "MapShowPunks")


	local function func (self, item)
		self.ShowUnexplored = item:GetChecked()
	end

	local item = showMenu:AddItem (0, "Show Unexplored Areas", func, m)
	item:SetChecked (m.ShowUnexplored)


	m.MenuIShowWorld = showMenu:AddItem (0, "Show World", self.Menu_OnShowWorld, m)


	local function forceShowCont (self)
		self.ScanContinentsMod = 10
	end

	local item = showMenu:AddItem (0, "Show Cities", forceShowCont, Map)
	item:SetChecked (gopts, "MapShowCCity")

	local item = showMenu:AddItem (0, "Show Towns", forceShowCont, Map)
	item:SetChecked (gopts, "MapShowCTown")

	local item = showMenu:AddItem (0, "Show Extras", forceShowCont, Map)
	item:SetChecked (gopts, "MapShowCExtra")

	local item = showMenu:AddItem (0, "Show Kill Icons", self.Menu_OnShowKills, m)
	item:SetChecked (m.KillShow)

	-- Create minimap sub menu

	if not Nx.Free then

		local mmmenu = Nx.Menu:Create (f)

		menu:AddSubMenu (mmmenu, "Minimap...")

		local function func (self, item)
			self.LOpts.NXMMFull = item:GetChecked()
			self.MMZoomChanged = true
		end

		local item = mmmenu:AddItem (0, "Full Size", func, m)
		self.MMMenuIFull = item
		item:SetChecked (opts.NXMMFull)

		local function func (self, item)
			self.LOpts.NXMMAlpha = item:GetSlider()
		end

		local item = mmmenu:AddItem (0, "Transparency", func, m)
		item:SetSlider (opts.NXMMAlpha, 0, 1)

		local function func (self, item)
			self.LOpts.NXMMDockScale = item:GetSlider()
			self.MMZoomChanged = true
		end

		local item = mmmenu:AddItem (0, "Docked Scale", func, m)
		item:SetSlider (opts.NXMMDockScale, .01, 3)

		local function func (self, item)
			self.LOpts.NXMMDockScaleBG = item:GetSlider()
			self.MMZoomChanged = true
		end

		local item = mmmenu:AddItem (0, "Docked Scale In BG", func, m)
		item:SetSlider (opts.NXMMDockScaleBG, .01, 3)

		local function func (self, item)
			self.LOpts.NXMMDockAlpha = item:GetSlider()
		end

		local item = mmmenu:AddItem (0, "Docked Transparency", func, m)
		item:SetSlider (opts.NXMMDockAlpha, 0, 1)

		local function func (self, item)
			self.LOpts.NXMMDockOnAtScale = item:GetSlider()
		end

		local item = mmmenu:AddItem (0, "Docking Below Map Scale", func, m)
		item:SetSlider (opts.NXMMDockOnAtScale, 0, 5)
	end

	-- Create scale sub menu

	local smenu = Nx.Menu:Create (f)

	menu:AddSubMenu (smenu, "Scale...")

	local item = smenu:AddItem (0, "Auto Scale Min")
	item:SetSlider (opts, .01, 10, nil, "NXAutoScaleMin")

	local item = smenu:AddItem (0, "Auto Scale Max")
	item:SetSlider (opts, .25, 10, nil, "NXAutoScaleMax")

	local item = smenu:AddItem (0, "Zone Dot Scale", self.Menu_OnDotZoneScale, m)
	item:SetSlider (m.DotZoneScale, 0.1, 2)

	local item = smenu:AddItem (0, "Friend/Guild Dot Scale", self.Menu_OnDotPalScale, m)
	item:SetSlider (m.DotPalScale, 0.1, 2)

	local item = smenu:AddItem (0, "Party Dot Scale", self.Menu_OnDotPartyScale, m)
	item:SetSlider (m.DotPartyScale, 0.1, 2)

	local item = smenu:AddItem (0, "Raid Dot Scale", self.Menu_OnDotRaidScale, m)
	item:SetSlider (m.DotRaidScale, 0.1, 2)

	local item = smenu:AddItem (0, "Icon Scale", self.Menu_OnIconScale, m)
	item:SetSlider (m.IconScale, 0.1, 3)

	local item = smenu:AddItem (0, "Navigation Icon Scale", self.Menu_OnIconNavScale, m)
	item:SetSlider (m.IconNavScale, 0.1, 3)

	local function func (self, item)
		self.LOpts.NXDetailScale = item:GetSlider()
	end

	local item = smenu:AddItem (0, "Details At Scale", func, m)
	item:SetSlider (opts.NXDetailScale, .05, 10)

	local item = smenu:AddItem (0, "Gather Icons At Scale")
	item:SetSlider (gopts, .01, 10, nil, "MapIconGatherAtScale")

	local item = smenu:AddItem (0, "POI Icons At Scale")
	item:SetSlider (opts, .1, 10, nil, "NXPOIAtScale")

	-- Create transparency sub menu

	local tmenu = Nx.Menu:Create (f)
	m.TransMenu = tmenu

	menu:AddSubMenu (tmenu, "Transparency...")

	local item = tmenu:AddItem (0, "Detail Transparency", self.Menu_OnDetailAlpha, m)
	item:SetSlider (opts.NXDetailAlpha, .25, 1)

	local item = tmenu:AddItem (0, "Fade In Transparency", self.Menu_OnBackgndAlphaFull, m)
	item:SetSlider (m.BackgndAlphaFull, .25, 1)

	local item = tmenu:AddItem (0, "Fade Out Transparency", self.Menu_OnBackgndAlphaFade, m)
	item:SetSlider (m.BackgndAlphaFade, 0, 1)

	local function func (self)
		self.Guide:UpdateGatherFolders()
	end

	local item = tmenu:AddItem (0, "Gather Icon Transparency", func, m)
	item:SetSlider (gopts, .2, 1, nil, "MapIconGatherA")

	local item = tmenu:AddItem (0, "POI Icon Transparency")
	item:SetSlider (gopts, .2, 1, nil, "MapIconPOIAlpha")

	local function func (self, item)
		self.LOpts.NXUnexploredAlpha = item:GetSlider()
	end

	local item = tmenu:AddItem (0, "Unexplored Transparency", func, m)
	item:SetSlider (opts.NXUnexploredAlpha, 0, .9)

	-- Options menu

	local item = menu:AddItem (0, "Options...", self.Menu_OnOptions, m)

	-- Debug menu

	if NxData.DebugMap then

		m.DebugMap = true

		local dbmenu = Nx.Menu:Create (f)

		menu:AddItem (0, "", nil, self)
		menu:AddSubMenu (dbmenu, "Debug...")

		local function func (self, item)
			self.Debug = item:GetChecked()
		end

		local item = dbmenu:AddItem (0, "Map Debug", func, m)
		item:SetChecked (false)

--[[
		Nx.prt ("*** DebugHotspots is ON")
		m["DebugHotspots"] = true
--]]
		local item = dbmenu:AddItem (0, "Hotspots", func, m)
		item:SetChecked (m, "DebugHotspots")

		dbmenu:AddItem (0, "Hotspots pack", self.PackHotspotsDebug, m)

		local function func (self, item)
			self.DebugTime = item:GetChecked()
		end

		local item = dbmenu:AddItem (0, "Map Debug Time", func, m)
		item:SetChecked (false)

		local item = dbmenu:AddItem (0, "Map Full Coords", self.Menu_OnMapDebugFullCoords, m)
		item:SetChecked (m.DebugFullCoords)

		local item = dbmenu:AddItem (0, "Quest Debug", self.Menu_OnQuestDebug, m)
		item:SetChecked (Nx.Quest.Debug)

		local function func (self, item)
			self.DebugScale = item:GetSlider()
		end

		local item = dbmenu:AddItem (0, "Scale", func, m)
		item:SetSlider (0, 4, 6)
	end

	-- Create player icon menu

	local menu = Nx.Menu:Create (f)
	m.PIconMenu = menu

	menu:AddItem (0, "Whisper", self.Menu_OnWhisper, m)
	menu:AddItem (0, "Invite", self.Menu_OnInvite, m)
	menu:AddItem (0, "Get Quests", self.Menu_OnGetQuests, m)

	local item = menu:AddItem (0, "Track Player", self.Menu_OnTrackPlyr, m)
	local item = menu:AddItem (0, "Remove From Tracking", self.Menu_OnRemoveTracking, m)

	menu:AddItem (0, "Report Player AFK", self.Menu_OnReportPlyrAFK, m)
--	menu:AddItem (0, "Report All AFK", self.Menu_OnReportAllAFK, m)

	menu:AddItem (0, "")

	local item = menu:AddItem (0, "Grow Conflict Bars", self.Menu_OnGrowConflictBars, m)
	item:SetChecked (true)
	m.BGGrowBars = true

	-- Create general icon menu

	m:CreateIconMenu (f)

	-- Create BG icon menu

	m.BGIncNum = 0

	local menu = Nx.Menu:Create (f)
	m.BGIconMenu = menu

	for n = 1, #NXlBGMessages, 2 do

		local function func (self)
			self:BGMenu_Send (NXlBGMessages[n + 1])	-- Inherit n from loop
		end

		menu:AddItem (0, NXlBGMessages[n], func, m)
	end

	menu:AddItem (0, NXlBGStatus, self.BGMenu_OnStatus, m)

	-- Create player icon

	local plf = CreateFrame ("Frame", nil, f)
	m.PlyrFrm = plf
	plf.NxMap = m

	plf:SetWidth (3)
	plf:SetHeight (3)

	t = plf:CreateTexture()
	plf.texture = t
	t:SetTexture ("Interface\\Minimap\\MinimapArrow")
	t:SetAllPoints (plf)

	plf:SetPoint ("CENTER", 0, (m.TitleH - 1) * -.5)
	plf:Show()

	-- Init map frames

	m:InitFrames()

	-- Test

--[[
	m:InitIconType ("Test", nil, "Interface\\TargetingFrame\\TargetDead", 64, 64)

	for i = 1, 100, .2 do
		m:AddIconData ("Test", i, 30, 0)
	end
--]]

--[[
	m:InitIconType ("Test2", nil, 64, 64)

	for i = 1, 100, .2 do
		m:AddIconData ("Test2", i, 10, "00ff00")
	end
--]]

--[[
	m:InitIconType ("TestZR", "ZR")

	for i = 1, 10, 2 do
		m:AddIconRect ("TestZR", i, 5, i+1, 6, "00ff0080")
	end
--]]

	--

	self.RMapId = 9000		-- Safe default

	m:SwitchOptions (-1, true)

	m:UpdateAll()

	m.Guide = Map.Guide:Create (m)

	--

	self.MMFrm = _G["Minimap"]
	assert (self.MMFrm)

	m:MinimapOwnInit()

	-- Force player to be shown after init done and not in BG

	local function func (self)
		if not Nx.InBG then
			self:GotoPlayer()
		end
	end

	Nx.Timer:Start ("MapIShow" .. m.MapIndex, 1, m, func)

	--

	return m
end

--------

function Nx.Map:GetWinName()
	return "NxMap" .. self.MapIndex
end

--------
-- Create map tool bar

function Nx.Map:CreateToolBar()

	local bar = Nx.ToolBar:Create (self:GetWinName().."TB", self.Frm, 22, true, true)
	self.ToolBar = bar

	bar:SetUser (self)

--	bar.Frm:SetFrameLevel (self.Frm:GetFrameLevel() + 30)

	local data = {
		{ "MapZIn", "Zoom In", self.OnButZoomIn, false },
		{ "MapZOut", "Zoom Out", self.OnButZoomOut, false },
		{ "MapFav", "-Favorites-", self.OnButToggleFav, false },
		{ "MapGuide", "-Guide-", self.OnButToggleGuide, false },
--		{ "MapQGivers", "-Quest Givers-", self.OnButToggleQuestGivers, false },
		{ "MapWarehouse", "-Warehouse-", self.OnButToggleWarehouse, false },
		{ "MapCombat", "-Combat-", self.OnButToggleCombat, false },
		{ "MapEvents", "-Events-", self.OnButToggleEvent, false },
--		{ "Scroll", "Debug Scroll", self.OnButScrollDebug, false },
--		{ "Scroll", "Debug Scroll2", self.OnButScrollDebug2, false },
	}

	for i, b in ipairs (data) do

		if Nx.Free and i > 3 then
			break
		end

		bar:AddButton (b[1], b[2], nil, b[3], b[4])
	end

	bar:Update()

	self:UpdateToolBar()
end

function Nx.Map:UpdateToolBar()

	local frm = self.ToolBar.Frm

	local opts = Nx:GetGlobalOpts()
	if opts["MapShowToolBar"] then
		frm:Show()
	else
		frm:Hide()
	end
end

function Nx.Map:CreateIconMenu (frm)

	-- Create general icon menu

	local menu = Nx.Menu:Create (frm)
	self.GIconMenu = menu

	self.GIconMenuITogInst = menu:AddItem (0, "Toggle Instance Map", self.GMenu_OnTogInst, self)
	self.GIconMenuIFindNote = menu:AddItem (0, "Find Note", self.GMenu_OnFindNote, self)

	Nx.Quest:CreateGiverIconMenu (menu, frm)

	menu:AddItem (0, "Goto", self.GMenu_OnGoto, self)
	menu:AddItem (0, "Clear Goto", self.Menu_OnClearGoto, self)
	menu:AddItem (0, "Paste Link", self.GMenu_OnPasteLink, self)

	menu:AddItem (0, "Add Note", self.Menu_OnAddNote, self)
end

--------

function Nx.Map:CreateLocationTip()

	local f = CreateFrame ("Frame", "NxMapTip", self.Frm)
--	f.NxInst = self
	self.LocTipFrm = f

	f:SetClampedToScreen()

--	f:ClearAllPoints()
--	f:SetPoint ("BOTTOMLEFT", 0, 0)

	local t = f:CreateTexture()
	f.texture = t
	t:SetAllPoints (f)
	t:SetTexture (0, 0, 0, .85)

	-- Font strings

	local fstrs = {}
	self.LocTipFStrs = fstrs

	local h = Nx.Font:GetH ("FontMapLoc")

	for n = 1, 4 do
		local fstr = f:CreateFontString()
		tinsert (fstrs, fstr)
		fstr:SetFontObject ("NxFontMapLoc")
		fstr:SetJustifyH ("LEFT")
	end
end

function Nx.Map:SetLocationTip (tipStr)

	local f = self.LocTipFrm
	local a = self.GOpts["MapLocTipAnchor"]

	if tipStr and a ~= "None" then

		local ar = self.GOpts["MapLocTipAnchorRel"]
		ar = ar == "None" and a or ar
		f:ClearAllPoints()
		f:SetPoint (a, self.Frm, ar)

		local h = Nx.Font:GetH ("FontMapLoc")
		local fstrs = self.LocTipFStrs
		local i = 1
		local textW = 0

		for s in gmatch (tipStr, "(%C+)") do		-- gmatch makes garbage!
--			Nx.prt (s)
			local fstr = fstrs[i]
			fstr:SetPoint ("TOPLEFT", 2, 0 - (i - 1) * h)
			fstr:SetText (s)
			textW = max (textW, fstr:GetStringWidth())
			i = i + 1
		end

		for n = i, #fstrs do
			fstrs[n]:SetText ("")
		end

--		Nx.prt (textW)

--		f:SetFrameStrata ("DIALOG")
		f:SetWidth (4 + textW)
		f:SetHeight (2 + (i - 1) * h)
		f:Show()

	else
		f:Hide()
	end
end

-------------------------------------------------------

--------
-- Startup so show map

function Nx.Map:StartupZoom()
--	local map = self:GetMap (1)
--	map:GotoCurrentZone()
end

--------
-- Main frame update event handler

function Nx.Map:MainOnUpdate (elapsed)

	if self.Created then

		local map = self:GetMap (1)
		local win = map.Win
		local show, showsv = win:IsShown()

--		if not show and GameTooltip:IsOwned (win.Frm) then
--			GameTooltip:Hide()
--		end

		if not show then

			if showsv and not win:IsCombatHidden() then	-- We've been hidden, but not by us (Escape key)?
				win:Show()
				map:RestoreSize()
				return
			end

			self.OnUpdate (map.Frm, elapsed)
		end
	end
end

--------

function Nx.Map:OnWin (typ)

--	Nx.prt ("MapOnWin %s", typ)

	if typ == "SizeNorm" then
		self:RestoreSize()

	elseif typ == "SizeMax" then

		if WorldMapFrame:IsShown() then
			HideUIPanel (WorldMapFrame)
		end
		tinsert (UISpecialFrames, self:GetWinName())

		self:AttachWorldMap()

	elseif typ == "Close" then

	end
end

--------
--

function Nx.Map:AttachWorldMap()

	if not self.GOpts["MapWOwn"] then
		return
	end

	local f = getglobal ("WorldMapButton")
	if f then

--		Nx.prt ("AttachWorldMap")

		self.WorldMapFrm = f
		self.WorldMapFrmParent = f:GetParent()
		self.WorldMapFrmScale = f:GetScale()

		f:SetParent (self.TextFrm)
		f:Show()

		f:EnableMouse (false)

		self:SetWorldMapIcons (.001)

		local tipf = getglobal ("WorldMapTooltip")
		if tipf then
			tipf:SetParent (self.Frm)
		end

		local af = getglobal ("WorldMapFrameAreaFrame")
		if af then
			af:Hide()
--			af:SetParent (self.Frm)
--			af:SetPoint ("TOP", self.Frm, "TOP", 0, 0)
		end

--		Gatherer.MapNotes.MapDraw = function() Nx.prt ("Gath mapdraw") end

		-- Remove leftovers
		for n = 1, NUM_WORLDMAP_POIS do
			local f = getglobal ("WorldMapFramePOI" .. n)
			f:Hide()
		end

		self.WorldMapFrmMapId = 0
	end
end

--------
--

function Nx.Map:DetachWorldMap()

	local f = self.WorldMapFrm

	if f then

--		Nx.prt ("DetachWorldMap")

		self.WorldMapFrm = nil

		f:SetParent (self.WorldMapFrmParent)
		f:SetScale (self.WorldMapFrmScale)
		f:SetPoint ("TOPLEFT", "WorldMapDetailFrame", "TOPLEFT", 0, 0)

		f:EnableMouse (true)

		self:SetWorldMapIcons (1)

		local tipf = getglobal ("WorldMapTooltip")
		if tipf then
			tipf:SetParent (self.WorldMapFrmParent)
			tipf:SetFrameStrata ("TOOLTIP")
		end

		local af = getglobal ("WorldMapFrameAreaFrame")
		if af then
			af:Show()
--			af:SetParent (f)
--			af:SetPoint ("TOP", f, "TOP", 0, -10)
		end

	end
end

--------
-- Update Blizzard world map frame if we grabbed it

function Nx.Map:UpdateWorldMap()

	local f = self.WorldMapFrm

	if f then

		if self.StepTime ~= 0 or self.Scrolling or IsShiftKeyDown() then
			f:Hide()
		else

			local tipf = getglobal ("WorldMapTooltip")
			if tipf then
				tipf:SetFrameStrata ("TOOLTIP")
			end

			local af = getglobal ("WorldMapFrameAreaFrame")
			if af then
				af:SetFrameStrata ("HIGH")
			end

			f:Show()

			self:ClipZoneFrm (self.Cont, self.Zone, f, 1)
			f:SetFrameLevel (self.Level)

			if self.WorldMapFrmMapId ~= self.MapId then

--				Nx.prt ("mapid %s", self.MapId)

				self.WorldMapFrmMapId = self.MapId

				self:SetChildLevels (f, self.Level + 1)

				self.Level = self.Level + 4
			end
		end

		for k, f in ipairs (_G["MAP_VEHICLES"]) do
			f:SetScale (.001)
		end
	end
end

--------
-- Hook of UpdateQuestMapPOI, so we can move icons after Blizz updates

--[[

function Nx.Map.UpdateQuestMapPOIHook()

	local self = Nx.Map:GetMap (1)

--	Nx.prt ("UpdateQuestMapPOI hook")

	local f = self.WorldMapFrm
	if f then

		-- Just hide, since we draw ourselves

  		for n = 1, QuestMapUpdateAllQuests() do
			local f = QUEST_MAP_POI[n]
			if f then
				f:Hide()
			end
		end

		for n = 1, #QUEST_MAP_ADDITIONAL_POI do
			QUEST_MAP_ADDITIONAL_POI[n]:Hide()
		end

-- OLD
		if false then
--		if SHOW_QUEST_OBJECTIVES_ON_MAP == "1" then

			local sc = f:GetScale()

			Nx.prt ("WMFrm %s %s %s", f:GetScale(), f:GetLeft(), f:GetRight())

			local numPOI = QuestMapUpdateAllQuests()

			for i = 1, numPOI do
				local pf = QUEST_MAP_POI[i]
				if pf and pf:IsShown() then
					Nx.prt ("QPOI %s %s %s", i, pf["x"], pf["y"])
					pf:SetPoint ("CENTER", f, "TOPLEFT", pf["x"] * sc, pf["y"] * sc)
					pf:SetScale (1 / sc)
--					pf:SetParent (self.TextFrm)
					pf:SetFrameLevel (100)
				end
			end
		end

	end
end

--]]

--------
-- Recursively set child levels

function Nx.Map:SetChildLevels (frm, lvl)

	local ch = { frm:GetChildren() }

	for n, chf in pairs (ch) do

		chf:SetFrameLevel (lvl)

		if chf:GetNumChildren() > 0 then
			self:SetChildLevels (chf, lvl + 1)
		end
	end
end

--------
-- Set scale on world map icons

function Nx.Map:SetWorldMapIcons (scale)

	for n = 1, MAX_PARTY_MEMBERS do
		local f = getglobal ("WorldMapParty" .. n)
		if f then
			f:SetScale (scale)
		end
	end
	for n = 1, MAX_RAID_MEMBERS do
		local f = getglobal ("WorldMapRaid" .. n)
		if f then
			f:SetScale (scale)
		end
	end
	local flags = GetNumBattlefieldFlagPositions()
	for n = 1, flags do
		local f = getglobal ("WorldMapFlag" .. n)
		if f then
			f:SetScale (scale)
		end
	end

	for k, f in ipairs (_G["MAP_VEHICLES"]) do
		f:SetScale (scale)
	end

	for k, name in ipairs (Nx.Map.WorldMapHideNames) do

		local f = getglobal (name)
		if f then
			f:SetScale (scale)
		end
	end

--[[
	for n = 1, QuestMapUpdateAllQuests() do
		local f = QUEST_MAP_POI[n]
		if f then
			f:SetScale (scale)
		end
	end

	for i = 1, #QUEST_MAP_ADDITIONAL_POI do
		QUEST_MAP_ADDITIONAL_POI[i]:Hide();
	end
--]]
end

--------
--

function Nx.Map:OnButZoomIn()
	self:SetScaleOverTime (2)
end

function Nx.Map:OnButZoomOut()
	self:SetScaleOverTime (-2)
end

function Nx.Map:OnButToggleFav (but)
	Nx.Fav:ToggleShow()
end

function Nx.Map:OnButToggleGuide (but)
	self.Guide:ToggleShow()
end

function Nx.Map:OnButToggleWarehouse (but)
	Nx.Warehouse:ToggleShow()
end

--[[
function Nx.Map:OnButToggleQuestGivers (but)
	local guide = self.Guide
	local folder = guide:FindFolder ("Quest Givers")
	guide:AddShowFolders (folder, guide:IsShowFolders (folder))
	guide:Update()
end
--]]

function Nx.Map:OnButToggleCombat (but)
	Nx.Combat:Open()
end

function Nx.Map:OnButToggleEvent (but)
	Nx.UEvents.List:Open()
end

--[[
function Nx.Map:OnButScrollDebug (but, click, x, y)

	x = x * self.DebugAdjustScale
	y = y * self.DebugAdjustScale

	if self["DebugHotspots"] then
		return self:HotspotDebugScroll (x, y)
	end

	local id = self.DebugMapId		-- Double click map to set
	if not id then
		return
	end

	local mode = 2

	if mode == 1 then

		if IsControlKeyDown() then
			x = x * .1 + self.MapWorldInfo[id][1]
			self.MapWorldInfo[id][1] = x
			Nx.prt ("Sc %f", x)
		else

			local info = self.MapWorldInfo[id]

			info[2] = x + info[2]
			info[3] = y + info[3]
			info[4] = x + info[4]
			info[5] = y + info[5]

			info.XOff = x + (info.XOff or 0)
			info.YOff = y + (info.YOff or 0)

			Nx.prt ("XY %f %f, %f %f", info[2], info[3], info.XOff, info.YOff)
		end

	elseif mode == 2 then	-- Minimap data

		local miniT = self:GetMiniInfo (id)
		miniT[5] = x + miniT[5]
		miniT[6] = y + miniT[6]
		Nx.prt ("XY %f %f", miniT[5], miniT[6])
	end

	return true
end

function Nx.Map:OnButScrollDebug2 (but, click, x, y)

	self.DebugAdjustScale = self.DebugAdjustScale + x * .001

	Nx.prt ("Adj scale %s", self.DebugAdjustScale)
end
--]]

--------
-- Init static map frames

function Nx.Map:InitFrames()

	local f = self.Frm
	local m = self

	-- Create map zone tile frames

	self.TileFrms = {}

	local tf

	for i = 1, 12 do

		tf = CreateFrame ("Frame", nil, f)
		m.TileFrms[i] = tf

		local t = tf:CreateTexture()
		t:SetAllPoints (tf)
		tf.texture = t
	end

	-- Init continent frames

	Nx.ContBlks = {
		{ 0,1,1,0, 0,1,1,0, 0,1,1,0 },
		{ 0,1,1,0, 0,1,1,0, 0,1,1,0 },
		{ 1,1,1,1, 1,1,1,1, 1,1,1,1 },
		{ 1,1,1,1, 1,1,1,1, 1,1,1,1 },
		{ 1,1,1,0, 1,1,1,0, 1,1,1,0 },
		{ 1,1,1,1, 1,1,1,1, 1,1,1,1 },
	}

	self.ContFrms = {}

	for n = 1, Nx.Map.ContCnt do

		self.ContFrms[n] = {}

		local mapFileName = self.MapInfo[n].FileName

		local texi = 1

--		Nx.prtD ("Map Update ".. mapFileName)

		for i = 1, 12 do

			if Nx.ContBlks[n][i] ~= 0 then

				local cf = CreateFrame ("Frame", nil, f)
				m.ContFrms[n][i] = cf

				local t = cf:CreateTexture()
				t:SetAllPoints (cf)
				cf.texture = t

				if n == 0 then
					t:SetTexture ("Interface\\AddOns\\Carbonite\\Gfx\\Map\\Cont\\".."Kal"..texi)
					texi = texi + 1
				else
					t:SetTexture ("Interface\\WorldMap\\"..mapFileName.."\\"..mapFileName..i)
				end
			end
		end
	end

--[[
	local cf = CreateFrame ("Frame", nil, f)
	self.ContFillFrm = cf

	local t = cf:CreateTexture()
	t:SetAllPoints (cf)
	cf.texture = t
--	t:SetTexture (.5, .5, .45, .99)
	t:SetTexture ("Interface\\AddOns\\Carbonite\\Gfx\\Map\\HBlend")
	t:SetVertexColor (1, 1, 1, .7)
--]]

	-- Create mini frames

	self.MiniFrms = {}

	for n = 1, self.MiniBlks ^ 2 do

		local tf = CreateFrame ("Frame", nil, f)
		m.MiniFrms[n] = tf

		local t = tf:CreateTexture()
		tf.texture = t
		t:SetAllPoints (tf)
	end

	self:InitHotspots()

--	self:InitHotspotsDebug()
end

--------
-- Init hotspots

function Nx.Map:InitHotspots()

	local quad = {}
	self.WorldHotspots = quad
	local quadCity = {}
	self.WorldHotspotsCity = quadCity

	for contN = 1, Nx.Map.ContCnt do

		cname = self:GetWorldContinentInfo (contN)
		if not cname then
			break
		end

		local zoneN = 1

		while true do

			zname, zx, zy, zw, zh = self:GetWorldZoneInfo (contN, zoneN)
			if not zx then
				break
			end

			local mapId = self:CZ2MapId (contN, zoneN)
			local nxz = Nx.MapIdToNxzone[mapId] or 0

--			local zscale = self:GetWorldZoneScale (mapId)

--			Nx.prt ("Hotspot %s+%s %d %s", contN, zoneN, mapId, nxz or "nil")

			local color, infoStr = self:GetMapNameDesc (mapId)
			local tipStr = format ("%s, %s%s (%s)", cname, color, zname, infoStr)

			local loc = Nx.MapWorldHotspots[nxz]	-- Old way
			local locSize = 4

			if not loc then

				loc = Nx.MapWorldHotspots2[mapId]
				if loc then
					locSize = 12
				else
					loc = format ("%c%c%c%c", 85, 85, 135, 135)
				end
			end

			for n = 0, 100 do

				local locN = n * locSize + 1

				local loc1 = strsub (loc, locN, locN + locSize - 1)
				if loc1 == "" then
					break
				end

				local zx, zy, zw, zh

				if locSize == 4 then
					zx, zy, zw, zh = Nx.Quest:UnpackLocRect (loc1)
				else
					zx = tonumber (strsub (loc1, 1, 3), 16) * 100 / 4095
					zy = tonumber (strsub (loc1, 4, 6), 16) * 100 / 4095
					zw = tonumber (strsub (loc1, 7, 9), 16) * 1002 / 4095
					zh = tonumber (strsub (loc1, 10, 12), 16) * 668 / 4095
				end

				local spot = {}

				local wzone = self:GetWorldZone (mapId)

				if wzone.City or wzone.StartZone then
					tinsert (quadCity, spot)
				else
					tinsert (quad, spot)
				end

				spot.MapId = mapId

				local wx, wy = self:GetWorldPos (mapId, zx, zy)
				spot.WX1 = wx
				spot.WY1 = wy
				zw = zw / 1002 * 100
				zh = zh / 668 * 100
				local wx, wy = self:GetWorldPos (mapId, zx + zw, zy + zh)
				spot.WX2 = wx
				spot.WY2 = wy

				spot.NxTipBase = tipStr

--				if contN == 5 then
--					Nx.prtVar ("Spot", spot)
--				end

			end

			zoneN = zoneN + 1
		end
	end

end

--------
-- Init hotspots

--[[

function Nx.Map:InitHotspotsDebug()

	if 1 then return end

	NxData.NXDBHotspotsT = {}
--	NxData.NXDBHotspotsT = NxData.NXDBHotspotsT or {}

	self:InitHotspots2Debug (self.WorldHotspotsCity)
	self:InitHotspots2Debug (self.WorldHotspots)
end

function Nx.Map:InitHotspots2Debug (quad)

	local saved = NxData.NXDBHotspotsT

	for n, spot in ipairs (quad) do

		local mapId = spot.MapId

		if mapId then
--		if mapId ~= 2042 and mapId ~= 2043 then
--		if mapId == 1014 or mapId > 4000 and mapId <= 5999 then

			local x, y = self:GetZonePos (mapId, spot.WX1, spot.WY1)
			local w, h = self:GetZonePos (mapId, spot.WX2, spot.WY2)

			local mapT = saved[mapId] or {}
			saved[mapId] = mapT

			local saveSpot = { X = x, Y = y, W = w - x, H = h - y }
			tinsert (mapT, saveSpot)
		end
	end
end

function Nx.Map:HotspotDebugCmd (cmd)

	if cmd == "add" then

		local curT = self.HotspotDebugCurT
		if curT then
			Nx.prt ("Add")

			local x, y = self:GetZonePos (self.DebugMapId, self.DebugWX or 0, self.DebugWY or 0)
			local spot = { X = x, Y = y, W = 10, H = 10 }
			tinsert (curT, spot)
		end

	elseif cmd == "del" then

		local curT = self.HotspotDebugCurT
		if curT then
			tremove (curT, self.HotspotDebugCurI)
		end
	end
end


function Nx.Map:PackHotspotsDebug()

	local saved = NxData.NXDBHotspotsT

	if saved then

		local cnt = 0

		local packed = {}
		NxData.NXDBHotspotsPacked = packed

		for mapId, mapT in pairs (saved) do

			local s = ""

			for n, spot in ipairs (mapT) do

				local x = min (max (spot.X * 4095 / 100, 0), 4095)
				local y = min (max (spot.Y * 4095 / 100, 0), 4095)
				local w = min (max (spot.W * 4095 / 100, 0), 4095)
				local h = min (max (spot.H * 4095 / 100, 0), 4095)
				s = format ("%s%03x%03x%03x%03x", s, x, y, w, h)

				cnt = cnt + 1
			end

			packed[mapId] = s
		end

		Nx.prt ("Packed %d", cnt)
	end
end

function Nx.Map:UpdateHotspotsDebug()

	local saved = NxData.NXDBHotspotsT or {}
	NxData.NXDBHotspotsT = saved

	local curMapId = self.DebugMapId

	for mapId, mapT in pairs (saved) do

		local zscale = self:GetWorldZoneScale (mapId)

		for n, spot in ipairs (mapT) do

			local f = self:GetIcon (0)

			local x, y = self:GetWorldPos (mapId, spot.X, spot.Y)
			if self:ClipFrameTL (f, x, y, spot.W * zscale, spot.H * zscale / 1.5) then

				f.NXType = 8000
				f.NXData = mapId
				f.NXData2 = n

				if IsControlKeyDown() then
					f.NxTip = format ("%s %d #%d\n%s %s\n%s %s", self:IdToName (mapId), mapId, n, spot.X, spot.Y, spot.W, spot.H)
				end

				if mapId == curMapId then

					if mapT == self.HotspotDebugCurT and n == self.HotspotDebugCurI then
						f.texture:SetTexture (.2, .2, .5, .5)
					else
						f.texture:SetTexture (0, 0, .4, .35)
					end
				else
					f.texture:SetTexture (.1, 0, 0, .3)
				end
			end

--			Nx.prt ("spot #%d %s %s %s %s", n, spot.X, spot.Y, spot.W, spot.H)
		end
	end
end

function Nx.Map:HotspotDebugClick (button)

--	this = self.Frm
	self.OnMouseDown (self.Frm, button)

	local icon = self.ClickFrm
	local mapId = icon.NXData

	if mapId == self.DebugMapId then

		local index = icon.NXData2

		local saved = NxData.NXDBHotspotsT

		self.HotspotDebugCurT = saved[mapId]
		self.HotspotDebugCurI = index

--		local spot = saved[mapId][index]
--		Nx.prt ("spot #%d %s %s %s %s", n, spot.X, spot.Y, spot.W, spot.H)
	end
end

function Nx.Map:HotspotDebugScroll (x, y)

	local curT = self.HotspotDebugCurT

	local spot = curT and curT[self.HotspotDebugCurI]
	if spot then

		x = x * 2
		y = y * 2

		if IsShiftKeyDown() then
			x = x * .05
			y = y * .05
		end

		if not IsAltKeyDown() then

			spot.X = spot.X + x
			spot.Y = spot.Y + y
		else
			spot.W = spot.W + x
			spot.H = spot.H + y
		end

		Nx.prt ("%s spot #%d %s %s %s %s", self.DebugMapId, self.HotspotDebugCurI, spot.X, spot.Y, spot.W, spot.H)

		return true
	end
end

--]]

--------
-- Get description (color) that goes with a map nane

function Nx.Map:GetMapNameDesc (mapId)

--	Nx.prt ("MapId %s", mapId)

	local nxz = Nx.MapIdToNxzone[mapId] or 0
	local _, minLvl, maxLvl, faction = strsplit ("!", Nx.Zones[nxz])
	minLvl = tonumber (minLvl)
	faction = tonumber (faction)

	local infoStr = format ("%d-%d", minLvl, maxLvl)

	local color = "|cffffffff"
	if self.PlFactionNum == faction then
		color = "|cff20ff20"
	elseif faction == 2 then
		color = "|cffffff00"
	elseif faction < 2 then
		color = "|cffff6060"
	end

	if minLvl == 0 then
		infoStr = "Any"
	end

	if self:GetWorldZone (mapId).City then
		infoStr = "City"
		minLvl = -1
	end

	return color, infoStr, minLvl
end

--------
-- Init minimap ownership

function Nx.Map:MinimapOwnInit()

	self.MMScales = {}

	for n = 1, 6 do
		self.MMScales[n] = (8 - n) * 66.6666666666666 / 5.0
	end

	self.MMScalesC = { 300, 240, 180, 120, 80, 50 }

	for n = 1, 6 do
		self.MMScalesC[n] = self.MMScalesC[n] / 5.0
	end

	--

	local mm = self.MMFrm
	local mmc = getglobal ("MinimapCluster")

	-- Reset since this seems to be getting remembered
	mm:SetMaskTexture ("textures\\MinimapMask")

--	self:MinimapNodeGlowInit()

	-- Commom data for map and dock

	self.MMChkDelay = 5		-- Prevent crash here
	self.MMOwnedFrms = {}

	--

	if not self.MMOwn then
		self.Win:Show (self.StartupShown)
		Nx.Map:MinimapButtonShowUpdate()
		return
	end

	Nx.Map:MinimapButtonShowUpdate (true)

--	self.Win:RegisterEvent ("MINIMAP_UPDATE_ZOOM", self.OnEvent)

	mm:SetClampedToScreen (true)
	mm:SetWidth (140)
	mm:SetHeight (140)
	self.MMAlphaDelay = 100

	mm:SetParent (self.Frm)
    self.MMFrm:SetQuestBlobRingAlpha(1)
	self.MMFrm:SetPOIArrowTexture("")
	mm:SetScript ("OnMouseDown", self.MinimapOnMouseDown)
	mm:SetScript ("OnMouseUp", self.MinimapOnMouseUp)
	mm:SetScript ("OnEnter", self.MinimapOnEnter)
	mm:SetScript ("OnLeave", self.MinimapOnLeave)

--	local prtFrameChildren = Nx.prtFrameChildren
--	prtFrameChildren ("Minimap", mm)
--	prtFrameChildren ("MinimapCluster", mmc)

--[[
	local mmsf = CreateFrame ("ScrollFrame", nil, self.Frm)
	self.MMScFrm = mmsf

	mm:SetParent (mmsf)

	mmsf:SetAllPoints (self.Frm)
--	mmsf:SetScrollChild (mm)
--]]

	-- Ping

--[[	--V4
	local ping = MinimapPing
	ping:SetParent (UIParent)

	self.MMOwnedFrms[ping] = 0
--]]

	-- Data setup

	self.MMModels = {}
	self.MMAddonFrms = {}

	local f = getglobal ("MinimapBackdrop")
	if f then
		f:Hide()
		f:SetParent (mmc)
	end

	hooksecurefunc ("Minimap_ZoomIn", Nx.Map.Minimap_ZoomInClick)
	hooksecurefunc ("Minimap_ZoomOut", Nx.Map.Minimap_ZoomOutClick)

-- Dont work anymore
--	hooksecurefunc ("Minimap_ZoomInClick", Nx.Map.Minimap_ZoomInClick)
--	hooksecurefunc ("Minimap_ZoomOutClick", Nx.Map.Minimap_ZoomOutClick)
--	hooksecurefunc ("MinimapPing_OnEvent", Nx.Map.Minimap_OnEvent)
end

function Nx.Map.Minimap_ZoomInClick()
	local map = Nx.Map:GetMap (1)
	map:MinimapZoom (2)
end

function Nx.Map.Minimap_ZoomOutClick()
	local map = Nx.Map:GetMap (1)
	map:MinimapZoom (-2)
end

function Nx.Map.Minimap_OnEvent()

--	Nx.prt ("Minimap_OnEvent %s", arg1 or "nil")

	local map = Nx.Map:GetMap (1)
	map:MinimapZoom()
end

function Nx.Map:MinimapZoom (value)

--	Nx.prt ("MinimapZoom %s", value)

	if value then
		self:SetScaleOverTime (value)
	end

	local f = getglobal ("MinimapZoomIn")
	if f then
		f:Enable()
	end

	local f = getglobal ("MinimapZoomOut")
	if f then
		f:Enable()
	end
end

function Nx.Map:MinimapOnMouseDown (button)
--	Nx.prt ("click %s", button)

	local this = self			--V4

	local map = Nx.Map.Maps[1]

	if (map.MMZoomType == 0 and button == "LeftButton") or
			(IsShiftKeyDown() and not IsControlKeyDown()) then
		this.NXPing = true
	else
		this.NXPing = nil

		this.NxMap = map
		map.OnMouseDown (this, button)
	end
end

function Nx.Map:MinimapOnMouseUp (button)
--	Nx.prt ("click %s", button)

	local this = self			--V4

	local map = Nx.Map.Maps[1]

	if this.NXPing then
		if map.MMZoomType == 0 then
			Minimap_OnClick (this)
		else
			map:Ping()
		end

	else
		this.NxMap = map
		map.OnMouseUp (this, button)
	end
end

function Nx.Map:Ping()

	local frm = self.Frm
	local mx, my = Nx.Util_GetMouseClampedXY (frm)
	local top = frm:GetTop()
	local bottom = frm:GetBottom()
	my = top - (my + bottom)

	local mm = self.MMFrm

	local scales = self.MMScales
	local info = self.MapWorldInfo[self.MapId]

	if info.City and not info.MMOutside then
		scales = self.MMScalesC
	end

	local zoom = mm:GetZoom() + 1

	local wx, wy = self:FramePosToWorldPos (mx, my)

	local sc = scales[zoom] / mm:GetWidth()
	local x = wx - self.PlyrX
	local y = self.PlyrY - wy

--	Nx.prt ("Ping %f %f %f %f", x, y, sc, mm:GetScale())
--	Nx.prt (" Sc %f, W %f", scales[zoom], mm:GetWidth())

	mm:PingLocation (x / sc, y / sc)
end

function Nx.Map:MinimapOnEnter (motion)

	local map = Nx.Map.Maps[1]
	if map.MMZoomType ~= 0 then
		local this = self			--V4
		this.NxMap = map
		map.IconOnEnter (self, motion)
	end
end

function Nx.Map:MinimapOnLeave (motion)

	local map = Nx.Map.Maps[1]
	if map.MMZoomType ~= 0 then
		local this = self			--V4
		this.NxMap = map
		map.IconOnLeave (self, motion)
	end
end

function Nx.Map:MinimapButtonShowUpdate (justNameplate)

	local opts = Nx:GetGlobalOpts()
	local t = {
		"MinimapCluster", "MapMMShowOldNameplate",
		"NXMiniMapBut", "MapMMButShowCarb",
		"GameTimeFrame", "MapMMButShowCalendar",
		"TimeManagerClockButton", "MapMMButShowClock",
		"MiniMapWorldMapButton", "MapMMButShowWorldMap",
	}

	for n = 1, #t, 2 do

		local skip

		if Nx.Free then
			if t[n] == "MinimapCluster" then
				skip = true
			end
		end

		if not skip then

			local f = getglobal (t[n])
			if f then
				if opts[t[n + 1]] then
					f:Show()
				else
					f:Hide()
				end
			end
		end

		if justNameplate then
			break
		end
	end
end

--------
-- Init minimap node glow. Also called by options

function Nx.Map:MinimapNodeGlowInit (reset)

	local mm = self.MMFrm

	Nx.Timer:Stop ("MapNodeGlow")

	if reset then
		mm:SetBlipTexture ("Interface\\Minimap\\objecticons")
	end

	local delay = self.GOpts["MapMMNodeGD"]

	if delay > 0 then

		if not self.MMGlowInited then

			self.MMGlowInited = true

			-- Force blip textures to stay in mem, so minimap does not show corruption when switching

			local t = mm:CreateTexture (nil, "BACKGROUND")
			t:SetAllPoints()
			t:SetTexture ("Interface\\AddOns\\Carbonite\\Gfx\\Map\\MMOIcons")
			t:Hide()
			local t = mm:CreateTexture (nil, "BACKGROUND")
			t:SetAllPoints()
			t:SetTexture ("Interface\\AddOns\\Carbonite\\Gfx\\Map\\MMOIconsG")
			t:Hide()
		end

		Nx.Timer:Start ("MapNodeGlow", .1, self, self.OnMinimapNodeGlowTimer)
	end
end

function Nx.Map:OnMinimapNodeGlowTimer (name)

	self:MinimapNodeGlowSet ("")

	Nx.Timer:SetFunc (name, self.OnMinimapNodeGlowTimerG)

	return self.GOpts["MapMMNodeGD"] * 2
end

function Nx.Map:OnMinimapNodeGlowTimerG (name)

	self:MinimapNodeGlowSet ("G")

	Nx.Timer:SetFunc (name, self.OnMinimapNodeGlowTimer)

	return self.GOpts["MapMMNodeGD"] * 2
end

function Nx.Map:MinimapNodeGlowSet (letter)

	local count = GetNumTrackingTypes()
	for n = 1, count do
		local name, texture, active, category = GetTrackingInfo (n)

		if active and category == "spell" then
--			Nx.prt ("#%d %s %s %s %s", n, name, texture, active or "0", category or "nil")

--			if IsAltKeyDown() then
--				self.MMFrm:SetBlipTexture ("Interface\\Minimap\\objecticons")
				self.MMFrm:SetBlipTexture ("Interface\\AddOns\\Carbonite\\Gfx\\Map\\MMOIcons" .. letter)
--			end

			break
		end
	end
end

--------
--

function Nx.Map:MinimapUpdate()

	if not self.MMOwn then
		self:MinimapDetachFrms()
		return
	end

	--

	if self.GOpts["MapMMMoveCapBars"] then

		local y = 0

		for n = 1, NUM_EXTENDED_UI_FRAMES do

			local f = getglobal ("WorldStateCaptureBar" .. n)
			if f and f:IsShown() then

--				Nx.prtFrame ("WorldState", f)

				f:SetPoint ("TOPRIGHT", self.Win.Frm, "BOTTOMRIGHT", 0, y)
				y = y + f:GetHeight()
			end
		end
	end

	--

	if self.DebugScale then
		self.MMScale = 0
		local sc = self.DebugScale

		for n = 1, 6 do
			self.MMScales[n] = (8 - n) * 66.6666666666666 / sc
		end
	end

	--

	local mm = self.MMFrm
	local lOpts = self.LOpts

	local scales = self.MMScales
	local info = self.MapWorldInfo[self.RMapId]

	if info.City and not info.MMOutside then
		scales = self.MMScalesC
	end

	local zoomType = 0
	local zoom = mm:GetZoom (1)

	local dock = lOpts.NXMMFull or self.GOpts["MapMMDockAlways"]

	if self.Win:IsSizeMax() and self.GOpts["MapMMDockOnMax"] then
		dock = true
	end

	if not dock and not self.InstanceId
			and self.ScaleDraw > lOpts.NXMMDockOnAtScale then

		mm:ClearAllPoints()

		for n = 1, 6 do

			local sz = scales[n]

			if self:ClipMMW (mm, self.PlyrX, self.PlyrY, sz, sz) then
--				Nx.prt ("MM #%d sz %d", n, sz)
				zoomType = n
--				self.MMSize = sz * self.ScaleDraw
				break
			end
		end
	end

	local al = lOpts.NXMMAlpha

	local indoors = IsIndoors()
	local resting = IsResting()
	local indoorChange = self.Indoors ~= indoors
	self.Indoors = indoors
    local bugged = false
    local bugtest = GetSubZoneText()
    for _,zonetest in ipairs(Nx.BuggedAreas) do
		if GetCurrentMapAreaID() == zonetest and not indoors then
			bugged = true
		end
	end
	for _,zonetest in ipairs(Nx.BuggedSubZones) do
		if bugtest == zonetest then
			bugged = true
		end
	end
	if self.InstanceId then
		al = 1
	else
		if indoors and self.GOpts["MapMMDockIndoors"] then
			zoomType = 0
		end
		if bugged and self.GOpts["MapMMDockBugged"] then
			zoomType = 0
		end
		if indoorChange and self.GOpts["MapMMIndoorTogFullSize"] then
			lOpts.NXMMFull = false
			if not info.City and indoors then
				lOpts.NXMMFull = true
			end
			self.MMMenuIFull:SetChecked (lOpts.NXMMFull)
			Nx.Menu:CheckUpdate (self.MMMenuIFull)
		end
		if self.GOpts["MapMMBuggedTogFullSize"] then
			if bugged then
				lOpts.NXMMFull=true
			else
				lOpts.NXMMFull=false
			end
			self.MMMenuIFull:SetChecked(lOpts.NXMMFull)
			Nx.Menu:CheckUpdate(self.MMMenuIFull)
		end
		if zoomType == 0 then
			al = lOpts.NXMMDockAlpha
		end

		if IsControlKeyDown() then
			al = IsAltKeyDown() and 1 or .8
			self.MMZoomChanged = true
		end
	end

	if self.MMZoomType ~= zoomType or zoomType > 0 and self.MMScale ~= self.ScaleDraw
			or indoorChange then

		self.MMZoomType = zoomType
		self.MMScale = self.ScaleDraw

		self.MMZoomChanged = true

--		Nx.prt ("MMZoomChanged")

		if self.MMAlphaDelay == 0 then
--		if zoomType == 0 and self.MMAlphaDelay == 0 then
			self.MMAlphaDelay = 2
		end
	end

	if self.MMAlphaDelay > 0 then
		self.MMAlphaDelay = self.MMAlphaDelay - 1
--		al = 1
--		Nx.prt ("MMAFix %s", self.MMAlphaDelay)
		self.MMZoomChanged = true
	end

	mm:SetAlpha (al)

	self:MinimapDetachFrms()

	if zoomType > 0 then

		self:MinimapUpdateMask ("MapMMSquare")

		local above = IsControlKeyDown()

		if self.GOpts["MapMMAboveIcons"] then
			above = not above
		end

		local lvl = self.Level
		if above then
			lvl = lvl + 15
		end

		mm:SetFrameLevel (lvl)

		self:MinimapUpdateDetachedFrms (lvl + 1)
		self.Level = self.Level + 2

	else

		local sc = self.MMFScale

		self.MMFScale = Nx.InBG and lOpts.NXMMDockScaleBG or lOpts.NXMMDockScale

		if lOpts.NXMMFull then
			self.MMFScale = min (self.MapW, self.MapH) / 140
		end
	end

--[[
	local mmsf = self.MMScFrm

	mmsf:SetScrollChild (mm)
	mmsf:UpdateScrollChildRect()

	mmsf:SetFrameLevel (self.Level)
--]]

end

function Nx.Map:MinimapUpdateEnd()

	if not self.MMOwn then
		return
	end

	local mm = self.MMFrm
	local mmfull = self.LOpts.NXMMFull

	if self.Win:IsSizeMax() and self.GOpts["MapMMHideOnMax"] or self.MMFScale < .02 then

		mm:SetPoint ("TOPLEFT", 1, 0)
		mm:SetScale (.02)
		mm:SetFrameLevel (1)
--		mm:Hide()

		for n, f in ipairs (self.MMModels) do
			f:SetScale (.001)
		end
		return
	end

	if self.MMZoomType == 0 then

		self:MinimapUpdateMask ("MapMMDockSquare")

		local iconScale = self.GOpts["MapMMDockIScale"]

		self:MinimapSetScale (self.MMFScale, iconScale)
--		mm:SetScale (self.MMFScale)

		local x = 0
		local y = 0
		local sz = 140 * self.MMFScale

		if self.GOpts["MapMMDockRight"] then
			x = (self.MapW - sz + 1)
		end
		if self.GOpts["MapMMDockBottom"] then
			y = (self.MapH - sz + 1)
		end

		mm:ClearAllPoints()
		mm:SetPoint ("TOPLEFT", (x + self.GOpts["MapMMDXO"]) / iconScale,
										(-y - self.GOpts["MapMMDYO"]) / iconScale)
		mm:Show()

		mm:SetFrameLevel (self.Level)
		self:MinimapUpdateDetachedFrms (self.Level + 1)
		self.Level = self.Level + 2
	end

	if self.MMZoomChanged then

		self.MMZoomChanged = false

		local zoom = max (self.MMZoomType - 1, 0)

		if self.MMZoomType == 0 then
			zoom = self.GOpts["MapMMDockZoom"]
		end

		local z = zoom - 1
		if z < 0 then
			z = 1
		end

		-- Force icon update
		mm:SetZoom (z)
		mm:SetZoom (zoom)

--		Nx.prt ("zoom %d", zoom)

		if self.MMZoomType == 0 then
			mm:SetAlpha (1)
		end

--		if Gatherer then
--			Nx.Timer:Start ("Gatherer", .1, self, self.OnGathererTimer)
--		end
	end

	-- Transfer window and minimap scale

	--V4 gone TEST!!!!!!!!!
--	MinimapPing:SetScale (self.Win.Frm:GetScale() * mm:GetScale())
end

function Nx.Map:MinimapSetScale (scale, iconScale)

	local mm = self.MMFrm
	local sz = 140 * scale / iconScale
	mm:SetWidth (sz)
	mm:SetHeight (sz)
	mm:SetScale (iconScale)
end

function Nx.Map:MinimapUpdateMask (optName)

	local name = self.GOpts[optName] and "Interface\\Buttons\\White8x8" or "textures\\MinimapMask"

--	if IsControlKeyDown() then
--		self.MMMaskName = nil
--	end

	if self.MMMaskName ~= name then
		self.MMMaskName = name

		local mm = self.MMFrm
		mm:SetMaskTexture (name)

--		Nx.prt ("MMmask %s", name)
	end

	local name = self.MMZoomType == 0 and "Interface\\Minimap\\MinimapArrow" or ""

	if self.MMArrowName ~= name then
		self.MMArrowName = name

		self.MMFrm:SetPlayerTexture (name)

--		Nx.prt ("MMmask %s", name)
	end
end

--[[
function Nx.Map.OnGathererTimer()

	Nx.prt ("Gatherer update")

	local mn = Gatherer["MiniNotes"]
	if mn then
		local update = mn["ForceUpdate"]
		if update then
			update()
		end
	else
		Nx.prt ("no mn")
	end
end
--]]

function Nx.Map:MinimapDetachFrms()

	local mm = self.MMFrm
	local mmc = getglobal ("MinimapCluster")
	local winf = self.Win.Frm
	local dock = Nx.Map.Dock

	if dock.InitPending then
		return
	end

	self.MMChkDelay = self.MMChkDelay - 1

	if self.MMChkDelay < 1 then

		self.MMChkDelay = 40		-- Reset

		-- Find and own any minimap child windows

		local mmNames = self.AddonMinimapNames
		local ch = { mm:GetChildren() }

		for n = 1, #ch do

			local c = ch[n]
			if c ~= mmc then

--				if not c:IsVisible() then
--					Nx.prt ("MM Frm v0 %s", c:GetName() or "nil")
--				end

				if c:IsShown() and not self.MMOwnedFrms[c] then

					if c:IsObjectType ("Model") then
--						Nx.prt ("MM Model %s", c:GetName() or "nil")

						if self.MMOwn then

							c:SetParent (winf)
							self.MMOwnedFrms[c] = 0
							tinsert (self.MMModels, c)
						end

					elseif c:IsObjectType ("Frame") then

						local name = gsub (c:GetName() or "", "%d", "")

						if mmNames[name] then

							if self.MMOwn then
								self.MMOwnedFrms[c] = 0
								self.MMAddonFrms[c] = 1
							end

						elseif dock.MMFrms then

							self.MMOwnedFrms[c] = 0
							tinsert (dock.MMFrms, c)

--							Nx.prt ("MM Frm %s #%s", c:GetName() or "nil", c:GetNumChildren())
--							Nx.prtFrameChildren (c:GetName() or "nil", c)

							if c:GetNumChildren() > 0 then

								local ch = { c:GetChildren() }
								for k, c in ipairs (ch) do

--									if not c:IsShown() then
--										Nx.prt ("MM Frm v0 %s", c:GetName() or "nil")
--									end

									if c:IsShown() then
										if c:IsObjectType ("Frame") then
											local pt, relTo = c:GetPoint()
											if relTo == mm then
												tinsert (dock.MMFrms, c)
--												Nx.prt ("MMCC %s", c:GetName())
											end
										end
									end
								end
							end

						end
					end
				end
			end
		end
	end

	--

	dock:MinimapDetachFrms()
end

function Nx.Map:MinimapUpdateDetachedFrms (lvl)

--	self:MinimapDetachFrms()

	local sc = self.MMFScale
	local arrowsc = sc
	local msc = min (1 / sc, 1) * .5

	if Nx.InBG then
		arrowsc = .001
	end

	local mmplyr = Nx.Map.MinimapPlyrModel		-- Old

	for n, f in ipairs (self.MMModels) do

		if f:IsShown() then

			f:SetFrameLevel (lvl)

			local name = f:GetModel()

			if name == "interface\\minimap\\ping\\minimapping.m2" then
				f:SetScale (sc)
				f:SetModelScale (msc)
				f:SetAlpha (1)

			elseif f == mmplyr then
				if self.MMZoomType == 0 then
					f:SetScale (max (.4, min (.9, sc)) * self.GOpts["MapPlyrArrowSize"] / 28)
--					f:SetModelScale (min (.2, sc))
					f:SetModelScale (1)
				else
					f:SetScale (.1)
					f:SetModelScale (1)
				end
			else
				f:SetScale (arrowsc)
				f:SetModelScale (msc)
			end
--[[
			if IsAltKeyDown() then
				Nx.prt ("Model %s (%s %s)", f:GetModel() or "", f:GetScale(), f:GetModelScale())
			end
--]]
		end
	end

	for f, v in pairs (self.MMAddonFrms) do
		f:SetFrameLevel (lvl)
--		f:SetScale (sc)
--		f:SetModelScale (msc)
	end
end

--------

function Nx.Map:Menu_OnGoto (item)
	self:SetTargetAtClick()
end

function Nx.Map:Menu_OnClearGoto (item)
	self:ClearTargets()
	self.Guide:ClearAll()
end

function Nx.Map:Menu_OnAddNote()
	local wx, wy = self:FramePosToWorldPos (self.ClickFrmX, self.ClickFrmY)
	local zx, zy = self:GetZonePos (self.MapId, wx, wy)
	self:AddNote ("?", self.MapId, zx, zy)
end

function Nx.Map:AddNote (name, id, x, y)
	Nx.Fav:Record ("Note", name, id, x, y)
end

function Nx.Map:Menu_OnMonitorZone (item)
	Nx.Com:MonitorZone (self.MenuMapId, item:GetChecked())
end

function Nx.Map:Menu_OnScaleSave()
	self.CurOpts.NXScaleSave = self.Scale
end

function Nx.Map:Menu_OnScaleRestore()
	local s = self.CurOpts.NXScaleSave
	if s then
		self.Scale = s
		self.StepTime = 10
	else
		Nx.prt ("Scale not set")
	end
end

function Nx:NXMapKeyScaleRestore()
	local map = self.Map:GetMap (1)
	map:Menu_OnScaleRestore()
end

function Nx.Map:Menu_OnPlyrFollow (item)
	self.CurOpts.NXPlyrFollow = item:GetChecked()
end

function Nx.Map:Menu_OnShowWorld (item)
	self.CurOpts.NXWorldShow = item:GetChecked()
end

function Nx.Map:Menu_OnShowPlayerZone()
	self:GotoCurrentZone()
end

function Nx.Map:Menu_OnShowKills (item)
	self.KillShow = item:GetChecked()
end

function Nx.Map:Menu_OnDetailAlpha (item)
	self.LOpts.NXDetailAlpha = item:GetSlider()
end

function Nx.Map:Menu_OnBackgndAlphaFade (item)
	self.BackgndAlphaFade = item:GetSlider()
end

function Nx.Map:Menu_OnBackgndAlphaFull (item)
	self.BackgndAlphaFull = item:GetSlider()
end

function Nx.Map:Menu_OnDotZoneScale (item)
	self.DotZoneScale = item:GetSlider()
end

function Nx.Map:Menu_OnDotPalScale (item)
	self.DotPalScale = item:GetSlider()
end

function Nx.Map:Menu_OnDotPartyScale (item)
	self.DotPartyScale = item:GetSlider()
end

function Nx.Map:Menu_OnDotRaidScale (item)
	self.DotRaidScale = item:GetSlider()
end

function Nx.Map:Menu_OnIconScale (item)
	self.IconScale = item:GetSlider()
end

function Nx.Map:Menu_OnIconNavScale (item)
	self.IconNavScale = item:GetSlider()
end

function Nx.Map:Menu_OnOptions (item)
	Nx.Opts:Open ("Map")
end

-- Debug sub menu

function Nx.Map:Menu_OnMapDebugFullCoords (item)
	self.DebugFullCoords = item:GetChecked()
end

function Nx.Map:Menu_OnQuestDebug (item)
	Nx.Quest.Debug = item:GetChecked()
end

-- Plyr icon menu

function Nx.Map:Menu_OnWhisper()

	for _, name in pairs (Nx.Map.PlyrNames) do

		local box = ChatEdit_ChooseBoxForSend()
		ChatEdit_ActivateChat (box)
		box:SetText ("/w " .. name .. " " .. box:GetText())
--[[
		local frm = DEFAULT_CHAT_FRAME
		local eb = frm["editBox"]
		if not eb:IsVisible() then
			ChatFrame_OpenChat ("/w " .. name, frm)
		else
			eb:SetText ("/w " .. name .. " " .. eb:GetText())
		end
--]]
		break
	end
end

-- Plyr icon menu

function Nx.Map:Menu_OnInvite()

	for _, name in pairs (Nx.Map.PlyrNames) do
		InviteUnit (name)
		break
	end
end

function Nx.Map:Menu_OnGetQuests (item)

	for _, name in pairs (Nx.Map.PlyrNames) do
		Nx.Quest:GetFromPlyr (name)
		break
	end
end

function Nx.Map:Menu_OnTrackPlyr (item)

	for _, name in pairs (Nx.Map.PlyrNames) do
		self.TrackPlyrs[name] = true
	end
end

function Nx.Map:Menu_OnRemoveTracking (item)

	for _, name in pairs (Nx.Map.PlyrNames) do
		self.TrackPlyrs[name] = nil
	end
end

function Nx.Map:Menu_OnReportPlyrAFK (item)
	local n = 0
	for k, v in pairs (Nx.Map.AFKers) do
		ReportPlayerIsPVPAFK (v)
		n = n + 1
	end
	Nx.prt ("%d reported", n)
end

--[[
function Nx.Map:Menu_OnReportAllAFK (item)

	local members = MAX_PARTY_MEMBERS
	local unitName = "party"

	if IsInRaid() > 0 then
		members = MAX_RAID_MEMBERS
		unitName = "raid"
	end

	local cnt = 0

	for i = 1, members do

		local unit = unitName..i

		if not UnitIsUnit (unit, "player") then
			ReportPlayerIsPVPAFK (unit)
			cnt = cnt + 1
		end
	end

	Nx.prt ("%d reported", cnt)
end
--]]

function Nx.Map:Menu_OnGrowConflictBars (item)
	self.BGGrowBars = item:GetChecked()
end

function Nx.Map:GMenuOpen (icon, typ)

	self.GIconMenuITogInst:Show (false)
	self.GIconMenuIFindNote:Show (false)

	if typ == 3000 then
		if icon.UData then
			self.GIconMenuITogInst:Show()
		end

		if icon.FavData1 then
			self.GIconMenuIFindNote:Show()
		end
	end

	Nx.Quest:OpenGiverIconMenu (icon, typ)

	self.GIconMenu:Open()
end

--------
-- Instance icon

function Nx.Map:GMenu_OnTogInst()

	local icon = self.ClickIcon

	local mapId = icon.UData

	if mapId then
		if self.InstMapId == mapId then
			self:SetInstanceMap()
		else
			local atlas = _G["AtlasMaps"]

--			Nx.prt ("%s", mapId)

			if not (Nx.Map.InstanceInfo[mapId] or atlas) then
				UIErrorsFrame:AddMessage ("This instance map requires the Atlas addon be installed", 1, .1, .1, 1)
				return
			end

			self:SetInstanceMap (mapId)
		end
	end
end

--------
-- Favorite icon

function Nx.Map:GMenu_OnFindNote()
	Nx.Fav:ShowIconNote (self.ClickIcon)
end

--------
-- Generic icon goto

function Nx.Map:GMenu_OnGoto()

	Nx.Quest.Watch:ClearAutoTarget()

	if self.ClickType == 3001 then
		Nx.Social:GotoPunk (self.ClickIcon)

	else
		local icon = self.ClickIcon
		local x = icon.X
		local y = icon.Y
		local name = icon.Tip and strsplit ("\n", icon.Tip) or ""

		self:SetTarget ("Goto", x, y, x, y, false, 0, name)
	end
end

--------
-- Generic icon goto

function Nx.Map:GMenu_OnPasteLink()

	local name

	if self.ClickType == 3001 then
		name = Nx.Social:GetPunkPasteInfo (self.ClickIcon)
	else
		local icon = self.ClickIcon
		name = gsub (icon.Tip, "\n", ", ")
	end

	name = gsub (name, "|cff......", "")
	name = gsub (name, "|r", "")

	local frm = DEFAULT_CHAT_FRAME
	local eb = frm["editBox"]
	if eb:IsVisible() then
		eb:SetText (eb:GetText() .. name)
	else
		Nx.prt ("No edit box open!")
	end
end

--------
-- Send BG messages

function Nx.Map:BGMenu_OnIncoming (item)
	self:BGMenu_Send (NXlBGMsgIncoming)
end

function Nx.Map:BGMenu_OnClear (item)
	self:BGMenu_Send ("Clear")
end

function Nx.Map:BGMenu_OnHelp (item)
	self:BGMenu_Send ("Help")
end

function Nx.Map:BGMenu_OnAttack (item)
	self:BGMenu_Send ("Attack")
end

function Nx.Map:BGMenu_OnGuard (item)
	self:BGMenu_Send ("Guard")
end

function Nx.Map:BGMenu_OnLosing (item)
	self:BGMenu_Send ("Losing")
end

--------
-- BG icon status

function Nx.Map:BGMenu_OnStatus (item)

	local id, x, y, str = strsplit ("~", self.BGMsg)
	if id == "1" then
		self:BGMenu_Send()
--		SendChatMessage (str, "BATTLEGROUND")
	else
		Nx.prt ("No Status")
	end
end

--------
-- BG icon status

function Nx.Map:BGMenu_Send (msg)

	local id, tx, ty, str = strsplit ("~", self.BGMsg)
	tx, ty = self:GetWorldPos (self.RMapId, tonumber (tx), tonumber (ty))

	local members = MAX_PARTY_MEMBERS
	local unitName = "party"

	if IsInRaid() then
		members = MAX_RAID_MEMBERS
		unitName = "raid"
	end

	local cnt = 0
	local maxDist = (100 / 4.575) ^ 2	-- Yards to world space squared

	for i = 1, members do

		local unit = unitName..i
		local pX, pY = GetPlayerMapPosition (unit)

		if (pX > 0 or pY > 0) and not UnitIsDeadOrGhost (unit) then

			local x, y = self:GetWorldPos (self.RMapId, pX * 100, pY * 100)
			local dist = (tx - x) ^ 2 + (ty - y) ^ 2

--			Nx.prt ("%s %s %s = %s", unit, pX, pY, sqrt (dist) * 4.575)

			if dist <= maxDist then
				cnt = cnt + 1
--				Nx.prt (" %s", UnitName (unit))
			end
		end
	end

	local dstr = ", No "
	if cnt > 0 then
		dstr = format (", %d ", cnt)
	end

	dstr = dstr .. Nx.Map.PlFactionShort .. " in area"

	if msg then
		SendChatMessage (msg .. " - " .. str .. dstr, "BATTLEGROUND")
	else
		SendChatMessage (str .. dstr, "BATTLEGROUND")
	end

--	Nx.prt ("count %d", cnt)
--	Nx.prt (msg .. " - " .. str .. dstr)
end

function Nx.Map:BGIncSendTimer()

	local str = format ("Inc %s", self.BGIncNum)
	self:BGMenu_Send (str)

	self.BGIncNum = 0

--	Nx.prt ("BGIncSendTimer %s", str)
end

--------
-- Override blizz BC map toggle

function ToggleWorldMap()

--	Nx.prt ("ToggleWorldMap")

	local opts = Nx:GetGlobalOpts()

	if Nx.Map.BlizzToggling or WorldMapFrame:IsShown() or IsAltKeyDown() or not opts["MapMaxOverride"] then
		Nx.Map:BlizzToggleWorldMap()
	else
		Nx.Map:ToggleSize()
	end
end

--------
-- Override blizz WotLK frame toggle

function ToggleFrame (frame)

	if frame ~= WorldMapFrame then
		if frame:IsShown() then
			HideUIPanel (frame)
		else
			ShowUIPanel (frame)
		end
		return
	end

--	Nx.prt ("ToggleWorldMap")

	local opts = Nx:GetGlobalOpts()

	if Nx.Map.BlizzToggling or WorldMapFrame:IsShown() or IsAltKeyDown() or not opts["MapMaxOverride"] then
		Nx.Map:BlizzToggleWorldMap()
	else
		Nx.Map:ToggleSize()
	end
end

--------
-- Hook
--[[
function Nx.Map.ToggleWorldMapHook()

	Nx.prt ("ToggleWorldMapHook")

	if 1 then return end

	local self = Nx.Map:GetMap (1)
	local ismax = self.Win:IsSizeMax()

	if ismax then

		Nx.prt ("ToggleWorldMapHook Max")

--		HideUIPanel (WorldMapFrame)
		self:RestoreSize()
--		return
	end

	if IsAltKeyDown() or Nx.Map.BlizzToggling then
		Nx.prt ("ToggleWorldMapHook Alt or Tog")
	else

		if WorldMapFrame:IsShown() then

			HideUIPanel (WorldMapFrame)

			if not ismax then
				self:ToggleSize()
			end
		end
	end
end
--]]

--------
-- Blizz map toggle

function Nx.Map:BlizzToggleWorldMap()

	if WorldMapFrame:IsShown() then
		HideUIPanel (WorldMapFrame)
	else
		local map = self:GetMap (1)
		map:DetachWorldMap()
		ShowUIPanel (WorldMapFrame)
	end
end

--------
-- Key binding toggle map size. Open blizz map
-- global func

function Nx:NXMapKeyTogOriginal()

	-- Does not get called when map fullscreen

	Nx.Map.BlizzToggling = true
	ToggleWorldMap()
	Nx.Map.BlizzToggling = nil
end

--------
-- Key binding toggle map size
-- global func

function Nx:NXMapKeyTogNormalMax()
	Nx.Map:ToggleSize()
end

--------
-- Key binding toggle map size
-- global func

function Nx:NXMapKeyTogNoneMax()
	Nx.Map:ToggleSize (1)
end

--------
-- Key binding toggle map size
-- global func

function Nx:NXMapKeyTogNoneNormal()
	Nx.Map:ToggleSize (0)
end

--------
-- Toggle map size
-- global func

function Nx.Map:ToggleSize (szmode)

	if not self.Maps then	-- Healbot called ToggleFrame on load that caused us to fail in GetMap
		return
	end

	local map = self:GetMap (1)
	local win = map.Win

	if not win:IsShown() then

		win:Show()

		if szmode == 0 then
			map:RestoreSize()

		elseif szmode == 1 then
			map:MaxSize()

		elseif self.GOpts["MapMaxCenter"] then
			map:MaxSize()
		end

	elseif szmode then
		win:Show (false)

	elseif not win:IsSizeMax() then
		map:MaxSize()

	else
		map:RestoreSize()
	end

	if Nx.TooltipOwner == win.Frm then
		GameTooltip:Hide()
		Nx.TooltipOwner = nil
	end
end

--------
-- Restore map size

function Nx.Map:RestoreSize()

	self:MouseEnable (false)

	if self.Win:IsSizeMax() then

--		Nx.prt ("Map RestoreSize ToggleSize")
		self.Win:ToggleSize()

		self:RestoreView ("")
		self:DetachWorldMap()

		if self.GOpts["MapMaxRestoreHide"] then
			self.Win:Show (false)
		end
	end

	local wname = self:GetWinName()
	for n, name in pairs (UISpecialFrames) do
		if name == wname then
			tremove (UISpecialFrames, n)
			break
		end
	end
end

--------
-- Maximize map size

function Nx.Map:MaxSize()

	if not self.Win:IsSizeMax() then

		if NxData.NXDBMapMax then
			Nx.prt ("MapMax %s", debugstack (2, 4, 0))
		end

		self.Win:ToggleSize()

		self:SaveView ("")

		self:MouseEnable (true)

		if self.GOpts["MapMaxCenter"] then
			self:CenterMap()
		end
		self.StepTime = min (self.StepTime, 1)
	end
end

--------
-- Key binding toggle owned minimap to full size
-- global func

function Nx:NXMapKeyTogMiniFull()

	if Nx.Free then
		return
	end

	local map = Nx.Map:GetMap (1)
	map.LOpts.NXMMFull = not map.LOpts.NXMMFull
	map.MMZoomChanged = true
	map.MMMenuIFull:SetChecked (map.LOpts.NXMMFull)
	Nx.Menu:CheckUpdate (map.MMMenuIFull)
end

--------
-- Key binding toggle herbs
-- global func

function Nx:NXMapKeyTogHerb()

	local map = Nx.Map:GetMap (1)
	Nx.CharOpts["MapShowGatherH"] = not Nx.CharOpts["MapShowGatherH"]
	map.MenuIShowHerb:SetChecked (Nx.CharOpts, "MapShowGatherH")
	map.Guide:UpdateGatherFolders()

end

--------
-- Key binding toggle mining
-- global func

function Nx:NXMapKeyTogMine()

	local map = Nx.Map:GetMap (1)
	Nx.CharOpts["MapShowGatherM"] = not Nx.CharOpts["MapShowGatherM"]
	map.MenuIShowMine:SetChecked (Nx.CharOpts, "MapShowGatherM")
	map.Guide:UpdateGatherFolders()

end

--------
-- Enable or disable map mouse input

function Nx.Map:MouseEnable (max)

--	Nx.prt ("MouseEnable %s %s", max and "max" or "min", alt and 1 or 0)

	local on = true

	if max then
		if self.GOpts["MapMaxMouseIgnore"] then
			on = IsAltKeyDown() and true or false		-- IsAltKeyDown returns nil or 1
		end
	else
		if self.GOpts["MapMouseIgnore"] then
			on = IsAltKeyDown() and true or false		-- IsAltKeyDown returns nil or 1
		end
	end

	if self.MouseEnabled ~= on then

--		Nx.prt ("MouseEnable up")

		self.MouseEnabled = on

		self.Win:EnableMouse (on)

		if on then
			self.ButAutoScaleOn.Frm:Show()
			self:UpdateToolBar()		-- Will show or hide
		else
			self.ButAutoScaleOn.Frm:Hide()
			self.ToolBar.Frm:Hide()
		end

		self.Frm:EnableMouse (on)
		self.Frm:EnableMouseWheel (on)

		self.MMFrm:EnableMouse (on)
		self.MMFrm:EnableMouseWheel (on)

		for n, f in ipairs (self.IconFrms) do
			f:EnableMouse (on)
		end

		for n, f in ipairs (self.IconStaticFrms) do
			f:EnableMouse (on)
		end
	end
end

--------
-- Handle events
-- self = map table

function Nx.Map:OnEvent (event, ...)

	local this = self.Win.Frm

--	Nx.prtVar ("Map Event", self)
--	Nx.prt ("Map Event %s", event)

	if event == "WORLD_MAP_UPDATE" then

		if Nx.Quest then
			Nx.Quest:MapChanged()
		end

		if this:IsVisible() then
			this.NxMap:UpdateAll()
		end

--	elseif event == "MINIMAP_UPDATE_ZOOM" then		-- Seems useless
--		Nx.prt ("Z %s", GetCVar ("minimapZoom") or "nil")
--		Nx.prt ("IZ %s", GetCVar ("minimapInsideZoom") or "nil")

--[[
	elseif event == "PLAYER_ENTERING_WORLD" then

		local map = this.NxMap

--		if not map.RMapId then

			local rid = map:GetRealMapId()
--			map.RMapId = rid

			Nx.prt ("PLAYER_ENTERING_WORLD rid %s", rid)

			map.CurOpts = nil
			map:SwitchOptions (rid)
			map:SwitchRealMap (rid)
--		end
--]]
	end
end

function Nx.Map:OnMouseDown (button)

	local map = self.NxMap	--V4 this
	local this = map.Frm

	local x, y = GetCursorPosition()
	x = x / this:GetEffectiveScale()
	y = y / this:GetEffectiveScale()

	map:CalcClick()

	ResetCursor()

--	Nx.prt ("Map MouseDown %s %s %s %s %s", button, x, y, rgt, bot)

	if button == "LeftButton" then

--[[
		if map["DebugHotspots"] then

			map.HotspotDebugCurT = nil

			if map:IsDoubleClick() then
				map.DebugMapId = map.MapId
			else
				map.LClickTime = GetTime()
				map.Scrolling = true
				map.ScrollingX = x
				map.ScrollingY = y
				map.ScrollingFrm = map.ClickFrm
			end
			return
		end
--]]

		if IsControlKeyDown() and map:CallFunc ("MapButLCtrl") then	-- If func does nothing continue

		elseif IsAltKeyDown() and map:CallFunc ("MapButLAlt") then	-- If func does nothing continue

		elseif IsShiftKeyDown() then
			map:Ping()

		else

			if map:IsDoubleClick() then

				map:CenterMap()
				map.DebugMapId = map.MapId

			else
				map.LClickTime = GetTime()
				map.Scrolling = true
				map.ScrollingX = x
				map.ScrollingY = y
				map.ScrollingFrm = map.ClickFrm
			end
		end

	elseif button == "MiddleButton" then

		if IsControlKeyDown() then
			map:CallFunc ("MapButMCtrl")
		elseif IsAltKeyDown() then
			map:CallFunc ("MapButMAlt")
		else
			map:CallFunc ("MapButM")
		end

	elseif button == "RightButton" then

		if IsControlKeyDown() and map:CallFunc ("MapButRCtrl") then

		elseif IsAltKeyDown() and map:CallFunc ("MapButRAlt") then

		else
			map:CallFunc ("MapButR")
		end

	elseif button == "Button4" then

		if IsControlKeyDown() then
			map:CallFunc ("MapBut4Ctrl")
		elseif IsAltKeyDown() then
			map:CallFunc ("MapBut4Alt")
		else
			map:CallFunc ("MapBut4")
		end
	end
end

function Nx.Map:CalcClick()

	local f = self.Frm

	local x, y = GetCursorPosition()
	x = x / f:GetEffectiveScale()
	y = y / f:GetEffectiveScale()

	self.ClickFrmX = x - f:GetLeft()
	self.ClickFrmY = f:GetTop() - y
end

function Nx.Map:IsDoubleClick()

	if GetTime() - self.LClickTime < .5 then
--		Nx.prt ("Map DBL Click")
		self.LClickTime = 0
		return true
	end
end

function Nx.Map:OpenMenu()

	local opts = self:GetOptionsT (self.MapIndex)

	self.MenuIPlyrFollow:SetChecked (self.CurOpts.NXPlyrFollow)
	self.MenuIShowWorld:SetChecked (self.CurOpts.NXWorldShow)

--PAIDS!
	self.MenuIMonitorZone:SetChecked (Nx.Com:IsZoneMonitored (self.MapId))
--PAIDE!

	self.MenuMapId = self.MapId

	self.Menu:Open()
end

function Nx.Map:OnMouseUp (button)

--	Nx.prt ("Map MouseUp "..tostring (button))

	local this = self			--V4
	local map = this.NxMap
	map.Scrolling = false
end

--------

function Nx.Map:ClickZoomIn()
	self:MouseWheel (1)
end

function Nx.Map:ClickZoomOut()
	self:MouseWheel (-1)
end

function Nx.Map:OnMouseWheel (value)
	self.NxMap:MouseWheel (value)
end

function Nx.Map:MouseWheel (value)

--	Nx.prt ("Map MouseWheel "..tostring (value))

	local map = self
	local this = map.Frm

	if map.MMZoomType == 0 and Nx.Util_IsMouseOver (map.MMFrm) then

		map.MMZoomChanged = true

		local i = map.GOpts["MapMMDockZoom"]

		if value < 0 then
			i = max (i - 1, 0)
		else
			i = min (i + 1, 5)
		end

		map.GOpts["MapMMDockZoom"] = i

		return
	end

	local x, y = GetCursorPosition()
	x = x / this:GetEffectiveScale()
	y = y / this:GetEffectiveScale()

	local left = this:GetLeft()
	local rgt = this:GetRight()
	local top = this:GetTop()
	local bot = this:GetBottom()

	local ox = map.MapPosX + (x - left - map.PadX - map.MapW / 2) / map.Scale
	local oy = map.MapPosY + (top - y - map.TitleH - map.MapH / 2) / map.Scale

	map.Scale = map:ScrollScale (value)
	map.StepTime = 10

	map.MapScale = map.Scale / 10.02

	local nx = map.MapPosX + (x - left - map.PadX - map.MapW / 2) / map.Scale
	local ny = map.MapPosY + (top - y - map.TitleH - map.MapH / 2) / map.Scale

	map.MapPosX = map.MapPosX + ox - nx
	map.MapPosY = map.MapPosY + oy - ny
end

function Nx.Map:ScrollScale (value)

	local s = self.Scale
	if value < 0 then
		value = value * .76923
	end

	return math.max (s + value * s * .3, .015)
end

function Nx.Map:SetScaleOverTime (steps)

	local step = steps >= 0 and 1 or -1
	for n = 1, abs (steps) do
		self.Scale = self:ScrollScale (step)
	end

	self.StepTime = 10
end

--------
-- Update event handler

function Nx.Map.OnUpdate (this, elapsed)	--V4 this


--	if IsControlKeyDown() then		return	end


	local Nx = Nx

	Nx.Timer:ProfilerStart ("Map OnUpdate")

	local profileTime = GetTime()

	local map = this.NxMap
	local gopts = map.GOpts
	local Quest = Nx.Quest

	map.Tick = map.Tick + 1

	map.EffScale = this:GetEffectiveScale()
	map.Size1 = gopts["MapLineThick"] * .75 / map.EffScale

	Nx.Map:UpdateOptions (map.MapIndex)

	local winx, winy = Nx.Util_IsMouseOver (this)

	if not this:IsVisible() or not map.MouseEnabled then
		winx = nil
		map.Scrolling = false
	end

	if map.MMZoomType == 0 and Nx.Util_IsMouseOver (map.MMFrm) then
		winx = nil
	end

	map.MouseIsOver = winx

	-- Scroll map with mouse

	if map.Scrolling then

		local cx, cy = GetCursorPosition()
		cx = cx / map.EffScale
		cy = cy / map.EffScale

		local x = cx - map.ScrollingX
		local y = cy - map.ScrollingY

--[[
		if map["DebugHotspots"] or (map.Debug and IsAltKeyDown()) then
			if map:OnButScrollDebug (0, 0, x, -y) then
				x = 0
				y = 0
			end
		end
--]]

		if x ~= 0 or y ~= 0 then		-- Moved? Cancel double click
			map.LClickTime = 0
		end

		map.ScrollingX = cx
		map.ScrollingY = cy

		local left = this:GetLeft()
		local top = this:GetTop()

		local mx = x / map.ScaleDraw
		local my = y / map.ScaleDraw
		map.MapPosXDraw = map.MapPosXDraw - mx
		map.MapPosYDraw = map.MapPosYDraw + my

		map.MapPosX = map.MapPosXDraw
		map.MapPosY = map.MapPosYDraw
		map.Scale = map.ScaleDraw
	end

	map:Update (elapsed)

	-- Title text

	local title = ""

	if gopts["MapShowTitleName"] then

		title = map:IdToName (map.RMapId)

--		for n = 1, MAX_BATTLEFIELD_QUEUES do
		for n = 1, GetMaxBattlefieldID() do		-- Patch 4.3

			local status, _, instId = GetBattlefieldStatus (n)
			if status == "active" then
				title = title .. format (" #%s", instId)
				break
			end
		end
	end

	if gopts["MapShowTitleXY"] then
		if map.DebugFullCoords then
			title = title .. format (" %4.2f, %4.2f", map.PlyrRZX, map.PlyrRZY)
		else
			title = title .. format (" %4.1f, %4.1f", map.PlyrRZX, map.PlyrRZY)
		end
	end

	if map.PlyrSpeed > 0 and gopts["MapShowTitleSpeed"] then

		local speed = map.PlyrSpeed

		local sa = Nx.Map.MapWorldInfo[map.MapId].ScaleAdjust
		if sa then
			speed = speed * sa
		end

		speed = speed / 6.4 * 100 - 100
		if abs (speed) < .5 then	-- Removes small -0%
			speed = 0
		end
		title = title..format (" |cffa0a0a0Speed %+.0f%%", speed)
--		Nx.prt ("Speed %f %f, Tm %.4f, %.3f %.3f", map.PlyrSpeed, speed, elapsed, map.PlyrX, map.PlyrY)		-- DEBUG!
	end

--	title = title..format (" Dir %.1f", map.PlyrDir)

	local cursorLocStr = ""
	local cursorLocXY = ""

	local menuOpened = Nx.Menu:IsAnyOpened()

	if winx then

		map.BackgndAlphaTarget = map.BackgndAlphaFull

		winy = this:GetHeight() - winy

		if winy >= map.TitleH then

			local wx, wy = map:FramePosToWorldPos (winx, winy)

			if not menuOpened then
--				local tm = GetTime()
				map:CheckWorldHotspots (wx, wy)
--				Nx.prt ("CheckWorldHotspots Time %s", GetTime() - tm)
			end

			local x, y = map:GetZonePos (map.MapId, wx, wy)

			x = floor (x * 10) / 10	-- Chop fraction to tenths
			y = floor (y * 10) / 10
			local dist = ((wx - map.PlyrX) ^ 2 + (wy - map.PlyrY) ^ 2) ^ .5 * 4.575

			cursorLocXY = format ("|cff80b080%.1f %.1f %.0f yds", x, y, dist)
			cursorLocStr = cursorLocXY

			local name = UpdateMapHighlight (x / 100, y / 100)
			if name then
				cursorLocStr = format ("%s\n|cffafafaf%s", cursorLocStr, name)
			end
		end

	else

--		if GameTooltip:IsOwned (map.Win.Frm) and map.TooltipType == 1 then
--			Nx.prt ("map TT hide")
--			map.TooltipType = 0
--			GameTooltip:Hide()
--		end

		if not map.Scrolling and not menuOpened then

			map.BackgndAlphaTarget = map.BackgndAlphaFade

			local rid = map:GetRealMapId()
			if rid ~= 9000 and not WorldMapFrame:IsShown() then

				local mapId = map:GetCurrentMapId()

				if map:IsInstanceMap (rid) then

					if not Nx.Map.InstanceInfo[rid] then		-- Don't convert WotLK/Cata instances
						rid = Nx.Map.MapWorldInfo[rid].EntryMId
					end

					local lvl = GetCurrentMapDungeonLevel()
					if lvl ~= map.InstLevelSet then
						mapId = 0	-- Force set
--						Nx.prt ("map force set inst")
					end
				end

				if mapId ~= rid then

					if map:IsBattleGroundMap (rid) then
						SetMapToCurrentZone()
					else
						map:SetCurrentMap (rid)
					end
				end
			end
		end
	end

	-- Check quest window

	if map.Guide.Win.Frm:IsVisible() or Quest.List.Win and Quest.List.Win.Frm:IsVisible() then
		map.BackgndAlphaTarget = map.BackgndAlphaFull
	end

	-- Profiling

	if map.DebugTime then

		profileTime = GetTime() - profileTime
		local t = map.DebugProfileTime or .01
		t = t * .95 + profileTime * .05
		map.DebugProfileTime = t

		UpdateAddOnMemoryUsage()
		local mem = GetAddOnMemoryUsage ("Carbonite")

		local memdif = mem - (map.DebugMemUse or 0)
		map.DebugMemUse = mem

		title = title..format (" Time %.4f Mem %d %.4f", t, mem, memdif)
	end

	if GetCVar ("scriptProfile") == "1" then

		UpdateAddOnCPUUsage()

		title = title..format (" |cffffffffCPU %6.3f %6.3f", GetAddOnCPUUsage ("CARBONITE"), GetScriptCPUUsage())

		ResetCPUUsage()
	end

	--

	if Nx.Tick % 3 == 0 then	-- Do less often, since tip makes garbage

		local tip = format (" %s", cursorLocStr)
		if map.Debug and winx then
			local x, y = map:FramePosToWorldPos (winx, winy)
			tip = tip .. format ("\n|cffc080a0%.2f WXY %6.2f %6.2f PXY %6.2f %6.2f", map.Scale, x, y, map.PlyrX, map.PlyrY)
			map.DebugWX = x
			map.DebugWY = y
		end

		local over = winx and not Nx.Util_IsMouseOver (map.ToolBar.Frm)
		map:SetLocationTip (over and not menuOpened and map.WorldHotspotTipStr and (map.WorldHotspotTipStr .. tip))
	end

	if map.Win:IsSizeMax() then
		local s = Nx.Quest:GetZoneAchievement (true)
		if s then
			title = title .. "  " .. s
		end
	end

	map.Win:SetTitle (title, 1)

	if map.GOpts["MapShowTitle2"] then

		local s = GetSubZoneText()
		local pvpType = GetZonePVPInfo()
		if pvpType then
			s = s .. " (" .. pvpType .. ")"
		end
		map.Win:SetTitle (format ("%s %s", s, cursorLocXY), 2)
	end

	Nx.Timer:ProfilerEnd ("Map OnUpdate")
end

--------
-- Handle mouse click on icon

--[[
function Nx.Map:WorldHotspotOnMouseDown (button)

--	Nx.prt ("MapWorldHotspotOnMouseDown "..button.." "..(this:GetName() or "?"))

	local map = this.NxMap
	map.ClickFrm = this
	this = map.Frm
	map:OnMouseDown (button)
end
--]]

--------
-- Handle mouse on icon

--[[
function Nx.Map:WorldHotspotOnEnter (motion)

	local map = this.NxMap

	if not map.Scrolling then

		local mapId = this.NxMapId
		map:SetCurrentMap (mapId)

		map:IconOnEnter (motion)
	end
end
--]]

--------
-- Flag for update all map data

function Nx.Map:UpdateAll()

	self.NeedWorldUpdate = true

--	Nx.prt ("%d Map UpdateAll %d (%d)", self.Tick, self:GetCurrentMapId(), self.MapId)
end

--------
-- Update all map data

function Nx.Map:UpdateWorld()

	if self.Debug then
		Nx.prt ("%d Map UpdateWorld1 %d L%d",
				self.Tick, self:GetCurrentMapId(), GetCurrentMapDungeonLevel())
	end

	self.NeedWorldUpdate = false

	local mapId = self:GetCurrentMapId()
	local winfo = self.MapWorldInfo[mapId]

	if winfo.MapLevel then
		if GetCurrentMapDungeonLevel() ~= winfo.MapLevel then	-- Wrong level?
			SetDungeonMapLevel (winfo.MapLevel)
		end
	end

	local i = self:GetExploredOverlayNum()

	if self.CurWorldUpdateMapId == mapId and i == self.CurWorldUpdateOverlayNum then
		return
	end

	self.CurWorldUpdateMapId = mapId
	self.CurWorldUpdateOverlayNum = i

	local mapFileName = GetMapInfo()

	if not mapFileName then
		if GetCurrentMapContinent() == WORLDMAP_COSMIC_ID then
			mapFileName = "Cosmic"
		else
			mapFileName = "World"
		end
	end

	self:UpdateOverlayUnexplored()

	if self.Debug then
		Nx.prt ("%d Map UpdateWorld %d", self.Tick, self:GetCurrentMapId())
		Nx.prt (" File %s", mapFileName)
	end

	Nx.UEvents:UpdateMap (true)

	if not winfo.NoBackground then

		local name = winfo.MapBaseName or mapFileName

		for i = 1, 12 do
			self.TileFrms[i].texture:SetTexture ("Interface\\WorldMap\\" .. mapFileName .. "\\" .. name .. i)
		end
	end
end

--------
-- Update window fade

function Nx.Map:WinUpdateFade (fade)

	self.ToolBar:SetFade (fade)
	self.ButAutoScaleOn.Frm:SetAlpha (fade)
end

--------
-- Update map. Called every tick, make it quick

function Nx.Map:Update (elapsed)

	local Nx = Nx
	local Map = Nx.Map

	self:MouseEnable (self.Win:IsSizeMax())

	if self.NeedWorldUpdate then
		self:UpdateWorld()
	end

	self.MapW = self.Frm:GetWidth() - self.PadX * 2
	self.MapH = self.Frm:GetHeight() - self.TitleH
	self.Level = self.Frm:GetFrameLevel() + 1

	local mapId = self:GetCurrentMapId()
	self.Cont, self.Zone = self:IdToContZone (mapId)

	Nx.InSanctuary = GetZonePVPInfo() == "sanctuary"

	local doSetCurZone
	local mapChange

	if self.MapId ~= mapId then

		if self.Debug then
			Nx.prt ("%d Map change %d to %d", self.Tick, self.MapId, mapId)
		end

		self.CurMapBG = self:IsBattleGroundMap (mapId)

		if not self:IsBattleGroundMap (self.MapId) then
--			self.MapIdOld = self.MapId
			self:AddOldMap (mapId)
		end

		self.MapId = mapId
		mapChange = true

		Nx.Com.PlyrChange = GetTime()
	end

	local rid = self:GetRealMapId()
	local inBG = self:IsBattleGroundMap (rid)

	if Nx.InBG and Nx.InBG ~= rid then	-- Left or changed BG?

--		Nx.prt ("Left BG %s", Nx.InBG)

		local cb = Nx.Combat

		if Nx.InArena then
			local s = Nx.Map:GetShortName (Nx.InArena)
			Nx.UEvents:AddInfo (format ("Left %s %d %d %dD %dH", s, cb.KBs, cb.Deaths, cb.DamDone, cb.HealDone))

		else
			local total = cb.KBs + cb.Deaths + cb.HKs + cb.Honor
			if total > 0 then
				local sname = Nx.Map:GetShortName (Nx.InBG)
				Nx.UEvents:AddInfo (format ("Left %s %d %d %d %d", sname, cb.KBs, cb.Deaths, cb.HKs, cb.Honor))

				local tm = GetTime() - cb.BGEnterTime
				local _, honor = GetCurrencyInfo (392)		--V4
				local hGain = honor - cb.BGEnterHonor
				Nx.UEvents:AddInfo (format (" %s +%d honor, +%d hour", Nx.Util_GetTimeElapsedMinSecStr (tm), hGain, hGain / tm * 3600))

				local xpGain = UnitXP ("player") - cb.BGEnterXP
				if xpGain > 0 then
					Nx.UEvents:AddInfo (format (" +%d xp, +%d hour", xpGain, xpGain / tm * 3600))
				end
			end
		end

		cb.KBs = 0
		cb.Deaths = 0
		cb.HKs = 0
		cb.Honor = 0
		Nx.InBG = nil

		if Nx.InArena then
			self.LOpts.NXMMFull = false
		end
		Nx.InArena = nil
	end

	if inBG and Nx.InBG ~= rid then
		Nx.InBG = rid

		local cb = Nx.Combat
		cb.BGEnterTime = GetTime()
		local _, honor = GetCurrencyInfo (392)		--V4
		cb.BGEnterHonor = honor
		cb.BGEnterXP = UnitXP ("player")

		if self.MapWorldInfo[rid].Arena then
			Nx.InArena = rid
			self.LOpts.NXMMFull = true
		end

--		Nx.prt ("Entering BG %s", rid)
		doSetCurZone = true
	end

	-- Taxi update

	local ontaxi = UnitOnTaxi ("player")

	if ontaxi then
		if not Map.TaxiOn then	-- New taxi ride?
			Map.TaxiStartTime = GetTime()
			Map.TaxiOn = true
			if NxData.DebugMap then
				Nx.prt ("Taxi start")
			end
		end

	elseif Map.TaxiOn then	-- Done with taxi
		Map.TaxiOn = false
		Map.TaxiX = nil		-- Clear so if we pop on a taxi by a unhooked method we don't track old

		local tm = GetTime() - Map.TaxiStartTime

		Nx.Travel:TaxiSaveTime (tm)

		if NxData.DebugMap then
			Nx.prt ("Taxi time %.1f seconds", tm)
		end
	end

	-- Real map switch

	if self.RMapId ~= rid then
		if rid ~= 9000 then

--			Nx.prt ("Map zone changed %d, %d", rid, mapId)

			if self.RMapId == 9000 then	-- Loading?
				self.CurOpts = nil
				self:SwitchOptions (rid, true)
			end

			self.RMapId = rid

			self:SwitchOptions (rid)
			self:SwitchRealMap (rid)
		end
	end

	local plZX, plZY = GetPlayerMapPosition ("player")

	self.InstanceId = false

	if self:IsInstanceMap (rid) then

		self.InstanceId = rid

		plZX = plZX * 100
		plZY = plZY * 100

--		self.PlyrInstX = plZX
--		self.PlyrInstY = plZY

--		Nx.prt ("XY %s %s", plZX, plZY)

		self.PlyrRZX = plZX
		self.PlyrRZY = plZY

		local x, y = self:GetWorldPos (rid, 0, 0)

		local lvl = max (GetCurrentMapDungeonLevel(), 1)		-- 0 if no level

		if not self.InstMapId then		-- Not showing instance?
			plZX = 0
			plZY = 0

		elseif plZX == 0 and plZY == 0 then

			self.InstLevelSet = -1
		end

		self.PlyrX = x + plZX * 1002 / 25600
		self.PlyrY = y + plZY * 668 / 25600 + (lvl - 1) * 668 / 256

--		self.InstanceLevel = GetCurrentMapDungeonLevel()

		self.PlyrSpeed = 0

	elseif plZX > 0 or plZY > 0 then	-- Update world position of player if we can get it

		plZX = plZX * 100
		plZY = plZY * 100

		local x, y = self:GetWorldPos (mapId, plZX, plZY)

		if elapsed > 0 then

			if x == self.PlyrX and y == self.PlyrY then	-- Not moving?
				self.PlyrSpeedCalcTime = GetTime()
				self.PlyrSpeed = 0
				self.PlyrSpeedX = x
				self.PlyrSpeedY = y

			else
				local tmDif = GetTime() - self.PlyrSpeedCalcTime
				if tmDif > .5 then
					self.PlyrSpeedCalcTime = GetTime()
					self.PlyrSpeed = ((x - self.PlyrSpeedX) ^ 2 + (y - self.PlyrSpeedY) ^ 2) ^ .5 * 4.575 / tmDif
					self.PlyrSpeedX = x
					self.PlyrSpeedY = y
				end
			end
		end

--		if elapsed > 0 then
--			self.PlyrSpeed = ((x - self.PlyrX) ^ 2 + (y - self.PlyrY) ^ 2) ^ .5 * 4.575 / elapsed
--		end

		self.PlyrX = x
		self.PlyrY = y

		if mapId ~= rid then			-- Not in real zone?
			plZX, plZY = self:GetZonePos (rid, x, y)
		end

		self.PlyrRZX = plZX
		self.PlyrRZY = plZY

		if mapChange then
			self.MoveLastX = x
			self.MoveLastY = y
		end
	end

--	Nx.prt ("Dir %s", GetPlayerFacing())
	self.PlyrDir = 360 - GetPlayerFacing() / 2 / math.pi * 360

	local plX = self.PlyrX
	local plY = self.PlyrY

	local x = plX - self.MoveLastX
	local y = plY - self.MoveLastY
	local ang = self.PlyrDir - self.PlyrLastDir

	local moveDist = (x * x + y * y) ^ .5

--	if moveDist > 0 then Nx.prt ("MoveDist %f %f", moveDist, self.BaseScale) end

	if moveDist >= .01 * self.BaseScale or abs (ang) > .01 then

		Nx.Com.PlyrChange = GetTime()

		if self.MoveLastX ~= -1 then
			self.MoveDir = math.deg (math.atan2 (x, -y / 1.5))
		end

		self.MoveLastX = plX
		self.MoveLastY = plY

--		if not rotOk then
--			self.PlyrDir = self.MoveDir
--		end

		self.PlyrLastDir = self.PlyrDir

		if not self.Scrolling and not self.MouseIsOver and not WorldMapFrame:IsVisible() then

			if self.CurOpts.NXPlyrFollow then

				local scOn = self.LOpts.NXAutoScaleOn		--self.GOpts["MapFollowChangeScale"]

				if plZX ~= 0 or plZY ~= 0 then

					if #self.Tracking == 0 or not scOn then
						self:Move (plX, plY, nil, 60)
					end
				end

				if scOn then

					local midX
					local midY
					local dtx
					local dty

					local cX, cY = GetCorpseMapPosition()

					if cX ~= 0 or cY ~= 0 then

						midX, midY = self:GetWorldPos (mapId, cX * 100, cY * 100)
						dtx = 1
						dty = 1

					elseif #self.Tracking > 0 then

						local tr = self.Tracking[1]
						midX = tr.TargetMX
						midY = tr.TargetMY
						dtx = abs (tr.TargetX1 - tr.TargetX2)
						dty = abs (tr.TargetY1 - tr.TargetY2)

					elseif Map.TaxiX then

						midX, midY = self.TaxiX, self.TaxiY
						dtx = 1
						dty = 1
					end

					if midX then

						local mX = (midX + self.PlyrX) * .5
						local mY = (midY + self.PlyrY) * .5

						local dx = abs (midX - self.PlyrX)
						local dy = abs (midY - self.PlyrY)
--						Nx.prt ("Map scale target %f %f", dx, dy)
						dx = self.MapW / dx
						dy = self.MapH / dy
						local scale = min (dx, dy) * .5
--						Nx.prt ("Map scales %f %f", dx, dy)

--						Nx.prt ("Map scale target %f %f", dtx, dty)
						dx = self.MapW / dtx
						dy = self.MapH / dty
						scale = min (min (dx, dy), scale)	-- Smaller of target rect of player to target center

						local scmax = self.InstanceId and 800 or self.LOpts.NXAutoScaleMax

						scale = max (min (scale, scmax), self.LOpts.NXAutoScaleMin)
						self:Move (mX, mY, scale, 60)
					end
				end

				if rid ~= mapId then
					doSetCurZone = true
--					Nx.prt ("Map SetMapToCurrentZone")
				end
			end
		end
	end

	-- Adjust draw scale and position

	local scaleDiff = abs (self.ScaleDraw - self.Scale)
	local xDiff = self.MapPosXDraw - self.MapPosX
	local yDiff = self.MapPosYDraw - self.MapPosY

	if self.StepTime ~= 0 and (scaleDiff > 0 or xDiff ~= 0 or yDiff ~= 0) then
--	if (xDiff ~= 0 or yDiff ~= 0) and (self.Tick % 1 == 0) then

--		Nx.prt ("Tick %f", self.Tick)

		if self.StepTime > 0 then

--			Nx.prt ("Steptime Go #%d %f", self.Tick, self.StepTime)

			self.StepTime = -self.StepTime

			self.ScaleDrawW = 1 / self.ScaleDraw
			self.ScaleW = 1 / self.Scale
		end

		local st = -self.StepTime

		self.MapPosXDraw = Nx.Util_StepValue (self.MapPosXDraw, self.MapPosX, abs (xDiff) / st)
		self.MapPosYDraw = Nx.Util_StepValue (self.MapPosYDraw, self.MapPosY, abs (yDiff) / st)
		self.ScaleDrawW = Nx.Util_StepValue (self.ScaleDrawW, self.ScaleW, abs (self.ScaleDrawW - self.ScaleW) / st)
		self.ScaleDraw = 1 / self.ScaleDrawW

--		Nx.prt ("Map scrl #%d %f %f", self.StepTime, self.MapPosXDraw, self.MapPosX)
--		Nx.prt ("Map scrl %f %f", self.OCur, self.OEnd)
--		Nx.prt ("Map scrl %f %f", self.ScaleDraw, self.Scale)

		self.StepTime = self.StepTime + 1
	end

	local _, zx, zy, zw = self:GetWorldZoneInfo (self.Cont, self.Zone)
	if zx then
		self.MapScale = self.Scale / 10.02
	end

	--

	local plSize = self.GOpts["MapPlyrArrowSize"]
	if IsShiftKeyDown() then
		plSize = 5
	end

	self.PlyrFrm:Show()
	self:ClipFrameW (self.PlyrFrm, self.PlyrX, self.PlyrY, plSize, plSize, self.PlyrDir)

	self.InCombat = UnitAffectingCombat ("player")

	local g = 1
	local b = 1
	local al = 1
	if self.InCombat then
		g = 0
		b = 0
		al = abs (GetTime() % 1 - .5) / .5 * .5 + .4
	end

	self.PlyrFrm.texture:SetVertexColor (1, g, b, al)
--	local str = format ("%s %d %d", UnitName ("player"), UnitHealth ("player"), UnitMana ("player"))
--	self.PlyrFrm.NxTip = str

	--

	self.BackgndAlpha = Nx.Util_StepValue (self.BackgndAlpha, self.BackgndAlphaTarget, .05)
	self.Frm.texture:SetVertexColor (1, 1, 1, self.BackgndAlpha)

	self.WorldAlpha = (self.BackgndAlpha - self.BackgndAlphaFade) / (self.BackgndAlphaFull - self.BackgndAlphaFade) * self.BackgndAlphaFull

	self:ResetIcons()

	self:MoveContinents()
--	self:MoveWorldHotspots()

--	if not (IsAltKeyDown() and IsControlKeyDown()) then
--	end

	self:UpdateZones()
	self:UpdateInstanceMap()

	self:MinimapUpdate()
	self:UpdateWorldMap()

	self:DrawContinentsPOIs()

	if self.GOpts["MapShowTrail"] then
		self:UpdatePlyrHistory()
	end

	if self.GOpts["MapShowPunks"] then
		Nx.Social:UpdateIcons (self)
	end

--[[
	if self["DebugHotspots"] then
		self:UpdateHotspotsDebug()
	end
--]]

	-- Battlefield Vehicles

	local vtex = _G["VEHICLE_TEXTURES"]

	for n = 1, GetNumBattlefieldVehicles() do

		local x, y, unitName, possessed, typ, orientation, player = GetBattlefieldVehicleInfo (n)
		if x and x > 0 and not player then

--			Nx.prtCtrl ("#%s %s %.2f %.2f %.3f %s %s %s", n, unitName or "nil", x or -1, y or -1, orientation or -1, typ or "no type", possessed and "poss" or "-poss", player and "plyr" or "-plyr")

			if vtex[typ] then
				local f2 = self:GetIconNI (1)

				local sc = self.ScaleDraw * .8		-- Airships
				if typ == "Drive" or typ == "Fly" then
					sc = 1
				end

				if self:ClipFrameZ (f2, x * 100, y * 100, vtex[typ]["width"] * sc, vtex[typ]["height"] * sc, orientation / PI * -180) then
					f2.texture:SetTexture (WorldMap_GetVehicleTexture (typ, possessed))
				end

--				Nx.prtCtrl ("%s %s %s %s", unitName, x, y, orientation)
			end
		end
	end

	-- POI's (Points of interest)

	local oldLev = self.Level

	if IsShiftKeyDown() then

		oldLev = oldLev - 4
		self.Level = self.Level + 16
	end

	local name, description, txIndex, pX, pY
	local txX1, txX2, txY1, txY2
	local poiNum = GetNumMapLandmarks()

--	Nx.prt ("poiNum %d", poiNum)

	for i = 1, poiNum do
		name, desc, txIndex, pX, pY = GetMapLandmarkInfo (i)

		if txIndex ~= 0 then		-- WotLK has 0 index POIs for named locations

			local tip = name
			if desc then
				tip = format ("%s\n%s", name, desc)
			end

			pX = pX * 100
			pY = pY * 100

--			Nx.prtCtrl ("poi %d %s %s %d", i, name, desc, txIndex)

			local f = self:GetIcon (3)

			if self.CurMapBG then

				f.NXType = 2000

				local iconType = Nx.MapPOITypes[txIndex]

				local sideStr = ""
				if iconType == 1 then	-- Ally?
					sideStr = " (Ally)"
				elseif iconType == 2 then	-- Horde?
					sideStr = " (Horde)"
				end

				if desc == NXlINCONFLICT then

					local state = self.BGTimers[name]
					if state ~= txIndex then
						self.BGTimers[name] = txIndex
						self.BGTimers[name.."#"] = GetTime()
					end

					local dur = GetTime() - self.BGTimers[name.."#"]
					local doneDur = (rid == 9001 or rid == 9009 or rid == 9010) and 64 or 241
					local leftDur = max (doneDur - dur, 0)
					local tmStr

					if leftDur < 60 then
						tmStr = format (":%02d", leftDur)
					else
						tmStr = format ("%d:%02d", floor (leftDur / 60), floor (leftDur % 60))
					end

					f.NXData = format ("1~%f~%f~%s%s %s", pX, pY, name, sideStr, tmStr)

					tip = format ("%s\n%s", tip, tmStr)

					-- Horizontal bar

					local sz = 30 / self.ScaleDraw

					local f2 = self:GetIcon (0)
					self:ClipFrameZTLO (f2, pX, pY, sz, sz, -15, -15)
					f2.texture:SetTexture (0, 0, 0, .35)

					f2.NXType = 2000
					f2.NxTip = tip
					f2.NXData = f.NXData

					local f2 = self:GetIconNI (1)

					if leftDur < 10 then

						if self.BGGrowBars then

							local al = abs (GetTime() % .4 - .2) / .2 * .2 + .8

							local f3 = self:GetIconNI (2)
							self:ClipFrameZTLO (f3, pX, pY, sz * (10 - leftDur) * .1, 3 / self.ScaleDraw, -15, -15)
							f3.texture:SetTexture (.5, 1, .5, al)

							local f3 = self:GetIconNI (2)
							self:ClipFrameZTLO (f3, pX, pY, sz * (10 - leftDur) * .1, 3 / self.ScaleDraw, -15, 12)
							f3.texture:SetTexture (.5, 1, .5, al)
						end

--						f2.texture:SetTexture (.5, 1, .5, abs (GetTime() % .6 - .3) / .3 * .7 + .3)
					end

					local red = .3
					local blue = 1
					if iconType == 2 then	-- Horde?
						red = 1
						blue = .3
					end

					f2.texture:SetTexture (red, .3, blue, abs (GetTime() % 2 - 1) * .5 + .5)

					local per = leftDur / doneDur
					local vper = per > .1 and 1 or per * 10

					if self.BGGrowBars then
						per = 1 - per
						vper = 1
					else
						per = max (per, .1)
					end

					self:ClipFrameZTLO (f2, pX, pY, sz * per, sz * vper, -15, -15)

				else	-- No conflict

					f.NXData = format ("0~%f~%f~%s%s", pX, pY, name, sideStr)

					self.BGTimers[name] = nil

					-- Rect

					local sz = 30 / self.ScaleDraw

					local f2 = self:GetIcon (0)
					self:ClipFrameZTLO (f2, pX, pY, sz, sz, -15, -15)

--					Nx.prtCtrl ("I %s %s %s", name, txIndex, iconType or "nil")

					if iconType == 1 then	-- Ally?
						f2.texture:SetTexture (0, 0, 1, .3)
--						Nx.prtCtrl ("Blue")
					elseif iconType == 2 then	-- Horde?
						f2.texture:SetTexture (1, 0, 0, .3)
--						Nx.prtCtrl ("Red")
					else
						f2.texture:SetTexture (0, 0, 0, .3)
					end

					f2.NXType = 2000
					f2.NxTip = tip
					f2.NXData = f.NXData

				end
			end

			f.NxTip = tip

			self:ClipFrameZ (f, pX, pY, 16, 16, 0)

			f.texture:SetTexture ("Interface\\Minimap\\POIIcons")
			txX1, txX2, txY1, txY2 = GetPOITextureCoords (txIndex)
			f.texture:SetTexCoord (txX1 + .003, txX2 - .003, txY1 + .003, txY2 - .003)
			f.texture:SetVertexColor (1, 1, 1, 1)
		end
	end

	self.Level = oldLev + 4

	-- Update misc icons (herbs, ore, ...)
	-- Levels:
	--  +0 quest areas
	--  +1 quest area target
	--  +2-3 com players (++3 if alt key)
	--  +4 quest icons
	--  +5 ?

	Nx.HUD:Update (self)

	local comTrackName, comTrackX, comTrackY = Nx.Com:UpdateIcons (self)

	self.Level = self.Level + 2
	self.Guide:UpdateZonePOIIcons()
	Nx.Fav:UpdateIcons()
	self:UpdateIcons (self.KillShow)
	self.Level = self.Level - 2

	if Nx.Quest.Enabled then
		Nx.Quest:UpdateIcons (self)
	end

	self.Level = self.Level + 7

	-- Test

--	for n = 0, 100 do
--		local f = self:GetIcon()
--		f.texture:SetTexture (1, 1, .5, 1)
--		self:ClipFrameZ (f, n, 50, 2, 2)
--	end

	-- Battlefield flags

	local fX, fY, fToken
	local flagNum = GetNumBattlefieldFlagPositions()

	for i = 1, flagNum do

		fX, fY, fToken = GetBattlefieldFlagPosition (i)

		if fX ~= 0 or fY ~= 0 then

			local f = self:GetIconNI()
			f.texture:SetTexture ("Interface\\WorldStateFrame\\"..fToken)
			self:ClipFrameZ (f, fX * 100, fY * 100, 36, 36, 0)
		end
	end

	self.Level = self.Level + 1

	-- Raid or party icons (AKA group)

	local palName, palX, palY = self:UpdateGroup (plX, plY)

	-- Tracking animation

	if self.PlyrSpeed == 0 then

--		self.ArrowPulse = self.ArrowPulse + .05
--		if self.ArrowPulse > cnt then
--			self.ArrowPulse = 1
--		end

		self.ArrowScroll = self.ArrowScroll + .01
		if self.ArrowScroll >= 1 then
			self.ArrowScroll = 0
		end
	end

	-- Corpse or target tracking

	self.TrackDir = false

	self.Guide:OnMapUpdate()	-- For closest target

	if #self.Targets > 0 then

		self:UpdateTargets()
		self:UpdateTracking()
		self.Level = self.Level + 2
	end

	self.TrackETA = false

	local cX, cY = GetCorpseMapPosition()

	if (cX > 0 or cY > 0) and not inBG then	-- We dead, but not in BG?

		if self.GOpts["HUDATCorpse"] then

			self.TrackName = "Corpse"

			local x, y = self:GetWorldPos (mapId, cX * 100, cY * 100)
			self:DrawTracking (plX, plY, x, y, false, "D")

			local f = self:GetIcon (1)

			f.NxTip = "Your corpse"
			f.texture:SetTexture ("Interface\\Minimap\\POIIcons")
			self:ClipFrameZ (f, cX * 100, cY * 100, 16, 16, 0)
			-- Override clipping (FIX maybe?)
			f.texture:SetTexCoord (.502, .5605, 0, .0605)	-- 16x16 grid (.0625 uv size)

			self.Level = self.Level + 2
		end

	elseif ontaxi and Map.TaxiX then

		if self.GOpts["HUDATTaxi"] then

			self.TrackName = Map.TaxiName
			self.TrackETA = Map.TaxiETA

			local x, y = self.TaxiX, self.TaxiY
			self:DrawTracking (plX, plY, x, y, false, "F")

--			Nx.prt ("taxi %s %s", x, y)

			local f = self:GetIcon (1)

			f.NxTip = Map.TaxiName
			f.texture:SetTexture ("Interface\\Icons\\Ability_Mount_Wyvern_01")
			self:ClipFrameW (f, x, y, 16, 16, 0)

			self.Level = self.Level + 2
		end
	end

	-- Battle ground or manual pal tracking

	if (palX or comTrackX) and (inBG or next (self.TrackPlyrs)) then

		if palX then

			self.TrackName = palName
			self:DrawTracking (plX, plY, palX, palY, false, "B")
		else
			self.TrackName = comTrackName
			self:DrawTracking (plX, plY, comTrackX, comTrackY, false)
		end

		self.Level = self.Level + 2
	end

	-- Set final levels

	self.TextScFrm:SetFrameLevel (self.Level)
	self.PlyrFrm:SetFrameLevel (self.Level + 1)

	self.ToolBar:SetLevels (self.Level + 2)

	self.Level = self.Level + 3

	self:MinimapUpdateEnd()		-- Uses 2 levels

	self.LocTipFrm:SetFrameLevel (self.Level + 2)

	-- Hide leftovers

	self:HideExtraIcons()

	-- Scan. Switch maps if needed. Do at end so we dont glitch

	if Nx.Tick % self.ScanContinentsMod == 3 then
		self:ScanContinents()
	end

	if doSetCurZone then
		self:SetToCurrentZone()
	end

	-- Debug
--[[
	Nx.prt ("Map WPos %s ZPos %s WScale %s", self.GetWorldPosCnt or 0, self.GetZonePosCnt or 0, self.GetWorldZoneScaleCnt or 0)
	self.GetWorldPosCnt = 0
	self.GetZonePosCnt = 0
	self.GetWorldZoneScaleCnt = 0
--]]

end

--------
-- Switch to a new map
-- self = map

function Nx.Map:SwitchRealMap (id)

	if self:IsInstanceMap (id) then
		self:SetInstanceMap (id)			-- Turn it on
	else
		self:SetInstanceMap()				-- Turn it off
	end

	if self.GOpts["MapMMInstanceTogFullSize"] then
		self.LOpts.NXMMFull = false
		if self:IsInstanceMap (id) then
			self.LOpts.NXMMFull = true
		end
	end
end

--------

function Nx.Map:SetInstanceMap (mapId)

	self.InstMapId = nil

	if not mapId then
		return
	end

	local Map = Nx.Map

	local info = Map.InstanceInfo[mapId]
	local sizex, sizey = 1002, 668
	self.InstMapAtlas = nil

	if not info and _G["AtlasMaps"] then
		info = Map.AtlasInstanceInfo[mapId]
		sizex, sizey = 668, 668
		self.InstMapAtlas = true
	end

	if info then

		self:SetCurrentMap (mapId)

		self.InstMapId = mapId
		self.InstMapInfo = info

		local winfo = Map.MapWorldInfo[mapId]

		local wx = winfo[2]
		local wy = winfo[3]

--		wx = wx - info[1] * 4
--		wy = wy - info[2] * 4

		self.InstMapWX1 = wx
		self.InstMapWY1 = wy
		self.InstMapWX2 = wx + sizex / 256
		self.InstMapWY2 = wy + sizey / 256 * #info / 3
	end
end

--------
-- Scan the continents for POI data

function Nx.Map:ScanContinents()

	Nx.Map.ScanContinentsMod = 500

	local oldCont = GetCurrentMapContinent()
	if oldCont < 0 then
		return
	end

	local oldZone = GetCurrentMapZone()
	local mapLvl = GetCurrentMapDungeonLevel()

--	Nx.prt ("ScanContinents cont zone, %s %s", oldCont, oldZone)

	--

	WatchFrame:UnregisterEvent ("WORLD_MAP_UPDATE")

	local hideT = {}
	hideT[0] = true	-- WotLK has 0 index POIs for named locations
	hideT[6] = not self.GOpts["MapShowCCity"]
	hideT[41] = not self.GOpts["MapShowCExtra"]
	hideT[5] = not self.GOpts["MapShowCTown"]

	for cont = 1, self.ContCnt do

		local poiT = {}
		self.ContPOIs[cont] = poiT

		SetMapZoom (cont)
		local mapId = cont * 1000

		local name, description, txIndex, pX, pY
		local txX1, txX2, txY1, txY2
		local poiNum = GetNumMapLandmarks()

--		Nx.prt ("poiNum %d", poiNum)

		for n = 1, poiNum do
			name, desc, txIndex, pX, pY = GetMapLandmarkInfo (n)

			if name and not hideT[txIndex] then

				local poi = {}
				tinsert (poiT, poi)
				poi.Name = name
				poi.Desc = desc
				poi.TxIndex = txIndex

				local x, y = self:GetWorldPos (mapId, pX * 100, pY * 100)
				poi.WX = x
				poi.WY = y
			end
		end
	end

	--
--[[
	local members = MAX_PARTY_MEMBERS
	local unitName = "party"
	local raid

	if IsInRaid() > 0 then
		members = MAX_RAID_MEMBERS
		unitName = "raid"
		raid = true
	end

	local pals = Nx.Com.PalNames
	local palName
	local palDist = 99999999
	local palX, palY
	local combatName
	local combatUnit
	local combatHealth
	local combatDist = 99999999
	local combatX, combatY

	local palsInfo = Nx.Com.PalsInfo

	for i = 1, members do

		local unit = unitName .. i
		local name, unitRealm = UnitName (unit)

		local mapId = self.MapId

		local pX, pY = GetPlayerMapPosition (unit)
		if pX <= 0 and pY <= 0 then

			local info = palsInfo[name]
			if info and info.EntryMId == mapId then
				mapId = info.MId
				pX = info.X + .00001
				pY = info.Y
			end

		else
			pX = pX * 100
			pY = pY * 100
		end

		if (pX ~= 0 or pY ~= 0) and not UnitIsUnit (unit, "player") then

			local fullName = unitRealm and (name .. "-" .. unitRealm) or name

			local wx, wy = self:GetWorldPos (mapId, pX, pY)

		end
	end
--]]
	-- Restore

	SetMapZoom (oldCont, oldZone)
	SetDungeonMapLevel (mapLvl)

	WatchFrame:RegisterEvent ("WORLD_MAP_UPDATE")
end

--------
-- Draw the continents POI data

function Nx.Map:DrawContinentsPOIs()

	if self.ScaleDraw > self.LOpts.NXPOIAtScale then

		if not Nx.CharOpts["MapShowGatherA"] then
			return
		end
	end

	local getCoords = GetPOITextureCoords

	for cont = 1, self.ContCnt do

		for k, poi in ipairs (self.ContPOIs[cont]) do

			local txi = poi.TxIndex
			local z = txi == 177 and 13 or 3

			local f = self:GetIcon (z)

			if self:ClipFrameW (f, poi.WX, poi.WY, 16, 16, 0) then

				f.NxTip = poi.Name			 --.. poi.TxIndex

				-- 1 2
				-- 4 3

				local t1x, t1y, t4x, t4y, t2x = f.texture:GetTexCoord()

				f.texture:SetTexture ("Interface\\Minimap\\POIIcons")
				local txX1, txX2, txY1, txY2 = getCoords (txi)
--				f.texture:SetTexCoord (txX1 + .003, txX2 - .003, txY1 + .003, txY2 - .003)

				local x = txX1 + .003
				local y = txY1 + .003
				local w = txX2 - .003 - x
				local h = txY2 - .003 - y
				f.texture:SetTexCoord (x + w * t1x, x + w * t2x,
							y + h * t1y, y + h * t4y)

--				Nx.prtCtrl ("%s %s %s %s %s %s", t1x, t1y, t4x, t4y, t2x, t2y)
			end
		end
	end

	self.Level = self.Level + 1
end

--------
-- Draw group (raid or party)
-- (player world pos)

function Nx.Map:UpdateGroup (plX, plY)

	local alt = IsAltKeyDown()
	local redGlow = abs (GetTime() * 400 % 200 - 100) / 200 + .5

	local members = MAX_PARTY_MEMBERS
	local unitName = "party"
	local raid

	if IsInRaid() then
		members = MAX_RAID_MEMBERS
		unitName = "raid"
		raid = true
	end

	local pals = Nx.Com.PalNames
	local palName
	local palDist = 99999999
	local palX, palY
	local combatName
	local combatUnit
	local combatHealth
	local combatDist = 99999999
	local combatX, combatY

	local palsInfo = Nx.Com.PalsInfo

	for i = 1, members do

		local unit = unitName .. i
		local name, unitRealm = UnitName (unit)

		local mapId = self.MapId

		local pX, pY = GetPlayerMapPosition (unit)
		if pX <= 0 and pY <= 0 then

			local info = palsInfo[name]
			if info and info.EntryMId == mapId then
				mapId = info.MId
				pX = info.X + .00001
				pY = info.Y
			end

		else
			pX = pX * 100
			pY = pY * 100
		end

		if (pX ~= 0 or pY ~= 0) and not UnitIsUnit (unit, "player") then

			local fullName = unitRealm and #unitRealm > 0 and (name .. "-" .. unitRealm) or name

			local wx, wy = self:GetWorldPos (mapId, pX, pY)

			local sz = 16 * self.DotRaidScale
			if UnitInParty (unit) then
				sz = 18 * self.DotPartyScale
			end

			local cls = UnitClass (unit) or ""
			local inCombat
--PAIDS!
			inCombat = UnitAffectingCombat (unit)
--PAIDE!
			local h = UnitHealth (unit)
			if UnitIsDeadOrGhost (unit) then
				h = 0
			end
			local m = UnitHealthMax (unit)
			local per = min (h / m, 1)			-- Can overflow?

			if per > 0 then

				if pals[name] ~= nil or self.TrackPlyrs[name] then

--					Nx.prtCtrl ("Pal %s", name)

					sz = 20 * self.DotPalScale

					if self.TrackPlyrs[name] then
						sz = 25 * self.DotPalScale
					end

					local dist = (plX - wx) ^ 2 + (plY - wy) ^ 2
					if dist < palDist then
						palName = name
						palDist = dist
						palX, palY = wx, wy

--						Nx.prtCtrl ("Pal %s %s", name, dist)
					end
				end

				if inCombat then

					local dist = (plX - wx) ^ 2 + (plY - wy) ^ 2
					if dist < combatDist then
						combatName = name
						combatUnit = unit
						combatHealth = per
						combatDist = dist
						combatX, combatY = wx, wy
					end
				end
			end

			local f1 = self:GetIcon (1)

			if self:ClipFrameW (f1, wx, wy, sz, sz, 0) then

				f1.NXType = 1000
				f1.NXData = unit
				f1.NXData2 = fullName

				local inactive
				for n = 1, MAX_TARGET_DEBUFFS do
					if UnitDebuff (unit, n) == "Inactive" then
						inactive = true
						per = 0
						break
					end
				end

				local txName = "IconPlyrP"

				if pals[name] == false then
					txName = "IconPlyrF"
				elseif pals[name] == true then
					txName = "IconPlyrG"
				end

				if inCombat then
					txName = txName.."C"
				end

				f1.texture:SetTexture ("Interface\\AddOns\\Carbonite\\Gfx\\Map\\"..txName)

--				Nx.prt ("#%d %.1f %.1f", i, pX, pY)

				-- Show health

				local tStr = ""
--PAIDS!
				f = self:GetIconNI (2)

				if per > .33 then

					-- Horizontal bar at top left

					local sc = self.ScaleDraw
					self:ClipFrameTL (f, wx - 9 / sc, wy - 10 / sc, 16 * per / sc, 1 / sc)
--					self:ClipFrameZTLO (f, pX, pY, 12 * per / self.ScaleDraw, .9 / self.ScaleDraw, -7, -7)
					f.texture:SetTexture (1, 1, 1, 1)

				else
					self:ClipFrameW (f, wx, wy, 7, 7, 0)
--					self:ClipFrameZ (f, pX, pY, 7, 7, 0)

					if per > 0 then
						f.texture:SetTexture (1, .1, .1, 1 - per * 2)
					else
						if inactive then
							f.texture:SetTexture (1, 0, 1, .7)	-- Punk
						else
							f.texture:SetTexture (0, 0, 0, .5)	-- Dead
						end
					end
				end

				-- Show target info

				local unitTarget = unit.."target"
				local tName = UnitName (unitTarget)
				local tEnPlayer

				if tName then

					local tLvl = UnitLevel (unitTarget)
					local tCls = UnitClass (unitTarget) or ""
					if tName == tCls then
						tCls = ""
					end

					local th = UnitHealth (unitTarget)
					if UnitIsDeadOrGhost (unitTarget) then
						th = 0
					end
					local tm = max (UnitHealthMax (unitTarget), 1)
					local per = min (th / tm, 1)

--					Nx.prt ("H %d", th)

					local f = self:GetIconNI (2)
					local sc = self.ScaleDraw

					if UnitIsFriend ("player", unitTarget) then

						-- Horizontal green bar
						self:ClipFrameTL (f, wx - 9 / sc, wy - 2 / sc, 16 * per / sc, 1 / sc)
						f.texture:SetTexture (0, 1, 0, 1)

						tStr = format ("\n|cff80ff80%s %d %s %d", tName, tLvl, tCls, th)

						if not UnitIsPlayer (unitTarget) then	-- NPC?
							tStr = tStr .. "%"
						end
					else
						self:ClipFrameTL (f, wx - 9 / sc, wy - 9 / sc, 1 / sc, 15 * per / sc)

						if UnitIsPlayer (unitTarget) then

							tEnPlayer = true
							tStr = format ("\n|cffff4040%s %d %s %d%%", tName, tLvl, tCls, th)
							f.texture:SetTexture (redGlow, .1, 0, 1)

						elseif UnitIsEnemy ("player", unitTarget) then

							tStr = format ("\n|cffffff40%s %d %s %d%%", tName, tLvl, tCls, th)

							if Nx:UnitIsPlusMob (unitTarget) then
								f.texture:SetTexture (1, .4, 1, 1)
							else
								f.texture:SetTexture (1, 1, 0, 1)
							end

						else
							tStr = format ("\n|cffc0c0ff%s %d %s %d%%", tName, tLvl, tCls, th)
							f.texture:SetTexture (.7, .7, 1, 1)
						end
					end
				end
--PAIDE!
				local lvl = UnitLevel (unit)
				local qStr = Nx.Com:GetPlyrQStr (name)

				if raid then
					local name, rank, grp = GetRaidRosterInfo (i)
					cls = cls .. " G" .. grp
				end

				f1.NxTip = format ("%s %d %s %d%%\n(%d,%d) %s %s%s", fullName, lvl, cls, per * 100, pX, pY, inactive and "Inactive" or "", tStr, qStr or "")

				if alt then
					-- tStr has \n
					local s = tEnPlayer and (name .. tStr) or name
					local txt = self:GetText (s)
					self:MoveTextToIcon (txt, f1, 15, 1)
				end
			end
		end
	end

	self.Level = self.Level + 3

	if palName and self.GOpts["HUDATBGPal"] then
		if not combatName or combatDist > palDist then
			self.TrackPlayer = palName
			return palName, palX, palY
		end
	end

	if combatName then

		if not self.InCombat or combatDist > 35 then
			self.TrackPlayer = combatName
			return format ("Combat, %s %d%%", combatName, combatHealth * 100), combatX, combatY
		end
	end
end

--------
-- Draw player position history

function Nx.Map:UpdatePlyrHistory()

	local Map = Nx.Map			-- Use global map
	local hist = Map.PlyrHist

	local tm = GetTime()

	local scale = self.BaseScale

	local x = hist.LastX - self.MoveLastX
	local y = hist.LastY - self.MoveLastY
	local moveDist = (x * x + y * y) ^ .5

	if moveDist > self.GOpts["MapTrailDist"] * scale then

		hist.LastX = self.MoveLastX
		hist.LastY = self.MoveLastY

		hist.Time = tm

		local next = hist.Next
		local o = next * 4 - 3

		hist[o] = GetTime()
		hist[o + 1] = self.PlyrX
		hist[o + 2] = self.PlyrY
		hist[o + 3] = self.PlyrDir

		if next >= hist.Cnt then
			next = 0
		end

		hist.Next = next + 1
	end

	local size = min (max (4 * self.ScaleDraw * self.BaseScale, 3), 25)
	local fadeTime = self.GOpts["MapTrailTime"]

	for n = 1, hist.Cnt * 4, 4 do

		local secs = hist[n]
		local tmdif = tm - secs

		if tmdif < fadeTime then

			local x = hist[n + 1]
			local y = hist[n + 2]
			local dir = hist[n + 3]

			local f = self:GetIconNI()

			if self:ClipFrameW (f, x, y, size, size, dir) then

				f.texture:SetTexture ("Interface\\AddOns\\Carbonite\\Gfx\\Map\\IconCircleFade")
				local a = (fadeTime - tmdif) / fadeTime * .9
				f.texture:SetVertexColor (1, 0, 0, a)

--				f.texture:SetTexture ("Interface\\Minimap\\MinimapArrow")
--				self:ClipFrameW (f, x, y, 16, 16, dir)
--				f.texture:SetVertexColor (1, 1, 1, .3)
			end

		end
	end
end

--------
-- Targets

function Nx.Map:UpdateTargets()

	local delay = self.UpdateTargetDelay
	if delay > 0 then
		self.UpdateTargetDelay = delay - 1
		return
	end

	local tar = self.Targets[1]

	local x = tar.TargetMX - self.PlyrX
	local y = tar.TargetMY - self.PlyrY
	local distYd = (x * x + y * y) ^ .5 * 4.575

	if distYd < (tar.Radius or 7) * self.BaseScale then

		if tar.TargetType ~= "Q" then	-- Not for quest, so clear

			self.UpdateTargetDelay = 20
			self.UpdateTrackingDelay = 0

--			Nx.prtVar ("", self.Targets[1])

			tremove (self.Targets, 1)

			if #self.Targets > 0 and self.GOpts["RouteRecycle"] then
				tinsert (self.Targets, tar)
			end

			if self.GOpts["HUDTSoundOn"] then
				Nx:PlaySoundFile ("sound\\interface\\magicclick.wav")
			end

			UIErrorsFrame:AddMessage ("Target " .. tar.TargetName .. " reached", 1, 1, 1, 1)

			self.Guide:ClearAll()

			if tar.RadiusFunc then
--				Nx.prt ("Target radius func")
				tar.RadiusFunc ("distance", tar.UniqueId, tar.Radius, distYd, distYd)
				tar.RadiusFunc = nil
			end
		end
	end
end

function Nx.Map:UpdateTracking()

	local delay = self.UpdateTrackingDelay - 1

	if delay <= 0 then

		self:CalcTracking()
		delay = 45
	end

	self.UpdateTrackingDelay = delay

	--

	self.Level = self.Level + 2

	local dist1
	local dir1

	local srcX = self.PlyrX
	local srcY = self.PlyrY

	for n = 1, #self.Tracking do

		local tr = self.Tracking[n]

		self:DrawTracking (srcX, srcY, tr.TargetMX, tr.TargetMY, tr.TargetTex, tr.Mode, tr.TargetName)

		if n == 1 then
			self.TrackName = tr.TargetName
			dist1 = self.TrackDistYd
			dir1 = self.TrackDir
		end

		srcX = tr.TargetMX
		srcY = tr.TargetMY
	end

	self.TrackDistYd = dist1
	self.TrackDir = dir1
end

function Nx.Map:CalcTracking()

	Nx.Timer:ProfilerStart ("Map Tracking")

	local Travel = Nx.Travel

	local tr = {}
	self.Tracking = tr

	local srcX = self.PlyrX
	local srcY = self.PlyrY
	local srcMapId = self.RMapId

	for n, tar in ipairs (self.Targets) do

		Travel:MakePath (tr, srcMapId, srcX, srcY, tar.MapId, tar.TargetMX, tar.TargetMY, tar.TargetType)

		tinsert (tr, tar)

		srcX = tar.TargetMX
		srcY = tar.TargetMY
		srcMapId = tar.MapId
	end

	Nx.Timer:ProfilerEnd ("Map Tracking")
end

--------
-- Draw a tracking cursor and lines

function Nx.Map:DrawTracking (srcX, srcY, dstX, dstY, tex, mode, name)

	local x = dstX - srcX
	local y = dstY - srcY

	local dist = (x * x + y * y) ^ .5
	self.TrackDistYd = dist * 4.575

	if tex ~= false then

		local f = self:GetIcon (1)

		local size = 16 * self.IconNavScale
		self:ClipFrameW (f, dstX, dstY, size, size, 0)

		local s = name or self.TrackName

		f.NxTip = format ("%s\n%d yds", s, dist * 4.575)

		f.texture:SetTexture (tex or "Interface\\AddOns\\Carbonite\\Gfx\\Map\\IconWayTarget")
	end

	self.TrackDir = false

	if 1 then

		local dir = math.deg (math.atan2 (y, x)) + 90

		self.TrackDir = dir

		local sx = self.ScaleDraw
		local sy = self.ScaleDraw / 1.5

		x = x * sx
		y = y * sy

		-- Offset toward target
--		local off = 1 / sqrt (x * x + y * y)
--		local xo = x * off
--		local yo = y * off

		local cnt = (x * x + y * y) ^ .5 / 15 / self.IconNavScale
		if cnt < 5 then
			cnt = cnt + .5
		end
		cnt = min (floor (cnt), 40)

		if cnt >= 1 then

			local dx = x / cnt
			local dy = y / cnt

			local offset = self.ArrowScroll
			x = dx * offset
			y = dy * offset

			local size = 16 * self.IconNavScale
--			local pulse = floor (self.ArrowPulse)
			local usedIcon = true
			local f

			for n = 1, cnt do

				local wx = srcX + x / sx
				local wy = srcY + y / sy

--[[			-- Needed if we use an offset
				if n >= cnt then
					if sqrt ((wx - srcX) ^ 2 + (wy - plY) ^ 2) > dist then
						Nx.prt ("Target arrow")
						break
					end
				end
--]]
				if usedIcon then
					usedIcon = false
					f = self:GetIconNI()
				end

				if self:ClipFrameW (f, wx, wy, size, size, dir) then

					f.texture:SetTexture ("Interface\\AddOns\\Carbonite\\Gfx\\Map\\IconArrowGrad")

--					local a = n == pulse and .8 or .2
--					f.texture:SetVertexColor (1, 1, 1, a)

					if mode == "B" then
						f.texture:SetVertexColor (.7, .7, 1, .5)
					elseif mode == "F" then
						f.texture:SetVertexColor (1, 1, 0, .9)
					elseif mode == "D" then
						f.texture:SetVertexColor (1, 0, 0, 1)
					end

					usedIcon = true
				end

				x = x + dx
				y = y + dy
			end
		end
	end
end

--------
-- Move continent frames

function Nx.Map:MoveContinents()

	if self.CurOpts.NXWorldShow then

		for contN = 1, Nx.Map.ContCnt do
			local lvl = contN <= 2 and self.Level or self.Level + 1
			self:MoveZoneTiles (contN, 0, self.ContFrms[contN], self.WorldAlpha, lvl)
		end

		local f = self.ContFillFrm
		if f then
			if Nx.V30 then
				self:ClipFrameTL (f, 1600, -1600, 1500, 4400, 0)
			else
				self:ClipFrameTL (f, 1600, -1900, 1500, 4650, 0)
			end
			f:SetFrameLevel (self.Level + 1)
			f:SetAlpha (self.WorldAlpha)
		end

--[[	-- Map cap
		local frms, frm

		frms = self.ContFrms[self.Cont or 1]

		for i = 1, 12 do
			frm = frms[i]
			if frm then
				frm.texture:SetVertexColor (0, 0, 0, 1)
			end
		end
--]]
		self.Level = self.Level + 2

	else

		local frms, frm

		for contN = 1, Nx.Map.ContCnt do
			frms = self.ContFrms[contN]

			for i = 1, 12 do
				frm = frms[i]
				if frm then
					frm:Hide()
				end
			end
		end

		if self.ContFillFrm then
			self.ContFillFrm:Hide()
		end
	end
end

--------
-- Check world zone hotspots

function Nx.Map:CheckWorldHotspots (wx, wy)

	if self.InstMapId then
		if wx >= self.InstMapWX1 and wx <= self.InstMapWX2 and wy >= self.InstMapWY1 and wy <= self.InstMapWY2 then

			local lvl = floor ((wy - self.InstMapWY1) / 668 * 256) + 1

			if self.InstMapId ~= self.MapId then

--				Nx.prt ("Hit Inst %s, lvl %s", self.InstMapId, lvl)

				self:SetCurrentMap (self.InstMapId)
			end

			SetDungeonMapLevel (lvl)

			self.InstLevelSet = -1

			self.WorldHotspotTipStr = Nx.MapIdToName[self.InstMapId] .. "\n"

			return
		end
	end

	local quad1 = self.WorldHotspotsCity
	local quad2 = self.WorldHotspots

	if self.NXCitiesUnder then
		quad1, quad2 = quad2, quad1
	end

	if self:CheckWorldHotspotsType (wx, wy, quad1) then
		return
	end

	if self:CheckWorldHotspotsType (wx, wy, quad2) then
		return
	end

	self.WorldHotspotTipStr = false

--	local tt = GameTooltip
--	if tt:IsOwned (self.Win.Frm) and self.TooltipType == 1 then
--		tt:Hide()
--	end
end

--------
-- Check world zone hotspots type
-- This is very fast. No need to make a quad tree

function Nx.Map:CheckWorldHotspotsType (wx, wy, quad)

	for n, spot in ipairs (quad) do
		if wx >= spot.WX1 and wx <= spot.WX2 and wy >= spot.WY1 and wy <= spot.WY2 then

			local curId = self:GetCurrentMapId()
			curId = self.MapWorldInfo[curId].Level1Id or curId

			if spot.MapId ~= curId then

--				Nx.prt ("hotspot %s %s %s %s %s", spot.MapId, spot.WX1, spot.WX2, spot.WY1, spot.WY2)

				self:SetCurrentMap (spot.MapId)
			end

			self.WorldHotspotTipStr = spot.NxTipBase .. "\n"
--[[
			if false then

				local tt = GameTooltip
				local owner = self.Win.Frm

				owner.NXTip = spot.NxTipBase

				if not tt:IsVisible() then

--					Nx.prt ("hotspot tip")

					local tippos = "ANCHOR_TOPLEFT"

					Nx.TooltipOwner = owner
					self.TooltipType = 1

					tt:SetOwner (owner, tippos, 0, 0)
					Nx:SetTooltipText (owner.NXTip .. Nx.Map.PlyrNamesTipStr)

					owner["UpdateTooltip"] = Nx.Map.OnUpdateTooltip
				end
			end
--]]
			return true
		end
	end
end

--------
-- Called by tooltip
-- self = frame

--[[
function Nx.Map:OnUpdateTooltip()

	local map = self.NxMap
	map:BuildPlyrLists()

	Nx:SetTooltipText (self.NXTip .. map.TipStr .. Nx.Map.PlyrNamesTipStr)

--	Nx.prt ("OnUpdateTooltip %s %s", self.NXTip, map.TipStr)
end
--]]

--------
-- Move world zone hotspot frames

--[[
function Nx.Map:MoveWorldHotspots()

	local clip = self.ClipFrameTL

	local level = self.Level
	local alpha = 0

	if self.Debug then
		level = level + 15
		alpha = self.BackgndAlpha * .5
	end

	self.Level = self.Level + 2

	local cont = self.Cont
	local zone = self.Zone

	local _, zx, zy, zw, zh = self:GetWorldZoneInfo (cont, zone)
	if not zx then
		return
	end

--	Nx.prt ("Map World XY "..basex.." "..basey)

	local frms = self.WorldHotFrms

	frms.UpStart = (frms.UpStart + 1) % 3

	for n = frms.UpStart + 1, #frms, 3 do

		local f = frms[n]

		if clip (self, f, f.NXWX, f.NXWY, f.NXW, f.NXH) then

			if self.DebugMap then
				f.texture:SetVertexColor (1, 1, 1, alpha)
			end

			f:SetFrameLevel (level + f.NXLev)

			f.NxTip = f.NxTipBase .. self.TipStr .. "~T"
		end
	end
end
--]]

--------
-- Update

function Nx.Map:SetLevelWorldHotspots()
--[[
	local lvl =	self.NXCitiesUnder and -1 or 1

	for _, f in ipairs (self.WorldHotFrms) do
		if f.NXLev ~= 0 then		-- A city?
			f.NXLev = lvl
		end
	end
--]]
end

--------
-- Update map zone tiles

function Nx.Map:MoveCurZoneTiles (clear)

	local mapId = self.MapId
	local wzone = self:GetWorldZone (mapId)

	if not clear and
			(not wzone or wzone.City or (wzone.StartZone and self.RMapId == mapId) or
			self:IsBattleGroundMap (mapId)) then

--		Nx.prt ("MoveCurZoneTiles %d", mapId)

		local alpha = self.BackgndAlpha * (wzone.Alpha or 1)

		self:MoveZoneTiles (self.Cont, self.Zone, self.TileFrms, alpha, self.Level)
		self.Level = self.Level + 1

	else
		local frms, frm

		frms = self.TileFrms

		for i = 1, 12 do
			frm = frms[i]
			if frm then
				frm:Hide()
			end
		end
	end
end

--------
-- Hide extra (Dalaran) map zone tiles

function Nx.Map:HideExtraZoneTiles()

	local frms = self.TileFrms
	frms[4]:Hide()
	frms[8]:Hide()
	frms[9]:Hide()
	frms[12]:Hide()
end

--------
-- Update map zone tiles (4x3 blocks)

function Nx.Map:MoveZoneTiles (cont, zone, frms, alpha, level)

	local zname, zx, zy, zw, zh = self:GetWorldZoneInfo (cont, zone)
	if not zx then
		return
	end

--	Nx.prt ("MapZ %f, %f", zx, zy)

	local scale = self.ScaleDraw

	local clipW = self.MapW
	local clipH = self.MapH
	local x = (zx - self.MapPosXDraw) * scale + clipW / 2
	local y = (zy - self.MapPosYDraw) * scale + clipH / 2
	local bx = 0
	local by = 0
	local bw = zw * 1024 / 1002 / 4 * scale
	local bh = zh * 768 / 668 / 3 * scale
	local w, h
	local texX1, texX2
	local texY1, texY2

	for i = 1, 12 do

		local frm = frms[i]
		if frm then

			texX1 = 0
			texX2 = 1
			texY1 = 0
			texY2 = 1

			local vx0 = bx * bw + x
			local vx1 = vx0
			local vx2 = vx0 + bw

			if vx1 < 0 then
				vx1 = 0
				texX1 = (vx1 - vx0) / bw
			end

			if vx2 > clipW then
				vx2 = clipW
				texX2 = (vx2 - vx0) / bw
			end

			local vy0 = by * bh + y
			local vy1 = vy0
			local vy2 = vy0 + bh

			if vy1 < 0 then
				vy1 = 0
				texY1 = (vy1 - vy0) / bh
			end

			if vy2 > clipH then
				vy2 = clipH
				texY2 = (vy2 - vy0) / bh
			end

			w = vx2 - vx1
			h = vy2 - vy1

			if w <= 0 or h <= 0 then
				frm:Hide()
			else
				frm:SetPoint ("TOPLEFT", vx1, -vy1 - self.TitleH)
				frm:SetWidth (w)
				frm:SetHeight (h)
				frm:SetFrameLevel (level)

				frm.texture:SetTexCoord (texX1, texX2, texY1, texY2)
				frm.texture:SetVertexColor (1, 1, 1, self.BackgndAlpha)

				frm:Show()
			end
		end

		bx = bx + 1

		if bx >= 4 then
			bx = 0
			by = by + 1
		end
	end
end

--------
-- Add old map zone to list

function Nx.Map:AddOldMap (newMapId)

	if self.MapId == 0 then		-- Happens on startup
		return
	end

	-- Remove any for new zone

	local off = 1
	local dup

	for n = 1, #self.MapsDrawnOrder do
		if self.MapsDrawnOrder[off] == newMapId then
			tremove (self.MapsDrawnOrder, off)
			dup = true
		else
			off = off + 1
		end
	end

	local drawCnt = self.GOpts["MapZoneDrawCnt"]

	if not dup then

--		Nx.prt ("no dup")

		local extra = #self.MapsDrawnOrder - drawCnt + 2
		for n = 1, extra do
			tremove (self.MapsDrawnOrder, 1)
		end
	end

	if drawCnt > 1 then

		self.MapsDrawnFade[self.MapId] = -1
		tinsert (self.MapsDrawnOrder, self.MapId)		-- Newest at end

--		Nx.prt ("Cur map %s", self:GetCurrentMapId())
--		Nx.prtVar ("order", self.MapsDrawnOrder)
	end
end

--------
-- Update the zones

function Nx.Map:UpdateZones()

	local mapId = self.MapId
	local winfo = self.MapWorldInfo[mapId]

--	Nx.prt ("UpdateZones %s, %s", mapId, winfo.Name or "nil")

	local s = self.LOpts.NXDetailScale

	local freeOrScale = self.ScaleDraw <= s

	if freeOrScale or
		winfo.City or
		(winfo.StartZone and self.RMapId == mapId) or
		self:IsBattleGroundMap (mapId) then

--		if freeOrScale and self.MapIdOld and self.MapIdOld ~= mapId then
--			self:UpdateOverlay (id, .8, true)
--		end

		for n, id in ipairs (self.MapsDrawnOrder) do
			self:UpdateOverlay (id, .8, true)
		end
--[[
		if freeOrScale then

			local abs = abs

			for id, fade in pairs (self.MapsDrawnFade) do

				if id ~= mapId then
					self:UpdateOverlay (id, abs (fade) * .8, true)
				end

				if fade > 0 then
					fade = fade + .1
					self.MapsDrawnFade[id] = fade <= 1 and fade or 1

				elseif fade < 0 then
					fade = fade + .1
					self.MapsDrawnFade[id] = fade < 0 and fade or nil
				end
			end
		end
--]]
--[[
			if mapId >= 1000 and mapId <= 1999 then

				for id = 1001, 1024 do
					if id ~= mapId then
						self:UpdateOverlay (id, .8)
					end
				end

			elseif mapId >= 2000 and mapId <= 2999 then

				for id = 2001, 2028 do
					if id ~= mapId then
						self:UpdateOverlay (id, .8)
					end
				end

			elseif mapId >= 3000 and mapId <= 3999 then

				for id = 3001, 3008 do
					if id ~= mapId then
						self:UpdateOverlay (id, .8)
					end
				end
			end
--]]
		if winfo.City then
--			Nx.prt ("city %s", self.Level)
			self:UpdateMiniFrames()
			self:MoveCurZoneTiles()

		else
			self:MoveCurZoneTiles()
			self:UpdateOverlay (mapId, 1)
			self:UpdateMiniFrames()
		end

	else
		self:MoveCurZoneTiles (true)		-- Clear
		self:UpdateMiniFrames()

	end
end

function Nx.Map:GetExploredOverlayNum()

--	local overlayNum = GetNumMapOverlays()		-- Cartographer makes this return 0

	for i = 1, 999 do
		local txName = GetMapOverlayInfo (i)
		if not txName then
			return i
		end
	end
end

function Nx.Map:UpdateOverlayUnexplored()

	self.CurOverlays = false
	local txFolder

	local mapId = self:GetCurrentMapId()

	local wzone = self:GetWorldZone (mapId)
	if wzone then
		if wzone.City then
			return
		end
		txFolder = wzone.Overlay
	end

	local overlays

	if txFolder then
		overlays = Nx.Map.ZoneOverlays[txFolder]
	end

	if not overlays or not self.ShowUnexplored then

--		local overlayNum = GetNumMapOverlays()		-- Cartographer makes this return 0
--		Nx.prt ("Overlays %s", overlayNum)

		local s1, s2, file
		local ol = {}

		if overlays then
			for txName, whxyStr in pairs (overlays) do	-- Copy overlay table
				ol[txName] = whxyStr
			end
		end

		overlays = ol

		for i = 1, 99 do

			-- Terrokar has 4 overlays with "" for name

			local txName, txW, txH, oX, oY = GetMapOverlayInfo (i)
			if not txName then
				break
			end

			local s1, s2, folder, file = strfind (txName, ".+\\.+\\(.+)\\(.+)")
			if s1 then
				txFolder = folder
				file = strlower (file)
				overlays[file] = format ("%d,%d,%d,%d", oX - 10000, oY, txW, txH)

--				Nx.prt (" %s %s", txName, overlays[file])
			end
		end

		if not txFolder then		-- Can happen on log in
			overlays = false
		end
	end

	self.CurOverlays = overlays
	self.CurOverlaysTexFolder = txFolder
end

function Nx.Map:TargetOverlayUnexplored()

	local mapId = self:GetCurrentMapId()

	self:ClearTargets()		-- Will change current mapid

	local wzone = self:GetWorldZone (mapId)
	if wzone and wzone.City then
		return
	end

	local overlays = self.CurOverlays

	if not overlays then	-- Not found? New stuff probably
		return
	end

	for txName, whxyStr in pairs (overlays) do

		local oX, oY, txW, txH = strsplit (",", whxyStr)

		oX = tonumber (oX)
		oY = tonumber (oY)

		if oX >= 0 then

			txW = tonumber (txW)
			txH = tonumber (txH)

			if txW == 512 then
				txW = txW * .75
			end
			if txH == 512 then
				txH = txH * .75
			end

--			Nx.prt ("%s %s %s %s %s", txName, oX, oY, txW, txH)

			local x, y = (oX + txW / 2) / 1002 * 100, (oY + txH / 2) / 668 * 100
--			local wx, wy = self:GetWorldPos (mapId, (oX) / 1002 * 100, (oY) / 668 * 100)

			self:SetTargetXY (mapId, x, y, "Explore", true)
		end
	end
end

--------
-- Update the overlays

function Nx.Map:UpdateOverlay (mapId, bright, noUnexplored)

	local wzone = self:GetWorldZone (mapId)
	if wzone and wzone.City then
		return
	end

	local txFolder = wzone and wzone.Overlay or ""
	local overlays = Nx.Map.ZoneOverlays[txFolder]
	local unex

	if not noUnexplored and (not overlays or not self.ShowUnexplored) then

		if not (wzone and wzone.Explored) then
			unex = true
		end

		overlays = self.CurOverlays
		txFolder = self.CurOverlaysTexFolder
	end

	if not overlays then	-- Not found? New stuff probably
		return
	end

	local bW, bH
	local txIndex
	local txPixelW, txFileW, txPixelH, txFileH

	local path = "Interface\\Worldmap\\" .. txFolder .. "\\"

	local alpha = self.BackgndAlpha
	local unExAl = self.LOpts.NXUnexploredAlpha

	local zscale = self:GetWorldZoneScale (mapId) / 10

	for txName, whxyStr in pairs (overlays) do

		local lev = 0
		local brt = bright

		txName = path .. txName

		local oX, oY, txW, txH, mode = strsplit (",", whxyStr)

		txW = tonumber (txW)
		txH = tonumber (txH)
		oX = tonumber (oX)
		oY = tonumber (oY)

		if unex then		-- Dimming unexplored?
			if oX < 0 then
				oX = oX + 10000	-- Fix explored x
			else
				brt = unExAl		-- Dim
				lev = 1
			end
		end

--		if self.Debug then
--			Nx.prt ("%d %f %f", i, oX, oY)
--		end

		bW = ceil (txW / 256)
		bH = ceil (txH / 256)
		txIndex = 1

		for bY = 0, bH - 1 do

			if bY < bH - 1 then
				txPixelH = 256
				txFileH = 256
			else
				txPixelH = mod (txH, 256)
				if txPixelH == 0 then
					txPixelH = 256
				end
				txFileH = 16
				while txFileH < txPixelH do
					txFileH = txFileH * 2
				end
			end

			for bX = 0, bW - 1 do

				if bX < bW - 1 then
					txPixelW = 256
					txFileW = 256
				else
					txPixelW = mod (txW, 256)
					if txPixelW == 0 then
						txPixelW = 256
					end
					txFileW = 16
					while txFileW < txPixelW do
						txFileW = txFileW * 2
					end
				end

				local f = self:GetIconNI (lev)

				local wx, wy = self:GetWorldPos (mapId, (oX + bX * 256) / 1002 * 100, (oY + bY * 256) / 668 * 100)

				if self:ClipFrameTL (f, wx, wy, txFileW * zscale, txFileH * zscale) then

--					if IsShiftKeyDown() then
--						f.texture:SetTexture (1, 0, 0)
--					end
--[[
					if IsAltKeyDown() then		-- DEBUG!
						alpha = .2
					end
--]]
					f.texture:SetTexture (mode and txName or txName .. txIndex)
					f.texture:SetVertexColor (brt, brt, brt, alpha)

--					if IsControlKeyDown() then
--						Nx.prt ("Overlay %s, %s, %s %s", txName, txIndex, oX, oY)
--					end

--[[ -- Map cap
					if bright < 1 then
						f.texture:SetVertexColor (1, 1, 1, 1)
--						SetDesaturation (f.texture, 1)
					end
--]]
				end

				txIndex = txIndex + 1
			end
		end
	end

	self.Level = self.Level + 2
end

--------
-- Update frames for mini map texture layer (detail layer)

function Nx.Map:UpdateMiniFrames()

--[[
	SaveView (5)
--	local x = GetCVar ("cameraYawD")
--	local y = GetCVar ("cameraPitchD")
--	local isLook = IsMouselooking()

--	Nx.prtCtrl ("Cam %s %s %s", x, y, isLook or "nil")

	local vartest = {
		"cameraYaw",
		"cameraYawA",
		"cameraYawB",
		"cameraYawC",
		"cameraYawD",
	}

	for k, name in pairs (vartest) do
		Nx.prtCtrl ("Cam %s %s", name, GetCVar (name) or "nil")
	end
--]]

	local Map = Nx.Map
	local mapId = self.MapId
	local winfo = self.MapWorldInfo[mapId]

	local opts = self.LOpts

	local alphaRange = opts.NXDetailScale * .35
	local s = opts.NXDetailScale - alphaRange

--	s = .1

--	or winfo.City

	if self.ScaleDraw <= s or opts.NXDetailAlpha <= 0 or self:IsBattleGroundMap (mapId) then
		self:HideMiniFrames()
		return
	end

	local alphaPer = min ((self.ScaleDraw - s) / alphaRange, 1)
--	Nx.prt ("alpha %s", alphaPer)

--	local cont = self.Cont
--	local zname, zx, zy

	local miniT, basex, basey = self:GetMiniInfo (mapId)
	if not miniT then
		self:HideMiniFrames()
		return
	end

	local level = self.Level
	self.Level = self.Level + 1

	local f
	local frmNum = 1

	local scale = 256 * 0.416767770014
	local size = scale

--	size = size - 4

	local miniX = floor ((self.MapPosXDraw - basex) / scale - self.MiniBlks / 2 + .5)
	local miniY = floor ((self.MapPosYDraw - basey) / scale - self.MiniBlks / 2 + .5)

--	Nx.prt ("MiniXY %f %f", miniX, miniY)

	basex = basex + miniX * scale
	basey = basey + miniY * scale

	local wx
	local wy = basey
	local al = self.BackgndAlpha * opts.NXDetailAlpha * alphaPer

	for y = miniY, miniY + self.MiniBlks - 1 do

		wx = basex

		for x = miniX, miniX + self.MiniBlks - 1 do

			f = self.MiniFrms[frmNum]
			local txname = Map:GetMiniBlkName (miniT, x, y)

			if txname then

				if self:ClipFrameTL (f, wx, wy, size, size) then

					f:SetFrameLevel (level)
					f.texture:SetVertexColor (1, 1, 1, al)
--					txname = "Textures\\Minimap\\"..txname
					f.texture:SetTexture (txname)

--[[
--					Nx.prtCtrl ("%s %s, %s", x, y, txname)

					local r, r2, r3 = f.texture:SetTexture (txname)		--V4 always returns r=1?
					Nx.prtVar ("mmtex", r)
					Nx.prtVar ("mmtex2", r2)
					Nx.prtVar ("mmtex3", r3)
					if not r then
						Nx.prtCtrl ("%s %s, %s", x, y, txname)
					end
--]]
				end

			else

				f:Hide()
			end

			wx = wx + scale
			frmNum = frmNum + 1
		end

		wy = wy + scale
	end

end

function Nx.Map:HideMiniFrames()

	for n = 1, self.MiniBlks ^ 2 do
		self.MiniFrms[n]:Hide()
	end
end

--------
-- Update POI

--[[
Nx.POITypes = { "Mailbox", "Anvil", "Forge", "Moonwell", "Manaloom" }
Nx.POITex = { "Icons\\INV_Letter_15",
	"Icons\\Trade_BlackSmithing",
	"Icons\\INV_Sword_09",
	"Icons\\Spell_Fire_BlueFlameRing",
	"Icons\\Spell_Fire_BlueFlameRing"
}

function Nx.Map:UpdateNxPOI()

	local Map = Nx.Map

	for poiType, v in pairs (Nx.POI) do

		local str = Nx.POI[poiType]

		for n = 1, #str, 5 do

			local zone = strbyte (str, n)
			if not zone then
				break
			end

			zone = zone - 35

			local x = ((strbyte (str, n + 1) - 35) * 221 + (strbyte (str, n + 2) - 35)) / 100
			local y = ((strbyte (str, n + 3) - 35) * 221 + (strbyte (str, n + 4) - 35)) / 100

			if self.Debug then
--				Nx.prt ("POI #%d %d %d %d", (n+4) / 5, zone, x, y)
			end

			local mapId = Map.NxzoneToMapId[tonumber (zone)]

			if not mapId then
--				Nx.prt ("POI #%d %d %d %d", (n+4) / 5, zone, x, y)

			else
				x, y = self:GetWorldPos (mapId, x, y)

				local f = self:GetIcon()

				if self:ClipFrameW (f, x, y, 16, 16, 0) then
					f.NxTip = format ("%s", Nx.POITypes[poiType])
					f.texture:SetTexture ("Interface\\" .. Nx.POITex[poiType])
				end
			end
		end
	end
end
--]]

--------
--
--[[
function Nx.Map:DrawZoneRect (mapId, x, y, w, h, tip)

	mapId = mapId or self.MapId

	local wx, wy = self:GetWorldPos (mapId, x, y)
	local scale = self:GetWorldZoneScale (mapId) / 10.02

	local f = map:GetIcon()

	if self:ClipFrameTL (f, wx, wy, w * scale, h * scale) then
		f.NxTip = tip
		f.texture:SetTexture (r, g, b, .5)
	end
end
--]]

--------
-- Zone clip a frame to the map and set position, size and texture coords
-- XY is center. Width and height are not scaled

function Nx.Map:ClipFrameZ (frm, x, y, w, h, dir)

	x, y = self:GetWorldPos (self.MapId, x, y)
	return self:ClipFrameW (frm, x, y, w, h, dir)
end

--------
-- Zone clip a frame to the map and set position (top left), size and texture coords
-- Width and height are scaled

function Nx.Map:ClipFrameZTL (frm, x, y, w, h)

	x, y = self:GetWorldPos (self.MapId, x, y)
	return self:ClipFrameTL (frm, x, y, w, h)
end

--------
-- Zone clip a frame (top left with offset)
-- Width and height are scaled

function Nx.Map:ClipFrameZTLO (frm, x, y, w, h, xo, yo)

	x, y = self:GetWorldPos (self.MapId, x, y)
	return self:ClipFrameTL (frm, x + xo / self.ScaleDraw, y + yo / self.ScaleDraw, w, h)
end

--------
-- Clip a frame to the map and set position, size and texture coords
-- XY is center. Width and height are not scaled

function Nx.Map:ClipFrameW (frm, bx, by, w, h, dir)

--	Nx.Timer:ProfilerStart ("Map ClipFrameW", true)
--	Nx.Timer:ProfilerEnd ("Map ClipFrameW")

	local scale = self.ScaleDraw

	-- Calc x

	local bw = w
	local clipW = self.MapW
	local x = (bx - self.MapPosXDraw) * scale + clipW * .5

	local texX1 = 0
	local texX2 = 1

	local vx0 = x - bw * .5		-- Center frame at bx
	local vx1 = vx0
	local vx2 = vx0 + bw

	if vx1 < 0 then
		vx1 = 0
		texX1 = (vx1 - vx0) / bw
	end

	if vx2 > clipW then
		vx2 = clipW
		texX2 = (vx2 - vx0) / bw
	end

	w = vx2 - vx1

	if w < .3 then

--		Nx.Timer:ProfilerStart ("Map ClipFrameW x", true)
--		Nx.Timer:ProfilerEnd ("Map ClipFrameW x")

		if self.ScrollingFrm ~= frm then
			frm:Hide()
		else
			frm:SetWidth (.001)
		end
		return false
	end

	-- Calc y

	local bh = h
	local clipH = self.MapH
	local y = (by - self.MapPosYDraw) * scale + clipH * .5

	local texY1 = 0
	local texY2 = 1

	local vy0 = y - bh * .5		-- Center frame at by
	local vy1 = vy0
	local vy2 = vy0 + bh

	if vy1 < 0 then
		vy1 = 0
		texY1 = (vy1 - vy0) / bh
	end

	if vy2 > clipH then
		vy2 = clipH
		texY2 = (vy2 - vy0) / bh
	end

	h = vy2 - vy1

	if h < .3 then

--		Nx.Timer:ProfilerStart ("Map ClipFrameW y", true)
--		Nx.Timer:ProfilerEnd ("Map ClipFrameW y")

		if self.ScrollingFrm ~= frm then
			frm:Hide()
		else
			frm:SetWidth (.001)
		end
		return false
	end

	-- Set frame

	frm:SetPoint ("TOPLEFT", vx1, -vy1 - self.TitleH)
	frm:SetWidth (w)
	frm:SetHeight (h)

	if dir == 0 then

		frm.texture:SetTexCoord (texX1, texX2, texY1, texY2)

	else
		-- 13 UV order
		-- 24
		local t1x, t1y, t2x, t2y, t3x, t3y, t4x, t4y

		-- Make UV range -.5 to .5
		texX1 = texX1 - .5
		texX2 = texX2 - .5
		texY1 = texY1 - .5
		texY2 = texY2 - .5

		local co = cos (dir)
		local si = sin (dir)
		t1x = texX1 * co + texY1 * si + .5
		t1y = texX1 * -si + texY1 * co + .5
		t2x = texX1 * co + texY2 * si + .5
		t2y = texX1 * -si + texY2 * co + .5
		t3x = texX2 * co + texY1 * si + .5
		t3y = texX2 * -si + texY1 * co + .5
		t4x = texX2 * co + texY2 * si + .5
		t4y = texX2 * -si + texY2 * co + .5
		frm.texture:SetTexCoord (t1x, t1y, t2x, t2y, t3x, t3y, t4x, t4y)

--		Nx.prt (" T1 "..t1x.." "..t1y)
--		Nx.prt (" T2 "..t2x.." "..t2y)
	end

	frm:Show()

	return true
end

--------
-- Clip a frame to the map and set position, size and texture coords
-- XY is center. Width and height are not scaled

function Nx.Map:ClipFrameWChop (frm, bx, by, w, h)

	local bw = w
	local bh = h
	local clipW = self.MapW
	local clipH = self.MapH

	local scale = self.ScaleDraw
	local x = (bx - self.MapPosXDraw) * scale + clipW / 2
	local y = (by - self.MapPosYDraw) * scale + clipH / 2

	local texX1 = 0
	local texX2 = 1

	-- Center frame at bx, by

	local vx0 = x - bw * .5
	local vx1 = vx0
	local vx2 = vx0 + bw

	if vx1 < 0 then
		vx1 = 0
		texX1 = (vx1 - vx0) / bw
	end

	if vx2 > clipW then
		vx2 = clipW
		texX2 = (vx2 - vx0) / bw
	end

	w = vx2 - vx1

	if w < .3 then
		if self.ScrollingFrm ~= frm then
			frm:Hide()
		else
			frm:SetWidth (.001)
		end
		return false
	end

	local texY1 = 0
	local texY2 = 1

	local vy0 = y - bh * .5
	local vy1 = vy0
	local vy2 = vy0 + bh

	if vy1 < 0 then
		vy1 = 0
		texY1 = (vy1 - vy0) / bh
	end

	if vy2 > clipH then
		vy2 = clipH
		texY2 = (vy2 - vy0) / bh
	end

	h = vy2 - vy1

	if h < .3 then
		if self.ScrollingFrm ~= frm then
			frm:Hide()
		else
			frm:SetWidth (.001)
		end
		return false
	end

	frm:SetPoint ("TOPLEFT", vx1, -vy1 - self.TitleH)
	frm:SetWidth (w)
	frm:SetHeight (h)

	frm.texture:SetTexCoord (texX1 * .9 + .05, texX2 * .9 + .05, texY1 * .9 + .05, texY2 * .9 + .05)

	frm:Show()

	return true
end

--------
-- Clip minimap frame to the map and set position, size and texture coords
-- XY is center. Width and height are not scaled

function Nx.Map:ClipMMW (frm, bx, by, w, h)

	local scale = self.ScaleDraw

	-- Each world unit maps to a pixel, so w * scale == size in pixels

	local bw = w * scale
	local bh = h * scale
	local clipW = self.MapW
	local clipH = self.MapH
	local x = (bx - self.MapPosXDraw) * scale + clipW * .5
	local y = (by - self.MapPosYDraw) * scale + clipH * .5

	local vx0 = x - bw * .5
	local vx1 = vx0
	local vx2 = vx0 + bw

	if vx1 < 0 or vx2 > clipW then
		return false
	end

	w = vx2 - vx1

	if w <= 0 then
		return false
	end

	local vy0 = y - bh * .5
	local vy1 = vy0
	local vy2 = vy0 + bh

	if vy1 < 0 or vy2 > clipH then
		return false
	end

	h = vy2 - vy1

	if h <= 0 then
		return false
	end

--	frm:SetPoint ("TOPLEFT", vx1, -vy1 - self.TitleH)
--	frm:SetWidth (w)
--	frm:SetHeight (h)

	local sc = w / 140
	self.MMFScale = sc

	local isc = self.GOpts["MapMMIScale"]
	self:MinimapSetScale (sc, isc)

--	frm:SetScale (sc)
	frm:SetPoint ("TOPLEFT", self.Frm, "TOPLEFT", vx1 / isc, (-vy1 - self.TitleH) / isc)

	frm:Show()

	return true
end

--------
-- Clip a frame to the map and set position (top left), size and texture coords
-- Width and height are scaled by base (zone) scale

function Nx.Map:ClipFrameTL (frm, bx, by, w, h)

	-- Each world unit maps to a pixel, so w * scale == size in pixels

	local scale = self.ScaleDraw

	-- Calc x

	local bw = w * scale
	local clipW = self.MapW
	local x = (bx - self.MapPosXDraw) * scale + clipW * .5

	local texX1 = 0
	local texX2 = 1

	local vx1 = x
	local vx2 = x + bw

	if vx1 < 0 then
		vx1 = 0
		texX1 = (vx1 - x) / bw
	end

	if vx2 > clipW then
		vx2 = clipW
		texX2 = (vx2 - x) / bw
	end

	w = vx2 - vx1

	if w < .3 then
		if self.ScrollingFrm ~= frm then
			frm:Hide()
		else
			frm:SetWidth (.001)
		end
		return false
	end

	-- Calc y

	local bh = h * scale
	local clipH = self.MapH
	local y = (by - self.MapPosYDraw) * scale + clipH * .5

	local texY1 = 0
	local texY2 = 1

	local vy1 = y
	local vy2 = y + bh

	if vy1 < 0 then
		vy1 = 0
		texY1 = (vy1 - y) / bh
	end

	if vy2 > clipH then
		vy2 = clipH
		texY2 = (vy2 - y) / bh
	end

	h = vy2 - vy1

	if h < .3 then
		if self.ScrollingFrm ~= frm then
			frm:Hide()
		else
			frm:SetWidth (.001)
		end
		return false
	end

	frm:SetPoint ("TOPLEFT", vx1, -vy1 - self.TitleH)

	if w <= 1.2 then		-- Adjust so we get a clean line
		w = self.Size1
		if w <= 0 then
			frm:SetWidth (.001)
			return
		end
	end

	if h <= 1.2 then
		h = self.Size1
		if h <= 0 then
			frm:SetWidth (.001)
			return
		end
	end

	frm:SetWidth (w)
	frm:SetHeight (h)

	frm.texture:SetTexCoord (texX1, texX2, texY1, texY2)

	frm:Show()

	return true
end

--------
-- Clip a frame to the map and set position (top left), size and texture coords
-- Width and height are scaled by base (zone) scale
-- Solid color texture version

function Nx.Map:ClipFrameTLSolid (frm, bx, by, w, h)

--	Nx.Timer:ProfilerStart ("Map ClipFrameTLSolid", true)
--	Nx.Timer:ProfilerEnd ("Map ClipFrameTLSolid")

	-- Each world unit maps to a pixel, so w * scale == size in pixels

	local scale = self.ScaleDraw

	-- Calc x

	local clipW = self.MapW
	local vx1 = (bx - self.MapPosXDraw) * scale + clipW * .5
	local vx2 = vx1 + w * scale

	if vx1 < 0 then
		vx1 = 0
	end

	if vx2 > clipW then
		vx2 = clipW
	end

	w = vx2 - vx1

	if w < .3 then
		if self.ScrollingFrm ~= frm then
			frm:Hide()
		else
			frm:SetWidth (.001)
		end
		return false
	end

	-- Calc y

	local clipH = self.MapH
	local vy1 = (by - self.MapPosYDraw) * scale + clipH * .5
	local vy2 = vy1 + h * scale

	if vy1 < 0 then
		vy1 = 0
	end

	if vy2 > clipH then
		vy2 = clipH
	end

	h = vy2 - vy1

	if h < .3 then
		if self.ScrollingFrm ~= frm then
			frm:Hide()
		else
			frm:SetWidth (.001)
		end
		return false
	end

	frm:SetPoint ("TOPLEFT", vx1, -vy1 - self.TitleH)

	frm:SetWidth (w)
	frm:SetHeight (h)

	frm:Show()

	return true
end

--------
-- Clip full zone frame to map

function Nx.Map:ClipZoneFrm (cont, zone, frm, alpha)

	local zname, zx, zy, zw, zh

	zname, zx, zy, zw, zh = self:GetWorldZoneInfo (cont, zone)
	if not zx then
		return
	end

	local scale = self.ScaleDraw

	local clipW = self.MapW
	local clipH = self.MapH
	local x = (zx - self.MapPosXDraw) * scale + clipW / 2
	local y = (zy - self.MapPosYDraw) * scale + clipH / 2
	local bx = 0
	local by = 0
	local bw = zw * scale
	local bh = zh * scale
	local w, h

	local level = self.Level

	if frm then

		local vx0 = bx * bw + x
		local vx1 = vx0
		local vx2 = vx0 + bw

		local vy0 = by * bh + y
		local vy1 = vy0
		local vy2 = vy0 + bh

		w = vx2 - vx1
		h = vy2 - vy1

		if w <= 0 or h <= 0 then
			frm:Hide()
		else
			local sc = w / 1002
			vx1 = vx1 / sc
			vy1 = vy1 / sc
			frm:SetPoint ("TOPLEFT", vx1, -vy1 - self.TitleH)
			frm:SetScale (sc)
			frm:SetFrameLevel (level)

			frm:Show()

--			Nx.prt ("ClipZF %f, %f (%s)", vx1, vy1, sc)
		end
	end
end

--------
-- Init a map icon type and set drawing info

function Nx.Map:InitIconType (iconType, drawMode, texture, w, h)

	local d = self.Data

	local t = wipe (d[iconType] or {})
	d[iconType] = t
	t.Num = 0
	t.Enabled = true
	t.DrawMode = drawMode or "ZP"	-- Zone point is default
	t.Tex = texture
	t.W = w
	t.H = h
	t.Scale = 1	-- USED???
	t.ClipFunc = self.ClipFrameW		-- For WP mode
end

--------
-- Clear a map icon type

function Nx.Map:ClearIconType (iconType)

	local d = self.Data
	d[iconType] = nil
end

--------
-- Set alpha for an icon type

function Nx.Map:SetIconTypeAlpha (iconType, alpha, alphaNear)

	local d = self.Data
	assert (d[iconType])

	d[iconType].Alpha = alpha
	d[iconType].AlphaNear = alphaNear
end

--------
-- Set at scale for an icon type

function Nx.Map:SetIconTypeAtScale (iconType, scale)

	local d = self.Data
	assert (d[iconType])

	d[iconType].AtScale = scale
end

--------
-- Set level for an icon type

function Nx.Map:SetIconTypeLevel (iconType, level)

	local d = self.Data
	assert (d[iconType])

	d[iconType].Lvl = level
end

--------
-- Set level for an icon type

function Nx.Map:SetIconTypeChop (iconType, on)

	local d = self.Data
	assert (d[iconType])

	d[iconType].ClipFunc = on and self.ClipFrameWChop or self.ClipFrameW
end

--------
-- Add point icon to map data
-- ret: icon

function Nx.Map:AddIconPt (iconType, x, y, color, texture)

	local d = self.Data

	assert (d[iconType])

	local tdata = d[iconType]
	tdata.Num = tdata.Num + 1		-- Use # instead??

	local icon = {}
	tdata[tdata.Num] = icon

	icon.X = x
	icon.Y = y
	icon.Color = color
	icon.Tex = texture

	assert (tdata.Tex or texture or color)

	return icon
end

--------
-- Get icon count

function Nx.Map:GetIconCnt (iconType)

	return #self.Data[iconType]
end

--------
-- Get point icon XY at index (for routing code)

function Nx.Map:GetIconPt (iconType, index)

	local icon = self.Data[iconType][index]
	return icon.X, icon.Y
end

--------
-- Add rectangle icon to map data (zone coords)
-- ret: icon

function Nx.Map:AddIconRect (iconType, mapId, x, y, x2, y2, color)

	local d = self.Data

	assert (d[iconType])

	local tdata = d[iconType]
	tdata.Num = tdata.Num + 1

	local icon = {}
	tdata[tdata.Num] = icon

	icon.MapId = mapId
	icon.X = x
	icon.Y = y
	icon.X2 = x2
	icon.Y2 = y2
	icon.Color = color

	return icon
end

--------
-- Add icon tool tip

function Nx.Map:SetIconTip (icon, tip)
	icon.Tip = tip
end

--------
-- Set user data

function Nx.Map:SetIconUserData (icon, data)
	icon.UData = data
end

--------
-- Set favorite data

function Nx.Map:SetIconFavData (icon, data1, data2)
	icon.FavData1 = data1
	icon.FavData2 = data2
end

--------
-- Set favorite data

function Nx.Map:GetIconFavData (icon)
	return icon.FavData1, icon.FavData2
end

--------
-- Update all icons

function Nx.Map:UpdateIcons (drawNonGuide)

	local c2rgb = Nx.Util_c2rgb
	local c2rgba = Nx.Util_c2rgba
	local d = self.Data

	local wpScale = 1
	local wpMin = self.GOpts["MapIconScaleMin"]
	if wpMin >= 0 then
		wpScale = self.ScaleDraw * .08
	end

	for type, v in pairs (d) do
		v.Enabled = drawNonGuide or strbyte (type) == 33	-- "!" is guide types

		if v.AtScale then
			if self.ScaleDraw < v.AtScale then
				v.Enabled = false
			end
		end
	end

	for k, v in pairs (d) do

--		Nx.prt ("UpdateIcons %s %s", k, v.DrawMode)

		if v.Enabled then

			if v.DrawMode == "ZP" then		-- Zone point

				local scale = self.IconScale
				local w = v.W * scale
				local h = v.H * scale

				for n = 1, v.Num do

					local icon = v[n]
					local f = self:GetIconStatic (v.Lvl)

					if self:ClipFrameZ (f, icon.X, icon.Y, w, h, 0) then

						f.NxTip = icon.Tip

--						assert (icon.Tex or v.Tex or icon.Color)

						if icon.Tex then
							f.texture:SetTexture (icon.Tex)

						elseif v.Tex then
							f.texture:SetTexture (v.Tex)

						else
							f.texture:SetTexture (c2rgb (icon.Color))
						end
					end
				end

			elseif v.DrawMode == "WP" then		-- World point

				local scale = self.IconScale * v.Scale * wpScale
				local w = max (v.W * scale, wpMin)
				local h = max (v.H * scale, wpMin)

				if v.AlphaNear then

					local aNear = v.AlphaNear * (abs (GetTime() % .7 - .35) / .7 + .5)	-- 50% to 100% pulse

					for n = 1, v.Num do

						local icon = v[n]
						local f = self:GetIconStatic (v.Lvl)

						if v.ClipFunc (self, f, icon.X, icon.Y, w, h, 0) then

							f.NxTip = icon.Tip
							f.NXType = 3000
							f.NXData = icon

							if icon.Tex then
								f.texture:SetTexture (icon.Tex)

							elseif v.Tex then
								f.texture:SetTexture (v.Tex)

							else
								f.texture:SetTexture (c2rgb (icon.Color))
							end

							local a = v.Alpha

							local dist = (icon.X - self.PlyrX) ^ 2 + (icon.Y - self.PlyrY) ^ 2
							if dist < 306 then	-- 80 yards * 4.575 ^ 2
								a = aNear
--								Nx.prt ("fade %s %s", dist ^ .5, a)
							end

							f.texture:SetVertexColor (1, 1, 1, a)
						end
					end

				else
					for n = 1, v.Num do

						local icon = v[n]
						local f = self:GetIconStatic (v.Lvl)

						if v.ClipFunc (self, f, icon.X, icon.Y, w, h, 0) then

							f.NxTip = icon.Tip
							f.NXType = 3000
							f.NXData = icon

							if icon.Tex then
								f.texture:SetTexture (icon.Tex)

							elseif v.Tex then
								f.texture:SetTexture (v.Tex)

							else
								f.texture:SetTexture (c2rgb (icon.Color))
							end

							if v.Alpha then
								f.texture:SetVertexColor (1, 1, 1, v.Alpha)
							end
						end
					end
				end

			elseif v.DrawMode == "ZR" then	-- Zone rectangle

				local x, y, x2, y2
--				local x0, y0, x2, y2 = self:GetWorldRect (self.MapId, 0, 0, 100, 100)

				for n = 1, v.Num do

					local icon = v[n]
					local f = self:GetIconStatic (v.Lvl)

--					Nx.prt ("ZR #%d %f %f %f %f", n, icon.X, icon.Y, icon.X2, icon.Y2)

					f.NxTip = icon.Tip

					x, y = self:GetWorldPos (icon.MapId, icon.X, icon.Y)
					x2, y2 = self:GetWorldPos (icon.MapId, icon.X2, icon.Y2)

--					Nx.prt ("%f %f %f %f", x, y, x2, y2)

					if self:ClipFrameTL (f, x, y, x2-x, y2-y) then
						if v.Texture then
							f.texture:SetTexture (v.Tex)
						else
							f.texture:SetTexture (c2rgba (icon.Color))
						end
					end
				end
			end
		end
	end
end

------
-- Reset icons

function Nx.Map:ResetIcons()

	local frms = self.IconFrms
	frms.Used = frms.Next - 1		-- Save last frame used
	frms.Next = 1

	local frms = self.IconNIFrms
	frms.Used = frms.Next - 1		-- Save last frame used
	frms.Next = 1

	local frms = self.IconStaticFrms
	frms.Used = frms.Next - 1		-- Save last frame used
	frms.Next = 1

	local data = self.TextFStrs
	data.Used = data.Next - 1		-- Save last used
	data.Next = 1
end

------
-- Hide extra icons (a hide and then show will reset mouse state, breaking OnMouseUp)

function Nx.Map:HideExtraIcons()

	local frms = self.IconFrms

	for n = frms.Next, frms.Used do		-- Hide up to last frames used amount
		frms[n]:Hide()
	end

	local frms = self.IconNIFrms

	for n = frms.Next, frms.Used do		-- Hide up to last frames used amount
		frms[n]:Hide()
	end

	local frms = self.IconStaticFrms

	for n = frms.Next, frms.Used do		-- Hide up to last frames used amount
		frms[n]:Hide()
	end

	local data = self.TextFStrs

	for n = data.Next, data.Used do		-- Hide up to last used amount
		data[n]:Hide()
	end
end

------
-- Get next available map icon or create one
-- ret: icon frame

function Nx.Map:GetIcon (levelAdd)

	local frms = self.IconFrms
	local pos = frms.Next

	if pos > 1500 then
		pos = 1500	-- Too many used. Reuse
	end

	local f = frms[pos]
	if not f then

		f = CreateFrame ("Frame", "NxIcon"..pos, self.Frm)
		frms[pos] = f
		f.NxMap = self

		f:SetScript ("OnMouseDown", self.IconOnMouseDown)
		f:SetScript ("OnMouseUp", self.IconOnMouseUp)
		f:SetScript ("OnEnter", self.IconOnEnter)
		f:SetScript ("OnLeave", self.IconOnLeave)
		f:SetScript ("OnHide", self.IconOnLeave)

		f:EnableMouse (true)

		local t = f:CreateTexture()
		f.texture = t
		t:SetAllPoints (f)
	end

	f:SetFrameLevel (self.Level + (levelAdd or 0))

	f.texture:SetVertexColor (1, 1, 1, 1)
--	f.texture:SetBlendMode ("BLEND")

	f.NxTip = nil
	f.NXType = nil			-- 1000 plyr, 2000 BG, 3000 POI, 8000 debug, 9000+ quest
	f.NXData = nil
	f.NXData2 = nil

	frms.Next = pos + 1

	return f
end

------
-- Get next available map non interactive icon or create one
-- ret: icon frame

function Nx.Map:GetIconNI (levelAdd)

	local frms = self.IconNIFrms
	local pos = frms.Next

	if pos > 1500 then
		pos = 1500	-- Too many used. Reuse
	end

	local f = frms[pos]
	if not f then

		f = CreateFrame ("Frame", "NxIconNI"..pos, self.Frm)
		frms[pos] = f
		f.NxMap = self

		local t = f:CreateTexture()
		f.texture = t
		t:SetAllPoints (f)
	end

	local add = levelAdd or 0
	f:SetFrameLevel (self.Level + add)

	f.texture:SetVertexColor (1, 1, 1, 1)
	f.texture:SetBlendMode ("BLEND")

	frms.Next = pos + 1

	return f
end

------
-- Get next available map static (for non moving stuff) icon or create one
-- ret: icon frame

function Nx.Map:GetIconStatic (levelAdd)

	local frms = self.IconStaticFrms
	local pos = frms.Next

	if pos > 1500 then
		pos = 1500	-- Too many used. Reuse
	end

	local f = frms[pos]
	if not f then

		f = CreateFrame ("Frame", "NxIconS"..pos, self.Frm)
		frms[pos] = f
		f.NxMap = self

		f:SetScript ("OnMouseDown", self.IconOnMouseDown)
		f:SetScript ("OnMouseUp", self.IconOnMouseUp)
		f:SetScript ("OnEnter", self.IconOnEnter)
		f:SetScript ("OnLeave", self.IconOnLeave)
		f:SetScript ("OnHide", self.IconOnLeave)

		f:EnableMouse (true)

		local t = f:CreateTexture()
		f.texture = t
		t:SetAllPoints (f)
	end

	local add = levelAdd or 0
	f:SetFrameLevel (self.Level + add)

	f.texture:SetVertexColor (1, 1, 1, 1)
--	f.texture:SetBlendMode ("BLEND")

	f.NxTip = nil
	f.NXType = nil			-- 1000 plyr, 2000 BG, 3000 POI, 8000 debug, 9000+ quest
	f.NXData = nil
	f.NXData2 = nil

	frms.Next = pos + 1

	return f
end

--------
-- Handle mouse click on icon

function Nx.Map:IconOnMouseDown (button)

	local this = self			--V4

--	Nx.prt ("MapIconMouseDown "..button.." "..(this:GetName() or "?"))

	local map = this.NxMap
	map:CalcClick()
	map.ClickFrm = this
	map.ClickType = this.NXType
	map.ClickIcon = this.NXData

	local shift = IsShiftKeyDown()

	if button == "LeftButton" then

		local cat = floor ((this.NXType or 0) / 1000)

		if cat == 2 and shift then		-- BG location

			if map.BGIncNum > 0 then

				local _, _, _, str = strsplit ("~", map.BGMsg)
				local _, _, _, str2 = strsplit ("~", this.NXData)

				if str ~= str2 then				-- Different node?
					Nx.Timer:Fire ("BGInc")		-- Clear if pending
				end
			end

			map.BGMsg = this.NXData
			map.BGIncNum = map.BGIncNum + 1

			UIErrorsFrame:AddMessage ("Inc " .. map.BGIncNum, 1, 1, 1, 1)

			Nx.Timer:Start ("BGInc", 1.5, map, map.BGIncSendTimer)

--		elseif cat == 8 then
--			map:HotspotDebugClick (button)

		else
			if map:IsDoubleClick() then
				if cat == 3 then
--					Nx.prt ("Icon dbl click")
					map:GMenu_OnGoto()
				end

			else
				map.OnMouseDown (map.Frm, button)
			end
		end

	else		--if button == "MiddleButton" or button == "Button4" or IsShiftKeyDown() then

		if button == "RightButton" then

			local typ = this.NXType

--			Nx.prt ("Icon type %s", typ or 0)

			if typ then

				local i = floor (typ / 1000)

				if i == 1 then
					map:BuildPlyrLists()
					map.PIconMenu:Open()

				elseif i == 2 then				-- BG location

					Nx.Timer:Fire ("BGInc")		-- Clear if pending

					map.BGMsg = this.NXData
					map.BGIconMenu:Open()

				elseif i == 3 then

					map:GMenuOpen (this.NXData, typ)

				elseif i == 9 then				-- Quest

					Nx.Quest:IconOnMouseDown (this)
				end
			end

		else
			map.OnMouseDown (map.Frm, button)
		end
	end
end

function Nx.Map:BuildPlyrLists()

	local Map = Nx.Map

	Map.PlyrNames = {}
	Map.AFKers = {}
	local tipStr = ""

	local frms = self.IconFrms
	local f

	local cnt = 0

	for n = 1, frms.Next-1 do

		f = frms[n]
		local plyr = f.NXType == 1000 and f.NXData2
		if plyr then

			local x, y = Nx.Util_IsMouseOver (f)
			if x then

--				Nx.prt ("Plyr %s", plyr)

				tinsert (Map.PlyrNames, plyr)

				if f.NXData then
					tinsert (Map.AFKers, f.NXData)
--					Nx.prt ("AFKer %d %s %s", #Nx.Map.AFKers, plyr, f.NXData)
				end
			end
		end
	end

	if #Map.PlyrNames >= 2 then

		tipStr = format ("\n\n|cff00cf00%s players:", #Map.PlyrNames)

		sort (Map.PlyrNames)

		for _, name in ipairs (Map.PlyrNames) do
			tipStr = tipStr .. "\n" .. name
		end
	end

	Map.PlyrNamesTipStr = tipStr

end

function Nx.Map:IconOnMouseUp (button)

--	Nx.prt ("MapIconMouseUp "..button)

	local this = self			--V4
	this.NxMap.OnMouseUp (this.NxMap.Frm, button)
end

--------
-- Handle mouse on icon

function Nx.Map:IconOnEnter (motion)

	local this = self			--V4

--	Nx.prt ("MapIconEnter %s", this:GetName() or "nil")

	local map = this.NxMap

--	map.BackgndAlphaTarget = map.BackgndAlphaFull

	map:BuildPlyrLists()

	if this.NxTip then
--		Nx.prt ("MapIconEnter %s %s", this:GetName() or "nil", this.NxTip)

		local tt = GameTooltip

		local str = strsplit ("~", this.NxTip)

		local owner = this
		local tippos = "ANCHOR_CURSOR"

		local opts = Nx:GetGlobalOpts()
		if opts["MapTopTooltip"] then
			owner = map.Win.Frm
			tippos = "ANCHOR_TOPLEFT"
		end

		owner.NXIconFrm = this

--		Nx.TooltipOwner = owner
--		map.TooltipType = 2

		tt:SetOwner (owner, tippos, 0, 0)

		Nx:SetTooltipText (str .. Nx.Map.PlyrNamesTipStr)

		owner["UpdateTooltip"] = Nx.Map.IconOnUpdateTooltip
	end

	local t = this.NXType or -1

	if t >= 9000 then					-- Quest
		Nx.Quest:IconOnEnter (this)
	end
end

--------
-- Called by tooltip
-- self = frame

function Nx.Map:IconOnUpdateTooltip()

	local f = self.NXIconFrm

	if f and f.NxTip then

		local map = f.NxMap
		map:BuildPlyrLists()

		local str = strsplit ("~", f.NxTip)
		Nx:SetTooltipText (str .. Nx.Map.PlyrNamesTipStr)

		if Nx.Quest.Enabled then
			Nx.Quest:TooltipProcess()
		end

--		Nx.prt ("IconOnUpdateTooltip")
	end
end

--------
-- Handle mouse leaving icon (or icon hiding)

function Nx.Map:IconOnLeave (motion)

	local this = self			--V4
	local t = this.NXType or -1

	if t >= 9000 then					-- Quest
		Nx.Quest:IconOnLeave (this)
	end

--	self.TooltipType = 0

--	if not this:IsVisible() then
--		Nx.prt ("IconOnLeave not vis")
--		return
--	end

--	local map = this.NxMap
--	local owner = map.Win.Frm

	if GameTooltip:IsOwned (this) or GameTooltip:IsOwned (this.NxMap.Win.Frm) then
		GameTooltip:Hide()
--		Nx.prt ("MapIconLeave hide tip")
	end
end

--------
-- Get next available map text or create one
-- ret: text font string

function Nx.Map:GetText (text, levelAdd)

	local data = self.TextFStrs
	local pos = data.Next

	if pos > 100 then
		pos = 1	-- Reset. Too many used
	end

	local fstr = data[pos]
	if not fstr then

		fstr = self.TextFrm:CreateFontString()
		data[pos] = fstr

		fstr:SetFontObject ("NxFontMap")
		fstr:SetJustifyH ("LEFT")
		fstr:SetJustifyV ("TOP")
--		fstr:SetWidth (400)
		fstr:SetHeight (100)
		fstr:SetTextColor (1, 1, 1, 1)
	end

	fstr:SetText (text)

--	local add = levelAdd or 0
--	f:SetFrameLevel (self.Level + add)

	data.Next = pos + 1

	return fstr
end

--------
-- Move text

function Nx.Map:MoveTextToIcon (fstr, icon, ox, oy)

	local f = icon
	local atPt, relTo, relPt, x, y = f:GetPoint()

--	Nx.prt ("Text %s %s %s", relPt, x, y)

	fstr:SetPoint ("TOPLEFT", x + ox, y - oy)
	fstr:Show()
end

--------
-- Update instance map

function Nx.Map:UpdateInstanceMap()

	local mapId = self.InstMapId
	if not mapId then
		return
	end

	local Map = Nx.Map
	local winfo = Map.MapWorldInfo[mapId]
	local info = self.InstMapInfo				-- Valid if Id not nil

--	Nx.prt ("Inst id %s", mapId)

	if self.InstMapAtlas then

		local wx = winfo[2]
		local wy = winfo[3]

		for n = 1, #info, 3 do

			local sc = 668 / 256
			local f = self:GetIconNI()

			if self:ClipFrameTL (f, wx, wy + (n - 1) * 668 / 768, sc, sc) then
				local tex = info[n + 2]
				tex = "Interface\\Addons\\Atlas\\Images\\Maps\\" .. tex
				f.texture:SetTexture (tex)
			end
		end

		self.Level = self.Level + 1

	else

		local wx = winfo[2]
		local wy = winfo[3]

		for n = 1, #info, 3 do

			local imgI = 1

			local offx = 0		-- info[n] * .04 * 1002 / 1024
			local offy = info[n + 1] * .03 * 668 / 768

			for by = 0, 2 do

				for bx = 0, 3 do

					local sc = 1
					local f = self:GetIconNI()

--					Nx.prt ("Inst %s, %s %s %s %s", mapId, wx, wy, bx, by)

					if self:ClipFrameTL (f, wx + bx - offx, wy + by - offy, sc, sc) then
						local tex = info[n + 2]
						tex = "Interface\\WorldMap\\" .. tex .. imgI
						f.texture:SetTexture (tex)
					end

					imgI = imgI + 1
				end
			end
		end

		self.Level = self.Level + 1
	end
end

------------------------------------------
-- Helpers

--------
-- Get a map by index

function Nx.Map:GetMap (mapIndex)
	return self.Maps[mapIndex]
end

--------
-- Set map target name

function Nx.Map:SetTargetName (name)

	local tar = self.Maps[1].Targets[1]
	if tar then
		tar.TargetName = name
	end
end

--------
-- Get map target type, id

function Nx.Map:GetTargetInfo()

	local tar = self.Maps[1].Targets[1]
	if tar then
		return tar.TargetType, tar.TargetId
	end
end

--------
-- Get map target pos

function Nx.Map:GetTargetPos()

	local tar = self.Maps[1].Targets[1]
	if tar then
		return tar.TargetX1, tar.TargetY1, tar.TargetX2, tar.TargetY2
	end
end

--------
-- Center the map in view

function Nx.Map:CurrentCenterMap (mapId)

	local map = self.Maps[1]
	map:CenterMap (mapId)
end

------------------------------------------
-- Map table access

--------
-- Init map tables

function Nx.Map:InitTables()

	local Nx = Nx

--[[
	NxData.NXMapDebugZones1 = { GetMapZones (1) }
	NxData.NXMapDebugZones2 = { GetMapZones (2) }
	NxData.NXMapDebugZones3 = { GetMapZones (3) }
	NxData.NXMapDebugZones4 = { GetMapZones (4) }
	Nx.prt ("zone cap!!!!!")
--]]

	local worldInfo = self.MapWorldInfo

	Nx.MapNameToId = {}
	Nx.MapIdToName = {}
	Nx.MapIdToNxzone = {}
	self.NxzoneToMapId = {}
	Nx.NxzoneToMapId = self.NxzoneToMapId
	Nx.MapOverlayToMapId = {}

	-- Get Blizzard's alphabetical set of names

	--V403

	self.MapNames = {
		{ GetMapZones (1) },
		{ GetMapZones (2) },
		{ GetMapZones (3) },
		{ GetMapZones (4) },
		{ GetMapZones (5) },
		{ GetMapZones (6) },
	}

	tinsert (self.MapNames[2], NXlMapNames["Plaguelands: The Scarlet Enclave"] or "Plaguelands: The Scarlet Enclave")
	tinsert (self.MapNames[2], NXlMapNames["Gilneas"] or "Gilneas")
	tinsert (self.MapNames[2], NXlMapNames["Gilneas City"] or "Gilneas City")

	for mi, mapName in pairs (self.MapNames[2]) do
		for mi2, mapName2 in pairs (self.MapNames[2]) do
			if mapName == mapName2 and mi ~= mi2 then			-- Duplicate name? (Gilneas, Ruins of Gilneas (EU))
				self.MapNames[2][mi2] = mapName .. "2"			-- Hack it!
--				Nx.prt ("Dup zone name %s", mapName)
				break
			end
		end
	end

	local BGNames = {}
	self.MapNames[9] = BGNames

	for n = 1, 999 do
		local winfo = worldInfo[9000 + n]
		if not winfo then
			break
		end

		BGNames[n] = NXlMapNames[winfo.Name] or winfo.Name
	end

--	Nx.Zones[152]="Icecrown: The Forge of Souls!80!80!3!5!128!####!5"


	-- Set overlays

	self.ZoneOverlays["lakewintergrasp"]["lakewintergrasp"] = "0,0,1024,768"

	-- Support maps with multiple levels

	self.MapSubNames = NXlMapSubNames

	tinsert (self.MapNames[4], "Molten Front")
	tinsert (self.MapNames[4], "Dalaran Underbelly")
	tinsert (self.MapNames[5], "The Darkmoon Faire")
    tinsert (self.MapNames[6], "The Wandering Isle")
	-- Setup mapping to and from Blizzard cont/zone to Map Id
	-- and overlay name to map id

	self.ContCnt = 6
	continentNums = { 1, 2, 3, 4, 5, 6, 9 }

	local CZ2Id = {}
	self.CZ2Id = CZ2Id

	for _, ci in ipairs (continentNums) do

		local z2id = {}
		CZ2Id[ci] = z2id

		for n = 1, 999 do

			local mapId = ci * 1000 + n
			local winfo = worldInfo[mapId]
			if not winfo then
				break
			end

			if Nx.PlFactionNum == 0 and winfo.QAchievementIdA then
				winfo.QAchievementId = winfo.QAchievementIdA				-- Copy Ally Id to generic Id
			end
			if Nx.PlFactionNum == 1 and winfo.QAchievementIdH then
				winfo.QAchievementId = winfo.QAchievementIdH				-- Copy Horde Id to generic Id
			end

			local locName = NXlMapNames[winfo.Name] or winfo.Name

			for i, name in ipairs (self.MapNames[ci]) do
				if name == locName then
--					Nx.prt ("%s #%s = %s", name, i, mapId)
					z2id[i] = mapId
					break
				end
			end
		end

		for k, v in ipairs (CZ2Id[ci]) do

			worldInfo[v].Cont = ci
			worldInfo[v].Zone = k

			local ov = worldInfo[v].Overlay
			if ov then
				Nx.MapOverlayToMapId[ov] = v
			end
		end
	end

	for n = 1, self.ContCnt do
		CZ2Id[n][0] = n * 1000
	end

	-- Init for getting map id to and from name

	for _, ci in ipairs (continentNums) do

		for mi, mapName in pairs (self.MapNames[ci]) do

--			if ci == 2 then
--				Nx.prt ("Map %s %s", mapName, self.CZ2Id[ci][mi] or "nil")
--			end
			local mid = self.CZ2Id[ci][mi]

			if Nx.MapNameToId[mapName] then
				Nx.prt ("Dup map name: %s (%s %s)", mapName, ci, mi)
				Nx.MapNameToId[mapName .. "2"] = mid
			else
				Nx.MapNameToId[mapName] = mid
			end

			if not mid then
				Nx.prt ("Unknown map name: %s (%s %s)", mapName, ci, mi)
			else
				Nx.MapIdToName[mid] = mapName
			end
		end
	end

	-- Localize the zone name

	for id, v in ipairs (Nx.Zones) do

		local i = strfind (v, "!")
		local name = strsub (v, 1, i - 1)
		local data = strsub (v, i + 1)

		local locName = NXlMapNames[name]
		if locName then
			Nx.Zones[id] = locName .. "!" .. data
		end
	end

	-- Move MapGenAreas to MapWorldInfo (scale, x, y, overlay)

	for id, area in pairs (Nx.Map.MapGenAreas) do

		local s = Nx.Zones[id]
		local name = strsplit ("!", s)
		local mapId = Nx.MapNameToId[name]

		if not mapId then
			Nx.prt ("Err MapGenAreas %s", name)

		else

			local cont = floor (mapId / 1000)
			if cont <= 2 or cont == 5 then

				local wi = worldInfo[mapId]
				wi[1] = area[1]				-- Scale
				wi[2] = area[2]				-- X
				wi[3] = area[3]				-- Y

				if wi.XOff then	-- Had pos offset?
					wi[2] = wi[2] + wi.XOff	-- X
					wi[3] = wi[3] + wi.YOff	-- Y
					wi.XOff = nil
					wi.YOff = nil
				end

				wi.Overlay = area[4]
			end
		end
	end

	Nx.Map.MapGenAreas = nil

	-- Make world coords for each zone

	for _, ci in ipairs (continentNums) do

		local info = self.MapInfo[ci]
		local cx = info.X
		local cy = info.Y

--		Nx.prt ("WC %s %s %s", ci, cx, cy)

		for n = 0, 999 do
			local winfo = worldInfo[ci * 1000 + n]
			if not winfo then
				break
			end
			winfo[4] = cx + winfo[2]
			winfo[5] = cy + winfo[3]
		end
	end
	--

	for id, v in pairs (Nx.Zones) do

		local name, minLvl, maxLvl, faction, cont, entryId, entryPos = strsplit ("!", v)

		-- Faction:
		-- 0 Alliance
		-- 1 Horde
		-- 2 Contested
		-- 3 Instance
		-- 4 Unknown

		-- Continent:
		-- 1 Kalimdor
		-- 2 EasternKingdoms
		-- 3 Outland
		-- 4 Battleground
		-- 5 Dungeon
		-- 6 Raid					-- OLD
		-- 7 Northrend
		-- 8 The Maelstrom
		-- 9 Unknown

		if faction == "3" and cont == "5" then		-- Instance

			assert (entryId and entryPos)

			if entryId == "0" then
				entryId = "125"
			end

--[[
			local i = strfind (name, ": ")
			if i then
				name = strsub (name, i + 2)
			end
--]]

--			Nx.prt ("Inst %s %d", name, id)

			local entryZone = Nx.Zones[tonumber (entryId)]
			local ename, _, _, _, cont = strsplit ("!", entryZone)
			if cont == "7" then
				cont = 4
			end
			if cont == "8" then
				cont = 5
			end
			local mid = cont * 1000 + 10000 + id

			Nx.MapNameToId[name] = mid
			Nx.MapIdToName[mid] = name

			local emid = Nx.MapNameToId[ename]

			local ex, ey = Nx.Quest:UnpackLocPt (entryPos)

			if self.MapWorldInfo[mid] then			-- Adjustment exists?
				ex = ex + self.MapWorldInfo[mid][2]
				ey = ey + self.MapWorldInfo[mid][3]
			end

--			Nx.prt ("Inst %s %s, %s %s %f %f", name, mid, ename, emid or "nil", ex, ey)

			local x, y = self:GetWorldPos (emid, ex, ey)

--			Nx.prt ("Inst %s %d, %d %f %f", name, mid, emid, x, y)

			local ewinfo = self.MapWorldInfo[emid]
			if not ewinfo then
--				Nx.prt ("? %s %s", ename, emid or "nil")
			end

			local winfo = {}

			winfo.EntryMId = emid
			winfo[1] = 1002 / 25600 --ewinfo[1]		-- Scale
			winfo[2] = x				-- X
			winfo[3] = y				-- Y
			winfo[4] = x				-- X
			winfo[5] = y				-- Y
			self.MapWorldInfo[mid] = winfo
		end
	end
	Nx.prt("debug: ")
	-- Init NxzoneToMapId, MapIdToNxzone

	for id, v in ipairs (Nx.Zones) do

		local name, minLvl, maxLvl, faction = strsplit ("!", v)
--[[
		if id ~= 146 then		-- The Scarlet Enclave needs to keep the :
			local i = strfind (name, ": ")
			if i then
				name = strsub (name, i + 2)
			end
		end
--]]
		local mapId = Nx.MapNameToId[name]

		if mapId then

			if not Nx.MapIdToNxzone[mapId] then
				Nx.MapIdToNxzone[mapId] = id
			else
--				Nx.prt ("Map Init %s %s dup %s", name, id, Nx.MapIdToNxzone[mapId])
			end

			self.NxzoneToMapId[id] = mapId
		else
--			Nx.prt ("Inst %s %d", name, id)
		end
	end

	-- Init AId2Id (Blizzard area id to map id and back)

	Nx.AIdToId = {}
	Nx.IdToAId = {}

	for aid, zid in pairs (Nx.ID2Zone) do
		if zid ~= 0 then
			local id = self.NxzoneToMapId[zid]
			Nx.AIdToId[aid] = id
			if id then
				Nx.IdToAId[id] = aid
			end
--			if not id then
--				Nx.prt ("AId %s (%s) = %s", aid, zid, id or "nil")
--			end

		end
	end

	-- Test
--[[

	Nx.prt ("Test Map Id")

	for k, v in ipairs (Nx.Zones) do

		if k ~= 16 and k ~= 34 and k ~= 102 then
			local mapId = self.NxzoneToMapId[k]
			assert (Nx.MapIdToNxzone[mapId] == k)
		end
	end
--]]

	-- Init instance entries

	for k, v in ipairs (Nx.Zones) do

		local name, minLvl, maxLvl, faction, cont, entryId = strsplit ("!", v)

		if faction ~= "3" then		-- Not instance

			if entryId and entryId ~= "" then
				self.NxzoneToMapId[k] = self.NxzoneToMapId[tonumber (entryId)]
			end
		end
	end

	--	DEBUG for Jamie

	Nx.ZoneConnections = Nx["ZoneConnections"] or Nx.ZoneConnections	-- Copy unmunged data to munged data

	-- Init zone connections

	for ci = 1, self.ContCnt do
		for n = 0, 999 do

			local mapId = ci * 1000 + n
			local winfo = worldInfo[mapId]
			if not winfo then
				break
			end
			local cons = {}
			winfo.Connections = cons

			for _, str in ipairs (Nx.ZoneConnections) do

				local flags, ta, tb, z1, x1a, x1b, y1a, y1b, z2, x2a, x2b, y2a, y2b, name1len = strbyte (str, 1, 14)

				flags = flags - 35
				local conTime = (ta - 35) * 221 + tb - 35
				local mapId1 = self.NxzoneToMapId[z1 - 35]
				local mapId2 = self.NxzoneToMapId[z2 - 35]
--[[
				if mapId1 == 1017 or mapId2 == 1017 or mapId1 == 1028 or mapId2 == 1028 then
					Nx.prt ("%s %d %d to %s %d %d, %s f%x",
							Nx.MapIdToName[mapId1] or mapId1, ((x1a - 35) * 221 + x1b - 35) / 100, ((y1a - 35) * 221 + y1b - 35) / 100,
							Nx.MapIdToName[mapId2] or mapId2, ((x2a - 35) * 221 + x2b - 35) / 100, ((y2a - 35) * 221 + y2b - 35) / 100,
							conTime, flags)
				end
--]]
				if not (mapId1 and mapId2) then
--					Nx.prt ("zone conn err %s to %s", z1 - 35, z2 - 35)
					conTime = 0
				end

				if conTime == 1 and (mapId == mapId1 or (mapId == mapId2 and bit.band (flags, 1) == 1)) then

					local cont1 = self:IdToContZone (mapId1)
					local cont2 = self:IdToContZone (mapId2)

					if cont1 == cont2 then

						name1len = name1len - 35
						local name1 = name1len == 0 and "" or strsub (str, 15, 14 + name1len)
						local i = 15 + name1len
						local name2len = strbyte (str, i)
						local name2 = name2len == 0 and "" or strsub (str, i + 1, i + name2len)

						local x1 = ((x1a - 35) * 221 + x1b - 35) / 100
						local y1 = ((y1a - 35) * 221 + y1b - 35) / 100
						local x2 = ((x2a - 35) * 221 + x2b - 35) / 100
						local y2 = ((y2a - 35) * 221 + y2b - 35) / 100
--[[
						if mapId1 == 1017 or mapId2 == 1017 or mapId1 == 1028 or mapId2 == 1028 then
							Nx.prt ("%s %d %d to %s %d %d, %s, %s %s",
									Nx.MapIdToName[mapId1] or mapId1, x1, y1,
									Nx.MapIdToName[mapId2] or mapId2, x2, y2,
									conTime, name1, name2)
						end
--]]
						if mapId == mapId2 then		-- Swap?
							mapId1, mapId2 = mapId2, mapId1
							x1, y1, x2, y2 = x2, y2, x1, y1
						end

						local zcons = cons[mapId2] or {}
						cons[mapId2] = zcons

						if x1 ~= 0 and y1 ~= 0 then	-- Specific connection? Else connects anywhere

							local con = {}
							tinsert (zcons, con)

							x1, y1 = self:GetWorldPos (mapId1, x1, y1)
							x2, y2 = self:GetWorldPos (mapId2, x2, y2)

							con.StartMapId = mapId1
							con.StartX = x1
							con.StartY = y1
							con.EndMapId = mapId2
							con.EndX = x2
							con.EndY = y2
							con.Dist = ((x1 - x2) ^ 2 + (y1 - y2) ^ 2) ^ .5
						end

--					else
--						Nx.prt ("%s to %s", mapId1, mapId2)
					end
				end
			end
		end
	end
end

--------
-- Convert raw cont and zone to mapid
-- self: not used

function Nx.Map:ConnectionUnpack (str)

--[[
	Format is (base 221 packed):

	1 byte flags Bit 0 = two-way, Bit 1 = Alliance, Bit 2 = Horde
	2 byte time,  0 = Instant (Portal)  1 = Normal connection  >1 = Some type of boat/zeppelin
	1 byte zone 1
	2 byte zone 1 location
	1 byte zone 2
	2 byte zone 2 location
	1 byte name 1 length
	name 1
	1 byte name 2 length
	name 2

	To choose icons to show you could search the string to work out what it is.
	Boat, Tram, Zeppelin, Portal
--]]

	local flags, ta, tb, z1, x1a, x1b, y1a, y1b, z2, x2a, x2b, y2a, y2b, name1len = strbyte (str, 1, 14)

	flags = flags - 35
	local conTime = (ta - 35) * 221 + tb - 35
	local mapId1 = self.NxzoneToMapId[z1 - 35]
	local mapId2 = self.NxzoneToMapId[z2 - 35]

--	local cont1 = self:IdToContZone (mapId1)
--	local cont2 = self:IdToContZone (mapId2)

	name1len = name1len - 35
	local name1 = name1len == 0 and "" or strsub (str, 15, 14 + name1len)
	local i = 15 + name1len
	local name2len = strbyte (str, i)
	local name2 = name2len == 0 and "" or strsub (str, i + 1, i + name2len)

	local x1 = ((x1a - 35) * 221 + x1b - 35) / 100
	local y1 = ((y1a - 35) * 221 + y1b - 35) / 100
	local x2 = ((x2a - 35) * 221 + x2b - 35) / 100
	local y2 = ((y2a - 35) * 221 + y2b - 35) / 100

	return flags, conTime, mapId1, x1, y1, mapId2, x2, y2, name1, name2
end

--------
-- Convert raw cont and zone to mapid
-- self: not used

function Nx.Map:CZ2MapId (cont, zone)

	if cont <= 0 then
		return 9000
	end

	return self.CZ2Id[cont][zone]
end

--------
-- Get the real player location map id without map level calculation

function Nx.Map:GetRealBaseMapId()
	return Nx.MapNameToId[GetRealZoneText()] or 9000
end

--------
-- Get the real player location map id

function Nx.Map:GetRealMapId()

--	Nx.prtCtrl ("RealMId %s %s #%s", GetRealZoneText(), GetSubZoneText(), GetCurrentMapDungeonLevel())

	local zName = GetRealZoneText()
	local mapId = Nx.MapNameToId[zName] or 9000
	local name, instanceType, difficultyIndex, difficultyName, maxPlayers, dynamicDifficulty, isDynamic, mapID = GetInstanceInfo()
	if (difficultyIndex == 1) then
		local aid=GetCurrentMapAreaID()
		local id=Nx.AIdToId[aid]
		return id
	end
	local subT = self.MapSubNames[zName]	-- Find subzone name

	if subT then
		if subT[GetSubZoneText()] then
			return self.MapWorldInfo[mapId].Level2Id or mapId
		end
	end

--	if GetCurrentMapDungeonLevel() > 1 then
--		return self.MapWorldInfo[mapId].Level2Id or mapId
--	end

	return mapId
end

--------
-- Get the current selected map id
-- Do not call SetMapToCurrentZone() here or crash

function Nx.Map:GetCurrentMapId()

	--V403

	local cont = GetCurrentMapContinent()
	local zone = GetCurrentMapZone()

	if cont <= 0 or cont >= 6 then

		local aid = GetCurrentMapAreaID()

--		Nx.prtCtrl ("GetCurrentMapId %s, %s+%s", aid, cont, zone)

--		if cont == -1 and (self.MapId or 0) > 11000 then
--			return self.MapId
--		end

		local id = Nx.AIdToId[aid]
		if id then
--			Nx.prt ("GetCurrentMapId from aid %s", id)
			return id
		end

		return self:GetRealMapId()
	end

--[[
	if not self.CZ2Id[cont] then
		Nx.prt ("cont %s", cont)
	end

	if not self.CZ2Id[cont][zone] then
		Nx.prt ("%s %s", cont, zone)
	end
--]]

	local mapId = self.CZ2Id[cont][zone] or 9000

	if mapId == Nx.MapNameToId[GetRealZoneText()] then		-- Same as real zone?
		return self:GetRealMapId()
	end

	return mapId
end

--------
-- Set the current using a map id

function Nx.Map:SetCurrentMap (mapId)

	if mapId then

--		Nx.prt ("SetMapToCurrentZone %s", mapId)

		self.BaseScale = 1

		if mapId > 1000 and mapId < 7000 then

			local cont = self.MapWorldInfo[mapId].Cont
			local zone = self.MapWorldInfo[mapId].Zone

			if not cont or not zone or mapId == self:GetRealBaseMapId() or mapId == self:GetRealMapId() then

--				Nx.prt ("SetMapToCurrentZone %s", mapId)

				SetMapToCurrentZone()		-- This fixes the Scarlet Enclave map selection, so we get player position
				SetDungeonMapLevel (1)

			else

				if self.MapWorldInfo[mapId].UseAId then
					SetMapByID (Nx.IdToAId[mapId])
				else
					SetMapZoom (cont, zone)
				end
			end

--			Nx.prt ("CurLvl %s", GetCurrentMapDungeonLevel())

--[[
			local lvl = self.MapWorldInfo[mapId].MapLevel
			if lvl then
				Nx.prt ("SetDLvl %s", lvl)
				SetDungeonMapLevel (lvl)
			end
--]]
		elseif mapId > 11000 then	-- Instance?

			self.BaseScale = .025

			local aid = Nx.IdToAId[mapId]

--			Nx.prtCtrl ("SetCurrentMap %s %s", mapId, aid or "nil")

			if aid then
				self.MapId = 0				-- Force change (needed?)

				if mapId == self:GetRealBaseMapId() then
					SetMapToCurrentZone()

				else
					local caid = GetCurrentMapAreaID()

					if caid ~= aid then
--						Nx.prt ("SetCurrentMap dif %s", caid)
						SetMapByID (aid)
						SetDungeonMapLevel (1)
					end
				end


			else
				if mapId == self:GetRealBaseMapId() then

					self.MapId = 0				-- Force change
					SetMapToCurrentZone()
				else
					self.MapId = mapId
					SetMapZoom (-1)			-- Cosmic map. Has no POIs
				end
			end
		end

		self.InstLevelSet = GetCurrentMapDungeonLevel()
	end
end

--------
-- Set the map to current zone

function Nx.Map:SetToCurrentZone()

	SetMapToCurrentZone()

	local aid = GetCurrentMapAreaID()
	local id = Nx.AIdToId[aid]

	if id == 1014 then					-- Orgrimmar
		SetDungeonMapLevel (1)			-- Don't allow cleft of shadows
	end

--	Nx.prt ("SetToCurrentZone %s %s", aid, id or "nil")
end

--------
-- Save map view

function Nx.Map:SaveView (name)

	local str = format ("%s%s", Nx.InBG or "", name)

--	Nx.prt ("Save view %s", str)

	local v = self.ViewSavedData[str]

	if not v then
		v = {}
		self.ViewSavedData[str] = v
	end

	v.Scale = self.Scale
	v.X = self.MapPosX
	v.Y = self.MapPosY
end

--------
-- Restore map view

function Nx.Map:RestoreView (name)

	local str = format ("%s%s", Nx.InBG or "", name)

--	Nx.prt ("Restore view %s", str)

	local v = self.ViewSavedData[str]

	if v then
		self.Scale = v.Scale
		self.MapPosX = v.X
		self.MapPosY = v.Y

		self.StepTime = 5
	end
end

--------
-- Move map

function Nx.Map:Move (x, y, scale, stepTime)

--	Nx.prt ("Move %s %s sc %s time %s", x, y, scale or "nil", stepTime)

	self.MapPosX = x
	self.MapPosY = y

	if scale then
		self.Scale = scale
	end

	local dist = ((self.MapPosXDraw - self.MapPosX) ^ 2 + (self.MapPosYDraw - self.MapPosY) ^ 2) ^ .5

	local sz = max (self.MapW, self.MapH)

--	Nx.prt ("Move dist %f %f val %f", sz, dist, dist * self.Scale / sz)

	if dist * self.Scale / sz > 10 then	-- Zoomed in and a large jump?
		stepTime = 1	-- Go fast
	end

--	Nx.prt ("Move #%d %f steptime %f ", self.Tick, dist, self.StepTime)

	local st = abs (self.StepTime)	-- Will be neg if triggered

	if st > 0 and st < stepTime then		-- Already stepping? Use short time
		stepTime = st
--		Nx.prt ("Move steptime %f", stepTime)
	end

	self.StepTime = stepTime

	if dist < .25 then

--		Nx.prt ("Move snap XY")

		self.MapPosXDraw = self.MapPosX
		self.MapPosYDraw = self.MapPosY
	end

	if abs (1 / self.ScaleDraw - 1 / self.Scale) < .01 then
		self.ScaleDraw = self.Scale

--		Nx.prt ("Move snap scale")

		if dist < .25 then
			self.StepTime = 0
		end
	end

--[[
	if self.Debug then
		local plZX, plZY = GetPlayerMapPosition ("player")
		Nx.prt ("Move %f %f (%f %f)", x, y, plZX, plZY)
	end
--]]
end

--------
-- Goto current zone and center map

function Nx.Map:GotoCurrentZone()

--	Nx.prt ("GotoCurrentZone")

	if self.InstanceId then
		self:Move (self.PlyrX, self.PlyrY, 20, 30)
	else

		self:SetToCurrentZone()
		local mapId = self:GetCurrentMapId()
		self:CenterMap (mapId)
	end
end

--------
-- Goto current zone and cause player moved update

function Nx.Map:GotoPlayer()

--	Nx.prt ("GotoPlayer")

	self:CalcTracking()

	self:SetToCurrentZone()

	self.MoveLastX = -1
	self.MoveLastY = -1
end

--------
-- Center the map in view

function Nx.Map:CenterMap (mapId, scale)

	mapId = mapId or self.MapId

--[[ -- Map capture
	if 1 then
		self:CenterMap1To1 (floor (mapId / 1000) * 1000)
		return
	end
--]]

	if self:GetWorldZone (mapId).City then
		scale = 1
	end

	self.MapW = self.Frm:GetWidth() - self.PadX * 2
	self.MapH = self.Frm:GetHeight() - self.TitleH

	local x, y = self:GetWorldPos (mapId, 50, 50)
	local size = min (self.MapW / 1002, self.MapH / 668)
	if self.MapW < GetScreenWidth() / 2 then
		size = size * (scale or 1.5)
	end

	local scale = size / self:GetWorldZoneScale (mapId) * 10.02

	self:Move (x, y, scale, 30)

--	Nx.prt ("Center #%d %f (%f %f) (%d %d)", mapId, self.Scale, self.MapPosX, self.MapPosY, self.MapW, self.MapH)
end

--------
-- Center the map in view and 1 to 1 scale

function Nx.Map:CenterMap1To1 (mapId)

	self.MapPosX, self.MapPosY = self:GetWorldPos (mapId, 50, 50)

	self.Scale = 1002 / 100 / self:GetWorldZoneScale (mapId) * GetScreenWidth() / 1680 * 2
	self.ScaleDraw = self.Scale
	self.StepTime = 10
end

--------
-- Get a cont zone from the map id

function Nx.Map:IdToContZone (mapId)

	if mapId >= 10000 then
		return floor (mapId / 1000) - 10, 0
	end

	local info = self.MapWorldInfo[mapId]

--	if not info then
--		Nx.prt ("IdToContZone info nil %s", mapId)
--	end

	return info.Cont or 9, info.Zone or 0
end

--------
-- Get map name from id

function Nx.Map:IdToName (mapId)

--	Nx.prt ("MapId "..mapId)

	return Nx.MapIdToName[mapId] or "?"
end

--------
-- Get map id from name

function Nx.Map:NameToId (mapName)
	return Nx.MapNameToId[mapName]
end

--------
--

function Nx.Map:IsNormalMap (mapId)
	return mapId > 1000 and mapId % 1000 > 0 and mapId < 7000
end

--------
--

function Nx.Map:IsOutlandMap (mapId)
	return mapId >= 3000 and mapId <= 3999
end

--------
--

function Nx.Map:IsInstanceMap (mapId)
	return mapId >= 10000
end

--------
--

function Nx.Map:IsBattleGroundMap (mapId)
	return mapId >= 9001 and mapId <= 9099
end

--------
-- Get map short name (only BGs have)

function Nx.Map:GetShortName (mapId)
	return Nx.Map.MapWorldInfo[mapId].Short
end

--------
-- Get world info for a continent
-- (cont #)

function Nx.Map:GetWorldContinentInfo (cont)

	local info = self.MapInfo[cont]
	if not info then
		return
	end

	return info.Name, info.X, info.Y
end

--------
-- Get world info for a continent and zone
-- (cont #, zone #)

function Nx.Map:GetWorldZoneInfo (cont, zone)

	local nt = self.MapNames[cont] or self.MapNames[5]
	local name = nt[zone] or "?"

	local info = self.MapInfo[cont]
	if not info then
		return name, 0, 0, 1002, 668
	end

	local id = self.CZ2Id[cont][zone]

	local winfo = self.MapWorldInfo[id]
	if not winfo then
		return
	end

	local x = info.X + winfo[2]
	local y = info.Y + winfo[3]
	local scale = winfo[1] * 100

	return name, x, y, scale, scale / 1.5		-- x, y, w, h
end

--------
-- Get world zone from map id
-- (id)

function Nx.Map:GetWorldZone (mapId)
	return self.MapWorldInfo[mapId]
end

--------
-- Get world zone scale from map id
-- (id)

function Nx.Map:GetWorldZoneScale (mapId)

--	self.GetWorldZoneScaleCnt = (self.GetWorldZoneScaleCnt or 0) + 1

--	if not self.MapWorldInfo[mapId] then
--		Nx.prt ("GetWorldZoneScale %s %s %s", mapId)
--	end

--	return (not self.MapWorldInfo[mapId] and 10.02) or self.MapWorldInfo[mapId][1]
	return self.MapWorldInfo[mapId][1]
end

--------
-- Get world position of map (zone) location
-- (id, x (0-100), y (0-100)

function Nx.Map:GetWorldPos (mapId, mapX, mapY)

--	self.GetWorldPosCnt = (self.GetWorldPosCnt or 0) + 1

--[[
	local info = self.MapInfo[floor (mapId / 1000)]
	if not info then

		if IsControlKeyDown() then
			Nx.prt ("GetWorldPos inst %s %s %s", mapId, mapX, mapY)
--			Nx.prt (" %s", debugstack (2, 3, 0))
		end

--		info = self.MapInfo[0]
		mapX = 0
		mapY = 0
	end
--]]

	local winfo = self.MapWorldInfo[mapId]
	if winfo then

		local scale = winfo[1]

		return	winfo[4] + mapX * scale,
					winfo[5] + mapY * scale / 1.5

--		if mapId == 11050 then
--			Nx.prt ("%s %s %s %s", info.Y, winfo[3], mapY, scale)
--		end

--		return	info.X + winfo[2] + mapX * scale,
--					info.Y + winfo[3] + mapY / 1.5 * scale
	end

	return 0, 0
end

--------
-- Get world positions of map (zone) rectangle
-- (id, x, y)

function Nx.Map:GetWorldRect (mapId, mapX, mapY, mapX2, mapY2)

	local x, y = self:GetWorldPos (mapId, mapX, mapY)
	local x2, y2 = self:GetWorldPos (mapId, mapX2, mapY2)

	return x, y, x2, y2
end

--------
-- Get zone position of world location
-- (id, x, y)

function Nx.Map:GetZonePos (mapId, worldX, worldY)

--	self.GetZonePosCnt = (self.GetZonePosCnt or 0) + 1

--	Nx.prt ("WXY %f %f", worldX, worldY)

	local winfo = self.MapWorldInfo[mapId]
	if winfo then

		local scale = winfo[1]

		return	(worldX - winfo[4]) / scale,
					(worldY - winfo[5]) / scale * 1.5

--		local x = (worldX - info.X - winfo[2]) / scale
--		local y = (worldY - info.Y - winfo[3]) / scale * 1.5

--		Nx.prt ("XY %f %f %f", x, y, scale)

--		return x, y
	end

	return 0, 0
end

--------
-- Get continent of world location
-- (wx, wy)

--[[
function Nx.Map:GetContFromPos (worldX, worldY)

	if worldY < -2050 then
		return 3
	end

	if worldX > 2200 then
		return 2
	end

	return 1
end
--]]

--------
-- Convert frame (top left) to zone positions
--[[
function Nx.Map:FramePosToZonePos (x, y)
	x = self.ZonePosX + (x - self.PadX - self.MapW / 2) / 10.02 / self.Scale
	y = self.ZonePosY + (y - self.TitleH - self.MapH / 2) / 6.68 / self.Scale
	return x, y
end
--]]

--------
-- Convert frame (top left) to world positions

function Nx.Map:FramePosToWorldPos (x, y)
	x = self.MapPosX + (x - self.PadX - self.MapW / 2) / 10.02 / self.MapScale
	y = self.MapPosY + (y - self.TitleH - self.MapH / 2) / 10.02 / self.MapScale
	return x, y
end

--------
-- Set map target
-- (type string, x, y, x2, y2, texture (nil for default, false for none), user id, name)

function Nx.Map:SetTarget (typ, x1, y1, x2, y2, tex, id, name, keep, mapId)

	self.UpdateTrackingDelay = 0

	local sbt = self.ScaleBeforeTarget

--	if self.ScaleBeforeTarget then
--		self.Scale = self.ScaleBeforeTarget
		self.ScaleBeforeTarget = false
--	end

	if not keep then
		self:ClearTargets()
	end

	self.ScaleBeforeTarget = sbt or not next (self.Targets) and self.GOpts["MapRestoreScaleAfterTrack"] and self.Scale

	local tar = {}
	tinsert (self.Targets, tar)

	assert (x1)

	tar.TargetType = typ
	tar.TargetX1 = x1
	tar.TargetY1 = y1
	tar.TargetX2 = x2
	tar.TargetY2 = y2
	tar.TargetMX = (x1 + x2) * .5		-- Mid point
	tar.TargetMY = (y1 + y2) * .5
	tar.TargetTex = tex
	tar.TargetId = id
	tar.TargetName = name
--	tar.ArrowPulse = 1

	mapId = mapId or self.MapId
	tar.MapId = mapId

	local i = self.TargetNextUniqueId
	tar.UniqueId = i
	self.TargetNextUniqueId = i + 1

	local typ = keep and "Target" or "TargetS"
	local zx, zy = self:GetZonePos (mapId, tar.TargetMX, tar.TargetMY)

	Nx.Fav:Record (typ, name, mapId, zx, zy)

	return tar
end

--------
-- Clear all targets
-- (matchType we will clear or nil for any)

function Nx.Map:ClearTargets (matchType)

	if matchType then
		local tar = self.Targets[1]
		if tar then
			if tar.TargetType ~= matchType then
				return
			end
		end
	end

	self.Targets = {}
	self.Tracking = {}

	if self.LOpts.NXAutoScaleOn and self.ScaleBeforeTarget then

--		Nx.prt ("ScaleBeforeTarget trigger %s", matchType or "nil")
--		self.Scale = self.ScaleBeforeTarget

		self:GotoPlayer()		-- Map won't move if cursor on it
		self:Move (self.PlyrX, self.PlyrY, self.ScaleBeforeTarget, 60)
	end

	self.ScaleBeforeTarget = false
end

--------

function Nx.Map:ClearTarget (uniqueId)

	self.Tracking = {}

	local tar, i = self:FindTarget (uniqueId)

	if tar then
--		Nx.prt ("ClearTarget %s %s", uniqueId, i)
		tremove (self.Targets, i)
	end

end

function Nx.Map:FindTarget (uniqueId)

	for n, tar in ipairs (self.Targets) do
		if tar.UniqueId == uniqueId then
			return tar, n
		end
	end
end

--------
-- Changes indexed or last target to a new index

function Nx.Map:ChangeTargetOrder (srcI, dstI)

	srcI = srcI >= 0 and srcI or #self.Targets	-- -1 for last target

	local t = tremove (self.Targets, srcI)
	tinsert (self.Targets, dstI, t)

	self.Tracking = {}
end

--------
-- Reverse order of all targets

function Nx.Map:ReverseTargets()

	local tar = self.Targets
	local n2 = #tar

	for n = 1, n2 / 2 do
		local a = tar[n]
		tar[n] = tar[n2]
		tar[n2] = a
		n2 = n2 - 1
	end

	self.Tracking = {}
end

--------
-- Set map target at zone xy (pos 0-100)
-- Ret target table

function Nx.Map:SetTargetXY (mid, zx, zy, name, keep)

	Nx.Quest.Watch:ClearAutoTarget()

	local wx, wy = self:GetWorldPos (mid, zx, zy)
	return self:SetTarget ("Goto", wx, wy, wx, wy, nil, nil, name or "", keep, mid)
end

--------
-- Set map target at mouse click

function Nx.Map:SetTargetAtClick()

	Nx.Quest.Watch:ClearAutoTarget()

	local wx, wy = self:FramePosToWorldPos (self.ClickFrmX, self.ClickFrmY)
	local zx, zy = self:GetZonePos (self.MapId, wx, wy)
	local str = format ("Goto %.0f, %.0f", zx, zy)

	self:SetTarget ("Goto", wx, wy, wx, wy, nil, nil, str, IsShiftKeyDown())
end

function Nx.Map:SetTargetAtStr (str, keep)

	local mId, zx, zy = self:ParseTargetStr (str)
	if mId then
		local wx, wy = self:GetWorldPos (mId, zx, zy)
		local str = format ("%.0f, %.0f", zx, zy)
		self:SetTarget ("Goto", wx, wy, wx, wy, nil, nil, str, keep, mId)
	end
end

--------
-- Parse map target string. "[zone] x y"
-- (string)

function Nx.Map:ParseTargetStr (str)

--	Nx.prt (str)

	local str = gsub (strlower (str), ",", " ")

	local zone
	local zx, zy

	for s in gmatch (str, "%S+") do
		local i = tonumber (s)
		if i then
			if zx then
				zy = zy or i
			else
				zx = i
			end
		else
			if zone then
				zone = zone .. " " .. s
			else
				zone = s
			end
		end
	end

	local mid = self.RMapId

	if zone then

		mid = nil

		for name, id in pairs (Nx.MapNameToId) do
			if strfind (strlower (name), zone, 1, true) then
				mid = id
--				Nx.prt (" %s", name)
				break
			end
		end

		if not mid then
			Nx.prt ("zone %s not found", zone)
			return
		end
	end

	if not zx or not zy then
		Nx.prt ("zone coordinate error")
		return
	end

	return mid, zx, zy
end

----------------
-- External functions (for TomTom and Cartographer emulation)

--------
-- Used for id = TomTom:AddWaypoint (x, y, desc, persistent, minimap, world, silent)
-- self is bad if called by TomTom

function Nx:TTAddWaypoint (zx, zy, name)

	local map = Nx.Map:GetMap (1)
	local mid = map:GetCurrentMapId()
	local tar = map:SetTargetXY (mid, zx, zy, name, true)

	map:ChangeTargetOrder (-1, 1)

	return tar.UniqueId
end

--------
-- Used for id = TomTom:AddZWaypoint (c, z, x, y, desc, persistent, minimap, world, callbacks, silent, crazy)

function Nx:TTAddZWaypoint (cont, zone, zx, zy, name, _persist, _minimap, _world, callbackT)

	local map = Nx.Map:GetMap (1)
	local mid = map:GetCurrentMapId()

	if cont and zone then
		mid = map:CZ2MapId (cont, zone)
	end

	return Nx:TTSetTarget (mid, zx, zy, name, callbackT)
end

--------
--	Used for id = TomTom:SetCustomWaypoint (c, z, x, y, callback, minimap, world, silent)

function Nx:TTSetCustomWaypoint (cont, zone, zx, zy, callbackT)

	return Nx:TTAddZWaypoint (cont, zone, zx, zy, "", false, nil, nil, callbackT)
end

--------
--	Used for id = TomTom:SetCustomMFWaypoint (m, f, x, y, opts)

function Nx:TTSetCustomMFWaypoint (aid, _floor, zx, zy, opts)

	zx = zx * 100
	zy = zy * 100

	return Nx:TTSetTarget (Nx.AIdToId[aid], zx, zy, opts["title"], opts["callbacks"])
end


function Nx:TTSetTarget (mid, zx, zy, name, callbackT)

--	Nx.prt ("TTSetTarget %s %s %s %s", name or "nil", mid or "nil", zx, zy)

	local map = Nx.Map:GetMap (1)
	local tar = map:SetTargetXY (mid, zx, zy, name, true)

	map:ChangeTargetOrder (-1, 1)

	if callbackT and callbackT["distance"] then

		local d = 99999
		local f

		for dist, func in pairs (callbackT["distance"]) do
			if dist < d then
				d = dist
				f = func
			end
		end

		tar.Radius = d
		tar.RadiusFunc = f

--		Nx.prt (" R %s", d)
	end

	return tar.UniqueId
end

--------
-- Used for TomTom:RemoveWaypoint (uid)

function Nx:TTRemoveWaypoint (id)

	local map = Nx.Map:GetMap (1)
	map:ClearTarget (id)
end

--------
-- Used for TomTom:SetCrazyArrow (uid, dist, title)

function Nx:TTSetCrazyArrow (id, dist, str)

	local map = Nx.Map:GetMap (1)
	local tar = map:FindTarget (id)
	if tar then
		tar.Radius = dist
		tar.TargetName = str
	end
end

--------
-- Cartographer emulation

function Nx:CartGetDistanceToPoint (zx, zy, mapName)

	local map = Nx.Map:GetMap (1)
	local mapId = Nx.MapNameToId[mapName]
	if not mapId then
		return 1000
	end

	local wx, wy = map:GetWorldPos (mapId, zx * 100, zy * 100)
	local x = wx - map.PlyrX
	local y = wy - map.PlyrY
	local distYd = (x * x + y * y) ^ .5 * 4.575

	return distYd
end

function Nx:CartSetPointAsWaypoint (zx, zy)

	Nx:TTAddZWaypoint (nil, nil, zx * 100, zy * 100, "Waypoint")

--	local map = Nx.Map:GetMap (1)
--	map:ChangeTargetOrder (-1, 1)
end

function Nx:CartAddLHWaypoint (cont, zone, zx, zy, name)

	Nx:TTAddZWaypoint (cont, zone, zx, zy, name)
end

function Nx:CartAddRoutesWaypoint (mapName, yx, name)

--	Nx.prt ("AddRoutesWaypoint %s %s", mapName, name or "nil")

	local map = Nx.Map:GetMap (1)
	local mapId = Nx.MapNameToId[mapName]
	local cont, zone
	if mapId then
		cont, zone = map:IdToContZone (mapId)
	end

	local zx = (yx % 10001) / 100
	local zy = floor (yx / 10001) / 100

	Nx:TTAddZWaypoint (cont, zone, zx, zy, name)

--	map:ChangeTargetOrder (-1, 1)
end

function Nx:CartAddWaypoint (note)

	local map = Nx.Map:GetMap (1)
	local mapId = Nx.MapNameToId[note["z"]]
	local cont, zone
	if mapId then
		cont, zone = map:IdToContZone (mapId)
	end

	local id = Nx:TTAddZWaypoint (cont, zone, note["x"] * 100, note["y"] * 100, note["n"])

	note["WaypointID"] = id

--	map:ChangeTargetOrder (-1, 1)
end

function Nx:CartCancelWaypoint (id)

	local map = Nx.Map:GetMap (1)
	map:ClearTargets()						-- Just clear all, since id can be some weird junk
end

function Nx:CartSetWaypointHitDistance (dist)

--	Nx.prt ("SetWaypointHitDistance %s", dist)

	local map = Nx.Map:GetMap (1)

	if map.Targets[1] then
		map.Targets[1].Radius = dist
	end
end

function Nx:CartNotePointNew (zone, x, y, name)

	local note = {}

	if not y then
		y = floor (x / 10001) / 100
		x = (x % 10001) / 100
	end

--	note["WaypointID"] = 0
	note["z"] = zone
	note["x"] = x
	note["y"] = y
	note["n"] = name or ""

	return note
end

--------
-- For RareSeeker and others

function Nx.MapMinimapOwned()
	local map = Nx.Map:GetMap (1)
	return map.MMOwn
end

function Nx.MapInitIconType (iconType, drawMode)

	local map = Nx.Map:GetMap (1)
	map:InitIconType (iconType, drawMode)
end

function Nx.MapAddIconPoint (iconType, mapName, x, y, texture)

	local map = Nx.Map:GetMap (1)
	local mapId = Nx.MapNameToId[mapName]

	if mapId then
		local wx, wy = map:GetWorldPos (mapId, x, y)
		map:AddIconPt (iconType, wx, wy, nil, texture)
	end
end

function Nx.MapAddIconRect (iconType, mapName, x, y, x2, y2, color)

	local map = Nx.Map:GetMap (1)
	local mapId = Nx.MapNameToId[mapName]

	if mapId then
		map:AddIconRect (iconType, mapId, x, y, x2, y2, color)
	end
end

function Nx.MapSetIconTip (icon, tip)

	local map = Nx.Map:GetMap (1)
	map:SetIconTip (icon, tip)
end

--------

function Nx.NXMapKeyTargetSkip()

	local self = Nx.Map:GetMap (1)

	local tar = self.Targets[1]
	if tar then
		tar.Radius = 999999999999
	end
end

-------------------------------------------------------------------------------
--

function Nx.Map.Dock:Create()

--PAIDS!

	if Nx.Free then
		return
	end

	self.UpdateMod = 100		-- Prevent error

	local gopts = Nx.GetGlobalOpts()
	self.GOpts = gopts

	if not gopts["MapMMButOwn"] then
		return
	end

	-- Create Window

	Nx.Window:SetCreateFade (1, 0)

	local win = Nx.Window:Create ("NxMapDock", nil, nil, nil, 1, 1, nil, true)
	self.Win = win

	win:SetBGAlpha (0, 1)

--	if gopts["MapMMDockHigh"] then
--		win:SetStrata ("HIGH")
--	end

	win:CreateButtons()
	win:InitLayoutData (nil, 100045, -.08, 45, 50, 2)

--	win:SetUser (m, self.OnWin)

	win.Frm:SetToplevel (true)

	self:UpdateOptions()

	self.InitPending = true

	Nx.Timer:Start ("DockMinimapScan", 3, self, self.MinimapOwnInit)

--PAIDE!
end

--------
-- Init minimap button ownership

function Nx.Map.Dock:MinimapOwnInit()

	self.InitPending = nil

	local map = Nx.Map:GetMap (1)
	local mm = getglobal ("Minimap")

	local mmOwnerNames = {
		"NXMiniMapBut","GameTimeFrame","TimeManagerClockButton","MiniMapWorldMapButton","MiniMapMailFrame","MiniMapTracking","MiniMapVoiceChatFrame","QueueStatusMinimapButton","MiniMapInstanceDifficulty",
	}

	local f = getglobal ("MinimapBackdrop")	-- Add so it gets ignored
	map.MMOwnedFrms[f] = 0

	self.MMFrms = {}

	for k, name in ipairs (mmOwnerNames) do
		local f = getglobal (name)
		if f then

			map.MMOwnedFrms[f] = 0
			tinsert (self.MMFrms, f)

			f:SetParent (self.Win.Frm)

			if name == "MiniMapTracking" then
				f:Show()
			end
		end
	end

	-- Scan all frames

	local texnames = { ["Interface\\AddOns\\CT_Core\\Images\\minimapIcon"] = 1, }

	local mapf = map.Frm
	local winf = self.Win.Frm

	local found = {}

	local f = EnumerateFrames()
	while f do

		if not f:IsObjectType ("Model") then
--		if f:IsShown() and not f:IsObjectType ("Model") then

			local pt, relTo = f:GetPoint()
			if relTo == mm then

				local parent = f:GetParent()
				if parent ~= mm and parent ~= mapf then
--					Nx.prtFrame ("Dock Scan", f)

					found[f] = 1
				end
			end

			local reg = { f:GetRegions() }
			for k, v in ipairs (reg) do

				if v:IsObjectType ("Texture") then
					local tname = v:GetTexture()
					if tname and texnames[tname] then
--					if tname and strfind (tname, "CT") then
--						Nx.prt ("Tex %s", tname)

						found[f] = 1
						break
					end
				end
			end
		end

		f = EnumerateFrames (f)
	end

	-- Add found frames to list

	for f in pairs (found) do

		if not map.MMOwnedFrms[f] then
			map.MMOwnedFrms[f] = 0
			tinsert (self.MMFrms, f)
			f:SetParent (winf)
		end
	end

	map.Win:Show (map.StartupShown)

	Nx.Map:MinimapButtonShowUpdate()

	if NxData.DebugDock then
		Nx.prt ("DockScan %s", #self.MMFrms)
	end
end

--------

function Nx.Map.Dock:UpdateOptions()

	local win = self.Win
	if win then

		local lock = win:IsLocked()
		win:SetBGAlpha (0, lock and 0 or 1)

		self.UpdateMod = 1
	end
end

--------

function Nx.Map.Dock:MinimapDetachFrms()

	if Nx.Tick % self.UpdateMod ~= 0 then
		return
	end

	if not self.Win then
		return
	end

	self.UpdateMod = 30

	if NxData.DebugDock then
		Nx.prt ("Dock %s", #self.MMFrms)
	end

	local mm = getglobal ("Minimap")
--	local mmt = getglobal ("MiniMapTracking")
	local mmClock = getglobal ("TimeManagerClockButton")
	local win = self.Win
	local winf = win.Frm
	local butLvl = winf:GetFrameLevel() + 1
	local cLvl = butLvl + 1
	local setCLvls = Nx.Util_SetChildLevels
	local strata = winf:GetFrameStrata()

	local mini = self.GOpts["MMButWinMinimize"]

	local cx, cy = win:GetClientOffset()
	local cw, ch = win:GetSize()

	local columnCnt = mini and 1 or self.GOpts["MapMMButColumns"]		-- max (floor (cw / 30), 1)
	local columnStart = 0
	local columnEnd = columnCnt - 1

	local colAdd = 1
	local spacing = self.GOpts["MapMMButSpacing"]
	local yAdd = spacing
	local y = cy + 6

	local s = self.GOpts["MapMMButCorner"]
	if s == "TopRight" or s == "BottomRight" then
		columnEnd = -columnEnd
--		columnStart, columnEnd = columnEnd, columnStart
		colAdd = -1
	end
	if s == "BottomLeft" or s == "BottomRight" then
		yAdd = -yAdd
		y = cy - 6
	end

	local column = columnStart
	local borderSize = win:GetBorderSize()
	local baseX = borderSize + 17
	local baseY = y
	local incRow

--	if IsAltKeyDown() then return end

	for n, f in ipairs (self.MMFrms) do

		if f:IsVisible() then

			local w = f:GetWidth()
			if w > 0 then

				local sc = 32 / max (w, 32)

				if incRow then
					incRow = false
					column = columnStart
					y = y + yAdd
				end

				if mini and n > 1 then
					column = 0
					y = baseY
					butLvl = 1
					cLvl = 1
					sc = .1
				end

				local x = baseX + column * spacing

				f:SetParent (winf)
				f:ClearAllPoints()

				if f == mmClock then
					sc = sc * 1.5
--					f:SetPoint ("CENTER", winf, "TOPLEFT", x / sc - 3, -y / sc + 3)
				end

				f:SetPoint ("CENTER", winf, "TOPLEFT", x / sc, -y / sc)

--				if f == mmt then
--					sc = .82
--					f:SetPoint ("CENTER", winf, "TOPLEFT", x / sc - 3, -y / sc + 3)
--				end

				f:SetScale (sc)

				f:SetFrameStrata (strata)
				f:SetFrameLevel (butLvl)
				setCLvls (f, cLvl)
--[[
				if IsControlKeyDown() then
					Nx.prt ("MMFrms #%d %s %s", n, f:GetName() or "nil", w)
					Nx.prtFrame ("", f)
				end
--]]
				if column == columnEnd then
					incRow = true
				end

				column = column + colAdd
			end

		elseif f:IsShown() then		-- Strange? Fixes Outfitter Minimap button not drawing

--			Nx.prt ("Frm vis error %s", f:GetName() or "nil")
			f:Show()
		end
	end

	if not win.MovSizing then
--		local h = min (ch, cy - y)

		local x = winf:GetLeft()

		win:SetSize (34, 11)
	end

	-- Debug

--[[

	if IsControlKeyDown() then

		local frms = {}

		for n, f in ipairs (self.MMFrms) do
			if frms[f] then
				Nx.prt ("Dup frm %s #%s", f:GetName() or "nil", n)
			end
			frms[f] = n
		end
	end
--]]
end

-------------------------------------------------------------------------------
-- Routing

--------
-- Gather icons

function Nx.Map:RouteGathers()

	local points = {}
	local cnt = self:GetIconCnt ("!Ga")

	for n = 1, cnt do
		local wx, wy = self:GetIconPt ("!Ga", n)
		local x, y = self:GetZonePos (self.MapId, wx, wy)

		local pt = {}
		tinsert (points, pt)
		pt.X = x
		pt.Y = y
	end

	self:RouteMerge (points)

	local route = self:Route (points)
	if route then
		self:RouteToTargets (route, false)
	end
end

function Nx.Map:RouteTargets()

	local points = {}

	for n, tar in ipairs (self.Targets) do

		local wx = tar.TargetMX
		local wy = tar.TargetMY
		local x, y = self:GetZonePos (self.MapId, wx, wy)

		local pt = {}
		tinsert (points, pt)
		pt.Name = tar.TargetName
		pt.X = x
		pt.Y = y
	end

	local route = self:Route (points)
	if route then
		self:RouteToTargets (route)
	end
end

function Nx.Map:RouteQuests (points)

	local route = self:Route (points)
	if route then
		self:RouteToTargets (route, false)
	end
end

--------
-- Merge close points

function Nx.Map:RouteMerge (points)

	local radius = self.GOpts["RouteMergeRadius"]

	if #points < 2 or radius < 1 then
		return
	end

	local tm = GetTime()

	sort (points, function (a, b) return a.X < b.X end)

--	for n, pt in ipairs (points) do
--		Nx.prt ("%s %s", n, pt.X)
--	end

	radius = radius / Nx.Map:GetWorldZoneScale (self.MapId) / 4.575
--	Nx.prt ("rad %s", radius)

	local radiusSq = radius ^ 2	-- Yards to world space squared

	local startCnt = #points
	local merged = true

	while merged do

		merged = false

		local close = 999999999
		local closeI1
		local closeI2

		for n1, pt1 in ipairs (points) do

--			local done

			for n2 = n1 + 1, #points do

				local pt2 = points[n2]

				if pt2.X - pt1.X > radius then
--					Nx.prt ("done %s %s", pt1.X, pt2.X)
--					done = true
					break
				end

				local d = (pt1.X - pt2.X) ^ 2 + ((pt1.Y - pt2.Y) / 1.5) ^ 2
				if d < close then
					close = d
					closeI1 = n1
					closeI2 = n2
--[[
					if d ^ .5 < radius * .5 then	-- Close enough? Early out
						Nx.prt ("%s + %s, %s", n1, n2, d ^ .5)
						done = true
						break
					end
--]]
				end
			end

--			if done then
--				break
--			end
		end

		if close ^ .5 < radius then

--			Nx.prt (" + %s %s", closeI1, closeI2)

			local pt1 = points[closeI1]
			local pt2 = points[closeI2]

			pt1.X = (pt1.X + pt2.X) * .5		-- Average
			pt1.Y = (pt1.Y + pt2.Y) * .5

			tremove (points, closeI2)
			merged = true

			sort (points, function (a, b) return a.X < b.X end)
		end
	end

	Nx.prt ("Merged %s in %.1f secs", startCnt - #points, GetTime() - tm)
end

--------
-- Make a route and target it

function Nx.Map:Route (points)

	if #points == 0 then
		return
	end

	-- Test

	local tm = GetTime()
--[[
	local blks = 20
	local scale = 5

	local points = {}
	for n = 0, 399 do
		local pt = {}
		points[n + 1] = pt
		pt.X = n % blks * scale + floor (n / 7) % 2 * 3
		pt.Y = floor (n / blks) * scale + floor (n / 3) % 2 * 4
--		pt.X = random (1, 20)
--		pt.Y = random (1, 20)
	end
--]]

	--

	local route = {}

	for n, pt in ipairs (points) do
		pt.Y = pt.Y / 1.5		-- Make Y same units as X
	end

	-- Nearest neighbor

	if #points > 1 then

		local x = points[1].X
		local y = points[1].Y
		if x == points[#points].X and y == points[#points].Y then	-- End same as start?
			tremove (points)
		end
	end

	local x, y = self:GetZonePos (self.MapId, self.PlyrX, self.PlyrY)
	y = y / 1.5

	while #points > 0 do

		local closeDist = 999999999
		local closeI

		for n, pt in ipairs (points) do

			local dist = (x - pt.X) ^ 2 + (y - pt.Y) ^ 2
			if dist < closeDist then
				closeDist = dist
				closeI = n
			end
		end

		local pt = tremove (points, closeI)

		local r = {}
		tinsert (route, r)
		r.Name = pt.Name
		r.X = pt.X
		r.Y = pt.Y
		r.Weight = pt.Weight or 1

		x = pt.X
		y = pt.Y
	end

	-- Add first node at end if needed for loop

	local x = route[1].X
	local y = route[1].Y

	if x ~= route[#route].X or y ~= route[#route].Y then

--		Nx.prt ("%f %f %f %f", x, route[#route].X, y, route[#route].Y)

		local r = {}
		r.X = x
		r.Y = y
		tinsert (route, r)
	end

	-- Calc length (sets node distances)

	local len = self:RouteLen (route)
--	Nx.prt ("Route len %s, %s secs", len, GetTime() - tm)

	-- Optimize .517 secs on 400

	for n = 1, 5 do
		local swap = self:RouteOptimize (route)
--		local len = self:RouteLen (route)
--		Nx.prt ("Route opt #%s len %s, %s secs", #route, len, GetTime() - tm)
		if not swap then
			break
		end
	end

	-- Show info

	local scale = self:GetWorldZoneScale (self.MapId)
	local len = self:RouteLen (route)
	Nx.prt ("Routed %s nodes, %d yards in %.1f secs", #route, len * scale * 4.575, GetTime() - tm)

	return route
end

function Nx.Map:RouteToTargets (route, targetIcon)

	Nx.Quest.Watch:ClearAutoTarget()

	local mapId = self.MapId

	for n, r in ipairs (route) do

		local wx, wy = self:GetWorldPos (mapId, r.X, r.Y * 1.5)
		local s = format ("Route%s (%s) %s", n, #route - n + 1, r.Name or "")
		local tar = self:SetTarget ("Route", wx, wy, wx, wy, targetIcon, nil, s, n ~= 1)

		tar.Radius = self.GOpts["RouteGatherRadius"]

--		self:SetTargetXY (mapId, r.X, r.Y * 1.5, "r" .. n, true)
	end
end

function Nx.Map:RouteLen (route)

	local len = 0

	for n = 1, #route - 1 do
		local r1 = route[n]
		local r2 = route[n + 1]
		r1.Dist = ((r1.X - r2.X) ^ 2 + (r1.Y - r2.Y) ^ 2) ^ .5
		len = len + r1.Dist
--		Nx.prt ("Route %s len %s", n, ((x - r.X) ^ 2 + (y - r.Y) ^ 2) ^ .5)
	end

	return len
end

function Nx.Map:RouteOptimize (route)

	local swap

---[[ 1394.4788 len
	for len = #route - 2, 2, -1 do

		for n = 1, #route - len - 1 do

			local r1 = route[n]
			local r2 = route[n + 1]
			local n2 = n + len
			local r3 = route[n2]
			local r4 = route[n2 + 1]

			if r1.Dist + r3.Dist >
				((r1.X - r3.X) ^ 2 + (r1.Y - r3.Y) ^ 2) ^ .5 + ((r2.X - r4.X) ^ 2 + (r2.Y - r4.Y) ^ 2) ^ .5 then

				self:RouteSwap (route, n + 1, len)

--				local dist1 = ((r1.X - r2.X) ^ 2 + (r1.Y - r2.Y) ^ 2) ^ .5 + ((r3.X - r4.X) ^ 2 + (r3.Y - r4.Y) ^ 2) ^ .5
--				local dist2 = ((r1.X - r3.X) ^ 2 + (r1.Y - r3.Y) ^ 2) ^ .5 + ((r2.X - r4.X) ^ 2 + (r2.Y - r4.Y) ^ 2) ^ .5
--				Nx.prt ("Route %s swap %s %s %s", len, n, dist1 or 0, dist2 or 0)

				swap = true
			end
		end
	end
--]]

--[[	Little slower
	for last = #route - 1, 2, -1 do

		for n = 1, last - 2 do

			local r1 = route[n]
			local r2 = route[n + 1]
			local n2 = last
			local r3 = route[n2]
			local r4 = route[n2 + 1]

			if r1.Dist + r3.Dist >
				((r1.X - r3.X) ^ 2 + (r1.Y - r3.Y) ^ 2) ^ .5 + ((r2.X - r4.X) ^ 2 + (r2.Y - r4.Y) ^ 2) ^ .5 then

				self:RouteSwap (route, n + 1, last - n)

--				local dist1 = ((r1.X - r2.X) ^ 2 + (r1.Y - r2.Y) ^ 2) ^ .5 + ((r3.X - r4.X) ^ 2 + (r3.Y - r4.Y) ^ 2) ^ .5
--				local dist2 = ((r1.X - r3.X) ^ 2 + (r1.Y - r3.Y) ^ 2) ^ .5 + ((r2.X - r4.X) ^ 2 + (r2.Y - r4.Y) ^ 2) ^ .5
				Nx.prt ("Route %s swap %s %s %s", last - n, n, dist1 or 0, dist2 or 0)

				swap = true
			end
		end
	end
--]]

	return swap
end

function Nx.Map:RouteSwap (route, first, len)

	-- 1 2 3 4 5 6 7 8	Before (3, +4)
	-- 1 2 6 5 4 3 7 8	After

	local last = first + len - 1
	local stop = first + floor (len / 2) - 1

--[[	Loop unroll does not help
	local loops = floor (len / 2)

--	if loops > 1 then

--		Nx.prt ("Route swap loops %s", loops)

		if loops % 2 ~= 0 then
			route[first], route[last] = route[last], route[first]
			first = first + 1
			last = last - 1
		end

		for n = first, stop, 2 do
			route[n], route[last] = route[last], route[n]
			route[n+1], route[last-1] = route[last-1], route[n+1]
			last = last - 2
		end
	else
--]]

	local n2 = last
	for n = first, stop do
		route[n], route[n2] = route[n2], route[n]
		n2 = n2 - 1
	end

	for n = first - 1, last do
		local r1 = route[n]
		local r2 = route[n + 1]
		r1.Dist = ((r1.X - r2.X) ^ 2 + (r1.Y - r2.Y) ^ 2) ^ .5
	end
end

-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-- Test code

function Nx.Map:VehicleDumpPos()

	-- Right Reaver guard .431 -.701  -359

	for n = 1, GetNumBattlefieldVehicles() do

		local x, y, unitName, possessed, typ, dir, player = GetBattlefieldVehicleInfo (n)
		if x and not player then

			local xo = self.PlyrRZX - x * 100
			local yo = (self.PlyrRZY - y * 100) / 1.5
			dir = dir / PI * -180
			xo, yo = xo * cos (dir) + yo * sin (dir), (xo * -sin (dir) + yo * cos (dir)) * 1.5

			Nx.prt ("#%s %s %f %f %.3f %s", n, unitName or "nil", xo, yo, dir or -1, typ or "no type")
		end
	end
end

--[[

--------
-- Get zone position of world location
-- (id, x, y)

function Nx.Map:OLD_GetZonePos (mapId, worldX, worldY)

--	Nx.prt ("WXY %f %f", worldX, worldY)

	local cont = floor (mapId / 1000)

	local info = self.MapInfo[cont]
	if info then

		local winfo = self.MapWorldInfo[mapId]
		if winfo then

			local scale = winfo[1]

			local x = (worldX - info.X - winfo[2]) / scale
			local y = (worldY - info.Y - winfo[3]) / scale * 1.5

--			Nx.prt ("XY %f %f %f", x, y, scale)

			return x, y
		end
	end

	return 0, 0
end

--------
-- Test empty function

function Nx.Map:TestEmpty()
end

function Nx.Map:Test1()

	local s = "########"
	local loc1 = strsub (s, 1, 4)
	local x, y = Nx.Quest:UnpackLocPtOff (loc1, 1)
end

function Nx.Map:Test2()
	local s = "####"
	local x, y = Nx.Quest:UnpackLocPtOff (s, 1)
end

--------
-- Do test

function Nx.Map:Test()

--[
	local tm = GetTime()
	for n = 1, 1000000 do
		self:TestEmpty()
	end
	Nx.prt ("Time %.3f empty", GetTime() - tm)

	local tm = GetTime()
	for n = 1, 1000000 do
		self:GetZonePos (1001, 0, 0)
	end
	Nx.prt ("Time %.3f", GetTime() - tm)

	local tm = GetTime()
	for n = 1, 1000000 do
		self:OLD_GetZonePos (1001, 0, 0)
	end
	Nx.prt ("Time %.3f", GetTime() - tm)
--]

	-- Precondition
---[
	for n = 1, 1000 do
		self:Test1()
		self:Test2()
	end

	-- Test

	local tm = GetTime()
	local func = Nx.Map.TestEmpty
	for n = 1, 5000000 do
		func (self)
	end
	Nx.prt ("Time %.3f empty", GetTime() - tm)

	local tm = GetTime()
	local func = Nx.Map.Test1
	for n = 1, 5000000 do
		func (self)
	end
	Nx.prt ("Time %.3f #1", GetTime() - tm)

	local tm = GetTime()
	local func = Nx.Map.Test2
	for n = 1, 5000000 do
		func (self)
	end
	Nx.prt ("Time %.3f #2", GetTime() - tm)
--]
end

--]]

-------------------------------------------------------------------------------
-- EOF