All pastes #561937 Raw Edit

Mine

public text v1 · immutable
·published 2007-06-12 16:56 UTC
rendered paste body
local InfoMsg=true;				-- Is info messages shown
local DebugMsg=false;			-- Is debug messages shown
local Loaded=false;				-- Is addon loaded
local ForceVisible=false;		-- If bar is forced to be shown
local FadeOut=false;			-- If bar is to be faded out

-- Spell data
local SpellName=nil;			-- Spell name, set to nil to disable
local SpellRank=0;				-- Spell rank
local SpellHealAmount=1;		-- Suspected healing amount
local SpellTarget=nil;			-- Spell target
local SpellTargetName=nil;		-- Target's name
local UpdateHealth=false;		-- Is target's health changed
local OverhealDetected=false;	-- If spell detected to do overhealing

-- Party data
local GroupMaxHeal={};			-- How much each party member can receive healing, sorted by name

-- Healing meter
local TotalHealing=0;			-- Total healing
local TotalOverHealing=0;		-- Total overhealing

-- Saved variables
HealingEstimatorData=
	{
	Scale=1;					-- Healing bar scale
	Limit=0.5;					-- Overhealing alert limit
	Icon=true;					-- If minimap icon is shown
	IconPos=4.98;				-- Position in radians
	IconAnchoredAtMinimap=true;	-- If icon is anchored to minimap
	ConvertCrits=true;			-- If crits are to be converted to normal heals, new in 1.2
	};
HealingEstimatorCharData={};	-- Spell data goes here

--[[
TODO:
- Different limits for different spells
- Fix localization in menu
]]--

--------------------------------------------------------------------
-- Debug print
--
function HealingEstimator_Print(Visible, Txt)
	if (Visible and Txt) then
		DEFAULT_CHAT_FRAME:AddMessage("|cFFA0A0FF[HealingEstimator]|r "..Txt,0.5,0.5,1);
	end
end

--------------------------------------------------------------------
-- Initialization
--------------------------------------------------------------------

--------------------------------------------------------------------
-- OnLoad
--
function HealingEstimator_OnLoad()
	-- Register events

	-- For initialization
	this:RegisterEvent("VARIABLES_LOADED");
end

--------------------------------------------------------------------
-- Init
--
function HealingEstimator_Init()
	if (Loaded) then
		return;
	end
	Loaded=true;

	-- Check if addon needs to be enabled
	if ( not HealingEstimator_IsHealingClass(UnitClass("player")) ) then
		return;
	end

	-- Register more events
	-- When unit heal changes
	this:RegisterEvent("UNIT_HEALTH");
	this:RegisterEvent("UNIT_MAXHEALTH");
	this:RegisterEvent("PARTY_MEMBERS_CHANGED");
	-- Healing spell you casted
	this:RegisterEvent("CHAT_MSG_SPELL_SELF_BUFF");
	this:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_SELF_BUFFS");
	this:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_CREATURE_BUFFS");
	this:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_FRIENDLYPLAYER_BUFFS");
	this:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_PARTY_BUFFS");
	-- For spells
	this:RegisterEvent("UNIT_SPELLCAST_SENT");
	this:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED");
	this:RegisterEvent("UNIT_SPELLCAST_STOP");
	this:RegisterEvent("UNIT_SPELLCAST_FAILED");
	this:RegisterEvent("UNIT_SPELLCAST_INTERRUPTED");

	-- Set slash command
	SlashCmdList["HEALINGESTIMATORCOMMAND"] = HealingEstimator_SlashHandler;
	SLASH_HEALINGESTIMATORCOMMAND1 = "/healingestimator";
	SLASH_HEALINGESTIMATORCOMMAND2 = "/heal";

	-- Set new saved variables
	if (HealingEstimatorData.ConvertCrits==nil) then HealingEstimatorData.ConvertCrits=true; end

	-- Init data
	GroupMaxHeal={};
	GroupMaxHeal[UnitName("player")]=UnitHealthMax("player")-UnitHealth("player");

	-- Set scale
	HealingEstimator:SetScale( HealingEstimatorData.Scale );

	-- Set BarBG color
	HealingEstimatorBarBG:SetVertexColor(0.4,0.4,0.4, 0.8);

	-- Replace text parsing functions on "difficult" locales
	local Locale=GetLocale();
	if (Locale=="frFR") then
		HealingEstimator_ParseSelfBuff=HealingEstimator_ParseSelfBuff_FR;
		HealingEstimator_ParsePeriodicSelfBuff=HealingEstimator_ParsePeriodicSelfBuff_FR;
	end

	-- Print welcome message
	HealingEstimator_Print(InfoMsg,HealingEstimatorLoc.Welcome);
