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

local OVALE, Ovale = ...

local LibBabbleCreatureType = LibStub("LibBabble-CreatureType-3.0", true)
local LibRangeCheck = LibStub("LibRangeCheck-2.0", true)

local OvaleBestAction = Ovale.OvaleBestAction
local OvaleCompile = Ovale.OvaleCompile
local OvaleCondition = Ovale.OvaleCondition
local OvaleCooldown = Ovale.OvaleCooldown
local OvaleDamageTaken = Ovale.OvaleDamageTaken
local OvaleData = Ovale.OvaleData
local OvaleEquipment = Ovale.OvaleEquipment
local OvaleFuture = Ovale.OvaleFuture
local OvaleGUID = Ovale.OvaleGUID
local OvaleLatency = Ovale.OvaleLatency
local OvalePower = Ovale.OvalePower
local OvaleRunes = Ovale.OvaleRunes
local OvaleSpellBook = Ovale.OvaleSpellBook
local OvaleSpellDamage = Ovale.OvaleSpellDamage

local floor = math.floor
local ipairs = ipairs
local pairs = pairs
local tonumber = tonumber
local tostring = tostring
local type = type
local wipe = wipe
local API_GetBuildInfo = GetBuildInfo
local API_GetItemCooldown = GetItemCooldown
local API_GetItemCount = GetItemCount
local API_GetNumTrackingTypes = GetNumTrackingTypes
local API_GetTime = GetTime
local API_GetTrackingInfo = GetTrackingInfo
local API_GetUnitSpeed = GetUnitSpeed
local API_GetWeaponEnchantInfo = GetWeaponEnchantInfo
local API_HasFullControl = HasFullControl
local API_IsSpellOverlayed = IsSpellOverlayed
local API_IsStealthed = IsStealthed
local API_UnitCastingInfo = UnitCastingInfo
local API_UnitChannelInfo = UnitChannelInfo
local API_UnitClass = UnitClass
local API_UnitClassification = UnitClassification
local API_UnitCreatureFamily = UnitCreatureFamily
local API_UnitCreatureType = UnitCreatureType
local API_UnitDetailedThreatSituation = UnitDetailedThreatSituation
local API_UnitExists = UnitExists
local API_UnitHealth = UnitHealth
local API_UnitHealthMax = UnitHealthMax
local API_UnitIsDead = UnitIsDead
local API_UnitIsFriend = UnitIsFriend
local API_UnitIsPVP = UnitIsPVP
local API_UnitIsUnit = UnitIsUnit
local API_UnitLevel = UnitLevel
local API_UnitName = UnitName
local API_UnitPower = UnitPower
local API_UnitPowerMax = UnitPowerMax
local API_UnitStagger = UnitStagger
local INFINITY = math.huge

local Compare = OvaleCondition.Compare
local ParseCondition = OvaleCondition.ParseCondition
local TestBoolean = OvaleCondition.TestBoolean
local TestValue = OvaleCondition.TestValue

--[[--------------------
	Helper functions
--]]--------------------

-- Return the target's damage reduction from armor, assuming the target is boss-level.
-- This function makes heavy use of magic constants and is only valid for level 93 bosses.
local function BossArmorDamageReduction(target, state)
	-- Boss armor value empirically determined.
	local armor = 24835
	local constant = 4037.5 * state.level - 317117.5
	if constant < 0 then
		constant = 0
	end
	return armor / (armor + constant)
end

--[[
	Return the value of a parameter from the named spell's information.  If the value is the name of a
	function in the script, then return the compute the value of that function instead.
--]]
local function ComputeParameter(spellId, paramName, state)
	local si = OvaleData:GetSpellInfo(spellId)
	if si and si[paramName] then
		local name = si[paramName]
		local node = OvaleCompile:GetFunctionNode(name)
		if node then
			local timeSpan, priority, element = OvaleBestAction:Compute(node.child[1], state)
			if element and element.type == "value" then
				local value = element.value + (state.currentTime - element.origin) * element.rate
				return value
			end
		else
			return si[paramName]
		end
	end
	return nil
end

-- Return the time in seconds, adjusted by the named haste effect.
local function GetHastedTime(seconds, haste, state)
	seconds = seconds or 0
	if not haste then
		return seconds
	elseif haste == "spell" then
		return seconds / state:GetSpellHasteMultiplier()
	elseif haste == "melee" then
		return seconds / state:GetMeleeHasteMultiplier()
	else
		state:Log("Unknown haste parameter haste=%s", haste)
		return seconds
	end
end

--[[---------------------
	Script conditions
--]]---------------------

do
	-- Test if a white hit just occured
	-- 1 : maximum time after a white hit
	-- Not useful anymore. No widely used spell reset swing timer anyway

	local function AfterWhiteHit(condition, state)
		local seconds, comparator, limit = condition[1], condition[2], condition[3]
		local value = 0
		Ovale:OneTimeMessage("Warning: 'AfterWhiteHit()' is not implemented.")
		return TestValue(0, INFINITY, value, state.currentTime, -1, comparator, limit)
	end

	--OvaleCondition:RegisterCondition("afterwhitehit", false, AfterWhiteHit)
end

do
	--- Check whether the player currently has an armor set bonus.
	-- @name ArmorSetBonus
	-- @paramsig number
	-- @param name The name of the armor set.
	--     Valid names: T11, T12, T13, T14, T15, T16
	--     Valid names for hybrid classes: append _caster, _heal, _melee, _tank.
	-- @param count The number of pieces needed to activate the armor set bonus.
	-- @return 1 if the set bonus is active, or 0 otherwise.
	-- @usage
	-- if ArmorSetBonus(T16_melee 2) == 1 Spell(unleash_elements)

	local function ArmorSetBonus(condition, state)
		local armorSet, count = condition[1], condition[2]
		local value = (OvaleEquipment:GetArmorSetCount(armorSet) >= count) and 1 or 0
		return 0, INFINITY, value, 0, 0
	end

	OvaleCondition:RegisterCondition("armorsetbonus", false, ArmorSetBonus)
end

do
	--- Get how many pieces of an armor set, e.g., Tier 14 set, are equipped by the player.
	-- @name ArmorSetParts
	-- @paramsig number or boolean
	-- @param name The name of the armor set.
	--     Valid names: T11, T12, T13, T14, T15.
	--     Valid names for hybrid classes: append _caster, _heal, _melee, _tank.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The number of pieces of the named set that are equipped by the player.
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- if ArmorSetParts(T13) >=2 and target.HealthPercent() <60
	--     Spell(ferocious_bite)
	-- if ArmorSetParts(T13 more 1) and TargetHealthPercent(less 60)
	--     Spell(ferocious_bite)

	local function ArmorSetParts(condition, state)
		local armorSet, comparator, limit = condition[1], condition[2], condition[3]
		local value = OvaleEquipment:GetArmorSetCount(armorSet)
		return Compare(value, comparator, limit)
	end

	OvaleCondition:RegisterCondition("armorsetparts", false, ArmorSetParts)
end

do
	--- Get the base duration of the aura in seconds if it is applied at the current time.
	-- @name BaseDuration
	-- @paramsig number or boolean
	-- @param id The aura spell ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The base duration in seconds.
	-- @return A boolean value for the result of the comparison.
	-- @see BuffDuration
	-- @usage
	-- if BaseDuration(slice_and_dice_buff) > BuffDuration(slice_and_dice_buff)
	--     Spell(slice_and_dice)

	local function BaseDuration(condition, state)
		local auraId, comparator, limit = condition[1], condition[2], condition[3]
		local value = OvaleData:GetBaseDuration(auraId, state)
		return Compare(value, comparator, limit)
	end

	OvaleCondition:RegisterCondition("baseduration", false, BaseDuration)
	OvaleCondition:RegisterCondition("buffdurationifapplied", false, BaseDuration)
	OvaleCondition:RegisterCondition("debuffdurationifapplied", false, BaseDuration)
end

do
	--- Get the value of a buff as a number.  Not all buffs return an amount.
	-- @name BuffAmount
	-- @paramsig number
	-- @param id The spell ID of the aura or the name of a spell list.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @param any Optional. Sets by whom the aura was applied. If the aura can be applied by anyone, then set any=1.
	--     Defaults to any=0.
	--     Valid values: 0, 1.
	-- @param value Optional. Sets which aura value to return from UnitAura().
	--     Defaults to value=1.
	--     Valid values: 1, 2, 3.
	-- @return The value of the buff as a number.
	-- @see DebuffAmount
	-- @see TickValue
	-- @usage
	-- if DebuffAmount(stagger) >10000 Spell(purifying_brew)
	-- if DebuffAmount(stagger more 10000) Spell(purifying_brew)

	local function BuffAmount(condition, state)
		local auraId, comparator, limit = condition[1], condition[2], condition[3]
		local target, filter, mine = ParseCondition(condition, state)
		local value = condition.value or 1
		local statName = "value1"
		if value == 1 then
			statName = "value1"
		elseif value == 2 then
			statName = "value2"
		elseif value == 3 then
			statName = "value3"
		end
		local aura = state:GetAura(target, auraId, filter, mine)
		if state:IsActiveAura(aura) then
			local gain, start, ending = aura.gain, aura.start, aura.ending
			local value = aura[statName] or 0
			return TestValue(gain, ending, value, start, 0, comparator, limit)
		end
		return Compare(0, comparator, limit)
	end

	OvaleCondition:RegisterCondition("buffamount", false, BuffAmount)
	OvaleCondition:RegisterCondition("debuffamount", false, BuffAmount)
	OvaleCondition:RegisterCondition("tickvalue", false, BuffAmount)
end

do
	--- Get the player's combo points for the given aura at the time the aura was applied on the target.
	-- @name BuffComboPoints
	-- @paramsig number or boolean
	-- @param id The aura spell ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The number of combo points.
	-- @return A boolean value for the result of the comparison.
	-- @see DebuffComboPoints
	-- @usage
	-- if target.DebuffComboPoints(rip) <5 Spell(rip)

	local function BuffComboPoints(condition, state)
		local auraId, comparator, limit = condition[1], condition[2], condition[3]
		local target, filter, mine = ParseCondition(condition, state)
		local aura = state:GetAura(target, auraId, filter, mine)
		if state:IsActiveAura(aura) then
			local gain, start, ending = aura.gain, aura.start, aura.ending
			local value = aura and aura.combo or 0
			return TestValue(gain, ending, value, start, 0, comparator, limit)
		end
		return Compare(0, comparator, limit)
	end

	OvaleCondition:RegisterCondition("buffcombopoints", false, BuffComboPoints)
	OvaleCondition:RegisterCondition("debuffcombopoints", false, BuffComboPoints)
end

do
	--- Get the number of seconds before a buff can be gained again.
	-- @name BuffCooldown
	-- @paramsig number or boolean
	-- @param id The spell ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The number of seconds.
	-- @return A boolean value for the result of the comparison.
	-- @see DebuffCooldown
	-- @usage
	-- if BuffCooldown(trinket_stat_agility_buff) > 45
	--     Spell(tigers_fury)

	local function BuffCooldown(condition, state)
		local auraId, comparator, limit = condition[1], condition[2], condition[3]
		local target, filter, mine = ParseCondition(condition, state)
		local aura = state:GetAura(target, auraId, filter, mine)
		if aura then
			local gain, cooldownEnding = aura.gain, aura.cooldownEnding
			cooldownEnding = aura.cooldownEnding or 0
			return TestValue(gain, INFINITY, 0, cooldownEnding, -1, comparator, limit)
		end
		return Compare(0, comparator, limit)
	end

	OvaleCondition:RegisterCondition("buffcooldown", false, BuffCooldown)
	OvaleCondition:RegisterCondition("debuffcooldown", false, BuffCooldown)
end

do
	--- Get the total count of the given aura across all targets.
	-- @name BuffCountOnAny
	-- @paramsig number or boolean
	-- @param id The spell ID of the aura or the name of a spell list.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param stacks Optional. The minimum number of stacks of the aura required.
	--     Defaults to stacks=1.
	--     Valid values: any number greater than zero.
	-- @param any Optional. Sets by whom the aura was applied. If the aura can be applied by anyone, then set any=1.
	--     Defaults to any=0.
	--     Valid values: 0, 1.
	-- @param excludeTarget Optional. Sets whether to ignore the current target when scanning targets.
	--     Defaults to excludeTarget=0.
	--     Valid values: 0, 1.
	-- @return The total aura count.
	-- @return A boolean value for the result of the comparison.
	-- @see DebuffCountOnAny

	local function BuffCountOnAny(condition, state)
		local auraId, comparator, limit = condition[1], condition[2], condition[3]
		local _, filter, mine = ParseCondition(condition, state)
		local excludeUnitId = (condition.excludeTarget == 1) and state.defaultTarget or nil

		local count, stacks, startChangeCount, endingChangeCount, startFirst, endingLast = state:AuraCount(auraId, filter, mine, condition.stacks, excludeUnitId)
		if count > 0 and startChangeCount < INFINITY then
			local origin = startChangeCount
			local rate = -1 / (endingChangeCount - startChangeCount)
			local start, ending = startFirst, endingLast
			return TestValue(start, ending, count, origin, rate, comparator, limit)
		end
		return Compare(count, comparator, limit)
	end

	OvaleCondition:RegisterCondition("buffcountonany", false, BuffCountOnAny)
	OvaleCondition:RegisterCondition("debuffcountonany", false, BuffCountOnAny)

	-- Deprecated.
	OvaleCondition:RegisterCondition("buffcount", false, BuffCountOnAny)
	OvaleCondition:RegisterCondition("debuffcount", false, BuffCountOnAny)
end

do
	--- Get the player's damage multiplier for the given aura at the time the aura was applied on the target.
	-- @name BuffDamageMultiplier
	-- @paramsig number or boolean
	-- @param id The aura spell ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The damage multiplier.
	-- @return A boolean value for the result of the comparison.
	-- @see DebuffDamageMultiplier
	-- @usage
	-- if target.DebuffDamageMultiplier(rake) <1 Spell(rake)

	local function BuffDamageMultiplier(condition, state)
		local auraId, comparator, limit = condition[1], condition[2], condition[3]
		local target, filter, mine = ParseCondition(condition, state)
		local aura = state:GetAura(target, auraId, filter, mine)
		if state:IsActiveAura(aura) then
			local gain, start, ending = aura.gain, aura.start, aura.ending
			local baseDamageMultiplier = aura.snapshot and aura.snapshot.baseDamageMultiplier or 1
			local damageMultiplier = aura.damageMultiplier or 1
			local value = baseDamageMultiplier * damageMultiplier
			return TestValue(gain, ending, value, start, 0, comparator, limit)
		end
		return Compare(1, comparator, limit)
	end

	OvaleCondition:RegisterCondition("buffdamagemultiplier", false, BuffDamageMultiplier)
	OvaleCondition:RegisterCondition("debuffdamagemultiplier", false, BuffDamageMultiplier)
end

do
	--- Get the current direction of an aura's stack count.
	-- A negative number means the aura is decreasing in stack count.
	-- A positive number means the aura is increasing in stack count.
	-- @name BuffDirection
	-- @paramsig number or boolean
	-- @param id The spell ID of the aura or the name of a spell list.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param any Optional. Sets by whom the aura was applied. If the aura can be applied by anyone, then set any=1.
	--     Defaults to any=0.
	--     Valid values: 0, 1.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The current direction.
	-- @return A boolean value for the result of the comparison.
	-- @see DebuffDirection

	local function BuffDirection(condition, state)
		local auraId, comparator, limit = condition[1], condition[2], condition[3]
		local target, filter, mine = ParseCondition(condition, state)
		local aura = state:GetAura(target, auraId, filter, mine)
		if aura then
			local gain, start, ending, direction = aura.gain, aura.start, aura.ending, aura.direction
			return TestValue(gain, INFINITY, direction, gain, 0, comparator, limit)
		end
		return Compare(0, comparator, limit)
	end

	OvaleCondition:RegisterCondition("buffdirection", false, BuffDirection)
	OvaleCondition:RegisterCondition("debuffdirection", false, BuffDirection)
end

do
	--- Get the total duration of the aura from when it was first applied to when it ended.
	-- @name BuffDuration
	-- @paramsig number or boolean
	-- @param id The aura spell ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The total duration of the aura.
	-- @return A boolean value for the result of the comparison.
	-- @see DebuffDuration

	local function BuffDuration(condition, state)
		local auraId, comparator, limit = condition[1], condition[2], condition[3]
		local target, filter, mine = ParseCondition(condition, state)
		local aura = state:GetAura(target, auraId, filter, mine)
		if state:IsActiveAura(aura) then
			local gain, start, ending = aura.gain, aura.start, aura.ending
			local value = ending - start
			return TestValue(gain, ending, value, start, 0, comparator, limit)
		end
		return Compare(0, comparator, limit)
	end

	OvaleCondition:RegisterCondition("buffduration", false, BuffDuration)
	OvaleCondition:RegisterCondition("debuffduration", false, BuffDuration)
end

do
	--- Test if an aura is expired, or will expire after a given number of seconds.
	-- @name BuffExpires
	-- @paramsig boolean
	-- @param id The spell ID of the aura or the name of a spell list.
	-- @param seconds Optional. The maximum number of seconds before the buff should expire.
	--     Defaults to 0 (zero).
	-- @param any Optional. Sets by whom the aura was applied. If the aura can be applied by anyone, then set any=1.
	--     Defaults to any=0.
	--     Valid values: 0, 1.
	-- @param haste Optional. Sets whether "seconds" should be lengthened or shortened due to haste.
	--     Defaults to haste=none.
	--     Valid values: melee, spell, none.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return A boolean value.
	-- @see DebuffExpires
	-- @usage
	-- if BuffExpires(stamina any=1)
	--     Spell(power_word_fortitude)
	-- if target.DebuffExpires(rake 2)
	--     Spell(rake)

	local function BuffExpires(condition, state)
		local auraId, seconds = condition[1], condition[2]
		local target, filter, mine = ParseCondition(condition, state)
		local aura = state:GetAura(target, auraId, filter, mine)
		if aura then
			local gain, start, ending = aura.gain, aura.start, aura.ending
			seconds = GetHastedTime(seconds, condition.haste)
			if ending - seconds <= gain then
				return gain, INFINITY
			else
				return ending - seconds, INFINITY
			end
		end
		return 0, INFINITY
	end

	OvaleCondition:RegisterCondition("buffexpires", false, BuffExpires)
	OvaleCondition:RegisterCondition("debuffexpires", false, BuffExpires)

	--- Test if an aura is present or if the remaining time on the aura is more than the given number of seconds.
	-- @name BuffPresent
	-- @paramsig boolean
	-- @param id The spell ID of the aura or the name of a spell list.
	-- @param seconds Optional. The mininum number of seconds before the buff should expire.
	--     Defaults to 0 (zero).
	-- @param any Optional. Sets by whom the aura was applied. If the aura can be applied by anyone, then set any=1.
	--     Defaults to any=0.
	--     Valid values: 0, 1.
	-- @param haste Optional. Sets whether "seconds" should be lengthened or shortened due to haste.
	--     Defaults to haste=none.
	--     Valid values: melee, spell, none.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return A boolean value.
	-- @see DebuffPresent
	-- @usage
	-- if not BuffPresent(stamina any=1)
	--     Spell(power_word_fortitude)
	-- if not target.DebuffPresent(rake 2)
	--     Spell(rake)

	local function BuffPresent(condition, state)
		local auraId, seconds = condition[1], condition[2]
		local target, filter, mine = ParseCondition(condition, state)
		local aura = state:GetAura(target, auraId, filter, mine)
		if aura then
			local gain, start, ending = aura.gain, aura.start, aura.ending
			seconds = GetHastedTime(seconds, condition.haste)
			if ending - seconds <= gain then
				return nil
			else
				return gain, ending - seconds
			end
		end
		return nil
	end

	OvaleCondition:RegisterCondition("buffpresent", false, BuffPresent)
	OvaleCondition:RegisterCondition("debuffpresent", false, BuffPresent)
end

do
	--- Get the time elapsed since the aura was last gained on the target.
	-- @paramsig number or boolean
	-- @param id The spell ID of the aura or the name of a spell list.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param any Optional. Sets by whom the aura was applied. If the aura can be applied by anyone, then set any=1.
	--     Defaults to any=0.
	--     Valid values: 0, 1.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The number of seconds.
	-- @return A boolean value for the result of the comparison.
	-- @see DebuffGain

	local function BuffGain(condition, state)
		local auraId, comparator, limit = condition[1], condition[2], condition[3]
		local target, filter, mine = ParseCondition(condition, state)
		local aura = state:GetAura(target, auraId, filter, mine)
		if aura then
			local gain = aura.gain or 0
			return TestValue(gain, INFINITY, 0, gain, 1, comparator, limit)
		end
		return Compare(0, comparator, limit)
	end

	OvaleCondition:RegisterCondition("buffgain", false, BuffGain)
	OvaleCondition:RegisterCondition("debuffgain", false, BuffGain)
end

do
	--- Get the player's persistent multiplier for the given aura at the time the aura was applied on the target.
	-- The persistent multiplier is snapshotted to the aura for its duration at the time the aura is applied.
	-- @name BuffPersistentMultiplier
	-- @paramsig number or boolean
	-- @param id The aura spell ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The persistent multiplier.
	-- @return A boolean value for the result of the comparison.
	-- @see DebuffPersistentMultiplier
	-- @usage
	-- if target.DebuffPersistentMultiplier(rake) < 1 Spell(rake)

	local function BuffPersistentMultiplier(condition, state)
		local auraId, comparator, limit = condition[1], condition[2], condition[3]
		local target, filter, mine = ParseCondition(condition, state)
		local aura = state:GetAura(target, auraId, filter, mine)
		if state:IsActiveAura(aura) then
			local gain, start, ending = aura.gain, aura.start, aura.ending
			local value = aura.damageMultiplier or 1
			return TestValue(gain, ending, value, start, 0, comparator, limit)
		end
		return Compare(1, comparator, limit)
	end

	OvaleCondition:RegisterCondition("buffpersistentmultiplier", false, BuffPersistentMultiplier)
	OvaleCondition:RegisterCondition("debuffpersistentmultiplier", false, BuffPersistentMultiplier)
end

do
	--- Get the remaining time in seconds on an aura.
	-- @name BuffRemaining
	-- @paramsig number or boolean
	-- @param id The spell ID of the aura or the name of a spell list.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param any Optional. Sets by whom the aura was applied. If the aura can be applied by anyone, then set any=1.
	--     Defaults to any=0.
	--     Valid values: 0, 1.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The number of seconds remaining on the aura.
	-- @return A boolean value for the result of the comparison.
	-- @see DebuffRemaining
	-- @usage
	-- if BuffRemaining(slice_and_dice) <2
	--     Spell(slice_and_dice)

	local function BuffRemaining(condition, state)
		local auraId, comparator, limit = condition[1], condition[2], condition[3]
		local target, filter, mine = ParseCondition(condition, state)
		local aura = state:GetAura(target, auraId, filter, mine)
		if aura then
			local gain, start, ending = aura.gain, aura.start, aura.ending
			return TestValue(gain, INFINITY, 0, ending, -1, comparator, limit)
		end
		return Compare(0, comparator, limit)
	end

	OvaleCondition:RegisterCondition("buffremaining", false, BuffRemaining)
	OvaleCondition:RegisterCondition("debuffremaining", false, BuffRemaining)
	OvaleCondition:RegisterCondition("buffremains", false, BuffRemaining)
	OvaleCondition:RegisterCondition("debuffremains", false, BuffRemaining)
