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.
1712 lines
73 KiB
1712 lines
73 KiB
local MAJ, REV, COMPAT, ADDON, T, ORI = 4, 128, select(4,GetBuildInfo()), ...
|
|
local MODERN, CF_WRATH = COMPAT >= 10e4, COMPAT < 10e4 and COMPAT >= 3e4
|
|
local EV, api, private = T.Evie, {ActionBook=T.ActionBook}, {}
|
|
local OR_Rings, OR_LoadedState, OR_ModifierLockState = {}, 1, nil
|
|
local defaultConfig = {
|
|
InteractionMode=1, ClickPriority=true, CloseOnRelease=false, NoClose=false, NoCloseOnSlice=false, ReOpenAction=2,
|
|
IndicationOffsetX=0, IndicationOffsetY=0, RingAtMouse=false, RingScale=1,
|
|
CenterAction=false, MotionAction=false, QuickActionOnRelease=true,
|
|
MouseBucket=1,
|
|
UseDefaultBindings=true, PrimaryButton="BUTTON4", SecondaryButton="BUTTON5",
|
|
SliceBinding=false, SliceBindingString="1 2 3 4 5 6 7 8 9 0",
|
|
OpenNestedRingButton="BUTTON3", ScrollNestedRingUpButton="MOUSEWHEELUP", ScrollNestedRingDownButton="MOUSEWHEELDOWN", SelectedSliceBind="",
|
|
OpenNestedRingButton2="", ScrollNestedRingUpButton2="", ScrollNestedRingDownButton2="", SelectedSliceBind2="",
|
|
PadSupportMode="none", PSOpenSwitchMode=1, PSRestoreOnClose=true, PSThawHold=0.75, PSThawDuration=4,
|
|
}
|
|
local PADSUPPORT_MODE_MAP = {freelook1="freelook"}
|
|
local configRoot, configInstance, activeProfile, PersistentStorageInfo, optionValidators, optionsMeta = {CharProfiles={}, ProfileStorage={}, PersistentStorage={}}, nil, nil, {}, {}, {__index=defaultConfig}
|
|
local charId, internalFreeId = ("%s-%s"):format(GetRealmName(), UnitName("player")), 424
|
|
local svMigrationState = 0 -- DEPRECATED[2211/Y1]
|
|
local TB_THRESH
|
|
|
|
local L do
|
|
local TL = T.L or {}
|
|
T.L = newproxy(true)
|
|
L, getmetatable(T.L).__call = T.L, function(_,k,t) return TL[k] or t or k end
|
|
end
|
|
if T.PackTag ~= "smile" then
|
|
return C_Timer.After(1, function()
|
|
print("|cffe82020" .. L"Restart World of Warcraft. If this message continues to appear, delete and re-install OPie.")
|
|
end)
|
|
end
|
|
|
|
local AB, KR, RW = T.ActionBook:compatible(2,42), T.ActionBook:compatible("Kindred", 1,11), T.ActionBook:compatible("Rewire",1,31) do
|
|
local function createRingAction(name)
|
|
local ringInfo = type(name) == "string" and OR_Rings[name]
|
|
return ringInfo and ringInfo.action or nil
|
|
end
|
|
local function describeRingAction(name)
|
|
return L"OPie Ring", OR_Rings[name] and OR_Rings[name].name or name, [[Interface\AddOns\OPie\gfx\opie_ring_icon]], nil, nil, nil, "collection"
|
|
end
|
|
AB:RegisterActionType("ring", createRingAction, describeRingAction, 1)
|
|
AB:AugmentCategory(L"OPie rings", function(_, add)
|
|
for i=1,#OR_Rings do
|
|
add("ring", OR_Rings[i])
|
|
end
|
|
end)
|
|
end
|
|
|
|
local function assert(condition, text, level, ...)
|
|
return condition or error(tostring(text):format(...), 1 + (level or 1))((0)[0])
|
|
end
|
|
local function copy(t, copies, into)
|
|
copies, into = copies or {}, into or {}
|
|
copies[t] = into
|
|
for k,v in pairs(t) do
|
|
k = type(k) == "table" and (copies[k] or copy(k, copies)) or k
|
|
v = type(v) == "table" and (copies[v] or copy(v, copies)) or v
|
|
into[k] = v
|
|
end
|
|
return into
|
|
end
|
|
local function tostringf(b)
|
|
return b and "true" or "nil"
|
|
end
|
|
local function getSpecCharIdent(specIdx)
|
|
local tg = specIdx or MODERN and GetSpecialization() or CF_WRATH and GetActiveTalentGroup() or 1
|
|
return (tg == 1 and "%s" or "%s-%d"):format(charId, tg)
|
|
end
|
|
local function getNumSpecs()
|
|
return MODERN and GetNumSpecializations() or CF_WRATH and 2 or 1
|
|
end
|
|
local function getProfile(k)
|
|
k = configRoot.ProfileStorage[k] and k or "default"
|
|
return k, configRoot.ProfileStorage[k]
|
|
end
|
|
local function getProfileForSpec(specIdx)
|
|
return getProfile(configRoot.CharProfiles[getSpecCharIdent(specIdx)])
|
|
end
|
|
local function normalizeStoredProfileIdent(ident)
|
|
return ident ~= "default" and ident or nil
|
|
end
|
|
local safequote do
|
|
local r = {u="\\117", ["{"]="\\123", ["}"]="\\125"}
|
|
function safequote(s)
|
|
return (("%q"):format(s):gsub("[{}u]", r))
|
|
end
|
|
end
|
|
local function getTimeBand(a,b, c,d)
|
|
local t, p = GetServerTime()
|
|
if t >= d then
|
|
return 2
|
|
elseif DEV_EARLY_WARNING or t >= b and t <= c then
|
|
return 1
|
|
elseif t <= a then
|
|
return 0
|
|
end
|
|
TB_THRESH = TB_THRESH or math.random(127)/128
|
|
p = t > c and (t-c)/(d-c) or ((t-a)/(b-a))
|
|
return (t > b and 1 or 0) + (p^3 > TB_THRESH and 1 or 0)
|
|
end
|
|
|
|
-- Here be (Secure) Dragons
|
|
local OR_SecCore = CreateFrame("Button", ("OPieRT-%08x-%04x"):format(time() % 2^30, math.random(2^16)-1), UIParent, "SecureActionButtonTemplate,SecureHandlerAttributeTemplate,SecureHandlerMouseWheelTemplate")
|
|
local OR_OpenProxy = CreateFrame("Button", "ORLOpen", nil, "SecureActionButtonTemplate")
|
|
local OR_SecEnv, OR_ActiveRingName, OR_ActiveCollectionID, OR_ActiveSliceCount
|
|
local OR_PadRestoreState = {}
|
|
OR_SecCore:SetSize(2^15, 2^15)
|
|
OR_SecCore:SetFrameStrata("FULLSCREEN_DIALOG")
|
|
OR_SecCore:RegisterForClicks("AnyUp", "AnyDown")
|
|
OR_SecCore:EnableMouseWheel(true)
|
|
OR_SecCore:Hide()
|
|
OR_SecCore:SetFrameRef("AB", AB:seclib())
|
|
OR_SecCore:SetFrameRef("KR", KR:seclib())
|
|
OR_SecCore:SetFrameRef("RW", RW:seclib())
|
|
local OR_DeferExecute do
|
|
local queue, qc, qpos = {}, 0
|
|
function OR_DeferExecute(block, ...)
|
|
if block then queue[qc+1], qc = block:format(...), qc + 1 end
|
|
if InCombatLockdown() or qpos then return end
|
|
qpos = 1 while qpos <= qc do
|
|
OR_SecCore:Execute(queue[qpos])
|
|
qpos = qpos + 1
|
|
end
|
|
qc, qpos = 0
|
|
end
|
|
end
|
|
do -- Click dispatcher
|
|
local lowCH = CreateFrame("Button", ("OPieLBC-%08x-%04x"):format(time() % 2^30, math.random(2^16)-1), nil, "SecureActionButtonTemplate")
|
|
lowCH:SetFrameStrata("BACKGROUND")
|
|
lowCH:SetFrameLevel(1)
|
|
lowCH:RegisterForClicks("AnyDown", "AnyUp")
|
|
lowCH:SetAllPoints()
|
|
lowCH:Hide()
|
|
OR_SecCore:WrapScript(lowCH, "OnClick", "return owner:RunFor(self, ORL_OnClick, button, down)", "owner:RunFor(self, ORL_PostClick, message)")
|
|
local bindProxy = CreateFrame("Frame", "ORL_BindProxy", nil, "SecureFrameTemplate")
|
|
OR_SecCore:SetFrameRef("bindProxy", bindProxy)
|
|
OR_SecCore:SetFrameRef("sliceBindProxy", CreateFrame("Frame", "ORL_BindProxySlice", nil, "SecureFrameTemplate"))
|
|
OR_SecCore:SetFrameRef("overBindProxy", CreateFrame("Frame", "ORL_BindProxyOverride", nil, "SecureFrameTemplate"))
|
|
OR_SecCore:SetFrameRef("lowCH", lowCH)
|
|
OR_SecCore:SetFrameRef("screen", CreateFrame("Frame", nil, nil, "SecureFrameTemplate"))
|
|
local closeProxy = CreateFrame("Button", "CloseOPieRing", nil, "SecureFrameTemplate")
|
|
closeProxy:Hide()
|
|
do -- Motion Trap
|
|
local trap = CreateFrame("Frame", nil, nil, "SecureFrameTemplate")
|
|
trap:SetFrameStrata("TOOLTIP")
|
|
trap:SetAllPoints()
|
|
trap:Hide()
|
|
OR_SecCore:SetFrameRef("motion0", trap)
|
|
OR_SecCore:WrapScript(trap, "OnEnter", "return owner:Run(ORL_SetMotionTrapArmed, false, true)")
|
|
trap:SetMouseClickEnabled(false)
|
|
for i=1,6 do
|
|
local f = CreateFrame("Frame", nil, trap, "SecureFrameTemplate")
|
|
if i < 3 then
|
|
f:SetSize(1/8, 1/8)
|
|
f:SetFrameLevel(9001)
|
|
f:SetMouseMotionEnabled(true)
|
|
else
|
|
OR_SecCore:WrapScript(f, "OnEnter", "return owner:Run(ORL_SetMotionTrapArmed, false, true)")
|
|
end
|
|
f:SetMouseClickEnabled(false)
|
|
OR_SecCore:SetFrameRef("motion" .. i, f)
|
|
end
|
|
end
|
|
OR_SecCore:Execute([=[-- OR_SecCore_Init
|
|
ORL_GlobalOptions, ORL_RingData, ORL_RingDataN = newtable(), newtable(), newtable()
|
|
ORL_KnownCollections, ORL_StoredCA, ORL_OverSAB = newtable(), newtable(), nil
|
|
POL_RUN_ONOPEN_ON_SWITCH, POL_RUN_ONOPEN_ON_REOPEN, POL_JUMP_COUNT_LIMIT = true, true, 50
|
|
collections, ctokens, rotation, rtokens, fcIgnore, rotationMode, emptyTable = newtable(), newtable(), newtable(), newtable(), newtable(), newtable(), newtable()
|
|
modState, modLockState, sizeSq, bindProxy, sliceProxy, overProxy = "", nil, 16*9001^2, self:GetFrameRef("bindProxy"), self:GetFrameRef("sliceBindProxy"), self:GetFrameRef("overBindProxy")
|
|
lowCH = self:GetFrameRef("lowCH")
|
|
sliceBindState, visitedSlices, buttonBindings = newtable(), newtable(), newtable()
|
|
BUTTON_NAMEKEY_MAP = newtable()
|
|
BUTTON_NAMEKEY_MAP.LeftButton, BUTTON_NAMEKEY_MAP.RightButton, BUTTON_NAMEKEY_MAP.MiddleButton = "BUTTON1", "BUTTON2", "BUTTON3"
|
|
BUTTON_NAMEKEY_MAP.Button4, BUTTON_NAMEKEY_MAP.Button5 = "BUTTON4", "BUTTON5"
|
|
AIP_Binding, AIP_Key, AIP_Modifier, AIP_Handler, AIP_VirtualKey, AI_NoPointer = nil
|
|
AIP_Action, AI_LeftAction, AI_RightAction, AIP_ActionFirstUp = nil
|
|
AIP_OnDown, AI_LeftOnDown, AI_RightOnDown = nil
|
|
AI_MotionArmed, AI_MotionArmedFC = false, false
|
|
AI_ClickStack, AI_ClickStackIndex = newtable(), 0
|
|
AB, KR, RW = self:GetFrameRef("AB"), self:GetFrameRef("KR"), self:GetFrameRef("RW")
|
|
SCREEN, CENTERED_CURSOR_YPOS, MOTION_TRAP = self:GetFrameRef("screen"), "0.6", newtable()
|
|
SCREEN:SetAllPoints()
|
|
SCREEN:Hide()
|
|
for i=0,6 do
|
|
MOTION_TRAP[i] = self:GetFrameRef("motion" .. i)
|
|
end
|
|
pmode_RotationModeMap = newtable() do
|
|
local m = pmode_RotationModeMap
|
|
m[20] = "cycle"
|
|
m[36] = "shuffle"
|
|
m[52] = "random"
|
|
m[68] = "reset"
|
|
m[84] = "jump"
|
|
end
|
|
|
|
ORL_SetMotionTrapArmed = [==[-- ORL_SetMotionTrapArmed
|
|
local arm, armFC = ...
|
|
if not arm then
|
|
MOTION_TRAP[0]:Hide()
|
|
AI_MotionArmed, AI_MotionArmedFC = false, false
|
|
if armFC and AI_LeftAction then
|
|
bindProxy:SetBindingClick(true, "BUTTON1", owner, "BUTTON1")
|
|
lowCH:Hide()
|
|
end
|
|
return
|
|
end
|
|
local x, y = SCREEN:GetMousePosition()
|
|
local m, w, h = 0.125, SCREEN:GetWidth(), SCREEN:GetHeight()
|
|
x, y = x*w, y*h
|
|
for i=1,6 do
|
|
MOTION_TRAP[i]:ClearAllPoints()
|
|
end
|
|
MOTION_TRAP[1]:SetPoint("CENTER", SCREEN, "BOTTOM", 0, h * CENTERED_CURSOR_YPOS)
|
|
MOTION_TRAP[2]:SetPoint("CENTER", SCREEN, "BOTTOMLEFT", x, y)
|
|
MOTION_TRAP[3]:SetPoint("TOPLEFT")
|
|
MOTION_TRAP[3]:SetPoint("BOTTOMRIGHT", SCREEN, "BOTTOMLEFT", x-m, 0)
|
|
MOTION_TRAP[4]:SetPoint("TOPLEFT")
|
|
MOTION_TRAP[4]:SetPoint("BOTTOMRIGHT", SCREEN, "BOTTOMRIGHT", 0, y+m)
|
|
MOTION_TRAP[5]:SetPoint("TOPLEFT", SCREEN, "TOPLEFT", x+m, 0)
|
|
MOTION_TRAP[5]:SetPoint("BOTTOMRIGHT")
|
|
MOTION_TRAP[6]:SetPoint("TOPLEFT", SCREEN, "BOTTOMLEFT", 0, y-m)
|
|
MOTION_TRAP[6]:SetPoint("BOTTOMRIGHT")
|
|
MOTION_TRAP[0]:Show()
|
|
AI_MotionArmed, AI_MotionArmedFC = true, armFC == true
|
|
]==]
|
|
ORL_PrepareCollection = [==[-- ORL_PrepareCollection
|
|
wipe(collections) wipe(ctokens)
|
|
local firstFC, root, colData = nil, ..., AB:RunAttribute("GetCollectionContent", ...)
|
|
for cid, i, aid, tok, pmode in (colData or ""):gmatch("\n(%d+) (%d+) (%d+) (%S+) (%d+)") do
|
|
cid, i, aid, pmode = tonumber(cid), tonumber(i), tonumber(aid), tonumber(pmode) or 0
|
|
if not collections[cid] then collections[cid], ctokens[cid] = newtable(), newtable() end
|
|
if pmode > 0 then
|
|
local b01 = pmode % 4
|
|
fcIgnore[tok] = b01 >= 2 or nil
|
|
if pmode % 8 >= 4 then
|
|
rotationMode[tok] = pmode_RotationModeMap[pmode % 128 - b01]
|
|
end
|
|
end
|
|
collections[cid][i], ctokens[cid][i], ctokens[cid][tok], firstFC = aid, tok, i, firstFC or (cid == root and not fcIgnore[tok] and tok)
|
|
end
|
|
collections[root], ctokens[root] = collections[root] or emptyTable, ctokens[root] or emptyTable
|
|
for cid, list in pairs(collections) do
|
|
for i, aid in pairs(list) do
|
|
if collections[aid] then
|
|
local tok = ctokens[cid][i]
|
|
local rMode, rIdx, tIdx = rotationMode[tok]
|
|
local isStoredRotation = rMode ~= "reset" and rMode ~= "jump"
|
|
if isStoredRotation then
|
|
rIdx = ctokens[aid][rtokens[tok]]
|
|
if (rMode == "random" or rMode == "shuffle" and not rIdx) and #ctokens[aid] > 1 then
|
|
tIdx = math.random(#ctokens[aid] - (rIdx and 1 or 0))
|
|
rIdx = rIdx and tIdx >= rIdx and (tIdx + 1) or tIdx
|
|
end
|
|
end
|
|
rotation[tok], rtokens[tok] = rIdx or 1, isStoredRotation and ctokens[aid][rIdx] or rtokens[tok] or nil
|
|
end
|
|
end
|
|
end
|
|
return collections[root][0], firstFC
|
|
]==]
|
|
ORL_CloseActiveRing = [[-- ORL_CloseActiveRing
|
|
local old, shouldKeepOwner, selSliceIndex, selSliceAction = activeRing, ...
|
|
if not shouldKeepOwner then
|
|
owner:Hide()
|
|
end
|
|
bindProxy:ClearBindings()
|
|
sliceProxy:ClearBindings()
|
|
activeRing, openCollection, openCollectionID = nil
|
|
AIP_Action, AIP_ActionFirstUp, AI_LeftAction, AI_RightAction = nil
|
|
AIP_OnDown, AI_LeftOnDown, AI_RightOnDown = nil
|
|
AIP_Binding, AIP_Key, AIP_Modifier, AIP_Handler, AIP_VirtualKey = nil
|
|
self:Run(ORL_SetMotionTrapArmed, false)
|
|
lowCH:Hide()
|
|
owner:CallMethod("NotifyState", "close", old.name, old.action, selSliceIndex, selSliceAction)
|
|
]]
|
|
ORL_RegisterVariations = [[-- ORL_RegisterVariations
|
|
local binding, mapkey, downmix, skipKey = ...
|
|
local bindKey = binding and binding:match("[^-]*.$")
|
|
if skipKey and bindKey == skipKey or (binding or "") == "" then
|
|
return
|
|
end
|
|
if bindKey:match("^BUTTON%d+$") then
|
|
buttonBindings[binding] = mapkey
|
|
end
|
|
for alt=0,downmix:match("ALT") and 1 or 0 do for ctrl=0,downmix:match("CTRL") and 1 or 0 do for shift=0,downmix:match("SHIFT") and 1 or 0 do for meta=0,downmix:match("META") and 1 or 0 do
|
|
self:SetBindingClick(true, (alt == 1 and "ALT-" or "") .. (ctrl == 1 and "CTRL-" or "") .. (shift == 1 and "SHIFT-" or "") .. (meta == 1 and "META-" or "") .. binding, owner, mapkey)
|
|
end end end end
|
|
]]
|
|
ORL_CheckSliceBindings = [==[-- ORL_CheckSliceBindings
|
|
local usedKeys, uncombine, selectedSliceBindings = newtable(), false, newtable()
|
|
selectedSliceBindings[0], selectedSliceBindings[-1] = ...
|
|
for j=0, 1 do
|
|
for i, b in pairs(j == 0 and selectedSliceBindings or
|
|
j > 0 and activeRing.SliceBinding2 or emptyTable) do
|
|
local bk = b and b:match("[^-]*.$")
|
|
local uk = usedKeys[bk]
|
|
if uk then
|
|
uncombine = uncombine or newtable()
|
|
uncombine[uk], uncombine[i] = 1, 1
|
|
else
|
|
usedKeys[bk or 0] = i
|
|
end
|
|
end
|
|
end
|
|
activeRing.SliceBindingUncombine = uncombine
|
|
return uncombine and true
|
|
]==]
|
|
ORL_UpdateInteractionBindings = [==[-- ORL_UpdateInteractionBindings
|
|
local setToVirtualKey = ...
|
|
|
|
local interactKey = AIP_Binding == "-" and "-" or (AIP_Binding or ""):match("[^-]*.$")
|
|
if setToVirtualKey then
|
|
AIP_Binding, AIP_Key, AIP_Modifier, AIP_Handler, AIP_VirtualKey =
|
|
AIP_Binding, interactKey, interactKey ~= AIP_Binding and AIP_Binding or nil, self, setToVirtualKey
|
|
end
|
|
|
|
bindProxy:ClearBindings()
|
|
wipe(buttonBindings)
|
|
wheelBucket = 0
|
|
|
|
local avoidPrimary = AIP_Action and AIP_Action ~= "close" and interactKey or nil
|
|
local down, up, open = ORL_GlobalOptions.ScrollNestedRingDownBinding or "", ORL_GlobalOptions.ScrollNestedRingUpBinding or "", ORL_GlobalOptions.OpenNestedRingBinding or ""
|
|
local down2, up2, open2 = ORL_GlobalOptions.ScrollNestedRingDownBinding2 or "", ORL_GlobalOptions.ScrollNestedRingUpBinding2 or "", ORL_GlobalOptions.OpenNestedRingBinding2 or ""
|
|
owner:RunFor(bindProxy, ORL_RegisterVariations, down, down:match("MOUSEWHEEL") and "mwdownW" or "mwdownK", "ALT-CTRL-SHIFT", avoidPrimary)
|
|
owner:RunFor(bindProxy, ORL_RegisterVariations, down2, down2:match("MOUSEWHEEL") and "mwdownW" or "mwdownK", "ALT-CTRL-SHIFT", avoidPrimary)
|
|
owner:RunFor(bindProxy, ORL_RegisterVariations, up, up:match("MOUSEWHEEL") and "mwupW" or "mwupK", "ALT-CTRL-SHIFT", avoidPrimary)
|
|
owner:RunFor(bindProxy, ORL_RegisterVariations, up2, up2:match("MOUSEWHEEL") and "mwupW" or "mwupK", "ALT-CTRL-SHIFT", avoidPrimary)
|
|
owner:RunFor(bindProxy, ORL_RegisterVariations, open, "mwin", "ALT-CTRL-SHIFT", avoidPrimary)
|
|
owner:RunFor(bindProxy, ORL_RegisterVariations, open2, "mwin", "ALT-CTRL-SHIFT", avoidPrimary)
|
|
|
|
sliceProxy:ClearBindings()
|
|
wipe(sliceBindState)
|
|
local sliceBindings = activeRing.SliceBinding or false
|
|
local selectedSliceBinding, selectedSliceBinding2 = activeRing.SelectedSliceBind or "", activeRing.SelectedSliceBind2 or ""
|
|
if sliceBindings or selectedSliceBinding ~= "" or selectedSliceBinding2 ~= "" then
|
|
local prefix = AIP_Action and AIP_Binding and AIP_Binding:match("^(.-)[^-]*%-?$") or ""
|
|
local uncombine = prefix ~= "" and (sliceBindings or selectedSliceBinding ~= "" and selectedSliceBinding2 ~= "") and activeRing.SliceBindingUncombine
|
|
uncombine = uncombine == nil and owner:Run(ORL_CheckSliceBindings, selectedSliceBinding, selectedSliceBinding2) and activeRing.SliceBindingUncombine or uncombine
|
|
for i=1, sliceBindings and #openCollection or 0 do
|
|
owner:RunFor(sliceProxy, ORL_RegisterVariations, sliceBindings[i], "slice" .. i, not (uncombine and uncombine[i]) and prefix or "")
|
|
owner:RunFor(sliceProxy, ORL_RegisterVariations, sliceBindings[i+0.5], "slice" .. i, not (uncombine and uncombine[i+0.5]) and prefix or "")
|
|
end
|
|
owner:RunFor(sliceProxy, ORL_RegisterVariations, selectedSliceBinding, "usenow", not (uncombine and uncombine[0]) and prefix or "")
|
|
owner:RunFor(sliceProxy, ORL_RegisterVariations, selectedSliceBinding2, "usenow", not (uncombine and uncombine[-1]) and prefix or "")
|
|
end
|
|
|
|
if AIP_Binding and AIP_Action then
|
|
bindProxy:SetBindingClick(true, AIP_Binding, AIP_Handler, AIP_VirtualKey)
|
|
end
|
|
lowCH[AI_LeftAction and "Show" or "Hide"](lowCH)
|
|
if AI_RightAction and AIP_Binding ~= "BUTTON2" and not AI_NoPointer then
|
|
bindProxy:SetBindingClick(true, "BUTTON2", owner, "BUTTON2")
|
|
end
|
|
bindProxy:SetBindingClick(true, "ESCAPE", owner, "close")
|
|
]==]
|
|
ORL_OpenRing = [==[-- ORL_OpenRing
|
|
local ring, ringID, openCause, ocIndex = ORL_RingData[...], ...
|
|
local fastSwitch, cid, setToVirtualKey = activeRing == ring and ring.ReOpenAction, ring.action
|
|
if fastSwitch == 2 or fastSwitch == 1 and openCollectionID == cid then
|
|
return false, owner:Run(ORL_CloseActiveRing)
|
|
elseif activeRing and not fastSwitch then
|
|
-- If the click-capturing overlay gets the binding DOWN event, only *it* will be notified of the corresponding UP.
|
|
owner:Run(ORL_CloseActiveRing, self == owner)
|
|
end
|
|
local blockOnOpenAction
|
|
AI_NoPointer = ring.NoPointer
|
|
if openCause == "primary-binding" then
|
|
AIP_Binding, setToVirtualKey = ring[ocIndex == 2 and "bind2" or "bind"], "r" .. ringID .. (ocIndex == 2 and "b" or "")
|
|
AIP_Action, AIP_ActionFirstUp = ring.PrimaryAction, ring.PrimaryActionUp
|
|
AI_LeftAction, AI_RightAction = ring.LeftAction, ring.RightAction
|
|
AIP_OnDown, AI_LeftOnDown, AI_RightOnDown = AIP_Action == "close", false, true
|
|
elseif openCause == "override-binding" then
|
|
AIP_Binding, setToVirtualKey = bindOverrides[ringID], "ro" .. ringID
|
|
AIP_Action, AIP_ActionFirstUp, AI_LeftAction, AI_RightAction = "use", nil, nil, "close"
|
|
AIP_OnDown, AI_LeftOnDown, AI_RightOnDown, AI_NoPointer = false, false, true, false
|
|
elseif openCause == "open-macro" then
|
|
AIP_Binding, AIP_Key, AIP_Modifier, AIP_Handler, AIP_VirtualKey = nil
|
|
AIP_Action, AIP_ActionFirstUp = nil, nil
|
|
AI_LeftAction, AI_RightAction = ring.LeftAction2, ring.RightAction2
|
|
AIP_OnDown, AI_LeftOnDown, AI_RightOnDown = false, false, true
|
|
blockOnOpenAction = AB:GetAttribute("PY")
|
|
end
|
|
if AI_NoPointer then
|
|
AI_LeftOnDown, AI_RightAction = nil
|
|
end
|
|
local stickyPrimaryMods = AIP_Action and AIP_Action ~= "close"
|
|
modLockState, modState = KR:RunAttribute("ComputeConditionalLock", stickyPrimaryMods and AIP_Binding or "", "1", (not stickyPrimaryMods and "") or nil)
|
|
|
|
local openAction, firstFC = owner:Run(ORL_PrepareCollection, cid)
|
|
activeRing, openCollection, openCollectionID = ring, collections[cid], cid
|
|
if ORL_StoredCA[ring.name] and not ring.fcToken then
|
|
ring.fcToken, ORL_StoredCA[ring.name] = ORL_StoredCA[ring.name]
|
|
end
|
|
fastClick = (ring.CenterAction or ring.MotionAction) and ((not fcIgnore[ring.fcToken] and ctokens[cid][ring.fcToken]) or (ring.OpprotunisticCA and ctokens[cid][firstFC])) or nil
|
|
fastClick = not AI_NoPointer and fastClick ~= 0 and fastClick or nil
|
|
|
|
owner:SetScale(ring.scale)
|
|
owner:SetPoint('CENTER', ring.ofs, ring.ofsx/owner:GetEffectiveScale(), ring.ofsy/owner:GetEffectiveScale())
|
|
owner:RunFor(self, ORL_UpdateInteractionBindings, setToVirtualKey)
|
|
if ring.ClickPriority and not AI_NoPointer then
|
|
owner:Show()
|
|
lowCH:Hide()
|
|
end
|
|
|
|
local armMotionFC = fastClick and ring.MotionAction and true
|
|
local needMotionTrap = armMotionFC or AI_LeftAction or AI_MotionArmed
|
|
owner:CallMethod("NotifyState", "open", ring.name, ring.action, fastClick, fastSwitch and true, modLockState)
|
|
owner:Run(ORL_SetMotionTrapArmed, needMotionTrap, armMotionFC)
|
|
if openCollection[0] and (POL_RUN_ONOPEN_ON_REOPEN or not fastSwitch) then
|
|
if blockOnOpenAction then
|
|
owner:CallMethod("NoOnOpenForMacros")
|
|
else
|
|
return owner:RunFor(self, ORL_PerformSliceAction, 0, false, true, "on-open")
|
|
end
|
|
end
|
|
return false
|
|
]==]
|
|
ORL_SwitchRing = [==[-- ORL_SwitchRing
|
|
local colID, switchCause = ...
|
|
local ringID = ORL_KnownCollections[colID]
|
|
local col, ring = collections[colID], ORL_RingData[ringID]
|
|
if not col then
|
|
owner:CallMethod("throw", "Cannot switch to unknown collection " .. tostring(colID))
|
|
return false
|
|
end
|
|
local ring2 = ring or activeRing -- non-ring collections inherit preferences
|
|
if switchCause == "switch-binding" then
|
|
elseif switchCause == "jump-slice-release" or switchCause == "jump-slice-used" or switchCause == "jump-slice-switch" then
|
|
AI_NoPointer = ring2.NoPointer -- override binding could have deferred this
|
|
AIP_Binding, AIP_Key, AIP_Modifier, AIP_Handler, AIP_VirtualKey = nil
|
|
AIP_Action, AIP_ActionFirstUp = (AIP_Action or "noop") ~= "noop" and "close" or nil, nil
|
|
AI_LeftAction, AI_RightAction = ring2.LeftAction2, ring2.RightAction2
|
|
if AI_NoPointer then
|
|
AI_LeftAction, AI_RightAction = nil, nil
|
|
end
|
|
AIP_OnDown, AI_LeftOnDown, AI_RightOnDown = true, false, true
|
|
modLockState, modState = nil, ""
|
|
end
|
|
|
|
activeRing = ORL_RingData[ringID] or activeRing
|
|
openCollection, openCollectionID, fastClick = col, colID, nil
|
|
owner:RunFor(self, ORL_UpdateInteractionBindings, nil)
|
|
owner:CallMethod("NotifyState", "switch", ring and ring.name, openCollectionID, fastClick, true, modLockState)
|
|
if openCollection[0] and POL_RUN_ONOPEN_ON_SWITCH and (ORL_JumpCount or 0) < POL_JUMP_COUNT_LIMIT then
|
|
return owner:RunFor(self, ORL_PerformSliceAction, 0, false, true, "switch-onopen")
|
|
end
|
|
return false
|
|
]==]
|
|
ORL_GetCursorSlice = [[-- ORL_GetCursorSlice
|
|
if not openCollection[1] or AI_NoPointer then return nil end
|
|
local psm = IsGamePadEnabled and IsGamePadEnabled() and ORL_GlobalOptions.PadSupportMode
|
|
if psm == "freelook" and SCREEN:GetMousePosition() == 0.5 then
|
|
local ms = GetGamePadState()
|
|
local st = ms and ms.sticks
|
|
st = st and st[ORL_GlobalOptions.PSStickIndex]
|
|
if st then
|
|
local radius, angle = 10000*st.len, math.deg(math.atan2(st.y, st.x))
|
|
if radius > 2500 then
|
|
local segAngle = 360/#openCollection
|
|
return floor(((90 - angle + segAngle/2 - activeRing.ofsDeg) % 360) / segAngle) + 1, false
|
|
elseif radius < 100 then
|
|
return fastClick, true
|
|
else
|
|
return nil, false
|
|
end
|
|
end
|
|
end
|
|
if AI_MotionArmedFC then
|
|
return fastClick, true
|
|
end
|
|
local x, y = owner:GetMousePosition()
|
|
x, y = x - 0.5, y - 0.5
|
|
local radius, segAngle = (x*x*sizeSq + y*y*sizeSq)^0.5, 360/#openCollection
|
|
if radius >= 40 then
|
|
return floor(((math.deg(math.atan2(x, y)) + segAngle/2 - activeRing.ofsDeg) % 360) / segAngle) + 1, false
|
|
elseif radius <= 20 and activeRing.CenterAction then
|
|
return fastClick, true
|
|
end
|
|
]]
|
|
ORL_ResolveNestedSlice = [==[-- ORL_ResolveNestedSlice
|
|
local visitedSlices, col, index, willPerform = wipe(visitedSlices), ...
|
|
while 1 do
|
|
local aid, ct = collections[col] and collections[col][index], ctokens[col] and ctokens[col][index]
|
|
if visitedSlices[ct] or not aid then
|
|
return
|
|
elseif not collections[aid] then
|
|
if willPerform then
|
|
for ict, icol in pairs(visitedSlices) do
|
|
local ort = rtokens[ict]
|
|
local irMode, ctk = rotationMode[ict], ctokens[icol]
|
|
if #ctk <= 1 then
|
|
elseif irMode == "cycle" then
|
|
local nid = (rotation[ict] or 0) % #ctk + 1
|
|
rotation[ict], rtokens[ict] = nid, ctk[nid]
|
|
elseif irMode == "shuffle" then
|
|
local oid = rotation[ict]
|
|
local nid = math.random(#ctk - (oid and 1 or 0))
|
|
nid = nid + (oid and nid >= oid and 1 or 0)
|
|
rotation[ict], rtokens[ict] = nid, ctk[nid]
|
|
end
|
|
end
|
|
end
|
|
return aid, "act"
|
|
elseif rotationMode[ct] == "jump" then
|
|
return aid, "jump"
|
|
end
|
|
visitedSlices[ct], col, index = aid, aid, rotation[ct] or 1
|
|
end
|
|
]==]
|
|
ORL_PerformSliceAction = [[-- ORL_PerformSliceAction
|
|
local pureSlice, shouldUpdateFastClick, noClose, interactionSource = ...
|
|
local pureToken = ctokens[openCollectionID][pureSlice]
|
|
local action, at = owner:Run(ORL_ResolveNestedSlice, openCollectionID, pureSlice, true)
|
|
activeRing.fcToken = shouldUpdateFastClick and (activeRing.CenterAction or activeRing.MotionAction) and not fcIgnore[pureToken] and pureToken or activeRing.fcToken
|
|
if at == "jump" then
|
|
ORL_JumpCount = (ORL_JumpCount or 0) + 1
|
|
return owner:RunFor(self, ORL_SwitchRing, action, interactionSource == "primary-binding" and "jump-slice-release" or "jump-slice-used")
|
|
end
|
|
if not noClose then
|
|
owner:Run(ORL_CloseActiveRing, nil, pureSlice, action)
|
|
end
|
|
if action then
|
|
local w, p, s = ORL_OverSAB or self, AI_ClickStackIndex + 1
|
|
s = AI_ClickStack[p] or newtable()
|
|
local mt, checkExecID, notifyToken = AB:RunAttribute("UseAction", action, modLockState)
|
|
w:SetAttribute("pressAndHoldAction", 1)
|
|
w:SetAttribute("type", "macro")
|
|
w:SetAttribute("typerelease", "macro")
|
|
w:SetAttribute("macrotext", mt)
|
|
s[1], s[2], s[3] = checkExecID and AB:GetAttribute("execID") or -2, RW:GetAttribute("execPending"), notifyToken
|
|
AI_ClickStack[p], AI_ClickStackIndex = s, p
|
|
return action, AI_ClickStackIndex
|
|
end
|
|
return false
|
|
]]
|
|
ORL_OnWheel = [==[-- ORL_OnWheel
|
|
local slice = owner:Run(ORL_GetCursorSlice)
|
|
local nestedCol = collections[openCollection[slice]]
|
|
local rMode = rotationMode[(ctokens[openCollectionID] or emptyTable)[slice]]
|
|
if not (slice and nestedCol and rMode ~= "jump") then return end
|
|
if slice ~= wheelSlice then wheelSlice, wheelBucket = slice, 0 end
|
|
wheelBucket = wheelBucket + (...)
|
|
if abs(wheelBucket) >= activeRing.bucket then wheelBucket = 0 else return end
|
|
local stoken, step, count, c = ctokens[openCollectionID][slice], (...) > 0 and 0 or -2, #nestedCol, 0
|
|
repeat
|
|
rotation[stoken], c = (rotation[stoken] + step) % count + 1, c + 1
|
|
until owner:Run(ORL_ResolveNestedSlice, openCollectionID, slice, false) or c == count
|
|
rtokens[stoken] = (ctokens[openCollection[slice]] or emptyTable)[rotation[stoken]] or rtokens[stoken]
|
|
]==]
|
|
ORL_CheckButtonBindings = [==[-- ORL_CheckButtonBindings
|
|
local lvalue, lkey, BUTTON, checkRingBindings = 0, nil, ...
|
|
for bind, vbtn in pairs(buttonBindings) do
|
|
local pm, btn = bind:match("()([^-]*%-?)$")
|
|
if btn == BUTTON and #bind > lvalue and (pm == 1 or IsModifiedClick(bind)) then
|
|
lkey, lvalue = vbtn, #bind
|
|
end
|
|
end
|
|
if checkRingBindings and not lkey then
|
|
for k, v in pairs(ORL_RingData) do
|
|
if v.bindButton == BUTTON and (v.bindModifiedClick == nil or IsModifiedClick(v.bindModifiedClick)) and #v.bind > lvalue then
|
|
lkey, lvalue = "r" .. k, #v.bind
|
|
elseif v.bind2Button == BUTTON and (v.bind2ModifiedClick == nil or IsModifiedClick(v.bind2ModifiedClick)) and #v.bind2 > lvalue then
|
|
lkey, lvalue = "r" .. k .. "b", #v.bind2
|
|
end
|
|
end
|
|
end
|
|
return lkey
|
|
]==]
|
|
ORL_OnClick = [==[-- ORL_OnClick
|
|
local button, down = ...
|
|
local BUTTON = BUTTON_NAMEKEY_MAP[button] or button and button:upper()
|
|
local isActiveRingTriggerClick = (AIP_VirtualKey and AIP_VirtualKey == button) or (AIP_Key and AIP_Key == BUTTON and (AIP_Modifier == nil or IsModifiedClick(AIP_Modifier)))
|
|
local openHotkeyOverride, openHotkeyId, openHotkeyV = button:match("^r(o?)(%d+)(b?)$")
|
|
openHotkeyId = tonumber(openHotkeyId)
|
|
if isActiveRingTriggerClick and (AIP_Action or AIP_ActionFirstUp and not down) then
|
|
button = down == AIP_OnDown and AIP_Action or AIP_ActionFirstUp or "noop"
|
|
-- If hovering over a nested ring/jump slice, allow global mwin/up/down bindings to override the ring binding (when down)
|
|
local globalAction = down and owner:Run(ORL_CheckButtonBindings, (AIP_VirtualKey == ... and AIP_Key or BUTTON), false)
|
|
local pointerAID = globalAction and openCollection[owner:Run(ORL_GetCursorSlice)]
|
|
if collections[pointerAID] or ORL_KnownCollections[pointerAID] then
|
|
-- Global override wins; ignore future (primary binding, up)
|
|
button, AIP_ActionFirstUp = globalAction, nil
|
|
end
|
|
elseif BUTTON == "BUTTON1" and activeRing and AI_LeftAction then
|
|
button = down == AI_LeftOnDown and AI_LeftAction or "noop"
|
|
elseif BUTTON == "BUTTON2" and AI_RightAction then
|
|
button = down == AI_RightOnDown and AI_RightAction or "noop"
|
|
end
|
|
|
|
if button == "noop" then
|
|
return false
|
|
elseif activeRing and (button == "use" or button == "useFC") then
|
|
local slice, isFC = owner:Run(ORL_GetCursorSlice)
|
|
if button == "useFC" and not (isFC and slice) then
|
|
return false
|
|
end
|
|
return owner:RunFor(self, ORL_PerformSliceAction, slice, openCollectionID == activeRing.action, false, isActiveRingTriggerClick and "primary-binding")
|
|
elseif activeRing and button == "usenow" then
|
|
return owner:RunFor(self, ORL_PerformSliceAction, owner:Run(ORL_GetCursorSlice), false, true, "use-binding")
|
|
elseif activeRing and button == "close" then
|
|
return false, owner:Run(ORL_CloseActiveRing)
|
|
elseif activeRing and button:match("^slice%d+") then
|
|
local b = tonumber(button:match("slice(%d+)"))
|
|
if openCollection and openCollection[b] then
|
|
if down then
|
|
sliceBindState[b] = true
|
|
elseif sliceBindState[b] then
|
|
return owner:RunFor(self, ORL_PerformSliceAction, b, true, activeRing.NoCloseOnSlice, "slice-binding")
|
|
end
|
|
end
|
|
elseif activeRing and button == "mwin" and down then
|
|
local slice = owner:Run(ORL_GetCursorSlice)
|
|
local aid, rt = openCollection[slice], rotationMode[(ctokens[openCollectionID] or "")[slice]]
|
|
if collections[aid] then
|
|
return owner:RunFor(self, ORL_SwitchRing, aid, rt == "jump" and "jump-slice-switch" or "switch-binding")
|
|
end
|
|
elseif activeRing and button:match("^mw[ud]") then
|
|
return false, down and owner:Run(ORL_OnWheel, (button:match("^mwup") and 1 or -1) * (button:match("K$") and activeRing.bucket or 1))
|
|
elseif BUTTON:match("^BUTTON%d+$") then
|
|
-- The click-capturing overlay captures all mouse clicks, including bindings
|
|
local lkey = owner:Run(ORL_CheckButtonBindings, BUTTON, true)
|
|
if lkey then
|
|
return owner:RunFor(self, ORL_OnClick, lkey, down)
|
|
end
|
|
elseif openHotkeyId and down then
|
|
local isOverrideBinding = openHotkeyOverride == "o" and bindOverrides[openHotkeyId]
|
|
return owner:RunFor(self, ORL_OpenRing, openHotkeyId, isOverrideBinding and "override-binding" or "primary-binding", openHotkeyV == "b" and 2 or 1)
|
|
end
|
|
return false
|
|
]==]
|
|
ORL_PostClick = [[-- ORL_PostClick
|
|
local message = ...
|
|
local cs, oi, bad = AI_ClickStack[message], AI_ClickStackIndex
|
|
self:SetAttribute('type', nil)
|
|
self:SetAttribute('typerelease', nil)
|
|
AI_ClickStackIndex, ORL_JumpCount = oi-1
|
|
bad = message ~= oi or cs == nil or AB:GetAttribute("execID") == cs[1]
|
|
if message == oi and cs and cs[3] then
|
|
AB:RunAttribute("NotifyPostUse", cs[3])
|
|
end
|
|
if bad or (cs[2] or 0) < (RW:GetAttribute("execPending") or 0) then
|
|
owner:CallMethod("BadLizardError")
|
|
end
|
|
]]
|
|
ORL_OpenClick = [[-- ORL_OpenClick
|
|
local rdata = ORL_RingDataN[...]
|
|
if not rdata then
|
|
return false, print('|cffff0000[OPie] Cannot open unknown ring "' .. tostring((...)) .. '".')
|
|
end
|
|
ORL_OverSAB = self -- Don't RunFor the /click handler button, as it won't fire an UP
|
|
return owner:Run(ORL_ClearOverSAB, owner:Run(ORL_OpenRing, rdata.id, "open-macro"))
|
|
]]
|
|
ORL_ClearOverSAB = [[-- ORL_ClearOverSAB
|
|
ORL_OverSAB = nil
|
|
return ...
|
|
]]
|
|
]=])
|
|
OR_SecCore:SetAttribute("_onmousewheel", [[-- OR_OnMouseWheel
|
|
local wheelDirection = delta == 1 and "UP" or "DOWN"
|
|
local bind = ORL_GlobalOptions.ScrollNestedRingDownBinding
|
|
for bindDelta=-1,1,2 do
|
|
if bind and bind:match("MOUSEWHEEL([UPDOWN]+)$") == wheelDirection and (bind:match("%-") == nil or IsModifiedClick(bind)) then
|
|
delta = bindDelta
|
|
break
|
|
end
|
|
bind = ORL_GlobalOptions.ScrollNestedRingUpBinding
|
|
end
|
|
return self:Run(ORL_OnWheel, delta)
|
|
]])
|
|
OR_SecCore:WrapScript(OR_SecCore, "OnClick", "return self:Run(ORL_OnClick, button, down)", "owner:RunFor(self, ORL_PostClick, message)")
|
|
OR_SecCore:WrapScript(OR_OpenProxy, "OnClick", "return owner:RunFor(self, ORL_OpenClick, button)", "owner:RunFor(self, ORL_PostClick, message)")
|
|
OR_SecCore:WrapScript(bindProxy, "OnAttributeChanged", [[-- ORL.BindProxy-OnAttributeChanged
|
|
local rid, bv, data = (type(name) == "string" and name or ""):match("^binding%-r(%d+)(b?)$")
|
|
data = ORL_RingData[rid and rid+0]
|
|
if data then
|
|
local bind, bindButton, bindModifiedClick
|
|
if (value or "") ~= "" then
|
|
local modifiedClick, modifiers, undash, button = value:match("^((.-)([^-]?)(BUTTON%d+))$")
|
|
if undash ~= "" or modifiedClick == button then
|
|
modifiedClick, modifiers = nil
|
|
end
|
|
bind, bindButton, bindModifiedClick = value, button, modifiedClick
|
|
end
|
|
if bv == "b" then
|
|
data.bind2, data.bind2Button, data.bind2ModifiedClick = bind, bindButton, bindModifiedClick
|
|
else
|
|
data.bind, data.bindButton, data.bindModifiedClick = bind, bindButton, bindModifiedClick
|
|
end
|
|
end
|
|
]])
|
|
OR_SecCore:WrapScript(closeProxy, "OnClick", "return activeRing and owner:Run(ORL_CloseActiveRing) and false")
|
|
function OR_SecCore:throw(err)
|
|
return error(err, 2)
|
|
end
|
|
OR_SecEnv = GetManagedEnvironment(OR_SecCore)
|
|
end
|
|
local function OR_GetRingOption(ringName, option)
|
|
if not configInstance then return defaultConfig[option], nil, nil, nil, defaultConfig[option] end
|
|
local ring, global, default, value, setting = configInstance.RingOptions[ringName and (ringName .. "#" .. option)], rawget(configInstance, option), defaultConfig[option]
|
|
if ringName ~= nil then setting = ring else setting = global end
|
|
if ringName ~= nil and ring ~= nil then value = ring
|
|
elseif ring == nil and global ~= nil then value = global
|
|
else value = default end
|
|
return value, setting, ring, global, default
|
|
end
|
|
local OR_SyncRingBinding do -- Binding management
|
|
local bindingEncodeChars = {["["]="OPEN", ["]"]="CLOSE", [";"]="SEMICOLON"}
|
|
local encodedBindings = {OPEN="[", CLOSE="]", SEMICOLON=";"}
|
|
local function RemoveConflictingBindings(bind)
|
|
local rawBind = KR:UnescapeCmdOptionsValue(bind:gsub("%a+$", encodedBindings))
|
|
return GetBindingAction(rawBind) ~= "" and ";" or nil
|
|
end
|
|
function OR_SyncRingBinding(name, props)
|
|
if InCombatLockdown() then return end
|
|
local hotkey, isSoftBinding, id = configInstance and configInstance.Bindings[name], false, props.internalID
|
|
local hotkey2 = configInstance and configInstance.Bindings2[name]
|
|
if hotkey == nil and type(props.hotkey) == "string" and OR_GetRingOption(name, "UseDefaultBindings") then
|
|
hotkey, isSoftBinding = props.hotkey, true
|
|
end
|
|
if hotkey then
|
|
if not hotkey:match("%[.*%]") then
|
|
hotkey = "[] " .. hotkey:gsub("([^-!%s]+)%s*$", bindingEncodeChars)
|
|
end
|
|
if isSoftBinding then
|
|
hotkey = (hotkey .. ";"):gsub("%s*!*%s*([^%s%[%];]+)%s*;", RemoveConflictingBindings):sub(1,-2)
|
|
end
|
|
end
|
|
if hotkey2 and not hotkey2:match("%[.*%]") then
|
|
hotkey2 = "[] " .. hotkey2:gsub("([^-!]+)%s*$", bindingEncodeChars)
|
|
end
|
|
if not OR_SecCore:GetAttribute("frameref-proxy" .. id) then
|
|
local f = CreateFrame("Button", "ORL_RProxy" .. id, nil, "SecureActionButtonTemplate")
|
|
f:RegisterForClicks("AnyUp", "AnyDown")
|
|
OR_SecCore:WrapScript(f, "OnClick", "return owner:RunFor(self, ORL_OnClick, button, down)", 'owner:RunFor(self, ORL_PostClick, message)')
|
|
OR_SecCore:SetFrameRef("proxy" .. id, f)
|
|
local bk, lab = "BINDING_NAME_CLICK ".. f:GetName() .. ":r" .. id, (L"OPie ring: %s"):format(props.name or "?")
|
|
_G[bk], _G[bk .. "b"] = lab, lab
|
|
end
|
|
if props.currentBindingConditional == hotkey and
|
|
props.currentBindingConditional2 == hotkey2 and
|
|
props.currentBindingSoft == isSoftBinding then
|
|
return
|
|
end
|
|
props.currentBindingConditional, props.currentBindingConditional2, props.currentBindingSoft = hotkey, hotkey2, isSoftBinding
|
|
OR_DeferExecute([=[-- OR_SyncRingBinding
|
|
local ringName, internalId = %s, %d
|
|
local clickProxy = self:GetFrameRef("proxy" .. internalId)
|
|
KR:SetAttribute("frameref-RegisterBindingDriver-target", clickProxy)
|
|
KR:SetAttribute("frameref-RegisterBindingDriver-notify", bindProxy)
|
|
KR:RunAttribute("RegisterBindingDriver", "r" .. internalId, %s, %d)
|
|
KR:SetAttribute("frameref-RegisterBindingDriver-target", clickProxy)
|
|
KR:SetAttribute("frameref-RegisterBindingDriver-notify", bindProxy)
|
|
KR:RunAttribute("RegisterBindingDriver", "r" .. internalId .. "b", %s, %d)
|
|
]=], safequote(name), id, safequote(hotkey or ""), isSoftBinding and -2600 or -2100, safequote(hotkey2 or ""), 20-2100)
|
|
end
|
|
OR_SecCore:Execute([=[-- BindingInit
|
|
bindOverrides = newtable()
|
|
RegisterStateDriver(self, "combat", "[combat] combat; nocombat")
|
|
ORL_RegisterOverride = [[-- ORL_RegisterOverride
|
|
local id, bind = ...
|
|
if bindOverrides[id] then overProxy:ClearBinding(bindOverrides[id]) end
|
|
if bindOverrides[bind] then bindOverrides[ bindOverrides[bind] ] = nil end
|
|
if bind then
|
|
overProxy:SetBindingClick(true, bind, self:GetFrameRef("proxy" .. id), "ro" .. id)
|
|
bindOverrides[bind] = ring
|
|
end
|
|
bindOverrides[id] = bind
|
|
]]
|
|
]=])
|
|
OR_SecCore:SetAttribute("_onattributechanged", [=[-- ORL_UpdateBinding
|
|
if name == "state-combat" and value == "combat" then
|
|
overProxy:ClearBindings()
|
|
wipe(bindOverrides)
|
|
end
|
|
]=])
|
|
OR_SecCore.BadLizardError = GenerateClosure(error, "bad lizard", 2)
|
|
function OR_SecCore.NoOnOpenForMacros()
|
|
print("|cffe84010Cannot execute on-open actions when using |cffffffff/click ORLOpen|r.")
|
|
end
|
|
end
|
|
local function OR_SyncRing(name, actionId, newprops)
|
|
local props = OR_Rings[name] or {}
|
|
if not OR_Rings[name] then
|
|
OR_Rings[name], OR_Rings[#OR_Rings+1], props.internalID, props.internalName, props.internalAP, internalFreeId = props, name, internalFreeId, name, #OR_Rings+1, internalFreeId+1
|
|
end
|
|
if newprops then
|
|
props.action, props.offset, props.name, props.hotkey, props.internal = actionId, newprops.offset or 0, newprops.name or name, newprops.hotkey, newprops.internal
|
|
local fcBlock = ""
|
|
for i=1,#newprops do
|
|
-- DEPRECATED[2306/Y11]: Slice presentation mode hints in ActionBook's collections override these values.
|
|
if newprops[i].sliceToken then
|
|
local pMode, rMode = newprops[i].rotationMode, nil
|
|
if pMode and (pMode == "random" or pMode == "shuffle" or pMode == "cycle" or pMode == "reset" or pMode == "jump") then
|
|
rMode = pMode
|
|
end
|
|
fcBlock = fcBlock .. ("fcIgnore[%s], rotationMode[%1$s] = %s, %s "):format(safequote(newprops[i].sliceToken), tostringf(not newprops[i].fastClick), rMode and safequote(rMode) or "nil")
|
|
end
|
|
end
|
|
props.fcBlock, props.opportunisticCA, props.noPersistentCA = fcBlock, not newprops.noOpportunisticCA, newprops.noPersistentCA
|
|
props.sortScope = type(newprops.sortScope) == "number" and newprops.sortScope or 2
|
|
end
|
|
|
|
local imode, noClose = OR_GetRingOption(name, "InteractionMode"), OR_GetRingOption(name, "NoClose")
|
|
local sliceBindings = (imode == 3 or OR_GetRingOption(name, "SliceBinding")) and OR_GetRingOption(name, "SliceBindingString")
|
|
local centerAction, motionAction = OR_GetRingOption(name, "CenterAction"), OR_GetRingOption(name, "MotionAction")
|
|
local primaryAction = imode == 1 and "use" or imode == 2 and OR_GetRingOption(name, "CloseOnRelease") and "close"
|
|
local primaryActionUp = imode == 2 and (centerAction or motionAction) and OR_GetRingOption(name, "QuickActionOnRelease") and "useFC"
|
|
local leftAction = imode == 2 and (noClose and "usenow" or "use")
|
|
local leftAction2 = imode ~= 3 and (noClose and "usenow" or "use")
|
|
local rightAction = imode ~= 3 and "close"
|
|
OR_DeferExecute([[-- SyncRing
|
|
local ringName, internalId, actionId = %s, %d, %d
|
|
local data = ORL_RingData[internalId] or newtable()
|
|
ORL_KnownCollections[actionId], ORL_RingData[internalId], ORL_RingDataN[ringName], data.action, data.name, data.id = internalId, data, data, actionId, ringName, internalId
|
|
|
|
data.name, data.ofs, data.ofsx, data.ofsy, data.ofsDeg = ringName, %q, %d, %d, %f
|
|
data.CenterAction, data.MotionAction, data.ClickPriority, data.ReOpenAction = %s, %s, %s, %d
|
|
data.NoClose, data.NoCloseOnSlice, data.NoPointer = %s, %s, %s
|
|
data.PrimaryAction, data.PrimaryActionUp = %s, %s
|
|
data.LeftAction, data.LeftAction2 = %s, %s
|
|
data.RightAction, data.RightAction2 = %s, %s
|
|
data.scale, data.bucket = %f, %d
|
|
local sbs = %s
|
|
local sb, bn = sbs and newtable() or false, 1
|
|
for s, s2 in (sbs or ""):gmatch("([^%%s\31]+)\31?(%%S*)") do -- NB: in string.format
|
|
s = not (s == "false" or s:match("[Bb][Uu][Tt][Tt][Oo][Nn][123]$") or s:match("[Ee][Ss][Cc][Aa][Pp][Ee]$")) and s
|
|
s2 = not (s2 == "false" or s2 == "" or s2:match("[Bb][Uu][Tt][Tt][Oo][Nn][123]$") or s2:match("[Ee][Ss][Cc][Aa][Pp][Ee]$")) and s2
|
|
sb[bn], sb[bn+0.5], bn = s, s2, bn + 1
|
|
end
|
|
data.SliceBinding, data.SliceBindingUncombine, data.SelectedSliceBind, data.OpprotunisticCA = sb, nil, %s, %s
|
|
%s
|
|
]], safequote(name), props.internalID, props.action,
|
|
OR_GetRingOption(name, "RingAtMouse") and "$cursor" or "$screen", OR_GetRingOption(name, "IndicationOffsetX"), -OR_GetRingOption(name, "IndicationOffsetY"), props.offset,
|
|
tostringf(centerAction), tostringf(motionAction), tostringf(OR_GetRingOption(name, "ClickPriority")), OR_GetRingOption(name, "ReOpenAction") % 1e6,
|
|
tostringf(noClose), tostringf(OR_GetRingOption(name, "NoCloseOnSlice")), tostringf(imode == 3),
|
|
primaryAction and safequote(primaryAction) or "nil", primaryActionUp and safequote(primaryActionUp) or "nil",
|
|
leftAction and safequote(leftAction) or "nil", leftAction2 and safequote(leftAction2) or "nil",
|
|
rightAction and safequote(rightAction) or "nil", rightAction and safequote(rightAction) or "nil",
|
|
math.max(0.1, (OR_GetRingOption(name, "RingScale"))), (OR_GetRingOption(name, "MouseBucket")),
|
|
(sliceBindings or "") ~= "" and safequote(sliceBindings) or "nil",
|
|
safequote(OR_GetRingOption(name, "SelectedSliceBind") or ""), tostringf(props.opportunisticCA),
|
|
props.fcBlock or "")
|
|
OR_SyncRingBinding(name, props)
|
|
if newprops and AB then AB:NotifyObservers("ring") end
|
|
end
|
|
local function OR_DeleteRing(name, data)
|
|
OR_DeferExecute([[-- DeleteRing
|
|
local ringName, internalId, actionId = %s, %d, %d
|
|
self:SetAttribute("state-r" .. internalId, nil)
|
|
ORL_KnownCollections[actionId], ORL_RingData[internalId], ORL_RingDataN[ringName] = nil
|
|
KR:SetAttribute("frameref-UnregisterBindingDriver-target", self:GetFrameRef("proxy" .. internalId))
|
|
KR:RunAttribute("UnregisterBindingDriver", "r" .. internalId)
|
|
]], safequote(name), data.internalID, data.action or 0)
|
|
|
|
local bind = configInstance and configInstance.Bindings[name]
|
|
if configRoot and configRoot.ProfileStorage then
|
|
local rnOpt = "^" .. name:gsub("[%]%[().+*%-?^$%%]", "%%%1") .. "#"
|
|
for _,v in pairs(configRoot.ProfileStorage) do
|
|
if v.Bindings then
|
|
v.Bindings[name] = nil
|
|
end
|
|
if v.RingOptions then
|
|
for k2, _v2 in pairs(v.RingOptions) do
|
|
if type(k2) ~= "string" or k2:match(rnOpt) then
|
|
v.RingOptions[k2] = nil
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
OR_Rings[data.internalAP], OR_Rings[OR_Rings[#OR_Rings]].internalAP = OR_Rings[#OR_Rings], data.internalAP
|
|
OR_Rings[name], OR_Rings[#OR_Rings] = nil
|
|
|
|
if bind then
|
|
for k, v in pairs(OR_Rings) do
|
|
if v.hotkey == bind then
|
|
OR_SyncRing(k)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
local function OR_SecProfilePull()
|
|
local pInstance = configRoot.ProfileStorage[OR_SecEnv.activeProfile]
|
|
if not pInstance then return end
|
|
for k, v in rtable.pairs(OR_SecEnv.rtokens) do
|
|
pInstance.RotationTokens[k] = v
|
|
end
|
|
end
|
|
local function OR_SecProfilePush()
|
|
if InCombatLockdown() or (activeProfile == OR_SecEnv.activeProfile) then return end
|
|
local e = ("-- SecProfilePush\n activeProfile = %s"):format(safequote(activeProfile))
|
|
for k,v in pairs(configInstance.RotationTokens) do
|
|
e = e .. ("\n rtokens[%s] = %s"):format(safequote(k), safequote(v))
|
|
end
|
|
OR_SecCore:Execute(e)
|
|
end
|
|
local function OR_PullCAs()
|
|
local t = {}
|
|
for k,v in rtable.pairs(OR_SecEnv.ORL_StoredCA) do
|
|
t[k] = v
|
|
end
|
|
for _, k in ipairs(OR_Rings) do
|
|
local rt = OR_SecEnv.ORL_RingDataN[k] and OR_SecEnv.ORL_RingDataN[k].fcToken or t[k]
|
|
t[k] = not ((OR_Rings[k] and OR_Rings[k].noPersistentCA) or OR_SecEnv.fcIgnore[rt]) and rt or nil
|
|
end
|
|
return next(t) and t or nil
|
|
end
|
|
local OR_FindFinalAction do
|
|
local seen, wipe = {}, table.wipe
|
|
local secRotation, secCollections, secTokens, secRotationMode = OR_SecEnv.rotation, OR_SecEnv.collections, OR_SecEnv.ctokens, OR_SecEnv.rotationMode
|
|
function OR_FindFinalAction(collection, id, from, rotationBonus, followJumps)
|
|
wipe(seen)
|
|
for k=1, 50 do
|
|
local col = secCollections[collection]
|
|
local act = col and col[id]
|
|
if act then
|
|
local nCol, tok, rot = secCollections[act], secTokens[collection][id]
|
|
local isJump = secRotationMode[tok] == "jump"
|
|
if nCol == nil then
|
|
return act, tok, "act", k
|
|
elseif isJump and tok ~= from and not followJumps then
|
|
return act, tok, "jump", k
|
|
elseif not seen[tok] then
|
|
seen[tok], rot = true, secRotation[tok] or 1
|
|
if tok == from then rot = (rot + rotationBonus - 1) % #nCol + 1 end
|
|
collection, id = act, rot
|
|
else -- nCol and seen[tok] and (not isJump or tok == from or followJumps); arbitrarily break nesting loops
|
|
return act, tok, isJump and "jump" or "act", k
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
local OR_PadStickCapture = {} do
|
|
local hasStoredState, sinkStick, waitingStickLock, storedYaw, storedPitch = false, nil
|
|
local isThawing, sinkFrame, ouFrame, thawEnd, thawL, thawH = false, CreateFrame("Frame"), CreateFrame("Frame")
|
|
ouFrame:Hide()
|
|
sinkFrame:Hide()
|
|
sinkFrame:SetScript("OnGamePadStick", function() end)
|
|
local function StickThaw_OnUpdate(s, e)
|
|
local t = GetTime()
|
|
if t >= thawEnd or not isThawing then
|
|
if isThawing then
|
|
if hasStoredState then
|
|
SetCVar("GamePadCameraYawSpeed", storedYaw)
|
|
SetCVar("GamePadCameraPitchSpeed", storedPitch)
|
|
end
|
|
isThawing, hasStoredState, sinkStick = false, false, nil
|
|
sinkFrame:Hide()
|
|
end
|
|
s:Hide()
|
|
return
|
|
end
|
|
local ms = C_GamePad.GetDeviceMappedState()
|
|
local st = ms and ms.sticks
|
|
st = st and st[hasStoredState and 2 or sinkStick]
|
|
if st and st.len == 0 then
|
|
thawEnd = t-1
|
|
return StickThaw_OnUpdate(s, e)
|
|
elseif not hasStoredState then
|
|
return
|
|
end
|
|
local r = (thawEnd-t)/thawL
|
|
local p = r >= thawH and 0 or (1-r/(1-thawH))
|
|
p = p < 0 and 0 or p > 1 and 1 or p
|
|
if p > 0 then
|
|
local s = p*p
|
|
SetCVar("GamePadCameraYawSpeed", storedYaw*s)
|
|
SetCVar("GamePadCameraPitchSpeed", storedPitch*s)
|
|
end
|
|
end
|
|
local function FreelookWait_OnUpdate(s)
|
|
if not waitingStickLock then
|
|
s:Hide()
|
|
elseif IsGamePadFreelookEnabled() then
|
|
OR_PadStickCapture:Lock(waitingStickLock)
|
|
end
|
|
end
|
|
function OR_PadStickCapture:Lock(stick)
|
|
if stick == 2 then
|
|
if not hasStoredState then
|
|
hasStoredState, storedYaw, storedPitch = true, GetCVar("GamePadCameraYawSpeed"), GetCVar("GamePadCameraPitchSpeed")
|
|
end
|
|
SetCVar("GamePadCameraYawSpeed", 0)
|
|
SetCVar("GamePadCameraPitchSpeed", 0)
|
|
elseif stick then
|
|
sinkStick = stick
|
|
sinkFrame:Show()
|
|
end
|
|
isThawing, waitingStickLock = false
|
|
ouFrame:Hide()
|
|
end
|
|
function OR_PadStickCapture:WaitForFreelook(stick)
|
|
local wasThawing = isThawing
|
|
waitingStickLock, isThawing, sinkStick = stick, false, nil
|
|
if wasThawing then
|
|
StickThaw_OnUpdate(ouFrame, 0)
|
|
end
|
|
ouFrame:SetScript("OnUpdate", FreelookWait_OnUpdate)
|
|
ouFrame:Show()
|
|
end
|
|
function OR_PadStickCapture:Release()
|
|
if (hasStoredState or sinkStick) and not isThawing then
|
|
isThawing, thawL, thawH = true, OR_GetRingOption(nil, "PSThawDuration"), OR_GetRingOption(nil, "PSThawHold")
|
|
thawEnd = thawL + GetTime()
|
|
ouFrame:SetScript("OnUpdate", StickThaw_OnUpdate)
|
|
ouFrame:Show()
|
|
end
|
|
waitingStickLock = nil
|
|
end
|
|
function EV:GAME_PAD_ACTIVE_CHANGED(isActive)
|
|
if sinkStick and not (isActive or isThawing) then
|
|
sinkFrame:Hide()
|
|
OR_PadStickCapture:WaitForFreelook(sinkStick)
|
|
end
|
|
end
|
|
function EV:PLAYER_LOGOUT()
|
|
if hasStoredState then
|
|
SetCVar("GamePadCameraYawSpeed", storedYaw)
|
|
SetCVar("GamePadCameraPitchSpeed", storedPitch)
|
|
end
|
|
end
|
|
end
|
|
function OR_PadRestoreState:SaveAndSet(freelook, cursor)
|
|
if not self.saved then
|
|
self.InFreeLook = IsGamePadFreelookEnabled()
|
|
self.InCursorControl = IsGamePadCursorControlEnabled()
|
|
self.saved = true
|
|
end
|
|
SetGamePadFreeLook(freelook)
|
|
SetGamePadCursorControl(cursor)
|
|
end
|
|
function OR_SecCore:CheckCVars()
|
|
local ccy = not InCombatLockdown() and GetCVar("CursorCenteredYPos")
|
|
if tonumber(ccy) and ccy ~= OR_SecEnv.CENTERED_CURSOR_YPOS then
|
|
OR_SecCore:Execute("CENTERED_CURSOR_YPOS = " .. safequote(ccy))
|
|
end
|
|
end
|
|
function OR_SecCore:NotifyState(state, _ringName, collection, ...)
|
|
if state == "open" then
|
|
local fastClick, fastOpen, ms = ...
|
|
OR_ActiveCollectionID, OR_ActiveRingName, OR_ActiveSliceCount, OR_ModifierLockState = collection, OR_SecEnv.activeRing.name, #OR_SecEnv.openCollection, ms
|
|
if ORI then
|
|
securecall(ORI.Show, ORI, collection, fastClick, fastOpen, self)
|
|
end
|
|
if OR_SecEnv.AI_NoPointer then
|
|
return
|
|
end
|
|
MouselookStop()
|
|
local psm = C_GamePad.IsEnabled() and OR_SecEnv.ORL_GlobalOptions.PadSupportMode
|
|
local switchOnOpen = psm and OR_GetRingOption(nil, "PSOpenSwitchMode") or 0
|
|
switchOnOpen = switchOnOpen == 2 or switchOnOpen ~= 0 and (OR_SecEnv.AIP_Key or ""):match("GAMEPAD")
|
|
if psm == "freelook" then
|
|
if switchOnOpen then
|
|
OR_PadRestoreState:SaveAndSet(true, false)
|
|
OR_PadStickCapture:Lock(OR_SecEnv.ORL_GlobalOptions.PSStickIndex)
|
|
else
|
|
OR_PadStickCapture:WaitForFreelook(OR_SecEnv.ORL_GlobalOptions.PSStickIndex)
|
|
end
|
|
elseif psm == "cursor" then
|
|
if switchOnOpen then
|
|
OR_PadRestoreState:SaveAndSet(false, true)
|
|
end
|
|
end
|
|
self:CheckCVars()
|
|
elseif state == "switch" then
|
|
OR_ActiveCollectionID, OR_ActiveSliceCount = collection, #OR_SecEnv.openCollection
|
|
if ORI then
|
|
securecall(ORI.Show, ORI, collection, ..., "inplace-switch", self)
|
|
end
|
|
elseif state == "close" then
|
|
if ORI then
|
|
securecall(ORI.Hide, ORI, ...)
|
|
end
|
|
OR_ActiveSliceCount, OR_ActiveCollectionID, OR_ActiveRingName = 0
|
|
OR_PadStickCapture:Release()
|
|
if OR_PadRestoreState.saved and OR_GetRingOption(nil, "PSRestoreOnClose") then
|
|
SetGamePadFreeLook(OR_PadRestoreState.InFreeLook)
|
|
SetGamePadCursorControl(OR_PadRestoreState.InCursorControl)
|
|
end
|
|
OR_PadRestoreState.saved = nil
|
|
end
|
|
end
|
|
|
|
-- Responding to WoW Events
|
|
local OR_NotifyPVars do
|
|
local function callNotify(k, event)
|
|
local v = PersistentStorageInfo[k]
|
|
v.f(event, k, v.t)
|
|
return true
|
|
end
|
|
function OR_NotifyPVars(event, filter, perProfile)
|
|
local ok = true
|
|
for k, v in pairs(PersistentStorageInfo) do
|
|
if type(v.f) == "function" and v.t == (filter or v.t) and (perProfile == nil or perProfile == v.perProfile) then
|
|
ok = securecall(callNotify, k, event) and ok
|
|
end
|
|
end
|
|
return ok
|
|
end
|
|
end
|
|
local function OR_ForceResync(filter)
|
|
for _,v in ipairs(OR_Rings) do
|
|
if (filter or v) == v then
|
|
OR_SyncRing(v)
|
|
end
|
|
end
|
|
if (filter or true) == true then
|
|
local psm = OR_GetRingOption(nil, "PadSupportMode")
|
|
local up1, up2 = OR_GetRingOption(nil, "ScrollNestedRingUpButton"), OR_GetRingOption(nil, "ScrollNestedRingUpButton2")
|
|
local down1, down2 = OR_GetRingOption(nil, "ScrollNestedRingDownButton"), OR_GetRingOption(nil, "ScrollNestedRingDownButton2")
|
|
local p = "[mM][oO][uU][sS][eE][wW][hH][eE][eE][lL]"
|
|
local hasWheelScroll = (up1 or ""):match(p) or (up2 or ""):match(p) or (down1 or ""):match(p) or (down2 or ""):match(p)
|
|
OR_DeferExecute([[-- SyncGlobalOptions
|
|
ORL_GlobalOptions.OpenNestedRingBinding, ORL_GlobalOptions.ScrollNestedRingUpBinding, ORL_GlobalOptions.ScrollNestedRingDownBinding = %s, %s, %s
|
|
ORL_GlobalOptions.OpenNestedRingBinding2, ORL_GlobalOptions.ScrollNestedRingUpBinding2, ORL_GlobalOptions.ScrollNestedRingDownBinding2 = %s, %s, %s
|
|
ORL_GlobalOptions.PadSupportMode, ORL_GlobalOptions.PSStickIndex = %s, %d
|
|
self:CallMethod("EnableMouseWheel", %s)]],
|
|
safequote(OR_GetRingOption(nil, "OpenNestedRingButton")), safequote(up1), safequote(down1),
|
|
safequote(OR_GetRingOption(nil, "OpenNestedRingButton2")), safequote(up2), safequote(down2),
|
|
safequote(PADSUPPORT_MODE_MAP[psm] or psm or "none"), psm == "freelook1" and 1 or 2,
|
|
hasWheelScroll and "true" or "false"
|
|
)
|
|
end
|
|
end
|
|
local function OR_CheckBindings()
|
|
if InCombatLockdown() then return end
|
|
for i=1,#OR_Rings do
|
|
local k = OR_Rings[i]
|
|
OR_SyncRingBinding(k, OR_Rings[k])
|
|
end
|
|
end
|
|
function EV:PLAYER_REGEN_ENABLED()
|
|
OR_CheckBindings()
|
|
OR_SecProfilePush()
|
|
OR_DeferExecute()
|
|
end
|
|
function EV:PLAYER_REGEN_DISABLED()
|
|
OR_SecCore:CheckCVars()
|
|
end
|
|
local function OR_UnserializeConfigInstance(profile)
|
|
local newCI
|
|
activeProfile, newCI = getProfile(profile)
|
|
for t in ("RingOptions Bindings Bindings2 RotationTokens"):gmatch("%S+") do
|
|
if type(newCI[t]) ~= "table" then
|
|
newCI[t] = {}
|
|
end
|
|
end
|
|
for k,v in pairs(PersistentStorageInfo) do
|
|
if v.perProfile then
|
|
copy(newCI[k], nil, v.t)
|
|
end
|
|
end
|
|
configInstance = setmetatable(newCI, optionsMeta)
|
|
end
|
|
local function OR_NotifyOptions()
|
|
for option, func in pairs(optionValidators) do
|
|
if func then
|
|
securecall(func, option, configInstance[option])
|
|
end
|
|
end
|
|
end
|
|
local function OR_UpgradeOptions(pv, isRingOptions)
|
|
local nv = {}
|
|
for k, v in pairs(pv) do
|
|
local domain, opt = (isRingOptions and type(k) == "string" and k or ""):match("^(.*#)(.-)$")
|
|
if not domain then domain, opt = "", k end
|
|
if opt == "ClickActivation" and type(v) == "boolean" then
|
|
nv[domain .. "InteractionMode"], pv[k] = pv[domain .. "InteractionMode"] or v and 2 or nil, nil -- DEPRECATED[2406/Z7]
|
|
elseif opt == "PSSwitchOnOpen" and type(v) == "boolean" then
|
|
nv[domain .. "PSOpenSwitchMode"], pv[k] = pv[domain .. "PSOpenSwitchMode"] or v and 1 or 0, nil -- DEPRECATED[2405/Z6]
|
|
end
|
|
end
|
|
for k,v in pairs(nv) do
|
|
pv[k], nv[k] = v, nil
|
|
end
|
|
end
|
|
local function OR_UpgradeConfig()
|
|
if not OR_UpgradeConfig then return end
|
|
local tb = configRoot._TimeBand
|
|
TB_THRESH = type(tb) == "number" and tb < 1 and tb >= 0 and tb or TB_THRESH or nil
|
|
local gameVersion, opieVersion = GetBuildInfo(), ("%s (%d.%d)"):format(api:GetVersion())
|
|
if (configRoot._OPieVersion or opieVersion) ~= opieVersion then
|
|
for _,pv in pairs(configRoot.ProfileStorage) do
|
|
if type(pv) == "table" then
|
|
OR_UpgradeOptions(pv, false)
|
|
if type(pv.RingOptions) == "table" then
|
|
OR_UpgradeOptions(pv.RingOptions, true)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
if configRoot._GameVersion ~= gameVersion then
|
|
for _,v in pairs(configRoot.ProfileStorage) do
|
|
if type(v) == "table" then
|
|
v.RotationTokens = nil
|
|
end
|
|
end
|
|
end
|
|
configRoot._GameVersion, configRoot._OPieVersion, configRoot._GameLocale = gameVersion, opieVersion, GetLocale()
|
|
OR_UpgradeConfig = nil
|
|
return true
|
|
end
|
|
local function OR_InitConfigState()
|
|
local from
|
|
if type(OPie_SavedData) == "table" then
|
|
svMigrationState, from = 2, OPie_SavedData
|
|
elseif type(OneRing_Config) == "table" then
|
|
svMigrationState, from = 1, OneRing_Config
|
|
end
|
|
if from then
|
|
for k, v in pairs(from) do
|
|
configRoot[k] = v
|
|
end
|
|
end
|
|
for t in ("CharProfiles PersistentStorage ProfileStorage"):gmatch("%S+") do
|
|
configRoot[t] = type(configRoot[t]) == "table" and copy(configRoot[t]) or {}
|
|
end
|
|
if type(configRoot.ProfileStorage.default) ~= "table" then
|
|
configRoot.ProfileStorage.default = {Bindings={}, RingOptions={}}
|
|
end
|
|
|
|
local ok = securecall(OR_UpgradeConfig)
|
|
OR_UnserializeConfigInstance(configRoot.CharProfiles[getSpecCharIdent()])
|
|
|
|
if type(configRoot.CenterActions) == "table" then
|
|
local exec = "-- InitCA"
|
|
for name, tok in pairs(configRoot.CenterActions) do
|
|
exec = ("%s\nORL_StoredCA[%s] = %s"):format(exec, safequote(name), safequote(tok))
|
|
end
|
|
OR_SecCore:Execute(exec)
|
|
end
|
|
|
|
-- Load variables into relevant tables, unlock core and fire notifications.
|
|
for k, v in pairs(configRoot.PersistentStorage) do
|
|
if PersistentStorageInfo[k] and not PersistentStorageInfo[k].perProfile then
|
|
copy(v, nil, PersistentStorageInfo[k].t)
|
|
end
|
|
end
|
|
ok = OR_NotifyPVars("LOADED") and ok
|
|
OR_NotifyOptions()
|
|
OR_LoadedState = OR_LoadedState == 2 and (ok and 4 or 3) or OR_LoadedState
|
|
if ok then
|
|
OPie_SavedData, OneRing_Config, configRoot.CenterActions = nil
|
|
end
|
|
end
|
|
function EV:ADDON_LOADED(addon)
|
|
if addon == ADDON then
|
|
ORI, OR_LoadedState = T.OPieUI, OR_LoadedState == 1 and 2 or OR_LoadedState
|
|
OR_InitConfigState()
|
|
OR_SecProfilePush()
|
|
OR_ForceResync()
|
|
C_AddOns.DisableAddOn("OPie_Classic")
|
|
return "remove"
|
|
end
|
|
end
|
|
function EV:SAVED_VARIABLES_TOO_LARGE(addon)
|
|
if addon == ADDON then
|
|
OR_LoadedState = false
|
|
end
|
|
end
|
|
function EV:PLAYER_LOGIN()
|
|
OR_LoadedState = OR_LoadedState == 1 and -1 or OR_LoadedState
|
|
OR_NotifyPVars("LOGIN")
|
|
OR_NotifyPVars("POST-LOGIN")
|
|
return "remove"
|
|
end
|
|
function EV:PLAYER_ENTERING_WORLD(_, isReload)
|
|
if isReload and type(OPie_SavedDataPC) == "table" and type(OPie_SavedDataPC.FlagState) == "table" then
|
|
local FM = AB and AB:compatible("FlagMast", 1)
|
|
if FM then
|
|
FM:RestoreState(OPie_SavedDataPC.FlagState)
|
|
end
|
|
end
|
|
OPie_SavedDataPC = nil
|
|
return "remove"
|
|
end
|
|
function EV:PLAYER_LOGOUT()
|
|
OPie_SavedData = configRoot
|
|
OneRing_Config = svMigrationState ~= 2 and configRoot or nil
|
|
OR_NotifyPVars("LOGOUT")
|
|
OR_SecProfilePull()
|
|
configRoot._TimeBand = TB_THRESH
|
|
configRoot.CenterActions = OR_PullCAs()
|
|
for k, v in pairs(configInstance) do
|
|
if v == defaultConfig[k] then
|
|
configInstance[k] = nil
|
|
end
|
|
end
|
|
for k, v in pairs(PersistentStorageInfo) do
|
|
local store = v.perProfile and configInstance or configRoot.PersistentStorage
|
|
store[k] = next(v.t) ~= nil and v.t or nil
|
|
end
|
|
for t in ("RingOptions Bindings Bindings2 RotationTokens"):gmatch("%S+") do
|
|
for _, v in pairs(configRoot.ProfileStorage) do
|
|
if type(v[t]) ~= "table" or next(v[t]) == nil then
|
|
v[t] = nil
|
|
end
|
|
end
|
|
end
|
|
local FM = AB and AB:compatible("FlagMast", 1)
|
|
local fs = FM and FM:GetState()
|
|
OPie_SavedDataPC = fs and {FlagState=fs} or nil
|
|
end
|
|
local function OR_SaveCurrentProfile()
|
|
OR_NotifyPVars("SAVE", nil, true)
|
|
for k, v in pairs(PersistentStorageInfo) do
|
|
if v.perProfile then
|
|
configInstance[k] = next(v.t) and copy(v.t)
|
|
end
|
|
end
|
|
OR_SecProfilePull()
|
|
end
|
|
local function OR_SwitchProfile(ident)
|
|
if ident ~= activeProfile then OR_SaveCurrentProfile() end
|
|
local prevProfile = activeProfile
|
|
OR_UnserializeConfigInstance(ident)
|
|
OR_NotifyPVars("UPDATE", nil, true)
|
|
OR_NotifyOptions()
|
|
OR_SecProfilePush()
|
|
OR_ForceResync()
|
|
if activeProfile ~= prevProfile then
|
|
EV("OPIE_PROFILE_SWITCHED", activeProfile, prevProfile)
|
|
end
|
|
end
|
|
function EV.ACTIVE_TALENT_GROUP_CHANGED()
|
|
local newProfile = getProfileForSpec()
|
|
if newProfile ~= activeProfile then
|
|
OR_SwitchProfile(newProfile)
|
|
end
|
|
end
|
|
EV.UPDATE_BINDINGS = OR_CheckBindings
|
|
|
|
local function cmpRingProps(a, b)
|
|
local ac, bc = b.sortScope or 0, a.sortScope or 0
|
|
if ac == bc then
|
|
ac, bc = (a.name or a.internalName), (b.name or b.internalName)
|
|
end
|
|
return ac < bc
|
|
end
|
|
local getProfileIdentComparator do
|
|
local pt
|
|
local function cmpProfileIdent(a, b)
|
|
local ac, bc = pt[a] or 0, pt[b] or 0
|
|
if ac ~= bc then
|
|
return ac > bc
|
|
end
|
|
return strcmputf8i(a,b) < 0
|
|
end
|
|
function getProfileIdentComparator()
|
|
pt = {}
|
|
for i=1, getNumSpecs() do
|
|
pt[getProfileForSpec(i) or 0] = 1
|
|
end
|
|
pt[activeProfile or 0] = 2
|
|
pt.default, pt[0] = 3, nil
|
|
return cmpProfileIdent
|
|
end
|
|
end
|
|
local function retProfiles(n, i)
|
|
if i <= n then
|
|
return getProfileForSpec(i), retProfiles(n, i+1)
|
|
end
|
|
end
|
|
local function getRingBindings(ringName, skipDefault)
|
|
local b1c, b1, isUser = configInstance.Bindings[ringName]
|
|
if b1c == nil and not skipDefault then
|
|
b1, isUser = OR_Rings[ringName].hotkey, false
|
|
else
|
|
b1, isUser = b1c, true
|
|
end
|
|
return b1, configInstance.Bindings2[ringName], isUser, b1c
|
|
end
|
|
|
|
function private:GetSVState()
|
|
return OR_LoadedState
|
|
end
|
|
function private:RegisterOption(name, default, validator)
|
|
assert(type(name) == "string" and default ~= nil and (validator == nil or type(validator) == "function"), 'Syntax: api:RegisterOption("name", defaultValue[, validatorFunc])', 2)
|
|
assert(defaultConfig[name] == nil and PersistentStorageInfo[name] == nil, "Option %q has a conflicting name", 2, name)
|
|
defaultConfig[name], optionValidators[name] = default, validator or false
|
|
end
|
|
function private:RegisterPVar(name, into, notifier, perProfile)
|
|
assert(type(name) == "string" and (into == nil or type(into) == "table") and (notifier == nil or type(notifier) == "function"), 'Syntax: api:RegisterPVar("name"[, storageTable[, notifierFunc[, perProfile]]])', 2)
|
|
assert(PersistentStorageInfo[name] == nil and defaultConfig[name] == nil, "Persistent variable %q already declared.", 2, name)
|
|
assert(name:match("^%a"), "%q is not a valid persistent variable name", 2, name)
|
|
local store, into = ((perProfile == true) and configInstance or configRoot.PersistentStorage), into or {}
|
|
PersistentStorageInfo[name] = {t=into, f=notifier, perProfile=perProfile == true}
|
|
if configInstance then
|
|
if store and store[name] then
|
|
copy(store[name], nil, into)
|
|
end
|
|
OR_NotifyPVars("LOADED", into)
|
|
end
|
|
return into
|
|
end
|
|
function private:GetOption(option, ringName)
|
|
assert(type(option) == "string" and (ringName == nil or type(ringName) == "string"), 'Syntax: value, setting, ring, global, default = api:GetOption("option"[, "ringName"])', 2)
|
|
if defaultConfig[option] == nil then return end
|
|
return OR_GetRingOption(ringName, option)
|
|
end
|
|
function private:SetOption(option, value, ringName)
|
|
assert(type(option) == "string" and (ringName == nil or type(ringName) == "string"), 'Syntax: api:SetOption("option", value[, "ringName"])', 2)
|
|
assert(defaultConfig[option] ~= nil, "Option %q is undefined.", 2, option)
|
|
assert(ringName == nil or OR_Rings[ringName], "Ring %q is undefined.", 2, ringName)
|
|
assert(value == nil or type(defaultConfig[option]) == type(value), "Type mismatch: %q expected to be a %s (got %s).", 2, option, type(defaultConfig[option]), type(value))
|
|
assert(not optionValidators[option] or optionValidators[option](option, value, ringName) ~= false, "Value rejected by option validator.", 2)
|
|
local scope, prefix = ringName and configInstance.RingOptions or configInstance, ringName and (ringName .. "#") or ""
|
|
scope[prefix .. option] = value
|
|
if optionValidators[option] == nil then
|
|
OR_ForceResync(ringName)
|
|
end
|
|
end
|
|
function private:GetRingBinding(ringName, bidx)
|
|
assert(type(ringName) == "string" and (bidx == nil or bidx == 1 or bidx == 2),
|
|
'Syntax: binding, currentKey, isUserBinding, isActiveInternal, isActiveExternal = api:GetRingBinding("ringName"[, bindingIndex])', 2)
|
|
local rprop = assert(OR_Rings[ringName], 'Ring %q is not defined', 2, ringName)
|
|
local b1, b2, isUser = getRingBindings(ringName)
|
|
local iid, secK, bindSuffix = rprop.internalID, "bind", ""
|
|
if (b1 ~= nil) == (bidx == 2) then
|
|
b1, isUser, secK, bindSuffix = b2, true, "bind2", "b"
|
|
end
|
|
local secRingData = OR_SecEnv.ORL_RingData[iid]
|
|
local curKey = secRingData and secRingData[secK]
|
|
local curAct = curKey and GetBindingAction(curKey, 1)
|
|
curAct = (curKey == nil) or (curAct == ("CLICK ORL_RProxy" .. iid .. ":r" .. iid .. bindSuffix)) or curAct
|
|
return b1, curKey, isUser, curKey ~= nil, curAct
|
|
end
|
|
function private:SetRingBinding(ringName, bidx, bind)
|
|
assert(type(ringName) == "string" and (bidx == 1 or bidx == 2) and (type(bind) == "string" or bind == false or bind == nil),
|
|
'Syntax: api:SetRingBinding("ringName", bindingIndex, "binding" or false or nil)', 2)
|
|
local rprop = assert(OR_Rings[ringName], "Ring %q is not defined", 2, ringName)
|
|
local b1, b2, _b1u, b1c = getRingBindings(ringName)
|
|
local niOffset = b2 and b1 == nil and 3 or -1 -- would GetRingBinding rewrite indices?
|
|
local nb1, nb2 = select(niOffset + bidx + bidx, bind,b2, b1c,bind, bind,nil, b2,bind)
|
|
if nb2 and not nb1 and (bidx ~= 2 or b1 == nil) then
|
|
nb1, nb2 = nb2, nil
|
|
elseif nb1 == nb2 then
|
|
nb2 = nil
|
|
end
|
|
nb2 = nb2 or nil
|
|
if (nb1 == b1c and nb2 == b2) or (nb1 == b2 and nb2 == b1c) then
|
|
return
|
|
end
|
|
configInstance.Bindings[ringName], configInstance.Bindings2[ringName] = nb1, nb2
|
|
local obind = b1 ~= nb1 and b1 ~= nb2 and b1 or b2 ~= nb1 and b2 ~= nb2 and b2
|
|
for i=1, #OR_Rings do
|
|
local ikey = OR_Rings[i]
|
|
local ib1, ib2, ib1u = getRingBindings(ikey)
|
|
if ikey ~= ringName and (bind and (ib1 == bind or ib2 == bind) or obind and (ib1 == obind or ib2 == obind)) then
|
|
if bind then
|
|
if ib2 == bind then
|
|
configInstance.Bindings2[ikey], ib2 = nil, nil
|
|
end
|
|
if ib1 == bind and ib1u then
|
|
configInstance.Bindings[ikey], configInstance.Bindings2[ikey] = ib2, nil
|
|
end
|
|
end
|
|
OR_SyncRingBinding(ikey, OR_Rings[ikey])
|
|
end
|
|
end
|
|
OR_SyncRingBinding(ringName, rprop)
|
|
end
|
|
function private:ProfileExists(ident)
|
|
return configRoot.ProfileStorage[ident] ~= nil
|
|
end
|
|
function private:GetAllProfiles()
|
|
if not configInstance then return end
|
|
local r = {}
|
|
for k in pairs(configRoot.ProfileStorage) do
|
|
r[#r+1] = k
|
|
end
|
|
table.sort(r, getProfileIdentComparator())
|
|
return r
|
|
end
|
|
function private:GetSpecProfiles()
|
|
return retProfiles(getNumSpecs(), 1)
|
|
end
|
|
function private:SetSpecProfiles(...)
|
|
assert(select("#", ...) == getNumSpecs(), 'SetSpecProfiles(...): improper argument count', 2)
|
|
for i=1, getNumSpecs() do
|
|
configRoot.CharProfiles[getSpecCharIdent(i)] = normalizeStoredProfileIdent(select(i, ...))
|
|
end
|
|
local np = getProfileForSpec()
|
|
if np ~= activeProfile then
|
|
OR_SwitchProfile(np)
|
|
end
|
|
end
|
|
function private:SwitchProfile(ident, inherit)
|
|
assert(type(ident) == "string" and (inherit == nil or type(inherit) == "boolean" or type(inherit) == "table"), 'Syntax: api:SwitchProfile("profile"[, deriveFromCurrent or profileData])', 2)
|
|
if type(inherit) == "table" then
|
|
local data = copy(inherit)
|
|
if data._usedBy then
|
|
for _, charid in pairs(data._usedBy) do
|
|
configRoot.CharProfiles[charid] = ident
|
|
end
|
|
data._usedBy = nil
|
|
end
|
|
configRoot.ProfileStorage[ident] = data
|
|
elseif not configRoot.ProfileStorage[ident] then
|
|
configRoot.ProfileStorage[ident] = inherit and copy(configInstance) or {}
|
|
end
|
|
OR_SwitchProfile(ident)
|
|
configRoot.CharProfiles[getSpecCharIdent()] = normalizeStoredProfileIdent(activeProfile)
|
|
end
|
|
function private:ExportProfile(ident)
|
|
assert(type(ident) == "string" or ident == nil, 'Syntax: profileData = api:ExportProfile(["profile"])', 2)
|
|
assert(ident == nil or configRoot.ProfileStorage[ident], 'Profile %q does not exist.', 2, ident)
|
|
if ident == nil then OR_SaveCurrentProfile() end
|
|
local data = copy(ident == nil and configInstance or configRoot.ProfileStorage[ident])
|
|
if configRoot.CharProfiles then
|
|
local id, ni, usedBy = ident or activeProfile, 1, {}
|
|
for k,v in pairs(configRoot.CharProfiles) do
|
|
if v == id then
|
|
usedBy[ni], ni = k, ni + 1
|
|
end
|
|
end
|
|
data._usedBy = ni > 1 and usedBy or nil
|
|
end
|
|
return data
|
|
end
|
|
function private:DeleteProfile(ident)
|
|
assert(type(ident) == "string", 'Syntax: api:DeleteProfile("profile")', 2)
|
|
local oldP = configRoot.ProfileStorage[ident]
|
|
if not oldP then
|
|
return
|
|
end
|
|
if configRoot.CharProfiles then
|
|
for k,v in pairs(configRoot.CharProfiles) do
|
|
if v == ident then configRoot.CharProfiles[k] = nil end
|
|
end
|
|
end
|
|
configRoot.ProfileStorage[ident] = nil
|
|
if configInstance == oldP then private:SwitchProfile("default") end
|
|
end
|
|
function private:ResetRingBindings()
|
|
wipe(configInstance.Bindings)
|
|
OR_ForceResync()
|
|
end
|
|
function private:ResetOptions(includePerRing)
|
|
assert(type(includePerRing) == "boolean" or includePerRing == nil, "Syntax: api:ResetOptions([includePerRing])", 2)
|
|
for k, v in pairs(defaultConfig) do
|
|
local iv = configInstance[k]
|
|
configInstance[k] = nil
|
|
if optionValidators[k] and (iv ~= nil and iv ~= v) then
|
|
securecall(optionValidators[k], k, v)
|
|
end
|
|
end
|
|
if includePerRing then
|
|
configInstance.RingOptions = {}
|
|
end
|
|
OR_ForceResync()
|
|
end
|
|
function private:GetOpenRing(optTable)
|
|
if type(optTable) == "table" then
|
|
for k in pairs(defaultConfig) do
|
|
optTable[k] = OR_GetRingOption(OR_ActiveRingName or "default", k)
|
|
end
|
|
end
|
|
return OR_ActiveRingName, OR_ActiveSliceCount, OR_SecEnv.activeRing and OR_SecEnv.activeRing.ofsDeg or 0
|
|
end
|
|
function private:GetOpenRingSlice(id)
|
|
if type(id) ~= "number" or id < 1 or id > OR_ActiveSliceCount then return false end
|
|
local sbt, act, tok, atype, nestLevel = OR_SecEnv.activeRing.SliceBinding, OR_FindFinalAction(OR_ActiveCollectionID, id)
|
|
local nt = OR_SecEnv.collections[OR_SecEnv.collections[OR_ActiveCollectionID][id]]
|
|
return act, tok, sbt and sbt[id], sbt and sbt[id+0.5], nt and #nt or 0, atype, nestLevel and nestLevel > 1
|
|
end
|
|
function private:GetOpenRingSliceAction(id, id2)
|
|
if id < 1 or id > OR_ActiveSliceCount then return end
|
|
local s, tok, atype, nestLevel = OR_FindFinalAction(OR_ActiveCollectionID, id, id2 and OR_SecEnv.ctokens[OR_ActiveCollectionID][id] or nil, (id2 or 1)-1)
|
|
if type(s) == "number" then
|
|
if atype == "jump" then
|
|
local icon, aid, tok2, _ = nil, OR_FindFinalAction(s, 1, nil, 0, true)
|
|
if type(aid) == "number" then
|
|
_, _, icon = AB:GetSlotInfo(aid, OR_ModifierLockState)
|
|
end
|
|
local rid = OR_SecEnv.ORL_RingData[OR_SecEnv.ORL_KnownCollections[s]]
|
|
rid = OR_Rings[rid and rid.name]
|
|
local rname = rid and rid.name
|
|
return tok, true, nestLevel and nestLevel > (id2 and 2 or 1) and 4096+8192 or 4096, icon or [[Interface\AddOns\OPie\gfx\opie_ring_icon]], rname or L"Open nested ring", tok2, 0, 0
|
|
end
|
|
return tok, AB:GetSlotInfo(s, OR_ModifierLockState)
|
|
end
|
|
return tok, false, 0, [[Interface\Icons\INV_Misc_QuestionMark]], "Unknown Slice", 0, 0, 0
|
|
end
|
|
function private:GetCurrentInputs()
|
|
if OR_SecEnv.AI_NoPointer then
|
|
return "nopoint", nil, 0, false, 0
|
|
end
|
|
local aframe, imode, cx, cy = OR_SecCore, "cursor", GetCursorPosition()
|
|
local scale, l, b, w, h = aframe:GetEffectiveScale(), aframe:GetRect()
|
|
local dx, dy = (cx / scale) - (l + w / 2), (cy / scale) - (b + h / 2)
|
|
local radius2 = dx*dx+dy*dy
|
|
local isActiveRadius, isCenterRadius = radius2 >= 1600, radius2 <= 400
|
|
|
|
local go, stl = OR_SecEnv.ORL_GlobalOptions, 0
|
|
if go.PadSupportMode == "freelook" and C_GamePad.IsEnabled() and IsGamePadFreelookEnabled() and not IsGamePadCursorControlEnabled() then
|
|
local ms = C_GamePad.GetDeviceMappedState()
|
|
local st = ms and ms.sticks
|
|
st = st and st[go.PSStickIndex]
|
|
if st then
|
|
dx, dy, stl = st.x, st.y, st.len
|
|
imode, isActiveRadius, isCenterRadius = "stick", stl > 0.25, stl < 0.01
|
|
end
|
|
end
|
|
|
|
local aidx, qidx = OR_SecEnv.fastClick, nil
|
|
if aidx then
|
|
local ar = OR_SecEnv.activeRing
|
|
if ar.MotionAction and OR_SecEnv.AI_MotionArmedFC and imode ~= "stick" then
|
|
qidx = aidx
|
|
elseif ar.CenterAction and isCenterRadius then
|
|
qidx = aidx
|
|
end
|
|
end
|
|
return imode, qidx, atan2(dy, dx) % 360, isActiveRadius, stl
|
|
end
|
|
function private:FutureDeprecationError(msg, depth, a,b,c,d)
|
|
local sev = getTimeBand(a,b,c,d)
|
|
if sev == 2 then
|
|
error(msg, 1 + (depth or 1))((0)[0])
|
|
elseif sev == 1 then
|
|
securecall(error, msg, 2 + (depth or 1))
|
|
end
|
|
end
|
|
|
|
-- Public API
|
|
function api:SetRing(name, actionId, props)
|
|
assert(type(name) == "string" and (actionId == nil or (type(props) == "table" or type(actionId) == "number")), 'Syntax: OPie:SetRing("ringName"[, actionId, propsTable])', 2)
|
|
if actionId then
|
|
OR_SyncRing(name, actionId, props)
|
|
elseif OR_Rings[name] then
|
|
OR_DeleteRing(name, OR_Rings[name])
|
|
end
|
|
end
|
|
function api:GetNumRings()
|
|
return #OR_Rings
|
|
end
|
|
function api:GetRingInfo(ring)
|
|
assert(type(ring) == "number" or type(ring) == "string", 'Syntax: name, key, macro, flags = OPie:GetRingInfo(index or "ringName")', 2)
|
|
local key = type(ring) == "string" and OR_Rings[ring] and ring or OR_Rings[ring]
|
|
if not key then return end
|
|
local props = OR_Rings[key]
|
|
return props.name, key, "/click "..OR_OpenProxy:GetName().." "..key, (props.internal and 1 or 0)
|
|
end
|
|
function api:IterateRings(includeInternalRings)
|
|
local ot = {}
|
|
for i=1,#OR_Rings do
|
|
local props = OR_Rings[OR_Rings[i]]
|
|
if props and (includeInternalRings or not props.internal) then
|
|
ot[#ot+1] = props
|
|
end
|
|
end
|
|
table.sort(ot, cmpRingProps)
|
|
local p, props = 1
|
|
return function()
|
|
props, p = ot[p], p + 1
|
|
if props then
|
|
return props.internalName, props.name or props.internalName, props.sortScope, props.internal and 1 or 0
|
|
end
|
|
end
|
|
end
|
|
function api:IsKnownRingName(ringName)
|
|
assert(type(ringName) == "string", 'Syntax: isKnown = OPie:IsKnownRingName("ringName")', 2)
|
|
if OR_Rings[ringName] then return true end
|
|
for _, v in pairs(configRoot.ProfileStorage) do
|
|
if type(v.Bindings) == "table" and v.Bindings[ringName] then
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
function api:GetCurrentProfile()
|
|
return activeProfile
|
|
end
|
|
function api:GetVersion()
|
|
return C_AddOns.GetAddOnMetadata(ADDON, "Version") or "?", MAJ, REV
|
|
end
|
|
|
|
-- HIDDEN, UNSUPPORTED METHODS: May vanish at any time.
|
|
local hum = {}
|
|
setmetatable(api, {__index=hum})
|
|
hum.HUM = hum
|
|
hum.NotGameTooltip = T.NotGameTooltip
|
|
hum.GetOption = private.GetOption
|
|
hum.GetRingBinding = private.GetRingBinding
|
|
hum.GetOpenRing = private.GetOpenRing
|
|
hum.GetOpenRingSlice = private.GetOpenRingSlice
|
|
hum.GetOpenRingSliceAction = private.GetOpenRingSliceAction
|
|
function hum:OverrideRingBinding(ringName, bind)
|
|
assert(type(ringName) == "string" and (bind == nil or type(bind) == "string"), 'Syntax: api:OverrideRingBinding("ringName", "binding")', 2)
|
|
assert(OR_Rings[ringName], 'Ring %q is not defined', 2, ringName)
|
|
if OR_SecEnv.bindOverrides[OR_Rings[ringName].internalID] ~= bind then
|
|
OR_SecCore:Execute(("owner:Run(ORL_RegisterOverride, %d, %s)"):format(OR_Rings[ringName].internalID, bind and safequote(bind) or "nil"))
|
|
end
|
|
end
|
|
function hum:SetRingOpensAtMousePreference(ringName, pref)
|
|
assert(type(ringName) == "string" and type(pref) == "boolean", 'Syntax: api:SetRingOpensAtMousePreference("ringName", preferAtMouse)')
|
|
if select(3, OR_GetRingOption(ringName, "RingAtMouse")) == nil then
|
|
private:SetOption("RingAtMouse", pref, ringName)
|
|
end
|
|
end
|
|
hum.GetSVState = private.GetSVState
|
|
|
|
for k,v in pairs(api) do
|
|
if private[k] == nil then
|
|
private[k] = v
|
|
end
|
|
end
|
|
|
|
T.FutureDeprecationError = private.FutureDeprecationError
|
|
_G.OPie, T.OPieCore = api, private
|