end

--------------------------------------------------------------------
-- Slash command handling
--------------------------------------------------------------------

--------------------------------------------------------------------
-- Slash handler
--
function HealingEstimator_SlashHandler(msg)
	if (msg=="debug") then
		-- "/heal debug" - turn debug on/off
		if (DebugMsg) then
			DebugMsg=false;
		else
			DebugMsg=true;
		end

	else
		-- Show menu
		HealingEstimatorMenu:Show();
	end
end

--------------------------------------------------------------------
-- Event handling
--------------------------------------------------------------------

--------------------------------------------------------------------
-- OnEvent
--
function HealingEstimator_OnEvent(event)
	HealingEstimator_Print(DebugMsg,event);
	if (arg1) then HealingEstimator_Print(DebugMsg,"1: "..arg1) end;
	if (arg2) then HealingEstimator_Print(DebugMsg,"2: "..arg2) end;
	if (arg3) then HealingEstimator_Print(DebugMsg,"3: "..arg3) end;
	if (arg4) then HealingEstimator_Print(DebugMsg,"4: "..arg4) end;
	if (arg5) then HealingEstimator_Print(DebugMsg,"5: "..arg5) end;

	if (event=="UNIT_HEALTH" or event=="UNIT_MAXHEALTH") then
		-- Unit health changed
		-- Update max heal
		local Name=UnitName(arg1);
		if (Name) then
			GroupMaxHeal[Name]=UnitHealthMax(arg1)-UnitHealth(arg1);
			HealingEstimator_Print(DebugMsg,Name..": "..GroupMaxHeal[Name]);
		end
		-- Check if this is spell target
		if (arg1==SpellTarget) then
			UpdateHealth=true;
			
			-- Update maximum values if needed
			if (event=="UNIT_MAXHEALTH") then
				local MaxHealth=UnitHealthMax(arg1);
				HealingEstimatorBar:SetMinMaxValues(0,MaxHealth);
				HealingEstimatorHealingBar:SetMinMaxValues(0,MaxHealth);
				HealingEstimatorHealFlash:SetWidth( (SpellHealAmount*200)/MaxHealth );
			end
		end
	elseif (event=="PARTY_MEMBERS_CHANGED") then
		-- Party changed
		-- Clear data
		GroupMaxHeal={};
		-- Set player data
		GroupMaxHeal[UnitName("player")]=UnitHealthMax("player")-UnitHealth("player");

		-- Set group data
		local c;
		local Group,Count=HealingEstimator_GetGroupInfo();
		for c=1,Count do
			local Name=Group..c;
			if (UnitName(Name)) then
				GroupMaxHeal[UnitName(Name)]=UnitHealthMax(Name)-UnitHealth(Name);
			end
		end

	elseif (event=="UNIT_SPELLCAST_SENT" and arg1=="player") then
		-- Spellcast started, get spell info
		HealingEstimator_StartSpell(arg2,arg3,arg4);
		
	elseif (event=="UNIT_SPELLCAST_SUCCEEDED" and arg1=="player") then
		-- Spell succeeded
		FadeOut=true;
	elseif (event=="UNIT_SPELLCAST_FAILED" or 
			event=="UNIT_SPELLCAST_INTERRUPTED" or
			event=="UNIT_SPELLCAST_STOP" and arg1=="player") then
		-- Spell failed
		FadeOut=true;
		SpellName=nil;
		OverhealDetected=false;
		
	elseif (event=="CHAT_MSG_SPELL_SELF_BUFF") then
		-- You healed someone
		-- Parse combat log message at localized function
		HealingEstimator_AddHealing( true, HealingEstimator_ParseSelfBuff(arg1) );
		
		-- Show flash
		HealingEstimatorFlash:Show();
		HealingEstimatorHealFlash:SetPoint("LEFT","HealingEstimatorHealingBar","LEFT");
		HealingEstimatorHealFlash:Show();

	elseif (event=="CHAT_MSG_SPELL_PERIODIC_SELF_BUFFS" or
			event=="CHAT_MSG_SPELL_PERIODIC_CREATURE_BUFFS" or
			event=="CHAT_MSG_SPELL_PERIODIC_FRIENDLYPLAYER_BUFFS" or
			event=="CHAT_MSG_SPELL_PERIODIC_PARTY_BUFFS" ) then
		-- Your heal-over-time healed someone
		-- Parse combat log message at localized function
		HealingEstimator_AddHealing( false, HealingEstimator_ParsePeriodicSelfBuff(arg1) );

	elseif (event == "VARIABLES_LOADED") then
		-- Variables loaded, do initialization
		HealingEstimator_Init();
	end
