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

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

--[[----------------------------------------------------------------------------
	Script conditions.

	A script condition must have a name that is lowercase.

	A script condition can return in two different ways:

	(1) start, ending
		This returns a time interval representing when the condition is true
		and is used by conditions that return only a time interval.

	(2) start, ending, value, origin, rate
		This returns a function f(t) = value + (t - origin) * rate that is
		valid for start < t < ending.  This return method is used by
		conditions that return a value that is used in numerical comparisons
		or operations.

	The endpoint of a time interval must be between 0 and infinity, where
	infinity is represented by math.huge.  Time is a value such as returned by
	the API function GetTime().

	Examples:

	(1)	(0, math.huge) means the condition is always true.

	(2)	nil is the empty set and means the condition is always false.

	(3)	(0, math.huge, constant, 0, 0) means the condition has a constant value.

	(4)	(start, ending, ending - start, start, -1) means the condition has a
		value of f(t) = ending - t, at time t between start and ending.  This
		basically returns how much time is left within the time interval.
--]]----------------------------------------------------------------------------

local _, Ovale = ...
local OvaleCondition = Ovale:NewModule("OvaleCondition")
Ovale.OvaleCondition = OvaleCondition

--<private-static-properties>
local type = type
local wipe = table.wipe

-- Table of script conditions.
self_condition = {}
-- List of script conditions that refer to a castable spell from the player's spellbook.
self_spellBookCondition = {}
do
	-- Spell(spellId) can be used as a condition instead of an action.
	self_spellBookCondition["spell"] = true
end
--</private-static-properties>

--<public-static-properties>
--[[
	The actual target referenced when the "target" parameter is used in a condition.
	This is to support setting a different target in an AddIcon "target" parameter,
	e.g., target=focus, while re-using the same script.
--]]
OvaleCondition.defaultTarget = "target"
OvaleCondition.Compare = nil
OvaleCondition.ParseCondition = nil
OvaleCondition.ParseRuneCondition = nil
OvaleCondition.TestBoolean = nil
OvaleCondition.TestValue = nil

OvaleCondition.COMPARATOR = {
	atLeast = true,
	atMost = true,
	equal = true,
	less = true,
	more = true,
}
--</public-static-properties>

--<public-static-methods>
function OvaleCondition:RegisterCondition(name, isSpellBookCondition, func, arg)
	if arg then
		if type(func) == "string" then
			func = arg[func]
		end
		self_condition[name] = function(...) func(arg, ...) end
	else
		self_condition[name] = func
	end
	if isSpellBookCondition then
		self_spellBookCondition[name] = true
	end
end

function OvaleCondition:UnregisterCondition(name)
	self_condition[name] = nil
end

function OvaleCondition:IsCondition(name)
	return (self_condition[name] ~= nil)
end

function OvaleCondition:IsSpellBookCondition(name)
	return (self_spellBookCondition[name] ~= nil)
end

function OvaleCondition:EvaluateCondition(name, ...)
	return self_condition[name](...)
end

OvaleCondition.ParseCondition = function(condition, defaultTarget)
	defaultTarget = defaultTarget or "player"
	local target = condition.target or defaultTarget
	-- Side-effect: set condition.target to the correct value if not present.
	condition.target = condition.target or target
	if target == "target" then
		target = OvaleCondition.defaultTarget
	end

	local filter
	if condition.filter then
		if condition.filter == "debuff" then
			filter = "HARMFUL"
		elseif condition.filter == "buff" then
			filter = "HELPFUL"
		end
	end

	local mine = true
	if condition.any and condition.any == 1 then
		mine = false
	else
		-- Legacy parameter "mine"; no longer documented.
		if not condition.any and condition.mine and condition.mine ~= 1 then
			mine = false
		end
	end

	return target, filter, mine
end

-- Returns whether "a" matches "yesno".
OvaleCondition.TestBoolean = function(a, yesno)
	if not yesno or yesno == "yes" then
		if a then
			return 0, math.huge
		end
	else
		if not a then
			return 0, math.huge
		end
	end
	return nil
end

-- Returns either an "Ovale value" or a boolean, depending on whether "comparator" is given.
-- An "Ovale value" is a quintuplet (start, ending, value, origin, rate) that determines a
-- linear function A(t) = value + (t - origin)*rate, with domain (start, ending).
OvaleCondition.TestValue = function(start, ending, value, origin, rate, comparator, limit)
	--[[
							 A(t) = limit
		value + (t - origin)*rate = limit
				(t - origin)*rate = limit - value
	--]]
	if not value or not origin or not rate then
		return nil
	end

	start = start or 0
	ending = ending or math.huge

	if not comparator then
		if start < ending then
			return start, ending, value, origin, rate
		else
			return 0, math.huge, 0, 0, 0
		end
	elseif not OvaleCondition.COMPARATOR[comparator] then
		Ovale:Errorf("unknown comparator %s", comparator)
	elseif not limit then
		Ovale:Errorf("comparator %s missing limit", comparator)
	elseif rate == 0 then
		if (comparator == "less" and value < limit)
				or (comparator == "atMost" and value <= limit)
				or (comparator == "equal" and value == limit)
				or (comparator == "atLeast" and value >= limit)
				or (comparator == "more" and value > limit) then
			return start, ending
		end
	elseif (comparator == "less" and rate > 0)
			or (comparator == "atMost" and rate > 0)
			or (comparator == "atLeast" and rate < 0)
			or (comparator == "more" and rate < 0) then
		local t = (limit - value)/rate + origin
		ending = (ending < t) and ending or t
		return start, ending
	elseif (comparator == "less" and rate < 0)
			or (comparator == "atMost" and rate < 0)
			or (comparator == "atLeast" and rate > 0)
			or (comparator == "more" and rate > 0) then
		local t = (limit - value)/rate + origin
		start = (start > t) and start or t
		return start, math.huge
	end
	return nil
end

OvaleCondition.Compare = function(value, comparator, limit)
	return OvaleCondition.TestValue(0, math.huge, value, 0, 0, comparator, limit)
end
--</public-static-methods>