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.

183 lines
5.8 KiB

local MAJ, _, T = 1, ...
if T.SkipLocalActionBook then return end
if T.TenEnv then T.TenEnv() end
local EV, AB, KR, RW = T.Evie, T.ActionBook:compatible(2,36), T.ActionBook:compatible("Kindred", 1,22), T.ActionBook:compatible("Rewire", 1, 31)
assert(EV and AB and KR and RW and 1, "Incompatible library bundle")
local FM, core, cenv = {}, CreateFrame("Frame", nil, nil, "SecureHandlerBaseTemplate")
core:SetFrameRef("KR", KR:seclib())
core:SetAttribute("seed", math.random(2^24)-1)
core:Execute([=[-- ActionBook:flag-init
KR, flags, cargcache, crand, crm = self:GetFrameRef('KR'), newtable(), newtable(), newtable(self:GetAttribute('seed')), 2^24
]=])
cenv = GetManagedEnvironment(core)
core:SetAttribute("RunSlashCmd", [=[-- flag-RunSlashCmd
local slash, clause, target = ...
local flag, nv
if (clause or "") == "" then
elseif slash == "/setflag" then
local name, eq, v = clause:match("^%s*([^=<%s/]+)%s*(=?)%s*(.-)%s*$")
if name then
flag, nv = name:lower(), eq == "" and "1" or (v ~= "" and v ~= "0" and v:lower()) or nil
end
elseif slash == "/cycleflag" then
local name, eq, top, step = clause:match("^%s*([^=<%s/]+)%s*([<=]?)%s*(%d*)%+?(%-?%d*)%s*$")
if name and (top == "") == (eq == "")then
flag, top, step = name:lower(), top ~= "" and 0+top or 2, step ~= "" and 0+step or 1
nv = ((tonumber(flags[flag]) or 0) + step) % top
nv = nv > 0 and nv .. "" or nil
end
elseif slash == "/randflag" then
local name, top = clause:match("^%s*([^=<%s/]+)%s*<%s*(%d*)%s*$")
if not name then return end
nv, flag, top = 0, name:lower(), top ~= "" and 0+top or 2
if top > 1 then
local cr = crand[clause]
local rv = ((cr or crand[1]) * 12616645 + 16777213) % crm
if cr == nil then
crand[1] = rv
end
crand[clause], nv = rv, rv % top
end
nv = nv ~= 0 and nv .. "" or nil
end
if flag ~= nil and flags[flag] ~= nv then
flags[flag] = nv
if KR then
KR:RunAttribute("PokeConditional", "flag")
end
end
]=])
core:SetAttribute("EvaluateMacroConditional", [=[-- flag-EvaluateMacroConditional
local name, cv, target = ...
if name ~= "flag" or not cv then return end
local ca, ni = cargcache[cv]
if not ca then
ca, ni = newtable(), 1
for s in cv:gmatch("[^/]*") do
local name, eq, v = s:match("^%s*([^=%s]+)%s*(=?)%s*(.-)%s*$")
if name then
name, v = name:lower(), v:lower()
if eq == "=" then
ca[ni], ca[ni+1], ni = name, v ~= "0" and v, ni + 2
else
ca[ni], ca[ni+1], ni = name, false, ni + 2
end
end
end
cargcache[cv] = ca
end
for i=1, #ca, 2 do
local cv, dv = flags[ca[i]], ca[i+1]
if cv == dv or (dv == false and cv) then
return true
end
end
return false
]=])
local flagHint, flagCommandHint do
local currentFutureID
local function newSpeculativeProxy(base)
local ov, ot = {}, {}
local function getValue(_, k)
if currentFutureID and ot[k] == currentFutureID then
return ov[k]
end
return base[k]
end
local function setValue(_, k, nv)
if k ~= nil and currentFutureID then
ov[k], ot[k] = nv, currentFutureID
end
end
return setmetatable({}, {__index=getValue, __newindex=setValue})
end
local flagProxy, crandProxy = newSpeculativeProxy(cenv.flags), newSpeculativeProxy(cenv.crand)
local flagHintI = loadstring(("local cargcache, flags, newtable = ... return function(...) %s end"):format(core:GetAttribute("EvaluateMacroConditional")))({}, flagProxy, function() return {} end)
local runSlashI = loadstring(("local KR, flags, crand, crm = false, ... return function(...) %s end"):format(core:GetAttribute("RunSlashCmd")))(flagProxy, crandProxy, cenv.crm)
function flagHint(...)
local _; _, _, _, _, currentFutureID = ...
return flagHintI(...)
end
function flagCommandHint(slash, _, args2, target, _, _, _, speculationID)
currentFutureID = speculationID
runSlashI(slash, args2, target)
end
end
local pendingRestoreState, DoRestoreState = nil do
local function RestoreFlags(flags)
if type(flags) ~= "table" then
return
end
for k,v in pairs(flags) do
if type(k) == 'string' and type(v) == 'string' then
core:SetAttribute('setflag-name', k)
core:SetAttribute('setflag-value', v)
core:Execute([[-- flag-RestoreFlagState
flags[self:GetAttribute('setflag-name')] = self:GetAttribute('setflag-value')
]])
end
end
core:SetAttribute('setflag-name', nil)
core:SetAttribute('setflag-value', nil)
end
local function RestoreReVars(revars)
if type(revars) ~= "table" then
return
end
for k,v in pairs(revars) do
if type(k) == 'string' and type(v) == 'string' then
RW:SetMacroVarValue(k, v)
end
end
end
function DoRestoreState(state)
pendingRestoreState = 0
RestoreFlags(state.flags)
RestoreReVars(state.revars)
end
end
function FM:GetState()
local rf, rv = {}, {}
for f, v in rtable.pairs(cenv.flags) do
rf[f] = v
end
for n, v in RW:AllMacroVars() do
rv[n] = v ~= "" and v or nil
end
rf, rv = next(rf) ~= nil and rf or nil, next(rv) ~= nil and rv or nil
return (rf or rv) and {flags=rf, revars=rv, at=GetServerTime()} or nil
end
function FM:RestoreState(state)
if type(state) == "table" and pendingRestoreState == nil and
(type(state.flags) == "table" or type(state.revars) == "table") then
pendingRestoreState = state
if InCombatLockdown() then
EV.PLAYER_REGEN_ENABLED = function()
DoRestoreState(pendingRestoreState)
return "remove"
end
else
DoRestoreState(state)
end
end
end
KR:SetSecureExternalConditional("flag", core, flagHint)
RW:RegisterCommand("/setflag", true, true, core)
RW:RegisterCommand("/cycleflag", true, true, core)
RW:RegisterCommand("/randflag", true, true, core)
RW:SetCommandHint("/setflag", 9e9, flagCommandHint)
RW:SetCommandHint("/cycleflag", 9e9, flagCommandHint)
RW:SetCommandHint("/randflag", 9e9, flagCommandHint)
function FM:compatible(maj)
return MAJ == maj and FM or nil
end
AB:RegisterModule("FlagMast", FM)