Quantcast
--[[--------------------------------------------------------------------
    Copyright (C) 2012 Sidoine De Wispelaere.
    Copyright (C) 2012, 2013, 2014 Johnny C. Lam.
    See the file LICENSE.txt for copying permission.
--]]--------------------------------------------------------------------

local OVALE, Ovale = ...
local OvaleData = Ovale:NewModule("OvaleData")
Ovale.OvaleData = OvaleData

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

local format = string.format
local gmatch = string.gmatch
local type = type
local pairs = pairs
local tonumber = tonumber
local INFINITY = math.huge

-- Registered "run-time requirement" handlers: self_requirement[name] = handler
-- Handler is invoked as handler(state, name, tokenIterator, target).
local self_requirement = {}

local STAT_NAMES = { "agility", "bonus_armor", "crit", "haste", "intellect", "mastery", "multistrike", "spirit", "spellpower", "strength", "versatility" }
local STAT_FULLNAME = {
	agi = "agility",
	int = "intellect",
	str = "strength",
	spi = "spirit",
}
local TRINKET_USE_NAMES = { "proc", "stacking_proc", "stacking_stat", "stat" }
--<private-static-properties>

--<public-static-properties>
OvaleData.itemList = {}
--spell info from the current script (by spellId)
OvaleData.spellInfo = {}

