Quest tooltip fixes and addition of theme switch

This commit is contained in:
User
2026-08-15 21:29:35 -05:00
commit 76505bf183
161 changed files with 1527477 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
* -text
+160
View File
@@ -0,0 +1,160 @@
QuestieOcto.API = QuestieOcto.API or {}
local A = QuestieOcto.API
A.valid = false
A.missing = {}
A.optional = {}
local function Has(tableValue, functionName)
return tableValue and type(tableValue[functionName])=="function"
end
function A:Validate()
self.missing = {}
local required = {
{ "C_QuestLog.GetQuestIDForLogIndex", C_QuestLog, "GetQuestIDForLogIndex" },
{ "C_QuestLog.GetLogIndexForQuestID", C_QuestLog, "GetLogIndexForQuestID" },
{ "C_QuestLog.IsOnQuest", C_QuestLog, "IsOnQuest" },
{ "C_Map.GetBestMapForUnit", C_Map, "GetBestMapForUnit" },
{ "C_GossipInfo.GetAvailableQuests", C_GossipInfo, "GetAvailableQuests" },
{ "C_CreatureInfo.GetCreatureID", C_CreatureInfo, "GetCreatureID" },
{ "C_GameObjectInfo.GetGameObjectInfoByID", C_GameObjectInfo, "GetGameObjectInfoByID" },
{ "C_Item.GetItemNameByID", C_Item, "GetItemNameByID" },
}
for _,req in pairs(required) do
if not Has(req[2],req[3]) then table.insert(self.missing,req[1]) end
end
self.valid = table.getn(self.missing)==0
self.optional = {
coroutines = coroutine and type(coroutine.create)=="function",
hooksecurefunc = type(hooksecurefunc)=="function",
tooltipUnit = GameTooltip and type(GameTooltip.GetUnitGUID)=="function",
tooltipObject = GameTooltip and type(GameTooltip.GetGameObject)=="function",
tooltipItem = GameTooltip and type(GameTooltip.GetItem)=="function",
gossipActive = Has(C_GossipInfo,"GetActiveQuests"),
questDetails = Has(C_QuestLog,"GetQuestDetails"),
questObjectives = Has(C_QuestLog,"GetQuestObjectives"),
questsCompleted = type(GetQuestsCompleted)=="function" or Has(C_QuestLog,"GetQuestsCompleted"),
queryQuestsCompleted = type(QueryQuestsCompleted)=="function",
questFlaggedCompleted = type(IsQuestFlaggedCompleted)=="function" or Has(C_QuestLog,"IsQuestFlaggedCompleted"),
mapWorldSize = Has(C_Map,"GetMapWorldSize"),
}
return self.valid
end
function A:GetQuestIDForLogIndex(index)
if not self.valid then return nil end
return C_QuestLog.GetQuestIDForLogIndex(index)
end
-- Questie-facing normalized quest-log info.
--
-- Questie 5.2.3/6.0.0 and Questie 3.3.5 all consume the normalized fields
-- title, level, tag, isHeader, isCollapsed, isComplete, frequency, questID.
--
-- The raw client signature differs by expansion. For our actual target
-- Interface 11200, the supplied pfQuest Vanilla compatibility layer confirms:
-- title, level, tag, isHeader, isCollapsed, isComplete = GetQuestLogTitle(index)
-- There is NO suggestedGroup return on Vanilla 1.12.
--
-- Questie 3.3.5's raw suggestedGroup handling is a WotLK-era compatibility
-- detail, so applying it directly to Turtle 11200 shifts completion by one
-- field and makes completed quests look incomplete.
function A:GetQuestLogInfo(index)
if not GetQuestLogTitle then return nil end
local _,_,_,client=GetBuildInfo()
client=tonumber(client) or 11200
local title,level,tag,isHeader,isCollapsed,isComplete,isDaily,rawQuestID
if client<=11200 then
title,level,tag,isHeader,isCollapsed,isComplete=GetQuestLogTitle(index)
else
local suggestedGroup
title,level,tag,suggestedGroup,isHeader,isCollapsed,isComplete,isDaily,rawQuestID=GetQuestLogTitle(index)
end
local questID=self:GetQuestIDForLogIndex(index) or tonumber(rawQuestID)
return {
title=title,
level=level,
tag=tag,
isHeader=isHeader and true or false,
isCollapsed=isCollapsed and true or false,
isComplete=isComplete,
isDaily=isDaily,
questID=questID
}
end
function A:GetLogIndexForQuestID(questID)
if not self.valid then return nil end
return C_QuestLog.GetLogIndexForQuestID(questID)
end
function A:IsOnQuest(questID)
if not self.valid then return false end
return C_QuestLog.IsOnQuest(questID) and true or false
end
function A:GetBestMapForPlayer()
if not self.valid then return nil end
return C_Map.GetBestMapForUnit("player")
end
function A:GetQuestObjectives(questID,questLogIndex)
if C_QuestLog and type(C_QuestLog.GetQuestObjectives)=="function" then
-- Questie 3.3.5's Vanilla compatibility implementation accepts the log
-- index as a second argument; modern-style implementations ignore extras.
return C_QuestLog.GetQuestObjectives(questID,questLogIndex)
end
return nil
end
function A:QueryQuestsCompleted()
if type(QueryQuestsCompleted)~="function" then return false end
local ok=pcall(QueryQuestsCompleted)
return ok and true or false
end
function A:GetQuestsCompleted()
if type(GetQuestsCompleted)=="function" then
local ok,result=pcall(GetQuestsCompleted)
if ok and type(result)=="table" then return result end
-- Some compatibility layers use the older "fill table" form.
local target={}
ok=pcall(GetQuestsCompleted,target)
if ok and next(target) then return target end
end
if C_QuestLog and type(C_QuestLog.GetQuestsCompleted)=="function" then
local ok,result=pcall(C_QuestLog.GetQuestsCompleted)
if ok and type(result)=="table" then return result end
end
return nil
end
function A:IsQuestFlaggedCompleted(questID)
if type(IsQuestFlaggedCompleted)=="function" then
local ok,result=pcall(IsQuestFlaggedCompleted,questID)
if ok then return result and true or false end
end
if C_QuestLog and type(C_QuestLog.IsQuestFlaggedCompleted)=="function" then
local ok,result=pcall(C_QuestLog.IsQuestFlaggedCompleted,questID)
if ok then return result and true or false end
end
return nil
end
+374
View File
@@ -0,0 +1,374 @@
SLASH_QUESTIEOCTO1="/qo"
SLASH_QUESTIEOCTO2="/questieocto"
local function Bool(v)
return v and "true" or "false"
end
SlashCmdList["QUESTIEOCTO"]=function(msg)
msg=string.lower(msg or "")
local creatureStart,creatureEnd,creatureID=string.find(msg,"^creature%s+(%d+)$")
local minimapStart,minimapEnd,minimapArg=string.find(msg,"^minimap%s+(.+)$")
if msg=="visualdiag" then
local mm=QuestieOcto.Minimap
local shown=0
local objective=0
local colored=0
local glowing=0
if mm and mm.frames then
for key,pin in pairs(mm.frames) do
if pin:IsShown() then
shown=shown+1
local isObjective=pin.role=="objectiveCreature" or
pin.role=="objectiveObject" or
pin.role=="objectiveItemSource"
if isObjective then
objective=objective+1
local r=pin.iconR or 1
local g=pin.iconG or 1
local b=pin.iconB or 1
local glowShown=pin.glowTexture and pin.glowTexture:IsShown()
if r~=1 or g~=1 or b~=1 then colored=colored+1 end
if glowShown then glowing=glowing+1 end
QuestieOcto:Print("VIS pin role/q/obj/rgb/glow="..
tostring(pin.role).."/"..
tostring(pin.questID or 0).."/"..
tostring((pin.entries and next(pin.entries)) and "yes" or "no").."/"..
string.format("%.2f,%.2f,%.2f",r,g,b).."/"..
tostring(glowShown and true or false))
end
end
end
end
QuestieOcto:Print("VIS summary shown/objective/colored/glowing="..
tostring(shown).."/"..tostring(objective).."/"..
tostring(colored).."/"..tostring(glowing))
elseif msg=="" or msg=="options" or msg=="config" then
QuestieOcto.Options:Toggle()
elseif msg=="quests" then
if QuestieOcto.QuestResearch then QuestieOcto.QuestResearch:OpenWindow() end
elseif msg=="help" then
QuestieOcto:Print("/qo -- toggle Questie-Octo options")
QuestieOcto:Print("/qo quests -- open the Quests browser")
QuestieOcto:Print("/qo options, /qo info, /qo minimap, /qo api, /qo resync, /qo questlog, /qo map")
elseif msg=="info" then
QuestieOcto:Print(tostring(QuestieOcto.version).." infrastructure diagnostic")
QuestieOcto:Print("enabled="..Bool(QuestieOcto.enabled).." ready="..Bool(QuestieOcto.ready)..
" ClassicAPI="..Bool(QuestieOcto.API.valid))
local q=QuestieOcto.DatabaseAPI and QuestieOcto.DatabaseAPI:GetQuestCount() or 0
QuestieOcto:Print("DB ready="..Bool(QuestieOcto.Database.ready)..
" mergeJob="..tostring(QuestieOcto.Database.jobIndex).."/"..tostring(QuestieOcto.Database.stats.jobs)..
" merged="..tostring(QuestieOcto.Database.stats.merged)..
" quests="..tostring(q))
local ql=QuestieOcto.QuestLog
local activeComplete=0
local activeObjectives=0
for _,state in pairs(ql.active or {}) do
if state.complete then activeComplete=activeComplete+1 end
activeObjectives=activeObjectives+table.getn(state.objectives or {})
end
QuestieOcto:Print("QuestLog running="..Bool(QuestieOcto.QuestLog.running)..
" entries="..tostring(QuestieOcto.QuestLog.stats.entries)..
" quests="..tostring(QuestieOcto.QuestLog.stats.quests)..
" resolvedIDs="..tostring(QuestieOcto.QuestLog.stats.resolved)..
" activeComplete/objectives="..tostring(activeComplete).."/"..tostring(activeObjectives)..
" refreshes="..tostring(QuestieOcto.QuestLog.stats.refreshes)..
" acceptedFast="..tostring(QuestieOcto.QuestLog.stats.acceptedFastRefreshes or 0)..
" acceptedEvt/resolved/fromIndex/last="..
tostring(QuestieOcto.QuestLog.stats.acceptedEvents or 0).."/"..
tostring(QuestieOcto.QuestLog.stats.acceptedResolved or 0).."/"..
tostring(QuestieOcto.QuestLog.stats.acceptedFromIndex or 0).."/"..
tostring(QuestieOcto.QuestLog.stats.lastAcceptedQuestID or 0)..
" acceptedHint/prime/poll="..
tostring(QuestieOcto.QuestLog.stats.acceptedHintsUsed or 0).."/"..
tostring(QuestieOcto.QuestLog.stats.acceptedPrimes or 0).."/"..
tostring(QuestieOcto.QuestLog.stats.acceptedPolls or 0)..
" removedEvt/resolved/fromIndex/last="..
tostring(QuestieOcto.QuestLog.stats.removedEvents or 0).."/"..
tostring(QuestieOcto.QuestLog.stats.removedResolved or 0).."/"..
tostring(QuestieOcto.QuestLog.stats.removedFromIndex or 0).."/"..
tostring(QuestieOcto.QuestLog.stats.lastRemovedQuestID or 0))
QuestieOcto:Print("QuestLog complete raw/objectives/money="..tostring(ql.completeRaw or 0).."/"..tostring(ql.completeObjectives or 0).."/"..tostring(ql.completeMoney or 0))
local cp=QuestieOcto.Completion
local completeIDs={}
for questID,state in pairs(ql.active or {}) do
if state.complete then table.insert(completeIDs,tostring(questID)) end
end
table.sort(completeIDs)
QuestieOcto:Print("QuestLog complete IDs="..table.concat(completeIDs,","))
QuestieOcto:Print("QuestLog complete raw/objectives/noObjectives/money="..
tostring(QuestieOcto.QuestLog.stats.completeRaw or 0).."/"..
tostring(QuestieOcto.QuestLog.stats.completeObjectives or 0).."/"..
tostring(QuestieOcto.QuestLog.stats.completeNoObjectives or 0).."/"..
tostring(QuestieOcto.QuestLog.stats.completeMoney or 0))
QuestieOcto:Print("Completion ready="..Bool(cp.ready)..
" packets="..tostring(cp.receivedPackets)..
" completed="..tostring(cp:Count())..
" turnIns="..tostring(cp.stats.turnIns or 0))
local av=QuestieOcto.AvailableQuests
local st=av.stats or {}
QuestieOcto:Print("AvailableQuests ready="..Bool(av.ready)..
" running="..Bool(av.running)..
" scan="..tostring(math.min(av.pos or 0,table.getn(av.queue or {}))).."/"..tostring(table.getn(av.queue or {}))..
" available="..tostring(st.available or 0))
QuestieOcto:Print("Availability rejected completed/active/level/pre/exclusive/race/class/event/noStarter="..
tostring(st.completed or 0).."/"..tostring(st.active or 0).."/"..tostring(st.level or 0).."/"..
tostring(st.prerequisite or 0).."/"..tostring(st.exclusive or 0).."/"..
tostring(st.race or 0).."/"..tostring(st.class or 0).."/"..tostring(st.noStarter or 0))
local ob=QuestieOcto.Objectives
local objectiveQuestModels=0
for _ in pairs(ob.byQuest or {}) do objectiveQuestModels=objectiveQuestModels+1 end
QuestieOcto:Print("Objectives ready/running="..Bool(ob.ready).."/"..Bool(ob.running)..
" questModels="..tostring(objectiveQuestModels)..
" scanned="..tostring(ob.stats.quests or 0)..
" creature/object/item/itemSources="..
tostring(ob.stats.creature or 0).."/"..tostring(ob.stats.object or 0).."/"..
tostring(ob.stats.item or 0).."/"..tostring(ob.stats.itemSources or 0)..
" mapped/unmapped="..tostring(ob.stats.mapped or 0).."/"..tostring(ob.stats.unmapped or 0)..
" direct/fallback/typeMismatch="..tostring(ob.stats.direct or 0).."/"..
tostring(ob.stats.fallback or 0).."/"..tostring(ob.stats.typeMismatch or 0)..
" fuzzy/single="..tostring(ob.stats.fuzzy or 0).."/"..tostring(ob.stats.single or 0)..
" rebuilds/waits="..tostring(ob.stats.rebuilds or 0).."/"..tostring(ob.stats.dependencyWaits or 0))
local it=QuestieOcto.ItemStarts
QuestieOcto:Print("ItemStarts ready="..Bool(it.ready)..
" quests/items/creatureSources/objectSources="..
tostring(it.stats.quests or 0).."/"..tostring(it.stats.starterItems or 0).."/"..
tostring(it.stats.creatureSources or 0).."/"..tostring(it.stats.objectSources or 0))
local nd=QuestieOcto.Nodes
QuestieOcto:Print("Nodes ready="..Bool(nd.ready)..
" total="..tostring(nd.stats.total or 0)..
" availCreature/object/itemStart="..
tostring(nd.stats.availableCreature or 0).."/"..tostring(nd.stats.availableObject or 0).."/"..
tostring(nd.stats.itemStart or 0))
QuestieOcto:Print("Nodes active creature/object/itemSource/turnin="..
tostring(nd.stats.objectiveCreature or 0).."/"..tostring(nd.stats.objectiveObject or 0).."/"..
tostring(nd.stats.objectiveItemSource or 0).."/"..tostring(nd.stats.turnin or 0))
local mp=QuestieOcto.Map
QuestieOcto:Print("Map active/created/reused/hidden="..
tostring(mp.stats.active or 0).."/"..tostring(mp.stats.created or 0).."/"..
tostring(mp.stats.reused or 0).."/"..tostring(mp.stats.hidden or 0)..
" syncs="..tostring(mp.stats.syncs or 0))
QuestieOcto:Print("Map visible available/itemStart/objective/turnin="..
tostring(mp.stats.visibleAvailable or 0).."/"..tostring(mp.stats.visibleItemStart or 0).."/"..
tostring(mp.stats.visibleObjective or 0).."/"..tostring(mp.stats.visibleTurnin or 0)..
" mapID="..tostring(mp.mapID)..
" inputNodes="..tostring(mp.stats.inputNodes or 0)..
" mergedPins="..tostring(mp.stats.multiEntryPins or 0))
QuestieOcto:Print("ItemStart rawNodes/areaPins="..
tostring(mp.stats.itemStartRawNodes or 0).."/"..tostring(mp.stats.itemStartAreaPins or 0))
local pm=QuestieOcto.PreparedMap
QuestieOcto:Print("Prepared maps/descriptors/running/currentReady="..
tostring(pm.stats.preparedMaps or 0).."/"..tostring(pm.stats.descriptors or 0).."/"..
Bool(pm.running).."/"..Bool(pm.stats.currentReady)..
" revision/bumps="..tostring(pm.stateRevision or 0).."/"..tostring(pm.stats.revisionBumps or 0))
QuestieOcto:Print("Map prepared hits/misses/descriptors="..
tostring(mp.stats.preparedHits or 0).."/"..tostring(mp.stats.preparedMisses or 0).."/"..
tostring(mp.stats.preparedDescriptors or 0))
local ci=QuestieOcto.MapCandidateIndex
QuestieOcto:Print("CandidateIndex ready/running/scanned/maps/links/currentReady="..
Bool(ci.ready).."/"..Bool(ci.running).."/"..tostring(ci.stats.scanned or 0).."/"..
tostring(ci.stats.maps or 0).."/"..tostring(ci.stats.links or 0).."/"..Bool(ci.stats.currentReady))
local zb=QuestieOcto.ZoneBootstrap
QuestieOcto:Print("ZoneBootstrap ready/running/indexed/map/requested/scanned/candidates/available/nodes/itemSources="..
Bool(zb.ready).."/"..Bool(zb.running).."/"..Bool(zb.stats.indexed).."/"..tostring(zb.mapID).."/"..tostring(zb.requestedMapID).."/"..
tostring(zb.stats.scanned or 0).."/"..tostring(zb.stats.candidates or 0).."/"..
tostring(zb.stats.available or 0).."/"..tostring(zb.stats.nodes or 0).."/"..
tostring(zb.stats.itemSources or 0))
QuestieOcto:Print("ZoneBootstrap requests/cancelled mapPriorityRequests="..
tostring(zb.stats.requests or 0).."/"..tostring(zb.stats.cancelled or 0).."/"..
tostring(mp.stats.mapPriorityRequests or 0))
QuestieOcto:Print("Quest visibility policy=low-level quests shown")
QuestieOcto:Print("Quest refresh policy=Questie immediate unload + 335 fast refresh")
QuestieOcto:Print("Scheduler policy=Questie-335 every-frame thread slices")
QuestieOcto:Print("Startup policy=immediate completion/log + 335 fast initial availability")
QuestieOcto:Print("Quest accept policy=immediate unload + fast objective-cache retry")
local mm=QuestieOcto.Minimap
QuestieOcto:Print("Minimap enabled/active/map/refreshes/updates="..
Bool(mm and mm.enabled).."/"..tostring(mm and mm.stats.active or 0).."/"..
tostring(mm and mm.mapID or "nil").."/"..
tostring(mm and mm.stats.refreshes or 0).."/"..
tostring(mm and mm.stats.positionUpdates or 0))
local ms=QuestieOcto.MinimapSettings
QuestieOcto:Print("Minimap mapContext restores/reason="..
tostring(mm and mm.stats.mapContextRestores or 0).."/"..
tostring(mm and mm.stats.lastMapContextReason or "none"))
QuestieOcto:Print("Scale rescalePasses map/minimap="..
tostring(QuestieOcto.Map.stats.rescalePasses or 0).."/"..
tostring(QuestieOcto.Minimap.stats.rescalePasses or 0))
QuestieOcto:Print("Scale resize map count/last="..
tostring(QuestieOcto.Map.stats.scaleResizes or 0).."/"..
tostring(QuestieOcto.Map.stats.lastScaleSize or 0)..
" minimap count/last="..
tostring(QuestieOcto.Minimap.stats.scaleResizes or 0).."/"..
tostring(QuestieOcto.Minimap.stats.lastScaleSize or 0))
QuestieOcto:Print("Map settings enabled/scale/objectives/turnins/available="..
Bool(ms:Get("enableMapIcons")).."/"..
tostring(ms:Get("globalScale")).."/"..
Bool(ms:Get("enableObjectives")).."/"..
Bool(ms:Get("enableTurnins")).."/"..
Bool(ms:Get("enableAvailable")))
QuestieOcto:Print("Minimap settings enabled/scale="..
Bool(ms:Get("enableMiniMapIcons")).."/"..tostring(ms:Get("globalMiniMapScale")))
QuestieOcto:Print("Minimap unload policy=Questie quest-wide world+minimap + prepared-cache purge")
QuestieOcto:Print("Minimap visibility policy=all marker centers visible to minimap edge")
QuestieOcto:Print("Objective icon policy=Questie slay/loot/interact by objective kind")
QuestieOcto:Print("Turnin icon policy=Questie separate exact frame + complete overlay 6 above available 5")
QuestieOcto:Print("Quest remove policy=Questie immediate quest-wide unload before redraw")
QuestieOcto:Print("Minimap visible available/itemStart/objective/turnin="..
tostring(mm and mm.stats.visibleAvailable or 0).."/"..
tostring(mm and mm.stats.visibleItemStart or 0).."/"..
tostring(mm and mm.stats.visibleObjective or 0).."/"..
tostring(mm and mm.stats.visibleTurnin or 0))
local op=QuestieOcto.Options
QuestieOcto:Print("Options Ace initialized/shown/opens/closes/changes="..
Bool(op and op.initialized).."/"..
Bool(op and op.configFrame and op.configFrame:IsShown()).."/"..
tostring(op and op.stats.opens or 0).."/"..tostring(op and op.stats.closes or 0).."/"..
tostring(op and op.stats.changes or 0))
QuestieOcto:Print("Options tabs=General/Map/Minimap/Tooltips/Advanced/Quests")
QuestieOcto:Print("Options visuals glowMap/glowMini/colorMap/colorMini="..
tostring(QuestieOcto.MinimapSettings:Get("alwaysGlowMap")).."/"..
tostring(QuestieOcto.MinimapSettings:Get("alwaysGlowMinimap")).."/"..
tostring(QuestieOcto.MinimapSettings:Get("questObjectiveColors")).."/"..
tostring(QuestieOcto.MinimapSettings:Get("questMinimapObjectiveColors")))
local miniGlow=0
local miniColor=0
if QuestieOcto.Minimap and QuestieOcto.Minimap.frames then
for _,pin in pairs(QuestieOcto.Minimap.frames) do
if pin.glowTexture and pin.glowTexture:IsShown() then miniGlow=miniGlow+1 end
if (pin.iconR and pin.iconR~=1) or (pin.iconG and pin.iconG~=1) or (pin.iconB and pin.iconB~=1) then
miniColor=miniColor+1
end
end
end
QuestieOcto:Print("Minimap visual applied glow/colored="..tostring(miniGlow).."/"..tostring(miniColor))
QuestieOcto:Print("Options availability lowLevel/repeatable/event/pvp="..
tostring(QuestieOcto.MinimapSettings:Get("showLowLevelQuests")).."/"..
tostring(QuestieOcto.MinimapSettings:Get("showRepeatableQuests")).."/"..
tostring(QuestieOcto.MinimapSettings:Get("showEventQuests")).."/"..
tostring(QuestieOcto.MinimapSettings:Get("showPvPRelatedQuests")))
QuestieOcto:Print("Options AceGUI/registry/dialog="..
Bool(op and op.stats.aceGUI).."/"..Bool(op and op.stats.aceRegistry).."/"..Bool(op and op.stats.aceDialog))
QuestieOcto:Print("Options lastSet key/value="..
tostring(op and op.stats.lastSetKey or "none").."/"..
tostring(op and op.stats.lastSetValue or "none"))
local gm=QuestieOcto.GameMenu
QuestieOcto:Print("GameMenu installed/clicks/anchor="..
Bool(gm and gm.installed).."/"..tostring(gm and gm.stats.clicks or 0).."/"..
tostring(gm and gm.stats.anchor or "none"))
QuestieOcto:Print("Rare tooltip policy=static DB respawn only; no kill tracking")
QuestieOcto:Print("Scheduler queued="..tostring(table.getn(QuestieOcto.Scheduler.queue))..
" executed="..tostring(QuestieOcto.Scheduler.executed))
elseif msg=="minimap" then
local ms=QuestieOcto.MinimapSettings
QuestieOcto:Print("minimap enabled="..Bool(ms:Get("enableMiniMapIcons"))..
" scale="..tostring(ms:Get("globalMiniMapScale")))
QuestieOcto:Print("/qo minimap on|off, scale <0.01-4>, reset")
elseif minimapArg then
local ms=QuestieOcto.MinimapSettings
local actionStart,actionEnd,action,value=string.find(minimapArg,"^(%S+)%s*(.*)$")
local changed=false
if action=="on" then
changed=ms:Set("enableMiniMapIcons",true)
elseif action=="off" then
changed=ms:Set("enableMiniMapIcons",false)
elseif action=="scale" then
changed=ms:Set("globalMiniMapScale",tonumber(value))
elseif action=="reset" then
ms:Reset()
changed=true
end
if changed then
QuestieOcto:Print("minimap setting updated")
else
QuestieOcto:Print("invalid minimap setting; use /qo minimap")
end
elseif msg=="api" then
local ok=QuestieOcto.API:Validate()
QuestieOcto:Print("ClassicAPI contract valid="..Bool(ok))
if not ok then
for _,name in pairs(QuestieOcto.API.missing) do
QuestieOcto:Error("missing "..name)
end
else
local o=QuestieOcto.API.optional
QuestieOcto:Print("optional: coroutines="..Bool(o.coroutines)..
" hooksecurefunc="..Bool(o.hooksecurefunc)..
" questDetails="..Bool(o.questDetails)..
" mapWorldSize="..Bool(o.mapWorldSize))
end
elseif msg=="resync" then
QuestieOcto.Completion:Start()
QuestieOcto:Print("requested Turtle completion history")
elseif msg=="questlog" then
QuestieOcto.QuestLog:Schedule(0)
QuestieOcto:Print("scheduled quest-log refresh")
elseif msg=="map" then
QuestieOcto.Map:RequestSync(true)
QuestieOcto:Print("scheduled world-map sync")
elseif creatureID then
QuestieOcto.CreatureDiagnostics:Trace(tonumber(creatureID))
elseif msg=="quest178" then
local q=QuestieOcto.QuestModel:Get(178)
if not q then
QuestieOcto:Print("quest 178 model unavailable")
else
QuestieOcto:Print("quest178 title="..tostring(q.title)..
" level="..tostring(q.level)..
" required="..tostring(q.requiredLevel))
QuestieOcto:Print("quest178 startItem="..tostring(q.starts.item and q.starts.item[1])..
" completed="..Bool(QuestieOcto.Completion:IsComplete(178))..
" available="..Bool(QuestieOcto.AvailableQuests:IsQuestAvailable(178)))
local resolved=QuestieOcto.ItemStarts.byQuest[178]
local creatures=0
local objects=0
if resolved then
for _,item in pairs(resolved.items) do
creatures=creatures+table.getn(item.creatureSources or {})
objects=objects+table.getn(item.objectSources or {})
end
end
QuestieOcto:Print("quest178 resolved item-start sources creature/object="..
tostring(creatures).."/"..tostring(objects))
end
else
QuestieOcto:Print("/qo -- options; /qo help for commands")
end
end
+41
View File
@@ -0,0 +1,41 @@
local f=CreateFrame("Frame","QuestieOctoBootstrapFrame",UIParent)
f:RegisterEvent("PLAYER_ENTERING_WORLD")
function QuestieOcto:OnFoundationServiceReady()
if self.ready then return end
if self.Database.ready and self.DatabaseAPI:IsReady() and self.Completion.ready and self.QuestLog.snapshot then
self.ready=true
local q=self.DatabaseAPI:GetQuestCount()
self:Print("foundation ready - "..tostring(q).." quests, "..tostring(self.Completion:Count())..
" completed, "..tostring(self.QuestLog.stats.resolved).." active quest IDs resolved.")
self:SendMessage("FOUNDATION_READY")
end
end
f:SetScript("OnEvent",function()
f:UnregisterEvent("PLAYER_ENTERING_WORLD")
QuestieOcto.startedAt=GetTime()
if not QuestieOcto.API:Validate() then
QuestieOcto.enabled=false
QuestieOcto:Error("ClassicAPI is required. Missing "..tostring(table.getn(QuestieOcto.API.missing)).." required API calls.")
QuestieOcto:Error("Run /qo api after installing/updating ClassicAPI.")
return
end
QuestieOcto.enabled=true
if QuestieOcto.Tooltips and QuestieOcto.Tooltips.Initialize then
QuestieOcto.Tooltips:Initialize()
end
QuestieOcto:RegisterMessage("DATABASE_READY",QuestieOcto,"OnFoundationServiceReady")
QuestieOcto:RegisterMessage("DATABASE_API_READY",QuestieOcto,"OnFoundationServiceReady")
QuestieOcto:RegisterMessage("COMPLETION_READY",QuestieOcto,"OnFoundationServiceReady")
QuestieOcto:RegisterMessage("QUEST_LOG_CHANGED",QuestieOcto,"OnFoundationServiceReady")
QuestieOcto:Print("ClassicAPI detected; starting Questie-Octo.")
QuestieOcto.Database:Start()
QuestieOcto.QuestLog:Start()
QuestieOcto.Completion:ScheduleStart()
end)
+59
View File
@@ -0,0 +1,59 @@
QuestieOcto.Scheduler = QuestieOcto.Scheduler or {}
local S = QuestieOcto.Scheduler
S.queue = {}
S.delayed = {}
S.elapsed = 0
S.interval = 0
S.executed = 0
function S:Enqueue(fn, label)
if not fn then return end
table.insert(self.queue, { fn=fn, label=label })
end
function S:After(delay, fn, key)
if not fn then return end
if key then
self.delayed[key] = { remaining=delay or 0, fn=fn }
else
table.insert(self.delayed, { remaining=delay or 0, fn=fn })
end
end
local function RunDelayed(self, delta)
local remove = {}
for k,v in pairs(self.delayed) do
v.remaining = v.remaining - delta
if v.remaining <= 0 then table.insert(remove, k) end
end
for _,k in pairs(remove) do
local entry = self.delayed[k]
self.delayed[k] = nil
if entry and entry.fn then entry.fn() end
end
end
function S:Tick()
-- Questie 3.3.5 ThreadLib resumes every active delay-0 thread once per frame.
-- Snapshot the queue length so a continuation re-enqueued by its own slice
-- cannot run twice in the same frame.
local count=table.getn(self.queue)
for i=1,count do
local entry=table.remove(self.queue,1)
if entry and entry.fn then
entry.fn()
self.executed=self.executed+1
end
end
end
local f=CreateFrame("Frame","QuestieOctoSchedulerFrame",UIParent)
f:SetScript("OnUpdate",function()
local delta=arg1 or 0
RunDelayed(S,delta)
-- Questie ThreadLib delay < 0.05 means "every frame".
S.elapsed=0
S:Tick()
end)
+35
View File
@@ -0,0 +1,35 @@
-- Questie-Octo curated Turtle WoW calendar rules.
--
-- These windows were transcribed from the in-game Turtle calendar for 2026.
-- The calendar is treated as a recurring annual schedule: only visible event
-- text counts. Raid Reset, roadmap/release announcements and graphic-only
-- calendar cells are intentionally ignored.
--
-- Darkmoon Faire is not part of this table. It follows its own anchored
-- 14-day Elwynn/Mulgore cycle in Quest/EventAvailability.lua.
QuestieOcto.CalendarEventRules = {
-- Canonical seasonal/gameplay festivals.
[1] = { name="Midsummer Fire Festival", startMonth=6, startDay=21, endMonth=7, endDay=12 },
[2] = { name="Feast of Winter Veil", startMonth=12, startDay=3, endMonth=1, endDay=14 },
[7] = { name="Lunar Festival", startMonth=2, startDay=16, endMonth=3, endDay=4 },
[8] = { name="Love is in the Air", startMonth=2, startDay=1, endMonth=2, endDay=16 },
-- Turtle currently has no quest rows tied to the Noblegarden game_event,
-- but keep both known IDs here so future DB revisions inherit the calendar
-- without another code change.
[9] = { name="Noblegarden", startMonth=4, startDay=17, endMonth=5, endDay=8 },
[28] = { name="Noblegarden", startMonth=4, startDay=17, endMonth=5, endDay=8 },
[11] = { name="Harvest Festival", startMonth=10, startDay=1, endMonth=10, endDay=8 },
[12] = { name="Hallow's End", startMonth=10, startDay=20, endMonth=11, endDay=2 },
-- Brewfest is a Turtle custom event ID present in quest data but absent
-- from the bundled tw_world_game_event snapshot.
[26] = { name="Brewfest", startMonth=9, startDay=20, endMonth=10, endDay=6 },
}
-- Calendar entries intentionally NOT used for quest availability:
-- Raid Reset; Phase 1/Phase 2; Festival; War; Frostmane Hollow;
-- Windhorn Canyon; Dragonmaw Retreat; Northwind; Stormwrought Ruins;
-- Moonwhisper Coast; and similar roadmap/release announcements.
+101
View File
@@ -0,0 +1,101 @@
-- Generated from authoritative Turtle WoW tw_world_game_event.sql.
-- Do not hand-edit: event membership and event presentation are separate.
QuestieOcto.EventScheduleData = {
[1]={start="2020-06-22 03:00:00",finish="2031-01-01 06:59:59",occurrence=525600,length=30240,holiday=341,name="Midsummer Fire Festival",hardcoded=false,disabled=false,requiredPhase=0},
[2]={start="2020-12-11 20:00:00",finish="2031-01-01 06:59:59",occurrence=525600,length=44640,holiday=141,name="Feast of Winter Veil",hardcoded=false,disabled=false,requiredPhase=0},
[4]={start="0000-00-00 00:00:00",finish="0000-00-00 00:00:00",occurrence=525600,length=2592000,holiday=374,name="Darkmoon Faire (Elwynn)",hardcoded=true,disabled=false,requiredPhase=0},
[5]={start="0000-00-00 00:00:00",finish="0000-00-00 00:00:00",occurrence=525600,length=2592000,holiday=375,name="Darkmoon Faire (Mulgore)",hardcoded=true,disabled=false,requiredPhase=0},
[6]={start="2017-01-01 11:00:00",finish="2026-12-30 12:00:00",occurrence=525600,length=10,holiday=0,name="Fireworks",hardcoded=true,disabled=false,requiredPhase=0},
[7]={start="2020-01-25 06:00:00",finish="2031-01-01 06:59:59",occurrence=525600,length=43200,holiday=327,name="Lunar Festival",hardcoded=false,disabled=true,requiredPhase=0},
[8]={start="2021-02-12 06:00:00",finish="2030-02-17 06:00:00",occurrence=525600,length=20160,holiday=335,name="Love is in the Air",hardcoded=false,disabled=true,requiredPhase=0},
[9]={start="2020-03-30 03:00:00",finish="2030-03-30 05:59:59",occurrence=525600,length=18720,holiday=181,name="Noblegarden",hardcoded=false,disabled=false,requiredPhase=0},
[10]={start="2022-02-01 20:00:00",finish="2031-01-01 06:59:59",occurrence=40320,length=10080,holiday=201,name="Children's Week",hardcoded=false,disabled=false,requiredPhase=0},
[11]={start="2020-09-30 05:00:00",finish="2031-01-01 06:59:59",occurrence=525600,length=10080,holiday=321,name="Harvest Festival",hardcoded=false,disabled=false,requiredPhase=0},
[12]={start="2020-10-19 03:00:00",finish="2031-01-01 06:59:59",occurrence=525600,length=20160,holiday=324,name="Hallow's End",hardcoded=false,disabled=false,requiredPhase=0},
[13]={start="0000-00-00 00:00:00",finish="0000-00-00 00:00:00",occurrence=1,length=999999999,holiday=0,name="Elemental Invasion",hardcoded=true,disabled=false,requiredPhase=0},
[14]={start="2017-01-07 16:00:00",finish="2027-01-01 07:00:00",occurrence=10080,length=1920,holiday=0,name="Stranglethorn Fishing Extravaganza: Announce",hardcoded=false,disabled=false,requiredPhase=0},
[15]={start="2017-01-08 21:00:00",finish="2027-01-01 07:00:00",occurrence=10080,length=120,holiday=301,name="Stranglethorn Fishing Extravaganza: Tournament",hardcoded=false,disabled=false,requiredPhase=0},
[16]={start="2021-01-01 07:00:00",finish="2031-01-01 06:59:59",occurrence=180,length=120,holiday=0,name="Gurubashi Arena Booty Run",hardcoded=false,disabled=false,requiredPhase=0},
[17]={start="0000-00-00 00:00:00",finish="0000-00-00 00:00:00",occurrence=525600,length=999999999,holiday=0,name="Scourge Invasion",hardcoded=true,disabled=true,requiredPhase=3},
[18]={start="2020-05-01 07:00:01",finish="2031-01-01 06:59:59",occurrence=7200,length=1440,holiday=283,name="Call to arms: Alterac Valley",hardcoded=false,disabled=false,requiredPhase=0},
[19]={start="2020-05-02 07:00:01",finish="2031-01-01 06:59:59",occurrence=7200,length=1440,holiday=284,name="Call to Arms: Warsong Gulch",hardcoded=false,disabled=false,requiredPhase=0},
[20]={start="2020-05-03 07:00:01",finish="2031-01-01 06:59:59",occurrence=7200,length=1440,holiday=285,name="Call to Arms: Arathi Basin",hardcoded=false,disabled=false,requiredPhase=0},
[21]={start="2020-12-25 08:00:00",finish="2030-01-03 07:59:59",occurrence=525600,length=12960,holiday=0,name="Feast of Winter Veil: Gifts",hardcoded=false,disabled=false,requiredPhase=0},
[22]={start="2020-08-03 07:00:00",finish="2030-12-31 10:00:00",occurrence=1,length=2592000,holiday=0,name="AQ War Effort (NPC & GO 'Initial')",hardcoded=true,disabled=false,requiredPhase=2},
[23]={start="0000-00-00 00:00:00",finish="0000-00-00 00:00:00",occurrence=525600,length=2592000,holiday=374,name="Darkmoon Faire Building (Elwynn) (TODO)",hardcoded=true,disabled=false,requiredPhase=0},
[24]={start="0000-00-00 00:00:00",finish="0000-00-00 00:00:00",occurrence=525600,length=2592000,holiday=375,name="Darkmoon Faire Building (Mulgore) (TODO)",hardcoded=true,disabled=false,requiredPhase=0},
[25]={start="0000-00-00 00:00:00",finish="0000-00-00 00:00:00",occurrence=525600,length=1,holiday=0,name="Call to arms",hardcoded=false,disabled=false,requiredPhase=0},
[27]={start="2020-01-02 08:00:00",finish="2030-12-31 11:00:00",occurrence=1440,length=720,holiday=0,name="Night",hardcoded=false,disabled=false,requiredPhase=0},
[28]={start="2024-03-30 07:00:01",finish="2030-04-11 07:00:01",occurrence=525600,length=17280,holiday=0,name="Noblegarden",hardcoded=false,disabled=false,requiredPhase=0},
[29]={start="2020-06-10 07:00:01",finish="2031-01-01 06:59:59",occurrence=80640,length=20160,holiday=0,name="Edge of Madness, Gri'lek",hardcoded=false,disabled=false,requiredPhase=0},
[30]={start="2020-06-23 07:00:01",finish="2031-01-01 06:59:59",occurrence=80640,length=20160,holiday=0,name="Edge of Madness, Hazza'rah",hardcoded=false,disabled=false,requiredPhase=0},
[31]={start="2020-05-12 07:00:01",finish="2031-01-01 06:59:59",occurrence=80640,length=20160,holiday=0,name="Edge of Madness, Renataki",hardcoded=false,disabled=false,requiredPhase=0},
[32]={start="2020-05-26 07:00:01",finish="2031-01-01 06:59:59",occurrence=80640,length=20160,holiday=0,name="Edge of Madness, Wushoolay",hardcoded=false,disabled=false,requiredPhase=0},
[33]={start="0000-00-00 00:00:00",finish="0000-00-00 00:00:00",occurrence=5184000,length=2592000,holiday=0,name="Arena Tournament",hardcoded=false,disabled=false,requiredPhase=0},
[34]={start="2020-12-31 14:00:00",finish="2030-01-01 14:00:05",occurrence=525600,length=1445,holiday=0,name="New Year's Eve",hardcoded=false,disabled=false,requiredPhase=0},
[35]={start="2010-03-24 17:00:00",finish="2030-01-01 11:00:00",occurrence=1260,length=420,holiday=0,name="The Maul: Mushgog",hardcoded=false,disabled=false,requiredPhase=0},
[36]={start="2010-03-25 00:00:00",finish="2030-01-01 11:00:00",occurrence=1260,length=420,holiday=0,name="The Maul: Skarr the Unbreakable",hardcoded=false,disabled=false,requiredPhase=0},
[37]={start="2010-03-25 07:00:00",finish="2030-01-01 11:00:00",occurrence=1260,length=420,holiday=0,name="The Maul: Razza",hardcoded=false,disabled=false,requiredPhase=0},
[38]={start="2020-02-08 14:00:00",finish="2030-02-09 14:00:00",occurrence=525600,length=1445,holiday=0,name="Chinese New Year",hardcoded=false,disabled=false,requiredPhase=0},
[39]={start="0000-00-00 00:00:00",finish="0000-00-00 00:00:00",occurrence=60,length=10,holiday=0,name="Post Firework Toasting Goblets",hardcoded=true,disabled=false,requiredPhase=0},
[40]={start="2017-01-08 19:00:00",finish="2027-01-01 07:00:00",occurrence=10080,length=300,holiday=0,name="Stranglethorn Fishing Extravaganza: The Crew",hardcoded=false,disabled=false,requiredPhase=0},
[41]={start="2020-06-04 07:00:00",finish="2027-06-05 07:00:00",occurrence=525600,length=1440,holiday=0,name="July 4th Fireworks Spectacular (US Only)",hardcoded=false,disabled=false,requiredPhase=0},
[42]={start="2020-09-30 07:00:00",finish="2027-10-01 07:00:00",occurrence=525600,length=1440,holiday=0,name="September 30th Peon Day (EU Only)",hardcoded=false,disabled=false,requiredPhase=0},
[45]={start="2020-03-20 17:00:00",finish="2030-01-01 12:00:00",occurrence=525600,length=262800,holiday=0,name="Fishing: summer period (for Raw Summer Bass)",hardcoded=false,disabled=false,requiredPhase=0},
[46]={start="2020-09-23 16:00:00",finish="2030-01-01 12:00:00",occurrence=525600,length=262800,holiday=0,name="Fishing: winter period (for Winter Squid)",hardcoded=false,disabled=false,requiredPhase=0},
[47]={start="2017-01-01 13:00:00",finish="2030-12-31 07:00:00",occurrence=1440,length=1080,holiday=0,name="Fishing: Sunscale Cycle",hardcoded=false,disabled=false,requiredPhase=0},
[48]={start="2017-01-02 01:00:00",finish="2030-12-31 19:00:00",occurrence=1440,length=1080,holiday=0,name="Fishing: Nightfin Cycle",hardcoded=false,disabled=false,requiredPhase=0},
[49]={start="2020-01-03 05:00:00",finish="2030-12-31 13:30:00",occurrence=1440,length=480,holiday=0,name="Pyrewood Village Night",hardcoded=false,disabled=false,requiredPhase=0},
[50]={start="2020-05-04 07:00:01",finish="2031-01-01 06:59:59",occurrence=7200,length=1440,holiday=286,name="Call to Arms: Blood Ring",hardcoded=false,disabled=false,requiredPhase=0},
[51]={start="2020-05-05 07:00:01",finish="2031-01-01 06:59:59",occurrence=7200,length=1440,holiday=287,name="Call to Arms: Sunnyglade",hardcoded=false,disabled=false,requiredPhase=0},
[52]={start="0000-00-00 00:00:00",finish="0000-00-00 00:00:00",occurrence=1,length=2592000,holiday=0,name="AQ War Effort (NPC Reput 'Only War Effort')",hardcoded=true,disabled=false,requiredPhase=2},
[53]={start="0000-00-00 00:00:00",finish="0000-00-00 00:00:00",occurrence=1,length=2592000,holiday=0,name="AQ War Effort (NPC Reput Named 'Officer')",hardcoded=true,disabled=false,requiredPhase=2},
[54]={start="0000-00-00 00:00:00",finish="0000-00-00 00:00:00",occurrence=1,length=2592000,holiday=0,name="AQ War Troop Silithus (NPC & GO) DAY 1",hardcoded=true,disabled=true,requiredPhase=2},
[55]={start="0000-00-00 00:00:00",finish="0000-00-00 00:00:00",occurrence=1,length=2592000,holiday=0,name="AQ War Troop Silithus (NPC & GO) DAY 2",hardcoded=true,disabled=true,requiredPhase=2},
[56]={start="0000-00-00 00:00:00",finish="0000-00-00 00:00:00",occurrence=1,length=2592000,holiday=0,name="AQ War Troop Silithus (NPC & GO) DAY 3",hardcoded=true,disabled=true,requiredPhase=2},
[57]={start="0000-00-00 00:00:00",finish="0000-00-00 00:00:00",occurrence=1,length=2592000,holiday=0,name="AQ War Troop Silithus (NPC & GO) DAY 4",hardcoded=true,disabled=true,requiredPhase=2},
[58]={start="0000-00-00 00:00:00",finish="0000-00-00 00:00:00",occurrence=1,length=2592000,holiday=0,name="AQ War Troop Silithus (NPC & GO) DAY 5",hardcoded=true,disabled=true,requiredPhase=2},
[59]={start="0000-00-00 00:00:00",finish="0000-00-00 00:00:00",occurrence=1,length=2592000,holiday=0,name="AQ War Effort (Cenarion Hold attack)",hardcoded=true,disabled=true,requiredPhase=2},
[60]={start="0000-00-00 00:00:00",finish="0000-00-00 00:00:00",occurrence=1,length=2592000,holiday=0,name="AQ War Troop Silithus 3 (NPC & GO)",hardcoded=true,disabled=false,requiredPhase=2},
[61]={start="0000-00-00 00:00:00",finish="0000-00-00 00:00:00",occurrence=1,length=2592000,holiday=0,name="AQ War Final Battle (NPC)",hardcoded=true,disabled=true,requiredPhase=2},
[62]={start="0000-00-00 00:00:00",finish="0000-00-00 00:00:00",occurrence=1,length=2592000,holiday=0,name="AQ War - Secrets of the Colossus Ashi",hardcoded=false,disabled=true,requiredPhase=2},
[63]={start="0000-00-00 00:00:00",finish="0000-00-00 00:00:00",occurrence=1,length=2592000,holiday=0,name="AQ War - Secrets of the Colossus Regal",hardcoded=false,disabled=true,requiredPhase=2},
[64]={start="0000-00-00 00:00:00",finish="0000-00-00 00:00:00",occurrence=1,length=2592000,holiday=0,name="AQ War - Secrets of the Colossus Zora",hardcoded=false,disabled=true,requiredPhase=2},
[65]={start="0000-00-00 00:00:00",finish="0000-00-00 00:00:00",occurrence=1,length=2592000,holiday=0,name="AQ War - Spawn of Crystals",hardcoded=true,disabled=true,requiredPhase=2},
[66]={start="0000-00-00 00:00:00",finish="0000-00-00 00:00:00",occurrence=1,length=999999999,holiday=0,name="Dragons of Nightmare Spawn",hardcoded=true,disabled=false,requiredPhase=1},
[68]={start="0000-00-00 00:00:00",finish="0000-00-00 00:00:00",occurrence=1,length=999999999,holiday=0,name="Elemental Invasion: Fire (Un'goro Crater)",hardcoded=true,disabled=false,requiredPhase=0},
[69]={start="0000-00-00 00:00:00",finish="0000-00-00 00:00:00",occurrence=1,length=999999999,holiday=0,name="Elemental Invasion: Air (Silithus)",hardcoded=true,disabled=false,requiredPhase=0},
[70]={start="0000-00-00 00:00:00",finish="0000-00-00 00:00:00",occurrence=1,length=999999999,holiday=0,name="Elemental Invasion: Earth (Azshara)",hardcoded=true,disabled=false,requiredPhase=0},
[71]={start="0000-00-00 00:00:00",finish="0000-00-00 00:00:00",occurrence=1,length=999999999,holiday=0,name="Elemental Invasion: Water (Winterspring)",hardcoded=true,disabled=false,requiredPhase=0},
[72]={start="0000-00-00 00:00:00",finish="0000-00-00 00:00:00",occurrence=1,length=999999999,holiday=0,name="Elemental Invasion: Baron Charr (Un'goro Crater)",hardcoded=true,disabled=false,requiredPhase=0},
[73]={start="0000-00-00 00:00:00",finish="0000-00-00 00:00:00",occurrence=1,length=999999999,holiday=0,name="Elemental Invasion: The Windreaver (Silithus)",hardcoded=true,disabled=false,requiredPhase=0},
[74]={start="0000-00-00 00:00:00",finish="0000-00-00 00:00:00",occurrence=1,length=999999999,holiday=0,name="Elemental Invasion: Avalanchion (Azshara)",hardcoded=true,disabled=false,requiredPhase=0},
[75]={start="0000-00-00 00:00:00",finish="0000-00-00 00:00:00",occurrence=1,length=999999999,holiday=0,name="Elemental Invasion: Princess Tempestria (Winterspring)",hardcoded=true,disabled=false,requiredPhase=0},
[78]={start="2010-01-01 08:00:00",finish="2025-01-01 08:00:00",occurrence=60,length=1,holiday=0,name="Hourly Bells",hardcoded=false,disabled=false,requiredPhase=0},
[80]={start="0000-00-00 00:00:00",finish="0000-00-00 00:00:00",occurrence=525600,length=999999999,holiday=0,name="Scourge Invasion - Skeleton spawn / Quest activation lvl 9-11",hardcoded=false,disabled=false,requiredPhase=3},
[81]={start="0000-00-00 00:00:00",finish="0000-00-00 00:00:00",occurrence=525600,length=999999999,holiday=0,name="Scourge Invasion - Boss in instance activation",hardcoded=false,disabled=false,requiredPhase=3},
[83]={start="2005-01-01 07:00:00",finish="2025-01-01 08:00:00",occurrence=1,length=2592000,holiday=0,name="AQ Gate",hardcoded=false,disabled=false,requiredPhase=0},
[84]={start="2005-01-01 07:00:00",finish="2025-01-01 08:00:00",occurrence=1,length=2,holiday=0,name="AQ War Effort (master event)",hardcoded=true,disabled=false,requiredPhase=2},
[85]={start="0000-00-00 00:00:00",finish="0000-00-00 00:00:00",occurrence=1,length=999999999,holiday=0,name="AQ War Effort (gong)",hardcoded=true,disabled=true,requiredPhase=2},
[86]={start="2005-01-01 07:00:00",finish="2025-01-01 08:00:00",occurrence=1,length=2,holiday=0,name="AQ War Effort (post-event)",hardcoded=true,disabled=false,requiredPhase=2},
[103]={start="2020-09-08 11:00:00",finish="2030-12-31 11:00:00",occurrence=525600,length=33720,holiday=0,name="Event PVP Silithus",hardcoded=false,disabled=false,requiredPhase=0},
[110]={start="2021-02-17 06:00:00",finish="2030-02-12 06:00:00",occurrence=525600,length=525600,holiday=335,name="Love is in the Air - Popularity Contest Winner: Darnassus",hardcoded=false,disabled=true,requiredPhase=0},
[111]={start="2021-02-17 06:00:00",finish="2030-02-12 06:00:00",occurrence=525600,length=525600,holiday=335,name="Love is in the Air - Popularity Contest Winner: Ironforge",hardcoded=false,disabled=true,requiredPhase=0},
[112]={start="2021-02-17 06:00:00",finish="2030-02-12 06:00:00",occurrence=525600,length=525600,holiday=335,name="Love is in the Air - Popularity Contest Winner: Stormwind",hardcoded=false,disabled=true,requiredPhase=0},
[113]={start="2021-02-17 06:00:00",finish="2030-02-12 06:00:00",occurrence=525600,length=525600,holiday=335,name="Love is in the Air - Popularity Contest Winner: Orgrimmar",hardcoded=false,disabled=true,requiredPhase=0},
[114]={start="2021-02-17 06:00:00",finish="2030-02-12 06:00:00",occurrence=525600,length=525600,holiday=335,name="Love is in the Air - Popularity Contest Winner: Thunder Bluff",hardcoded=false,disabled=true,requiredPhase=0},
[115]={start="2021-02-17 06:00:00",finish="2030-02-12 06:00:00",occurrence=525600,length=525600,holiday=335,name="Love is in the Air - Popularity Contest Winner: Undercity",hardcoded=false,disabled=true,requiredPhase=0},
[129]={start="0000-00-00 00:00:00",finish="0000-00-00 00:00:00",occurrence=525600,length=999999999,holiday=0,name="Scourge Invasion - Phase 2 - Invasion Stormwind",hardcoded=false,disabled=false,requiredPhase=3},
[130]={start="0000-00-00 00:00:00",finish="0000-00-00 00:00:00",occurrence=525600,length=999999999,holiday=0,name="Scourge Invasion - Phase 2 - Invasion Undercity",hardcoded=false,disabled=false,requiredPhase=3},
[150]={start="0000-00-00 00:00:00",finish="0000-00-00 00:00:00",occurrence=525600,length=33720,holiday=0,name="Leprithus Spawn (Westfall)",hardcoded=true,disabled=false,requiredPhase=0},
[151]={start="0000-00-00 00:00:00",finish="0000-00-00 00:00:00",occurrence=525600,length=33720,holiday=0,name="Fleshrippers Spawn (Westfall, Moonbrook)",hardcoded=true,disabled=false,requiredPhase=0},
[152]={start="2022-01-02 01:00:00",finish="2030-04-01 01:00:00",occurrence=1440,length=60,holiday=0,name="Southshore Assassins",hardcoded=false,disabled=false,requiredPhase=0},
[156]={start="2014-03-30 11:00:00",finish="2030-03-30 10:00:00",occurrence=1,length=2,holiday=0,name="DM release : Recipes and misc item",hardcoded=false,disabled=false,requiredPhase=0},
[158]={start="2015-03-30 11:00:00",finish="2030-03-30 10:00:00",occurrence=1,length=2,holiday=0,name="1.10 Patch : Recipes",hardcoded=false,disabled=false,requiredPhase=2},
[159]={start="2005-03-30 11:00:00",finish="2030-03-30 10:00:00",occurrence=1,length=2,holiday=0,name="DM Release : Cloth turning NPC",hardcoded=false,disabled=false,requiredPhase=0},
[160]={start="2023-03-30 11:00:00",finish="2030-03-30 10:00:00",occurrence=1,length=2,holiday=0,name="1.10 Patch : t0.5 Quest Chain",hardcoded=false,disabled=false,requiredPhase=0},
[161]={start="2017-01-02 01:00:00",finish="2030-12-31 19:00:00",occurrence=1,length=9999999999,holiday=0,name="Mirage Race",hardcoded=true,disabled=false,requiredPhase=0},
[162]={start="2021-01-02 02:00:00",finish="2030-12-31 20:00:00",occurrence=1,length=9999999999,holiday=0,name="Uldum Boss",hardcoded=true,disabled=false,requiredPhase=0},
[163]={start="2023-03-30 11:00:00",finish="2030-03-30 10:00:00",occurrence=1,length=2,holiday=0,name="1.6 Patch: BWL Attunement",hardcoded=false,disabled=false,requiredPhase=1},
[164]={start="2023-03-30 11:00:00",finish="2030-03-30 10:00:00",occurrence=1,length=2,holiday=0,name="1.9 Patch: Quests",hardcoded=false,disabled=false,requiredPhase=2},
}
+244
View File
@@ -0,0 +1,244 @@
-- Questie-Octo PvP quest type projection.
-- Generated from the authoritative Turtle server quest_template supplied with the project.
-- Type 41 is the canonical PvP quest type used by Questie 6.
--
-- The Blood Ring entries 41107-41110 are verified Turtle PvP quests whose
-- current source rows report Type 0; Questie-Octo corrects those locally to 41.
QuestieOcto.PvPQuestTypes = {
[1657] = 41,
[1658] = 41,
[5342] = 41,
[5344] = 41,
[6781] = 41,
[6801] = 41,
[6825] = 41,
[6826] = 41,
[6827] = 41,
[6847] = 41,
[6848] = 41,
[6861] = 41,
[6862] = 41,
[6881] = 41,
[6901] = 41,
[6941] = 41,
[6942] = 41,
[6943] = 41,
[7001] = 41,
[7002] = 41,
[7026] = 41,
[7027] = 41,
[7081] = 41,
[7082] = 41,
[7101] = 41,
[7102] = 41,
[7121] = 41,
[7122] = 41,
[7123] = 41,
[7124] = 41,
[7141] = 41,
[7142] = 41,
[7223] = 41,
[7224] = 41,
[7241] = 41,
[7261] = 41,
[7301] = 41,
[7302] = 41,
[7361] = 41,
[7362] = 41,
[7363] = 41,
[7364] = 41,
[7365] = 41,
[7366] = 41,
[7385] = 41,
[7386] = 41,
[7401] = 41,
[7402] = 41,
[7421] = 41,
[7422] = 41,
[7423] = 41,
[7424] = 41,
[7425] = 41,
[7426] = 41,
[7427] = 41,
[7428] = 41,
[7788] = 41,
[7789] = 41,
[7810] = 41,
[7841] = 41,
[7842] = 41,
[7843] = 41,
[7863] = 41,
[7864] = 41,
[7865] = 41,
[7866] = 41,
[7867] = 41,
[7868] = 41,
[7871] = 41,
[7872] = 41,
[7873] = 41,
[7874] = 41,
[7875] = 41,
[7876] = 41,
[7886] = 41,
[7887] = 41,
[7888] = 41,
[7921] = 41,
[7922] = 41,
[7923] = 41,
[7924] = 41,
[7925] = 41,
[8001] = 41,
[8080] = 41,
[8081] = 41,
[8105] = 41,
[8114] = 41,
[8115] = 41,
[8120] = 41,
[8121] = 41,
[8122] = 41,
[8123] = 41,
[8124] = 41,
[8154] = 41,
[8155] = 41,
[8156] = 41,
[8157] = 41,
[8158] = 41,
[8159] = 41,
[8160] = 41,
[8161] = 41,
[8162] = 41,
[8163] = 41,
[8164] = 41,
[8165] = 41,
[8166] = 41,
[8167] = 41,
[8168] = 41,
[8169] = 41,
[8170] = 41,
[8171] = 41,
[8260] = 41,
[8261] = 41,
[8262] = 41,
[8263] = 41,
[8264] = 41,
[8265] = 41,
[8289] = 41,
[8290] = 41,
[8291] = 41,
[8292] = 41,
[8293] = 41,
[8294] = 41,
[8295] = 41,
[8296] = 41,
[8297] = 41,
[8298] = 41,
[8299] = 41,
[8300] = 41,
[8322] = 41,
[8367] = 41,
[8368] = 41,
[8369] = 41,
[8370] = 41,
[8371] = 41,
[8372] = 41,
[8373] = 41,
[8374] = 41,
[8375] = 41,
[8383] = 41,
[8384] = 41,
[8385] = 41,
[8386] = 41,
[8387] = 41,
[8388] = 41,
[8389] = 41,
[8390] = 41,
[8391] = 41,
[8392] = 41,
[8393] = 41,
[8394] = 41,
[8395] = 41,
[8396] = 41,
[8397] = 41,
[8398] = 41,
[8399] = 41,
[8400] = 41,
[8401] = 41,
[8402] = 41,
[8403] = 41,
[8404] = 41,
[8405] = 41,
[8406] = 41,
[8407] = 41,
[8408] = 41,
[8409] = 41,
[8426] = 41,
[8427] = 41,
[8428] = 41,
[8429] = 41,
[8430] = 41,
[8431] = 41,
[8432] = 41,
[8433] = 41,
[8434] = 41,
[8435] = 41,
[8436] = 41,
[8437] = 41,
[8438] = 41,
[8439] = 41,
[8440] = 41,
[8441] = 41,
[8442] = 41,
[8443] = 41,
[8493] = 41,
[8495] = 41,
[8500] = 41,
[8504] = 41,
[8506] = 41,
[8510] = 41,
[8512] = 41,
[8514] = 41,
[8516] = 41,
[8518] = 41,
[8521] = 41,
[8523] = 41,
[8525] = 41,
[8527] = 41,
[8529] = 41,
[8533] = 41,
[8543] = 41,
[8546] = 41,
[8550] = 41,
[8581] = 41,
[8583] = 41,
[8589] = 41,
[8591] = 41,
[8601] = 41,
[8605] = 41,
[8608] = 41,
[8610] = 41,
[8612] = 41,
[8614] = 41,
[8616] = 41,
[8846] = 41,
[8847] = 41,
[8849] = 41,
[8850] = 41,
[8851] = 41,
[8852] = 41,
[8854] = 41,
[8855] = 41,
[9419] = 41,
[9422] = 41,
[9664] = 41,
[9665] = 41,
[40079] = 41,
[41107] = 41, -- verified Blood Ring correction (server Type 0 -> 41)
[41108] = 41, -- verified Blood Ring correction (server Type 0 -> 41)
[41109] = 41, -- verified Blood Ring correction (server Type 0 -> 41)
[41110] = 41, -- verified Blood Ring correction (server Type 0 -> 41)
[42098] = 41,
[42099] = 41,
[140821] = 41,
[1140820] = 41,
}
File diff suppressed because it is too large Load Diff
+13
View File
@@ -0,0 +1,13 @@
<Ui xmlns="http://www.blizzard.com/wow/ui/">
<Include file="..\pfDB\items-turtle.lua"/>
<Include file="..\pfDB\units-turtle.lua"/>
<Include file="..\pfDB\objects-turtle.lua"/>
<Include file="..\pfDB\refloot-turtle.lua"/>
<Include file="..\pfDB\quests-itemreq-turtle.lua"/>
<Include file="..\pfDB\quests-turtle.lua"/>
<Include file="..\pfDB\patches-turtle.lua"/>
<Include file="..\pfDB\zones-turtle.lua"/>
<Include file="..\pfDB\minimap-turtle.lua"/>
<Include file="..\pfDB\areatrigger-turtle.lua"/>
<Include file="..\pfDB\meta-turtle.lua"/>
</Ui>
+13
View File
@@ -0,0 +1,13 @@
<Ui xmlns="http://www.blizzard.com/wow/ui/">
<Include file="..\pfDB\init.lua"/>
<Include file="..\pfDB\items.lua"/>
<Include file="..\pfDB\units.lua"/>
<Include file="..\pfDB\objects.lua"/>
<Include file="..\pfDB\refloot.lua"/>
<Include file="..\pfDB\quests-itemreq.lua"/>
<Include file="..\pfDB\quests.lua"/>
<Include file="..\pfDB\zones.lua"/>
<Include file="..\pfDB\minimap.lua"/>
<Include file="..\pfDB\areatrigger.lua"/>
<Include file="..\pfDB\meta.lua"/>
</Ui>
+8
View File
@@ -0,0 +1,8 @@
<Ui xmlns="http://www.blizzard.com/wow/ui/">
<Include file="..\pfDB\enUS\items-turtle.lua"/>
<Include file="..\pfDB\enUS\units-turtle.lua"/>
<Include file="..\pfDB\enUS\objects-turtle.lua"/>
<Include file="..\pfDB\enUS\quests-turtle.lua"/>
<Include file="..\pfDB\enUS\zones-turtle.lua"/>
<Include file="..\pfDB\enUS\professions-turtle.lua"/>
</Ui>
+8
View File
@@ -0,0 +1,8 @@
<Ui xmlns="http://www.blizzard.com/wow/ui/">
<Include file="..\pfDB\enUS\items.lua"/>
<Include file="..\pfDB\enUS\units.lua"/>
<Include file="..\pfDB\enUS\objects.lua"/>
<Include file="..\pfDB\enUS\quests.lua"/>
<Include file="..\pfDB\enUS\zones.lua"/>
<Include file="..\pfDB\enUS\professions.lua"/>
</Ui>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+136
View File
@@ -0,0 +1,136 @@
pfDB["professions"]["enUS-turtle"] = {
[6] = "Frost",
[8] = "Fire",
[26] = "Arms",
[38] = "Combat",
[39] = "Subtlety",
[40] = "Poisons",
[43] = "Swords",
[44] = "Axes",
[45] = "Bows",
[46] = "Guns",
[50] = "Beast Mastery",
[51] = "Survival",
[54] = "Maces",
[55] = "Two-Handed Swords",
[56] = "Holy",
[78] = "Shadow Magic",
[95] = "Defense",
[98] = "Language: Common",
[101] = "Dwarven Racial",
[109] = "Language: Orcish",
[111] = "Language: Dwarven",
[113] = "Language: Darnassian",
[115] = "Language: Taurahe",
[118] = "Dual Wield",
[124] = "Tauren Racial",
[125] = "Orc Racial",
[126] = "Night Elf Racial",
[129] = "First Aid",
[134] = "Feral Combat",
[136] = "Staves",
[137] = "Language: Thalassian",
[138] = "Language: Draconic",
[139] = "Language: Demon Tongue",
[140] = "Language: Titan",
[141] = "Language: Old Tongue",
[142] = "Survival",
[148] = "Horse Riding",
[149] = "Wolf Riding",
[150] = "Tiger Riding",
[152] = "Ram Riding",
[155] = "Swimming",
[160] = "Two-Handed Maces",
[162] = "Unarmed",
[163] = "Marksmanship",
[164] = "Blacksmithing",
[165] = "Leatherworking",
[171] = "Alchemy",
[172] = "Two-Handed Axes",
[173] = "Daggers",
[176] = "Thrown",
[182] = "Herbalism",
[183] = "GENERIC (DND)",
[184] = "Retribution",
[185] = "Cooking",
[186] = "Mining",
[188] = "Pet - Imp",
[189] = "Pet - Felhunter",
[197] = "Tailoring",
[202] = "Engineering",
[203] = "Pet - Spider",
[204] = "Pet - Voidwalker",
[205] = "Pet - Succubus",
[206] = "Pet - Infernal",
[207] = "Pet - Doomguard",
[208] = "Pet - Wolf",
[209] = "Pet - Cat",
[210] = "Pet - Bear",
[211] = "Pet - Boar",
[212] = "Pet - Crocilisk",
[213] = "Pet - Carrion Bird",
[214] = "Pet - Crab",
[215] = "Pet - Gorilla",
[217] = "Pet - Raptor",
[218] = "Pet - Tallstrider",
[220] = "Racial - Undead",
[226] = "Crossbows",
[227] = "Spears",
[228] = "Wands",
[229] = "Polearms",
[236] = "Pet - Scorpid",
[237] = "Arcane",
[251] = "Pet - Turtle",
[253] = "Assassination",
[256] = "Fury",
[257] = "Protection",
[261] = "Beast Training",
[267] = "Protection",
[270] = "Pet - Generic",
[293] = "Plate Mail",
[313] = "Language: Gnomish",
[315] = "Language: Troll",
[333] = "Enchanting",
[354] = "Demonology",
[355] = "Affliction",
[356] = "Fishing",
[373] = "Enhancement",
[374] = "Restoration",
[375] = "Elemental Combat",
[393] = "Skinning",
[413] = "Mail",
[414] = "Leather",
[415] = "Cloth",
[433] = "Shield",
[473] = "Fist Weapons",
[533] = "Raptor Riding",
[553] = "Mechanostrider Piloting",
[554] = "Undead Horsemanship",
[573] = "Restoration",
[574] = "Balance",
[593] = "Destruction",
[594] = "Holy",
[613] = "Discipline",
[633] = "Lockpicking",
[653] = "Pet - Bat",
[654] = "Pet - Hyena",
[655] = "Pet - Owl",
[656] = "Pet - Wind Serpent",
[673] = "Language: Gutterspeak",
[713] = "Kodo Riding",
[733] = "Racial - Troll",
[753] = "Racial - Gnome",
[754] = "Racial - Human",
[755] = "Jewelcrafting",
[756] = "Racial - Elf",
[758] = "Pet - Event - Remote Control",
[762] = "Riding",
[790] = "Racial - Goblin",
[792] = "Language: Goblin",
[1005] = "ZzCompanions",
[1006] = "ZMounts",
[1007] = "ZzzGlyphs",
[1008] = "ZzzzToys",
[1009] = "Pet - Serpent",
[1010] = "Pet - Fox",
}
+125
View File
@@ -0,0 +1,125 @@
pfDB["professions"]["enUS"] = {
[6] = "Frost",
[8] = "Fire",
[26] = "Arms",
[38] = "Combat",
[39] = "Subtlety",
[40] = "Poisons",
[43] = "Swords",
[44] = "Axes",
[45] = "Bows",
[46] = "Guns",
[50] = "Beast Mastery",
[51] = "Survival",
[54] = "Maces",
[55] = "Two-Handed Swords",
[56] = "Holy",
[78] = "Shadow Magic",
[95] = "Defense",
[98] = "Language: Common",
[101] = "Dwarven Racial",
[109] = "Language: Orcish",
[111] = "Language: Dwarven",
[113] = "Language: Darnassian",
[115] = "Language: Taurahe",
[118] = "Dual Wield",
[124] = "Tauren Racial",
[125] = "Orc Racial",
[126] = "Night Elf Racial",
[129] = "First Aid",
[134] = "Feral Combat",
[136] = "Staves",
[137] = "Language: Thalassian",
[138] = "Language: Draconic",
[139] = "Language: Demon Tongue",
[140] = "Language: Titan",
[141] = "Language: Old Tongue",
[142] = "Survival",
[148] = "Horse Riding",
[149] = "Wolf Riding",
[150] = "Tiger Riding",
[152] = "Ram Riding",
[155] = "Swimming",
[160] = "Two-Handed Maces",
[162] = "Unarmed",
[163] = "Marksmanship",
[164] = "Blacksmithing",
[165] = "Leatherworking",
[171] = "Alchemy",
[172] = "Two-Handed Axes",
[173] = "Daggers",
[176] = "Thrown",
[182] = "Herbalism",
[183] = "GENERIC (DND)",
[184] = "Retribution",
[185] = "Cooking",
[186] = "Mining",
[188] = "Pet - Imp",
[189] = "Pet - Felhunter",
[197] = "Tailoring",
[202] = "Engineering",
[203] = "Pet - Spider",
[204] = "Pet - Voidwalker",
[205] = "Pet - Succubus",
[206] = "Pet - Infernal",
[207] = "Pet - Doomguard",
[208] = "Pet - Wolf",
[209] = "Pet - Cat",
[210] = "Pet - Bear",
[211] = "Pet - Boar",
[212] = "Pet - Crocilisk",
[213] = "Pet - Carrion Bird",
[214] = "Pet - Crab",
[215] = "Pet - Gorilla",
[217] = "Pet - Raptor",
[218] = "Pet - Tallstrider",
[220] = "Racial - Undead",
[226] = "Crossbows",
[228] = "Wands",
[229] = "Polearms",
[236] = "Pet - Scorpid",
[237] = "Arcane",
[251] = "Pet - Turtle",
[253] = "Assassination",
[256] = "Fury",
[257] = "Protection",
[261] = "Beast Training",
[267] = "Protection",
[270] = "Pet - Generic",
[293] = "Plate Mail",
[313] = "Language: Gnomish",
[315] = "Language: Troll",
[333] = "Enchanting",
[354] = "Demonology",
[355] = "Affliction",
[356] = "Fishing",
[373] = "Enhancement",
[374] = "Restoration",
[375] = "Elemental Combat",
[393] = "Skinning",
[413] = "Mail",
[414] = "Leather",
[415] = "Cloth",
[433] = "Shield",
[473] = "Fist Weapons",
[533] = "Raptor Riding",
[553] = "Mechanostrider Piloting",
[554] = "Undead Horsemanship",
[573] = "Restoration",
[574] = "Balance",
[593] = "Destruction",
[594] = "Holy",
[613] = "Discipline",
[633] = "Lockpicking",
[653] = "Pet - Bat",
[654] = "Pet - Hyena",
[655] = "Pet - Owl",
[656] = "Pet - Wind Serpent",
[673] = "Language: Gutterspeak",
[713] = "Kodo Riding",
[733] = "Racial - Troll",
[753] = "Racial - Gnome",
[754] = "Racial - Human",
[758] = "Pet - Event - Remote Control",
[762] = "Riding",
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+492
View File
@@ -0,0 +1,492 @@
pfDB["zones"]["enUS-turtle"] = {
[65] = "_",
[296] = "South Seas",
[409] = "Lapidis Isle",
[2037] = "Quel\'Thalas",
[2038] = "UNUSEDShadowfang Keep 003",
[2040] = "Alah\'Thalas",
[2041] = "Amani\'Alor",
[2137] = "Windswept Heights",
[3430] = "Eversong Woods",
[3431] = "Sunstrider Isle",
[3432] = "Shrine of Dath\'Remar",
[3433] = "Ghostlands",
[3434] = "Scarab Terrace",
[3435] = "General\'s Terrace",
[3436] = "The Reservoir",
[3437] = "The Hatchery",
[3438] = "The Comb",
[3439] = "Watchers\' Terrace",
[3440] = "Scarab Terrace",
[3441] = "General\'s Terrace",
[3442] = "The Reservoir",
[3443] = "The Hatchery",
[3444] = "The Comb",
[3445] = "Watchers\' Terrace",
[3455] = "The North Sea",
[3457] = "Tower of Karazhan",
[3460] = "Golden Strand",
[3461] = "Sunsail Anchorage",
[3462] = "Fairbreeze Village",
[3463] = "Magisters Gate",
[3464] = "Farstrider Retreat",
[3465] = "North Sanctum",
[3466] = "West Sanctum",
[3467] = "East Sanctum",
[3468] = "Saltheril\'s Haven",
[3469] = "Thuron\'s Livery",
[3470] = "Stillwhisper Pond",
[3471] = "The Living Wood",
[3472] = "Azurebreeze Coast",
[3473] = "Lake Elrendar",
[3474] = "The Scorched Grove",
[3475] = "Zeb\'Watha",
[3476] = "Tor\'Watha",
[3479] = "The Veiled Sea",
[3480] = "Duskwither Grounds",
[3481] = "Duskwither Spire",
[3482] = "The Dead Scar",
[3484] = "The Sunspire",
[3485] = "Falthrien Academy",
[3487] = "Silvermoon City",
[3488] = "Tranquillien",
[3489] = "Suncrown Village",
[3490] = "Goldenmist Village",
[3491] = "Windrunner Village",
[3492] = "Windrunner Spire",
[3493] = "Sanctum of the Sun",
[3494] = "Sanctum of the Moon",
[3495] = "Dawnstar Spire",
[3496] = "Farstrider Enclave",
[3497] = "An\'daroth",
[3498] = "An\'telas",
[3499] = "An\'owyn",
[3500] = "Deatholme",
[3501] = "Bleeding Ziggurat",
[3502] = "Howling Ziggurat",
[3503] = "Shalandis Isle",
[3504] = "Toryl Estate",
[3505] = "Underlight Mines",
[3506] = "Andilien Estate",
[3507] = "Hatchet Hills",
[3508] = "Amani Pass",
[3509] = "Sungraze Peak",
[3510] = "Amani Catacombs",
[3511] = "Tower of the Damned",
[3512] = "Zeb\'Sora",
[3513] = "Lake Elrendar",
[3514] = "The Dead Scar",
[3515] = "Elrendar River",
[3516] = "Zeb\'Tela",
[3517] = "Zeb\'Nowa",
[3531] = "Murder Row",
[3532] = "Dawning Lane",
[3533] = "Ruins of Silvermoon",
[3534] = "Feth\'s Way",
[3558] = "Elrendar Falls",
[3805] = "Zul\'Aman",
[3881] = "Silver Sun Mine",
[3882] = "Citadel of the Sun",
[3883] = "Golden Dawn Institute",
[4010] = "Mudsprocket",
[4011] = "Venture Company Camp",
[4012] = "Scarlet Enclave",
[4013] = "Lordaeron Arena",
[4014] = "Blood Ring",
[4015] = "Isle of the Spirits",
[4016] = "Kamio",
[4017] = "Kontsuma",
[4018] = "Hawk\'s Vigil",
[4019] = "Dun Agrath",
[4020] = "Ironforge Airfields",
[4021] = "Sparkwater Port",
[4022] = "Scarlet Citadel",
[4046] = "Direhorn Post",
[4047] = "Nat\'s Landing",
[4067] = "Plaguewood Tower",
[4298] = "Plaguelands",
[4343] = "New Avalon",
[4346] = "New Avalon Town Hall",
[4347] = "Havenshire",
[4354] = "Light\'s Point",
[4356] = "Gloom Hill",
[4358] = "Tyr\'s Hand",
[4359] = "King\'s Harbor",
[4365] = "Havenshire Mine",
[4381] = "Waygate",
[4382] = "Shaper\'s Terrace",
[4411] = "Stormwind Harbor",
[4636] = "The Noxious Pass",
[4688] = "The Turtle Farm",
[5000] = "Thrallmar",
[5001] = "Honor Hold",
[5002] = "Sha\'naar",
[5003] = "Illidari Point",
[5004] = "Forge Camp: Rage",
[5005] = "Watcher\'s Rise",
[5006] = "Dark Portal",
[5007] = "Pools of Aggonar",
[5008] = "Devouring Flats",
[5009] = "Shattered Wastes",
[5010] = "Tanaan Crag",
[5011] = "Farwell Stead",
[5012] = "Wildtusk Village",
[5013] = "Ruins of Zul\'Rasaz",
[5014] = "The Rasaz Trails",
[5015] = "The Highlands",
[5016] = "The Grim Reaches",
[5017] = "Dun Galar",
[5018] = "Flickerlode Mine",
[5019] = "Grimstone Field",
[5020] = "The Parched Basin",
[5021] = "The Grim Batol Memorial",
[5022] = "Karfang Retreat",
[5023] = "Sunnyglade Valley",
[5024] = "Icepoint Rock",
[5025] = "The Hidden Grove",
[5026] = "The Perch",
[5027] = "North Sea",
[5028] = "Steepcliff Port",
[5029] = "The Whispering Forest",
[5030] = "The Rogue Heights",
[5031] = "The Lafford House",
[5032] = "The Garrick Stead",
[5033] = "Crumblepoint Tower",
[5034] = "Shatteridge Tower",
[5035] = "The Remnants Camp",
[5036] = "The Blacktower Inn",
[5037] = "The Corinth Farmstead",
[5038] = "The Jagged Hills",
[5039] = "Gracestone Mine",
[5040] = "Glenshire",
[5041] = "Ishnu\'Danil",
[5042] = "Shalla\'Aran",
[5043] = "Shank\'s Reef",
[5044] = "Crown Island",
[5045] = "The Rock",
[5046] = "Bright Coast",
[5047] = "Zul\'Hazu",
[5048] = "The Wallowing Coast",
[5049] = "Hazzuri Glade",
[5050] = "Gor\'dosh Heights",
[5051] = "Caelan\'s Rest",
[5052] = "Kaneq\'nuun",
[5053] = "Moomoo Grove",
[5054] = "The Broken Reef",
[5055] = "Deeptide Sanctum",
[5056] = "The Silver Coast",
[5057] = "The Silver Sandbar",
[5058] = "Zul\'Razar",
[5059] = "The Tangled Wood",
[5060] = "The Jade Mine",
[5061] = "Ruins of Zul\'Razar",
[5062] = "Gillijim Strand",
[5063] = "Deepneck Cove",
[5064] = "Gillijim Canyon",
[5065] = "Maul\'ogg Refuge",
[5066] = "Maul\'ogg Post",
[5067] = "Kalkor Point",
[5068] = "Kazon Island",
[5069] = "The Southsea Sandbar",
[5070] = "Distillery Island",
[5071] = "Faelon\'s Folly",
[5072] = "The Tower of Lapidis",
[5073] = "Flaxwhisker Front",
[5074] = "Bloodfist Point",
[5075] = "Sorrowguard Keep",
[5076] = "Tamatsune Shrine",
[5077] = "Crescent Grove",
[5078] = "Vilethorn Scar",
[5079] = "Nalash\'ir Ruins",
[5080] = "Tsatsuri Wilds",
[5081] = "Kontsuma Logging Camp",
[5082] = "Umikari Strand",
[5083] = "Konko Depths",
[5084] = "Bloodsail Retreat",
[5085] = "Kontsuma Market",
[5086] = "Karazhan Crypt",
[5087] = "Stormwind Vault",
[5088] = "Everwinter Vale",
[5089] = "Kontsuma Harbor",
[5090] = "The Dungeon",
[5091] = "Frostmane Retreat",
[5092] = "Zul\'Drakash",
[5093] = "Karfang Hold",
[5094] = "Gallant Square",
[5095] = "Livingstone Croft",
[5096] = "Red Cloud Roost",
[5097] = "Emerald Sanctum",
[5098] = "Hateforge Quarry",
[5099] = "Sandmoon Village",
[5100] = "Suntail Pass",
[5101] = "Hateforge Excavation",
[5102] = "Gartside Plot",
[5103] = "Hateforge Quarry",
[5104] = "Ruins of Corthan",
[5105] = "Temple of Corthan",
[5106] = "Angor Digsite",
[5107] = "Ruins of Zeth",
[5108] = "Crystalline Oasis",
[5109] = "Crystalline Pinnacle",
[5110] = "Redbrand\'s Digsite",
[5111] = "Scalebane Ridge",
[5112] = "Blackhorn Village",
[5113] = "Deserter\'s Hideout",
[5114] = "Westhaven Hollow",
[5115] = "Davenburg",
[5116] = "Anchor\'s Edge",
[5117] = "Blacksand Oil Fields",
[5118] = "Powder Town",
[5119] = "Bramblethorn Pass",
[5120] = "Bael Hardul",
[5121] = "Tel\'Abim",
[5122] = "The Shallow Strand",
[5123] = "Tel Co. Basecamp",
[5124] = "Bixxle\'s Storehouse",
[5125] = "Highvale Rise",
[5126] = "The Derelict Camp",
[5127] = "Tazzo\'s Shack",
[5128] = "The Jagged Isles",
[5129] = "The Washing Shore",
[5130] = "Winter Veil Vale",
[5131] = "Icepaw Village",
[5132] = "Shadowfang Keep",
[5134] = "Gnomeregan",
[5135] = "Scarlet Monastery Library",
[5136] = "Scarlet Monastery Graveyard",
[5137] = "The Temple of Atal\'Hakkar",
[5138] = "The Deadmines",
[5139] = "Blackrock Spire",
[5140] = "Blackrock Depths",
[5142] = "Scholomance",
[5144] = "Deeprun Tram",
[5145] = "Dire Maul",
[5146] = "Blackwing Lair",
[5147] = "Ahn\'Qiraj",
[5148] = "The Upper Necropolis",
[5150] = "Shadowfang Keep",
[5152] = "Gnomeregan",
[5153] = "Scarlet Monastery Armory",
[5154] = "The Temple of Atal\'Hakkar",
[5155] = "Blackrock Spire",
[5156] = "Scholomance",
[5157] = "Dire Maul",
[5158] = "Blackwing Lair",
[5161] = "Shadowfang Keep",
[5162] = "Gnomeregan",
[5163] = "Scarlet Monastery Cathedral",
[5164] = "Blackrock Spire",
[5165] = "Scholomance",
[5166] = "Dire Maul",
[5167] = "Blackwing Lair",
[5169] = "Shadowfang Keep",
[5170] = "Blackrock Spire",
[5171] = "Dire Maul",
[5173] = "Shadowfang Keep",
[5174] = "Blackrock Spire",
[5175] = "Dire Maul",
[5177] = "Shadowfang Keep",
[5178] = "Blackrock Spire",
[5179] = "Gilneas",
[5180] = "Gilneas City",
[5181] = "Ravenshire",
[5182] = "Ravenwood Keep",
[5183] = "Brol\'ok Mound",
[5184] = "Southmire Orchard",
[5185] = "Hollow Web Woods",
[5186] = "Hollow Web Cemetery",
[5187] = "Shademore Tavern",
[5188] = "Ruins of Greyshire",
[5189] = "Northgate Tower",
[5190] = "Westgate Tower",
[5191] = "Rosewick Plantation",
[5192] = "Glaymore Stead",
[5193] = "Vagrant Encampment",
[5194] = "Ebonmere Farm",
[5195] = "The Overgrown Acre",
[5196] = "The Dryrock Pit",
[5197] = "The Dryrock Mine",
[5198] = "Freyshear Keep",
[5199] = "Greymane\'s Watch",
[5200] = "Oldrock Pass",
[5201] = "Stillward Church",
[5202] = "Mossgrove Farm",
[5203] = "The Greymane Wall",
[5204] = "The Black Morass",
[5205] = "Dryrock Valley",
[5206] = "Blackthorn\'s Camp",
[5207] = "Dawnstone Mine",
[5208] = "Gilneas City",
[5209] = "Greymane Tower",
[5210] = "Chimaera Roost Vale",
[5211] = "Gnomeregan Reclamation Facility",
[5212] = "Bleakhollow Crater",
[5213] = "Nordrassil Glade",
[5214] = "Nordanaar",
[5215] = "Barkskin Plateau",
[5216] = "Barkskin Village",
[5217] = "Circle of Power",
[5218] = "The Ruins of Telennas",
[5219] = "Zul\'Hatha",
[5220] = "Darkhollow Pass",
[5221] = "The Emerald Gateway",
[5222] = "Darkroot Cave",
[5223] = "Barkskin Hold",
[5224] = "Shrine of the Betrayer",
[5225] = "Thalassian Highlands",
[5526] = "Wretched Ghetto",
[5527] = "Ballador\'s Chapel",
[5528] = "Felstrider Retreat",
[5529] = "The Farstride",
[5530] = "Deepmurk Cave",
[5531] = "Isle of Eternal Autumn",
[5532] = "Anasterian Park",
[5533] = "Brinthilien",
[5534] = "Ruins of Nashal\'aran",
[5535] = "The Scourged Pass",
[5536] = "Blackstone Island",
[5537] = "Rustgate Ridge",
[5538] = "Black Ash Coalpits",
[5539] = "Black Ash Mine",
[5540] = "The Water Hole",
[5541] = "Gazzik\'s Workshop",
[5542] = "Venture Co. Slums",
[5543] = "Rustgate Lumber Yard",
[5544] = "Thaumarium",
[5545] = "Silver Covenant Camp",
[5546] = "The Last Runestone",
[5547] = "Forlorn Summit",
[5548] = "Broken Cliff Mine",
[5550] = "Ruins of Grim Batol",
[5552] = "The Bazaar",
[5553] = "Windrunner Pier",
[5554] = "Sunsworn Mansion",
[5555] = "Regency Spire",
[5556] = "Feralkin Cave",
[5557] = "The Rock of Desolation",
[5558] = "The Rock of Desolation",
[5559] = "Sanv Tribe Village",
[5560] = "The Seat of Desolation",
[5561] = "Balor",
[5562] = "Gullwing Wreckage",
[5563] = "Bilgerat Compound",
[5564] = "SI:7 Outpost",
[5565] = "Ruins of Breezehaven",
[5566] = "Croaking Plateau",
[5567] = "Langston Orchard",
[5568] = "Sorrowmore Lake",
[5569] = "Scurrying Thicket",
[5570] = "Stormwrought Castle",
[5571] = "Stormreaver Spire",
[5572] = "Windrock Cliffs",
[5573] = "Treacherous Crags",
[5574] = "Vander Farmstead",
[5575] = "Grahan Estate",
[5576] = "Stormbreaker Point",
[5579] = "Temple of Aka\'Sha",
[5580] = "Redwall Keep",
[5581] = "Northwind",
[5582] = "Merchant\'s Highroad",
[5583] = "Ambershire",
[5584] = "Jousting Grounds",
[5585] = "Amberwood Keep",
[5586] = "Auburn Forest",
[5587] = "Crystal Falls",
[5588] = "Crawford Winery",
[5589] = "Northwind Logging Camp",
[5590] = "Witch Coven",
[5591] = "Ruins of Birkhaven",
[5592] = "Mildenhall Horse Farm",
[5593] = "Sherwood Quarry",
[5594] = "Blackrock Breach",
[5595] = "Grimmen Lake",
[5596] = "Abbey Gardens",
[5597] = "Stillheart Port",
[5598] = "Earthen Ring",
[5599] = "Rugford\'s Mountain Rest",
[5600] = "Dragonmaw Retreat",
[5601] = "Dragonmaw Retreat",
[5602] = "Grim Reaches",
[5603] = "Dun Kithas",
[5604] = "Grim Batol Burial Grounds",
[5605] = "Davingrad\'s Camp",
[5606] = "The Grim Hollow",
[5607] = "Lake Kithas",
[5608] = "Slatebeard\'s Forge",
[5609] = "The High Pass",
[5610] = "Baggoth\'s Wall",
[5611] = "Sal\'Galaz Mines",
[5612] = "East Ridge Outpost",
[5613] = "Baggoth\'s Rampart",
[5614] = "Ruins of Stolgaz Keep",
[5615] = "Groldan\'s Excavation",
[5616] = "Zarm\'geth Stronghold",
[5617] = "Geth\'kar",
[5618] = "Zarm\'geth Point",
[5619] = "Shatterblade Post",
[5620] = "Grim Batol Memorial",
[5621] = "Brangar\'s Folly",
[5622] = "Tower of Magilou",
[5623] = "Bristlewhisker Cavern",
[5624] = "Amberpaw Hideout",
[5625] = "Northridge Point",
[5626] = "Cinderfall Pass",
[5627] = "Ursan Heights",
[5628] = "Stormwrought Ruins",
[5629] = "Hand of Mephistroth",
[5630] = "Barleycrest Farmstead",
[5631] = "Incindis\' Lair",
[5632] = "Old Shadowforge",
[5633] = "Maw of Ursoc",
[5634] = "Zuluhed\'s Terrace",
[5635] = "Slickwick Oil Rig",
[5638] = "Sunstrider Court",
[5639] = "Ruins of Grim Batol",
-- Turtle WoW 1.18.x additions (from AreaTable.dbc)
[5640] = "Timbermaw Hold",
[5641] = "Windhorn Canyon",
[5642] = "Moonwhisper Coast",
[5643] = "Timbermaw Hold",
[5644] = "Blackroot Village",
[5645] = "Maras\'ethil",
[5646] = "Moonsilk Hollow",
[5647] = "Blackroot Hold",
[5648] = "Shrine of Tides",
[5649] = "Moro\'gai Village",
[5650] = "Shimmerstar Lake",
[5651] = "Grove of the Moon",
[5652] = "Lunarclaw Den",
[5653] = "Ruins of Nendis",
[5654] = "Grove of the Sun",
[5655] = "Shrine of Cyclones",
[5656] = "Riverhorn Village",
[5657] = "Moonhoof Village",
[5658] = "Foulheart Sanctum",
[5659] = "Narvalis Point",
[5660] = "Tyrandas",
[5661] = "Temple of Elunaris",
[5662] = "Withered Enclave",
[5663] = "Elun\'aran",
[5664] = "Moonhoof Retreat",
[5665] = "Shrine of Blazes",
[5666] = "Shrine of Crags",
[5667] = "An\'she\'s Respite",
[5668] = "Ancestral Grounds",
[5669] = "Whispering Shores",
[5670] = "Servitor\'s Sanctuary",
[5671] = "Starshard Cradle",
[5672] = "Vysnagosa\'s Rest",
[5722] = "Thorn Gorge",
[5723] = "Lower Karazhan Halls",
[5725] = "Amberhorn Bluffs",
[5726] = "Dun Bardal",
[5727] = "Mage Tower",
[5728] = "Grimtotem Ruins",
[5729] = "Amberhorn Village",
[5730] = "Farseer Spire",
[5731] = "Windhorn Caverns",
[5732] = "Sunsworn Camp",
[5733] = "Maw of Ursol",
[5734] = "Frostmane Hollow",
[5735] = "Frostmane Hollow",
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+15
View File
@@ -0,0 +1,15 @@
pfDB = {
["areatrigger"] = {},
["items"] = {},
["meta"] = {},
["meta-tbc"] = {},
["minimap"] = {},
["minimap-tbc"] = {},
["objects"] = {},
["professions"] = {},
["quests"] = {},
["quests-itemreq"] = {},
["refloot"] = {},
["units"] = {},
["zones"] = {},
}
File diff suppressed because it is too large Load Diff
+284305
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+2212
View File
File diff suppressed because it is too large Load Diff
+100
View File
@@ -0,0 +1,100 @@
pfDB["minimap-turtle"] = {
[0] = { 0.0, 0.0 },
[10] = { 2700.0, 1800.03 },
[11] = { 4135.42, 2756.25 },
[25] = { 711.56, 468.68 },
[28] = { 4300.0, 2866.67 },
[36] = { 2800.0, 1866.67 },
[40] = { 3500.0, 2333.3 },
[41] = { 2500.0, 1666.63 },
[45] = { 3600.0, 2400.0 },
[46] = { 2929.16, 1952.08 },
[51] = { 2231.25, 1487.5 },
[209] = { 381.0, 254.0 },
[215] = { 5137.5, 3425.0 },
[331] = { 5766.67, 3843.75 },
[400] = { 4400.0, 2933.33 },
[405] = { 4495.83, 2997.91 },
[406] = { 6018.0, 4010.0 },
[408] = { 3092.1, 2047.0 },
[409] = { 2901.45, 1915.9 },
[490] = { 3700.0, 2466.66 },
[491] = { 736.45, 490.96 },
[616] = { 3206.0, 2142.0 },
[618] = { 7100.0, 4733.33 },
[717] = { 378.15, 252.1 },
[718] = { 572.78, 381.85 },
[719] = { 1221.87, 806.42 },
[721] = { 769.67, 513.11 },
[722] = { 881.0, 589.0 },
[796] = { 203.66, 135.04 },
[876] = { 828.0, 541.0 },
[1176] = { 1383.33, 922.91 },
[1337] = { 893.67, 595.78 },
[1377] = { 3483.33, 2322.92 },
[1477] = { 695.03, 463.35 },
[1497] = { 959.37, 640.11 },
[1519] = { 1737.5, 1158.34 },
[1537] = { 790.63, 527.61 },
[1581] = { 449.89, 300.0 },
[1583] = { 886.84, 591.23 },
[1584] = { 1985.0, 1268.0 },
[1941] = { 1348.24, 888.09 },
[1977] = { 2120.83, 1414.6 },
[2017] = { 1185.35, 789.86 },
[2040] = { 1468.0, 976.0 },
[2057] = { 320.05, 213.37 },
[2100] = { 2112.09, 1410.89 },
[2159] = { 483.11, 322.08 },
[2257] = { 312.0, 208.0 },
[2366] = { 1085.86, 726.61 },
[2437] = { 738.86, 492.57 },
[2557] = { 1324.0, 869.0 },
[2677] = { 499.43, 332.95 },
[2717] = { 1495.0, 983.0 },
[3277] = { 1145.84, 764.58 },
[3358] = { 1756.25, 1170.83 },
[3428] = { 977.56, 651.7 },
[3429] = { 2512.5, 1675.0 },
[3456] = { 1991.69, 1318.42 },
[3457] = { 823.0, 549.0 },
[3478] = { 4139.02, 2946.0 },
[4012] = { 3159.0, 2108.0 },
[5023] = { 988.63, 1393.17 },
[5024] = { 1608.0, 1075.0 },
[5053] = { 1007.7, 671.8 },
[5077] = { 2643.21, 1751.16 },
[5086] = { 546.75, 391.9 },
[5087] = { 354.5, 234.74 },
[5097] = { 1273.1, 853.72 },
[5103] = { 752.12, 510.33 },
[5121] = { 3227.0, 2187.0 },
[5130] = { 1432.0, 977.0 },
[5135] = { 320.19, 213.46 },
[5136] = { 619.98, 413.32 },
[5138] = { 656.59, 434.97 },
[5144] = { 309.0, 208.0 },
[5147] = { 2777.54, 1851.69 },
[5148] = { 652.1, 439.67 },
[5153] = { 612.69, 408.46 },
[5163] = { 703.3, 468.87 },
[5179] = { 3666.0, 2442.0 },
[5204] = { 1271.99, 845.48 },
[5208] = { 1250.18, 837.44 },
[5225] = { 3082.0, 2061.0 },
[5536] = { 2472.0, 1665.0 },
[5557] = { 2800.0, 1868.0 },
[5561] = { 3098.0, 2068.0 },
[5581] = { 3241.0, 2157.0 },
[5601] = { 1634.0, 1073.0 },
[5602] = { 5387.0, 3584.0 },
[5628] = { 1980.0, 1350.0 },
-- Turtle WoW 1.18.x additions (from WorldMapArea.dbc)
[1769] = { 861, 607 },
[5640] = { 962, 1425 },
[5641] = { 829, 1234 },
[5642] = { 7856, 5241 },
[5722] = { 694, 1047 },
[5731] = { 279, 440 },
[5734] = { 653, 1011 },
}
+52
View File
@@ -0,0 +1,52 @@
pfDB["minimap"] = {
[0] = { 35199.9, 23466.6 },
[1] = { 4925.0, 3283.34 },
[3] = { 2487.5, 1658.34 },
[4] = { 3350.0, 2233.3 },
[8] = { 2293.75, 1529.17 },
[10] = { 2700.003, 1800.03 },
[11] = { 4135.417, 2756.25 },
[12] = { 3470.84, 2314.62 },
[14] = { 5287.5, 3525.0 },
[15] = { 5250.0, 3500.0 },
[16] = { 5070.84, 3381.25 },
[17] = { 10133.34, 6756.25 },
[28] = { 4299.997, 2866.67 },
[33] = { 6381.25, 4254.1 },
[36] = { 2800.003, 1866.667 },
[38] = { 2758.33, 1839.58 },
[40] = { 3500.003, 2333.3 },
[41] = { 2499.997, 1666.63 },
[44] = { 2170.84, 1447.9 },
[45] = { 3600.003, 2399.997 },
[46] = { 2929.163, 1952.08 },
[47] = { 3850.0, 2566.67 },
[51] = { 2231.253, 1487.5 },
[85] = { 4518.75, 3012.5 },
[130] = { 4200.0, 2800.0 },
[139] = { 3870.83, 2581.25 },
[141] = { 5091.66, 3393.7 },
[148] = { 6550.0, 4366.66 },
[215] = { 5137.5, 3425.003 },
[267] = { 3200.0, 2133.33 },
[331] = { 5766.67, 3843.753 },
[357] = { 6950.0, 4633.33 },
[361] = { 5750.0, 3833.33 },
[400] = { 4399.997, 2933.33 },
[405] = { 4495.83, 2997.913 },
[406] = { 4883.33, 3256.253 },
[440] = { 6900.0, 4600.0 },
[490] = { 3700.003, 2466.66 },
[493] = { 2308.33, 1539.59 },
[618] = { 7100.003, 4733.33 },
[1377] = { 3483.334, 2322.92 },
[1497] = { 959.3754, 640.11 },
[1519] = { 1344.2694, 896.36 },
[1537] = { 790.629, 527.61 },
[1637] = { 1402.61, 935.42 },
[1638] = { 1043.75, 695.83 },
[1657] = { 1058.33, 705.71 },
[2597] = { 4237.5, 2825.0 },
[3277] = { 1145.837, 764.58 },
[3358] = { 1756.247, 1170.83 },
}
File diff suppressed because it is too large Load Diff
+87371
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+528
View File
@@ -0,0 +1,528 @@
pfDB["quests"]["patch"] = {
-- Quests introduced in Turtle WoW 1.18.1 (absent from 1.17 quests-turtle.lua)
[41898] = "1.18.1",
[41899] = "1.18.1",
[41900] = "1.18.1",
[41901] = "1.18.1",
[41902] = "1.18.1",
[41903] = "1.18.1",
[41904] = "1.18.1",
[41905] = "1.18.1",
[41906] = "1.18.1",
[41907] = "1.18.1",
[41908] = "1.18.1",
[41909] = "1.18.1",
[41910] = "1.18.1",
[41911] = "1.18.1",
[41912] = "1.18.1",
[41913] = "1.18.1",
[41914] = "1.18.1",
[41915] = "1.18.1",
[41916] = "1.18.1",
[41917] = "1.18.1",
[41918] = "1.18.1",
[41919] = "1.18.1",
[41920] = "1.18.1",
[41921] = "1.18.1",
[41922] = "1.18.1",
[41923] = "1.18.1",
[41924] = "1.18.1",
[41925] = "1.18.1",
[41926] = "1.18.1",
[41927] = "1.18.1",
[41928] = "1.18.1",
[41929] = "1.18.1",
[41930] = "1.18.1",
[41931] = "1.18.1",
[41932] = "1.18.1",
[41933] = "1.18.1",
[41934] = "1.18.1",
[41935] = "1.18.1",
[41936] = "1.18.1",
[41937] = "1.18.1",
[41938] = "1.18.1",
[41939] = "1.18.1",
[41940] = "1.18.1",
[41941] = "1.18.1",
[41942] = "1.18.1",
[41943] = "1.18.1",
[41944] = "1.18.1",
[41945] = "1.18.1",
[41946] = "1.18.1",
[41947] = "1.18.1",
[41948] = "1.18.1",
[41949] = "1.18.1",
[41950] = "1.18.1",
[41951] = "1.18.1",
[41952] = "1.18.1",
[41953] = "1.18.1",
[41954] = "1.18.1",
[41955] = "1.18.1",
[41956] = "1.18.1",
[41957] = "1.18.1",
[41958] = "1.18.1",
[41959] = "1.18.1",
[41960] = "1.18.1",
[41961] = "1.18.1",
[41962] = "1.18.1",
[41963] = "1.18.1",
[41964] = "1.18.1",
[41965] = "1.18.1",
[41966] = "1.18.1",
[41967] = "1.18.1",
[41968] = "1.18.1",
[41969] = "1.18.1",
[41970] = "1.18.1",
[41972] = "1.18.1",
[41973] = "1.18.1",
[41974] = "1.18.1",
[41975] = "1.18.1",
[41976] = "1.18.1",
[41977] = "1.18.1",
[41978] = "1.18.1",
[41979] = "1.18.1",
[41980] = "1.18.1",
[41981] = "1.18.1",
[41982] = "1.18.1",
[41983] = "1.18.1",
[41984] = "1.18.1",
[41985] = "1.18.1",
[41986] = "1.18.1",
[41987] = "1.18.1",
[41988] = "1.18.1",
[41989] = "1.18.1",
[41990] = "1.18.1",
[41991] = "1.18.1",
[41992] = "1.18.1",
[41993] = "1.18.1",
[41994] = "1.18.1",
[41995] = "1.18.1",
[41996] = "1.18.1",
[41997] = "1.18.1",
[41998] = "1.18.1",
[41999] = "1.18.1",
[42000] = "1.18.1",
[42001] = "1.18.1",
[42002] = "1.18.1",
[42003] = "1.18.1",
[42004] = "1.18.1",
[42005] = "1.18.1",
[42006] = "1.18.1",
[42007] = "1.18.1",
[42008] = "1.18.1",
[42009] = "1.18.1",
[42010] = "1.18.1",
[42011] = "1.18.1",
[42012] = "1.18.1",
[42013] = "1.18.1",
[42014] = "1.18.1",
[42015] = "1.18.1",
[42016] = "1.18.1",
[42017] = "1.18.1",
[42018] = "1.18.1",
[42019] = "1.18.1",
[42020] = "1.18.1",
[42021] = "1.18.1",
[42022] = "1.18.1",
[42023] = "1.18.1",
[42024] = "1.18.1",
[42025] = "1.18.1",
[42026] = "1.18.1",
[42027] = "1.18.1",
[42028] = "1.18.1",
[42029] = "1.18.1",
[42030] = "1.18.1",
[42031] = "1.18.1",
[42032] = "1.18.1",
[42033] = "1.18.1",
[42034] = "1.18.1",
[42035] = "1.18.1",
[42036] = "1.18.1",
[42037] = "1.18.1",
[42038] = "1.18.1",
[42039] = "1.18.1",
[42040] = "1.18.1",
[42041] = "1.18.1",
[42042] = "1.18.1",
[42043] = "1.18.1",
[42044] = "1.18.1",
[42045] = "1.18.1",
[42046] = "1.18.1",
[42047] = "1.18.1",
[42048] = "1.18.1",
[42049] = "1.18.1",
[42050] = "1.18.1",
[42051] = "1.18.1",
[42052] = "1.18.1",
[42053] = "1.18.1",
[42054] = "1.18.1",
[42055] = "1.18.1",
[42056] = "1.18.1",
[42057] = "1.18.1",
[42058] = "1.18.1",
[42059] = "1.18.1",
[42060] = "1.18.1",
[42061] = "1.18.1",
[42062] = "1.18.1",
[42063] = "1.18.1",
[42064] = "1.18.1",
[42065] = "1.18.1",
[42066] = "1.18.1",
[42067] = "1.18.1",
[42068] = "1.18.1",
[42069] = "1.18.1",
[42070] = "1.18.1",
[42071] = "1.18.1",
[42072] = "1.18.1",
[42073] = "1.18.1",
[42074] = "1.18.1",
[42075] = "1.18.1",
[42076] = "1.18.1",
[42077] = "1.18.1",
[42078] = "1.18.1",
[42079] = "1.18.1",
[42080] = "1.18.1",
[42081] = "1.18.1",
[42082] = "1.18.1",
[42083] = "1.18.1",
[42084] = "1.18.1",
[42085] = "1.18.1",
[42086] = "1.18.1",
[42087] = "1.18.1",
[42088] = "1.18.1",
[42089] = "1.18.1",
[42090] = "1.18.1",
[42091] = "1.18.1",
[42092] = "1.18.1",
[42093] = "1.18.1",
[42094] = "1.18.1",
[42095] = "1.18.1",
[42096] = "1.18.1",
[42097] = "1.18.1",
[42098] = "1.18.1",
[42099] = "1.18.1",
[42100] = "1.18.1",
[42101] = "1.18.1",
}
pfDB["units"]["patch"] = {
-- NPCs introduced in Turtle WoW 1.18.1 (absent from 1.17 units-turtle.lua)
[29479] = "1.18.1",
[60686] = "1.18.1",
[62757] = "1.18.1",
[62759] = "1.18.1",
[62760] = "1.18.1",
[62761] = "1.18.1",
[62762] = "1.18.1",
[62763] = "1.18.1",
[62764] = "1.18.1",
[62765] = "1.18.1",
[62766] = "1.18.1",
[62767] = "1.18.1",
[62768] = "1.18.1",
[62769] = "1.18.1",
[62770] = "1.18.1",
[62771] = "1.18.1",
[62772] = "1.18.1",
[62773] = "1.18.1",
[62774] = "1.18.1",
[62777] = "1.18.1",
[62778] = "1.18.1",
[62779] = "1.18.1",
[62780] = "1.18.1",
[62781] = "1.18.1",
[62782] = "1.18.1",
[62783] = "1.18.1",
[62784] = "1.18.1",
[62786] = "1.18.1",
[62787] = "1.18.1",
[62788] = "1.18.1",
[62789] = "1.18.1",
[62790] = "1.18.1",
[62791] = "1.18.1",
[62792] = "1.18.1",
[62793] = "1.18.1",
[62794] = "1.18.1",
[62795] = "1.18.1",
[62796] = "1.18.1",
[62797] = "1.18.1",
[62802] = "1.18.1",
[62803] = "1.18.1",
[62804] = "1.18.1",
[62806] = "1.18.1",
[62807] = "1.18.1",
[62808] = "1.18.1",
[62809] = "1.18.1",
[62810] = "1.18.1",
[62811] = "1.18.1",
[62812] = "1.18.1",
[62813] = "1.18.1",
[62814] = "1.18.1",
[62815] = "1.18.1",
[62816] = "1.18.1",
[62817] = "1.18.1",
[62818] = "1.18.1",
[62820] = "1.18.1",
[62821] = "1.18.1",
[62822] = "1.18.1",
[62823] = "1.18.1",
[62824] = "1.18.1",
[62825] = "1.18.1",
[62826] = "1.18.1",
[62827] = "1.18.1",
[62828] = "1.18.1",
[62829] = "1.18.1",
[62830] = "1.18.1",
[62831] = "1.18.1",
[62832] = "1.18.1",
[62834] = "1.18.1",
[62835] = "1.18.1",
[62836] = "1.18.1",
[62838] = "1.18.1",
[62839] = "1.18.1",
[62842] = "1.18.1",
[62843] = "1.18.1",
[62849] = "1.18.1",
[62862] = "1.18.1",
[62865] = "1.18.1",
[62867] = "1.18.1",
[62870] = "1.18.1",
[62871] = "1.18.1",
[62872] = "1.18.1",
[62873] = "1.18.1",
[62875] = "1.18.1",
[62876] = "1.18.1",
[62877] = "1.18.1",
[62878] = "1.18.1",
[62879] = "1.18.1",
[62880] = "1.18.1",
[62881] = "1.18.1",
[62882] = "1.18.1",
[62883] = "1.18.1",
[62884] = "1.18.1",
[62885] = "1.18.1",
[62887] = "1.18.1",
[62888] = "1.18.1",
[62889] = "1.18.1",
[62890] = "1.18.1",
[62891] = "1.18.1",
[62892] = "1.18.1",
[62893] = "1.18.1",
[62894] = "1.18.1",
[62895] = "1.18.1",
[62899] = "1.18.1",
[62900] = "1.18.1",
[62901] = "1.18.1",
[62902] = "1.18.1",
[62904] = "1.18.1",
[62905] = "1.18.1",
[62906] = "1.18.1",
[62907] = "1.18.1",
[62910] = "1.18.1",
[62913] = "1.18.1",
[62914] = "1.18.1",
[62917] = "1.18.1",
[62924] = "1.18.1",
[62925] = "1.18.1",
[62926] = "1.18.1",
[62927] = "1.18.1",
[62928] = "1.18.1",
[62929] = "1.18.1",
[62930] = "1.18.1",
[62931] = "1.18.1",
[62932] = "1.18.1",
[62933] = "1.18.1",
[62934] = "1.18.1",
[62935] = "1.18.1",
[62936] = "1.18.1",
[62938] = "1.18.1",
[62940] = "1.18.1",
[62941] = "1.18.1",
[62942] = "1.18.1",
[62946] = "1.18.1",
[62947] = "1.18.1",
[62948] = "1.18.1",
[62949] = "1.18.1",
[62950] = "1.18.1",
[62951] = "1.18.1",
[62952] = "1.18.1",
[62953] = "1.18.1",
[62954] = "1.18.1",
[62955] = "1.18.1",
[62956] = "1.18.1",
[62957] = "1.18.1",
[62958] = "1.18.1",
[62959] = "1.18.1",
[62960] = "1.18.1",
[62961] = "1.18.1",
[62962] = "1.18.1",
[62964] = "1.18.1",
[62965] = "1.18.1",
[62966] = "1.18.1",
[62967] = "1.18.1",
[62972] = "1.18.1",
[62973] = "1.18.1",
[62974] = "1.18.1",
[62976] = "1.18.1",
[62977] = "1.18.1",
[62978] = "1.18.1",
[62979] = "1.18.1",
[62980] = "1.18.1",
[62981] = "1.18.1",
[62983] = "1.18.1",
[62984] = "1.18.1",
[62985] = "1.18.1",
[62988] = "1.18.1",
[62990] = "1.18.1",
[62992] = "1.18.1",
[62996] = "1.18.1",
[62997] = "1.18.1",
[62998] = "1.18.1",
[62999] = "1.18.1",
[63000] = "1.18.1",
[63001] = "1.18.1",
[63002] = "1.18.1",
[63003] = "1.18.1",
[63004] = "1.18.1",
[63005] = "1.18.1",
[63006] = "1.18.1",
[63007] = "1.18.1",
[63008] = "1.18.1",
[63009] = "1.18.1",
[63010] = "1.18.1",
[63011] = "1.18.1",
[63014] = "1.18.1",
[63015] = "1.18.1",
[63019] = "1.18.1",
[63026] = "1.18.1",
[63027] = "1.18.1",
[63028] = "1.18.1",
[63029] = "1.18.1",
[63030] = "1.18.1",
[63031] = "1.18.1",
[63032] = "1.18.1",
[63033] = "1.18.1",
[63034] = "1.18.1",
[63035] = "1.18.1",
[63036] = "1.18.1",
[63037] = "1.18.1",
[63038] = "1.18.1",
[63039] = "1.18.1",
[63040] = "1.18.1",
[63041] = "1.18.1",
[63042] = "1.18.1",
[63043] = "1.18.1",
[63052] = "1.18.1",
[63054] = "1.18.1",
[63055] = "1.18.1",
[63056] = "1.18.1",
[63059] = "1.18.1",
[63061] = "1.18.1",
[63062] = "1.18.1",
[63063] = "1.18.1",
[63064] = "1.18.1",
[63065] = "1.18.1",
[63066] = "1.18.1",
[63067] = "1.18.1",
[63068] = "1.18.1",
[63071] = "1.18.1",
[63072] = "1.18.1",
[63075] = "1.18.1",
[63078] = "1.18.1",
[63079] = "1.18.1",
[63080] = "1.18.1",
[63081] = "1.18.1",
[63083] = "1.18.1",
[63084] = "1.18.1",
[63085] = "1.18.1",
[63086] = "1.18.1",
[63087] = "1.18.1",
[63088] = "1.18.1",
[63090] = "1.18.1",
[63092] = "1.18.1",
[63094] = "1.18.1",
[63095] = "1.18.1",
[63096] = "1.18.1",
[63097] = "1.18.1",
[63098] = "1.18.1",
[63099] = "1.18.1",
[63102] = "1.18.1",
[63106] = "1.18.1",
[63116] = "1.18.1",
[63117] = "1.18.1",
[63118] = "1.18.1",
[63119] = "1.18.1",
[63121] = "1.18.1",
[63122] = "1.18.1",
[63124] = "1.18.1",
[63127] = "1.18.1",
[63129] = "1.18.1",
[63130] = "1.18.1",
[63131] = "1.18.1",
[63133] = "1.18.1",
[63134] = "1.18.1",
[63135] = "1.18.1",
[63136] = "1.18.1",
[63137] = "1.18.1",
[63138] = "1.18.1",
[63139] = "1.18.1",
[63141] = "1.18.1",
[63142] = "1.18.1",
[63143] = "1.18.1",
[63144] = "1.18.1",
[63145] = "1.18.1",
[63146] = "1.18.1",
[63147] = "1.18.1",
[63148] = "1.18.1",
[63149] = "1.18.1",
[63150] = "1.18.1",
[63151] = "1.18.1",
[63152] = "1.18.1",
[63153] = "1.18.1",
[63154] = "1.18.1",
[63155] = "1.18.1",
[63156] = "1.18.1",
[63157] = "1.18.1",
[63158] = "1.18.1",
[63159] = "1.18.1",
[63160] = "1.18.1",
[63161] = "1.18.1",
[63162] = "1.18.1",
[63163] = "1.18.1",
[63164] = "1.18.1",
[63165] = "1.18.1",
[63166] = "1.18.1",
[63167] = "1.18.1",
[63168] = "1.18.1",
[63169] = "1.18.1",
[63170] = "1.18.1",
[63172] = "1.18.1",
[63173] = "1.18.1",
[63182] = "1.18.1",
[63183] = "1.18.1",
[63184] = "1.18.1",
[63185] = "1.18.1",
[63186] = "1.18.1",
[63187] = "1.18.1",
[63188] = "1.18.1",
[63189] = "1.18.1",
[63190] = "1.18.1",
[63192] = "1.18.1",
[63193] = "1.18.1",
[63194] = "1.18.1",
[63195] = "1.18.1",
[63196] = "1.18.1",
[63197] = "1.18.1",
[63198] = "1.18.1",
[63199] = "1.18.1",
[63201] = "1.18.1",
[63203] = "1.18.1",
[63204] = "1.18.1",
[63205] = "1.18.1",
[63206] = "1.18.1",
[63207] = "1.18.1",
[63208] = "1.18.1",
[63209] = "1.18.1",
[63210] = "1.18.1",
[93353] = "1.18.1",
}
+153
View File
@@ -0,0 +1,153 @@
pfDB["quests-itemreq"]["data-turtle"] = {
[4702] = {
[-3000687] = "4954",
[-3000684] = "4954",
[-184617] = "4954",
[-181130] = "4954",
[-180913] = "4954",
[-179924] = "4954",
[-179886] = "4954",
[-179863] = "4954",
[-179844] = "4954",
[-178684] = "4954",
[-176895] = "4954",
[-176509] = "4954",
[-175851] = "4954",
[-175144] = "4954",
[-173095] = "4954",
[-173064] = "4954",
[-173063] = "4954",
[-171717] = "4954",
[-171716] = "4954",
[-169969] = "4954",
[-161487] = "4954",
[-153459] = "4954",
[-152045] = "4954",
[-152042] = "4954",
[-152034] = "4954",
[-148960] = "4954",
[-147285] = "4954",
[-144133] = "4954",
[-142078] = "4954",
[-141841] = "4954",
[-141838] = "4954",
[-138317] = "4954",
[-130668] = "4954",
[-123210] = "4954",
[-123209] = "4954",
[-123208] = "4954",
[-123207] = "4954",
[-113754] = "4954",
[-92490] = "4954",
[-56897] = "4954",
[-52176] = "4954",
[-52175] = "4954",
[-51949] = "4954",
[-50985] = "4954",
[-50984] = "4954",
[-50983] = "4954",
[-50831] = "4954",
[-50469] = "4954",
[-38491] = "4954",
[-34572] = "4954",
[-34571] = "4954",
[-24746] = "4954",
[-24745] = "4954",
[-23305] = "4954",
[-23304] = "4954",
[-21679] = "4954",
[-20986] = "4954",
[-20738] = "4954",
[-19902] = "4954",
[-17190] = "4954",
[-4090] = "4954",
[-3223] = "4954",
[-2728] = "4954",
[-2727] = "4954",
[-2575] = "4954",
[-2573] = "4954",
[-2015] = "4954",
[-1896] = "4954",
[-1797] = "4954",
[-1749] = "4954",
[-1745] = "4954",
[-1743] = "4954",
[-1685] = "4954",
},
[5623] = "_",
[9364] = {
[6218] = "12709",
[6219] = "12709",
[6220] = "12709",
[6329] = "12709",
},
[11473] = "_",
[15826] = {
[12296] = 0,
[12298] = 0,
},
[18488] = "_",
[18688] = {
[-187115] = "23180",
[-180632] = "23180",
[-180631] = "23180",
[-177387] = "23180",
},
[19710] = "_",
[19711] = "_",
[19712] = "_",
[19713] = "_",
[19714] = "_",
[19715] = "_",
[41064] = {
[61700] = 0,
},
[41065] = {
[61695] = 0,
},
[41066] = {
[61776] = 0,
},
[41557] = {
[12296] = 0,
},
[50445] = {
[61662] = 0,
},
[50446] = {
[61680] = 0,
},
[50447] = {
[61657] = 0,
},
[60329] = {
[1547] = 0,
},
[60330] = {
[1553] = 0,
},
[60331] = {
[80466] = 0,
},
[61812] = {
[113] = 0,
},
[61813] = {
[118] = 0,
},
[61814] = {
[822] = 0,
},
[91770] = {
[3099] = 0,
},
[91772] = {
[3126] = 0,
},
[91773] = {
[1126] = 0,
},
[91775] = {
[1196] = 0,
},
}
+708
View File
@@ -0,0 +1,708 @@
pfDB["quests-itemreq"]["data"] = {
[3467] = {
[-1721] = 0,
},
[3499] = {
[-1722] = 0,
},
[3710] = {
[-1770] = 0,
[-1769] = 0,
[-1768] = 0,
},
[3912] = {
[2530] = 0,
},
[4027] = {
[-2576] = 0,
},
[4472] = {
[-2705] = "4141",
},
[4479] = {
[2762] = "4132",
},
[4480] = {
[2761] = "4131",
},
[4481] = {
[2760] = "4130",
},
[4529] = {
[-2715] = 0,
},
[4702] = {
[-181130] = "4954",
[-180913] = "4954",
[-179924] = "4954",
[-179886] = "4954",
[-179863] = "4954",
[-179844] = "4954",
[-178684] = "4954",
[-176895] = "4954",
[-176509] = "4954",
[-175851] = "4954",
[-175144] = "4954",
[-173095] = "4954",
[-173064] = "4954",
[-173063] = "4954",
[-171717] = "4954",
[-171716] = "4954",
[-169969] = "4954",
[-161487] = "4954",
[-153459] = "4954",
[-152045] = "4954",
[-152042] = "4954",
[-152034] = "4954",
[-148960] = "4954",
[-147285] = "4954",
[-144133] = "4954",
[-142078] = "4954",
[-141841] = "4954",
[-141838] = "4954",
[-138317] = "4954",
[-130668] = "4954",
[-123210] = "4954",
[-123209] = "4954",
[-123208] = "4954",
[-123207] = "4954",
[-113754] = "4954",
[-92490] = "4954",
[-56897] = "4954",
[-52176] = "4954",
[-52175] = "4954",
[-51949] = "4954",
[-50985] = "4954",
[-50984] = "4954",
[-50983] = "4954",
[-50831] = "4954",
[-50469] = "4954",
[-38491] = "4954",
[-34572] = "4954",
[-34571] = "4954",
[-24746] = "4954",
[-24745] = "4954",
[-23305] = "4954",
[-23304] = "4954",
[-21679] = "4954",
[-20986] = "4954",
[-20738] = "4954",
[-19902] = "4954",
[-17190] = "4954",
[-4090] = "4954",
[-3223] = "4954",
[-2728] = "4954",
[-2727] = "4954",
[-2575] = "4954",
[-2573] = "4954",
[-2015] = "4954",
[-1896] = "4954",
[-1797] = "4954",
[-1749] = "4954",
[-1745] = "4954",
[-1743] = "4954",
[-1685] = "4954",
},
[4823] = {
[-2914] = "4981",
},
[5021] = {
[-3644] = 0,
},
[5068] = {
[-3737] = 0,
},
[5165] = {
[-6908] = 0,
[-6907] = 0,
[-6906] = 0,
},
[5185] = {
[-19549] = "4976",
},
[5338] = {
[-16393] = 0,
},
[5411] = {
[-2913] = "4975",
},
[5415] = {
[-2911] = "4977",
},
[5416] = {
[-2909] = "4978",
},
[5619] = {
[-19550] = "6609",
},
[5621] = {
[-19551] = "6610",
},
[5623] = {
[-19552] = "6611",
},
[5638] = {
[-19594] = "6620",
},
[5692] = {
[-19592] = "6627",
},
[5693] = {
[-19601] = "6656",
},
[5694] = {
[-19600] = "6630",
},
[5695] = {
[-19591] = "6626",
},
[5810] = {
[-19876] = "6717",
},
[5867] = {
[-20806] = "6803",
},
[5880] = {
[-20919] = "6918",
},
[6074] = {
[-138497] = 0,
},
[6284] = {
[-37097] = "9221",
[37097] = "9221",
},
[6286] = {
[-37097] = "9223",
[37097] = "9223",
},
[6436] = {
[4663] = "7914",
[4664] = "7914",
[4665] = "7914",
[4666] = "7914",
[4667] = "7914",
},
[6626] = {
[-105576] = "9224",
[105576] = "9224",
},
[6635] = {
[-100035] = "8202",
[-63674] = "8202",
[100028] = "8202",
[101749] = "8202",
},
[6637] = {
[-100035] = "8899",
[-63674] = "8899",
[101750] = "8899",
[113791] = "8899",
},
[6684] = {
[4781] = "8283",
},
[6866] = {
[6172] = "8593",
[6177] = "8593",
},
[6913] = {
[-92015] = "8674",
[92015] = "8674",
},
[6928] = {
[-92015] = "7728",
[92015] = "7728",
},
[6931] = {
[-92388] = "8712",
[92252] = "8712",
[92388] = "8712",
},
[6997] = {
[-92388] = "8712",
[92252] = "8712",
[92388] = "8712",
},
[6999] = {
[-92388] = "8712",
[92252] = "8712",
[92388] = "8712",
},
[7207] = {
[-174794] = "8919",
},
[7247] = {
[-103708] = "9082",
[6492] = "9082",
},
[7269] = {
[-174797] = "9052",
},
[7273] = {
[-103660] = "9055",
},
[7297] = {
[1200] = "8913",
},
[7308] = {
[-103708] = "9095",
[6492] = "9095",
},
[7464] = {
[-105576] = "9222",
[105576] = "9222",
},
[7766] = {
[-107046] = "9586",
},
[7767] = {
[-107047] = "9584",
},
[7768] = {
[-107045] = "9583",
},
[8051] = {
[7233] = "10113",
},
[8149] = {
[7318] = 0,
},
[8155] = {
[-138498] = "10459",
},
[8584] = {
[-174796] = "11758",
},
[9189] = {
[7774] = "11402",
},
[9263] = {
[-144013] = "11537",
},
[9283] = {
[6213] = "11513",
[6329] = "11513",
},
[9323] = {
[-142716] = "11548",
},
[9328] = {
[7977] = 0,
},
[9364] = {
[6218] = "12709",
[6219] = "12709",
[6220] = "12709",
},
[9466] = {
[144050] = "11757",
},
[9472] = {
[-144070] = 0,
[144070] = "11792",
},
[9530] = {
[-144569] = "11840",
},
[9606] = {
[7584] = 0,
},
[9618] = {
[2927] = 0,
[2928] = 0,
[2929] = 0,
[7808] = 0,
},
[9619] = {
[5300] = 0,
[5304] = 0,
[5305] = 0,
[5306] = 0,
},
[9620] = {
[5276] = 0,
[5278] = 0,
},
[9621] = {
[5357] = 0,
[5358] = 0,
[14604] = 0,
[14640] = 0,
},
[10327] = {
[-164651] = "12189",
},
[10338] = {
[-164644] = "12199",
},
[10465] = {
[-148422] = "12346",
},
[10515] = {
[-149032] = 0,
[-149031] = 0,
[-149030] = 0,
[-149025] = 0,
},
[10622] = {
[-152093] = "12684",
},
[10699] = {
[5307] = "12699",
[5308] = "12699",
},
[10757] = {
[-153205] = 0,
},
[11079] = {
[-160840] = 0,
},
[11132] = {
[-174793] = "13630",
},
[11169] = {
[-148507] = "13978",
},
[11231] = {
[9016] = "13982",
},
[11243] = {
[-164799] = "14050",
},
[11286] = {
[9520] = "14250",
},
[11412] = {
[-165678] = "14929",
},
[11473] = {
[-166807] = "15066",
},
[11522] = {
[9376] = "14247",
},
[11682] = {
[-138498] = "15491",
},
[11804] = {
[9999] = "15591",
},
[11833] = {
[-210338] = "15627",
[-210337] = "15627",
[-174792] = "15627",
},
[12262] = {
[10221] = "15998",
},
[12284] = {
[7047] = "16007",
[7048] = "16007",
},
[12287] = {
[175124] = "15958",
},
[12346] = {
[-174795] = "16073",
},
[12350] = {
[-175371] = "16077",
[-4005] = "16077",
},
[12368] = {
[-176111] = "17045",
},
[12472] = {
[-148503] = "16378",
[10541] = "16378",
},
[12565] = {
[-148501] = "15647",
},
[12566] = {
[-148501] = "15649",
},
[12627] = {
[-175795] = "16613",
[175795] = "16613",
},
[12647] = {
[4678] = 0,
[4681] = 0,
[4685] = 0,
},
[12733] = {
[-103819] = "16796",
},
[12785] = {
[-175944] = 0,
},
[12807] = {
[-176088] = "16989",
},
[12815] = {
[-176097] = "17016",
[-176096] = "17016",
[-176095] = "17016",
[-176094] = "17016",
[176094] = "17016",
[176095] = "17016",
[176096] = "17016",
[176097] = "17016",
},
[12848] = {
[10899] = "17048",
},
[12906] = {
[-176161] = 0,
[-176160] = 0,
[-176159] = 0,
[-176158] = 0,
},
[12922] = {
[-176184] = "17161",
},
[12928] = {
[7583] = "17166",
[10977] = "17166",
[10978] = "17166",
},
[13289] = {
[10384] = "17368",
[10385] = "17368",
[11122] = "17368",
},
[13536] = {
[11218] = "17536",
},
[13752] = {
[10508] = "17179",
},
[14338] = {
[-174795] = "18397",
},
[14523] = {
[-176581] = 0,
},
[14547] = {
[-177400] = 0,
[-177399] = 0,
[-177398] = 0,
[-177397] = 0,
[-177369] = 0,
[-177368] = 0,
[-177367] = 0,
[-177366] = 0,
[-177365] = 0,
[-177243] = 0,
},
[14644] = {
[-4004] = "18711",
},
[15208] = {
[-177525] = "18974",
},
[15209] = {
[-177528] = "18987",
},
[15710] = {
[-177525] = "19138",
},
[15736] = {
[-177668] = "19250",
[177668] = "19250",
},
[15842] = {
[-19464] = "19565",
},
[15844] = {
[-19463] = "19564",
},
[15848] = {
[-177748] = "19588",
},
[15877] = {
[-177788] = "19719",
},
[15883] = {
[-177788] = "19720",
},
[15908] = {
[1196] = 0,
},
[15911] = {
[1126] = 0,
},
[15913] = {
[1201] = 0,
},
[15914] = {
[2956] = 0,
},
[15915] = {
[2959] = 0,
},
[15916] = {
[2970] = 0,
},
[15917] = {
[3099] = 0,
},
[15919] = {
[3107] = 0,
},
[15920] = {
[3126] = 0,
},
[15921] = {
[1998] = 0,
},
[15922] = {
[2043] = 0,
},
[15923] = {
[1996] = 0,
},
[16208] = {
[-177929] = 0,
},
[16333] = {
[-178090] = 0,
},
[16603] = {
[-178145] = 0,
},
[16972] = {
[-178206] = "20737",
},
[16974] = {
[-178225] = "20814",
},
[17626] = {
[10981] = "21794",
},
[17689] = {
[10990] = "21866",
},
[17693] = {
[-178907] = "21884",
},
[17696] = {
[178905] = "21885",
[178908] = "21885",
},
[18152] = {
[-19552] = "22562",
},
[18488] = {
[10184] = "22906",
},
[18601] = {
[12396] = "23015",
},
[18626] = {
[-179677] = "23054",
},
[18688] = {
[-180632] = "23180",
[-180631] = "23180",
[-177387] = "23180",
},
[18746] = {
[-179746] = "23204",
},
[18749] = {
[-179746] = "23254",
},
[18752] = {
[-179747] = "23208",
[179747] = "23208",
},
[18904] = {
[5357] = "23359",
[5358] = "23359",
[5359] = "23359",
[5360] = "23359",
[5361] = "23359",
},
[19036] = {
[-179912] = "23514",
},
[19710] = {
[-180375] = "24377",
},
[19711] = {
[-180375] = "24377",
},
[19712] = {
[-180375] = "24377",
},
[19713] = {
[-180375] = "24377",
},
[19714] = {
[-180375] = "24377",
},
[19715] = {
[-180375] = "24377",
},
[19850] = {
[-180204] = "24194",
},
[19851] = {
[-180205] = "24195",
},
[20387] = {
[-180504] = "24706",
},
[20464] = {
[-180505] = "24815",
},
[20604] = {
[180449] = "24973",
[180450] = "24973",
},
[21136] = {
[-300057] = "25783",
[-180669] = "25783",
},
[21144] = {
[-180673] = "25793",
},
[21986] = {
[-181059] = "27517",
[16079] = "27517",
},
[22048] = {
[16042] = 0,
},
[22049] = {
[-181046] = "27184",
[16044] = "27184",
},
[22050] = {
[-181047] = "27190",
[16045] = "27190",
},
[22051] = {
[-181048] = "27191",
[16046] = "27191",
},
[22052] = {
[-181049] = "27201",
[16047] = "27201",
},
[22056] = {
[-181050] = "27202",
[16048] = "27202",
},
[22432] = {
[6498] = 0,
[6499] = 0,
[6500] = 0,
},
[22737] = {
[-181206] = "28354",
},
}
File diff suppressed because it is too large Load Diff
+56030
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+30786
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+127159
View File
File diff suppressed because it is too large Load Diff
+150
View File
@@ -0,0 +1,150 @@
pfDB["zones"]["data-turtle"] = {
[460] = { 406, 7.49, 7.49, 49.55, 61.98 },
[461] = { 406, 14.47, 18.71, 66.32, 51.57 },
[464] = { 406, 8.88, 10.33, 51.05, 45.88 },
[465] = { 406, 10.28, 11.83, 38.27, 68.64 },
[467] = { 406, 12.67, 13.47, 41.67, 22.16 },
[1076] = { 406, 15.17, 30.54, 60.88, 70.36 },
[2040] = { 0, 0, 0, 0, 0 },
[2041] = { 406, 5.99, 9.28, 24.05, 12.13 },
[2537] = { 406, 9.28, 5.84, 75.3, 83.16 },
[2538] = { 406, 4.69, 5.99, 75.1, 88.47 },
[2539] = { 406, 6.29, 5.99, 71.21, 90.87 },
[2540] = { 406, 6.19, 6.89, 63.47, 88.62 },
[2541] = { 406, 5.49, 7.63, 56.04, 72.83 },
[3881] = { 5225, 3.19, 3.14, 41.42, 74.33 },
[4011] = { 406, 7.19, 7.49, 41.12, 40.27 },
[5043] = { 409, 10.58, 9.58, 57.19, 22.31 },
[5044] = { 409, 9.98, 9.13, 57.58, 12.35 },
[5045] = { 409, 5.09, 8.23, 72.21, 28.22 },
[5046] = { 409, 12.28, 14.82, 33.68, 58.31 },
[5047] = { 409, 14.17, 14.37, 47.5, 75.45 },
[5048] = { 409, 6.29, 14.22, 69.81, 61.6 },
[5049] = { 409, 6.09, 7.63, 52.94, 52.02 },
[5050] = { 409, 11.68, 23.8, 37.87, 33.91 },
[5051] = { 409, 7.58, 9.13, 59.98, 43.94 },
[5052] = { 5024, 9.28, 8.83, 55.34, 64.45 },
[5055] = { 408, 16.27, 10.48, 29.69, 87.28 },
[5056] = { 408, 13.27, 7.04, 45.16, 80.16 },
[5057] = { 408, 3.39, 15.42, 27.84, 69.39 },
[5058] = { 408, 6.39, 12.13, 45.61, 57.11 },
[5059] = { 408, 7.98, 12.87, 64.27, 57.19 },
[5060] = { 408, 4.19, 5.09, 58.48, 48.2 },
[5061] = { 408, 7.78, 6.14, 45.51, 69.99 },
[5062] = { 408, 6.89, 21.11, 70.81, 56.36 },
[5065] = { 408, 9.58, 14.07, 69.46, 78.74 },
[5066] = { 408, 3.79, 5.09, 59.08, 82.04 },
[5067] = { 408, 4.29, 6.44, 49.75, 38.1 },
[5068] = { 408, 17.27, 14.22, 52.45, 15.49 },
[5069] = { 408, 10.78, 22.16, 24.35, 22.01 },
[5070] = { 408, 7.29, 7.34, 27.69, 37.5 },
[5071] = { 408, 6.99, 10.18, 37.62, 52.1 },
[5072] = { 409, 5.29, 7.34, 60.03, 31.81 },
[5117] = { 406, 13.27, 20.51, 28.19, 32.41 },
[5118] = { 406, 6.49, 6.14, 36.98, 50.07 },
[5119] = { 406, 13.27, 13.62, 75, 72.38 },
[5120] = { 406, 6.19, 7.49, 77.84, 61.83 },
[5123] = { 5121, 7.58, 6.59, 42.12, 79.04 },
[5124] = { 5121, 5.49, 4.94, 51.35, 33.76 },
[5125] = { 5121, 9.58, 17.81, 55.19, 50.37 },
[5126] = { 5121, 3.69, 4.94, 41.57, 52.77 },
[5127] = { 5121, 4.59, 6.59, 53.09, 81.44 },
[5128] = { 5121, 19.26, 23.2, 53.84, 19.54 },
[5180] = { 5179, 11.78, 23.95, 23.15, 36.23 },
[5181] = { 5179, 6.69, 7.34, 58.63, 72.23 },
[5182] = { 5179, 7.19, 12.43, 63.67, 83.61 },
[5183] = { 5179, 7.29, 7.04, 54.84, 63.55 },
[5184] = { 5179, 5.79, 5.09, 44.61, 65.57 },
[5185] = { 5179, 11.78, 7.63, 42.91, 75.37 },
[5186] = { 5179, 3.09, 3.44, 33.38, 76.27 },
[5187] = { 5179, 2, 2.54, 33.53, 53.67 },
[5188] = { 5179, 7.49, 6.14, 33.68, 47.68 },
[5189] = { 5179, 2.5, 2.99, 47.95, 35.63 },
[5191] = { 5179, 2.4, 3.74, 43.81, 27.77 },
[5192] = { 5179, 2.99, 3.74, 53.39, 29.27 },
[5195] = { 5179, 3.09, 4.34, 48.05, 56.66 },
[5196] = { 5179, 4.19, 10.48, 20.26, 64.67 },
[5197] = { 5179, 0, 0, 0, 0 },
[5198] = { 5179, 2.79, 4.04, 66.17, 65.19 },
[5199] = { 5179, 2.59, 4.34, 64.97, 57.86 },
[5200] = { 5179, 4.79, 16.32, 39.62, 15.94 },
[5201] = { 5179, 4.89, 5.84, 56.94, 40.04 },
[5203] = { 5179, 5.09, 6.59, 46.76, 17.07 },
[5206] = { 5179, 3.29, 4.64, 14.32, 33.76 },
[5207] = { 5179, 0, 0, 0, 0 },
[5212] = { 616, 22.16, 30.39, 55.49, 32.11 },
[5213] = { 616, 14.77, 33.53, 73.95, 31.89 },
[5214] = { 616, 5.49, 7.19, 84.08, 28.89 },
[5215] = { 616, 13.37, 12.72, 50.7, 55.46 },
[5216] = { 616, 13.27, 7.78, 63.52, 67.07 },
[5217] = { 616, 8.28, 9.13, 33.68, 40.94 },
[5218] = { 616, 11.18, 18.11, 21.76, 49.33 },
[5219] = { 616, 10.08, 12.13, 9.93, 44.84 },
[5220] = { 616, 9.08, 11.98, 43.26, 76.5 },
[5221] = { 616, 5.59, 6.59, 20.56, 60.48 },
[5528] = { 5225, 13.37, 20.21, 27.74, 78.82 },
[5529] = { 5225, 16.77, 10.03, 32.24, 48.13 },
[5531] = { 5225, 16.07, 19.01, 33.38, 28.97 },
[5532] = { 5225, 11.08, 12.43, 52.05, 49.48 },
[5533] = { 5225, 10.48, 12.87, 48.65, 85.48 },
[5534] = { 5225, 13.67, 16.32, 16.92, 44.99 },
[5537] = { 5536, 9.78, 10.33, 48.3, 65.79 },
[5538] = { 5536, 8.48, 9.73, 35.78, 61.15 },
[5539] = { 5536, 9.18, 9.43, 53.29, 50.22 },
[5540] = { 5536, 5.29, 6.59, 57.83, 24.1 },
[5541] = { 5536, 9.28, 11.23, 62.62, 82.41 },
[5542] = { 5536, 5.19, 8.08, 54.89, 32.34 },
[5543] = { 5536, 8.68, 10.33, 62.33, 58.46 },
[5546] = { 5225, 7.58, 8.83, 57.19, 71.33 },
[5548] = { 406, 5.29, 6.44, 47.55, 34.96 },
[5558] = { 5557, 9.28, 8.83, 55.34, 64.45 },
[5562] = { 5561, 6.29, 8.53, 27.3, 12.8 },
[5563] = { 5561, 7.58, 13.02, 41.42, 21.63 },
[5564] = { 5561, 6.49, 5.84, 70.11, 77.32 },
[5565] = { 5561, 8.28, 8.08, 53.64, 49.25 },
[5566] = { 5561, 5.69, 7.49, 46.96, 45.36 },
[5567] = { 5561, 5.49, 5.69, 47.85, 57.93 },
[5568] = { 5561, 8.88, 10.93, 39.07, 52.62 },
[5569] = { 5561, 7.19, 16.92, 35.03, 37.05 },
[5570] = { 5561, 9.68, 13.32, 56.94, 63.4 },
[5571] = { 5561, 7.68, 7.78, 55.34, 74.55 },
[5572] = { 5561, 6.99, 32.78, 30.14, 65.34 },
[5573] = { 5561, 13.57, 7.49, 52.69, 86.68 },
[5574] = { 5561, 5.09, 8.53, 39.87, 62.95 },
[5575] = { 5561, 4.39, 7.78, 35.63, 66.17 },
[5576] = { 5561, 5.69, 9.88, 71.11, 47.6 },
[5582] = { 5581, 9.98, 6.44, 47.01, 81.96 },
[5583] = { 5581, 7.39, 7.63, 46.71, 58.61 },
[5585] = { 5581, 4.89, 5.54, 32.88, 60.55 },
[5587] = { 5581, 9.78, 11.08, 76.35, 86.38 },
[5588] = { 5581, 4.39, 6.29, 56.99, 58.83 },
[5589] = { 5581, 5.09, 7.93, 38.77, 43.34 },
[5590] = { 5581, 5.89, 6.59, 32.88, 26.05 },
[5591] = { 5581, 5.29, 9.13, 74, 31.06 },
[5593] = { 5581, 13.47, 30.39, 84.18, 19.69 },
[5594] = { 5581, 5.49, 21.26, 82.98, 47.9 },
[5595] = { 5581, 5.69, 13.62, 67.91, 69.24 },
[5596] = { 5581, 8.28, 9.88, 62.23, 23.8 },
[5597] = { 5581, 6.39, 8.08, 21.26, 44.16 },
[5598] = { 406, 5.79, 8.68, 49.6, 71.56 },
[5603] = { 5602, 5.89, 5.84, 53.74, 56.81 },
[5606] = { 5602, 19.26, 18.26, 55.14, 83.08 },
[5607] = { 5602, 5.99, 7.63, 54.19, 48.13 },
[5608] = { 5602, 3.99, 5.54, 56.19, 70.13 },
[5609] = { 5602, 2.59, 9.58, 45.21, 56.29 },
[5611] = { 5602, 6.49, 10.03, 60.03, 66.24 },
[5612] = { 5602, 3.99, 5.09, 46.91, 35.33 },
[5613] = { 5602, 5.39, 4.19, 44.51, 42.22 },
[5614] = { 5602, 6.09, 5.24, 42.76, 17.14 },
[5615] = { 5602, 4.99, 7.49, 61.78, 44.16 },
[5616] = { 5602, 5.89, 7.49, 54.64, 15.42 },
[5617] = { 5602, 3.29, 4.49, 50.05, 21.26 },
[5618] = { 5602, 2.99, 4.34, 50.4, 10.4 },
[5619] = { 5602, 5.59, 5.69, 59.48, 32.04 },
[5621] = { 5602, 3.09, 5.99, 60.73, 14.97 },
[5622] = { 5581, 4.89, 6.29, 30.39, 42.96 },
[5623] = { 5581, 3.99, 4.79, 66.67, 45.36 },
[5625] = { 5581, 3.89, 4.79, 54.24, 32.04 },
[5626] = { 5581, 9.58, 15.42, 83.73, 70.43 },
[5630] = { 5602, 3.09, 5.54, 57.44, 39.45 },
}
+528
View File
@@ -0,0 +1,528 @@
pfDB["zones"]["data"] = {
[9] = { 12, 17.47, 27.69, 51.15, 42.29 },
[18] = { 12, 15.47, 14.22, 52.64, 64 },
[19] = { 33, 14.97, 22.46, 59.88, 18.71 },
[20] = { 40, 10.48, 20.21, 44.16, 65.49 },
[35] = { 33, 8.48, 10.48, 26.2, 74.1 },
[37] = { 33, 5.99, 7.49, 40.42, 18.71 },
[42] = { 10, 12.48, 14.97, 77.1, 44.91 },
[43] = { 33, 4.99, 7.49, 32.44, 79.34 },
[57] = { 12, 16.97, 15.72, 38.42, 84.21 },
[60] = { 12, 9.98, 29.94, 24.95, 81.59 },
[62] = { 12, 11.98, 10.48, 67.37, 79.34 },
[68] = { 44, 34.93, 20.96, 41.92, 59.13 },
[69] = { 44, 17.96, 11.98, 25.45, 43.41 },
[70] = { 44, 13.47, 20.21, 66.12, 55.01 },
[71] = { 44, 18.46, 12.72, 74.6, 66.99 },
[72] = { 4, 12.97, 15.72, 58.38, 56.51 },
[73] = { 4, 22.95, 36.68, 37.92, 62.5 },
[74] = { 8, 18.46, 26.2, 71.11, 52.77 },
[75] = { 8, 21.46, 20.21, 47.65, 55.01 },
[76] = { 8, 22.46, 26.95, 65.12, 22.46 },
[86] = { 12, 15.47, 24.7, 75.1, 49.78 },
[87] = { 12, 8.48, 9.73, 41.17, 64.75 },
[88] = { 12, 12.48, 12.72, 83.58, 66.24 },
[91] = { 12, 13.47, 13.47, 67.61, 66.62 },
[93] = { 10, 15.47, 16.47, 36.18, 73.35 },
[94] = { 10, 10.48, 10.48, 20.71, 56.14 },
[95] = { 44, 22.46, 18.71, 28.69, 28.07 },
[97] = { 44, 11.98, 18.71, 53.39, 42.29 },
[99] = { 33, 7.98, 5.24, 37.43, 5.61 },
[100] = { 33, 7.49, 8.23, 34.18, 13.1 },
[101] = { 33, 7.98, 8.23, 45.41, 9.36 },
[102] = { 33, 7.98, 8.23, 25.95, 12.35 },
[103] = { 33, 6.49, 9.73, 46.66, 42.29 },
[104] = { 33, 12.48, 14.97, 24.2, 27.69 },
[105] = { 33, 7.98, 11.98, 49.4, 29.94 },
[107] = { 40, 9.98, 11.23, 55.39, 32.56 },
[108] = { 40, 8.98, 15.72, 55.89, 49.78 },
[109] = { 40, 8.98, 12.72, 50.9, 21.33 },
[111] = { 40, 8.48, 11.23, 44.66, 24.33 },
[113] = { 40, 11.98, 14.22, 31.44, 41.54 },
[115] = { 40, 11.98, 13.47, 31.94, 86.08 },
[116] = { 8, 16.47, 14.97, 13.22, 35.93 },
[117] = { 33, 6.49, 6.74, 31.69, 27.32 },
[118] = { 11, 9.98, 9.73, 35.93, 49.03 },
[121] = { 10, 9.48, 11.23, 80.09, 69.99 },
[122] = { 33, 4.49, 5.99, 21.21, 14.97 },
[123] = { 33, 4.99, 5.24, 29.44, 19.84 },
[125] = { 33, 4.99, 5.99, 34.43, 20.96 },
[127] = { 33, 5.49, 6.74, 41.67, 31.06 },
[128] = { 33, 4.49, 11.98, 40.67, 41.17 },
[129] = { 33, 3.99, 5.24, 36.93, 29.57 },
[131] = { 1, 10.98, -18.71, 49.9, 67.74 },
[132] = { 1, 14.97, 17.22, 26.45, 75.22 },
[133] = { 1, 7.49, 11.98, 24.2, 38.17 },
[134] = { 1, 9.48, 13.47, 68.61, 57.63 },
[135] = { 1, 4.49, 8.98, 28.19, 51.65 },
[136] = { 1, 5.99, 11.23, 40.92, 58.01 },
[137] = { 1, 4.99, 9.73, 30.44, 46.03 },
[138] = { 1, 5.99, 11.98, 57.39, 42.66 },
[142] = { 38, 13.97, 17.22, 69.36, 65.49 },
[143] = { 38, 17.96, 18.71, 70.86, 25.07 },
[144] = { 38, 9.98, 12.72, 34.93, 46.03 },
[146] = { 38, 19.96, 11.98, 47.9, 12.72 },
[147] = { 38, 8.48, 11.98, 83.08, 62.87 },
[149] = { 38, 10.98, 16.47, 34.43, 21.71 },
[150] = { 11, 6.49, 10.48, 10.23, 56.89 },
[152] = { 85, 13.47, 25.45, 83.58, 69.61 },
[154] = { 85, 13.17, 18.26, 34.43, 63.92 },
[156] = { 85, 15.27, 12.13, 35.98, 49.48 },
[157] = { 85, 13.77, 17.96, 45.21, 36.38 },
[159] = { 85, 7.49, 14.67, 59.63, 55.24 },
[160] = { 85, 16.67, 22.6, 84.78, 32.86 },
[162] = { 85, 10.18, 34.88, 68.26, 42.14 },
[164] = { 85, 9.58, 21.86, 57.78, 38.17 },
[165] = { 85, 13.07, 23.05, 72.8, 61.68 },
[166] = { 85, 7.29, 10.63, 53.74, 59.96 },
[167] = { 85, 14.47, 15.42, 77.89, 51.42 },
[172] = { 130, 18.96, 20.21, 71.86, 28.82 },
[186] = { 141, 11.48, 11.98, 55.14, 56.89 },
[188] = { 141, 12.48, 18.71, 59.63, 40.04 },
[190] = { 28, 21.46, 19.46, 47.16, 17.96 },
[192] = { 28, 9.98, 14.22, 47.41, 36.3 },
[193] = { 28, 11.48, 12.72, 43.66, 69.24 },
[197] = { 28, 9.48, 13.47, 51.65, 79.34 },
[198] = { 28, 7.49, 12.72, 65.12, 42.29 },
[199] = { 28, 7.98, 9.73, 37.43, 55.76 },
[200] = { 28, 8.98, 11.23, 46.41, 52.77 },
[201] = { 28, 9.98, 11.23, 62.87, 57.26 },
[202] = { 28, 6.99, 9.73, 53.39, 65.49 },
[204] = { 130, 7.98, 8.23, 44.91, 73.73 },
[205] = { 11, 9.98, 9.73, 49.4, 16.84 },
[211] = { 1, 6.99, 18.71, 34.93, 38.55 },
[212] = { 1, 7.98, 14.22, 76.85, 54.27 },
[213] = { 130, 6.49, 11.23, 56.14, 48.28 },
[219] = { 40, 11.48, 16.47, 38.17, 52.4 },
[220] = { 215, 21.96, 21.71, 44.91, 82.71 },
[222] = { 215, 11.98, 14.97, 47.41, 59.13 },
[224] = { 215, 7.98, 8.98, 54.39, 47.16 },
[225] = { 215, 11.48, 17.96, 61.13, 20.21 },
[226] = { 130, 9.98, 10.48, 37.43, 15.72 },
[228] = { 130, 8.48, 9.73, 44.66, 40.79 },
[229] = { 130, 7.98, 8.98, 46.91, 53.89 },
[230] = { 130, 8.98, 13.47, 46.41, 86.08 },
[231] = { 130, 7.98, 9.73, 58.88, 72.23 },
[233] = { 130, 10.98, 14.97, 63.37, 62.87 },
[236] = { 130, 10.48, 9.73, 44.16, 66.24 },
[237] = { 130, 8.98, 11.23, 55.39, 36.3 },
[238] = { 130, 6.49, 5.99, 56.64, 10.48 },
[240] = { 130, 6.99, 8.98, 46.41, 21.71 },
[241] = { 10, 12.48, 17.22, 64.12, 72.98 },
[242] = { 10, 11.48, 32.19, 62.62, 40.79 },
[245] = { 10, 9.48, 14.97, 49.15, 73.35 },
[246] = { 51, 29.44, 20.96, 46.16, 49.4 },
[247] = { 51, 17.96, 14.22, 64.87, 58.76 },
[249] = { 46, 13.97, 21.71, 81.34, 41.54 },
[250] = { 46, 20.46, 25.45, 65.12, 34.43 },
[252] = { 46, 13.47, 22.46, 43.66, 35.18 },
[253] = { 46, 17.47, 15.72, 50.15, 56.51 },
[254] = { 46, 15.97, 20.21, 29.94, 31.81 },
[255] = { 46, 11.48, 19.46, 15.72, 32.19 },
[259] = { 141, 16.47, 14.97, 57.63, 71.11 },
[260] = { 141, 10.48, 11.98, 65.62, 57.63 },
[261] = { 141, 7.98, 10.48, 43.91, 74.85 },
[264] = { 141, 5.99, 21.71, 37.92, 36.3 },
[266] = { 141, 10.98, 21.71, 46.91, 35.55 },
[271] = { 267, 10.48, 17.96, 49.65, 55.39 },
[272] = { 267, 13.47, 21.71, 61.13, 25.82 },
[275] = { 267, 12.97, 15.72, 76.85, 40.79 },
[278] = { 36, 8.98, 12.72, 20.46, 84.96 },
[279] = { 36, 15.47, 23.95, 15.22, 65.87 },
[280] = { 36, 12.97, 14.22, 63.37, 45.28 },
[281] = { 36, 13.97, 18.71, 39.92, 53.52 },
[282] = { 36, 9.98, 19.46, 48.4, 41.92 },
[284] = { 36, 12.97, 18.71, 56.39, 27.32 },
[285] = { 267, 15.97, 18.71, 15.47, 46.03 },
[286] = { 267, 19.96, 29.94, 34.93, 44.91 },
[288] = { 267, 9.48, 11.23, 26.2, 57.26 },
[289] = { 267, 11.98, 23.95, 63.87, 55.39 },
[290] = { 267, 9.48, 21.71, 72.11, 71.48 },
[294] = { 267, 13.97, 17.22, 61.88, 74.48 },
[295] = { 267, 23.45, 11.98, 35.68, 68.11 },
[297] = { 33, 7.98, 11.98, 38.92, 82.34 },
[300] = { 8, 13.97, 83.08, 88.32, 52.77 },
[307] = { 47, 7.98, 32.93, 79.34, 68.86 },
[309] = { 11, 12.48, 17.22, 44.66, 31.06 },
[310] = { 33, 5.99, 8.23, 40.92, 50.52 },
[311] = { 33, 4.49, 5.99, 39.67, 59.13 },
[313] = { 45, 8.48, 17.96, 31.69, 31.44 },
[314] = { 45, 7.98, 11.23, 61.38, 56.51 },
[315] = { 45, 9.48, 10.48, 55.14, 39.67 },
[316] = { 45, 13.47, 20.21, 54.14, 73.73 },
[317] = { 45, 14.47, 11.23, 66.62, 66.99 },
[320] = { 45, 7.98, 16.47, 45.91, 47.16 },
[321] = { 45, 12.48, 16.47, 75.1, 36.68 },
[324] = { 45, 17.47, 22.46, 22.7, 62.13 },
[327] = { 45, 18.96, 17.22, 30.94, 79.72 },
[333] = { 45, 8.48, 15.72, 63.62, 34.06 },
[334] = { 45, 10.48, 17.96, 22.21, 25.45 },
[335] = { 45, 10.48, 14.22, 36.68, 61.75 },
[336] = { 45, 8.48, 9.73, 52.64, 52.77 },
[337] = { 3, 11.48, 15.72, 15.72, 61.75 },
[338] = { 3, 13.97, 15.72, 42.91, 33.31 },
[339] = { 3, 19.96, 34.43, 78.34, 50.9 },
[340] = { 3, 15.47, 15.72, 8.73, 47.53 },
[341] = { 3, 12.48, 14.97, 66.12, 22.46 },
[342] = { 3, 13.97, 25.45, 61.38, 72.6 },
[344] = { 3, 18.46, 20.96, 13.22, 81.59 },
[345] = { 3, 14.47, 22.46, 47.16, 74.1 },
[346] = { 3, 13.47, 15.72, 56.64, 36.3 },
[348] = { 47, 12.48, 15.72, 14.72, 50.52 },
[350] = { 47, 11.98, 18.71, 31.44, 43.04 },
[351] = { 47, 8.48, 11.23, 59.13, 43.04 },
[353] = { 47, 10.48, 14.22, 34.68, 71.48 },
[354] = { 47, 14.97, 31.44, 62.38, 69.61 },
[355] = { 47, 9.48, 11.23, 48.65, 66.24 },
[356] = { 47, 16.47, 23.95, 64.12, 25.45 },
[359] = { 17, 5.49, 8.98, 47.65, 83.83 },
[360] = { 215, 6.49, 16.47, 61.63, 50.15 },
[362] = { 14, 9.48, 11.98, 54.64, 42.66 },
[363] = { 14, 9.98, 15.72, 44.41, 66.99 },
[366] = { 14, 5.99, 8.98, 48.9, 79.34 },
[367] = { 14, 7.49, 11.23, 56.14, 72.98 },
[368] = { 14, 10.98, 28.44, 65.37, 84.58 },
[369] = { 14, 10.48, 17.22, 40.67, 24.33 },
[370] = { 14, 9.98, 13.47, 52.4, 24.7 },
[372] = { 14, 8.48, 14.97, 57.63, 56.14 },
[378] = { 17, 6.49, 8.23, 45.66, 61 },
[379] = { 17, 4.49, 8.23, 61.13, 21.33 },
[380] = { 17, 6.49, 7.49, 51.65, 28.44 },
[381] = { 17, 4.99, 10.48, 61.88, 6.74 },
[382] = { 17, 9.98, 8.98, 55.39, 7.49 },
[383] = { 17, 9.48, 11.23, 39.67, 14.6 },
[384] = { 17, 6.99, 8.23, 48.4, 18.34 },
[385] = { 17, 7.49, 8.98, 61.13, 55.39 },
[386] = { 17, 6.99, 9.73, 45.41, 23.58 },
[387] = { 17, 6.49, 10.48, 46.66, 38.92 },
[388] = { 17, 5.49, 8.98, 55.64, 42.66 },
[390] = { 17, 7.49, 8.23, 46.16, 70.73 },
[391] = { 17, 4.49, 9.73, 63.62, 45.28 },
[392] = { 17, 5.49, 7.49, 62.62, 37.43 },
[396] = { 215, 7.49, 8.98, 54.14, 65.87 },
[397] = { 215, 6.49, 10.48, 44.66, 45.66 },
[398] = { 215, 12.97, 9.73, 39.42, 12.35 },
[404] = { 215, 9.48, 14.97, 33.68, 46.41 },
[413] = { 331, 8.48, 24.7, 29.19, 25.82 },
[414] = { 331, 16.97, 26.2, 12.97, 22.08 },
[415] = { 331, 12.48, 14.97, 37.18, 51.65 },
[416] = { 331, 9.48, 14.22, 22.21, 52.77 },
[417] = { 331, 6.49, 10.48, 26.7, 63.62 },
[418] = { 331, 7.98, 13.47, 33.43, 67.37 },
[419] = { 331, 12.48, 14.22, 56.14, 37.05 },
[421] = { 331, 14.97, 17.96, 49.4, 73.35 },
[422] = { 331, 11.48, 12.72, 69.11, 81.96 },
[424] = { 331, 6.99, 11.23, 46.41, 46.78 },
[426] = { 331, 9.98, 11.98, 60.88, 51.65 },
[428] = { 331, 11.48, 16.47, 71.61, 59.13 },
[430] = { 331, 13.97, 9.73, 81.84, 48.28 },
[434] = { 331, 16.47, 15.72, 83.08, 77.47 },
[437] = { 331, 11.48, 11.98, 91.07, 59.13 },
[438] = { 331, 8.98, 12.72, 93.81, 36.3 },
[439] = { 400, 24.95, 48.65, 78.34, 72.98 },
[441] = { 331, 5.99, 11.98, 20.46, 40.42 },
[442] = { 148, 7.49, 11.23, 39.17, 43.79 },
[443] = { 148, 6.49, 14.97, 58.63, 16.47 },
[444] = { 148, 8.98, 8.98, 56.39, 26.2 },
[446] = { 148, 6.99, 8.23, 44.91, 36.3 },
[447] = { 148, 6.49, 10.48, 42.66, 57.63 },
[448] = { 148, 8.98, 11.98, 42.91, 77.1 },
[449] = { 148, 8.98, 11.23, 41.42, 88.7 },
[450] = { 148, 5.99, 8.98, 34.93, 85.33 },
[456] = { 148, 10.48, 9.73, 53.64, 33.31 },
[459] = { 85, 7.98, 31.59, 77.84, 32.86 },
[460] = { 406, 10.98, 13.47, 46.41, 58.38 },
[461] = { 406, 25.95, 29.19, 69.86, 50.52 },
[464] = { 406, 13.47, 15.72, 49.15, 40.79 },
[465] = { 406, 14.97, 21.71, 31.94, 66.99 },
[467] = { 406, 20.46, 17.96, 37.18, 13.47 },
[477] = { 33, 4.49, 5.99, 35.68, 51.65 },
[478] = { 141, 5.99, 13.47, 40.42, 63.62 },
[480] = { 400, 19.46, 23.2, 16.22, 17.59 },
[481] = { 400, 15.97, 14.97, 47.9, 41.17 },
[482] = { 400, 12.97, 23.2, 13.47, 34.06 },
[483] = { 400, 16.47, 24.7, 30.69, 47.53 },
[484] = { 400, 12.48, 15.72, 46.16, 52.77 },
[485] = { 400, 9.48, 10.48, 32.19, 22.46 },
[492] = { 10, 11.48, 20.96, 20.21, 41.92 },
[496] = { 15, 12.97, 24.7, 36.93, 22.83 },
[502] = { 15, 18.96, 27.69, 53.89, 22.83 },
[509] = { 15, 10.98, 19.46, 36.93, 61.38 },
[511] = { 15, 17.47, 20.21, 52.64, 75.22 },
[513] = { 15, 13.47, 18.71, 68.11, 51.27 },
[536] = { 10, 11.48, 14.22, 21.71, 69.24 },
[537] = { 490, 17.96, 24.7, 50.4, 49.78 },
[538] = { 490, 30.44, 33.68, 50.65, 20.58 },
[539] = { 490, 14.47, 23.95, 37.18, 74.1 },
[540] = { 490, 15.47, 19.46, 51.65, 80.09 },
[543] = { 490, 17.47, 29.94, 30.19, 50.15 },
[556] = { 38, 19.46, 50.15, 50.15, 42.29 },
[596] = { 405, 14.47, 18.71, 50.65, 58.01 },
[599] = { 405, 10.98, 11.23, 54.39, 28.07 },
[602] = { 405, 15.97, 17.96, 53.39, 79.34 },
[603] = { 405, 12.97, 17.22, 75.85, 22.83 },
[604] = { 405, 12.48, 18.71, 71.11, 72.98 },
[606] = { 405, 12.97, 16.47, 38.42, 89.07 },
[607] = { 405, 16.47, 27.69, 33.18, 52.02 },
[608] = { 405, 9.98, 11.23, 64.87, 9.36 },
[609] = { 405, 9.98, 14.97, 74.35, 48.65 },
[656] = { 493, 28.44, 38.92, 50.65, 54.64 },
[657] = { 8, 12.48, 14.97, 27.69, 35.93 },
[702] = { 141, 1.5, 2.25, 55.64, 90.94 },
[736] = { 141, 8.98, 23.2, 46.41, 57.26 },
[797] = { 12, 15.47, 17.96, 53.64, 83.08 },
[798] = { 12, 12.48, 9.73, 85.08, 80.46 },
[799] = { 10, 84.33, 17.22, 54.64, 21.33 },
[800] = { 1, 5.99, 10.48, 34.93, 68.11 },
[801] = { 1, 7.98, 10.48, 35.93, 53.14 },
[802] = { 1, 5.99, 14.22, 40.92, 40.04 },
[803] = { 1, 5.99, 6.74, 63.37, 50.52 },
[804] = { 1, 8.98, 8.23, 59.88, 57.26 },
[806] = { 1, 5.99, 10.48, 85.33, 50.9 },
[808] = { 1, 5.99, 14.22, 82.83, 37.8 },
[809] = { 1, 10.98, 13.47, 49.4, 37.43 },
[810] = { 85, 10.28, 10.63, 47.95, 51.27 },
[811] = { 85, 14.57, 14.07, 47.31, 66.32 },
[812] = { 85, 19.26, 25.15, 87.18, 48.5 },
[813] = { 28, 11.98, 11.23, 26.45, 58.01 },
[816] = { 14, 12.48, 17.96, 41.17, 43.41 },
[817] = { 14, 7.49, 7.49, 53.64, 11.98 },
[818] = { 215, 6.99, 17.96, 36.43, 62.13 },
[819] = { 215, 10.48, 10.48, 50.15, 11.98 },
[820] = { 215, 8.98, 23.95, 52.89, 29.94 },
[821] = { 215, 10.98, 12.72, 62.87, 67.74 },
[838] = { 38, 12.97, 17.22, 22.46, 15.34 },
[856] = { 10, 21.46, 37.43, 47.65, 40.42 },
[880] = { 45, 11.98, 20.96, 45.91, 83.08 },
[896] = { 267, 12.48, 15.72, 17.22, 79.72 },
[916] = { 40, 9.48, 12.72, 56.64, 17.59 },
[917] = { 40, 9.98, 11.98, 62.38, 61.38 },
[918] = { 40, 10.98, 12.72, 44.91, 36.3 },
[920] = { 40, 17.96, 11.23, 46.91, 78.97 },
[921] = { 40, 10.48, 13.47, 31.19, 69.61 },
[922] = { 40, 14.97, 10.48, 63.37, 72.6 },
[923] = { 38, 14.97, 26.95, 34.93, 77.84 },
[924] = { 38, 10.98, 27.69, 20.46, 73.73 },
[927] = { 130, 6.49, 11.23, 54.14, 22.83 },
[928] = { 130, 7.98, 8.98, 40.42, 28.44 },
[936] = { 38, 11.48, 19.46, 45.16, 68.86 },
[976] = { 440, 10.48, 12.72, 51.15, 27.32 },
[977] = { 440, 7.98, 9.73, 66.87, 21.33 },
[978] = { 440, 14.97, 17.96, 37.43, 12.72 },
[979] = { 440, 6.99, 10.48, 39.92, 28.44 },
[980] = { 440, 12.48, 17.22, 30.69, 62.5 },
[981] = { 440, 10.98, 17.22, 54.39, 70.73 },
[982] = { 440, 7.49, 14.97, 34.68, 45.66 },
[983] = { 440, 7.98, 11.98, 41.92, 56.14 },
[984] = { 440, 6.49, 7.49, 46.66, 65.12 },
[985] = { 440, 9.48, 12.72, 59.63, 37.8 },
[986] = { 440, 5.99, 9.73, 66.87, 33.31 },
[987] = { 440, 13.97, 14.22, 54.89, 91.69 },
[990] = { 440, 7.98, 14.22, 36.93, 80.46 },
[992] = { 440, 7.49, 9.73, 40.17, 71.48 },
[996] = { 44, 19.46, 26.2, 40.17, 16.84 },
[997] = { 44, 39.42, 14.97, 73.1, 77.84 },
[1000] = { 44, 16.47, 26.95, 78.09, 42.66 },
[1001] = { 44, 32.44, 13.47, 38.17, 73.35 },
[1002] = { 44, 17.96, 26.2, 18.46, 69.24 },
[1016] = { 11, 11.48, 20.21, 60.63, 32.56 },
[1017] = { 11, 11.48, 13.47, 72.11, 38.17 },
[1018] = { 11, 14.97, 12.72, 19.96, 49.03 },
[1020] = { 11, 10.98, 17.96, 61.88, 54.64 },
[1021] = { 11, 11.98, 16.47, 56.39, 68.86 },
[1022] = { 11, 14.47, 11.23, 19.21, 36.3 },
[1023] = { 11, 13.47, 20.21, 34.18, 24.33 },
[1024] = { 11, 18.46, 14.97, 23.2, 28.44 },
[1025] = { 11, 10.48, 22.46, 53.64, 36.68 },
[1036] = { 11, 14.97, 15.72, 47.41, 48.28 },
[1038] = { 11, 21.96, 43.41, 81.84, 63.62 },
[1056] = { 267, 14.97, 10.48, 50.9, 33.68 },
[1076] = { 406, 20.96, 42.66, 60.38, 68.49 },
[1097] = { 10, 8.88, 31.74, 10.03, 44.46 },
[1098] = { 10, 9.98, 8.98, 74.35, 33.68 },
[1099] = { 357, 7.98, 9.73, 75.35, 42.29 },
[1100] = { 357, 5.99, 14.97, 67.86, 40.42 },
[1101] = { 357, 16.97, 20.21, 72.85, 61 },
[1103] = { 357, 9.98, 9.73, 77.35, 31.06 },
[1105] = { 357, 6.99, 8.98, 54.39, 56.89 },
[1106] = { 357, 6.49, 17.96, 53.64, 71.11 },
[1108] = { 357, 6.99, 41.92, 46.41, 62.13 },
[1111] = { 357, 4.99, 8.23, 51.4, 10.1 },
[1113] = { 357, 5.49, 5.24, 54.64, 16.84 },
[1114] = { 357, 7.49, 8.23, 39.67, 10.85 },
[1119] = { 357, 15.47, 20.96, 47.16, 25.45 },
[1120] = { 357, 13.47, 16.47, 30.19, 50.15 },
[1121] = { 357, 14.47, 35.18, 29.69, 78.97 },
[1137] = { 357, 12.48, 14.22, 86.08, 43.04 },
[1216] = { 16, 10.48, 15.72, 37.18, 33.31 },
[1219] = { 16, 16.97, 8.98, 57.88, 17.96 },
[1220] = { 16, 14.97, 9.73, 60.88, 28.82 },
[1221] = { 16, 12.48, 15.72, 36.68, 54.27 },
[1225] = { 16, 7.98, 11.98, 46.91, 28.44 },
[1226] = { 16, 10.48, 14.97, 76.6, 41.17 },
[1227] = { 16, 16.97, 32.93, 58.88, 54.64 },
[1228] = { 16, 5.49, 18.71, 45.66, 46.03 },
[1229] = { 16, 6.99, 12.72, 88.32, 30.31 },
[1230] = { 16, 47.9, 11.98, 63.87, 11.23 },
[1231] = { 16, 28.94, 17.96, 58.38, 71.86 },
[1232] = { 16, 9.48, 9.73, 70.61, 84.96 },
[1233] = { 16, 14.97, 15.72, 29.44, 71.48 },
[1234] = { 16, 11.48, 13.47, 43.16, 80.09 },
[1235] = { 16, 9.98, 13.47, 17.47, 74.85 },
[1236] = { 16, 8.48, 9.73, 20.71, 60.25 },
[1237] = { 16, 9.98, 8.23, 21.46, 50.52 },
[1256] = { 16, 28.44, 9.73, 58.13, 91.69 },
[1316] = { 17, 8.98, 9.73, 48.9, 91.69 },
[1336] = { 440, 8.48, 17.96, 71.61, 47.16 },
[1357] = { 36, 8.48, 11.23, 50.15, 58.76 },
[1437] = { 4, 9.48, 8.98, 43.16, 14.22 },
[1438] = { 4, 9.98, 11.23, 64.37, 18.34 },
[1439] = { 4, 10.98, 11.98, 50.4, 44.16 },
[1440] = { 4, 10.98, 9.73, 62.87, 33.31 },
[1441] = { 4, 6.49, 9.73, 39.67, 31.81 },
[1442] = { 51, 23.95, 28.44, 27.94, 33.68 },
[1444] = { 51, 23.95, 30.69, 44.91, 84.21 },
[1457] = { 4, 7.98, 8.23, 55.89, 13.85 },
[1497] = { 0, 14.57, 21.71, 61.88, 71.63 },
[1519] = { 0, 25.45, 35.18, 24.2, 35.55 },
[1637] = { 0, 22.95, 7.49, 46.91, 5.24 },
[1638] = { 0, 18.46, 23.95, 38.67, 27.69 },
[1657] = { 0, 14.97, 28.44, 27.45, 57.63 },
[1677] = { 36, 7.49, 14.22, 30.69, 85.7 },
[1678] = { 36, 8.98, 13.47, 58.38, 68.86 },
[1679] = { 36, 9.48, 19.46, 48.15, 83.83 },
[1680] = { 36, 8.98, 17.22, 38.92, 90.19 },
[1681] = { 36, 12.48, 18.71, 28.19, 37.8 },
[1682] = { 36, 6.99, 13.47, 40.42, 17.22 },
[1683] = { 36, 10.48, 12.72, 39.67, 69.24 },
[1684] = { 36, 18.46, 34.43, 82.09, 66.62 },
[1697] = { 17, 5.99, 6.74, 55.89, 51.27 },
[1698] = { 17, 4.49, 10.48, 50.65, 58.38 },
[1699] = { 17, 5.99, 7.49, 56.89, 28.44 },
[1700] = { 17, 8.48, 14.22, 43.16, 48.28 },
[1701] = { 17, 6.49, 11.23, 41.67, 78.97 },
[1702] = { 17, 7.98, 10.48, 37.43, 28.44 },
[1703] = { 17, 7.98, 8.98, 47.9, 6.74 },
[1704] = { 17, 5.49, 6.74, 55.64, 19.09 },
[1717] = { 17, 7.98, 10.48, 40.92, 88.32 },
[1737] = { 33, 3.49, 10.48, 33.68, 64.37 },
[1738] = { 33, 5.49, 9.73, 25.7, 62.5 },
[1739] = { 33, 9.98, 9.73, 26.95, 53.52 },
[1740] = { 33, 4.49, 9.73, 44.16, 19.84 },
[1741] = { 33, 8.98, 11.98, 29.44, 44.16 },
[1761] = { 361, 8.48, 8.98, 49.15, 91.32 },
[1762] = { 361, 9.98, 11.23, 63.37, 10.85 },
[1763] = { 361, 12.97, 7.49, 36.43, 59.88 },
[1765] = { 361, 12.97, 8.98, 42.42, 48.65 },
[1766] = { 361, 10.48, 17.96, 42.17, 35.18 },
[1767] = { 361, 9.48, 11.23, 50.15, 23.58 },
[1777] = { 8, 9.98, 20.96, 14.97, 62.87 },
[1778] = { 8, 12.97, 40.42, 83.83, 46.41 },
[1780] = { 8, 18.96, 17.96, 24.45, 52.4 },
[1797] = { 8, 17.96, 21.71, 70.36, 78.97 },
[1798] = { 8, 23.95, 21.71, 43.91, 34.81 },
[1857] = { 45, 10.48, 21.71, 17.71, 37.8 },
[1858] = { 45, 5.49, 18.71, 37.67, 41.54 },
[1877] = { 3, 12.97, 17.96, 46.41, 56.89 },
[1878] = { 3, 16.97, 20.96, 30.44, 51.65 },
[1879] = { 3, 18.46, 24.7, 30.69, 75.22 },
[1882] = { 47, 7.49, 22.46, 22.7, 38.17 },
[1883] = { 47, 7.49, 10.48, 40.17, 58.38 },
[1884] = { 47, 11.48, 12.72, 49.65, 37.05 },
[1885] = { 47, 10.48, 14.22, 28.19, 61.75 },
[1886] = { 47, 9.48, 12.72, 50.15, 53.52 },
[1897] = { 3, 18.46, 17.96, 49.15, 16.47 },
[1898] = { 3, 12.48, 18.71, 62.62, 46.03 },
[1917] = { 47, 13.47, 15.72, 70.11, 50.52 },
[1937] = { 440, 5.99, 7.49, 59.88, 24.7 },
[1938] = { 440, 6.49, 8.98, 53.14, 45.66 },
[1939] = { 440, 9.48, 13.47, 45.66, 42.66 },
[1940] = { 440, 10.98, 9.73, 62.38, 59.51 },
[1942] = { 490, 16.47, 35.18, 69.61, 63.25 },
[1943] = { 490, 19.96, 26.2, 72.36, 34.81 },
[1957] = { 51, 14.97, 22.46, 22.95, 73.35 },
[1958] = { 51, 17.96, 20.96, 69.36, 76.35 },
[1959] = { 51, 28.94, 40.42, 67.37, 29.19 },
[1998] = { 361, 7.49, 9.73, 62.13, 24.33 },
[2079] = { 15, 12.48, 18.71, 75.6, 17.59 },
[2097] = { 400, 8.98, 15.72, 34.93, 36.3 },
[2198] = { 405, 8.98, 13.47, 80.34, 79.34 },
[2241] = { 618, 10.98, 8.98, 49.4, 12.72 },
[2242] = { 618, 9.48, 10.48, 64.62, 17.22 },
[2243] = { 618, 7.49, 5.99, 40.17, 43.41 },
[2244] = { 618, 6.99, 7.49, 68.36, 36.68 },
[2245] = { 618, 5.99, 8.23, 58.38, 58.01 },
[2246] = { 618, 11.98, 7.49, 33.43, 36.68 },
[2247] = { 618, 6.49, 11.98, 68.11, 45.66 },
[2249] = { 618, 8.98, 11.23, 61.88, 68.49 },
[2250] = { 618, 6.99, 9.73, 65.37, 61.75 },
[2251] = { 618, 9.48, 11.98, 53.14, 43.41 },
[2253] = { 618, 10.48, 8.98, 49.65, 28.44 },
[2255] = { 618, 8.98, 9.73, 60.88, 37.8 },
[2256] = { 618, 17.96, 16.47, 57.88, 83.08 },
[2258] = { 139, 9.98, 14.22, 40.42, 52.02 },
[2260] = { 139, 10.48, 10.48, 26.7, 73.35 },
[2261] = { 139, 7.98, 13.47, 28.44, 84.58 },
[2262] = { 139, 11.98, 11.98, 39.92, 89.82 },
[2263] = { 139, 8.48, 11.98, 38.67, 75.6 },
[2264] = { 139, 9.48, 11.98, 59.63, 70.36 },
[2266] = { 139, 16.97, 14.97, 84.83, 83.08 },
[2268] = { 139, 7.49, 11.98, 82.09, 58.38 },
[2270] = { 139, 11.98, 15.72, 84.33, 41.54 },
[2271] = { 139, 6.99, 12.72, 67.86, 49.78 },
[2272] = { 139, 8.48, 12.72, 70.61, 31.81 },
[2273] = { 139, 12.48, 11.23, 73.1, 14.6 },
[2275] = { 139, 12.97, 13.47, 57.88, 28.44 },
[2276] = { 139, 8.98, 8.98, 52.89, 16.47 },
[2277] = { 139, 25.95, 19.46, 36.43, 33.68 },
[2279] = { 139, 13.47, 20.21, 31.19, 15.34 },
[2297] = { 28, 25.95, 24.7, 68.36, 73.73 },
[2298] = { 28, 9.98, 14.22, 68.86, 75.22 },
[2300] = { 440, 9.48, 10.48, 64.12, 49.4 },
[2301] = { 331, 6.99, 8.98, 36.93, 36.68 },
[2302] = { 15, 24.95, 25.45, 44.41, 48.65 },
[2303] = { 400, 14.97, 24.7, 62.38, 55.01 },
[2404] = { 405, 9.98, 9.73, 54.39, 12.35 },
[2405] = { 405, 12.97, 20.96, 40.92, 30.69 },
[2406] = { 405, 6.99, 8.98, 28.94, 8.23 },
[2407] = { 405, 10.98, 11.98, 64.37, 40.42 },
[2408] = { 405, 11.98, 14.97, 25.45, 70.36 },
[2417] = { 46, 14.47, 21.71, 75.1, 68.49 },
[2418] = { 46, 16.47, 24.7, 88.57, 64.75 },
[2420] = { 46, 21.46, 37.43, 88.57, 35.93 },
[2421] = { 46, 30.94, 32.19, 27.94, 65.49 },
[2478] = { 361, 7.49, 8.23, 57.63, 87.95 },
[2479] = { 361, 9.48, 8.23, 51.65, 79.72 },
[2480] = { 361, 8.48, 12.72, 41.17, 82.71 },
[2481] = { 361, 8.98, 11.23, 37.92, 69.24 },
[2497] = { 16, 16.47, 14.97, 76.6, 19.46 },
[2517] = { 4, 8.48, 11.98, 48.65, 29.94 },
[2522] = { 357, 9.98, 21.71, 61.38, 66.99 },
[2537] = { 406, 11.48, 7.49, 75.6, 86.83 },
[2538] = { 406, 5.99, 5.24, 77.35, 92.44 },
[2539] = { 406, 6.99, 8.23, 72.85, 94.69 },
[2540] = { 406, 9.98, 11.98, 64.87, 92.81 },
[2541] = { 406, 8.98, 10.48, 54.39, 74.85 },
[2561] = { 41, 15.47, 32.19, 58.63, 64.75 },
[2562] = { 41, 18.46, 23.95, 42.66, 68.86 },
[2577] = { 357, 12.97, 13.47, 58.88, 41.92 },
[2618] = { 361, 8.48, 10.48, 41.17, 17.96 },
[2619] = { 139, 10.48, 44.16, 7.73, 60.25 },
[2620] = { 28, 10.48, 41.17, 67.61, 38.55 },
[2621] = { 139, 12.48, 13.47, 62.62, 80.09 },
[2622] = { 139, 9.48, 18.71, 74.1, 62.5 },
[2623] = { 139, 10.48, 11.98, 52.64, 66.62 },
[2624] = { 139, 8.98, 12.72, 51.9, 49.78 },
[2627] = { 139, 12.48, 10.48, 17.71, 32.19 },
[2697] = { 41, 27.45, 34.43, 45.16, 35.93 },
[2737] = { 1377, 13.97, 5.99, 28.94, 95.06 },
[2738] = { 1377, 7.98, 11.98, 62.38, 53.14 },
[2740] = { 1377, 16.47, 20.21, 23.2, 17.59 },
[2742] = { 1377, 12.97, 18.71, 44.91, 24.33 },
[2743] = { 1377, 16.47, 23.95, 24.7, 58.38 },
[2744] = { 1377, 16.47, 33.68, 57.14, 80.46 },
[2959] = { 2597, 28.94, 26.2, 41.92, 20.58 },
[2977] = { 2597, 21.96, 29.94, 48.9, 49.4 },
[2978] = { 2597, 16.97, 29.19, 51.9, 78.22 },
[3425] = { 1377, 6.49, 10.48, 50.65, 37.43 },
}
+353
View File
@@ -0,0 +1,353 @@
-- Questie-Octo canonical database facade.
--
-- IMPORTANT:
-- pfDB is not exposed beyond Database/. The rest of Questie-Octo talks only
-- through this API.
QuestieOcto.DatabaseAPI = QuestieOcto.DatabaseAPI or {}
local DB = QuestieOcto.DatabaseAPI
DB.ready = false
DB.questIDs = DB.questIDs or nil
DB.questTitleCache = DB.questTitleCache or {}
DB.questSearchTitleCache = DB.questSearchTitleCache or {}
local function RawQuest(id)
return QuestieOcto.Database:GetRawQuest(id)
end
local function QuestLocale(id)
return QuestieOcto.Database:GetQuestLocale(id)
end
function DB:IsReady()
return self.ready and true or false
end
function DB:GetQuestCount()
-- questIDs is built incrementally before DATABASE_API_READY is published, so
-- counting it is O(1) and avoids walking the entire pfDB quest table again.
return self.questIDs and table.getn(self.questIDs) or 0
end
function DB:BuildQuestIDCache()
local result={}
if not pfDB or not pfDB.quests or not pfDB.quests.data then
self.questIDs=result
return result
end
for id in pairs(pfDB.quests.data) do table.insert(result,id) end
self.questIDs=result
return result
end
function DB:GetQuestIDs()
if not self.ready then return {} end
return self.questIDs or self:BuildQuestIDCache()
end
function DB:GetQuestRaw(id)
if not self.ready then return nil end
return RawQuest(id)
end
function DB:GetQuestTitle(id)
if not self.ready then return nil end
local cached=self.questTitleCache and self.questTitleCache[id]
if cached then return cached end
local loc=QuestLocale(id)
local title=nil
if type(loc)=="table" then title=loc["T"] or loc[1]
elseif type(loc)=="string" then title=loc end
title=title or ("Quest "..tostring(id))
self.questTitleCache[id]=title
self.questSearchTitleCache[id]=string.lower(title)
return title
end
function DB:GetQuestSearchTitle(id)
if not self.ready then return nil end
local cached=self.questSearchTitleCache and self.questSearchTitleCache[id]
if cached then return cached end
local title=self:GetQuestTitle(id)
return title and string.lower(title) or nil
end
function DB:GetQuestSearchIndex()
if not self.ready then return {} end
return self.questSearchTitleCache or {}
end
function DB:GetQuestRewards(id)
if not self.ready then return nil end
return QuestieOcto.QuestRewardsData and QuestieOcto.QuestRewardsData[tonumber(id)] or nil
end
-- Questie Journey presents the quest's authored objective/description text
-- rather than reconstructing every objective from database entity rows.
-- pfDB's localized quest table stores that objective sentence in field "O".
function DB:GetQuestObjectiveText(id)
if not self.ready then return nil end
local loc=QuestLocale(id)
if type(loc)=="table" then
local text=loc["O"]
if type(text)=="string" and text~="" then return text end
end
return nil
end
-- pfDB's localized quest table stores the main quest-offer description in
-- field "D". Keep it separate from "O" (the objective summary), matching the
-- way the Vanilla quest dialog presents description and objectives.
function DB:GetQuestDescriptionText(id)
if not self.ready then return nil end
local loc=QuestLocale(id)
if type(loc)=="table" then
local text=loc["D"]
if type(text)=="string" and text~="" then return text end
end
return nil
end
function DB:GetItemRaw(id)
if not self.ready then return nil end
return QuestieOcto.Database:GetRawItem(id)
end
function DB:GetCreatureRaw(id)
if not self.ready then return nil end
return QuestieOcto.Database:GetRawUnit(id)
end
function DB:GetObjectRaw(id)
if not self.ready then return nil end
return QuestieOcto.Database:GetRawObject(id)
end
function DB:GetItemName(id)
if pfDB and pfDB.items and pfDB.items.loc then
local loc=pfDB.items.loc[id]
if type(loc)=="string" and loc~="" then return loc end
end
if C_Item and C_Item.GetItemNameByID then
local name=C_Item.GetItemNameByID(id)
if name then return name end
end
if GetItemInfo then
local name=GetItemInfo(id)
if name then return name end
end
return "Item "..tostring(id)
end
function DB:GetCreatureName(id)
if pfDB and pfDB.units and pfDB.units.loc then
local loc=pfDB.units.loc[id]
if type(loc)=="string" then return loc end
end
return "Creature "..tostring(id)
end
function DB:GetObjectName(id)
if pfDB and pfDB.objects and pfDB.objects.loc then
local loc=pfDB.objects.loc[id]
if type(loc)=="string" then return loc end
end
return "Object "..tostring(id)
end
function DB:GetZoneName(id)
if not self.ready then return nil end
id=tonumber(id)
if not id then return nil end
if pfDB and pfDB.zones and pfDB.zones.loc then
local loc=pfDB.zones.loc[id]
if type(loc)=="string" and loc~="" then return loc end
end
return nil
end
function DB:GetItemSources(id)
local raw=self:GetItemRaw(id)
if not raw then return nil end
return {
Creature=raw["U"],
GameObject=raw["O"],
Reference=raw["R"],
Vendor=raw["V"],
}
end
function DB:GetReferenceLootRaw(id)
if not self.ready then return nil end
if not pfDB or not pfDB.refloot or not pfDB.refloot.data then return nil end
return pfDB.refloot.data[id]
end
function DB:GetCreatureCoords(id)
local raw=self:GetCreatureRaw(id)
return raw and raw["coords"] or nil
end
function DB:GetObjectCoords(id)
local raw=self:GetObjectRaw(id)
return raw and raw["coords"] or nil
end
function DB:OnLegacyReady()
-- Build the quest-ID index incrementally as well. Iterating the entire quest
-- database in the DATABASE_READY callback creates an avoidable single-frame
-- spike on Vanilla clients. No consumer is released until the cache is done.
self.ready=false
self.questIDs={}
self.questTitleCache={}
self.questSearchTitleCache={}
local data=pfDB and pfDB.quests and pfDB.quests.data or {}
local cursor=nil
local function Slice()
local count=0
while count<200 do
local key=next(data,cursor)
if key==nil then
DB.ready=true
QuestieOcto:SendMessage("DATABASE_API_READY")
return
end
cursor=key
table.insert(DB.questIDs,key)
local loc=QuestLocale(key)
local title=nil
if type(loc)=="table" then title=loc["T"] or loc[1]
elseif type(loc)=="string" then title=loc end
title=title or ("Quest "..tostring(key))
DB.questTitleCache[key]=title
DB.questSearchTitleCache[key]=string.lower(title)
count=count+1
end
QuestieOcto.Scheduler:Enqueue(Slice,"database-quest-id-cache")
end
QuestieOcto.Scheduler:Enqueue(Slice,"database-quest-id-cache")
end
QuestieOcto:RegisterMessage("DATABASE_READY",DB,"OnLegacyReady")
function DB:GetQuestItemRequirementRaw(id)
if not self.ready then return nil end
if not pfDB or not pfDB["quests-itemreq"] or not pfDB["quests-itemreq"].data then return nil end
return pfDB["quests-itemreq"].data[id]
end
function DB:GetCreatureType(id)
local raw=self:GetCreatureRaw(id)
return raw and raw["type"] or nil
end
function DB:GetCreatureRank(id)
local raw=self:GetCreatureRaw(id)
return raw and (raw["rnk"] or raw["rank"]) or nil
end
function DB:GetCreatureFaction(id)
local raw=self:GetCreatureRaw(id)
return raw and (raw["fac"] or raw["faction"]) or nil
end
function DB:GetMapIDByName(name)
if not self.ready or not name then return nil end
if not pfDB or not pfDB.zones or not pfDB.zones.loc then return nil end
for id,zoneName in pairs(pfDB.zones.loc) do
if zoneName==name then return id end
end
-- The active localized table uses English through __index for missing direct
-- lookups. pairs() does not enumerate __index entries on Vanilla Lua, so
-- search the English fallback explicitly for untranslated Turtle zones.
if pfDB.zones.enUS and pfDB.zones.loc~=pfDB.zones.enUS then
for id,zoneName in pairs(pfDB.zones.enUS) do
if pfDB.zones.loc[id]==nil and zoneName==name then return id end
end
end
return nil
end
function DB:GetCreatureLevel(id)
local raw=self:GetCreatureRaw(id)
return raw and raw["lvl"] or nil
end
function DB:GetCreatureRespawnSeconds(id)
local coords=self:GetCreatureCoords(id)
if not coords then return nil end
local best=nil
for _,coord in pairs(coords) do
if type(coord)=="table" then
local seconds=tonumber(coord[4])
if seconds and seconds>0 then
if not best or seconds>best then best=seconds end
end
end
end
return best
end
function DB:GetCreatureMapIDs(id)
local result={}
local seen={}
local coords=self:GetCreatureCoords(id)
if coords then
for _,coord in pairs(coords) do
if type(coord)=="table" and tonumber(coord[3]) then
local mapID=tonumber(coord[3])
if not seen[mapID] then
seen[mapID]=true
table.insert(result,mapID)
end
end
end
end
table.sort(result)
return result
end
function DB:GetProfessionName(id)
if not self.ready then return nil end
id=tonumber(id)
if not id then return nil end
local loc=pfDB and pfDB.professions and (pfDB.professions.loc or pfDB.professions.enUS)
return loc and loc[id] or nil
end
function DB:GetTrackingMeta(metaKey)
if not self.ready or not metaKey or not pfDB then return nil end
local turtle=pfDB["meta-turtle"]
if turtle and turtle[metaKey] then return turtle[metaKey] end
return pfDB.meta and pfDB.meta[metaKey] or nil
end
function DB:GetMinimapSize(mapID)
mapID=tonumber(mapID)
if not self.ready or not mapID then return nil,nil end
if not pfDB or not pfDB["minimap"] then return nil,nil end
local size=pfDB["minimap"][mapID]
if type(size)~="table" then return nil,nil end
return tonumber(size[1]),tonumber(size[2])
end
+202
View File
@@ -0,0 +1,202 @@
QuestieOcto.Database = QuestieOcto.Database or {}
local D = QuestieOcto.Database
D.ready = false
D.running = false
D.jobs = {}
D.jobIndex = 1
D.stats = { merged=0, jobs=0 }
local DATASETS = {
"items", "quests", "quests-itemreq", "objects", "units",
"zones", "professions", "areatrigger", "refloot", "minimap", "meta"
}
local TEXT_DATASETS = {
"items", "quests", "objects", "units", "zones", "professions"
}
local FIELD_MERGE_DATASETS = { items=true, quests=true, objects=true, units=true }
local function AddMergeJob(target,patch,name,mode)
if target and patch then
table.insert(D.jobs,{ target=target, patch=patch, name=name, mode=mode or "replace" })
end
end
local function BuildMergeJobs()
D.jobs = {}
D.jobIndex = 1
D.stats = { merged=0, jobs=0 }
if not pfDB then return end
-- Locale-independent Turtle patches always merge into the core data first.
for _,name in pairs(DATASETS) do
local bucket=pfDB[name]
if bucket and bucket["data-turtle"] and bucket["data"] then
AddMergeJob(bucket["data"],bucket["data-turtle"],name..".data",FIELD_MERGE_DATASETS[name] and "fields" or "replace")
end
end
-- English is the only packaged presentation database.
for _,name in pairs(TEXT_DATASETS) do
local bucket=pfDB[name]
if bucket then
AddMergeJob(bucket["enUS"],bucket["enUS-turtle"],name..".enUS","locale")
end
end
if pfDB["minimap-turtle"] and pfDB["minimap"] then
AddMergeJob(pfDB["minimap"],pfDB["minimap-turtle"],"minimap")
end
if pfDB["meta-turtle"] and pfDB["meta"] then
AddMergeJob(pfDB["meta"],pfDB["meta-turtle"],"meta")
end
D.stats.jobs=table.getn(D.jobs)
end
local function StartJob(job)
if job.started then return end
-- Never materialize every patch key in one frame. Some Turtle datasets are
-- large enough that the old key-array construction caused a visible startup
-- hitch before the incremental merge even began.
job.started=true
job.cursor=nil
end
local function ProcessJob(job)
StartJob(job)
local count=0
while count<256 do
local key,value=next(job.patch,job.cursor)
if key==nil then return true end
job.cursor=key
if type(value)=="string" and value=="_" then
job.target[key]=nil
elseif (job.mode=="fields" or job.mode=="locale") and type(value)=="table" and type(job.target[key])=="table" then
local base=job.target[key]
for field,fieldValue in pairs(value) do
if type(fieldValue)=="string" and fieldValue=="_" then
base[field]=nil
else
base[field]=fieldValue
end
end
else
job.target[key]=value
end
D.stats.merged=D.stats.merged+1
count=count+1
end
return false
end
local function Finalize()
if pfDB.items then pfDB.items.loc=pfDB.items.enUS end
if pfDB.units then pfDB.units.loc=pfDB.units.enUS end
if pfDB.objects then pfDB.objects.loc=pfDB.objects.enUS end
if pfDB.quests then pfDB.quests.loc=pfDB.quests.enUS end
if pfDB.zones then pfDB.zones.loc=pfDB.zones.enUS end
if pfDB.professions then pfDB.professions.loc=pfDB.professions.enUS end
for _,name in pairs(TEXT_DATASETS) do
local bucket=pfDB[name]
if bucket then bucket["enUS-turtle"]=nil end
end
-- Apply the sparse Tortoise/Octo enrichment only after the base + Turtle
-- field-by-field merge is final. This restores server fields the compact
-- pfQuest runtime database intentionally discarded without replacing the
-- working pfQuest quest records wholesale.
if QuestieOcto.Enrichment then
if not QuestieOcto.Enrichment:Apply() then
D.running=false
D.ready=false
QuestieOcto:Error("quest-truth enrichment could not be applied")
return
end
end
if QuestieOcto.QuestModel and QuestieOcto.QuestModel.Clear then
QuestieOcto.QuestModel:Clear()
end
D.running=false
D.ready=true
QuestieOcto:SendMessage("DATABASE_READY")
end
function D:Start()
if self.ready or self.running then return end
if not pfDB then
QuestieOcto:Error("raw quest database did not load")
return
end
BuildMergeJobs()
self.running=true
local function step()
local job=D.jobs[D.jobIndex]
if not job then
Finalize()
return
end
if ProcessJob(job) then D.jobIndex=D.jobIndex+1 end
QuestieOcto.Scheduler:Enqueue(step,"database-merge")
end
QuestieOcto.Scheduler:Enqueue(step,"database-merge")
end
local function Count(t)
local n=0
if t then for _ in pairs(t) do n=n+1 end end
return n
end
function D:GetActiveLocale()
return "enUS"
end
function D:Counts()
if not pfDB then return 0,0,0,0 end
return
Count(pfDB.quests and pfDB.quests.data),
Count(pfDB.items and pfDB.items.data),
Count(pfDB.units and pfDB.units.data),
Count(pfDB.objects and pfDB.objects.data)
end
function D:GetRawQuest(id)
if not self.ready then return nil end
return pfDB.quests and pfDB.quests.data and pfDB.quests.data[id]
end
function D:GetQuestLocale(id)
if not self.ready then return nil end
return pfDB.quests and pfDB.quests.loc and pfDB.quests.loc[id]
end
function D:GetRawItem(id)
if not self.ready then return nil end
return pfDB.items and pfDB.items.data and pfDB.items.data[id]
end
function D:GetRawUnit(id)
if not self.ready then return nil end
return pfDB.units and pfDB.units.data and pfDB.units.data[id]
end
function D:GetRawObject(id)
if not self.ready then return nil end
return pfDB.objects and pfDB.objects.data and pfDB.objects.data[id]
end
+197
View File
@@ -0,0 +1,197 @@
-- Questie-Octo canonical quest model.
-- Questie-facing semantic model over pfQuest/Tortoise quest truth.
QuestieOcto.QuestModel = QuestieOcto.QuestModel or {}
local QM = QuestieOcto.QuestModel
QM.cache = {}
local function CopyArray(src)
if not src then return nil end
local out={}
for _,v in pairs(src) do table.insert(out,v) end
return out
end
local function CopyGroups(src)
if not src then return nil end
local out={}
for _,group in pairs(src) do
table.insert(out,CopyArray(group) or {})
end
return out
end
-- Supplied Questie 3.3.5 classic corrections:
-- these quests have their item objective before the normal category order.
local ITEM_OBJECTIVE_FIRST={
[503]=true,
[5088]=true,
}
-- Quests that are not normal static NPC offers. Questie 6 blacklists 7946
-- because Morja only exposes it after the Dark Iron Ale/Jubjub interaction.
local CONDITIONAL_OFFERS={
[7946]="Requires Jubjub to be lured back with Dark Iron Ale.",
}
local function AddObjectiveData(list,kind,id)
if not id then return end
local typ=nil
if kind=="creature" then typ="monster"
elseif kind=="gameObject" then typ="object"
elseif kind=="item" then typ="item"
end
table.insert(list,{
kind=kind,
type=typ,
id=id
})
end
local function BuildObjectiveData(questID,objectives)
local result={}
local itemFirst=ITEM_OBJECTIVE_FIRST[questID] and true or false
if itemFirst then
for i=1,table.getn(objectives.item or {}) do
AddObjectiveData(result,"item",objectives.item[i])
end
end
-- Questie 3.3.5/7/8 DB compiler category order.
for i=1,table.getn(objectives.creature or {}) do
AddObjectiveData(result,"creature",objectives.creature[i])
end
for i=1,table.getn(objectives.gameObject or {}) do
AddObjectiveData(result,"gameObject",objectives.gameObject[i])
end
if not itemFirst then
for i=1,table.getn(objectives.item or {}) do
AddObjectiveData(result,"item",objectives.item[i])
end
end
return result
end
function QM:Clear()
self.cache={}
end
local function ObservedRepeatables()
QuestieOctoGlobalDB=QuestieOctoGlobalDB or {}
QuestieOctoGlobalDB.observedRepeatableQuests=QuestieOctoGlobalDB.observedRepeatableQuests or {}
return QuestieOctoGlobalDB.observedRepeatableQuests
end
function QM:MarkObservedRepeatable(questID)
questID=tonumber(questID)
if not questID then return false end
local db=ObservedRepeatables()
if db[questID] then return false end
db[questID]=true
if self.cache[questID] then self.cache[questID].repeatable=true end
if QuestieOcto.AvailableQuests and QuestieOcto.AvailableQuests.Schedule then
QuestieOcto.AvailableQuests:Schedule(true,0.02)
end
return true
end
function QM:Get(questID)
if self.cache[questID] then return self.cache[questID] end
if not QuestieOcto.DatabaseAPI:IsReady() then return nil end
local raw=QuestieOcto.DatabaseAPI:GetQuestRaw(questID)
if not raw then return nil end
local q={
id=questID,
title=QuestieOcto.DatabaseAPI:GetQuestTitle(questID),
descriptionText=QuestieOcto.DatabaseAPI.GetQuestDescriptionText and QuestieOcto.DatabaseAPI:GetQuestDescriptionText(questID) or nil,
objectiveText=QuestieOcto.DatabaseAPI.GetQuestObjectiveText and QuestieOcto.DatabaseAPI:GetQuestObjectiveText(questID) or nil,
-- lvl is the displayed quest level. min/max are the actual server
-- acceptance bounds and are deliberately kept separate.
level=tonumber(raw["lvl"] or 0) or 0,
requiredLevel=tonumber(raw["min"] or 0) or 0,
maximumLevel=tonumber(raw["max"] or 0) or 0,
-- Questie 6 uses quest Type 41 as the canonical PvP classification.
-- This comes from the authoritative Turtle enrichment, including only
-- verified local corrections for misclassified custom PvP quests.
questType=tonumber(raw["type"] or 0) or 0,
pvp=(tonumber(raw["type"] or 0) or 0)==41,
raceMask=raw["race"],
classMask=raw["class"],
requiredSkill=raw["skill"],
requiredSkillValue=tonumber(raw["skillValue"] or 1) or 1,
repMinFaction=tonumber(raw["repMinFaction"]),
repMinValue=tonumber(raw["repMinValue"] or 0) or 0,
repMaxFaction=tonumber(raw["repMaxFaction"]),
repMaxValue=tonumber(raw["repMaxValue"] or 0) or 0,
-- Preserve the authoritative raw event association, but normalize the two
-- Darkmoon Faire location IDs into one logical event for every runtime
-- consumer. Turtle/pfQuest reuse the same Faire NPCs and quest offers at
-- both Elwynn and Mulgore while many shared quest rows are inconsistently
-- tagged as event 4 or event 5. Keeping rawEventID makes diagnostics lossless;
-- eventID is the logical availability/presentation identity.
rawEventID=tonumber(raw["event"]),
eventID=(tonumber(raw["event"])==5 and 4 or tonumber(raw["event"])),
event=raw["event"],
repeatable=(raw["repeatable"] or ObservedRepeatables()[tonumber(questID)]) and true or false,
hideAfterFirstCompletion=raw["hideAfterFirstCompletion"] and true or false,
daily=raw["daily"] and true or false,
yearly=raw["yearly"] and true or false,
hardcore=raw["hardcore"] and true or false,
timed=raw["timed"] and true or false,
disabled=raw["disabled"] and true or false,
conditionalOffer=CONDITIONAL_OFFERS[tonumber(questID)],
exclusive=raw["exclusive"] and true or false,
nextChain=tonumber(raw["nextChain"]),
-- pfQuest's ordinary pre remains OR semantics. The enrichment restores
-- signed active predecessors and negative ExclusiveGroup all-of groups
-- separately so their server meaning is not flattened again.
preQuestSingle=CopyArray(raw["pre"]),
preQuestActive=CopyArray(raw["preActive"]),
preQuestAll=CopyGroups(raw["preAll"]),
-- close is only authoritative when the migrated server still marks the
-- quest as a positive ExclusiveGroup member (q.exclusive=true).
exclusiveTo=CopyArray(raw["close"]),
starts={
creature=raw["start"] and CopyArray(raw["start"]["U"]) or nil,
gameObject=raw["start"] and CopyArray(raw["start"]["O"]) or nil,
item=raw["start"] and CopyArray(raw["start"]["I"]) or nil,
},
finishes={
creature=raw["end"] and CopyArray(raw["end"]["U"]) or nil,
gameObject=raw["end"] and CopyArray(raw["end"]["O"]) or nil,
},
objectives={
creature=raw["obj"] and CopyArray(raw["obj"]["U"]) or nil,
gameObject=raw["obj"] and CopyArray(raw["obj"]["O"]) or nil,
item=raw["obj"] and CopyArray(raw["obj"]["I"]) or nil,
irItems=raw["obj"] and CopyArray(raw["obj"]["IR"]) or nil,
},
}
-- IR items are intentionally NOT added to objectiveData. They are a
-- pfQuest-special interaction relationship that becomes target guidance
-- only while the player actually possesses the required item.
q.objectiveData=BuildObjectiveData(questID,q.objectives)
self.cache[questID]=q
return q
end
+90
View File
@@ -0,0 +1,90 @@
# Questie-Octo architecture base
## Non-negotiable design
- ClassicAPI is the only required enhanced-client compatibility layer.
- SuperWoW and Nampower may be detected later, but no core feature may depend on them.
- Questie 5.2.3 is the main UI/behavior target.
- Questie 6.0.0 is the architecture bridge.
- Questie 7-9 are bug-fix references.
- Questie 10-11 are references for service separation and mature availability logic.
- Questie 11.34.1 is an edge-case reference, not a runtime base.
- pfQuest/pfQuest-turtle are transitional raw data/reference sources only.
- Tortoise/Turtle server data should become the authoritative offline-generated relationship layer.
- No heavy whole-world calculation may run in one frame.
- Login and zone changes must remain responsive.
- Map nodes will use persistent diff/reuse, never continuous clear/recreate.
## Runtime service order
ClassicAPI contract
-> Scheduler
-> raw-data provider merge (incremental)
-> QuestLog cache (ClassicAPI IDs)
-> Turtle completion history
-> canonical Quest model
-> AvailableQuests
-> Objectives / ItemStarts
-> persistent Map + Minimap
-> Tracker / Tooltips / Options
## 0.1.0 scope
0.1.0 intentionally stops after:
- ClassicAPI verification
- gentle scheduler
- isolated raw DB provider
- ClassicAPI quest-ID quest log cache
- Turtle .queststatus / TWQUEST completion service
- diagnostics
No map, minimap, tracker, availability engine, or tooltip UI yet.
## 0.1.1 milestone
Introduces the public database boundary and canonical quest services:
Legacy/raw data provider
-> DatabaseAPI
-> QuestModel
-> Completion + QuestLog
-> AvailableQuests
No module outside Database/ should add new direct pfDB dependencies.
## 0.1.3 milestone
Adds the canonical node-generation boundary:
QuestModel
-> Objectives
-> ItemStarts
-> Nodes
-> future persistent Map/Minimap
Map/Minimap must consume Nodes and must not rediscover quest/item/source
relationships itself.
## Quest-truth publication rules
The runtime quest system is intentionally layered:
pfQuest/Turtle raw DB truth
-> QuestModel semantic normalization
-> Completion / EventAvailability / QuestLog runtime truth
-> AvailableQuests transactional snapshot
-> Objectives / ItemStarts
-> Nodes transactional snapshot
-> PreparedMap
-> World Map / Minimap / Tracker / Quests browser
Important invariants:
- `lvl` is presentation/difficulty; `min` is the minimum-level availability gate.
- A quest DB `event` value is event membership only. It never means the event is active now.
- Repeatability and event membership are independent flags; rendering must use an explicit precedence instead of assuming one classification excludes the other.
- Completion data must be queried authoritatively before it is published. When `QueryQuestsCompleted` exists, request the cache and wait for `QUEST_QUERY_COMPLETE` before reading `GetQuestsCompleted`; otherwise use the Turtle `.queststatus` / `TWQUEST` fallback.
- Async services never clear a published live snapshot while rebuilding it. Build into a private buffer and atomically swap at the publication boundary. This prevents map/minimap blinking during settings or quest-state refreshes.
- Prepared/map presentation stays valid until a complete replacement node snapshot exists.
File diff suppressed because it is too large Load Diff
+356
View File
@@ -0,0 +1,356 @@
## 1.0.1 first patch release
- Turtle quest `42045` (Tainted Rune) now carries `class = 256` in `Data/pfDB/quests-turtle.lua`, matching Tortoise WoW's Warlock-only requirement while preserving its Dwarf race mask and prerequisite 179.
- First-time quest acceptance keeps the native quest-log priming/fast-poll path validated during post-1.0 testing.
- World-map objective glow defaults OFF with the existing one-time migration from the former default ON state.
- World Map density refresh prioritizes the currently displayed map zone, then the player's minimap zone, before background prepared-map work.
- Official release metadata is version `1.0.1`; future patch releases follow `1.0.2`, `1.0.3`, and so on.
## 1.0 final quest-option and branding pass
- Added character option `showPvPRelatedQuests` (default ON), matching Questie 6's PvP quest visibility semantics for map/minimap markers. OFF hides available PvP starts, active PvP objectives, and PvP turn-ins.
- Reordered Quest Options so Event follows Repeatable and added the requested `Show PVP Related Quests` toggle.
- Final release metadata is version 1.0 and the startup chat message no longer includes a Beta suffix.
## Beta 1.6 world-map visibility scope correction
- `showAllQuestsWorldMap` and `showSpecialQuestsWorldMap` are continent/world-overview filters only.
- Selected zone and city maps no longer apply those overview filters to available/item-start/turn-in quest markers.
- Zone/city quest marker visibility remains governed by the ordinary available/completed/item-start controls.
- `showSpecialQuestsWorldMap` remains default OFF.
## Beta 1.3 world-map special markers / tracker defaults / scrollable rewards
- `showSpecialQuestsWorldMap` defaults OFF and controls special blue/red/green quest pickup/turn-in markers independently from ordinary yellow quest markers.
- Tracker defaults are font size 11, 30 visible rows, background opacity 0.
- Rewards are children of the quest-detail scroll content and scroll naturally after Turn In.
- Internal addon folder is permanently `Questie-Octo` and the TOC is permanently `Questie-Octo.toc`; ZIP filenames carry the version instead.
- Version metadata: `Beta 1.3`.
- Quest 7946 (`Spawn of Jubjub`) is a conditional world-interaction offer and is not advertised as a static Morja pickup.
- Unavailable-reason strings were shortened for player readability.
- Rewards are rendered inside the scrollable quest-detail content with money and native item hyperlink buttons.
## Beta 1.1 post-package corrections
- Darkmoon event IDs 4 and 5 are treated as one logical active Faire for quest
eligibility/presentation during the six active days. The DB assigns shared
Faire quests to both IDs even though those NPCs exist at both locations.
- Auctioneer, Banker, and Mailbox markers use a hidden 0.90 intrinsic multiplier; global scale 1 remains
the user-facing default.
- General options expose direct World Map Visibility controls for
Show Quests on the World Map, Show Special Quests on the World Map, and Show Flight Master on the World Map.
# Quest system infrastructure audit — Beta 1.1
## Full Beta 1.1 regression audit (2026-08-14)
- 100 Lua files syntax-load cleanly under the supplied Lua compatibility checker.
- TOC/XML/file references resolve; addon identity is `Questie-Octo`, author `Questie-Octo`, version `Beta 1.1`.
- No unrelated generated data, code or assets are present in the addon package.
- pfDB access remains confined to the Database/Data boundary.
- `pfquest_node.tga` and Questie 6 `repeatable.blp` still match the supplied authoritative assets byte-for-byte.
- PvP Type-41 enrichment and the verified Blood Ring Type-0 corrections are present; marker priority remains PvP > Repeatable > Event > Normal.
- Quests browser search no longer depends on the shared scheduler. It performs a bounded direct scan of the already-cached lowercase title index and caps visible results at 100.
- General-tab World Map controls are flat AceConfig entries rather than an inline nested group for Ace3v/Vanilla compatibility.
- Seasonal event presentation is emitted only for a currently scheduled/active seasonal quest; repeatable and PvP presentation still take priority.
## Reference sources used
This pass was checked against the user-supplied reference archives rather than
an online mirror:
- current `pfQuest.zip` base source/database;
- current `pfQuest-turtle.zip` Turtle overlay/database;
- `Questie-v6.0.0.zip` for Questie presentation behavior/artwork;
- the supplied Turtle server source/database snapshot for quest requirements,
rewards and game-event scheduling.
The normal pfQuest/Turtle entity and coordinate database remains the primary
static data source. Questie-Octo layers its own normalization/enrichment after
that data instead of maintaining a second independent quest database.
`UI/Icons/pfquest_node.tga` is byte-for-byte identical to pfQuest's `img/node.tga`.
`UI/Icons/repeatable.blp` is byte-for-byte identical to Questie 6.0.0's
`Icons/repeatable.blp`.
## Authority chain
1. pfQuest + Turtle + Octo static data — quest/entity/source truth.
2. `DatabaseAPI` — raw-data boundary for the rest of the addon.
3. `QuestModel` — normalized quest semantics (`lvl`, `min`, restrictions,
prerequisites, repeatability and event membership).
4. Runtime truth:
- `QuestLog` — active/objective/failure state;
- `Completion` — rewarded/completed history;
- `EventAvailability` — current server event state.
5. `AvailableQuests` — transactional availability snapshot.
6. `Objectives` / `ItemStarts` — source relationships.
7. `Nodes` — normalized map-node snapshot.
8. `PreparedMap` — shared immutable render plan.
9. World Map / Minimap / Tracker / Quests — presentation only.
No first-party runtime module outside `Database/` reads raw `pfDB` directly.
## Availability invariants
- `lvl` is display/difficulty information only.
- `min` is the minimum-level acceptance gate.
- The old user-facing Level Above Player option is gone.
- Active/completed state, chain prerequisites, exclusivity, race, class,
Hardcore, skill/profession, reputation, event activity, level bounds,
repeatability and valid starters are evaluated before publication.
- `Show Low-Level Quests`, `Show Event Quests` and `Show Repeatable Quests` are
display/availability preferences, not replacements for server truth.
- Repeatability and event membership are independent properties.
`AvailableQuests:GetUnavailableReason()` exposes the first authoritative failing
condition to the Quests browser. It can identify disabled/not-currently-
obtainable quests, active/completed state, chain/exclusive prerequisites, race,
class, Hardcore, skill, reputation, inactive event, level bounds, hidden
repeatables, timed-quest conflicts and quests with no valid starter/source.
Unknown server-only conditions are reported as unknown instead of guessed.
## Completion and repeatables
Completion initialization runs the server completed-quest query protocol before
reading completed history, with the Turtle quest-status path retained as a
fallback.
Repeatable flags come from the server-derived enrichment or trustworthy live
observation. One-and-done repeatables can opt into `hideAfterFirstCompletion`;
`CLUCK!` uses that rule.
The cloth donation chain is intentionally not flattened into one repeatable
category: Wool/Silk/Mageweave/Runecloth donation steps are one-time quests, while
the `Additional Runecloth` follow-ups carry the repeatable flag.
Questie 6 presentation is used for repeatables: available repeatable starters
use the authentic blue `repeatable.blp`; repeatable turn-ins use Questie's
ordinary completion question-mark artwork. If a quest is both repeatable and an
event quest, PvP presentation wins first; otherwise repeatable presentation wins instead of turning the marker green.
## Event activity and presentation
The pfQuest quest `event` field means membership in a server game-event group.
It does **not** by itself mean either “active now” or “draw this quest green.”
Questie-Octo now resolves those concepts separately.
`Data/EventSchedule.lua` is a compact projection of the supplied Turtle
`game_event` table. Ordinary scheduled events are evaluated against realm wall
clock. Continuous release/content gates (`length >= occurrence`) remain usable
for availability but do not receive seasonal-event presentation. This prevents
permanent content such as event 159 (`DM Release : Cloth turning NPC`) from
making normal cloth donation quests green.
Darkmoon Faire uses the manually verified Turtle 14-day cycle, anchored at
2026-08-13:
- six days Elwynn (Thursday through Tuesday);
- one Wednesday construction day;
- six days Mulgore (Thursday through Tuesday);
- one Wednesday construction day;
- repeat indefinitely.
Major annual festivals use `Data/CalendarEventRules.lua`, transcribed from the
2026 in-game Turtle calendar. Only visible event text counts. Raid Reset,
roadmap/release announcements and graphic-only calendar cells are ignored.
Known calendar windows are authoritative for quest visibility; clicking a
festival NPC outside its verified window cannot reactivate that festival.
Unknown/custom event IDs still require runtime evidence.
A few quest event IDs exist in the current pfQuest/Turtle quest data without a
matching row in the supplied `game_event` table. Event 172 is explicitly
handled as a persistent compatibility gate because the authoritative quest data
uses it for Molten Core attunement. Missing true-event IDs 26, 99, 166 and 171
retain event presentation but require runtime observation rather than being
assumed active globally.
## Map/node invariants
- Availability, node and prepared-map rebuilds publish transactionally; the
previous complete snapshot remains visible until its replacement is ready.
- Full Nodes use pfQuest's exact `node.tga` and **native 14 px pfQuest baseline
at global scale 1.0**.
- Full Nodes keep the requested additional 15% transparency and subdued color.
- There is no separate Full Node or category scale setting. Only global World
Map and global Minimap scale remain.
- Global scaling changes frame dimensions only; it cannot replace Full Node
artwork with Questie objective artwork.
- Full-node semantic state survives visual refreshes/rescaling.
- World map and minimap retain a hidden 1000-node emergency cap.
- General -> Show All Quests on World Map defaults enabled. It controls available pickup/item-start presentation on the World Map only; active objectives/turn-ins, minimap, and tracker are unaffected.
- Distance fading is absent.
- Object-ID display is absent; Quest, NPC and Item IDs remain optional.
## Quests browser
The browser remains a fixed native frame rather than a large AceGUI result tree:
- 18 recycled result rows;
- no full-database scan merely for opening the browser;
- Options-to-Quests handoff is deferred until after AceConfig's execute refresh,
then Questie Options and the Game Menu are explicitly hidden;
- searches only run on Enter/Search;
- search titles are lowercased/cached during the existing incremental database
index build;
- full text/ID searches run in 33-quest scheduler slices (about 32% more work per
frame than the previous 25-row search while remaining frame-sliced);
- visible results remain capped at 100;
- Available/Active/Completed no-query views use their state tables directly;
- closing the browser invalidates an outstanding search generation.
Unavailable rows explain *why* the quest cannot currently be taken in the right
pane.
`Data/QuestRewards.lua` is a compact projection of the supplied authoritative
Turtle `quest_template` reward fields. The right pane shows positive money
rewards in gold/silver/copper text, guaranteed reward items as native clickable
item hyperlinks, and choice rewards under `Choose one:`. No reward icons are
created. Item links use the same Vanilla `SetItemRef` / `GameTooltip:SetHyperlink`
pattern as pfQuest's item browser.
## Static audit completed for Beta 1.1
- 99 bundled Lua files pass a Lua parser syntax-load check.
- Every TOC file reference exists.
- Every XML Include/Script reference exists.
- All literal settings reads/writes resolve to defined defaults.
- No first-party runtime module outside `Database/` directly reads `pfDB`.
- No runtime distance-fading option/path remains.
- No user-facing Level Above Player setting/path remains.
- No Completion tracker-sort mode remains.
- No Object-ID option/render path remains (only stale SavedVariable cleanup).
- Only global Map and Minimap scale sliders remain.
- Full-node 1000-pin safety caps remain in both renderers.
- pfQuest Full Node and Questie repeatable assets match their supplied reference
files byte-for-byte.
- 33 of the 34 raw loaded pfQuest/Turtle DB files are byte-identical to their
supplied source files; the remaining Turtle `patchtable.lua` is the existing
Questie-Octo compatibility adaptation that applies the overlay without
loading pfQuest's addon UI/runtime.
## Recommended in-game regression pass
1. Open **Quests** from Questie Options and confirm Options/Game Menu disappear.
2. Search `defias`; compare response time and inspect several Unavailable rows.
3. Select quests with money, fixed-item and choice-item rewards; hover/click the
hyperlink text.
4. Compare a normal cloth donation with an `Additional Runecloth` follow-up.
5. Verify `Give Gerard a Drink` uses the authentic blue repeatable pickup icon.
6. Verify Darkmoon immediately after `/reload` and again across its Wednesday
installation transition.
7. Move global Map/Minimap scale repeatedly while Full Nodes are visible.
8. Recheck failed timed quests, reputation gates and multi-step prerequisites.
## 0.3.6 corrective notes
- Event availability now mirrors pfQuest: a quest's `event` membership does not
locally gate availability. The Show Event Quests toggle is the display gate.
The separate presentation classifier still prevents permanent Turtle content
gates such as event 159 from being painted as festival quests.
- Quests search uses the database's already-cached lowercase titles in 768-ID
bounded slices (roughly nine frames for the entire current DB) and exact numeric
quest IDs bypass the scan entirely.
- Authoritative donation verification: 80374 (Additional Runecloth: Silvermoon
Remnants) and 80379 (Additional Runecloth: Revantusk Tribe) are repeatable.
80374 has server minimum level 50; it is correctly unavailable to a level-34
character. Goblin donation quests 41115 (Cartel Gold Donations, min 30) and
41123 (Donations to Vizlow, min 1) are repeatable and level-eligible at 34.
## 0.3.7 conservative event beta notes
- Fresh installs default `showEventQuests=false`; existing character choices are
not overwritten.
- `IsPresentationEvent` separates true festival presentation from permanent
game_event release/content gates.
- Available festival markers require both the user toggle and
`EventAvailability:IsActiveForQuest()`. Scheduled/hardcoded event truth is
preferred; unknown schedules require runtime observation.
- Runtime observation triggers a bounded availability refresh only on the first
inactive-to-active transition.
- Active quests already in the quest log bypass available-quest filtering and
therefore retain objectives/turn-ins even if event display is disabled.
## 0.3.8 curated Turtle calendar notes
The manually verified annual windows are:
- Love is in the Air: February 1-16
- Lunar Festival: February 16-March 4
- Noblegarden: April 17-May 8
- Midsummer Fire Festival: June 21-July 12
- Brewfest: September 20-October 6
- Harvest Festival: October 1-8
- Hallow's End: October 20-November 2
- Feast of Winter Veil: December 3-January 14
Darkmoon Faire is intentionally separate from the annual calendar and uses the
anchored alternating 14-day rule described above. Roadmap names such as
Frostmane Hollow, Windhorn Canyon, Dragonmaw Retreat, Northwind, Stormwrought
Ruins, Moonwhisper Coast, Phase 1/2 and similar release notices never affect
quest availability.
## Beta 1.1 event standby policy
Event membership is not itself proof of a seasonal event. Beta 1.1 uses an
explicit three-way classification:
- verified non-seasonal/content gates: ordinary quest availability/presentation;
- verified seasonal events: gated by the curated annual calendar, the anchored
Darkmoon cycle, or a separately verified narrow timer;
- unknown/unverified event IDs: standby and therefore not published as available
quest markers. Active quests already accepted by the player remain visible.
Event 159 is explicitly verified as the cloth-turn-in content/release gate and
is not seasonal. Repeatability remains independent of event classification.
## Beta 1.1 package identity
- Install folder: `Questie-Octo`
- TOC file: `Questie-Octo.toc`
- Display title: `Questie-Octo`
- Author: `Questie-Octo`
- Version: `Beta 1.1`
The versioned install folder is intentional for this beta package. First-party
texture paths were updated to the same folder name and validated before packaging.
## PvP quest classification
Questie-Octo preserves the authoritative Turtle quest `Type` for PvP quests.
`Type == 41` is the canonical PvP classification used by Questie 6.
Presentation priority for quest pickup/turn-in markers is:
1. PvP (red)
2. Repeatable (blue)
3. verified Event (green)
4. Normal (yellow)
The supplied Turtle source currently reports Blood Ring quests 41107-41110 as
Type 0 even though their objectives/rewards explicitly identify them as PvP.
Questie-Octo corrects only those verified rows locally to Type 41; Type 0 is
not broadly reinterpreted.
## Beta 1.1 Darkmoon live-offer correction
Live Elwynn Faire cross-checks on 2026-08-14 confirmed that Turtle reuses the
same Darkmoon hand-in NPCs/quests across Elwynn and Mulgore while pfQuest splits
those shared rows between event IDs 4 and 5. The verified starter set is Silas
Darkmoon (14823), Gelvas Grimegate (14828), Yebb Neblegear (14829), Kerri Hicks
(14832), Chronos (14833), and Rinling (14841).
For quests tagged event 4/5 and started by those NPCs, Questie-Octo treats the
Faire as one logical active event. During an active Faire it keeps completion,
race/class, level, max-level, timed and repeatable-option checks, but does not
let stale pfQuest Darkmoon reputation/chain metadata suppress an offer that the
live Turtle quest dialog exposes. Silas's one-time Torta's Egg remains hidden
once historical completion says it was rewarded.
### Darkmoon logical event alias
Turtle/pfQuest quest rows use both `event = 4` and `event = 5` for quest offers that are shared by the same Darkmoon Faire NPCs in both Elwynn and Mulgore. Questie-Octo preserves the authoritative value as `rawEventID`, but normalizes runtime `eventID` 5 to logical event 4. Availability and presentation therefore see one `DARKMOON_FAIRE` event; the anchored 14-day schedule and NPC coordinates determine the physical location. Construction events 23/24 remain distinct.
+113
View File
@@ -0,0 +1,113 @@
# Questie-Octo source provenance and licensing audit
Audit date: 2026-08-15
## Purpose
This document records the source snapshots available during the GitHub/licensing
pass and preserves provenance for future human and AI-assisted maintenance.
This pass is documentation/licensing-only: no gameplay, Lua logic, TOC entry,
quest data, map behavior, tracker behavior, artwork, settings, or existing
project note was changed or removed.
The baseline addon archive audited here was:
- `Questie-Octo(1).zip`
- SHA-256: `3e0528012df2a0a36cd9185850ec71a330e5feb2abc87dc6b44e8f765e5f2fea`
- 139 regular files before licensing documents were added.
## Source snapshots available for this audit
| Source snapshot | SHA-256 | Role / note |
| --- | --- | --- |
| Questie 5.2.3 | `806a12d894a9d8568b73d86a95aeda79f29bebf90320a5f0007bec8db9803335` | Main historical UI/behavior target; no project-level Questie license found in supplied ZIP. |
| Questie 6.0.0 | `de997f793871b898f6a8f9f6494b79fcd55fc82f8e087b88a026a3dfc99550b9` | Architecture bridge and source of some reference assets/libraries; no project-level Questie license found in supplied ZIP. |
| Ace3v | `0482801133653d0e611e29335b22e77ad3de05a14ab8c35c163eb3f05eaaebeb` | Bundled/adapted Ace3 compatibility libraries. |
| pfQuest-classicAPI-octo | `484553544a30f4031273873c9673b779240339626455aa0e3c05f5c3df577dfb` | pfQuest-family data/runtime heritage and compatibility reference. |
| pfQuest-octo | `f4180ff88fbca25e7c673ab8fb5903c8a94807678b89ca70eefede0a8b3bd7c4` | Octo-specific pfQuest branch/reference. |
| ShaguTweaks | `3d772e41ea576a9b8e3d25017cb3cc2f93347d7ee4da4fa7bb69f2e403bcc007` | Vanilla UI/compatibility reference. |
| ShaguTweaks-extras | `2727f09cbbbdc08b2a06f424758363d0f206fef2ff0485449e03867a41a84357` | Additional Shagu compatibility/reference material. |
| MoveAnything | `452b9d7dc5c74d7f0c4a2f51954bfcaa0b20b951fbf742951a48390dd21cf7fd` | Compatibility/debugging reference; no general license file found in supplied snapshot. |
| Tortoise/Turtle server snapshot | `ed3b3638f26733d30993e0d2a1fac1e31a0738d5f66bd11a23c52509f7d47ee9` | Offline server-data provenance recorded by existing generated-data notes. |
| Calendar reference archive | `f4418d71aaf86fcc1067f3f02b7291aab6f3c37bb4d607a440127879270553a9` | Event/calendar verification material. |
The filenames used for upload can carry duplicate suffixes such as `(2)` or
`(3)`. The SHA-256 values above identify the actual snapshots independently of
those local upload names.
## Later compatibility reference snapshots
The following snapshots were supplied later in the same project and were used
only to cross-check UI compatibility behavior. They are not bundled with
Questie-Octo and this record does not change their licensing or ownership.
| Compatibility reference | SHA-256 | Role / note |
| --- | --- | --- |
| pfUI-classicAPI-octo | `5b4ed17b4ca94712ea7cc35cd86f31dfdc7b5d94a83bb51abb3cfca2e8e4911b` | Tooltip lifecycle, minimap shape, and Vanilla/ClassicAPI UI compatibility reference. |
| DragonflightUI-Reforged | `2c08e1ae203f54d7969262b65056d58d5e9e8c26f3b61f2c935ff0825525ac68` | Replacement ESC/GameMenu behavior and square-minimap compatibility reference. |
## Historical sources no longer available as exact archives
Existing Questie-Octo comments and README history establish that Questie 3.3.5,
7.0.0, and 8.0.0 were used during the earlier backport work. Those exact source
archives are no longer available in the current workspace, so they cannot be
re-hashed during this licensing pass.
Do not delete their references from source comments or documentation. Those
references are the remaining provenance record for the earlier work.
Later `Docs/ARCHITECTURE.md` records additional Questie generations as reference
material, including Questie 7-9, Questie 10-11, and Questie 11.34.1 as an
edge-case reference. Earlier README sections saying Questie 9/10/11 were not
used are historical stage-specific notes and intentionally remain intact.
## Exact-match audit observations
The baseline Questie-Octo archive contained many files that are byte-identical
to supplied source snapshots, especially under `Libs/` and `Data/`. Exact hash
matches are only one kind of provenance: adapted code, generated tables,
converted artwork, copied behavior, and compatibility patterns can remain
third-party-derived even when a whole-file hash no longer matches.
For this reason future audits must consult both hashes and the retained source
comments/documentation.
## License findings
### Questie-Octo original contributions
Original portions first authored for Questie-Octo are offered under the MIT
License. See `../LICENSE` and `../LICENSES/Questie-Octo-MIT.txt`.
### Ace3v
The supplied Ace3v snapshot includes the Ace3 Development Team license. Its
verbatim text is preserved at `../LICENSES/Ace3v-LICENSE.txt`.
### pfQuest / Shagu
The supplied pfQuest and Shagu snapshots contain MIT license files. Their exact
texts are preserved individually in `../LICENSES/` so their original copyright
years and notices are not collapsed into a generic replacement.
### Questie
No project-level Questie license file was found in the supplied Questie 5.2.3
or 6.0.0 snapshots. The `Questie/Libs/LICENSE.txt` file in those snapshots is
Ace3's license and is not treated as Questie's project license. Questie-derived
material is therefore excluded from the scope of the Questie-Octo MIT grant in
this repository notice.
### ClassicAPI
ClassicAPI.dll is an external dependency and is not bundled. Upstream states
GPL v3 or later. The upstream GPLv3 license document is included at
`../LICENSES/ClassicAPI-GPL-3.0.txt` for reference and compliance clarity.
## Packaging invariant
The GitHub/licensing package must preserve every pre-existing archive member's
uncompressed bytes. Licensing/provenance files are additions only. A future AI
or human contributor should not use a licensing cleanup as a reason to rewrite
source, change gameplay, strip comments, remove historical notes, or normalize
third-party files.
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Questie-Octo contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+29
View File
@@ -0,0 +1,29 @@
Copyright (c) 2007, Ace3 Development Team
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Redistribution of a stand alone version is strictly prohibited without
prior written authorization from the Lead of the Ace3 Development Team.
* Neither the name of the Ace3 Development Team nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+674
View File
@@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.
+11
View File
@@ -0,0 +1,11 @@
LibStub public-domain notice
============================
The bundled LibStub source contains the following upstream notice:
"LibStub is hereby placed in the Public Domain"
Credits recorded in the bundled source:
Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke.
The notice is also retained directly in Libs/LibStub/LibStub.lua.
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Questie-Octo contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+34
View File
@@ -0,0 +1,34 @@
Questie-Octo Licensing Notice
=============================
Questie-Octo is a mixed-source open-source project.
Original Questie-Octo contributions
------------------------------------
Code, documentation, integration work, and other material first authored for
Questie-Octo, to the extent that those portions are not copied from or derived
from third-party material, are licensed under the MIT License.
The full MIT text for those original contributions is in:
LICENSES/Questie-Octo-MIT.txt
Third-party material
--------------------
This notice does not relicense third-party code, data, artwork, libraries, or
other material. Third-party material remains under its own copyright and
license terms. See THIRD_PARTY_NOTICES.md and the files in LICENSES/.
In particular, the MIT grant for original Questie-Octo contributions must not
be read as a claim that Questie-derived material, Ace3 material, pfQuest/Shagu
material, ClassicAPI, or any other third-party material has been relicensed by
Questie-Octo.
Where the exact upstream project license could not be verified from the source
snapshot used during development, the upstream rights are expressly retained
and no additional permission is asserted here.
This licensing layout is intentionally additive. Historical README sections,
source comments, architecture notes, audit notes, generated-data headers, and
AI-oriented maintenance notes are part of the project's provenance record and
should be preserved when modifying or redistributing the project.
+21
View File
@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2021 Eric Mauser (Shagu)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+21
View File
@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2025 Eric Mauser (Shagu)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017-2021 Eric Mauser (Shagu)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Eric Mauser (Shagu)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,4 @@
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
..\FrameXML\UI.xsd">
<Script file="AceConfigDialog-3.0.lua"/>
</Ui>
@@ -0,0 +1,352 @@
--- AceConfigRegistry-3.0 handles central registration of options tables in use by addons and modules.\\
-- Options tables can be registered as raw tables, OR as function refs that return a table.\\
-- Such functions receive three arguments: "uiType", "uiName", "appName". \\
-- * Valid **uiTypes**: "cmd", "dropdown", "dialog". This is verified by the library at call time. \\
-- * The **uiName** field is expected to contain the full name of the calling addon, including version, e.g. "FooBar-1.0". This is verified by the library at call time.\\
-- * The **appName** field is the options table name as given at registration time \\
--
-- :IterateOptionsTables() (and :GetOptionsTable() if only given one argument) return a function reference that the requesting config handling addon must call with valid "uiType", "uiName".
-- @class file
-- @name AceConfigRegistry-3.0
-- @release $Id: AceConfigRegistry-3.0.lua 1139 2016-07-03 07:43:51Z nevcairiel $
local MAJOR, MINOR = "AceConfigRegistry-3.0", 16
local AceConfigRegistry = LibStub:NewLibrary(MAJOR, MINOR)
if not AceConfigRegistry then return end
AceConfigRegistry.tables = AceConfigRegistry.tables or {}
local CallbackHandler = LibStub:GetLibrary("CallbackHandler-1.0")
if not AceConfigRegistry.callbacks then
AceConfigRegistry.callbacks = CallbackHandler:New(AceConfigRegistry)
end
-- Lua APIs
local tinsert, tconcat, tgetn = table.insert, table.concat, table.getn
local strfind = string.find
local type, tostring, pairs = type, tostring, pairs
local error, assert = error, assert
-----------------------------------------------------------------------
-- Validating options table consistency:
AceConfigRegistry.validated = {
-- list of options table names ran through :ValidateOptionsTable automatically.
-- CLEARED ON PURPOSE, since newer versions may have newer validators
cmd = {},
dropdown = {},
dialog = {},
}
local function err(msg, errlvl, ...)
local l = tgetn(arg)
local i,j = 1,l
while i < j do
arg[i], arg[j] = arg[j], arg[i]
i = i+1
j = j-1
end
error(MAJOR..":ValidateOptionsTable(): "..tconcat(arg,".")..msg, errlvl+2)
end
local isstring={["string"]=true, _="string"}
local isstringfunc={["string"]=true,["function"]=true, _="string or funcref"}
local istable={["table"]=true, _="table"}
local ismethodtable={["table"]=true,["string"]=true,["function"]=true, _="methodname, funcref or table"}
local optstring={["nil"]=true,["string"]=true, _="string"}
local optstringfunc={["nil"]=true,["string"]=true,["function"]=true, _="string or funcref"}
local optstringnumberfunc={["nil"]=true,["string"]=true,["number"]=true,["function"]=true, _="string, number or funcref"}
local optnumber={["nil"]=true,["number"]=true, _="number"}
local optmethod={["nil"]=true,["string"]=true,["function"]=true, _="methodname or funcref"}
local optmethodfalse={["nil"]=true,["string"]=true,["function"]=true,["boolean"]={[false]=true}, _="methodname, funcref or false"}
local optmethodnumber={["nil"]=true,["string"]=true,["function"]=true,["number"]=true, _="methodname, funcref or number"}
local optmethodtable={["nil"]=true,["string"]=true,["function"]=true,["table"]=true, _="methodname, funcref or table"}
local optmethodbool={["nil"]=true,["string"]=true,["function"]=true,["boolean"]=true, _="methodname, funcref or boolean"}
local opttable={["nil"]=true,["table"]=true, _="table"}
local optbool={["nil"]=true,["boolean"]=true, _="boolean"}
local optboolnumber={["nil"]=true,["boolean"]=true,["number"]=true, _="boolean or number"}
local basekeys={
type=isstring,
name=isstringfunc,
desc=optstringfunc,
descStyle=optstring,
order=optmethodnumber,
validate=optmethodfalse,
confirm=optmethodbool,
confirmText=optstring,
disabled=optmethodbool,
hidden=optmethodbool,
guiHidden=optmethodbool,
dialogHidden=optmethodbool,
dropdownHidden=optmethodbool,
cmdHidden=optmethodbool,
icon=optstringnumberfunc,
iconCoords=optmethodtable,
handler=opttable,
get=optmethodfalse,
set=optmethodfalse,
func=optmethodfalse,
arg={["*"]=true},
width=optstring,
}
local typedkeys={
header={},
description={
image=optstringnumberfunc,
imageCoords=optmethodtable,
imageHeight=optnumber,
imageWidth=optnumber,
fontSize=optstringfunc,
},
group={
args=istable,
plugins=opttable,
inline=optbool,
cmdInline=optbool,
guiInline=optbool,
dropdownInline=optbool,
dialogInline=optbool,
childGroups=optstring,
},
execute={
image=optstringnumberfunc,
imageCoords=optmethodtable,
imageHeight=optnumber,
imageWidth=optnumber,
},
input={
pattern=optstring,
usage=optstring,
control=optstring,
dialogControl=optstring,
dropdownControl=optstring,
multiline=optboolnumber,
nullable=optbool,
},
toggle={
tristate=optbool,
image=optstringnumberfunc,
imageCoords=optmethodtable,
},
tristate={
},
range={
min=optnumber,
softMin=optnumber,
max=optnumber,
softMax=optnumber,
step=optnumber,
bigStep=optnumber,
isPercent=optbool,
},
select={
values=ismethodtable,
valuesTableDestroyable=optbool, -- Ace3v: if the values table is generated by AceCore.new
style={
["nil"]=true,
["string"]={dropdown=true,radio=true},
_="string: 'dropdown' or 'radio'"
},
control=optstring,
dialogControl=optstring,
dropdownControl=optstring,
itemControl=optstring,
},
multiselect={
values=ismethodtable,
valuesTableDestroyable=optbool, -- Ace3v: if the values table is generated by AceCore.new
style=optstring,
tristate=optbool,
control=optstring,
dialogControl=optstring,
dropdownControl=optstring,
},
color={
hasAlpha=optmethodbool,
},
keybinding={
-- TODO
},
}
local function validateKey(k,errlvl,arg)
errlvl=(errlvl or 0)+1
if type(k)~="string" then
err("["..tostring(k).."] - key is not a string", errlvl, arg)
end
if strfind(k, "[%c\127]") then
err("["..tostring(k).."] - key name contained control characters", errlvl, arg)
end
end
local function validateVal(v, oktypes, errlvl, arg)
errlvl=(errlvl or 0)+1
local isok=oktypes[type(v)] or oktypes["*"]
if not isok then
err(": expected a "..oktypes._..", got '"..tostring(v).."'", errlvl, arg)
end
if type(isok)=="table" then -- isok was a table containing specific values to be tested for!
if not isok[v] then
err(": did not expect "..type(v).." value '"..tostring(v).."'", errlvl, arg)
end
end
end
local function validate(options,errlvl,arg)
errlvl=(errlvl or 0)+1
-- basic consistency
if type(options)~="table" then
err(": expected a table, got a "..type(options), errlvl, arg)
end
if type(options.type)~="string" then
err(".type: expected a string, got a "..type(options.type), errlvl, arg)
end
-- get type and 'typedkeys' member
local tk = typedkeys[options.type]
if not tk then
err(".type: unknown type '"..options.type.."'", errlvl, arg)
end
-- make sure that all options[] are known parameters
for k,v in pairs(options) do
if not (tk[k] or basekeys[k]) then
err(": unknown parameter", errlvl,tostring(k), arg)
end
end
-- verify that required params are there, and that everything is the right type
for k,oktypes in pairs(basekeys) do
validateVal(options[k], oktypes, errlvl, k, arg)
end
for k,oktypes in pairs(tk) do
validateVal(options[k], oktypes, errlvl, k, arg)
end
-- extra logic for groups
if options.type=="group" then
for k,v in pairs(options.args) do
validateKey(k,errlvl,"args", arg)
validate(v, errlvl,k,"args", arg)
end
if options.plugins then
for plugname,plugin in pairs(options.plugins) do
if type(plugin)~="table" then
err(": expected a table, got '"..tostring(plugin).."'", errlvl,tostring(plugname), "plugins", arg)
end
for k,v in pairs(plugin) do
validateKey(k,errlvl,tostring(plugname),"plugins",arg)
validate(v, errlvl,k,tostring(plugname),"plugins",arg)
end
end
end
end
end
--- Validates basic structure and integrity of an options table \\
-- Does NOT verify that get/set etc actually exist, since they can be defined at any depth
-- @param options The table to be validated
-- @param name The name of the table to be validated (shown in any error message)
-- @param errlvl (optional number) error level offset, default 0 (=errors point to the function calling :ValidateOptionsTable)
function AceConfigRegistry:ValidateOptionsTable(options,name,errlvl)
errlvl=(errlvl or 0)+1
name = name or "Optionstable"
if not options.name then
options.name=name -- bit of a hack, the root level doesn't really need a .name :-/
end
validate(options,errlvl,name)
end
--- Fires a "ConfigTableChange" callback for those listening in on it, allowing config GUIs to refresh.
-- You should call this function if your options table changed from any outside event, like a game event
-- or a timer.
-- @param appName The application name as given to `:RegisterOptionsTable()`
function AceConfigRegistry:NotifyChange(appName)
if not AceConfigRegistry.tables[appName] then return end
AceConfigRegistry.callbacks:Fire("ConfigTableChange", 1, appName)
end
-- -------------------------------------------------------------------
-- Registering and retreiving options tables:
-- validateGetterArgs: helper function for :GetOptionsTable (or, rather, the getter functions returned by it)
local function validateGetterArgs(uiType, uiName, errlvl)
errlvl=(errlvl or 0)+2
if uiType~="cmd" and uiType~="dropdown" and uiType~="dialog" then
error(MAJOR..": Requesting options table: 'uiType' - invalid configuration UI type, expected 'cmd', 'dropdown' or 'dialog'", errlvl)
end
if not strfind(uiName, "[A-Za-z]+-[0-9]") then -- Expecting e.g. "MyLib-1.2"
error(MAJOR..": Requesting options table: 'uiName' - badly formatted or missing version number. Expected e.g. 'MyLib-1.2'", errlvl)
end
end
--- Register an options table with the config registry.
-- @param appName The application name as given to `:RegisterOptionsTable()`
-- @param options The options table, OR a function reference that generates it on demand. \\
-- See the top of the page for info on arguments passed to such functions.
-- @param skipValidation Skip options table validation (primarily useful for extremely huge options, with a noticeable slowdown)
function AceConfigRegistry:RegisterOptionsTable(appName, options, skipValidation)
if type(options)=="table" then
if options.type~="group" then -- quick sanity checker
error(MAJOR..": RegisterOptionsTable(appName, options): 'options' - missing type='group' member in root group", 2)
end
AceConfigRegistry.tables[appName] = function(uiType, uiName, errlvl)
errlvl=(errlvl or 0)+1
validateGetterArgs(uiType, uiName, errlvl)
if not AceConfigRegistry.validated[uiType][appName] and not skipValidation then
AceConfigRegistry:ValidateOptionsTable(options, appName, errlvl) -- upgradable
AceConfigRegistry.validated[uiType][appName] = true
end
return options
end
elseif type(options)=="function" then
AceConfigRegistry.tables[appName] = function(uiType, uiName, errlvl)
errlvl=(errlvl or 0)+1
validateGetterArgs(uiType, uiName, errlvl)
local tab = assert(options(uiType, uiName, appName))
if not AceConfigRegistry.validated[uiType][appName] and not skipValidation then
AceConfigRegistry:ValidateOptionsTable(tab, appName, errlvl) -- upgradable
AceConfigRegistry.validated[uiType][appName] = true
end
return tab
end
else
error(MAJOR..": RegisterOptionsTable(appName, options): 'options' - expected table or function reference", 2)
end
end
--- Returns an iterator of ["appName"]=funcref pairs
function AceConfigRegistry:IterateOptionsTables()
return pairs(AceConfigRegistry.tables)
end
--- Query the registry for a specific options table.
-- If only appName is given, a function is returned which you
-- can call with (uiType,uiName) to get the table.\\
-- If uiType&uiName are given, the table is returned.
-- @param appName The application name as given to `:RegisterOptionsTable()`
-- @param uiType The type of UI to get the table for, one of "cmd", "dropdown", "dialog"
-- @param uiName The name of the library/addon querying for the table, e.g. "MyLib-1.0"
function AceConfigRegistry:GetOptionsTable(appName, uiType, uiName)
local f = AceConfigRegistry.tables[appName]
if not f then
return nil
end
if uiType then
return f(uiType,uiName,1) -- get the table for us
else
return f -- return the function
end
end
@@ -0,0 +1,4 @@
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
..\FrameXML\UI.xsd">
<Script file="AceConfigRegistry-3.0.lua"/>
</Ui>
+205
View File
@@ -0,0 +1,205 @@
local ACECORE_MAJOR, ACECORE_MINOR = "AceCore-3.0", 1
local AceCore, oldminor = LibStub:NewLibrary(ACECORE_MAJOR, ACECORE_MINOR)
if not AceCore then return end -- No upgrade needed
AceCore._G = AceCore._G or getfenv()
local _G = AceCore._G
local strsub, strgsub, strfind = string.sub, string.gsub, string.find
local tremove, tconcat = table.remove, table.concat
local tgetn, tsetn = table.getn, table.setn
local new, del
do
local list = setmetatable({}, {__mode = "k"})
function new()
local t = next(list)
if not t then
return {}
end
list[t] = nil
return t
end
function del(t)
setmetatable(t, nil)
for k in pairs(t) do
t[k] = nil
end
tsetn(t,0)
list[t] = true
end
-- debug
function AceCore.listcount()
local count = 0
for k in list do
count = count + 1
end
return count
end
end -- AceCore.new, AceCore.del
AceCore.new, AceCore.del = new, del
local function errorhandler(err)
return geterrorhandler()(err)
end
AceCore.errorhandler = errorhandler
local function CreateSafeDispatcher(argCount)
local code = [[
local errorhandler = LibStub("AceCore-3.0").errorhandler
local method, UP_ARGS
local function call()
local func, ARGS = method, UP_ARGS
method, UP_ARGS = nil, NILS
return func(ARGS)
end
return function(func, ARGS)
method, UP_ARGS = func, ARGS
return xpcall(call, errorhandler)
end
]]
local c = 4*argCount-1
local s = "b01,b02,b03,b04,b05,b06,b07,b08,b09,b10,b11,b12,b13,b14,b15,b16,b17,b18,b19,b20"
code = strgsub(code, "UP_ARGS", string.sub(s,1,c))
s = "a01,a02,a03,a04,a05,a06,a07,a08,a09,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20"
code = strgsub(code, "ARGS", string.sub(s,1,c))
s = "nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil"
code = strgsub(code, "NILS", string.sub(s,1,c))
return assert(loadstring(code, "safecall SafeDispatcher["..tostring(argCount).."]"))()
end
local SafeDispatchers = setmetatable({}, {__index=function(self, argCount)
local dispatcher
if not tonumber(argCount) then dbg(debugstack()) end
if argCount > 0 then
dispatcher = CreateSafeDispatcher(argCount)
else
dispatcher = function(func) return xpcall(func,errorhandler) end
end
rawset(self, argCount, dispatcher)
return dispatcher
end})
local function safecall(func,argc,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
-- we check to see if the func is passed is actually a function here and don't error when it isn't
-- this safecall is used for optional functions like OnInitialize OnEnable etc. When they are not
-- present execution should continue without hinderance
if type(func) == "function" then
return SafeDispatchers[argc](func,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
end
end
AceCore.safecall = safecall
local function CreateDispatcher(argCount)
local code = [[
return function(func,ARGS)
return func(ARGS)
end
]]
local s = "a01,a02,a03,a04,a05,a06,a07,a08,a09,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20"
code = strgsub(code, "ARGS", string.sub(s,1,4*argCount-1))
return assert(loadstring(code, "call Dispatcher["..tostring(argCount).."]"))()
end
AceCore.Dispatchers = setmetatable({}, {__index=function(self, argCount)
local dispatcher
if argCount > 0 then
dispatcher = CreateDispatcher(argCount)
else
dispatcher = function(func) return func() end
end
rawset(self, argCount, dispatcher)
return dispatcher
end})
-- some string functions
-- vanilla available string operations:
-- sub, gfind, rep, gsub, char, dump, find, upper, len, format, byte, lower
-- we will just replace every string.match with string.find in the code
function AceCore.strtrim(s)
return strgsub(s, "^%s*(.-)%s*$", "%1")
end
local function strsplit(delim, s, n)
if n and n < 2 then return s end
beg = beg or 1
local i,j = string.find(s,delim,beg)
if not i then
return s, nil
end
return string.sub(s,1,j-1), strsplit(delim, string.sub(s,j+1), n and n-1 or nil)
end
AceCore.strsplit = strsplit
-- Ace3v: fonctions copied from AceHook-2.1
local protFuncs = {
CameraOrSelectOrMoveStart = true, CameraOrSelectOrMoveStop = true,
TurnOrActionStart = true, TurnOrActionStop = true,
PitchUpStart = true, PitchUpStop = true,
PitchDownStart = true, PitchDownStop = true,
MoveBackwardStart = true, MoveBackwardStop = true,
MoveForwardStart = true, MoveForwardStop = true,
Jump = true, StrafeLeftStart = true,
StrafeLeftStop = true, StrafeRightStart = true,
StrafeRightStop = true, ToggleMouseMove = true,
ToggleRun = true, TurnLeftStart = true,
TurnLeftStop = true, TurnRightStart = true,
TurnRightStop = true,
}
local function issecurevariable(x)
return protFuncs[x] and 1 or nil
end
AceCore.issecurevariable = issecurevariable
local function hooksecurefunc(arg1, arg2, arg3)
if type(arg1) == "string" then
arg1, arg2, arg3 = _G, arg1, arg2
end
local orig = arg1[arg2]
if type(orig) ~= "function" then
error("The function "..arg2.." does not exist", 2)
end
arg1[arg2] = function(...)
local tmp = {orig(unpack(arg))}
arg3(unpack(arg))
return unpack(tmp)
end
end
AceCore.hooksecurefunc = hooksecurefunc
-- pickfirstset() - picks the first non-nil value and returns it
local function pickfirstset(argc,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
if (argc <= 1) or (a1 ~= nil) then
return a1
else
return pickfirstset(argc-1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
end
end
AceCore.pickfirstset = pickfirstset
local function countargs(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
if (a1 == nil) then return 0 end
return 1 + countargs(a2,a3,a4,a5,a6,a7,a8,a9,a10)
end
AceCore.countargs = countargs
-- wipe preserves metatable
function AceCore.wipe(t)
for k,v in pairs(t) do t[k] = nil end
tsetn(t,0)
return t
end
function AceCore.truncate(t,e)
e = e or tgetn(t)
for i=1,e do
if t[i] == nil then
tsetn(t,i-1)
return
end
end
tsetn(t,e)
end
+4
View File
@@ -0,0 +1,4 @@
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
..\FrameXML\UI.xsd">
<Script file="AceCore-3.0.lua"/>
</Ui>
+809
View File
@@ -0,0 +1,809 @@
--- **AceGUI-3.0** provides access to numerous widgets which can be used to create GUIs.
-- AceGUI is used by AceConfigDialog to create the option GUIs, but you can use it by itself
-- to create any custom GUI. There are more extensive examples in the test suite in the Ace3
-- stand-alone distribution.
--
-- **Note**: When using AceGUI-3.0 directly, please do not modify the frames of the widgets directly,
-- as any "unknown" change to the widgets will cause addons that get your widget out of the widget pool
-- to misbehave. If you think some part of a widget should be modifiable, please open a ticket, and we"ll
-- implement a proper API to modify it.
-- @usage
-- local AceGUI = LibStub("AceGUI-3.0")
-- -- Create a container frame
-- local f = AceGUI:Create("Frame")
-- f:SetCallback("OnClose",function(widget) AceGUI:Release(widget) end)
-- f:SetTitle("AceGUI-3.0 Example")
-- f:SetStatusText("Status Bar")
-- f:SetLayout("Flow")
-- -- Create a button
-- local btn = AceGUI:Create("Button")
-- btn:SetWidth(170)
-- btn:SetText("Button !")
-- btn:SetCallback("OnClick", function() print("Click!") end)
-- -- Add the button to the container
-- f:AddChild(btn)
-- @class file
-- @name AceGUI-3.0
-- @release $Id: AceGUI-3.0.lua 1102 2013-10-25 14:15:23Z nevcairiel $
local ACEGUI_MAJOR, ACEGUI_MINOR = "AceGUI-3.0", 34
local AceGUI, oldminor = LibStub:NewLibrary(ACEGUI_MAJOR, ACEGUI_MINOR)
if not AceGUI then return end -- No upgrade needed
local AceCore = LibStub("AceCore-3.0")
local hooksecurefunc = AceCore.hooksecurefunc
local safecall = AceCore.safecall
-- Lua APIs
local tconcat, tremove, tinsert, tgetn, tsetn = table.concat, table.remove, table.insert, table.getn, table.setn
local pairs, next, type = pairs, next, type
local error, assert, loadstring = error, assert, loadstring
local setmetatable, rawget, rawset = setmetatable, rawget, rawset
local math_max = math.max
local strupper, strfmt = string.upper, string.format
-- WoW APIs
local UIParent = UIParent
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
-- List them here for Mikk's FindGlobals script
-- GLOBALS: geterrorhandler, LibStub
--local con = LibStub("AceConsole-3.0",true)
AceGUI.WidgetRegistry = AceGUI.WidgetRegistry or {}
AceGUI.LayoutRegistry = AceGUI.LayoutRegistry or {}
AceGUI.WidgetBase = AceGUI.WidgetBase or {}
AceGUI.WidgetContainerBase = AceGUI.WidgetContainerBase or {}
AceGUI.WidgetVersions = AceGUI.WidgetVersions or {}
AceGUI.HookedFunctions = AceGUI.HookedFunctions or {}
-- local upvalues
local WidgetRegistry = AceGUI.WidgetRegistry
local LayoutRegistry = AceGUI.LayoutRegistry
local WidgetVersions = AceGUI.WidgetVersions
local HookedFunctions = AceGUI.HookedFunctions
-- Recycling functions
local newWidget, delWidget
do
AceGUI.objPools = AceGUI.objPools or {}
local objPools = AceGUI.objPools
--Returns a new instance, if none are available either returns a new table or calls the given contructor
function newWidget(type)
if not WidgetRegistry[type] then
error("Attempt to instantiate unknown widget type", 2)
end
if not objPools[type] then
objPools[type] = {}
end
local newObj = next(objPools[type])
if not newObj then
newObj = WidgetRegistry[type]()
newObj.AceGUIWidgetVersion = WidgetVersions[type]
else
objPools[type][newObj] = nil
-- if the widget is older then the latest, don't even try to reuse it
-- just forget about it, and grab a new one.
if not newObj.AceGUIWidgetVersion or newObj.AceGUIWidgetVersion < WidgetVersions[type] then
return newWidget(type)
end
end
return newObj
end
-- Releases an instance to the Pool
function delWidget(obj,type)
if not objPools[type] then
objPools[type] = {}
end
if objPools[type][obj] then
error("Attempt to Release Widget that is already released", 2)
end
objPools[type][obj] = true
end
end
-- Ace3v: when a container contains many children, we can only use the variable arguments
local function _fixlevels(parent, ...)
local lv = parent:GetFrameLevel() + 1
for i = 1, tgetn(arg) do
local child = arg[i]
child:SetFrameLevel(lv)
_fixlevels(child, child:GetChildren())
end
end
local function fixlevels(parent)
return _fixlevels(parent, parent:GetChildren())
end
AceGUI.fixlevels = fixlevels
-- Ace3v: attention! this function is recursive
local function _fixstrata(strata, parent, ...)
parent:SetFrameStrata(strata)
for i = 1, tgetn(arg) do
local child = arg[i]
_fixstrata(strata, child, child:GetChildren())
end
end
local function fixstrata(strata, parent)
return _fixstrata(strata, parent, parent:GetChildren())
end
AceGUI.fixstrata = fixstrata
-------------------
-- API Functions --
-------------------
-- Gets a widget Object
--- Create a new Widget of the given type.
-- This function will instantiate a new widget (or use one from the widget pool), and call the
-- OnAcquire function on it, before returning.
-- @param type The type of the widget.
-- @return The newly created widget.
function AceGUI:Create(type)
if WidgetRegistry[type] then
local widget = newWidget(type)
if widget.OnAcquire then
widget:OnAcquire()
else
error(strfmt("Widget type %s doesn't supply an OnAcquire Function", type))
end
-- Set the default Layout ("List")
safecall(widget.SetLayout, 2, widget, "List")
safecall(widget.ResumeLayout, 1, widget)
return widget
end
end
--- Releases a widget Object.
-- This function calls OnRelease on the widget and places it back in the widget pool.
-- Any data on the widget is being erased, and the widget will be hidden.\\
-- If this widget is a Container-Widget, all of its Child-Widgets will be releases as well.
-- @param widget The widget to release
function AceGUI:Release(widget)
safecall(widget.PauseLayout, 1, widget)
widget:Fire("OnRelease")
safecall(widget.ReleaseChildren, 1, widget)
if widget.OnRelease then
widget:OnRelease()
-- else
-- error(strfmt("Widget type %s doesn't supply an OnRelease Function", widget.type))
end
for k in pairs(widget.userdata) do
widget.userdata[k] = nil
end
for k in pairs(widget.events) do
widget.events[k] = nil
end
widget.width = nil
widget.relWidth = nil
widget.height = nil
widget.relHeight = nil
widget.noAutoHeight = nil
widget.frame:ClearAllPoints()
widget.frame:Hide()
widget.frame:SetParent(UIParent)
widget.frame.width = nil
widget.frame.height = nil
if widget.content then
widget.content.width = nil
widget.content.height = nil
end
delWidget(widget, widget.type)
end
-----------
-- Focus --
-----------
--- Called when a widget has taken focus.
-- e.g. Dropdowns opening, Editboxes gaining kb focus
-- @param widget The widget that should be focused
function AceGUI:SetFocus(widget)
if self.FocusedWidget and self.FocusedWidget ~= widget then
safecall(self.FocusedWidget.ClearFocus, 1, self.FocusedWidget)
end
self.FocusedWidget = widget
end
--- Called when something has happened that could cause widgets with focus to drop it
-- e.g. titlebar of a frame being clicked
function AceGUI:ClearFocus()
if self.FocusedWidget then
safecall(self.FocusedWidget.ClearFocus, 1, self.FocusedWidget)
self.FocusedWidget = nil
end
end
-------------
-- Widgets --
-------------
--[[
Widgets must provide the following functions
OnAcquire() - Called when the object is acquired, should set everything to a default hidden state
And the following members
frame - the frame or derivitive object that will be treated as the widget for size and anchoring purposes
type - the type of the object, same as the name given to :RegisterWidget()
Widgets contain a table called userdata, this is a safe place to store data associated with the wigdet
It will be cleared automatically when a widget is released
Placing values directly into a widget object should be avoided
If the Widget can act as a container for other Widgets the following
content - frame or derivitive that children will be anchored to
The Widget can supply the following Optional Members
:OnRelease() - Called when the object is Released, should remove any additional anchors and clear any data
:OnWidthSet(width) - Called when the width of the widget is changed
:OnHeightSet(height) - Called when the height of the widget is changed
Widgets should not use the OnSizeChanged events of thier frame or content members, use these methods instead
AceGUI already sets a handler to the event
:LayoutFinished(width, height) - called after a layout has finished, the width and height will be the width and height of the
area used for controls. These can be nil if the layout used the existing size to layout the controls.
]]
--------------------------
-- Widget Base Template --
--------------------------
do
local WidgetBase = AceGUI.WidgetBase
WidgetBase.SetParent = function(self, parent)
local frame = self.frame
frame:SetParent(nil)
frame:SetParent(parent.content)
self.parent = parent
fixlevels(frame)
end
WidgetBase.SetCallback = function(self, name, func)
if type(func) == "function" then
self.events[name] = func
end
end
WidgetBase.Fire = function(self,name,argc,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
argc = argc or 0
local func = self.events[name]
if func then
local success, ret = safecall(func,argc+3,self,name,argc,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
if success then
return ret
end
end
end
WidgetBase.SetWidth = function(self, width)
self.frame:SetWidth(width)
self.frame.width = width
if self.OnWidthSet then
self:OnWidthSet(width)
end
end
WidgetBase.SetRelativeWidth = function(self, width)
if width <= 0 or width > 1 then
error(":SetRelativeWidth(width): Invalid relative width.", 2)
end
self.relWidth = width
self.width = "relative"
end
WidgetBase.SetHeight = function(self, height)
self.frame:SetHeight(height)
self.frame.height = height
if self.OnHeightSet then
self:OnHeightSet(height)
end
end
--[[ WidgetBase.SetRelativeHeight = function(self, height)
if height <= 0 or height > 1 then
error(":SetRelativeHeight(height): Invalid relative height.", 2)
end
self.relHeight = height
self.height = "relative"
end ]]
WidgetBase.IsVisible = function(self)
return self.frame:IsVisible()
end
WidgetBase.IsShown= function(self)
return self.frame:IsShown()
end
WidgetBase.Release = function(self)
AceGUI:Release(self)
end
WidgetBase.SetPoint = function(self,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
return self.frame:SetPoint(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
end
WidgetBase.ClearAllPoints = function(self)
return self.frame:ClearAllPoints()
end
WidgetBase.GetNumPoints = function(self)
return self.frame:GetNumPoints()
end
WidgetBase.GetPoint = function(self,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
return self.frame:GetPoint(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
end
WidgetBase.GetUserDataTable = function(self)
return self.userdata
end
WidgetBase.SetUserData = function(self, key, value)
self.userdata[key] = value
end
WidgetBase.GetUserData = function(self, key)
return self.userdata[key]
end
WidgetBase.IsFullHeight = function(self)
return self.height == "fill"
end
WidgetBase.SetFullHeight = function(self, isFull)
if isFull then
self.height = "fill"
else
self.height = nil
end
end
WidgetBase.IsFullWidth = function(self)
return self.width == "fill"
end
WidgetBase.SetFullWidth = function(self, isFull)
if isFull then
self.width = "fill"
else
self.width = nil
end
end
-- local function LayoutOnUpdate(this)
-- this:SetScript("OnUpdate",nil)
-- this.obj:PerformLayout()
-- end
local WidgetContainerBase = AceGUI.WidgetContainerBase
WidgetContainerBase.PauseLayout = function(self)
self.LayoutPaused = true
end
WidgetContainerBase.ResumeLayout = function(self)
self.LayoutPaused = nil
end
WidgetContainerBase.PerformLayout = function(self)
if self.LayoutPaused then
return
end
safecall(self.LayoutFunc, 2, self.content, self.children)
end
--call this function to layout, makes sure layed out objects get a frame to get sizes etc
WidgetContainerBase.DoLayout = function(self)
self:PerformLayout()
-- if not self.parent then
-- self.frame:SetScript("OnUpdate", LayoutOnUpdate)
-- end
end
WidgetContainerBase.AddChild = function(self, child, beforeWidget)
if beforeWidget then
local siblingIndex = 1
for _, widget in pairs(self.children) do
if widget == beforeWidget then
break
end
siblingIndex = siblingIndex + 1
end
tinsert(self.children, siblingIndex, child)
else
tinsert(self.children, child)
end
child:SetParent(self)
child.frame:Show()
self:DoLayout()
end
do
local args = {nil,nil,nil,nil,nil,nil,nil,nil,nil,nil}
WidgetContainerBase.AddChildren = function(self,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
args[1] = a1
args[2] = a2
args[3] = a3
args[4] = a4
args[5] = a5
args[6] = a6
args[7] = a7
args[8] = a8
args[9] = a9
args[10] = a10
for i = 1,10 do
local child = args[i]
arg[i] = nil
if not child then break end
tinsert(self.children, child)
child:SetParent(self)
child.frame:Show()
end
self:DoLayout()
end
end -- WidgetContainerBase.AddChildren
WidgetContainerBase.ReleaseChildren = function(self)
local children = self.children
for i = 1,tgetn(children) do
AceGUI:Release(tremove(children))
end
end
WidgetContainerBase.SetParent = function(self, parent)
WidgetBase.SetParent(self, parent)
local lv = self.frame:GetFrameLevel()
self.content:SetFrameLevel(lv+1)
local children = self.children
for i = 1,tgetn(children) do
local child = children[i]
child:SetParent(self)
end
end
WidgetContainerBase.SetLayout = function(self, Layout)
self.LayoutFunc = AceGUI:GetLayout(Layout)
end
WidgetContainerBase.SetAutoAdjustHeight = function(self, adjust)
if adjust then
self.noAutoHeight = nil
else
self.noAutoHeight = true
end
end
local function FrameResize()
local self = this.obj
if this:GetWidth() and this:GetHeight() then
if self.OnWidthSet then
self:OnWidthSet(this:GetWidth())
end
if self.OnHeightSet then
self:OnHeightSet(this:GetHeight())
end
end
end
local function ContentResize()
if this:GetWidth() and this:GetHeight() then
this.width = this:GetWidth()
this.height = this:GetHeight()
this.obj:DoLayout()
end
end
setmetatable(WidgetContainerBase, {__index=WidgetBase})
--One of these function should be called on each Widget Instance as part of its creation process
--- Register a widget-class as a container for newly created widgets.
-- @param widget The widget class
function AceGUI:RegisterAsContainer(widget)
widget.children = {}
widget.userdata = {}
widget.events = {}
widget.base = WidgetContainerBase
widget.content.obj = widget
widget.frame.obj = widget
widget.content:SetScript("OnSizeChanged", ContentResize)
widget.frame:SetScript("OnSizeChanged", FrameResize)
setmetatable(widget, {__index = WidgetContainerBase})
widget:SetLayout("List")
return widget
end
--- Register a widget-class as a widget.
-- @param widget The widget class
function AceGUI:RegisterAsWidget(widget)
widget.userdata = {}
widget.events = {}
widget.base = WidgetBase
widget.frame.obj = widget
widget.frame:SetScript("OnSizeChanged", FrameResize)
setmetatable(widget, {__index = WidgetBase})
return widget
end
end
------------------
-- Widget API --
------------------
--- Registers a widget Constructor, this function returns a new instance of the Widget
-- @param Name The name of the widget
-- @param Constructor The widget constructor function
-- @param Version The version of the widget
function AceGUI:RegisterWidgetType(Name, Constructor, Version)
assert(type(Constructor) == "function")
assert(type(Version) == "number")
local oldVersion = WidgetVersions[Name]
if oldVersion and oldVersion >= Version then return end
WidgetVersions[Name] = Version
WidgetRegistry[Name] = Constructor
end
--- Registers a Layout Function
-- @param Name The name of the layout
-- @param LayoutFunc Reference to the layout function
function AceGUI:RegisterLayout(Name, LayoutFunc)
assert(type(LayoutFunc) == "function")
if type(Name) == "string" then
Name = string.upper(Name)
end
LayoutRegistry[Name] = LayoutFunc
end
--- Get a Layout Function from the registry
-- @param Name The name of the layout
function AceGUI:GetLayout(Name)
if type(Name) == "string" then
Name = strupper(Name)
end
return LayoutRegistry[Name]
end
AceGUI.counts = AceGUI.counts or {}
--- A type-based counter to count the number of widgets created.
-- This is used by widgets that require a named frame, e.g. when a Blizzard
-- Template requires it.
-- @param type The widget type
function AceGUI:GetNextWidgetNum(type)
if not self.counts[type] then
self.counts[type] = 0
end
self.counts[type] = self.counts[type] + 1
return self.counts[type]
end
--- Return the number of created widgets for this type.
-- In contrast to GetNextWidgetNum, the number is not incremented.
-- @param type The widget type
function AceGUI:GetWidgetCount(type)
return self.counts[type] or 0
end
--- Return the version of the currently registered widget type.
-- @param type The widget type
function AceGUI:GetWidgetVersion(type)
return WidgetVersions[type]
end
-------------
-- Layouts --
-------------
--[[
A Layout is a func that takes 2 parameters
content - the frame that widgets will be placed inside
children - a table containing the widgets to layout
]]
-- Very simple Layout, Children are stacked on top of each other down the left side
AceGUI:RegisterLayout("List",
function(content, children)
local height = 0
local width = content.width or content:GetWidth() or 0
for i = 1, tgetn(children) do
local child = children[i]
local frame = child.frame
frame:ClearAllPoints()
frame:Show()
if i == 1 then
frame:SetPoint("TOPLEFT", content)
else
frame:SetPoint("TOPLEFT", children[i-1].frame, "BOTTOMLEFT")
end
if child.width == "fill" then
child:SetWidth(width)
frame:SetPoint("RIGHT", content)
if child.DoLayout then
child:DoLayout()
end
elseif child.width == "relative" then
child:SetWidth(width * child.relWidth)
if child.DoLayout then
child:DoLayout()
end
end
height = height + (frame.height or frame:GetHeight() or 0)
end
safecall(content.obj.LayoutFinished, 3, content.obj, nil, height)
end)
-- A single control fills the whole content area
AceGUI:RegisterLayout("Fill",
function(content, children)
if children[1] then
children[1]:SetWidth(content:GetWidth() or 0)
children[1]:SetHeight(content:GetHeight() or 0)
children[1].frame:SetAllPoints(content)
children[1].frame:Show()
safecall(content.obj.LayoutFinished, 3, content.obj, nil, children[1].frame:GetHeight())
end
end)
-- Ace3v: currently only a1 used
local layoutrecursionblock = nil
local function safelayoutcall(object, func, a1)
layoutrecursionblock = true
object[func](object, a1)
layoutrecursionblock = nil
end
AceGUI:RegisterLayout("Flow",
function(content, children)
if layoutrecursionblock then return end
--used height so far
local height = 0
--width used in the current row
local usedwidth = 0
--height of the current row
local rowheight = 0
local rowoffset = 0
local lastrowoffset
local width = content.width or content:GetWidth() or 0
--control at the start of the row
local rowstart
local rowstartoffset
local lastrowstart
local isfullheight
local frameoffset
local lastframeoffset
local oversize
for i = 1, tgetn(children) do
local child = children[i]
oversize = nil
local frame = child.frame
local frameheight = frame.height or frame:GetHeight() or 0
local framewidth = frame.width or frame:GetWidth() or 0
lastframeoffset = frameoffset
-- HACK: Why did we set a frameoffset of (frameheight / 2) ?
-- That was moving all widgets half the widgets size down, is that intended?
-- Actually, it seems to be neccessary for many cases, we'll leave it in for now.
-- If widgets seem to anchor weirdly with this, provide a valid alignoffset for them.
-- TODO: Investigate moar!
frameoffset = child.alignoffset or (frameheight / 2)
if child.width == "relative" then
framewidth = width * child.relWidth
end
frame:Show()
frame:ClearAllPoints()
if i == 1 then
-- anchor the first control to the top left
frame:SetPoint("TOPLEFT", content)
rowheight = frameheight
rowoffset = frameoffset
rowstart = frame
rowstartoffset = frameoffset
usedwidth = framewidth
if usedwidth > width then
oversize = true
end
else
-- if there isn't available width for the control start a new row
-- if a control is "fill" it will be on a row of its own full width
if usedwidth == 0 or ((framewidth) + usedwidth > width) or child.width == "fill" then
if isfullheight then
-- a previous row has already filled the entire height, there's nothing we can usefully do anymore
-- (maybe error/warn about this?)
break
end
--anchor the previous row, we will now know its height and offset
rowstart:SetPoint("TOPLEFT", content, "TOPLEFT", 0, -(height + (rowoffset - rowstartoffset) + 3))
height = height + rowheight + 3
--save this as the rowstart so we can anchor it after the row is complete and we have the max height and offset of controls in it
rowstart = frame
rowstartoffset = frameoffset
rowheight = frameheight
rowoffset = frameoffset
usedwidth = framewidth
if usedwidth > width then
oversize = true
end
-- put the control on the current row, adding it to the width and checking if the height needs to be increased
else
--handles cases where the new height is higher than either control because of the offsets
--math.max(rowheight-rowoffset+frameoffset, frameheight-frameoffset+rowoffset)
--offset is always the larger of the two offsets
rowoffset = math_max(rowoffset, frameoffset)
rowheight = math_max(rowheight, rowoffset + (frameheight / 2))
frame:SetPoint("TOPLEFT", children[i-1].frame, "TOPRIGHT", 0, frameoffset - lastframeoffset)
usedwidth = framewidth + usedwidth
end
end
if child.width == "fill" then
safelayoutcall(child, "SetWidth", width)
frame:SetPoint("RIGHT", content)
usedwidth = 0
rowstart = frame
rowstartoffset = frameoffset
if child.DoLayout then
child:DoLayout()
end
rowheight = frame.height or frame:GetHeight() or 0
rowoffset = child.alignoffset or (rowheight / 2)
rowstartoffset = rowoffset
elseif child.width == "relative" then
safelayoutcall(child, "SetWidth", width * child.relWidth)
if child.DoLayout then
child:DoLayout()
end
elseif oversize then
if width > 1 then
frame:SetPoint("RIGHT", content)
end
end
if child.height == "fill" then
frame:SetPoint("BOTTOM", content)
isfullheight = true
end
end
--anchor the last row, if its full height needs a special case since its height has just been changed by the anchor
if isfullheight then
rowstart:SetPoint("TOPLEFT", content, "TOPLEFT", 0, -height)
elseif rowstart then
rowstart:SetPoint("TOPLEFT", content, "TOPLEFT", 0, -(height + (rowoffset - rowstartoffset) + 3))
end
height = height + rowheight + 3
safecall(content.obj.LayoutFinished, 3, content.obj, nil, height)
end)
+4
View File
@@ -0,0 +1,4 @@
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
..\FrameXML\UI.xsd">
<Script file="AceGUI-3.0.lua"/>
</Ui>
@@ -0,0 +1,138 @@
--[[-----------------------------------------------------------------------------
BlizOptionsGroup Container
Simple container widget for the integration of AceGUI into the Blizzard Interface Options
-------------------------------------------------------------------------------]]
local Type, Version = "BlizOptionsGroup", 21
local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
-- Lua APIs
local pairs = pairs
-- WoW APIs
local CreateFrame = CreateFrame
--[[-----------------------------------------------------------------------------
Scripts
-------------------------------------------------------------------------------]]
local function OnShow(frame)
frame.obj:Fire("OnShow")
end
local function OnHide(frame)
frame.obj:Fire("OnHide")
end
--[[-----------------------------------------------------------------------------
Support functions
-------------------------------------------------------------------------------]]
local function okay(frame)
frame.obj:Fire("okay")
end
local function cancel(frame)
frame.obj:Fire("cancel")
end
local function default(frame)
frame.obj:Fire("default")
end
local function refresh(frame)
frame.obj:Fire("refresh")
end
--[[-----------------------------------------------------------------------------
Methods
-------------------------------------------------------------------------------]]
local methods = {
["OnAcquire"] = function(self)
self:SetName()
self:SetTitle()
end,
-- ["OnRelease"] = nil,
["OnWidthSet"] = function(self, width)
local content = self.content
local contentwidth = width - 63
if contentwidth < 0 then
contentwidth = 0
end
content:SetWidth(contentwidth)
content.width = contentwidth
end,
["OnHeightSet"] = function(self, height)
local content = self.content
local contentheight = height - 26
if contentheight < 0 then
contentheight = 0
end
content:SetHeight(contentheight)
content.height = contentheight
end,
["SetName"] = function(self, name, parent)
self.frame.name = name
self.frame.parent = parent
end,
["SetTitle"] = function(self, title)
local content = self.content
content:ClearAllPoints()
if not title or title == "" then
content:SetPoint("TOPLEFT", 10, -10)
self.label:SetText("")
else
content:SetPoint("TOPLEFT", 10, -40)
self.label:SetText(title)
end
content:SetPoint("BOTTOMRIGHT", -10, 10)
end
}
--[[-----------------------------------------------------------------------------
Constructor
-------------------------------------------------------------------------------]]
local function Constructor()
local frame = CreateFrame("Frame")
frame:Hide()
-- support functions for the Blizzard Interface Options
frame.okay = okay
frame.cancel = cancel
frame.default = default
frame.refresh = refresh
frame:SetScript("OnHide", OnHide)
frame:SetScript("OnShow", OnShow)
local label = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
label:SetPoint("TOPLEFT", 10, -15)
label:SetPoint("BOTTOMRIGHT", frame, "TOPRIGHT", 10, -45)
label:SetJustifyH("LEFT")
label:SetJustifyV("TOP")
--Container Support
local content = CreateFrame("Frame", nil, frame)
content:SetPoint("TOPLEFT", 10, -10)
content:SetPoint("BOTTOMRIGHT", -10, 10)
local widget = {
label = label,
frame = frame,
content = content,
type = Type
}
for method, func in pairs(methods) do
widget[method] = func
end
return AceGUI:RegisterAsContainer(widget)
end
AceGUI:RegisterWidgetType(Type, Constructor, Version)
@@ -0,0 +1,157 @@
--[[-----------------------------------------------------------------------------
DropdownGroup Container
Container controlled by a dropdown on the top.
-------------------------------------------------------------------------------]]
local Type, Version = "DropdownGroup", 21
local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
-- Lua APIs
local assert, pairs, type = assert, pairs, type
-- WoW APIs
local CreateFrame = CreateFrame
--[[-----------------------------------------------------------------------------
Scripts
-------------------------------------------------------------------------------]]
local function SelectedGroup(self, event, _, value)
local group = self.parentgroup
local status = group.status or group.localstatus
status.selected = value
self.parentgroup:Fire("OnGroupSelected", 1, value)
end
--[[-----------------------------------------------------------------------------
Methods
-------------------------------------------------------------------------------]]
local methods = {
["OnAcquire"] = function(self)
self.dropdown:SetText("")
self:SetDropdownWidth(200)
self:SetTitle("")
end,
["OnRelease"] = function(self)
self.dropdown.list = nil
self.status = nil
for k in pairs(self.localstatus) do
self.localstatus[k] = nil
end
end,
["SetTitle"] = function(self, title)
self.titletext:SetText(title)
self.dropdown.frame:ClearAllPoints()
if title and title ~= "" then
self.dropdown.frame:SetPoint("TOPRIGHT", -2, 0)
else
self.dropdown.frame:SetPoint("TOPLEFT", -1, 0)
end
end,
["SetGroupList"] = function(self,list,order)
self.dropdown:SetList(list,order)
end,
["SetStatusTable"] = function(self, status)
assert(type(status) == "table")
self.status = status
end,
["SetGroup"] = function(self,group)
self.dropdown:SetValue(group)
local status = self.status or self.localstatus
status.selected = group
self:Fire("OnGroupSelected", 1, group)
end,
["OnWidthSet"] = function(self, width)
local content = self.content
local contentwidth = width - 26
if contentwidth < 0 then
contentwidth = 0
end
content:SetWidth(contentwidth)
content.width = contentwidth
end,
["OnHeightSet"] = function(self, height)
local content = self.content
local contentheight = height - 63
if contentheight < 0 then
contentheight = 0
end
content:SetHeight(contentheight)
content.height = contentheight
end,
["LayoutFinished"] = function(self, width, height)
self:SetHeight((height or 0) + 63)
end,
["SetDropdownWidth"] = function(self, width)
self.dropdown:SetWidth(width)
end
}
--[[-----------------------------------------------------------------------------
Constructor
-------------------------------------------------------------------------------]]
local PaneBackdrop = {
bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
tile = true, tileSize = 16, edgeSize = 16,
insets = { left = 3, right = 3, top = 5, bottom = 3 }
}
local function Constructor()
local frame = CreateFrame("Frame")
frame:SetHeight(100)
frame:SetWidth(100)
frame:SetFrameStrata("FULLSCREEN_DIALOG")
local titletext = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
titletext:SetPoint("TOPLEFT", 4, -5)
titletext:SetPoint("TOPRIGHT", -4, -5)
titletext:SetJustifyH("LEFT")
titletext:SetHeight(18)
local dropdown = AceGUI:Create("Dropdown")
dropdown.frame:SetParent(frame)
dropdown.frame:SetFrameLevel(dropdown.frame:GetFrameLevel() + 2)
dropdown:SetCallback("OnValueChanged", SelectedGroup)
dropdown.frame:SetPoint("TOPLEFT", -1, 0)
dropdown.frame:Show()
dropdown:SetLabel("")
local border = CreateFrame("Frame", nil, frame)
border:SetPoint("TOPLEFT", 0, -26)
border:SetPoint("BOTTOMRIGHT", 0, 3)
border:SetBackdrop(PaneBackdrop)
border:SetBackdropColor(0.1,0.1,0.1,0.5)
border:SetBackdropBorderColor(0.4,0.4,0.4)
--Container Support
local content = CreateFrame("Frame", nil, border)
content:SetPoint("TOPLEFT", 10, -10)
content:SetPoint("BOTTOMRIGHT", -10, 10)
local widget = {
frame = frame,
localstatus = {},
titletext = titletext,
dropdown = dropdown,
border = border,
content = content,
type = Type
}
for method, func in pairs(methods) do
widget[method] = func
end
dropdown.parentgroup = widget
return AceGUI:RegisterAsContainer(widget)
end
AceGUI:RegisterWidgetType(Type, Constructor, Version)
@@ -0,0 +1,303 @@
--[[-----------------------------------------------------------------------------
Frame Container
-------------------------------------------------------------------------------]]
local Type, Version = "Frame", 24
local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
local AceCore = LibStub("AceCore-3.0")
local wipe = AceCore.wipe
-- Lua APIs
local pairs, assert, type = pairs, assert, type
-- WoW APIs
local PlaySound = PlaySound
local CreateFrame, UIParent = CreateFrame, UIParent
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
-- List them here for Mikk's FindGlobals script
-- GLOBALS: CLOSE
--[[-----------------------------------------------------------------------------
Scripts
-------------------------------------------------------------------------------]]
local function Button_OnClick()
PlaySound("gsTitleOptionExit")
this.obj:Hide()
end
local function Frame_OnClose()
this.obj:Fire("OnClose")
end
local function Frame_OnMouseDown()
AceGUI:ClearFocus()
end
local function Title_OnMouseDown()
this:GetParent():StartMoving()
AceGUI:ClearFocus()
end
local function MoverSizer_OnMouseUp()
local frame = this:GetParent()
frame:StopMovingOrSizing()
local self = frame.obj
local status = self.status or self.localstatus
status.width = frame:GetWidth()
status.height = frame:GetHeight()
status.top = frame:GetTop()
status.left = frame:GetLeft()
end
local function SizerSE_OnMouseDown()
this:GetParent():StartSizing("BOTTOMRIGHT")
AceGUI:ClearFocus()
end
local function SizerS_OnMouseDown()
this:GetParent():StartSizing("BOTTOM")
AceGUI:ClearFocus()
end
local function SizerE_OnMouseDown()
this:GetParent():StartSizing("RIGHT")
AceGUI:ClearFocus()
end
local function StatusBar_OnEnter()
this.obj:Fire("OnEnterStatusBar")
end
local function StatusBar_OnLeave()
this.obj:Fire("OnLeaveStatusBar")
end
--[[-----------------------------------------------------------------------------
Methods
-------------------------------------------------------------------------------]]
local methods = {
["OnAcquire"] = function(self)
self.frame:SetParent(UIParent)
self.frame:SetFrameStrata("FULLSCREEN_DIALOG")
self:SetTitle()
self:SetStatusText()
self:ApplyStatus()
self:Show()
self:EnableResize(true)
end,
["OnRelease"] = function(self)
self.status = nil
wipe(self.localstatus)
end,
["OnWidthSet"] = function(self, width)
local content = self.content
local contentwidth = width - 34
if contentwidth < 0 then
contentwidth = 0
end
content:SetWidth(contentwidth)
content.width = contentwidth
end,
["OnHeightSet"] = function(self, height)
local content = self.content
local contentheight = height - 57
if contentheight < 0 then
contentheight = 0
end
content:SetHeight(contentheight)
content.height = contentheight
end,
["SetTitle"] = function(self, title)
self.titletext:SetText(title)
self.titlebg:SetWidth((self.titletext:GetWidth() or 0) + 10)
end,
["SetStatusText"] = function(self, text)
self.statustext:SetText(text)
end,
["Hide"] = function(self)
self.frame:Hide()
end,
["Show"] = function(self)
self.frame:Show()
end,
["EnableResize"] = function(self, state)
local func = state and "Show" or "Hide"
self.sizer_se[func](self.sizer_se)
self.sizer_s[func](self.sizer_s)
self.sizer_e[func](self.sizer_e)
end,
-- called to set an external table to store status in
["SetStatusTable"] = function(self, status)
assert(type(status) == "table")
self.status = status
self:ApplyStatus()
end,
["ApplyStatus"] = function(self)
local status = self.status or self.localstatus
local frame = self.frame
self:SetWidth(status.width or 700)
self:SetHeight(status.height or 500)
frame:ClearAllPoints()
if status.top and status.left then
frame:SetPoint("TOP", UIParent, "BOTTOM", 0, status.top)
frame:SetPoint("LEFT", UIParent, "LEFT", status.left, 0)
else
frame:SetPoint("CENTER", UIParent)
end
end
}
--[[-----------------------------------------------------------------------------
Constructor
-------------------------------------------------------------------------------]]
local FrameBackdrop = {
bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background",
edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
tile = true, tileSize = 32, edgeSize = 32,
insets = { left = 8, right = 8, top = 8, bottom = 8 }
}
local PaneBackdrop = {
bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
tile = true, tileSize = 16, edgeSize = 16,
insets = { left = 3, right = 3, top = 5, bottom = 3 }
}
local function Constructor()
local frame = CreateFrame("Frame", nil, UIParent)
frame:Hide()
frame:EnableMouse(true)
frame:SetMovable(true)
frame:SetResizable(true)
frame:SetFrameStrata("FULLSCREEN_DIALOG")
frame:SetBackdrop(FrameBackdrop)
frame:SetBackdropColor(0, 0, 0, 1)
frame:SetMinResize(400, 200)
frame:SetToplevel(true)
frame:SetScript("OnHide", Frame_OnClose)
frame:SetScript("OnMouseDown", Frame_OnMouseDown)
local closebutton = CreateFrame("Button", nil, frame, "UIPanelButtonTemplate")
closebutton:SetScript("OnClick", Button_OnClick)
closebutton:SetPoint("BOTTOMRIGHT", -27, 17)
closebutton:SetHeight(20)
closebutton:SetWidth(100)
closebutton:SetText(CLOSE)
local statusbg = CreateFrame("Button", nil, frame)
statusbg:SetPoint("BOTTOMLEFT", 15, 15)
statusbg:SetPoint("BOTTOMRIGHT", -132, 15)
statusbg:SetHeight(24)
statusbg:SetBackdrop(PaneBackdrop)
statusbg:SetBackdropColor(0.1,0.1,0.1)
statusbg:SetBackdropBorderColor(0.4,0.4,0.4)
statusbg:SetScript("OnEnter", StatusBar_OnEnter)
statusbg:SetScript("OnLeave", StatusBar_OnLeave)
local statustext = statusbg:CreateFontString(nil, "OVERLAY", "GameFontNormal")
statustext:SetPoint("TOPLEFT", 7, -2)
statustext:SetPoint("BOTTOMRIGHT", -7, 2)
statustext:SetHeight(20)
statustext:SetJustifyH("LEFT")
statustext:SetText("")
local titlebg = frame:CreateTexture(nil, "OVERLAY")
titlebg:SetTexture("Interface\\DialogFrame\\UI-DialogBox-Header")
titlebg:SetTexCoord(0.31, 0.67, 0, 0.63)
titlebg:SetPoint("TOP", 0, 12)
titlebg:SetWidth(100)
titlebg:SetHeight(40)
local title = CreateFrame("Frame", nil, frame)
title:EnableMouse(true)
title:SetScript("OnMouseDown", Title_OnMouseDown)
title:SetScript("OnMouseUp", MoverSizer_OnMouseUp)
title:SetAllPoints(titlebg)
local titletext = title:CreateFontString(nil, "OVERLAY", "GameFontNormal")
titletext:SetPoint("TOP", titlebg, "TOP", 0, -14)
local titlebg_l = frame:CreateTexture(nil, "OVERLAY")
titlebg_l:SetTexture("Interface\\DialogFrame\\UI-DialogBox-Header")
titlebg_l:SetTexCoord(0.21, 0.31, 0, 0.63)
titlebg_l:SetPoint("RIGHT", titlebg, "LEFT")
titlebg_l:SetWidth(30)
titlebg_l:SetHeight(40)
local titlebg_r = frame:CreateTexture(nil, "OVERLAY")
titlebg_r:SetTexture("Interface\\DialogFrame\\UI-DialogBox-Header")
titlebg_r:SetTexCoord(0.67, 0.77, 0, 0.63)
titlebg_r:SetPoint("LEFT", titlebg, "RIGHT")
titlebg_r:SetWidth(30)
titlebg_r:SetHeight(40)
-- bottom right sizer
local sizer_se = CreateFrame("Frame", nil, frame)
sizer_se:SetPoint("BOTTOMRIGHT", 0, 0)
sizer_se:SetWidth(25)
sizer_se:SetHeight(25)
sizer_se:EnableMouse()
sizer_se:SetScript("OnMouseDown",SizerSE_OnMouseDown)
sizer_se:SetScript("OnMouseUp", MoverSizer_OnMouseUp)
local line1 = sizer_se:CreateTexture(nil, "BACKGROUND")
line1:SetPoint("BOTTOMRIGHT", -10, 10)
line1:SetTexture("Interface\\Cursor\\Item")
line1:SetTexCoord(1, 0, 1, 0)
local sizer_s = CreateFrame("Frame", nil, frame)
sizer_s:SetPoint("BOTTOMRIGHT", -25, 0)
sizer_s:SetPoint("BOTTOMLEFT", 0, 0)
sizer_s:SetHeight(25)
sizer_s:EnableMouse(true)
sizer_s:SetScript("OnMouseDown", SizerS_OnMouseDown)
sizer_s:SetScript("OnMouseUp", MoverSizer_OnMouseUp)
local sizer_e = CreateFrame("Frame", nil, frame)
sizer_e:SetPoint("BOTTOMRIGHT", 0, 25)
sizer_e:SetPoint("TOPRIGHT", 0, 0)
sizer_e:SetWidth(25)
sizer_e:EnableMouse(true)
sizer_e:SetScript("OnMouseDown", SizerE_OnMouseDown)
sizer_e:SetScript("OnMouseUp", MoverSizer_OnMouseUp)
--Container Support
local content = CreateFrame("Frame", nil, frame)
content:SetPoint("TOPLEFT", 17, -27)
content:SetPoint("BOTTOMRIGHT", -17, 40)
local widget = {
localstatus = {},
titletext = titletext,
statustext = statustext,
titlebg = titlebg,
sizer_se = sizer_se,
sizer_s = sizer_s,
sizer_e = sizer_e,
content = content,
frame = frame,
type = Type
}
for method, func in pairs(methods) do
widget[method] = func
end
closebutton.obj, statusbg.obj = widget, widget
return AceGUI:RegisterAsContainer(widget)
end
AceGUI:RegisterWidgetType(Type, Constructor, Version)
@@ -0,0 +1,103 @@
--[[-----------------------------------------------------------------------------
InlineGroup Container
Simple container widget that creates a visible "box" with an optional title.
-------------------------------------------------------------------------------]]
local Type, Version = "InlineGroup", 21
local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
-- Lua APIs
local pairs = pairs
-- WoW APIs
local CreateFrame, UIParent = CreateFrame, UIParent
--[[-----------------------------------------------------------------------------
Methods
-------------------------------------------------------------------------------]]
local methods = {
["OnAcquire"] = function(self)
self:SetWidth(300)
self:SetHeight(100)
self:SetTitle("")
end,
-- ["OnRelease"] = nil,
["SetTitle"] = function(self,title)
self.titletext:SetText(title)
end,
["LayoutFinished"] = function(self, width, height)
if self.noAutoHeight then return end
self:SetHeight((height or 0) + 40)
end,
["OnWidthSet"] = function(self, width)
local content = self.content
local contentwidth = width - 20
if contentwidth < 0 then
contentwidth = 0
end
content:SetWidth(contentwidth)
content.width = contentwidth
end,
["OnHeightSet"] = function(self, height)
local content = self.content
local contentheight = height - 20
if contentheight < 0 then
contentheight = 0
end
content:SetHeight(contentheight)
content.height = contentheight
end
}
--[[-----------------------------------------------------------------------------
Constructor
-------------------------------------------------------------------------------]]
local PaneBackdrop = {
bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
tile = true, tileSize = 16, edgeSize = 16,
insets = { left = 3, right = 3, top = 5, bottom = 3 }
}
local function Constructor()
local frame = CreateFrame("Frame", nil, UIParent)
frame:SetFrameStrata("FULLSCREEN_DIALOG")
local titletext = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
titletext:SetPoint("TOPLEFT", 14, 0)
titletext:SetPoint("TOPRIGHT", -14, 0)
titletext:SetJustifyH("LEFT")
titletext:SetHeight(18)
local border = CreateFrame("Frame", nil, frame)
border:SetPoint("TOPLEFT", 0, -17)
border:SetPoint("BOTTOMRIGHT", -1, 3)
border:SetBackdrop(PaneBackdrop)
border:SetBackdropColor(0.1, 0.1, 0.1, 0.5)
border:SetBackdropBorderColor(0.4, 0.4, 0.4)
--Container Support
local content = CreateFrame("Frame", nil, border)
content:SetPoint("TOPLEFT", 10, -10)
content:SetPoint("BOTTOMRIGHT", -10, 10)
local widget = {
frame = frame,
content = content,
titletext = titletext,
type = Type
}
for method, func in pairs(methods) do
widget[method] = func
end
return AceGUI:RegisterAsContainer(widget)
end
AceGUI:RegisterWidgetType(Type, Constructor, Version)
@@ -0,0 +1,213 @@
--[[-----------------------------------------------------------------------------
ScrollFrame Container
Plain container that scrolls its content and doesn't grow in height.
-------------------------------------------------------------------------------]]
local Type, Version = "ScrollFrame", 24
local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
local fixlevels = AceGUI.fixlevels
-- Lua APIs
local pairs, assert, type = pairs, assert, type
local min, max, floor, abs = math.min, math.max, math.floor, math.abs
local format = string.format
-- WoW APIs
local CreateFrame, UIParent = CreateFrame, UIParent
--[[-----------------------------------------------------------------------------
Support functions
-------------------------------------------------------------------------------]]
local function FixScrollOnUpdate()
this:SetScript("OnUpdate", nil)
this.obj:FixScroll()
end
--[[-----------------------------------------------------------------------------
Scripts
-------------------------------------------------------------------------------]]
local function ScrollFrame_OnMouseWheel()
this.obj:MoveScroll(arg1)
end
local function ScrollFrame_OnSizeChanged()
this:SetScript("OnUpdate", FixScrollOnUpdate)
end
local function ScrollBar_OnScrollValueChanged()
this.obj:SetScroll(arg1)
end
--[[-----------------------------------------------------------------------------
Methods
-------------------------------------------------------------------------------]]
local methods = {
["OnAcquire"] = function(self)
self:SetScroll(0)
self.scrollframe:SetScript("OnUpdate", FixScrollOnUpdate)
end,
["OnRelease"] = function(self)
self.status = nil
for k in pairs(self.localstatus) do
self.localstatus[k] = nil
end
self.scrollframe:SetPoint("BOTTOMRIGHT",0,0)
self.scrollbar:Hide()
self.scrollBarShown = nil
self.content.height, self.content.width = nil, nil
end,
["SetScroll"] = function(self, value)
local status = self.status or self.localstatus
local viewheight = self.scrollframe:GetHeight()
local height = self.content:GetHeight()
local offset
if viewheight > height then
offset = 0
else
offset = floor((height - viewheight) / 1000.0 * value)
end
self.content:ClearAllPoints()
self.content:SetPoint("TOPLEFT", 0, offset)
self.content:SetPoint("TOPRIGHT", 0, offset)
status.offset = offset
status.scrollvalue = value
end,
["MoveScroll"] = function(self, value)
local status = self.status or self.localstatus
local height, viewheight = self.scrollframe:GetHeight(), self.content:GetHeight()
if self.scrollBarShown then
local diff = height - viewheight
local delta = 1
if value < 0 then
delta = -1
end
self.scrollbar:SetValue(min(max(status.scrollvalue + delta*(1000/(diff/45)),0), 1000))
end
end,
["FixScroll"] = function(self)
if self.updateLock then return end
self.updateLock = true
local status = self.status or self.localstatus
local scrollframe, content, scrollbar = self.scrollframe, self.content, self.scrollbar
local viewheight, height = self.scrollframe:GetHeight(), self.content:GetHeight()
local offset = status.offset or 0
local curvalue = scrollbar:GetValue()
-- Give us a margin of error of 2 pixels to stop some conditions that i would blame on floating point inaccuracys
-- No-one is going to miss 2 pixels at the bottom of the frame, anyhow!
if height < viewheight + 2 then
if self.scrollBarShown then
self.scrollBarShown = nil
scrollbar:Hide()
scrollbar:SetValue(0)
scrollframe:SetPoint("BOTTOMRIGHT",0,0)
self:DoLayout()
end
offset = 0
else
if not self.scrollBarShown then
self.scrollBarShown = true
scrollbar:Show()
scrollframe:SetPoint("BOTTOMRIGHT", -20, 0)
self:DoLayout()
end
local value = (offset / (height - viewheight) * 1000)
if value > 1000 then
value = 1000
offset = height - viewheight
end
scrollbar:SetValue(value)
self:SetScroll(value)
end
status.offset = offset
scrollframe:SetScrollChild(content)
content:ClearAllPoints()
content:SetPoint("TOPLEFT", 0, offset)
content:SetPoint("TOPRIGHT", 0, offset)
self.updateLock = nil
end,
["LayoutFinished"] = function(self, width, height)
self.content:SetHeight(height or 0 + 20)
self.scrollframe:SetScript("OnUpdate", FixScrollOnUpdate)
end,
["SetStatusTable"] = function(self, status)
assert(type(status) == "table")
self.status = status
if not status.scrollvalue then
status.scrollvalue = 0
end
end,
["OnWidthSet"] = function(self, width)
local content = self.content
content.width = width
end,
["OnHeightSet"] = function(self, height)
local content = self.content
content.height = height
end
}
--[[-----------------------------------------------------------------------------
Constructor
-------------------------------------------------------------------------------]]
local function Constructor()
local frame = CreateFrame("Frame", nil, UIParent)
local num = AceGUI:GetNextWidgetNum(Type)
local scrollframe = CreateFrame("ScrollFrame", nil, frame)
scrollframe:SetPoint("TOPLEFT",0,0)
scrollframe:SetPoint("BOTTOMRIGHT",0,0)
scrollframe:EnableMouseWheel(true)
scrollframe:SetScript("OnMouseWheel", ScrollFrame_OnMouseWheel)
scrollframe:SetScript("OnSizeChanged", ScrollFrame_OnSizeChanged)
local scrollbar = CreateFrame("Slider", format("AceConfigDialogScrollFrame%dScrollBar", num), scrollframe, "UIPanelScrollBarTemplate")
scrollbar:SetPoint("TOPLEFT", scrollframe, "TOPRIGHT", 4, -16)
scrollbar:SetPoint("BOTTOMLEFT", scrollframe, "BOTTOMRIGHT", 4, 16)
scrollbar:SetMinMaxValues(0, 1000)
scrollbar:SetValueStep(1)
scrollbar:SetValue(0)
scrollbar:SetWidth(16)
scrollbar:Hide()
-- set the script as the last step, so it doesn't fire yet
scrollbar:SetScript("OnValueChanged", ScrollBar_OnScrollValueChanged)
local scrollbg = scrollbar:CreateTexture(nil, "BACKGROUND")
scrollbg:SetAllPoints(scrollbar)
scrollbg:SetTexture(0, 0, 0, 0.4)
--Container Support
local content = CreateFrame("Frame", nil, scrollframe)
content:SetPoint("TOPLEFT",0,0)
content:SetPoint("TOPRIGHT",0,0)
content:SetHeight(400)
scrollframe:SetScrollChild(content)
local widget = {
localstatus = { scrollvalue = 0 },
scrollframe = scrollframe,
scrollbar = scrollbar,
content = content,
frame = frame,
type = Type
}
for method, func in pairs(methods) do
widget[method] = func
end
scrollframe.obj, scrollbar.obj = widget, widget
return AceGUI:RegisterAsContainer(widget)
end
AceGUI:RegisterWidgetType(Type, Constructor, Version)
@@ -0,0 +1,69 @@
--[[-----------------------------------------------------------------------------
SimpleGroup Container
Simple container widget that just groups widgets.
-------------------------------------------------------------------------------]]
local Type, Version = "SimpleGroup", 20
local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
-- Lua APIs
local pairs = pairs
-- WoW APIs
local CreateFrame, UIParent = CreateFrame, UIParent
--[[-----------------------------------------------------------------------------
Methods
-------------------------------------------------------------------------------]]
local methods = {
["OnAcquire"] = function(self)
self:SetWidth(300)
self:SetHeight(100)
end,
-- ["OnRelease"] = nil,
["LayoutFinished"] = function(self, width, height)
if self.noAutoHeight then return end
self:SetHeight(height or 0)
end,
["OnWidthSet"] = function(self, width)
local content = self.content
content:SetWidth(width)
content.width = width
end,
["OnHeightSet"] = function(self, height)
local content = self.content
content:SetHeight(height)
content.height = height
end
}
--[[-----------------------------------------------------------------------------
Constructor
-------------------------------------------------------------------------------]]
local function Constructor()
local frame = CreateFrame("Frame", nil, UIParent)
frame:SetFrameStrata("FULLSCREEN_DIALOG")
--Container Support
local content = CreateFrame("Frame", nil, frame)
content:SetPoint("TOPLEFT")
content:SetPoint("BOTTOMRIGHT")
local widget = {
frame = frame,
content = content,
type = Type
}
for method, func in pairs(methods) do
widget[method] = func
end
return AceGUI:RegisterAsContainer(widget)
end
AceGUI:RegisterWidgetType(Type, Constructor, Version)
@@ -0,0 +1,378 @@
--[[-----------------------------------------------------------------------------
TabGroup Container
Container that uses tabs on top to switch between groups.
-------------------------------------------------------------------------------]]
local Type, Version = "TabGroup", 35
local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
local AceCore = LibStub("AceCore-3.0")
local wipe = AceCore.wipe
-- Lua APIs
local pairs, ipairs, assert, type, wipe = pairs, ipairs, assert, type, wipe
local tgetn = table.getn
local strfmt = string.format
-- WoW APIs
local PlaySound = PlaySound
local CreateFrame, UIParent = CreateFrame, UIParent
local _G = AceCore._G
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
-- List them here for Mikk's FindGlobals script
-- GLOBALS: PanelTemplates_TabResize, PanelTemplates_SetDisabledTabState, PanelTemplates_SelectTab, PanelTemplates_DeselectTab
-- local upvalue storage used by BuildTabs
local widths = {}
local rowwidths = {}
local rowends = {}
--[[-----------------------------------------------------------------------------
Support functions
-------------------------------------------------------------------------------]]
local function UpdateTabLook(frame)
if frame.disabled then
PanelTemplates_SetDisabledTabState(frame)
frame:SetAlpha(0.5)
elseif frame.selected then
PanelTemplates_SelectTab(frame)
frame:SetAlpha(1)
else
PanelTemplates_DeselectTab(frame)
frame:SetAlpha(0.5)
end
end
local function Tab_SetText(tab, text)
tab:_SetText(text)
local width = tab.obj.frame.width or tab.obj.frame:GetWidth() or 0
PanelTemplates_TabResize(0, tab, nil, width)
end
local function Tab_SetSelected(tab, selected)
tab.selected = selected
UpdateTabLook(tab)
end
local function Tab_SetDisabled(tab, disabled)
tab.disabled = disabled
UpdateTabLook(tab)
end
local function BuildTabsOnUpdate()
local self = this.obj
self:BuildTabs()
this:SetScript("OnUpdate", nil)
end
--[[-----------------------------------------------------------------------------
Scripts
-------------------------------------------------------------------------------]]
local function Tab_OnClick()
if not (this.selected or this.disabled) then
PlaySound("igCharacterInfoTab")
this.obj:SelectTab(this.value)
end
end
local function Tab_OnEnter()
local self = this.obj
self:Fire("OnTabEnter", 2, self.tabs[this.id].value, this)
end
local function Tab_OnLeave()
local self = this.obj
self:Fire("OnTabLeave", 2, self.tabs[this.id].value, this)
end
local function Tab_OnShow()
_G[this:GetName().."HighlightTexture"]:SetWidth(this:GetTextWidth() + 30)
end
--[[-----------------------------------------------------------------------------
Methods
-------------------------------------------------------------------------------]]
local methods = {
["OnAcquire"] = function(self)
self:SetTitle()
end,
["OnRelease"] = function(self)
self.status = nil
for k in pairs(self.localstatus) do
self.localstatus[k] = nil
end
self.tablist = nil
for _, tab in pairs(self.tabs) do
tab:Hide()
end
end,
["CreateTab"] = function(self, id)
local tabname = strfmt("AceGUITabGroup%dTab%d", self.num, id)
local tab = CreateFrame("Button", tabname, self.border, "TabButtonTemplate")
-- Normal texture
local texture = getglobal(tabname.."Left")
texture:SetTexture("Interface\\ChatFrame\\ChatFrameTab")
texture:ClearAllPoints()
texture:SetPoint("BOTTOMLEFT", tab,"BOTTOMLEFT")
texture:SetPoint("TOPLEFT", tab,"TOPLEFT")
texture = getglobal(tabname.."Middle")
texture:SetTexture("Interface\\ChatFrame\\ChatFrameTab")
texture = getglobal(tabname.."Right")
texture:SetTexture("Interface\\ChatFrame\\ChatFrameTab")
-- Disabled texture
texture = getglobal(tabname.."LeftDisabled")
texture:SetTexture("Interface\\ChatFrame\\ChatFrameTab")
texture:SetPoint("BOTTOMLEFT", tab,"BOTTOMLEFT")
texture:SetPoint("TOPLEFT", tab,"TOPLEFT")
texture = getglobal(tabname.."MiddleDisabled")
texture:SetTexture("Interface\\ChatFrame\\ChatFrameTab")
texture = getglobal(tabname.."RightDisabled")
texture:SetTexture("Interface\\ChatFrame\\ChatFrameTab")
tab.obj = self
tab.id = id
tab:SetScript("OnClick", Tab_OnClick)
tab:SetScript("OnEnter", Tab_OnEnter)
tab:SetScript("OnLeave", Tab_OnLeave)
tab:SetScript("OnShow", Tab_OnShow)
tab._SetText = tab.SetText
tab.SetText = Tab_SetText
tab.SetSelected = Tab_SetSelected
tab.SetDisabled = Tab_SetDisabled
return tab
end,
["SetTitle"] = function(self, text)
self.titletext:SetText(text or "")
if text and text ~= "" then
self.alignoffset = 25
else
self.alignoffset = 18
end
self:BuildTabs()
end,
["SetStatusTable"] = function(self, status)
assert(type(status) == "table")
self.status = status
end,
["SelectTab"] = function(self, value)
local status = self.status or self.localstatus
local found
for i, v in ipairs(self.tabs) do
if v.value == value then
v:SetSelected(true)
found = true
else
v:SetSelected(false)
end
end
status.selected = value
if found then
self:Fire("OnGroupSelected",1,value)
end
end,
["SetTabs"] = function(self, tabs)
self.tablist = tabs
self:BuildTabs()
end,
["BuildTabs"] = function(self)
local hastitle = (self.titletext:GetText() and self.titletext:GetText() ~= "")
local status = self.status or self.localstatus
local tablist = self.tablist
local tabs = self.tabs
if not tablist then return end
local width = self.frame.width or self.frame:GetWidth() or 0
wipe(widths)
wipe(rowwidths)
wipe(rowends)
--Place Text into tabs and get thier initial width
for i, v in ipairs(tablist) do
local tab = tabs[i]
if not tab then
tab = self:CreateTab(i)
tabs[i] = tab
end
tab:Show()
if type(v) == "table" then
tab:SetText(v.text)
tab:SetDisabled(v.disabled)
tab.value = v.value
elseif type(v) == "string" then
tab:SetText(v)
tab.value = v
end
widths[i] = tab:GetWidth() - 6 --tabs are anchored 10 pixels from the right side of the previous one to reduce spacing, but add a fixed 4px padding for the text
end
for i = tgetn(tablist)+1, tgetn(tabs), 1 do
tabs[i]:Hide()
end
--First pass, find the minimum number of rows needed to hold all tabs and the initial tab layout
local numtabs = tgetn(tablist)
local numrows = 1
local usedwidth = 0
for i = 1, tgetn(tablist) do
--If this is not the first tab of a row and there isn't room for it
if usedwidth ~= 0 and (width - usedwidth - widths[i]) < 0 then
rowwidths[numrows] = usedwidth + 10 --first tab in each row takes up an extra 10px
rowends[numrows] = i - 1
numrows = numrows + 1
usedwidth = 0
end
usedwidth = usedwidth + widths[i]
end
rowwidths[numrows] = usedwidth + 10 --first tab in each row takes up an extra 10px
rowends[numrows] = tgetn(tablist)
--Fix for single tabs being left on the last row, move a tab from the row above if applicable
if numrows > 1 then
--if the last row has only one tab
if rowends[numrows-1] == numtabs-1 then
--if there are more than 2 tabs in the 2nd last row
if (numrows == 2 and rowends[numrows-1] > 2) or (rowends[numrows] - rowends[numrows-1] > 2) then
--move 1 tab from the second last row to the last, if there is enough space
if (rowwidths[numrows] + widths[numtabs-1]) <= width then
rowends[numrows-1] = rowends[numrows-1] - 1
rowwidths[numrows] = rowwidths[numrows] + widths[numtabs-1]
rowwidths[numrows-1] = rowwidths[numrows-1] - widths[numtabs-1]
end
end
end
end
--anchor the rows as defined and resize tabs to fill thier row
local starttab = 1
for row, endtab in ipairs(rowends) do
local first = true
for tabno = starttab, endtab do
local tab = tabs[tabno]
tab:ClearAllPoints()
if first then
tab:SetPoint("TOPLEFT", self.frame, "TOPLEFT", 0, -(hastitle and 14 or 7)-(row-1)*20 )
first = false
else
tab:SetPoint("LEFT", tabs[tabno-1], "RIGHT", -10, 0)
end
end
-- equal padding for each tab to fill the available width,
-- if the used space is above 75% already
-- the 18 pixel is the typical width of a scrollbar, so we can have a tab group inside a scrolling frame,
-- and not have the tabs jump around funny when switching between tabs that need scrolling and those that don't
local padding = 0
if not (numrows == 1 and rowwidths[1] < width*0.75 - 18) then
padding = (width - rowwidths[row]) / (endtab - starttab+1)
end
for i = starttab, endtab do
PanelTemplates_TabResize(padding + 4, tabs[i], nil, width)
end
starttab = endtab + 1
end
self.borderoffset = (hastitle and 17 or 10)+((numrows)*20)+7
self.border:SetPoint("TOPLEFT", 1, -self.borderoffset)
end,
["OnWidthSet"] = function(self, width)
local content = self.content
local contentwidth = width - 60
if contentwidth < 0 then
contentwidth = 0
end
content:SetWidth(contentwidth)
content.width = contentwidth
self:BuildTabs(self)
self.frame:SetScript("OnUpdate", BuildTabsOnUpdate)
end,
["OnHeightSet"] = function(self, height)
local content = self.content
local contentheight = height - (self.borderoffset + 23)
if contentheight < 0 then
contentheight = 0
end
content:SetHeight(contentheight)
content.height = contentheight
end,
["LayoutFinished"] = function(self, width, height)
if self.noAutoHeight then return end
self:SetHeight((height or 0) + (self.borderoffset + 23))
end
}
--[[-----------------------------------------------------------------------------
Constructor
-------------------------------------------------------------------------------]]
local PaneBackdrop = {
bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
tile = true, tileSize = 16, edgeSize = 16,
insets = { left = 3, right = 3, top = 5, bottom = 3 }
}
local function Constructor()
local num = AceGUI:GetNextWidgetNum(Type)
local frame = CreateFrame("Frame",nil,UIParent)
frame:SetHeight(100)
frame:SetWidth(100)
frame:SetFrameStrata("FULLSCREEN_DIALOG")
local titletext = frame:CreateFontString(nil,"OVERLAY","GameFontNormal")
titletext:SetPoint("TOPLEFT", 14, 0)
titletext:SetPoint("TOPRIGHT", -14, 0)
titletext:SetJustifyH("LEFT")
titletext:SetHeight(18)
titletext:SetText("")
local border = CreateFrame("Frame", nil, frame)
border:SetPoint("TOPLEFT", 1, -27)
border:SetPoint("BOTTOMRIGHT", -1, 3)
border:SetBackdrop(PaneBackdrop)
border:SetBackdropColor(0.1, 0.1, 0.1, 0.5)
border:SetBackdropBorderColor(0.4, 0.4, 0.4)
local content = CreateFrame("Frame", nil, border)
content:SetPoint("TOPLEFT", 10, -7)
content:SetPoint("BOTTOMRIGHT", -10, 7)
local widget = {
num = num,
frame = frame,
localstatus = {},
alignoffset = 18,
titletext = titletext,
border = border,
borderoffset = 27,
tabs = {},
content = content,
type = Type
}
for method, func in pairs(methods) do
widget[method] = func
end
return AceGUI:RegisterAsContainer(widget)
end
AceGUI:RegisterWidgetType(Type, Constructor, Version)
@@ -0,0 +1,769 @@
--[[-----------------------------------------------------------------------------
TreeGroup Container
Container that uses a tree control to switch between groups.
-------------------------------------------------------------------------------]]
local Type, Version = "TreeGroup", 40
local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
local AceCore = LibStub("AceCore-3.0")
local strsplit = AceCore.strsplit
local _G = AceCore._G
-- Lua APIs
local next, pairs, ipairs, assert, type = next, pairs, ipairs, assert, type
local math_min, math_max, floor = math.min, math.max, floor
local tgetn, tremove, unpack, tconcat = table.getn, table.remove, unpack, table.concat
local strfmt = string.format
-- WoW APIs
local CreateFrame, UIParent = CreateFrame, UIParent
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
-- List them here for Mikk's FindGlobals script
-- GLOBALS: GameTooltip, FONT_COLOR_CODE_CLOSE
-- Recycling functions
local new, del
do
local pool = setmetatable({},{__mode='k'})
function new()
local t = next(pool)
if t then
pool[t] = nil
return t
else
return {}
end
end
function del(t)
for k in pairs(t) do
t[k] = nil
end
pool[t] = true
end
end
local DEFAULT_TREE_WIDTH = 175
local DEFAULT_TREE_SIZABLE = true
--[[-----------------------------------------------------------------------------
Support functions
-------------------------------------------------------------------------------]]
local function GetButtonUniqueValue(line)
local parent = line.parent
if parent and parent.value then
return GetButtonUniqueValue(parent).."\001"..line.value
else
return line.value
end
end
local function UpdateButton(button, treeline, selected, canExpand, isExpanded)
local self = button.obj
local toggle = button.toggle
local frame = self.frame
local text = treeline.text or ""
local icon = treeline.icon
local iconCoords = treeline.iconCoords
local level = treeline.level
local value = treeline.value
local uniquevalue = treeline.uniquevalue
local disabled = treeline.disabled
button.treeline = treeline
button.value = value
button.uniquevalue = uniquevalue
if selected then
button:LockHighlight()
button.selected = true
else
button:UnlockHighlight()
button.selected = false
end
local normalTexture = button:GetNormalTexture()
local line = button.line
button.level = level
if ( level == 1 ) then
button.text:SetFontObject("GameFontNormal")
button:SetHighlightFontObject("GameFontHighlight")
button.text:SetPoint("LEFT", (icon and 16 or 0) + 8, 2)
else
button.text:SetFontObject("GameFontHighlightSmall")
button:SetHighlightFontObject("GameFontHighlightSmall")
button.text:SetPoint("LEFT", (icon and 16 or 0) + 8 * level, 2)
end
if disabled then
button:EnableMouse(false)
button.text:SetText("|cff808080"..text..FONT_COLOR_CODE_CLOSE)
else
button.text:SetText(text)
button:EnableMouse(true)
end
if icon then
button.icon:SetTexture(icon)
button.icon:SetPoint("LEFT", 8 * level, (level == 1) and 0 or 1)
else
button.icon:SetTexture(nil)
end
if iconCoords then
button.icon:SetTexCoord(unpack(iconCoords))
else
button.icon:SetTexCoord(0, 1, 0, 1)
end
if canExpand then
if not isExpanded then
toggle:SetNormalTexture("Interface\\Buttons\\UI-PlusButton-UP")
toggle:SetPushedTexture("Interface\\Buttons\\UI-PlusButton-DOWN")
else
toggle:SetNormalTexture("Interface\\Buttons\\UI-MinusButton-UP")
toggle:SetPushedTexture("Interface\\Buttons\\UI-MinusButton-DOWN")
end
toggle:Show()
else
toggle:Hide()
end
end
local function ShouldDisplayLevel(tree)
local result = false
for k, v in ipairs(tree) do
if v.children == nil and v.visible ~= false then
result = true
elseif v.children then
result = result or ShouldDisplayLevel(v.children)
end
if result then return result end
end
return false
end
local function addLine(self, v, tree, level, parent)
local line = new()
line.value = v.value
line.text = v.text
line.icon = v.icon
line.iconCoords = v.iconCoords
line.disabled = v.disabled
line.tree = tree
line.level = level
line.parent = parent
line.visible = v.visible
line.uniquevalue = GetButtonUniqueValue(line)
if v.children then
line.hasChildren = true
else
line.hasChildren = nil
end
tinsert(self.lines, line)
return line
end
--fire an update after one frame to catch the treeframes height
local function FirstFrameUpdate()
local self = this.obj
this:SetScript("OnUpdate", nil)
self:RefreshTree()
end
local BuildUniqueValue
do
local args = {nil,nil,nil,nil,nil,nil,nil,nil,nil,nil}
function BuildUniqueValue(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
args[1] = a1
args[2] = a2
args[3] = a3
args[4] = a4
args[5] = a5
args[6] = a6
args[7] = a7
args[8] = a8
args[9] = a9
args[10] = a10
return tconcat(tmp, "\001", 1, tgetn(args))
end
end
--[[-----------------------------------------------------------------------------
Scripts
-------------------------------------------------------------------------------]]
local function Expand_OnClick()
local button = this.button
local self = button.obj
local status = (self.status or self.localstatus).groups
status[button.uniquevalue] = not status[button.uniquevalue]
self:RefreshTree()
end
local function Button_OnClick()
local self = this.obj
self:Fire("OnClick", 2, this.uniquevalue, this.selected)
if not this.selected then
self:SetSelected(this.uniquevalue)
this.selected = true
this:LockHighlight()
self:RefreshTree()
end
AceGUI:ClearFocus()
end
local function Button_OnDoubleClick()
local self = this.obj
local status = self.status or self.localstatus
local status = (self.status or self.localstatus).groups
status[this.uniquevalue] = not status[this.uniquevalue]
self:RefreshTree()
end
local function Button_OnEnter()
local self = this.obj
self:Fire("OnButtonEnter", 2, this.uniquevalue, this)
if self.enabletooltips then
GameTooltip:SetOwner(this, "ANCHOR_NONE")
GameTooltip:SetPoint("LEFT",this,"RIGHT")
GameTooltip:SetText(this.text:GetText() or "", 1, .82, 0, true)
GameTooltip:Show()
end
end
local function Button_OnLeave()
local self = this.obj
self:Fire("OnButtonLeave", 2, this.uniquevalue, this)
if self.enabletooltips then
GameTooltip:Hide()
end
end
local function OnScrollValueChanged()
if this.obj.noupdate then return end
local self = this.obj
local status = self.status or self.localstatus
status.scrollvalue = floor(arg1 + 0.5)
self:RefreshTree()
AceGUI:ClearFocus()
end
local function Tree_OnSizeChanged()
this.obj:RefreshTree()
end
local function Tree_OnMouseWheel()
local self = this.obj
if self.showscroll then
local scrollbar = self.scrollbar
local min, max = scrollbar:GetMinMaxValues()
local value = scrollbar:GetValue()
local newvalue = math_min(max,math_max(min,value - arg1))
if value ~= newvalue then
scrollbar:SetValue(newvalue)
end
end
end
local function Dragger_OnLeave()
this:SetBackdropColor(1, 1, 1, 0)
end
local function Dragger_OnEnter()
this:SetBackdropColor(1, 1, 1, 0.8)
end
local function Dragger_OnMouseDown()
local treeframe = this:GetParent()
treeframe:StartSizing("RIGHT")
end
local function Dragger_OnMouseUp()
local treeframe = this:GetParent()
local self = treeframe.obj
local this = treeframe:GetParent()
treeframe:StopMovingOrSizing()
--treeframe:SetScript("OnUpdate", nil)
treeframe:SetUserPlaced(false)
--Without this :GetHeight will get stuck on the current height, causing the tree contents to not resize
treeframe:SetHeight(0)
treeframe:SetPoint("TOPLEFT", this, "TOPLEFT",0,0)
treeframe:SetPoint("BOTTOMLEFT", this, "BOTTOMLEFT",0,0)
local status = self.status or self.localstatus
status.treewidth = treeframe:GetWidth()
treeframe.obj:Fire("OnTreeResize", 1, treeframe:GetWidth())
-- recalculate the content width
treeframe.obj:OnWidthSet(status.fullwidth)
-- update the layout of the content
treeframe.obj:DoLayout()
end
--[[-----------------------------------------------------------------------------
Methods
-------------------------------------------------------------------------------]]
local methods
do
local select_args = {nil,nil,nil,nil,nil,nil,nil,nil,nil,nil}
methods = {
["OnAcquire"] = function(self)
self:SetTreeWidth(DEFAULT_TREE_WIDTH, DEFAULT_TREE_SIZABLE)
self:EnableButtonTooltips(true)
self.frame:SetScript("OnUpdate", FirstFrameUpdate)
end,
["OnRelease"] = function(self)
self.status = nil
for k, v in pairs(self.localstatus) do
if k == "groups" then
for k2 in pairs(v) do
v[k2] = nil
end
else
self.localstatus[k] = nil
end
end
self.localstatus.scrollvalue = 0
self.localstatus.treewidth = DEFAULT_TREE_WIDTH
self.localstatus.treesizable = DEFAULT_TREE_SIZABLE
end,
["EnableButtonTooltips"] = function(self, enable)
self.enabletooltips = enable
end,
["CreateButton"] = function(self)
local num = AceGUI:GetNextWidgetNum("TreeGroupButton")
local button = CreateFrame("Button", strfmt("AceGUI30TreeButton%d", num), self.treeframe)
button.obj = self
button:SetWidth(175)
button:SetHeight(18)
local toggle = CreateFrame("Button", nil, button)
toggle.obj = button
button.toggle = toggle
toggle:SetWidth(14)
toggle:SetHeight(14)
toggle:ClearAllPoints()
toggle:SetPoint("TOPRIGHT", button, "TOPRIGHT", -6, -1)
toggle:SetScript("OnClick", Button_OnClick)
toggle:SetNormalTexture("Interface\\Buttons\\UI-MinusButton-UP")
toggle:SetPushedTexture("Interface\\Buttons\\UI-MinusButton-DOWN")
toggle:SetHighlightTexture("Interface\\Buttons\\UI-PlusButton-Hilight", "ADD")
local text = button:CreateFontString()
button.text = text
text:SetFontObject(GameFontNormal)
button:SetHighlightFontObject(GameFontHighlight)
text:SetPoint("RIGHT", toggle, "LEFT", -2, 0);
text:SetJustifyH("LEFT")
local highlight = button:CreateTexture(nil, "HIGHLIGHT");
button.highlight = highlight
highlight:SetTexture("Interface\\QuestFrame\\UI-QuestLogTitleHighlight")
highlight:SetBlendMode("ADD")
highlight:SetVertexColor(.196, .388, .8);
highlight:ClearAllPoints()
highlight:SetPoint("TOPLEFT",0,1)
highlight:SetPoint("BOTTOMRIGHT",0,1)
local icon = button:CreateTexture(nil, "OVERLAY")
icon:SetWidth(14)
icon:SetHeight(14)
button.icon = icon
button:SetScript("OnClick",Button_OnClick)
button:SetScript("OnDoubleClick", Button_OnDoubleClick)
button:SetScript("OnEnter",Button_OnEnter)
button:SetScript("OnLeave",Button_OnLeave)
button.toggle.button = button
button.toggle:SetScript("OnClick",Expand_OnClick)
button.text:SetHeight(14) -- Prevents text wrapping
return button
end,
["SetStatusTable"] = function(self, status)
assert(type(status) == "table")
self.status = status
if not status.groups then
status.groups = {}
end
if not status.scrollvalue then
status.scrollvalue = 0
end
if not status.treewidth then
status.treewidth = DEFAULT_TREE_WIDTH
end
if status.treesizable == nil then
status.treesizable = DEFAULT_TREE_SIZABLE
end
self:SetTreeWidth(status.treewidth,status.treesizable)
self:RefreshTree()
end,
--sets the tree to be displayed
["SetTree"] = function(self, tree, filter)
self.filter = filter
if tree then
assert(type(tree) == "table")
end
self.tree = tree
self:RefreshTree()
end,
["BuildLevel"] = function(self, tree, level, parent)
local groups = (self.status or self.localstatus).groups
local hasChildren = self.hasChildren
for i, v in ipairs(tree) do
if v.children then
if not self.filter or ShouldDisplayLevel(v.children) then
local line = addLine(self, v, tree, level, parent)
if groups[line.uniquevalue] then
self:BuildLevel(v.children, level+1, line)
end
end
elseif v.visible ~= false or not self.filter then
addLine(self, v, tree, level, parent)
end
end
end,
["RefreshTree"] = function(self,scrollToSelection)
local buttons = self.buttons
local lines = self.lines
for i, v in ipairs(buttons) do
v:Hide()
end
while lines[1] do
local t = tremove(lines)
for k in pairs(t) do
t[k] = nil
end
del(t)
end
if not self.tree then return end
--Build the list of visible entries from the tree and status tables
local status = self.status or self.localstatus
local groupstatus = status.groups
local tree = self.tree
local treeframe = self.treeframe
status.scrollToSelection = status.scrollToSelection or scrollToSelection -- needs to be cached in case the control hasn't been drawn yet (code bails out below)
self:BuildLevel(tree, 1)
local numlines = tgetn(lines)
local maxlines = (floor(((self.treeframe:GetHeight()or 0) - 20 ) / 18))
if maxlines <= 0 then return end
local first, last
scrollToSelection = status.scrollToSelection
status.scrollToSelection = nil
if numlines <= maxlines then
--the whole tree fits in the frame
status.scrollvalue = 0
self:ShowScroll(false)
first, last = 1, numlines
else
self:ShowScroll(true)
--scrolling will be needed
self.noupdate = true
self.scrollbar:SetMinMaxValues(0, numlines - maxlines)
--check if we are scrolled down too far
if numlines - status.scrollvalue < maxlines then
status.scrollvalue = numlines - maxlines
end
self.noupdate = nil
first, last = status.scrollvalue+1, status.scrollvalue + maxlines
--show selection?
if scrollToSelection and status.selected then
local show
for i,line in ipairs(lines) do -- find the line number
if line.uniquevalue==status.selected then
show=i
end
end
if not show then
-- selection was deleted or something?
elseif show>=first and show<=last then
-- all good
else
-- scrolling needed!
if show<first then
status.scrollvalue = show-1
else
status.scrollvalue = show-maxlines
end
first, last = status.scrollvalue+1, status.scrollvalue + maxlines
end
end
if self.scrollbar:GetValue() ~= status.scrollvalue then
self.scrollbar:SetValue(status.scrollvalue)
end
end
local buttonnum = 1
for i = first, last do
local line = lines[i]
local button = buttons[buttonnum]
if not button then
button = self:CreateButton()
buttons[buttonnum] = button
button:SetParent(treeframe)
button:SetFrameLevel(treeframe:GetFrameLevel()+1)
button:ClearAllPoints()
if buttonnum == 1 then
if self.showscroll then
button:SetPoint("TOPRIGHT", -22, -10)
button:SetPoint("TOPLEFT", 0, -10)
else
button:SetPoint("TOPRIGHT", 0, -10)
button:SetPoint("TOPLEFT", 0, -10)
end
else
button:SetPoint("TOPRIGHT", buttons[buttonnum-1], "BOTTOMRIGHT",0,0)
button:SetPoint("TOPLEFT", buttons[buttonnum-1], "BOTTOMLEFT",0,0)
end
end
UpdateButton(button, line, status.selected == line.uniquevalue, line.hasChildren, groupstatus[line.uniquevalue] )
button:Show()
buttonnum = buttonnum + 1
end
end,
["SetSelected"] = function(self, value)
local status = self.status or self.localstatus
if status.selected ~= value then
status.selected = value
self:Fire("OnGroupSelected", 1, value)
end
end,
["Select"] = function(self, uniquevalue, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
self.filter = false
local status = self.status or self.localstatus
local groups = status.groups
select_args[1] = a1
select_args[2] = a2
select_args[3] = a3
select_args[4] = a4
select_args[5] = a5
select_args[6] = a6
select_args[7] = a7
select_args[8] = a8
select_args[9] = a9
select_args[10] = a10
for i = 1, tgetn(select_args) do
groups[tconcat(select_args, "\001", 1, i)] = true
end
status.selected = uniquevalue
self:RefreshTree(true)
self:Fire("OnGroupSelected", 1, uniquevalue)
end,
["SelectByPath"] = function(self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
self:Select(BuildUniqueValue(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10), a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
end,
["SelectByValue"] = function(self, uniquevalue)
self:Select(uniquevalue, strsplit("\001", uniquevalue))
end,
["ShowScroll"] = function(self, show)
self.showscroll = show
if show then
self.scrollbar:Show()
if self.buttons[1] then
self.buttons[1]:SetPoint("TOPRIGHT", self.treeframe,"TOPRIGHT",-22,-10)
end
else
self.scrollbar:Hide()
if self.buttons[1] then
self.buttons[1]:SetPoint("TOPRIGHT", self.treeframe,"TOPRIGHT",0,-10)
end
end
end,
["OnWidthSet"] = function(self, width)
local content = self.content
local treeframe = self.treeframe
local status = self.status or self.localstatus
status.fullwidth = width
local contentwidth = width - status.treewidth - 20
if contentwidth < 0 then
contentwidth = 0
end
content:SetWidth(contentwidth)
content.width = contentwidth
local maxtreewidth = math_min(400, width - 50)
if maxtreewidth > 100 and status.treewidth > maxtreewidth then
self:SetTreeWidth(maxtreewidth, status.treesizable)
end
treeframe:SetMaxResize(maxtreewidth, 1600)
end,
["OnHeightSet"] = function(self, height)
local content = self.content
local contentheight = height - 20
if contentheight < 0 then
contentheight = 0
end
content:SetHeight(contentheight)
content.height = contentheight
end,
["SetTreeWidth"] = function(self, treewidth, resizable)
if not resizable then
if type(treewidth) == 'number' then
resizable = false
elseif type(treewidth) == 'boolean' then
resizable = treewidth
treewidth = DEFAULT_TREE_WIDTH
else
resizable = false
treewidth = DEFAULT_TREE_WIDTH
end
end
self.treeframe:SetWidth(treewidth)
self.dragger:EnableMouse(resizable)
local status = self.status or self.localstatus
status.treewidth = treewidth
status.treesizable = resizable
-- recalculate the content width
if status.fullwidth then
self:OnWidthSet(status.fullwidth)
end
end,
["GetTreeWidth"] = function(self)
local status = self.status or self.localstatus
return status.treewidth or DEFAULT_TREE_WIDTH
end,
["LayoutFinished"] = function(self, width, height)
if self.noAutoHeight then return end
self:SetHeight((height or 0) + 20)
end
}
end -- method
--[[-----------------------------------------------------------------------------
Constructor
-------------------------------------------------------------------------------]]
local PaneBackdrop = {
bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
tile = true, tileSize = 16, edgeSize = 16,
insets = { left = 3, right = 3, top = 5, bottom = 3 }
}
local DraggerBackdrop = {
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
edgeFile = nil,
tile = true, tileSize = 16, edgeSize = 0,
insets = { left = 3, right = 3, top = 7, bottom = 7 }
}
local function Constructor()
local num = AceGUI:GetNextWidgetNum(Type)
local frame = CreateFrame("Frame", nil, UIParent)
local treeframe = CreateFrame("Frame", nil, frame)
treeframe:SetPoint("TOPLEFT", 0, 0)
treeframe:SetPoint("BOTTOMLEFT", 0, 0)
treeframe:SetWidth(DEFAULT_TREE_WIDTH)
treeframe:EnableMouseWheel(true)
treeframe:SetBackdrop(PaneBackdrop)
treeframe:SetBackdropColor(0.1, 0.1, 0.1, 0.5)
treeframe:SetBackdropBorderColor(0.4, 0.4, 0.4)
treeframe:SetResizable(true)
treeframe:SetMinResize(100, 1)
treeframe:SetMaxResize(400, 1600)
treeframe:SetScript("OnUpdate", FirstFrameUpdate)
treeframe:SetScript("OnSizeChanged", Tree_OnSizeChanged)
treeframe:SetScript("OnMouseWheel", Tree_OnMouseWheel)
local dragger = CreateFrame("Frame", nil, treeframe)
dragger:SetWidth(8)
dragger:SetPoint("TOP", treeframe, "TOPRIGHT")
dragger:SetPoint("BOTTOM", treeframe, "BOTTOMRIGHT")
dragger:SetBackdrop(DraggerBackdrop)
dragger:SetBackdropColor(1, 1, 1, 0)
dragger:SetScript("OnEnter", Dragger_OnEnter)
dragger:SetScript("OnLeave", Dragger_OnLeave)
dragger:SetScript("OnMouseDown", Dragger_OnMouseDown)
dragger:SetScript("OnMouseUp", Dragger_OnMouseUp)
local scrollbar = CreateFrame("Slider", strfmt("AceConfigDialogTreeGroup%dScrollBar", num), treeframe, "UIPanelScrollBarTemplate")
scrollbar:SetScript("OnValueChanged", nil)
scrollbar:SetPoint("TOPRIGHT", -10, -26)
scrollbar:SetPoint("BOTTOMRIGHT", -10, 26)
scrollbar:SetMinMaxValues(0,0)
scrollbar:SetValueStep(1)
scrollbar:SetValue(0)
scrollbar:SetWidth(16)
scrollbar:SetScript("OnValueChanged", OnScrollValueChanged)
local scrollbg = scrollbar:CreateTexture(nil, "BACKGROUND")
scrollbg:SetAllPoints(scrollbar)
scrollbg:SetTexture(0,0,0,0.4)
local border = CreateFrame("Frame",nil,frame)
border:SetPoint("TOPLEFT", treeframe, "TOPRIGHT")
border:SetPoint("BOTTOMRIGHT", 0, 0)
border:SetBackdrop(PaneBackdrop)
border:SetBackdropColor(0.1, 0.1, 0.1, 0.5)
border:SetBackdropBorderColor(0.4, 0.4, 0.4)
--Container Support
local content = CreateFrame("Frame", nil, border)
content:SetPoint("TOPLEFT", 10, -10)
content:SetPoint("BOTTOMRIGHT", -10, 10)
local widget = {
frame = frame,
lines = {},
levels = {},
buttons = {},
hasChildren = {},
localstatus = { groups = {}, scrollvalue = 0 },
filter = false,
treeframe = treeframe,
dragger = dragger,
scrollbar = scrollbar,
border = border,
content = content,
type = Type
}
for method, func in pairs(methods) do
widget[method] = func
end
treeframe.obj, dragger.obj, scrollbar.obj = widget, widget, widget
return AceGUI:RegisterAsContainer(widget)
end
AceGUI:RegisterWidgetType(Type, Constructor, Version)
@@ -0,0 +1,331 @@
local AceGUI = LibStub("AceGUI-3.0")
-- Lua APIs
local pairs, assert, type = pairs, assert, type
-- WoW APIs
local PlaySound = PlaySound
local CreateFrame, UIParent = CreateFrame, UIParent
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
-- List them here for Mikk's FindGlobals script
-- GLOBALS: GameFontNormal
----------------
-- Main Frame --
----------------
--[[
Events :
OnClose
]]
do
local Type = "Window"
local Version = 4
local function frameOnClose()
this.obj:Fire("OnClose")
end
local function closeOnClick()
PlaySound("gsTitleOptionExit")
this.obj:Hide()
end
local function frameOnMouseDown()
AceGUI:ClearFocus()
end
local function titleOnMouseDown()
this:GetParent():StartMoving()
AceGUI:ClearFocus()
end
local function frameOnMouseUp()
local frame = this:GetParent()
frame:StopMovingOrSizing()
local self = frame.obj
local status = self.status or self.localstatus
status.width = frame:GetWidth()
status.height = frame:GetHeight()
status.top = frame:GetTop()
status.left = frame:GetLeft()
end
local function sizerseOnMouseDown()
this:GetParent():StartSizing("BOTTOMRIGHT")
AceGUI:ClearFocus()
end
local function sizersOnMouseDown()
this:GetParent():StartSizing("BOTTOM")
AceGUI:ClearFocus()
end
local function sizereOnMouseDown()
this:GetParent():StartSizing("RIGHT")
AceGUI:ClearFocus()
end
local function sizerOnMouseUp()
this:GetParent():StopMovingOrSizing()
end
local function SetTitle(self,title)
self.titletext:SetText(title)
end
local function SetStatusText(self,text)
-- self.statustext:SetText(text)
end
local function Hide(self)
self.frame:Hide()
end
local function Show(self)
self.frame:Show()
end
local function OnAcquire(self)
self.frame:SetParent(UIParent)
self.frame:SetFrameStrata("FULLSCREEN_DIALOG")
self:ApplyStatus()
self:EnableResize(true)
self:Show()
end
local function OnRelease(self)
self.status = nil
for k in pairs(self.localstatus) do
self.localstatus[k] = nil
end
end
-- called to set an external table to store status in
local function SetStatusTable(self, status)
assert(type(status) == "table")
self.status = status
self:ApplyStatus()
end
local function ApplyStatus(self)
local status = self.status or self.localstatus
local frame = self.frame
self:SetWidth(status.width or 700)
self:SetHeight(status.height or 500)
if status.top and status.left then
frame:SetPoint("TOP",UIParent,"BOTTOM",0,status.top)
frame:SetPoint("LEFT",UIParent,"LEFT",status.left,0)
else
frame:SetPoint("CENTER",UIParent,"CENTER")
end
end
local function OnWidthSet(self, width)
local content = self.content
local contentwidth = width - 34
if contentwidth < 0 then
contentwidth = 0
end
content:SetWidth(contentwidth)
content.width = contentwidth
end
local function OnHeightSet(self, height)
local content = self.content
local contentheight = height - 57
if contentheight < 0 then
contentheight = 0
end
content:SetHeight(contentheight)
content.height = contentheight
end
local function EnableResize(self, state)
local func = state and "Show" or "Hide"
self.sizer_se[func](self.sizer_se)
self.sizer_s[func](self.sizer_s)
self.sizer_e[func](self.sizer_e)
end
local function Constructor()
local frame = CreateFrame("Frame",nil,UIParent)
local self = {}
self.type = "Window"
self.Hide = Hide
self.Show = Show
self.SetTitle = SetTitle
self.OnRelease = OnRelease
self.OnAcquire = OnAcquire
self.SetStatusText = SetStatusText
self.SetStatusTable = SetStatusTable
self.ApplyStatus = ApplyStatus
self.OnWidthSet = OnWidthSet
self.OnHeightSet = OnHeightSet
self.EnableResize = EnableResize
self.localstatus = {}
self.frame = frame
frame.obj = self
frame:SetWidth(700)
frame:SetHeight(500)
frame:SetPoint("CENTER",UIParent,"CENTER",0,0)
frame:EnableMouse()
frame:SetMovable(true)
frame:SetResizable(true)
frame:SetFrameStrata("FULLSCREEN_DIALOG")
frame:SetScript("OnMouseDown", frameOnMouseDown)
frame:SetScript("OnHide",frameOnClose)
frame:SetMinResize(240,240)
frame:SetToplevel(true)
local titlebg = frame:CreateTexture(nil, "BACKGROUND")
titlebg:SetTexture([[Interface\PaperDollInfoFrame\UI-GearManager-Title-Background]])
titlebg:SetPoint("TOPLEFT", 9, -6)
titlebg:SetPoint("BOTTOMRIGHT", frame, "TOPRIGHT", -28, -24)
local dialogbg = frame:CreateTexture(nil, "BACKGROUND")
dialogbg:SetTexture([[Interface\Tooltips\UI-Tooltip-Background]])
dialogbg:SetPoint("TOPLEFT", 8, -24)
dialogbg:SetPoint("BOTTOMRIGHT", -6, 8)
dialogbg:SetVertexColor(0, 0, 0, .75)
local topleft = frame:CreateTexture(nil, "BORDER")
topleft:SetTexture([[Interface\PaperDollInfoFrame\UI-GearManager-Border]])
topleft:SetWidth(64)
topleft:SetHeight(64)
topleft:SetPoint("TOPLEFT")
topleft:SetTexCoord(0.501953125, 0.625, 0, 1)
local topright = frame:CreateTexture(nil, "BORDER")
topright:SetTexture([[Interface\PaperDollInfoFrame\UI-GearManager-Border]])
topright:SetWidth(64)
topright:SetHeight(64)
topright:SetPoint("TOPRIGHT")
topright:SetTexCoord(0.625, 0.75, 0, 1)
local top = frame:CreateTexture(nil, "BORDER")
top:SetTexture([[Interface\PaperDollInfoFrame\UI-GearManager-Border]])
top:SetHeight(64)
top:SetPoint("TOPLEFT", topleft, "TOPRIGHT")
top:SetPoint("TOPRIGHT", topright, "TOPLEFT")
top:SetTexCoord(0.25, 0.369140625, 0, 1)
local bottomleft = frame:CreateTexture(nil, "BORDER")
bottomleft:SetTexture([[Interface\PaperDollInfoFrame\UI-GearManager-Border]])
bottomleft:SetWidth(64)
bottomleft:SetHeight(64)
bottomleft:SetPoint("BOTTOMLEFT")
bottomleft:SetTexCoord(0.751953125, 0.875, 0, 1)
local bottomright = frame:CreateTexture(nil, "BORDER")
bottomright:SetTexture([[Interface\PaperDollInfoFrame\UI-GearManager-Border]])
bottomright:SetWidth(64)
bottomright:SetHeight(64)
bottomright:SetPoint("BOTTOMRIGHT")
bottomright:SetTexCoord(0.875, 1, 0, 1)
local bottom = frame:CreateTexture(nil, "BORDER")
bottom:SetTexture([[Interface\PaperDollInfoFrame\UI-GearManager-Border]])
bottom:SetHeight(64)
bottom:SetPoint("BOTTOMLEFT", bottomleft, "BOTTOMRIGHT")
bottom:SetPoint("BOTTOMRIGHT", bottomright, "BOTTOMLEFT")
bottom:SetTexCoord(0.376953125, 0.498046875, 0, 1)
local left = frame:CreateTexture(nil, "BORDER")
left:SetTexture([[Interface\PaperDollInfoFrame\UI-GearManager-Border]])
left:SetWidth(64)
left:SetPoint("TOPLEFT", topleft, "BOTTOMLEFT")
left:SetPoint("BOTTOMLEFT", bottomleft, "TOPLEFT")
left:SetTexCoord(0.001953125, 0.125, 0, 1)
local right = frame:CreateTexture(nil, "BORDER")
right:SetTexture([[Interface\PaperDollInfoFrame\UI-GearManager-Border]])
right:SetWidth(64)
right:SetPoint("TOPRIGHT", topright, "BOTTOMRIGHT")
right:SetPoint("BOTTOMRIGHT", bottomright, "TOPRIGHT")
right:SetTexCoord(0.1171875, 0.2421875, 0, 1)
local close = CreateFrame("Button", nil, frame, "UIPanelCloseButton")
close:SetPoint("TOPRIGHT", 2, 1)
close:SetScript("OnClick", closeOnClick)
self.closebutton = close
close.obj = self
local titletext = frame:CreateFontString(nil, "ARTWORK")
titletext:SetFontObject(GameFontNormal)
titletext:SetPoint("TOPLEFT", 12, -8)
titletext:SetPoint("TOPRIGHT", -32, -8)
self.titletext = titletext
local title = CreateFrame("Button", nil, frame)
title:SetPoint("TOPLEFT", titlebg)
title:SetPoint("BOTTOMRIGHT", titlebg)
title:EnableMouse()
title:SetScript("OnMouseDown",titleOnMouseDown)
title:SetScript("OnMouseUp", frameOnMouseUp)
self.title = title
local sizer_se = CreateFrame("Frame",nil,frame)
sizer_se:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",0,0)
sizer_se:SetWidth(25)
sizer_se:SetHeight(25)
sizer_se:EnableMouse()
sizer_se:SetScript("OnMouseDown",sizerseOnMouseDown)
sizer_se:SetScript("OnMouseUp", sizerOnMouseUp)
self.sizer_se = sizer_se
local line1 = sizer_se:CreateTexture(nil, "BACKGROUND")
self.line1 = line1
line1:SetWidth(14)
line1:SetHeight(14)
line1:SetPoint("BOTTOMRIGHT", -8, 8)
line1:SetTexture("Interface\\Tooltips\\UI-Tooltip-Border")
local x = 0.1 * 14/17
line1:SetTexCoord(0.05 - x, 0.5, 0.05, 0.5 + x, 0.05, 0.5 - x, 0.5 + x, 0.5)
local line2 = sizer_se:CreateTexture(nil, "BACKGROUND")
self.line2 = line2
line2:SetWidth(8)
line2:SetHeight(8)
line2:SetPoint("BOTTOMRIGHT", -8, 8)
line2:SetTexture("Interface\\Tooltips\\UI-Tooltip-Border")
local x = 0.1 * 8/17
line2:SetTexCoord(0.05 - x, 0.5, 0.05, 0.5 + x, 0.05, 0.5 - x, 0.5 + x, 0.5)
local sizer_s = CreateFrame("Frame",nil,frame)
sizer_s:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-25,0)
sizer_s:SetPoint("BOTTOMLEFT",frame,"BOTTOMLEFT",0,0)
sizer_s:SetHeight(25)
sizer_s:EnableMouse()
sizer_s:SetScript("OnMouseDown",sizersOnMouseDown)
sizer_s:SetScript("OnMouseUp", sizerOnMouseUp)
self.sizer_s = sizer_s
local sizer_e = CreateFrame("Frame",nil,frame)
sizer_e:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",0,25)
sizer_e:SetPoint("TOPRIGHT",frame,"TOPRIGHT",0,0)
sizer_e:SetWidth(25)
sizer_e:EnableMouse()
sizer_e:SetScript("OnMouseDown",sizereOnMouseDown)
sizer_e:SetScript("OnMouseUp", sizerOnMouseUp)
self.sizer_e = sizer_e
--Container Support
local content = CreateFrame("Frame",nil,frame)
self.content = content
content.obj = self
content:SetPoint("TOPLEFT",frame,"TOPLEFT",12,-32)
content:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-12,13)
AceGUI:RegisterAsContainer(self)
return self
end
AceGUI:RegisterWidgetType(Type,Constructor,Version)
end
@@ -0,0 +1,106 @@
--[[-----------------------------------------------------------------------------
Button Widget
Graphical Button.
-------------------------------------------------------------------------------]]
local Type, Version = "Button", 23
local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
local AceCore = LibStub("AceCore-3.0")
-- Lua APIs
local pairs = pairs
-- WoW APIs
local _G = AceCore._G
local PlaySound, CreateFrame, UIParent = PlaySound, CreateFrame, UIParent
--[[-----------------------------------------------------------------------------
Scripts
-------------------------------------------------------------------------------]]
-- arg1 is the button for OnClick event
local function Button_OnClick()
AceGUI:ClearFocus()
PlaySound("igMainMenuOption")
this.obj:Fire("OnClick", 1, arg1)
end
local function Control_OnEnter()
this.obj:Fire("OnEnter")
end
local function Control_OnLeave()
this.obj:Fire("OnLeave")
end
--[[-----------------------------------------------------------------------------
Methods
-------------------------------------------------------------------------------]]
local methods = {
["OnAcquire"] = function(self)
-- restore default values
self:SetHeight(24)
self:SetWidth(200)
self:SetDisabled(false)
self:SetAutoWidth(false)
self:SetText()
end,
-- ["OnRelease"] = nil,
["SetText"] = function(self, text)
self.text:SetText(text)
if self.autoWidth then
self:SetWidth(self.text:GetStringWidth() + 30)
end
end,
["SetAutoWidth"] = function(self, autoWidth)
self.autoWidth = autoWidth
if self.autoWidth then
self:SetWidth(self.text:GetStringWidth() + 30)
end
end,
["SetDisabled"] = function(self, disabled)
self.disabled = disabled
if disabled then
self.frame:Disable()
else
self.frame:Enable()
end
end
}
--[[-----------------------------------------------------------------------------
Constructor
-------------------------------------------------------------------------------]]
local function Constructor()
local name = "AceGUI30Button" .. AceGUI:GetNextWidgetNum(Type)
local frame = CreateFrame("Button", name, UIParent, "UIPanelButtonTemplate2")
frame:Hide()
frame:EnableMouse(true)
frame:SetScript("OnClick", Button_OnClick)
frame:SetScript("OnEnter", Control_OnEnter)
frame:SetScript("OnLeave", Control_OnLeave)
local text = frame:GetFontString()
text:ClearAllPoints()
text:SetPoint("TOPLEFT", 15, -1)
text:SetPoint("BOTTOMRIGHT", -15, 1)
text:SetJustifyV("MIDDLE")
local widget = {
text = text,
frame = frame,
type = Type
}
for method, func in pairs(methods) do
widget[method] = func
end
return AceGUI:RegisterAsWidget(widget)
end
AceGUI:RegisterWidgetType(Type, Constructor, Version)
@@ -0,0 +1,296 @@
--[[-----------------------------------------------------------------------------
Checkbox Widget
-------------------------------------------------------------------------------]]
local Type, Version = "CheckBox", 22
local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
-- Lua APIs
local pairs = pairs
-- WoW APIs
local PlaySound = PlaySound
local CreateFrame, UIParent = CreateFrame, UIParent
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
-- List them here for Mikk's FindGlobals script
-- GLOBALS: SetDesaturation, GameFontHighlight
--[[-----------------------------------------------------------------------------
Support functions
-------------------------------------------------------------------------------]]
local function AlignImage(self)
local img = self.image:GetTexture()
self.text:ClearAllPoints()
if not img then
self.text:SetPoint("LEFT", self.checkbg, "RIGHT")
self.text:SetPoint("RIGHT",0,0)
else
self.text:SetPoint("LEFT", self.image,"RIGHT", 1, 0)
self.text:SetPoint("RIGHT",0,0)
end
end
--[[-----------------------------------------------------------------------------
Scripts
-------------------------------------------------------------------------------]]
local function Control_OnEnter()
this.obj:Fire("OnEnter")
end
local function Control_OnLeave()
this.obj:Fire("OnLeave")
end
local function CheckBox_OnMouseDown()
local self = this.obj
if not self.disabled then
if self.image:GetTexture() then
self.text:SetPoint("LEFT", self.image,"RIGHT", 2, -1)
else
self.text:SetPoint("LEFT", self.checkbg, "RIGHT", 1, -1)
end
end
AceGUI:ClearFocus()
end
local function CheckBox_OnMouseUp()
local self = this.obj
if not self.disabled then
self:ToggleChecked()
if self.checked then
PlaySound("igMainMenuOptionCheckBoxOn")
else -- for both nil and false (tristate)
PlaySound("igMainMenuOptionCheckBoxOff")
end
self:Fire("OnValueChanged", 1, self.checked)
AlignImage(self)
end
end
--[[-----------------------------------------------------------------------------
Methods
-------------------------------------------------------------------------------]]
local methods = {
["OnAcquire"] = function(self)
self:SetType()
self:SetValue(false)
self:SetTriState(nil)
-- height is calculated from the width and required space for the description
self:SetWidth(200)
self:SetImage()
self:SetDisabled(nil)
self:SetDescription(nil)
end,
-- ["OnRelease"] = nil,
["OnWidthSet"] = function(self, width)
if self.desc then
self.desc:SetWidth(width - 30)
if self.desc:GetText() and self.desc:GetText() ~= "" then
self:SetHeight(28 + self.desc:GetHeight())
end
end
end,
["SetDisabled"] = function(self, disabled)
self.disabled = disabled
if disabled then
self.frame:Disable()
self.text:SetTextColor(0.5, 0.5, 0.5)
SetDesaturation(self.check, true)
if self.desc then
self.desc:SetTextColor(0.5, 0.5, 0.5)
end
else
self.frame:Enable()
self.text:SetTextColor(1, 1, 1)
if self.tristate and self.checked == nil then
SetDesaturation(self.check, true)
else
SetDesaturation(self.check, false)
end
if self.desc then
self.desc:SetTextColor(1, 1, 1)
end
end
end,
["SetValue"] = function(self,value)
local check = self.check
self.checked = value
if value then
SetDesaturation(self.check, false)
self.check:Show()
else
--Nil is the unknown tristate value
if self.tristate and value == nil then
SetDesaturation(self.check, true)
self.check:Show()
else
SetDesaturation(self.check, false)
self.check:Hide()
end
end
self:SetDisabled(self.disabled)
end,
["GetValue"] = function(self)
return self.checked
end,
["SetTriState"] = function(self, enabled)
self.tristate = enabled
self:SetValue(self:GetValue())
end,
["SetType"] = function(self, type)
local checkbg = self.checkbg
local check = self.check
local highlight = self.highlight
local size
if type == "radio" then
size = 16
checkbg:SetTexture("Interface\\Buttons\\UI-RadioButton")
checkbg:SetTexCoord(0, 0.25, 0, 1)
check:SetTexture("Interface\\Buttons\\UI-RadioButton")
check:SetTexCoord(0.25, 0.5, 0, 1)
check:SetBlendMode("ADD")
highlight:SetTexture("Interface\\Buttons\\UI-RadioButton")
highlight:SetTexCoord(0.5, 0.75, 0, 1)
else
size = 24
checkbg:SetTexture("Interface\\Buttons\\UI-CheckBox-Up")
checkbg:SetTexCoord(0, 1, 0, 1)
check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
check:SetTexCoord(0, 1, 0, 1)
check:SetBlendMode("BLEND")
highlight:SetTexture("Interface\\Buttons\\UI-CheckBox-Highlight")
highlight:SetTexCoord(0, 1, 0, 1)
end
checkbg:SetHeight(size)
checkbg:SetWidth(size)
end,
["ToggleChecked"] = function(self)
local value = self:GetValue()
if self.tristate then
--cycle in true, nil, false order
if value then
self:SetValue(nil)
elseif value == nil then
self:SetValue(false)
else
self:SetValue(true)
end
else
self:SetValue(not self:GetValue())
end
end,
["SetLabel"] = function(self, label)
self.text:SetText(label)
end,
["SetDescription"] = function(self, desc)
if desc then
if not self.desc then
local desc = self.frame:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
desc:ClearAllPoints()
desc:SetPoint("TOPLEFT", self.checkbg, "TOPRIGHT", 5, -21)
desc:SetWidth(self.frame.width - 30)
desc:SetJustifyH("LEFT")
desc:SetJustifyV("TOP")
self.desc = desc
end
self.desc:Show()
--self.text:SetFontObject(GameFontNormal)
self.desc:SetText(desc)
self:SetHeight(28 + self.desc:GetHeight())
else
if self.desc then
self.desc:SetText("")
self.desc:Hide()
end
--self.text:SetFontObject(GameFontHighlight)
self:SetHeight(24)
end
end,
["SetImage"] = function(self, path, a1,a2,a3,a4,a5,a6,a7,a8)
local image = self.image
image:SetTexture(path)
if image:GetTexture() then
if a4 or a8 then
image:SetTexCoord(a1,a2,a3,a4,a5,a6,a7,a8)
else
image:SetTexCoord(0, 1, 0, 1)
end
end
AlignImage(self)
end
}
--[[-----------------------------------------------------------------------------
Constructor
-------------------------------------------------------------------------------]]
local function Constructor()
local frame = CreateFrame("Button", nil, UIParent)
frame:Hide()
frame:EnableMouse(true)
frame:SetScript("OnEnter", Control_OnEnter)
frame:SetScript("OnLeave", Control_OnLeave)
frame:SetScript("OnMouseDown", CheckBox_OnMouseDown)
frame:SetScript("OnMouseUp", CheckBox_OnMouseUp)
local checkbg = frame:CreateTexture(nil, "ARTWORK")
checkbg:SetWidth(24)
checkbg:SetHeight(24)
checkbg:SetPoint("TOPLEFT",0,0)
checkbg:SetTexture("Interface\\Buttons\\UI-CheckBox-Up")
local check = frame:CreateTexture(nil, "OVERLAY")
check:SetAllPoints(checkbg)
check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
local text = frame:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
text:SetJustifyH("LEFT")
text:SetHeight(18)
text:SetPoint("LEFT", checkbg, "RIGHT")
text:SetPoint("RIGHT",0,0)
local highlight = frame:CreateTexture(nil, "HIGHLIGHT")
highlight:SetTexture("Interface\\Buttons\\UI-CheckBox-Highlight")
highlight:SetBlendMode("ADD")
highlight:SetAllPoints(checkbg)
local image = frame:CreateTexture(nil, "OVERLAY")
image:SetHeight(16)
image:SetWidth(16)
image:SetPoint("LEFT", checkbg, "RIGHT", 1, 0)
local widget = {
checkbg = checkbg,
check = check,
text = text,
highlight = highlight,
image = image,
frame = frame,
type = Type
}
for method, func in pairs(methods) do
widget[method] = func
end
return AceGUI:RegisterAsWidget(widget)
end
AceGUI:RegisterWidgetType(Type, Constructor, Version)
@@ -0,0 +1,187 @@
--[[-----------------------------------------------------------------------------
ColorPicker Widget
-------------------------------------------------------------------------------]]
local Type, Version = "ColorPicker", 23
local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
-- Lua APIs
local pairs = pairs
-- WoW APIs
local CreateFrame, UIParent = CreateFrame, UIParent
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
-- List them here for Mikk's FindGlobals script
-- GLOBALS: ShowUIPanel, HideUIPanel, ColorPickerFrame, OpacitySliderFrame
--[[-----------------------------------------------------------------------------
Support functions
-------------------------------------------------------------------------------]]
local function ColorCallback(self, r, g, b, a, isAlpha)
if not self.HasAlpha then
a = 1
end
self:SetColor(r, g, b, a)
if ColorPickerFrame:IsVisible() then
--colorpicker is still open
self:Fire("OnValueChanged", 4, r, g, b, a)
else
--colorpicker is closed, color callback is first, ignore it,
--alpha callback is the final call after it closes so confirm now
if isAlpha then
self:Fire("OnValueConfirmed", 4, r, g, b, a)
end
end
end
--[[-----------------------------------------------------------------------------
Scripts
-------------------------------------------------------------------------------]]
local function Control_OnEnter()
this.obj:Fire("OnEnter")
end
local function Control_OnLeave()
this.obj:Fire("OnLeave")
end
local function ColorSwatch_OnClick()
HideUIPanel(ColorPickerFrame)
local self = this.obj
if not self.disabled then
ColorPickerFrame:SetFrameStrata("FULLSCREEN_DIALOG")
ColorPickerFrame:SetClampedToScreen(true)
ColorPickerFrame.func = function()
local r, g, b = ColorPickerFrame:GetColorRGB()
local a = 1 - OpacitySliderFrame:GetValue()
ColorCallback(self, r, g, b, a)
end
ColorPickerFrame.hasOpacity = self.HasAlpha
ColorPickerFrame.opacityFunc = function()
local r, g, b = ColorPickerFrame:GetColorRGB()
local a = 1 - OpacitySliderFrame:GetValue()
ColorCallback(self, r, g, b, a, true)
end
local r, g, b, a = self.r, self.g, self.b, self.a
if self.HasAlpha then
ColorPickerFrame.opacity = 1 - (a or 0)
end
ColorPickerFrame:SetColorRGB(r, g, b)
ColorPickerFrame.cancelFunc = function()
ColorCallback(self, r, g, b, a, true)
end
ShowUIPanel(ColorPickerFrame)
end
AceGUI:ClearFocus()
end
--[[-----------------------------------------------------------------------------
Methods
-------------------------------------------------------------------------------]]
local methods = {
["OnAcquire"] = function(self)
self:SetHeight(24)
self:SetWidth(200)
self:SetHasAlpha(false)
self:SetColor(0, 0, 0, 1)
self:SetDisabled(nil)
self:SetLabel(nil)
end,
-- ["OnRelease"] = nil,
["SetLabel"] = function(self, text)
self.text:SetText(text)
end,
["SetColor"] = function(self, r, g, b, a)
self.r = r
self.g = g
self.b = b
self.a = a or 1
self.colorSwatch:SetVertexColor(r, g, b, a)
end,
["SetHasAlpha"] = function(self, HasAlpha)
self.HasAlpha = HasAlpha
end,
["SetDisabled"] = function(self, disabled)
self.disabled = disabled
if self.disabled then
self.frame:Disable()
self.text:SetTextColor(0.5, 0.5, 0.5)
else
self.frame:Enable()
self.text:SetTextColor(1, 1, 1)
end
end
}
--[[-----------------------------------------------------------------------------
Constructor
-------------------------------------------------------------------------------]]
local function Constructor()
local frame = CreateFrame("Button", nil, UIParent)
frame:Hide()
frame:EnableMouse(true)
frame:SetScript("OnEnter", Control_OnEnter)
frame:SetScript("OnLeave", Control_OnLeave)
frame:SetScript("OnClick", ColorSwatch_OnClick)
local colorSwatch = frame:CreateTexture(nil, "OVERLAY")
colorSwatch:SetWidth(19)
colorSwatch:SetHeight(19)
colorSwatch:SetTexture("Interface\\ChatFrame\\ChatFrameColorSwatch")
colorSwatch:SetPoint("LEFT",0,0)
local texture = frame:CreateTexture(nil, "BACKGROUND")
texture:SetWidth(16)
texture:SetHeight(16)
texture:SetTexture(1, 1, 1)
texture:SetPoint("CENTER", colorSwatch)
texture:Show()
local checkers = frame:CreateTexture(nil, "BACKGROUND")
checkers:SetWidth(14)
checkers:SetHeight(14)
checkers:SetTexture("Tileset\\Generic\\Checkers")
checkers:SetTexCoord(.25, 0, 0.5, .25)
checkers:SetDesaturated(true)
checkers:SetVertexColor(1, 1, 1, 0.75)
checkers:SetPoint("CENTER", colorSwatch)
checkers:Show()
local text = frame:CreateFontString(nil,"OVERLAY","GameFontHighlight")
text:SetHeight(24)
text:SetJustifyH("LEFT")
text:SetTextColor(1, 1, 1)
text:SetPoint("LEFT", colorSwatch, "RIGHT", 2, 0)
text:SetPoint("RIGHT",0,0)
--local highlight = frame:CreateTexture(nil, "HIGHLIGHT")
--highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
--highlight:SetBlendMode("ADD")
--highlight:SetAllPoints(frame)
local widget = {
colorSwatch = colorSwatch,
text = text,
frame = frame,
type = Type
}
for method, func in pairs(methods) do
widget[method] = func
end
return AceGUI:RegisterAsWidget(widget)
end
AceGUI:RegisterWidgetType(Type, Constructor, Version)
@@ -0,0 +1,457 @@
--[[ $Id: AceGUIWidget-DropDown-Items.lua 1137 2016-05-15 10:57:36Z nevcairiel $ ]]--
local AceGUI = LibStub("AceGUI-3.0")
local IsLegion = false
-- Lua APIs
local assert = assert
local tgetn = table.getn
-- WoW APIs
local PlaySound = PlaySound
local CreateFrame = CreateFrame
local fixlevels = AceGUI.fixlevels
local fixstrata = AceGUI.fixstrata
-- ItemBase is the base "class" for all dropdown items.
-- Each item has to use ItemBase.Create(widgetType) to
-- create an initial 'self' value.
-- ItemBase will add common functions and ui event handlers.
-- Be sure to keep basic usage when you override functions.
local ItemBase = {
-- NOTE: The ItemBase version is added to each item's version number
-- to ensure proper updates on ItemBase changes.
-- Use at least 1000er steps.
version = 1000,
counter = 0,
}
function ItemBase.Frame_OnEnter()
local self = this.obj
if self.useHighlight then
self.highlight:Show()
end
self:Fire("OnEnter")
if self.specialOnEnter then
self.specialOnEnter(self)
end
end
function ItemBase.Frame_OnLeave()
local self = this.obj
self.highlight:Hide()
self:Fire("OnLeave")
if self.specialOnLeave then
self.specialOnLeave(self)
end
end
-- exported, AceGUI callback
function ItemBase.OnAcquire(self)
self.frame:SetToplevel(true)
self.frame:SetFrameStrata("FULLSCREEN_DIALOG")
end
-- exported, AceGUI callback
function ItemBase.OnRelease(self)
self:SetDisabled(false)
self.pullout = nil
self.frame:SetParent(nil)
self.frame:ClearAllPoints()
self.frame:Hide()
end
-- exported
-- NOTE: this is called by a Dropdown-Pullout.
-- Do not call this method directly
function ItemBase.SetPullout(self, pullout)
self.pullout = pullout
self.frame:SetParent(nil)
local itemFrame = pullout.itemFrame
self.frame:SetParent(itemFrame)
self.parent = itemFrame
fixlevels(itemFrame)
end
-- exported
function ItemBase.SetText(self, text)
self.text:SetText(text or "")
end
-- exported
function ItemBase.GetText(self)
return self.text:GetText()
end
-- exported
function ItemBase.SetPoint(self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
self.frame:SetPoint(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
end
-- exported
function ItemBase.Show(self)
self.frame:Show()
end
-- exported
function ItemBase.Hide(self)
self.frame:Hide()
end
-- exported
function ItemBase.SetDisabled(self, disabled)
self.disabled = disabled
if disabled then
self.useHighlight = false
self.text:SetTextColor(.5, .5, .5)
else
self.useHighlight = true
self.text:SetTextColor(1, 1, 1)
end
end
-- exported
-- NOTE: this is called by a Dropdown-Pullout.
-- Do not call this method directly
function ItemBase.SetOnLeave(self, func)
self.specialOnLeave = func
end
-- exported
-- NOTE: this is called by a Dropdown-Pullout.
-- Do not call this method directly
function ItemBase.SetOnEnter(self, func)
self.specialOnEnter = func
end
function ItemBase.Create(type)
-- NOTE: Most of the following code is copied from AceGUI-3.0/Dropdown widget
local count = AceGUI:GetNextWidgetNum(type)
local frame = CreateFrame("Button", "AceGUI30DropDownItem"..count)
local self = {}
self.frame = frame
frame.obj = self
self.type = type
self.useHighlight = true
frame:SetHeight(17)
frame:SetFrameStrata("FULLSCREEN_DIALOG")
local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall")
text:SetTextColor(1,1,1)
text:SetJustifyH("LEFT")
text:SetPoint("TOPLEFT",frame,"TOPLEFT",18,0)
text:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-8,0)
self.text = text
local highlight = frame:CreateTexture(nil, "OVERLAY")
highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
highlight:SetBlendMode("ADD")
highlight:SetHeight(14)
highlight:ClearAllPoints()
highlight:SetPoint("RIGHT",frame,"RIGHT",-3,0)
highlight:SetPoint("LEFT",frame,"LEFT",5,0)
highlight:Hide()
self.highlight = highlight
local check = frame:CreateTexture("OVERLAY")
check:SetWidth(16)
check:SetHeight(16)
check:SetPoint("LEFT",frame,"LEFT",3,-1)
check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
check:Hide()
self.check = check
local sub = frame:CreateTexture("OVERLAY")
sub:SetWidth(16)
sub:SetHeight(16)
sub:SetPoint("RIGHT",frame,"RIGHT",-3,-1)
sub:SetTexture("Interface\\ChatFrame\\ChatFrameExpandArrow")
sub:Hide()
self.sub = sub
frame:SetScript("OnEnter", ItemBase.Frame_OnEnter)
frame:SetScript("OnLeave", ItemBase.Frame_OnLeave)
self.OnAcquire = ItemBase.OnAcquire
self.OnRelease = ItemBase.OnRelease
self.SetPullout = ItemBase.SetPullout
self.GetText = ItemBase.GetText
self.SetText = ItemBase.SetText
self.SetDisabled = ItemBase.SetDisabled
self.SetPoint = ItemBase.SetPoint
self.Show = ItemBase.Show
self.Hide = ItemBase.Hide
self.SetOnLeave = ItemBase.SetOnLeave
self.SetOnEnter = ItemBase.SetOnEnter
return self
end
-- Register a dummy LibStub library to retrieve the ItemBase, so other addons can use it.
local IBLib = LibStub:NewLibrary("AceGUI-3.0-DropDown-ItemBase", ItemBase.version)
if IBLib then
IBLib.GetItemBase = function() return ItemBase end
end
--[[
Template for items:
-- Item:
--
do
local widgetType = "Dropdown-Item-"
local widgetVersion = 1
local function Constructor()
local self = ItemBase.Create(widgetType)
AceGUI:RegisterAsWidget(self)
return self
end
AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion + ItemBase.version)
end
--]]
-- Item: Header
-- A single text entry.
-- Special: Different text color and no highlight
do
local widgetType = "Dropdown-Item-Header"
local widgetVersion = 1
local function OnEnter(this)
local self = this.obj
self:Fire("OnEnter")
if self.specialOnEnter then
self.specialOnEnter(self)
end
end
local function OnLeave()
local self = this.obj
self:Fire("OnLeave")
if self.specialOnLeave then
self.specialOnLeave(self)
end
end
-- exported, override
local function SetDisabled(self, disabled)
ItemBase.SetDisabled(self, disabled)
if not disabled then
self.text:SetTextColor(1, 1, 0)
end
end
local function Constructor()
local self = ItemBase.Create(widgetType)
self.SetDisabled = SetDisabled
self.frame:SetScript("OnEnter", OnEnter)
self.frame:SetScript("OnLeave", OnLeave)
self.text:SetTextColor(1, 1, 0)
AceGUI:RegisterAsWidget(self)
return self
end
AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion + ItemBase.version)
end
-- Item: Execute
-- A simple button
do
local widgetType = "Dropdown-Item-Execute"
local widgetVersion = 1
local function Frame_OnClick(this, button)
local self = this.obj
if self.disabled then return end
self:Fire("OnClick")
if self.pullout then
self.pullout:Close()
end
end
local function Constructor()
local self = ItemBase.Create(widgetType)
self.frame:SetScript("OnClick", Frame_OnClick)
AceGUI:RegisterAsWidget(self)
return self
end
AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion + ItemBase.version)
end
-- Item: Toggle
-- Some sort of checkbox for dropdown menus.
-- Does not close the pullout on click.
do
local widgetType = "Dropdown-Item-Toggle"
local widgetVersion = 3
local function UpdateToggle(self)
if self.value then
self.check:Show()
else
self.check:Hide()
end
end
local function OnRelease(self)
ItemBase.OnRelease(self)
self:SetValue(nil)
end
local function Frame_OnClick()
local self = this.obj
if self.disabled then return end
self.value = not self.value
if self.value then
PlaySound("igMainMenuOptionCheckBoxOn")
else
PlaySound("igMainMenuOptionCheckBoxOff")
end
UpdateToggle(self)
self:Fire("OnValueChanged", 1, self.value)
end
-- exported
local function SetValue(self, value)
self.value = value
UpdateToggle(self)
end
-- exported
local function GetValue(self)
return self.value
end
local function Constructor()
local self = ItemBase.Create(widgetType)
self.frame:SetScript("OnClick", Frame_OnClick)
self.SetValue = SetValue
self.GetValue = GetValue
self.OnRelease = OnRelease
AceGUI:RegisterAsWidget(self)
return self
end
AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion + ItemBase.version)
end
-- Item: Menu
-- Shows a submenu on mouse over
-- Does not close the pullout on click
do
local widgetType = "Dropdown-Item-Menu"
local widgetVersion = 2
local function OnEnter()
local self = this.obj
self:Fire("OnEnter")
if self.specialOnEnter then
self.specialOnEnter(self)
end
self.highlight:Show()
if not self.disabled and self.submenu then
self.submenu:Open("TOPLEFT", self.frame, "TOPRIGHT", self.pullout:GetRightBorderWidth(), 0, self.frame:GetFrameLevel() + 100)
end
end
local function OnHide()
local self = this.obj
if self.submenu then
self.submenu:Close()
end
end
-- exported
local function SetMenu(self, menu)
assert(menu.type == "Dropdown-Pullout")
self.submenu = menu
end
-- exported
local function CloseMenu(self)
self.submenu:Close()
end
local function Constructor()
local self = ItemBase.Create(widgetType)
self.sub:Show()
self.frame:SetScript("OnEnter", OnEnter)
self.frame:SetScript("OnHide", OnHide)
self.SetMenu = SetMenu
self.CloseMenu = CloseMenu
AceGUI:RegisterAsWidget(self)
return self
end
AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion + ItemBase.version)
end
-- Item: Separator
-- A single line to separate items
do
local widgetType = "Dropdown-Item-Separator"
local widgetVersion = 2
-- exported, override
local function SetDisabled(self, disabled)
ItemBase.SetDisabled(self, disabled)
self.useHighlight = false
end
local function Constructor()
local self = ItemBase.Create(widgetType)
self.SetDisabled = SetDisabled
local line = self.frame:CreateTexture(nil, "OVERLAY")
line:SetHeight(1)
line:SetTexture(.5, .5, .5)
line:SetPoint("LEFT", self.frame, "LEFT", 10, 0)
line:SetPoint("RIGHT", self.frame, "RIGHT", -10, 0)
self.text:Hide()
self.useHighlight = false
AceGUI:RegisterAsWidget(self)
return self
end
AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion + ItemBase.version)
end
@@ -0,0 +1,726 @@
--[[ $Id: AceGUIWidget-DropDown.lua 1116 2014-10-12 08:15:46Z nevcairiel $ ]]--
local AceGUI = LibStub("AceGUI-3.0")
local AceCore = LibStub("AceCore-3.0")
-- Lua APIs
local min, max, floor = math.min, math.max, math.floor
local pairs, ipairs, type = pairs, ipairs, type
local tsort, tinsert, tgetn, tsetn = table.sort, table.insert, table.getn, table.setn
-- WoW APIs
local PlaySound = PlaySound
local UIParent, CreateFrame = UIParent, CreateFrame
local _G = AceCore._G
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
-- List them here for Mikk's FindGlobals script
-- GLOBALS: CLOSE
local fixlevels = AceGUI.fixlevels
local fixstrata = AceGUI.fixstrata
do
local widgetType = "Dropdown-Pullout"
local widgetVersion = 3
--[[ Static data ]]--
local backdrop = {
bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
edgeSize = 32,
tileSize = 32,
tile = true,
insets = { left = 11, right = 12, top = 12, bottom = 11 },
}
local sliderBackdrop = {
bgFile = "Interface\\Buttons\\UI-SliderBar-Background",
edgeFile = "Interface\\Buttons\\UI-SliderBar-Border",
tile = true, tileSize = 8, edgeSize = 8,
insets = { left = 3, right = 3, top = 3, bottom = 3 }
}
local defaultWidth = 200
local defaultMaxHeight = 600
--[[ UI Event Handlers ]]--
-- HACK: This should be no part of the pullout, but there
-- is no other 'clean' way to response to any item-OnEnter
-- Used to close Submenus when an other item is entered
local function OnEnter(item)
local self = item.pullout
for k, v in ipairs(self.items) do
if v.CloseMenu and v ~= item then
v:CloseMenu()
end
end
end
-- See the note in Constructor() for each scroll related function
local function OnMouseWheel()
this.obj:MoveScroll(arg1)
end
local function OnScrollValueChanged()
this.obj:SetScroll(arg1)
end
local function OnSizeChanged()
this.obj:FixScroll()
end
--[[ Exported methods ]]--
-- exported
local function SetScroll(self, value)
local status = self.scrollStatus
local frame, child = self.scrollFrame, self.itemFrame
local height, viewheight = frame:GetHeight(), child:GetHeight()
local offset
if height > viewheight then
offset = 0
else
offset = floor((viewheight - height) / 1000 * value)
end
child:ClearAllPoints()
child:SetPoint("TOPLEFT", frame, "TOPLEFT", 0, offset)
child:SetPoint("TOPRIGHT", frame, "TOPRIGHT", self.slider:IsShown() and -12 or 0, offset)
status.offset = offset
status.scrollvalue = value
end
-- exported
local function MoveScroll(self, value)
local status = self.scrollStatus
local frame, child = self.scrollFrame, self.itemFrame
local height, viewheight = frame:GetHeight(), child:GetHeight()
if height > viewheight then
self.slider:Hide()
else
self.slider:Show()
local diff = height - viewheight
local delta = 1
if value < 0 then
delta = -1
end
self.slider:SetValue(min(max(status.scrollvalue + delta*(1000/(diff/45)),0), 1000))
end
end
-- exported
local function FixScroll(self)
local status = self.scrollStatus
local frame, child = self.scrollFrame, self.itemFrame
local height, viewheight = frame:GetHeight(), child:GetHeight()
local offset = status.offset or 0
if viewheight < height then
self.slider:Hide()
child:SetPoint("TOPRIGHT", frame, "TOPRIGHT", 0, offset)
self.slider:SetValue(0)
else
self.slider:Show()
local value = (offset / (viewheight - height) * 1000)
if value > 1000 then value = 1000 end
self.slider:SetValue(value)
self:SetScroll(value)
if value < 1000 then
child:ClearAllPoints()
child:SetPoint("TOPLEFT", frame, "TOPLEFT", 0, offset)
child:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -12, offset)
status.offset = offset
end
end
end
-- exported, AceGUI callback
local function OnAcquire(self)
self.frame:SetParent(UIParent)
--self.itemFrame:SetToplevel(true)
end
-- exported, AceGUI callback
local function OnRelease(self)
self:Clear()
self.frame:ClearAllPoints()
self.frame:Hide()
end
-- exported
local function AddItem(self, item)
tinsert(self.items, item)
local h = tgetn(self.items) * 16
self.itemFrame:SetHeight(h)
self.frame:SetHeight(min(h + 34, self.maxHeight)) -- +34: 20 for scrollFrame placement (10 offset) and +14 for item placement
item.frame:SetPoint("LEFT", self.itemFrame, "LEFT")
item.frame:SetPoint("RIGHT", self.itemFrame, "RIGHT")
item:SetPullout(self)
item:SetOnEnter(OnEnter)
end
-- exported
local function Open(self, point, relFrame, relPoint, x, y)
local items = self.items
local frame = self.frame
local itemFrame = self.itemFrame
frame:SetPoint(point, relFrame, relPoint, x, y)
local height = 8
for i, item in pairs(items) do
if i == 1 then
item:SetPoint("TOP", itemFrame, "TOP", 0, -2)
else
item:SetPoint("TOP", items[i-1].frame, "BOTTOM", 0, 1)
end
item:Show()
height = height + 16
end
itemFrame:SetHeight(height)
fixstrata("TOOLTIP", frame)
frame:Show()
self:Fire("OnOpen")
end
-- exported
local function Close(self)
self.frame:Hide()
self:Fire("OnClose")
end
-- exported
local function Clear(self)
local items = self.items
for i, item in pairs(items) do
AceGUI:Release(item)
items[i] = nil
end
tsetn(items,0)
end
-- exported
local function IterateItems(self)
return ipairs(self.items)
end
-- exported
local function SetHideOnLeave(self, val)
self.hideOnLeave = val
end
-- exported
local function SetMaxHeight(self, height)
self.maxHeight = height or defaultMaxHeight
if self.frame:GetHeight() > height then
self.frame:SetHeight(height)
elseif (self.itemFrame:GetHeight() + 34) < height then
self.frame:SetHeight(self.itemFrame:GetHeight() + 34) -- see :AddItem
end
end
-- exported
local function GetRightBorderWidth(self)
return 6 + (self.slider:IsShown() and 12 or 0)
end
-- exported
local function GetLeftBorderWidth(self)
return 6
end
--[[ Constructor ]]--
local function Constructor()
local count = AceGUI:GetNextWidgetNum(widgetType)
local frame = CreateFrame("Frame", "AceGUI30Pullout"..count, UIParent)
local self = {}
self.count = count
self.type = widgetType
self.frame = frame
frame.obj = self
self.OnAcquire = OnAcquire
self.OnRelease = OnRelease
self.AddItem = AddItem
self.Open = Open
self.Close = Close
self.Clear = Clear
self.IterateItems = IterateItems
self.SetHideOnLeave = SetHideOnLeave
self.SetScroll = SetScroll
self.MoveScroll = MoveScroll
self.FixScroll = FixScroll
self.SetMaxHeight = SetMaxHeight
self.GetRightBorderWidth = GetRightBorderWidth
self.GetLeftBorderWidth = GetLeftBorderWidth
self.items = {}
self.scrollStatus = {
scrollvalue = 0,
}
self.maxHeight = defaultMaxHeight
frame:SetBackdrop(backdrop)
frame:SetBackdropColor(0, 0, 0)
frame:SetFrameStrata("FULLSCREEN_DIALOG")
frame:SetClampedToScreen(true)
frame:SetWidth(defaultWidth)
frame:SetHeight(self.maxHeight)
--frame:SetToplevel(true)
-- NOTE: The whole scroll frame code is copied from the AceGUI-3.0 widget ScrollFrame
local scrollFrame = CreateFrame("ScrollFrame", nil, frame)
local itemFrame = CreateFrame("Frame", nil, scrollFrame)
self.scrollFrame = scrollFrame
self.itemFrame = itemFrame
scrollFrame.obj = self
itemFrame.obj = self
local slider = CreateFrame("Slider", "AceGUI30PulloutScrollbar"..count, scrollFrame)
slider:SetOrientation("VERTICAL")
slider:SetHitRectInsets(0, 0, -10, 0)
slider:SetBackdrop(sliderBackdrop)
slider:SetWidth(8)
slider:SetThumbTexture("Interface\\Buttons\\UI-SliderBar-Button-Vertical")
slider:SetFrameStrata("FULLSCREEN_DIALOG")
self.slider = slider
slider.obj = self
scrollFrame:SetScrollChild(itemFrame)
scrollFrame:SetPoint("TOPLEFT", frame, "TOPLEFT", 6, -12)
scrollFrame:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -6, 12)
scrollFrame:EnableMouseWheel(true)
scrollFrame:SetScript("OnMouseWheel", OnMouseWheel)
scrollFrame:SetScript("OnSizeChanged", OnSizeChanged)
scrollFrame:SetToplevel(true)
scrollFrame:SetFrameStrata("FULLSCREEN_DIALOG")
itemFrame:SetPoint("TOPLEFT", scrollFrame, "TOPLEFT", 0, 0)
itemFrame:SetPoint("TOPRIGHT", scrollFrame, "TOPRIGHT", -12, 0)
itemFrame:SetHeight(400)
itemFrame:SetToplevel(true)
itemFrame:SetFrameStrata("FULLSCREEN_DIALOG")
slider:SetPoint("TOPLEFT", scrollFrame, "TOPRIGHT", -16, 0)
slider:SetPoint("BOTTOMLEFT", scrollFrame, "BOTTOMRIGHT", -16, 0)
slider:SetScript("OnValueChanged", OnScrollValueChanged)
slider:SetMinMaxValues(0, 1000)
slider:SetValueStep(1)
slider:SetValue(0)
scrollFrame:Show()
itemFrame:Show()
slider:Hide()
self:FixScroll()
AceGUI:RegisterAsWidget(self)
return self
end
AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion)
end
do
local widgetType = "Dropdown"
local widgetVersion = 30
--[[ Static data ]]--
--[[ UI event handler ]]--
local function Control_OnEnter()
this.obj.button:LockHighlight()
this.obj:Fire("OnEnter")
end
local function Control_OnLeave()
this.obj.button:UnlockHighlight()
this.obj:Fire("OnLeave")
end
local function Dropdown_OnHide()
local self = this.obj
if self.open then
self.pullout:Close()
end
end
local function Dropdown_TogglePullout()
local self = this.obj
PlaySound("igMainMenuOptionCheckBoxOn") -- missleading name, but the Blizzard code uses this sound
if self.open then
self.open = nil
self.pullout:Close()
AceGUI:ClearFocus()
else
self.open = true
self.pullout:SetWidth(self.pulloutWidth or self.frame:GetWidth())
self.pullout:Open("TOPLEFT", self.frame, "BOTTOMLEFT", 0, self.label:IsShown() and -2 or 0)
AceGUI:SetFocus(self)
end
end
local function OnPulloutOpen(this)
local self = this.userdata.obj
local value = self.value
if not self.multiselect then
for i, item in this:IterateItems() do
item:SetValue(item.userdata.value == value)
end
end
self.open = true
self:Fire("OnOpened")
end
local function OnPulloutClose(this)
local self = this.userdata.obj
self.open = nil
self:Fire("OnClosed")
end
local function ShowMultiText(self)
local text
for i, widget in self.pullout:IterateItems() do
if widget.type == "Dropdown-Item-Toggle" then
if widget:GetValue() then
if text then
text = text..", "..widget:GetText()
else
text = widget:GetText()
end
end
end
end
self:SetText(text)
end
local function OnItemValueChanged(this, event, _, checked)
local self = this.userdata.obj
if self.multiselect then
self:Fire("OnValueChanged", 2, this.userdata.value, checked)
ShowMultiText(self)
else
if checked then
self:SetValue(this.userdata.value)
self:Fire("OnValueChanged", 1, this.userdata.value)
this:SetValue(false)
else
self:SetValue(nil)
self:Fire("OnValueChanged", 1, nil)
this:SetValue(true)
end
if self.open then
self.pullout:Close()
end
end
end
--[[ Exported methods ]]--
-- exported, AceGUI callback
local function OnAcquire(self)
local pullout = AceGUI:Create("Dropdown-Pullout")
self.pullout = pullout
pullout.userdata.obj = self
pullout:SetCallback("OnClose", OnPulloutClose)
pullout:SetCallback("OnOpen", OnPulloutOpen)
self.pullout.frame:SetFrameLevel(self.frame:GetFrameLevel() + 1)
local frame = self.pullout.frame
fixlevels(frame)
self:SetHeight(44)
self:SetWidth(200)
self:SetLabel()
self:SetPulloutWidth(nil)
end
-- exported, AceGUI callback
local function OnRelease(self)
if self.open then
self.pullout:Close()
end
AceGUI:Release(self.pullout)
self.pullout = nil
self:SetText("")
self:SetDisabled(false)
self:SetMultiselect(false)
self.value = nil
self.list = nil
self.open = nil
self.hasClose = nil
self.frame:ClearAllPoints()
self.frame:Hide()
end
-- exported
local function SetDisabled(self, disabled)
self.disabled = disabled
if disabled then
self.text:SetTextColor(0.5,0.5,0.5)
self.button:Disable()
self.button_cover:Disable()
self.label:SetTextColor(0.5,0.5,0.5)
else
self.button:Enable()
self.button_cover:Enable()
self.label:SetTextColor(1,.82,0)
self.text:SetTextColor(1,1,1)
end
end
-- exported
local function ClearFocus(self)
if self.open then
self.pullout:Close()
end
end
-- exported
local function SetText(self, text)
self.text:SetText(text or "")
end
-- exported
local function SetLabel(self, text)
if text and text ~= "" then
self.label:SetText(text)
self.label:Show()
self.dropdown:SetPoint("TOPLEFT",self.frame,"TOPLEFT",-15,-14)
self:SetHeight(40)
self.alignoffset = 26
else
self.label:SetText("")
self.label:Hide()
self.dropdown:SetPoint("TOPLEFT",self.frame,"TOPLEFT",-15,0)
self:SetHeight(26)
self.alignoffset = 12
end
end
-- exported
local function SetValue(self, value)
if self.list then
self:SetText(self.list[value] or "")
end
self.value = value
end
-- exported
local function GetValue(self)
return self.value
end
-- exported
local function SetItemValue(self, item, value)
if not self.multiselect then return end
for i, widget in self.pullout:IterateItems() do
if widget.userdata.value == item then
if widget.SetValue then
widget:SetValue(value)
end
end
end
ShowMultiText(self)
end
-- exported
local function SetItemDisabled(self, item, disabled)
for i, widget in self.pullout:IterateItems() do
if widget.userdata.value == item then
widget:SetDisabled(disabled)
end
end
end
local function AddListItem(self, value, text, itemType)
if not itemType then itemType = "Dropdown-Item-Toggle" end
local exists = AceGUI:GetWidgetVersion(itemType)
if not exists then error(("The given item type, %q, does not exist within AceGUI-3.0"):format(tostring(itemType)), 2) end
local item = AceGUI:Create(itemType)
item:SetText(text)
item.userdata.obj = self
item.userdata.value = value
item:SetCallback("OnValueChanged", OnItemValueChanged)
self.pullout:AddItem(item)
end
local function AddCloseButton(self)
if not self.hasClose then
local close = AceGUI:Create("Dropdown-Item-Execute")
close:SetText(CLOSE)
self.pullout:AddItem(close)
self.hasClose = true
end
end
-- exported
local sortlist = {}
local function SetList(self, list, order, itemType)
self.list = list
self.pullout:Clear()
self.hasClose = nil
if not list then return end
if type(order) ~= "table" then
for v in pairs(list) do
tinsert(sortlist, v)
end
tsort(sortlist)
for i, key in ipairs(sortlist) do
AddListItem(self, key, list[key], itemType)
sortlist[i] = nil
end
tsetn(sortlist,0)
else
for i, key in ipairs(order) do
AddListItem(self, key, list[key], itemType)
end
end
if self.multiselect then
ShowMultiText(self)
AddCloseButton(self)
end
end
-- exported
local function AddItem(self, value, text, itemType)
if self.list then
self.list[value] = text
AddListItem(self, value, text, itemType)
end
end
-- exported
local function SetMultiselect(self, multi)
self.multiselect = multi
if multi then
ShowMultiText(self)
AddCloseButton(self)
end
end
-- exported
local function GetMultiselect(self)
return self.multiselect
end
local function SetPulloutWidth(self, width)
self.pulloutWidth = width
end
--[[ Constructor ]]--
local function Constructor()
local count = AceGUI:GetNextWidgetNum(widgetType)
local frame = CreateFrame("Frame", nil, UIParent)
local dropdown = CreateFrame("Frame", "AceGUI30DropDown"..count, frame, "UIDropDownMenuTemplate")
local self = {}
self.type = widgetType
self.frame = frame
self.dropdown = dropdown
self.count = count
frame.obj = self
dropdown.obj = self
self.OnRelease = OnRelease
self.OnAcquire = OnAcquire
self.ClearFocus = ClearFocus
self.SetText = SetText
self.SetValue = SetValue
self.GetValue = GetValue
self.SetList = SetList
self.SetLabel = SetLabel
self.SetDisabled = SetDisabled
self.AddItem = AddItem
self.SetMultiselect = SetMultiselect
self.GetMultiselect = GetMultiselect
self.SetItemValue = SetItemValue
self.SetItemDisabled = SetItemDisabled
self.SetPulloutWidth = SetPulloutWidth
self.alignoffset = 26
frame:SetScript("OnHide",Dropdown_OnHide)
dropdown:ClearAllPoints()
dropdown:SetPoint("TOPLEFT",frame,"TOPLEFT",-15,0)
dropdown:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",17,0)
dropdown:SetScript("OnHide", nil)
local left = _G[dropdown:GetName() .. "Left"]
local middle = _G[dropdown:GetName() .. "Middle"]
local right = _G[dropdown:GetName() .. "Right"]
middle:ClearAllPoints()
right:ClearAllPoints()
middle:SetPoint("LEFT", left, "RIGHT", 0, 0)
middle:SetPoint("RIGHT", right, "LEFT", 0, 0)
right:SetPoint("TOPRIGHT", dropdown, "TOPRIGHT", 0, 17)
local button = _G[dropdown:GetName() .. "Button"]
self.button = button
button.obj = self
button:SetScript("OnEnter",Control_OnEnter)
button:SetScript("OnLeave",Control_OnLeave)
button:SetScript("OnClick",Dropdown_TogglePullout)
local button_cover = CreateFrame("BUTTON",nil,self.frame)
self.button_cover = button_cover
button_cover.obj = self
button_cover:SetPoint("TOPLEFT",self.frame,"BOTTOMLEFT",0,25)
button_cover:SetPoint("BOTTOMRIGHT",self.frame,"BOTTOMRIGHT")
button_cover:SetScript("OnEnter",Control_OnEnter)
button_cover:SetScript("OnLeave",Control_OnLeave)
button_cover:SetScript("OnClick",Dropdown_TogglePullout)
local text = _G[dropdown:GetName() .. "Text"]
self.text = text
text.obj = self
text:ClearAllPoints()
text:SetPoint("RIGHT", right, "RIGHT" ,-43, 2)
text:SetPoint("LEFT", left, "LEFT", 25, 2)
local label = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall")
label:SetPoint("TOPLEFT",frame,"TOPLEFT",0,0)
label:SetPoint("TOPRIGHT",frame,"TOPRIGHT",0,0)
label:SetJustifyH("LEFT")
label:SetHeight(18)
label:Hide()
self.label = label
AceGUI:RegisterAsWidget(self)
return self
end
AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion)
end
@@ -0,0 +1,367 @@
--[[-----------------------------------------------------------------------------
EditBox Widget
-------------------------------------------------------------------------------]]
local Type, Version = "EditBox", 26
local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
local AceCore = LibStub("AceCore-3.0")
local hooksecurefunc = AceCore.hooksecurefunc
local _G = AceCore._G
local GetCursorInfo = _G.GetCursorInfo
-- Lua APIs
local tostring, pairs = tostring, pairs
-- WoW APIs
local PlaySound = PlaySound
local GetCursorInfo, ClearCursor, GetSpellInfo = GetCursorInfo, ClearCursor, GetSpellInfo
local CreateFrame, UIParent = CreateFrame, UIParent
local strlen = string.len
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
-- List them here for Mikk's FindGlobals script
-- GLOBALS: AceGUIEditBoxInsertLink, ChatFontNormal, OKAY
--[[-----------------------------------------------------------------------------
Support functions
-------------------------------------------------------------------------------]]
if not AceGUIEditBoxInsertLink then
-- upgradeable hook
hooksecurefunc("BankFrameItemButtonGeneric_OnClick",
function(button)
if button == "LeftButton" and IsShiftKeyDown() and not this.isBag then
return _G.AceGUIEditBoxInsertLink(GetContainerItemLink(BANK_CONTAINER, this:GetID()))
end
end)
hooksecurefunc("ContainerFrameItemButton_OnClick",
function(button, ignoreModifiers)
if button == "LeftButton" and IsShiftKeyDown() and not ignoreModifiers then
return _G.AceGUIEditBoxInsertLink(GetContainerItemLink(this:GetParent():GetID(), this:GetID()))
end
end)
hooksecurefunc("KeyRingItemButton_OnClick",
function(button)
if button == "LeftButton" and IsShiftKeyDown() and not this.isBag then
return _G.AceGUIEditBoxInsertLink(GetContainerItemLink(KEYRING_CONTAINER, this:GetID()))
end
end)
hooksecurefunc("LootFrameItem_OnClick",
function(button)
if button == "LeftButton" and IsShiftKeyDown() then
return _G.AceGUIEditBoxInsertLink(GetLootSlotLink(this.slot))
end
end)
hooksecurefunc("SetItemRef",
function(link, text, button)
if IsShiftKeyDown() then
if strsub(link,1,6) == "player" then
local name = strsub(link,8)
if name and (strlen(name) > 0) then
return _G.AceGUIEditBoxInsertLink(name)
end
else
return _G.AceGUIEditBoxInsertLink(text)
end
end
end)
hooksecurefunc("MerchantItemButton_OnClick",
function(button, ignoreModifiers)
if MerchantFrame.selectedTab == 1 and button == "LeftButton" and IsShiftKeyDown() and not ignoreModifiers then
return _G.AceGUIEditBoxInsertLink(GetMerchantItemLink(this:GetID()))
end
end)
hooksecurefunc("PaperDollItemSlotButton_OnClick",
function(button, ignoreModifiers)
if button == "LeftButton" and IsShiftKeyDown() and not ignoreModifiers then
return _G.AceGUIEditBoxInsertLink(GetInventoryItemLink("player", this:GetID()))
end
end)
hooksecurefunc("QuestItem_OnClick",
function()
if IsShiftKeyDown() and this.rewardType ~= "spell" then
return _G.AceGUIEditBoxInsertLink(GetQuestItemLink(this.type, this:GetID()))
end
end)
hooksecurefunc("QuestRewardItem_OnClick",
function()
if IsShiftKeyDown() and this.rewardType ~= "spell" then
return _G.AceGUIEditBoxInsertLink(GetQuestItemLink(this.type, this:GetID()))
end
end)
hooksecurefunc("QuestLogTitleButton_OnClick",
function(button)
if IsShiftKeyDown() and (not this.isHeader) then
return _G.AceGUIEditBoxInsertLink(gsub(this:GetText(), " *(.*)", "%1"))
end
end)
hooksecurefunc("QuestLogRewardItem_OnClick",
function()
if IsShiftKeyDown() and this.rewardType ~= "spell" then
return _G.AceGUIEditBoxInsertLink(GetQuestLogItemLink(this.type, this:GetID()))
end
end)
hooksecurefunc("SpellButton_OnClick",
function(drag)
local id = SpellBook_GetSpellID(this:GetID())
if id <= MAX_SPELLS and (not drag) and IsShiftKeyDown() then
local spellName, subSpellName = GetSpellName(id, SpellBookFrame.bookType)
if spellName and not IsSpellPassive(id, SpellBookFrame.bookType) then
if subSpellName and (strlen(subSpellName) > 0) then
_G.AceGUIEditBoxInsertLink(spellName.."("..subSpellName..")");
else
_G.AceGUIEditBoxInsertLink(spellName);
end
end
end
end)
end
function _G.AceGUIEditBoxInsertLink(text)
for i = 1, AceGUI:GetWidgetCount(Type) do
local editbox = _G["AceGUI-3.0EditBox"..i]
if editbox and editbox:IsVisible() and editbox.hasfocus then
editbox:Insert(text)
return true
end
end
end
local function ShowButton(self)
if not self.disablebutton then
self.button:Show()
self.editbox:SetTextInsets(0, 20, 3, 3)
end
end
local function HideButton(self)
self.button:Hide()
self.editbox:SetTextInsets(0, 0, 3, 3)
end
--[[-----------------------------------------------------------------------------
Scripts
-------------------------------------------------------------------------------]]
local function Control_OnEnter()
this.obj:Fire("OnEnter")
end
local function Control_OnLeave()
this.obj:Fire("OnLeave")
end
local function Frame_OnShowFocus()
this.obj.editbox:SetFocus()
this:SetScript("OnShow", nil)
end
local function EditBox_OnEscapePressed()
AceGUI:ClearFocus()
end
local function EditBox_OnEnterPressed()
local self = this.obj
local value = this:GetText()
local cancel = self:Fire("OnEnterPressed", 1, value)
if not cancel then
PlaySound("igMainMenuOptionCheckBoxOn")
HideButton(self)
end
end
local function EditBox_OnReceiveDrag()
if not GetCursorInfo then return end
local self = this.obj
local type, id, info = GetCursorInfo()
if type == "item" then
self:SetText(info)
self:Fire("OnEnterPressed", 1, info)
ClearCursor()
elseif type == "spell" then
local spell, rank = GetSpellName(id, info)
if rank ~= "" then spell = spell.."("..rank..")" end
self:SetText(spell)
self:Fire("OnEnterPressed", 1, spell)
ClearCursor()
elseif type == "macro" then
local name = GetMacroInfo(id)
self:SetText(name)
self:Fire("OnEnterPressed", 1, name)
ClearCursor()
end
HideButton(self)
AceGUI:ClearFocus()
end
local function EditBox_OnTextChanged()
local self = this.obj
local value = this:GetText()
if tostring(value) ~= tostring(self.lasttext) then
self:Fire("OnTextChanged", 1, value)
self.lasttext = value
ShowButton(self)
end
end
local function EditBox_OnFocusGained()
this.hasfocus = true
AceGUI:SetFocus(this.obj)
end
local function EditBox_OnFocusLost()
this.hasfocus = nil
end
local function Button_OnClick()
local editbox = this.obj.editbox
editbox:ClearFocus()
this = editbox -- Ace3v: this is kinda hack here
EditBox_OnEnterPressed()
end
--[[-----------------------------------------------------------------------------
Methods
-------------------------------------------------------------------------------]]
local methods = {
["OnAcquire"] = function(self)
-- height is controlled by SetLabel
self:SetWidth(200)
self:SetDisabled(false)
self:SetLabel()
self:SetText()
self:DisableButton(false)
self:SetMaxLetters(0)
end,
["OnRelease"] = function(self)
self:ClearFocus()
end,
["SetDisabled"] = function(self, disabled)
self.disabled = disabled
if disabled then
self.editbox:EnableMouse(false)
self.editbox:ClearFocus()
self.editbox:SetTextColor(0.5,0.5,0.5)
self.label:SetTextColor(0.5,0.5,0.5)
else
self.editbox:EnableMouse(true)
self.editbox:SetTextColor(1,1,1)
self.label:SetTextColor(1,.82,0)
end
end,
["SetText"] = function(self, text)
self.lasttext = text or ""
self.editbox:SetText(text or "")
self.editbox:HighlightText(0)
HideButton(self)
end,
["GetText"] = function(self, text)
return self.editbox:GetText()
end,
["SetLabel"] = function(self, text)
if text and text ~= "" then
self.label:SetText(text)
self.label:Show()
self.editbox:SetPoint("TOPLEFT",self.frame,"TOPLEFT",7,-18)
self:SetHeight(44)
self.alignoffset = 30
else
self.label:SetText("")
self.label:Hide()
self.editbox:SetPoint("TOPLEFT",self.frame,"TOPLEFT",7,0)
self:SetHeight(26)
self.alignoffset = 12
end
end,
["DisableButton"] = function(self, disabled)
self.disablebutton = disabled
if disabled then
HideButton(self)
end
end,
["SetMaxLetters"] = function (self, num)
self.editbox:SetMaxLetters(num or 0)
end,
["ClearFocus"] = function(self)
self.editbox:ClearFocus()
self.frame:SetScript("OnShow", nil)
end,
["SetFocus"] = function(self)
self.editbox:SetFocus()
if not self.frame:IsShown() then
self.frame:SetScript("OnShow", Frame_OnShowFocus)
end
end,
["HighlightText"] = function(self, from, to)
self.editbox:HighlightText(from, to)
end
}
--[[-----------------------------------------------------------------------------
Constructor
-------------------------------------------------------------------------------]]
local function Constructor()
local num = AceGUI:GetNextWidgetNum(Type)
local frame = CreateFrame("Frame", nil, UIParent)
frame:Hide()
local editbox = CreateFrame("EditBox", "AceGUI-3.0EditBox"..num, frame, "InputBoxTemplate")
editbox:SetAutoFocus(false)
editbox:SetFontObject(ChatFontNormal)
editbox:SetScript("OnEnter", Control_OnEnter)
editbox:SetScript("OnLeave", Control_OnLeave)
editbox:SetScript("OnEscapePressed", EditBox_OnEscapePressed)
editbox:SetScript("OnEnterPressed", EditBox_OnEnterPressed)
editbox:SetScript("OnTextChanged", EditBox_OnTextChanged)
editbox:SetScript("OnReceiveDrag", EditBox_OnReceiveDrag)
editbox:SetScript("OnMouseDown", EditBox_OnReceiveDrag)
editbox:SetScript("OnEditFocusGained", EditBox_OnFocusGained)
editbox:SetScript("OnEditFocusLost", EditBox_OnFocusLost)
editbox:SetTextInsets(0, 0, 3, 3)
editbox:SetMaxLetters(256)
editbox:SetPoint("BOTTOMLEFT", 6, 0)
editbox:SetPoint("BOTTOMRIGHT", 0, 0)
editbox:SetHeight(19)
local label = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
label:SetPoint("TOPLEFT", 0, -2)
label:SetPoint("TOPRIGHT", 0, -2)
label:SetJustifyH("LEFT")
label:SetHeight(18)
local button = CreateFrame("Button", nil, editbox, "UIPanelButtonTemplate")
button:SetWidth(40)
button:SetHeight(20)
button:SetPoint("RIGHT", -2, 0)
button:SetText(OKAY)
button:SetScript("OnClick", Button_OnClick)
button:Hide()
local widget = {
alignoffset = 30,
editbox = editbox,
label = label,
button = button,
frame = frame,
type = Type
}
for method, func in pairs(methods) do
widget[method] = func
end
editbox.obj, button.obj = widget, widget
return AceGUI:RegisterAsWidget(widget)
end
AceGUI:RegisterWidgetType(Type, Constructor, Version)
@@ -0,0 +1,78 @@
--[[-----------------------------------------------------------------------------
Heading Widget
-------------------------------------------------------------------------------]]
local Type, Version = "Heading", 20
local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
-- Lua APIs
local pairs = pairs
-- WoW APIs
local CreateFrame, UIParent = CreateFrame, UIParent
--[[-----------------------------------------------------------------------------
Methods
-------------------------------------------------------------------------------]]
local methods = {
["OnAcquire"] = function(self)
self:SetText()
self:SetFullWidth()
self:SetHeight(18)
end,
-- ["OnRelease"] = nil,
["SetText"] = function(self, text)
self.label:SetText(text or "")
if text and text ~= "" then
self.left:SetPoint("RIGHT", self.label, "LEFT", -5, 0)
self.right:Show()
else
self.left:SetPoint("RIGHT", -3, 0)
self.right:Hide()
end
end
}
--[[-----------------------------------------------------------------------------
Constructor
-------------------------------------------------------------------------------]]
local function Constructor()
local frame = CreateFrame("Frame", nil, UIParent)
frame:Hide()
local label = frame:CreateFontString(nil, "BACKGROUND", "GameFontNormal")
label:SetPoint("TOP",0,0)
label:SetPoint("BOTTOM",0,0)
label:SetJustifyH("CENTER")
local left = frame:CreateTexture(nil, "BACKGROUND")
left:SetHeight(8)
left:SetPoint("LEFT", 3, 0)
left:SetPoint("RIGHT", label, "LEFT", -5, 0)
left:SetTexture("Interface\\Tooltips\\UI-Tooltip-Border")
left:SetTexCoord(0.81, 0.94, 0.5, 1)
local right = frame:CreateTexture(nil, "BACKGROUND")
right:SetHeight(8)
right:SetPoint("RIGHT", -3, 0)
right:SetPoint("LEFT", label, "RIGHT", 5, 0)
right:SetTexture("Interface\\Tooltips\\UI-Tooltip-Border")
right:SetTexCoord(0.81, 0.94, 0.5, 1)
local widget = {
label = label,
left = left,
right = right,
frame = frame,
type = Type
}
for method, func in pairs(methods) do
widget[method] = func
end
return AceGUI:RegisterAsWidget(widget)
end
AceGUI:RegisterWidgetType(Type, Constructor, Version)
@@ -0,0 +1,139 @@
--[[-----------------------------------------------------------------------------
Icon Widget
-------------------------------------------------------------------------------]]
local Type, Version = "Icon", 21
local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
-- Lua APIs
local pairs, print = pairs, print
-- WoW APIs
local CreateFrame, UIParent = CreateFrame, UIParent
--[[-----------------------------------------------------------------------------
Scripts
-------------------------------------------------------------------------------]]
local function Control_OnEnter()
this.obj:Fire("OnEnter")
end
local function Control_OnLeave()
this.obj:Fire("OnLeave")
end
local function Button_OnClick()
this.obj:Fire("OnClick", 1, arg1)
AceGUI:ClearFocus()
end
--[[-----------------------------------------------------------------------------
Methods
-------------------------------------------------------------------------------]]
local methods = {
["OnAcquire"] = function(self)
self:SetHeight(110)
self:SetWidth(110)
self:SetLabel()
self:SetImage(nil)
self:SetImageSize(64, 64)
self:SetDisabled(false)
end,
-- ["OnRelease"] = nil,
["SetLabel"] = function(self, text)
if text and text ~= "" then
self.label:Show()
self.label:SetText(text)
self:SetHeight(self.image:GetHeight() + 25)
else
self.label:Hide()
self:SetHeight(self.image:GetHeight() + 10)
end
end,
["SetImage"] = function(self, path, a1,a2,a3,a4,a5,a6,a7,a8)
local image = self.image
image:SetTexture(path)
if image:GetTexture() then
if a4 or a8 then
image:SetTexCoord(a1,a2,a3,a4,a5,a6,a7,a8)
else
image:SetTexCoord(0, 1, 0, 1)
end
end
end,
["SetImageSize"] = function(self, width, height)
self.image:SetWidth(width)
self.image:SetHeight(height)
--self.frame:SetWidth(width + 30)
if self.label:IsShown() then
self:SetHeight(height + 25)
else
self:SetHeight(height + 10)
end
end,
["SetDisabled"] = function(self, disabled)
self.disabled = disabled
if disabled then
self.frame:Disable()
self.label:SetTextColor(0.5, 0.5, 0.5)
self.image:SetVertexColor(0.5, 0.5, 0.5, 0.5)
else
self.frame:Enable()
self.label:SetTextColor(1, 1, 1)
self.image:SetVertexColor(1, 1, 1, 1)
end
end
}
--[[-----------------------------------------------------------------------------
Constructor
-------------------------------------------------------------------------------]]
local function Constructor()
local frame = CreateFrame("Button", nil, UIParent)
frame:Hide()
frame:EnableMouse(true)
frame:SetScript("OnEnter", Control_OnEnter)
frame:SetScript("OnLeave", Control_OnLeave)
frame:SetScript("OnClick", Button_OnClick)
local label = frame:CreateFontString(nil, "BACKGROUND", "GameFontHighlight")
label:SetPoint("BOTTOMLEFT")
label:SetPoint("BOTTOMRIGHT")
label:SetJustifyH("CENTER")
label:SetJustifyV("TOP")
label:SetHeight(18)
local image = frame:CreateTexture(nil, "BACKGROUND")
image:SetWidth(64)
image:SetHeight(64)
image:SetPoint("TOP", 0, -5)
local highlight = frame:CreateTexture(nil, "HIGHLIGHT")
highlight:SetAllPoints(image)
highlight:SetTexture("Interface\\PaperDollInfoFrame\\UI-Character-Tab-Highlight")
highlight:SetTexCoord(0, 1, 0.23, 0.77)
highlight:SetBlendMode("ADD")
local widget = {
label = label,
image = image,
frame = frame,
type = Type
}
for method, func in pairs(methods) do
widget[method] = func
end
widget.SetText = function(self,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10) print("AceGUI-3.0-Icon: SetText is deprecated! Use SetLabel instead!"); self:SetLabel(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10) end
return AceGUI:RegisterAsWidget(widget)
end
AceGUI:RegisterWidgetType(Type, Constructor, Version)
@@ -0,0 +1,100 @@
--[[-----------------------------------------------------------------------------
InteractiveLabel Widget
-------------------------------------------------------------------------------]]
local Type, Version = "InteractiveLabel", 20
local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
-- Lua APIs
local pairs = pairs
-- WoW APIs
local CreateFrame, UIParent = CreateFrame, UIParent
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
-- List them here for Mikk's FindGlobals script
-- GLOBALS: GameFontHighlightSmall
--[[-----------------------------------------------------------------------------
Scripts
-------------------------------------------------------------------------------]]
local function Control_OnEnter()
this.obj:Fire("OnEnter")
end
local function Control_OnLeave()
this.obj:Fire("OnLeave")
end
local function Label_OnClick()
this.obj:Fire("OnClick", 1, arg1)
AceGUI:ClearFocus()
end
--[[-----------------------------------------------------------------------------
Methods
-------------------------------------------------------------------------------]]
local methods = {
["OnAcquire"] = function(self)
self:LabelOnAcquire()
self:SetHighlight()
self:SetHighlightTexCoord()
self:SetDisabled(false)
end,
-- ["OnRelease"] = nil,
["SetHighlight"] = function(self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
self.highlight:SetTexture(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
end,
["SetHighlightTexCoord"] = function(self, a1,a2,a3,a4,a5,a6,a7,a8)
if a4 or a8 then
self.highlight:SetTexCoord(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
else
self.highlight:SetTexCoord(0, 1, 0, 1)
end
end,
["SetDisabled"] = function(self,disabled)
self.disabled = disabled
if disabled then
self.frame:EnableMouse(false)
self.label:SetTextColor(0.5, 0.5, 0.5)
else
self.frame:EnableMouse(true)
self.label:SetTextColor(1, 1, 1)
end
end
}
--[[-----------------------------------------------------------------------------
Constructor
-------------------------------------------------------------------------------]]
local function Constructor()
-- create a Label type that we will hijack
local label = AceGUI:Create("Label")
local frame = label.frame
frame:EnableMouse(true)
frame:SetScript("OnEnter", Control_OnEnter)
frame:SetScript("OnLeave", Control_OnLeave)
frame:SetScript("OnMouseDown", Label_OnClick)
local highlight = frame:CreateTexture(nil, "HIGHLIGHT")
highlight:SetTexture(nil)
highlight:SetAllPoints()
highlight:SetBlendMode("ADD")
label.highlight = highlight
label.type = Type
label.LabelOnAcquire = label.OnAcquire
for method, func in pairs(methods) do
label[method] = func
end
return label
end
AceGUI:RegisterWidgetType(Type, Constructor, Version)
@@ -0,0 +1,259 @@
--[[-----------------------------------------------------------------------------
Keybinding Widget
Set Keybindings in the Config UI.
-------------------------------------------------------------------------------]]
local Type, Version = "Keybinding", 25
local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
-- Lua APIs
local pairs = pairs
-- WoW APIs
local IsShiftKeyDown, IsControlKeyDown, IsAltKeyDown = IsShiftKeyDown, IsControlKeyDown, IsAltKeyDown
local CreateFrame, UIParent = CreateFrame, UIParent
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
-- List them here for Mikk's FindGlobals script
-- GLOBALS: NOT_BOUND
--[[-----------------------------------------------------------------------------
Scripts
-------------------------------------------------------------------------------]]
local function Control_OnEnter()
this.obj:Fire("OnEnter")
end
local function Control_OnLeave()
this.obj:Fire("OnLeave")
end
local function Keybinding_OnHide()
local self = this.obj
this:EnableKeyboard(false)
this:EnableMouseWheel(false)
self.msgframe:Hide()
this:UnlockHighlight()
self.waitingForKey = nil
end
local ignoreKeys = {
["BUTTON1"] = true, ["BUTTON2"] = true,
["UNKNOWN"] = true,
["SHIFT"] = true, ["CTRL"] = true, ["ALT"] = true,
}
local function Keybinding_OnKeyDown()
local self = this.obj
if self.waitingForKey then
local keyPressed = arg1
if keyPressed == "ESCAPE" then
keyPressed = ""
else
if ignoreKeys[keyPressed] then return end
if IsShiftKeyDown() then
keyPressed = "SHIFT-"..keyPressed
end
if IsControlKeyDown() then
keyPressed = "CTRL-"..keyPressed
end
if IsAltKeyDown() then
keyPressed = "ALT-"..keyPressed
end
end
this:EnableKeyboard(false)
this:EnableMouseWheel(false)
self.msgframe:Hide()
this:UnlockHighlight()
self.waitingForKey = nil
if not self.disabled then
self:SetKey(keyPressed)
self:Fire("OnKeyChanged", 1, keyPressed)
end
end
end
local function Keybinding_OnMouseDown()
getglobal(this:GetName().."Left"):SetTexture("Interface\\Buttons\\UI-Panel-Button-Down");
getglobal(this:GetName().."Middle"):SetTexture("Interface\\Buttons\\UI-Panel-Button-Down");
getglobal(this:GetName().."Right"):SetTexture("Interface\\Buttons\\UI-Panel-Button-Down");
end
local function Keybinding_OnMouseUp()
getglobal(this:GetName().."Left"):SetTexture("Interface\\Buttons\\UI-Panel-Button-Up");
getglobal(this:GetName().."Middle"):SetTexture("Interface\\Buttons\\UI-Panel-Button-Up");
getglobal(this:GetName().."Right"):SetTexture("Interface\\Buttons\\UI-Panel-Button-Up");
local self = this.obj
if MouseIsOver(this) and not self.disabled then
if self.waitingForKey then
if arg1 ~= "LeftButton" and arg1 ~= "RightButton" then
Keybinding_OnKeyDown()
end
this:EnableKeyboard(false)
this:EnableMouseWheel(false)
self.msgframe:Hide()
this:UnlockHighlight()
self.waitingForKey = nil
else
this:EnableKeyboard(true)
this:EnableMouseWheel(true)
self.msgframe:Show()
this:LockHighlight()
self.waitingForKey = true
end
end
AceGUI:ClearFocus()
end
local function Keybinding_OnMouseWheel()
if arg1 >= 0 then
arg1 = "MOUSEWHEELUP"
else
arg1 = "MOUSEWHEELDOWN"
end
Keybinding_OnKeyDown()
end
--[[-----------------------------------------------------------------------------
Methods
-------------------------------------------------------------------------------]]
local methods = {
["OnAcquire"] = function(self)
self:SetWidth(200)
self:SetLabel("")
self:SetKey("")
self.waitingForKey = nil
self.msgframe:Hide()
self:SetDisabled(false)
self.button:EnableKeyboard(false)
self.button:EnableMouseWheel(false)
end,
-- ["OnRelease"] = nil,
["SetDisabled"] = function(self, disabled)
self.disabled = disabled
if disabled then
self.button:Disable()
self.label:SetTextColor(0.5,0.5,0.5)
else
self.button:Enable()
self.label:SetTextColor(1,1,1)
end
end,
["SetKey"] = function(self, key)
if (key or "") == "" then
self.button:SetText(NOT_BOUND)
self.text:SetFontObject("GameFontNormal")
else
self.button:SetText(key)
self.text:SetFontObject("GameFontHighlight")
end
end,
["GetKey"] = function(self)
local key = self.button:GetText()
if key == NOT_BOUND then
key = nil
end
return key
end,
["SetLabel"] = function(self, label)
self.label:SetText(label or "")
if (label or "") == "" then
self.alignoffset = nil
self:SetHeight(24)
else
self.alignoffset = 30
self:SetHeight(44)
end
end,
}
--[[-----------------------------------------------------------------------------
Constructor
-------------------------------------------------------------------------------]]
local ControlBackdrop = {
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
tile = true, tileSize = 16, edgeSize = 16,
insets = { left = 3, right = 3, top = 3, bottom = 3 }
}
local function keybindingMsgFixWidth()
this:SetWidth(this.msg:GetWidth() + 10)
this:SetScript("OnUpdate", nil)
end
local function Constructor()
local name = "AceGUI30KeybindingButton" .. AceGUI:GetNextWidgetNum(Type)
local frame = CreateFrame("Frame", nil, UIParent)
local button = CreateFrame("Button", name, frame, "UIPanelButtonTemplate2")
button:EnableMouse(true)
button:EnableMouseWheel(false)
button:SetScript("OnEnter", Control_OnEnter)
button:SetScript("OnLeave", Control_OnLeave)
button:SetScript("OnKeyDown", Keybinding_OnKeyDown)
button:RegisterForClicks("AnyDown","AnyUp")
-- Ace3v: RegisterForClicks means OnClick will not be triggered, so use OnKeyDown and OnKeyUp
button:SetScript("OnMouseDown", Keybinding_OnMouseDown)
button:SetScript("OnMouseUp", Keybinding_OnMouseUp)
button:SetScript("OnMouseWheel", Keybinding_OnMouseWheel)
button:SetScript("OnHide", Keybinding_OnHide)
button:SetPoint("BOTTOMLEFT",0,0)
button:SetPoint("BOTTOMRIGHT",0,0)
button:SetHeight(24)
button:EnableKeyboard(false)
local text = button:GetFontString()
text:SetPoint("LEFT", 7, 0)
text:SetPoint("RIGHT", -7, 0)
local label = frame:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
label:SetPoint("TOPLEFT",0,0)
label:SetPoint("TOPRIGHT",0,0)
label:SetJustifyH("CENTER")
label:SetHeight(18)
local msgframe = CreateFrame("Frame", nil, UIParent)
msgframe:SetHeight(30)
msgframe:SetBackdrop(ControlBackdrop)
msgframe:SetBackdropColor(0,0,0)
msgframe:SetFrameStrata("FULLSCREEN_DIALOG")
msgframe:SetFrameLevel(1000)
msgframe:SetToplevel(true)
local msg = msgframe:CreateFontString(nil, "OVERLAY", "GameFontNormal")
msg:SetText("Press a key to bind, ESC to clear the binding or click the button again to cancel.")
msgframe.msg = msg
msg:SetPoint("TOPLEFT", 5, -5)
msgframe:SetScript("OnUpdate", keybindingMsgFixWidth)
msgframe:SetPoint("BOTTOM", button, "TOP")
msgframe:Hide()
local widget = {
button = button,
label = label,
msgframe = msgframe,
frame = frame,
alignoffset = 30,
type = Type,
text = text
}
for method, func in pairs(methods) do
widget[method] = func
end
button.obj = widget
return AceGUI:RegisterAsWidget(widget)
end
AceGUI:RegisterWidgetType(Type, Constructor, Version)
@@ -0,0 +1,165 @@
--[[-----------------------------------------------------------------------------
Label Widget
Displays text and optionally an icon.
-------------------------------------------------------------------------------]]
local Type, Version = "Label", 23
local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
-- Lua APIs
local max, pairs = math.max, pairs
-- WoW APIs
local CreateFrame, UIParent = CreateFrame, UIParent
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
-- List them here for Mikk's FindGlobals script
-- GLOBALS: GameFontHighlightSmall
--[[-----------------------------------------------------------------------------
Support functions
-------------------------------------------------------------------------------]]
local function UpdateImageAnchor(self)
if self.resizing then return end
local frame = self.frame
local width = frame.width or frame:GetWidth() or 0
local image = self.image
local label = self.label
local height
label:ClearAllPoints()
image:ClearAllPoints()
if self.imageshown then
local imagewidth = image:GetWidth()
if (width - imagewidth) < 200 or (label:GetText() or "") == "" then
-- image goes on top centered when less than 200 width for the text, or if there is no text
image:SetPoint("TOP",0,0)
label:SetPoint("TOP", image, "BOTTOM")
label:SetPoint("LEFT",0,0)
label:SetWidth(width)
height = image:GetHeight() + label:GetHeight()
else
-- image on the left
image:SetPoint("TOPLEFT",0,0)
if image:GetHeight() > label:GetHeight() then
label:SetPoint("LEFT", image, "RIGHT", 4, 0)
else
label:SetPoint("TOPLEFT", image, "TOPRIGHT", 4, 0)
end
label:SetWidth(width - imagewidth - 4)
height = max(image:GetHeight(), label:GetHeight())
end
else
-- no image shown
label:SetPoint("TOPLEFT",0,0)
label:SetWidth(width)
height = label:GetHeight()
end
self.resizing = true
frame:SetHeight(height)
frame.height = height
self.resizing = nil
end
--[[-----------------------------------------------------------------------------
Methods
-------------------------------------------------------------------------------]]
local methods = {
["OnAcquire"] = function(self)
-- set the flag to stop constant size updates
self.resizing = true
-- height is set dynamically by the text and image size
self:SetWidth(200)
self:SetText()
self:SetImage(nil)
self:SetImageSize(16, 16)
self:SetColor()
self:SetFontObject()
-- reset the flag
self.resizing = nil
-- run the update explicitly
UpdateImageAnchor(self)
end,
-- ["OnRelease"] = nil,
["OnWidthSet"] = function(self, width)
UpdateImageAnchor(self)
end,
["SetText"] = function(self, text)
self.label:SetText(text)
UpdateImageAnchor(self)
end,
["SetColor"] = function(self, r, g, b)
if not (r and g and b) then
r, g, b = 1, 1, 1
end
self.label:SetVertexColor(r, g, b)
end,
["SetImage"] = function(self, path, a1,a2,a3,a4,a5,a6,a7,a8)
local image = self.image
image:SetTexture(path)
if image:GetTexture() then
self.imageshown = true
if a4 or a8 then
image:SetTexCoord(a1,a2,a3,a4,a5,a6,a7,a8)
else
image:SetTexCoord(0, 1, 0, 1)
end
else
self.imageshown = nil
end
UpdateImageAnchor(self)
end,
["SetFont"] = function(self, font, height, flags)
self.label:SetFont(font, height, flags)
end,
["SetFontObject"] = function(self, font)
self:SetFont((font or GameFontHighlightSmall):GetFont())
end,
["SetImageSize"] = function(self, width, height)
self.image:SetWidth(width)
self.image:SetHeight(height)
UpdateImageAnchor(self)
end,
}
--[[-----------------------------------------------------------------------------
Constructor
-------------------------------------------------------------------------------]]
local function Constructor()
local frame = CreateFrame("Frame", nil, UIParent)
frame:Hide()
local label = frame:CreateFontString(nil, "BACKGROUND", "GameFontHighlightSmall")
label:SetJustifyH("LEFT")
label:SetJustifyV("TOP")
local image = frame:CreateTexture(nil, "BACKGROUND")
-- create widget
local widget = {
label = label,
image = image,
frame = frame,
type = Type
}
for method, func in pairs(methods) do
widget[method] = func
end
return AceGUI:RegisterAsWidget(widget)
end
AceGUI:RegisterWidgetType(Type, Constructor, Version)
@@ -0,0 +1,475 @@
local Type, Version = "MultiLineEditBox", 28
local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
local AceCore = LibStub("AceCore-3.0")
local hooksecurefunc = AceCore.hooksecurefunc
-- Lua APIs
local strfmt = string.format
local pairs = pairs
-- WoW APIs
local GetCursorInfo, GetSpellInfo, ClearCursor = GetCursorInfo, GetSpellInfo, ClearCursor
local CreateFrame, UIParent = CreateFrame, UIParent
local _G = AceCore._G
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
-- List them here for Mikk's FindGlobals script
-- GLOBALS: ACCEPT, ChatFontNormal
--[[-----------------------------------------------------------------------------
Support functions
-------------------------------------------------------------------------------]]
if not AceGUIMultiLineEditBoxInsertLink then
-- upgradeable hook
hooksecurefunc("BankFrameItemButtonGeneric_OnClick",
function(button)
if button == "LeftButton" and IsShiftKeyDown() and not this.isBag then
return _G.AceGUIMultiLineEditBoxInsertLink(GetContainerItemLink(BANK_CONTAINER, this:GetID()))
end
end)
hooksecurefunc("ContainerFrameItemButton_OnClick",
function(button, ignoreModifiers)
if button == "LeftButton" and IsShiftKeyDown() and not ignoreModifiers then
return _G.AceGUIMultiLineEditBoxInsertLink(GetContainerItemLink(this:GetParent():GetID(), this:GetID()))
end
end)
hooksecurefunc("KeyRingItemButton_OnClick",
function(button)
if button == "LeftButton" and IsShiftKeyDown() and not this.isBag then
return _G.AceGUIMultiLineEditBoxInsertLink(GetContainerItemLink(KEYRING_CONTAINER, this:GetID()))
end
end)
hooksecurefunc("LootFrameItem_OnClick",
function(button)
if button == "LeftButton" and IsShiftKeyDown() then
return _G.AceGUIMultiLineEditBoxInsertLink(GetLootSlotLink(this.slot))
end
end)
hooksecurefunc("SetItemRef",
function(link, text, button)
if IsShiftKeyDown() then
if strsub(link,1,6) == "player" then
local name = strsub(link,8)
if name and (strlen(name) > 0) then
return _G.AceGUIMultiLineEditBoxInsertLink(name)
end
else
return _G.AceGUIMultiLineEditBoxInsertLink(text)
end
end
end)
hooksecurefunc("MerchantItemButton_OnClick",
function(button, ignoreModifiers)
if MerchantFrame.selectedTab == 1 and button == "LeftButton" and IsShiftKeyDown() and not ignoreModifiers then
return _G.AceGUIMultiLineEditBoxInsertLink(GetMerchantItemLink(this:GetID()))
end
end)
hooksecurefunc("PaperDollItemSlotButton_OnClick",
function(button, ignoreModifiers)
if button == "LeftButton" and IsShiftKeyDown() and not ignoreModifiers then
return _G.AceGUIMultiLineEditBoxInsertLink(GetInventoryItemLink("player", this:GetID()))
end
end)
hooksecurefunc("QuestItem_OnClick",
function()
if IsShiftKeyDown() and this.rewardType ~= "spell" then
return _G.AceGUIMultiLineEditBoxInsertLink(GetQuestItemLink(this.type, this:GetID()))
end
end)
hooksecurefunc("QuestRewardItem_OnClick",
function()
if IsShiftKeyDown() and this.rewardType ~= "spell" then
return _G.AceGUIMultiLineEditBoxInsertLink(GetQuestItemLink(this.type, this:GetID()))
end
end)
hooksecurefunc("QuestLogTitleButton_OnClick",
function(button)
if IsShiftKeyDown() and (not this.isHeader) then
return _G.AceGUIMultiLineEditBoxInsertLink(gsub(this:GetText(), " *(.*)", "%1"))
end
end)
hooksecurefunc("QuestLogRewardItem_OnClick",
function()
if IsShiftKeyDown() and this.rewardType ~= "spell" then
return _G.AceGUIMultiLineEditBoxInsertLink(GetQuestLogItemLink(this.type, this:GetID()))
end
end)
hooksecurefunc("SpellButton_OnClick",
function(drag)
local id = SpellBook_GetSpellID(this:GetID())
if id <= MAX_SPELLS and (not drag) and IsShiftKeyDown() then
local spellName, subSpellName = GetSpellName(id, SpellBookFrame.bookType)
if spellName and not IsSpellPassive(id, SpellBookFrame.bookType) then
if subSpellName and (strlen(subSpellName) > 0) then
_G.AceGUIMultiLineEditBoxInsertLink(spellName.."("..subSpellName..")");
else
_G.AceGUIMultiLineEditBoxInsertLink(spellName);
end
end
end
end)
end
function _G.AceGUIMultiLineEditBoxInsertLink(text)
for i = 1, AceGUI:GetWidgetCount(Type) do
local editbox = _G[strfmt("MultiLineEditBox%uEdit",i)]
if editbox and editbox:IsVisible() and editbox.hasfocus then
editbox:Insert(text)
return true
end
end
end
local function Layout(self)
self:SetHeight(self.numlines * 14 + (self.disablebutton and 19 or 41) + self.labelHeight)
if self.labelHeight == 0 then
self.scrollBar:SetPoint("TOP", self.frame, "TOP", 0, -23)
else
self.scrollBar:SetPoint("TOP", self.label, "BOTTOM", 0, -19)
end
if self.disablebutton then
self.scrollBar:SetPoint("BOTTOM", self.frame, "BOTTOM", 0, 21)
self.scrollBG:SetPoint("BOTTOMLEFT", 0, 4)
else
self.scrollBar:SetPoint("BOTTOM", self.button, "TOP", 0, 18)
self.scrollBG:SetPoint("BOTTOMLEFT", self.button, "TOPLEFT")
end
end
--[[-----------------------------------------------------------------------------
Scripts
-------------------------------------------------------------------------------]]
local function OnClick() -- Button
local self = this.obj
self.editBox:ClearFocus()
if not self:Fire("OnEnterPressed", 1, self.editBox:GetText()) then
self.button:Disable()
end
end
local function OnCursorChanged() -- EditBox
local self, y = this.obj.scrollFrame, -arg2
local offset = self:GetVerticalScroll()
if y < offset then
self:SetVerticalScroll(y)
else
y = y + arg4 - self:GetHeight()
if y > offset then
self:SetVerticalScroll(y)
end
end
end
local function OnEditFocusLost() -- EditBox
this.hasfocus = nil
this:HighlightText(0, 0)
this.obj:Fire("OnEditFocusLost")
end
local function OnEnter() -- EditBox / ScrollFrame
local self = this.obj
if not self.entered then
self.entered = true
self:Fire("OnEnter")
end
end
local function OnLeave() -- EditBox / ScrollFrame
local self = this.obj
if self.entered then
self.entered = nil
self:Fire("OnLeave")
end
end
local function OnMouseUp() -- ScrollFrame
local self = this.obj.editBox
self:SetFocus()
local n = self:GetNumLetters()
self:HighlightText(n,n)
end
local function OnReceiveDrag() -- EditBox / ScrollFrame
if not GetCursorInfo then return end
local type, id, info = GetCursorInfo()
if type == "spell" then
local spell, rank = GetSpellName(id, info)
if rank ~= "" then spell = spell.."("..rank..")" end
info = spell
elseif type ~= "item" then
return
end
ClearCursor()
local self = this.obj
local editBox = self.editBox
if not this.hasfocus then
this.hasfocus = true
editBox:SetFocus()
local n = editBox:GetNumLetters()
editBox:HighlightText(n,n)
end
editBox:Insert(info)
self.button:Enable()
end
local function OnSizeChanged() -- ScrollFrame
this.obj.editBox:SetWidth(arg1)
end
local function OnTextChanged() -- EditBox
local self = this.obj
local value = this:GetText()
if tostring(value) ~= tostring(self.lasttext) then
self:Fire("OnTextChanged", 1, value)
self.lasttext = value
self.button:Enable()
end
end
local function OnTextSet() -- EditBox
this:HighlightText(0, 0)
this.obj.button:Disable()
end
local function OnVerticalScroll() -- ScrollFrame
local self = this.obj
local editBox = self.editBox
editBox:SetHitRectInsets(0, 0, arg1, editBox:GetHeight() - arg1 - this:GetHeight())
self.scrollFrame:SetScrollChild(self.editBox)
self.editBox:SetPoint("TOPLEFT",0,arg1)
self.editBox:SetPoint("TOPRIGHT",0,arg1)
end
local function OnShowFocus()
this.obj.editBox:SetFocus()
this:SetScript("OnShow", nil)
end
local function OnEditFocusGained()
this.hasfocus = true
AceGUI:SetFocus(this.obj)
this.obj:Fire("OnEditFocusGained")
end
local function OnEscapePressed() -- EditBox
AceGUI:ClearFocus()
end
--[[-----------------------------------------------------------------------------
Methods
-------------------------------------------------------------------------------]]
local methods = {
["OnAcquire"] = function(self)
self.editBox:SetText("")
self:SetDisabled(false)
self:SetWidth(200)
self:DisableButton(false)
self:SetNumLines()
self.entered = nil
self:SetMaxLetters(0)
end,
["OnRelease"] = function(self)
self:ClearFocus()
end,
["SetDisabled"] = function(self, disabled)
local editBox = self.editBox
if disabled then
editBox:ClearFocus()
editBox:EnableMouse(false)
editBox:SetTextColor(0.5, 0.5, 0.5)
self.label:SetTextColor(0.5, 0.5, 0.5)
self.scrollFrame:EnableMouse(false)
self.button:Disable()
else
editBox:EnableMouse(true)
editBox:SetTextColor(1, 1, 1)
self.label:SetTextColor(1, 0.82, 0)
self.scrollFrame:EnableMouse(true)
end
end,
["SetLabel"] = function(self, text)
if text and text ~= "" then
self.label:SetText(text)
if self.labelHeight ~= 10 then
self.labelHeight = 10
self.label:Show()
end
elseif self.labelHeight ~= 0 then
self.labelHeight = 0
self.label:Hide()
end
Layout(self)
end,
["SetNumLines"] = function(self, value)
if not value or value < 4 then
value = 4
end
self.numlines = value
Layout(self)
end,
["SetText"] = function(self, text)
self.lasttext = text or ""
self.editBox:SetText(text or "")
self.editBox:HighlightText(0)
self.button:Disable()
end,
["GetText"] = function(self)
return self.editBox:GetText()
end,
["SetMaxLetters"] = function (self, num)
self.editBox:SetMaxLetters(num or 0)
end,
["DisableButton"] = function(self, disabled)
self.disablebutton = disabled
if disabled then
self.button:Hide()
else
self.button:Show()
end
Layout(self)
end,
["ClearFocus"] = function(self)
self.editBox:ClearFocus()
self.frame:SetScript("OnShow", nil)
end,
["SetFocus"] = function(self)
self.editBox:SetFocus()
if not self.frame:IsShown() then
self.frame:SetScript("OnShow", OnShowFocus)
end
end,
["HighlightText"] = function(self, from, to)
self.editBox:HighlightText(from, to)
end,
}
--[[-----------------------------------------------------------------------------
Constructor
-------------------------------------------------------------------------------]]
local backdrop = {
bgFile = [[Interface\Tooltips\UI-Tooltip-Background]],
edgeFile = [[Interface\Tooltips\UI-Tooltip-Border]], edgeSize = 16,
insets = { left = 4, right = 3, top = 4, bottom = 3 }
}
local function Constructor()
local frame = CreateFrame("Frame", nil, UIParent)
frame:Hide()
local widgetNum = AceGUI:GetNextWidgetNum(Type)
local label = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
label:SetPoint("TOPLEFT", frame, "TOPLEFT", 0, -4)
label:SetPoint("TOPRIGHT", frame, "TOPRIGHT", 0, -4)
label:SetJustifyH("LEFT")
label:SetText(ACCEPT)
label:SetHeight(10)
local button = CreateFrame("Button", strfmt("%s%dButton", Type, widgetNum), frame, "UIPanelButtonTemplate")
button:SetPoint("BOTTOMLEFT", 0, 4)
button:SetHeight(22)
button:SetWidth(label:GetStringWidth() + 24)
button:SetText(ACCEPT)
button:SetScript("OnClick", OnClick)
button:Disable()
local text = button:GetFontString()
text:ClearAllPoints()
text:SetPoint("TOPLEFT", button, "TOPLEFT", 5, -5)
text:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", -5, 1)
text:SetJustifyV("MIDDLE")
local scrollBG = CreateFrame("Frame", nil, frame)
scrollBG:SetBackdrop(backdrop)
scrollBG:SetBackdropColor(0, 0, 0)
scrollBG:SetBackdropBorderColor(0.4, 0.4, 0.4)
local scrollFrame = CreateFrame("ScrollFrame", strfmt("%s%dScrollFrame", Type, widgetNum), frame, "UIPanelScrollFrameTemplate")
local scrollBar = _G[scrollFrame:GetName() .. "ScrollBar"]
scrollBar:ClearAllPoints()
scrollBar:SetPoint("TOP", label, "BOTTOM", 0, -19)
scrollBar:SetPoint("BOTTOM", button, "TOP", 0, 18)
scrollBar:SetPoint("RIGHT", frame, "RIGHT")
scrollBG:SetPoint("TOPRIGHT", scrollBar, "TOPLEFT", 0, 19)
scrollBG:SetPoint("BOTTOMLEFT", button, "TOPLEFT")
scrollFrame:SetPoint("TOPLEFT", scrollBG, "TOPLEFT", 5, -6)
scrollFrame:SetPoint("BOTTOMRIGHT", scrollBG, "BOTTOMRIGHT", -4, 4)
scrollFrame:SetScript("OnEnter", OnEnter)
scrollFrame:SetScript("OnLeave", OnLeave)
scrollFrame:SetScript("OnMouseUp", OnMouseUp)
scrollFrame:SetScript("OnReceiveDrag", OnReceiveDrag)
scrollFrame:SetScript("OnSizeChanged", OnSizeChanged)
local old = scrollFrame:GetScript("OnVerticalScroll");
if old then
scrollFrame:SetScript("OnVerticalScroll", function()
old()
OnVerticalScroll()
end)
else
scrollFrame:SetScript("OnVerticalScroll", OnVerticalScroll)
end
local editBox = CreateFrame("EditBox", strfmt("%s%dEdit", Type, widgetNum), scrollFrame)
editBox:SetFontObject(ChatFontNormal)
editBox:SetMultiLine(true)
editBox:EnableMouse(true)
editBox:SetAutoFocus(false)
--editBox:SetCountInvisibleLetters(false)
editBox:SetScript("OnCursorChanged", OnCursorChanged)
editBox:SetScript("OnEditFocusLost", OnEditFocusLost)
editBox:SetScript("OnEnter", OnEnter)
editBox:SetScript("OnEscapePressed", OnEscapePressed)
editBox:SetScript("OnLeave", OnLeave)
editBox:SetScript("OnMouseDown", OnReceiveDrag)
editBox:SetScript("OnReceiveDrag", OnReceiveDrag)
editBox:SetScript("OnTextChanged", OnTextChanged)
editBox:SetScript("OnTextSet", OnTextSet)
editBox:SetScript("OnEditFocusGained", OnEditFocusGained)
-- Ace3v: the orders are important here
scrollFrame:SetScrollChild(editBox)
editBox:SetPoint("TOPLEFT",0,0)
editBox:SetPoint("TOPRIGHT",0,0)
local widget = {
button = button,
editBox = editBox,
frame = frame,
label = label,
labelHeight = 10,
numlines = 4,
scrollBar = scrollBar,
scrollBG = scrollBG,
scrollFrame = scrollFrame,
type = Type
}
for method, func in pairs(methods) do
widget[method] = func
end
button.obj, editBox.obj, scrollFrame.obj = widget, widget, widget
return AceGUI:RegisterAsWidget(widget)
end
AceGUI:RegisterWidgetType(Type, Constructor, Version)
@@ -0,0 +1,312 @@
--[[-----------------------------------------------------------------------------
Slider Widget
Graphical Slider, like, for Range values.
-------------------------------------------------------------------------------]]
local Type, Version = "Slider", 22
local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
-- Lua APIs
local min, max, floor = math.min, math.max, math.floor
local tonumber, pairs = tonumber, pairs
-- WoW APIs
local PlaySound = PlaySound
local CreateFrame, UIParent = CreateFrame, UIParent
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
-- List them here for Mikk's FindGlobals script
-- GLOBALS: GameFontHighlightSmall
--[[-----------------------------------------------------------------------------
Support functions
-------------------------------------------------------------------------------]]
local function DisplayValue(self, value)
if self.valueLabels and self.valueLabels[value] then
return self.valueLabels[value]
end
return value
end
local function UpdateText(self)
local value = self.value or 0
if self.valueLabels and self.valueLabels[value] then
self.editbox:SetText(self.valueLabels[value])
elseif self.ispercent then
self.editbox:SetText(("%s%%"):format(floor(value * 1000 + 0.5) / 10))
else
self.editbox:SetText(floor(value * 100 + 0.5) / 100)
end
end
local function UpdateLabels(self)
local min, max = (self.min or 0), (self.max or 100)
if self.valueLabels then
self.lowtext:SetText(DisplayValue(self, min))
self.hightext:SetText(DisplayValue(self, max))
elseif self.ispercent then
self.lowtext:SetFormattedText("%s%%", (min * 100))
self.hightext:SetFormattedText("%s%%", (max * 100))
else
self.lowtext:SetText(min)
self.hightext:SetText(max)
end
end
--[[-----------------------------------------------------------------------------
Scripts
-------------------------------------------------------------------------------]]
local function Control_OnEnter()
this.obj:Fire("OnEnter")
end
local function Control_OnLeave()
this.obj:Fire("OnLeave")
end
local function Frame_OnMouseDown()
this.obj.slider:EnableMouseWheel(true)
AceGUI:ClearFocus()
end
local function Slider_OnValueChanged()
local self = this.obj
if not this.setup then
local newvalue = this:GetValue()
if self.step and self.step > 0 then
local min_value = self.min or 0
newvalue = floor((newvalue - min_value) / self.step + 0.5) * self.step + min_value
end
if newvalue ~= self.value and not self.disabled then
self.value = newvalue
self:Fire("OnValueChanged", 1, newvalue)
end
if self.value then
UpdateText(self)
end
end
end
local function Slider_OnMouseUp()
local self = this.obj
self:Fire("OnMouseUp", 1, self.value)
end
local function Slider_OnMouseWheel()
local self = this.obj
if not self.disabled then
local value = self.value
if arg1 > 0 then
value = min(value + (self.step or 1), self.max)
else
value = max(value - (self.step or 1), self.min)
end
self.slider:SetValue(value)
end
end
local function EditBox_OnEscapePressed()
this:ClearFocus()
end
local function EditBox_OnEnterPressed()
local self = this.obj
local value = this:GetText()
if self.valueLabels then
local typed=value
value=tonumber(value)
if not value then
for sliderValue,label in pairs(self.valueLabels) do
if tostring(label)==typed then value=sliderValue break end
end
end
elseif self.ispercent then
value = value:gsub('%%', '')
value = tonumber(value) / 100
else
value = tonumber(value)
end
if value then
PlaySound("igMainMenuOptionCheckBoxOn")
self.slider:SetValue(value)
self:Fire("OnMouseUp", 1, value)
end
end
local function EditBox_OnEnter()
this:SetBackdropBorderColor(0.5, 0.5, 0.5, 1)
end
local function EditBox_OnLeave()
this:SetBackdropBorderColor(0.3, 0.3, 0.3, 0.8)
end
--[[-----------------------------------------------------------------------------
Methods
-------------------------------------------------------------------------------]]
local methods = {
["OnAcquire"] = function(self)
self:SetWidth(200)
self:SetHeight(44)
self:SetDisabled(false)
self:SetValueLabels(nil)
self:SetIsPercent(nil)
self:SetSliderValues(0,100,1)
self:SetValue(0)
self.slider:EnableMouseWheel(false)
end,
-- ["OnRelease"] = nil,
["SetDisabled"] = function(self, disabled)
self.disabled = disabled
if disabled then
self.slider:EnableMouse(false)
self.label:SetTextColor(.5, .5, .5)
self.hightext:SetTextColor(.5, .5, .5)
self.lowtext:SetTextColor(.5, .5, .5)
--self.valuetext:SetTextColor(.5, .5, .5)
self.editbox:SetTextColor(.5, .5, .5)
self.editbox:EnableMouse(false)
self.editbox:ClearFocus()
else
self.slider:EnableMouse(true)
self.label:SetTextColor(1, .82, 0)
self.hightext:SetTextColor(1, 1, 1)
self.lowtext:SetTextColor(1, 1, 1)
--self.valuetext:SetTextColor(1, 1, 1)
self.editbox:SetTextColor(1, 1, 1)
self.editbox:EnableMouse(true)
end
end,
["SetValue"] = function(self, value)
self.slider.setup = true
self.slider:SetValue(value)
self.value = value
UpdateText(self)
self.slider.setup = nil
end,
["GetValue"] = function(self)
return self.value
end,
["SetLabel"] = function(self, text)
self.label:SetText(text)
end,
["SetSliderValues"] = function(self, min, max, step)
local frame = self.slider
frame.setup = true
self.min = min
self.max = max
self.step = step
frame:SetMinMaxValues(min or 0,max or 100)
UpdateLabels(self)
frame:SetValueStep(step or 1)
if self.value then
frame:SetValue(self.value)
end
frame.setup = nil
end,
["SetValueLabels"] = function(self, labels)
self.valueLabels = labels
UpdateLabels(self)
UpdateText(self)
end,
["SetIsPercent"] = function(self, value)
self.ispercent = value
UpdateLabels(self)
UpdateText(self)
end
}
--[[-----------------------------------------------------------------------------
Constructor
-------------------------------------------------------------------------------]]
local SliderBackdrop = {
bgFile = "Interface\\Buttons\\UI-SliderBar-Background",
edgeFile = "Interface\\Buttons\\UI-SliderBar-Border",
tile = true, tileSize = 8, edgeSize = 8,
insets = { left = 3, right = 3, top = 6, bottom = 6 }
}
local ManualBackdrop = {
bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
edgeFile = "Interface\\ChatFrame\\ChatFrameBackground",
tile = true, edgeSize = 1, tileSize = 5,
}
local function Constructor()
local frame = CreateFrame("Frame", nil, UIParent)
frame:EnableMouse(true)
frame:SetScript("OnMouseDown", Frame_OnMouseDown)
local label = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
label:SetPoint("TOPLEFT",0,0)
label:SetPoint("TOPRIGHT",0,0)
label:SetJustifyH("CENTER",0,0)
label:SetHeight(15)
local slider = CreateFrame("Slider", nil, frame)
slider:SetOrientation("HORIZONTAL")
slider:SetHeight(15)
slider:SetHitRectInsets(0, 0, -10, 0)
slider:SetBackdrop(SliderBackdrop)
slider:SetThumbTexture("Interface\\Buttons\\UI-SliderBar-Button-Horizontal")
slider:SetPoint("TOP", label, "BOTTOM")
slider:SetPoint("LEFT", 3, 0)
slider:SetPoint("RIGHT", -3, 0)
slider:SetValue(0)
slider:SetScript("OnValueChanged",Slider_OnValueChanged)
slider:SetScript("OnEnter", Control_OnEnter)
slider:SetScript("OnLeave", Control_OnLeave)
slider:SetScript("OnMouseUp", Slider_OnMouseUp)
slider:SetScript("OnMouseWheel", Slider_OnMouseWheel)
local lowtext = slider:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
lowtext:SetPoint("TOPLEFT", slider, "BOTTOMLEFT", 2, 3)
local hightext = slider:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
hightext:SetPoint("TOPRIGHT", slider, "BOTTOMRIGHT", -2, 3)
local editbox = CreateFrame("EditBox", nil, frame)
editbox:SetAutoFocus(false)
editbox:SetFontObject(GameFontHighlightSmall)
editbox:SetPoint("TOP", slider, "BOTTOM")
editbox:SetHeight(14)
editbox:SetWidth(70)
editbox:SetJustifyH("CENTER")
editbox:EnableMouse(true)
editbox:SetBackdrop(ManualBackdrop)
editbox:SetBackdropColor(0, 0, 0, 0.5)
editbox:SetBackdropBorderColor(0.3, 0.3, 0.30, 0.80)
editbox:SetScript("OnEnter", EditBox_OnEnter)
editbox:SetScript("OnLeave", EditBox_OnLeave)
editbox:SetScript("OnEnterPressed", EditBox_OnEnterPressed)
editbox:SetScript("OnEscapePressed", EditBox_OnEscapePressed)
local widget = {
label = label,
slider = slider,
lowtext = lowtext,
hightext = hightext,
editbox = editbox,
alignoffset = 25,
frame = frame,
type = Type
}
for method, func in pairs(methods) do
widget[method] = func
end
slider.obj, editbox.obj = widget, widget
return AceGUI:RegisterAsWidget(widget)
end
AceGUI:RegisterWidgetType(Type,Constructor,Version)
+14
View File
@@ -0,0 +1,14 @@
<Ui xmlns="http://www.blizzard.com/wow/ui/">
<Script file="AceGUIContainer-Frame.lua"/>
<Script file="AceGUIContainer-InlineGroup.lua"/>
<Script file="AceGUIContainer-ScrollFrame.lua"/>
<Script file="AceGUIContainer-SimpleGroup.lua"/>
<Script file="AceGUIContainer-TabGroup.lua"/>
<Script file="AceGUIWidget-Button.lua"/>
<Script file="AceGUIWidget-CheckBox.lua"/>
<Script file="AceGUIWidget-DropDown.lua"/>
<Script file="AceGUIWidget-DropDown-Items.lua"/>
<Script file="AceGUIWidget-Heading.lua"/>
<Script file="AceGUIWidget-Label.lua"/>
<Script file="AceGUIWidget-Slider.lua"/>
</Ui>
@@ -0,0 +1,252 @@
--[[ Questie CallbackHandler-1.0 API, Lua 5.0 syntax compatibility ]]
-- Original Questie foundation: CallbackHandler-1.0 r1131 / MINOR 6.
-- Turtle 1.12 Lua 5.0 supports vararg declarations but consumes them through
-- the implicit 'arg' table rather than Lua 5.1's '...' expression.
local MAJOR, MINOR = "CallbackHandler-1.0", 6
local CallbackHandler = LibStub:NewLibrary(MAJOR, MINOR)
if not CallbackHandler then return end
local meta = {__index = function(tbl, key) tbl[key] = {} return tbl[key] end}
local tconcat = table.concat
local assert, error, loadstring = assert, error, loadstring
local setmetatable, rawset, rawget = setmetatable, rawset, rawget
local next, pairs, type, tostring = next, pairs, type, tostring
local unpack = unpack
local xpcall = xpcall
local function errorhandler(err)
return geterrorhandler()(err)
end
-- Same Questie dispatcher strategy, emitted with fixed named arguments so the
-- generated source contains no Lua 5.1 vararg expressions.
local function CreateDispatcher(argCount)
local args={}
local oldargs={}
for i=1,argCount do
args[i]="arg"..i
oldargs[i]="old_arg"..i
end
local argList=tconcat(args,", ")
local oldArgList=tconcat(oldargs,", ")
local dispatchSignature="handlers"
if argCount>0 then dispatchSignature=dispatchSignature..", "..argList end
local saveOld
local assignArgs
local restoreOld
if argCount>0 then
saveOld="local old_CALL_ARGS = CALL_ARGS"
assignArgs="CALL_ARGS = {"..argList.."}"
restoreOld="CALL_ARGS = old_CALL_ARGS"
else
saveOld="local old_CALL_ARGS = CALL_ARGS"
assignArgs="CALL_ARGS = {}"
restoreOld="CALL_ARGS = old_CALL_ARGS"
end
local code=[[
return function(next, xpcall, eh, unpack)
local method
local CALL_ARGS = {}
local function call()
method(unpack(CALL_ARGS, 1, CALL_ARGS.n or table.getn(CALL_ARGS)))
end
local function dispatch(DISPATCH_SIGNATURE)
local index
index, method = next(handlers)
if not method then return end
SAVE_OLD
ASSIGN_ARGS
CALL_ARGS.n = ARG_COUNT
repeat
xpcall(call, eh)
index, method = next(handlers, index)
until not method
RESTORE_OLD
end
return dispatch
end
]]
code=string.gsub(code,"DISPATCH_SIGNATURE",dispatchSignature)
code=string.gsub(code,"SAVE_OLD",saveOld)
code=string.gsub(code,"ASSIGN_ARGS",assignArgs)
code=string.gsub(code,"RESTORE_OLD",restoreOld)
code=string.gsub(code,"ARG_COUNT",tostring(argCount))
local chunk=assert(loadstring(code,"safecall Dispatcher["..argCount.."]"))
local factory=chunk()
return factory(next,xpcall,errorhandler,unpack)
end
local Dispatchers=setmetatable({},{
__index=function(self,argCount)
local dispatcher=CreateDispatcher(argCount)
rawset(self,argCount,dispatcher)
return dispatcher
end
})
function CallbackHandler:New(target, RegisterName, UnregisterName, UnregisterAllName)
RegisterName=RegisterName or "RegisterCallback"
UnregisterName=UnregisterName or "UnregisterCallback"
if UnregisterAllName==nil then
UnregisterAllName="UnregisterAllCallbacks"
end
local events=setmetatable({},meta)
local registry={recurse=0,events=events}
function registry:Fire(eventname, ...)
if not rawget(events,eventname) or not next(events[eventname]) then return end
local oldrecurse=registry.recurse
registry.recurse=oldrecurse+1
local count=arg.n or table.getn(arg)
local callargs={eventname}
callargs.n=count+1
for i=1,count do callargs[i+1]=arg[i] end
Dispatchers[count+1](events[eventname],unpack(callargs,1,callargs.n))
registry.recurse=oldrecurse
if registry.insertQueue and oldrecurse==0 then
for queuedEvent,callbacks in pairs(registry.insertQueue) do
local first=not rawget(events,queuedEvent) or not next(events[queuedEvent])
for self,func in pairs(callbacks) do
events[queuedEvent][self]=func
if first and registry.OnUsed then
registry.OnUsed(registry,target,queuedEvent)
first=nil
end
end
end
registry.insertQueue=nil
end
end
target[RegisterName]=function(self,eventname,method,...)
if type(eventname)~="string" then
error("Usage: "..RegisterName.."(eventname, method[, arg]): 'eventname' - string expected.",2)
end
method=method or eventname
local first=not rawget(events,eventname) or not next(events[eventname])
if type(method)~="string" and type(method)~="function" then
error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): 'methodname' - string or function expected.",2)
end
local regfunc
local optionalCount=arg.n or table.getn(arg)
if type(method)=="string" then
if type(self)~="table" then
error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): self was not a table?",2)
elseif self==target then
error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): do not use Library:"..RegisterName.."(), use your own 'self'",2)
elseif type(self[method])~="function" then
error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): 'methodname' - method '"..tostring(method).."' not found on self.",2)
end
if optionalCount>=1 then
local extra=arg[1]
regfunc=function(...)
self[method](self,extra,unpack(arg,1,arg.n or table.getn(arg)))
end
else
regfunc=function(...)
self[method](self,unpack(arg,1,arg.n or table.getn(arg)))
end
end
else
if type(self)~="table" and type(self)~="string" and type(self)~="thread" then
error("Usage: "..RegisterName.."(self or \"addonId\", eventname, method): 'self or addonId': table or string or thread expected.",2)
end
if optionalCount>=1 then
local extra=arg[1]
regfunc=function(...)
method(extra,unpack(arg,1,arg.n or table.getn(arg)))
end
else
regfunc=method
end
end
if events[eventname][self] or registry.recurse<1 then
events[eventname][self]=regfunc
if registry.OnUsed and first then
registry.OnUsed(registry,target,eventname)
end
else
registry.insertQueue=registry.insertQueue or setmetatable({},meta)
registry.insertQueue[eventname][self]=regfunc
end
end
target[UnregisterName]=function(self,eventname)
if not self or self==target then
error("Usage: "..UnregisterName.."(eventname): bad 'self'",2)
end
if type(eventname)~="string" then
error("Usage: "..UnregisterName.."(eventname): 'eventname' - string expected.",2)
end
if rawget(events,eventname) and events[eventname][self] then
events[eventname][self]=nil
if registry.OnUnused and not next(events[eventname]) then
registry.OnUnused(registry,target,eventname)
end
end
if registry.insertQueue and rawget(registry.insertQueue,eventname) and registry.insertQueue[eventname][self] then
registry.insertQueue[eventname][self]=nil
end
end
if UnregisterAllName then
target[UnregisterAllName]=function(...)
local count=arg.n or table.getn(arg)
if count<1 then
error("Usage: "..UnregisterAllName.."([whatFor]): missing 'self' or \"addonId\" to unregister events for.",2)
end
if count==1 and arg[1]==target then
error("Usage: "..UnregisterAllName.."([whatFor]): supply a meaningful 'self' or \"addonId\"",2)
end
for i=1,count do
local self=arg[i]
if registry.insertQueue then
for eventname,callbacks in pairs(registry.insertQueue) do
if callbacks[self] then callbacks[self]=nil end
end
end
for eventname,callbacks in pairs(events) do
if callbacks[self] then
callbacks[self]=nil
if registry.OnUnused and not next(callbacks) then
registry.OnUnused(registry,target,eventname)
end
end
end
end
end
end
return registry
end
@@ -0,0 +1,4 @@
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
..\FrameXML\UI.xsd">
<Script file="CallbackHandler-1.0.lua"/>
</Ui>
+30
View File
@@ -0,0 +1,30 @@
-- LibStub is a simple versioning stub meant for use in Libraries. http://www.wowace.com/wiki/LibStub for more info
-- LibStub is hereby placed in the Public Domain Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke
local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2 -- NEVER MAKE THIS AN SVN REVISION! IT NEEDS TO BE USABLE IN ALL REPOS!
local LibStub = _G[LIBSTUB_MAJOR]
if not LibStub or LibStub.minor < LIBSTUB_MINOR then
LibStub = LibStub or {libs = {}, minors = {} }
_G[LIBSTUB_MAJOR] = LibStub
LibStub.minor = LIBSTUB_MINOR
function LibStub:NewLibrary(major, minor)
assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)")
minor = assert(tonumber(strmatch(minor, "%d+")), "Minor version must either be a number or contain a number.")
local oldminor = self.minors[major]
if oldminor and oldminor >= minor then return nil end
self.minors[major], self.libs[major] = minor, self.libs[major] or {}
return self.libs[major], oldminor
end
function LibStub:GetLibrary(major, silent)
if not self.libs[major] and not silent then
error(("Cannot find a library instance of %q."):format(tostring(major)), 2)
end
return self.libs[major], self.minors[major]
end
function LibStub:IterateLibraries() return pairs(self.libs) end
setmetatable(LibStub, { __call = LibStub.GetLibrary })
end

Some files were not shown because too many files have changed in this diff Show More