end

do
	--- Get the remaining time in seconds before the aura expires across all targets.
	-- @name BuffRemainingOnAny
	-- @paramsig number or boolean
	-- @param id The spell ID of the aura or the name of a spell list.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param stacks Optional. The minimum number of stacks of the aura required.
	--     Defaults to stacks=1.
	--     Valid values: any number greater than zero.
	-- @param any Optional. Sets by whom the aura was applied. If the aura can be applied by anyone, then set any=1.
	--     Defaults to any=0.
	--     Valid values: 0, 1.
	-- @param excludeTarget Optional. Sets whether to ignore the current target when scanning targets.
	--     Defaults to excludeTarget=0.
	--     Valid values: 0, 1.
	-- @return The number of seconds.
	-- @return A boolean value for the result of the comparison.
	-- @see DebuffRemainingOnAny

	local function BuffRemainingOnAny(condition, state)
		local auraId, comparator, limit = condition[1], condition[2], condition[3]
		local _, filter, mine = ParseCondition(condition, state)
		local excludeUnitId = (condition.excludeTarget == 1) and state.defaultTarget or nil

		local count, stacks, startChangeCount, endingChangeCount, startFirst, endingLast = state:AuraCount(auraId, filter, mine, condition.stacks, excludeUnitId)
		if count > 0 then
			local start, ending = startFirst, endingLast
			return TestValue(start, INFINITY, 0, ending, -1, comparator, limit)
		end
		return Compare(0, comparator, limit)
	end

	OvaleCondition:RegisterCondition("buffremainingonany", false, BuffRemainingOnAny)
	OvaleCondition:RegisterCondition("debuffremainingonany", false, BuffRemainingOnAny)
	OvaleCondition:RegisterCondition("buffremainsonany", false, BuffRemainingOnAny)
	OvaleCondition:RegisterCondition("debuffremainsonany", false, BuffRemainingOnAny)
end

do
	-- Return the value of the stat from the aura snapshot at the time the aura was applied.
	local function BuffSnapshot(statName, defaultValue, condition, state)
		local auraId, comparator, limit = condition[1], condition[2], condition[3]
		local target, filter, mine = ParseCondition(condition, state)
		local aura = state:GetAura(target, auraId, filter, mine)
		if state:IsActiveAura(aura) then
			local gain, start, ending = aura.gain, aura.start, aura.ending
			local value = aura.snapshot and aura.snapshot[statName] or defaultValue
			return TestValue(gain, ending, value, start, 0, comparator, limit)
		end
		return Compare(defaultValue, comparator, limit)
	end

	-- Return the value of the given critical strike chance from the aura snapshot at the time the aura was applied.
	local function BuffSnapshotCritChance(statName, defaultValue, condition, state)
		local auraId, comparator, limit = condition[1], condition[2], condition[3]
		local target, filter, mine = ParseCondition(condition, state)
		local aura = state:GetAura(target, auraId, filter, mine)
		if state:IsActiveAura(aura) then
			local gain, start, ending = aura.gain, aura.start, aura.ending
			local value = aura.snapshot and aura.snapshot[statName] or defaultValue
			if condition.unlimited ~= 1 and value > 100 then
				value = 100
			end
			return TestValue(gain, ending, value, start, 0, comparator, limit)
		end
		return Compare(defaultValue, comparator, limit)
	end

	--- Get the player's attack power at the time the given aura was applied on the target.
	-- @name BuffAttackPower
	-- @paramsig number or boolean
	-- @param id The aura spell ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The attack power.
	-- @return A boolean value for the result of the comparison.
	-- @see DebuffAttackPower
	-- @usage
	-- if AttackPower() >target.DebuffAttackPower(rake) Spell(rake)

	local function BuffAttackPower(condition, state)
		return BuffSnapshot("attackPower", 0, condition, state)
	end

	OvaleCondition:RegisterCondition("buffattackpower", false, BuffAttackPower)
	OvaleCondition:RegisterCondition("debuffattackpower", false, BuffAttackPower)

	--- Get the player's mastery effect at the time the given aura was applied on the target.
	-- @name BuffMasteryEffect
	-- @paramsig number or boolean
	-- @param id The aura spell ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The mastery effect.
	-- @return A boolean value for the result of the comparison.
	-- @see DebuffMasteryEffect
	-- @usage
	-- if MasteryEffect() >target.DebuffMasteryEffect(rip) Spell(rip)

	local function BuffMasteryEffect(condition, state)
		return BuffSnapshot("masteryEffect", 0, condition, state)
	end

	OvaleCondition:RegisterCondition("buffmastery", false, BuffMasteryEffect)
	OvaleCondition:RegisterCondition("buffmasteryeffect", false, BuffMasteryEffect)
	OvaleCondition:RegisterCondition("debuffmastery", false, BuffMasteryEffect)
	OvaleCondition:RegisterCondition("debuffmasteryeffect", false, BuffMasteryEffect)

	--- Get the player's melee critical strike chance at the time the given aura was applied on the target.
	-- @name BuffMeleeCritChance
	-- @paramsig number or boolean
	-- @param id The aura spell ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param unlimited Optional. Set unlimited=1 to allow critical strike chance to exceed 100%.
	--     Defaults to unlimited=0.
	--     Valid values: 0, 1
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The critical strike chance.
	-- @return A boolean value for the result of the comparison.
	-- @see DebuffMeleeCritChance
	-- @usage
	-- if MeleeCritChance() >target.DebuffMeleeCritChance(rake) Spell(rake)

	local function BuffMeleeCritChance(condition, state)
		return BuffSnapshotCritChance("meleeCrit", 0, condition, state)
	end

	OvaleCondition:RegisterCondition("buffmeleecritchance", false, BuffMeleeCritChance)
	OvaleCondition:RegisterCondition("debuffmeleecritchance", false, BuffMeleeCritChance)

	--- Get the player's multistrike chance at the time the given aura was applied on the target.
	-- @name BuffMultistrikeChance
	-- @paramsig number or boolean
	-- @param id The aura spell ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The multistrike chance (in percent).
	-- @return A boolean value for the result of the comparison.
	-- @see DebuffMultistrikeChance
	-- @usage
	-- if MultistrikeChance() >target.DebuffMultistrikeChance(rip) Spell(rip)

	local function BuffMultistrikeChance(condition, state)
		return BuffSnapshot("multistrike", 0, condition, state)
	end

	OvaleCondition:RegisterCondition("buffmultistrikechance", false, BuffMultistrikeChance)
	OvaleCondition:RegisterCondition("debuffmultistrikechance", false, BuffMultistrikeChance)

	--- Get the player's ranged attack power at the time the given aura was applied on the target.
	-- @name BuffRangedAttackPower
	-- @paramsig number or boolean
	-- @param id The aura spell ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The ranged attack power.
	-- @return A boolean value for the result of the comparison.
	-- @see DebuffRangedAttackPower
	-- @usage
	-- if RangedAttackPower() >target.DebuffRangedAttackPower(serpent_sting_dot)
	--     Spell(serpent_sting)

	local function BuffRangedAttackPower(condition, state)
		return BuffSnapshot("rangedAttackPower", 0, condition, state)
	end

	OvaleCondition:RegisterCondition("buffrangedattackpower", false, BuffRangedAttackPower)
	OvaleCondition:RegisterCondition("debuffrangedattackpower", false, BuffRangedAttackPower)

	--- Get the player's ranged critical strike chance at the time the given aura was applied on the target.
	-- @name BuffRangedCritChance
	-- @paramsig number or boolean
	-- @param id The aura spell ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param unlimited Optional. Set unlimited=1 to allow critical strike chance to exceed 100%.
	--     Defaults to unlimited=0.
	--     Valid values: 0, 1
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The critical strike chance.
	-- @return A boolean value for the result of the comparison.
	-- @see DebuffRangedCritChance
	-- @usage
	-- if RangedCritChance() >target.DebuffRangedCritChance(serpent_sting_dot)
	--     Spell(serpent_sting)

	local function BuffRangedCritChance(condition, state)
		return BuffSnapshotCritChance("rangedCrit", 0, condition, state)
	end

	OvaleCondition:RegisterCondition("buffrangedcritchance", false, BuffRangedCritChance)
	OvaleCondition:RegisterCondition("debuffrangedcritchance", false, BuffRangedCritChance)

	--- Get the player's spell critical strike chance at the time the given aura was applied on the target.
	-- @name BuffSpellCritChance
	-- @paramsig number or boolean
	-- @param id The aura spell ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param unlimited Optional. Set unlimited=1 to allow critical strike chance to exceed 100%.
	--     Defaults to unlimited=0.
	--     Valid values: 0, 1
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The critical strike chance.
	-- @return A boolean value for the result of the comparison.
	-- @see DebuffSpellCritChance
	-- @usage
	-- if SpellCritChance() >target.DebuffSpellCritChance(moonfire) Spell(moonfire)

	local function BuffSpellCritChance(condition, state)
		return BuffSnapshotCritChance("spellCrit", 0, condition, state)
	end

	OvaleCondition:RegisterCondition("buffspellcritchance", false, BuffSpellCritChance)
	OvaleCondition:RegisterCondition("debuffspellcritchance", false, BuffSpellCritChance)

	--- Get the player's spell haste at the time the given aura was applied on the target.
	-- @name BuffSpellHaste
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param id The aura spell ID.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The percent increase to spell haste.
	-- @return A boolean value for the result of the comparison.
	-- @see DebuffSpellHaste
	-- @usage
	-- if SpellHaste() >target.DebuffSpellHaste(moonfire) Spell(moonfire)

	local function BuffSpellHaste(condition, state)
		return BuffSnapshot("spellHaste", 0, condition, state)
	end

	OvaleCondition:RegisterCondition("buffspellhaste", false, BuffSpellHaste)
	OvaleCondition:RegisterCondition("debuffspellhaste", false, BuffSpellHaste)

	--- Get the player's spellpower at the time the given aura was applied on the target.
	-- @name BuffSpellpower
	-- @paramsig number or boolean
	-- @param id The aura spell ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The spellpower.
	-- @return A boolean value for the result of the comparison.
	-- @see DebuffSpellpower
	-- @usage
	-- if Spellpower() >target.DebuffSpellpower(moonfire) Spell(moonfire)

	local function BuffSpellpower(condition, state)
		return BuffSnapshot("spellBonusDamage", 0, condition, state)
	end

	OvaleCondition:RegisterCondition("buffspellpower", false, BuffSpellpower)
	OvaleCondition:RegisterCondition("debuffspellpower", false, BuffSpellpower)
end

do
	--- Get the number of stacks of an aura on the target.
	-- @name BuffStacks
	-- @paramsig number or boolean
	-- @param id The spell ID of the aura or the name of a spell list.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param any Optional. Sets by whom the aura was applied. If the aura can be applied by anyone, then set any=1.
	--     Defaults to any=0.
	--     Valid values: 0, 1.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The number of stacks of the aura.
	-- @return A boolean value for the result of the comparison.
	-- @see DebuffStacks
	-- @usage
	-- if BuffStacks(pet_frenzy any=1) ==5
	--     Spell(focus_fire)
	-- if target.DebuffStacks(weakened_armor) <3
	--     Spell(faerie_fire)

	local function BuffStacks(condition, state)
		local auraId, comparator, limit = condition[1], condition[2], condition[3]
		local target, filter, mine = ParseCondition(condition, state)
		local aura = state:GetAura(target, auraId, filter, mine)
		if state:IsActiveAura(aura) then
			local gain, start, ending = aura.gain, aura.start, aura.ending
			local value = aura.stacks or 0
			return TestValue(gain, ending, value, start, 0, comparator, limit)
		end
		return Compare(0, comparator, limit)
	end

	OvaleCondition:RegisterCondition("buffstacks", false, BuffStacks)
	OvaleCondition:RegisterCondition("debuffstacks", false, BuffStacks)
end

do
	--- Get the total number of stacks of the given aura across all targets.
	-- @name BuffStacksOnAny
	-- @paramsig number or boolean
	-- @param id The spell ID of the aura or the name of a spell list.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param any Optional. Sets by whom the aura was applied. If the aura can be applied by anyone, then set any=1.
	--     Defaults to any=0.
	--     Valid values: 0, 1.
	-- @param excludeTarget Optional. Sets whether to ignore the current target when scanning targets.
	--     Defaults to excludeTarget=0.
	--     Valid values: 0, 1.
	-- @return The total number of stacks.
	-- @return A boolean value for the result of the comparison.
	-- @see DebuffStacksOnAny

	local function BuffStacksOnAny(condition, state)
		local auraId, comparator, limit = condition[1], condition[2], condition[3]
		local _, filter, mine = ParseCondition(condition, state)
		local excludeUnitId = (condition.excludeTarget == 1) and state.defaultTarget or nil

		local count, stacks, startChangeCount, endingChangeCount, startFirst, endingLast = state:AuraCount(auraId, filter, mine, 1, excludeUnitId)
		if count > 0 then
			local start, ending = startFirst, endingChangeCount
			return TestValue(start, ending, stacks, start, 0, comparator, limit)
		end
		return Compare(count, comparator, limit)
	end

	OvaleCondition:RegisterCondition("buffstacksonany", false, BuffStacksOnAny)
	OvaleCondition:RegisterCondition("debuffstacksonany", false, BuffStacksOnAny)
end

do
	--- Test if there is a stealable buff on the target.
	-- @name BuffStealable
	-- @paramsig boolean
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return A boolean value.
	-- @usage
	-- if target.BuffStealable()
	--     Spell(spellsteal)

	local function BuffStealable(condition, state)
		local target = ParseCondition(condition, state)
		return state:GetAuraWithProperty(target, "stealable", "HELPFUL")
	end

	OvaleCondition:RegisterCondition("buffstealable", false, BuffStealable)
end

do
	--- Check if the player can cast the given spell (not on cooldown).
	-- @name CanCast
	-- @paramsig boolean
	-- @param id The spell ID to check.
	-- @return True if the spell cast be cast; otherwise, false.

	local function CanCast(condition, state)
		local spellId = condition[1]
		local start, duration = state:GetSpellCooldown(spellId)
		return start + duration, INFINITY
	end

	OvaleCondition:RegisterCondition("cancast", true, CanCast)
end

do
	--- Get the cast time in seconds of the spell for the player, taking into account current haste effects.
	-- @name CastTime
	-- @paramsig number or boolean
	-- @param id The spell ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The number of seconds.
	-- @return A boolean value for the result of the comparison.
	-- @see ExecuteTime
	-- @usage
	-- if target.DebuffRemaining(flame_shock) < CastTime(lava_burst)
	--     Spell(lava_burst)

	local function CastTime(condition, state)
		local spellId, comparator, limit = condition[1], condition[2], condition[3]
		local castTime = OvaleSpellBook:GetCastTime(spellId) or 0
		return Compare(castTime, comparator, limit)
	end

	--- Get the cast time in seconds of the spell for the player or the GCD for the player, whichever is greater.
	-- @name ExecuteTime
	-- @paramsig number or boolean
	-- @param id The spell ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target of the spell. The target may also be given as a prefix to the condition.
	--     Defaults to target=target.
	--     Valid values: player, target, focus, pet.
	-- @return The number of seconds.
	-- @return A boolean value for the result of the comparison.
	-- @see CastTime
	-- @usage
	-- if target.DebuffRemaining(flame_shock) < ExecuteTime(lava_burst)
	--     Spell(lava_burst)

	local function ExecuteTime(condition, state)
		local spellId, comparator, limit = condition[1], condition[2], condition[3]
		local target = ParseCondition(condition, state, "target")
		local castTime = OvaleSpellBook:GetCastTime(spellId) or 0
		local gcd = state:GetGCD(nil, target)
		local t = (castTime > gcd) and castTime or gcd
		return Compare(t, comparator, limit)
	end

	OvaleCondition:RegisterCondition("casttime", true, CastTime)
	OvaleCondition:RegisterCondition("executetime", true, ExecuteTime)
end

do
	--- Test if the target is casting the given spell.
	-- The spell may be specified either by spell ID, spell list name (as defined in SpellList),
	-- "harmful" for any harmful spell, or "helpful" for any helpful spell.
	-- @name Casting
	-- @paramsig boolean
	-- @param spell The spell to check.
	--     Valid values: spell ID, spell list name, harmful, helpful
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return A boolean value.
	-- @usage
	-- Define(maloriak_release_aberrations 77569)
	-- if target.Casting(maloriak_release_aberrations)
	--     Spell(pummel)

	local function Casting(condition, state)
		local spellId = condition[1]
		local target = ParseCondition(condition, state)

		-- Get the information about the current spellcast.
		local start, ending, castSpellId, castSpellName
		if target == "player" then
			start = state.startCast
			ending = state.endCast
			castSpellId = state.currentSpellId
			castSpellName = OvaleSpellBook:GetSpellName(castSpellId)
		else
			local spellName, _, _, _, startTime, endTime = API_UnitCastingInfo(target)
			if not spellName then
				spellName, _, _, _, startTime, endTime = API_UnitChannelInfo("unit")
			end
			if spellName then
				castSpellName = spellName
				start = startTime/1000
				ending = endTime/1000
			end
		end

		if castSpellId or castSpellName then
			if not spellId then
				-- No spell specified, so whatever spell is currently casting.
				return start, ending
			elseif OvaleData.buffSpellList[spellId] then
				for id in pairs(OvaleData.buffSpellList[spellId]) do
					if id == castSpellId or OvaleSpellBook:GetSpellName(id) == castSpellName then
						return start, ending
					end
				end
			elseif spellId == "harmful" and OvaleSpellBook:IsHarmfulSpell(spellId) then
				return start, ending
			elseif spellId == "helpful" and OvaleSpellBook:IsHelpfulSpell(spellId) then
				return start, ending
			elseif spellId == castSpellId then
				return start, ending
			elseif type(spellId) == "number" and OvaleSpellBook:GetSpellName(spellId) == castSpellName then
				return start, ending
			end
		end
		return nil
	end

	OvaleCondition:RegisterCondition("casting", false, Casting)
end

do
	--- Test if all of the listed checkboxes are off.
	-- @name CheckBoxOff
	-- @paramsig boolean
	-- @param id The name of a checkbox. It should match one defined by AddCheckBox(...).
	-- @param ... Optional. Additional checkbox names.
	-- @return A boolean value.
	-- @see CheckBoxOn
	-- @usage
	-- AddCheckBox(opt_black_arrow "Black Arrow" default)
	-- if CheckBoxOff(opt_black_arrow) Spell(explosive_trap)

	local function CheckBoxOff(condition, state)
		for i = 1, #condition do
			if Ovale:IsChecked(condition[i]) then
				return nil
			end
		end
		return 0, INFINITY
	end

	--- Test if all of the listed checkboxes are on.
	-- @name CheckBoxOn
	-- @paramsig boolean
	-- @param id The name of a checkbox. It should match one defined by AddCheckBox(...).
	-- @param ... Optional. Additional checkbox names.
	-- @return A boolean value.
	-- @see CheckBoxOff
	-- @usage
	-- AddCheckBox(opt_black_arrow "Black Arrow" default)
	-- if CheckBoxOn(opt_black_arrow) Spell(black_arrow)

	local function CheckBoxOn(condition, state)
		for i = 1, #condition do
			if not Ovale:IsChecked(condition[i]) then
				return nil
			end
		end
		return 0, INFINITY
	end

	OvaleCondition:RegisterCondition("checkboxoff", false, CheckBoxOff)
	OvaleCondition:RegisterCondition("checkboxon", false, CheckBoxOn)
end

do
	--- Test whether the target's class matches the given class.
	-- @name Class
	-- @paramsig boolean
	-- @param class The class to check.
	--     Valid values: DEATHKNIGHT, DRUID, HUNTER, MAGE, MONK, PALADIN, PRIEST, ROGUE, SHAMAN, WARLOCK, WARRIOR.
	-- @param yesno Optional. If yes, then return true if it matches. If no, then return true if it doesn't match.
	--     Default is yes.
	--     Valid values: yes, no.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return A boolean value.
	-- @usage
	-- if target.Class(PRIEST) Spell(cheap_shot)

	local function Class(condition, state)
		local class, yesno = condition[1], condition[2]
		local target = ParseCondition(condition, state)
		local _, classToken = API_UnitClass(target)
		local boolean = (classToken == class)
		return TestBoolean(boolean, yesno)
	end

	OvaleCondition:RegisterCondition("class", false, Class)
end

do
	--- Test whether the target's classification matches the given classification.
	-- @name Classification
	-- @paramsig boolean
	-- @param classification The unit classification to check.
	--     Valid values: normal, elite, worldboss.
	-- @param yesno Optional. If yes, then return true if it matches. If no, then return true if it doesn't match.
	--     Default is yes.
	--     Valid values: yes, no.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return A boolean value.
	-- @usage
	-- if target.Classification(worldboss) Item(virmens_bite_potion)

	local function Classification(condition, state)
		local classification, yesno = condition[1], condition[2]
		local targetClassification
		local target = ParseCondition(condition, state)
		if API_UnitLevel(target) < 0 then
			targetClassification = "worldboss"
		else
			targetClassification = API_UnitClassification(target)
			if targetClassification == "rareelite" then
				targetClassification = "elite"
			elseif targetClassification == "rare" then
				targetClassification = "normal"
			end
		end
		local boolean = (targetClassification == classification)
		return TestBoolean(boolean, yesno)
	end

	OvaleCondition:RegisterCondition("classification", false, Classification)
end

do
	--- Get the number of combo points on the currently selected target for a feral druid or a rogue.
	-- @name ComboPoints
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The number of combo points.
	-- @return A boolean value for the result of the comparison.
	-- @see LastComboPoints
	-- @usage
	-- if ComboPoints() >=1 Spell(savage_roar)
	-- if ComboPoints(more 0) Spell(savage_roar)

	local function ComboPoints(condition, state)
		local comparator, limit = condition[1], condition[2]
		local value = state.combo
		return Compare(value, comparator, limit)
	end

	OvaleCondition:RegisterCondition("combopoints", false, ComboPoints)
end

do
	--- Get the current value of a script counter.
	-- @name Counter
	-- @paramsig number or boolean
	-- @param id The name of the counter. It should match one that's defined by inccounter=xxx in SpellInfo(...).
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The current value the counter.
	-- @return A boolean value for the result of the comparison.

	local function Counter(condition, state)
		local counter, comparator, limit = condition[1], condition[2], condition[3]
		local value = state:GetCounterValue(counter)
		return Compare(value, comparator, limit)
	end

	OvaleCondition:RegisterCondition("counter", false, Counter)
end