end

--------------------------------------------------------------------
-- Misc functions
--------------------------------------------------------------------

--------------------------------------------------------------------
-- Adds healing data
--
function HealingEstimator_AddHealing(UpdateSpellData, Spell, Target, Heal, Crit)
	-- If spell name and heal value was resolved
	if (not (Spell and Target and Heal)) then
		-- Was not, bail out
		return;
	end

	-- Update healing amount
	Heal=Heal+0;		-- Convert healing amount to number
	TotalHealing=TotalHealing+Heal;
	
	-- Check that unit data exists
	if (not GroupMaxHeal[Target]) then 
		-- Somehow unit data was missing, do not process further
		return;
	end
	local MaxHeal=GroupMaxHeal[Target];

	-- Check overhealing
	if (Heal>MaxHeal) then
		TotalOverHealing=TotalOverHealing+(Heal-MaxHeal);
		local Over=floor(((Heal-MaxHeal)/Heal)*100);
		HealingEstimator_Print(DebugMsg,Over.."% overheal");
	end
	HealingEstimatorPie_SetValue(TotalOverHealing/TotalHealing);

	-- Check if healing data is to be updated to spell data
	if (not UpdateSpellData) then
		return;
	end

	-- Check if heal was critical
	if (Crit and HealingEstimatorData.ConvertCrits) then
		Heal=Heal/1.5;
	end

	-- Make sure that data exists
	HealingEstimator_Print(DebugMsg,Spell.." "..Heal);
	if (not HealingEstimatorCharData[Spell]) then return; end
	if (not HealingEstimatorCharData[Spell][SpellRank]) then return; end
	local Avg=HealingEstimatorCharData[Spell][SpellRank];
	if (Avg==0) then
		Avg=Heal;
	else
		Avg=(Avg*3 + Heal)/4;	
		--Avg=(Avg+Heal)/2;
	end
	HealingEstimatorCharData[Spell][SpellRank]=Avg;
end

--------------------------------------------------------------------
-- Returns healing data
--
function HealingEstimator_GetData()
	return TotalHealing,TotalOverHealing;
end

--------------------------------------------------------------------
-- Clears healing data
--
function HealingEstimator_ClearData()
	TotalHealing=0;
	TotalOverHealing=0;
	HealingEstimatorPie_SetValue(0);
	HealingEstimator_Print(InfoMsg,"Healing data cleared");
end

--------------------------------------------------------------------
-- Forces bar visible
--
function HealingEstimator_ForceVisible(Value)
	ForceVisible=Value;
	if (ForceVisible) then
		HealingEstimatorBar:SetMinMaxValues(0,1);
		HealingEstimatorBar:SetValue(1);
		HealingEstimatorHealingBar:SetMinMaxValues(0,1);
		HealingEstimatorHealingBar:SetValue(0);
		
		HealingEstimatorText:SetText(HealingEstimatorLoc.TargetName);
		HealingEstimatorFlash:Hide();
		HealingEstimatorHealFlash:Hide();
		HealingEstimator:SetAlpha(1);
		HealingEstimator:Show();
	else
		HealingEstimator:Hide();
	end	
