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.
251 lines
6.6 KiB
251 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
|
|
|