do
	--- Test whether the target's creature family matches the given name.
	-- Applies only to beasts that can be taken as hunter pets (e.g., cats, worms, and ravagers but not zhevras, talbuks and pterrordax),
	-- demons that can be summoned by Warlocks (e.g., imps and felguards, but not demons that require enslaving such as infernals
	-- and doomguards or world demons such as pit lords and armored voidwalkers), and Death Knight's pets (ghouls)
	-- @name CreatureFamily
	-- @paramsig boolean
	-- @param name The English name of the creature family to check.
	--     Valid values: Bat, Beast, Felguard, Imp, Ravager, etc.
	-- @param yesno Optional. If yes, then return true if it matches. If no, then return true if it doesn't match.
	--     Default is yes.
	--     Valid values: yes, no.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return A boolean value.
	-- @usage
	-- if pet.CreatureFamily(Felguard)
	--     Spell(summon_felhunter)
	-- if target.CreatureFamily(Dragonkin)
	--     Spell(hibernate)

	local function CreatureFamily(condition, state)
		local name, yesno = condition[1], condition[2]
		local target = ParseCondition(condition, state)
		local family = API_UnitCreatureFamily(target)
		local lookupTable = LibBabbleCreatureType and LibBabbleCreatureType:GetLookupTable()
		local boolean = (lookupTable and family == lookupTable[name])
		return TestBoolean(boolean, yesno)
	end

	OvaleCondition:RegisterCondition("creaturefamily", false, CreatureFamily)
end

do
	--- Test if the target is any of the listed creature types.
	-- @name CreatureType
	-- @paramsig boolean
	-- @param name The English name of a creature type.
	--     Valid values: Beast, Humanoid, Undead, etc.
	-- @param ... Optional. Additional creature types.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return A boolean value.
	-- @usage
	-- if target.CreatureType(Humanoid Critter)
	--     Spell(polymorph)

	local function CreatureType(condition, state)
		local target = ParseCondition(condition, state)
		local creatureType = API_UnitCreatureType(target)
		local lookupTable = LibBabbleCreatureType and LibBabbleCreatureType:GetLookupTable()
		if lookupTable then
			for _, name in ipairs(condition) do
				if creatureType == lookupTable[name] then
					return 0, INFINITY
				end
			end
		end
		return nil
	end

	OvaleCondition:RegisterCondition("creaturetype", false, CreatureType)
end

do
	-- Return the non-critical-strike damage of a spell, given the player's current stats.
	local function GetDamage(spellId, state)
		-- TODO: Use target's debuffs in this calculation.
		local ap = state.snapshot.attackPower or 0
		local sp = state.snapshot.spellBonusDamage or 0
		local mh = state.snapshot.mainHandWeaponDamage or 0
		local oh = state.snapshot.offHandWeaponDamage or 0
		local bdm = state.snapshot.baseDamageMultiplier or 1
		local dm = state:GetDamageMultiplier(spellId) or 1
		local combo = state.combo or 0
		return OvaleData:GetDamage(spellId, ap, sp, mh, oh, combo) * bdm * dm
	end

	--- Get the current estimated damage of a spell on the target if it is a critical strike.
	-- @name CritDamage
	-- @paramsig number or boolean
	-- @param id The spell ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The estimated critical strike damage of the given spell.
	-- @return A boolean value for the result of the comparison.
	-- @see Damage, LastDamage, LastEstimatedDamage

	local AMPLIFICATION = 146051
	local INCREASED_CRIT_EFFECT_3_PERCENT = 44797

	local function CritDamage(condition, state)
		local spellId, comparator, limit = condition[1], condition[2], condition[3]
		local target = ParseCondition(condition, state, "target")
		local value = ComputeParameter(spellId, "damage", state)
		if not value then
			value = GetDamage(spellId, state)
		end
		-- Reduce by armor damage reduction for physical attacks.
		local physical = state:GetSpellInfoProperty(spellId, "physical", target)
		if physical == 1 then
			value = value * (1 - BossArmorDamageReduction(target))
		end

		-- Default crit damage is 2 times normal damage.
		local critMultiplier = 2
		-- Add additional critical strike damage from MoP amplification trinkets.
		do
			local aura = state:GetAura("player", AMPLIFICATION, "HELPFUL")
			if state:IsActiveAura(aura) then
				critMultiplier = critMultiplier + aura.value1
			end
		end
		-- Multiply by increased crit effect from the meta gem.
		do
			local aura = state:GetAura("player", INCREASED_CRIT_EFFECT_3_PERCENT, "HELPFUL")
			if state:IsActiveAura(aura) then
				critMultiplier = critMultiplier * aura.value1
			end
		end

		local value = critMultiplier * value
		return Compare(value, comparator, limit)
	end

	OvaleCondition:RegisterCondition("critdamage", false, CritDamage)

	--- Get the current estimated damage of a spell on the target.
	-- The calculated damage takes into account the current attack power, spellpower, weapon damage and combo points (if used).
	-- The damage is computed from information for the spell set via SpellInfo(...):
	--
	-- damage = base + bonusmainhand * MH + bonusoffhand * OH + bonusap * AP + bonuscp * CP + bonusapcp * AP * CP + bonussp * SP
	-- @name Damage
	-- @paramsig number or boolean
	-- @param id The spell ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The estimated damage of the given spell on the target.
	-- @return A boolean value for the result of the comparison.
	-- @see CritDamage, LastDamage, LastEstimatedDamage
	-- @usage
	-- if {target.Damage(rake) / target.LastEstimateDamage(rake)} >1.1
	--     Spell(rake)

	local function Damage(condition, state)
		local spellId, comparator, limit = condition[1], condition[2], condition[3]
		local target = ParseCondition(condition, state, "target")
		local value = ComputeParameter(spellId, "damage", state)
		if not value then
			value = GetDamage(spellId, state)
		end
		-- Reduce by armor damage reduction for physical attacks.
		local physical = state:GetSpellInfoProperty(spellId, "physical", target)
		if physical == 1 then
			value = value * (1 - BossArmorDamageReduction(target))
		end
		return Compare(value, comparator, limit)
	end

	OvaleCondition:RegisterCondition("damage", false, Damage)
end

do
	--- Get the current damage multiplier of a spell.
	-- This currently does not take into account increased damage due to mastery.
	-- @name DamageMultiplier
	-- @paramsig number or boolean
	-- @param id The spell ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The current damage multiplier of the given spell.
	-- @return A boolean value for the result of the comparison.
	-- @see LastDamageMultiplier
	-- @usage
	-- if {DamageMultiplier(rupture) / LastDamageMultiplier(rupture)} >1.1
	--     Spell(rupture)

	local function DamageMultiplier(condition, state)
		local spellId, comparator, limit = condition[1], condition[2], condition[3]
		local bdm = state.snapshot.baseDamageMultiplier
		local dm = state:GetDamageMultiplier(spellId)
		local value = bdm * dm
		return Compare(value, comparator, limit)
	end

	OvaleCondition:RegisterCondition("damagemultiplier", false, DamageMultiplier)
end

do
	--- Get the damage taken by the player in the previous time interval.
	-- @name DamageTaken
	-- @paramsig number or boolean
	-- @param interval The number of seconds before now.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The amount of damage taken in the previous interval.
	-- @return A boolean value for the result of the comparison.
	-- @see IncomingDamage
	-- @usage
	-- if DamageTaken(5) > 50000 Spell(death_strike)

	local function DamageTaken(condition, state)
		-- Damage taken shouldn't be smoothed since spike damage is important data.
		-- Just present damage taken as a constant value.
		local interval, comparator, limit = condition[1], condition[2], condition[3]
		local value = 0
		if interval > 0 then
			value = OvaleDamageTaken:GetRecentDamage(interval)
		end
		return Compare(value, comparator, limit)
	end

	OvaleCondition:RegisterCondition("damagetaken", false, DamageTaken)
	OvaleCondition:RegisterCondition("incomingdamage", false, DamageTaken)
end

do
	local NECROTIC_PLAGUE_TALENT = 19
	local NECROTIC_PLAGUE_DEBUFF = 155159
	local BLOOD_PLAGUE_DEBUFF = 55078
	local FROST_FEVER_DEBUFF = 55095

	local function GetDiseases(target, state)
		local npAura, bpAura, ffAura
		local talented = (OvaleSpellBook:GetTalentPoints(NECROTIC_PLAGUE_TALENT) > 0)
		if talented then
			npAura = state:GetAura(target, NECROTIC_PLAGUE_DEBUFF, "HARMFUL", true)
		else
			bpAura = state:GetAura(target, BLOOD_PLAGUE_DEBUFF, "HARMFUL", true)
			ffAura = state:GetAura(target, FROST_FEVER_DEBUFF, "HARMFUL", true)
		end
		return talented, npAura, bpAura, ffAura
	end

	--- Get the remaining time in seconds before any diseases applied by the death knight will expire.
	-- @name DiseasesRemaining
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The number of seconds.
	-- @return A boolean value for the result of the comparison.

	local function DiseasesRemaining(condition, state)
		local comparator, limit = condition[1], condition[2], condition[3]
		local target, filter, mine = ParseCondition(condition, state)
		local talented, npAura, bpAura, ffAura = GetDiseases(target, state)
		local aura
		if talented and state:IsActiveAura(npAura) then
			aura = npAura
		elseif not talented and state:IsActiveAura(bpAura) and state:IsActiveAura(ffAura) then
			aura = (bpAura.ending < ffAura.ending) and bpAura or ffAura
		end
		if aura then
			local gain, start, ending = aura.gain, aura.start, aura.ending
			return TestValue(gain, INFINITY, 0, ending, -1, comparator, limit)
		end
		return Compare(0, comparator, limit)
	end

	--- Test if all diseases applied by the death knight are present on the target.
	-- @name DiseasesTicking
	-- @paramsig boolean
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return A boolean value.

	local function DiseasesTicking(condition, state)
		local target, filter, mine = ParseCondition(condition, state)
		local talented, npAura, bpAura, ffAura = GetDiseases(target, state)
		local gain, start, ending
		if talented and npAura then
			gain, start, ending = npAura.gain, npAura.start, npAura.ending
		elseif not talented and bpAura and ffAura then
			-- Compute the intersection of the time spans for the two disease auras.
			gain = (bpAura.gain > ffAura.gain) and bpAura.gain or ffAura.gain
			start = (bpAura.start > ffAura.start) and bpAura.start or ffAura.start
			ending = (bpAura.ending < ffAura.ending) and bpAura.ending or ffAura.ending
		end
		if gain and ending and ending > gain then
			return gain, ending
		end
		return nil
	end

	--- Test if any diseases applied by the death knight are present on the target.
	-- @name DiseasesAnyTicking
	-- @paramsig boolean
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return A boolean value.

	local function DiseasesAnyTicking(condition, state)
		local target, filter, mine = ParseCondition(condition, state)
		local talented, npAura, bpAura, ffAura = GetDiseases(target, state)
		local aura
		if talented and npAura then
			aura = npAura
		elseif not talented and (bpAura or ffAura) then
			aura = bpAura or ffAura
			if bpAura and ffAura then
				-- Find the disease that expires latest.
				aura = (bpAura.ending > ffAura.ending) and bpAura or ffAura
			end
		end
		if aura then
			local gain, start, ending = aura.gain, aura.start, aura.ending
			if ending > gain then
				return gain, ending
			end
		end
		return nil
	end

	OvaleCondition:RegisterCondition("diseasesremaining", false, DiseasesRemaining)
	OvaleCondition:RegisterCondition("diseasesticking", false, DiseasesTicking)
	OvaleCondition:RegisterCondition("diseasesanyticking", false, DiseasesAnyTicking)
end

do
	--- Get the distance in yards to the target.
	-- The distances are from LibRangeCheck-2.0, which determines distance based on spell range checks, so results are approximate.
	-- You should not test for equality.
	-- @name Distance
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The distance to the target.
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- if target.Distance(less 25)
	--     Texture(ability_rogue_sprint)

	local function Distance(condition, state)
		local comparator, limit = condition[1], condition[2]
		local target = ParseCondition(condition, state)
		local value = LibRangeCheck and LibRangeCheck:GetRange(target) or 0
		return Compare(value, comparator, limit)
	end

	OvaleCondition:RegisterCondition("distance", false, Distance)
end

do
	--- Get the current amount of Eclipse power for balance druids.
	-- A negative amount of power signifies being closer to Lunar Eclipse.
	-- A positive amount of power signifies being closer to Solar Eclipse.
	-- @name Eclipse
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The amount of Eclipse power.
	-- @return A boolean value for the result of the comparison.
	-- @see EclipseDir
	-- @usage
	-- if Eclipse() < 0-70 and EclipseDir() <0 Spell(wrath)
	-- if Eclipse(less -70) and EclipseDir(less 0) Spell(wrath)

	local function Eclipse(condition, state)
		local comparator, limit = condition[1], condition[2]
		local value = state.eclipse
		return Compare(value, comparator, limit)
	end

	OvaleCondition:RegisterCondition("eclipse", false, Eclipse)
end

do
	--- Get the current direction of the Eclipse status on the Eclipse bar for balance druids.
	-- A negative number means heading toward Lunar Eclipse.
	-- A positive number means heading toward Solar Eclipse.
	-- Zero means it can head in either direction.
	-- @name EclipseDir
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The current direction.
	-- @return A boolean value for the result of the comparison.
	-- @see Eclipse
	-- @usage
	-- if Eclipse() < 0-70 and EclipseDir() <0 Spell(wrath)
	-- if Eclipse(less -70) and EclipseDir(less 0) Spell(wrath)

	local function EclipseDir(condition, state)
		local comparator, limit = condition[1], condition[2]
		local value = state.eclipseDirection
		return Compare(value, comparator, limit)
	end

	OvaleCondition:RegisterCondition("eclipsedir", false, EclipseDir)
end

do
	--- Get the number of hostile enemies on the battlefield.
	-- The minimum value returned is 1.
	-- @name Enemies
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param tagged Optional. By default, all enemies are counted. To count only enemies directly tagged by the player, set tagged=1.
	--     Defaults to tagged=0.
	--     Valid values: 0, 1.
	-- @return The number of enemies.
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- if Enemies() >4 Spell(fan_of_knives)
	-- if Enemies(more 4) Spell(fan_of_knives)

	local function Enemies(condition, state)
		local comparator, limit = condition[1], condition[2]
		local value = state.enemies
		if not value then
			if condition.tagged == 1 then
				value = state.taggedEnemies
			else
				value = state.activeEnemies
			end
		end
		-- This works around problems with testing on target dummies, which are never hostile.
		if value < 1 then
			value = 1
		end
		return Compare(value, comparator, limit)
	end

	OvaleCondition:RegisterCondition("enemies", false, Enemies)
end

do
	--- Get the amount of regenerated energy per second for feral druids, non-mistweaver monks, and rogues.
	-- @name EnergyRegenRate
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The current rate of energy regeneration.
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- if EnergyRegenRage() >11 Spell(stance_of_the_sturdy_ox)

	local function EnergyRegenRate(condition, state)
		local comparator, limit = condition[1], condition[2]
		local value = state.powerRate.energy
		return Compare(value, comparator, limit)
	end

	OvaleCondition:RegisterCondition("energyregen", false, EnergyRegenRate)
	OvaleCondition:RegisterCondition("energyregenrate", false, EnergyRegenRate)
end

do
	--- Test if the target exists. The target may be alive or dead.
	-- @name Exists
	-- @paramsig boolean
	-- @param yesno Optional. If yes, then return true if the target exists. If no, then return true if it doesn't exist.
	--     Default is yes.
	--     Valid values: yes, no.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return A boolean value.
	-- @see Present
	-- @usage
	-- if pet.Exists(no) Spell(summon_imp)

	local function Exists(condition, state)
		local yesno = condition[1]
		local target = ParseCondition(condition, state)
		local boolean = API_UnitExists(target)
		return TestBoolean(boolean, yesno)
	end

	OvaleCondition:RegisterCondition("exists", false, Exists)
end

do
	--- A condition that always returns false.
	-- @name False
	-- @paramsig boolean
	-- @return A boolean value.

	local function False(condition, state)
		return nil
	end

	OvaleCondition:RegisterCondition("false", false, False)
end

do
	--- Get the amount of regenerated focus per second for hunters.
	-- @name FocusRegenRate
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The current rate of focus regeneration.
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- if FocusRegenRate() >20 Spell(arcane_shot)
	-- if FocusRegenRate(more 20) Spell(arcane_shot)

	local function FocusRegenRate(condition, state)
		local comparator, limit = condition[1], condition[2]
		local value = state.powerRate.focus
		return Compare(value, comparator, limit)
	end

	OvaleCondition:RegisterCondition("focusregen", false, FocusRegenRate)
	OvaleCondition:RegisterCondition("focusregenrate", false, FocusRegenRate)
end

do
	--- Get the amount of focus that would be regenerated during the cast time of the given spell for hunters.
	-- @name FocusCastingRegen
	-- @paramsig number or boolean
	-- @param id The spell ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target of the spell. The target may also be given as a prefix to the condition.
	--     Defaults to target=target.
	--     Valid values: player, target, focus, pet.
	-- @return The amount of focus.
	-- @return A boolean value for the result of the comparison.

	local STEADY_FOCUS = 177668

	local function FocusCastingRegen(condition, state)
		local spellId, comparator, limit = condition[1], condition[2], condition[3]
		local target = ParseCondition(condition, state, "target")
		local regenRate = state.powerRate.focus
		local power = 0

		-- Get the "execute time" of the spell (smaller of GCD or the cast time).
		local castTime = OvaleSpellBook:GetCastTime(spellId) or 0
		local gcd = state:GetGCD(nil, target)
		local castSeconds = (castTime > gcd) and castTime or gcd
		power = power + regenRate * castSeconds

		-- Get the amount of time remaining on the Steady Focus buff.
		local aura = state:GetAura("player", STEADY_FOCUS, "HELPFUL", true)
		if aura then
			local seconds = aura.ending - state.currentTime
			if seconds <= 0 then
				seconds = 0
			elseif seconds > castSeconds then
				seconds = castSeconds
			end
			-- Steady Focus increases the focus regeneration rate by 50% for its duration.
			power = power + regenRate * 1.5 * seconds
		end
		return Compare(power, comparator, limit)
	end

	OvaleCondition:RegisterCondition("focuscastingregen", false, FocusCastingRegen)
end

do
	--- Get the player's global cooldown in seconds.
	-- @name GCD
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target of the previous spell. The target may also be given as a prefix to the condition.
	--     Defaults to target=target.
	--     Valid values: player, target, focus, pet.
	-- @return The number of seconds.
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- if GCD() <1.1 Spell(frostfire_bolt)
	-- if GCD(less 1.1) Spell(frostfire_bolt)

	local function GCD(condition, state)
		local comparator, limit = condition[1], condition[2]
		local target = ParseCondition(condition, state, "target")
		local value = state:GetGCD(nil, target)
		return Compare(value, comparator, limit)
	end

	OvaleCondition:RegisterCondition("gcd", false, GCD)
end

do
	--- Get the number of seconds before the player's global cooldown expires.
	-- @name GCDRemaining
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target of the previous spell. The target may also be given as a prefix to the condition.
	--     Defaults to target=target.
	--     Valid values: player, target, focus, pet.
	-- @return The number of seconds.
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- unless SpellCooldown(seraphim) < GCDRemaining() Spell(judgment)

	local function GCDRemaining(condition, state)
		local comparator, limit = condition[1], condition[2]
		local target = ParseCondition(condition, state, "target")
		if state.lastSpellId then
			local duration = state:GetGCD(state.lastSpellId, target)
			local start = state.startCast
			local ending = start + duration
			return TestValue(start, INFINITY, 0, ending, -1, comparator, limit)
		end
		return Compare(0, comparator, limit)
	end

	OvaleCondition:RegisterCondition("gcdremaining", false, GCDRemaining)
end

do
	--- Get the value of the named state variable from the simulator.
	-- @name GetState
	-- @paramsig number or boolean
	-- @param name The name of the state variable.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The value of the state variable.
	-- @return A boolean value for the result of the comparison.

	local function GetState(condition, state)
		local name, comparator, limit = condition[1], condition[2], condition[3]
		local value = state:GetState(name)
		return Compare(value, comparator, limit)
	end

	OvaleCondition:RegisterCondition("getstate", false, GetState)
end

do
	--- Test if the given glyph is active.
	-- @name Glyph
	-- @paramsig boolean
	-- @param id The glyph spell ID.
	-- @param yesno Optional. If yes, then return true if the glyph is active. If no, then return true if it isn't active.
	--     Default is yes.
	--     Valid values: yes, no.
	-- @return A boolean value.
	-- @usage
	-- if InCombat(no) and Glyph(glyph_of_savagery)
	--     Spell(savage_roar)

	local function Glyph(condition, state)
		local glyph, yesno = condition[1], condition[2]
		local boolean = OvaleSpellBook:IsActiveGlyph(glyph)
		return TestBoolean(boolean, yesno)
	end

	OvaleCondition:RegisterCondition("glyph", false, Glyph)
end

do
	--- Test if the player has a particular item equipped.
	-- @name HasEquippedItem
	-- @paramsig boolean
	-- @param item Item to be checked whether it is equipped.
	-- @param yesno Optional. If yes, then return true if the item is equipped. If no, then return true if it isn't equipped.
	--     Default is yes.
	--     Valid values: yes, no.
	-- @param ilevel Optional.  Checks the item level of the equipped item.  If not specified, then any item level is valid.
	--     Defaults to not specified.
	--     Valid values: ilevel=N, where N is any number.
	-- @param slot Optional. Sets the inventory slot to check.  If not specified, then all slots are checked.
	--     Defaults to not specified.
	--     Valid values: slot=SLOTNAME, where SLOTNAME is a valid slot name, e.g., HandSlot.

	local function HasEquippedItem(condition, state)
		local itemId, yesno = condition[1], condition[2]
		local ilevel, slot = condition.ilevel, condition.slot
		local boolean = false
		local slotId
		if type(itemId) == "number" then
			slotId = OvaleEquipment:HasEquippedItem(itemId, slot)
			if slotId then
				if not ilevel or (ilevel and ilevel == OvaleEquipment:GetEquippedItemLevel(slotId)) then
					boolean = true
				end
			end
		elseif OvaleData.itemList[itemId] then
			for _, v in pairs(OvaleData.itemList[itemId]) do
				slotId = OvaleEquipment:HasEquippedItem(v, slot)
				if slotId then
					if not ilevel or (ilevel and ilevel == OvaleEquipment:GetEquippedItemLevel(slotId)) then
						boolean = true
						break
					end
				end
			end
		end
		return TestBoolean(boolean, yesno)
	end

	OvaleCondition:RegisterCondition("hasequippeditem", false, HasEquippedItem)
end

do
	--- Test if the player has full control, i.e., isn't feared, charmed, etc.
	-- @name HasFullControl
	-- @paramsig boolean
	-- @param yesno Optional. If yes, then return true if the target exists. If no, then return true if it doesn't exist.
	--     Default is yes.
	--     Valid values: yes, no.
	-- @return A boolean value.
	-- @usage
	-- if HasFullControl(no) Spell(barkskin)

	local function HasFullControl(condition, state)
		local yesno = condition[1]
		local boolean = API_HasFullControl()
		return TestBoolean(boolean, yesno)
	end

	OvaleCondition:RegisterCondition("hasfullcontrol", false, HasFullControl)
end