end

--------------------------------------------------------------------
-- Tells if heal bar can be moved
--
function HealingEstimator_CanMove()
	if (IsShiftKeyDown() or ForceVisible) then
		return true;
	end
	return false;
end

--------------------------------------------------------------------
-- Gets the "name" and count of group
-- i.e. returns "party" or "raid", so those can be used like "partyX" or "raidX"
--
function HealingEstimator_GetGroupInfo()
	-- Assume that player is in raid
	local Group="raid";
	local Count=GetNumRaidMembers();

	-- If he is not in the group, assume party
	if (Count==0) then
		Group="party";
		Count=GetNumPartyMembers();
	end

	-- If he isn't even in raid, then he is alone
	if (Count==0) then
		Group=nil;
		Count=0;
	end

	-- Return group data
	return Group,Count;
end

--------------------------------------------------------------------
-- Tries to get group name for given unit
--
function HealingEstimator_GetGroupName(Name)
	if (UnitName("player")==Name) then
		return "player";
	else
		-- Check if unit is in group or raid
		local Group,Count = HealingEstimator_GetGroupInfo();
		if (Count~=0) then
			local c;
			for c=1,Count do
				if (UnitName(Group..c)==Name) then
					-- Unit was found, return
					HealingEstimator_Print(DebugMsg,"Group target: "..Group..c);
					return Group..c;
				end
			end
		end
	end
	-- Unit was not found from group or raid
	return nil;
end

--------------------------------------------------------------------
-- Sets visual data of the unit
--
function HealingEstimator_SetTargetData(Unit,HealAverage)
	-- Check that unit is not nil
	if (not Unit) then
		return;
	end

	-- Set target health bar
	local Health=UnitHealth(Unit);
	local MaxHealth=UnitHealthMax(Unit);
	HealingEstimatorBar:SetMinMaxValues(0,MaxHealth);
	HealingEstimatorBar:SetValue(Health);
	HealingEstimatorText:SetText(SpellTargetName);

	-- Set healing bar
	HealingEstimatorHealingBar:SetMinMaxValues(0,MaxHealth);
	HealingEstimatorHealingBar:SetValue(HealAverage);
	local Pos=(Health*200)/MaxHealth;
	HealingEstimatorHealingBar:SetPoint("LEFT","HealingEstimatorBar","LEFT",Pos,0);
	HealingEstimatorHealingBar:SetStatusBarColor(1,1,0);

	-- Set flash bar width
	HealingEstimatorHealFlash:SetWidth( (HealAverage*200)/MaxHealth );

	-- Show bars
	HealingEstimatorFlash:Hide();
	HealingEstimatorHealFlash:Hide();
	HealingEstimator:SetAlpha(1);
	HealingEstimator:Show();
	UpdateHealth=true;
	FadeOut=false;
end

--------------------------------------------------------------------
-- Find spell heal average
--
function HealingEstimator_FindSpellHealAverage(Spell,Rank)
	if (HealingEstimatorCharData[Spell]) then
		if (HealingEstimatorCharData[Spell][Rank]) then
			return HealingEstimatorCharData[Spell][Rank];
		end
	end

	-- Search the spell from spellbook
	local SpellId=1;
	local FoundSpell=nil;
	while true do
		-- Get spell name and rank
		local TempName,TempRank = GetSpellName(SpellId,BOOKTYPE_SPELL);
		if (not TempName) then 
			-- All spells tested, exit
			break;
		end
		
		-- Check spell name
		if (Spell==TempName and Rank==TempRank) then
			-- Correct spell found
			-- Set spell to tooltip and read healing average
			HealingEstimatorTooltip:ClearLines();
			HealingEstimatorTooltip:SetOwner(HealingEstimator,"ANCHOR_NONE");
			HealingEstimatorTooltip:SetSpell(SpellId,BOOKTYPE_SPELL);

			-- Read data from tooltip
			local HealAverage;
			local Text=HealingEstimatorTooltipTextLeft4:GetText();
			if (Text) then
				local t1,t2;
				for t1,t2 in string.gmatch(Text, HealingEstimatorLoc.HealValue) do
					HealingEstimator_Print(DebugMsg,"Heal: "..t1.." - "..t2);
					HealAverage=(t1+t2)/2;
					break;
				end
			end
			
			-- Add data to char data
			if (not HealingEstimatorCharData[Spell]) then
				HealingEstimatorCharData[Spell]={};
			end
			if (not HealingEstimatorCharData[Spell][Rank]) then
				HealingEstimatorCharData[Spell][Rank]={};
			end
			HealingEstimatorCharData[Spell][Rank]=HealAverage;
			
			-- Return value
			return HealAverage;
		end
		SpellId=SpellId+1;
	end
	
	-- Error, spell not found
	return nil;
