Quantcast
--[[--------------------------------------------------------------------
    Ovale Spell Priority
    Copyright (C) 2013, 2014 Johnny C. Lam

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License in the LICENSE
    file accompanying this program.
--]]--------------------------------------------------------------------

--[[
	This addon tracks Eclipse energy information on druids.
--]]

local _, Ovale = ...
local OvaleEclipse = Ovale:NewModule("OvaleEclipse", "AceEvent-3.0")
Ovale.OvaleEclipse = OvaleEclipse

--<private-static-properties>
-- Forward declarations for module dependencies.
local OvaleData = nil
local OvaleSpellBook = nil
local OvaleState = nil

local API_GetEclipseDirection = GetEclipseDirection
local API_UnitClass = UnitClass
local API_UnitGUID = UnitGUID
local API_UnitPower = UnitPower
local SPELL_POWER_ECLIPSE = SPELL_POWER_ECLIPSE

local OVALE_ECLIPSE_DEBUG = "eclipse"

-- Player's GUID.
local self_guid = nil
-- Player's class.
local _, self_class = API_UnitClass("player")

local LUNAR_ECLIPSE = ECLIPSE_BAR_LUNAR_BUFF_ID
local SOLAR_ECLIPSE = ECLIPSE_BAR_SOLAR_BUFF_ID
-- Nature's Grace: You gain 15% spell haste for 15 seconds each time you trigger an Eclipse.
local NATURES_GRACE = 16886
local CELESTIAL_ALIGNMENT = 112071
local EUPHORIA = 81062
local STARFALL = 48505
--</private-static-properties>

--<public-static-properties>
-- Direction that the eclipse status is moving: -1 = "lunar", 0 = "none", 1 = "solar".
OvaleEclipse.eclipse = 0
OvaleEclipse.eclipseDirection = 0
--<public-static-properties>

--<public-static-methods>
function OvaleEclipse:OnInitialize()
	-- Resolve module dependencies.
	OvaleData = Ovale.OvaleData
	OvaleSpellBook = Ovale.OvaleSpellBook
	OvaleState = Ovale.OvaleState
end

function OvaleEclipse:OnEnable()
	if self_class == "DRUID" then
		self_guid = API_UnitGUID("player")
		self:RegisterMessage("Ovale_SpecializationChanged")
	end
end

function OvaleEclipse:OnDisable()
	if self_class == "DRUID" then
		self:UnregisterMessage("Ovale_SpecializationChanged")
	end
end

function OvaleEclipse:Ovale_SpecializationChanged(event, specialization, previousSpecialization)
	if specialization == "balance" then
		self:Update()
		self:RegisterEvent("ECLIPSE_DIRECTION_CHANGE", "UpdateEclipseDirection")
		self:RegisterEvent("UNIT_POWER")
		self:RegisterEvent("UNIT_POWER_FREQUENT", "UNIT_POWER")
		self:RegisterMessage("Ovale_StanceChanged", "Update")
		self:RegisterMessage("Ovale_AuraAdded")
		OvaleState:RegisterState(self, self.statePrototype)
	else
		OvaleState:UnregisterState(self)
		self:UnregisterEvent("ECLIPSE_DIRECTION_CHANGE")
		self:UnregisterEvent("UNIT_POWER")
		self:UnregisterEvent("UNIT_POWER_FREQUENT")
		self:UnregisterMessage("Ovale_AuraAdded")
		self:UnregisterMessage("Ovale_StanceChanged")
	end
end

function OvaleEclipse:UNIT_POWER(event, unitId, powerToken)
	if unitId == "player" and powerToken == "ECLIPSE" then
		self:Update()
	end
end

function OvaleEclipse:Ovale_AuraAdded(event, timestamp, guid, spellId, caster)
	if guid == self_guid then
		if spellId == LUNAR_ECLIPSE or spellId == SOLAR_ECLIPSE then
			self:UpdateEclipseDirection()
		end
	end
end

function OvaleEclipse:Update()
	self:UpdateEclipse()
	self:UpdateEclipseDirection()
end

function OvaleEclipse:UpdateEclipse()
	self.eclipse = API_UnitPower("player", SPELL_POWER_ECLIPSE)
end

function OvaleEclipse:UpdateEclipseDirection()
	local direction = API_GetEclipseDirection()
	if direction == "moon" then
		self.eclipseDirection = -1
	elseif direction == "sun" then
		self.eclipseDirection = 1
	else -- if direction == "none" then
		if self.eclipse < 0 then
			self.eclipseDirection = -1
		elseif self.eclipse > 0 then
			self.eclipseDirection = 1
		else -- if self.eclipse == 0 then
			self.eclipseDirection = 0
		end
	end
end
--</public-static-methods>