do
	--- Test if the player has a shield equipped.
	-- @name HasShield
	-- @paramsig boolean
	-- @param yesno Optional. If yes, then return true if a shield is equipped. If no, then return true if it isn't equipped.
	--     Default is yes.
	--     Valid values: yes, no.
	-- @return A boolean value.
	-- @usage
	-- if HasShield() Spell(shield_wall)

	local function HasShield(condition, state)
		local yesno = condition[1]
		local boolean = OvaleEquipment:HasShield()
		return TestBoolean(boolean, yesno)
	end

	OvaleCondition:RegisterCondition("hasshield", false, HasShield)
end

do
	--- Test if the player has a particular trinket equipped.
	-- @name HasTrinket
	-- @paramsig boolean
	-- @param id The item ID of the trinket or the name of an item list.
	-- @param yesno Optional. If yes, then return true if the trinket is equipped. If no, then return true if it isn't equipped.
	--     Default is yes.
	--     Valid values: yes, no.
	-- @return A boolean value.
	-- @usage
	-- ItemList(rune_of_reorigination 94532 95802 96546)
	-- if HasTrinket(rune_of_reorigination) and BuffPresent(rune_of_reorigination_buff)
	--     Spell(rake)

	local function HasTrinket(condition, state)
		local trinketId, yesno = condition[1], condition[2]
		local boolean = false
		if type(trinketId) == "number" then
			boolean = OvaleEquipment:HasTrinket(trinketId)
		elseif OvaleData.itemList[trinketId] then
			for _, v in pairs(OvaleData.itemList[trinketId]) do
				boolean = OvaleEquipment:HasTrinket(v)
				if boolean then
					break
				end
			end
		end
		return TestBoolean(boolean, yesno)
	end

	OvaleCondition:RegisterCondition("hastrinket", false, HasTrinket)
end

do
	--- Test if the player has a weapon equipped.
	-- @name HasWeapon
	-- @paramsig boolean
	-- @param hand Sets which hand weapon.
	--     Valid values: main, off
	-- @param yesno Optional. If yes, then return true if the weapon is equipped. If no, then return true if it isn't equipped.
	--     Default is yes.
	--     Valid values: yes, no.
	-- @param type Optional. If set via type=value, then specify whether the weapon must be one-handed or two-handed.
	--     Default is unset.
	--     Valid values: one_handed, two_handed
	-- @return A boolean value.
	-- @usage
	-- if HasWeapon(offhand) and BuffStacks(killing_machine) Spell(frost_strike)

	local function HasWeapon(condition, state)
		local hand, yesno = condition[1], condition[2]
		local weaponType = condition.type
		local boolean = false
		if weaponType == "one_handed" then
			weaponType = 1
		elseif weaponType == "two_handed" then
			weaponType = 2
		end
		if hand == "offhand" or hand == "off" then
			boolean = OvaleEquipment:HasOffHandWeapon(weaponType)
		elseif hand == "mainhand" or hand == "main" then
			boolean = OvaleEquipment:HasMainHandWeapon(weaponType)
		end
		return TestBoolean(boolean, yesno)
	end

	OvaleCondition:RegisterCondition("hasweapon", false, HasWeapon)
end

do
	-- static properties for TimeToDie(), indexed by unit ID
	local lastTTDTime = {}
	local lastTTDHealth = {}
	local lastTTDguid = {}
	local lastTTDdps = {}

	--[[
		Returns:
			Estimated number of seconds before the specified unit reaches zero health
			The current time
			Unit's current health
			Unit's maximum health
	--]]
	local function EstimatedTimeToDie(state, unitId)
		-- Check for target switch.
		if lastTTDguid[unitId] ~= OvaleGUID:GetGUID(unitId) then
			lastTTDguid[unitId] = OvaleGUID:GetGUID(unitId)
			lastTTDTime[unitId] = nil
			if lastTTDHealth[unitId] then
				wipe(lastTTDHealth[unitId])
			else
				lastTTDHealth[unitId] = {}
			end
			lastTTDdps[unitId] = nil
		end

		local timeToDie
		local health = API_UnitHealth(unitId) or 0
		local maxHealth = API_UnitHealthMax(unitId) or 1
		local currentTime = API_GetTime()

		-- Clamp maxHealth to always be at least 1.
		if maxHealth < health then
			maxHealth = health
		end
		if maxHealth < 1 then
			maxHealth = 1
		end

		if health == 0 then
			timeToDie = 0
		elseif maxHealth <= 5 then
			timeToDie = INFINITY
		else
			local now = floor(currentTime)
			if (not lastTTDTime[unitId] or lastTTDTime[unitId] < now) and lastTTDguid[unitId] then
				lastTTDTime[unitId] = now
				local mod10, prevHealth
				for delta = 10, 1, -1 do
					mod10 = (now - delta) % 10
					prevHealth = lastTTDHealth[unitId][mod10]
					if delta == 10 then
						lastTTDHealth[unitId][mod10] = health
					end
					if prevHealth and prevHealth > health then
						lastTTDdps[unitId] = (prevHealth - health) / delta
						state:Log("prevHealth = %d, health = %d, delta = %d, dps = %d", prevHealth, health, delta, lastTTDdps[unitId])
						break
					end
				end
			end
			local dps = lastTTDdps[unitId]
			if dps and dps > 0 then
				timeToDie = health / dps
			else
				timeToDie = INFINITY
			end
		end
		-- Clamp time to die at a finite number.
		if timeToDie == INFINITY then
			-- Return time to die in the far-off future (one week).
			timeToDie = 3600 * 24 * 7
		end
		return timeToDie, currentTime, health, maxHealth
	end

	--- Get the current amount of health points of the target.
	-- @name Health
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The current health.
	-- @return A boolean value for the result of the comparison.
	-- @see Life
	-- @usage
	-- if Health() <10000 Spell(last_stand)
	-- if Health(less 10000) Spell(last_stand)

	local function Health(condition, state)
		local comparator, limit = condition[1], condition[2]
		local target = ParseCondition(condition, state)
		local timeToDie, now, health, maxHealth = EstimatedTimeToDie(state, target)
		if not timeToDie then
			return nil
		elseif timeToDie == 0 then
			return Compare(0, comparator, limit)
		end
		local value, origin, rate = health, now, -1 * health / timeToDie
		local start, ending = now, INFINITY
		return TestValue(start, ending, value, origin, rate, comparator, limit)
	end

	OvaleCondition:RegisterCondition("health", false, Health)
	OvaleCondition:RegisterCondition("life", false, Health)

	--- Get the number of health points away from full health of the target.
	-- @name HealthMissing
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The current missing health.
	-- @return A boolean value for the result of the comparison.
	-- @see LifeMissing
	-- @usage
	-- if HealthMissing() <20000 Item(healthstone)
	-- if HealthMissing(less 20000) Item(healthstone)

	local function HealthMissing(condition, state)
		local comparator, limit = condition[1], condition[2]
		local target = ParseCondition(condition, state)
		local timeToDie, now, health, maxHealth = EstimatedTimeToDie(state, target)
		if not timeToDie or timeToDie == 0 then
			return nil
		end
		local missing = maxHealth - health
		local value, origin, rate = missing, now, health / timeToDie
		local start, ending = now, INFINITY
		return TestValue(start, ending, value, origin, rate, comparator, limit)
	end

	OvaleCondition:RegisterCondition("healthmissing", false, Health)
	OvaleCondition:RegisterCondition("lifemissing", false, Health)

	--- Get the current percent level of health of the target.
	-- @name HealthPercent
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The current health percent.
	-- @return A boolean value for the result of the comparison.
	-- @see LifePercent
	-- @usage
	-- if HealthPercent() <20 Spell(last_stand)
	-- if target.HealthPercent(less 25) Spell(kill_shot)

	local function HealthPercent(condition, state)
		local comparator, limit = condition[1], condition[2]
		local target = ParseCondition(condition, state)
		local timeToDie, now, health, maxHealth = EstimatedTimeToDie(state, target)
		if not timeToDie then
			return nil
		elseif timeToDie == 0 then
			return Compare(0, comparator, limit)
		end
		local healthPercent = health / maxHealth * 100
		local value, origin, rate = healthPercent, now, -1 * healthPercent / timeToDie
		local start, ending = now, INFINITY
		return TestValue(start, ending, value, origin, rate, comparator, limit)
	end

	OvaleCondition:RegisterCondition("healthpercent", false, HealthPercent)
	OvaleCondition:RegisterCondition("lifepercent", false, HealthPercent)

	--- Get the amount of health points of the target when it is at full health.
	-- @name MaxHealth
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The maximum health.
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- if target.MaxHealth() >10000000 Item(mogu_power_potion)
	-- if target.MaxHealth(more 10000000) Item(mogu_power_potion)

	local function MaxHealth(condition, state)
		local comparator, limit = condition[1], condition[2]
		local target = ParseCondition(condition, state)
		local value = API_UnitHealthMax(target)
		return Compare(value, comparator, limit)
	end

	OvaleCondition:RegisterCondition("maxhealth", false, MaxHealth)

	--- Get the estimated number of seconds remaining before the target is dead.
	-- @name TimeToDie
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The number of seconds.
	-- @return A boolean value for the result of the comparison.
	-- @see DeadIn
	-- @usage
	-- if target.TimeToDie() <2 and ComboPoints() >0 Spell(eviscerate)

	local function TimeToDie(condition, state)
		local comparator, limit = condition[1], condition[2]
		local target = ParseCondition(condition, state)
		local timeToDie, now = EstimatedTimeToDie(state, target)
		local value, origin, rate = timeToDie, now, -1
		local start, ending = now, now + timeToDie
		return TestValue(start, ending, value, origin, rate, comparator, limit)
	end

	OvaleCondition:RegisterCondition("deadin", false, TimeToDie)
	OvaleCondition:RegisterCondition("timetodie", false, TimeToDie)

	--- Get the estimated number of seconds remaining before the target reaches the given percent of max health.
	-- @name TimeToHealthPercent
	-- @paramsig number or boolean
	-- @param percent The percent of maximum health of the target.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The number of seconds.
	-- @return A boolean value for the result of the comparison.
	-- @see TimeToDie
	-- @usage
	-- if target.TimeToHealthPercent(25) <15 Item(virmens_bite_potion)

	local function TimeToHealthPercent(condition, state)
		local percent, comparator, limit = condition[1], condition[2], condition[3]
		local target = ParseCondition(condition, state)
		local timeToDie, now, health, maxHealth = EstimatedTimeToDie(state, target)
		local healthPercent = health / maxHealth * 100
		if healthPercent >= percent then
			local t = timeToDie * (healthPercent - percent) / healthPercent
			local value, origin, rate = t, now, -1
			local start, ending = now, now + t
			return TestValue(start, ending, value, origin, rate, comparator, limit)
		end
		return Compare(0, comparator, limit)
	end

	OvaleCondition:RegisterCondition("timetohealthpercent", false, TimeToHealthPercent)
	OvaleCondition:RegisterCondition("timetolifepercent", false, TimeToHealthPercent)
end

do
	--- Test if the player is in combat.
	-- @name InCombat
	-- @paramsig boolean
	-- @param yesno Optional. If yes, then return true if the player is in combat. If no, then return true if the player isn't in combat.
	--     Default is yes.
	--     Valid values: yes, no.
	-- @return A boolean value.
	-- @usage
	-- if InCombat(no) and Stealthed(no) Spell(stealth)

	local function InCombat(condition, state)
		local yesno = condition[1]
		local boolean = state.inCombat
		return TestBoolean(boolean, yesno)
	end

	OvaleCondition:RegisterCondition("incombat", false, InCombat)
end

do
	--- Test if the given spell is in flight for spells that have a flight time after cast, e.g., Lava Burst.
	-- @name InFlightToTarget
	-- @paramsig boolean
	-- @param id The spell ID.
	-- @param yesno Optional. If yes, then return true if the spell is in flight. If no, then return true if it isn't in flight.
	--     Default is yes.
	--     Valid values: yes, no.
	-- @return A boolean value.
	-- @usage
	-- if target.DebuffRemaining(haunt) <3 and not InFlightToTarget(haunt)
	--     Spell(haunt)

	local function InFlightToTarget(condition, state)
		local spellId, yesno = condition[1], condition[2]
		local boolean = (state.currentSpellId == spellId) or OvaleFuture:InFlight(spellId)
		return TestBoolean(boolean, yesno)
	end

	OvaleCondition:RegisterCondition("inflighttotarget", false, InFlightToTarget)
end

do
	--- Test if the distance from the player to the target is within the spell's range.
	-- @name InRange
	-- @paramsig boolean
	-- @param id The spell ID.
	-- @param yesno Optional. If yes, then return true if the target is in range. If no, then return true if it isn't in range.
	--     Default is yes.
	--     Valid values: yes, no.
	-- @return A boolean value.
	-- @usage
	-- if target.IsInterruptible() and target.InRange(kick)
	--     Spell(kick)

	local function InRange(condition, state)
		local spellId, yesno = condition[1], condition[2]
		local target = ParseCondition(condition, state)
		local boolean = (OvaleSpellBook:IsSpellInRange(spellId, target) == 1)
		return TestBoolean(boolean, yesno)
	end

	OvaleCondition:RegisterCondition("inrange", false, InRange)
end

do
	--- Test if the target's primary aggro is on the player.
	-- Even if the target briefly targets and casts a spell on another raid member,
	-- this condition returns true as long as the player is highest on the threat table.
	-- @name IsAggroed
	-- @paramsig boolean
	-- @param yesno Optional. If yes, then return true if the target is aggroed. If no, then return true if it isn't aggroed.
	--     Default is yes.
	--     Valid values: yes, no.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return A boolean value.
	-- @usage
	-- if target.IsAggroed() Spell(feign_death)

	local function IsAggroed(condition, state)
		local yesno = condition[1]
		local target = ParseCondition(condition, state)
		local boolean = API_UnitDetailedThreatSituation("player", target)
		return TestBoolean(boolean, yesno)
	end

	OvaleCondition:RegisterCondition("isaggroed", false, IsAggroed)
end

do
	--- Test if the target is dead.
	-- @name IsDead
	-- @paramsig boolean
	-- @param yesno Optional. If yes, then return true if the target is dead. If no, then return true if it isn't dead.
	--     Default is yes.
	--     Valid values: yes, no.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return A boolean value.
	-- @usage
	-- if pet.IsDead() Spell(revive_pet)

	local function IsDead(condition, state)
		local yesno = condition[1]
		local target = ParseCondition(condition, state)
		local boolean = API_UnitIsDead(target)
		return TestBoolean(boolean, yesno)
	end

	OvaleCondition:RegisterCondition("isdead", false, IsDead)
end

do
	--- Test if the target is enraged.
	-- @name IsEnraged
	-- @paramsig boolean
	-- @param yesno Optional. If yes, then return true if enraged. If no, then return true if not enraged.
	--     Default is yes.
	--     Valid values: yes.  "no" currently doesn't work.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return A boolean value.
	-- @usage
	-- if target.IsEnraged() Spell(soothe)

	local function IsEnraged(condition, state)
		local yesno = condition[1]
		local target = ParseCondition(condition, state)
		return state:GetAuraWithProperty(target, "enraged", "HELPFUL")
	end

	OvaleCondition:RegisterCondition("isenraged", false, IsEnraged)
end

do
	--- Test if the player is feared.
	-- @name IsFeared
	-- @paramsig boolean
	-- @param yesno Optional. If yes, then return true if feared. If no, then return true if it not feared.
	--     Default is yes.
	--     Valid values: yes, no.
	-- @return A boolean value.
	-- @usage
	-- if IsFeared() Spell(every_man_for_himself)

	local function IsFeared(condition, state)
		local yesno = condition[1]
		local aura = state:GetAura("player", "fear_debuff", "HARMFUL")
		local boolean = not API_HasFullControl() and state:IsActiveAura(aura)
		return TestBoolean(boolean, yesno)
	end

	OvaleCondition:RegisterCondition("isfeared", false, IsFeared)
end

do
	--- Test if the target is friendly to the player.
	-- @name IsFriend
	-- @paramsig boolean
	-- @param yesno Optional. If yes, then return true if the target is friendly (able to help in combat). If no, then return true if it isn't friendly.
	--     Default is yes.
	--     Valid values: yes, no.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return A boolean value.
	-- @usage
	-- if target.IsFriend() Spell(healing_touch)

	local function IsFriend(condition, state)
		local yesno = condition[1]
		local target = ParseCondition(condition, state)
		local boolean = API_UnitIsFriend("player", target)
		return TestBoolean(boolean, yesno)
	end

	OvaleCondition:RegisterCondition("isfriend", false, IsFriend)
end

do
	--- Test if the player is incapacitated.
	-- @name IsIncapacitated
	-- @paramsig boolean
	-- @param yesno Optional. If yes, then return true if incapacitated. If no, then return true if it not incapacitated.
	--     Default is yes.
	--     Valid values: yes, no.
	-- @return A boolean value.
	-- @usage
	-- if IsIncapacitated() Spell(every_man_for_himself)

	local function IsIncapacitated(condition, state)
		local yesno = condition[1]
		local aura = state:GetAura("player", "incapacitate_debuff", "HARMFUL")
		local boolean = not API_HasFullControl() and state:IsActiveAura(aura)
		return TestBoolean(boolean, yesno)
	end

	OvaleCondition:RegisterCondition("isincapacitated", false, IsIncapacitated)
end

do
	--- Test if the target is currently casting an interruptible spell.
	-- @name IsInterruptible
	-- @paramsig boolean
	-- @param yesno Optional. If yes, then return true if the target is interruptible. If no, then return true if it isn't interruptible.
	--     Default is yes.
	--     Valid values: yes, no.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return A boolean value.
	-- @usage
	-- if target.IsInterruptible() Spell(kick)

	local function IsInterruptible(condition, state)
		local yesno = condition[1]
		local target = ParseCondition(condition, state)
		local name, _, _, _, _, _, _, _, notInterruptible = API_UnitCastingInfo(target)
		if not name then
			name, _, _, _, _, _, _, notInterruptible = API_UnitChannelInfo(target)
		end
		local boolean = notInterruptible ~= nil and not notInterruptible
		return TestBoolean(boolean, yesno)
	end

	OvaleCondition:RegisterCondition("isinterruptible", false, IsInterruptible)
end

do
	--- Test if the target is flagged for PvP activity.
	-- @name IsPVP
	-- @paramsig boolean
	-- @param yesno Optional. If yes, then return true if the target is flagged for PvP activity. If no, then return true if it isn't PvP-flagged.
	--     Default is yes.
	--     Valid values: yes, no.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return A boolean value.
	-- @usage
	-- if not target.IsFriend() and target.IsPVP() Spell(sap)

	local function IsPVP(condition, state)
		local yesno = condition[1]
		local target = ParseCondition(condition, state)
		local boolean = API_UnitIsPVP(target)
		return TestBoolean(boolean, yesno)
	end

	OvaleCondition:RegisterCondition("ispvp", false, IsPVP)
end

do
	--- Test if the player is rooted.
	-- @name IsRooted
	-- @paramsig boolean
	-- @param yesno Optional. If yes, then return true if rooted. If no, then return true if it not rooted.
	--     Default is yes.
	--     Valid values: yes, no.
	-- @return A boolean value.
	-- @usage
	-- if IsRooted() Item(Trinket0Slot usable=1)

	local function IsRooted(condition, state)
		local yesno = condition[1]
		local aura = state:GetAura("player", "root_debuff", "HARMFUL")
		local boolean = state:IsActiveAura(aura)
		return TestBoolean(boolean, yesno)
	end

	OvaleCondition:RegisterCondition("isrooted", false, IsRooted)
end

do
	--- Test if the player is stunned.
	-- @name IsStunned
	-- @paramsig boolean
	-- @param yesno Optional. If yes, then return true if stunned. If no, then return true if it not stunned.
	--     Default is yes.
	--     Valid values: yes, no.
	-- @return A boolean value.
	-- @usage
	-- if IsStunned() Item(Trinket0Slot usable=1)

	local function IsStunned(condition, state)
		local yesno = condition[1]
		local aura = state:GetAura("player", "stun_debuff", "HARMFUL")
		local boolean = not API_HasFullControl() and state:IsActiveAura(aura)
		return TestBoolean(boolean, yesno)
	end

	OvaleCondition:RegisterCondition("isstunned", false, IsStunned)
end

do
	--- Get the current number of charges of the given item in the player's inventory.
	-- @name ItemCharges
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The number of charges.
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- if ItemCount(mana_gem) ==0 or ItemCharges(mana_gem) <3
	--     Spell(conjure_mana_gem)
	-- if ItemCount(mana_gem equal 0) or ItemCharges(mana_gem less 3)
	--     Spell(conjure_mana_gem)

	local function ItemCharges(condition, state)
		local itemId, comparator, limit = condition[1], condition[2], condition[3]
		local value = API_GetItemCount(itemId, false, true)
		return Compare(value, comparator, limit)
	end

	OvaleCondition:RegisterCondition("itemcharges", false, ItemCharges)
end

do
	--- Get the cooldown time in seconds of an item, e.g., trinket.
	-- @name ItemCooldown
	-- @paramsig number or boolean
	-- @param id The item ID or the equipped slot name.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The number of seconds.
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- if not ItemCooldown(ancient_petrified_seed) > 0
	--     Spell(berserk_cat)
	-- if not ItemCooldown(Trinket0Slot) > 0
	--     Spell(berserk_cat)

	local function ItemCooldown(condition, state)
		local itemId, comparator, limit = condition[1], condition[2], condition[3]
		if itemId and type(itemId) ~= "number" then
			itemId = OvaleEquipment:GetEquippedItem(itemId)
		end
		if itemId then
			local start, duration = API_GetItemCooldown(itemId)
			if start > 0 and duration > 0 then
				return TestValue(start, start + duration, duration, start, -1, comparator, limit)
			end
		end
		return Compare(0, comparator, limit)
	end

	OvaleCondition:RegisterCondition("itemcooldown", false, ItemCooldown)
end

do
	--- Get the current number of the given item in the player's inventory.
	-- Items with more than one charge count as one item.
	-- @name ItemCount
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The count of the item.
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- if ItemCount(mana_gem) ==0 Spell(conjure_mana_gem)
	-- if ItemCount(mana_gem equal 0) Spell(conjure_mana_gem)

	local function ItemCount(condition, state)
		local itemId, comparator, limit = condition[1], condition[2], condition[3]
		local value = API_GetItemCount(itemId)
		return Compare(value, comparator, limit)
	end

	OvaleCondition:RegisterCondition("itemcount", false, ItemCount)
end

do
	--- Get the number of combo points consumed by the most recent cast of a spell on the target for a feral druid or a rogue.
	-- @name LastComboPoints
	-- @paramsig number or boolean
	-- @param id The spell ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=target.
	--     Valid values: player, target, focus, pet.
	-- @return The number of combo points.
	-- @return A boolean value for the result of the comparison.
	-- @see ComboPoints
	-- @usage
	-- if ComboPoints() >3 and target.LastComboPoints(rip) <3
	--     Spell(rip)

	local function LastComboPoints(condition, state)
		local spellId, comparator, limit = condition[1], condition[2], condition[3]
		local target = ParseCondition(condition, state, "target")
		local guid = OvaleGUID:GetGUID(target)
		local value = OvaleFuture:GetLastSpellInfo(guid, spellId, "combo") or 0
		return Compare(value, comparator, limit)
	end

	OvaleCondition:RegisterCondition("lastcombopoints", false, LastComboPoints)
	OvaleCondition:RegisterCondition("lastspellcombopoints", false, LastComboPoints)