OvaleData.buffSpellList =
{
	-- Debuffs
	fear_debuff = {
		[  5246] = true, -- Intimidating Shout
		[  5484] = true, -- Howl of terror
		[  5782] = true, -- Fear
		[  8122] = true, -- Psychic scream
	},
	incapacitate_debuff = {
		[  6770] = true, -- Sap
		[ 12540] = true, -- Gouge
		[ 20066] = true, -- Repentance
		[137460] = true, -- Incapacitated
	},
	root_debuff = {
		[   122] = true, -- Frost Nova
		[   339] = true, -- Entangling Roots
	},
	stun_debuff = {
		[   408] = true, -- Kidney Shot
		[   853] = true, -- Hammer of Justice
		[  1833] = true, -- Cheap Shot
		[  5211] = true, -- Mighty Bash
		[ 46968] = true, -- Shockwave
	},

	-- Raid buffs
	attack_power_multiplier_buff = {
		[  6673] = true, -- Battle Shout (warrior)
		[ 19506] = true, -- Trueshot Aura (hunter)
		[ 57330] = true, -- Horn of Winter (death knight)
	},
	critical_strike_buff = {
		[  1459] = true, -- Arcane Brillance (mage)
		[ 24604] = true, -- Furious Howl (wolf)
		[ 24932] = true, -- Leader of the Pack (feral druid)
		[ 61316] = true, -- Dalaran Brilliance (mage)
		[ 90309] = true, -- Terrifying Roar (devilsaur)
		[ 90363] = true, -- Embrace of the Shale Spider (shale spider)
		[ 97229] = true, -- Bellowing Roar (hydra)
		[116781] = true, -- Legacy of the White Tiger (bremaster/windwalker monk)
		[126309] = true, -- Still Water (water strider)
		[126373] = true, -- Fearless Roar (quilen)
		[160052] = true, -- Strength of the Pack (raptor)
	},
	haste_buff = {
		[ 49868] = true, -- Mind Quickening (shadow priest)
		[ 55610] = true, -- Unholy Aura (frost/unholy death knight)
		[113742] = true, -- Swiftblade's Cunning (rogue)
		[128432] = true, -- Cackling Howl (hyena)
		[135678] = true, -- Energizing Spores (sporebat)
		[160003] = true, -- Savage Vigor (rylak)
		[160074] = true, -- Speed of the Swarm (wasp)
	},
	mastery_buff = {
		[ 19740] = true, -- Blessing of Might (paladin)
		[ 24907] = true, -- Moonkin aura (balance druid)
		[ 93435] = true, -- Roar of Courage (cat)
		[116956] = true, -- Grace of Air (shaman)
		[128997] = true, -- Spirit Beast Blessing (spirit beast)
		[155522] = true, -- Power of the Grave (blood death knight)
		[160073] = true, -- Plainswalking (tallstrider)
	},
	multistrike_buff = {
		[ 24844] = true, -- Breath of the Winds (wind serpent)
		[ 34889] = true, -- Spry Attacks (dragonhawk)
		[ 49868] = true, -- Mind Quickening (shadow priest)
		[ 57386] = true, -- Wild Strength (clefthoof)
		[ 58604] = true, -- Double Bite (core hound)
		[109773] = true, -- Dark Intent (warlock)
		[113742] = true, -- Swiftblade's Cunning (rogue)
		[166916] = true, -- Windflurry (windwalker monks)
	},
	spell_power_multiplier_buff = {
		[  1459] = true, -- Arcane Brilliance (mage)
		[ 61316] = true, -- Dalaran Brilliance (mage)
		[ 90364] = true, -- Qiraji Fortitude (silithid)
		[109773] = true, -- Dark Intent (warlock)
		[126309] = true, -- Still Water (water strider)
		[128433] = true, -- Serpent's Cunning (serpent)
	},
	stamina_buff = {
		[   469] = true, -- Commanding Shout (warrior)
		[ 21562] = true, -- Power Word: Fortitude (priest)
		[ 50256] = true, -- Invigorating Roar (bear)
		[ 90364] = true, -- Qiraji Fortitude (silithid)
		[160003] = true, -- Savage Vigor (rylak)
		[160014] = true, -- Sturdiness (goat)
		[166928] = true, -- Blood Pact (warlock)
	},
	str_agi_int_buff = {
		[  1126] = true, -- Mark of the Wild (druid)
		[ 20217] = true, -- Blessing of Kings (paladin)
		[ 90363] = true, -- Embrace of the Shale Spider (shale spider)
		[115921] = true, -- Legacy of the Emperor (mistweaver monk)
		[116781] = true, -- Legacy of the White Tiger (brewmaster/windwalker monk)
		[159988] = true, -- Bark of the Wild (dog, riverbeast)
		[160017] = true, -- Blessing of Kongs (gorilla)
		[160077] = true, -- Strength of the Earth (worm)
	},
	versatility_buff = {
		[  1126] = true, -- Mark of the Wild (druid)
		[ 35290] = true, -- Indomitable (boar)
		[ 50518] = true, -- Chitinous Armor (ravager)
		[ 55610] = true, -- Unholy Aura (frost/unholy death knight)
		[ 57386] = true, -- Wild Strength (clefthoof)
		[159735] = true, -- Tenacity (bird of prey)
		[160045] = true, -- Defensive Quills (porcupine)
		[160077] = true, -- Strength of the Earth (worm)
		[167187] = true, -- Sanctity Aura (retribution paladins)
		[167188] = true, -- Inspiring Presence (arms/fury warriors)
	},

	-- Target debuffs
	bleed_debuff = {
		[  1079] = true, -- Rip (feral druid)
		[ 16511] = true, -- Hemorrhage (subtlety rogue)
		[ 33745] = true, -- Lacerate (guardian druid)
		[ 77758] = true, -- Thrash (druid)
		[113344] = true, -- Bloodbath (warrior)
		[115767] = true, -- Deep Wounds (warrior)
		[122233] = true, -- Crimson Tempest (rogue)
		[154953] = true, -- Internal Bleeding (rogue)
		[155722] = true, -- Rake (cat druid)
	},
	healing_reduced_debuff = {
		[  8680] = true, -- Wound Poison (rogue)
		[ 54680] = true, -- Monstrous Bite (devilsaur)
		[115804] = true, -- Mortal Wounds (arms/fury warriors, windwalker monk, warlock, carrion bird, scorpid)
	},

	-- Target buffs
	enrage_buff = {
		[ 12880] = true, -- Enrage (warrior)
		[ 18499] = true, -- Berserker Rage (warrior)
	},
	stealthed_buff = {
		[  1784] = true, -- Stealth
		[  5215] = true, -- Prowl
		[ 11327] = true, -- Vanish
		[ 24450] = true, -- Prowl (cat)
		[ 51713] = true, -- Shadow Dance (subtlety rogue); not truly "stealth" but functions like it for spell usage.
		[ 58984] = true, -- Shadowmeld
		[ 90328] = true, -- Spirit Walk (spirit beast)
		[102543] = true, -- Incarnation: King of the Jungle (feral druid); not truly "stealth" but functions like it for spell usage.
		[148523] = true, -- Jade Mist
		[115191] = true, -- Stealth (Subterfuge-talented rogue)
		[115192] = true, -- Subterfuge (rogue); not truly "stealth" but functions like it for spell usage.
		[115193] = true, -- Vanish (Subterfuge-talented rogue)
	},

	-- Raid buffs (short term)
	burst_haste_buff = {
		[  2825] = true, -- Bloodlust (Horde shaman)
		[ 32182] = true, -- Heroism (Alliance shaman)
		[ 80353] = true, -- Time Warp (mage)
		[ 90355] = true, -- Ancient Hysteria (core hound, nether ray)
	},
	burst_haste_debuff = {
		[ 57723] = true, -- Exhaustion (Heroism)
		[ 57724] = true, -- Sated (Bloodlust)
		[ 80354] = true, -- Temporal Displacement (Time Warp)
		[ 95809] = true, -- Insanity (Ancient Hysteria)
	},
	raid_movement_buff = {
		[106898] = true, -- Stampeding Roar
	},

	-- Trinket buffs
	trinket_proc_agility_buff = {
--		[126554] = true, -- Bottle of Infinite Stars
		[126690] = true, -- PvP agility trinket (on-use)
		[126707] = true, -- PvP agility trinket (proc)
		[126708] = true, -- PvP agility trinket (proc)
--		[128984] = true, -- Relic of Xuen (agility)
--		[138699] = true, -- Vicious Talisman of the Shado-Pan Assault
--		[138938] = true, -- Bad Juju
		[146308] = true, -- Assurance of Consequence
		[146310] = true, -- Ticking Ebon Detonator
		[148896] = true, -- Sigil of Rampage
		[148903] = true, -- Haromm's Talisman
		[177597] = true, -- Lucky Double-Sided Coin (on-use)
	},
	trinket_proc_bonus_armor_buff = {
		[176873] = true, -- Tablet of Turnbuckle Teamwork (on-use)
		[177055] = true, -- Evergaze Arcane Eidolon
	},
	trinket_proc_crit_buff = {
--		[138963] = true, -- Unerring Vision of Lei-Shen
		[162916] = true, -- Skull of War
		[162918] = true, -- Knight's Badge
		[162920] = true, -- Sandman's Pouch
		[165532] = true, -- Bonemaw's Big Toe (on-use)
		[165532] = true, -- Voidmender's Shadowgem (on-use)
		[176979] = true, -- Immaculate Living Mushroom
		[176983] = true, -- Stoneheart Idol
		[177041] = true, -- Tectus' Beating Heart
		[177047] = true, -- Goren Soul Repository
	},
	trinket_proc_haste_buff = {
		[165531] = true, -- Fleshrender's Meathook (on-use)
		[165821] = true, -- Munificent Bonds of Fury
		[165821] = true, -- Spores of Alacrity
		[165821] = true, -- Witherbark's Branch
		[176875] = true, -- Shards of Nothing (on-use)
		[176879] = true, -- Emblem of Caustic Healing (on-use)
		[176882] = true, -- Turbulent Focusing Crystal (on-use)
		[176885] = true, -- Turbulent Seal of Defiance (on-use)
		[176938] = true, -- Formidable Relic of Blood
		[176944] = true, -- Formidable Censer of Faith
		[176981] = true, -- Furyheart Talisman
		[177036] = true, -- Meaty Dragonspine Trophy
		[177052] = true, -- Darmac's Unstable Talisman
	},
	trinket_proc_intellect_buff = {
--		[126577] = true, -- Light of the Cosmos
		[126683] = true, -- PvP intellect trinket (on-use)
		[126705] = true, -- PvP intellect trinket (proc)
--		[128985] = true, -- Relic of Yu'lon
--		[136082] = true, -- Shock-Charger/Static-Caster's Medallion
--		[138898] = true, -- Breath of the Hydra
--		[139133] = true, -- Cha-Ye's Essence of Brilliance (assume 20% crit chance)
		[146046] = true, -- Purified Bindings of Immerseus
		[148897] = true, -- Frenzied Crystal of Rage
		[148906] = true, -- Kardris' Toxic Totem
	},
	trinket_proc_mastery_buff = {
		[165485] = true, -- Kihra's Adrenaline Injector (on-use)
		[165535] = true, -- Kyrak's Vileblood Serum (on-use)
		[165535] = true, -- Tharbek's Lucky Pebble
		[165825] = true, -- Munificent Censer of Tranquility
		[165825] = true, -- Xeri'tac's Unhatched Egg Sac
		[165835] = true, -- Munificent Emblem of Terror
		[176876] = true, -- Pol's Blinded Eye (on-use)
		[176883] = true, -- Turbulent Vial of Toxin (on-use)
		[176884] = true, -- Turbulent Relic of Mendacity (on-use)
		[176940] = true, -- Formidable Jar of Doom
		[176942] = true, -- Formidable Orb of Putrescence
		[177044] = true, -- Horn of Screaming Spirits
		[177057] = true, -- Blast Furnace Door
	},
	trinket_proc_multistrike_buff = {
		[165542] = true, -- Gor'ashan's Lodestone Spike (on-use)
		[165838] = true, -- Coagulated Genesaur Blood
		[176874] = true, -- Vial of Convulsive Shadows
		[176878] = true, -- Beating Heart of the Mountain (on-use)
		[176881] = true, -- Turbulent Emblem (on-use)
		[176936] = true, -- Formidable Fang
		[176987] = true, -- Blackheart Enforcer's Medallion
		[177039] = true, -- Scales of Doom
		[177064] = true, -- Elementalist's Shielding Talisman
	},
	trinket_proc_spellpower_buff = {
		[177594] = true, -- Copeland's Clarity (on-use)
	},
	trinket_proc_spirit_buff = {
		[162914] = true, -- Winged Hourglass
		[177062] = true, -- Ironspike Chew Toy
	},
	trinket_proc_strength_buff = {
--		[126582] = true, -- Lei Shen's Final Orders
		[126679] = true, -- PvP strength trinket (on-use)
		[126700] = true, -- PvP strength trinket (proc)
		[126702] = true, -- PvP strength trinket (proc)
--		[128986] = true, -- Relic of Xuen (strength)
--		[138702] = true, -- Brutal Talisman of the Shado-Pan Assault
		[146245] = true, -- Evil Eye of Galakras
		[146250] = true, -- Thok's Tail Tip
		[148899] = true, -- Fusion-Fire Core
		[177189] = true, -- Scabbard of Kyanos
	},
	trinket_proc_versatility_buff = {
		[165534] = true, -- Enforcer's Stun Grenade (on-use)
		[165543] = true, -- Emberscale Talisman (on-use)
		[165543] = true, -- Ragewing's Firefang (on-use)
		[165840] = true, -- Leaf of the Ancient Protectors
		[165840] = true, -- Munificent Orb of Ice
		[165840] = true, -- Munificent Soul of Compassion
		[176976] = true, -- Mote of the Mountain
	},
	trinket_stacking_proc_agility_buff = {
--		[138756] = true, -- Renataki's Soul Charm
	},
	trinket_stacking_proc_crit_buff = {
		[146285] = true, -- Skeer's Bloodsoaked Talisman
		[177071] = true, -- Humming Blackiron Trigger
	},
	trinket_stacking_proc_haste_buff = {
		[177090] = true, -- Auto-Repairing Autoclave
		[177104] = true, -- Battering Talisman
	},
	trinket_stacking_proc_intellect_buff = {
--		[138786] = true, -- Wushoolay's Final Choice
		[146184] = true, -- Black Blood of Y'Shaarj
	},
	trinket_stacking_proc_multistrike_buff = {
		[177085] = true, -- Blackiron Micro Crucible
		[177098] = true, -- Forgemaster's Insignia
	},
	trinket_stacking_proc_strength_buff = {
--		[138759] = true, -- Fabled Feather of Ji-Kun
--		[138870] = true, -- Primordius' Talisman of Rage
	},
}
-- Spell list aliases.
do
	local list = OvaleData.buffSpellList

	-- Create default, empty lists for "trinket_(proc|stacking_proc|stacking_stat|stat)_<stat>_buff".
	for _, useName in pairs(TRINKET_USE_NAMES) do
		for _, statName in pairs(STAT_NAMES) do
			local name = format("trinket_%s_%s_buff", useName, statName)
			list[name] = list[name] or {}
		end
	end

	-- Default aliases from "trinket_(stacking_stat|stat)_<stat>_buff" to "trinket_(stacking_proc|proc)_<stat>_buff".
	for _, statName in pairs(STAT_NAMES) do
		local name = format("trinket_stacking_proc_%s_buff", statName)
		local alias = format("trinket_stacking_stat_%s_buff", statName)
		list[alias] = list[alias] or list[name]
	end
	for _, statName in pairs(STAT_NAMES) do
		local name = format("trinket_proc_%s_buff", statName)
		local alias = format("trinket_stat_%s_buff", statName)
		list[alias] = list[alias] or list[name]
	end

	-- Create aliases for "trinket_(stacking_proc|stacking_stat|proc|stat)_<statAbbreviation>_buff".
	for _, useName in pairs(TRINKET_USE_NAMES) do
		for statAbbreviation, statName in pairs(STAT_FULLNAME) do
			local name = format("trinket_%s_%s_buff", useName, statName)
			local alias = format("trinket_%s_%s_buff", useName, statAbbreviation)
			list[alias] = list[alias] or list[name]
		end
	end

	-- Create lists for "trinket_(proc|stacking_proc|stacking_stat|stat)_any_buff".
	for _, useName in pairs(TRINKET_USE_NAMES) do
		local name = format("trinket_%s_any_buff", useName)
		list[name] = list[name] or {}
		for _, statName in pairs(STAT_NAMES) do
			local listName = format("trinket_%s_%s_buff", useName, statName)
			if list[listName] then
				for spellId in pairs(list[listName]) do
					list[name][spellId] = true
				end
			end
		end
	end

	-- Deprecated: spell list aliases.
	list.attack_power_multiplier	= list.attack_power_multiplier_buff
	list.bleed						= list.bleed_debuff
	list.bloodlust					= list.burst_haste_buff
	list.bloodlust_aura				= list.burst_haste_buff
	list.burst_haste				= list.burst_haste_buff
	list.cast_slow					= list.cast_slow_debuff
	list.critical_strike			= list.critical_strike_buff
	list.enrage						= list.enrage_buff
	list.fear						= list.fear_debuff
	list.healing_reduced			= list.healing_reduced_debuff
	list.heroism					= list.burst_haste_buff
	list.heroism_aura				= list.burst_haste_buff
	list.incapacitate				= list.incapacitate_debuff
	list.lower_physical_damage		= list.lower_physical_damage_debuff
	list.magic_vulnerability		= list.magic_vulnerability_debuff
	list.mastery					= list.mastery_buff
	list.melee_haste				= list.attack_speed_buff
	list.physical_vulnerability		= list.physical_vulnerability_debuff
	list.raid_movement				= list.raid_movement_buff
	list.ranged_vulnerability		= list.ranged_vulnerability_debuff
	list.root						= list.root_debuff
	list.spell_haste				= list.spell_haste_buff
	list.spell_power_multiplier		= list.spell_power_multiplier_buff
	list.stamina					= list.stamina_buff
	list.str_agi_int				= list.str_agi_int_buff
	list.stun						= list.stun_debuff