--[[----------------------------------------------------------------------------
	State machine for simulator.

	AFTER: OvalePower
--]]----------------------------------------------------------------------------

--<public-static-properties>
OvaleEclipse.statePrototype = {}
--</public-static-properties>

--<private-static-properties>
local statePrototype = OvaleEclipse.statePrototype
--</private-static-properties>

--<state-properties>
-- Direction in which the Eclipse bar is moving.
statePrototype.eclipseDirection = nil
--</state-properties>

--<public-static-methods>
-- Initialize the state.
function OvaleEclipse:InitializeState(state)
	state.eclipseDirection = 0
end

-- Reset the state to the current conditions.
function OvaleEclipse:ResetState(state)
	state.eclipseDirection = self.eclipseDirection
end

-- Apply the effects of the spell on the player's state, assuming the spellcast completes.
function OvaleEclipse:ApplySpellAfterCast(state, spellId, targetGUID, startCast, endCast, nextCast, isChanneled, nocd, spellcast)
	local si = OvaleData.spellInfo[spellId]
	if si and si.eclipse then
		local eclipse = state.eclipse
		local direction = state.eclipseDirection
		local energy = si.eclipse

		if energy == 0 then
			-- Spell resets Eclipse energy to zero, but leaves the Eclipse direction intact.
			eclipse = 0
		else -- if energy ~= 0 then
			-- If there is no Eclipse direction yet, then start moving in the direction generated
			-- by the energy of the spellcast.
			if direction == 0 then
				direction = (energy < 0) and -1 or 1
			end
			if si.eclipsedir then
				energy = energy * direction
			end
			local aura = state:GetAura("player", CELESTIAL_ALIGNMENT, "HELPFUL", true)
			if state:IsActiveAura(aura) then
				-- Celestial Alignment prevents gaining Eclipse energy during its duration.
				energy = 0
			elseif OvaleSpellBook:IsKnownSpell(EUPHORIA) then
				local lunar = state:GetAura("player", LUNAR_ECLIPSE, "HELPFUL", true)
				local solar = state:GetAura("player", SOLAR_ECLIPSE, "HELPFUL", true)
				if not state:IsActiveAura(lunar) and not state:IsActiveAura(solar) then
					-- Euphoria: While not in an Eclipse state, your spells generate double the normal
					-- amount of Solar or Lunar energy.
					energy = energy * 2
				end
			end
			-- Only adjust Eclipse energy if the spell moves the Eclipse bar in the right direction.
			if (direction <= 0 and energy < 0) or (direction >= 0 and energy > 0) then
				Ovale:Logf("[%s] Eclipse %d -> %d", OVALE_ECLIPSE_DEBUG, eclipse, eclipse + energy)
				eclipse = eclipse + energy

				-- Crossing zero energy removes the corresponding Eclipse state.
				if direction < 0 and eclipse <= 0 then
					state:RemoveAuraOnGUID(self_guid, SOLAR_ECLIPSE, "HELPFUL", true, endCast)
				elseif direction > 0 and eclipse >= 0 then
					state:RemoveAuraOnGUID(self_guid, LUNAR_ECLIPSE, "HELPFUL", true, endCast)
				end

				-- Clamp Eclipse energy to min/max values and note that an Eclipse state will be reached after the spellcast.
				if eclipse <= -100 then
					eclipse = -100
					direction = 1
					Ovale:Logf("[%s] Adding Lunar Eclipse (%d) at %f", OVALE_ECLIPSE_DEBUG, LUNAR_ECLIPSE, endCast)
					state:AddAuraToGUID(self_guid, LUNAR_ECLIPSE, self_guid, "HELPFUL", endCast, math.huge, spellcast.snapshot)
					-- Reaching Lunar Eclipse resets the cooldown of Starfall.
					state:ResetSpellCooldown(STARFALL, endCast)
					-- Reaching Eclipse state grants Nature's Grace.
					state:AddAuraToGUID(self_guid, NATURES_GRACE, self_guid, "HELPFUL", endCast, endCast + 15, spellcast.snapshot)
				elseif eclipse >= 100 then
					eclipse = 100
					direction = -1
					Ovale:Logf("[%s] Adding Solar Eclipse (%d) at %f", OVALE_ECLIPSE_DEBUG, SOLAR_ECLIPSE, endCast)
					state:AddAuraToGUID(self_guid, SOLAR_ECLIPSE, self_guid, "HELPFUL", endCast, math.huge, spellcast.snapshot)
					-- Reaching Eclipse state grants Nature's Grace.
					state:AddAuraToGUID(self_guid, NATURES_GRACE, self_guid, "HELPFUL", endCast, endCast + 15, spellcast.snapshot)
				end
			end
		end
		state.eclipse = eclipse
		state.eclipseDirection = direction
	end
end
--</public-static-methods>