end

do
	--- Get the damage done by the most recent damage event for the given spell.
	-- If the spell is a periodic aura, then it gives the damage done by the most recent tick.
	-- @name LastDamage
	-- @paramsig number or boolean
	-- @param id The spell ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The damage done.
	-- @return A boolean value for the result of the comparison.
	-- @see Damage, LastEstimatedDamage
	-- @usage
	-- if LastDamage(ignite) >10000 Spell(combustion)
	-- if LastDamage(ignite more 10000) Spell(combustion)

	local function LastDamage(condition, state)
		local spellId, comparator, limit = condition[1], condition[2], condition[3]
		local value = OvaleSpellDamage:Get(spellId)
		if value then
			return Compare(value, comparator, limit)
		end
		return nil
	end

	OvaleCondition:RegisterCondition("lastdamage", false, LastDamage)
	OvaleCondition:RegisterCondition("lastspelldamage", false, LastDamage)
end

do
	--- Get the damage multiplier of the most recent cast of a spell on the target.
	-- This currently does not take into account increased damage due to mastery.
	-- @name LastDamageMultiplier
	-- @paramsig number or boolean
	-- @param id The spell ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=target.
	--     Valid values: player, target, focus, pet.
	-- @return The previous damage multiplier.
	-- @return A boolean value for the result of the comparison.
	-- @see DamageMultiplier
	-- @usage
	-- if {DamageMultiplier(rupture) / target.LastDamageMultiplier(rupture)} >1.1
	--     Spell(rupture)

	local function LastDamageMultiplier(condition, state)
		local spellId, comparator, limit = condition[1], condition[2], condition[3]
		local target = ParseCondition(condition, state, "target")
		local guid = OvaleGUID:GetGUID(target)
		local bdm = OvaleFuture:GetLastSpellInfo(guid, spellId, "baseDamageMultiplier") or 1
		local dm = OvaleFuture:GetLastSpellInfo(guid, spellId, "damageMultiplier") or 1
		local value = bdm * dm
		return Compare(value, comparator, limit)
	end

	OvaleCondition:RegisterCondition("lastdamagemultiplier", false, LastDamageMultiplier)
	OvaleCondition:RegisterCondition("lastspelldamagemultiplier", false, LastDamageMultiplier)
end

do
	--- Get the estimated damage of the most recent cast of the player's spell on the target.
	-- The calculated damage takes into account the values of attack power, spellpower, weapon damage and combo points (if used)
	-- at the time the spell was most recent cast.
	-- The damage is computed from information for the spell set via SpellInfo(...):
	--
	-- damage = base + bonusmainhand * MH + bonusoffhand * OH + bonusap * AP + bonuscp * CP + bonusapcp * AP * CP + bonussp * SP
	-- @name LastEstimatedDamage
	-- @paramsig number or boolean
	-- @param id The spell ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=target.
	--     Valid values: player, target, focus, pet.
	-- @return The estimated damage of the most recent cast of the given spell by the player.
	-- @return A boolean value for the result of the comparison.
	-- @see Damage, LastDamage
	-- @usage
	-- if {Damage(rake) / target.LastEstimateDamage(rake)} >1.1
	--     Spell(rake)

	local function LastEstimatedDamage(condition, state)
		local spellId, comparator, limit = condition[1], condition[2], condition[3]
		local target = ParseCondition(condition, state, "target")
		local value = ComputeParameter(spellId, "lastEstimatedDamage", state)
		if not value then
			local guid = OvaleGUID:GetGUID(target)
			local ap = OvaleFuture:GetLastSpellInfo(guid, spellId, "attackPower") or 0
			local sp = OvaleFuture:GetLastSpellInfo(guid, spellId, "spellBonusDamage") or 0
			local mh = OvaleFuture:GetLastSpellInfo(guid, spellId, "mainHandWeaponDamage") or 0
			local oh = OvaleFuture:GetLastSpellInfo(guid, spellId, "offHandWeaponDamage") or 0
			local combo = OvaleFuture:GetLastSpellInfo(guid, spellId, "combo") or 0
			local bdm = OvaleFuture:GetLastSpellInfo(guid, spellId, "baseDamageMultiplier") or 1
			local dm = OvaleFuture:GetLastSpellInfo(guid, spellId, "damageMultiplier") or 1
			value = OvaleData:GetDamage(spellId, ap, sp, mh, oh, combo) * bdm * dm
		end
		-- Reduce by armor damage reduction for physical attacks.
		local physical = state:GetSpellInfoProperty(spellId, "physical", target)
		if physical == 1 then
			value = value * (1 - BossArmorDamageReduction(target))
		end
		return Compare(value, comparator, limit)
	end

	OvaleCondition:RegisterCondition("lastestimateddamage", false, LastEstimatedDamage)
	OvaleCondition:RegisterCondition("lastspellestimateddamage", false, LastEstimatedDamage)
end

do
	-- Return the value of the stat from the snapshot at the time the spell was cast.
	local function LastSnapshot(statName, defaultValue, condition, state)
		local spellId, comparator, limit = condition[1], condition[2], condition[3]
		local target = ParseCondition(condition, state, "target")
		local guid = OvaleGUID:GetGUID(target)
		local value = OvaleFuture:GetLastSpellInfo(guid, spellId, statName)
		value = value or defaultValue
		return Compare(value, comparator, limit)
	end

	-- Return the value of the given critical strike chance from the aura snapshot at the time the aura was applied.
	local function LastSnapshotCritChance(statName, defaultValue, condition, state)
		local spellId, comparator, limit = condition[1], condition[2], condition[3]
		local target = ParseCondition(condition, state)
		local guid = OvaleGUID:GetGUID(target)
		local value = OvaleFuture:GetLastSpellInfo(guid, spellId, statName)
		value = value or defaultValue
		if condition.unlimited ~= 1 and value > 100 then
			value = 100
		end
		return Compare(value, comparator, limit)
	end

	--- Get the attack power of the player during the most recent cast of a spell on the target.
	-- @name LastAttackPower
	-- @paramsig number or boolean
	-- @param id The spell ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=target.
	--     Valid values: player, target, focus, pet.
	-- @return The previous attack power.
	-- @return A boolean value for the result of the comparison.
	-- @see AttackPower
	-- @usage
	-- if {AttackPower() / target.LastAttackPower(hemorrhage)} >1.25
	--     Spell(hemorrhage)

	local function LastAttackPower(condition, state)
		return LastSnapshot("attackPower", 0, condition, state)
	end

	OvaleCondition:RegisterCondition("lastattackpower", false, LastAttackPower)
	OvaleCondition:RegisterCondition("lastspellattackpower", false, LastAttackPower)

	--- Get the mastery effect of the player during the most recent cast of a spell on the target.
	-- Mastery effect is the effect of the player's mastery, typically a percent-increase to damage
	-- or a percent-increase to chance to trigger some effect.
	-- @name LastMastery
	-- @paramsig number or boolean
	-- @param id The spell ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=target.
	--     Valid values: player, target, focus, pet.
	-- @return The previous mastery effect.
	-- @return A boolean value for the result of the comparison.
	-- @see Mastery
	-- @usage
	-- if {Mastery(shadow_bolt) - LastMastery(shadow_bolt)} > 1000
	--     Spell(metamorphosis)

	local function LastMasteryEffect(condition, state)
		return LastSnapshot("masteryEffect", 0, condition, state)
	end

	OvaleCondition:RegisterCondition("lastmastery", false, LastMasteryEffect)
	OvaleCondition:RegisterCondition("lastmasteryeffect", false, LastMasteryEffect)
	OvaleCondition:RegisterCondition("lastspellmastery", false, LastMasteryEffect)

	--- Get the melee critical strike chance of the player during the most recent cast of a spell on the target.
	-- @name LastMeleeCritChance
	-- @paramsig number or boolean
	-- @param id The spell ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param unlimited Optional. Set unlimited=1 to allow critical strike chance to exceed 100%.
	--     Defaults to unlimited=0.
	--     Valid values: 0, 1
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=target.
	--     Valid values: player, target, focus, pet.
	-- @return The previous critical strike chance.
	-- @return A boolean value for the result of the comparison.
	-- @see MeleeCritChance
	-- @usage
	-- if MeleeCritChance() > target.LastMeleeCritChance(rip)
	--     Spell(rip)

	local function LastMeleeCritChance(condition, state)
		return LastSnapshotCritChance("meleeCrit", 0, condition, state)
	end

	OvaleCondition:RegisterCondition("lastmeleecritchance", false, LastMeleeCritChance)
	OvaleCondition:RegisterCondition("lastspellmeleecritchance", false, LastMeleeCritChance)

	--- Get the multistrike chance of the player during the most recent cast of a spell on the target.
	-- @name LastMultistrikeEffect
	-- @paramsig number or boolean
	-- @param id The spell ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=target.
	--     Valid values: player, target, focus, pet.
	-- @return The previous multistrike chance (in percent).
	-- @return A boolean value for the result of the comparison.
	-- @see MultistrikeEffect
	-- @usage
	-- if MultistrikeChance(shadow_bolt) > LastMultistrikeChance(shadow_bolt)
	--     Spell(metamorphosis)

	local function LastMultistrikeChance(condition, state)
		return LastSnapshot("multistrike", 0, condition, state)
	end

	OvaleCondition:RegisterCondition("lastmultistrikechance", false, LastMultistrikeChance)

	--- Get the ranged critical strike chance of the player during the most recent cast of a spell on the target.
	-- @name LastRangedCritChance
	-- @paramsig number or boolean
	-- @param id The spell ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param unlimited Optional. Set unlimited=1 to allow critical strike chance to exceed 100%.
	--     Defaults to unlimited=0.
	--     Valid values: 0, 1
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=target.
	--     Valid values: player, target, focus, pet.
	-- @return The previous critical strike chance.
	-- @return A boolean value for the result of the comparison.
	-- @see RangedCritChance
	-- @usage
	-- if RangedCritChance() > target.LastRangedCritChance(serpent_sting_dot)
	--     Spell(serpent_sting)

	local function LastRangedCritChance(condition, state)
		return LastSnapshotCritChance("rangedCrit", 0, condition, state)
	end

	OvaleCondition:RegisterCondition("lastrangedcritchance", false, LastRangedCritChance)
	OvaleCondition:RegisterCondition("lastspellrangedcritchance", false, LastRangedCritChance)

	--- Get the spell critical strike chance of the player during the most recent cast of a spell on the target.
	-- @name LastSpellCritChance
	-- @paramsig number or boolean
	-- @param id The spell ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param unlimited Optional. Set unlimited=1 to allow critical strike chance to exceed 100%.
	--     Defaults to unlimited=0.
	--     Valid values: 0, 1
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=target.
	--     Valid values: player, target, focus, pet.
	-- @return The previous critical strike chance.
	-- @return A boolean value for the result of the comparison.
	-- @see SpellCritChance
	-- @usage
	-- if SpellCritChance() > target.LastSpellCritChance(shadow_bolt)
	--     Spell(metamorphosis)

	local function LastSpellCritChance(condition, state)
		return LastSnapshotCritChance("spellCrit", 0, condition, state)
	end

	OvaleCondition:RegisterCondition("lastspellcritchance", false, LastSpellCritChance)
	OvaleCondition:RegisterCondition("lastspellspellcritchance", false, LastSpellCritChance)

	--- Get the spellpower of the player during the most recent cast of a spell on the target.
	-- @name LastSpellpower
	-- @paramsig number or boolean
	-- @param id The spell ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=target.
	--     Valid values: player, target, focus, pet.
	-- @return The previous spellpower.
	-- @return A boolean value for the result of the comparison.
	-- @see Spellpower
	-- @usage
	-- if {Spellpower() / target.LastSpellpower(living_bomb)} >1.25
	--     Spell(living_bomb)

	local function LastSpellpower(condition, state)
		return LastSnapshot("spellBonusDamage", 0, condition, state)
	end

	OvaleCondition:RegisterCondition("lastspellpower", false, LastSpellpower)
	OvaleCondition:RegisterCondition("lastspellspellpower", false, LastSpellpower)
end

do
	--- Get the most recent estimate of roundtrip latency in milliseconds.
	-- @name Latency
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number of milliseconds to compare against.
	-- @return The most recent estimate of latency.
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- if Latency() >1000 Spell(sinister_strike)
	-- if Latency(more 1000) Spell(sinister_strike)

	local function Latency(condition, state)
		local comparator, limit = condition[1], condition[2]
		local value = OvaleLatency:GetLatency() * 1000
		return Compare(value, comparator, limit)
	end

	OvaleCondition:RegisterCondition("latency", false, Latency)
end

do
	--- Get the level of the target.
	-- @name Level
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The level of the target.
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- if Level() >=34 Spell(tiger_palm)
	-- if Level(more 33) Spell(tiger_palm)

	local function Level(condition, state)
		local comparator, limit = condition[1], condition[2]
		local target = ParseCondition(condition, state)
		local value
		if target == "player" then
			value = state.level
		else
			value = API_UnitLevel(target)
		end
		return Compare(value, comparator, limit)
	end

	OvaleCondition:RegisterCondition("level", false, Level)
end

do
	--- Test if a list is currently set to the given value.
	-- @name List
	-- @paramsig boolean
	-- @param id The name of a list. It should match one defined by AddListItem(...).
	-- @param value The value to test.
	-- @return A boolean value.
	-- @usage
	-- AddListItem(opt_curse coe "Curse of the Elements" default)
	-- AddListItem(opt_curse cot "Curse of Tongues")
	-- if List(opt_curse coe) Spell(curse_of_the_elements)

	local function List(condition, state)
		local name, value = condition[1], condition[2]
		if name and Ovale:GetListValue(name) == value then
			return 0, INFINITY
		end
		return nil
	end

	OvaleCondition:RegisterCondition("list", false, List)
end

do
	--- Test whether the target's name matches the given name.
	-- @name Name
	-- @paramsig boolean
	-- @param name The localized target name.
	-- @param yesno Optional. If yes, then return true if it matches. If no, then return true if it doesn't match.
	--     Default is yes.
	--     Valid values: yes, no.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return A boolean value.

	local function Name(condition, state)
		local name, yesno = condition[1], condition[2]
		local target = ParseCondition(condition, state)
		-- If the given name is a number, then look up the name of the corresponding spell.
		if type(name) == "number" then
			name = OvaleSpellBook:GetSpellName(name)
		end
		local targetName = API_UnitName(target)
		local boolean = (name == targetName)
		return TestBoolean(boolean, yesno)
	end

	OvaleCondition:RegisterCondition("name", false, Name)
end

do
	--- Test if the game is on a PTR server
	-- @paramsig boolean
	-- @param yesno Optional. If yes, then returns true if it is a PTR realm. If no, return true if it is a live realm.
	--     Default is yes.
	--     Valid values: yes, no.
	-- @return A boolean value

	local function PTR(condition, state)
		local yesno = condition[1]
		local _, _, _, uiVersion = API_GetBuildInfo()
		local boolean = (uiVersion > 50400)
		return TestBoolean(boolean, yesno)
	end

	OvaleCondition:RegisterCondition("ptr", false, PTR)
end

do
	--- Get the persistent multiplier to the given aura if applied.
	-- The persistent multiplier is snapshotted to the aura for its duration.
	-- @name PersistentMultiplier
	-- @paramsig number or boolean
	-- @param id The aura ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The persistent multiplier.
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- if PersistentMultiplier(rake_debuff) > target.DebuffPersistentMultiplier(rake_debuff)
	--     Spell(rake)

	local function PersistentMultiplier(condition, state)
		local spellId, comparator, limit = condition[1], condition[2], condition[3]
		local value = state:GetDamageMultiplier(spellId)
		return Compare(value, comparator, limit)
	end

	OvaleCondition:RegisterCondition("persistentmultiplier", false, PersistentMultiplier)
end

do
	--- Test if the pet exists and is alive.
	-- PetPresent() is equivalent to pet.Present().
	-- @name PetPresent
	-- @paramsig boolean
	-- @param yesno Optional. If yes, then return true if the target exists. If no, then return true if it doesn't exist.
	--     Default is yes.
	--     Valid values: yes, no.
	-- @return A boolean value.
	-- @see Present
	-- @usage
	-- if target.IsInterruptible() and PetPresent(yes)
	--     Spell(pet_pummel)

	local function PetPresent(condition, state)
		local yesno = condition[1]
		local target = "pet"
		local boolean = API_UnitExists(target) and not API_UnitIsDead(target)
		return TestBoolean(boolean, yesno)
	end

	OvaleCondition:RegisterCondition("petpresent", false, PetPresent)
end