end

--------------------------------------------------------------------
-- StartSpell
-- Does the initialization for spellcast
--
function HealingEstimator_StartSpell(Spell,Rank,Target)
	-- Check if spell is healing spell (localized function)
	if (not HealingEstimator_IsHealingSpell(Spell)) then
		-- It wasn't
		return;
	end

	-- Get spell info
	SpellName=Spell;
	SpellRank=Rank;
	SpellTarget=HealingEstimator_GetGroupName(Target);
	SpellTargetName=Target;
	SpellHealAmount=HealingEstimator_FindSpellHealAverage(Spell,Rank);

	-- Check that unit exists
	if ((not SpellTarget) or (not SpellHealAmount)) then
		-- Some error, no target, but casting heal
		return;
	end

	HealingEstimator_Print(DebugMsg,SpellName.." "..SpellRank.." ("..SpellHealAmount..") -> "..SpellTarget);

	-- Try to find target unit and set data
	HealingEstimator_SetTargetData(SpellTarget,SpellHealAmount);
end

--------------------------------------------------------------------
-- OnUpdate
-- Updates the target health, healing & overhealing values
--
function HealingEstimator_OnUpdate(Time)
	-- Check that there is some spell
	if (SpellName==nil or SpellTarget==nil or FadeOut==true) then
		--HealingEstimator:Hide();
		
		-- If this is forced to be visible don't fade out
		if (ForceVisible) then return; end
		
		-- Fade out
		local Alpha=this:GetAlpha()-Time;
		if (Alpha<0) then
			this:Hide();
		end
		-- Set alpha
		this:SetAlpha(Alpha);
		return;
	end

	-- Check if update is needed
	if (not UpdateHealth) then
		return;
	end
	UpdateHealth=false;

	-- Target is lost somehow
	if (UnitName(SpellTarget) ~= SpellTargetName) then
		SpellName=nil;
		OverhealDetected=false;
		return;
	end

	-- Find target health
	local Health=UnitHealth(SpellTarget);
	local MaxHealth=UnitHealthMax(SpellTarget);
	local MaxHeal=GroupMaxHeal[SpellTargetName];
	if (not MaxHeal) then
		-- Somehow unit data is missing
		MaxHeal=MaxHealth-Health;	-- Calculate "rough" value
	end

	-- Calculate healing and overhealing values
	local OverHealing=SpellHealAmount-MaxHeal;
	HealingEstimator_Print(DebugMsg,Health.."/"..MaxHealth..": "..SpellHealAmount.."("..OverHealing..")");

	if (OverHealing < 0) then
		-- There is no overhealing
		OverHealing=0;
	end

	-- Update target health bar
	HealingEstimatorBar:SetValue(Health);

	-- Set color of healing bar
	-- Yellow (1,1,0) if overhealing is under the limit
	-- Red    (1,0,0) if there is overhealing
	local Green=1;
	local Ratio=OverHealing/SpellHealAmount;
	OverhealDetected=false;
	if (Ratio > HealingEstimatorData.Limit) then
		-- Overhealing detected, change color to red
		OverhealDetected=true;
		Green=0;
	end
	HealingEstimatorHealingBar:SetStatusBarColor(1,Green,0);

	-- Update healing bar position
	local Pos=(Health*200)/MaxHealth;
	if (Pos>200) then
		Pos=200;
	end
	HealingEstimatorHealingBar:SetPoint("LEFT","HealingEstimatorBar","LEFT",Pos,0);
end