end

-- Unused public property to suppress lint warnings.
--OvaleData.defaultTarget = nil
--</public-static-properties>

--<private-static-methods>
--</private-static-methods>

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

function OvaleData:OnEnable()
	OvaleState:RegisterState(self, self.statePrototype)
end

function OvaleData:OnDisable()
	OvaleState:UnregisterState(self)
end

function OvaleData:RegisterRequirement(name, method, arg)
	self_requirement[name] = { method, arg }
end

function OvaleData:UnregisterRequirement(name)
	self_requirement[name] = nil
end

function OvaleData:ResetSpellInfo()
	self.spellInfo = {}
end

--[[
	Return the exact spell info table for the given spell, creating a new table entry if necessary.
	This method should only be used by modules that need to directly manipulate the table without
	incurring the overhead of function calls.
--]]
function OvaleData:SpellInfo(spellId)
	local si = self.spellInfo[spellId]
	if not si then
		si = {
			aura = {
				-- Auras applied by this spell on the player.
				player = {},
				-- Auras applied by this spell on its target.
				target = {},
				-- Auras granting extra damage multipliers for this spell.
				damage = {},
			},
			require = {},
		}
		self.spellInfo[spellId] = si
	end
	return si
end

function OvaleData:GetSpellInfo(spellId)
	if type(spellId) == "number" then
		return self.spellInfo[spellId]
	elseif self.buffSpellList[spellId] then
		for auraId in pairs(self.buffSpellList[spellId]) do
			if self.spellInfo[auraId] then
				return self.spellInfo[auraId]
			end
		end
	end