do
	-- Return the maximum power of the given power type on the target.
	local function MaxPower(powerType, condition, state)
		local comparator, limit = condition[1], condition[2]
		local target = ParseCondition(condition, state)
		local value
		if target == "player" then
			value = OvalePower.maxPower[powerType]
		else
			local powerInfo = OvalePower.POWER_INFO[powerType]
			value = API_UnitPowerMax(target, powerInfo.id, powerInfo.segments)
		end
		return Compare(value, comparator, limit)
	end

	-- Return the amount of power of the given power type on the target.
	local function Power(powerType, condition, state)
		local comparator, limit = condition[1], condition[2]
		local target = ParseCondition(condition, state)
		if target == "player" then
			local value, origin, rate = state[powerType], state.currentTime, state.powerRate[powerType]
			local start, ending = state.currentTime, INFINITY
			return TestValue(start, ending, value, origin, rate, comparator, limit)
		else
			local powerInfo = OvalePower.POWER_INFO[powerType]
			local value = API_UnitPower(target, powerInfo.id)
			return Compare(value, comparator, limit)
		end
	end

	--- Return the current deficit of power from max power on the target.
	local function PowerDeficit(powerType, condition, state)
		local comparator, limit = condition[1], condition[2]
		local target = ParseCondition(condition, state)
		if target == "player" then
			local powerMax = OvalePower.maxPower[powerType] or 0
			if powerMax > 0 then
				local value, origin, rate = powerMax - state[powerType], state.currentTime, -1 * state.powerRate[powerType]
				local start, ending = state.currentTime, INFINITY
				return TestValue(start, ending, value, origin, rate, comparator, limit)
			end
		else
			local powerInfo = OvalePower.POWER_INFO[powerType]
			local powerMax = API_UnitPowerMax(target, powerInfo.id, powerInfo.segments) or 0
			if powerMax > 0 then
				local power = API_UnitPower(target, powerInfo.id)
				local value = powerMax - power
				return Compare(value, comparator, limit)
			end
		end
		return Compare(0, comparator, limit)
	end

	--- Return the current percent level of power (between 0 and 100) on the target.
	local function PowerPercent(powerType, condition, state)
		local comparator, limit = condition[1], condition[2]
		local target = ParseCondition(condition, state)
		if target == "player" then
			local powerMax = OvalePower.maxPower[powerType] or 0
			if powerMax > 0 then
				local conversion = 100 / powerMax
				local value, origin, rate = state[powerType] * conversion, state.currentTime, state.powerRate[powerType] * conversion
				local start, ending = state.currentTime, INFINITY
				return TestValue(start, ending, value, origin, rate, comparator, limit)
			end
		else
			local powerInfo = OvalePower.POWER_INFO[powerType]
			local powerMax = API_UnitPowerMax(target, powerInfo.id, powerInfo.segments) or 0
			if powerMax > 0 then
				local conversion = 100 / powerMax
				local value = API_UnitPower(target, powerInfo.id) * conversion
				return Compare(value, comparator, limit)
			end
		end
		return Compare(0, comparator, limit)
	end

	--- Get the current amount of the player's primary resource for the given spell.
	-- @name PrimaryResource
	-- @paramsig number or boolean
	-- @param id The spell ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The amount of the primary resource.
	-- @return A boolean value for the result of the comparison.

	local function PrimaryResource(condition, state)
		local spellId, comparator, limit = condition[1], condition[2], condition[3]
		local primaryPowerType
		local si = OvaleData:GetSpellInfo(spellId)
		if si then
			-- Check the spell information to see if a primary resource cost was given.
			for powerType in pairs(OvalePower.PRIMARY_POWER) do
				if si[powerType] then
					primaryPowerType = powerType
					break
				end
			end
		end
		-- If no primary resource cost was found, then query using Blizzard API.
		if not primaryPowerType then
			local _, powerType = OvalePower:GetSpellCost(spellId)
			if powerType then
				primaryPowerType = powerType
			end
		end
		if primaryPowerType then
			local value, origin, rate = state[primaryPowerType], state.currentTime, state.powerRate[primaryPowerType]
			local start, ending = state.currentTime, INFINITY
			return TestValue(start, ending, value, origin, rate, comparator, limit)
		end
		return Compare(0, comparator, limit)
	end

	OvaleCondition:RegisterCondition("primaryresource", true, PrimaryResource)

	--- Get the current amount of alternate power displayed on the alternate power bar.
	-- @name AlternatePower
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The current alternate power.
	-- @return A boolean value for the result of the comparison.

	local function AlternatePower(condition, state)
		return Power("alternate", condition, state)
	end

	--- Get the current number of Burning Embers for destruction warlocks.
	-- @name BurningEmbers
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The number of Burning Embers.
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- if BurningEmbers() >10 Spell(chaos_bolt)
	-- if BurningEmbers(more 10) Spell(chaos_bolt)

	local function BurningEmbers(condition, state)
		return Power("burningembers", condition, state)
	end

	--- Get the current amount of stored Chi for monks.
	-- @name Chi
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The amount of stored Chi.
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- if Chi() ==4 Spell(chi_burst)
	-- if Chi(more 3) Spell(chi_burst)

	local function Chi(condition, state)
		return Power("chi", condition, state)
	end

	--- Get the current amount of demonic fury for demonology warlocks.
	-- @name DemonicFury
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The amount of demonic fury.
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- if DemonicFury() >=1000 Spell(metamorphosis)
	-- if DemonicFury(more 999) Spell(metamorphosis)

	local function DemonicFury(condition, state)
		return Power("demonicfury", condition, state)
	end

	--- Get the current amount of energy for feral druids, non-mistweaver monks, and rogues.
	-- @name Energy
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The current energy.
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- if Energy() >70 Spell(vanish)
	-- if Energy(more 70) Spell(vanish)

	local function Energy(condition, state)
		return Power("energy", condition, state)
	end

	--- Get the current amount of focus for hunters.
	-- @name Focus
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The current focus.
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- if Focus() >70 Spell(arcane_shot)
	-- if Focus(more 70) Spell(arcane_shot)

	local function Focus(condition, state)
		return Power("focus", condition, state)
	end

	--- Get the current amount of holy power for a paladin.
	-- @name HolyPower
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The amount of holy power.
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- if HolyPower() >=3 Spell(word_of_glory)
	-- if HolyPower(more 2) Spell(word_of_glory)

	local function HolyPower(condition, state)
		return Power("holy", condition, state)
	end

	--- Get the current level of mana of the target.
	-- @name Mana
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The current mana.
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- if {MaxMana() - Mana()} > 12500 Item(mana_gem)

	local function Mana(condition, state)
		return Power("mana", condition, state)
	end

	--- Get the current amount of rage for guardian druids and warriors.
	-- @name Rage
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The current rage.
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- if Rage() >70 Spell(heroic_strike)
	-- if Rage(more 70) Spell(heroic_strike)

	local function Rage(condition, state)
		return Power("rage", condition, state)
	end

	--- Get the current amount of runic power for death knights.
	-- @name RunicPower
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The current runic power.
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- if RunicPower() >70 Spell(frost_strike)
	-- if RunicPower(more 70) Spell(frost_strike)

	local function RunicPower(condition, state)
		return Power("runicpower", condition, state)
	end

	--- Get the current number of Shadow Orbs for shadow priests.
	-- @name ShadowOrbs
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The number of Shadow Orbs.
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- if ShadowOrbs() >2 Spell(mind_blast)
	-- if ShadowOrbs(more 2) Spell(mind_blast)

	local function ShadowOrbs(condition, state)
		return Power("shadoworbs", condition, state)
	end

	--- Get the current number of Soul Shards for warlocks.
	-- @name SoulShards
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The number of Soul Shards.
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- if SoulShards() >0 Spell(summon_felhunter)
	-- if SoulShards(more 0) Spell(summon_felhunter)

	local function SoulShards(condition, state)
		return Power("shards", condition, state)
	end

	OvaleCondition:RegisterCondition("alternatepower", false, AlternatePower)
	OvaleCondition:RegisterCondition("burningembers", false, BurningEmbers)
	OvaleCondition:RegisterCondition("chi", false, Chi)
	OvaleCondition:RegisterCondition("demonicfury", false, DemonicFury)
	OvaleCondition:RegisterCondition("energy", false, Energy)
	OvaleCondition:RegisterCondition("focus", false, Focus)
	OvaleCondition:RegisterCondition("holypower", false, HolyPower)
	OvaleCondition:RegisterCondition("mana", false, Mana)
	OvaleCondition:RegisterCondition("rage", false, Rage)
	OvaleCondition:RegisterCondition("runicpower", false, RunicPower)
	OvaleCondition:RegisterCondition("shadoworbs", false, ShadowOrbs)
	OvaleCondition:RegisterCondition("soulshards", false, SoulShards)

	--- Get the number of lacking resource points for a full alternate power bar, between 0 and maximum alternate power, of the target.
	-- @name AlternatePowerDeficit
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The current alternate power deficit.
	-- @return A boolean value for the result of the comparison.

	local function AlternatePowerDeficit(condition, state)
		return PowerDeficit("alternatepower", condition, state)
	end

	--- Get the number of lacking resource points for a full burning embers bar, between 0 and maximum burning embers, of the target.
	-- @name BurningEmbersDeficit
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The current burning embers deficit.
	-- @return A boolean value for the result of the comparison.

	local function BurningEmbersDeficit(condition, state)
		return PowerDeficit("burningembers", condition, state)
	end

	--- Get the number of lacking resource points for full chi, between 0 and maximum chi, of the target.
	-- @name ChiDeficit
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The current chi deficit.
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- if ChiDeficit() >=2 Spell(keg_smash)
	-- if ChiDeficit(more 1) Spell(keg_smash)

	local function ChiDeficit(condition, state)
		return PowerDeficit("chi", condition, state)
	end

	--- Get the number of lacking resource points for a full demonic fury bar, between 0 and maximum demonic fury, of the target.
	-- @name DemonicFuryDeficit
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The current demonic fury deficit.
	-- @return A boolean value for the result of the comparison.

	local function DemonicFuryDeficit(condition, state)
		return PowerDeficit("demonicfury", condition, state)
	end

	--- Get the number of lacking resource points for a full energy bar, between 0 and maximum energy, of the target.
	-- @name EnergyDeficit
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The current energy deficit.
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- if EnergyDeficit() >60 Spell(tigers_fury)
	-- if EnergyDeficit(more 60) Spell(tigers_fury)

	local function EnergyDeficit(condition, state)
		return PowerDeficit("energy", condition, state)
	end

	--- Get the number of lacking resource points for a full focus bar, between 0 and maximum focus, of the target.
	-- @name FocusDeficit
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The current focus deficit.
	-- @return A boolean value for the result of the comparison.

	local function FocusDeficit(condition, state)
		return PowerDeficit("focus", condition, state)
	end

	--- Get the number of lacking resource points for full holy power, between 0 and maximum holy power, of the target.
	-- @name HolyPowerDeficit
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The current holy power deficit.
	-- @return A boolean value for the result of the comparison.

	local function HolyPowerDeficit(condition, state)
		return PowerDeficit("holypower", condition, state)
	end

	--- Get the number of lacking resource points for a full mana bar, between 0 and maximum mana, of the target.
	-- @name ManaDeficit
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The current mana deficit.
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- if ManaDeficit() >30000 Item(mana_gem)
	-- if ManaDeficit(more 30000) Item(mana_gem)

	local function ManaDeficit(condition, state)
		return PowerDeficit("mana", condition, state)
	end

	--- Get the number of lacking resource points for a full rage bar, between 0 and maximum rage, of the target.
	-- @name RageDeficit
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The current rage deficit.
	-- @return A boolean value for the result of the comparison.

	local function RageDeficit(condition, state)
		return PowerDeficit("rage", condition, state)
	end

	--- Get the number of lacking resource points for a full runic power bar, between 0 and maximum runic power, of the target.
	-- @name RunicPowerDeficit
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The current runic power deficit.
	-- @return A boolean value for the result of the comparison.

	local function RunicPowerDeficit(condition, state)
		return PowerDeficit("runicpower", condition, state)
	end

	--- Get the number of lacking resource points for full shadow orbs, between 0 and maximum shadow orbs, of the target.
	-- @name ShadowOrbsDeficit
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The current shadow orbs deficit.
	-- @return A boolean value for the result of the comparison.

	local function ShadowOrbsDeficit(condition, state)
		return PowerDeficit("shadoworbs", condition, state)
	end

	--- Get the number of lacking resource points for full soul shards, between 0 and maximum soul shards, of the target.
	-- @name SoulShardsDeficit
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The current soul shards deficit.
	-- @return A boolean value for the result of the comparison.

	local function SoulShardsDeficit(condition, state)
		return PowerDeficit("shards", condition, state)
	end

	OvaleCondition:RegisterCondition("alternatepowerdeficit", false, AlternatePowerDeficit)
	OvaleCondition:RegisterCondition("burningembersdeficit", false, BurningEmbersDeficit)
	OvaleCondition:RegisterCondition("chideficit", false, ChiDeficit)
	OvaleCondition:RegisterCondition("demonicfurydeficit", false, DemonicFuryDeficit)
	OvaleCondition:RegisterCondition("energydeficit", false, EnergyDeficit)
	OvaleCondition:RegisterCondition("focusdeficit", false, FocusDeficit)
	OvaleCondition:RegisterCondition("holypowerdeficit", false, HolyPowerDeficit)
	OvaleCondition:RegisterCondition("manadeficit", false, ManaDeficit)
	OvaleCondition:RegisterCondition("ragedeficit", false, RageDeficit)
	OvaleCondition:RegisterCondition("runicpowerdeficit", false, RunicPowerDeficit)
	OvaleCondition:RegisterCondition("shadoworbsdeficit", false, ShadowOrbsDeficit)
	OvaleCondition:RegisterCondition("soulshardsdeficit", false, SoulShardsDeficit)

	--- Get the current percent level of mana (between 0 and 100) of the target.
	-- @name ManaPercent
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The current mana percent.
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- if ManaPercent() >90 Spell(arcane_blast)
	-- if ManaPercent(more 90) Spell(arcane_blast)

	local function ManaPercent(condition, state)
		return PowerPercent("mana", condition, state)
	end

	OvaleCondition:RegisterCondition("manapercent", false, ManaPercent)

	--- Get the maximum amount of alternate power of the target.
	-- Alternate power is the resource tracked by the alternate power bar in certain boss fights.
	-- @name MaxAlternatePower
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The maximum value.
	-- @return A boolean value for the result of the comparison.

	local function MaxAlternatePower(condition, state)
		return MaxPower("alternate", condition, state)
	end

	--- Get the maximum amount of burning embers of the target.
	-- @name MaxBurningEmbers
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The maximum value.
	-- @return A boolean value for the result of the comparison.

	local function MaxBurningEmbers(condition, state)
		return MaxPower("burningembers", condition, state)
	end

	--- Get the maximum amount of Chi of the target.
	-- @name MaxChi
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The maximum value.
	-- @return A boolean value for the result of the comparison.

	local function MaxChi(condition, state)
		return MaxPower("chi", condition, state)
	end

	--- Get the maximum amount of Demonic Fury of the target.
	-- @name MaxDemonicFury
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The maximum value.
	-- @return A boolean value for the result of the comparison.

	local function MaxDemonicFury(condition, state)
		return MaxPower("demonicfury", condition, state)
	end

	--- Get the maximum amount of energy of the target.
	-- @name MaxEnergy
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The maximum value.
	-- @return A boolean value for the result of the comparison.

	local function MaxEnergy(condition, state)
		return MaxPower("energy", condition, state)
	end

	--- Get the maximum amount of focus of the target.
	-- @name MaxFocus
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The maximum value.
	-- @return A boolean value for the result of the comparison.

	local function MaxFocus(condition, state)
		return MaxPower("focus", condition, state)
	end

	--- Get the maximum amount of Holy Power of the target.
	-- @name MaxHolyPower
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The maximum value.
	-- @return A boolean value for the result of the comparison.

	local function MaxHolyPower(condition, state)
		return MaxPower("holy", condition, state)
	end

	--- Get the maximum amount of mana of the target.
	-- @name MaxMana
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The maximum value.
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- if {MaxMana() - Mana()} > 12500 Item(mana_gem)

	local function MaxMana(condition, state)
		return MaxPower("mana", condition, state)
	end

	--- Get the maximum amount of rage of the target.
	-- @name MaxRage
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The maximum value.
	-- @return A boolean value for the result of the comparison.

	local function MaxRage(condition, state)
		return MaxPower("rage", condition, state)
	end

	--- Get the maximum amount of Runic Power of the target.
	-- @name MaxRunicPower
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The maximum value.
	-- @return A boolean value for the result of the comparison.

	local function MaxRunicPower(condition, state)
		return MaxPower("runicpower", condition, state)
	end

	--- Get the maximum amount of Shadow Orbs of the target.
	-- @name MaxShadowOrbs
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The maximum value.
	-- @return A boolean value for the result of the comparison.

	local function MaxShadowOrbs(condition, state)
		return MaxPower("shadoworbs", condition, state)
	end

	--- Get the maximum amount of Soul Shards of the target.
	-- @name MaxSoulShards
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The maximum value.
	-- @return A boolean value for the result of the comparison.

	local function MaxSoulShards(condition, state)
		return MaxPower("shards", condition, state)
	end

	OvaleCondition:RegisterCondition("maxalternatepower", false, MaxAlternatePower)
	OvaleCondition:RegisterCondition("maxburningembers", false, MaxBurningEmbers)
	OvaleCondition:RegisterCondition("maxchi", false, MaxChi)
	OvaleCondition:RegisterCondition("maxdemonicfury", false, MaxDemonicFury)
	OvaleCondition:RegisterCondition("maxenergy", false, MaxEnergy)
	OvaleCondition:RegisterCondition("maxfocus", false, MaxFocus)
	OvaleCondition:RegisterCondition("maxholypower", false, MaxHolyPower)
	OvaleCondition:RegisterCondition("maxmana", false, MaxMana)
	OvaleCondition:RegisterCondition("maxrage", false, MaxRage)
	OvaleCondition:RegisterCondition("maxrunicpower", false, MaxRunicPower)
	OvaleCondition:RegisterCondition("maxshadoworbs", false, MaxShadowOrbs)
	OvaleCondition:RegisterCondition("maxsoulshards", false, MaxSoulShards)
end

do
	-- Return the amount of power of the given power type required to cast the given spell.
	local function PowerCost(powerType, condition, state)
		local spellId, comparator, limit = condition[1], condition[2], condition[3]
		local target = ParseCondition(condition, state, "target")
		local maxCost = (condition.max == 1)
		local value = state:PowerCost(spellId, powerType, target, maxCost) or 0
		return Compare(value, comparator, limit)
	end

	--- Get the amount of energy required to cast the given spell.
	-- This returns zero for spells that use either mana or another resource based on stance/specialization, e.g., Monk's Jab.
	-- @name EnergyCost
	-- @paramsig number or boolean
	-- @param id The spell ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param max Optional. Set max=1 to return the maximum energy cost for the spell.
	--     Defaults to max=0.
	--     Valid values: 0, 1
	-- @param target Optional. Sets the target of the spell. The target may also be given as a prefix to the condition.
	--     Defaults to target=target.
	--     Valid values: player, target, focus, pet.
	-- @return The amount of energy.
	-- @return A boolean value for the result of the comparison.

	local function EnergyCost(condition, state)
		return PowerCost("energy", condition, state)
	end

	--- Get the amount of focus required to cast the given spell.
	-- @name FocusCost
	-- @paramsig number or boolean
	-- @param id The spell ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param max Optional. Set max=1 to return the maximum focus cost for the spell.
	--     Defaults to max=0.
	--     Valid values: 0, 1
	-- @param target Optional. Sets the target of the spell. The target may also be given as a prefix to the condition.
	--     Defaults to target=target.
	--     Valid values: player, target, focus, pet.
	-- @return The amount of focus.
	-- @return A boolean value for the result of the comparison.

	local function FocusCost(condition, state)
		return PowerCost("focus", condition, state)
	end

	--- Get the amount of mana required to cast the given spell.
	-- This returns zero for spells that use either mana or another resource based on stance/specialization, e.g., Monk's Jab.
	-- @name ManaCost
	-- @paramsig number or boolean
	-- @param id The spell ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param max Optional. Set max=1 to return the maximum mana cost for the spell.
	--     Defaults to max=0.
	--     Valid values: 0, 1
	-- @param target Optional. Sets the target of the spell. The target may also be given as a prefix to the condition.
	--     Defaults to target=target.
	--     Valid values: player, target, focus, pet.
	-- @return The amount of mana.
	-- @return A boolean value for the result of the comparison.

	local function ManaCost(condition, state)
		return PowerCost("mana", condition, state)
	end

	--- Get the amount of rage required to cast the given spell.
	-- @name RageCost
	-- @paramsig number or boolean
	-- @param id The spell ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param max Optional. Set max=1 to return the maximum rage cost for the spell.
	--     Defaults to max=0.
	--     Valid values: 0, 1
	-- @param target Optional. Sets the target of the spell. The target may also be given as a prefix to the condition.
	--     Defaults to target=target.
	--     Valid values: player, target, focus, pet.
	-- @return The amount of rage.
	-- @return A boolean value for the result of the comparison.

	local function RageCost(condition, state)
		return PowerCost("rage", condition, state)
	end

	--- Get the amount of runic power required to cast the given spell.
	-- @name RunicPowerCost
	-- @paramsig number or boolean
	-- @param id The spell ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param max Optional. Set max=1 to return the maximum runic power cost for the spell.
	--     Defaults to max=0.
	--     Valid values: 0, 1
	-- @param target Optional. Sets the target of the spell. The target may also be given as a prefix to the condition.
	--     Defaults to target=target.
	--     Valid values: player, target, focus, pet.
	-- @return The amount of runic power.
	-- @return A boolean value for the result of the comparison.

	local function RunicPowerCost(condition, state)
		return PowerCost("runicpower", condition, state)
	end

	OvaleCondition:RegisterCondition("energycost", true, EnergyCost)
	OvaleCondition:RegisterCondition("focuscost", true, FocusCost)
	OvaleCondition:RegisterCondition("manacost", true, ManaCost)
	OvaleCondition:RegisterCondition("ragecost", true, RageCost)
	OvaleCondition:RegisterCondition("runicpowercost", true, RunicPowerCost)
end

do
	--- Test if the target exists and is alive.
	-- @name Present
	-- @paramsig boolean
	-- @param yesno Optional. If yes, then return true if the target exists. If no, then return true if it doesn't exist.
	--     Default is yes.
	--     Valid values: yes, no.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return A boolean value.
	-- @see Exists
	-- @usage
	-- if target.IsInterruptible() and pet.Present(yes)
	--     Spell(pet_pummel)

	local function Present(condition, state)
		local yesno = condition[1]
		local target = ParseCondition(condition, state)
		local boolean = API_UnitExists(target) and not API_UnitIsDead(target)
		return TestBoolean(boolean, yesno)
	end

	OvaleCondition:RegisterCondition("present", false, Present)
end

do
	--- Test if the previous spell cast that invoked the GCD matches the given spell.
	-- @name PreviousGCDSpell
	-- @paramsig boolean
	-- @param id The spell ID.
	-- @param yesno Optional. If yes, then return true if there is a match. If no, then return true if it doesn't match.
	--     Default is yes.
	--     Valid values: yes, no.
	-- @return A boolean value.

	local function PreviousGCDSpell(condition, state)
		local spellId, yesno = condition[1], condition[2]
		local boolean = (spellId == state.lastGCDSpellId)
		return TestBoolean(boolean, yesno)
	end

	OvaleCondition:RegisterCondition("previousgcdspell", true, PreviousGCDSpell)
end

do
	--- Test if the previous spell cast matches the given spell.
	-- @name PreviousSpell
	-- @paramsig boolean
	-- @param id The spell ID.
	-- @param yesno Optional. If yes, then return true if there is a match. If no, then return true if it doesn't match.
	--     Default is yes.
	--     Valid values: yes, no.
	-- @return A boolean value.

	local function PreviousSpell(condition, state)
		local spellId, yesno = condition[1], condition[2]
		local boolean = (spellId == state.lastSpellId)
		return TestBoolean(boolean, yesno)
	end

	OvaleCondition:RegisterCondition("previousspell", true, PreviousSpell)
end

do
	--- Get the result of the target's level minus the player's level. This number may be negative.
	-- @name RelativeLevel
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The difference in levels.
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- if target.RelativeLevel() >3
	--     Texture(ability_rogue_sprint)
	-- if target.RelativeLevel(more 3)
	--     Texture(ability_rogue_sprint)

	local function RelativeLevel(condition, state)
		local comparator, limit = condition[1], condition[2]
		local target = ParseCondition(condition, state)
		local value, level
		if target == "player" then
			level = state.level
		else
			level = API_UnitLevel(target)
		end
		if level < 0 then
			-- World boss, so treat it as three levels higher.
			value = 3
		else
			value = level - state.level
		end
		return Compare(value, comparator, limit)
	end

	OvaleCondition:RegisterCondition("relativelevel", false, RelativeLevel)
end

do
	--- Get the remaining cast time in seconds of the target's current spell cast.
	-- @name RemainingCastTime
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The number of seconds.
	-- @return A boolean value for the result of the comparison.
	-- @see CastTime
	-- @usage
	-- if target.Casting(hour_of_twilight) and target.RemainingCastTime() <2
	--     Spell(cloak_of_shadows)

	local function RemainingCastTime(condition, state)
		local comparator, limit = condition[1], condition[2]
		local target = ParseCondition(condition, state)
		local _, _, _, _, startTime, endTime = API_UnitCastingInfo(target)
		if startTime and endTime then
			startTime = startTime / 1000
			endTime = endTime / 1000
			return TestValue(startTime, endTime, 0, endTime, -1, comparator, limit)
		end
		return nil
	end

	OvaleCondition:RegisterCondition("remainingcasttime", false, RemainingCastTime)
end

do
	--- Get the current number of active and regenerating (fractional) runes of the given type for death knights.
	-- @name Rune
	-- @paramsig number or boolean
	-- @param type The type of rune.
	--     Valid values: blood, frost, unholy, death
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param death Optional. Set death=1 to include all active and regenerating death runes in the count. Set death=0 to exclude all death runes.
	--     Defaults to unset.
	--     Valid values: unset, 0, 1
	-- @return The number of runes.
	-- @return A boolean value for the result of the comparison.
	-- @see DeathRune, RuneCount
	-- @usage
	-- if Rune(blood) > 1 Spell(blood_tap)

	local function Rune(condition, state)
		local name, comparator, limit = condition[1], condition[2], condition[3]
		local includeDeath
		if condition.death == 1 then
			includeDeath = true
		elseif condition.death == 0 then
			includeDeath = false
		--else
		--	includeDeath = nil
		end
		local count, startCooldown, endCooldown = state:RuneCount(name, includeDeath)
		if startCooldown < INFINITY then
			local origin = startCooldown
			local rate = 1 / (endCooldown - startCooldown)
			local start, ending = startCooldown, INFINITY
			return TestValue(start, ending, count, origin, rate, comparator, limit)
		end
		return Compare(count, comparator, limit)
	end

	--- Get the current number of active and regenerating (fractional) death runes of the given type for death knights.
	-- @name DeathRune
	-- @paramsig number or boolean
	-- @param type The type of rune.
	--     Valid values: blood, frost, unholy
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The number of runes.
	-- @return A boolean value for the result of the comparison.
	-- @see Rune
	-- @usage
	-- if DeathRune(blood) > 1 Spell(blood_tap)

	local function DeathRune(condition, state)
		local name, comparator, limit = condition[1], condition[2], condition[3]
		local count, startCooldown, endCooldown = state:DeathRuneCount(name)
		if startCooldown < INFINITY then
			local origin = startCooldown
			local rate = 1 / (endCooldown - startCooldown)
			local start, ending = startCooldown, INFINITY
			return TestValue(start, ending, count, origin, rate, comparator, limit)
		end
		return Compare(count, comparator, limit)
	end

	--- Get the current number of active runes of the given type for death knights.
	-- @name RuneCount
	-- @paramsig number or boolean
	-- @param type The type of rune.
	--     Valid values: blood, frost, unholy, death
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param death Optional. Set death=1 to include all active death runes in the count. Set death=0 to exclude all death runes.
	--     Defaults to unset.
	--     Valid values: unset, 0, 1
	-- @return The number of runes.
	-- @return A boolean value for the result of the comparison.
	-- @see Rune
	-- @usage
	-- if RuneCount(unholy) ==2 or RuneCount(frost) ==2 or RuneCount(death) ==2
	--     Spell(obliterate)

	local function RuneCount(condition, state)
		local name, comparator, limit = condition[1], condition[2], condition[3]
		local includeDeath
		if condition.death == 1 then
			includeDeath = true
		elseif condition.death == 0 then
			includeDeath = false
		--else
		--	includeDeath = nil
		end
		local count, startCooldown, endCooldown = state:RuneCount(name, includeDeath)
		if startCooldown < INFINITY then
			local start, ending = startCooldown, endCooldown
			return TestValue(start, ending, count, start, 0, comparator, limit)
		end
		return Compare(count, comparator, limit)
	end

	OvaleCondition:RegisterCondition("rune", false, Rune)
	OvaleCondition:RegisterCondition("deathrune", false, DeathRune)
	OvaleCondition:RegisterCondition("runecount", false, RuneCount)
