Quantcast

Merge branch 'master' of git.wowace.com:wow/startip/mainline

Scott Sibley [07-21-11 - 05:08]
Merge branch 'master' of git.wowace.com:wow/startip/mainline

Conflicts:
	.pkgmeta
	StarTip.lua
	StarTip.toc
	embeds.xml
Filename
Modules/Appearance.lua
Modules/Bars.lua
Modules/Fade.lua
Modules/Position.lua
Modules/PvP.lua
Modules/RaidIcon.lua
Modules/Targeting.lua
Modules/Text.lua
diff --git a/Modules/Appearance.lua b/Modules/Appearance.lua
new file mode 100644
index 0000000..51a6b8e
--- /dev/null
+++ b/Modules/Appearance.lua
@@ -0,0 +1,439 @@
+local mod = StarTip:NewModule("Appearance")
+mod.name = "Appearance"
+local _G = _G
+local StarTip = _G.StarTip
+local GameTooltip = _G.GameTooltip
+local ShoppingTooltip1 = _G.ShoppingTooltip1
+local ShoppingTooltip2 = _G.ShoppingTooltip2
+local self = mod
+local LSM = _G.LibStub("LibSharedMedia-3.0")
+
+local defaults = {
+	profile = {
+		scale = 1,
+		font = StarTip:GetLSMIndexByName("font", LSM:GetDefault("font")),
+		edgeFile = StarTip:GetLSMIndexByName("border", "Blizzard Tooltip"),
+		background = StarTip:GetLSMIndexByName("background", "Blizzard Tooltip"),
+		bgColor = { -- Default colors from CowTip
+			guild = {0, 0.15, 0, 1},
+			hostilePC = {0.25, 0, 0, 1},
+			hostileNPC = {0.15, 0, 0, 1},
+			neutralNPC = {0.15, 0.15, 0, 1},
+			friendlyPC = {0, 0, 0.25, 1},
+			friendlyNPC = {0, 0, 0.15, 1},
+			other = {0, 0, 0, 1},
+			dead = {0.15, 0.15, 0.15, 1},
+			tapped = {0.25, 0.25, 0.25, 1},
+		},
+		borderColor = { 1, 1, 1, 1 },
+		padding = 4,
+		edgeSize = 16,
+	}
+}
+
+local backdropParts = {bgFile = true, edgeFile = true, edgeSize = true, background = true}
+local otherParts = {scale = "SetScale", font = "SetFont"}
+
+local get = function(info)
+	return self.db.profile[info[#info]]
+end
+
+local set = function(info, v)
+	self.db.profile[info[#info]] = v
+	if info[#info] == "bgColor" then return end
+	if backdropParts[info[#info]] then
+		self:SetBackdrop()
+	elseif info[#info] == "scale" then
+		self:SetScale()
+	else
+		self:SetFont()
+	end
+end
+
+local options = {
+	scale = {
+		name = "Scale Slider",
+		desc = "Adjust tooltip scale",
+		type = "range",
+		min = 0.25,
+		max = 4,
+		step = 0.01,
+		bigStep = 0.05,
+		isPercent = true,
+		get = get,
+		set = set,
+		order = 4
+	},
+	font = {
+		name = "Tooltip Font",
+		desc = "Set the tooltip's font",
+		type = "select",
+		values = LSM:List("font"),
+		get = get,
+		set = set,
+		order = 5
+	},
+	edgeFile = {
+		name = "Tooltip Border",
+		desc = "Set the tooltip's border style",
+		type = "select",
+		values = LSM:List("border"),
+		get = get,
+		set = set,
+		order = 6
+	},
+	background = {
+		name = "Tooltip Background",
+		desc = "Set the tooltip's background style",
+		type = "select",
+		values = LSM:List("background"),
+		get = get,
+		set = set,
+		order = 7
+	},
+	borderColor = {
+		name = "Tooltip Border Color",
+		desc = "Set the color of the tooltip's border",
+		type = "color",
+		hasAlpha = true,
+		get = function() return unpack(self.db.profile.borderColor) end,
+		set = function(info, r, g, b, a)
+			self.db.profile.borderColor[1] = r
+			self.db.profile.borderColor[2] = g
+			self.db.profile.borderColor[3] = b
+			self.db.profile.borderColor[4] = a
+		end,
+		order = 8
+	},
+	padding = {
+		name = "Tooltip Padding",
+		desc = "Set the tooltip's padding",
+		type = "range",
+		min = 0,
+		max = 20,
+		step = 1,
+		get = get,
+		set = set,
+		order = 9
+	},
+	edgeSize = {
+		name = "Tooltip Edge Size",
+		desc = "Set the tooltip's edge size",
+		type = "range",
+		min = 0,
+		max = 20,
+		step = 1,
+		get = get,
+		set = set,
+		order = 10
+	},
+	bgColor = {
+		name = "Background Color",
+		desc = "Set options for background color",
+		type = "group",
+		get = function(info)
+			return unpack(self.db.profile.bgColor[info[#info]])
+		end,
+		set = function(info, r, g, b, a)
+			self.db.profile.bgColor[info[#info]][1] = r
+			self.db.profile.bgColor[info[#info]][2] = g
+			self.db.profile.bgColor[info[#info]][3] = b
+			self.db.profile.bgColor[info[#info]][4] = a
+			self:SetBackdropColor()
+		end,
+		args = {
+			header = {
+				name = "Background Color",
+				type = "header",
+				order = 1
+			},
+			guild = {
+				name = "Guild and friends",
+				desc = "Background color for your guildmates and friends.",
+				type = "color",
+				hasAlpha = true,
+				width = "full",
+				order = 2
+			},
+			hostilePC = {
+				name = "Hostile players",
+				desc = "Background color for hostile players.",
+				type = "color",
+				hasAlpha = true,
+				width = "full",
+				order = 3
+			},
+			hostileNPC = {
+				name = "Hostile non-player characters",
+				desc = "Background color for hostile non-player characters.",
+				type = "color",
+				hasAlpha = true,
+				width = "full",
+				order = 4
+			},
+			neutralNPC = {
+				name = "Neutral non-player characters",
+				desc = "Background color for neutral non-player characters.",
+				type = "color",
+				hasAlpha = true,
+				width = "full",
+				order = 5
+			},
+			friendlyPC = {
+				name = "Friendly players",
+				desc = "Background color for friendly players.",
+				type = "color",
+				hasAlpha = true,
+				width = "full",
+				order = 6
+			},
+			friendlyNPC = {
+				name = "Friendly non-player characters",
+				desc = "Background color for friendly non-player characters.",
+				type = "color",
+				hasAlpha = true,
+				width = "full",
+				order = 7
+			},
+			dead = {
+				name = "Dead",
+				desc = "Background color for dead units.",
+				type = "color",
+				hasAlpha = true,
+				width = "full",
+				order = 8
+			},
+			tapped = {
+				name = "Tapped",
+				desc = "Background color for when a unit is tapped by another.",
+				type = "color",
+				hasAlpha = true,
+				width = "full",
+				order = 9
+			},
+			other = {
+				name = "Other Tooltips",
+				desc = "Background color for other tooltips.",
+				type = "color",
+				hasAlpha = true,
+				width = "full",
+				order = 10
+			}
+		}
+	}
+}
+
+function mod:OnInitialize()
+	self.db = StarTip.db:RegisterNamespace(self:GetName(), defaults)
+	StarTip:SetOptionsDisabled(options, true)
+	self.st1left, self.st1right, self.st2left, self.st2right = {}, {}, {}, {}
+	for i = 1, 50 do
+		ShoppingTooltip1:AddDoubleLine(' ', ' ')
+		ShoppingTooltip2:AddDoubleLine(' ', ' ')
+		self.st1left[i] = _G["ShoppingTooltip1TextLeft" .. i]
+		self.st1right[i] = _G["ShoppingTooltip1TextLeft" .. i]
+		self.st2left[i] = _G["ShoppingTooltip2TextRight" .. i]
+		self.st2right[i] = _G["ShoppingTooltip2TextRight" .. i]
+	end
+	ShoppingTooltip1:Show()
+	ShoppingTooltip1:Hide()
+	ShoppingTooltip2:Show()
+	ShoppingTooltip2:Hide()
+end
+
+function mod:OnEnable()
+	self:SetScale()
+	self:SetFont()
+	self:SetBackdrop()
+	self:SetBorderColor()
+	self:SetBackdropColor(true)
+	StarTip:SetOptionsDisabled(options, false)
+end
+
+function mod:OnDisable()
+	self:SetScale(true)
+	self:SetFont(true)
+	self:SetBorderColor(true)
+	self:SetBackdrop(true)
+	self:SetBackdropColor(true)
+	StarTip:SetOptionsDisabled(options, true)
+end
+
+function mod:GetOptions()
+	return options
+end
+
+function mod:SetUnit()
+	self.origBackdrop = self.origBackdrop or GameTooltip:GetBackdrop()
+	self.origBackdropColor = self.origBackdropColor or {GameTooltip:GetBackdropColor()}
+end
+
+function mod:OnHide()
+	self:SetBackdropColor(true)
+end
+
+function mod:OnShow()
+	self:SetBackdropColor()
+end
+
+function mod:SetScale(reset)
+	if reset then
+		GameTooltip:SetScale(1)
+		ShoppingTooltip1:SetScale(1)
+		ShoppingTooltip2:SetScale(1)
+
+	else
+		GameTooltip:SetScale(self.db.profile.scale)
+		ShoppingTooltip1:SetScale(self.db.profile.scale)
+		ShoppingTooltip2:SetScale(self.db.profile.scale)
+
+	end
+end
+
+function mod:SetFont(reset)
+	local font
+	if reset then
+		font = "Friz Quadrata TT"
+	else
+		font = LSM:Fetch('font', LSM:List("font")[self.db.profile.font])
+	end
+
+	if StarTip.leftLines[1]:GetFont() == font then
+		return
+	end
+	for i = 1, 50 do
+		local left = StarTip.leftLines[i]
+		local right = StarTip.rightLines[i]
+		local _, size, style = left:GetFont()
+		left:SetFont(font, size, style)
+		_, size, style = right:GetFont()
+		right:SetFont(font, size, style)
+
+		left = self.st1left[i]
+		right = self.st1right[i]
+		_, size, style = left:GetFont()
+		left:SetFont(font, size, style)
+		_, size, style = right:GetFont()
+		right:SetFont(font, size, style)
+
+		left = self.st2left[i]
+		right = self.st2right[i]
+		_, size, style = left:GetFont()
+		left:SetFont(font, size, style)
+		_, size, style = right:GetFont()
+		right:SetFont(font, size, style)
+	end
+end
+
+
+local tmp, tmp2 = {}, {}
+function mod:SetBackdrop()
+	if reset then
+		GameTooltip:SetBackdrop(self.origBackdrop)
+		ShoppingTooltip1:SetBackdrop(self.origBackdrop)
+		ShoppingTooltip2:SetBackdrop(self.origBackdrop)
+	else
+		local bd = GameTooltip:GetBackdrop()
+		local changed = false
+		local bgFile = LSM:Fetch('background', LSM:List('background')[self.db.profile.background])
+		local edgeFile = LSM:Fetch('border', LSM:List('border')[self.db.profile.edgeFile])
+
+		if bd.bgFile ~= bgFile or bd.edgeFile ~= edgeFile or bd.edgeSize ~= self.db.profile.edgeSize or bd.insets.left ~= self.db.profile.padding then
+			changed = true
+		end
+
+		if changed then
+			tmp.bgFile = bgFile
+			tmp.edgeFile = edgeFile
+			tmp.tile = false
+			tmp.edgeSize = self.db.profile.edgeSize
+			tmp.insets = tmp2
+			tmp2.left = self.db.profile.padding
+			tmp2.right = self.db.profile.padding
+			tmp2.top = self.db.profile.padding
+			tmp2.bottom = self.db.profile.padding
+			GameTooltip:SetBackdrop(tmp)
+			ShoppingTooltip1:SetBackdrop(tmp)
+			ShoppingTooltip2:SetBackdrop(tmp)
+		end
+	end
+end
+
+function mod:SetBackdropColor(reset)
+	if reset then
+		if self.origBackdropColor then
+			GameTooltip:SetBackdropColor(unpack(self.origBackdropColor))
+			ShoppingTooltip1:SetBackdropColor(unpack(self.origBackdropColor))
+			ShoppingTooltip2:SetBackdropColor(unpack(self.origBackdropColor))
+		else
+			GameTooltip:SetBackdropColor(0,0,0,1)
+			ShoppingTooltip1:SetBackdropColor(0,0,0,1)
+			ShoppingTooltip2:SetBackdropColor(0,0,0,1)
+		end
+	else -- Snagged from CowTip
+		local kind
+		if UnitExists("mouseover") then
+			if UnitIsDeadOrGhost("mouseover") then
+				kind = 'dead'
+			elseif UnitIsTapped("mouseover") and not UnitIsTappedByPlayer("mouseover") then
+				kind = 'tapped'
+			elseif UnitIsPlayer("mouseover") then
+				if UnitIsFriend("player", "mouseover") then
+					local playerGuild = GetGuildInfo("player")
+					if playerGuild and playerGuild == GetGuildInfo("mouseover") or UnitIsUnit("player", "mouseover") then
+						kind = 'guild'
+					else
+						local friend = false
+						local name = UnitName("mouseover")
+						for i = 1, GetNumFriends() do
+							if GetFriendInfo(i) == name then
+								friend = true
+								break
+							end
+						end
+						if friend then
+							kind = 'guild'
+						else
+							kind = 'friendlyPC'
+						end
+					end
+				else
+					kind = 'hostilePC'
+				end
+			else
+				if UnitIsFriend("player", "mouseover") then
+					kind = 'friendlyNPC'
+				else
+					local reaction = UnitReaction("mouseover", "player")
+					if not reaction or reaction <= 2 then
+						kind = 'hostileNPC'
+					else
+						kind = 'neutralNPC'
+					end
+				end
+			end
+		else
+			kind = 'other'
+		end
+		GameTooltip:SetBackdropColor(unpack(self.db.profile.bgColor[kind]))
+		if kind == 'other' then
+			ShoppingTooltip1:SetBackdropColor(unpack(self.db.profile.bgColor[kind]))
+			ShoppingTooltip2:SetBackdropColor(unpack(self.db.profile.bgColor[kind]))
+		end
+	end
+end
+
+function mod:SetBorderColor(reset)
+	if reset then
+		GameTooltip:SetBackdropBorderColor(1,1,1,1)
+		ShoppingTooltip1:SetBackdropBorderColor(1,1,1,1)
+		ShoppingTooltip2:SetBackdropBorderColor(1,1,1,1)
+	else
+		GameTooltip:SetBackdropBorderColor(unpack(self.db.profile.borderColor))
+		ShoppingTooltip1:SetBackdropBorderColor(unpack(self.db.profile.borderColor))
+		ShoppingTooltip2:SetBackdropBorderColor(unpack(self.db.profile.borderColor))
+	end
+end
+
+
+
diff --git a/Modules/Bars.lua b/Modules/Bars.lua
new file mode 100644
index 0000000..f7b8a81
--- /dev/null
+++ b/Modules/Bars.lua
@@ -0,0 +1,217 @@
+local mod = StarTip:NewModule("Bars", "AceTimer-3.0")
+mod.name = "Bars"
+local _G = _G
+local StarTip = _G.StarTip
+local GameTooltip = _G.GameTooltip
+local GameTooltipStatusBar = _G.GameTooltipStatusBar
+local UnitIsPlayer = _G.UnitIsPlayer
+local RAID_CLASS_COLORS = _G.RAID_CLASS_COLORS
+local UnitSelectionColor = _G.UnitSelectionColor
+local UnitClass = _G.UnitClass
+local self = mod
+local timer
+local LSM = _G.LibStub("LibSharedMedia-3.0")
+
+local defaults = {
+	profile = {
+		showHP = true,
+		showMP = true,
+		hpTexture = StarTip:GetLSMIndexByName("statusbar", LSM:GetDefault("statusbar")),
+		mpTexture = StarTip:GetLSMIndexByName("statusbar", LSM:GetDefault("statusbar")),
+		useGradient = false,
+	}
+}
+
+local options = {
+	hpBar = {
+		name = "HP Bar",
+		type = "group",
+		args = {
+			show = {
+				name = "Show",
+				desc = "Toggle showing the HP bar",
+				type = "toggle",
+				get = function() return self.db.profile.showHP end,
+				set = function(info, v) self.db.profile.showHP = v end,
+				order = 1
+			},
+			useGradient = {
+				name = "Use Gradient",
+				desc = "Set whether to use a gradient based on unit health",
+				type = "toggle",
+				get = function() return self.db.profile.useGradient end,
+				set = function(info, v) self.db.profile.useGradient = v end,
+				order = 3
+			},
+			texture = {
+				name = "Texture",
+				desc = "Change the status bar's texture",
+				type = "select",
+				values = LSM:List("statusbar"),
+				get = function() return self.db.profile.hpTexture end,
+				set = function(info, v)
+					self.db.profile.hpTexture = v
+					self.hpBar:SetStatusBarTexture(LSM:Fetch("statusbar", LSM:List("statusbar")[v]))
+				end,
+				order = 2
+			},
+		}
+	},
+	mpBar = {
+		name = "MP Bar",
+		type = "group",
+		args = {
+			show = {
+				name = "Show",
+				desc = "Toggle showing the MP bar",
+			type = "toggle",
+				get = function() return self.db.profile.showMP end,
+				set = function(info, v) self.db.profile.showMP = v end,
+				order = 1
+			},
+			texture = {
+				name = "Texture",
+				desc = "Change the status bar's texture",
+				type = "select",
+				values = LSM:List("statusbar"),
+				get = function() return self.db.profile.mpTexture end,
+				set = function(info, v)
+					self.db.profile.mpTexture = v
+					self.mpBar:SetStatusBarTexture(LSM:Fetch("statusbar", LSM:List("statusbar")[v]))
+				end,
+				order = 2
+			}
+		}
+	}
+}
+
+
+function mod:OnInitialize()
+	self.db = StarTip.db:RegisterNamespace(self:GetName(), defaults)
+
+	local hpBar = CreateFrame("StatusBar", nil, GameTooltip)
+	hpBar:SetStatusBarTexture(LSM:Fetch("statusbar", LSM:List("statusbar")[self.db.profile.hpTexture]))
+	hpBar:SetPoint("BOTTOMLEFT", GameTooltip, "TOPLEFT")
+	hpBar:SetPoint("LEFT", GameTooltip, "LEFT")
+	hpBar:SetPoint("RIGHT", GameTooltip, "RIGHT")
+	hpBar:SetHeight(5)
+	hpBar:Hide()
+	self.hpBar = hpBar
+
+	local mpBar = CreateFrame("StatusBar", nil, GameTooltip)
+	mpBar:SetStatusBarTexture(LSM:Fetch("statusbar", LSM:List("statusbar")[self.db.profile.mpTexture]))
+	mpBar:SetPoint("TOPLEFT", GameTooltip, "BOTTOMLEFT")
+	mpBar:SetPoint("LEFT")
+	mpBar:SetPoint("RIGHT")
+	mpBar:SetHeight(5)
+	mpBar:Hide()
+	self.mpBar = mpBar
+
+	StarTip:SetOptionsDisabled(options, true)
+end
+
+function mod:OnEnable()
+	local top, bottom = 0, 0
+	if self.db.profile.showHP then
+		top = 5
+	end
+	if self.db.profile.showMP then
+		bottom = -5
+	end
+	GameTooltip:SetClampRectInsets(0, 0, top, bottom)
+	StarTip:SetOptionsDisabled(options, false)
+end
+
+function mod:OnDisable()
+	GameTooltip:SetClampRectInsets(0, 0, 0, 0)
+	StarTip:SetOptionsDisabled(options, true)
+end
+
+function mod:GetOptions()
+	return options
+end
+
+local function updateBars()
+	if self.db.profile.showHP then self:UpdateHealth() end
+	if self.db.profile.showMP then self:UpdateMana() end
+end
+
+function mod:SetUnit()
+	GameTooltipStatusBar:Hide()
+	updateBars()
+	if self.db.profile.showHP then self.hpBar:Show() end
+	if self.db.profile.showMP then self.mpBar:Show() end
+	timer = timer or self:ScheduleRepeatingTimer(updateBars, .5)
+end
+
+function mod:OnHide()
+	if timer then
+		self:CancelTimer(timer)
+		timer = nil
+	end
+	self.hpBar:Hide()
+	self.mpBar:Hide()
+end
+
+local function colorGradient(perc)
+    if perc <= 0.5 then
+        return 1, perc*2, 0
+    else
+        return 2 - perc*2, 1, 0
+    end
+end
+
+-- Colors, snagged from oUF
+local power = {
+	[0] = { r = 48/255, g = 113/255, b = 191/255}, -- Mana
+	[1] = { r = 226/255, g = 45/255, b = 75/255}, -- Rage
+	[2] = { r = 255/255, g = 178/255, b = 0}, -- Focus
+	[3] = { r = 1, g = 1, b = 34/255}, -- Energy
+	[4] = { r = 0, g = 1, b = 1}, -- Happiness
+	[5] = {}, --Unknown
+	[6] = { r = 0.23, g = 0.12, b = 0.77 } -- Runic Power
+}
+local health = {
+	[0] = {r = 49/255, g = 207/255, b = 37/255}, -- Health
+	[1] = {r = .6, g = .6, b = .6} -- Tapped targets
+}
+local happiness = {
+	[1] = {r = 1, g = 0, b = 0}, -- need.... | unhappy
+	[2] = {r = 1 ,g = 1, b = 0}, -- new..... | content
+	[3] = {r = 0, g = 1, b = 0}, -- colors.. | happy
+}
+
+-- Logic snagged from oUF
+function mod:UpdateHealth()
+	local unit = "mouseover"
+	if not UnitExists(unit) then return end
+	local min, max = UnitHealth(unit), UnitHealthMax(unit)
+	self.hpBar:SetMinMaxValues(0, max)
+	self.hpBar:SetValue(min)
+
+	local color
+	if self.db.profile.useGradient then
+		color = {}
+		color.r, color.g, color.b = colorGradient(min/max)
+	elseif(UnitIsTapped(unit) and not UnitIsTappedByPlayer(unit) or not UnitIsConnected(unit)) then
+		color = health[1]
+	elseif UnitIsPlayer(unit) then
+		color = RAID_CLASS_COLORS[select(2, UnitClass(unit))]
+	else
+		color = {}
+		color.r, color.g, color.b = UnitSelectionColor(unit)
+	end
+	if not color then color = health[0] end
+	self.hpBar:SetStatusBarColor(color.r, color.g, color.b)
+end
+
+function mod:UpdateMana()
+	local unit = "mouseover"
+	if not UnitExists(unit) then return end
+	local min, max = UnitMana(unit), UnitManaMax(unit)
+	self.mpBar:SetMinMaxValues(0, max)
+	self.mpBar:SetValue(min)
+
+	local color = power[UnitPowerType(unit)]
+	self.mpBar:SetStatusBarColor(color.r, color.g, color.b)
+end
diff --git a/Modules/Fade.lua b/Modules/Fade.lua
new file mode 100644
index 0000000..be7d0ae
--- /dev/null
+++ b/Modules/Fade.lua
@@ -0,0 +1,201 @@
+local mod = StarTip:NewModule("Fade", "AceHook-3.0")
+mod.name = "Fade"
+local _G = _G
+local GameTooltip = _G.GameTooltip
+local StarTip = _G.StarTip
+local UnitExists = _G.UnitExists
+local self = mod
+
+local defaults = {
+	profile = {
+		unitFrames = 1,
+		otherFrames = 1,
+		units = 2,
+		objects = 2,
+	}
+}
+
+local choices = {
+	"Hide",
+	"Fade out"
+}
+
+local get = function(info)
+	return self.db.profile[info[#info]]
+end
+
+local set = function(info, v)
+	self.db.profile[info[#info]] = v
+end
+
+local options = {
+	units = {
+		name = "World Units",
+		desc = "What to do with tooltips for world frames",
+		type = "select",
+		values = choices,
+		get = get,
+		set = set,
+		order = 4
+	},
+	unitFrames = {
+		name = "Unit Frames",
+		desc = "What to do with tooltips for unit frames",
+		type = "select",
+		values = choices,
+		get = get,
+		set = set,
+		order = 5
+	},
+	otherFrames = {
+		name = "Other Frames",
+		desc = "What to do with tooltips for other frames (spells, macros, items, etc..)",
+		type = "select",
+		values = choices,
+		get = get,
+		set = set,
+		order = 6
+	},
+	objects = {
+		name = "World Objects",
+		desc = "What to do with tooltips for world objects (mailboxes, portals, etc..)",
+		type = "select",
+		values = choices,
+		get = get,
+		set = set,
+		order = 7
+	}
+}
+
+function mod:OnInitialize()
+	self.db = StarTip.db:RegisterNamespace(self:GetName(), defaults)
+	StarTip:SetOptionsDisabled(options, true)
+end
+
+function mod:OnEnable()
+	self:RawHook(GameTooltip, "FadeOut", "GameTooltipFadeOut", true)
+	self:RawHook(GameTooltip, "Hide", "GameTooltipHide", true)
+	StarTip:SetOptionsDisabled(options, false)
+end
+
+function mod:OnDisable()
+	self:Unhook(GameTooltip, "FadeOut")
+	self:Unhook(GameTooltip, "Hide")
+	StarTip:SetOptionsDisabled(options, true)
+	if timer then
+		self:CancelTimer(timer)
+		timer = nil
+	end
+end
+
+function mod:GetOptions()
+	return options
+end
+
+-- CowTip's solution below
+local updateExistenceFrame = CreateFrame("Frame")
+local updateAlphaFrame = CreateFrame("Frame")
+
+local checkExistence = function()
+	if not UnitExists("mouseover") then
+		updateExistenceFrame:SetScript("OnUpdate", nil)
+		local kind
+		if GameTooltip:IsOwned(UIParent) then
+			kind = self.db.profile.units
+		else
+			kind = self.db.profile.unitFrames
+		end
+		if kind == 2 then
+			GameTooltip:FadeOut()
+		else
+			GameTooltip:Hide()
+		end
+	end
+end
+
+local checkTooltipAlpha = function()
+	if GameTooltip:GetAlpha() < 1 then
+		updateAlphaFrame:SetScript("OnUpdate", nil)
+		local kind
+		if GameTooltip:IsOwned(UIParent) then
+			kind = self.db.profile.objects
+		else
+			kind = self.db.profile.otherFrames
+		end
+		if kind == 2 then
+			GameTooltip:FadeOut()
+		else
+			GameTooltip:Hide()
+		end
+	end
+end
+
+function mod:OnShow()
+	if UnitExists("mouseover") then
+		updateExistenceFrame:SetScript("OnUpdate", checkExistence)
+	else
+		updateAlphaFrame:SetScript("OnUpdate", checkTooltipAlpha)
+	end
+end
+
+function mod:GameTooltipFadeOut(this, ...)
+	if self.justFade then
+		self.justFade = nil
+		self.hooks[this].FadeOut(this, ...)
+		return
+	end
+	local kind
+	if self.isUnit then
+		if GameTooltip:IsOwned(UIParent) then
+			kind = self.db.profile.units
+		else
+			kind = self.db.profile.unitFrames
+		end
+		self.isUnit = nil
+	else
+		if GameTooltip:IsOwned(UIParent) then
+			kind = self.db.profile.objects
+		else
+			kind = self.db.profile.otherFrames
+		end
+	end
+	if kind == 2 then
+		self.hooks[this].FadeOut(this, ...)
+	else
+		self.justHide = true
+		GameTooltip:Hide()
+	end
+end
+
+function mod:GameTooltipHide(this, ...)
+	if self.justHide then
+		self.justHide = nil
+		return self.hooks[this].Hide(this, ...)
+	end
+	local kind
+	if self.isUnit then
+		if GameTooltip:IsOwned(UIParent) then
+			kind = self.db.profile.units
+		else
+			kind = self.db.profile.unitFrames
+		end
+		self.isUnit = nil
+	else
+		if GameTooltip:IsOwned(UIParent) then
+			kind = self.db.profile.objects
+		else
+			kind = self.db.profile.otherFrames
+		end
+	end
+	if kind == 2 then
+		self.justFade = true
+		return GameTooltip:FadeOut()
+	else
+		return self.hooks[this].Hide(this, ...)
+	end
+end
+
+function mod:SetUnit()
+	self.isUnit = true
+end
+
diff --git a/Modules/Position.lua b/Modules/Position.lua
new file mode 100644
index 0000000..7ac613b
--- /dev/null
+++ b/Modules/Position.lua
@@ -0,0 +1,364 @@
+local mod = StarTip:NewModule("Position", "AceEvent-3.0", "AceHook-3.0")
+mod.name = "Positioning and Hiding"
+local _G = _G
+local GameTooltip = _G.GameTooltip
+local StarTip = _G.StarTip
+local UIParent = _G.UIParent
+local self = mod
+
+local defaults = {
+	profile = {
+		inCombat = 1,
+		anchor = 1,
+		unitFrames = 13,
+		other = 1,
+		inCombatXOffset = 0,
+		inCombatYOffset = 0,
+		anchorXOffset = 0,
+		anchorYOffset = 0,
+		unitFramesXOffset = 0,
+		unitFramesYOffset = 0,
+		otherXOffset = 0,
+		otherYOffset = 0
+	}
+}
+
+local anchors = {
+	"CURSOR_TOP",
+	"CURSOR_TOPRIGHT",
+	"CURSOR_TOPLEFT",
+	"CURSOR_BOTTOM",
+	"CURSOR_BOTTOMRIGHT",
+	"CURSOR_BOTTOMLEFT",
+	"CURSOR_LEFT",
+	"CURSOR_RIGHT",
+	"TOP",
+	"TOPRIGHT",
+	"TOPLEFT",
+	"BOTTOM",
+	"BOTTOMRIGHT",
+	"BOTTOMLEFT",
+	"RIGHT",
+	"LEFT",
+	"CENTER"
+}
+
+local anchorText = {
+	"Cursor Top",
+	"Cursor Top-right",
+	"Cursor Top-left",
+	"Cursor Bottom",
+	"Cursor Bottom-right",
+	"Cursor Bottom-left",
+	"Cursor Left",
+	"Cursor Right",
+	"Screen Top",
+	"Screen Top-right",
+	"Screen Top-left",
+	"Screen Bottom",
+	"Screen Bottom-right",
+	"Screen Bottom-left",
+	"Screen Right",
+	"Screen Left",
+	"Screen Center"
+}
+
+local opposites = {
+	TOP = "BOTTOM",
+	TOPRIGHT = "BOTTOMLEFT",
+	TOPLEFT = "BOTTOMRIGHT",
+	BOTTOM = "TOP",
+	BOTTOMRIGHT = "TOPLEFT",
+	BOTTOMLEFT = "TOPRIGHT",
+	LEFT = "RIGHT",
+	RIGHT = "LEFT",
+}
+
+local selections = {}
+for i, v in ipairs(anchorText) do
+	selections[i] = v
+end
+selections[#selections+1] = "Hide"
+
+local get = function(info)
+	return self.db.profile[info[#info]]
+end
+
+local set = function(info,v)
+	self.db.profile[info[#info]] = v
+end
+
+local minX = -math.floor(GetScreenWidth()/5 + 0.5) * 5
+local minY = -math.floor(GetScreenHeight()/5 + 0.5) * 5
+local maxX = math.floor(GetScreenWidth()/5 + 0.5) * 5
+local maxY = math.floor(GetScreenHeight()/5 + 0.5) * 5
+local options = {
+	anchor = {
+		name = "World Units",
+		desc = "Where to anchor the tooltip when mousing over world characters",
+		type = "select",
+		values = selections,
+		get = get,
+		set = set,
+		order = 4
+	},
+	anchorXOffset = {
+		name = "X-axis offset",
+		desc = "The x-axis offset used to position the tooltip in relationship to the anchor point",
+		type = "range",
+		min = minX,
+		max = maxX,
+		step = 1,
+		bigStep = 5,
+		get = get,
+		set = set,
+		order = 5
+	},
+	anchorYOffset = {
+		name = "Y-axis offset",
+		desc = "The y-axis offset used to position the tooltip in relationship to the anchor point",
+		type = "range",
+		min = minY,
+		max = maxY,
+		step = 1,
+		bigStep = 5,
+		get = get,
+		set = set,
+		order = 6
+
+	},
+	inCombatHeader = {
+		name = "",
+		type = "header",
+		order = 7
+	},
+	inCombat = {
+		name = "In Combat",
+		desc = "Where to anchor the world unit tooltip while in combat",
+		type = "select",
+		values = selections,
+		get = get,
+		set = set,
+		order = 8
+	},
+	inCombatXOffset = {
+		name = "X-axis offset",
+		desc = "The x-axis offset used to position the tooltip in relationship to the anchor point",
+		type = "range",
+		min = minX,
+		max = maxX,
+		step = 1,
+		bigStep = 5,
+		get = get,
+		set = set,
+		order = 9
+	},
+	inCombatYOffset = {
+		name = "Y-axis offset",
+		desc = "The y-axis offset used to position the tooltip in relationship to the anchor point",
+		type = "range",
+		min = minY,
+		max = maxY,
+		step = 1,
+		bigStep = 5,
+		get = get,
+		set = set,
+		order = 10
+	},
+	unitFramesHeader = {
+		name = "",
+		type = "header",
+		order = 11
+	},
+	unitFrames = {
+		name = "Unit Frames",
+		desc = "Where to anchor the tooltip when mousing over a unit frame",
+		type = "select",
+		values = selections,
+		get = get,
+		set = set,
+		order = 12
+	},
+	unitFramesXOffset = {
+		name = "X-axis offset",
+		desc = "The x-axis offset used to position the tooltip in relationship to the anchor point",
+		type = "range",
+		min = minX,
+		max = maxX,
+		step = 1,
+		bigStep = 5,
+		get = get,
+		set = set,
+		order = 13
+	},
+	unitFramesYOffset = {
+		name = "Y-axis offset",
+		desc = "The y-axis offset used to position the tooltip in relationship to the anchor point",
+		type = "range",
+		min = minY,
+		max = maxY,
+		step = 1,
+		bigStep = 5,
+		get = get,
+		set = set,
+		order = 14
+	},
+	otherHeader = {
+		name = "",
+		type = "header",
+		order = 15
+	},
+	other = {
+		name = "Other tooltips",
+		desc = "Where to anchor tooltips that are not unit tooltips",
+		type = "select",
+		values = selections,
+		get = get,
+		set = set,
+		order = 16
+	},
+	otherXOffset = {
+		name = "X-axis offset",
+		desc = "The x-axis offset used to position the tooltip in relationship to the anchor point",
+		type = "range",
+		min = minX,
+		max = maxX,
+		step = 1,
+		bigStep = 5,
+		get = get,
+		set = set,
+		order = 17
+	},
+	otherYOffset = {
+		name = "Y-axis offset",
+		desc = "The y-axis offset used to position the tooltip in relationship to the anchor point",
+		type = "range",
+		min = minY,
+		max = maxY,
+		step = 1,
+		bigStep = 5,
+		get = get,
+		set = set,
+		order = 18
+	}
+}
+
+function mod:OnInitialize()
+	self.db = StarTip.db:RegisterNamespace(self:GetName(), defaults)
+	StarTip:SetOptionsDisabled(options, true)
+end
+
+function mod:OnEnable()
+	self:RegisterEvent("REGEN_DISABLED")
+	self:RegisterEvent("REGEN_ENABLED")
+	self:SecureHook("GameTooltip_SetDefaultAnchor")
+	StarTip:SetOptionsDisabled(options, false)
+end
+
+function mod:OnDisable()
+	self:UnregisterEvent("REGEN_DISABLED")
+	self:UnregisterEvent("REGEN_ENABLED")
+	self:Unhook("GameTooltip_SetDefaultAnchor")
+	StarTip:SetOptionsDisabled(options, true)
+end
+
+function mod:GetOptions()
+	return options
+end
+
+local updateFrame = CreateFrame("Frame")
+local oldX, oldY
+local currentAnchor
+local xoffset, yoffset
+local positionTooltip = function()
+	local x, y = GetCursorPosition()
+	local effScale = GameTooltip:GetEffectiveScale()
+	if x ~= oldX or y ~= oldY then
+		GameTooltip:ClearAllPoints()
+		GameTooltip:SetPoint(currentAnchor, UIParent, "BOTTOMLEFT", (x + xoffset) / effScale, (y + yoffset) / effScale)
+	end
+	oldX, oldY = x, y
+end
+
+local getIndex = function(owner)
+	local index
+	if UnitExists("mouseover") then
+		if InCombatLockdown() then
+			index = self.db.profile.inCombat
+		elseif owner == UIParent then
+			index = self.db.profile.anchor
+		else
+			index = self.db.profile.unitFrames
+		end
+	else
+		index = self.db.profile.other
+	end
+	return index
+end
+
+local setOffsets = function(owner)
+	if owner == UIParent then
+		if UnitExists("mouseover") then
+			if InCombatLockdown() then
+				xoffset = self.db.profile.inCombatXOffset
+				yoffset = self.db.profile.inCombatYOffset
+			else
+				xoffset = self.db.profile.anchorXOffset
+				yoffset = self.db.profile.anchorYOffset
+			end
+		else
+			xoffset = self.db.profile.otherXOffset
+			yoffset = self.db.profile.otherYOffset
+		end
+	else
+		if UnitExists("mouseover") then
+			xoffset = self.db.profile.unitFramesXOffset
+			yoffset = self.db.profile.unitFramesYOffset
+		else
+			xoffset = self.db.profile.otherXOffset
+			yoffset = self.db.profile.otherYOffset
+		end
+	end
+end
+
+local currentOwner
+local currentThis
+local f = CreateFrame("Frame")
+local function delayAnchor()
+	f:SetScript("OnUpdate", nil)
+	local this = currentThis
+	local owner = currentOwner
+	this:ClearAllPoints()
+	setOffsets(owner)
+	local index = getIndex(owner)
+	if index == #selections then
+		this:Hide()
+		return
+	elseif anchors[index]:find("^CURSOR_")  then
+		oldX, oldY = 0, 0
+		currentAnchor = opposites[anchors[index]:sub(8)]
+		updateFrame:SetScript("OnUpdate", positionTooltip)
+		positionTooltip()
+	else
+		if updateFrame:GetScript("OnUpdate") then updateFrame:SetScript("OnUpdate", nil) end
+		this:SetPoint(anchors[index], UIParent, anchors[index], xoffset, yoffset)
+	end
+end
+
+function mod:GameTooltip_SetDefaultAnchor(this, owner)
+	currentOwner = owner
+	currentThis = this
+	if not f:GetScript("OnUpdate") then f:SetScript("OnUpdate", delayAnchor) end
+end
+
+function mod:REGEN_DISABLED()
+	if not currentOwner then return end
+	updateFrame:SetScript("OnUpdate", nil)
+	self:GameTooltip_SetDefaultAnchor(GameTooltip, currentOwner)
+end
+
+mod.REGEN_ENABLED = mod.REGEN_DISABLED
+
+function mod:OnHide()
+	updateFrame:SetScript("OnUpdate", nil)
+end
diff --git a/Modules/PvP.lua b/Modules/PvP.lua
new file mode 100644
index 0000000..c458f0d
--- /dev/null
+++ b/Modules/PvP.lua
@@ -0,0 +1,48 @@
+local mod = StarTip:NewModule("PvP", "AceEvent-3.0")
+mod.name = "PvP"
+local _G = _G
+local GameTooltip = _G.GameTooltip
+local UnitFactionGroup = _G.UnitFactionGroup
+local self = mod
+
+function mod:OnInitialize()
+	self.db = StarTip.db:RegisterNamespace(self:GetName(), defaults)
+	local frame = _G.CreateFrame("Frame", nil, GameTooltip)
+	local pvp = frame:CreateTexture(nil, "OVERLAY")
+	pvp:SetHeight(30)
+	pvp:SetWidth(30)
+	pvp:SetPoint("TOPRIGHT", GameTooltip, 20, 10)
+	pvp:Hide()
+	self.PvP = pvp
+end
+
+function mod:OnEnable()
+	self:RegisterEvent("UNIT_FACTION")
+end
+
+function mod:OnDisable()
+	self:UnregisterEvent("UNIT_FACTION")
+end
+
+function mod:UNIT_FACTION(event, unit)
+	if unit ~= "mouseover" then return end
+
+	local factionGroup = UnitFactionGroup(unit)
+	if(UnitIsPVPFreeForAll(unit)) then
+		self.PvP:SetTexture[[Interface\TargetingFrame\UI-PVP-FFA]]
+		self.PvP:Show()
+	elseif(factionGroup and UnitIsPVP(unit)) then
+		self.PvP:SetTexture([[Interface\TargetingFrame\UI-PVP-]]..factionGroup)
+		self.PvP:Show()
+	else
+		self.PvP:Hide()
+	end
+end
+
+function mod:SetUnit()
+	self:UNIT_FACTION(nil, "mouseover")
+end
+
+function mod:OnHide()
+	self.PvP:Hide()
+end
diff --git a/Modules/RaidIcon.lua b/Modules/RaidIcon.lua
new file mode 100644
index 0000000..bc7f51a
--- /dev/null
+++ b/Modules/RaidIcon.lua
@@ -0,0 +1,47 @@
+local mod = StarTip:NewModule("RaidIcon", "AceEvent-3.0")
+mod.name = "RaidIcon"
+local _G = _G
+local GameTooltip = _G.GameTooltip
+local GetRaidTargetIndex = _G.GetRaidTargetIndex
+local SetRaidTargetIconTexture = _G.SetRaidTargetIconTexture
+local self = mod
+
+function mod:OnInitialize()
+	self.db = StarTip.db:RegisterNamespace(self:GetName(), defaults)
+	local frame = CreateFrame("Frame", nil, GameTooltip)
+	local icon = frame:CreateTexture(nil, "OVERLAY")
+	icon:SetHeight(16)
+    icon:SetWidth(16)
+    icon:SetPoint("TOP", GameTooltip, 0, 4)
+    icon:SetTexture"Interface\\TargetingFrame\\UI-RaidTargetingIcons"
+	icon:Hide()
+	self.icon = icon
+end
+
+function mod:OnEnable()
+	self:RegisterEvent("RAID_TARGET_UPDATE")
+end
+
+function mod:OnDisable()
+	self:UnregisterEvent("RAID_TARGET_UPDATE")
+end
+
+function mod:SetUnit()
+	self:RAID_TARGET_UPDATE()
+end
+
+function mod:OnHide()
+	if self.icon:IsShown() then self.icon:Hide() end
+end
+
+function mod:RAID_TARGET_UPDATE(event)
+	local index = _G.GetRaidTargetIndex("mouseover")
+
+	if(index) then
+		_G.SetRaidTargetIconTexture(self.icon, index)
+		self.icon:Show()
+	else
+		self.icon:Hide()
+	end
+end
+
diff --git a/Modules/Targeting.lua b/Modules/Targeting.lua
new file mode 100644
index 0000000..a6e400c
--- /dev/null
+++ b/Modules/Targeting.lua
@@ -0,0 +1,34 @@
+local mod = StarTip:NewModule("Targeting", "AceEvent-3.0")
+mod.name = "Targeting"
+local _G = _G
+local GameTooltip = _G.GameTooltip
+local UnitFactionGroup = _G.UnitFactionGroup
+local RAID_CLASS_COLORS = _G.RAID_CLASS_COLORS
+local StarTip = _G.StarTip
+local self = mod
+
+function mod:OnInitialize()
+end
+
+function mod:OnEnable()
+end
+
+function mod:OnDisable()
+end
+
+function mod:SetUnit()
+	if UnitInRaid("player") then
+		local txt = ''
+		for i=1, GetNumRaidMembers() do
+			if UnitExists("mouseover") and UnitGUID("mouseover") == UnitGUID("raid" .. i .. "target") then
+				local c = RAID_CLASS_COLORS[select(2, UnitClass("raid" .. i))]
+				local name = UnitName("raid" .. i)
+				txt = txt .. ("|cFF%02x%02x%02x%s|r "):format(c.r*255, c.g*255, c.b*255, name)
+			end
+		end
+		if txt ~= '' then
+			GameTooltip:AddLine("Targeting: " .. txt, .5, .5, 1, 1)
+		end
+	end
+end
+
diff --git a/Modules/Text.lua b/Modules/Text.lua
new file mode 100644
index 0000000..0190883
--- /dev/null
+++ b/Modules/Text.lua
@@ -0,0 +1,730 @@
+local mod = StarTip:NewModule("Text", "AceTimer-3.0", "AceEvent-3.0")
+mod.name = "Text"
+local _G = _G
+local GameTooltip = _G.GameTooltip
+local StarTip = _G.StarTip
+local self = mod
+local GameTooltip = _G.GameTooltip
+local tinsert = _G.tinsert
+local unpack = _G.unpack
+local select = _G.select
+local format = _G.format
+local floor = _G.floor
+local tostring = _G.tostring
+local UnitExists = _G.UnitExists
+local UnitIsPlayer = _G.UnitIsPlayer
+local UnitBuff = _G.UnitBuff
+local GetSpellInfo = _G.GetSpellInfo
+local UnitIsConnected = _G.UnitIsConnected
+local UnitIsFeignDeath = _G.UnitIsFeignDeath
+local UnitIsGhost = _G.UnitIsGhost
+local UnitIsDead = _G.UnitIsDead
+local UnitLevel = _G.UnitLevel
+local UnitClassification = _G.UnitClassification
+local UnitSelectionColor = _G.UnitSelectionColor
+local UnitRace = _G.UnitRace
+local GetNumTalentTabs = _G.GetNumTalentTabs
+local GetTalentTabInfo = _G.GetTalentTabInfo
+local GetGuildInfo = _G.GetGuildInfo
+local UnitName = _G.UnitName
+local UnitClass = _G.UnitClass
+local UnitMana = _G.UnitMana
+local UnitManaMax = _G.UnitManaMax
+local UnitFactionGroup = _G.UnitFactionGroup
+local UnitCreatureFamily = _G.UnitCreatureFamily
+local UnitCreatureType = _G.UnitCreatureType
+local UnitIsUnit = _G.UnitIsUnit
+local RAID_CLASS_COLORS = _G.RAID_CLASS_COLORS
+local timer, talentTimer
+local TalentQuery = LibStub:GetLibrary("LibTalentQuery-1.0", true)
+local TalentGuess = LibStub:GetLibrary("TalentGuess-1.0", true) and LibStub:GetLibrary("TalentGuess-1.0"):Register()
+local spec = setmetatable({}, {__mode='v'})
+local factionList = {}
+local linesToAdd = {}
+local linesToAddR = {}
+local linesToAddG = {}
+local linesToAddB = {}
+local linesToAddRight = {}
+local linesToAddRightR = {}
+local linesToAddRightG = {}
+local linesToAddRightB = {}
+local unitLocation
+local unitName
+local unitGuild
+local NUM_LINES
+
+-- Thanks to ckknight for this
+local short = function(value)
+	if value >= 10000000 or value <= -10000000 then
+		value = ("%.1fm"):format(value / 1000000)
+	elseif value >= 1000000 or value <= -1000000 then
+		value = ("%.2fm"):format(value / 1000000)
+	elseif value >= 100000 or value <= -100000 then
+		value = ("%.0fk"):format(value / 1000)
+	elseif value >= 10000 or value <= -10000 then
+		value = ("%.1fk"):format(value / 1000)
+	else
+		value = tostring(floor(value+0.5))
+	end
+	return value
+end
+
+local classifications = {
+	worldboss = "Boss",
+	rareelite = "+ Rare",
+	elite = "+",
+	rare = "Rare"
+}
+
+local talentTrees = {
+	["Druid"] = {"Balance", "Feral Combat", "Restoration"},
+	["Hunter"] = {"Beast Mastery", "Marksmanship", "Survival"},
+	["Mage"] = {"Arcane", "Fire", "Frost"},
+	["Paladin"] = {"Holy", "Protection", "Retribution"},
+	["Priest"] = {"Discipline", "Holy", "Shadow"},
+	["Rogue"] = {"Assassination", "Combat", "Subtlety"},
+	["Shaman"] = {"Elemental", "Enhancement", "Restoration"},
+	["Warlock"] = {"Affliction", "Demonology", "Destruction"},
+	["Warrior"] = {"Arms", "Fury", "Protection"},
+}
+
+local powers = {
+	["WARRIOR"] = "Rage:",
+	["ROGUE"] = "Energy:",
+}
+
+powers = setmetatable(powers, {__index=function(self,key)
+	if type(key) == nil then return nil end
+	if rawget(self,key) then
+		return self[key]
+	else
+		return "Mana:"
+	end
+end})
+
+local updateTalents = function()
+	if not UnitExists("mouseover") then
+		self:CancelTimer(talentTimer)
+		talentTimer = nil
+		return
+	end
+	local nameRealm = select(1, UnitName("mouseover")) .. (select(2, UnitName("mouseover")) or '')
+	if spec[nameRealm] and spec[nameRealm][4] and spec[nameRealm][1] and spec[nameRealm][2] and spec[nameRealm][3] then
+		local specText = ('%s (%d/%d/%d)'):format(spec[nameRealm][4], spec[nameRealm][1], spec[nameRealm][2], spec[nameRealm][3])
+		local lineNum
+		if NUM_LINES < GameTooltip:NumLines() then
+			lineNum = NUM_LINES + 1
+			local j = 0
+			for i = lineNum, GameTooltip:NumLines() do
+				local left = mod.leftLines[i]
+				j = j + 1
+				linesToAdd[j] = left:GetText()
+				local r, g, b = left:GetTextColor()
+				linesToAddR[j] = r
+				linesToAddG[j] = g
+				linesToAddB[j] = b
+				local right = mod.rightLines[i]
+				if right:IsShown() then
+					linesToAddRight[j] = right:GetText()
+					local r, g, b = right:GetTextColor()
+					linesToAddRightR[j] = r
+					linesToAddRightG[j] = g
+					linesToAddRightB[j] = b
+				end
+			end
+		else
+			lineNum = GameTooltip:NumLines() + 1
+		end
+		local replaced
+		for i = 1, GameTooltip:NumLines() do
+			if mod.leftLines[i]:GetText() == "Talents Guessed:" then
+				lineNum = i
+				replaced = true
+			end
+		end
+		if not replaced then
+			GameTooltip:AddDoubleLine(' ', ' ')
+		end
+		local left = mod.leftLines[lineNum]
+		local right = mod.rightLines[lineNum]
+		left:SetText("Talents:")
+		right:SetText(specText)
+		if not right:IsShown() then
+			right:Show()
+		end
+		left:SetTextColor(1, 1, 1)
+		right:SetTextColor(1, 1, 1)
+
+		for i=1, #linesToAdd do
+			local left = mod.leftLines[i + lineNum]
+			left:SetText(linesToAdd[i])
+			left:SetTextColor(linesToAddR[i], linesToAddG[i], linesToAddB[i])
+			if linesToAddRight[i] then
+				local right = mod.rightLines[i + lineNum]
+				right:SetText(linesToAddRight[i])
+				right:SetTextColor(linesToAddRightR[i], linesToAddRightG[i], linesToAddRightB[i])
+			end
+			linesToAdd[i] = nil
+			linesToAddR[i] = nil
+			linesToAddG[i] = nil
+			linesToAddB[i] = nil
+			linesToAddRight[i] = nil
+			linesToAddRightR[i] = nil
+			linesToAddRightG[i] = nil
+			linesToAddRightB[i] = nil
+		end
+
+		self:CancelTimer(talentTimer)
+		talentTimer =  nil
+		GameTooltip:Show()
+	elseif spec[nameRealm] then
+		for k in pairs(spec[nameRealm]) do
+			spec[nameRealm][k] = nil
+		end
+		spec[nameRealm] = nil
+	end
+end
+
+local indexOf = function(t, val)
+	for i=1, #t do
+		if t[i] == val then
+			return i
+		end
+	end
+end
+
+local indicesOf = function(t, val)
+	local a = {}
+	for i=1, #t do
+		if t[i] == val then
+			tinsert(a, i)
+		end
+	end
+	return unpack(a)
+end
+
+function mod:TalentQuery_Ready(e, name, realm)
+	if not TalentQuery then return end
+	local nameRealm = name .. (realm or '')
+	local isnotplayer = (name ~= UnitName("player"))
+	if not spec[nameRealm] then
+		spec[nameRealm] = {[4]=NONE}
+		local highPoints = {}
+		local specNames = {}
+		for tab = 1, GetNumTalentTabs(isnotplayer) do
+			local treename, _, pointsspent = GetTalentTabInfo(tab, isnotplayer)
+			highPoints[tab] = pointsspent
+			spec[nameRealm][tab] = pointsspent
+			specNames[tab] = treename
+		end
+		if highPoints[1] == nil or highPoints[2] == nil or highPoints[3] == nil  then spec[nameRealm] = nil return end
+		table.sort(highPoints, function(a,b) return a>b end)
+		local first, second = select(1, indicesOf(spec[nameRealm], highPoints[1])), select(2, indicesOf(spec[nameRealm], highPoints[1]))
+		if highPoints[1] > 0 and highPoints[2] > 0 and highPoints[1] - highPoints[2] <= 5 and highPoints[1] ~= highPoints[2] then
+			spec[nameRealm][4] = specNames[indexOf(spec[nameRealm], highPoints[1])] .. "/" .. specNames[indexOf(spec[nameRealm], highPoints[2])]
+		elseif highPoints[1] > 0 and first and second then
+			spec[nameRealm][4] = specNames[first] .. "/" .. specNames[second]
+		elseif highPoints[1] > 0 then
+			spec[nameRealm][4] = specNames[indexOf(spec[nameRealm], highPoints[1])]
+		end
+	end
+end
+
+local unitHasAura = function(aura)
+	local i = 1
+	while true do
+		local buff = UnitBuff("mouseover", i, true)
+		if not buff then return end
+		if buff == aura then return true end
+		i = i + 1
+	end
+end
+
+local options = {
+	titles = {
+		name = "Titles",
+		desc = "Toggle whether to show titles or not",
+		type = "toggle",
+		set = function(info, v) self.db.profile.titles = v end,
+		get = function() return self.db.profile.titles end,
+		order = 5
+	},
+}
+
+local lines = setmetatable({
+	[1] = {
+		db = "Name:",
+		name = "UnitName",
+		left = function()
+			local c
+			if UnitIsPlayer("mouseover") then
+				c = RAID_CLASS_COLORS[select(2, UnitClass("mouseover"))]
+			else
+				c = {}
+				c.r, c.g, c.b = UnitSelectionColor("mouseover")
+			end
+			return unitName, c
+		end,
+		right = nil,
+		updating = false
+	},
+	[2] = {
+		db = "Target:",
+		name = "Target",
+		left = function() return "Target:" end,
+		right = function()
+			if UnitExists("mouseovertarget") then
+				local c
+				if UnitIsPlayer("mouseovertarget") then
+					c = RAID_CLASS_COLORS[select(2, UnitClass("mouseovertarget"))]
+				else
+					c = {}
+					c.r, c.g, c.b = UnitSelectionColor("mouseovertarget")
+				end
+				local name = UnitName("mouseovertarget")
+				return name, c
+			else
+				return "None", {r=1, g=1, b=1}
+			end
+		end,
+		updating = true
+	},
+	[3] = {
+		db = "Guild:",
+		name = "Guild",
+		left = function() return "Guild:" end,
+		right = function()
+			local guild = GetGuildInfo("mouseover")
+			if guild then return guild else return unitGuild end
+		end,
+		updating = false
+	},
+	[4] = {
+		db = "Rank:",
+		name = "Rank",
+		left = function() return "Rank:" end,
+		right =  function()
+			return select(2, GetGuildInfo("mouseover"))
+		end,
+		updating = false
+	},
+	[5] = {
+		db = "Realm:",
+		name = "Realm",
+		left = function() return "Realm:" end,
+		right = function()
+			return select(2, UnitName("mouseover"))
+		end,
+		updating = false
+	},
+	[6] = {
+		db = "Level:",
+		name = "Level",
+		left = function() return "Level:" end,
+		right = function()
+			local lvl = UnitLevel("mouseover")
+			local class = UnitClassification("mouseover")
+
+			if lvl <= 0 then
+				lvl = ''
+			end
+
+			if classifications[class] then
+				lvl = lvl .. classifications[class]
+			end
+
+			return lvl
+		end,
+		updating = false
+	},
+	[7] = {
+		db = "Race:",
+		name = "Race",
+		left = function() return "Race:" end,
+		right = function()
+			local race
+			if UnitIsPlayer("mouseover") then
+				race = UnitRace("mouseover");
+			else
+				race = UnitCreatureFamily("mouseover") or UnitCreatureType("mouseover")
+			end
+			return race
+		end,
+		updating = false
+	},
+	[8] = {
+		db = "Class:",
+		name = "Class",
+		left = function() return "Class:" end,
+		right = function()
+			local class = UnitClass("mouseover")
+			if class == UnitName("mouseover") then return end
+			local c = UnitIsPlayer("mouseover") and RAID_CLASS_COLORS[select(2, UnitClass("mouseover"))]
+			return class, c
+		end,
+		updating = false
+	},
+	[9] = {
+		db = "Faction:",
+		name = "Faction",
+		left = function() return "Faction:" end,
+		right = function()
+			return UnitFactionGroup("mouseover")
+		end,
+		updating = false
+	},
+	[10] = {
+		db = "Status:",
+		name = "Status",
+		left = function() return "Status:" end,
+		right = function()
+			if not UnitIsConnected("mouseover") then
+				return "Offline"
+			elseif unitHasAura(GetSpellInfo(19752)) then
+				return "Divine Intervention"
+			elseif UnitIsFeignDeath("mouseover") then
+				return "Feigned Death"
+			elseif UnitIsGhost("mouseover") then
+				return "Ghost"
+			elseif UnitIsDead("mouseover") and  unitHasAura(GetSpellInfo(20707)) then
+				return "Soulstoned"
+			elseif UnitIsDead("mouseover") then
+				return "Dead"
+			end
+		end,
+		updating = true
+	},
+	[11] = {
+		db = "Health:",
+		name = "Health",
+		left = function() return "Health:" end,
+		right = function()
+			local health, maxHealth = UnitHealth("mouseover"), UnitHealthMax("mouseover")
+			local value
+			if maxHealth == 100 then
+				value = health .. "%"
+			elseif maxHealth ~= 0 then
+				value = format("%s/%s (%d%%)", short(health), short(maxHealth), health/maxHealth*100)
+			end
+			return value
+		end,
+		updating = true
+	},
+	[12] = {
+		db = "Mana:",
+		name = "Mana",
+		left = function()
+			local class = select(2, UnitClass("mouseover"))
+			return powers[class]
+		end,
+		right = function()
+			local mana = UnitMana("mouseover")
+			local maxMana = UnitManaMax("mouseover")
+			local value
+			if maxMana == 100 then
+				value = mana
+			elseif maxMana ~= 0 then
+				value = format("%s/%s (%d%%)", short(mana), short(maxMana), mana/maxMana*100)
+			end
+			return value
+		end,
+		updating = true
+	},
+	[13] = {
+		db = "Location:",
+		name = "Location",
+		left = function() return "Location:" end,
+		right = function()
+			return unitLocation
+		end,
+		updating = true
+	},
+	[14] = {
+		db = "Talents:",
+		name = "Talents",
+		left = function() return "Talents:" end,
+		right = function()
+			local name = UnitName("mouseover")
+			if TalentQuery and UnitIsUnit("mouseover", "player") then
+				mod:TalentQuery_Ready(_, name)
+			elseif TalentQuery and UnitExists("mouseover") and UnitIsPlayer("mouseover") then
+				TalentQuery:Query("mouseover")
+				talentTimer = talentTimer or self:ScheduleRepeatingTimer(updateTalents, 0)
+			end
+		end,
+		updating = false
+	},
+	[15] = {
+		db = "TalentsGuessed:",
+		name = "Talents Guesssed",
+		left = function() return "Talents Guessed:" end,
+		right = function()
+			local name, realm = UnitName("mouseover")
+			local nameRealm
+			if realm then
+				nameRealm = name .. '-' .. realm
+			else
+				nameRealm = name
+			end
+			local specText
+			if nameRealm and TalentGuess and TalentGuess:GetTalents(nameRealm) then
+				local talents = {}
+				local high = 1
+				talents[1], talents[2], talents[3] = TalentGuess:GetTalents(nameRealm)
+				for i = 2, 3 do
+					if talents[i] > talents[high] then
+						high = i
+					end
+				end
+				local class = UnitClass("mouseover")
+				specText = ("%s (%d/%d/%d)"):format(talentTrees[class][high], talents[1], talents[2], talents[3])
+			end
+			return specText
+		end,
+		updating = true
+	}
+}, {__call=function(this)
+	local lineNum = 0
+	for i, v in ipairs(this) do
+		if self.db.profile[v.db] then
+			local left, right, c
+			if v.right then
+				right, c = v.right()
+				left = v.left()
+			else
+				right = ''
+				left, c = v.left()
+			end
+			if left and right then
+				lineNum = lineNum + 1
+				if v.right then
+					GameTooltip:AddDoubleLine(' ', ' ', 1, 1, 1, 1, 1, 1)
+					mod.leftLines[lineNum]:SetText(left)
+					mod.rightLines[lineNum]:SetText(right)
+					if type(c) == "table" and c.r then
+						mod.rightLines[lineNum]:SetVertexColor(c.r, c.g, c.b)
+					end
+				else
+					GameTooltip:AddLine(' ', 1, 1, 1)
+					mod.leftLines[lineNum]:SetText(left)
+					if type(c) == "table" and c.r then
+						mod.leftLines[lineNum]:SetVertexColor(c.r, c.g, c.b)
+					end
+				end
+			end
+		end
+	end
+	NUM_LINES = lineNum
+end})
+
+local function updateLines()
+	if not UnitExists("mouseover") then
+		mod:CancelTimer(timer)
+		timer = nil
+		return
+	end
+	for _, v in ipairs(lines) do
+		if v.updating and v.right and self.db.profile[v.db] then
+			local left = v.left()
+			local right, c = v.right()
+			if left and right then
+				for i = 1, NUM_LINES do
+					if mod.leftLines[i]:GetText() == left then
+						mod.rightLines[i]:SetText(right)
+						if type(c) == "table" and c.r then
+							mod.rightLines[i]:SetVertexColor(c.r, c.g, c.b)
+						end
+					end
+				end
+			end
+		end
+	end
+end
+
+local defaults = {profile={titles=true}}
+
+do
+	local lnum = 1
+	for i, v in ipairs(lines) do
+		options[v.db] = {
+			name = v.name,
+			desc = "Toggle showing this line",
+			type = "toggle",
+			set = function(info, val) self.db.profile[v.db] = val end,
+			get = function() return self.db.profile[v.db] end,
+			order = 5 + lnum
+		}
+		lnum = lnum + 1
+		defaults.profile[v.db] = true
+	end
+end
+
+function mod:OnInitialize()
+	self.db = StarTip.db:RegisterNamespace(self:GetName(), defaults)
+	self.leftLines = StarTip.leftLines
+	self.rightLines = StarTip.rightLines
+	self:RegisterEvent("UPDATE_FACTION")
+	StarTip:SetOptionsDisabled(options, true)
+end
+
+function mod:OnEnable()
+	local i = 1
+	while true do
+		if not self.db.profile[i] then break end
+		lines[i] = self.db.profile[i]
+		i = i + 1
+	end
+	if TalentQuery then TalentQuery.RegisterCallback(self, "TalentQuery_Ready") end
+	StarTip:SetOptionsDisabled(options, false)
+end
+
+function mod:OnDisable()
+	if TalentQuery then TalentQuery.UnregisterCallback(self, "TalentQuery_Ready") end
+	StarTip:SetOptionsDisabled(options, true)
+end
+
+function mod:GetOptions()
+	return options
+end
+
+function mod:UPDATE_FACTION()
+	for i = 1, GetNumFactions() do
+		local name = GetFactionInfo(i)
+		factionList[name] = true
+	end
+end
+
+
+local getName = function()
+	if self.db.profile.titles then
+		local name = self.leftLines[1]:GetText()
+		if UnitIsPlayer("mouseover") and name:find(" %- ") then
+			name = name:sub(1, name:find(" %- "))
+		end
+		return name
+	else
+		return UnitName("mouseover")
+	end
+end
+
+-- Taken from LibDogTag-Unit-3.0
+local LEVEL_start = "^" .. (type(LEVEL) == "string" and LEVEL or "Level")
+local getLocation = function()
+	if UnitIsVisible("mouseover") or not UnitIsConnected("mouseover") then
+		return nil
+	end
+
+	local left_2 = self.leftLines[2]:GetText()
+	local left_3 = self.leftLines[3]:GetText()
+	if not left_2 or not left_3 then
+		return nil
+	end
+	local hasGuild = not left_2:find(LEVEL_start)
+	local factionText = not hasGuild and left_3 or self.leftLines[4]:GetText()
+	if factionText == PVP then
+		factionText = nil
+	end
+	local hasFaction = factionText and not UnitPlayerControlled("mouseover") and not UnitIsPlayer("mouseover") and (UnitFactionGroup("mouseover") or factionList[factionText])
+	if hasGuild and hasFaction then
+		return self.leftLines[5]:GetText()
+	elseif hasGuild or hasFaction then
+		return self.leftLines[4]:GetText()
+	else
+		return left_3
+	end
+end
+
+local getGuild = function()
+	local left_2 = self.leftLines[2]:GetText()
+	if left_2:find(LEVEL_start) then return nil end
+	return "<" .. left_2 .. ">"
+end
+
+local ff = CreateFrame("Frame")
+function mod:SetUnit()
+	--[[self = mod
+	if not UnitExists("mouseover") then
+		if ff:GetScript("OnUpdate") then
+			ff:SetScript("OnUpdate", nil)
+		else
+			ff:SetScript("OnUpdate", self.SetUnit)
+		end
+		return
+	end]]
+
+	if ff:GetScript("OnUpdate") then ff:SetScript("OnUpdate", nil) end
+
+	unitName = getName()
+	unitLocation = getLocation()
+	unitGuild = getGuild()
+
+	-- Taken from CowTip
+	local lastLine = 2
+	local text2 = self.leftLines[2]:GetText()
+
+	if not text2 then
+		lastLine = lastLine - 1
+	elseif not text2:find("^"..LEVEL) then
+		lastLine = lastLine + 1
+	end
+	if not UnitPlayerControlled("mouseover") and not UnitIsPlayer("mouseover") then
+		local factionText = self.leftLines[lastLine + 1]:GetText()
+		if factionText == PVP then
+			factionText = nil
+		end
+		if factionText and (factionList[factionText] or UnitFactionGroup("mouseover")) then
+			lastLine = lastLine + 1
+		end
+	end
+	if not UnitIsConnected("mouseover") or not UnitIsVisible("mouseover") or UnitIsPVP("mouseover") then
+		lastLine = lastLine + 1
+	end
+
+	lastLine = lastLine + 1
+
+	for i = lastLine, GameTooltip:NumLines() do
+		local left = self.leftLines[i]
+		local j = i - lastLine + 1
+		linesToAdd[j] = left:GetText()
+		local r, g, b = left:GetTextColor()
+		linesToAddR[j] = r
+		linesToAddG[j] = g
+		linesToAddB[j] = b
+		local right = self.rightLines[i]
+		if right:IsShown() then
+			linesToAddRight[j] = right:GetText()
+			local r, g, b = right:GetTextColor()
+			linesToAddRightR[j] = r
+			linesToAddRightG[j] = g
+			linesToAddRightB[j] = b
+		end
+	end
+	-- End
+
+	GameTooltip:ClearLines()
+
+	lines()
+
+	-- Another part taken from CowTip
+	for i, left in ipairs(linesToAdd) do
+		local right = linesToAddRight[i]
+		if right then
+			GameTooltip:AddDoubleLine(left, right, linesToAddR[i], linesToAddG[i], linesToAddB[i], linesToAddRightR[i], linesToAddRightG[i], linesToAddRightB[i])
+		else
+			GameTooltip:AddLine(left, linesToAddR[i], linesToAddG[i], linesToAddB[i], true)
+		end
+		linesToAdd[i] = nil
+		linesToAddR[i] = nil
+		linesToAddG[i] = nil
+		linesToAddB[i] = nil
+		linesToAddRight[i] = nil
+		linesToAddRightR[i] = nil
+		linesToAddRightG[i] = nil
+		linesToAddRightB[i] = nil
+	end
+	-- End
+
+	timer = timer or self:ScheduleRepeatingTimer(updateLines, .5)
+
+	GameTooltip:Show()
+end