Multientry (#7)

* from retail

* from retail

* from retail

* from retail

* from retail

* from retail

* remove new threat functions as they are not well implemented for now
This commit is contained in:
NoM0Re
2024-11-27 12:09:02 +01:00
committed by GitHub
parent eb8221cf89
commit 13f734038d
67 changed files with 3493 additions and 969 deletions
+275 -137
View File
@@ -1,6 +1,6 @@
local AddonName, Private = ...
local internalVersion = 52
local internalVersion = 67
-- Lua APIs
local insert = table.insert
@@ -537,6 +537,147 @@ function Private.ParseNumber(numString)
end
end
local function EvalBooleanArg(arg, trigger, default)
if(type(arg) == "function") then
return arg(trigger);
elseif type(arg) == "boolean" then
return arg
elseif type(arg) == "nil" then
return default
end
end
local function singleTest(arg, trigger, use, name, value, operator, use_exact, caseInsensitive)
local number = value and tonumber(value) or nil
if(arg.type == "tristate") then
if(use == false) then
return "(not "..name..")";
elseif(use) then
if(arg.test) then
return "("..arg.test:format(value)..")";
else
return name;
end
end
elseif(arg.type == "tristatestring") then
if(use == false) then
return "("..name.. "~=".. (number or string.format("%s", Private.QuotedString(value or ""))) .. ")"
elseif(use) then
return "("..name.. "==".. (number or string.format("%s", Private.QuotedString(value or ""))) .. ")"
end
elseif(arg.type == "multiselect") then
if arg.multiNoSingle then
-- convert single to multi
-- this is a lazy migration because multiNoSingle is not set for all game versions
if use == true then
trigger["use_"..name] = false
trigger[name] = trigger[name] or {}
trigger[name].multi = {};
if trigger[name].single ~= nil then
trigger[name].multi[trigger[name].single] = true;
trigger[name].single = nil
end
end
end
if(use == false) then -- multi selection
local any = false;
if (value and value.multi) then
local test = "(";
for value, positive in pairs(value.multi) do
local arg1 = tonumber(value) or ("[["..value.."]]")
local arg2
if arg.extraOption then
arg2 = trigger[name .. "_extraOption"] or 0
elseif arg.multiTristate then
arg2 = positive and 4 or 5
end
local testEnabled = true
if type(arg.enableTest) == "function" then
testEnabled = arg.enableTest(trigger, arg1, arg2)
end
if testEnabled then
local check
if not arg.test then
check = name.."=="..arg1
else
check = arg.test:format(arg1, arg2)
end
if arg.multiAll then
test = test..check.." and "
else
test = test..check.." or "
end
any = true;
end
end
if(any) then
test = test:sub(1, -6);
else
test = "(false";
end
test = test..")"
if arg.inverse then
if type(arg.inverse) == "boolean" then
test = "not " .. test
elseif type(arg.inverse) == "function" then
if arg.inverse(trigger) then
test = "not " .. test
end
end
end
return test
end
elseif(use) then -- single selection
local value = value and value.single or nil;
if not arg.test then
return value and "("..name.."=="..(tonumber(value) or ("[["..value.."]]"))..")";
else
return value and "("..arg.test:format(tonumber(value) or ("[["..value.."]]"))..")";
end
end
elseif(arg.type == "toggle") then
if(use) then
if(arg.test) then
return "("..arg.test:format(value)..")";
else
return name;
end
end
elseif (arg.type == "spell") then
if arg.showExactOption then
return "("..arg.test:format(value, tostring(use_exact) or "false") ..")";
else
return "("..arg.test:format(value)..")";
end
elseif(arg.test) then
return "("..arg.test:format(value)..")";
elseif(arg.type == "longstring" and operator) then
if(operator == "==") then
if caseInsensitive then
return ("(%s and %s:lower() == [[%s]]:lower())"):format(name, name, value)
else
return "("..name.."==[["..value.."]])";
end
else
if caseInsensitive then
local op = operator:format(value:lower())
return ("(%s:lower():%s)"):format(name, op)
else
return "("..name..":"..operator:format(value)..")";
end
end
elseif(arg.type == "number") then
if number then
return "("..name..(operator or "==").. number ..")";
end
else
if(type(value) == "table") then
value = "error";
end
return "("..name..(operator or "==")..(number or ("[["..(value or "").."]]"))..")";
end
end
-- Used for the load function, could be simplified a bit
-- It used to be also used for the generic trigger system
local function ConstructFunction(prototype, trigger, skipOptional)
@@ -547,164 +688,108 @@ local function ConstructFunction(prototype, trigger, skipOptional)
local events = {}
local init;
local preambles = ""
local orConjunctionGroups = {}
if(prototype.init) then
init = prototype.init(trigger);
else
init = "";
end
for index, arg in pairs(prototype.args) do
local enable = arg.type ~= "collpase";
if(type(arg.enable) == "function") then
enable = arg.enable(trigger);
elseif type(arg.enable) == "boolean" then
enable = arg.enable
local enable = EvalBooleanArg(arg.enable, trigger, true)
local init = arg.init
local name = arg.name;
if(arg.init == "arg") then
tinsert(input, name);
end
if(enable) then
local name = arg.name;
if not(arg.name or arg.hidden) then
tinsert(input, "_");
else
if(arg.init == "arg") then
tinsert(input, name);
end
if (arg.optional and skipOptional) then
-- Do nothing
elseif(arg.hidden or arg.type == "tristate" or arg.type == "toggle" or arg.type == "tristatestring"
or (arg.type == "multiselect" and trigger["use_"..name] ~= nil)
or ((trigger["use_"..name] or arg.required) and trigger[name])) then
if(arg.init and arg.init ~= "arg") then
init = init.."local "..name.." = "..arg.init.."\n";
end
local number = trigger[name] and tonumber(trigger[name]);
local test;
if(arg.type == "tristate") then
if(trigger["use_"..name] == false) then
test = "(not "..name..")";
elseif(trigger["use_"..name]) then
if(arg.test) then
test = "("..arg.test:format(trigger[name])..")";
else
test = name;
end
end
elseif(arg.type == "tristatestring") then
if(trigger["use_"..name] == false) then
test = "("..name.. "~=".. (number or string.format("%s", Private.QuotedString(trigger[name] or ""))) .. ")"
elseif(trigger["use_"..name]) then
test = "("..name.. "==".. (number or string.format("%s", Private.QuotedString(trigger[name] or ""))) .. ")"
end
elseif(arg.type == "multiselect") then
if(trigger["use_"..name] == false) then -- multi selection
local any = false;
if (trigger[name] and trigger[name].multi) then
test = "(";
for value, _ in pairs(trigger[name].multi) do
if not arg.test then
test = test..name.."=="..(tonumber(value) or "[["..value.."]]").." or ";
else
if arg.extraOption then
test = test..arg.test:format(tonumber(value) or "[["..value.."]]", trigger[name .. "_extraOption"] or 0).." or ";
else
test = test..arg.test:format(tonumber(value) or "[["..value.."]]").." or ";
end
end
any = true;
end
if(any) then
test = test:sub(1, -5);
else
test = "(false";
end
test = test..")"
if arg.inverse then
if type(arg.inverse) == "boolean" then
test = "not " .. test
elseif type(arg.inverse) == "function" then
if arg.inverse(trigger) then
test = "not " .. test
end
end
end
end
elseif(trigger["use_"..name]) then -- single selection
local value = trigger[name] and trigger[name].single;
if not arg.test then
test = trigger[name] and trigger[name].single and "("..name.."=="..(tonumber(value) or "[["..value.."]]")..")";
else
test = trigger[name] and trigger[name].single and "("..arg.test:format(tonumber(value) or "[["..value.."]]")..")";
end
end
elseif(arg.type == "toggle") then
if(trigger["use_"..name]) then
if(arg.test) then
test = "("..arg.test:format(trigger[name])..")";
else
test = name;
end
end
elseif (arg.type == "spell") then
if arg.showExactOption then
test = "("..arg.test:format(trigger[name], tostring(trigger["use_exact_" .. name]) or "false") ..")";
else
test = "("..arg.test:format(trigger[name])..")";
end
elseif(arg.test) then
test = "("..arg.test:format(trigger[name])..")";
elseif(arg.type == "longstring" and trigger[name.."_operator"]) then
if(trigger[name.."_operator"] == "==") then
test = "("..name.."==[["..trigger[name].."]])";
else
test = "("..name..":"..trigger[name.."_operator"]:format(trigger[name])..")";
end
elseif(arg.type == "number") then
if number then
test = "("..name..(trigger[name.."_operator"] or "==").. number ..")";
end
else
if(type(trigger[name]) == "table") then
trigger[name] = "error";
end
test = "("..name..(trigger[name.."_operator"] or "==")..(number or "[["..(trigger[name] or "").."]]")..")";
end
if (arg.preamble) then
preambles = preambles .. arg.preamble:format(trigger[name]) .. "\n"
end
if test ~= "(test)" then
if(arg.required) then
tinsert(required, test);
if(enable) then
if (arg.optional and skipOptional) then
-- Do nothing
elseif arg.type == "tristate"
or arg.type == "toggle"
or arg.type == "tristatestring"
or (arg.type == "multiselect" and trigger["use_"..name] ~= nil)
or ((trigger["use_"..name] or arg.required) and trigger[name])
then
local test;
if arg.multiEntry then
if type(trigger[name]) == "table" and #trigger[name] > 0 then
test = ""
for i, value in ipairs(trigger[name]) do
local operator = name and type(trigger[name.."_operator"]) == "table" and trigger[name.."_operator"][i]
local caseInsensitive = name and arg.canBeCaseInsensitive and type(trigger[name.."_caseInsensitive"]) == "table" and trigger[name.."_caseInsensitive"][i]
local use_exact = name and type(trigger["use_exact_" .. name]) == "table" and trigger["use_exact_" .. name][i]
local use = name and trigger["use_"..name]
local single = singleTest(arg, trigger, use, name, value, operator, use_exact, caseInsensitive)
if single then
if test ~= "" then
test = test .. arg.multiEntry.operator
end
test = test .. single
end
end
if test == "" then
test = nil
else
test = "(" .. test .. ")"
end
end
else
local value = trigger[name]
local operator = name and trigger[name.."_operator"]
local caseInsensitive = name and trigger[name.."_caseInsensitive"]
local use_exact = name and trigger["use_exact_" .. name]
local use = name and trigger["use_"..name]
test = singleTest(arg, trigger, use, name, value, operator, use_exact, caseInsensitive)
end
if (arg.preamble) then
preambles = preambles .. arg.preamble:format(trigger[name]) .. "\n"
end
if test ~= "(test)" then
if(arg.required) then
tinsert(required, test);
elseif test ~= nil then
if arg.orConjunctionGroup then
orConjunctionGroups[arg.orConjunctionGroup ] = orConjunctionGroups[arg.orConjunctionGroup ] or {}
tinsert(orConjunctionGroups[arg.orConjunctionGroup ], test)
else
tinsert(tests, test);
end
end
end
if test and arg.events then
for index, event in ipairs(arg.events) do
events[event] = true
end
if test and arg.events then
for index, event in ipairs(arg.events) do
events[event] = true
end
end
if(arg.debug) then
tinsert(debug, arg.debug:format(trigger[name]));
end
if(arg.debug) then
tinsert(debug, arg.debug:format(trigger[name]));
end
end
end
end
local ret = preambles .. "return function("..table.concat(input, ", ")..")\n";
ret = ret..(init or "");
ret = ret..(#debug > 0 and table.concat(debug, "\n") or "");
ret = ret.."if(";
ret = ret..((#required > 0) and table.concat(required, " and ").." and " or "");
ret = ret..(#tests > 0 and table.concat(tests, " and ") or "true");
ret = ret..") then\n";
if(#debug > 0) then
ret = ret.."print('ret: true');\n";
for _, orConjunctionGroup in pairs(orConjunctionGroups) do
tinsert(tests, "("..table.concat(orConjunctionGroup , " or ")..")")
end
ret = ret.."return true else return false end end";
local ret = {preambles .. "return function("..table.concat(input, ", ")..")\n"};
table.insert(ret, (init or ""));
table.insert(ret, (#debug > 0 and table.concat(debug, "\n") or ""));
table.insert(ret, "if(");
table.insert(ret, ((#required > 0) and table.concat(required, " and ").." and " or ""));
table.insert(ret, (#tests > 0 and table.concat(tests, " and ") or "true"));
table.insert(ret, ") then\n");
if(#debug > 0) then
table.insert(ret, "print('ret: true');\n");
end
table.insert(ret, "return true else return false end end");
return ret, events;
return table.concat(ret), events;
end
function WeakAuras.GetActiveConditions(id, cloneId)
@@ -4846,6 +4931,37 @@ function WeakAuras.IsAuraLoaded(id)
return Private.loaded[id]
end
function WeakAuras.CreateSpellChecker()
local matcher = {
names = {},
spellIds = {},
AddName = function(self, name)
local spellId = tonumber(name)
if spellId then
name = GetSpellInfo(spellId)
if name then
self.names[name] = true
end
else
self.names[name] = true
end
end,
AddExact = function(self, spellId)
spellId = tonumber(spellId)
self.spellIds[spellId] = true
end,
Check = function(self, spellId)
if spellId then
return self.spellIds[spellId] or self.names[GetSpellInfo(spellId)]
end
end,
CheckName = function(self, name)
return self.names[name]
end
}
return matcher
end
function Private.IconSources(data)
local values = {
[-1] = L["Dynamic Information"],
@@ -4874,6 +4990,28 @@ function WeakAuras.GetTriggerCategoryFor(triggerType)
return prototype and prototype.type
end
function Private.SortOrderForValues(values)
local sortOrder = {}
for key, value in pairs(values) do
tinsert(sortOrder, key)
end
table.sort(sortOrder, function(aKey, bKey)
local aValue = values[aKey]
local bValue = values[bKey]
if aValue:sub(1, #WeakAuras.newFeatureString) == WeakAuras.newFeatureString then
aValue = aValue:sub(#WeakAuras.newFeatureString + 1)
end
if bValue:sub(1, #WeakAuras.newFeatureString) == WeakAuras.newFeatureString then
bValue = bValue:sub(#WeakAuras.newFeatureString + 1)
end
return aValue < bValue
end)
return sortOrder
end
do
local function shouldInclude(data, includeGroups, includeLeafs)
if data.controlledChildren then