end

do
	local RUNE_OF_POWER = 116011
	local RUNE_OF_POWER_BUFF = 116014

	--- Get the remaining time in seconds before the latest Rune of Power expires.
	--- Returns non-zero only if the player is standing within an existing Rune of Power.
	-- @name RuneOfPowerRemaining
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The number of seconds.
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- if RuneOfPowerRemaining() < CastTime(rune_of_power) Spell(rune_of_power)

	local function RuneOfPowerRemaining(condition, state)
		Ovale:OneTimeMessage("Warning: 'RuneOfPowerRemaining()' is deprecated; use 'TotemRemaining(rune_of_power)' instead.")
		local comparator, limit = condition[1], condition[2]
		local count, start, ending = state:GetTotemCount(RUNE_OF_POWER)
		if count > 0 then
			return TestValue(start, ending, 0, ending, -1, comparator, limit)
		end
		return Compare(0, comparator, limit)
	end

	OvaleCondition:RegisterCondition("runeofpowerremaining", false, RuneOfPowerRemaining)
	OvaleCondition:RegisterCondition("runeofpowerremains", false, RuneOfPowerRemaining)
end

do
	local RUNE_TYPE = OvaleRunes.RUNE_TYPE

	local runes = {
		blood = 0,
		unholy = 0,
		frost = 0,
		death = 0,
	}

	local function ParseRuneCondition(condition, state)
		for name in pairs(RUNE_TYPE) do
			runes[name] = 0
		end
		local k = 1
		while true do
			local name, count = condition[2*k - 1], condition[2*k]
			if not RUNE_TYPE[name] then break end
			runes[name] = runes[name] + count
			k = k + 1
		end
		return runes.blood, runes.unholy, runes.frost, runes.death
	end

	--- Test if the current active rune counts meets the minimum rune requirements set out in the parameters.
	-- This condition takes pairs of "type number" to mean that there must be a minimum of number runes of the named type.
	-- E.g., Runes(blood 1 frost 1 unholy 1) means at least one blood, one frost, and one unholy rune is available,
	-- death runes included.
	-- @name Runes
	-- @paramsig boolean
	-- @param type The type of rune.
	--     Valid values: blood, frost, unholy, death
	-- @param number The number of runes
	-- @param ... Optional. Additional "type number" pairs for minimum rune requirements.
	-- @return A boolean value.
	-- @usage
	-- if Runes(frost 1) Spell(howling_blast)

	local function Runes(condition, state)
		local blood, unholy, frost, death = ParseRuneCondition(condition, state)
		local seconds = state:GetRunesCooldown(blood, unholy, frost, death)
		return state.currentTime + seconds, INFINITY
	end

	--- Get the number of seconds before the rune conditions are met.
	-- This condition takes pairs of "type number" to mean that there must be a minimum of number runes of the named type.
	-- E.g., RunesCooldown(blood 1 frost 1 unholy 1) returns the number of seconds before
	-- there are at least one blood, one frost, and one unholy rune, death runes included.
	-- @name RunesCooldown
	-- @paramsig number
	-- @param type The type of rune.
	--     Valid values: blood, frost, unholy, death
	-- @param number The number of runes
	-- @param ... Optional. Additional "type number" pairs for minimum rune requirements.
	-- @return The number of seconds.

	local function RunesCooldown(condition, state)
		local blood, unholy, frost, death = ParseRuneCondition(condition, state)
		local seconds = state:GetRunesCooldown(blood, unholy, frost, death)
		return 0, state.currentTime + seconds, seconds, state.currentTime, -1
	end

	OvaleCondition:RegisterCondition("runes", false, Runes)
	OvaleCondition:RegisterCondition("runescooldown", false, RunesCooldown)
end

do
	-- Returns the value of the given snapshot stat.
	local function Snapshot(statName, defaultValue, condition, state)
		local comparator, limit = condition[1], condition[2]
		local value = state.snapshot[statName] or defaultValue
		return Compare(value, comparator, limit)
	end

	-- Returns the critical strike chance of the given snapshot stat.
	local function SnapshotCritChance(statName, defaultValue, condition, state)
		local comparator, limit = condition[1], condition[2]
		local value = state.snapshot[statName] or defaultValue
		if condition.unlimited ~= 1 and value > 100 then
			value = 100
		end
		return Compare(value, comparator, limit)
	end

	--- Get the current agility of the player.
	-- @name Agility
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The current agility.
	-- @return A boolean value for the result of the comparison.

	local function Agility(condition, state)
		return Snapshot("agility", 0, condition, state)
	end

	--- Get the current attack power of the player.
	-- @name AttackPower
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The current attack power.
	-- @return A boolean value for the result of the comparison.
	-- @see LastAttackPower
	-- @usage
	-- if AttackPower() >10000 Spell(rake)
	-- if AttackPower(more 10000) Spell(rake)

	local function AttackPower(condition, state)
		return Snapshot("attackPower", 0, condition, state)
	end

	--- Get the current critical strike rating of the player.
	-- @name CritRating
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The current critical strike rating.
	-- @return A boolean value for the result of the comparison.

	local function CritRating(condition, state)
		return Snapshot("critRating", 0, condition, state)
	end

	--- Get the current haste rating of the player.
	-- @name HasteRating
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The current haste rating.
	-- @return A boolean value for the result of the comparison.

	local function HasteRating(condition, state)
		return Snapshot("hasteRating", 0, condition, state)
	end

	--- Get the current intellect of the player.
	-- @name Intellect
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The current intellect.
	-- @return A boolean value for the result of the comparison.

	local function Intellect(condition, state)
		return Snapshot("intellect", 0, condition, state)
	end

	--- Get the current mastery effect of the player.
	-- Mastery effect is the effect of the player's mastery, typically a percent-increase to damage
	-- or a percent-increase to chance to trigger some effect.
	-- @name MasteryEffect
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The current mastery effect.
	-- @return A boolean value for the result of the comparison.
	-- @see LastMasteryEffect
	-- @usage
	-- if {DamageMultiplier(rake) * {1 + MasteryEffect()/100}} >1.8
	--     Spell(rake)

	local function MasteryEffect(condition, state)
		return Snapshot("masteryEffect", 0, condition, state)
	end

	--- Get the current mastery rating of the player.
	-- @name MasteryRating
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The current mastery rating.
	-- @return A boolean value for the result of the comparison.

	local function MasteryRating(condition, state)
		return Snapshot("masteryRating", 0, condition, state)
	end

	--- Get the current melee critical strike chance of the player.
	-- @name MeleeCritChance
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param unlimited Optional. Set unlimited=1 to allow critical strike chance to exceed 100%.
	--     Defaults to unlimited=0.
	--     Valid values: 0, 1
	-- @return The current critical strike chance (in percent).
	-- @return A boolean value for the result of the comparison.
	-- @see LastMeleeCritChance
	-- @usage
	-- if MeleeCritChance() >90 Spell(rip)

	local function MeleeCritChance(condition, state)
		return SnapshotCritChance("meleeCrit", 0, condition, state)
	end

	--- Get the current multistrike chance of the player.
	-- @name MultistrikeChance
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The current multistrike chance (in percent).
	-- @return A boolean value for the result of the comparison.
	-- @see LastMultistrikeChance

	local function MultistrikeChance(condition, state)
		return Snapshot("multistrike", 0, condition, state)
	end

	--- Get the current ranged critical strike chance of the player.
	-- @name RangedCritChance
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param unlimited Optional. Set unlimited=1 to allow critical strike chance to exceed 100%.
	--     Defaults to unlimited=0.
	--     Valid values: 0, 1
	-- @return The current critical strike chance (in percent).
	-- @return A boolean value for the result of the comparison.
	-- @see LastRangedCritChance
	-- @usage
	-- if RangedCritChance() >90 Spell(serpent_sting)

	local function RangedCritChance(condition, state)
		return SnapshotCritChance("rangedCrit", 0, condition, state)
	end

	--- Get the current spell critical strike chance of the player.
	-- @name SpellCritChance
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param unlimited Optional. Set unlimited=1 to allow critical strike chance to exceed 100%.
	--     Defaults to unlimited=0.
	--     Valid values: 0, 1
	-- @return The current critical strike chance (in percent).
	-- @return A boolean value for the result of the comparison.
	-- @see CritChance, LastSpellCritChance
	-- @usage
	-- if SpellCritChance() >30 Spell(immolate)

	local function SpellCritChance(condition, state)
		return SnapshotCritChance("spellCrit", 0, condition, state)
	end

	--- Get the current percent increase to spell haste of the player.
	-- @name SpellHaste
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The current percent increase to spell haste.
	-- @return A boolean value for the result of the comparison.
	-- @see BuffSpellHaste
	-- @usage
	-- if SpellHaste() >target.DebuffSpellHaste(moonfire) Spell(moonfire)

	local function SpellHaste(condition, state)
		return Snapshot("spellHaste", 0, condition, state)
	end

	--- Get the current spellpower of the player.
	-- @name Spellpower
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The current spellpower.
	-- @return A boolean value for the result of the comparison.
	-- @see LastSpellpower
	-- @usage
	-- if {Spellpower() / LastSpellpower(living_bomb)} >1.25
	--     Spell(living_bomb)

	local function Spellpower(condition, state)
		return Snapshot("spellBonusDamage", 0, condition, state)
	end

	--- Get the current spirit of the player.
	-- @name Spirit
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The current spirit.
	-- @return A boolean value for the result of the comparison.

	local function Spirit(condition, state)
		return Snapshot("spirit", 0, condition, state)
	end

	--- Get the current stamina of the player.
	-- @name Stamina
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The current stamina.
	-- @return A boolean value for the result of the comparison.

	local function Stamina(condition, state)
		return Snapshot("stamina", 0, condition, state)
	end

	--- Get the current strength of the player.
	-- @name Strength
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The current strength.
	-- @return A boolean value for the result of the comparison.

	local function Strength(condition, state)
		return Snapshot("strength", 0, condition, state)
	end

	OvaleCondition:RegisterCondition("agility", false, Agility)
	OvaleCondition:RegisterCondition("attackpower", false, AttackPower)
	OvaleCondition:RegisterCondition("critrating", false, CritRating)
	OvaleCondition:RegisterCondition("hasterating", false, HasteRating)
	OvaleCondition:RegisterCondition("intellect", false, Intellect)
	OvaleCondition:RegisterCondition("mastery", false, MasteryEffect)
	OvaleCondition:RegisterCondition("masteryeffect", false, MasteryEffect)
	OvaleCondition:RegisterCondition("masteryrating", false, MasteryRating)
	OvaleCondition:RegisterCondition("meleecritchance", false, MeleeCritChance)
	OvaleCondition:RegisterCondition("multistrikechance", false, MultistrikeChance)
	OvaleCondition:RegisterCondition("rangedcritchance", false, RangedCritChance)
	OvaleCondition:RegisterCondition("spellcritchance", false, SpellCritChance)
	OvaleCondition:RegisterCondition("spellhaste", false, SpellHaste)
	OvaleCondition:RegisterCondition("spellpower", false, Spellpower)
	OvaleCondition:RegisterCondition("spirit", false, Spirit)
	OvaleCondition:RegisterCondition("stamina", false, Stamina)
	OvaleCondition:RegisterCondition("strength", false, Strength)
end

do
	--- Get the current speed of the target.
	-- If the target is not moving, then this condition returns 0 (zero).
	-- If the target is at running speed, then this condition returns 100.
	-- @name Speed
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The speed of the target.
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- if Speed(more 0) and not BuffPresent(aspect_of_the_fox)
	--     Spell(aspect_of_the_fox)

	local function Speed(condition, state)
		local comparator, limit = condition[1], condition[2]
		local target = ParseCondition(condition, state)
		local value = API_GetUnitSpeed(target) * 100 / 7
		return Compare(value, comparator, limit)
	end

	OvaleCondition:RegisterCondition("speed", false, Speed)
end

do
	--- Get the cooldown in seconds on a spell before it gains another charge.
	-- @name SpellChargeCooldown
	-- @paramsig number or boolean
	-- @param id The spell ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The number of seconds.
	-- @return A boolean value for the result of the comparison.
	-- @see SpellCharges
	-- @usage
	-- if SpellChargeCooldown(roll) <2
	--     Spell(roll usable=1)

	local function SpellChargeCooldown(condition, state)
		local spellId, comparator, limit = condition[1], condition[2], condition[3]
		local charges, maxCharges, start, duration = state:GetSpellCharges(spellId)
		if charges and charges < maxCharges then
			return TestValue(start, start + duration, duration, start, -1, comparator, limit)
		end
		return Compare(0, comparator, limit)
	end

	OvaleCondition:RegisterCondition("spellchargecooldown", true, SpellChargeCooldown)
end

do
	--- Get the number of charges of the spell.
	-- @name SpellCharges
	-- @paramsig number or boolean
	-- @param id The spell ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param count Optional. Sets whether a count or a fractional value is returned.
	--     Defaults to count=1.
	--     Valid values: 0, 1.
	-- @return The number of charges.
	-- @return A boolean value for the result of the comparison.
	-- @see SpellChargeCooldown
	-- @usage
	-- if SpellCharges(savage_defense) >1
	--     Spell(savage_defense)

	local function SpellCharges(condition, state)
		local spellId, comparator, limit = condition[1], condition[2], condition[3]
		local charges, maxCharges, start, duration = state:GetSpellCharges(spellId)
		charges = charges or 0
		maxCharges = maxCharges or 1
		if condition.count == 0 and charges < maxCharges then
			return TestValue(state.currentTime, INFINITY, charges + 1, start + duration, 1, comparator, limit)
		end
		return Compare(charges, comparator, limit)
	end

	OvaleCondition:RegisterCondition("charges", true, SpellCharges)
	OvaleCondition:RegisterCondition("spellcharges", true, SpellCharges)
end

do
	--- Get the number of seconds before any of the listed spells are ready for use.
	-- @name SpellCooldown
	-- @paramsig number or boolean
	-- @param id The spell ID.
	-- @param ... Optional. Additional spell IDs.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The number of seconds.
	-- @return A boolean value for the result of the comparison.
	-- @see TimeToSpell
	-- @usage
	-- if ShadowOrbs() ==3 and SpellCooldown(mind_blast) <2
	--     Spell(devouring_plague)

	local function SpellCooldown(condition, state)
		local comparator, limit
		local usable = (condition.usable == 1)
		local target = ParseCondition(condition, state, "target")
		local atTime = INFINITY
		for i = 1, #condition do
			local spellId = condition[1]
			if OvaleCondition.COMPARATOR[spellId] then
				comparator, limit = spellId, condition[i+1]
				break
			elseif not OvaleSpellBook:IsKnownSpell(spellId) then
				-- Skip unknown spells.
			elseif not usable or state:IsUsableSpell(spellId, target) then
				local start, duration = state:GetSpellCooldown(spellId)
				local t = 0
				if start > 0 and duration > 0 then
					t = start + duration
				end
				if t < atTime then
					atTime = t
				end
			end
		end
		--[[
			If there are no known spells in the list, then treat the spell as ready.
			This matches SimulationCraft's behavior regarding cooldowns of spells that
			are not known -- they are considered to have a cooldown of zero.
		--]]
		if atTime == INFINITY then
			return Compare(0, comparator, limit)
		elseif atTime > 0 then
			return TestValue(0, atTime, 0, atTime, -1, comparator, limit)
		end
		return Compare(0, comparator, limit)
	end

	OvaleCondition:RegisterCondition("spellcooldown", true, SpellCooldown)
end

do
	--- Get the cooldown duration in seconds for a given spell.
	-- @name SpellCooldownDuration
	-- @paramsig number or boolean
	-- @param id The spell ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target of the spell. The target may also be given as a prefix to the condition.
	--     Defaults to target=target.
	--     Valid values: player, target, focus, pet.
	-- @return The number of seconds.
	-- @return A boolean value for the result of the comparison.

	local function SpellCooldownDuration(condition, state)
		local spellId, comparator, limit = condition[1], condition[2], condition[3]
		local target = ParseCondition(condition, state, "target")
		local duration = state:GetSpellCooldownDuration(spellId, state.currentTime, target)
		return Compare(duration, comparator, limit)
	end

	OvaleCondition:RegisterCondition("spellcooldownduration", true, SpellCooldownDuration)
end

do
	--- Get data for the given spell defined by SpellInfo(...)
	-- @name SpellData
	-- @paramsig number or boolean
	-- @param id The spell ID.
	-- @param key The name of the data set by SpellInfo(...).
	--     Valid values are any alphanumeric string.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The number data associated with the given key.
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- if BuffRemaining(slice_and_dice) >= SpellData(shadow_blades duration)
	--     Spell(shadow_blades)

	local function SpellData(condition, state)
		local spellId, key, comparator, limit = condition[1], condition[2], condition[3], condition[4]
		local si = OvaleData.spellInfo[spellId]
		if si then
			local value = si[key]
			if value then
				return Compare(value, comparator, limit)
			end
		end
		return nil
	end

	OvaleCondition:RegisterCondition("spelldata", false, SpellData)
end

do
	--- Test if the given spell is in the spellbook.
	-- A spell is known if the player has learned the spell and it is in the spellbook.
	-- @name SpellKnown
	-- @paramsig boolean
	-- @param id The spell ID.
	-- @param yesno Optional. If yes, then return true if the spell has been learned.
	--     If no, then return true if the player hasn't learned the spell.
	--     Default is yes.
	--     Valid values: yes, no.
	-- @return A boolean value.
	-- @see SpellUsable

	local function SpellKnown(condition, state)
		local spellId, yesno = condition[1], condition[2]
		local boolean = OvaleSpellBook:IsKnownSpell(spellId)
		return TestBoolean(boolean, yesno)
	end

	OvaleCondition:RegisterCondition("spellknown", true, SpellKnown)
end

do
	--- Test if the given spell is usable.
	-- A spell is usable if the player has learned the spell and meets any requirements for casting the spell.
	-- Does not account for spell cooldowns or having enough of a primary (pooled) resource.
	-- @name SpellUsable
	-- @paramsig boolean
	-- @param id The spell ID.
	-- @param yesno Optional. If yes, then return true if the spell is usable. If no, then return true if it isn't usable.
	--     Default is yes.
	--     Valid values: yes, no.
	-- @return A boolean value.
	-- @see SpellKnown

	local function SpellUsable(condition, state)
		local spellId, yesno = condition[1], condition[2]
		local target = ParseCondition(condition, state, "target")
		local isUsable, noMana = state:IsUsableSpell(spellId, target)
		local boolean = isUsable or noMana
		return TestBoolean(boolean, yesno)
	end

	OvaleCondition:RegisterCondition("spellusable", true, SpellUsable)
end

do
	local LIGHT_STAGGER = 124275
	local MODERATE_STAGGER = 124274
	local HEAVY_STAGGER = 124273

	--- Get the remaining amount of damage Stagger will cause to the target.
	-- @name StaggerRemaining
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The amount of damage.
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- if StaggerRemaining() / MaxHealth() >0.4 Spell(purifying_brew)

	local function StaggerRemaining(condition, state)
		local comparator, limit = condition[1], condition[2]
		local target = ParseCondition(condition, state)
		local aura = state:GetAura(target, HEAVY_STAGGER, "HARMFUL")
		if not state:IsActiveAura(aura) then
			aura = state:GetAura(target, MODERATE_STAGGER, "HARMFUL")
		end
		if not state:IsActiveAura(aura) then
			aura = state:GetAura(target, LIGHT_STAGGER, "HARMFUL")
		end
		if state:IsActiveAura(aura) then
			local gain, start, ending = aura.gain, aura.start, aura.ending
			local stagger = API_UnitStagger(target)
			local rate = -1 * stagger / (ending - start)
			return TestValue(gain, ending, 0, ending, rate, comparator, limit)
		end
		return Compare(0, comparator, limit)
	end

	OvaleCondition:RegisterCondition("staggerremaining", false, StaggerRemaining)
	OvaleCondition:RegisterCondition("staggerremains", false, StaggerRemaining)
end

do
	--- Test if the player is in a given stance.
	-- @name Stance
	-- @paramsig boolean
	-- @param stance The stance name or a number representing the stance index.
	-- @param yesno Optional. If yes, then return true if the player is in the given stance. If no, then return true otherwise.
	--     Default is yes.
	--     Valid values: yes, no.
	-- @return A boolean value.
	-- @usage
	-- unless Stance(druid_bear_form) Spell(bear_form)

	local function Stance(condition, state)
		local stance, yesno = condition[1], condition[2]
		local boolean = state:IsStance(stance)
		return TestBoolean(boolean, yesno)
	end

	OvaleCondition:RegisterCondition("stance", false, Stance)
end

do
	--- Test if the player is currently stealthed.
	-- The player is stealthed if rogue Stealth, druid Prowl, or a similar ability is active.
	-- @name Stealthed
	-- @paramsig boolean
	-- @param yesno Optional. If yes, then return true if stealthed. If no, then return true if it not stealthed.
	--     Default is yes.
	--     Valid values: yes, no.
	-- @return A boolean value.
	-- @usage
	-- if Stealthed() or BuffPresent(shadow_dance)
	--     Spell(ambush)

	local function Stealthed(condition, state)
		local yesno = condition[1]
		local boolean = state:GetAura("player", "stealthed_buff") or API_IsStealthed()
		return TestBoolean(boolean, yesno)
	end

	OvaleCondition:RegisterCondition("isstealthed", false, Stealthed)
	OvaleCondition:RegisterCondition("stealthed", false, Stealthed)
end

do
	--- Get the time elapsed in seconds since the player's previous melee swing (white attack).
	-- @name LastSwing
	-- @paramsig number or boolean
	-- @param hand Optional. Sets which hand weapon's melee swing.
	--     If no hand is specified, then return the time elapsed since the previous swing of either hand's weapon.
	--     Valid values: main, off.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The number of seconds.
	-- @return A boolean value for the result of the comparison.
	-- @see NextSwing

	local function LastSwing(condition, state)
		local swing = condition[1]
		local comparator, limit
		local start
		if swing and swing == "main" or swing == "off" then
			comparator, limit = condition[2], condition[3]
			start = 0
		else
			comparator, limit = condition[1], condition[2]
			start = 0
		end
		Ovale:OneTimeMessage("Warning: 'LastSwing()' is not implemented.")
		return TestValue(start, INFINITY, 0, start, 1, comparator, limit)
	end

	--- Get the time in seconds until the player's next melee swing (white attack).
	-- @name NextSwing
	-- @paramsig number or boolean
	-- @param hand Optional. Sets which hand weapon's melee swing.
	--     If no hand is specified, then return the time until the next swing of either hand's weapon.
	--     Valid values: main, off.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The number of seconds
	-- @return A boolean value for the result of the comparison.
	-- @see LastSwing

	local function NextSwing(condition, state)
		local swing = condition[1]
		local comparator, limit
		local ending
		if swing and swing == "main" or swing == "off" then
			comparator, limit = condition[2], condition[3]
			ending = 0
		else
			comparator, limit = condition[1], condition[2]
			ending = 0
		end
		Ovale:OneTimeMessage("Warning: 'NextSwing()' is not implemented.")
		return TestValue(0, ending, 0, ending, -1, comparator, limit)
	end

	OvaleCondition:RegisterCondition("lastswing", false, LastSwing)
	OvaleCondition:RegisterCondition("nextswing", false, NextSwing)