end

-- Returns the tag for the spell and whether the spell invokes the GCD.
function OvaleData:GetSpellTagInfo(spellId)
	local tag = "main"
	local invokesGCD = true

	local si = self.spellInfo[spellId]
	if si then
		invokesGCD = not si.gcd or si.gcd > 0
		tag = si.tag
		if not tag then
			local cd = si.cd
			if cd then
				if cd > 90 then
					tag = "cd"
				elseif cd > 29 or not invokesGCD then
					tag = "shortcd"
				end
			elseif not invokesGCD then
				tag = "shortcd"
			end
		end
		tag = tag or "main"
	end
	return tag, invokesGCD
end

-- Check "run-time" requirements specified in SpellRequire().
-- NOTE: Mirrored in statePrototype below.
function OvaleData:CheckRequirements(spellId, tokenIterator, target)
	target = target or self.defaultTarget or "target"
	local name = tokenIterator()
	if name then
		self:Log("Checking requirements:")
		local verified = true
		local requirement = name
		while verified and name do
			local handler = self_requirement[name]
			if handler then
				local method, arg = handler[1], handler[2]
				-- Check for inherited/mirrored method first (for statePrototype).
				if self[method] then
					verified, requirement = self[method](self, spellId, name, tokenIterator, target)
				else
					verified, requirement = arg[method](arg, spellId, name, tokenIterator, target)
				end
				name = tokenIterator()
			else
				Ovale:OneTimeMessage("Warning: requirement '%s' has no registered handler; FAILING requirement.", name)
				verified = false
			end
		end
		return verified, requirement
	end
	return true
