/trunk/ReadySpells/libs/LibDualSpec-1.0
tag v1.13 |
3344dceb2c4f1a8a236a927ea2ec290c22099d91 |
Kyle Buller <bullerk@gmail.com> |
2016-11-15 14:41:16 -0600 |
Tagging as v1.13 |
-------------------- |
Kyle Buller: |
- Update zhCN locale from ananhaid. |
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
--]] |
local MAJOR, MINOR = "LibDualSpec-1.0", 11 |
local MAJOR, MINOR = "LibDualSpec-1.0", 17 |
assert(LibStub, MAJOR.." requires LibStub") |
local lib, minor = LibStub:NewLibrary(MAJOR, MINOR) |
if not lib then return end |
lib.registry = lib.registry or {} |
lib.options = lib.options or {} |
lib.mixin = lib.mixin or {} |
lib.upgrades = lib.upgrades or {} |
lib.currentSpec = lib.currentSpec or 0 |
-- Rename .talent* to .spec* |
if minor and minor < 11 then |
lib.specLoaded = lib.talentsLoaded |
lib.specGroup = lib.talentGroup |
if minor and minor < 15 then |
lib.talentsLoaded, lib.talentGroup = nil, nil |
lib.specLoaded, lib.specGroup = nil, nil |
lib.eventFrame:UnregisterAllEvents() |
wipe(lib.options) |
end |
-- ---------------------------------------------------------------------------- |
local registry = lib.registry |
local options = lib.options |
local mixin = lib.mixin |
local upgrades = lib.upgrades |
-- "Externals" |
local AceDB3 = LibStub('AceDB-3.0', true) |
local AceDBOptions3 = LibStub('AceDBOptions-3.0', true) |
local AceConfigRegistry3 = LibStub('AceConfigRegistry-3.0', true) |
-- ---------------------------------------------------------------------------- |
-- MoP compatibility |
-- ---------------------------------------------------------------------------- |
-- classId specialization functions don't require player data to be loaded |
local _, _, classId = UnitClass("player") |
local numSpecs = GetNumSpecializationsForClassID(classId) |
local GetActiveSpecGroup = GetActiveSpecGroup or GetActiveTalentGroup |
local GetNumSpecGroups = GetNumSpecGroups or GetNumTalentGroups |
-- ---------------------------------------------------------------------------- |
-- Localization |
-- ---------------------------------------------------------------------------- |
local L_DUALSPEC_DESC, L_ENABLED, L_ENABLED_DESC, L_DUAL_PROFILE, L_DUAL_PROFILE_DESC |
local L_ENABLED = "Enable spec profiles" |
local L_ENABLED_DESC = "When enabled, your profile will be set to the specified profile when you change specialization." |
local L_CURRENT = "%s (Current)" -- maybe something like >> %s << and/or coloring to avoid localization? |
do |
L_DUALSPEC_DESC = "When enabled, this feature allow you to select a different ".. |
"profile for each talent spec. The dual profile will be swapped with the ".. |
"current profile each time you switch from a talent spec to the other." |
L_ENABLED = 'Enable dual profile' |
L_ENABLED_DESC = 'Check this box to automatically swap profiles on talent switch.' |
L_DUAL_PROFILE = 'Dual profile' |
L_DUAL_PROFILE_DESC = 'Select the profile to swap with on talent switch.' |
local locale = GetLocale() |
if locale == "frFR" then |
L_DUALSPEC_DESC = "Lorsqu'elle est activée, cette fonctionnalité vous permet de choisir un profil différent pour chaque spécialisation de talents. Le second profil sera échangé avec le profil courant chaque fois que vous passerez d'une spécialisation à l'autre." |
L_DUAL_PROFILE = "Second profil" |
L_DUAL_PROFILE_DESC = "Sélectionnez le profil à échanger avec le profil courant lors du changement de spécialisation." |
L_ENABLED = "Activez le second profil" |
L_ENABLED_DESC = "Cochez cette case pour échanger automatiquement les profils lors d'un changement de spécialisation." |
-- L_ENABLED = "Enable spec profiles" |
-- L_ENABLED_DESC = "When enabled, your profile will be set to the specified profile when you change specialization." |
-- L_CURRENT = "%s (Current)" |
elseif locale == "deDE" then |
L_DUALSPEC_DESC = "Wenn aktiv, wechselt dieses Feature bei jedem Wechsel der dualen Talentspezialisierung das Profil. Das duale Profil wird beim Wechsel automatisch mit dem derzeit aktiven Profil getauscht." |
L_DUAL_PROFILE = "Duales Profil" |
L_DUAL_PROFILE_DESC = "Wähle das Profil, das beim Wechsel der Talente aktiviert wird." |
L_ENABLED = "Aktiviere Duale Profile" |
L_ENABLED_DESC = "Aktiviere diese Option, um beim Talentwechsel automatisch zwischen den Profilen zu wechseln." |
L_ENABLED = "Spezialisierungsprofile aktivieren" |
L_ENABLED_DESC = "Falls diese Option aktiviert ist, wird dein Profil auf das angegebene Profil gesetzt, wenn du die Spezialisierung wechselst." |
L_CURRENT = "%s (Momentan)" |
elseif locale == "koKR" then |
L_DUALSPEC_DESC = "ì´ì¤ í¹ì±ì ìíì¬ ë¤ë¥¸ íë¡íì ì íí ì ìê² í©ëë¤. ì´ì¤ íë¡íì íì¬ íë¡íê³¼ ë²ê°ìì í¹ì±ì´ ë³ê²½ë ë ê°ì´ ì ì©ë©ëë¤." |
L_DUAL_PROFILE = "ì´ì¤ íë¡í" |
L_DUAL_PROFILE_DESC = "í¹ì±ì´ ë°ë ë íë¡íì ì íí©ëë¤." |
L_ENABLED = "ì´ì¤ íë¡í ì¬ì©" |
L_ENABLED_DESC = "í¹ì±ì´ ë³ê²½ ë ë ìëì¼ë¡ íë¡íì ë³ê²½íëë¡ ì íí©ëë¤." |
-- L_ENABLED = "Enable spec profiles" |
-- L_ENABLED_DESC = "When enabled, your profile will be set to the specified profile when you change specialization." |
-- L_CURRENT = "%s (Current)" |
elseif locale == "ruRU" then |
L_DUALSPEC_DESC = "Ðвойной пÑоÑÐ¸Ð»Ñ Ð¿Ð¾Ð·Ð²Ð¾Ð»ÑÐµÑ Ð²Ð°Ð¼ вÑбÑаÑÑ ÑазлиÑнÑе пÑоÑили Ð´Ð»Ñ ÐºÐ°Ð¶Ð´Ð¾Ð¹ ÑаÑкладки ÑаланÑов. ÐÑоÑили бÑдÑÑ Ð¿ÐµÑеклÑÑаÑÑÑÑ ÐºÐ°Ð¶Ð´Ñй Ñаз, когда Ð²Ñ Ð¿ÐµÑеклÑÑаеÑе ÑаÑÐºÐ»Ð°Ð´ÐºÑ ÑаланÑов." |
L_DUAL_PROFILE = "ÐÑоÑой пÑоÑилÑ" |
L_DUAL_PROFILE_DESC = "ÐÑбеÑиÑе пÑоÑилÑ, коÑоÑÑй Ð½ÐµÐ¾Ð±Ñ Ð¾Ð´Ð¸Ð¼Ð¾ акÑивиÑоваÑÑ Ð¿Ñи пеÑеклÑÑениии ÑаланÑов." |
L_ENABLED = "ÐклÑÑиÑÑ Ð´Ð²Ð¾Ð¹Ð½Ð¾Ð¹ пÑоÑилÑ" |
L_ENABLED_DESC = "ÐклÑÑиÑе ÑÑÑ Ð¾Ð¿ÑÐ¸Ñ Ð´Ð»Ñ Ð°Ð²ÑомаÑиÑеÑкого пеÑеклÑÑÐµÐ½Ð¸Ñ Ð¼ÐµÐ¶Ð´Ñ Ð¿ÑоÑилÑми пÑи пеÑеклÑÑении ÑаÑкладки ÑаланÑов." |
L_ENABLED = "ÐклÑÑиÑÑ Ð¿ÑоÑили ÑпеÑиализаÑии" |
L_ENABLED_DESC = "ÐÑли вклÑÑено, Ð²Ð°Ñ Ð¿ÑоÑÐ¸Ð»Ñ Ð±ÑÐ´ÐµÑ Ð·Ð°Ð²Ð¸ÑеÑÑ Ð¾Ñ Ð²ÑбÑанной ÑпеÑиализаÑии." |
L_CURRENT = "%s (ТекÑÑий)" |
elseif locale == "zhCN" then |
L_DUALSPEC_DESC = "å¯æ¶ï¼ä½ å¯ä»¥ä¸ºä½ çå天èµè®¾å®å¦ä¸ç»é ç½®æ件ï¼ä½ çåéé ç½®æ件å°å¨ä½ 转æ¢å¤©èµæ¶èªå¨ä¸ç®å使ç¨é ç½®æ件交æ¢ã" |
L_DUAL_PROFILE = "åéé ç½®æ件" |
L_DUAL_PROFILE_DESC = "éæ©è½¬æ¢å¤©èµæ¶æè¦ä½¿ç¨çé ç½®æ件" |
L_ENABLED = "å¼å¯åéé ç½®æ件" |
L_ENABLED_DESC = "å¾é以便转æ¢å¤©èµæ¶èªå¨äº¤æ¢é ç½®æ件ã" |
L_ENABLED = "å¯ç¨ä¸ç²¾é ç½®æ件" |
L_ENABLED_DESC = "å½å¯ç¨åï¼å½åæ¢ä¸ç²¾æ¶é ç½®æ件å°è®¾ç½®ä¸ºä¸ç²¾é ç½®æ件ã" |
L_CURRENT = "%sï¼å½åï¼" |
elseif locale == "zhTW" then |
L_DUALSPEC_DESC = "åç¨æï¼ä½ å¯ä»¥çºä½ çé天賦è¨å®å¦ä¸çµè¨å®æªãä½ çéè¨å®æªå°å¨ä½ è½æ天賦æèªåèç®å使ç¨è¨å®æªäº¤æã" |
L_DUAL_PROFILE = "éè¨å®æª" |
L_DUAL_PROFILE_DESC = "é¸æè½æ天賦å¾æè¦ä½¿ç¨çè¨å®æª" |
L_ENABLED = "åç¨éè¨å®æª" |
L_ENABLED_DESC = "å¾é¸ä»¥å¨è½æ天賦æèªå交æè¨å®æª" |
elseif locale == "esES" then |
L_DUALSPEC_DESC = "Si está activa, esta caracterÃstica te permite seleccionar un perfil distinto para cada configuración de talentos. El perfil secundario será intercambiado por el activo cada vez que cambies de una configuración de talentos a otra." |
L_DUAL_PROFILE = "Perfil secundario" |
L_DUAL_PROFILE_DESC = "Elige el perfil secundario que se usará cuando cambies de talentos." |
L_ENABLED = "Activar perfil secundario" |
L_ENABLED_DESC = "Activa esta casilla para alternar automáticamente entre prefiles cuando cambies de talentos." |
L_ENABLED = "åç¨å°ç²¾è¨å®æª" |
L_ENABLED_DESC = "ç¶åç¨å¾ï¼ç¶ä½ åæå°ç²¾æè¨å®æªæè¨å®çºå°ç²¾è¨å®æªã" |
L_CURRENT = "%s (ç®å) " |
elseif locale == "esES" or locale == "esMX" then |
-- L_ENABLED = "Enable spec profiles" |
-- L_ENABLED_DESC = "When enabled, your profile will be set to the specified profile when you change specialization." |
-- L_CURRENT = "%s (Current)" |
elseif locale == "ptBR" then |
-- L_ENABLED = "Enable spec profiles" |
-- L_ENABLED_DESC = "When enabled, your profile will be set to the specified profile when you change specialization." |
-- L_CURRENT = "%s (Current)" |
elseif locale == "itIT" then |
-- L_ENABLED = "Enable spec profiles" |
-- L_ENABLED_DESC = "When enabled, your profile will be set to the specified profile when you change specialization." |
-- L_CURRENT = "%s (Current)" |
end |
end |
-- @param enabled (boolean) true to enable dual spec feature, false to disable it. |
-- @name enhancedDB:SetDualSpecEnabled |
function mixin:SetDualSpecEnabled(enabled) |
local db = registry[self].db |
if enabled and not db.char.specGroup then |
db.char.specGroup = lib.specGroup |
db.char.profile = self:GetCurrentProfile() |
db.char.enabled = true |
else |
db.char.enabled = enabled |
self:CheckDualSpecState() |
local db = registry[self].db.char |
db.enabled = not not enabled |
local currentProfile = self:GetCurrentProfile() |
for i = 1, numSpecs do |
-- nil out entries on disable, set nil entries to the current profile on enable |
db[i] = enabled and (db[i] or currentProfile) or nil |
end |
self:CheckDualSpecState() |
end |
--- Get the alternate profile name. |
--- Get the profile assigned to a specialization. |
-- Defaults to the current profile. |
-- @return (string) Alternate profile name. |
-- @param spec (number) the specialization index. |
-- @return (string) the profile name. |
-- @name enhancedDB:GetDualSpecProfile |
function mixin:GetDualSpecProfile() |
return registry[self].db.char.profile or self:GetCurrentProfile() |
function mixin:GetDualSpecProfile(spec) |
return registry[self].db.char[spec or lib.currentSpec] or self:GetCurrentProfile() |
end |
--- Set the alternate profile name. |
--- Set the profile assigned to a specialization. |
-- No validation are done to ensure the profile is valid. |
-- @param profileName (string) the profile name to use. |
-- @param spec (number) the specialization index. |
-- @name enhancedDB:SetDualSpecProfile |
function mixin:SetDualSpecProfile(profileName) |
registry[self].db.char.profile = profileName |
function mixin:SetDualSpecProfile(profileName, spec) |
spec = spec or lib.currentSpec |
if spec < 1 or spec > numSpecs then return end |
registry[self].db.char[spec] = profileName |
self:CheckDualSpecState() |
end |
--- Check if a profile swap should occur. |
-- Do nothing if the dual spec feature is disabled. In the other |
-- case, if the internally stored talent spec is different from the |
-- actual active talent spec, the database swaps to the alternate profile. |
-- There is normally no reason to call this method directly as LibDualSpec |
-- takes care of calling it at appropriate times. |
-- takes care of calling it at the appropriate time. |
-- @name enhancedDB:CheckDualSpecState |
function mixin:CheckDualSpecState() |
local db = registry[self].db |
if lib.specLoaded and db.char.enabled and db.char.specGroup ~= lib.specGroup then |
local currentProfile = self:GetCurrentProfile() |
local newProfile = db.char.profile |
db.char.specGroup = lib.specGroup |
if newProfile ~= currentProfile then |
db.char.profile = currentProfile |
self:SetProfile(newProfile) |
end |
if not registry[self].db.char.enabled then return end |
if lib.currentSpec == 0 then return end |
local profileName = self:GetDualSpecProfile() |
if profileName ~= self:GetCurrentProfile() then |
self:SetProfile(profileName) |
end |
end |
-- ---------------------------------------------------------------------------- |
local function EmbedMixin(target) |
for k,v in pairs(mixin) do |
for k,v in next, mixin do |
rawset(target, k, v) |
end |
end |
-- Upgrade existing mixins |
for target in pairs(registry) do |
EmbedMixin(target) |
-- Upgrade settings from current/alternate system. |
-- This sets the current profile as the profile for your current spec and your |
-- swapped profile as the profile for the rest of your specs. |
local function UpgradeDatabase(target) |
if lib.currentSpec == 0 then |
upgrades[target] = true |
return |
end |
local db = target:GetNamespace(MAJOR, true) |
if db and db.char.profile then |
for i = 1, numSpecs do |
if i == lib.currentSpec then |
db.char[i] = target:GetCurrentProfile() |
else |
db.char[i] = db.char.profile |
end |
end |
db.char.profile = nil |
db.char.specGroup = nil |
end |
end |
-- Reset a spec profile to the current one if its profile is deleted. |
function lib:OnProfileDeleted(event, target, profileName) |
local db = registry[target].db.char |
if not db.enabled then return end |
for i = 1, numSpecs do |
if db[i] == profileName then |
db[i] = target:GetCurrentProfile() |
end |
end |
end |
-- Actually enhance the database |
-- This is used on first initialization and everytime the database is reset using :ResetDB |
function lib:_EnhanceDatabase(event, target) |
return |
end |
registry[target] = { name = name } |
UpgradeDatabase(target) |
lib:_EnhanceDatabase("EnhanceDatabase", target) |
target.RegisterCallback(lib, "OnDatabaseReset", "_EnhanceDatabase") |
target.RegisterCallback(lib, "OnProfileDeleted") |
end |
-- ---------------------------------------------------------------------------- |
-- ---------------------------------------------------------------------------- |
local function NoDualSpec() |
return GetNumSpecGroups() == 1 |
return UnitLevel("player") < 11 |
end |
options.dualSpecDesc = { |
name = L_DUALSPEC_DESC, |
type = 'description', |
order = 40.1, |
hidden = NoDualSpec, |
options.new = { |
name = "New", |
type = "input", |
order = 30, |
get = false, |
set = function(info, value) |
local db = info.handler.db |
if db:IsDualSpecEnabled() then |
db:SetDualSpecProfile(value, lib.currentSpec) |
else |
db:SetProfile(value) |
end |
end, |
} |
options.choose = { |
name = "Existing Profiles", |
type = "select", |
order = 40, |
get = "GetCurrentProfile", |
set = "SetProfile", |
values = "ListProfiles", |
arg = "common", |
disabled = function(info) |
return info.handler.db:IsDualSpecEnabled() |
end |
} |
options.enabled = { |
name = L_ENABLED, |
name = "|cffffd200"..L_ENABLED.."|r", |
desc = L_ENABLED_DESC, |
type = 'toggle', |
order = 40.2, |
descStyle = "inline", |
type = "toggle", |
order = 41, |
width = "full", |
get = function(info) return info.handler.db:IsDualSpecEnabled() end, |
set = function(info, value) info.handler.db:SetDualSpecEnabled(value) end, |
hidden = NoDualSpec, |
} |
options.dualProfile = { |
name = L_DUAL_PROFILE, |
desc = L_DUAL_PROFILE_DESC, |
type = 'select', |
order = 40.3, |
get = function(info) return info.handler.db:GetDualSpecProfile() end, |
set = function(info, value) info.handler.db:SetDualSpecProfile(value) end, |
values = "ListProfiles", |
arg = "common", |
hidden = NoDualSpec, |
disabled = function(info) return not info.handler.db:IsDualSpecEnabled() end, |
} |
for i = 1, numSpecs do |
local _, specName = GetSpecializationInfoForClassID(classId, i) |
options["specProfile" .. i] = { |
type = "select", |
name = function() return lib.currentSpec == i and L_CURRENT:format(specName) or specName end, |
order = 42 + i, |
get = function(info) return info.handler.db:GetDualSpecProfile(i) end, |
set = function(info, value) info.handler.db:SetDualSpecProfile(value, i) end, |
values = "ListProfiles", |
arg = "common", |
disabled = function(info) return not info.handler.db:IsDualSpecEnabled() end, |
hidden = NoDualSpec, |
} |
end |
--- Embed dual spec options into an existing AceDBOptions-3.0 option table. |
-- @name LibDualSpec:EnhanceOptions |
-- @param target (table) The AceDB-3.0 the options operate on. |
function lib:EnhanceOptions(optionTable, target) |
AceDBOptions3 = AceDBOptions3 or LibStub('AceDBOptions-3.0', true) |
AceConfigRegistry3 = AceConfigRegistry3 or LibStub('AceConfigRegistry-3.0', true) |
if type(optionTable) ~= "table" then |
error("Usage: LibDualSpec:EnhanceOptions(optionTable, target): optionTable should be a table.", 2) |
elseif type(target) ~= "table" then |
error("Usage: LibDualSpec:EnhanceOptions(optionTable, target): target should be a table.", 2) |
elseif not (AceDBOptions3 and AceDBOptions3.optionTables[target]) then |
elseif not AceDBOptions3 or not AceDBOptions3.optionTables[target] then |
error("Usage: LibDualSpec:EnhanceOptions(optionTable, target): optionTable is not an AceDBOptions-3.0 table.", 2) |
elseif optionTable.handler.db ~= target then |
error("Usage: LibDualSpec:EnhanceOptions(optionTable, target): optionTable must be the option table of target.", 2) |
elseif not registry[target] then |
error("Usage: LibDualSpec:EnhanceOptions(optionTable, target): EnhanceDatabase should be called before EnhanceOptions(optionTable, target).", 2) |
elseif optionTable.plugins and optionTable.plugins[MAJOR] then |
return |
end |
-- localize our replacements |
options.new.name = optionTable.args.new.name |
options.new.desc = optionTable.args.new.desc |
options.choose.name = optionTable.args.choose.name |
options.choose.desc = optionTable.args.choose.desc |
-- add our new options |
if not optionTable.plugins then |
optionTable.plugins = {} |
end |
end |
-- ---------------------------------------------------------------------------- |
-- Upgrade existing |
-- ---------------------------------------------------------------------------- |
for target in next, registry do |
UpgradeDatabase(target) |
EmbedMixin(target) |
target:CheckDualSpecState() |
local optionTable = AceDBOptions3 and AceDBOptions3.optionTables[target] |
if optionTable then |
lib:EnhanceOptions(optionTable, target) |
end |
end |
-- ---------------------------------------------------------------------------- |
-- Inspection |
-- ---------------------------------------------------------------------------- |
-- Switching logic |
-- ---------------------------------------------------------------------------- |
lib.eventFrame:RegisterEvent('PLAYER_TALENT_UPDATE') |
lib.eventFrame:SetScript('OnEvent', function() |
lib.specLoaded = true |
local newSpecGroup = GetActiveSpecGroup() |
if lib.specGroup ~= newSpecGroup then |
lib.specGroup = newSpecGroup |
for target in pairs(registry) do |
target:CheckDualSpecState() |
local function eventHandler(self, event) |
lib.currentSpec = GetSpecialization() or 0 |
if event == "PLAYER_LOGIN" then |
self:UnregisterEvent(event) |
self:RegisterUnitEvent("PLAYER_SPECIALIZATION_CHANGED", "player") |
end |
if lib.currentSpec > 0 and next(upgrades) then |
for target in next, upgrades do |
UpgradeDatabase(target) |
end |
wipe(upgrades) |
end |
end) |
for target in next, registry do |
target:CheckDualSpecState() |
end |
if AceConfigRegistry3 and next(registry) then |
-- Update the "Current" text in options |
-- We don't get the key for the actual registered options table, and we can't |
-- really check for our enhanced options without walking every options table, |
-- so just refresh anything. |
for appName in AceConfigRegistry3:IterateOptionsTables() do |
AceConfigRegistry3:NotifyChange(appName) |
end |
end |
end |
lib.eventFrame:SetScript("OnEvent", eventHandler) |
if IsLoggedIn() then |
eventHandler(lib.eventFrame, "PLAYER_LOGIN") |
else |
lib.eventFrame:RegisterEvent("PLAYER_LOGIN") |
end |
## Interface: 50100 |
## Interface: 70000 |
## LoadOnDemand: 1 |
## Title: Lib: DualSpec-1.0 |
## Version: v1.7 |
## X-Date: @project-date@ |
## Notes: Adds dual spec support to individual AceDB-3.0 databases |
## Version: v1.13 |
## X-Date: 2016-08-24T06:22:17Z |
## Notes: Adds spec switching support to individual AceDB-3.0 databases. |
## Author: Adirelle |
## OptionalDeps: LibStub, Ace3 |
## X-Curse-Packaged-Version: v1.7 |
## OptionalDeps: Ace3 |
## X-Curse-Packaged-Version: v1.13 |
## X-Curse-Project-Name: LibDualSpec-1.0 |
## X-Curse-Project-ID: libdualspec-1-0 |
## X-Curse-Repository-ID: wow/libdualspec-1-0/mainline |
LibStub.lua |
LibDualSpec-1.0.lua |