end

do
	--- Test if the given talent is active.
	-- @name Talent
	-- @paramsig boolean
	-- @param id The talent ID.
	-- @param yesno Optional. If yes, then return true if the glyph is active. If no, then return true if it isn't active.
	--     Default is yes.
	--     Valid values: yes, no.
	-- @return A boolean value.
	-- @usage
	-- if Talent(blood_tap_talent) Spell(blood_tap)

	local function Talent(condition, state)
		local talentId, yesno = condition[1], condition[2]
		local boolean = (OvaleSpellBook:GetTalentPoints(talentId) > 0)
		return TestBoolean(boolean, yesno)
	end

	OvaleCondition:RegisterCondition("talent", false, Talent)
end

do
	--- Get the number of points spent in a talent (0 or 1)
	-- @name TalentPoints
	-- @paramsig number or boolean
	-- @param talent Talent to inspect.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The number of talent points.
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- if TalentPoints(blood_tap_talent) Spell(blood_tap)

	local function TalentPoints(condition, state)
		local talent, comparator, limit = condition[1], condition[2], condition[3]
		local value = OvaleSpellBook:GetTalentPoints(talent)
		return Compare(value, comparator, limit)
	end

	OvaleCondition:RegisterCondition("talentpoints", false, TalentPoints)
end

do
	--- Test if the player is the in-game target of the target.
	-- @name TargetIsPlayer
	-- @paramsig boolean
	-- @param yesno Optional. If yes, then return true if it matches. If no, then return true if it doesn't match.
	--     Default is yes.
	--     Valid values: yes, no.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return A boolean value.
	-- @usage
	-- if target.TargetIsPlayer() Spell(feign_death)

	local function TargetIsPlayer(condition, state)
		local yesno = condition[1]
		local target = ParseCondition(condition, state)
		local boolean = API_UnitIsUnit("player", target .. "target")
		return TestBoolean(boolean, yesno)
	end

	OvaleCondition:RegisterCondition("istargetingplayer", false, TargetIsPlayer)
	OvaleCondition:RegisterCondition("targetisplayer", false, TargetIsPlayer)
end

do
	--- Get the amount of threat on the current target relative to the its primary aggro target, scaled to between 0 (zero) and 100.
	-- This is a number between 0 (no threat) and 100 (will become the primary aggro target).
	-- @name Threat
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=target.
	--     Valid values: player, target, focus, pet.
	-- @return The amount of threat.
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- if Threat() >90 Spell(fade)
	-- if Threat(more 90) Spell(fade)

	local function Threat(condition, state)
		local comparator, limit = condition[1], condition[2]
		local target = ParseCondition(condition, state, "target")
		local _, _, value = API_UnitDetailedThreatSituation("player", target)
		return Compare(value, comparator, limit)
	end

	OvaleCondition:RegisterCondition("threat", false, Threat)
end

do
	--- Get the number of seconds between ticks of a periodic aura on a target.
	-- @name TickTime
	-- @paramsig number or boolean
	-- @param id The spell ID of the aura or the name of a spell list.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param filter Optional. The type of aura to check.
	--     Default is any.
	--     Valid values: any, buff, debuff
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The number of seconds.
	-- @return A boolean value for the result of the comparison.
	-- @see TicksRemaining

	local function TickTime(condition, state)
		local auraId, comparator, limit = condition[1], condition[2], condition[3]
		local target, filter, mine = ParseCondition(condition, state)
		local aura = state:GetAura(target, auraId, filter, mine)
		local tickTime
		if state:IsActiveAura(aura) then
			tickTime = aura.tick
		else
			tickTime = OvaleData:GetTickLength(auraId, state.snapshot)
		end
		if tickTime and tickTime > 0 then
			return Compare(tickTime, comparator, limit)
		end
		return Compare(INFINITY, comparator, limit)
	end

	OvaleCondition:RegisterCondition("ticktime", false, TickTime)
end

do
	--- Get the remaining number of ticks of a periodic aura on a target.
	-- @name TicksRemaining
	-- @paramsig number or boolean
	-- @param id The spell ID of the aura or the name of a spell list.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param filter Optional. The type of aura to check.
	--     Default is any.
	--     Valid values: any, buff, debuff
	-- @param target Optional. Sets the target to check. The target may also be given as a prefix to the condition.
	--     Defaults to target=player.
	--     Valid values: player, target, focus, pet.
	-- @return The number of ticks.
	-- @return A boolean value for the result of the comparison.
	-- @see TickTime
	-- @usage
	-- if target.TicksRemaining(shadow_word_pain) <2
	--     Spell(shadow_word_pain)

	local function TicksRemaining(condition, state)
		local auraId, comparator, limit = condition[1], condition[2], condition[3]
		local target, filter, mine = ParseCondition(condition, state)
		local aura = state:GetAura(target, auraId, filter, mine)
		if aura then
			local gain, start, ending, tick = aura.gain, aura.start, aura.ending, aura.tick
			if tick and tick > 0 then
				return TestValue(gain, INFINITY, 1, ending, -1/tick, comparator, limit)
			end
		end
		return Compare(0, comparator, limit)
	end

	OvaleCondition:RegisterCondition("ticksremaining", false, TicksRemaining)
	OvaleCondition:RegisterCondition("ticksremain", false, TicksRemaining)
end

do
	--- Get the number of seconds elapsed since the player entered combat.
	-- @name TimeInCombat
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The number of seconds.
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- if TimeInCombat(more 5) Spell(bloodlust)

	local function TimeInCombat(condition, state)
		local comparator, limit = condition[1], condition[2]
		if state.inCombat then
			local start = state.combatStartTime
			return TestValue(start, INFINITY, 0, start, 1, comparator, limit)
		end
		return Compare(0, comparator, limit)
	end

	OvaleCondition:RegisterCondition("timeincombat", false, TimeInCombat)
end

do
	--- Get the number of seconds elapsed since the player cast the given spell.
	-- @name TimeSincePreviousSpell
	-- @paramsig number or boolean
	-- @param id The spell ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The number of seconds.
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- if TimeSincePreviousSpell(pestilence) > 28 Spell(pestilence)

	local function TimeSincePreviousSpell(condition, state)
		local spellId, comparator, limit = condition[1], condition[2], condition[3]
		local t = state:TimeOfLastCast(spellId)
		return TestValue(0, INFINITY, t, 0, 1, comparator, limit)
	end

	OvaleCondition:RegisterCondition("timesincepreviousspell", false, TimeSincePreviousSpell)
end

do
	--- Get the time in seconds until the next scheduled Bloodlust cast.
	-- Not implemented, always returns 3600 seconds.
	-- @name TimeToBloodlust
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The number of seconds.
	-- @return A boolean value for the result of the comparison.

	local function TimeToBloodlust(condition, state)
		local comparator, limit = condition[1], condition[2], condition[3]
		local value = 3600
		return Compare(value, comparator, limit)
	end

	OvaleCondition:RegisterCondition("timetobloodlust", false, TimeToBloodlust)
end

do
	--- Get the number of seconds before the player reaches the given power level.
	local function TimeToPower(powerType, level, comparator, limit, state)
		local level = level or 0
		local power = state[powerType] or 0
		local powerRegen = state.powerRate[powerType] or 1
		if powerRegen == 0 then
			if power == level then
				return Compare(0, comparator, limit)
			end
			return Compare(INFINITY, comparator, limit)
		else
			local t = (level - power) / powerRegen
			if t > 0 then
				local ending = state.currentTime + t
				return TestValue(0, ending, 0, ending, -1, comparator, limit)
			end
			return Compare(0, comparator, limit)
		end
	end

	--- Get the number of seconds before the player reaches the given energy level for feral druids, non-mistweaver monks and rogues.
	-- @name TimeToEnergy
	-- @paramsig number or boolean
	-- @param level. The level of energy to reach.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The number of seconds.
	-- @see TimeToEnergyFor, TimeToMaxEnergy
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- if TimeToEnergy(100) < 1.2 Spell(sinister_strike)

	local function TimeToEnergy(condition, state)
		local level, comparator, limit = condition[1], condition[2], condition[3]
		return TimeToPower("energy", level, comparator, limit, state)
	end

	--- Get the number of seconds before the player reaches maximum energy for feral druids, non-mistweaver monks and rogues.
	-- @name TimeToMaxEnergy
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The number of seconds.
	-- @see TimeToEnergy, TimeToEnergyFor
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- if TimeToMaxEnergy() < 1.2 Spell(sinister_strike)

	local function TimeToMaxEnergy(condition, state)
		local powerType = "energy"
		local comparator, limit = condition[1], condition[2]
		local level = OvalePower.maxPower[powerType] or 0
		return TimeToPower(powerType, level, comparator, limit, state)
	end

	--- Get the number of seconds before the player reaches the given focus level for hunters.
	-- @name TimeToFocus
	-- @paramsig number or boolean
	-- @param level. The level of focus to reach.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The number of seconds.
	-- @see TimeToFocusFor, TimeToMaxFocus
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- if TimeToFocus(100) < 1.2 Spell(cobra_shot)

	local function TimeToFocus(condition, state)
		local level, comparator, limit = condition[1], condition[2], condition[3]
		return TimeToPower("focus", level, comparator, limit, state)
	end

	--- Get the number of seconds before the player reaches maximum focus for hunters.
	-- @name TimeToMaxFocus
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The number of seconds.
	-- @see TimeToFocus, TimeToFocusFor
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- if TimeToMaxFocus() < 1.2 Spell(cobra_shot)

	local function TimeToMaxFocus(condition, state)
		local powerType = "focus"
		local comparator, limit = condition[1], condition[2]
		local level = OvalePower.maxPower[powerType] or 0
		return TimeToPower(powerType, level, comparator, limit, state)
	end

	OvaleCondition:RegisterCondition("timetoenergy", false, TimeToEnergy)
	OvaleCondition:RegisterCondition("timetofocus", false, TimeToFocus)
	OvaleCondition:RegisterCondition("timetomaxenergy", false, TimeToMaxEnergy)
	OvaleCondition:RegisterCondition("timetomaxfocus", false, TimeToMaxFocus)
end

do
	local function TimeToPowerFor(powerType, condition, state)
		local spellId, comparator, limit = condition[1], condition[2], condition[3]
		local target = ParseCondition(condition, state, "target")
		if not powerType then
			local _, pt = OvalePower:GetSpellCost(spellId)
			powerType = pt
		end
		local seconds = state:TimeToPower(spellId, target, powerType)

		if seconds == 0 then
			return Compare(0, comparator, limit)
		elseif seconds < INFINITY then
			return TestValue(0, state.currentTime + seconds, seconds, state.currentTime, -1, comparator, limit)
		else -- if seconds == INFINITY then
			return Compare(INFINITY, comparator, limit)
		end
	end

	--- Get the number of seconds before the player has enough energy to cast the given spell.
	-- @name TimeToEnergyFor
	-- @paramsig number or boolean
	-- @param id The spell ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target of the spell. The target may also be given as a prefix to the condition.
	--     Defaults to target=target.
	--     Valid values: player, target, focus, pet.
	-- @return The number of seconds.
	-- @return A boolean value for the result of the comparison.
	-- @see TimeToEnergyFor, TimeToMaxEnergy

	local function TimeToEnergyFor(condition, state)
		return TimeToPowerFor("energy", condition, state)
	end

	--- Get the number of seconds before the player has enough focus to cast the given spell.
	-- @name TimeToFocusFor
	-- @paramsig number or boolean
	-- @param id The spell ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The number of seconds.
	-- @return A boolean value for the result of the comparison.
	-- @see TimeToFocusFor

	local function TimeToFocusFor(condition, state)
		return TimeToPowerFor("focus", condition, state)
	end

	OvaleCondition:RegisterCondition("timetoenergyfor", true, TimeToEnergyFor)
	OvaleCondition:RegisterCondition("timetofocusfor", true, TimeToFocusFor)
end

do
	--- Get the number of seconds before the spell is ready to be cast, either due to cooldown or resources.
	-- @name TimeToSpell
	-- @paramsig number or boolean
	-- @param id The spell ID.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target of the spell. The target may also be given as a prefix to the condition.
	--     Defaults to target=target.
	--     Valid values: player, target, focus, pet.
	-- @return The number of seconds.
	-- @return A boolean value for the result of the comparison.

	local function TimeToSpell(condition, state)
		local spellId, comparator, limit = condition[1], condition[2], condition[3]
		local target = ParseCondition(condition, state, "target")
		local seconds = state:GetTimeToSpell(spellId, target)
		if seconds == 0 then
			return Compare(0, comparator, limit)
		elseif seconds < INFINITY then
			return TestValue(0, state.currentTime + seconds, seconds, state.currentTime, -1, comparator, limit)
		else -- if seconds == INFINITY then
			return Compare(INFINITY, comparator, limit)
		end
	end

	OvaleCondition:RegisterCondition("timetospell", true, TimeToSpell)
end

do
	--- Get the time scaled by the specified haste type, defaulting to spell haste.
	--- For example, if a DoT normally ticks every 3 seconds and is scaled by spell haste, then it ticks every TimeWithHaste(3 haste=spell) seconds.
	-- @name TimeWithHaste
	-- @paramsig number or boolean
	-- @param time The time in seconds.
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param haste Optional. Sets whether "time" should be lengthened or shortened due to haste.
	--     Defaults to haste=spell.
	--     Valid values: melee, spell.
	-- @return The time in seconds scaled by haste.
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- if target.DebuffRemaining(flame_shock) < TimeWithHaste(3)
	--     Spell(flame_shock)

	local function TimeWithHaste(condition, state)
		local seconds, comparator, limit = condition[1], condition[2], condition[3]
		local haste = condition.haste or "spell"
		local value = GetHastedTime(seconds, haste)
		return Compare(value, comparator, limit)
	end

	OvaleCondition:RegisterCondition("timewithhaste", false, TimeWithHaste)
end

do
	-- Deprecated: totem types
	local function CheckDeprecatedTotem(id, state)
		local warning = false
		local specialization = state.specialization
		if id == "mushroom" then
			warning = id
			if specialization == 1 then
				-- Balance.
				id = 88747
			elseif specialization == 4 then
				-- Restoration.
				id = 145205
			end
		elseif id == "statue" then
			warning = id
			if specialization == 1 then
				-- Brewmaster.
				id = 115315
			elseif specialization == 2 then
				-- Mistweaver.
				id = 115313
			end
		elseif id == "ghoul" then
			-- Ghouls are no longer totems, but pets summoned by Unholy Death Knights.
			warning = id
		end
		if warning then
			Ovale:OneTimeMessage("Warning: '%s' is deprecated; using '%s' instead.", warning, tostring(id))
		end
		return id
	end

	--- Test if the totem has expired.
	-- @name TotemExpires
	-- @paramsig boolean
	-- @param id The ID of the spell used to summon the totem or one of the four shaman totem categories (air, earth, fire, water).
	-- @param seconds Optional. The maximum number of seconds before the totem should expire.
	--     Defaults to 0 (zero).
	-- @return A boolean value.
	-- @see TotemPresent, TotemRemaining
	-- @usage
	-- if TotemExpires(fire) Spell(searing_totem)
	-- if TotemPresent(healing_stream_totem) and TotemExpires(water 3) Spell(totemic_recall)

	local function TotemExpires(condition, state)
		local id, seconds = condition[1], condition[2]
		seconds = seconds or 0
		if condition.totem then
			id = condition.totem
			Ovale:OneTimeMessage("Warning: using 'totem' parameter in 'TotemExpires()' is deprecated.")
		end
		id = CheckDeprecatedTotem(id, state)
		if type(id) == "string" then
			local haveTotem, name, startTime, duration = state:GetTotemInfo(id)
			if haveTotem and startTime then
				return startTime + duration - seconds, INFINITY
			end
		else -- if type(id) == "number" then
			local count, start, ending = state:GetTotemCount(id)
			if count > 0 then
				return ending - seconds, INFINITY
			end
		end
		return 0, INFINITY
	end

	--- Test if the totem is present.
	-- @name TotemPresent
	-- @paramsig boolean
	-- @param id The ID of the spell used to summon the totem or one of the four shaman totem categories (air, earth, fire, water).
	-- @return A boolean value.
	-- @see TotemExpires, TotemRemaining
	-- @usage
	-- if not TotemPresent(fire) Spell(searing_totem)
	-- if TotemPresent(healing_stream_totem) and TotemExpires(water 3) Spell(totemic_recall)

	local function TotemPresent(condition, state)
		local id = condition[1]
		if condition.totem then
			id = condition.totem
			Ovale:OneTimeMessage("Warning: using 'totem' parameter in 'TotemPresent()' is deprecated.")
		end
		id = CheckDeprecatedTotem(id, state)
		if type(id) == "string" then
			local haveTotem, name, startTime, duration = state:GetTotemInfo(id)
			if haveTotem and startTime then
				return startTime, startTime + duration
			end
		else -- if type(id) == "number" then
			local count, start, ending = state:GetTotemCount(id)
			if count > 0 then
				return start, ending
			end
		end
		return nil
	end

	OvaleCondition:RegisterCondition("totemexpires", false, TotemExpires)
	OvaleCondition:RegisterCondition("totempresent", false, TotemPresent)

	--- Get the remaining time in seconds before a totem expires.
	-- @name TotemRemaining
	-- @paramsig number or boolean
	-- @param id The ID of the spell used to summon the totem or one of the four shaman totem categories (air, earth, fire, water).
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The number of seconds.
	-- @return A boolean value for the result of the comparison.
	-- @see TotemExpires, TotemPresent
	-- @usage
	-- if TotemRemaining(healing_stream_totem) <2 Spell(totemic_recall)

	local function TotemRemaining(condition, state)
		local id, comparator, limit = condition[1], condition[2], condition[3]
		if condition.totem then
			id = condition.totem
			Ovale:OneTimeMessage("Warning: using 'totem' parameter in 'TotemRemaining()' is deprecated.")
		end
		id = CheckDeprecatedTotem(id, state)
		if type(id) == "string" then
			local haveTotem, name, startTime, duration = state:GetTotemInfo(id)
			if haveTotem and startTime then
				local start, ending = startTime, startTime + duration
				return TestValue(start, ending, 0, ending, -1, comparator, limit)
			end
		else -- if type(id) == "number" then
			local count, start, ending = state:GetTotemCount(id)
			if count > 0 then
				return TestValue(start, ending, 0, ending, -1, comparator, limit)
			end
		end
		return Compare(0, comparator, limit)
	end

	OvaleCondition:RegisterCondition("totemremaining", false, TotemRemaining)
	OvaleCondition:RegisterCondition("totemremains", false, TotemRemaining)
end

do
	-- Check if a tracking is enabled
	-- 1: the spell id
	-- return bool

	local function Tracking(condition, state)
		local spellId, yesno = condition[1], condition[2]
		local spellName = OvaleSpellBook:GetSpellName(spellId)
		local numTrackingTypes = API_GetNumTrackingTypes()
		local boolean = false
		for i = 1, numTrackingTypes do
			local name, _, active = API_GetTrackingInfo(i)
			if name and name == spellName then
				boolean = (active == 1)
				break
			end
		end
		return TestBoolean(boolean, yesno)
	end

	OvaleCondition:RegisterCondition("tracking", false, Tracking)
end

do
	--- The maximum travel time of a spell in seconds.
	-- This is a fixed guess at 1s or the maximum travel time of the spell in the spell information if given.
	-- @name MaxTravelTime
	-- @paramsig number or boolean
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @param target Optional. Sets the target of the spell. The target may also be given as a prefix to the condition.
	--     Defaults to target=target.
	--     Valid values: player, target, focus, pet.
	-- @return The number of seconds.
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- if target.DebuffPresent(shadowflame_debuff) < MaxTravelTime(hand_of_guldan) + GCD()
	--     Spell(hand_of_guldan)

	local function MaxTravelTime(condition, state)
		local spellId, comparator, limit = condition[1], condition[2], condition[3]
		local target = ParseCondition(condition, state, "target")
		local si = spellId and OvaleData.spellInfo[spellId]
		-- TODO: Track average time in flight to target for the spell.
		local value = state:GetSpellInfoProperty(spellId, "max_travel_time", target) or 1
		return Compare(value, comparator, limit)
	end

	OvaleCondition:RegisterCondition("maxtraveltime", true, MaxTravelTime)
end

do
	--- A condition that always returns true.
	-- @name True
	-- @paramsig boolean
	-- @return A boolean value.

	local function True(condition, state)
		return 0, INFINITY
	end

	OvaleCondition:RegisterCondition("true", false, True)
end

do
	--- The normalized weapon damage of the weapon in the given hand.
	-- @name WeaponDamage
	-- @paramsig number or boolean
	-- @param hand Optional. Sets which hand weapon.
	--     Defaults to main.
	--     Valid values: main, off
	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
	-- @param number Optional. The number to compare against.
	-- @return The normalized weapon damage.
	-- @return A boolean value for the result of the comparison.
	-- @usage
	-- AddFunction MangleDamage {
	--     WeaponDamage() * 5 + 78
	-- }

	local function WeaponDamage(condition, state)
		local hand = condition[1]
		local comparator, limit
		local value = 0
		if hand == "offhand" or hand == "off" then
			comparator, limit = condition[2], condition[3]
			value = state.snapshot.offHandWeaponDamage
		elseif hand == "mainhand" or hand == "main" then
			comparator, limit = condition[2], condition[3]
			value = state.snapshot.mainHandWeaponDamage
		else
			comparator, limit = condition[1], condition[2]
			value = state.snapshot.mainHandWeaponDamage
		end
		return Compare(value, comparator, limit)
	end

	OvaleCondition:RegisterCondition("weapondamage", false, WeaponDamage)
end

do
	--- Test if the weapon imbue on the given weapon has expired or will expire after a given number of seconds.
	-- @name WeaponEnchantExpires
	-- @paramsig boolean
	-- @param hand Sets which hand weapon.
	--     Valid values: main, off.
	-- @param seconds Optional. The maximum number of seconds before the weapon imbue should expire.
	--     Defaults to 0 (zero).
	-- @return A boolean value.
	-- @usage
	-- if WeaponEnchantExpires(main) Spell(windfury_weapon)

	local function WeaponEnchantExpires(condition, state)
		local hand, seconds = condition[1], condition[2]
		seconds = seconds or 0
		local hasMainHandEnchant, mainHandExpiration, _, hasOffHandEnchant, offHandExpiration = API_GetWeaponEnchantInfo()
		local now = API_GetTime()
		if hand == "mainhand" or hand == "main" then
			if hasMainHandEnchant then
				mainHandExpiration = mainHandExpiration / 1000
				return now + mainHandExpiration - seconds, INFINITY
			end
		elseif hand == "offhand" or hand == "off" then
			if hasOffHandEnchant then
				offHandExpiration = offHandExpiration / 1000
				return now + offHandExpiration - seconds, INFINITY
			end
		end
		return 0, INFINITY
	end

	OvaleCondition:RegisterCondition("weaponenchantexpires", false, WeaponEnchantExpires)
end