end

-- Check "run-time" requirements specified in SpellInfo().
-- NOTE: Mirrored in statePrototype below.
function OvaleData:CheckSpellInfo(spellId, target)
	target = target or self.defaultTarget or "target"
	local verified = true
	local requirement
	for name, handler in pairs(self_requirement) do
		local value = self:GetSpellInfoProperty(spellId, name, target)
		if value then
			local method, arg = handler[1], handler[2]
			-- Check for inherited/mirrored method first (for statePrototype).
			if self[method] then
				verified, requirement = self[method](self, spellId, name, gmatch(value, ".+"), target)
			else
				verified, requirement = arg[method](arg, spellId, name, gmatch(value, ".+"), target)
			end
			if not verified then
				break
			end
		end
	end
	return verified, requirement
end

-- Get SpellInfo property with run-time checks as specified in SpellRequire().
-- NOTE: Mirrored in statePrototype below.
function OvaleData:GetSpellInfoProperty(spellId, property, target)
	target = target or self.defaultTarget or "target"
	local si = OvaleData.spellInfo[spellId]
	local value = si and si[property]
	local requirements = si and si.require[property]
	if requirements then
		for v, requirement in pairs(requirements) do
			local tokenIterator = gmatch(requirement, "[^,]+")
			local verified = self:CheckRequirements(spellId, tokenIterator, target)
			if verified then
				value = tonumber(v) or v
				break
			end
		end
	end
	return value
