You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

252 lines
6.6 KiB

--[[
Director.lua
@Author : DengSir (tdaddon@163.com)
@Link : https://dengsir.github.io
]]
local ns = select(2, ...)
local Util = ns.Util
local Addon = ns.Addon
local Action = ns.Action
local L = ns.L
local Condition = ns.Condition
local Director = Addon:NewModule('Director', 'AceEvent-3.0')
local GUI = LibStub('tdGUI-1.0')
function Director:OnInitialize()
self:RegisterEvent('PET_BATTLE_CLOSE', 'ClearScript')
end
function Director:Run()
if not self.script then
return
end
self:Action(self.script:GetScript())
end
function Director:Debug(script)
self:Action(script)
end
function Director:Test(item)
if type(item) == 'table' then
for i, v in ipairs(item) do
if Condition:Run(v[2]) and self:Test(v[1]) then
return true
end
end
elseif type(item) == 'string' then
if Action:Test(item) then
GUI:Notify {
text = format('%s\n|cff00ffff%s: |cffffff00%s|r', L.ADDON_NAME, L.DIRECTOR_TEST_NEXT_ACTION, item or "-"),
icon = ns.ICON,
help = ''
}
return true
end
end
end
function Director:Action(item)
if type(item) == 'table' then
for i, v in ipairs(item) do
if Condition:Run(v[2]) and self:Action(v[1]) then
return true
end
end
elseif type(item) == 'string' then
if Action:Run(item) then
return true
end
else
error('No item')
end
end
local function FormatMacro(tab, action, condition)
if not condition then
return tab .. action
elseif type(condition) == 'string' then
return format('%s%s [ %s ]', tab, action, condition)
else
return format('%s%s [ %s ]', tab, action, table.concat(condition, ' & '))
end
end
local function mergeCondition(dest, ...)
for i = 1, select('#', ...) do
local v = select(i, ...)
if type(v) == 'table' then
mergeCondition(dest, unpack(v))
elseif type(v) == 'string' then
v = v:trim()
if v ~= '' then
tinsert(dest, v)
end
end
end
return dest
end
local function MergeCondition(...)
local condition = mergeCondition({}, ...)
if #condition > 1 then
return condition
elseif #condition == 1 then
return condition[1]
else
return nil
end
end
local function CheckCondition(item)
if type(item) == 'string' then
return Condition:ParseCondition(item)
elseif type(item) == 'table' then
for i, v in ipairs(item) do
CheckCondition(v)
end
elseif not item then
return
else
Util.assert(false, 'Invalid Script (Struct Error)')
end
end
local function CheckAction(item)
if type(item) == 'string' then
return Action:ParseAction(item)
elseif type(item) == 'table' then
for i, v in ipairs(item) do
CheckCondition(v[2])
CheckAction(v[1])
end
else
Util.assert(false, 'Invalid Script (Struct Error)')
end
end
function Director:Check(item)
return pcall(CheckAction, item)
end
function Director:BuildScript(code)
if type(code) ~= 'string' then
return nil, 'No code'
end
local script = {}
local stack = ns.Stack:New()
stack:Push(script)
for line in code:gmatch('[^\r\n]+') do
line = line:trim()
if line ~= '' then
local script = stack:Top()
local action, condition do
if line:find('^%-%-') then
action = line
elseif line:find('[', nil, true) then
action, condition = line:match('^/?([^%[]-)%s*%[([^%]]+)%]$')
else
action = line:match('^/?(.+)$')
end
end
if not action then
return nil, format('Invalid Line: `%s`', line)
end
if condition then
condition = MergeCondition({strsplit('&', condition)})
end
if action == 'if' then
stack:Push({})
tinsert(script, {stack:Top(), condition})
elseif action == 'endif' or action == 'ei' then
stack:Pop()
local parent = stack:Top()
if not parent then
return nil, 'Invalid Script: if endif unpaired'
end
if #script == 1 then
local dest = parent[#parent]
local item = script[1]
dest[1] = item[1]
dest[2] = MergeCondition(dest[2], item[2])
end
else
tinsert(script, {action, condition})
end
end
end
if stack:Top() ~= script then
return nil, 'Invalid Script: if endif unpaired'
end
local ok, err = Director:Check(script)
if not ok then
return nil, err
end
return script
end
function Director:BeautyScript(script, deep)
deep = deep or 0
local tab = strrep(' ', deep * 4)
local sb = {}
for i, v in ipairs(script) do
local condition = MergeCondition(v[2])
local action = v[1]
if type(action) == 'string' then
tinsert(sb, FormatMacro(tab, action, condition))
elseif type(action) == 'table' then
tinsert(sb, FormatMacro(tab, 'if', condition))
tinsert(sb, self:BeautyScript(action, deep + 1))
tinsert(sb, FormatMacro(tab, 'endif'))
end
end
return table.concat(sb, '\n')
end
local function errorhandler(err)
return geterrorhandler()(err)
end
function Director:Select()
local list = {}
for name, plugin in Addon:IterateEnabledPlugins() do
local ok, key = xpcall(function() return plugin:GetCurrentKey() end, errorhandler)
local script = ok and key and plugin:GetScript(key)
if script then
tinsert(list, script)
end
end
return list
end
function Director:SetScript(script)
self.script = script
self:SendMessage('PET_BATTLE_SCRIPT_SCRIPT_UPDATE')
end
function Director:GetScript()
return self.script
end
function Director:ClearScript()
self.script = nil
self:SendMessage('PET_BATTLE_SCRIPT_SCRIPT_UPDATE')
end