Quantcast
--[[
##############################################################################
_____/\\\\\\\\\\\____/\\\________/\\\__/\\\________/\\\__/\\\\\\\\\\\_       #
 ___/\\\/////////\\\_\/\\\_______\/\\\_\/\\\_______\/\\\_\/////\\\///__      #
  __\//\\\______\///__\//\\\______/\\\__\/\\\_______\/\\\_____\/\\\_____     #
   ___\////\\\__________\//\\\____/\\\___\/\\\_______\/\\\_____\/\\\_____    #
    ______\////\\\________\//\\\__/\\\____\/\\\_______\/\\\_____\/\\\_____   #
     _________\////\\\______\//\\\/\\\_____\/\\\_______\/\\\_____\/\\\_____  #
      __/\\\______\//\\\______\//\\\\\______\//\\\______/\\\______\/\\\_____ #
       _\///\\\\\\\\\\\/________\//\\\________\///\\\\\\\\\/____/\\\\\\\\\\\_#
        ___\///////////___________\///___________\/////////_____\///////////_#
##############################################################################
S U P E R - V I L L A I N - U I   By: Munglunch                              #
##############################################################################
##########################################################
LOCALIZED LUA FUNCTIONS
##########################################################
]]--
--[[ GLOBALS ]]--
local _G = _G;
local unpack 	= _G.unpack;
local select 	= _G.select;
local assert 	= _G.assert;
--[[
##########################################################
GET ADDON DATA
##########################################################
]]--
local SuperVillain, L = unpack(select(2, ...));
local MOD = SuperVillain.Registry:Expose('SVUnit');
local _, ns = ...
local oUF_SuperVillain = ns.oUF
--[[ MUNGLUNCH's FASTER ASSERT FUNCTION ]]--
local assert = enforce;
assert(oUF_SuperVillain, "SVUI was unable to locate oUF.");
local LSM = LibStub("LibSharedMedia-3.0");
--[[
##########################################################
LOCAL FUNCTIONS
##########################################################
]]--
local HEALTH_ANIM_FILE = [[Interface\Addons\SVUI\assets\artwork\Unitframe\UNIT-HEALTH-ANIMATION]];
local DEAD_MODEL_FILE = [[Spells\Monk_travelingmist_missile.m2]];

local Anim_OnUpdate = function(self)
	local parent = self.parent
	local coord = self._coords;
	parent:SetTexCoord(coord[1],coord[2],coord[3],coord[4])
end;

local Anim_OnPlay = function(self)
	local parent = self.parent
	parent:SetAlpha(1)
	if not parent:IsShown() then
		parent:Show()
	end
end;

local Anim_OnStop = function(self)
	local parent = self.parent
	parent:SetAlpha(0)
	if parent:IsShown() then
		parent:Hide()
	end
end;

local function SetNewAnimation(frame, animType, parent)
	local anim = frame:CreateAnimation(animType, subType)
	anim.parent = parent
	return anim
end

local function SetAnim(frame, parent)
	local speed = 0.08
	frame.anim = frame:CreateAnimationGroup("Sprite")
	frame.anim.parent = parent;
	frame.anim:SetScript("OnPlay", Anim_OnPlay)
	frame.anim:SetScript("OnFinished", Anim_OnStop)
	frame.anim:SetScript("OnStop", Anim_OnStop)

	frame.anim[1] = SetNewAnimation(frame.anim, "Translation", frame)
	frame.anim[1]:SetOrder(1)
	frame.anim[1]:SetDuration(speed)
	frame.anim[1]._coords = {0,0.5,0,0.25}
	frame.anim[1]:SetScript("OnUpdate", Anim_OnUpdate)

	frame.anim[2] = SetNewAnimation(frame.anim, "Translation", frame)
	frame.anim[2]:SetOrder(2)
	frame.anim[2]:SetDuration(speed)
	frame.anim[2]._coords = {0.5,1,0,0.25}
	frame.anim[2]:SetScript("OnUpdate", Anim_OnUpdate)

	frame.anim[3] = SetNewAnimation(frame.anim, "Translation", frame)
	frame.anim[3]:SetOrder(3)
	frame.anim[3]:SetDuration(speed)
	frame.anim[3]._coords = {0,0.5,0.25,0.5}
	frame.anim[3]:SetScript("OnUpdate", Anim_OnUpdate)

	frame.anim[4] = SetNewAnimation(frame.anim, "Translation", frame)
	frame.anim[4]:SetOrder(4)
	frame.anim[4]:SetDuration(speed)
	frame.anim[4]._coords = {0.5,1,0.25,0.5}
	frame.anim[4]:SetScript("OnUpdate", Anim_OnUpdate)

	frame.anim[5] = SetNewAnimation(frame.anim, "Translation", frame)
	frame.anim[5]:SetOrder(5)
	frame.anim[5]:SetDuration(speed)
	frame.anim[5]._coords = {0,0.5,0.5,0.75}
	frame.anim[5]:SetScript("OnUpdate", Anim_OnUpdate)

	frame.anim[6] = SetNewAnimation(frame.anim, "Translation", frame)
	frame.anim[6]:SetOrder(6)
	frame.anim[6]:SetDuration(speed)
	frame.anim[6]._coords = {0.5,1,0.5,0.75}
	frame.anim[6]:SetScript("OnUpdate", Anim_OnUpdate)

	frame.anim[7] = SetNewAnimation(frame.anim, "Translation", frame)
	frame.anim[7]:SetOrder(7)
	frame.anim[7]:SetDuration(speed)
	frame.anim[7]._coords = {0,0.5,0.75,1}
	frame.anim[7]:SetScript("OnUpdate", Anim_OnUpdate)

	frame.anim[8] = SetNewAnimation(frame.anim, "Translation", frame)
	frame.anim[8]:SetOrder(8)
	frame.anim[8]:SetDuration(speed)
	frame.anim[8]._coords = {0.5,1,0.75,1}
	frame.anim[8]:SetScript("OnUpdate", Anim_OnUpdate)

	frame.anim:SetLooping("REPEAT")
end

local function updateFrequentUpdates(self)
	local health = self.Health
	if health.frequentUpdates and not self:IsEventRegistered("UNIT_HEALTH_FREQUENT") then
		if GetCVarBool("predictedHealth") ~= 1 then
			SetCVar("predictedHealth", 1)
		end

		self:RegisterEvent('UNIT_HEALTH_FREQUENT', Path)

		if self:IsEventRegistered("UNIT_HEALTH") then
			self:UnregisterEvent("UNIT_HEALTH", Path)
		end
	elseif not self:IsEventRegistered("UNIT_HEALTH") then
		self:RegisterEvent('UNIT_HEALTH', Path)

		if self:IsEventRegistered("UNIT_HEALTH_FREQUENT") then
			self:UnregisterEvent("UNIT_HEALTH_FREQUENT", Path)
		end
	end
end

local CustomUpdate = function(self, event, unit)
	if(self.unit ~= unit) or not unit then return end
	local health = self.Health

	local min, max = UnitHealth(unit), UnitHealthMax(unit)
	local disconnected = not UnitIsConnected(unit)
	local invisible = ((min == max) or UnitIsDeadOrGhost(unit) or disconnected);
	local tapped = (UnitIsTapped(unit) and (not UnitIsTappedByPlayer(unit)));

	if health.fillInverted then
		health:SetReverseFill(true)
	end

	health:SetMinMaxValues(-max, 0)
	health:SetValue(-min)

	health.disconnected = disconnected

	if health.frequentUpdates ~= health.__frequentUpdates then
		health.__frequentUpdates = health.frequentUpdates
		updateFrequentUpdates(self)
	end

	local bg = health.bg;
	local mu = (min / max);

	if(invisible or not health.overlayAnimation) then
		health.animation[1].anim:Stop()
		health.animation[1]:SetAlpha(0)
	end

	if(invisible) then
		health:SetStatusBarColor(0.6,0.4,1,0.5)
		health.animation[1]:SetVertexColor(0.8,0.3,1,0.4)
	elseif(health.colorOverlay) then
		local t = health.colors.health
		health:SetStatusBarColor(t[1], t[2], t[3], 0.9)
	else
		health:SetStatusBarColor(1, 0.25 * mu, 0, 0.85)
		health.animation[1]:SetVertexColor(1, 0.1 * mu, 0, 0.5)
	end

	if(bg) then
		bg:SetVertexColor(0,0,0,0)
	end

	if(health.overlayAnimation and not invisible) then
		if(mu <= 0.25) then
			health.animation[1]:SetAlpha(1)
			health.animation[1].anim:Play()
		else
			health.animation[1].anim:Stop()
			health.animation[1]:SetAlpha(0)
		end
	end

	if self.ResurrectIcon then
		self.ResurrectIcon:SetAlpha(min == 0 and 1 or 0)
	end;
	if self.isForced then
		local current = random(1,max)
		health:SetValue(-current)
	end;

	-- local portrait = self.Portrait
	-- if(portrait and portrait:IsObjectType'Model') then
	-- 	if(UnitIsDeadOrGhost(unit) and not portrait.isdead) then
	-- 		portrait:SetCamDistanceScale(1)
	-- 		portrait:SetPortraitZoom(0)
	-- 		portrait:SetPosition(4,-1,1)
	-- 		portrait:ClearModel()
	-- 		portrait:SetModel(DEAD_MODEL_FILE)
	-- 		portrait.isdead = true
	-- 		portrait.guid = nil
	-- 	elseif(not UnitIsDeadOrGhost(unit) and portrait.isdead == true) then
	-- 		portrait.isdead = nil
	-- 		MOD.Update3DPortrait(self, event, unit)
	-- 	end
	-- end
end;

local Update = function(self, event, unit)
	if(self.unit ~= unit) or not unit then return end
	local health = self.Health
	local min, max = UnitHealth(unit), UnitHealthMax(unit)
	local disconnected = not UnitIsConnected(unit)
	if health.fillInverted then
		health:SetReverseFill(true)
	end
	health:SetMinMaxValues(0, max)

	if(disconnected) then
		health:SetValue(max)
	else
		health:SetValue(min)
	end

	health.disconnected = disconnected

	if health.frequentUpdates ~= health.__frequentUpdates then
		health.__frequentUpdates = health.frequentUpdates
		updateFrequentUpdates(self)
	end

	local bg = health.bg;
	local db = self:GetParent().db or SuperVillain.db.SVUnit;
	local r, g, b, t, t2;

	if(health.colorTapping and not UnitPlayerControlled(unit) and UnitIsTapped(unit) and not UnitIsTappedByPlayer(unit) and not UnitIsTappedByAllThreatList(unit)) then
		t = health.colors.tapped
	elseif(health.colorDisconnected and not UnitIsConnected(unit)) then
		t = health.colors.disconnected
	elseif(health.colorClass and UnitIsPlayer(unit)) or
		(health.colorClassNPC and not UnitIsPlayer(unit)) or
		(health.colorClassPet and UnitPlayerControlled(unit) and not UnitIsPlayer(unit)) then
		local _, class = UnitClass(unit)
		local tmp = oUF_SuperVillain.colors.class[class] or health.colors.health
		t = {(tmp[1] * 0.75),(tmp[2] * 0.75),(tmp[3] * 0.75)}
		if(bg and db.classbackdrop and UnitIsPlayer(unit)) then
			t2 = t
		end
	elseif(health.colorReaction and UnitReaction(unit, 'player')) then
		t = oUF_SuperVillain.colors.reaction[UnitReaction(unit, "player")]
		if(bg and db.classbackdrop and not UnitIsPlayer(unit) and UnitReaction(unit, "player")) then
			t2 = t
		end
	elseif(health.colorSmooth) then
		r, g, b = oUF_SuperVillain.ColorGradient(min, max, unpack(health.smoothGradient or oUF_SuperVillain.colors.smooth))
	elseif(health.colorHealth) then
		t = health.colors.health
	end

	if(t) then
		r, g, b = t[1], t[2], t[3]
	end

	if(b) then
		if(db.healthclass == true and db.colorhealthbyvalue == true or db.colorhealthbyvalue and self.isForced and not(UnitIsTapped(unit) and not UnitIsTappedByPlayer(unit))) then
			r, g, b = oUF_SuperVillain.ColorGradient(min,max,1,0,0,1,1,0,r,g,b)
		end
		health:SetStatusBarColor(r, g, b)
		if(bg) then
			local mu = bg.multiplier or 1
			if(t2) then
				r, g, b = t2[1], t2[2], t2[3]
			end
			bg:SetVertexColor(r * mu, g * mu, b * mu)
		end
	end

	if self.ResurrectIcon then
		self.ResurrectIcon:SetAlpha(min == 0 and 1 or 0)
	end;
	if self.isForced then
		min = random(1,max)
		health:SetValue(min)
	end;
	if self.db and self.db.gridMode then
		health:SetOrientation("VERTICAL")
	end;

	local portrait = self.Portrait
	if(portrait and portrait:IsObjectType'Model') then
		if(UnitIsDeadOrGhost(unit) and not portrait.isdead) then
			portrait:SetCamDistanceScale(1)
			portrait:SetPortraitZoom(0)
			portrait:SetPosition(4,-1,1)
			portrait:ClearModel()
			portrait:SetModel(DEAD_MODEL_FILE)
			portrait.isdead = true
			portrait.guid = nil
		elseif(not UnitIsDeadOrGhost(unit) and portrait.isdead == true) then
			portrait.isdead = nil
			MOD.Update3DPortrait(self, event, unit)
		end
	end
end
--[[
##########################################################
BUILD FUNCTION
##########################################################
]]--
function MOD:CreateHealthBar(frame, hasbg, text, reverse)
	local healthBar = CreateFrame("StatusBar", nil, frame)
	healthBar:SetFrameStrata("LOW")
	healthBar:SetFrameLevel(4)
	healthBar.colors = {}
	healthBar.colors.health = oUF_SuperVillain.colors.health
	healthBar.colors.tapped = oUF_SuperVillain.colors.tapped
	healthBar.colors.disconnected = oUF_SuperVillain.colors.disconnected
	healthBar:SetStatusBarTexture(SuperVillain.Media.bar.default)
	if hasbg then
		healthBar.bg = healthBar:CreateTexture(nil, "BORDER")
		healthBar.bg:SetAllPoints()
		healthBar.bg:SetTexture(SuperVillain.Media.bar.gradient)
		healthBar.bg:SetVertexColor(0.4, 0.1, 0.1)
		healthBar.bg.multiplier = 0.25
	end;

	local flasher = CreateFrame("Frame", nil, frame)
	flasher:SetFrameLevel(3)
	flasher:SetAllPoints(healthBar)

	flasher[1] = flasher:CreateTexture(nil, "OVERLAY", nil, 1)
	flasher[1]:SetTexture(HEALTH_ANIM_FILE)
	flasher[1]:SetTexCoord(0, 0.5, 0, 0.25)
	flasher[1]:SetVertexColor(1, 0.3, 0.1, 0.5)
	flasher[1]:SetBlendMode("ADD")
	flasher[1]:SetAllPoints(flasher)
	SetAnim(flasher[1], flasher)
	flasher:Hide()

	if text then
		healthBar.value = healthBar:CreateFontString(nil, "OVERLAY")
		healthBar.value.db = "health"
		MOD:SetUnitFont(healthBar.value)
		healthBar.value:SetParent(frame.InfoPanel)
		local offset = reverse and 2 or -2;
		local direction = reverse and "LEFT" or "RIGHT";
		healthBar.value:Point(direction, healthBar, direction, offset, 0)
	end;

	healthBar.animation = flasher
	healthBar.noupdate = false;
	healthBar.fillInverted = reverse;
	healthBar.colorTapping = true;
	healthBar.colorDisconnected = true
	healthBar.Override = Update;
	return healthBar
end;

function MOD:RefreshHealthBar(frame)
	if(frame.db and frame.db.portrait) then
		local db = frame.db.portrait
		local useOverlayHealth = (db.enable and db.overlay);
		if useOverlayHealth then
			frame.Health:SetStatusBarTexture(SuperVillain.Media.bar.default)
			frame.Health.Override = CustomUpdate;
		else
			frame.Health:SetStatusBarTexture(LSM:Fetch("statusbar", MOD.db.statusbar))
			frame.Health.Override = Update;
		end
	end;
end