end

--Compute the damage of the given spell.
function OvaleData:GetDamage(spellId, attackpower, spellpower, mainHandWeaponDamage, offHandWeaponDamage, combo)
	local si = self.spellInfo[spellId]
	if not si then
		return nil
	end
	local damage = si.base or 0
	attackpower = attackpower or 0
	spellpower = spellpower or 0
	mainHandWeaponDamage = mainHandWeaponDamage or 0
	offHandWeaponDamage = offHandWeaponDamage or 0
	combo = combo or 0
	if si.bonusmainhand then
		damage = damage + si.bonusmainhand * mainHandWeaponDamage
	end
	if si.bonusoffhand then
		damage = damage + si.bonusoffhand * offHandWeaponDamage
	end
	if si.bonuscp then
		damage = damage + si.bonuscp * combo
	end
	if si.bonusap then
		damage = damage + si.bonusap * attackpower
	end
	if si.bonusapcp then
		damage = damage + si.bonusapcp * attackpower * combo
	end
	if si.bonussp then
		damage = damage + si.bonussp * spellpower
	end
	return damage
end

-- Returns the base duration of an aura.
function OvaleData:GetBaseDuration(auraId, spellcast)
	local combo = spellcast and spellcast.combo
	local holy = spellcast and spellcast.holy
	local duration = INFINITY
	local si = OvaleData.spellInfo[auraId]
	if si and si.duration then
		duration = si.duration
		if si.addduration then
			duration = duration + si.addduration
		end
		if si.adddurationcp and combo then
			duration = duration + si.adddurationcp * combo
		end
		if si.adddurationholy and holy then
			duration = duration + si.adddurationholy * (holy - 1)
		end
	end
	return duration
end

-- Returns the length in seconds of a tick of the periodic aura.
function OvaleData:GetTickLength(auraId, snapshot)
	local tick = 3
	local si = OvaleData.spellInfo[auraId]
	if si then
		tick = si.tick or tick
		local hasteMultiplier = 1
		if si.haste then
			if si.haste == "spell" then
				hasteMultiplier = OvalePaperDoll:GetSpellHasteMultiplier(snapshot)
			elseif si.haste == "melee" then
				hasteMultiplier = OvalePaperDoll:GetMeleeHasteMultiplier(snapshot)
			end
			tick = tick / hasteMultiplier
		end
	end
	return tick
end
--</public-static-methods>

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

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

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

--<state-methods>
-- Mirrored methods.
statePrototype.CheckRequirements = OvaleData.CheckRequirements
statePrototype.CheckSpellInfo = OvaleData.CheckSpellInfo
statePrototype.GetSpellInfoProperty = OvaleData.GetSpellInfoProperty
--</state-methods>