Add files via upload

This commit is contained in:
Dusk-92
2026-06-09 11:23:40 +02:00
committed by GitHub
commit 11cb9ce00a
90 changed files with 57994 additions and 0 deletions
@@ -0,0 +1,371 @@
local L = AceLibrary("AceLocale-2.2"):new("NampowerSettings")
L:RegisterTranslations("enUS", function()
return {
["|cffffcc00Nampower v2|cffffaaaa not present hiding settings."] = true,
["update available"] = "|cffffcc00Nampower update v2.20.0 available.|cffffcc00 Some settings may be hidden until you update. Replace your existing nampower.dll with the latest from https://gitea.com/avitasia/nampower/releases",
["Superwow required to display queued spells."] = true,
["Enable Per Character Settings"] = true,
["Whether to use per character settings for all of the NP_ settings. This will cause settings saved in your character's NampowerSettings.lua to override any global settings in Config.wtf."] = true,
["Queue Cast Time Spells"] = true,
["Whether to enable spell queuing for spells with a cast time"] = true,
["Queue Instant Spells"] = true,
["Whether to enable spell queuing for instant cast spells tied to gcd"] = true,
["Queue On Swing Spells"] = true,
["Whether to enable on swing spell queuing"] = true,
["Queue Channeling Spells"] = true,
["Whether to enable channeling spell queuing"] = true,
["Queue Targeting Spells"] = true,
["Whether to enable terrain targeting spell queuing"] = true,
["Queue Spells Coming Off Cooldown"] = true,
["Whether to enable spell queuing for spells coming off cooldown"] = true,
["Queue Windows"] = true,
["How much time in ms you have before a cast ends to queue different types of spells"] = true,
["Spell Queue Window (ms)"] = true,
["The window in ms before a cast finishes where the next will get queued"] = true,
["On Swing Buffer Cooldown (ms)"] = true,
["The cooldown time in ms after an on swing spell before you can queue on swing spells"] = true,
["Channel Queue Window (ms)"] = true,
["The window in ms before a channel finishes where the next will get queued"] = true,
["Targeting Queue Window (ms)"] = true,
["The window in ms before a terrain targeting spell finishes where the next will get queued"] = true,
["Cooldown Queue Window (ms)"] = true,
["The window in ms before a spell coming off cooldown finishes where the next will get queued"] = true,
["Cast Options"] = true,
["Options for controlling casting behavior"] = true,
["QOL Options"] = true,
["Quality of life options to prevent common issues"] = true,
["Chat Bubbles"] = true,
["Chat bubble options"] = true,
["Chat Bubbles (Say/Yell)"] = true,
["Whether to enable chat bubbles for /say and /yell messages."] = true,
["Chat Bubbles (Party)"] = true,
["Whether to enable chat bubbles for /party messages."] = true,
["Chat Bubbles (Whisper)"] = true,
["Whether to enable chat bubbles for /whisper messages."] = true,
["Chat Bubbles (Raid)"] = true,
["Whether to enable chat bubbles for /raid messages."] = true,
["Chat Bubbles (Battleground)"] = true,
["Whether to enable chat bubbles for battleground messages."] = true,
["Chat Bubble Distance"] = true,
["The distance in yards to show chat bubbles"] = true,
["Advanced options"] = true,
["Collection of various advanced options"] = true,
["Spell Events"] = true,
["Controls optional spell and combat event CVars."] = true,
["Double Cast to End Channel Early"] = true,
["Whether to allow double casting a spell within 350ms to end channeling on the next tick. Takes into account your ChannelLatencyReductionPercentage."] = true,
["Quickcast Targeting Spells on Double Cast"] = true,
["Allows casting targeting spells by attempting to cast them twice as opposed to the default client behavior which cancels the targeting indicator"] = true,
["Interrupt Channels Outside Queue Window"] = true,
["Whether to allow interrupting channels (the original client behavior) when trying to cast a spell outside the channeling queue window"] = true,
["Retry Server Rejected Spells"] = true,
["Whether to retry spells that are rejected by the server for these reasons: SPELL_FAILED_ITEM_NOT_READY, SPELL_FAILED_NOT_READY, SPELL_FAILED_SPELL_IN_PROGRESS"] = true,
["Quickcast Targeting Spells"] = true,
["Whether to enable quick casting for ALL spells with terrain targeting"] = true,
["Replace Matching Non GCD Category"] = true,
["Whether to replace any queued non gcd spell when a new non gcd spell with the same non zero StartRecoveryCategory is cast. Most trinkets and spells are category 0 which are ignored by this setting. The primary use case is to switch which potion you have queued."] = true,
["Optimize Buffer Using Packet Timings"] = true,
["Whether to attempt to optimize your buffer using your latency and server packet timings"] = true,
["Minimum Buffer Time (ms)"] = true,
["The minimum buffer delay in ms added to each cast"] = true,
["Non GCD Buffer Time (ms)"] = true,
["The buffer delay in ms added AFTER each cast that is not tied to the gcd"] = true,
["Max Buffer Increase (ms)"] = true,
["The maximum amount of time in ms to increase the buffer by when the server rejects a cast"] = true,
["Channel Latency Reduction (%)"] = true,
["The percentage of your latency to subtract from the end of a channel duration to optimize cast time while hopefully not losing any ticks"] = true,
["Queued Spell Display Options"] = true,
["Options for displaying an icon for the queued spell"] = true,
["Display queued spell icon"] = true,
["Whether to display an icon of the queued spell"] = true,
["Icon size"] = true,
["Change the spell icon size"] = true,
["Allow dragging"] = true,
["Whether to allow interaction with the queued spell icon so you can move it around"] = true,
["Reset Position"] = true,
["Reset the position of the queued spell icon"] = true,
["Show/Hide for positioning"] = true,
["Test the queued spell icon and position it to your liking"] = true,
["Prevent Right Click Target Change"] = true,
["Whether to prevent right-clicking from changing your current target when in combat. If you don't have a target right click will still change your target even with this on. This is mainly to prevent accidentally changing targets in combat when trying to adjust your camera."] = true,
["Nameplate Distance"] = true,
["The distance in yards to show nameplates"] = true,
["Spam Protection"] = true,
["Whether to enable spam protection functionality that blocks spamming spells while waiting for the server to respond to your initial cast due to issues spamming can cause"] = true,
["Prevent Right Click PvP Attack"] = true,
["Whether to prevent right-clicking on PvP flagged players to avoid accidental PvP attacks"] = true,
["Prevent Mounting When Buff Capped"] = true,
["Whether to prevent mounting when you have 32 buffs (buff capped) and are not already mounted. This prevents the issue where you mount but cannot dismount because the mount aura fails to apply due to the buff cap. When blocked, displays an error message."] = true,
["Enable Enhanced Tooltips"] = true,
["Whether to enable nampower's tooltip additions such as spell proc chance, ICD, and item cooldown text."] = true,
["Enable Aura Cast Events"] = true,
["Whether to enable AURA_CAST_ON_SELF and AURA_CAST_ON_OTHER events."] = true,
["Enable Auto Attack Events"] = true,
["Whether to enable AUTO_ATTACK_SELF and AUTO_ATTACK_OTHER events."] = true,
["Enable Spell Start Events"] = true,
["Whether to enable SPELL_START_SELF and SPELL_START_OTHER events."] = true,
["Enable Spell Go Events"] = true,
["Whether to enable SPELL_GO_SELF and SPELL_GO_OTHER events."] = true,
["Enable Spell Heal Events"] = true,
["Whether to enable SPELL_HEAL_BY_SELF, SPELL_HEAL_BY_OTHER, and SPELL_HEAL_ON_SELF events."] = true,
["Enable Spell Energize Events"] = true,
["Whether to enable SPELL_ENERGIZE_BY_SELF, SPELL_ENERGIZE_BY_OTHER, and SPELL_ENERGIZE_ON_SELF events."] = true,
["Unit Event Filters"] = true,
["Controls which unit identifiers trigger unit events (UNIT_HEALTH, UNIT_COMBAT, etc.). In the base game the same event can fire multiple times for the same unit, once per identifying string (e.g. 'party1', 'raid1', 'mouseover'). Disabling unused identifiers reduces redundant event calls. Note: 'player', 'target', and 'pet' (your own pet) always trigger regardless of these settings, as they are critical."] = true,
["Enable Pet Unit Events"] = true,
["Whether to fire unit events (UNIT_HEALTH, UNIT_COMBAT, etc.) for party and raid pet identifiers ('party1pet', 'raid1pet', etc.). Party pets additionally require Enable Party Unit Events to be on; raid pets additionally require Enable Raid Unit Events to be on. Your own pet ('pet') always fires events regardless of this setting."] = true,
["Enable Party Unit Events"] = true,
["Whether to fire unit events (UNIT_HEALTH, UNIT_COMBAT, etc.) for party member identifiers ('party1', 'party2', 'party3', 'party4'). Also required alongside Enable Pet Unit Events for party pet identifiers to fire."] = true,
["Enable Raid Unit Events"] = true,
["Whether to fire unit events (UNIT_HEALTH, UNIT_COMBAT, etc.) for raid member identifiers ('raid1' through 'raid40'). Also required alongside Enable Pet Unit Events for raid pet identifiers to fire."] = true,
["Enable Mouseover Unit Events"] = true,
["Whether to fire unit events (UNIT_HEALTH, UNIT_COMBAT, etc.) for the 'mouseover' unit identifier."] = true,
["Enable GUID Unit Events"] = true,
["Whether to fire unit events (UNIT_HEALTH, UNIT_MANA, UNIT_AURA, etc.) using the raw GUID as the unit token, mirroring SuperWoW behavior. Fires for every unit the client tracks — not just named tokens like 'player', 'party1', 'raid1' — which can cause significant event spam in raids, BGs, and crowded zones. Older addons (e.g. pfUI) written for standard named tokens may have performance issues receiving GUID-based events. Addons needing GUID tracking (e.g. Automarker, Cursive) should use the new dedicated UNIT_HEALTH_GUID, UNIT_MANA_GUID, etc. events instead, allowing this to be safely disabled."] = true,
["Enable GUID Unit Event Filtering"] = true,
["When enabled, suppresses high-frequency GUID events that cause spam in older addons — specifically UNIT_AURA, UNIT_HEALTH, UNIT_MANA, and similar events below UNIT_COMBAT, plus UNIT_NAME_UPDATE, UNIT_PORTRAIT_UPDATE, UNIT_INVENTORY_CHANGED, and PLAYER_GUILD_UPDATE. UNIT_COMBAT_GUID and other less frequent GUID events are still fired. Has no effect if Enable GUID Unit Events is disabled. This is a direct replacement for the 'Filter GUID Events' option in PerfBoost."] = true,
["Preserve Greater Demon Autocast"] = true,
["Whether to remember and restore Felguard/Doomguard autocast preferences when swapping or resummoning those greater demons."] = true,
}
end)
L:RegisterTranslations("zhCN", function()
return {
["|cffffcc00Nampower v2|cffffaaaa not present hiding settings."] = "|cffffcc00Nampower v2|cffffaaaa 未安装,隐藏设置项。",
["update available"] = "|cffffcc00Nampower v2.20.0 更新可用。|cffffcc00 更新前部分设置可能隐藏。请从 https://gitea.com/avitasia/nampower/releases 下载最新 nampower.dll 替换当前版本。",
["Superwow required to display queued spells."] = "需 Superwow 插件显示队列中法术。",
["Enable Per Character Settings"] = "启用角色独立设置",
["Whether to use per character settings for all of the NP_ settings. This will cause settings saved in your character's NampowerSettings.lua to override any global settings in Config.wtf."] = "是否为所有 NP_ 设置启用角色独立配置。启用后,角色 NampowerSettings.lua 中的设置将覆盖 Config.wtf 中的全局设置。",
["Queue Cast Time Spells"] = "队列施法时间法术",
["Whether to enable spell queuing for spells with a cast time"] = "是否对施法时间类法术启用队列功能",
["Queue Instant Spells"] = "队列瞬发法术",
["Whether to enable spell queuing for instant cast spells tied to gcd"] = "是否对涉及公共冷却的瞬发法术启用队列功能",
["Queue On Swing Spells"] = "队列特殊攻击法术",
["Whether to enable on swing spell queuing"] = "是否对特殊攻击类法术(如战士压制)启用队列功能",
["Queue Channeling Spells"] = "队列引导法术",
["Whether to enable channeling spell queuing"] = "是否对引导类法术(如暴风雪)启用队列功能",
["Queue Targeting Spells"] = "队列目标区域法术",
["Whether to enable terrain targeting spell queuing"] = "是否对目标区域类法术(如暴风雪)启用队列功能",
["Queue Spells Coming Off Cooldown"] = "队列冷却结束法术",
["Whether to enable spell queuing for spells coming off cooldown"] = "是否对即将结束冷却的法术启用队列功能",
["Queue Windows"] = "队列窗口设置",
["How much time in ms you have before a cast ends to queue different types of spells"] = "各种法术类型在施法结束前多少毫秒(ms)开始进入队列",
["Spell Queue Window (ms)"] = "法术队列窗口(ms)",
["The window in ms before a cast finishes where the next will get queued"] = "当前施法结束前多少毫秒(ms)可将下一个法术加入队列",
["On Swing Buffer Cooldown (ms)"] = "特殊攻击缓冲冷却(ms)",
["The cooldown time in ms after an on swing spell before you can queue on swing spells"] = "特殊攻击法术施放后,多少毫秒(ms)内无法再次将同类法术加入队列",
["Channel Queue Window (ms)"] = "引导法术队列窗口(ms)",
["The window in ms before a channel finishes where the next will get queued"] = "引导法术结束前多少毫秒(ms)可将下一个法术加入队列",
["Targeting Queue Window (ms)"] = "目标区域法术队列窗口(ms)",
["The window in ms before a terrain targeting spell finishes where the next will get queued"] = "目标区域法术结束前多少毫秒(ms)可将下一个法术加入队列",
["Cooldown Queue Window (ms)"] = "冷却结束队列窗口(ms)",
["The window in ms before a spell coming off cooldown finishes where the next will get queued"] = "法术冷却结束前多少毫秒(ms)可将该法术加入队列",
["Cast Options"] = "施法选项",
["Options for controlling casting behavior"] = "控制施法行为的选项",
["QOL Options"] = "便利性选项",
["Quality of life options to prevent common issues"] = "用于防止常见错误的便利性选项",
["Chat Bubbles"] = "聊天气泡",
["Chat bubble options"] = "聊天气泡相关选项",
["Chat Bubbles (Say/Yell)"] = "聊天气泡(说话/大喊)",
["Whether to enable chat bubbles for /say and /yell messages."] = "是否为 /say 与 /yell 消息启用聊天气泡。",
["Chat Bubbles (Party)"] = "聊天气泡(队伍)",
["Whether to enable chat bubbles for /party messages."] = "是否为 /party 消息启用聊天气泡。",
["Chat Bubbles (Whisper)"] = "聊天气泡(密语)",
["Whether to enable chat bubbles for /whisper messages."] = "是否为 /whisper 消息启用聊天气泡。",
["Chat Bubbles (Raid)"] = "聊天气泡(团队)",
["Whether to enable chat bubbles for /raid messages."] = "是否为 /raid 消息启用聊天气泡。",
["Chat Bubbles (Battleground)"] = "聊天气泡(战场)",
["Whether to enable chat bubbles for battleground messages."] = "是否为战场消息启用聊天气泡。",
["Chat Bubble Distance"] = "聊天气泡距离",
["The distance in yards to show chat bubbles"] = "显示聊天气泡的最大距离(码)",
["Advanced options"] = "高级选项",
["Collection of various advanced options"] = "各类高级选项集合",
["Spell Events"] = "法术事件",
["Controls optional spell and combat event CVars."] = "控制可选的法术和战斗事件 CVar。",
["Double Cast to End Channel Early"] = "双重施法提前结束引导",
["Whether to allow double casting a spell within 350ms to end channeling on the next tick. Takes into account your ChannelLatencyReductionPercentage."] = "是否允许350毫秒内双重施法以在下一个伤害周期提前结束引导。受引导延迟削减比例影响。",
["Quickcast Targeting Spells on Double Cast"] = "双重施法快速施放目标区域法术",
["Allows casting targeting spells by attempting to cast them twice as opposed to the default client behavior which cancels the targeting indicator"] = "允许通过双次尝试施法来释放目标区域法术,而非默认客户端行为(取消目标指示器)",
["Interrupt Channels Outside Queue Window"] = "队列窗口外允许打断引导",
["Whether to allow interrupting channels (the original client behavior) when trying to cast a spell outside the channeling queue window"] = "在引导法术队列窗口外尝试施法时,是否允许打断引导(默认客户端行为)",
["Retry Server Rejected Spells"] = "重试服务器拒绝的法术",
["Whether to retry spells that are rejected by the server for these reasons: SPELL_FAILED_ITEM_NOT_READY, SPELL_FAILED_NOT_READY, SPELL_FAILED_SPELL_IN_PROGRESS"] = "是否对因以下原因被服务器拒绝的法术进行重试:未准备就绪、未冷却、已有法术施放中",
["Quickcast Targeting Spells"] = "快速施放目标区域法术",
["Whether to enable quick casting for ALL spells with terrain targeting"] = "是否为所有目标区域法术启用快速施放功能",
["Replace Matching Non GCD Category"] = "替换同类型非公共冷却法术",
["Whether to replace any queued non gcd spell when a new non gcd spell with the same non zero StartRecoveryCategory is cast. Most trinkets and spells are category 0 which are ignored by this setting. The primary use case is to switch which potion you have queued."] = "当施放新的非公共冷却法术且其启动类别相同时,是否替换队列中的同类法术。饰品和多数法术属类别0(不受影响),主要用于替换已队列的药水。",
["Optimize Buffer Using Packet Timings"] = "基于封包时间优化缓冲",
["Whether to attempt to optimize your buffer using your latency and server packet timings"] = "是否根据延迟和服务器封包时间优化法术缓冲机制",
["Minimum Buffer Time (ms)"] = "最低缓冲时间(ms)",
["The minimum buffer delay in ms added to each cast"] = "为每次施法添加的最小缓冲延迟(毫秒)",
["Non GCD Buffer Time (ms)"] = "非公共冷却缓冲时间(ms)",
["The buffer delay in ms added AFTER each cast that is not tied to the gcd"] = "在非公共冷却法术施放后添加的缓冲延迟(毫秒)",
["Max Buffer Increase (ms)"] = "最大缓冲增加量(ms)",
["The maximum amount of time in ms to increase the buffer by when the server rejects a cast"] = "当法术被服务器拒绝时,缓冲时间可增加的最大毫秒数",
["Channel Latency Reduction (%)"] = "引导延迟削减比例(%)",
["The percentage of your latency to subtract from the end of a channel duration to optimize cast time while hopefully not losing any ticks"] = "为优化施法时间,从引导结束时序中扣除延迟的百分比(避免丢失伤害周期)",
["Queued Spell Display Options"] = "队列法术显示选项",
["Options for displaying an icon for the queued spell"] = "队列中法术图标的显示设置",
["Display queued spell icon"] = "显示队列法术图标",
["Whether to display an icon of the queued spell"] = "是否显示队列中法术的图标",
["Icon size"] = "图标尺寸",
["Change the spell icon size"] = "修改法术图标大小",
["Allow dragging"] = "允许拖动",
["Whether to allow interaction with the queued spell icon so you can move it around"] = "是否允许通过拖动交互调整队列法术图标位置",
["Reset Position"] = "重置位置",
["Reset the position of the queued spell icon"] = "重置队列法术图标位置",
["Show/Hide for positioning"] = "显示/隐藏(用于定位)",
["Test the queued spell icon and position it to your liking"] = "测试队列法术图标位置,按需调整",
["Prevent Right Click Target Change"] = "阻止右键切换目标",
["Whether to prevent right-clicking from changing your current target when in combat. If you don't have a target right click will still change your target even with this on. This is mainly to prevent accidentally changing targets in combat when trying to adjust your camera."] = "战斗中右键点击是否不改变当前目标(无目标时仍可切换)。主要用于调整镜头时避免误切目标。",
["Nameplate Distance"] = "姓名板显示距离",
["The distance in yards to show nameplates"] = "显示姓名板的最大距离(码)",
["Spam Protection"] = "防连点保护",
["Whether to enable spam protection functionality that blocks spamming spells while waiting for the server to respond to your initial cast due to issues spamming can cause"] = "是否启用防连点保护功能,避免在等待服务器响应期间连点施法导致的问题",
["Prevent Right Click PvP Attack"] = "阻止右键PvP攻击",
["Whether to prevent right-clicking on PvP flagged players to avoid accidental PvP attacks"] = "是否防止右键点击PvP标记玩家以避免误触发PvP攻击",
["Prevent Mounting When Buff Capped"] = "Buff满时阻止上坐骑",
["Whether to prevent mounting when you have 32 buffs (buff capped) and are not already mounted. This prevents the issue where you mount but cannot dismount because the mount aura fails to apply due to the buff cap. When blocked, displays an error message."] = "当已有32个增益(Buff已满)且未在坐骑状态时,是否阻止上坐骑,以避免坐骑光环因Buff上限无法施加导致无法下马的问题。被阻止时会显示错误提示。",
["Enable Enhanced Tooltips"] = "启用增强工具提示",
["Whether to enable nampower's tooltip additions such as spell proc chance, ICD, and item cooldown text."] = "是否启用 nampower 的工具提示增强内容,例如法术触发几率、内置冷却时间(ICD)和物品冷却文本。",
["Enable Aura Cast Events"] = "启用光环施放事件",
["Whether to enable AURA_CAST_ON_SELF and AURA_CAST_ON_OTHER events."] = "是否启用 AURA_CAST_ON_SELF 和 AURA_CAST_ON_OTHER 事件。",
["Enable Auto Attack Events"] = "启用自动攻击事件",
["Whether to enable AUTO_ATTACK_SELF and AUTO_ATTACK_OTHER events."] = "是否启用 AUTO_ATTACK_SELF 和 AUTO_ATTACK_OTHER 事件。",
["Enable Spell Start Events"] = "启用法术开始事件",
["Whether to enable SPELL_START_SELF and SPELL_START_OTHER events."] = "是否启用 SPELL_START_SELF 和 SPELL_START_OTHER 事件。",
["Enable Spell Go Events"] = "启用法术释放事件",
["Whether to enable SPELL_GO_SELF and SPELL_GO_OTHER events."] = "是否启用 SPELL_GO_SELF 和 SPELL_GO_OTHER 事件。",
["Enable Spell Heal Events"] = "启用法术治疗事件",
["Whether to enable SPELL_HEAL_BY_SELF, SPELL_HEAL_BY_OTHER, and SPELL_HEAL_ON_SELF events."] = "是否启用 SPELL_HEAL_BY_SELF、SPELL_HEAL_BY_OTHER 和 SPELL_HEAL_ON_SELF 事件。",
["Enable Spell Energize Events"] = "启用法术回能事件",
["Whether to enable SPELL_ENERGIZE_BY_SELF, SPELL_ENERGIZE_BY_OTHER, and SPELL_ENERGIZE_ON_SELF events."] = "是否启用 SPELL_ENERGIZE_BY_SELF、SPELL_ENERGIZE_BY_OTHER 和 SPELL_ENERGIZE_ON_SELF 事件。",
["Unit Event Filters"] = "单位事件过滤器",
["Controls which unit identifiers trigger unit events (UNIT_HEALTH, UNIT_COMBAT, etc.). In the base game the same event can fire multiple times for the same unit, once per identifying string (e.g. 'party1', 'raid1', 'mouseover'). Disabling unused identifiers reduces redundant event calls. Note: 'player', 'target', and 'pet' (your own pet) always trigger regardless of these settings, as they are critical."] = "控制哪些单位标识符触发单位事件(UNIT_HEALTH、UNIT_COMBAT 等)。在原版游戏中,同一单位可能因多个标识符(如 'party1'、'raid1'、'mouseover')重复触发同一事件。禁用不需要的标识符可减少冗余事件调用。注意:'player'(自身)、'target'(目标)和 'pet'(自己的宠物)始终会触发,不受此处设置影响,因为它们是关键标识符。",
["Enable Pet Unit Events"] = "启用宠物单位事件",
["Whether to fire unit events (UNIT_HEALTH, UNIT_COMBAT, etc.) for party and raid pet identifiers ('party1pet', 'raid1pet', etc.). Party pets additionally require Enable Party Unit Events to be on; raid pets additionally require Enable Raid Unit Events to be on. Your own pet ('pet') always fires events regardless of this setting."] = "是否为队伍和团队宠物标识符('party1pet'、'raid1pet' 等)触发单位事件(UNIT_HEALTH、UNIT_COMBAT 等)。队伍宠物还需同时开启「启用队伍单位事件」;团队宠物还需同时开启「启用团队单位事件」。自己的宠物('pet')始终触发事件,不受此设置影响。",
["Enable Party Unit Events"] = "启用队伍单位事件",
["Whether to fire unit events (UNIT_HEALTH, UNIT_COMBAT, etc.) for party member identifiers ('party1', 'party2', 'party3', 'party4'). Also required alongside Enable Pet Unit Events for party pet identifiers to fire."] = "是否为队伍成员标识符('party1'、'party2'、'party3'、'party4')触发单位事件(UNIT_HEALTH、UNIT_COMBAT 等)。同时也是队伍宠物标识符触发的必要条件(需与「启用宠物单位事件」共同开启)。",
["Enable Raid Unit Events"] = "启用团队单位事件",
["Whether to fire unit events (UNIT_HEALTH, UNIT_COMBAT, etc.) for raid member identifiers ('raid1' through 'raid40'). Also required alongside Enable Pet Unit Events for raid pet identifiers to fire."] = "是否为团队成员标识符('raid1' 至 'raid40')触发单位事件(UNIT_HEALTH、UNIT_COMBAT 等)。同时也是团队宠物标识符触发的必要条件(需与「启用宠物单位事件」共同开启)。",
["Enable Mouseover Unit Events"] = "启用鼠标悬停单位事件",
["Whether to fire unit events (UNIT_HEALTH, UNIT_COMBAT, etc.) for the 'mouseover' unit identifier."] = "是否为 'mouseover'(鼠标悬停)单位标识符触发单位事件(UNIT_HEALTH、UNIT_COMBAT 等)。",
["Enable GUID Unit Events"] = "启用GUID单位事件",
["Whether to fire unit events (UNIT_HEALTH, UNIT_MANA, UNIT_AURA, etc.) using the raw GUID as the unit token, mirroring SuperWoW behavior. Fires for every unit the client tracks — not just named tokens like 'player', 'party1', 'raid1' — which can cause significant event spam in raids, BGs, and crowded zones. Older addons (e.g. pfUI) written for standard named tokens may have performance issues receiving GUID-based events. Addons needing GUID tracking (e.g. Automarker, Cursive) should use the new dedicated UNIT_HEALTH_GUID, UNIT_MANA_GUID, etc. events instead, allowing this to be safely disabled."] = "是否使用原始 GUID 作为单位标识符触发单位事件(UNIT_HEALTH、UNIT_MANA、UNIT_AURA 等),模拟 SuperWoW 行为。此功能会对客户端跟踪的所有单位触发事件——不仅限于 'player'、'party1'、'raid1' 等命名标识符——在团队、战场和人口密集区域可能产生大量事件。为标准命名标识符设计的旧版插件(如 pfUI)在收到 GUID 事件时可能出现性能问题。需要 GUID 跟踪的插件(如 Automarker、Cursive)应改用专用的 UNIT_HEALTH_GUID、UNIT_MANA_GUID 等事件,届时可安全禁用此选项。",
["Enable GUID Unit Event Filtering"] = "启用GUID单位事件过滤",
["When enabled, suppresses high-frequency GUID events that cause spam in older addons — specifically UNIT_AURA, UNIT_HEALTH, UNIT_MANA, and similar events below UNIT_COMBAT, plus UNIT_NAME_UPDATE, UNIT_PORTRAIT_UPDATE, UNIT_INVENTORY_CHANGED, and PLAYER_GUILD_UPDATE. UNIT_COMBAT_GUID and other less frequent GUID events are still fired. Has no effect if Enable GUID Unit Events is disabled. This is a direct replacement for the 'Filter GUID Events' option in PerfBoost."] = "启用后,将屏蔽对旧版插件造成刷屏的高频 GUID 事件——具体包括 UNIT_COMBAT 以下的事件(如 UNIT_AURA、UNIT_HEALTH、UNIT_MANA 等),以及 UNIT_NAME_UPDATE、UNIT_PORTRAIT_UPDATE、UNIT_INVENTORY_CHANGED 和 PLAYER_GUILD_UPDATE。UNIT_COMBAT_GUID 及其他低频 GUID 事件仍会正常触发。若已禁用「启用GUID单位事件」,则此设置无效。此选项直接替代 PerfBoost 中的「过滤GUID事件」功能。",
["Preserve Greater Demon Autocast"] = "保留大型恶魔自动施法",
["Whether to remember and restore Felguard/Doomguard autocast preferences when swapping or resummoning those greater demons."] = "切换或重新召唤地狱守卫/末日守卫时,是否记忆并恢复其自动施法设置。",
}
end)
@@ -0,0 +1,27 @@
## Interface: 11200
## Title: Nampower Settings
## Notes: the companion compatibility addon to the mod of the same name.
## Title-zhCN:|CFFEE7600[模组增强]|R |cff00ffffNampower设置|R|cffff1450[需Nampower模组]
## Notes-zhCN: Nampower模组配套的设置工具,能够根据自己的网络和需要设置该模组内部的各种参数。
## SavedVariablesPerCharacter: NampowerSettingsDB
## X-Embeds: Ace2, FuBarPlugin-2.0, CompostLib, DewDropLib, TabletLib
libs\AceLibrary\AceLibrary.lua
libs\AceOO-2.0\AceOO-2.0.lua
libs\AceAddon-2.0\AceAddon-2.0.lua
libs\AceConsole-2.0\AceConsole-2.0.lua
libs\AceHook-2.1\AceHook-2.1.lua
libs\AceDB-2.0\AceDB-2.0.lua
libs\AceDebug-2.0\AceDebug-2.0.lua
libs\AceEvent-2.0\AceEvent-2.0.lua
libs\AceLocale-2.2\AceLocale-2.2.lua
libs\AceModuleCore-2.0\AceModuleCore-2.0.lua
libs\Dewdrop\Dewdrop-2.0.lua
libs\Tablet\Tablet-2.0.lua
libs\FuBarPlugin\FuBarPlugin-2.0.lua
Localization.lua
settings.lua
helper.lua
@@ -0,0 +1,12 @@
### Nampower settings
See https://gitea.com/avitasia/nampower or in game tooltip for description of each setting.
Access settings in game by right clicking the minimap icon.
### Per Character settings
You can now use different nampower settings for each character. When you enable this option it will save all of your current nampower settings into
WTF\Account\YOUR_ACCOUNT\Nordanaar\YOUR_CHARACTER\SavedVariables\NampowerSettings.lua. Then every time the game loads the settings in that file will overwrite both the nampower defaults and whatever cvars were saved in your main Config.wtf. If you make changes to any settings while this option is enabled it will save to that character's settings file AS WELL AS the main Config.wtf.
#### Queued spell icon display
If you have superwow there is an option (default off) to display an icon for the most recently queued spell (the mini fireball in the castbar). You can resize and position it anywhere.
![img.png](img.png)
@@ -0,0 +1,952 @@
if not Nampower then
return
end
if not Nampower:HasMinimumVersion(2, 14, 0) then
return
end
local function GetSpellMod(num)
local modifierTypes = {
[0] = "DAMAGE",
[1] = "DURATION",
[2] = "THREAT",
[3] = "ATTACK_POWER",
[4] = "CHARGES",
[5] = "RANGE",
[6] = "RADIUS",
[7] = "CRITICAL_CHANCE",
[8] = "ALL_EFFECTS",
[9] = "NOT_LOSE_CASTING_TIME",
[10] = "CASTING_TIME",
[11] = "COOLDOWN",
[12] = "SPEED",
[14] = "COST",
[15] = "CRIT_DAMAGE_BONUS",
[16] = "RESIST_MISS_CHANCE",
[17] = "JUMP_TARGETS",
[18] = "CHANCE_OF_SUCCESS",
[19] = "ACTIVATION_TIME",
[20] = "EFFECT_PAST_FIRST",
[21] = "CASTING_TIME_OLD",
[22] = "DOT",
[23] = "HASTE",
[24] = "SPELL_BONUS_DAMAGE",
[27] = "MULTIPLE_VALUE",
[28] = "RESIST_DISPEL_CHANCE"
}
return modifierTypes[num] or "UNKNOWN"
end
function PrintTable(tbl, indent)
indent = indent or 0
local prefix = string.rep(" ", indent)
for k, v in pairs(tbl) do
if type(v) == "table" then
print(prefix .. tostring(k) .. ":")
PrintTable(v, indent + 1)
else
print(prefix .. tostring(k) .. ": " .. tostring(v))
end
end
end
if CombatLogAdd then
function PrintTableToCombatLog(tbl, indent)
indent = indent or 0
local prefix = string.rep(" ", indent)
for k, v in pairs(tbl) do
if type(v) == "table" then
CombatLogAdd(prefix .. tostring(k) .. ":")
PrintTableToCombatLog(v, indent + 1)
else
CombatLogAdd(prefix .. tostring(k) .. ": " .. tostring(v))
end
end
end
function LogUnitData(unit)
local data = GetUnitData(unit)
if data then
PrintTableToCombatLog(data)
else
print("No unit data available")
end
end
function LogSpellData(spellId)
local data = GetSpellRec(spellId)
if data then
PrintTableToCombatLog(data)
else
print("No spell data available")
end
end
end
function PrintBuffs(unit)
for i = 1, 32 do
local texture, stacks, spellId = UnitBuff(unit, i)
if not texture then
break
end
print(tostring(i) .. " " .. texture .. " " .. tostring(spellId))
end
end
function PrintAuras(unit)
local auras = GetUnitField(unit, "aura")
local applications = GetUnitField(unit, "auraApplications")
if not auras then return end
for i, auraId in ipairs(auras) do
local stacks = -1
if applications then
local stacks = (applications[i] or 0) + 1
end
if auraId > 0 then
print(tostring(i) .. " " .. tostring(auraId) .. " x" .. tostring(stacks))
end
end
end
function PrintUnitData(unit)
local data = GetUnitData(unit)
if data then
PrintTable(data)
else
print("No unit data available")
end
end
function PrintSpellMods(spellId)
for i = 0, 28 do
local flatMod, percentMod, ret = GetSpellModifiers(spellId, i)
if ret > 0 then
print(GetSpellMod(i) .. " (" .. tostring(i) .. "): flat:" .. tostring(flatMod) .. " percent:" .. tostring(percentMod))
end
end
end
function PrintSpellData(spellId)
local data = GetSpellRec(spellId)
if data then
PrintTable(data)
else
print("No spell data available")
end
end
if not Nampower:HasMinimumVersion(2, 16, 0) then
return
end
function PrintItemLocation(itemNameOrId)
local bagIndex, slot = FindPlayerItemSlot(itemNameOrId)
print(tostring(bagIndex) .. " " .. tostring(slot))
end
function PrintEquippedItems()
local target = "player"
if UnitExists("target") then
target = "target"
end
local data = GetEquippedItems(target)
PrintTable(data)
end
function LogEquippedItems()
local target = "player"
if UnitExists("target") then
target = "target"
end
local data = GetEquippedItems(target)
PrintTableToCombatLog(data)
end
function PrintBagItems()
local data = GetBagItems()
PrintTable(data)
end
function LogBagItems()
local data = GetBagItems()
PrintTableToCombatLog(data)
end
function TestBagItems(bagIndex)
local items = GetBagItems(bagIndex)
local numItemsChecked = 0
for slot, itemData in pairs(items) do
local itemLink = GetContainerItemLink(bagIndex, slot)
if not itemLink then
print(tostring(bagIndex) .. " " .. tostring(slot))
PrintTable(itemData)
end
local _, _, itemId = strfind(itemLink, "(%d+):")
numItemsChecked = numItemsChecked + 1
if (tostring(itemData.itemId) ~= itemId) then
print("Item mismatch at " .. tostring(bagIndex) .. " " .. tostring(slot) .. " " .. tostring(itemData.itemId) .. " " .. tostring(itemId))
print(itemLink)
end
end
print("checked " .. tostring(numItemsChecked) .. " items")
end
function TestAllBagItems()
local data = GetBagItems()
local numItemsChecked = 0
for bagIndex, items in pairs(data) do
for slot, itemData in pairs(items) do
local itemLink = GetContainerItemLink(bagIndex, slot)
if not itemLink then
print(tostring(bagIndex) .. " " .. tostring(slot))
PrintTable(itemData)
end
local _, _, itemId = strfind(itemLink, "(%d+):")
numItemsChecked = numItemsChecked + 1
if (tostring(itemData.itemId) ~= itemId) then
print("Item mismatch at " .. tostring(bagIndex) .. " " .. tostring(slot) .. " " .. tostring(itemData.itemId) .. " " .. tostring(itemId))
print(itemLink)
end
end
end
print("checked " .. tostring(numItemsChecked) .. " items")
end
function TestTrinkets()
local data = GetTrinkets()
for index, trinketData in pairs(data) do
local bagIndex = trinketData.bagIndex
local slot = trinketData.slotIndex
local itemLink
if bagIndex then
itemLink = GetContainerItemLink(bagIndex, slot)
else
itemLink = GetInventoryItemLink("player", slot)
end
local _, _, itemId = strfind(itemLink, "(%d+):")
if (tostring(trinketData.itemId) ~= itemId) then
print("Trinket mismatch at " .. tostring(bagIndex) .. " " .. tostring(slot) .. " " .. tostring(itemData.itemId) .. " " .. tostring(itemId))
print(itemLink)
end
end
end
if not Nampower:HasMinimumVersion(2, 17, 0) then
return
end
function PrintCastInfo()
local data = GetCastInfo()
if data then
PrintTable(data)
end
end
function PrintSpellCooldown(spellId)
local data = GetSpellIdCooldown(spellId)
if data then
PrintTable(data)
end
end
function PrintItemCooldown(itemId)
local data = GetItemIdCooldown(itemId)
if data then
PrintTable(data)
end
end
function PrintTrinketCooldown(trinketId)
local data = GetTrinketCooldown(trinketId)
if data then
PrintTable(data)
end
end
function PrintTrinkets()
local data = GetTrinkets(1)
if data then
PrintTable(data)
end
end
if not Nampower:HasMinimumVersion(2, 24, 0) then
return
end
-- Auto Attack Event Constants
local HITINFO_NORMALSWING = 0
local HITINFO_UNK0 = 1
local HITINFO_AFFECTS_VICTIM = 2
local HITINFO_LEFTSWING = 4
local HITINFO_UNK3 = 8
local HITINFO_MISS = 16
local HITINFO_ABSORB = 32
local HITINFO_RESIST = 64
local HITINFO_CRITICALHIT = 128
local HITINFO_UNK8 = 256
local HITINFO_UNK9 = 8192
local HITINFO_GLANCING = 16384
local HITINFO_CRUSHING = 32768
local HITINFO_NOACTION = 65536
local HITINFO_SWINGNOHITSOUND = 524288
local VICTIMSTATE_UNAFFECTED = 0
local VICTIMSTATE_NORMAL = 1
local VICTIMSTATE_DODGE = 2
local VICTIMSTATE_PARRY = 3
local VICTIMSTATE_INTERRUPT = 4
local VICTIMSTATE_BLOCKS = 5
local VICTIMSTATE_EVADES = 6
local VICTIMSTATE_IS_IMMUNE = 7
local VICTIMSTATE_DEFLECTS = 8
local victimStateNames = {
[0] = "Unaffected",
[1] = "Normal",
[2] = "Dodged",
[3] = "Parried",
[4] = "Interrupted",
[5] = "Blocked",
[6] = "Evaded",
[7] = "Immune",
[8] = "Deflected"
}
function GetHitType(hitInfo)
local isCrit = bit.band(hitInfo, HITINFO_CRITICALHIT) ~= 0
local isGlancing = bit.band(hitInfo, HITINFO_GLANCING) ~= 0
local isCrushing = bit.band(hitInfo, HITINFO_CRUSHING) ~= 0
local isMiss = bit.band(hitInfo, HITINFO_MISS) ~= 0
local isOffHand = bit.band(hitInfo, HITINFO_LEFTSWING) ~= 0
local hitType = "Normal"
if isMiss then
hitType = "Miss"
elseif isCrit then
hitType = "Critical"
elseif isGlancing then
hitType = "Glancing"
elseif isCrushing then
hitType = "Crushing"
end
if isOffHand then
hitType = hitType .. " (Off-hand)"
end
return hitType
end
function GetVictimStateName(victimState)
return victimStateNames[victimState] or "Unknown"
end
local function onAutoAttack(attackerGuid, targetGuid, totalDamage, hitInfo, victimState, subDamageCount, blockedAmount, totalAbsorb, totalResist)
local hitType = GetHitType(hitInfo)
local victimStateName = GetVictimStateName(victimState)
print(string.format(
"Auto Attack: %s -> %s | %d damage (%s) | State: %s | SubDmg: %d | Absorbed: %d | Resisted: %d | Blocked: %d",
attackerGuid, targetGuid, totalDamage, hitType, victimStateName,
subDamageCount, totalAbsorb, totalResist, blockedAmount
))
end
-- helpful event hooks for debugging
--Nampower:RegisterEvent("UNIT_CASTEVENT", function(casterGuid, targetGuid, event, spellID, castDuration)
-- if(casterGuid=="0x00000000001CD43C") then
-- print("CASTEVENT: " .. tostring(casterGuid) .. " -> " .. tostring(targetGuid) .. " event: " .. tostring(event) .. " spellID: " .. tostring(spellID) .. " castDuration: " .. tostring(castDuration))
-- end
--end)
--Nampower:RegisterEvent("AURA_CAST_ON_OTHER", function(spellId, caster, target, effect, effectname)
-- local unitName = target and UnitName(target) or ""
-- print("AURA_CAST_ON_OTHER: " .. tostring(spellId) .. " " .. tostring(GetSpellRecField(spellId, "name")) .. " by " .. tostring(UnitName(caster)) .. " on " .. unitName .. " effect: " .. tostring(effect) .. " effectname: " .. tostring(effectname))
--end)
--
--Nampower:RegisterEvent("AURA_CAST_ON_SELF", function(spellId, caster, target, effect, effectname)
-- local unitName = target and UnitName(target) or ""
-- print("AURA_CAST_ON_SELF: " .. tostring(spellId) .. " " .. tostring(GetSpellRecField(spellId, "name")) .. " by " .. tostring(UnitName(caster)) .. " on " .. unitName .. " effect: " .. tostring(effect) .. " effectname: " .. tostring(effectname))
--end)
--
--Nampower:RegisterEvent("AUTO_ATTACK_SELF", onAutoAttack)
--Nampower:RegisterEvent("AUTO_ATTACK_OTHER", onAutoAttack)
if not Nampower:HasMinimumVersion(2, 25, 0) then
return
end
local spellTypeNames = {
[0] = "Normal",
[1] = "Channeling",
[2] = "Autorepeating",
}
local function onSpellStart(itemId, spellId, casterGuid, targetGuid, castFlags, castTime, duration, spellType, corpseOwnerGuid)
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") or spellId
local spellTypeName = spellTypeNames[spellType] or "Unknown"
local caster = UnitName(casterGuid) or tostring(casterGuid)
local target = UnitName(targetGuid) or tostring(targetGuid)
local corpse = nil
if corpseOwnerGuid then
corpse = UnitName(corpseOwnerGuid) or tostring(corpseOwnerGuid)
end
print(string.format(
"SPELL_START: %s (id:%d) | Caster: %s -> Target: %s | ItemId: %d | Flags: %d | CastTime: %dms | Duration: %dms | Type: %s | CorpseOwner: %s",
tostring(spellName), spellId, caster, target, itemId, castFlags, castTime, duration, spellTypeName, tostring(corpse)
))
end
local function onSpellGo(itemId, spellId, casterGuid, targetGuid, castFlags, numTargetsHit, numTargetsMissed, corpseOwnerGuid)
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") or spellId
local caster = UnitName(casterGuid) or tostring(casterGuid)
local target = UnitName(targetGuid) or tostring(targetGuid)
local corpse = nil
if corpseOwnerGuid then
corpse = UnitName(corpseOwnerGuid) or tostring(corpseOwnerGuid)
end
print(string.format(
"SPELL_GO: %s (id:%d) | Caster: %s -> Target: %s | ItemId: %d | Flags: %d | Hit: %d | Missed: %d | CorpseOwner: %s",
tostring(spellName), spellId, caster, target, itemId, castFlags, numTargetsHit, numTargetsMissed, tostring(corpse)
))
end
local function onSpellFailedSelf(spellId, spellResult, failedByServer)
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") or spellId
local source = failedByServer == 1 and "Server" or "Client"
print(string.format(
"SPELL_FAILED_SELF: %s (id:%d) | Result: %d | Source: %s",
tostring(spellName), spellId, spellResult, source
))
end
local function onSpellFailedOther(casterGuid, spellId)
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") or spellId
print(string.format(
"SPELL_FAILED_OTHER: %s (id:%d) | Caster: %s",
tostring(spellName), spellId, casterGuid
))
end
local function onSpellDelayed(casterGuid, delayMs)
print(string.format(
"SPELL_DELAYED: Caster: %s | Delay: %dms",
casterGuid, delayMs
))
end
local function onSpellChannelStart(spellId, targetGuid, durationMs)
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") or spellId
print(string.format(
"SPELL_CHANNEL_START: %s (id:%d) | Target: %s | Duration: %dms",
tostring(spellName), spellId, targetGuid, durationMs
))
end
local function onSpellChannelUpdate(spellId, targetGuid, remainingMs)
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") or spellId
print(string.format(
"SPELL_CHANNEL_UPDATE: %s (id:%d) | Target: %s | Remaining: %dms",
tostring(spellName), spellId, targetGuid, remainingMs
))
end
--Nampower:RegisterEvent("SPELL_START_SELF", onSpellStart)
--Nampower:RegisterEvent("SPELL_START_OTHER", onSpellStart)
--Nampower:RegisterEvent("SPELL_GO_SELF", onSpellGo)
--Nampower:RegisterEvent("SPELL_GO_OTHER", onSpellGo)
--Nampower:RegisterEvent("SPELL_FAILED_SELF", onSpellFailedSelf)
--Nampower:RegisterEvent("SPELL_FAILED_OTHER", onSpellFailedOther)
--Nampower:RegisterEvent("SPELL_DELAYED_SELF", onSpellDelayed)
--Nampower:RegisterEvent("SPELL_DELAYED_OTHER", onSpellDelayed)
--Nampower:RegisterEvent("SPELL_CHANNEL_START", onSpellChannelStart)
--Nampower:RegisterEvent("SPELL_CHANNEL_UPDATE", onSpellChannelUpdate)
if not Nampower:HasMinimumVersion(2, 26, 0) then
return
end
-- Spell Heal Events (gated behind NP_EnableSpellHealEvents CVar, default 0)
-- SPELL_HEAL_BY_SELF - Fires when the active player is the caster (you healed someone)
-- SPELL_HEAL_BY_OTHER - Fires when someone other than the active player is the caster (someone else healed someone)
-- SPELL_HEAL_ON_SELF - Fires when the active player is the target (you received a heal)
-- Note: SPELL_HEAL_BY_SELF and SPELL_HEAL_ON_SELF can both fire for the same heal if you heal yourself
local function createSpellHealHandler(eventName)
return function(targetGuid, casterGuid, spellId, amount, critical, periodic)
local critText = critical == 1 and " (CRIT)" or ""
local periodicText = periodic == 1 and " (PERIODIC)" or ""
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") or spellId
print(string.format(
"%s: %s healed %s for %d with %s%s%s",
eventName, UnitName(casterGuid), UnitName(targetGuid), amount, tostring(spellName), critText, periodicText
))
end
end
-- Spell Energize Events (gated behind NP_EnableSpellEnergizeEvents CVar, default 0)
-- SPELL_ENERGIZE_BY_SELF - Fires when the active player is the caster (you restored power to someone)
-- SPELL_ENERGIZE_BY_OTHER - Fires when someone other than the active player is the caster (someone else restored power)
-- SPELL_ENERGIZE_ON_SELF - Fires when the active player is the target (you received power)
-- Note: SPELL_ENERGIZE_BY_SELF and SPELL_ENERGIZE_ON_SELF can both fire for the same energize if you restore power to yourself
-- Power Type Constants
local POWER_MANA = 0
local POWER_RAGE = 1
local POWER_FOCUS = 2
local POWER_ENERGY = 3
local POWER_HAPPINESS = 4
local POWER_HEALTH = -2 -- 0xFFFFFFFE as unsigned
local POWER_NAMES = {
[0] = "Mana",
[1] = "Rage",
[2] = "Focus",
[3] = "Energy",
[4] = "Happiness",
[-2] = "Health",
}
function GetPowerTypeName(powerType)
return POWER_NAMES[powerType] or "Unknown"
end
local function createSpellEnergizeHandler(eventName)
return function(targetGuid, casterGuid, spellId, powerType, amount, periodic)
local powerName = GetPowerTypeName(powerType)
local periodicText = periodic == 1 and " (PERIODIC)" or ""
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") or spellId
print(string.format(
"%s: %s restored %d %s to %s with %s%s",
eventName, UnitName(casterGuid), amount, powerName, UnitName(targetGuid), tostring(spellName), periodicText
))
end
end
--Nampower:RegisterEvent("SPELL_HEAL_BY_SELF", createSpellHealHandler("SPELL_HEAL_BY_SELF"))
--Nampower:RegisterEvent("SPELL_HEAL_BY_OTHER", createSpellHealHandler("SPELL_HEAL_BY_OTHER"))
--Nampower:RegisterEvent("SPELL_HEAL_ON_SELF", createSpellHealHandler("SPELL_HEAL_ON_SELF"))
--Nampower:RegisterEvent("SPELL_ENERGIZE_BY_SELF", createSpellEnergizeHandler("SPELL_ENERGIZE_BY_SELF"))
--Nampower:RegisterEvent("SPELL_ENERGIZE_BY_OTHER", createSpellEnergizeHandler("SPELL_ENERGIZE_BY_OTHER"))
--Nampower:RegisterEvent("SPELL_ENERGIZE_ON_SELF", createSpellEnergizeHandler("SPELL_ENERGIZE_ON_SELF"))
if not Nampower:HasMinimumVersion(2, 30, 0) then
return
end
-- Aura Duration Update Events
-- BUFF_UPDATE_DURATION_SELF - aura slot 0-31
-- DEBUFF_UPDATE_DURATION_SELF - aura slot 32-47
-- Parameters: auraSlot, durationMs, expirationTimeMs
local function createAuraDurationHandler(eventName)
return function(auraSlot, durationMs, expirationTimeMs)
print(string.format(
"%s: slot %d | Duration: %dms | Expires: %d",
eventName, auraSlot, durationMs, expirationTimeMs
))
end
end
--
--Nampower:RegisterEvent("DEBUFF_UPDATE_DURATION_SELF", createAuraDurationHandler("DEBUFF_UPDATE_DURATION_SELF"))
--Nampower:RegisterEvent("BUFF_UPDATE_DURATION_SELF", createAuraDurationHandler("BUFF_UPDATE_DURATION_SELF"))
-- Aura Added/Removed Events
-- BUFF_ADDED_SELF, BUFF_REMOVED_SELF, BUFF_ADDED_OTHER, BUFF_REMOVED_OTHER
-- DEBUFF_ADDED_SELF, DEBUFF_REMOVED_SELF, DEBUFF_ADDED_OTHER, DEBUFF_REMOVED_OTHER
-- Parameters: guid, luaSlot, spellId, stackCount, auraLevel, auraSlot
local function createAuraChangeHandler(eventName)
return function(guid, luaSlot, spellId, stackCount, auraLevel, auraSlot)
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") or spellId
print(string.format(
"%s: %s | luaSlot %d | %s (id:%d) | Stacks: %d | Level: %d | auraSlot: %d",
eventName, guid, luaSlot, tostring(spellName), spellId, stackCount, auraLevel, auraSlot
))
end
end
--Nampower:RegisterEvent("BUFF_ADDED_SELF", createAuraChangeHandler("BUFF_ADDED_SELF"))
--Nampower:RegisterEvent("BUFF_REMOVED_SELF", createAuraChangeHandler("BUFF_REMOVED_SELF"))
--Nampower:RegisterEvent("BUFF_ADDED_OTHER", createAuraChangeHandler("BUFF_ADDED_OTHER"))
--Nampower:RegisterEvent("BUFF_REMOVED_OTHER", createAuraChangeHandler("BUFF_REMOVED_OTHER"))
--Nampower:RegisterEvent("DEBUFF_ADDED_SELF", createAuraChangeHandler("DEBUFF_ADDED_SELF"))
--Nampower:RegisterEvent("DEBUFF_REMOVED_SELF", createAuraChangeHandler("DEBUFF_REMOVED_SELF"))
--Nampower:RegisterEvent("DEBUFF_ADDED_OTHER", createAuraChangeHandler("DEBUFF_ADDED_OTHER"))
--Nampower:RegisterEvent("DEBUFF_REMOVED_OTHER", createAuraChangeHandler("DEBUFF_REMOVED_OTHER"))
-- Spell Damage Events (gated behind NP_EnableSpellDamageEvents CVar, default 0)
-- SPELL_DAMAGE_EVENT_SELF - Fires when the active player deals spell damage
-- SPELL_DAMAGE_EVENT_OTHER - Fires when someone other than the active player deals spell damage
-- Spell Damage Hit Info Constants
local SPELL_DAMAGE_HIT_NORMAL = 0
local SPELL_DAMAGE_HIT_CRIT = 2
-- Spell School Constants
local SPELL_SCHOOL_NORMAL = 0
local SPELL_SCHOOL_HOLY = 1
local SPELL_SCHOOL_FIRE = 2
local SPELL_SCHOOL_NATURE = 3
local SPELL_SCHOOL_FROST = 4
local SPELL_SCHOOL_SHADOW = 5
local SPELL_SCHOOL_ARCANE = 6
local SPELL_SCHOOL_NAMES = {
[0] = "Physical",
[1] = "Holy",
[2] = "Fire",
[3] = "Nature",
[4] = "Frost",
[5] = "Shadow",
[6] = "Arcane",
}
function GetSpellSchoolName(spellSchool)
return SPELL_SCHOOL_NAMES[spellSchool] or "Unknown"
end
local function createSpellDamageHandler(eventName)
return function(targetGuid, casterGuid, spellId, amount, mitigationStr, hitInfo, spellSchool, effectAuraStr)
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") or spellId
local schoolName = GetSpellSchoolName(spellSchool)
local critText = hitInfo == SPELL_DAMAGE_HIT_CRIT and " (CRIT)" or ""
print(string.format(
"%s: %s -> %s | %s (id:%d) | %d %s damage%s | Mitigation: %s | HitInfo: %d | Effects: %s",
eventName, casterGuid, targetGuid, tostring(spellName), spellId, amount, schoolName, critText, mitigationStr, hitInfo, effectAuraStr
))
end
end
--Nampower:RegisterEvent("SPELL_DAMAGE_EVENT_SELF", createSpellDamageHandler("SPELL_DAMAGE_EVENT_SELF"))
--Nampower:RegisterEvent("SPELL_DAMAGE_EVENT_OTHER", createSpellDamageHandler("SPELL_DAMAGE_EVENT_OTHER"))
if not Nampower:HasMinimumVersion(2, 31, 0) then
return
end
-- Spell Miss Events (gated behind NP_EnableSpellMissEvents CVar, default 0)
-- SPELL_MISS_SELF - Fires when the active player's spell misses/resists/is immune/etc
-- SPELL_MISS_OTHER - Fires when someone else's spell misses/resists/is immune/etc
-- Triggered by SMSG_SPELLLOGMISS, SMSG_PROCRESIST, and SMSG_SPELLORDAMAGE_IMMUNE
-- SpellMissInfo Constants
local SPELL_MISS_NONE = 0
local SPELL_MISS_MISS = 1
local SPELL_MISS_RESIST = 2
local SPELL_MISS_DODGE = 3
local SPELL_MISS_PARRY = 4
local SPELL_MISS_BLOCK = 5
local SPELL_MISS_EVADE = 6
local SPELL_MISS_IMMUNE = 7
local SPELL_MISS_IMMUNE2 = 8
local SPELL_MISS_DEFLECT = 9
local SPELL_MISS_ABSORB = 10
local SPELL_MISS_REFLECT = 11
local SPELL_MISS_NAMES = {
[0] = "None",
[1] = "Miss",
[2] = "Resist",
[3] = "Dodge",
[4] = "Parry",
[5] = "Block",
[6] = "Evade",
[7] = "Immune",
[8] = "Immune",
[9] = "Deflect",
[10] = "Absorb",
[11] = "Reflect",
}
function GetSpellMissName(missInfo)
return SPELL_MISS_NAMES[missInfo] or "Unknown"
end
local function createSpellMissHandler(eventName)
return function(casterGuid, targetGuid, spellId, missInfo)
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") or spellId
local missName = GetSpellMissName(missInfo)
print(string.format(
"%s: %s -> %s | %s (id:%d) | %s",
eventName, UnitName(casterGuid), UnitName(targetGuid), tostring(spellName), spellId, missName
))
end
end
--Nampower:RegisterEvent("SPELL_MISS_SELF", createSpellMissHandler("SPELL_MISS_SELF"))
--Nampower:RegisterEvent("SPELL_MISS_OTHER", createSpellMissHandler("SPELL_MISS_OTHER"))
if not Nampower:HasMinimumVersion(2, 39, 0) then
return
end
-- UNIT_INVENTORY_CHANGED - fires when a unit's inventory changes (standard named token variant)
-- Parameters: unit (string) - unit token like "player", "target", "party1", etc.
-- UNIT_INVENTORY_CHANGED_GUID - fires when a unit's inventory changes (GUID variant)
-- Parameters:
-- guid (string) - unit GUID e.g. "0xF5300000000000A5"
-- isPlayer (int) - 1 if the unit is the active player, 0 otherwise
-- isTarget (int) - 1 if the unit is the current locked target, 0 otherwise
-- isMouseover (int) - 1 if the unit is the current mouseover, 0 otherwise
-- isPet (int) - 1 if the unit is the active player's pet, 0 otherwise
-- partyIndex (int) - party slot (1-4) if the unit is a party member, 0 otherwise
-- raidIndex (int) - raid slot (1-40) if the unit is a raid member, 0 otherwise
local function onUnitInventoryChanged(unit)
print(string.format("UNIT_MODEL_CHANGED: %s", unit))
end
local function onUnitInventoryChangedGuid(guid, isPlayer, isTarget, isMouseover, isPet, partyIndex, raidIndex)
print(string.format(
"UNIT_MODEL_CHANGED_GUID: %s | isPlayer:%d isTarget:%d isMouseover:%d isPet:%d party:%d raid:%d",
guid, isPlayer, isTarget, isMouseover, isPet, partyIndex, raidIndex
))
end
--Nampower:RegisterEvent("UNIT_MODEL_CHANGED", onUnitInventoryChanged)
--Nampower:RegisterEvent("UNIT_MODEL_CHANGED_GUID", onUnitInventoryChangedGuid)
-- UNIT_MANA - fires when a unit's mana changes (standard named token variant)
-- Parameters: unit (string) - unit token like "player", "target", "party1", etc.
-- UNIT_MANA_GUID - fires when a unit's mana changes (GUID variant)
-- Parameters:
-- guid (string) - unit GUID e.g. "0xF5300000000000A5"
-- isPlayer (int) - 1 if the unit is the active player, 0 otherwise
-- isTarget (int) - 1 if the unit is the current locked target, 0 otherwise
-- isMouseover (int) - 1 if the unit is the current mouseover, 0 otherwise
-- isPet (int) - 1 if the unit is the active player's pet, 0 otherwise
-- partyIndex (int) - party slot (1-4) if the unit is a party member, 0 otherwise
-- raidIndex (int) - raid slot (1-40) if the unit is a raid member, 0 otherwise
local function onUnitMana(unit)
print(string.format("UNIT_MANA: %s", unit))
end
local function onUnitManaGuid(guid, isPlayer, isTarget, isMouseover, isPet, partyIndex, raidIndex)
if UnitName(guid) == "Tankeboy" or UnitName(guid) == "Icansummon" then
print(string.format(
"UNIT_MANA_GUID: %s | isPlayer:%d isTarget:%d isMouseover:%d isPet:%d party:%d raid:%d",
UnitName(guid), isPlayer, isTarget, isMouseover, isPet, partyIndex, raidIndex
))
end
end
--Nampower:RegisterEvent("UNIT_MANA", onUnitMana)
--Nampower:RegisterEvent("UNIT_MANA_GUID", onUnitManaGuid)
-- UNIT_COMBAT - fires when a unit participates in combat (standard named token variant)
-- Parameters:
-- unit (string) - unit token like "player", "target", "party1", etc.
-- action (string) - e.g. "WOUND", "MISS", "DODGE", "PARRY", "BLOCK", "EVADE", "IMMUNE", "REFLECT", "ABSORB", "INTERRUPT"
-- critical (string) - "CRITICAL" if a crit, "" otherwise
-- damage (int) - raw damage amount (0 for non-damaging outcomes)
-- school (int) - damage school (0=Physical, 1=Holy, 2=Fire, 3=Nature, 4=Frost, 5=Shadow, 6=Arcane)
-- UNIT_COMBAT_GUID - fires once per combat feedback event, identified by GUID
-- Unlike other GUID events, carries full combat detail rather than unit membership flags.
-- Parameters:
-- guid (string) - unit GUID e.g. "0xF5300000000000A5"
-- action (string) - e.g. "WOUND", "MISS", "DODGE", "PARRY", "BLOCK", "EVADE", "IMMUNE", "REFLECT", "ABSORB", "INTERRUPT"
-- damage (int) - raw damage amount (0 for non-damaging outcomes like dodge/miss)
-- school (int) - damage school (0=Physical, 1=Holy, 2=Fire, 3=Nature, 4=Frost, 5=Shadow, 6=Arcane)
-- hitInfo (int) - hit info bitfield
-- isInteract (int) - 1 if the unit matches the current interact target, 0 otherwise
-- isPet (int) - 1 if the unit is the active player's pet, 0 otherwise
-- partyIndex (int) - party slot (1-4) if the unit is a party member, 0 otherwise
-- raidIndex (int) - raid slot (1-40) if the unit is a raid member, 0 otherwise
local function onUnitCombat(unit, action, critical, damage, school)
local schoolName = GetSpellSchoolName(school)
local critText = critical == "CRITICAL" and " (CRIT)" or ""
print(string.format(
"UNIT_COMBAT: %s | %s | %d %s damage%s",
unit, action, damage, schoolName, critText
))
end
local function onUnitCombatGuid(guid, action, damage, school, hitInfo, isInteract, isPet, partyIndex, raidIndex)
local name = UnitName(guid)
local schoolName = GetSpellSchoolName(school)
print(string.format(
"UNIT_COMBAT_GUID: %s | %s | %d %s | hitInfo:%d isInteract:%d isPet:%d party:%d raid:%d",
name, action, damage, schoolName, hitInfo, isInteract, isPet, partyIndex, raidIndex
))
end
--Nampower:RegisterEvent("UNIT_COMBAT", onUnitCombat)
--Nampower:RegisterEvent("UNIT_COMBAT_GUID", onUnitCombatGuid)
-- SPELL_CAST_EVENT - fires when you (or certain pets) initiate a spell cast
-- Triggered client-side before the cast is sent to the server.
-- Only fires for spells you initiated.
-- Parameters:
-- success (int) - 1 if cast succeeded, 0 if failed
-- spellId (int) - spell ID
-- castType (int) - see Cast Type Constants below
-- targetGuid (string) - guid string like "0xF5300000000000A5"
-- itemId (int) - item ID that triggered the spell, 0 if not item-triggered
-- Cast Type Constants
local CAST_TYPE_NORMAL = 0
local CAST_TYPE_NON_GCD = 1
local CAST_TYPE_ON_SWING = 2
local CAST_TYPE_CHANNEL = 3
local CAST_TYPE_TARGETING = 4
local CAST_TYPE_TARGETING_NON_GCD = 5
local CAST_TYPE_NAMES = {
[0] = "Normal",
[1] = "NonGCD",
[2] = "OnSwing",
[3] = "Channel",
[4] = "Targeting",
[5] = "TargetingNonGCD",
}
function GetCastTypeName(castType)
return CAST_TYPE_NAMES[castType] or "Unknown"
end
local function onSpellCastEvent(success, spellId, castType, targetGuid, itemId)
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") or spellId
local castTypeName = GetCastTypeName(castType)
local successText = success == 1 and "OK" or "FAILED"
local itemText = itemId ~= 0 and (" | Item: " .. tostring(itemId)) or ""
print(string.format(
"SPELL_CAST_EVENT: [%s] %s (id:%d) | Type: %s | Target: %s%s",
successText, tostring(spellName), spellId, castTypeName, tostring(targetGuid), itemText
))
end
--Nampower:RegisterEvent("SPELL_CAST_EVENT", onSpellCastEvent)
-- UNIT_AURA_GUID - fires when a unit's aura state changes (GUID variant)
-- All GUID events share the same parameter format:
-- guid (string) - unit guid like "0xF5300000000000A5"
-- isPlayer (int) - 1 if the unit is the active player, 0 otherwise
-- isTarget (int) - 1 if the unit is the current locked target, 0 otherwise
-- isMouseover (int) - 1 if the unit is the current mouseover, 0 otherwise
-- isPet (int) - 1 if the unit is the active player's pet, 0 otherwise
-- partyIndex (int) - party slot (1-4) if the unit is a party member, 0 otherwise
-- raidIndex (int) - raid slot (1-40) if the unit is a raid member, 0 otherwise
local function onUnitGuid(guid, isPlayer, isTarget, isMouseover, isPet, partyIndex, raidIndex)
isPlayer = isPlayer or 0
isTarget = isTarget or 0
isMouseover = isMouseover or 0
isPet = isPet or 0
partyIndex = partyIndex or 0
raidIndex = raidIndex or 0
print(string.format(
"%s: %s | isPlayer:%d isTarget:%d isMouseover:%d isPet:%d party:%d raid:%d",
"UNIT_AURA_GUID", tostring(UnitName(guid) or guid), isPlayer, isTarget, isMouseover, isPet, partyIndex, raidIndex
))
end
--Nampower:RegisterEvent("UNIT_AURA_GUID", onUnitGuid)
if not Nampower:HasMinimumVersion(3, 4, 0) then
return
end
-- ENVIRONMENTAL_DMG_SELF / ENVIRONMENTAL_DMG_OTHER
-- Parameters:
-- unitGuid (string) - guid of the unit that took damage
-- dmgType (int) - environmental damage type
-- damage (int) - amount of damage taken
-- absorb (int) - amount of damage absorbed
-- resist (int) - amount of damage resisted
local ENV_DMG_NAMES = {
[0] = "Exhausted",
[1] = "Drowning",
[2] = "Fall",
[3] = "Lava",
[4] = "Slime",
[5] = "Fire",
[6] = "FallToVoid",
}
local function onEnvironmentalDmg(unitGuid, dmgType, damage, absorb, resist)
dmgType = dmgType or -1
damage = damage or 0
absorb = absorb or 0
resist = resist or 0
print(string.format(
"ENVIRONMENTAL_DMG: %s took %d %s damage (absorbed:%d resisted:%d)",
tostring(unitGuid), damage, ENV_DMG_NAMES[dmgType] or "Unknown", absorb, resist
))
end
--Nampower:RegisterEvent("ENVIRONMENTAL_DMG_SELF", onEnvironmentalDmg)
--Nampower:RegisterEvent("ENVIRONMENTAL_DMG_OTHER", onEnvironmentalDmg)
-- DAMAGE_SHIELD_SELF / DAMAGE_SHIELD_OTHER
-- Parameters:
-- unitGuid (string) - guid of shield owner
-- targetGuid (string) - guid of attacker taking reflected damage
-- damage (int) - amount of shield damage dealt
-- spellSchool (int) - school of shield damage
local SCHOOL_NAMES = {
[0] = "Physical",
[1] = "Holy",
[2] = "Fire",
[3] = "Nature",
[4] = "Frost",
[5] = "Shadow",
[6] = "Arcane",
}
local function onDamageShield(unitGuid, targetGuid, damage, spellSchool)
damage = damage or 0
spellSchool = spellSchool or -1
print(string.format(
"DAMAGE_SHIELD: %s dealt %d %s damage to %s",
tostring(unitGuid), damage, SCHOOL_NAMES[spellSchool] or "Unknown", tostring(targetGuid)
))
end
--Nampower:RegisterEvent("DAMAGE_SHIELD_SELF", onDamageShield)
--Nampower:RegisterEvent("DAMAGE_SHIELD_OTHER", onDamageShield)
-- SPELL_DISPEL_BY_SELF / SPELL_DISPEL_BY_OTHER
-- Parameters:
-- casterGuid (string) - guid of dispel caster
-- targetGuid (string) - guid of dispelled unit
-- spellId (int) - dispelled spell id
local function onSpellDispel(casterGuid, targetGuid, spellId)
spellId = spellId or 0
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") or spellId
print(string.format(
"SPELL_DISPEL_BY: %s dispelled %s (id:%d) from %s",
tostring(casterGuid), tostring(spellName), spellId, tostring(targetGuid)
))
end
--Nampower:RegisterEvent("SPELL_DISPEL_BY_SELF", onSpellDispel)
--Nampower:RegisterEvent("SPELL_DISPEL_BY_OTHER", onSpellDispel)
-- PrintSpellRange(rangeIndex)
-- Prints the SpellRange DBC record for the given index using GetSpellRangeData.
-- GetSpellRangeData returns: minRange, maxRange, flags, name
function PrintSpellRange(rangeIndex)
if not GetSpellRangeData then
print("GetSpellRangeData is not available")
return
end
local minRange, maxRange, flags, name = GetSpellRangeData(rangeIndex)
if minRange == nil then
print(string.format("PrintSpellRange(%d): no record found", rangeIndex))
return
end
print(string.format(
"SpellRange[%d]: name=%q min=%.2f max=%.2f flags=0x%08X",
rangeIndex, tostring(name), minRange, maxRange, flags
))
end
Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

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
@@ -0,0 +1,245 @@
--[[
Name: AceDebug-2.0
Revision: $Rev: 17638 $
Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team)
Inspired By: Ace 1.x by Turan (turan@gryphon.com)
Website: http://www.wowace.com/
Documentation: http://www.wowace.com/index.php/AceDebug-2.0
SVN: http://svn.wowace.com/root/trunk/Ace2/AceDebug-2.0
Description: Mixin to allow for simple debugging capabilities.
Dependencies: AceLibrary, AceOO-2.0
]]
local MAJOR_VERSION = "AceDebug-2.0"
local MINOR_VERSION = "$Revision: 17639 $"
if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end
if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check
if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end
local DEBUGGING, TOGGLE_DEBUGGING
if GetLocale() == "frFR" then
DEBUGGING = "D\195\169boguage"
TOGGLE_DEBUGGING = "Activer/d\195\169sactiver le d\195\169boguage"
elseif GetLocale() == "esES" then
DEBUGGING = "Depurar"
TOGGLE_DEBUGGING = "Activar/desactivar depurar"
elseif GetLocale() == "deDE" then
DEBUGGING = "Debuggen"
TOGGLE_DEBUGGING = "Aktiviert/Deaktiviert Debugging"
elseif GetLocale() == "koKR" then
DEBUGGING = "디버깅"
TOGGLE_DEBUGGING = "디버깅 기능 사용함/사용안함"
elseif GetLocale() == "zhTW" then
DEBUGGING = "除錯"
TOGGLE_DEBUGGING = "啟用/停用除錯功能"
elseif GetLocale() == "zhCN" then
DEBUGGING = "\232\176\131\232\175\149"
TOGGLE_DEBUGGING = "\229\144\175\231\148\168/\231\166\129\231\148\168 \232\176\131\232\175\149"
elseif GetLocale() == "ruRU" then
DEBUGGING = "Отладка"
TOGGLE_DEBUGGING = "Вкл/Выкл отладку для этого аддона."
else -- enUS
DEBUGGING = "Debugging"
TOGGLE_DEBUGGING = "Enable/disable debugging"
end
local table_setn
do
local version = GetBuildInfo()
if string.find(version, "^2%.") then
-- 2.0.0
table_setn = function() end
else
table_setn = table.setn
end
end
local math_mod = math.mod or math.fmod
local AceOO = AceLibrary:GetInstance("AceOO-2.0")
local AceDebug = AceOO.Mixin {"Debug", "CustomDebug", "IsDebugging", "SetDebugging", "SetDebugLevel", "LevelDebug", "CustomLevelDebug", "GetDebugLevel"}
local function print(text, r, g, b, frame, delay)
(frame or DEFAULT_CHAT_FRAME):AddMessage(text, r, g, b, 1, delay or 5)
end
local tmp
function AceDebug:CustomDebug(r, g, b, frame, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
if not self.debugging then return end
local output = string.format("|cff7fff7f(DEBUG) %s:[%s%3d]|r", tostring(self), date("%H:%M:%S"), math_mod(GetTime(), 1) * 1000)
if string.find(tostring(a1), "%%") then
output = output .. " " .. string.format(tostring(a1), tostring(a2), tostring(a3), tostring(a4), tostring(a5), tostring(a6), tostring(a7), tostring(a8), tostring(a9), tostring(a10), tostring(a11), tostring(a12), tostring(a13), tostring(a14), tostring(a15), tostring(a16), tostring(a17), tostring(a18), tostring(a19), tostring(a20))
else
if not tmp then
tmp = {}
end
-- This block dynamically rebuilds the tmp array stopping on the first nil.
table.insert(tmp, output)
table.insert(tmp, tostring(a1))
table.insert(tmp, a2)
table.insert(tmp, a3)
table.insert(tmp, a4)
table.insert(tmp, a5)
table.insert(tmp, a6)
table.insert(tmp, a7)
table.insert(tmp, a8)
table.insert(tmp, a9)
table.insert(tmp, a10)
table.insert(tmp, a11)
table.insert(tmp, a12)
table.insert(tmp, a13)
table.insert(tmp, a14)
table.insert(tmp, a15)
table.insert(tmp, a16)
table.insert(tmp, a17)
table.insert(tmp, a18)
table.insert(tmp, a19)
table.insert(tmp, a20)
while tmp[table.getn(tmp)] == nil do
table.remove(tmp)
end
for k = 1, table.getn(tmp) do
tmp[k] = tostring(tmp[k])
end
output = table.concat(tmp, " ")
for k,v in pairs(tmp) do
tmp[k] = nil
end
table_setn(tmp, 0)
end
print(output, r, g, b, frame or self.debugFrame, delay)
end
function AceDebug:Debug(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
AceDebug.CustomDebug(self, nil, nil, nil, nil, nil, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
end
function AceDebug:IsDebugging()
return self.debugging
end
function AceDebug:SetDebugging(debugging)
self.debugging = debugging
end
-- Takes a number 1-3
-- Level 1: Critical messages that every user should receive
-- Level 2: Should be used for local debugging (function calls, etc)
-- Level 3: Very verbose debugging, will dump everything and anything
-- If set to nil, you will receive no debug information
function AceDebug:SetDebugLevel(level)
AceDebug:argCheck(level, 1, "number", "nil")
if not level then
self.debuglevel = nil
return
end
if level < 1 or level > 3 then
AceDebug:error("Bad argument #1 to `SetDebugLevel`, must be a number 1-3")
end
self.debuglevel = level
end
function AceDebug:GetDebugLevel()
return self.debuglevel
end
function AceDebug:CustomLevelDebug(level, r, g, b, frame, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
if not self.debugging or not self.debuglevel then return end
AceDebug:argCheck(level, 1, "number")
if level < 1 or level > 3 then
AceDebug:error("Bad argument #1 to `LevelDebug`, must be a number 1-3")
end
if level > self.debuglevel then return end
local output = string.format("|cff7fff7f(DEBUG) %s:[%s.%3d]|r", tostring(self), date("%H:%M:%S"), math_mod(GetTime(), 1) * 1000)
if string.find(tostring(a1), "%%") then
output = output .. " " .. string.format(tostring(a1), tostring(a2), tostring(a3), tostring(a4), tostring(a5), tostring(a6), tostring(a7), tostring(a8), tostring(a9), tostring(a10), tostring(a11), tostring(a12), tostring(a13), tostring(a14), tostring(a15), tostring(a16), tostring(a17), tostring(a18), tostring(a19), tostring(a20))
else
if not tmp then
tmp = {}
end
-- This block dynamically rebuilds the tmp array stopping on the first nil.
table.insert(tmp, output)
table.insert(tmp, tostring(a1))
table.insert(tmp, a2)
table.insert(tmp, a3)
table.insert(tmp, a4)
table.insert(tmp, a5)
table.insert(tmp, a6)
table.insert(tmp, a7)
table.insert(tmp, a8)
table.insert(tmp, a9)
table.insert(tmp, a10)
table.insert(tmp, a11)
table.insert(tmp, a12)
table.insert(tmp, a13)
table.insert(tmp, a14)
table.insert(tmp, a15)
table.insert(tmp, a16)
table.insert(tmp, a17)
table.insert(tmp, a18)
table.insert(tmp, a19)
table.insert(tmp, a20)
while tmp[table.getn(tmp)] == nil do
table.remove(tmp)
end
for k = 1, table.getn(tmp) do
tmp[k] = tostring(tmp[k])
end
output = table.concat(tmp, " ")
for k,v in pairs(tmp) do
tmp[k] = nil
end
table_setn(tmp, 0)
end
print(output, r, g, b, frame or self.debugFrame, delay)
end
function AceDebug:LevelDebug(level, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
if not self.debugging or not self.debuglevel then return end
AceDebug:argCheck(level, 1, "number")
if level < 1 or level > 3 then
AceDebug:error("Bad argument #1 to `LevelDebug`, must be a number 1-3")
end
if level > self.debuglevel then return end
AceDebug.CustomLevelDebug(self, level, nil, nil, nil, nil, nil, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
end
local options
function AceDebug:GetAceOptionsDataTable(target)
if not options then
options = {
debug = {
name = DEBUGGING,
desc = TOGGLE_DEBUGGING,
type = "toggle",
get = "IsDebugging",
set = "SetDebugging",
order = -2,
}
}
end
return options
end
AceLibrary:Register(AceDebug, MAJOR_VERSION, MINOR_VERSION, AceDebug.activate)
AceDebug = AceLibrary(MAJOR_VERSION)
@@ -0,0 +1,973 @@
--[[
Name: AceEvent-2.0
Revision: $Rev: 17803 $
Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team)
Inspired By: Ace 1.x by Turan (turan@gryphon.com)
Website: http://www.wowace.com/
Documentation: http://www.wowace.com/index.php/AceEvent-2.0
SVN: http://svn.wowace.com/root/trunk/Ace2/AceEvent-2.0
Description: Mixin to allow for event handling, scheduling, and inter-addon
communication.
Dependencies: AceLibrary, AceOO-2.0
]]
local MAJOR_VERSION = "AceEvent-2.0"
local MINOR_VERSION = "$Revision: 17803 $"
if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end
if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check
if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end
local AceOO = AceLibrary:GetInstance("AceOO-2.0")
local Mixin = AceOO.Mixin
local AceEvent = Mixin {
"RegisterEvent",
"RegisterAllEvents",
"UnregisterEvent",
"UnregisterAllEvents",
"TriggerEvent",
"ScheduleEvent",
"ScheduleRepeatingEvent",
"CancelScheduledEvent",
"CancelAllScheduledEvents",
"IsEventRegistered",
"IsEventScheduled",
"RegisterBucketEvent",
"UnregisterBucketEvent",
"UnregisterAllBucketEvents",
"IsBucketEventRegistered",
}
local table_setn
do
local version = GetBuildInfo()
if string.find(version, "^2%.") then
-- 2.0.0
table_setn = function() end
else
table_setn = table.setn
end
end
local weakKey = {__mode="k"}
local new, del
do
local list = setmetatable({}, weakKey)
function new()
local t = next(list)
if t then
list[t] = nil
return t
else
return {}
end
end
function del(t)
setmetatable(t, nil)
for k in pairs(t) do
t[k] = nil
end
list[t] = true
end
end
local FAKE_NIL
local RATE
local eventsWhichHappenOnce = {
PLAYER_LOGIN = true,
AceEvent_FullyInitialized = true,
VARIABLES_LOADED = true,
PLAYER_LOGOUT = true,
}
local registeringFromAceEvent
function AceEvent:RegisterEvent(event, method, once)
AceEvent:argCheck(event, 2, "string")
if self == AceEvent and not registeringFromAceEvent then
AceEvent:argCheck(method, 3, "function")
self = method
else
AceEvent:argCheck(method, 3, "string", "function", "nil", "boolean", "number")
if type(method) == "boolean" or type(method) == "number" then
AceEvent:argCheck(once, 4, "nil")
once, method = method, event
end
end
AceEvent:argCheck(once, 4, "number", "boolean", "nil")
if eventsWhichHappenOnce[event] then
once = true
end
local throttleRate
if type(once) == "number" then
throttleRate, once = once
end
if not method then
method = event
end
if type(method) == "string" and type(self[method]) ~= "function" then
AceEvent:error("Cannot register event %q to method %q, it does not exist", event, method)
else
assert(type(method) == "function" or type(method) == "string")
end
local AceEvent_registry = AceEvent.registry
if not AceEvent_registry[event] then
AceEvent_registry[event] = new()
AceEvent.frame:RegisterEvent(event)
end
local remember = true
if AceEvent_registry[event][self] then
remember = false
end
AceEvent_registry[event][self] = method
local AceEvent_onceRegistry = AceEvent.onceRegistry
if once then
if not AceEvent_onceRegistry then
AceEvent.onceRegistry = new()
AceEvent_onceRegistry = AceEvent.onceRegistry
end
if not AceEvent_onceRegistry[event] then
AceEvent_onceRegistry[event] = new()
end
AceEvent_onceRegistry[event][self] = true
else
if AceEvent_onceRegistry and AceEvent_onceRegistry[event] then
AceEvent_onceRegistry[event][self] = nil
if not next(AceEvent_onceRegistry[event]) then
AceEvent_onceRegistry[event] = del(AceEvent_onceRegistry[event])
end
end
end
local AceEvent_throttleRegistry = AceEvent.throttleRegistry
if throttleRate then
if not AceEvent_throttleRegistry then
AceEvent.throttleRegistry = new()
AceEvent_throttleRegistry = AceEvent.throttleRegistry
end
if not AceEvent_throttleRegistry[event] then
AceEvent_throttleRegistry[event] = new()
end
if AceEvent_throttleRegistry[event][self] then
AceEvent_throttleRegistry[event][self] = del(AceEvent_throttleRegistry[event][self])
end
AceEvent_throttleRegistry[event][self] = setmetatable(new(), weakKey)
local t = AceEvent_throttleRegistry[event][self]
t[RATE] = throttleRate
else
if AceEvent_throttleRegistry and AceEvent_throttleRegistry[event] then
if AceEvent_throttleRegistry[event][self] then
AceEvent_throttleRegistry[event][self] = del(AceEvent_throttleRegistry[event][self])
end
if not next(AceEvent_throttleRegistry[event]) then
AceEvent_throttleRegistry[event] = del(AceEvent_throttleRegistry[event])
end
end
end
if remember then
AceEvent:TriggerEvent("AceEvent_EventRegistered", self, event)
end
end
local ALL_EVENTS
function AceEvent:RegisterAllEvents(method)
if self == AceEvent then
AceEvent:argCheck(method, 1, "function")
self = method
else
AceEvent:argCheck(method, 1, "string", "function")
if type(method) == "string" and type(self[method]) ~= "function" then
AceEvent:error("Cannot register all events to method %q, it does not exist", method)
end
end
local AceEvent_registry = AceEvent.registry
if not AceEvent_registry[ALL_EVENTS] then
AceEvent_registry[ALL_EVENTS] = new()
AceEvent.frame:RegisterAllEvents()
end
AceEvent_registry[ALL_EVENTS][self] = method
end
local _G = getfenv(0)
local memstack, timestack = {}, {}
local memdiff, timediff
function AceEvent:TriggerEvent(event, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
AceEvent:argCheck(event, 2, "string")
local AceEvent_registry = AceEvent.registry
if (not AceEvent_registry[event] or not next(AceEvent_registry[event])) and (not AceEvent_registry[ALL_EVENTS] or not next(AceEvent_registry[ALL_EVENTS])) then
return
end
local _G_event = _G.event
_G.event = event
local lastEvent = AceEvent.currentEvent
AceEvent.currentEvent = event
local AceEvent_onceRegistry = AceEvent.onceRegistry
local AceEvent_debugTable = AceEvent.debugTable
if AceEvent_onceRegistry and AceEvent_onceRegistry[event] then
local tmp = new()
for obj, method in pairs(AceEvent_onceRegistry[event]) do
tmp[obj] = AceEvent_registry[event] and AceEvent_registry[event][obj] or nil
end
local obj = next(tmp)
while obj do
local mem, time
if AceEvent_debugTable then
if not AceEvent_debugTable[event] then
AceEvent_debugTable[event] = new()
end
if not AceEvent_debugTable[event][obj] then
AceEvent_debugTable[event][obj] = new()
AceEvent_debugTable[event][obj].mem = 0
AceEvent_debugTable[event][obj].time = 0
AceEvent_debugTable[event][obj].count = 0
end
if memdiff then
table.insert(memstack, memdiff)
table.insert(timestack, timediff)
end
memdiff, timediff = 0, 0
mem, time = gcinfo(), GetTime()
end
local method = tmp[obj]
AceEvent.UnregisterEvent(obj, event)
if type(method) == "string" then
local obj_method = obj[method]
if obj_method then
obj_method(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
end
elseif method then -- function
method(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
end
if AceEvent_debugTable then
local dmem, dtime = memdiff, timediff
mem, time = gcinfo() - mem - memdiff, GetTime() - time - timediff
AceEvent_debugTable[event][obj].mem = AceEvent_debugTable[event][obj].mem + mem
AceEvent_debugTable[event][obj].time = AceEvent_debugTable[event][obj].time + time
AceEvent_debugTable[event][obj].count = AceEvent_debugTable[event][obj].count + 1
memdiff, timediff = table.remove(memstack), table.remove(timestack)
if memdiff then
memdiff = memdiff + mem + dmem
timediff = timediff + time + dtime
end
end
tmp[obj] = nil
obj = next(tmp)
end
del(tmp)
end
local AceEvent_throttleRegistry = AceEvent.throttleRegistry
local throttleTable = AceEvent_throttleRegistry and AceEvent_throttleRegistry[event]
if AceEvent_registry[event] then
local tmp = new()
for obj, method in pairs(AceEvent_registry[event]) do
tmp[obj] = method
end
local obj = next(tmp)
while obj do
local method = tmp[obj]
local continue = false
if throttleTable and throttleTable[obj] then
local a1 = a1
if a1 == nil then
a1 = FAKE_NIL
end
if not throttleTable[obj][a1] or GetTime() - throttleTable[obj][a1] >= throttleTable[obj][RATE] then
throttleTable[obj][a1] = GetTime()
else
continue = true
end
end
if not continue then
local mem, time
if AceEvent_debugTable then
if not AceEvent_debugTable[event] then
AceEvent_debugTable[event] = new()
end
if not AceEvent_debugTable[event][obj] then
AceEvent_debugTable[event][obj] = new()
AceEvent_debugTable[event][obj].mem = 0
AceEvent_debugTable[event][obj].time = 0
AceEvent_debugTable[event][obj].count = 0
end
if memdiff then
table.insert(memstack, memdiff)
table.insert(timestack, timediff)
end
memdiff, timediff = 0, 0
mem, time = gcinfo(), GetTime()
end
if type(method) == "string" then
local obj_method = obj[method]
if obj_method then
obj_method(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
end
elseif method then -- function
method(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
end
if AceEvent_debugTable then
local dmem, dtime = memdiff, timediff
mem, time = gcinfo() - mem - memdiff, GetTime() - time - timediff
AceEvent_debugTable[event][obj].mem = AceEvent_debugTable[event][obj].mem + mem
AceEvent_debugTable[event][obj].time = AceEvent_debugTable[event][obj].time + time
AceEvent_debugTable[event][obj].count = AceEvent_debugTable[event][obj].count + 1
memdiff, timediff = table.remove(memstack), table.remove(timestack)
if memdiff then
memdiff = memdiff + mem + dmem
timediff = timediff + time + dtime
end
end
end
tmp[obj] = nil
obj = next(tmp)
end
del(tmp)
end
if AceEvent_registry[ALL_EVENTS] then
local tmp = new()
for obj, method in pairs(AceEvent_registry[ALL_EVENTS]) do
tmp[obj] = method
end
local obj = next(tmp)
while obj do
local method = tmp[obj]
local mem, time
if AceEvent_debugTable then
if not AceEvent_debugTable[event] then
AceEvent_debugTable[event] = new()
end
if not AceEvent_debugTable[event][obj] then
AceEvent_debugTable[event][obj] = new()
AceEvent_debugTable[event][obj].mem = 0
AceEvent_debugTable[event][obj].time = 0
AceEvent_debugTable[event][obj].count = 0
end
if memdiff then
table.insert(memstack, memdiff)
table.insert(timestack, timediff)
end
memdiff, timediff = 0, 0
mem, time = gcinfo(), GetTime()
end
if type(method) == "string" then
local obj_method = obj[method]
if obj_method then
obj_method(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
end
elseif method then -- function
method(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
end
if AceEvent_debugTable then
local dmem, dtime = memdiff, timediff
mem, time = gcinfo() - mem - memdiff, GetTime() - time - timediff
AceEvent_debugTable[event][obj].mem = AceEvent_debugTable[event][obj].mem + mem
AceEvent_debugTable[event][obj].time = AceEvent_debugTable[event][obj].time + time
AceEvent_debugTable[event][obj].count = AceEvent_debugTable[event][obj].count + 1
memdiff, timediff = table.remove(memstack), table.remove(timestack)
if memdiff then
memdiff = memdiff + mem + dmem
timediff = timediff + time + dtime
end
end
tmp[obj] = nil
obj = next(tmp)
end
del(tmp)
end
_G.event = _G_event
AceEvent.currentEvent = lastEvent
end
-- local accessors
local getn = table.getn
local tinsert = table.insert
local tremove = table.remove
local floor = math.floor
local GetTime = GetTime
local next = next
local pairs = pairs
local unpack = unpack
local delayRegistry
local tmp = {}
local function OnUpdate()
local t = GetTime()
for k,v in pairs(delayRegistry) do
tmp[k] = true
end
for k in pairs(tmp) do
local v = delayRegistry[k]
if v then
local v_time = v.time
if not v_time then
delayRegistry[k] = del(v)
elseif v_time <= t then
local v_repeatDelay = v.repeatDelay
if v_repeatDelay then
-- use the event time, not the current time, else timing inaccuracies add up over time
v.time = v_time + v_repeatDelay
end
local event = v.event
local mem, time
if AceEvent_debugTable then
mem, time = gcinfo(), GetTime()
end
if type(event) == "function" then
event(unpack(v))
else
AceEvent:TriggerEvent(event, unpack(v))
end
if AceEvent_debugTable then
mem, time = gcinfo() - mem, GetTime() - time
v.mem = v.mem + mem
v.timeSpent = v.timeSpent + time
v.count = v.count + 1
end
if not v_repeatDelay then
local x = delayRegistry[k]
if x and x.time == v_time then -- check if it was manually reset
delayRegistry[k] = del(v)
end
end
end
end
end
for k in pairs(tmp) do
tmp[k] = nil
end
if not next(delayRegistry) then
AceEvent.frame:Hide()
end
end
local function ScheduleEvent(self, repeating, event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
local id
if type(event) == "string" or type(event) == "table" then
if type(event) == "table" then
if not delayRegistry or not delayRegistry[event] then
AceEvent:error("Bad argument #2 to `ScheduleEvent'. Improper id table fed in.")
end
end
if type(delay) ~= "number" then
id, event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20 = event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20
AceEvent:argCheck(event, 3, "string", "function", --[[ so message is right ]] "number")
AceEvent:argCheck(delay, 4, "number")
self:CancelScheduledEvent(id)
end
else
AceEvent:argCheck(event, 2, "string", "function")
AceEvent:argCheck(delay, 3, "number")
end
if not delayRegistry then
AceEvent.delayRegistry = new()
delayRegistry = AceEvent.delayRegistry
AceEvent.frame:SetScript("OnUpdate", OnUpdate)
end
local t
if type(id) == "table" then
for k in pairs(id) do
id[k] = nil
end
t = id
else
t = new()
end
t[1] = a1
t[2] = a2
t[3] = a3
t[4] = a4
t[5] = a5
t[6] = a6
t[7] = a7
t[8] = a8
t[9] = a9
t[10] = a10
t[11] = a11
t[12] = a12
t[13] = a13
t[14] = a14
t[15] = a15
t[16] = a16
t[17] = a17
t[18] = a18
t[19] = a19
t[20] = a20
table_setn(t, 20)
t.event = event
t.time = GetTime() + delay
t.self = self
t.id = id or t
t.repeatDelay = repeating and delay
if AceEvent_debugTable then
t.mem = 0
t.count = 0
t.timeSpent = 0
end
delayRegistry[t.id] = t
AceEvent.frame:Show()
return t.id
end
function AceEvent:ScheduleEvent(event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
if type(event) == "string" or type(event) == "table" then
if type(event) == "table" then
if not delayRegistry or not delayRegistry[event] then
AceEvent:error("Bad argument #2 to `ScheduleEvent'. Improper id table fed in.")
end
end
if type(delay) ~= "number" then
AceEvent:argCheck(delay, 3, "string", "function", --[[ so message is right ]] "number")
AceEvent:argCheck(a1, 4, "number")
end
else
AceEvent:argCheck(event, 2, "string", "function")
AceEvent:argCheck(delay, 3, "number")
end
return ScheduleEvent(self, false, event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
end
function AceEvent:ScheduleRepeatingEvent(event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
if type(event) == "string" or type(event) == "table" then
if type(event) == "table" then
if not delayRegistry or not delayRegistry[event] then
AceEvent:error("Bad argument #2 to `ScheduleEvent'. Improper id table fed in.")
end
end
if type(delay) ~= "number" then
AceEvent:argCheck(delay, 3, "string", "function", --[[ so message is right ]] "number")
AceEvent:argCheck(a1, 4, "number")
end
else
AceEvent:argCheck(event, 2, "string", "function")
AceEvent:argCheck(delay, 3, "number")
end
return ScheduleEvent(self, true, event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
end
function AceEvent:CancelScheduledEvent(t)
AceEvent:argCheck(t, 2, "string", "table")
if delayRegistry then
local v = delayRegistry[t]
if v then
delayRegistry[t] = del(v)
if not next(delayRegistry) then
AceEvent.frame:Hide()
end
return true
end
end
return false
end
function AceEvent:IsEventScheduled(t)
AceEvent:argCheck(t, 2, "string", "table")
if delayRegistry then
local v = delayRegistry[t]
if v then
return true, v.time - GetTime()
end
end
return false, nil
end
function AceEvent:UnregisterEvent(event)
AceEvent:argCheck(event, 2, "string")
local AceEvent_registry = AceEvent.registry
if AceEvent_registry[event] and AceEvent_registry[event][self] then
AceEvent_registry[event][self] = nil
local AceEvent_onceRegistry = AceEvent.onceRegistry
if AceEvent_onceRegistry and AceEvent_onceRegistry[event] and AceEvent_onceRegistry[event][self] then
AceEvent_onceRegistry[event][self] = nil
if not next(AceEvent_onceRegistry[event]) then
AceEvent_onceRegistry[event] = del(AceEvent_onceRegistry[event])
end
end
local AceEvent_throttleRegistry = AceEvent.throttleRegistry
if AceEvent_throttleRegistry and AceEvent_throttleRegistry[event] and AceEvent_throttleRegistry[event][self] then
AceEvent_throttleRegistry[event][self] = del(AceEvent_throttleRegistry[event][self])
if not next(AceEvent_throttleRegistry[event]) then
AceEvent_throttleRegistry[event] = del(AceEvent_throttleRegistry[event])
end
end
if not next(AceEvent_registry[event]) then
AceEvent_registry[event] = del(AceEvent_registry[event])
if not AceEvent_registry[ALL_EVENTS] or not next(AceEvent_registry[ALL_EVENTS]) then
AceEvent.frame:UnregisterEvent(event)
end
end
else
if self == AceEvent then
error(string.format("Cannot unregister event %q. Improperly unregistering from AceEvent-2.0.", event), 2)
else
AceEvent:error("Cannot unregister event %q. %q is not registered with it.", event, self)
end
end
AceEvent:TriggerEvent("AceEvent_EventUnregistered", self, event)
end
function AceEvent:UnregisterAllEvents()
local AceEvent_registry = AceEvent.registry
if AceEvent_registry[ALL_EVENTS] and AceEvent_registry[ALL_EVENTS][self] then
AceEvent_registry[ALL_EVENTS][self] = nil
if not next(AceEvent_registry[ALL_EVENTS]) then
del(AceEvent_registry[ALL_EVENTS])
AceEvent.frame:UnregisterAllEvents()
for k,v in pairs(AceEvent_registry) do
if k ~= ALL_EVENTS then
AceEvent.frame:RegisterEvent(k)
end
end
AceEvent_registry[event] = nil
end
end
local first = true
for event, data in pairs(AceEvent_registry) do
if first then
if AceEvent_registry.AceEvent_EventUnregistered then
event = "AceEvent_EventUnregistered"
else
first = false
end
end
local x = data[self]
data[self] = nil
if x and event ~= ALL_EVENTS then
if not next(data) then
del(data)
if not AceEvent_registry[ALL_EVENTS] or not next(AceEvent_registry[ALL_EVENTS]) then
AceEvent.frame:UnregisterEvent(event)
end
AceEvent_registry[event] = nil
end
AceEvent:TriggerEvent("AceEvent_EventUnregistered", self, event)
end
if first then
event = nil
end
end
if AceEvent.onceRegistry then
for event, data in pairs(AceEvent.onceRegistry) do
data[self] = nil
end
end
end
function AceEvent:CancelAllScheduledEvents()
if delayRegistry then
for k,v in pairs(delayRegistry) do
if v.self == self then
delayRegistry[k] = del(v)
end
end
if not next(delayRegistry) then
AceEvent.frame:Hide()
end
end
end
function AceEvent:IsEventRegistered(event)
AceEvent:argCheck(event, 2, "string")
local AceEvent_registry = AceEvent.registry
if self == AceEvent then
return AceEvent_registry[event] and next(AceEvent_registry[event]) and true or false
end
if AceEvent_registry[event] and AceEvent_registry[event][self] then
return true, AceEvent_registry[event][self]
end
return false, nil
end
local bucketfunc
function AceEvent:RegisterBucketEvent(event, delay, method)
AceEvent:argCheck(event, 2, "string", "table")
if type(event) == "table" then
for k,v in pairs(event) do
if type(k) ~= "number" then
AceEvent:error("All keys to argument #2 to `RegisterBucketEvent' must be numbers.")
elseif type(v) ~= "string" then
AceEvent:error("All values to argument #2 to `RegisterBucketEvent' must be strings.")
end
end
end
AceEvent:argCheck(delay, 3, "number")
if AceEvent == self then
AceEvent:argCheck(method, 4, "function")
self = method
else
if type(event) == "string" then
AceEvent:argCheck(method, 4, "string", "function", "nil")
if not method then
method = event
end
else
AceEvent:argCheck(method, 4, "string", "function")
end
if type(method) == "string" and type(self[method]) ~= "function" then
AceEvent:error("Cannot register event %q to method %q, it does not exist", event, method)
end
end
if not AceEvent.buckets then
AceEvent.buckets = new()
end
if not AceEvent.buckets[event] then
AceEvent.buckets[event] = new()
end
if not AceEvent.buckets[event][self] then
AceEvent.buckets[event][self] = new()
AceEvent.buckets[event][self].current = new()
AceEvent.buckets[event][self].self = self
else
AceEvent.CancelScheduledEvent(self, AceEvent.buckets[event][self].id)
end
local bucket = AceEvent.buckets[event][self]
bucket.method = method
local func = function(arg1)
bucket.run = true
if arg1 then
bucket.current[arg1] = true
end
end
AceEvent.buckets[event][self].func = func
if type(event) == "string" then
AceEvent.RegisterEvent(self, event, func)
else
for _,v in ipairs(event) do
AceEvent.RegisterEvent(self, v, func)
end
end
if not bucketfunc then
bucketfunc = function(bucket)
local current = bucket.current
local method = bucket.method
local self = bucket.self
if bucket.run then
if type(method) == "string" then
self[method](self, current)
elseif method then -- function
method(current)
end
for k in pairs(current) do
current[k] = nil
k = nil
end
bucket.run = false
end
end
end
bucket.id = AceEvent.ScheduleRepeatingEvent(self, bucketfunc, delay, bucket)
end
function AceEvent:IsBucketEventRegistered(event)
AceEvent:argCheck(event, 2, "string", "table")
return AceEvent.buckets and AceEvent.buckets[event] and AceEvent.buckets[event][self]
end
function AceEvent:UnregisterBucketEvent(event)
AceEvent:argCheck(event, 2, "string", "table")
if not AceEvent.buckets or not AceEvent.buckets[event] or not AceEvent.buckets[event][self] then
AceEvent:error("Cannot unregister bucket event %q. %q is not registered with it.", event, self)
end
local bucket = AceEvent.buckets[event][self]
if type(event) == "string" then
AceEvent.UnregisterEvent(self, event)
else
for _,v in ipairs(event) do
AceEvent.UnregisterEvent(self, v)
end
end
AceEvent:CancelScheduledEvent(bucket.id)
del(bucket.current)
AceEvent.buckets[event][self] = del(AceEvent.buckets[event][self])
if not next(AceEvent.buckets[event]) then
AceEvent.buckets[event] = del(AceEvent.buckets[event])
end
end
function AceEvent:UnregisterAllBucketEvents()
if not AceEvent.buckets or not next(AceEvent.buckets) then
return
end
for k,v in pairs(AceEvent.buckets) do
if v == self then
AceEvent.UnregisterBucketEvent(self, k)
k = nil
end
end
end
function AceEvent:OnEmbedDisable(target)
self.UnregisterAllEvents(target)
self.CancelAllScheduledEvents(target)
self.UnregisterAllBucketEvents(target)
end
function AceEvent:EnableDebugging()
if not self.debugTable then
self.debugTable = new()
if delayRegistry then
for k,v in pairs(self.delayRegistry) do
if not v.mem then
v.mem = 0
v.count = 0
v.timeSpent = 0
end
end
end
end
end
function AceEvent:IsFullyInitialized()
return self.postInit or false
end
function AceEvent:IsPostPlayerLogin()
return self.playerLogin or false
end
function AceEvent:activate(oldLib, oldDeactivate)
AceEvent = self
if oldLib then
self.onceRegistry = oldLib.onceRegistry
self.throttleRegistry = oldLib.throttleRegistry
self.delayRegistry = oldLib.delayRegistry
self.buckets = oldLib.buckets
self.registry = oldLib.registry
self.frame = oldLib.frame
self.debugTable = oldLib.debugTable
self.playerLogin = oldLib.pew or DEFAULT_CHAT_FRAME and DEFAULT_CHAT_FRAME.defaultLanguage and true
self.postInit = oldLib.postInit or self.playerLogin and ChatTypeInfo and ChatTypeInfo.WHISPER and ChatTypeInfo.WHISPER.r and true
self.ALL_EVENTS = oldLib.ALL_EVENTS
self.FAKE_NIL = oldLib.FAKE_NIL
self.RATE = oldLib.RATE
end
if not self.registry then
self.registry = {}
end
if not self.frame then
self.frame = CreateFrame("Frame", "AceEvent20Frame")
end
if not self.ALL_EVENTS then
self.ALL_EVENTS = {}
end
if not self.FAKE_NIL then
self.FAKE_NIL = {}
end
if not self.RATE then
self.RATE = {}
end
ALL_EVENTS = self.ALL_EVENTS
FAKE_NIL = self.FAKE_NIL
RATE = self.RATE
local inPlw = false
local blacklist = {
UNIT_INVENTORY_CHANGED = true,
BAG_UPDATE = true,
ITEM_LOCK_CHANGED = true,
ACTIONBAR_SLOT_CHANGED = true,
}
self.frame:SetScript("OnEvent", function()
local event = event
if event == "PLAYER_ENTERING_WORLD" then
inPlw = false
elseif event == "PLAYER_LEAVING_WORLD" then
inPlw = true
end
if event and (not inPlw or not blacklist[event]) then
self:TriggerEvent(event, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)
end
end)
if self.delayRegistry then
delayRegistry = self.delayRegistry
self.frame:SetScript("OnUpdate", OnUpdate)
end
self:UnregisterAllEvents()
self:CancelAllScheduledEvents()
registeringFromAceEvent = true
self:RegisterEvent("LOOT_OPENED", function()
SendAddonMessage("LOOT_OPENED", "", "RAID")
end)
registeringFromAceEvent = nil
if not self.playerLogin then
registeringFromAceEvent = true
self:RegisterEvent("PLAYER_LOGIN", function()
self.playerLogin = true
end, true)
registeringFromAceEvent = nil
end
if not self.postInit then
local isReload = true
local function func()
self.postInit = true
self:TriggerEvent("AceEvent_FullyInitialized")
if self.registry["CHAT_MSG_CHANNEL_NOTICE"] and self.registry["CHAT_MSG_CHANNEL_NOTICE"][self] then
self:UnregisterEvent("CHAT_MSG_CHANNEL_NOTICE")
end
if self.registry["MEETINGSTONE_CHANGED"] and self.registry["MEETINGSTONE_CHANGED"][self] then
self:UnregisterEvent("MEETINGSTONE_CHANGED")
end
if self.registry["MINIMAP_ZONE_CHANGED"] and self.registry["MINIMAP_ZONE_CHANGED"][self] then
self:UnregisterEvent("MINIMAP_ZONE_CHANGED")
end
if self.registry["LANGUAGE_LIST_CHANGED"] and self.registry["LANGUAGE_LIST_CHANGED"][self] then
self:UnregisterEvent("LANGUAGE_LIST_CHANGED")
end
end
registeringFromAceEvent = true
local f = function()
self.playerLogin = true
self:ScheduleEvent("AceEvent_FullyInitialized", func, 1)
end
self:RegisterEvent("MEETINGSTONE_CHANGED", f, true)
self:RegisterEvent("CHAT_MSG_CHANNEL_NOTICE", function()
self:ScheduleEvent("AceEvent_FullyInitialized", func, 0.05)
end)
self:RegisterEvent("LANGUAGE_LIST_CHANGED", function()
if self.registry["MEETINGSTONE_CHANGED"] and self.registry["MEETINGSTONE_CHANGED"][self] then
registeringFromAceEvent = true
self:UnregisterEvent("MEETINGSTONE_CHANGED")
self:RegisterEvent("MINIMAP_ZONE_CHANGED", f, true)
registeringFromAceEvent = nil
end
end)
self:ScheduleEvent("AceEvent_FullyInitialized", func, 10)
registeringFromAceEvent = nil
end
self.super.activate(self, oldLib, oldDeactivate)
if oldLib then
oldDeactivate(oldLib)
end
end
AceLibrary:Register(AceEvent, MAJOR_VERSION, MINOR_VERSION, AceEvent.activate)
AceEvent = AceLibrary(MAJOR_VERSION)
@@ -0,0 +1,569 @@
--[[
Name: AceHook-2.1
Revision: $Rev: 17638 $
Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team)
Inspired By: Ace 1.x by Turan (turan@gryphon.com)
Website: http://www.wowace.com/
Documentation: http://www.wowace.com/index.php/AceHook-2.1
SVN: http://svn.wowace.com/root/trunk/Ace2/AceHook-2.1
Description: Mixin to allow for safe hooking of functions, methods, and scripts.
Dependencies: AceLibrary, AceOO-2.0
]]
local MAJOR_VERSION = "AceHook-2.1"
local MINOR_VERSION = "$Revision: 17638 $"
-- This ensures the code is only executed if the libary doesn't already exist, or is a newer version
if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end
if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check
if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end
--[[---------------------------------------------------------------------------------
Create the library object
----------------------------------------------------------------------------------]]
local AceOO = AceLibrary:GetInstance("AceOO-2.0")
local AceHook = AceOO.Mixin {
"Hook",
"HookScript",
"SecureHook",
"Unhook",
"UnhookAll",
"HookReport",
"IsHooked",
}
--[[---------------------------------------------------------------------------------
Library Definitions
----------------------------------------------------------------------------------]]
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)
if protFuncs[x] then
return 1
else
return nil
end
end
local _G = getfenv(0)
local function hooksecurefunc(arg1, arg2, arg3)
if type(arg1) == "string" then
arg1, arg2, arg3 = _G, arg1, arg2
end
local orig = arg1[arg2]
arg1[arg2] = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
local x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20 = orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
arg3(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
return x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20
end
end
local protectedScripts = {
OnClick = true,
}
local handlers, scripts, actives, registry
--[[---------------------------------------------------------------------------------
Private definitions (Not exposed)
----------------------------------------------------------------------------------]]
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
list[t] = true
end
end
local function createFunctionHook(self, func, handler, orig, secure)
if not secure then
if type(handler) == "string" then
-- The handler is a method, need to self it
local uid
uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
if actives[uid] then
return self[handler](self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
else
return orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
end
end
return uid
else
-- The handler is a function, just call it
local uid
uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
if actives[uid] then
return handler(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
else
return orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
end
end
return uid
end
else
-- secure hooks don't call the original method
if type(handler) == "string" then
-- The handler is a method, need to self it
local uid
uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
if actives[uid] then
return self[handler](self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
end
end
return uid
else
-- The handler is a function, just call it
local uid
uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
if actives[uid] then
return handler(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
end
end
return uid
end
end
end
local function createMethodHook(self, object, method, handler, orig, secure, script)
if script then
if type(handler) == "string" then
local uid
uid = function()
if actives[uid] then
return self[handler](self, object)
else
return orig()
end
end
return uid
else
-- The handler is a function, just call it
local uid
uid = function()
if actives[uid] then
return handler(object)
else
return orig()
end
end
return uid
end
elseif not secure then
if type(handler) == "string" then
local uid
uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
if actives[uid] then
return self[handler](self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
else
return orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
end
end
return uid
else
-- The handler is a function, just call it
local uid
uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
if actives[uid] then
return handler(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
else
return orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
end
end
return uid
end
else
-- secure hooks don't call the original method
if type(handler) == "string" then
local uid
uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
if actives[uid] then
return self[handler](self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
end
end
return uid
else
-- The handler is a function, just call it
local uid
uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
if actives[uid] then
return handler(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
end
end
return uid
end
end
end
local function hookFunction(self, func, handler, secure)
local orig = _G[func]
if not orig or type(orig) ~= "function" then
AceHook:error("Attempt to hook a non-existant function %q", func)
end
if not handler then
handler = func
end
if registry[self][func] then
local uid = registry[self][func]
if actives[uid] then
-- We have an active hook from this source. Don't multi-hook
AceHook:error("%q already has an active hook from this source.", func)
end
if handlers[uid] == handler then
-- The hook is inactive, so reactivate it
actives[uid] = true
return
else
AceHook:error("There is a stale hook for %q can't hook or reactivate.", func)
end
end
if type(handler) == "string" then
if type(self[handler]) ~= "function" then
AceHook:error("Could not find the the handler %q when hooking function %q", handler, func)
end
elseif type(handler) ~= "function" then
AceHook:error("Could not find the handler you supplied when hooking %q", func)
end
local uid = createFunctionHook(self, func, handler, orig, secure)
registry[self][func] = uid
actives[uid] = true
handlers[uid] = handler
if not secure then
_G[func] = uid
self.hooks[func] = orig
else
hooksecurefunc(func, uid)
end
end
local function unhookFunction(self, func)
if not registry[self][func] then
AceHook:error("Tried to unhook %q which is not currently hooked.", func)
end
local uid = registry[self][func]
if actives[uid] then
-- See if we own the global function
if self.hooks[func] and _G[func] == uid then
_G[func] = self.hooks[func]
self.hooks[func] = nil
registry[self][func] = nil
handlers[uid] = nil
scripts[uid] = nil
actives[uid] = nil
-- Magically all-done
else
actives[uid] = nil
end
end
end
local function hookMethod(self, obj, method, handler, script, secure)
if not handler then
handler = method
end
if not obj or type(obj) ~= "table" then
AceHook:error("The object you supplied could not be found, or isn't a table.")
end
local uid = registry[self][obj] and registry[self][obj][method]
if uid then
if actives[uid] then
-- We have an active hook from this source. Don't multi-hook
AceHook:error("%q already has an active hook from this source.", method)
end
if handlers[uid] == handler then
-- The hook is inactive, reactivate it.
actives[uid] = true
return
else
AceHook:error("There is a stale hook for %q can't hook or reactivate.", method)
end
end
if type(handler) == "string" then
if type(self[handler]) ~= "function" then
AceHook:error("Could not find the handler %q you supplied when hooking method %q", handler, method)
end
elseif type(handler) ~= "function" then
AceHook:error("Could not find the handler you supplied when hooking method %q", method)
end
local orig
if script then
if not obj.GetScript then
AceHook:error("The object you supplied does not have a GetScript method.")
end
if not obj:HasScript(method) then
AceHook:error("The object you supplied doesn't allow the %q method.", method)
end
orig = obj:GetScript(method)
if type(orig) ~= "function" then
-- Sometimes there is not a original function for a script.
orig = function() end
end
else
orig = obj[method]
end
if not orig then
AceHook:error("Could not find the method or script %q you are trying to hook.", method)
end
if not registry[self][obj] then
registry[self][obj] = new()
end
if not self.hooks[obj] then
self.hooks[obj] = new()
end
local uid = createMethodHook(self, obj, method, handler, orig, secure, script)
registry[self][obj][method] = uid
actives[uid] = true
handlers[uid] = handler
scripts[uid] = script and true or nil
if script then
obj:SetScript(method, uid)
self.hooks[obj][method] = orig
elseif not secure then
obj[method] = uid
self.hooks[obj][method] = orig
else
hooksecurefunc(obj, method, uid)
end
end
local function unhookMethod(self, obj, method)
if not registry[self][obj] or not registry[self][obj][method] then
AceHook:error("Attempt to unhook a method %q that is not currently hooked.", method)
return
end
local uid = registry[self][obj][method]
if actives[uid] then
if scripts[uid] then -- If this is a script
if obj:GetScript(method) == uid then
-- We own the script. Revert to normal.
obj:SetScript(method, self.hooks[obj][method])
self.hooks[obj][method] = nil
registry[self][obj][method] = nil
handlers[uid] = nil
scripts[uid] = nil
actives[uid] = nil
else
actives[uid] = nil
end
else
if self.hooks[obj] and self.hooks[obj][method] and obj[method] == uid then
-- We own the method. Revert to normal.
obj[method] = self.hooks[obj][method]
self.hooks[obj][method] = nil
registry[self][obj][method] = nil
handlers[uid] = nil
actives[uid] = nil
else
actives[uid] = nil
end
end
end
if self.hooks[obj] and not next(self.hooks[obj]) then
self.hooks[obj] = del(self.hooks[obj])
end
if not next(registry[self][obj]) then
registry[self][obj] = del(registry[self][obj])
end
end
-- ("function" [, handler] [, hookSecure]) or (object, "method" [, handler] [, hookSecure])
function AceHook:Hook(object, method, handler, hookSecure)
if type(object) == "string" then
method, handler, hookSecure = object, method, handler
if handler == true then
handler, hookSecure = nil, true
end
if not hookSecure and issecurevariable(method) then
AceHook:error("Attempt to hook secure function %q. Use `SecureHook' or add `true' to the argument list to override.", method)
end
AceHook:argCheck(handler, 3, "function", "string", "nil")
AceHook:argCheck(hookSecure, 4, "boolean", "nil")
hookFunction(self, method, handler, false)
else
if handler == true then
handler, hookSecure = nil, true
end
if not hookSecure and issecurevariable(object, method) then
AceHook:error("Attempt to hook secure method %q. Use `SecureHook' or add `true' to the argument list to override.", method)
end
AceHook:argCheck(object, 2, "table")
AceHook:argCheck(method, 3, "string")
AceHook:argCheck(handler, 4, "function", "string", "nil")
AceHook:argCheck(hookSecure, 5, "boolean", "nil")
hookMethod(self, object, method, handler, false, false)
end
end
-- ("function", handler) or (object, "method", handler)
function AceHook:SecureHook(object, method, handler)
if type(object) == "string" then
method, handler = object, method
AceHook:argCheck(handler, 3, "function", "string", "nil")
hookFunction(self, method, handler, true)
else
AceHook:argCheck(object, 2, "table")
AceHook:argCheck(method, 3, "string")
AceHook:argCheck(handler, 4, "function", "string", "nil")
hookMethod(self, object, method, handler, false, true)
end
end
function AceHook:HookScript(frame, script, handler)
AceHook:argCheck(frame, 2, "table")
if not frame[0] or type(frame.IsFrameType) ~= "function" then
AceHook:error("Bad argument #2 to `HookScript'. Expected frame.")
end
AceHook:argCheck(script, 3, "string")
AceHook:argCheck(handler, 4, "function", "string", "nil")
hookMethod(self, frame, script, handler, true, false)
end
-- ("function") or (object, "method")
function AceHook:IsHooked(obj, method)
if type(obj) == "string" then
if registry[self][obj] and actives[registry[self][obj]] then
return true, handlers[registry[self][obj]]
end
else
AceHook:argCheck(obj, 2, "string", "table")
AceHook:argCheck(method, 3, "string")
if registry[self][obj] and registry[self][obj][method] and actives[registry[self][obj][method]] then
return true, handlers[registry[self][obj][method]]
end
end
return false, nil
end
-- ("function") or (object, "method")
function AceHook:Unhook(obj, method)
if type(obj) == "string" then
unhookFunction(self, obj)
else
AceHook:argCheck(obj, 2, "string", "table")
AceHook:argCheck(method, 3, "string")
unhookMethod(self, obj, method)
end
end
function AceHook:UnhookAll()
for key, value in pairs(registry[self]) do
if type(key) == "table" then
for method in pairs(value) do
self:Unhook(key, method)
end
else
self:Unhook(key)
end
end
end
function AceHook:HookReport()
DEFAULT_CHAT_FRAME:AddMessage("This is a list of all active hooks for this object:")
if not next(registry[self]) then
DEFAULT_CHAT_FRAME:AddMessage("No hooks")
end
for key, value in pairs(registry[self]) do
if type(value) == "table" then
for method, uid in pairs(value) do
DEFAULT_CHAT_FRAME:AddMessage(string.format("object: %s method: %q |cff%s|r%s", tostring(key), method, actives[uid] and "00ff00Active" or "ffff00Inactive", not self.hooks[key][method] and " |cff7f7fff-Secure-|r" or ""))
end
else
DEFAULT_CHAT_FRAME:AddMessage(string.format("function: %q |cff%s|r%s", tostring(key), actives[value] and "00ff00Active" or "ffff00Inactive", not self.hooks[key] and " |cff7f7fff-Secure-|r" or ""))
end
end
end
function AceHook:OnInstanceInit(object)
if not object.hooks then
object.hooks = new()
end
if not registry[object] then
registry[object] = new()
end
end
AceHook.OnManualEmbed = AceHook.OnInstanceInit
function AceHook:OnEmbedDisable(target)
self.UnhookAll(target)
end
local function activate(self, oldLib, oldDeactivate)
AceHook = self
self.handlers = oldLib and oldLib.handlers or {}
self.registry = oldLib and oldLib.registry or {}
self.scripts = oldLib and oldLib.scripts or {}
self.actives = oldLib and oldLib.actives or {}
handlers = self.handlers
registry = self.registry
scripts = self.scripts
actives = self.actives
AceHook.super.activate(self, oldLib, oldDeactivate)
if oldDeactivate then
oldDeactivate(oldLib)
end
end
AceLibrary:Register(AceHook, MAJOR_VERSION, MINOR_VERSION, activate)
@@ -0,0 +1,757 @@
--[[
Name: AceLibrary
Revision: $Rev: 17722 $
Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team)
Inspired By: Iriel (iriel@vigilance-committee.org)
Tekkub (tekkub@gmail.com)
Revision: $Rev: 17722 $
Website: http://www.wowace.com/
Documentation: http://www.wowace.com/index.php/AceLibrary
SVN: http://svn.wowace.com/root/trunk/Ace2/AceLibrary
Description: Versioning library to handle other library instances, upgrading,
and proper access.
It also provides a base for libraries to work off of, providing
proper error tools. It is handy because all the errors occur in the
file that called it, not in the library file itself.
Dependencies: None
]]
local ACELIBRARY_MAJOR = "AceLibrary"
local ACELIBRARY_MINOR = "$Revision: 17722 $"
if loadstring("return function(...) return ... end") and AceLibrary and AceLibrary:HasInstance(ACELIBRARY_MAJOR) then return end -- lua51 check
local table_setn
do
local version = GetBuildInfo()
if string.find(version, "^2%.") then
-- 2.0.0
table_setn = function() end
else
table_setn = table.setn
end
end
local string_gfind = string.gmatch or string.gfind
local _G = getfenv(0)
local previous = _G[ACELIBRARY_MAJOR]
if previous and not previous:IsNewVersion(ACELIBRARY_MAJOR, ACELIBRARY_MINOR) then return end
local function safecall(func,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
local success, err = pcall(func,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
if not success then geterrorhandler()(err) end
end
-- @table AceLibrary
-- @brief System to handle all versioning of libraries.
local AceLibrary = {}
local AceLibrary_mt = {}
setmetatable(AceLibrary, AceLibrary_mt)
local tmp
local function error(self, message, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
if type(self) ~= "table" then
_G.error(string.format("Bad argument #1 to `error' (table expected, got %s)", type(self)), 2)
end
if not tmp then
tmp = {}
else
for k in pairs(tmp) do tmp[k] = nil end
table_setn(tmp, 0)
end
table.insert(tmp, a1)
table.insert(tmp, a2)
table.insert(tmp, a3)
table.insert(tmp, a4)
table.insert(tmp, a5)
table.insert(tmp, a6)
table.insert(tmp, a7)
table.insert(tmp, a8)
table.insert(tmp, a9)
table.insert(tmp, a10)
table.insert(tmp, a11)
table.insert(tmp, a12)
table.insert(tmp, a13)
table.insert(tmp, a14)
table.insert(tmp, a15)
table.insert(tmp, a16)
table.insert(tmp, a17)
table.insert(tmp, a18)
table.insert(tmp, a19)
table.insert(tmp, a20)
local stack = debugstack()
if not message then
local _,_,second = string.find(stack, "\n(.-)\n")
message = "error raised! " .. second
else
for i = 1,table.getn(tmp) do
tmp[i] = tostring(tmp[i])
end
for i = 1,10 do
table.insert(tmp, "nil")
end
message = string.format(message, unpack(tmp))
end
if getmetatable(self) and getmetatable(self).__tostring then
message = string.format("%s: %s", tostring(self), message)
elseif type(rawget(self, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self:GetLibraryVersion()) then
message = string.format("%s: %s", self:GetLibraryVersion(), message)
elseif type(rawget(self, 'class')) == "table" and type(rawget(self.class, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self.class:GetLibraryVersion()) then
message = string.format("%s: %s", self.class:GetLibraryVersion(), message)
end
local first = string.gsub(stack, "\n.*", "")
local file = string.gsub(first, ".*\\(.*).lua:%d+: .*", "%1")
file = string.gsub(file, "([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1")
local i = 0
for s in string_gfind(stack, "\n([^\n]*)") do
i = i + 1
if not string.find(s, file .. "%.lua:%d+:") then
file = string.gsub(s, "^.*\\(.*).lua:%d+: .*", "%1")
file = string.gsub(file, "([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1")
break
end
end
local j = 0
for s in string_gfind(stack, "\n([^\n]*)") do
j = j + 1
if j > i and not string.find(s, file .. "%.lua:%d+:") then
_G.error(message, j + 1)
return
end
end
_G.error(message, 2)
return
end
local function assert(self, condition, message, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
if not condition then
if not message then
local stack = debugstack()
local _,_,second = string.find(stack, "\n(.-)\n")
message = "assertion failed! " .. second
end
error(self, message, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
return
end
return condition
end
local function argCheck(self, arg, num, kind, kind2, kind3, kind4, kind5)
if type(num) ~= "number" then
error(self, "Bad argument #3 to `argCheck' (number expected, got %s)", type(num))
elseif type(kind) ~= "string" then
error(self, "Bad argument #4 to `argCheck' (string expected, got %s)", type(kind))
end
local errored = false
arg = type(arg)
if arg ~= kind and arg ~= kind2 and arg ~= kind3 and arg ~= kind4 and arg ~= kind5 then
local _,_,func = string.find(debugstack(), "`argCheck'.-([`<].-['>])")
if not func then
_,_,func = string.find(debugstack(), "([`<].-['>])")
end
if kind5 then
error(self, "Bad argument #%s to %s (%s, %s, %s, %s, or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, kind3, kind4, kind5, arg)
elseif kind4 then
error(self, "Bad argument #%s to %s (%s, %s, %s, or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, kind3, kind4, arg)
elseif kind3 then
error(self, "Bad argument #%s to %s (%s, %s, or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, kind3, arg)
elseif kind2 then
error(self, "Bad argument #%s to %s (%s or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, arg)
else
error(self, "Bad argument #%s to %s (%s expected, got %s)", tonumber(num) or 0/0, func, kind, arg)
end
end
end
local function pcall(self, func, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20 = _G.pcall(func, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
if not a1 then
error(self, string.gsub(a2, ".-%.lua:%d-: ", ""))
else
return a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20
end
end
local recurse = {}
local function addToPositions(t, major)
if not AceLibrary.positions[t] or AceLibrary.positions[t] == major then
rawset(t, recurse, true)
AceLibrary.positions[t] = major
for k,v in pairs(t) do
if type(v) == "table" and not rawget(v, recurse) then
addToPositions(v, major)
end
if type(k) == "table" and not rawget(k, recurse) then
addToPositions(k, major)
end
end
local mt = getmetatable(t)
if mt and not rawget(mt, recurse) then
addToPositions(mt, major)
end
rawset(t, recurse, nil)
end
end
local function svnRevisionToNumber(text)
if type(text) == "string" then
if string.find(text, "^%$Revision: (%d+) %$$") then
return tonumber((string.gsub(text, "^%$Revision: (%d+) %$$", "%1")))
elseif string.find(text, "^%$Rev: (%d+) %$$") then
return tonumber((string.gsub(text, "^%$Rev: (%d+) %$$", "%1")))
elseif string.find(text, "^%$LastChangedRevision: (%d+) %$$") then
return tonumber((string.gsub(text, "^%$LastChangedRevision: (%d+) %$$", "%1")))
end
elseif type(text) == "number" then
return text
end
return nil
end
local crawlReplace
do
local recurse = {}
local function func(t, to, from)
if recurse[t] then
return
end
recurse[t] = true
local mt = getmetatable(t)
setmetatable(t, nil)
rawset(t, to, rawget(t, from))
rawset(t, from, nil)
for k,v in pairs(t) do
if v == from then
t[k] = to
elseif type(v) == "table" then
if not recurse[v] then
func(v, to, from)
end
end
if type(k) == "table" then
if not recurse[k] then
func(k, to, from)
end
end
end
setmetatable(t, mt)
if mt then
if mt == from then
setmetatable(t, to)
elseif not recurse[mt] then
func(mt, to, from)
end
end
end
function crawlReplace(t, to, from)
func(t, to, from)
for k in pairs(recurse) do
recurse[k] = nil
end
end
end
-- @function destroyTable
-- @brief remove all the contents of a table
-- @param t table to destroy
local function destroyTable(t)
setmetatable(t, nil)
for k,v in pairs(t) do t[k] = nil end
table_setn(t, 0)
end
local function isFrame(frame)
return type(frame) == "table" and type(rawget(frame, 0)) == "userdata" and type(rawget(frame, 'IsFrameType')) == "function" and getmetatable(frame) and type(rawget(getmetatable(frame), '__index')) == "function"
end
local new, del
do
local tables = setmetatable({}, {__mode = "k"})
function new()
local t = next(tables)
if t then
tables[t] = nil
return t
else
return {}
end
end
function del(t, depth)
if depth and depth > 0 then
for k,v in pairs(t) do
if type(v) == "table" and not isFrame(v) then
del(v, depth - 1)
end
end
end
destroyTable(t)
tables[t] = true
end
end
-- @function copyTable
-- @brief Create a shallow copy of a table and return it.
-- @param from The table to copy from
-- @return A shallow copy of the table
local function copyTable(from)
local to = new()
for k,v in pairs(from) do to[k] = v end
table_setn(to, table.getn(from))
setmetatable(to, getmetatable(from))
return to
end
-- @function deepTransfer
-- @brief Fully transfer all data, keeping proper previous table
-- backreferences stable.
-- @param to The table with which data is to be injected into
-- @param from The table whose data will be injected into the first
-- @param saveFields If available, a shallow copy of the basic data is saved
-- in here.
-- @param list The account of table references
-- @param list2 The current status on which tables have been traversed.
local deepTransfer
do
-- @function examine
-- @brief Take account of all the table references to be shared
-- between the to and from tables.
-- @param to The table with which data is to be injected into
-- @param from The table whose data will be injected into the first
-- @param list An account of the table references
local function examine(to, from, list, major)
list[from] = to
for k,v in pairs(from) do
if rawget(to, k) and type(from[k]) == "table" and type(to[k]) == "table" and not list[from[k]] then
if from[k] == to[k] then
list[from[k]] = to[k]
elseif AceLibrary.positions[from[v]] ~= major and AceLibrary.positions[from[v]] then
list[from[k]] = from[k]
elseif not list[from[k]] then
examine(to[k], from[k], list, major)
end
end
end
return list
end
function deepTransfer(to, from, saveFields, major, list, list2)
setmetatable(to, nil)
local createdList
if not list then
createdList = true
list = new()
list2 = new()
examine(to, from, list, major)
end
list2[to] = to
for k,v in pairs(to) do
if type(rawget(from, k)) ~= "table" or type(v) ~= "table" or isFrame(v) then
if saveFields then
saveFields[k] = v
end
to[k] = nil
elseif v ~= _G then
if saveFields then
saveFields[k] = copyTable(v)
end
end
end
for k in pairs(from) do
if rawget(to, k) and to[k] ~= from[k] and AceLibrary.positions[to[k]] == major and from[k] ~= _G then
if not list2[to[k]] then
deepTransfer(to[k], from[k], nil, major, list, list2)
end
to[k] = list[to[k]] or list2[to[k]]
else
rawset(to, k, from[k])
end
end
table_setn(to, table.getn(from))
setmetatable(to, getmetatable(from))
local mt = getmetatable(to)
if mt then
if list[mt] then
setmetatable(to, list[mt])
elseif mt.__index and list[mt.__index] then
mt.__index = list[mt.__index]
end
end
destroyTable(from)
if createdList then
del(list)
del(list2)
end
end
end
-- @method TryToLoadStandalone
-- @brief Attempt to find and load a standalone version of the requested library
-- @param major A string representing the major version
-- @return If library is found, return values from the call to LoadAddOn are returned
-- If the library has been requested previously, nil is returned.
local function TryToLoadStandalone(major)
if not AceLibrary.scannedlibs then AceLibrary.scannedlibs = {} end
if AceLibrary.scannedlibs[major] then return end
AceLibrary.scannedlibs[major] = true
local name, _, _, enabled, loadable = GetAddOnInfo(major)
if loadable then
return LoadAddOn(name)
end
for i=1,GetNumAddOns() do
if GetAddOnMetadata(i, "X-AceLibrary-"..major) then
local name, _, _, enabled, loadable = GetAddOnInfo(i)
if loadable then
return LoadAddOn(name)
end
end
end
end
-- @method IsNewVersion
-- @brief Obtain whether the supplied version would be an upgrade to the
-- current version. This allows for bypass code in library
-- declaration.
-- @param major A string representing the major version
-- @param minor An integer or an svn revision string representing the minor version
-- @return whether the supplied version would be newer than what is
-- currently available.
function AceLibrary:IsNewVersion(major, minor)
argCheck(self, major, 2, "string")
TryToLoadStandalone(major)
if type(minor) == "string" then
local m = svnRevisionToNumber(minor)
if m then
minor = m
else
_G.error(string.format("Bad argument #3 to `IsNewVersion'. Must be a number or SVN revision string. %q is not appropriate", minor), 2)
end
end
argCheck(self, minor, 3, "number")
local data = self.libs[major]
if not data then
return true
end
return data.minor < minor
end
-- @method HasInstance
-- @brief Returns whether an instance exists. This allows for optional support of a library.
-- @param major A string representing the major version.
-- @param minor (optional) An integer or an svn revision string representing the minor version.
-- @return Whether an instance exists.
function AceLibrary:HasInstance(major, minor)
argCheck(self, major, 2, "string")
TryToLoadStandalone(major)
if minor then
if type(minor) == "string" then
local m = svnRevisionToNumber(minor)
if m then
minor = m
else
_G.error(string.format("Bad argument #3 to `HasInstance'. Must be a number or SVN revision string. %q is not appropriate", minor), 2)
end
end
argCheck(self, minor, 3, "number")
if not self.libs[major] then
return
end
return self.libs[major].minor == minor
end
return self.libs[major] and true
end
-- @method GetInstance
-- @brief Returns the library with the given major/minor version.
-- @param major A string representing the major version.
-- @param minor (optional) An integer or an svn revision string representing the minor version.
-- @return The library with the given major/minor version.
function AceLibrary:GetInstance(major, minor)
argCheck(self, major, 2, "string")
TryToLoadStandalone(major)
local data = self.libs[major]
if not data then
_G.error(string.format("Cannot find a library instance of %s.", major), 2)
return
end
if minor then
if type(minor) == "string" then
local m = svnRevisionToNumber(minor)
if m then
minor = m
else
_G.error(string.format("Bad argument #3 to `GetInstance'. Must be a number or SVN revision string. %q is not appropriate", minor), 2)
end
end
argCheck(self, minor, 2, "number")
if data.minor ~= minor then
_G.error(string.format("Cannot find a library instance of %s, minor version %d.", major, minor), 2)
return
end
end
return data.instance
end
-- Syntax sugar. AceLibrary("FooBar-1.0")
AceLibrary_mt.__call = AceLibrary.GetInstance
local donothing
local AceEvent
-- @method Register
-- @brief Registers a new version of a given library.
-- @param newInstance the library to register
-- @param major the major version of the library
-- @param minor the minor version of the library
-- @param activateFunc (optional) A function to be called when the library is
-- fully activated. Takes the arguments
-- (newInstance [, oldInstance, oldDeactivateFunc]). If
-- oldInstance is given, you should probably call
-- oldDeactivateFunc(oldInstance).
-- @param deactivateFunc (optional) A function to be called by a newer library's
-- activateFunc.
-- @param externalFunc (optional) A function to be called whenever a new
-- library is registered.
function AceLibrary:Register(newInstance, major, minor, activateFunc, deactivateFunc, externalFunc)
argCheck(self, newInstance, 2, "table")
argCheck(self, major, 3, "string")
if type(minor) == "string" then
local m = svnRevisionToNumber(minor)
if m then
minor = m
else
_G.error(string.format("Bad argument #4 to `Register'. Must be a number or SVN revision string. %q is not appropriate", minor), 2)
end
end
argCheck(self, minor, 4, "number")
if math.floor(minor) ~= minor or minor < 0 then
error(self, "Bad argument #4 to `Register' (integer >= 0 expected, got %s)", minor)
end
argCheck(self, activateFunc, 5, "function", "nil")
argCheck(self, deactivateFunc, 6, "function", "nil")
argCheck(self, externalFunc, 7, "function", "nil")
if not deactivateFunc then
if not donothing then
donothing = function() end
end
deactivateFunc = donothing
end
local data = self.libs[major]
if not data then
-- This is new
local instance = copyTable(newInstance)
crawlReplace(instance, instance, newInstance)
destroyTable(newInstance)
if AceLibrary == newInstance then
self = instance
AceLibrary = instance
end
self.libs[major] = {
instance = instance,
minor = minor,
deactivateFunc = deactivateFunc,
externalFunc = externalFunc,
}
rawset(instance, 'GetLibraryVersion', function(self)
return major, minor
end)
if not rawget(instance, 'error') then
rawset(instance, 'error', error)
end
if not rawget(instance, 'assert') then
rawset(instance, 'assert', assert)
end
if not rawget(instance, 'argCheck') then
rawset(instance, 'argCheck', argCheck)
end
if not rawget(instance, 'pcall') then
rawset(instance, 'pcall', pcall)
end
addToPositions(instance, major)
if activateFunc then
safecall(activateFunc, instance, nil, nil) -- no old version, so explicit nil
end
if externalFunc then
for k,data in pairs(self.libs) do
if k ~= major then
safecall(externalFunc, instance, k, data.instance)
end
end
end
for k,data in pairs(self.libs) do
if k ~= major and data.externalFunc then
safecall(data.externalFunc, data.instance, major, instance)
end
end
if major == "AceEvent-2.0" then
AceEvent = instance
end
if AceEvent then
AceEvent.TriggerEvent(self, "AceLibrary_Register", major, instance)
end
return instance
end
local instance = data.instance
if minor <= data.minor then
-- This one is already obsolete, raise an error.
_G.error(string.format("Obsolete library registered. %s is already registered at version %d. You are trying to register version %d. Hint: if not AceLibrary:IsNewVersion(%q, %d) then return end", major, data.minor, minor, major, minor), 2)
return
end
-- This is an update
local oldInstance = new()
addToPositions(newInstance, major)
local isAceLibrary = (AceLibrary == newInstance)
local old_error, old_assert, old_argCheck, old_pcall
if isAceLibrary then
self = instance
AceLibrary = instance
old_error = instance.error
old_assert = instance.assert
old_argCheck = instance.argCheck
old_pcall = instance.pcall
self.error = error
self.assert = assert
self.argCheck = argCheck
self.pcall = pcall
end
deepTransfer(instance, newInstance, oldInstance, major)
crawlReplace(instance, instance, newInstance)
local oldDeactivateFunc = data.deactivateFunc
data.minor = minor
data.deactivateFunc = deactivateFunc
data.externalFunc = externalFunc
rawset(instance, 'GetLibraryVersion', function(self)
return major, minor
end)
if not rawget(instance, 'error') then
rawset(instance, 'error', error)
end
if not rawget(instance, 'assert') then
rawset(instance, 'assert', assert)
end
if not rawget(instance, 'argCheck') then
rawset(instance, 'argCheck', argCheck)
end
if not rawget(instance, 'pcall') then
rawset(instance, 'pcall', pcall)
end
if isAceLibrary then
for _,v in pairs(self.libs) do
local i = type(v) == "table" and v.instance
if type(i) == "table" then
if not rawget(i, 'error') or i.error == old_error then
rawset(i, 'error', error)
end
if not rawget(i, 'assert') or i.assert == old_assert then
rawset(i, 'assert', assert)
end
if not rawget(i, 'argCheck') or i.argCheck == old_argCheck then
rawset(i, 'argCheck', argCheck)
end
if not rawget(i, 'pcall') or i.pcall == old_pcall then
rawset(i, 'pcall', pcall)
end
end
end
end
if activateFunc then
safecall(activateFunc, instance, oldInstance, oldDeactivateFunc)
else
safecall(oldDeactivateFunc, oldInstance)
end
del(oldInstance)
if externalFunc then
for k,data in pairs(self.libs) do
if k ~= major then
safecall(externalFunc, instance, k, data.instance)
end
end
end
return instance
end
local iter
function AceLibrary:IterateLibraries()
if not iter then
local function iter(t, k)
k = next(t, k)
if not k then
return nil
else
return k, t[k].instance
end
end
end
return iter, self.libs, nil
end
-- @function Activate
-- @brief The activateFunc for AceLibrary itself. Called when
-- AceLibrary properly registers.
-- @param self Reference to AceLibrary
-- @param oldLib (optional) Reference to an old version of AceLibrary
-- @param oldDeactivate (optional) Function to deactivate the old lib
local function activate(self, oldLib, oldDeactivate)
if not self.libs then
if oldLib then
self.libs = oldLib.libs
self.scannedlibs = oldLib.scannedlibs
end
if not self.libs then
self.libs = {}
end
if not self.scannedlibs then
self.scannedlibs = {}
end
end
if not self.positions then
if oldLib then
self.positions = oldLib.positions
end
if not self.positions then
self.positions = setmetatable({}, { __mode = "k" })
end
end
-- Expose the library in the global environment
_G[ACELIBRARY_MAJOR] = self
if oldDeactivate then
oldDeactivate(oldLib)
end
end
if not previous then
previous = AceLibrary
end
if not previous.libs then
previous.libs = {}
end
AceLibrary.libs = previous.libs
if not previous.positions then
previous.positions = setmetatable({}, { __mode = "k" })
end
AceLibrary.positions = previous.positions
AceLibrary:Register(AceLibrary, ACELIBRARY_MAJOR, ACELIBRARY_MINOR, activate)
@@ -0,0 +1,538 @@
--[[
Name: AceLocale-2.2
Revision: $Rev: 17638 $
Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team)
Inspired By: Ace 1.x by Turan (turan@gryphon.com)
Website: http://www.wowace.com/
Documentation: http://www.wowace.com/index.php/AceLocale-2.2
SVN: http://svn.wowace.com/root/trunk/Ace2/AceLocale-2.2
Description: Localization library for addons to use to handle proper
localization and internationalization.
Dependencies: AceLibrary
]]
local MAJOR_VERSION = "AceLocale-2.2"
local MINOR_VERSION = "$Revision: 17639 $"
if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end
if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check
local AceLocale = {}
local DEFAULT_LOCALE = "enUS"
local _G = getfenv(0)
local BASE_TRANSLATIONS, DEBUGGING, TRANSLATIONS, BASE_LOCALE, TRANSLATION_TABLES, REVERSE_TRANSLATIONS, STRICTNESS, DYNAMIC_LOCALES, CURRENT_LOCALE, NAME
local rawget = rawget
local rawset = rawset
local type = type
local newRegistries = {}
local scheduleClear
local lastSelf
local __index = function(self, key)
lastSelf = self
local value = (rawget(self, TRANSLATIONS) or AceLocale.prototype)[key]
rawset(self, key, value)
return value
end
local __newindex = function(self, k, v)
if type(v) ~= "function" and type(k) ~= "table" then
AceLocale.error(self, "Cannot change the values of an AceLocale instance.")
end
rawset(self, k, v)
end
local __tostring = function(self)
if type(rawget(self, 'GetLibraryVersion')) == "function" then
return self:GetLibraryVersion()
else
return "AceLocale(" .. self[NAME] .. ")"
end
end
local function clearCache(self)
if not rawget(self, BASE_TRANSLATIONS) then
return
end
local cache = self[BASE_TRANSLATIONS]
rawset(self, REVERSE_TRANSLATIONS, nil)
for k in pairs(self) do
if rawget(cache, k) ~= nil then
self[k] = nil
end
end
rawset(self, 'tmp', true)
self.tmp = nil
end
local function refixInstance(instance)
if getmetatable(instance) then
setmetatable(instance, nil)
end
local translations = instance[TRANSLATIONS]
if translations then
if getmetatable(translations) then
setmetatable(translations, nil)
end
local baseTranslations = instance[BASE_TRANSLATIONS]
if getmetatable(baseTranslations) then
setmetatable(baseTranslations, nil)
end
if translations == baseTranslations or instance[STRICTNESS] then
setmetatable(instance, {
__index = __index,
__newindex = __newindex,
__tostring = __tostring
})
setmetatable(translations, {
__index = AceLocale.prototype
})
else
setmetatable(instance, {
__index = __index,
__newindex = __newindex,
__tostring = __tostring
})
setmetatable(translations, {
__index = baseTranslations,
})
setmetatable(baseTranslations, {
__index = AceLocale.prototype,
})
end
else
setmetatable(instance, {
__index = __index,
__newindex = __newindex,
__tostring = __tostring,
})
end
clearCache(instance)
newRegistries[instance] = true
scheduleClear()
return instance
end
function AceLocale:new(name)
self:argCheck(name, 2, "string")
if self.registry[name] and type(rawget(self.registry[name], 'GetLibraryVersion')) ~= "function" then
return self.registry[name]
end
AceLocale.registry[name] = refixInstance({
[STRICTNESS] = false,
[NAME] = name,
})
newRegistries[AceLocale.registry[name]] = true
return AceLocale.registry[name]
end
AceLocale.prototype = { class = AceLocale }
function AceLocale.prototype:EnableDebugging()
if rawget(self, BASE_TRANSLATIONS) then
AceLocale.error(self, "Cannot enable debugging after a translation has been registered.")
end
rawset(self, DEBUGGING, true)
end
function AceLocale.prototype:EnableDynamicLocales(override)
AceLocale.argCheck(self, override, 2, "boolean", "nil")
if not override and rawget(self, BASE_TRANSLATIONS) then
AceLocale.error(self, "Cannot enable dynamic locales after a translation has been registered.")
end
if not rawget(self, DYNAMIC_LOCALES) then
rawset(self, DYNAMIC_LOCALES, true)
if rawget(self, BASE_LOCALE) then
if not rawget(self, TRANSLATION_TABLES) then
rawset(self, TRANSLATION_TABLES, {})
end
self[TRANSLATION_TABLES][self[BASE_LOCALE]] = self[BASE_TRANSLATIONS]
self[TRANSLATION_TABLES][self[CURRENT_LOCALE]] = self[TRANSLATIONS]
end
end
end
function AceLocale.prototype:RegisterTranslations(locale, func)
AceLocale.argCheck(self, locale, 2, "string")
AceLocale.argCheck(self, func, 3, "function")
if locale == rawget(self, BASE_LOCALE) then
AceLocale.error(self, "Cannot provide the same locale more than once. %q provided twice.", locale)
end
if rawget(self, BASE_TRANSLATIONS) and GetLocale() ~= locale then
if rawget(self, DEBUGGING) or rawget(self, DYNAMIC_LOCALES) then
if not rawget(self, TRANSLATION_TABLES) then
rawset(self, TRANSLATION_TABLES, {})
end
if self[TRANSLATION_TABLES][locale] then
AceLocale.error(self, "Cannot provide the same locale more than once. %q provided twice.", locale)
end
local t = func()
func = nil
if type(t) ~= "table" then
AceLocale.error(self, "Bad argument #3 to `RegisterTranslations'. function did not return a table. (expected table, got %s)", type(t))
end
self[TRANSLATION_TABLES][locale] = t
t = nil
end
func = nil
return
end
local t = func()
func = nil
if type(t) ~= "table" then
AceLocale.error(self, "Bad argument #3 to `RegisterTranslations'. function did not return a table. (expected table, got %s)", type(t))
end
rawset(self, TRANSLATIONS, t)
if not rawget(self, BASE_TRANSLATIONS) then
rawset(self, BASE_TRANSLATIONS, t)
rawset(self, BASE_LOCALE, locale)
for key,value in pairs(t) do
if value == true then
t[key] = key
end
end
else
for key, value in pairs(self[TRANSLATIONS]) do
if not rawget(self[BASE_TRANSLATIONS], key) then
AceLocale.error(self, "Improper translation exists. %q is likely misspelled for locale %s.", key, locale)
end
if value == true then
AceLocale.error(self, "Can only accept true as a value on the base locale. %q is the base locale, %q is not.", rawget(self, BASE_LOCALE), locale)
end
end
end
rawset(self, CURRENT_LOCALE, locale)
refixInstance(self)
if rawget(self, DEBUGGING) or rawget(self, DYNAMIC_LOCALES) then
if not rawget(self, TRANSLATION_TABLES) then
rawset(self, TRANSLATION_TABLES, {})
end
self[TRANSLATION_TABLES][locale] = t
end
t = nil
end
function AceLocale.prototype:SetLocale(locale)
AceLocale.argCheck(self, locale, 2, "string", "boolean")
if not rawget(self, DYNAMIC_LOCALES) then
AceLocale.error(self, "Cannot call `SetLocale' without first calling `EnableDynamicLocales'.")
end
if not rawget(self, TRANSLATION_TABLES) then
AceLocale.error(self, "Cannot call `SetLocale' without first calling `RegisterTranslations'.")
end
if locale == true then
locale = GetLocale()
if not self[TRANSLATION_TABLES][locale] then
locale = self[BASE_LOCALE]
end
end
if self[CURRENT_LOCALE] == locale then
return
end
if not self[TRANSLATION_TABLES][locale] then
AceLocale.error(self, "Locale %q not registered.", locale)
end
self[TRANSLATIONS] = self[TRANSLATION_TABLES][locale]
self[CURRENT_LOCALE] = locale
refixInstance(self)
end
function AceLocale.prototype:GetLocale()
if not rawget(self, TRANSLATION_TABLES) then
AceLocale.error(self, "Cannot call `GetLocale' without first calling `RegisterTranslations'.")
end
return self[CURRENT_LOCALE]
end
local function iter(t, position)
return (next(t, position))
end
function AceLocale.prototype:IterateAvailableLocales()
if not rawget(self, DYNAMIC_LOCALES) then
AceLocale.error(self, "Cannot call `IterateAvailableLocales' without first calling `EnableDynamicLocales'.")
end
if not rawget(self, TRANSLATION_TABLES) then
AceLocale.error(self, "Cannot call `IterateAvailableLocales' without first calling `RegisterTranslations'.")
end
return iter, self[TRANSLATION_TABLES], nil
end
function AceLocale.prototype:HasLocale(locale)
if not rawget(self, DYNAMIC_LOCALES) then
AceLocale.error(self, "Cannot call `HasLocale' without first calling `EnableDynamicLocales'.")
end
AceLocale.argCheck(self, locale, 2, "string")
return rawget(self, TRANSLATION_TABLES) and self[TRANSLATION_TABLES][locale] ~= nil
end
function AceLocale.prototype:SetStrictness(strict)
AceLocale.argCheck(self, strict, 2, "boolean")
local mt = getmetatable(self)
if not mt then
AceLocale.error(self, "Cannot call `SetStrictness' without a metatable.")
end
if not rawget(self, TRANSLATIONS) then
AceLocale.error(self, "No translations registered.")
end
rawset(self, STRICTNESS, strict)
refixInstance(self)
end
local function initReverse(self)
rawset(self, REVERSE_TRANSLATIONS, {})
local alpha = self[TRANSLATIONS]
local bravo = self[REVERSE_TRANSLATIONS]
for base, localized in pairs(alpha) do
bravo[localized] = base
end
end
function AceLocale.prototype:GetTranslation(text)
AceLocale.argCheck(self, text, 1, "string", "number")
if not rawget(self, TRANSLATIONS) then
AceLocale.error(self, "No translations registered")
end
return self[text]
end
function AceLocale.prototype:GetStrictTranslation(text)
AceLocale.argCheck(self, text, 1, "string", "number")
local x = rawget(self, TRANSLATIONS)
if not x then
AceLocale.error(self, "No translations registered")
end
local value = rawget(x, text)
if value == nil then
AceLocale.error(self, "Translation %q does not exist for locale %s", text, self[CURRENT_LOCALE])
end
return value
end
function AceLocale.prototype:GetReverseTranslation(text)
local x = rawget(self, REVERSE_TRANSLATIONS)
if not x then
if not rawget(self, TRANSLATIONS) then
AceLocale.error(self, "No translations registered")
end
initReverse(self)
x = self[REVERSE_TRANSLATIONS]
end
local translation = x[text]
if not translation then
AceLocale.error(self, "Reverse translation for %q does not exist", text)
end
return translation
end
function AceLocale.prototype:GetIterator()
local x = rawget(self, TRANSLATIONS)
if not x then
AceLocale.error(self, "No translations registered")
end
return next, x, nil
end
function AceLocale.prototype:GetReverseIterator()
local x = rawget(self, REVERSE_TRANSLATIONS)
if not x then
if not rawget(self, TRANSLATIONS) then
AceLocale.error(self, "No translations registered")
end
initReverse(self)
x = self[REVERSE_TRANSLATIONS]
end
return next, x, nil
end
function AceLocale.prototype:HasTranslation(text)
AceLocale.argCheck(self, text, 1, "string", "number")
local x = rawget(self, TRANSLATIONS)
if not x then
AceLocale.error(self, "No translations registered")
end
return rawget(x, text) and true
end
function AceLocale.prototype:HasReverseTranslation(text)
local x = rawget(self, REVERSE_TRANSLATIONS)
if not x then
if not rawget(self, TRANSLATIONS) then
AceLocale.error(self, "No translations registered")
end
initReverse(self)
x = self[REVERSE_TRANSLATIONS]
end
return x[text] and true
end
function AceLocale.prototype:Debug()
if not rawget(self, DEBUGGING) then
return
end
local words = {}
local locales = {"enUS", "ruRU", "deDE", "frFR", "koKR", "zhCN", "zhTW", "esES"}
local localizations = {}
DEFAULT_CHAT_FRAME:AddMessage("--- AceLocale Debug ---")
for _,locale in ipairs(locales) do
if not self[TRANSLATION_TABLES][locale] then
DEFAULT_CHAT_FRAME:AddMessage(string.format("Locale %q not found", locale))
else
localizations[locale] = self[TRANSLATION_TABLES][locale]
end
end
local localeDebug = {}
for locale, localization in pairs(localizations) do
localeDebug[locale] = {}
for word in pairs(localization) do
if type(localization[word]) == "table" then
if type(words[word]) ~= "table" then
words[word] = {}
end
for bit in pairs(localization[word]) do
if type(localization[word][bit]) == "string" then
words[word][bit] = true
end
end
elseif type(localization[word]) == "string" then
words[word] = true
end
end
end
for word in pairs(words) do
if type(words[word]) == "table" then
for bit in pairs(words[word]) do
for locale, localization in pairs(localizations) do
if not rawget(localization, word) or not localization[word][bit] then
localeDebug[locale][word .. "::" .. bit] = true
end
end
end
else
for locale, localization in pairs(localizations) do
if not rawget(localization, word) then
localeDebug[locale][word] = true
end
end
end
end
for locale, t in pairs(localeDebug) do
if not next(t) then
DEFAULT_CHAT_FRAME:AddMessage(string.format("Locale %q complete", locale))
else
DEFAULT_CHAT_FRAME:AddMessage(string.format("Locale %q missing:", locale))
for word in pairs(t) do
DEFAULT_CHAT_FRAME:AddMessage(string.format(" %q", word))
end
end
end
DEFAULT_CHAT_FRAME:AddMessage("--- End AceLocale Debug ---")
end
setmetatable(AceLocale.prototype, {
__index = function(self, k)
if type(k) ~= "table" and k ~= 0 and k ~= "GetLibraryVersion" and k ~= "error" and k ~= "assert" and k ~= "argCheck" and k ~= "pcall" then -- HACK: remove "GetLibraryVersion" and such later.
AceLocale.error(lastSelf or self, "Translation %q does not exist.", k)
end
return nil
end
})
local function activate(self, oldLib, oldDeactivate)
AceLocale = self
self.frame = oldLib and oldLib.frame or CreateFrame("Frame")
self.registry = oldLib and oldLib.registry or {}
self.BASE_TRANSLATIONS = oldLib and oldLib.BASE_TRANSLATIONS or {}
self.DEBUGGING = oldLib and oldLib.DEBUGGING or {}
self.TRANSLATIONS = oldLib and oldLib.TRANSLATIONS or {}
self.BASE_LOCALE = oldLib and oldLib.BASE_LOCALE or {}
self.TRANSLATION_TABLES = oldLib and oldLib.TRANSLATION_TABLES or {}
self.REVERSE_TRANSLATIONS = oldLib and oldLib.REVERSE_TRANSLATIONS or {}
self.STRICTNESS = oldLib and oldLib.STRICTNESS or {}
self.NAME = oldLib and oldLib.NAME or {}
self.DYNAMIC_LOCALES = oldLib and oldLib.DYNAMIC_LOCALES or {}
self.CURRENT_LOCALE = oldLib and oldLib.CURRENT_LOCALE or {}
BASE_TRANSLATIONS = self.BASE_TRANSLATIONS
DEBUGGING = self.DEBUGGING
TRANSLATIONS = self.TRANSLATIONS
BASE_LOCALE = self.BASE_LOCALE
TRANSLATION_TABLES = self.TRANSLATION_TABLES
REVERSE_TRANSLATIONS = self.REVERSE_TRANSLATIONS
STRICTNESS = self.STRICTNESS
NAME = self.NAME
DYNAMIC_LOCALES = self.DYNAMIC_LOCALES
CURRENT_LOCALE = self.CURRENT_LOCALE
local GetTime = GetTime
local timeUntilClear = GetTime() + 5
scheduleClear = function()
if next(newRegistries) then
self.frame:Show()
timeUntilClear = GetTime() + 5
end
end
if not self.registry then
self.registry = {}
else
for name, instance in pairs(self.registry) do
local name = name
local mt = getmetatable(instance)
setmetatable(instance, nil)
instance[NAME] = name
local strict
if instance[STRICTNESS] ~= nil then
strict = instance[STRICTNESS]
elseif instance[TRANSLATIONS] ~= instance[BASE_TRANSLATIONS] then
if getmetatable(instance[TRANSLATIONS]).__index == oldLib.prototype then
strict = true
end
end
instance[STRICTNESS] = strict and true or false
refixInstance(instance)
end
end
self.frame:SetScript("OnEvent", scheduleClear)
self.frame:SetScript("OnUpdate", function() -- (this, elapsed)
if timeUntilClear - GetTime() <= 0 then
self.frame:Hide()
for k in pairs(newRegistries) do
clearCache(k)
newRegistries[k] = nil
k = nil
end
end
end)
self.frame:UnregisterAllEvents()
self.frame:RegisterEvent("ADDON_LOADED")
self.frame:RegisterEvent("PLAYER_ENTERING_WORLD")
self.frame:Show()
if oldDeactivate then
oldDeactivate(oldLib)
end
end
AceLibrary:Register(AceLocale, MAJOR_VERSION, MINOR_VERSION, activate)
@@ -0,0 +1,462 @@
--[[
Name: AceModuleCore-2.0
Revision: $Rev: 17998 $
Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team)
Inspired By: Ace 1.x by Turan (turan@gryphon.com)
Website: http://www.wowace.com/
Documentation: http://www.wowace.com/index.php/AceModuleCore-2.0
SVN: http://svn.wowace.com/root/trunk/Ace2/AceModuleCore-2.0
Description: Mixin to provide a module system so that modules or plugins can
use an addon as its core.
Dependencies: AceLibrary, AceOO-2.0, AceAddon-2.0, AceEvent-2.0 (optional), Compost-2.0 (optional)
]]
local MAJOR_VERSION = "AceModuleCore-2.0"
local MINOR_VERSION = "$Revision: 17998 $"
if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end
if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check
if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end
local function safecall(func,a,b,c,d,e,f,g)
local success, err = pcall(func,a,b,c,d,e,f,g)
if not success then geterrorhandler()(err) end
end
local table_setn
do
local version = GetBuildInfo()
if string.find(version, "^2%.") then
-- 2.0.0
table_setn = function() end
else
table_setn = table.setn
end
end
local new, del
do
local list = setmetatable({}, {__mode = 'k'})
function new()
local t = next(list)
if t then
list[t] = nil
return t
else
return {}
end
end
function del(t)
for k in pairs(t) do
t[k] = nil
end
table_setn(t, 0)
list[t] = true
return nil
end
end
local AceOO = AceLibrary:GetInstance("AceOO-2.0")
local AceModuleCore = AceOO.Mixin {
"NewModule",
"HasModule",
"GetModule",
"IsModule",
"IterateModules",
"SetModuleMixins",
"SetModuleClass",
"IsModuleActive",
"ToggleModuleActive"
}
local AceEvent
local Compost = AceLibrary:HasInstance("Compost-2.0") and AceLibrary("Compost-2.0")
local function getlibrary(lib)
if type(lib) == "string" then
return AceLibrary(lib)
else
return lib
end
end
local tmp
function AceModuleCore:NewModule(name, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
if not self.modules then
AceModuleCore:error("CreatePrototype() must be called before attempting to create a new module.", 2)
end
AceModuleCore:argCheck(name, 2, "string")
if string.len(name) == 0 then
AceModuleCore:error("Bad argument #2 to `NewModule`, string must not be empty")
end
if self.modules[name] then
AceModuleCore:error("The module %q has already been registered", name)
end
if not tmp then
tmp = {}
end
if a1 then table.insert(tmp, a1)
if a2 then table.insert(tmp, a2)
if a3 then table.insert(tmp, a3)
if a4 then table.insert(tmp, a4)
if a5 then table.insert(tmp, a5)
if a6 then table.insert(tmp, a6)
if a7 then table.insert(tmp, a7)
if a8 then table.insert(tmp, a8)
if a9 then table.insert(tmp, a9)
if a10 then table.insert(tmp, a10)
if a11 then table.insert(tmp, a11)
if a12 then table.insert(tmp, a12)
if a13 then table.insert(tmp, a13)
if a14 then table.insert(tmp, a14)
if a15 then table.insert(tmp, a15)
if a16 then table.insert(tmp, a16)
if a17 then table.insert(tmp, a17)
if a18 then table.insert(tmp, a18)
if a19 then table.insert(tmp, a19)
if a20 then table.insert(tmp, a20)
end end end end end end end end end end end end end end end end end end end end
for k,v in ipairs(tmp) do
tmp[k] = getlibrary(v)
end
if self.moduleMixins then
for _,mixin in ipairs(self.moduleMixins) do
local exists = false
for _,v in ipairs(tmp) do
if mixin == v then
exists = true
break
end
end
if not exists then
table.insert(tmp, mixin)
end
end
end
local module = AceOO.Classpool(self.moduleClass, unpack(tmp)):new(name)
self.modules[name] = module
module.name = name
module.title = name
AceModuleCore.totalModules[module] = self
if AceEvent then
AceEvent:TriggerEvent("Ace2_ModuleCreated", module)
end
for k in pairs(tmp) do
tmp[k] = nil
end
table_setn(tmp, 0)
return module
end
function AceModuleCore:HasModule(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
if a1 then if not self.modules[a1] then return false end
if a2 then if not self.modules[a2] then return false end
if a3 then if not self.modules[a3] then return false end
if a4 then if not self.modules[a4] then return false end
if a5 then if not self.modules[a5] then return false end
if a6 then if not self.modules[a6] then return false end
if a7 then if not self.modules[a7] then return false end
if a8 then if not self.modules[a8] then return false end
if a9 then if not self.modules[a9] then return false end
if a10 then if not self.modules[a10] then return false end
if a11 then if not self.modules[a11] then return false end
if a12 then if not self.modules[a12] then return false end
if a13 then if not self.modules[a13] then return false end
if a14 then if not self.modules[a14] then return false end
if a15 then if not self.modules[a15] then return false end
if a16 then if not self.modules[a16] then return false end
if a17 then if not self.modules[a17] then return false end
if a18 then if not self.modules[a18] then return false end
if a19 then if not self.modules[a19] then return false end
if a20 then if not self.modules[a20] then return false end
end end end end end end end end end end end end end end end end end end end end
return true
end
function AceModuleCore:GetModule(name)
if not self.modules then
AceModuleCore:error("Error initializing class. Please report error.")
end
if not self.modules[name] then
AceModuleCore:error("Cannot find module %q.", name)
end
return self.modules[name]
end
function AceModuleCore:IsModule(module)
if self == AceModuleCore then
return AceModuleCore.totalModules[module]
else
for k,v in pairs(self.modules) do
if v == module then
return true
end
end
return false
end
end
function AceModuleCore:IterateModules()
local t = new()
for k in pairs(self.modules) do
table.insert(t, k)
end
table.sort(t)
local i = 0
return function()
i = i + 1
local x = t[i]
if x then
return x, self.modules[x]
else
t = del(t)
return nil
end
end, nil, nil
end
function AceModuleCore:SetModuleMixins(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
if self.moduleMixins then
AceModuleCore:error('Cannot call "SetModuleMixins" twice')
elseif not self.modules then
AceModuleCore:error("Error initializing class. Please report error.")
elseif next(self.modules) then
AceModuleCore:error('Cannot call "SetModuleMixins" after "NewModule" has been called.')
end
self.moduleMixins = Compost and Compost:Acquire(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) or {a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20}
for k,v in ipairs(self.moduleMixins) do
self.moduleMixins[k] = getlibrary(v)
end
end
function AceModuleCore:SetModuleClass(class)
class = getlibrary(class)
AceModuleCore:assert(AceOO.inherits(class, AceOO.Class), "Bad argument #2 to `SetModuleClass' (Class expected)")
if not self.modules then
AceModuleCore:error("Error initializing class. Please report error.")
end
if self.customModuleClass then
AceModuleCore:error("Cannot call `SetModuleClass' twice.")
end
self.customModuleClass = true
self.moduleClass = class
self.modulePrototype = class.prototype
end
function AceModuleCore:ToggleModuleActive(module, state)
AceModuleCore:argCheck(module, 2, "table", "string")
AceModuleCore:argCheck(state, 3, "nil", "boolean")
if type(module) == "string" then
if not self:HasModule(module) then
AceModuleCore:error("Cannot find module %q", module)
end
module = self:GetModule(module)
else
if not self:IsModule(module) then
AceModuleCore:error("%q is not a module", module)
end
end
local disable
if state == nil then
disable = self:IsModuleActive(module)
else
disable = not state
if disable ~= self:IsModuleActive(module) then
return
end
end
if type(module.ToggleActive) == "function" then
return module:ToggleActive(not disable)
elseif AceOO.inherits(self, "AceDB-2.0") then
if not self.db or not self.db.raw then
AceModuleCore:error("Cannot toggle a module until `RegisterDB' has been called and `ADDON_LOADED' has been fireed.")
end
if type(self.db.raw.disabledModules) ~= "table" then
self.db.raw.disabledModules = Compost and Compost:Acquire() or {}
end
local _,profile = self:GetProfile()
if type(self.db.raw.disabledModules[profile]) ~= "table" then
self.db.raw.disabledModules[profile] = Compost and Compost:Acquire() or {}
end
if type(self.db.raw.disabledModules[profile][module.name]) ~= "table" then
self.db.raw.disabledModules[profile][module.name] = disable or nil
end
if not disable then
if not next(self.db.raw.disabledModules[profile]) then
if Compost then
Compost:Reclaim(self.db.raw.disabledModules[profile])
end
self.db.raw.disabledModules[profile] = nil
end
if not next(self.db.raw.disabledModules) then
if Compost then
Compost:Reclaim(self.db.raw.disabledModules)
end
self.db.raw.disabledModules = nil
end
end
else
if type(self.disabledModules) ~= "table" then
self.disabledModules = Compost and Compost:Acquire() or {}
end
self.disabledModules[module.name] = disable or nil
end
if AceOO.inherits(module, "AceAddon-2.0") then
local AceAddon = AceLibrary("AceAddon-2.0")
if not AceAddon.addonsStarted[module] then
return
end
end
if not disable then
local current = module.class
while true do
if current == AceOO.Class then
break
end
if current.mixins then
for mixin in pairs(current.mixins) do
if type(mixin.OnEmbedEnable) == "function" then
safecall(mixin.OnEmbedEnable, mixin, module)
end
end
end
current = current.super
end
if type(module.OnEnable) == "function" then
safecall(module.OnEnable, module)
end
if AceEvent then
AceEvent:TriggerEvent("Ace2_AddonEnabled", module)
end
else
local current = module.class
while true do
if current == AceOO.Class then
break
end
if current.mixins then
for mixin in pairs(current.mixins) do
if type(mixin.OnEmbedDisable) == "function" then
safecall(mixin.OnEmbedDisable, mixin, module)
end
end
end
current = current.super
end
if type(module.OnDisable) == "function" then
safecall(module.OnDisable, module)
end
if AceEvent then
AceEvent:TriggerEvent("Ace2_AddonDisabled", module)
end
end
return not disable
end
function AceModuleCore:IsModuleActive(module)
AceModuleCore:argCheck(module, 2, "table", "string")
if AceModuleCore == self then
self:argCheck(module, 2, "table")
local core = AceModuleCore.totalModules[module]
if not core then
self:error("Bad argument #2 to `IsModuleActive'. Not a module")
end
return core:IsModuleActive(module)
end
if type(module) == "string" then
if not self:HasModule(module) then
AceModuleCore:error("Cannot find module %q", module)
end
module = self:GetModule(module)
else
if not self:IsModule(module) then
AceModuleCore:error("%q is not a module", module)
end
end
if type(module.IsActive) == "function" then
return module:IsActive()
elseif AceOO.inherits(self, "AceDB-2.0") then
local _,profile = self:GetProfile()
return not self.db or not self.db.raw or not self.db.raw.disabledModules or not self.db.raw.disabledModules[profile] or not self.db.raw.disabledModules[profile][module.name]
else
return not self.disabledModules or not self.disabledModules[module.name]
end
end
function AceModuleCore:OnInstanceInit(target)
if target.modules then
AceModuleCore:error("OnInstanceInit cannot be called twice")
end
target.modules = Compost and Compost:Acquire() or {}
target.moduleClass = AceOO.Class("AceAddon-2.0")
target.modulePrototype = target.moduleClass.prototype
end
AceModuleCore.OnManualEmbed = AceModuleCore.OnInstanceInit
function AceModuleCore.OnEmbedProfileDisable(AceModuleCore, self, newProfile)
if not AceOO.inherits(self, "AceDB-2.0") then
return
end
local _,currentProfile = self:GetProfile()
for k, module in pairs(self.modules) do
if type(module.IsActive) == "function" or type(module.ToggleActive) == "function" then
-- continue
else
local currentActive = not self.db or not self.db.raw or not self.db.raw.disabledModules or not self.db.raw.disabledModules[currentProfile] or not self.db.raw.disabledModules[currentProfile][module.name]
local newActive = not self.db or not self.db.raw or not self.db.raw.disabledModules or not self.db.raw.disabledModules[newProfile] or not self.db.raw.disabledModules[newProfile][module.name]
if currentActive ~= newActive then
self:ToggleModuleActive(module)
if not self.db.raw.disabledModules then
self.db.raw.disabledModules = {}
end
if not self.db.raw.disabledModules[currentProfile] then
self.db.raw.disabledModules[currentProfile] = {}
end
self.db.raw.disabledModules[currentProfile][module.name] = not currentActive or nil
end
end
end
end
local function activate(self, oldLib, oldDeactivate)
AceModuleCore = self
if oldLib then
self.totalModules = oldLib.totalModules
end
if not self.totalModules then
self.totalModules = {}
end
self.super.activate(self, oldLib, oldDeactivate)
end
local function external(self, major, instance)
if major == "Compost-2.0" then
Compost = instance
elseif major == "AceEvent-2.0" then
AceEvent = instance
end
end
AceLibrary:Register(AceModuleCore, MAJOR_VERSION, MINOR_VERSION, activate, nil, external)
AceModuleCore = AceLibrary(MAJOR_VERSION)
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